255 rubies in "The Legend of Zelda"?

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

Moderator: Moderators

User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

255 rubies in "The Legend of Zelda"?

Post by DRW »

Why is it possible to collect only 255 rubies in "The Legend of Zelda" for the NES?

I mean, normally, I would know why this is the case: 0-255 = one byte.

But the rubies are displayed as a decimal number. So, didn't they store the value in a three byte array with one byte representing one decimal digit, like they did with the score in "Super Mario Bros."?

Does "Zelda" really have a "binary number to decimal output" function in the program code? Because if not, why don't the rubies max out at 999 instead?
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: 255 rubies in "The Legend of Zelda"?

Post by Sik »

I don't know what it's doing internally but 255 is usually a dead giveaway of using a byte to store the value regardless of how it's being output on screen =P
User avatar
Alp
Posts: 223
Joined: Mon Oct 06, 2014 12:37 am

Re: 255 rubies in "The Legend of Zelda"?

Post by Alp »

As this was Nintendo's first game with save files, keeping the game's stored state simplified would have been advantageous.

The linear usage of RAM, starting from $0656, supports this idea.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: 255 rubies in "The Legend of Zelda"?

Post by koitsu »

Rupie count is stored in a single byte (range 0-255) in RAM location $066d. The code turns the decimal value of that byte into a "3-tile sequence". The raw value is literal and not BCD (i.e. a value of $12 will show up as 18). Effectively it's something like this -- it's been a long, long time since I've looked at the code:

Tile/CHR offsets $00 through $09 are literally 0 through 9
Tile/CHR offset $21 is an X (indicating "number of" or "count of"); it's the same tile that's used for "X" in game text
Tile/CHR offset $24 is a blank/empty tile

If the rupie count is between 0 and 9 (1 digit), it displays X followed the 1-digit value, followed by an empty/blank tile. I.e. a rupie count of 3 would render as $210324.

If the rupie count is between 10 and 99 (2 digits), it displays X followed by the 2-digit value. I.e. a rupie count of 49 would render as $210409.

Otherwise, it prints the rupie count as a 3-digit number.

It might also optimise out needing to display tile $24 by simply filling that area of the nametable with $24 by default, then only drawing the ones it needs to. Same end result either way.

And honestly, that's exactly how I'd do it -- only "splitting" the number into individual digits in the routine that needed to draw the proper on-screen tiles to reflect the value/number. The X stuff is a nice cosmetic touch that players don't get hung up on when playing; I never really noticed it until now, actually.

Footnote: bombs (RAM $0658) and keys (RAM $066e) are printed the same way (yup -- you can have up to 255 of those too! The limiting factor is mainly in the game mechanics (how/when you find maximum bomb increases, etc.)). The one exception is the master key, where after you pick that up, the game sets some flag (not sure of RAM location) that essentially ignores the RAM $066e value and instead just prints XA{blank} (tiles $210a24).
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Re: 255 rubies in "The Legend of Zelda"?

Post by Bregalad »

Aligning numbers left is indeed very elegant, and this prohibes working data in BCD in memory. However, other games commonly uses a variant of BCD to store their data, for instance Castlevania uses entierely BCD, despite the NES lacking BCD mode. Other games simply stores digits in the low nybble and waste the high nybble. After all, the NES has 2k of RAM, one byte more or less will rarely make the difference.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: 255 rubies in "The Legend of Zelda"?

Post by tepples »

Bregalad wrote:Aligning numbers left is indeed very elegant, and this prohibes working data in BCD in memory.
How does digit-by-digit storage prohibit left alignment? Just do the equivalent of this before you copy the text into the output buffer:

Code: Select all

while (i < NUM_DIGITS - 1 && digits[i] == 0) {
  ++i;
}
Other games simply stores digits in the low nybble and waste the high nybble. After all, the NES has 2k of RAM, one byte more or less will rarely make the difference.
Unless it's an RPG or strategy game that has a lot of stats that need to be tracked and displayed.

Among games I've programmed:
  • Concentration Room and "ZapPing" in Zap Ruder use 1-byte binary values for the players' scores and convert them to decimal on display
  • Thwaite stores the score as 3-byte "base 100", where each byte represents two digits from 0 to 99 ($00 to $63), and the ammo stocks are 1-byte binary, all converted to decimal on display
  • RHDE uses 2-byte values from 0 to 65535 for house scores and money amounts, converted to decimal on display
  • Haunted: Halloween '85 stores the kill count as a 2-byte value from 0 to (theoretically) 65535, converted to decimal on display
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: 255 rubies in "The Legend of Zelda"?

Post by DRW »

Thanks for the replies. So, "Zelda" does convert the number for display. Interesting.

