I've read the docs on using BCD to display a single byte in decimal (0 - 255). Is there any code out there to display a number up to 9999 ?
You'll need to choose an approach for storing your numbers in multiple bytes. There are various tradeoffs to consider for density vs. ease and speed of arithmetic vs. ease and speed of display. If you store a large number as a single digit per byte it will be quite easy to draw the digits, but e.g. incrementing or adding to that number will be more expensive than if you store two digits per byte.
See also: How Do I Display a Number?
In order to do this, you need to write a 16 bit math library: addition, subtraction, multiplication, division. The first two are relativly easy, just add the low and the hight byte and add or subtract the carry bit. For multiplication you may use the russian multipication algorythm (see for example https://www.wikihow.com/Multiply-Using-the-Russian-Peasant-Method) which only works by shifting a binary number and addition. Divison works like you divide on paper. You may convert z80 assembly to chip8. For example from here: https://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division or for 6502 (https://llx.com/Neil/a2/mult.html) with good explanation of the algorythm.
Lazy Multiplication by 10 can be done by
x = x + x ; *2
y = x
x = x + x ; *4
x = x + x ; * 8
x = x + y; * 10
You have to use your 16bit addition here and work with two registers. There is no lazy divide by 10
If you have all this, you can start decoding the number to decimal.
The last digit you get by
1. Divide by 10.
2. Multiply by 10.
3. Take difference.
Repeat this until the number is 0
You may also work the other way around.
digit1 = x / 1000
x = x / 10
digit2 = x / 100
x = x / 10
digit3 = x / 10
digit4 = x
As a general rule, I'd strongly recommend against starting with a collection of general-purpose routines; they will often do much more work than is actually necessary for a given application, wasting cycles and RAM.
Start with the game concept, and sketch out the design. For all data structures- including number representations- look at the ways your game will use them, and choose representations which make those operations easy.
An efficient program will flow from appropriate choices of data layout and structure. Different design constraints ask for different representations.
This code implements a 16 bit unsigned int library in octo. It can read and print 16 bit numbers and has the operations add, sub, mult, div and mod
https://github.com/tquester/J-Octo-Chip8-IDE/blob/main/samples/lib/libMath.8o
A bit late to the party, but this explains the general concepts well if you want to take the calculating road:
But I too think Internet Janitor's idea of storing each digit in its own byte is probably easier to do with less code. Especially if you're just keeping score in a game and you only need to increase your large (0-9999) value by one at a time.
Something like this:
: bump-score i := score load v3 v3 += 1 if v3 == 10 begin v3 := 0 v2 += 1 if v2 == 10 begin v2 := 0 v1 += 1 if v1 == 10 begin v1 := 0 v0 += 1 end end end i := score save v3 return : show-score v5 := 0 # Coordinates to draw to v6 := 0 i := score load v3 i := bcd-buffer bcd v0 i := bcd-digit load v0 i := hex v0 sprite v5 v6 5 v5 += 5 i := bcd-buffer bcd v1 i := bcd-digit load v0 i := hex v0 sprite v5 v6 5 v5 += 5 i := bcd-buffer bcd v2 i := bcd-digit load v0 i := hex v0 sprite v5 v6 5 v5 += 5 i := bcd-buffer bcd v3 i := bcd-digit load v0 i := hex v0 sprite v5 v6 5 return : score 0 0 0 0 : bcd-buffer 0 0 : bcd-digit 0