Methods for Storing Scores in Memory
There are a number of methods for storing a Score in memory, here are a few examples:
A Single Word
This is the simplest and smallest method for storing the score in memory, but it has a couple of disadvantages over some other methods. It can only hold a score up to 65535 (although you can add an extra '0' or two on the display to make that 655350 or 6553500), converting it to digits for display requires a relatively slow algorithm, and adding extra lives every 10000 points for example can take a complex routine. The advantage of using a straight Word is that the maths required to add points to the score is very simple and fast.
To define the score:
.score dw 0
To add some points (in BC):
.add_score_bc ld hl,(score) add hl,bc ld (score),hl ret
To display the score requires converting to a decimal:
.show_score ld hl,score ld bc,-10000 call show_digit ld bc,-1000 call show_digit ld bc,-100 call show_digit ld bc,-10 call show_digit ld a,l add '0' jp show_char .show_digit ld a,'0' - 1 .inc_digit inc a add hl,bc jr nc,inc_digit or a sbc hl,bc add '0' jp show_char ; This assumes the show_char routine doesn't corrupt HL
Checking for every 10000 points is complicated, this would normally done in the add_score routine, and I'm not going to write the code. It would however, be quite simple to add an extra life every multiple of 256 units (eg. 10240 points) by checking the most significant byte. eg:
.add_score_bc ld hl,(score) ld a,h add hl,bc ld (score),hl sub h cp 2 ret c ld hl,lives inc (hl) ret
A Double Word
Like the above method, this provides a simple and efficient way to store and add points, but the routine required to convert the number to decimal for display is even more difficult, as is the extra lives every 10000 points.
To define the score:
.score ds 4
To add BC points to the score:
.add_score_bc ld hl,(score) add hl,bc ld (score),hl ret nc ld hl,(score + 2) inc hl ld (score + 2),hl ret
Displaying the score is much more tricky since it involves a lot of DWord subtractions etc. Most Z80 assemblers (including WinAPE) can't handle DWord values, so you need to get your calculator out to figure out what the values are to subtract for successive digits. The score can go as high as 4294967295, but I wouldn't recommend doing the maths for all possible digits. The code to display a DWord value is too complicated for this document.
A Single BCD Word
This is similar to the above method, but the score is treated as Binary Coded Decimal. It's main disadvantage is that it can hold even less values than the single Word, restricting the score to 10000 possible combinations (one again with extra zeroes if required). Depending on the type of game and difficulty, this could be a good method to use. It's easy to store in memory and pass around, displaying and adding points are all relatively easy.
To define the score is exactly the same as for a Single Word:
.score dw 0
Adding BC points is also fairly straightforward (here with the extra life check every 1000 units):
.add_score_BC ld hl,(score) ld d,h ld a,l add c daa ld l,a ld a,h adc b daa ld h,a ld (score),hl sub d cp #10 ret c ld hl,lives inc (hl) ret
And the routine to display the score:
.show_score ld hl,(score) .bcd_hl ld a,h call bcd_a ld a,l .bcd_a ld h,a rra:rra:rra:rra and #0f add '0' call show_char ld a,h and #0f add '0' jp show_char
A Double BCD Word
This is actually one of the best methods of storing and displaying a score since the maths and display methods remain fairly simple, and your score can now hold up to 8 digits. Luckily the Z80 has just enough fast 16 bit registers (ie. Non-index registers) to be able to handle all the things you require.
To define the score:
.score ds 4
To add BC points to the score (here with the extra life check every 10000 units):
ld hl,(score) ld a,l add c daa ld l,a ld a,h adc b daa ld h,a ld (score),hl ret nc ld hl,lives inc (hl) ld hl,(score + 2) ld a,l add 1 daa ld l,a ld a,h adc 0 daa ld h,a ld (score + 2),hl ret
Displaying the score is relatively simple too, using the same routine as above:
.show_score ld hl,(score + 2) call bcd_hl ld hl,(score) .bcd_hl . . .
Multiple BCD Bytes
This is very similar to the above method, but the values are stored and manipulated in a slightly different way. With this method, you can determine exactly how many digits you require. Maths is pretty straightforward, as is extra lives and display. One advantage this method has is that if you want to add 100 to the score, you don't have to add the two zeroes since you can start at the second BCD digit. With this method, you can store the score either little-endian (least significant bytes first) or big-endian. Big-endian has the advantage that you can read the score in a memory editor left-to-right. eg. 1234500 points is displayed as 01 23 45 00. This can make debugging easier.
To define the score (6 digits):
.score ds 3
To add C points to the score:
ld b,3 .add_part ld hl,score + 2 ld a,(hl) add c daa ld (hl),a ret nc .add_loop dec hl ld a,(hl) adc 0 daa ld (hl),a djnz add_loop ret
Displaying the score is relatively simple too, using the same routine as above:
.show_score ld hl,(score + 2) call bcd_hl ld hl,(score) .bcd_hl . . .
More to come...
Executioner 01:27, 21 September 2007 (CEST)