## Compact /10 and %10 operations?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

na_th_an
Posts: 554
Joined: Mon May 27, 2013 9:40 am

### Compact /10 and %10 operations?

I use a couple of /10 and %10 in my code (to display numbers), which import fairly big portions of runtime.lib I'd like to leave out. Anybody knows of any compat ubyte/10 and ubyte%10 functions in assembly?

I found this ubyte/10 by Omegamatrix https://forums.nesdev.com/viewtopic.php?f=2&t=11336

Code: Select all

``````;Divide by 10
;17 bytes, 30 cycles
lsr
sta  temp
lsr
ror
lsr
lsr
ror
ror
lsr
lsr``````
Anybody knows something for ubyte%10? I could multiply by 10 (adding N<<3 and N<<1) the above results and substract, but is there a better method?

lidnariq
Posts: 9298
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

### Re: Compact /10 and %10 operations?

Tepples has an unrolled fast 8-bit-to-BCD converter here.

I bet there's other fast ones.

Posts: 7872
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

### Re: Compact /10 and %10 operations?

It's not fast however it's the shortest:

Code: Select all

``````; A = number (0-99)

ldx #ff
sec
-   inx
sbc #10
bcs -

; A = lower digit (0-9), X=upper digit(0-9)``````
Even in the worst case (number is in the 90s) it's still reasonably fast. If you want to go up to 255, it's simple to add a quick check for a number greater than 200, then 100 before this code.

Code: Select all

``````; A = number (0-255)

ldy #0
cmp #200
bcc +
sbc #200
ldy #2        ; Range 200-255
bne ++

+   cmp #100
bcc ++
sbc #100      ; Range 100-199
iny

++
ldx #ff       ; Conversion for lower 2 digits like before
sec
-   inx
sbc #10
bcc -

; A = lower digit (0-9), X=middle digit(0-9), Y=upper digit (0-2)``````
If you have a 16-bit number then it becomes worth doing an actual division/modulo operation.

(Note this code is untested so don't copy/paste without understanding and blame me if you get bugs)

na_th_an
Posts: 554
Joined: Mon May 27, 2013 9:40 am

### Re: Compact /10 and %10 operations?

Thank you, that's exactly what I needed It doesn't to be very fast, as it is only called once in a while. Up to 99 is enough.

Oziphantom
Posts: 825
Joined: Tue Feb 07, 2017 2:03 am

### Re: Compact /10 and %10 operations?

Gee not having BCD is a bit of a pain here isn't it... Just typed out a nice response, and then remembered NES doesn't have BCD... ouch

dougeff
Posts: 2673
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

### Re: Compact /10 and %10 operations?

NES doesn't have BCD
There's nothing stopping you from storing values this way, and modifying your code to make it work.

I like to keep values 0-9. If it goes higher, add another byte, but keep them both 0-9.

Then %10 is as simple as reading the lowest byte.
nesdoug.com -- blog/tutorial on programming for the NES

Posts: 7872
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

### Re: Compact /10 and %10 operations?

Just for the annedote, Castlevania games 1 and 3 (but not 2 which uses a very different engine) stores the number of hearts in BCD, so if you have, say, 99 hearts it will be stored as \$99. Personally I find this inneficient, as many shifts and bitmask operations are needed to retrive the digits, and this only saves a single byte of RAM (negligible). Storing data in BCD only makes sense for large arrays of numbers, where the RAM savings would be significant.

tepples
Posts: 21943
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

### Re: Compact /10 and %10 operations?

Thwaite stores the score in what amounts to base 100. In its representation, \$1163 has high byte 17, low byte 99, interpreted as 1799. Add 1, and you get 0x1200 (interpreted as 1800). This lets it correct for carry only between bytes (not between nibbles of one byte) while using the unrolled converter that lidnariq linked for display.

tokumaru
Posts: 11646
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

### Re: Compact /10 and %10 operations?

For decimal numbers that are involved only in additions or subtractions, I store each digit (0-9) in a byte and do all the math in that format, manually generating a carry when the result for each digit is larger than 9.

Modifying the numbers requires more code, but no conventions are necessary for displaying them.

na_th_an
Posts: 554
Joined: Mon May 27, 2013 9:40 am

### Re: Compact /10 and %10 operations?

I use unpacked bcd (byte per digit) when I have to display large scores, i.e. 5, or 6 digits. I just needed something simple to display numbers 0-99.

Posts: 7872
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

### Re: Compact /10 and %10 operations?

That was my point exactly, the usage of "unpacked BCD" (each digit being stored separatedly) makes most sense, and the usage of "packed by 100" such as what tepples describes is great too, considering how compact it is to decode a number 0-99 into its two separate digits (see my code above). However, using packed BCD makes little sense; retriving digits is barely easier than doing so from "packed by 100" BCD, but also it requires much more complex code to do calculations, and that only for saving a single RAM byte per digit. That's why it's notable Castlevania does this, despite this making few sense technically.

Oziphantom
Posts: 825
Joined: Tue Feb 07, 2017 2:03 am

### Re: Compact /10 and %10 operations?

if you do it digit per byte, and handle the roll over case yourself ( as once must without BCD) it makes sense to store your numbers like scores etc in "tileset" numbers. Rarealy do you put 0-9 at tiles 0-9 you put them somewhere else.
Say its at 40 - 49. You can then Start at 40, if you hit 50, sub 10 and then add 1 to the next etc.
On the C64 quite a few games actually store the score, lives etc variables in the screen RAM( as in what you see on the screen is the variable ), not really that practical to do on a NES I guess.

If you want a fixed time, or a tight loop version you can shift bits and add the digits for it
say 0-99
you test bit 6 if set tens = 6 digits = 4
bit 5 if set add 3 2
bit 4 if set add 1 6
bit 3 if set add 0 8
bit 2 if set add 0 4
bit 1 if set add 0 2
bit 0 if set add 0 1
etc
here having BCD makes the code more compact and faster, as you need 1 table and 1 add. For larger bits 16/24/32 you can just repeat but change the byte you modify. (after also adding in the 1 28 case. )

Posts: 7872
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

### Re: Compact /10 and %10 operations?

Oziphantom wrote:if you do it digit per byte, and handle the roll over case yourself ( as once must without BCD) it makes sense to store your numbers like scores etc in "tileset" numbers. Rarealy do you put 0-9 at tiles 0-9 you put them somewhere else.
Actually, avoiding an useless ADD or OR operation is in my opinion a good reason to put them at tiles 0-9 precisely. And if you have to put them somewhere else, at least put the "0" in the first column (i.e. tile \$x0) so that the conversion can be done with an "OR" operation, and a "CLC" instruction can be saved

Oziphantom
Posts: 825
Joined: Tue Feb 07, 2017 2:03 am

### Re: Compact /10 and %10 operations?

or you can put 0 at 246 and thus gain the natural overflow detection, so you code looks like

Code: Select all

``````adc Digit
sta Digit
bcc _noOver
inc DigitTens
adc #245 ; carry is set
_noOver
``````
And you avoid having to do any other adc/ora to convert them for display. If you need to worry about a tens overflow you can php/plp and then check to see if it was bpl _tensOverflow (or beq )

tokumaru
Posts: 11646
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

### Re: Compact /10 and %10 operations?

Offsetting the values to line them up with the pattern indices for 0-9 is a good idea, as long as only the final number is offset and any values you add/subtract to/from it are still 0-based. You'll only get automatic wrap around detection in one direction anyway (either when adding or subtracting).