Hex to Decimal (0-65535)
Continuing with my Hex to Decimal routines, I have written one for 16 bit numbers.
This routine is really geared toward a NES. The NES has no decimal mode. I think most of the time programmers will just break their scores out into multiple bytes (one for each digit), and then handle rollover cases for greater than 9, and less than 0.
Here we are dealing with the case where they do want to use just 2 bytes of ram to hold a score. The idea is to take 16 bit number and convert it back into the 5 temporary digits used for the score. The desired outcome is to be really quick (not spending 100's and 100's of cycles), while also not spending gobs and gobs of rom. Of course, a routine that is quick and short is desired in almost any situation.
My routine takes 263 bytes, and does the job in 156 to 168 cycles. Stats are below:
;-------------------------------- ;0-9999 conversion stats ;-------------------------------- ;cycles occurances ;156 - $0860 --> 2,144 ;157 - 0 ;158 - 0 ;159 - $16C0 --> 5,824 ;160 - 0 ;161 - 0 ;162 - $07F0 --> 2,032 ;163 - 0 ;164 - 0 ;165 - 0 ;166 - 0 ;167 - 0 ;168 - 0 ;average execution is 158.97 cycles ;-------------------------------- ;0-65535 conversion stats ;-------------------------------- ;cycles occurances ;156 - $1738 --> 5,944 ;157 - 0 ;158 - 0 ;159 - $9528 --> 38,184 ;160 - 0 ;161 - 0 ;162 - $2A30 --> 10,800 ;163 - 0 ;164 - 0 ;165 - $1F18 --> 7,960 ;166 - 0 ;167 - 0 ;168 - $0A58 --> 2,648 ;average execution is 160.31 cycles
I like it when the best case and worse case execution times are close together. Here 0-9999 conversion gets done in 156-162 cycles, and 0-65535 gets done in 156-168 cycles. Both are pretty good.
Finally the routine:
CONVERT_TO_ASCII = 0 IF CONVERT_TO_ASCII ASCII_OFFSET = $30 ELSE ASCII_OFFSET = $00 ENDIF Times256_Low: .byte $00,$38,$0C,$44,$18,$50,$24,$5C .byte $30,$04,$3C,$10,$48,$1C,$54,$28 Times256_Med: .byte $00,$02,$05,$07,$0A,$0C,$0F,$11 .byte $14,$17,$19,$1C,$1E,$21,$23,$26 Times16_Low: .byte $00 Times4096_Low: .byte $00 Times4096_Med: .byte $00 Times4096_High: .byte $00 + ASCII_OFFSET .byte $10,$60,$28,$00 + ASCII_OFFSET ; interlaced tables, this allows less shifts to be made... .byte $20,$5C,$51,$00 + ASCII_OFFSET .byte $30,$58,$16,$01 + ASCII_OFFSET .byte $40,$54,$3F,$01 + ASCII_OFFSET .byte $50,$50,$04,$02 + ASCII_OFFSET .byte $60,$4C,$2D,$02 + ASCII_OFFSET .byte $0C,$48,$56,$02 + ASCII_OFFSET .byte $1C,$44,$1B,$03 + ASCII_OFFSET .byte $2C,$40,$44,$03 + ASCII_OFFSET .byte $3C,$3C,$09,$04 + ASCII_OFFSET .byte $4C,$38,$32,$04 + ASCII_OFFSET .byte $5C,$34,$5B,$04 + ASCII_OFFSET .byte $08,$30,$20,$05 + ASCII_OFFSET .byte $18,$2C,$49,$05 + ASCII_OFFSET .byte $28,$28,$0E,$06 + ASCII_OFFSET ShiftedBcdTab .byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C .byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C .byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C .byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C .byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C StartHexToDec: lda hexHigh ;3 @3 and #$F0 ;2 @5 lsr ;2 @7 lsr ;2 @9 carry is clear, shifting just 2 times instead of 4, tay ;2 @11 since interlaced tables are used. lda hexHigh ;3 @14 and #$0F ;2 @16 tax ;2 @18 lda Times4096_High,Y ;4 @22 sta decTenThousands ;3 @25 lda Times4096_Low,Y ;4 @29 adc Times256_Low,X ;4 @33 sta temp ;3 @36 lda Times4096_Med,Y ;4 @40 adc Times256_Med,X ;4 @44 tay ;2 @46 lda hexLow ;3 @49 and #$F0 ;2 @51 lsr ;2 @53 lsr ;2 @55 tax ;2 @57 tya ;2 @59 cpx #13*4 ;2 @61 times 4 due to interlaced table adc #0 ;2 @63 cpx #7*4 ;2 @65 adc #0 ;2 @67 tay ;2 @69 medByte lda hexLow ;3 @72 and #$0F ;2 @74 adc Times16_Low,X ;4 @78 adc temp ;3 @81 bcs .sub100 ;2³ @83/84 cmp #100 ;2 @85 bcc .skip1 ;2³ @87/88 .sub100: sbc #100 ;2 @89 iny ;2 @91 .skip1: cmp #100 ;2 @93 bcc .skip2 ;2³ @95/96 sbc #100 ;2 @97 iny ;2 @99 .skip2: lsr ;2 @101 tax ;2 @103 lda ShiftedBcdTab,X ;4 @107 tax ;2 @109 rol ;2 @111 and #$0F ;2 @113 IF CONVERT_TO_ASCII ora #ASCII_OFFSET ;2 ENDIF sta decOnes ;3 @116 txa ;2 @118 lsr ;2 @120 lsr ;2 @122 lsr ;2 @124 IF CONVERT_TO_ASCII ora #ASCII_OFFSET ;2 ENDIF sta decTens ;3 @127 tya ;2 @129 cmp #100 ;2 @131 bcc .skip3 ;2³ @133/134 sbc #100 ;2 @135 inc decTenThousands ;5 @140 .skip3: lsr ;2 @142 tay ;2 @144 lda ShiftedBcdTab,Y ;4 @148 tay ;2 @150 rol ;2 @152 and #$0F ;2 @154 IF CONVERT_TO_ASCII ora #ASCII_OFFSET ;2 ENDIF sta decHundreds ;3 @157 tya ;2 @159 lsr ;2 @161 lsr ;2 @163 lsr ;2 @165 IF CONVERT_TO_ASCII ora #ASCII_OFFSET ;2 ENDIF sta decThousands ;3 @168
ASCII output can be built into the routine at a cost of 8 more bytes and 8 more cycles.
Test rom and source below. Note this rom is for the 2600, not the NES. The screen stays black for several seconds while it is running, and then a green passed screen appears.
0 Comments
Recommended Comments
There are no comments to display.