Ray,
Can you explain to us mere mortals where the BCD elements are in Dixon's code? I can't see them...
uqword proc ; lpbuf:DWORD, lpNumber:DWORD ;unsigned DWORD to ASCII, Paul Dixon
mov ecx, [esp+2*4] ; lp qword number
mov eax, [ecx] ; eax->low dword
mov edx, [ecx+4] ; edx->high dword
mov ecx, [esp+1*4] ; ecx, lpbuf
or edx, edx ;if top word is not used then..
jz udword ; .. use unsigned Dword routine as it;s faster
push ebp ;save registers that need to be saved
push esi
mov ebp, eax ; save a copy of low word for later
mov esi, edx ; save a copy of high word for later
mov pAnswer, ecx ; save a copy of buffer pointer for later, don;t stack it or it;s awkward to get back
; do 64 bit multiply by 2^110\1e14+1 to make it more likely I get no rounding errors = 0B424DC35 095CD810h
; this is a 4 part operation, LOxLO, LOxHI, HIxLO, HIxHI and add the 4 results offset appropriately
mov ecx, 095CD810h ; 2^110\1e14+1 low word
mul ecx ;
mov eax, esi ; get number high word, LSBs of MUL not needed so they;re ignored
; nop
push edi
push ebx
mov edi, edx ; save high word of result
mul ecx ; now do high word mul
mov ecx, 0B424DC35h ; get ready for other half of MUL
add edi, eax ; Add low word into result
adc edx, 0 ; and handle the possible carry
mov eax, ebp ; Get low word of number again
mov ebx, edx ; save high word of answer
mul ecx ; do next part
add edi, eax ; add into answer
mov eax, esi ; get high word of number
adc ebx, edx ; add it in to answer #####? possible carry to higher word? probably not..
mul ecx ; do final mul
mov ecx, 1000000 ; ready for later
add ebx, eax ; add in result
adc edx, 0 ; and carry
add edi, 16384 ; round up last bit to decrease error to within that required.
adc ebx, 0
adc edx, 0
shrd edi, ebx, 14 ;correct for the 14 bit shift used to increase accuracy
shrd ebx, edx, 14
shr edx, 14 ; edx contains top 6 digits, edi:ebx contain the information to get the next 6 digits
; edx = 2D093h ebx= 70D42573h edi=603A5EDAh
; 64 bit multiply done
; result in edx:ebx:edi , original number in esi:ebp
; x 1000000 to get next 6 digits
mov eax, edi
mov esi, edx ;save top6 in esi
mul ecx
mov eax, ebx ;do low word x1 000 000
mov ebx, edx ;
mul ecx ;do high word x 1 000 000
add eax, ebx ;add both together
adc edx, 0
mov ebx, edx ;save 2nd 6 in ebx
; now get ((top6 x 1e6) + next6)*1e8 and sub from original number to leave last 8 digits
; since the 8 digits we want are all contained in the low word we can completely ignore the high word
; this allows imul and does away with carries to the high word
mov eax, esi ;get top 6
imul eax, ecx ;top 6 x 1 000 000
mov ecx, 100000000
add eax, ebx ;top6 x 1 000 000 + next 6
imul eax, ecx
sub ebp, eax ;ebp=last 8 digits
; 20 digits broken into 6,6,8 now display them
; esi=top6, ebx=next 6, ebp=last 8
; do top 6 digits
mov eax, esi ; get top 6 digits
mov edi, 68DB9h ; =2^32\10000+1
mul edi
mov esi, pAnswer ;offset TimeBuffer ;get pointer into answer buffer
mov ecx, 100 ;multiplier for later
jnc qnextrw1 ;if zero, supress them by ignoring
cmp edx, 9 ;1 digit or 2?
ja qZeroSupressedo ;2 digits, just continue with pairs of digits to the end
mov edx, dword ptr chartab[edx+edx] ;look up 2 digits
mov [esi], dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp QZoS1 ;continue with pairs of digits to the end
qnextrw1:
mul ecx ;get next 2 digits
jnc qnextrw2 ;if zero, supress them by ignoring
cmp edx, 9 ;1 digit or 2?
ja QZoS1a ;2 digits, just continue with pairs of digits to the end
mov edx, dword ptr chartab[edx+edx] ;look up 2 digits
mov [esi], dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp QZoS2 ;continue with pairs of digits to the end
qnextrw2:
mul ecx ;get next 2 digits
jnc qnextrw3 ;if zero, supress them by ignoring
cmp edx, 9 ;1 digit or 2?
ja QZoS2a ;2 digits, just continue with pairs of digits to the end
mov edx, dword ptr chartab[edx+edx] ;look up 2 digits
mov [esi], dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp QZoS3 ;continue with pairs of digits to the end
; next 6 digits
qnextrw3:
mov eax,ebx ;get 2nd 6 digits
mov ebx, 28F5C29h ;=2^32\100+1 ready for later
mul edi ;edi=2^32\10000+1
jnc qnextrw4 ;if zero, supress them by ignoring
cmp edx, 9 ;1 digit or 2?
ja QZSo3a ;2 digits, just continue with pairs of digits to the end
mov edx, dword ptr chartab[edx+edx] ;look up 2 digits
mov [esi], dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp QZSo4 ;continue with pairs of digits to the end
qnextrw4:
mul ecx ;get next 2 digits
jnc QZSo5 ;if zero, supress them by ignoring
cmp edx, 9 ;1 digit or 2?
ja QZSo4a ;2 digits, just continue with pairs of digits to the end
mov edx, dword ptr chartab[edx+edx] ;look up 2 digits
mov [esi], dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp QZSo5 ;continue with pairs of digits to the end
; done top 10 digits
; since we took a short cut to the DWORD routine at the start we can never get beyond here
; as the DWORD routine would handle it instead.
; At this point we are guaranteed to have exactly 10 digits to print so just jump to the relevant spot.
qZeroSupressedo:
mov edx, dword ptr chartab[edx+edx] ;look up the 2 digits
mov [esi], dx
add esi, 2
QZoS1:
mul ecx
QZoS1a:
mov edx, dword ptr chartab[edx+edx] ;look up the 2 digits
mov [esi], dx
add esi, 2
QZoS2:
mul ecx
QZoS2a:
mov edx, dword ptr chartab[edx+edx] ;look up the 2 digits
mov [esi], dx
add esi, 2
sj:
QZoS3:
;do next 6 digits
mov eax, ebx ;get 2nd 6 digits
mov ebx, 28F5C29h ;=2^32\100+1 ready for later
mul edi ;edi=2^32\10000+1
QZSo3a:
mov edx, dword ptr chartab[edx+edx]
mov [esi], dx
add esi, 2
QZSo4:
mul ecx
QZSo4a:
mov edx, dword ptr chartab[edx+edx]
mov [esi], dx
add esi, 2
QZSo5:
mul ecx
;QZSo5a:
mov edx, dword ptr chartab[edx+edx]
mov [esi], dx
add esi, 2
;do final 8 digits
mov eax, ebp ;get last 8 digits
mul ebx ;ebx=2^32\100+1
mov eax, edx
mov ebx, edx
mul edi ;edi=2^32\10000+1
mov edx, dword ptr chartab[edx+edx] ;look up next 2 digits
mov [esi], dx
add esi, 2
mul ecx
mov edx, dword ptr chartab[edx+edx] ;look up next 2 digits
mov [esi], dx
add esi, 2
mul ecx
mov edx, dword ptr chartab[edx+edx] ;look up next 2 digits
mov [esi], dx
add esi, 2
mov eax, ebx ;first 6 digits of last 8
imul eax, ecx ;x100 to shift into place
pop ebx
pop edi
mov edx, ebp ;last 8 - 6 just done gives final 2 digits
sub edx, eax ;look up final 2 digits
mov edx, dword ptr chartab[edx+edx]
mov [esi], dx
add esi, 2
mov byte ptr [esi],0 ;need to zero terminate
pop esi
pop ebp
AllDone:
mov eax, [esp+4]
ret 2*4
udword:
push edi ;save registers that need to be saved
push esi
mov esi, ecx ; sptr
mov edi,eax ;eax= x ;save a copy of the number
mov edx, 0D1B71759h ;=2^45\10000 13 bit extra shift
mul edx ;gives 6 high digits in edx
mov eax,68DB9h ;=2^32\10000+1
shr edx,13 ;correct for multiplier offset used to give better accuracy
jz short skiphighdigits ;if zero then don;t need to process the top 6 digits
mov ecx,edx ;get a copy of high digits
imul ecx,10000 ;scale up high digits
sub edi,ecx ;subtract high digits from original. EDI now = lower 4 digits
mul edx ;get first 2 digits in edx
mov ecx,100 ;load ready for later
jnc short next1 ;if zero, supress them by ignoring
cmp edx,9 ;1 digit or 2?
ja short ZeroSupressed ;2 digits, just continue with pairs of digits to the end
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp short ZS1 ;continue with pairs of digits to the end
next1:
mul ecx ;get next 2 digits
jnc short next2 ;if zero, supress them by ignoring
cmp edx,9 ;1 digit or 2?
ja short ZS1a ;2 digits, just continue with pairs of digits to the end
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp short ZS2 ;continue with pairs of digits to the end
next2:
mul ecx ;get next 2 digits
jnc short next3 ;if zero, supress them by ignoring
cmp edx,9 ;1 digit or 2?
ja short ZS2a ;2 digits, just continue with pairs of digits to the end
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp short ZS3 ;continue with pairs of digits to the end
next3:
skiphighdigits:
mov eax,edi ;get lower 4 ditigs
mov ecx,100
mov edx,28F5C29h ;2^32\100 +1
mul edx
jnc short next4 ;if zero, supress them by ignoring
cmp edx,9 ;1 digit or 2?
ja short ZS3a ;2 digits, just continue with pairs of digits to the end
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dh ;but only write the 1 we need, supress the leading zero
inc esi ;update pointer by 1
jmp short ZS4 ;continue with pairs of digits to the end
next4:
mul ecx ;this is the last pair so don;t supress a single zero
cmp edx,9 ;1 digit or 2?
ja short ZS4a ;2 digits, just continue with pairs of digits to the end
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dh ;but only write the 1 we need, supress the leading zero
mov byte ptr [esi+1],0 ;zero terminate string
jmp short xit ;all done
ZeroSupressed:
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dx
add esi,2 ;write them to answer
ZS1:
mul ecx ;get next 2 digits
ZS1a:
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dx ;write them to answer
add esi,2
ZS2:
mul ecx ;get next 2 digits
ZS2a:
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dx ;write them to answer
add esi,2
ZS3:
mov eax,edi ;get lower 4 digits
mov edx,28F5C29h ;2^32\100 +1
mul edx ;edx= top pair
ZS3a:
mov edx,dword ptr chartab[edx*2] ;look up 2 digits
mov [esi],dx ;write to answer
add esi,2 ;update pointer
ZS4:
mul ecx ;get final 2 digits
ZS4a:
mov edx,dword ptr chartab[edx*2] ;look them up
mov [esi],dx ;write to answer
mov byte ptr [esi+2],0 ;zero terminate string
xit:
pop esi ;restore used registers
pop edi
jmp AllDone
uqword endp