Some time ago, I posted implementations of binary to decimal conversion using the Double Dabble algorithm for several microprocessors:
https://talk.dallasmakerspace.org/t/pro ... /18852/323https://en.wikipedia.org/wiki/Double_dabbleRudla posted code to convert a binary number to packed BCD:
viewtopic.php?f=2&t=6579I recognized that his code bore a definite resemblance to Double Dabble, but it was clearly not the same. Studying his implementation allowed me to step back and see the forest from the trees of the details of the Double Dabble algorithm. Improvements from a straight interpretation of Double Dabble include:
* all of that testing of and adding to individual BCD digits is merely a way to perform a "decimal adjustment" when the hardware does not directly support it. Hardware supporting decimal adjustment can combine the shifting and fiddling by adding to itself (doubling) with the decimal arithmetic mode built into the 6502 or a DAA decimal adjust instruction following the addition on other processors.
* shifting only one byte of the number being converted at a time instead of shifting the entire number. The savings become much more significant for larger numbers.
* growing the result as digits are added instead of needlessly shifting an ocean of zeroes in a large pre-cleared buffer.
I improveed on Rudla's implementation by starting with no bytes of result instead of one byte containing zero, saving up to seven shifts of the byte. I also did a minor tweak improving the innermost loop.
Using what I learned, I rewrote my conversion code from scratch, making it faster and simpler in the process.
My design is based on maximum reuse of memory and is intended to produce a printable character string instead of just a packed BCD number. Because the maximum number to be converted is eight bytes, the buffer is placed in the zero-page for maximum efficiency. The number to be converted is copied to the low end of the output buffer with the least-significant byte at the lowest address. The converted BCD digits grow downward from the end of the buffer and do not overlap the number until after its bytes are already consumed by the shifting. Because the largest number to be converted is 64 bits, it was reasonable to place the buffer in the zero page.
While I will not claim that these are the fastest routines to convert 32 and 64 bit integers to ASCII decimal strings, I am confident they are among the best.
Someone posted code to do decimal conversion to this forum including the time to convert $FFFFFFFF, but I cannot find it now to compare.
The 6502 version:
Code:
. 00191 ; Input:
. 00192 ; The absolute value of the number to convert is stored
. 00193 ; starting at OutBuf+2 in little-endian form
. 00194 ; A = number of bytes in the number
. 00195 ; Dabble is the label of last byte of the buffer
. 00196 ; OutBuf+0 = 0 if number is negative, other if negative
. 00197 ;
. 00198 ; Output:
. 00199 ; A:X contains the address of the resultant string
. 00200 ;
. 00201 ; Uses:
. 00202 ; Byt0 = number of bytes to convert
. 00203 ; Byt1 = number of packed BCD bytes in result
. 00204 ; Byt2 = bits of number being shifted
Code:
.206E 00225 Format:
.206E 85 12 [3] 00226 sta Byt0 ; Remember number of bytes to convert
.2070 AA [2] 00227 tax ; Use it as index into the number
. 00228
.2071 A9 00 [2] 00229 lda #0 ; Start with no bytes of BCD digits
.2073 85 13 [3] 00230 sta Byt1
. 00231
.2075 F8 [2] 00232 sed ; Switch to decimal mode
. 00233
.2076 00234 SkipLeadingZero:
.2076 B5 19 [4] 00235 lda OutBuf+1,X ; Get next highest byte of the number
.2078 D0 07 (2081) [2/3] 00236 bne NotLeadingZero
. 00237
.207A CA [2] 00238 dex ; Skip a leading zero
. 00239
.207B D0 F9 (2076) [2/3] 00240 bne SkipLeadingZero ; Check the next one if more
. 00241
.207D F0 2F (20AE) [2/3] 00242 beq BCD2ASCII ; All bytes are zero
. 00243
.207F 00244 ByteLoop:
.207F B5 19 [4] 00245 lda OutBuf+1,X ; Get next highest byte of the number
. 00246
.2081 00247 NotLeadingZero:
.2081 85 14 [3] 00248 sta Byt2 ; Set it for shifting
. 00249
.2083 86 12 [3] 00250 stx Byt0 ; Remember the address of the last one shifted
. 00251
.2085 38 [2] 00252 sec ; Shift in extra bit for eight trips through loop
. 00253
.2086 B0 12 (209A) [2/3] 00254 bcs EnterBitLoop ; Always branches
. 00255
.2088 00256 BitLoop:
.2088 A2 2D [2] 00257 ldx #Dabble ; Point to the BCD digits
. 00258
.208A A4 13 [3] 00259 ldy Byt1 ; Load count of number of digit bytes
.208C F0 0A (2098) [2/3] 00260 beq SkipDigitLoop ; Skip loop if none
. 00261
.208E 00262 DigitLoop:
.208E B5 00 [4] 00263 lda 0,X ; Double pair of BCD digits with carry
.2090 75 00 [4] 00264 adc 0,X
.2092 95 00 [4] 00265 sta 0,X
. 00266
.2094 CA [2] 00267 dex ; Point to next pair of BCD digits
. 00268
.2095 00269 IntoDigitLoop:
.2095 88 [2] 00270 dey ; More BCD digits?
.2096 D0 F6 (208E) [2/3] 00271 bne DigitLoop ; Yes
. 00272
.2098 00273 SkipDigitLoop:
.2098 B0 0B (20A5) [2/3] 00274 bcs Extend ; Carry out of accumulated digits?
. 00275
.209A 00276 EnterBitLoop:
.209A 26 14 [5] 00277 rol Byt2 ; Transfer upper bit to carry flag
.209C D0 EA (2088) [2/3] 00278 bne BitLoop ; Repeat if more
. 00279
.209E A6 12 [3] 00280 ldx Byt0 ; Point back to the number to convert
. 00281
.20A0 CA [2] 00282 dex ; Any more bytes of the number to convert?
.20A1 D0 DC (207F) [2/3] 00283 bne ByteLoop ; Repeat if more
. 00284
.20A3 F0 09 (20AE) [2/3] 00285 beq BCD2ASCII ; Always branches to unpacking stage
. 00286
.20A5 00287 Extend:
.20A5 E6 13 [5] 00288 inc Byt1 ; Add a new byte to the result
.20A7 A9 01 [2] 00289 lda #1
.20A9 95 00 [4] 00290 sta 0,X
. 00291
.20AB 18 [2] 00292 clc
.20AC 90 EC (209A) [2/3] 00293 bcc EnterBitLoop ; Always branches
. 00294
.20AE 00295 BCD2ASCII:
.20AE D8 [2] 00296 cld ; Return to binary mode
. 00297
.20AF A5 13 [3] 00298 lda Byt1 ; Any converted digits?
.20B1 F0 42 (20F5) [2/3] 00299 beq NoDigits ; No
. 00300
.20B3 38 [2] 00301 sec
.20B4 A9 2E [2] 00302 lda #Dabble+1 ; Point to most significant BCD pair
.20B6 E5 13 [3] 00303 sbc Byt1
.20B8 A8 [2] 00304 tay
. 00305
.20B9 A2 1A [2] 00306 ldx #<OutBuf+2 ; Point to result buffer
. 00307
.20BB B9 0000 [4/5] 00308 lda 0,Y ; Get most significant BCD pair
. 00309
.20BE 29 F0 [2] 00310 and #$F0 ; Is the upper one zero?
.20C0 F0 0E (20D0) [2/3] 00311 beq UpperZero
.20C2 D0 03 (20C7) [2/3] 00312 bne ConvertBoth
. 00313
.20C4 00314 ASCIILoop:
.20C4 B9 0000 [4/5] 00315 lda 0,Y ; Get next most significant BCD pair
. 00316
.20C7 00317 ConvertBoth:
.20C7 4A [2] 00318 lsr A ; Convert upper one to ASCII
.20C8 4A [2] 00319 lsr A
.20C9 4A [2] 00320 lsr A
.20CA 4A [2] 00321 lsr A
.20CB 09 30 [2] 00322 ora #'0'
.20CD 95 00 [4] 00323 sta 0,X
.20CF E8 [2] 00324 inx
. 00325
.20D0 00326 UpperZero:
.20D0 B9 0000 [4/5] 00327 lda 0,Y
.20D3 C8 [2] 00328 iny
.20D4 29 0F [2] 00329 and #$F ; And then the lower one
.20D6 09 30 [2] 00330 ora #'0'
.20D8 95 00 [4] 00331 sta 0,X
.20DA E8 [2] 00332 inx
. 00333
.20DB C6 13 [5] 00334 dec Byt1 ; More digits?
.20DD D0 E5 (20C4) [2/3] 00335 bne ASCIILoop ; Yes
. 00336
.20DF 8A [2] 00337 txa ; Store string length
.20E0 38 [2] 00338 sec
.20E1 E9 1A [2] 00339 sbc #OutBuf+2
. 00340
.20E3 A6 18 [3] 00341 ldx OutBuf ; Is the number negative?
.20E5 F0 14 (20FB) [2/3] 00342 beq NumberNotNegative
. 00343
.20E7 18 [2] 00344 clc ; Increment and store length
.20E8 69 01 [2] 00345 adc #1
.20EA 85 18 [3] 00346 sta OutBuf
. 00347
.20EC A9 2D [2] 00348 lda #'-' ; Prepend minus sign
.20EE 85 19 [3] 00349 sta OutBuf+1
. 00350
.20F0 A9 00 [2] 00351 lda #>OutBuf
.20F2 A2 18 [2] 00352 ldx #<OutBuf
. 00353
.20F4 60 [6] 00354 rts
. 00355
.20F5 00356 NoDigits:
.20F5 A9 30 [2] 00357 lda #'0' ; Emit single '0'
.20F7 85 1A [3] 00358 sta OutBuf+2
. 00359
.20F9 A9 01 [2] 00360 lda #1 ; String length one
. 00361
.20FB 00362 NumberNotNegative:
.20FB 85 19 [3] 00363 sta OutBuf+1
. 00364
.20FD A9 00 [2] 00365 lda #>OutBuf+1
.20FF A2 19 [2] 00366 ldx #<OutBuf+1
. 00367
.2101 60 [6] 00368 rts
The scorecard:
Code:
00001 ; Converting $FFFFFFFF
00002 ; old $2BC9 11209 cycles for call FormatLongword
00003 ; new $0A72 2674 cycles
00004 ;
00005 ; Converting $00000001
00006 ; old $2A96 10902 cycles for call FormatLongword
00007 ; new $015B 347 cycles
00008 ;
00009 ; Converting $00000000
00010 ; old $2A98 10904 cycles for call FormatLongword
00011 ; new $0075 117 cycles
The addressing modes and the three byte-sized registers worked well for this implementation.
The 6800 version:
Code:
. 00486 * Input:
. 00487 * The absolute value of the number to convert is stored
. 00488 * starting at OutBuf+2 in little-endian form
. 00489 * A = number of bytes in the number
. 00490 * Dabble is the label of last byte of the buffer
. 00491 * OutBuf+0 = 0 if number is negative, other if negative
. 00492 *
. 00493 * Output:
. 00494 * X contains the address of the resultant string
. 00495 *
. 00496 * Uses:
. 00497 * Byt0 = number of bytes to convert
. 00498 * Byt1 = number of packed BCD bytes in result
. 00499 * Byt2 = bits of number being shifted
. 00500 * Ptr0
. 00501 * Ptr1
Code:
.01F5 00522 Format
.01F5 97 10 [4] 00523 staa Byt0 ; Remember number of bytes to convert
. 00524
.01F7 8B 17 [2] 00525 adda #<OutBuf+1 ; Point to most significant byte of number
.01F9 97 0B [4] 00526 staa Ptr0+1
.01FB 86 00 [2] 00527 ldaa #>OutBuf+1
.01FD 89 00 [2] 00528 adca #0
.01FF 97 0A [4] 00529 staa Ptr0
.0201 DE 0A [4] 00530 ldx Ptr0
. 00531
.0203 7F 0011 [6] 00532 clr Byt1 ; Start with no bytes of BCD digits
. 00533
.0206 00534 SkipLeadingZero
.0206 E6 00 [5] 00535 ldab ,X ; Get next highest byte of the number
.0208 26 0D (0217) [4] 00536 bne NotLeadingZero
. 00537
.020A 09 [4] 00538 dex ; Skip a leading zero
. 00539
.020B 7A 0010 [6] 00540 dec Byt0
.020E 26 F6 (0206) [4] 00541 bne SkipLeadingZero ; Check the next one if more
. 00542
.0210 20 32 (0244) [4] 00543 bra BCD2ASCII ; All bytes are zero
. 00544
.0212 00545 ByteLoop
.0212 DE 0A [4] 00546 ldx Ptr0 ; Point back to the number to convert
.0214 09 [4] 00547 dex ; Point to next highest byte
. 00548
.0215 E6 00 [5] 00549 ldab ,X ; Get next highest byte of the number
. 00550
.0217 00551 NotLeadingZero
.0217 D7 12 [4] 00552 stab Byt2 ; Set it for shifting
. 00553
.0219 DF 0A [5] 00554 stx Ptr0 ; Remember the address of the last one shifted
. 00555
.021B 0D [2] 00556 sec ; Shift in extra bit for eight trips through loop
. 00557
.021C 20 1C (023A) [4] 00558 bra EnterBitLoop
. 00559
.021E 00560 BitLoop
.021E CE 002B [3] 00561 ldx #Dabble ; Point to the BCD digits
. 00562
.0221 D6 11 [3] 00563 ldab Byt1 ; Load count of number of digit bytes
.0223 27 0B (0230) [4] 00564 beq SkipDigitLoop ; Skip loop if none
. 00565
.0225 00566 DigitLoop
.0225 A6 00 [5] 00567 ldaa ,X ; Double pair of BCD digits with carry
.0227 A9 00 [5] 00568 adca ,X
.0229 19 [2] 00569 daa
.022A A7 00 [6] 00570 staa ,X
. 00571
.022C 09 [4] 00572 dex ; Point to next pair of BCD digits
. 00573
.022D 00574 IntoDigitLoop
.022D 5A [2] 00575 decb ; More BCD digits?
.022E 26 F5 (0225) [4] 00576 bne DigitLoop ; Yes
. 00577
.0230 00578 SkipDigitLoop
.0230 24 08 (023A) [4] 00579 bcc EnterBitLoop ; Carry out of accumulated digits?
. 00580
.0232 7C 0011 [6] 00581 inc Byt1 ; Add a new byte to the result
.0235 86 01 [2] 00582 ldaa #1
.0237 A7 00 [6] 00583 staa ,X
. 00584
.0239 0C [2] 00585 clc
. 00586
.023A 00587 EnterBitLoop
.023A 79 0012 [6] 00588 rol Byt2 ; Transfer upper bit to carry flag
.023D 26 DF (021E) [4] 00589 bne BitLoop ; Repeat if more
. 00590
.023F 7A 0010 [6] 00591 dec Byt0 ; Any more bytes of the number to convert?
.0242 26 CE (0212) [4] 00592 bne ByteLoop ; Repeat if more
. 00593
.0244 00594 BCD2ASCII
.0244 96 11 [3] 00595 ldaa Byt1 ; Any converted digits?
.0246 27 57 (029F) [4] 00596 beq NoDigits ; No
. 00597
.0248 86 2C [2] 00598 ldaa #<Dabble+1 ; Point to most significant BCD pair
.024A 90 11 [3] 00599 suba Byt1
.024C 97 0B [4] 00600 staa Ptr0+1
.024E 86 00 [2] 00601 ldaa #>Dabble+1
.0250 82 00 [2] 00602 sbca #0
.0252 97 0A [4] 00603 staa Ptr0
. 00604
.0254 CE 0018 [3] 00605 ldx #OutBuf+2 ; Point to result buffer
.0257 DF 0C [5] 00606 stx Ptr1
. 00607
.0259 DE 0A [4] 00608 ldx Ptr0 ; Get most significant BCD pair
.025B A6 00 [5] 00609 ldaa ,X
.025D 08 [4] 00610 inx
.025E DF 0A [5] 00611 stx Ptr0
.0260 DE 0C [4] 00612 ldx Ptr1
. 00613
.0262 16 [2] 00614 tab ; Clone it
. 00615
.0263 85 F0 [2] 00616 bita #$F0 ; Is the upper one zero?
.0265 27 17 (027E) [4] 00617 beq ConvertLowerOnly
.0267 20 0C (0275) [4] 00618 bra ConvertBoth
. 00619
.0269 00620 ASCIILoop
.0269 DF 0C [5] 00621 stx Ptr1
.026B DE 0A [4] 00622 ldx Ptr0 ; Get next most significant BCD pair
.026D A6 00 [5] 00623 ldaa ,X
.026F 08 [4] 00624 inx
.0270 DF 0A [5] 00625 stx Ptr0
.0272 DE 0C [4] 00626 ldx Ptr1
. 00627
.0274 16 [2] 00628 tab ; Clone it
. 00629
.0275 00630 ConvertBoth
.0275 44 [2] 00631 lsra ; Convert upper one to ASCII
.0276 44 [2] 00632 lsra
.0277 44 [2] 00633 lsra
.0278 44 [2] 00634 lsra
.0279 8A 30 [2] 00635 oraa #'0'
.027B A7 00 [6] 00636 staa ,X
.027D 08 [4] 00637 inx
. 00638
.027E 00639 ConvertLowerOnly
.027E C4 0F [2] 00640 andb #$F ; And then the lower one
.0280 CA 30 [2] 00641 orab #'0'
.0282 E7 00 [6] 00642 stab ,X
.0284 08 [4] 00643 inx
. 00644
.0285 7A 0011 [6] 00645 dec Byt1 ; More BCD pairs?
.0288 26 DF (0269) [4] 00646 bne ASCIILoop
. 00647
.028A DF 0C [5] 00648 stx Ptr1 ; Determine string length
.028C 96 0D [3] 00649 ldaa Ptr1+1
.028E 80 18 [2] 00650 suba #<OutBuf+2
. 00651
.0290 D6 16 [3] 00652 ldab OutBuf ; Is the number negative?
.0292 27 11 (02A5) [4] 00653 beq NumberNotNegative
. 00654
.0294 4C [2] 00655 inca ; Increment and store length byte
.0295 97 16 [4] 00656 staa OutBuf
. 00657
.0297 C6 2D [2] 00658 ldab #'-' ; Prepend minus sign
.0299 D7 17 [4] 00659 stab OutBuf+1
. 00660
.029B CE 0016 [3] 00661 ldx #OutBuf ; Point to the result
. 00662
.029E 39 [5] 00663 rts
. 00664
.029F 00665 NoDigits
.029F 86 30 [2] 00666 ldaa #'0' ; Emit single '0'
.02A1 97 18 [4] 00667 staa OutBuf+2
. 00668
.02A3 86 01 [2] 00669 ldaa #1 ; String length one
. 00670
.02A5 00671 NumberNotNegative
.02A5 97 17 [4] 00672 staa OutBuf+1
. 00673
.02A7 CE 0017 [3] 00674 ldx #OutBuf+1
. 00675
.02AA 39 [5] 00676 rts
The scorecard:
Code:
00001 * Converting $FFFFFFFF
00002 * old $38F0 14576 cycles for call FormatLongword
00003 * new $0FED 4077 cycles
00004 *
00005 * Converting $00000001
00006 * old $37B3 14259 cycles for call FormatLongword
00007 * new $01F2 498 cycles
00008 *
00009 * Converting $00000000
00010 * old $37B0 14256 cycles for call FormatLongword
00011 * new $00B9 185 cycles
The 6800 did not fare so well with only one index register and relatively slow memory access instructions.
The added capabilities of the 6809 do not appear to be much better.
The 8080 version:
Code:
. 00127 ; Input:
. 00128 ; The absolute value of the number to convert is stored
. 00129 ; starting at OutBuf+2 in little-endian form
. 00130 ; A = number of bytes in the number
. 00131 ; B = 0 if number is positive, other if negative
. 00132 ; Dabble is the label of last byte of the buffer
. 00133 ;
. 00134 ; Output:
. 00135 ; HL contains the address of the resultant string
Code:
.0173 00156 Format:
.0173 4F [5] 00157 mov C,A ; Remember number of bytes to convert
.0174 5F [5] 00158 mov E,A
. 00159
.0175 78 [5] 00160 mov A,B ; Remember negativity
.0176 32 0333 [13] 00161 sta OutBuf
. 00162
.0179 AF [4] 00163 xra A ; Start with no bytes of BCD digits
.017A 47 [5] 00164 mov B,A
. 00165
.017B 21 0334 [10] 00166 lxi H,OutBuf+1 ; Point DE to most significant byte of number
.017E 57 [5] 00167 mov D,A
.017F 19 [10] 00168 dad D
.0180 EB [4] 00169 xchg
. 00170
.0181 00171 SkipLeadingZero:
.0181 1A [7] 00172 ldax D ; Get next highest byte of the number
.0182 B7 [4] 00173 ora A
.0183 C2 018F [10] 00174 jnz NotLeadingZero
. 00175
.0186 1B [5] 00176 dcx D ; Skip a leading zero
. 00177
.0187 0D [5] 00178 dcr C
.0188 C2 0181 [10] 00179 jnz SkipLeadingZero ; Check the next one if more
. 00180
.018B C3 01BC [10] 00181 jmp BIN2ASCII ; All bytes are zero
. 00182
.018E 00183 ByteLoop:
.018E 1A [7] 00184 ldax D ; Get next highest byte of the number
. 00185
.018F 00186 NotLeadingZero:
.018F 1B [5] 00187 dcx D ; Point to next lower byte
. 00188
.0190 D5 [11] 00189 push D ; Stash pointer to next byte
. 00190
.0191 5F [5] 00191 mov E,A ; Set it for shifting
. 00192
.0192 37 [4] 00193 stc ; Shift in extra bit for eight times thru loop
. 00194
.0193 C3 01AF [10] 00195 jmp EnterBitLoop
. 00196
.0196 00197 BitLoop:
.0196 21 0348 [10] 00198 lxi H,Dabble ; Point to the BCD digits
. 00199
.0199 04 [5] 00200 inr B ; Check count of number of digit bytes
.019A 05 [5] 00201 dcr B ; Test for zero but preserve carry flag
.019B CA 01A8 [10] 00202 jz SkipDigitLoop ; Skip loop if none
. 00203
.019E 50 [5] 00204 mov D,B
. 00205
.019F 00206 DigitLoop:
.019F 7E [7] 00207 mov A,M ; Double pair of BCD digits with carry
.01A0 8E [7] 00208 adc M
.01A1 27 [4] 00209 daa
.01A2 77 [7] 00210 mov M,A
. 00211
.01A3 2B [5] 00212 dcx H ; Point to next pair of BCD digits
. 00213
.01A4 00214 IntoDigitLoop:
.01A4 15 [5] 00215 dcr D ; More BCD digits?
.01A5 C2 019F [10] 00216 jnz DigitLoop ; Yes
. 00217
.01A8 00218 SkipDigitLoop:
.01A8 D2 01AF [10] 00219 jnc EnterBitLoop ; Carry out of accumulated digits?
. 00220
.01AB 04 [5] 00221 inr B ; Add new byte to the result
.01AC 36 01 [10] 00222 mvi M,1
. 00223
.01AE B7 [4] 00224 ora A ; Clear carry flag
. 00225
.01AF 00226 EnterBitLoop:
.01AF 7B [5] 00227 mov A,E
.01B0 17 [4] 00228 ral ; Transfer upper bit to carry flag
.01B1 5F [5] 00229 mov E,A
. 00230
.01B2 1C [5] 00231 inr E
.01B3 1D [5] 00232 dcr E
.01B4 C2 0196 [10] 00233 jnz BitLoop ; Repeat if more
. 00234
.01B7 D1 [10] 00235 pop D ; Recover pointer to next byte
. 00236
.01B8 0D [5] 00237 dcr C ; Any more bytes of the number to convert?
.01B9 C2 018E [10] 00238 jnz ByteLoop ; Repeat if more
. 00239
.01BC 00240 BIN2ASCII:
.01BC 21 0335 [10] 00241 lxi H,OutBuf+2 ; Point to result buffer
. 00242
.01BF 78 [5] 00243 mov A,B ; Any converted digits?
.01C0 B7 [4] 00244 ora A
.01C1 CA 01F4 [10] 00245 jz NoDigits ; No
. 00246
.01C4 11 0349 [10] 00247 lxi D,Dabble+1 ; Point to most significant BCD pair
.01C7 7B [5] 00248 mov A,E
.01C8 90 [4] 00249 sub B
.01C9 5F [5] 00250 mov E,A
.01CA 7A [5] 00251 mov A,D
.01CB DE 00 [7] 00252 sbi 0
.01CD 57 [5] 00253 mov D,A
. 00254
.01CE 1A [7] 00255 ldax D ; Get most significant BCD pair
. 00256
.01CF E6 F0 [7] 00257 ani 0F0h ; Is the upper one zero?
.01D1 CA 01DF [10] 00258 jz ConvertLowerOnly
. 00259
.01D4 00260 ASCIILoop:
.01D4 1A [7] 00261 ldax D ; Get next most significant BCD pair
.01D5 0F [4] 00262 rrc ; Convert upper one to ASCII
.01D6 0F [4] 00263 rrc
.01D7 0F [4] 00264 rrc
.01D8 0F [4] 00265 rrc
.01D9 E6 0F [7] 00266 ani 0Fh
.01DB F6 30 [7] 00267 ori '0'
.01DD 77 [7] 00268 mov M,A
.01DE 23 [5] 00269 inx H
. 00270
.01DF 00271 ConvertLowerOnly:
.01DF 1A [7] 00272 ldax D ; And then the lower one
.01E0 13 [5] 00273 inx D
.01E1 E6 0F [7] 00274 ani 0Fh
.01E3 F6 30 [7] 00275 ori '0'
.01E5 77 [7] 00276 mov M,A
.01E6 23 [5] 00277 inx H
. 00278
.01E7 05 [5] 00279 dcr B
.01E8 C2 01D4 [10] 00280 jnz ASCIILoop
. 00281
.01EB 7D [5] 00282 mov A,L ; Store string length
.01EC DE 35 [7] 00283 sbi OutBuf+2 and 0FFh
.01EE 32 0334 [13] 00284 sta OutBuf+1
. 00285
.01F1 C3 01F9 [10] 00286 jmp FinishFormat
. 00287
.01F4 00288 NoDigits:
.01F4 36 30 [10] 00289 mvi M,'0' ; Emit single '0'
. 00290
.01F6 2B [5] 00291 dcx H ; String length one
.01F7 36 01 [10] 00292 mvi M,1
. 00293
.01F9 00294 FinishFormat:
.01F9 21 0334 [10] 00295 lxi H,OutBuf+1 ; Point to the result
. 00296
.01FC 3A 0333 [13] 00297 lda OutBuf ; Was number negative?
.01FF B7 [4] 00298 ora A
.0200 CA 0209 [10] 00299 jz NumberNotNegative ; No
. 00300
.0203 7E [7] 00301 mov A,M ; Get and increment length byte
.0204 3C [5] 00302 inr A
. 00303
.0205 36 2D [10] 00304 mvi M,'-' ; Prepend minus sign
. 00305
.0207 2B [5] 00306 dcx H ; Point to the new result
. 00307
.0208 77 [7] 00308 mov M,A ; Store new length
. 00309
.0209 00310 NumberNotNegative:
.0209 C9 [10] 00311 ret
The scorecard:
Code:
00001 ; Converting $FFFFFFFF
00002 ; old $7F53 32595 cycles for call FormatLongword
00003 ; new $2359 9049 cycles
00004 ; new $1F18 7960 cycles
00005 ;
00006 ; Converting $00000001
00007 ; old $7CEE 31982 cycles for call FormatLongword
00008 ; new $055A 1370 cycles
00009 ; new $04C5 1221 cycles
00010 ;
00011 ; Converting $00000000
00012 ; old $7D05 32005 cycles for call FormatLongword
00013 ; new $01E0 480 cycles
00014 ; new $01A2 418 cycles
Two sets of new numbers are reported. One for the initial version and another for after some intense code golf managed to eliminate variables in memory.
I actually wrote the 8080 version first, then moved on to the 6800 and then the 6502. Only then did I invest the effort to optimize this one.