Search code examples
assemblyx86dosmasmx86-16

Storing and incrementing integer variables in MASM x86 assembly


I'm trying to understand assembly. Today's topic is: "Storing and incrementing int variables and printing numbers" :)

So here's my code

dane segment
     count     db     0
dane ends

code segment
start:
     inc     byte ptr ds:[count]
     inc     byte ptr ds:[count]
     inc     byte ptr ds:[count]

     mov     dl,ds:[count]
     mov     ah,2
     int     21h

     mov     ah,4ch
     int        21h
code ends
end start

When I compile and run, I got this error:

enter image description here

It only happens when count is incremented more than once. What wrong am I doing?

Can somebody explain why I can't simply write:

inc     count

Also, why I have to put byte ptr when incrementing values, but not if I copy it to dl register?


Solution

  • Edit: rewrote and added text, added code (works in MASM 6.11):

    In x86 assembbly you need to specify the size of the memory operand for instructions in which there would be ambiguity otherwise. You have to tell the assembler the size of the operand with byte ptr, word ptr etc. For that reason inc byte ptr ds:[count] needs byte ptr. inc word ptr ds:[count is different instruction.

    mov dl,ds:[count] do not need byte ptr, because dl is a 8-bit register and therefore the size of the memory operand is always the same, a byte (8 bits).

    To print characters, you need to first convert the number into a string (or a single character for numbers less than 10). Otherwise you are printing control character ETX (see ASCII table) that has ASCII code 3. For numbers less than 10, printing in decimal is trivial, just add '0' (30h), like this:

    mov     dl,ds:[count]
    add     dl,'0'        ; add dl,30h
    

    Printing in decimal in x86 assembly is a asked quite often, see for example Is this code correct (Number plus number, then print the result) to get the idea.

    Edit: EXE files need also a stack segment.

    DOS EXE files need to have a stack segment. Assembling with MASM 6.11 does not give any warnings or errors, basically with dosexe.asm (replace dosexe.asm to your assembly code file name).

    But linking with ML (ml dosexe.obj) gives the following warning:

    LINK : warning L4021: no stack segment
    

    So, a stack segment needs to be added, add the following lines to top of the source.

    .model small
    .stack 4096
    

    There was also some problem with data segment definition, I fixed that too.

    The entire fixed assembly code could be something like this (works in MASM 6.11):

    .model small
    .stack 4096
    
    .data
            count     db 0
    
    .code
    start:
            mov     ax,seg count
            mov     ds,ax
    
            inc     byte ptr ds:[count]
            inc     byte ptr ds:[count]
            inc     byte ptr ds:[count]
    
    ; this code does the printing in decimal without needing to reverse
    ; the string, by dividing the divisor. 100/10 = 10
    
            mov     bl,100     ; original divisor.
    
            mov     al,ds:[count]
    
    print_loop:
            xor     ah,ah ; clear top 8 bits of the dividend (in ax).
            div     bl    ; divide ax by bl. quotient in al, remainder in ah.
    
            mov     dl,al     ; the value to be printed with ah = 2, int 21h
    
            mov     al,ah     ; remainder gets divided in the next loop.
            test    dl,dl     ; check if number is zero
            jz      not_print ; don't print leading zeros.
    
            add     dl,'0'    ; convert to ASCII numeric characters range.
                              ; '0' = 0x30 = 48
            push    ax
            mov     ah,2
            int     21h       ; print the value, according to Ralf Brown's
            pop     ax        ; interrupt list returns al.
    
    not_print:
            push    ax
            mov     al,bl
            xor     ah,ah
            mov     bl,10     ; divide the divisor by 10.
            div     bl
            mov     bl,al     ; the new divisor.
            pop     ax
            test    bl,bl
            jnz     print_loop
    
            mov     ah,4ch
            int     21h
    end start