# Write 32-bit number on 16-bit CPU

I'm writing some code that needs to be compatible with a 16-bit CPU (8086/8088). I have a 32-bit number stored in two word registers (e.g., DX:AX) and I need to write the value of the number in decimal format. For example, FFFF_FFFFh needs to be written as 4294967295.

Anybody have some sample code on how to do this?

• After some research, I found a way to do this. The algorithm I discovered is comparatively slow, but is general enough that it can easily be extended to any number of bits (it's not limited to 32). It can also easily be adjusted to work on 32-bit or 64-bit CPU's. If anybody cares, let me know and I'll post the code here.
• I'd love to see how you did it, if that's OK. I'm working on that exact same problem.

I'm using an old TMS9900 processor, and need to do some 32 bit arithmetic. I have done the adding and subraction (and by extension, multiplication and division), but the thing that is stumping me, is how do I display a 32 bit value, which is split across two 16 registers, in decimal, when the largest useful dividend I can express (for base 10) is 10,000. Hmmmph....

So, basically, I have: 1,234,567 which exists in the memory as:

First 16 bit word: \$0012
Second 16 bit word: \$D687

(The TMS9900 is big endian)

And I've got to convert that into 1234567 for screen display purposes.

Crikey. Bit stumped on that one!

(The TMS9900 has '32 bit' multiply and divide, but....

Multiply: You can multiply two 16 bit values, giving a 32 bit result.
Divide: You can divide a 32 bit value, by a 16 bit dividend (hence the largest 'useful' divisor for base 10 is 10,000).

I'd be extremely interested to see your solution. I hope I can understand Intel assembly!

Thanks

Mark
• Actually, that's the same way that 8086/8088's work. The CPU can do 32-bit multiplication and division, but uses 16-bit registers, so has the same issues your CPU does. Anyway, here's the code. Hopefully you'll be able to "translate" it so it works on your CPU.

[code]
;------------------------------------------------------------------------------
;WRITE A DWORD TO THE SCREEN AS A DECIMAL
;Inputs: DX:AX = DWord to Write
;Outputs: To Screen
;Changes:
;------------------------------------------------------------------------------
WriteDWordMin:
PUSH AX,BX,CX,DX ;Save used registers
PUSH DI,SI,BP ;Save used registers
MOV SI,03B9Ah ;Start Divisor (SI:DI)
MOV DI,0CA00h ; at 1,000,000,000
XOR BP,BP ;Initialize character counter
D10: ;Loop to here for each possible character
MOV CX,SI ;CX:BX =
MOV BX,DI ; Divisor
CALL Divide32s ;Do the division (rtns Quotient DX:AX, Remainder CX:BX)
OR AX,AX ;Non-zero character?
JNZ >D20 ;If so, we need to write it
OR BP,BP ;Has there already been a non-zero character?
JNZ >D20 ;If so, we need to write it
OR SI,SI ;Is this the last character?
JNZ >D40 ;If not, we don't need to write it
CMP DI,1 ;Is this the last character?
JNE >D40 ;If so, we need to write it
D20: ;Need to write this character
INC BP ;Increment character counter
ADD AL,'0' ;Convert the Number to ASCII
D30: ;Write the character
CALL WriteAL ;Write it
D40: ;Done writing character, if appropriate
JNZ >D45 ;If not, continue
JE >D90 ;If so, we're done
D45: ;Not done yet
MOV DX,SI ;DX:AX =
MOV AX,DI ; Current Divisor
PUSH CX,BX ;Save Current Remainder (New Dividend) on the stack
XOR CX,CX ;Divisor (CX:BX) =
MOV BX,10 ; 10
CALL Divide32s ;Do the division (calculate new Divisor)
MOV SI,DX ;SI:DI =
MOV DI,AX ; New Divisor
POP AX,DX ;DX:AX = New Dividend (Old Remainder)
JMP D10 ;Keep going
D90: ;We're done
POP BP,SI,DI ;Restore used registers
POP DX,CX,BX,AX ;Restore used registers
RET

;------------------------------------------------------------------------------
;DIVIDE TWO 32-BIT NUMBERS BY EACH OTHER, USING ONLY 16-BIT OPERATIONS
;Inputs: DX:AX = Dividend
; CX:BX = Divisor
;Outputs: DX:AX = Quotient
; CX:BX = Remainder
;Changes:
;NOTES: This is UNSIGNED division!
; This is also a slow division process, but the code is small and
; we only do it a few times in this program if we do it at all,
; so there's no compelling need to optimize for speed.
; This does not check for division by 0 (returns -1 instead of an error).
; I find the code quite interesting, because it does not actually require
; the CPU to do any divison at all -- only shifts, compares, and
; subtracts. The main reason it's so slow is that the loop must be
; performed all 32 times -- there's no way to exit early.
; This implementation came from The Art of Assembly Language by
; Randall Hyde. This is an ASM implementation of the following
; pseudocode:
;
; Quotient := Remainder;
; Remainder := 0;
; FOR I := 1 TO NumberOfBits DO
; Remainder:Quotient := Remainder:Quotient SHL 1;
; IF Remainder >= Divisor THEN
; Remainder := Remainder - Divisor;
; Quotient := Quotient + 1;
; ENDIF
; ENDFOR
;
; During the entire routine:
; DX:AX = Dividend
; CX:BX = Divisor
; SI:DI = Remainder
;------------------------------------------------------------------------------
Divide32s:
OR CX,CX ;Is the Divisor really 32 bits?
JNZ >V20 ;If so, handle it
CMP DX,BX ;Will we only need to do one division?
MOV CX,AX ;CX = Dividend-Low
MOV AX,DX ;DX:AX =
XOR DX,DX ; Dividend
DIV BX ;Do the Division (AX = Quotient)
XCHG AX,CX ;CX = Quotient-High, AX = Dividend-Low
V10: ;First division handled, if appropriate
DIV BX ;AX = Quotient-Low
MOV BX,DX ;BX = Remainder-Low
MOV DX,CX ;DX = Quotient-High
XOR CX,CX ;CX = Remainder-High
JMP >V90 ;Done

V20: ;Divisor really is 32 bits
PUSH DI,SI,BP ;Save used registers
MOV BP,32 ;Do 32 bits
XOR SI,SI ;Remainder :=
XOR DI,DI ; 0
V30: ;Loop to here for each bit
SHL AX,1 ;Remainder:Quotient :=
RCL DX,1 ; Remainder:Quotient
RCL DI,1 ; SHL
RCL SI,1 ; 1
CMP SI,CX ;Remainder HO word more than Divisor HO word?
JA >V40 ;If so, handle it
JB >V50 ;If not, we're done with this bit
V35: ;Remainder HO word = Divisor HO word
CMP DI,BX ;Remainder LO word more than Divisor LO word?
JB >V50 ;If not, we're done with this bit
V40:
SUB DI,BX ;Remainder :=
SBB SI,CX ; Remainder - Divisor
INC AX ;Increment Quotient
V50: ;Go to the next bit
DEC BP ;Decrement Loop Counter
JNZ V30 ;If not 0 yet, keep looking
MOV CX,SI ;Put Remainder
MOV BX,DI ; in CX:BX for the return
POP BP,SI,DI ;Restore used registers
V90: ;Done
RET
[/code]