ASM printing big numbers

2.3k views Asked by At

I'm supposed to write program in NASM (and test it under DosBox) that would calculate factorial with one constraint: result will be stored in upto 128bits. Therefore maximum to be calculated is factorial(34). My problem is that I have no idea how to print such a big number. The only hint I have is to use DOS interrupts but after a long research I've found nothing that would help me.

The part calculating factorial that I've already made is:

        org 100h
section     .text
factorial   dw 1200h

        xor edx, edx
        xor eax, eax
        mov ax, 6d      ;we'll be calculating 6!
        mov word [factorial+16], 1d     ;current result is 1

wloop:
        mov cx, 08h     ;set loop iterator to 8
        xor edx, edx    ;clear edx
    mloop:                  ;iterate over cx (shift)
        mov bx, cx      ;copy loop iterator to bx (indirect adressing will be used)
        add bx, bx      ;bx*=2
        push ax         ;store ax (number to multiply by)
        mul word[factorial+bx]  ;multiply ax and result
        mov word[factorial+bx], ax  ;store new result in [factorial+2*cx]
        pop ax          ;restore previous ax
        push dx         ;transfer from mul is stored in stack
        loop mloop      ;loop over cx until it's 0

        mov cx, 7h      ;set loop iterator to 7
    tloop:                  ;iterate over cx, adding transfers to result
        pop dx          ;pop it from stack
        mov bx, cx      ;bx = cx
        add bx, bx      ;bx = bx+bx
        adc [factorial+bx],dx ;add transfer to [factorial+2*cx]
        loop tloop      ;loop over cx until it's 0

        pop bx          ;one redundant transfer is removed from stack
        dec ax          ;decrease ax (number to multiply by)
        cmp ax, 0       ;if ax is non-zero...
        jne wloop       ;...continue multiplying

        movzx eax, word[factorial+16] ;load last 32 bits of result...
        call println    ;...and print them

        jmp exit        ;exit program

And well... I know how to print 32 bits number but it's probably not the way I would like to print 128b number:

println:                    ;Prints from eax
        push eax
        push edx
        mov ecx, 10
    loopr:  
        xor edx, edx
        div ecx         ; eax <- eax/10, edx <- eax % 10
        push eax        ; stack <- eax (store, because DOS will need it)
        add dl, '0'     ; edx to ASCII
        mov ah,2        ; DOS char print
        int 21h         ; interrupt to DOS
        pop eax         ; stack -> eax (restoring)
        cmp eax, 0
        jnz loopr

        mov dl, 13d     ;Carriage Return
        mov ah, 2h
        int 21h
        mov dl, 10d     ;Line Feed
        mov ah, 2h
        int 21h

        pop edx
        pop eax
        ret

Could anyone help me and give some hints how to deal with it? I don't even know whether factorial is calculated properly because I can't print anything bigger than 32b...

1

There are 1 answers

0
doynax On

You do it the precisely same way as you handle the small number, that is successively divide by ten and get the smallest digit as remainder. Don't forget to reverse the order of the digits when printing though (the stack comes in handy for this.)

A multi-word division by ten is easily implemented by walking through the words in the extended integer from most to least significant, dividing by ten, and carrying the intermediate remainders to the next division as the upper word (in DX, where DIV divides DX:AX.)

For reference here is the equivalent algorithm in C which you may use as a starting point. Where the 128-bit number is stored as eight little-endian 16-bit words.

uint16_t extended_div(uint16_t *num, uint16_t den, size_t len) {
    ldiv_t acc = { 0 };
    num += len;
    do {
        acc = ldiv(acc.rem << 16 | *--num, den);
        *num = acc.quot;
    } while(--len);
    return acc.rem;
}

For extra credit you might use the x86's BCD instructions (AAM in this case) as an alternative and calculate multiplications of the factorial directly in decimal format.


First of all be sure to get yourself a usable debugger if you haven't already, so you may step through the test cases and follow along on paper. Personally I used Turbo Debugger back in the day and I believe NASM is capable of producing compatible symbolic information. I don't mean to nag but previous posters concerned with 16-bit DOS assembly have tended to omit this crucial step of the process.