| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
32bit Decimal convertion
Anybody got this shitz for 32-bit numbers? http://codebase64.org/doku.php?id=base:hexadecimal_to_decimal_c.. |
|
| |
Krill
Registered: Apr 2002 Posts: 2980 |
You're too lazy to pick any of the 3 approaches and modify the routine for 32-bit numbers? :) |
| |
Frantic
Registered: Mar 2003 Posts: 1648 |
If he is, I could sort of sympathise with that, if it was part of a large project like Eye of the Beholder with zillions of lines of code and someone else had already implemented a 32-bit version of that routine.
I can't sympathise with the spelling of "Convertion" (Conversion) though. ;) |
| |
chatGPZ
Registered: Dec 2001 Posts: 11386 |
did you check 6502.org? i think they had sth.... i am too lazy to look though =D |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: You're too lazy to pick any of the 3 approaches and modify the routine for 32-bit numbers? :)
exactly |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: If he is, I could sort of sympathise with that, if it was part of a large project like Eye of the Beholder with zillions of lines of code and someone else had already implemented a 32-bit version of that routine.
I can't sympathise with the spelling of "Convertion" (Conversion) though. ;)
Dang!! :) |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: did you check 6502.org? i think they had sth.... i am too lazy to look though =D
Ahh no, I didn't. Checking now... nope, same there as on Codebase.
I guess I'll roll my own then. Just so annoyingly boring and my general purpose division in EotB is only 16-bit.. Thinking of some simple shift+add with the 1/10 since that is a very simple bitpattern 110011001100110011... |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Or I just show the experience points in hex... ;) |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: If he is, I could sort of sympathise with that, if it was part of a large project like Eye of the Beholder with zillions of lines of code and someone else had already implemented a 32-bit version of that routine.
I can't sympathise with the spelling of "Convertion" (Conversion) though. ;)
Also not zillions, but just 40688 at the moment (675kb). |
| |
Krill
Registered: Apr 2002 Posts: 2980 |
Quoting JackAssernope, same there as on Codebase. Indeed, Codebase has a straight 1:1 rip of the original 6502.org page, without giving proper credit. Not cool, Codebase, not cool.
Quoting JackAsserThinking of some simple shift+add with the 1/10 since that is a very simple bitpattern 110011001100110011... Please elaborate. :) |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: You're too lazy to pick any of the 3 approaches and modify the routine for 32-bit numbers? :)
Actually I didn't even bother to look closer on that code posted there but I didn't now and it's meant to be extended.
"The principle should be pretty clear. You can take it out to as many digits as you want."
However it relies on decimal mode being set, something my interrupt handlers can't handle and I'm not willing to fuck timing all over the place. |
| |
Krill
Registered: Apr 2002 Posts: 2980 |
You have shitloads of spare memory for lookup tables, right? :) That allows for a very simple and fast approach with 4 8-bit lookups and 3 adds, both times decimal places, or something, i think. |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: Quoting JackAssernope, same there as on Codebase. Indeed, Codebase has a straight 1:1 rip of the original 6502.org page, without giving proper credit. Not cool, Codebase, not cool.
Quoting JackAsserThinking of some simple shift+add with the 1/10 since that is a very simple bitpattern 110011001100110011... Please elaborate. :)
Basically you want to do:
int value = 0xdeadbeef;
int digit0 = value%10; value/=10;
int digit1 = value%10; value/=10;
int digit2 = value%10; value/=10;
int digit3 = value%10; value/=10;
int digit4 = value%10; value/=10;
int digit5 = value%10; value/=10;
.
.
.
When rolling your own division the remainder will pop out automatically. Doing a /10 is the same as doing a multiply by 1/10. Multiply by 1/10 is simply a shift+add multiplication with a %110011001100... bitpattern. |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: You have shitloads of spare memory for lookup tables, right? :) That allows for a very simple and fast approach with 4 8-bit lookups and 3 adds, both times decimal places, or something, i think.
I actually don't have shitloads of memory left and this code can be dead slow so I'd rather have it tight. |
| |
Krill
Registered: Apr 2002 Posts: 2980 |
Then i'd go with the first algorithm, bitwise conversion without any tables or div/mul. |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: Then i'd go with the first algorithm, bitwise conversion without any tables or div/mul.
Need to digest it. Don't understand at all how it works atm. :D |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: Need to digest it. Don't understand at all how it works atm. :D
Or I just substract billions, and when underflowing, roll back one step and start substracting 100-millions, and then 10-millions etc.. down to ones. Should be resonably fast.
ldx #<-1
:
sec
lda TMP+0
sbc #<(1000000000>>0)
sta TMP+0
lda TMP+1
sbc #<(1000000000>>8)
sta TMP+1
lda TMP+2
sbc #<(1000000000>>16)
sta TMP+2
lda TMP+1
sbc #<(1000000000>>24)
sta TMP+1
inx
bvc :-
etc.. |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Enjoy:
.export htd32
.proc htd32
HTD_IN = ARGS+0 ; 32-bit input
HTD_OUT = RESULT ; 10-digit output, one digit per ZP-loc
ldy #0 ;digit position
:
; Count digits on position y
ldx #<-1 ;digit value
:
inx
sec
lda HTD_IN+0
sbc table0,y
sta HTD_IN+0
lda HTD_IN+1
sbc table1,y
sta HTD_IN+1
lda HTD_IN+2
sbc table2,y
sta HTD_IN+2
lda HTD_IN+3
sbc table3,y
sta HTD_IN+3
bcs :-
; Rollback underflowed result
clc
lda HTD_IN+0
adc table0,y
sta HTD_IN+0
lda HTD_IN+1
adc table1,y
sta HTD_IN+1
lda HTD_IN+2
adc table2,y
sta HTD_IN+2
lda HTD_IN+3
adc table3,y
sta HTD_IN+3
txa
sta HTD_OUT,y
iny
cpy #10
bne :--
rts
table0: .byte <(1000000000>> 0),<(100000000>> 0),<(10000000>> 0),<(1000000>> 0),<(100000>> 0),<(10000>> 0),<(1000>> 0),<(100>> 0),<(10>> 0),<(1>> 0)
table1: .byte <(1000000000>> 8),<(100000000>> 8),<(10000000>> 8),<(1000000>> 8),<(100000>> 8),<(10000>> 8),<(1000>> 8),<(100>> 8),<(10>> 8),<(1>> 8)
table2: .byte <(1000000000>>16),<(100000000>>16),<(10000000>>16),<(1000000>>16),<(100000>>16), <(10000>>16),<(1000>>16),<(100>>16),<(10>>16),<(1>>16)
table3: .byte <(1000000000>>24),<(100000000>>24),<(10000000>>24),<(1000000>>24),<(100000>>24), <(10000>>24),<(1000>>24),<(100>>24),<(10>>24),<(1>>24)
.endproc
|
| |
Perplex
Registered: Feb 2009 Posts: 255 |
Storing and adding XP as base-10 strings is not an option? |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: Storing and adding XP as base-10 strings is not an option?
Brr no! :D |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1409 |
Alternately, here's my double dabble variant. No tables, 37 bytes of code including the RTS, assuming input and output in zero page. Might be able to save bytes by keeping the intermediate results in petscii rather than 0..9, but this is more readable.
.DEFINE blt bcc
scratch=$f0 ; input in scratch+10..scratch+13, output in scratch+0..scratch+9, most significant first
jmp test
convert:
lda#0
ldx#9
initlp:
sta scratch,x
dex
bpl initlp
ldy#31
bitlp:
ldx#9
correctionlp:
lda scratch,x
cmp#5
blt noover
;carry now set
adc#128-5-1
;carry now clear
sta scratch,x
noover:
dex
bpl correctionlp
ldx#13
shiftlp:
rol scratch,x
dex
bpl shiftlp
dey
bpl bitlp
rts
test:
lda#13
sta $d021
jsr $e536
lda#13
jsr $ffd2
lda#0
sta $d021
ldx#3
cpl:
lda testv,x
sta scratch+10,x
dex
bpl cpl
jsr convert
ldx#9
codelp:
lda#48
ora scratch,x
sta $040f,x
dex
bpl codelp
rts
testv:
.byte $49, $96, $02, $d2 ; 1234567890 = 0x499602d2
|
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Awesome! Please explain the math of it |
| |
Krill
Registered: Apr 2002 Posts: 2980 |
Seems to be a variant of that first algorithm.
Shift one bit of the binary value into the result, then avert decimal overflow for every decimal digit (when decimal digit >= 5, which would be >=10 upon the next iteration's shift) by subtracting 5 and setting decimal digit byte's MSB, which is the carry upon the next iteration's shift and will end up as the LSB of the next more significant decimal digit.
So basically you're doubling the result with every incoming bit of the argument (reflecting the binary nature of the argument) and fixing the result in every iteration (reflecting the decimal nature of the result). |
| |
JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: Seems to be a variant of that first algorithm.
Shift one bit of the binary value into the result, then avert decimal overflow for every decimal digit (when decimal digit >= 5, which would be >=10 upon the next iteration's shift) by subtracting 5 and setting decimal digit byte's MSB, which is the carry upon the next iteration's shift and will end up as the LSB of the next more significant decimal digit.
So basically you're doubling the result with every incoming bit of the argument (reflecting the binary nature of the argument) and fixing the result in every iteration (reflecting the decimal nature of the result).
Smart approach! |
| |
Krill
Registered: Apr 2002 Posts: 2980 |
Quoting JackAsserSmart approach! Indeed, it's pretty elegant. Kind of in the same "class" of algorithms with bit-wise multiplication, division and square root. |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1409 |
Thanks, JackAsser! I was hoping to find something a little different, so while I did do a quick google and found the initial plus three approach elsewhere, I deliberately avoided looking at the solutions posted thus far.
Got halfway through the nybble masking etc required for the hex to BCD conversion when I had the realisation that you'd just have to unpack the nybbles for display purposes at the end anyway.
Switching to one digit per byte from the get go eliminates more than half of the instructions in the inner loop, and potentially frees up a register too.
Nice explanation of how it works, Krill. |
| |
ChristopherJam
Registered: Aug 2004 Posts: 1409 |
This one converts directly to screencodes; saves two bytes in the "copy to screen" loop, needs an extra two bytes in the converter, takes an extra 1200 or so cycles. meh. Posting for posterity anyway.
.DEFINE blt bcc
digitbase=48
scratch=$f0 ; input in scratch+10..scratch+13, output in scratch+0..scratch+9, most significant first
jmp test
convert:
lda#digitbase
ldx#9
initlp:
sta scratch,x
dex
bpl initlp
ldy#31
bitlp:
ldx#9
correctionlp:
lda scratch,x
cmp#5+digitbase
blt noover
;carry now set
adc#128-5-1
;carry now clear
noover:
sbc #digitbase/2-1
sta scratch,x
dex
bpl correctionlp
; note - it doesn't matter what bits you shift into the bottom
; of the input bytes, as they never reach the output bytes
ldx#13
shiftlp:
rol scratch,x
dex
bpl shiftlp
dey
bpl bitlp
rts
test:
lda#13
sta $d021
jsr $e536
lda#13
jsr $ffd2
lda#0
sta $d021
ldx#3
cpl:
lda testv,x
sta scratch+10,x
dex
bpl cpl
jsr convert
ldx#9
codelp:
lda scratch,x
sta $040f,x
dex
bpl codelp
rts
testv:
.byte $49, $96, $02, $d2 ; 1234567890 = 0x499602d2
|
| |
Zirias
Registered: Jan 2014 Posts: 48 |
For the score display in 8192, I decided to create something reusable, although I needed it for 32 bit -- this should work for anything up to 128 bit if you adjust the constants, output is right-aligned and padded with spaces (0-padding would simplify it further):
.export numtostring
.exportzp nc_string
.exportzp nc_num
NUMSTRSIZE = $a
NUMSIZE = $4
.zeropage
nc_string: .res NUMSTRSIZE
nc_num: .res NUMSIZE
.code
numtostring:
ldy #NUMSTRSIZE
lda #$0
nts_fillzero: sta nc_string-1,y
dey
bne nts_fillzero
ldy #(NUMSIZE*8)
nts_bcdloop: ldx #(NUMSTRSIZE-2)
nts_addloop: lda nc_string+1,x
cmp #$5
bmi nts_noadd
adc #$2
sta nc_string+1,x
nts_noadd: dex
bpl nts_addloop
asl nc_num
ldx #($ff-NUMSIZE+2)
nts_rol: rol nc_num+NUMSIZE,x
inx
bne nts_rol
ldx #(NUMSTRSIZE-2)
nts_rolloop: lda nc_string+1,x
rol a
cmp #$10
and #$f
sta nc_string+1,x
nts_rolnext: dex
bpl nts_rolloop
dey
bne nts_bcdloop
nts_scan: cpx #(NUMSTRSIZE-1)
beq nts_digits
inx
lda nc_string,x
bne nts_digits
lda #$20
sta nc_string,x
bne nts_scan
nts_digits: lda nc_string,x
ora #$30
sta nc_string,x
inx
cpx #(NUMSTRSIZE)
bne nts_digits
rts
|