In my own game, there's only the score that needs to be displayed and a timer in certain situations.
Lives cannot go higher than 5. (Which is a design choice. I could increase them to 9 without altering the rest of the code.)
And energy is not shown as a number, but as individual units.

I save these values as one byte per digit since there's still plenty of room in RAM.
And each thing that gets you points can only have one non-zero digit. I.e. you can get 20 or 200 points for one thing, but not 250. Which makes the addition function quite simple.

But I agree that an RPG definitely profits from a conversion function. If you have dozens of statuses that can go from 0 to 50000, it definitely makes a difference whether you have a two bytes unsigned int or a five bytes array per status.
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
Dwedit
Posts: 4470
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: 255 rubies in "The Legend of Zelda"?

Post by Dwedit »

Converting small binary numbers to decimal is not hard, the simplest way to do it is repeated subtraction.
Let's say you have a 16-bit unsigned int (0-65535).
Count how many times you can subtract 10000, 1000, 100, 10, 1. That's your decimal number right there.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: 255 rubies in "The Legend of Zelda"?

Post by Sik »

The problem isn't that, the problem is doing it quickly.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: 255 rubies in "The Legend of Zelda"?

Post by tepples »

How quickly does it need to be? You only need to convert values during those frames when you're updating the status bar. The routine I used in Concentration Room, Thwaite, and Zap Ruder converts 8 bits to 3 digits in 80 cycles, and the routine in RHDE and HH85 converts 16 bits to 5 digits in about 670 cycles. Other faster routines have been posted.
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: 255 rubies in "The Legend of Zelda"?

Post by Sik »

Yeah usually you update like once every many frames =/ I imagine that most games actually just do some rather dumb decimal-to-BCD conversion and move on.
tomaitheous
Posts: 592
Joined: Thu Aug 28, 2008 1:17 am
Contact:

Re: 255 rubies in "The Legend of Zelda"?

Post by tomaitheous »

Sik wrote:The problem isn't that, the problem is doing it quickly.
What's the fast method for doing this on the NES?

I think having a 256 LUT, which outputs 0 to 99. And for the last digit in the 100s place, simply check of the original byte is greater than 200 (populate a 2) or greater than 100 (populate a 1). Since NES doesn't have decimal mode like other 65x, you can't easily cascade two more bytes in this process unfortunately.
__________________________
http://pcedev.wordpress.com
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: 255 rubies in "The Legend of Zelda"?

Post by tepples »

This is what Thwaite and Concentration Room use. For 69, it produces $0000 = $06 and A = $09. For 246, it produces $0000 = $24 and A = $06.

Code: Select all

; 8-bit binary to decimal converter
; copyright 2010 Damian Yerrick
; License: WTFPL http://www.wtfpl.net/

.macro bcd8bit_iter value
  .local skip
  cmp value
  bcc skip
  sbc value
skip:
  rol highDigits
.endmacro

;;
; Converts a decimal number to two or three BCD digits
; in no more than 84 cycles.
; @param A the number to change
; @return A: low digit; $0000: upper digits as nibbles
.proc bcd8bit
highDigits = 0
  asl highDigits
  asl highDigits

  ; Each iteration takes 11 if subtraction occurs or 10 if not.
  ; But if 80 is subtracted, 40 and 20 aren't, and if 200 is
  ; subtracted, 80 is not, and at least one of 40 and 20 is not.
  ; So this part takes up to 6*11-2 cycles.
  bcd8bit_iter #200
  bcd8bit_iter #100
  bcd8bit_iter #80
  bcd8bit_iter #40
  bcd8bit_iter #20
  bcd8bit_iter #10
  rts
.endproc
And it uses the "base 100" trick because of this lack of cascading.

Code: Select all

;;
; Adds between 1 and 255 points to the score.
; X, Y, and memory (apart from score) are unchanged.
.proc addScore
  clc
  adc score1s
  bcc notOver256
  inc score100s
  inc score100s
  adc #55
notOver256:
  cmp #100
  bcc notOver100
  sbc #100
  inc score100s
  bcs notOver256
notOver100:
  sta score1s
  lda bgDirty
  ora #BG_DIRTY_STATUS
  sta bgDirty
  rts
.endproc
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: 255 rubies in "The Legend of Zelda"?

Post by Sik »

tomaitheous wrote:I think having a 256 LUT, which outputs 0 to 99.
You only need 100 entries for that ;P

But yeah that's fast and feasible for homebrew, back in the day it wasn't that feasible with tiny PRG-ROMs though where every byte mattered, hence the slower methods. (well, and not every programmer being clever enough or having enough time to come up with a good mehtod)
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 255 rubies in "The Legend of Zelda"?

Post by tokumaru »

Sik wrote:You only need 100 entries for that ;P
Yes, just handle the hundreds digit first and whatever's left is guaranteed to be 99 or less.
Post Reply