Ashfaqur Rahaman
Ashfaqur Rahaman

Reputation: 758

nasm assembly: Can't find valid values for all labels after 1004 passes

I am trying to write an x86 assembly code for NASM assembler, which will convert a hexadecimal number into a string and print it. For simplicity I have assumed that my hexadecimal number will only contain digits(eg. 0x1234). Here is the code:

print_hex.asm


    [org 0x7c00]
    
    mov dx, 0x1234
    call print_hex
    
    jmp $
    
    print_hex:
        push bx
        push cx
        push dx
        push ax
    
        mov bx, HEX_STR
        mov cx, 0x000f
        mov ax, 0x0000  ; length counter
    
    loop: 
        push bx         ; save bx
        and cx, dx      ; 0xabcd & 0x000f
        add bx, 5       ; find position in the template
        sub bx, ax
        add [bx], cl 
        pop bx
        shr dx, 4       ; next digit
        mov cx, 0x000f
        inc ax          ; increment counter
        cmp ax, 4
        jl loop         ; loop through the digits
    
        pop ax
    
        call print_string
    
        pop dx
        pop cx
        pop bx
        ret
            
    
    %include "print_string.asm"
    
    HEX_STR:           ; template string
        db '0x0000', 0
    
    times 510-($-$$) db 0
    dw 0xaa55

print_string.asm


    print_string:
        mov ah, 0x0e
        push bx
    
        loop:
            cmp BYTE [bx], 0
            je end
            mov al, [bx]
            int 0x10
            inc bx
            jmp loop
    
        end:
            pop bx
            ret

If I try to assemble/compile print_hex.asm with NASM, it gives following error:

print_hex.asm:error: Can't find valid values for all labels after 1004 passes, giving up.

print_hex.asm:error: Possible causes: recursive EQUs, macro abuse.

I have noticed if I don't use any labels(like loop label here) code works fine.

Upvotes: 2

Views: 2028

Answers (1)

Peter Cordes
Peter Cordes

Reputation: 364068

Your actual problem is that you defined the label loop: twice.
NASM 2.14.02 prints a nice error message:

$ nasm print_hex.asm 
print_string.asm:5: error: label `loop' inconsistently redefined
print_hex.asm:19: note: label `loop' originally defined here

The %include preprocessor directive works just like in C #include: the sources effectively become part of the same file, sharing the same namespace for symbols.

Presumably you have an older version of NASM that printed the insane and unhelpful error message. IDK if the it's related to the fact that loop is also an instruction mnemonic. It shouldn't be, but it's hard to imagine NASM having such an unhelpful handling of duplicate labels in any recent version. That's a common enough mistake in hand-written asm, and that's what NASM is usually used on. So perhaps NASM had a bug that snuck loop past the usual duplicate detection and got it to try and fail.

loop: disambiguates the line as definitely a label, not an instruction line for NASM. (But not YASM: it won't let you use loop: as a label)


Use local label names

.loop: is scoped to the previous non-local label. So it's like shorthand for print_hex.loop and print_string.loop in both the declaration and use.

https://www.nasm.us/doc/nasmdoc3.html#section-3.9


Code review

BTW, your code is pretty bloated. You don't need to save/restore every register in every function; just let them clobber regs.

Also, you can convert int->hex more efficiently, and without needing a template to add into. (The initial 0x is handy but the rest you can calculate in regs). Also, I don't think your code handles the 0..9 vs. a..f split: those ranges of ASCII character codes are unfortunately not adjacent to each other.

See How to convert a number to hex? for a simple 32-bit version you can easily port to 16-bit, with a lookup table or with a conditional to handle the 0..9 vs. a..f. See also https://codegolf.stackexchange.com/revisions/193842/1 for a simple version with a conditional branch. (A later version saves even more code size using DAS).

Use small constants as immediate operands, like and al, 0xf, instead of putting them into CX. Decrement a pointer from the end of the buffer if you want, or with hex you can use a rotate to get nibbles from the top and generate in printing order.

Upvotes: 3

Related Questions