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