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?

Post by na_th_an » Thu Sep 06, 2018 11:44 pm

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
  adc  temp
  ror
  lsr
  lsr
  adc  temp
  ror
  adc  temp
  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?

Thanks in advance.

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

Re: Compact /10 and %10 operations?

Post by lidnariq » Thu Sep 06, 2018 11:50 pm

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

I bet there's other fast ones.

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

Re: Compact /10 and %10 operations?

Post by Bregalad » Fri Sep 07, 2018 12:19 am

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?

Post by na_th_an » Fri Sep 07, 2018 12:41 am

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?

Post by Oziphantom » Fri Sep 07, 2018 5:37 am

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

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

Re: Compact /10 and %10 operations?

Post by dougeff » Fri Sep 07, 2018 5:47 am

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

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

Re: Compact /10 and %10 operations?

Post by Bregalad » Fri Sep 07, 2018 6:01 am

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?

Post by tepples » Fri Sep 07, 2018 6:09 am

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.

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

Re: Compact /10 and %10 operations?

Post by tokumaru » Fri Sep 07, 2018 10:49 am

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?

Post by na_th_an » Mon Sep 10, 2018 2:08 am

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.

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

Re: Compact /10 and %10 operations?

Post by Bregalad » Mon Sep 10, 2018 5:58 am

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?

Post by Oziphantom » Mon Sep 10, 2018 8:17 am

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. )

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

Re: Compact /10 and %10 operations?

Post by Bregalad » Mon Sep 10, 2018 8:35 am

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?

Post by Oziphantom » Mon Sep 10, 2018 9:08 am

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 )

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

Re: Compact /10 and %10 operations?

Post by tokumaru » Mon Sep 10, 2018 10:07 am

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).

Post Reply