How to store and draw score

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
How to store and draw score
by on (#158433)
Hi,

New to this forum, I know this question must have been asked many times so maybe you can point me to a post or article explaining this, else, how to I store score (I as more than 1 byte will be needed for sure) and most important how to I translate the value to the score text on screen?
Re: How to store and draw score
by on (#158435)
I like to keep it simple for numbers that must be displayed on screen and are not used in complex calculations: each digit is a byte. Additions are performed one byte at a time, and after doing each digit you manually check for overflows (digit > 9) and set the carry so this overflow is carried over to the next digit.

For displaying, you just add each byte to the index of the tile that contains the digit "0", and write the result to the name tables or OAM, depending on whether your score display is drawn as background or sprites.

EDIT: Here's an example of how I would add two 3-digit (i.e. 0 to 999) numbers (num1 = num1 + num2):
Code:
   clc
   lda num1+0
   adc num2+0
   sta num1+0 ;assume no overflow
   sbc #9 ;subtract 10
   bcc + ;skip if the result is indeed less than 10
   sta num1+0 ;store the corrected result
+   lda num1+1
   adc num2+1
   sta num1+1 ;assume no overflow
   sbc #9 ;subtract 10
   bcc + ;skip if the result is indeed less than 10
   sta num1+1 ;store the corrected result
+   lda num1+2
   adc num2+2
   sta num1+2 ;assume no overflow
   sbc #9 ;subtract 10
   bcc + ;skip if the result is indeed less than 10
   sta num1+2 ;store the corrected result
+

You could use a loop instead of unrolling everything like in the example, and/or make a subroutine that uses pointers to access the numbers, so you can use the same routine for numbers anywhere in RAM.

EDIT 2: A generic subroutine could look something like this:
Code:
AddDecimal:

   ldx #6 ;do 6 digits
   ldy #0 ;start with the lowest digit

AddDigit:

   lda (Num1), y ;get digit from memory location [Num1+Y]
   adc (Num2), y ;add to digit at memory location [Num2+Y]
   sta (Num1), y ;assume no overflow
   sbc #9 ;subtract 10
   bcc + ;skip if the result is indeed less than 10
   sta (Num1), y ;store the corrected result
+   iny ;move on to the next digit
   dex ;decrement the digit counter
   bne AddDigit ;add again if there are digits left

   rts ;return
Re: How to store and draw score
by on (#158440)
sempressimo wrote:
how to I store score (I as more than 1 byte will be needed for sure) and most important how to I translate the value to the score text on screen?

For a 16-bit value, you store bits 15-8 in one byte and bits 7-0 in another. To add 16-bit numbers, you clear carry, add the low bytes, and then add the high bytes without clearing carry.

To display the score when it has changed, you can convert it to decimal and store the digits in whatever buffer you use to draw text to the screen.
Re: How to store and draw score
by on (#158443)
tepples wrote:

700 cycles? That seems kinda steep.
Re: How to store and draw score
by on (#158445)
Thanks!
Re: How to store and draw score
by on (#158446)
tomaitheous wrote:
tepples wrote:

700 cycles? That seems kinda steep.

It's not quite as steep for something you do only every few frames when you're not also pushing something else to video memory. When you update the score, set the "status bar dirty" flag, and then your "check for dirty things" routine will eventually pick it up and spend the 2.5% of active picture time turning it into a VRAM update.

I also made an 8-bit version in 80 cycles, which allows storing the score in base 100 like this:
Code:
clc
lda score_ones
adc add_ones
cmp #100
bcc :+
  sbc #100  ; and carry is still set
:
sta score_ones
lda score_hundreds
adc add_hundreds
cmp #100
bcc :+
  sbc #100
  inc score_myriads
:
sta score_hundreds

Thwaite uses this base 100 technique.
Re: How to store and draw score
by on (#158452)
If you can allow for it, you might be able to split the calculation across several frames.
Re: How to store and draw score
by on (#158456)
What tokumaru said...
Quote:
keep it simple for numbers that must be displayed on screen and are not used in complex calculations: each digit is a byte


Don't bother converting to decimal. Just do the math a hex-digit at a time and adjust for overflow (A-F).
Re: How to store and draw score
by on (#158484)
Are HUDs usually hard coded, or are they tied to a text engine?
Re: How to store and draw score
by on (#158485)
psycopathicteen wrote:
Are HUDs usually hard coded, or are they tied to a text engine?


I'm not sure how others do it, but I use a combination of the two.
That is, I use values starting from $F0, for hard-coded subroutines in the main text engine.
Re: How to store and draw score
by on (#158584)
tokumaru's method of using one byte per digit is called "unpacked BCD", by the way, and it happens to be what I'm using for the clone of Pac-Man I'm currently writing. I simply wrote a routine that adds two BCD numbers together and defined any numbers I was using my code like this:

Code:
Points200: .byte 0,0,0,2,0,0


After points are added, I also have to check if the new score is greater than the current high score. For that, just compare the two scores starting at the leftmost digit. If they're equal, proceed to the next digit, and so on until one of them differs. The carry flag will then tell you which score is higher.

You can look at my code if you like, though it's a bit messy as of this writing. Search for ".macro AddDigit".
Re: How to store and draw score
by on (#158585)
What I do was based on a suggetion by Tepples, a sugettion so excellent that I took it !
It's a compromise between storing it in full BCD (each digit is stored separatedly in memory) and storing it in binary.

Each pair of digits is encoded in one byte, and is encoded in binary in the 0-99 range. Since I had to have a binary->display routine for numbers between 0 and 99 anyway, it didn't cost me anything to do that. But thanks to encoding pair of digits, the score is stored in 3 RAM bytes instead of 6. This only works if the number of digits of your score is even.

Castlevania games stores the score in "true" BCD where each digit takes a nybble, but then it's extremely annoying to do addition/substractions operations since BCD mode is absent. I suspect most other games stores each digit separately, since it's the simplest.

Storing in binary would be in my opinion dumb, unless your game uses a lot of binary numbers elsewhere anyway, and as such the "price" of storing a large binary->decimal conversion routine becomes worth paying.
Re: How to store and draw score
by on (#158587)
Bregalad wrote:
What I do was based on a suggetion by Tepples, a sugettion so excellent that I took it !
It's a compromise between storing it in full BCD (each digit is stored separatedly in memory) and storing it in binary.

Yeah I also like this method. It's also very easy to implement the 0..99 => tile number conversion with a lookup table, if you're willing to trade off some ROM for the cycles.