1

So I have found numerous answers for this on Stack Overflow but I still cannot get it to work. What is wrong with my code?

        mov al, 12
        mov bl, 12
        mul bl          ; answer in ax
        aam

        mov bx, 10          ; Divisor constant
        xor cx, cx          ; Clear counter
        .a: xor dx, dx      ; Clear dx
            div bx          ; Divide ax with bx, quotient in al, remainder in ah 
            mov dh, ah      ; Move ah to dh, so we can push the 16 bit register of dx
            push dx         ; Push dx which contains the remainder
            inc cx          ; Increment counter
            test al, al     ; If al (the quotient) is not zero, loop again. 
            jnz .a          
        .b: pop dx          ; Pop the last remainder to dx from stack
            add dx, '0'     ; Add '0' to make it into character
            push cx         ; Push loop counter to stack so printing wont interfere with it
            mov eax, 4      ; 
            mov ebx, 1      ;
            mov ecx, edx    ; Print last popped character
            mov edx, 1      ;
            int 0x80        ;
            pop cx          ; Pop the loop counter back to cx
            loop .b         ; loop for as long as cx is not 0
Lauri
  • 61
  • 1
  • 8
  • 1
    Drop `aam`. Note that `div bx` (16-bit divisor) leaves remainder in `dx` quotient in `ax`. Even if the remainder was in `ah` you should move it to `dl` not `dh`. As is, drop `mov dh, ah` and change `test al, al` to `test ax, ax`. Furthermore I believe that int 80h service 4 takes a **pointer** in `ecx`, not a codepoint. Therefore move `pop dx` to after `pop cx` and replace `mov ecx, edx` by `lea ecx, [esp + 2]` (so it will point to the `dx` value that's still in memory on the stack). – ecm Nov 22 '21 at 18:56
  • @ecm You are right about the other stuff, but `lea ecx, [esp + 2]` is not right as it is dumping a bunch of unknown characters and a segfault in to the terminal. – Lauri Nov 22 '21 at 19:15
  • Does it work with `mov ecx, edx` ? – ecm Nov 22 '21 at 19:15
  • @ecm it does not. – Lauri Nov 22 '21 at 19:16
  • 1
    I forgot about the `add dx`. Move that from where it is to before `push dx`. Also you need an address size prefix in front of `loop` so that it will use a16 address size 16 bits, not your section's default a32. Then your .b loop should read `.b:` \ `push cx` \ `mov eax, 4` \ `mov ebx, 1` \ `lea ecx, [esp + 2]` \ `mov edx, 1` \ `int 80h` \ `pop cx` \ `pop dx` \ `a16 loop .b` – ecm Nov 22 '21 at 19:21
  • (Edited my last comment with a crucial `pop dx` added that I forgot about.) – ecm Nov 22 '21 at 19:24
  • @ecm Fantastic, now it is working. Thanks a lot. – Lauri Nov 22 '21 at 19:26
  • 1
    It's very inefficient to make a separate `write` system call for every character. Instead, store into a buffer on the stack (in descending order) and make one `write` call, as shown in [How do I print an integer in Assembly Level Programming without printf from the c library?](https://stackoverflow.com/a/46301894) – Peter Cordes Nov 22 '21 at 22:27

1 Answers1

2

The AAM instruction could be useful for cases where the result would have at most 2 digits. In your case 12 x 12 = 144, so that's a no-go.

The conversion loop that you use is almost correct, but you are mixing sizes in relation to the DIV instruction. If you give a byte-sized operand to DIV then the operation will divide AX by that byte and the remainder will be in AH. If you give a word-sized operand to DIV then the operation will divide DX:AX by that word and the remainder will be in DX.

Because you're writing 32-bit code, better also write the conversion using 32-bit registers.

Instead of using a digits counter, use a sentinel on the stack. No need to preserve ECX while displaying.

The display function expects a pointer in ECX. Just use the stackpointer for this and pop the value post-printing.

    mov   al, 12
    mov   bl, 12
    mul   bl               ; answer in AX
    movzx eax, ax

    mov   ebx, 10          ; Divisor constant
    push  ebx              ; Sentinel
.a: xor   edx, edx
    div   ebx              ; Divide EDX:EAX with EBX, quotient EAX, remainder EDX 
    add   edx, '0'         ; Add '0' to make it into character
    push  edx
    test  eax, eax         ; If EAX (the quotient) is not zero, loop again. 
    jnz   .a

.b: mov   eax, 4
    mov   ebx, 1
    mov   ecx, esp
    mov   edx, 1
    int   0x80
    pop   eax
    cmp   dword [esp], 10  ; Is it the sentinel ?
    jne   .b               ; No, it's a digit
    pop   eax              ; Remove sentinel

Although not 32-code, Displaying numbers with DOS has more info about converting numbers into text.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76