Score Keeping in ML
Moderator: Moderators
Score Keeping in ML
I want to get some ideas about how to keep and display a score in a ML program. The idea that I have in mind is using decimal mode and then reading the half bytes for each place and converting those numbers to ascii or screen codes. I then transfer those codes to the score counter on the screen. Anyone have a different approach to this?
Rob
Jungle Hunt uses BCD for score-keeping. Take a look at code starting from $A7EF.
- The score in $23-$25 is updated from current bonus in $26-$28.
- The points in $26-$28 are reset to zero
- The score in $23-$25 is written to screen.
There are perhaps simpler ways as mentioned by tlr, but it's an ancient example!
- The score in $23-$25 is updated from current bonus in $26-$28.
- The points in $26-$28 are reset to zero
- The score in $23-$25 is written to screen.
There are perhaps simpler ways as mentioned by tlr, but it's an ancient example!
Buy the new Bug-Wizard, the first 100 bugs are free!
I gave tlr's suggestion a try and came up with this:
Still a bit clunky and chunky. I see where I can make a loop out of it. I'm still working on it. But it does work.
Code: Select all
score
CLC
LDX #$04
next_digit
LDA score_loc,x
ADC #$05
CMP #$39
BCS add_100
STA score_loc,x
RTS
add_100
LDA #$30
STA score_loc,x
DEX
LDA score_loc,x
ADC #$00
CMP #$39
BCS add_100
STA score_loc,x
RTS
add_1000
LDA #$30
STA score_loc,x
DEX
LDA score_loc,x
ADC #$00
CMP #$39
BCS add_10000
STA score_loc,x
RTS
add_10000
LDA #$30
STA score_loc,x
DEX
LDA score_loc,x
ADC #$00
CMP #$39
BCS all_00000
STA score_loc,x
RTS
all_00000
RTS
I'll take a look at that too. I still have a little patch of hair that didn't get pulled out. I'm still having trouble with branching conditions.Kananga wrote:Jungle Hunt uses BCD for score-keeping. Take a look at code starting from $A7EF.
Rob
- Mike
- Herr VC
- Posts: 4845
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
My version:
The routine expects the display initialised with 00000 manually beforehand, located in the top-left corner of the screen (unexpanded VIC).
$FB/$FC point to the array which signifies how many points should be added to the score. For example, POKE 251,182:POKE252,2 set it to $02B6, directly behind the code. Then,
increases the score by 1 each call,
adds 25 points, etc.
This way, you can add different amounts to the score by providing a set of the possible numbers, and just set the pointer to the correct value instead of copying 5 bytes (or even more if you use a bigger display).
Code: Select all
.02A1 A0 04 LDY #$04 ; number of digits in score, minus 1
.02A3 18 CLC
.02A4 B9 00 1E LDA $1E00,Y ; load current digit of score
.02A7 71 FB ADC ($FB),Y ; add new points, and add in preceding carry
.02A9 C9 3A CMP #$3A ; digit >= 10 (i.e. > 9)?
.02AB 90 02 BCC $02AF
.02AD E9 0A SBC #$0A ; yes, adjust digit - incidentally this also keeps the C flag set!
.02AF 99 00 1E STA $1E00,Y ; store new digit
.02B2 88 DEY
.02B3 10 EF BPL $02A4 ; loop until all digits are done
.02B5 60 RTS
$FB/$FC point to the array which signifies how many points should be added to the score. For example, POKE 251,182:POKE252,2 set it to $02B6, directly behind the code. Then,
Code: Select all
>02B6 00 00 00 00 01
Code: Select all
>02B6 00 00 00 02 05
This way, you can add different amounts to the score by providing a set of the possible numbers, and just set the pointer to the correct value instead of copying 5 bytes (or even more if you use a bigger display).
-
- Omega Star Commander
- Posts: 1371
- Joined: Thu Jan 31, 2008 2:12 pm
- Website: https://robert.hurst-ri.us
- Location: Providence, RI
- Occupation: Tech & Innovation
Yes, if space is a premium and your chief concern, maintaining the score using on-screen bytes is the cheapest and most effective way of accomplishing that. And if the game's scoring is simplistic, Quikman 1984 comes to mind, that is clearly the way to go.
But displaying a score of whole numbers using 8-bit registers can get tricky and expensive too, especially when you cannot use any ROM integer display routine, i.e., outside of 65535 (or is it BASIC's line number limit of 63999?). BCD has benefits in this regard, outside of our conditioned thinking in base10 implications.
Shown here is a copy of Berzerk MMX scoring routine which adds a check of: zero scoring if the player is already completely vaporized and has no lives remaining; and award a single extra life upon 5000 points or more.
This is overkill for Berzerk, because scoring is only in 10-point bonus increments or 50-points per robot kill, i.e., A=50,Y=2. Omega Fury has a much more elaborate scoring system, with end of objective bonuses up to 5000-points, i.e., A=50,Y=1 yields adding 50 to the 100-point register. So it is fairly portable between games.
The apparent weakness of this routine is that additions to the player's score is only by a single register (A), so a score of +150 is not possible (although you could call it twice: +100 and +50).
Note: the ADC #$0E is my offset pointer to a set of custom character digits 0-9. That use to be (if I recall correctly) $C0 for the VIC ROM digits.
This may look like overkill, too, using the plot/print routines provided by the software sprite stack, whereas changes are going to the PENDING video buffer, which updates visible sprites and any other changes to the ACTIVE display in one consolidated operation.
I could have directly manipulated on-screen bytes and shortened this display update, however, I have to take consideration of the possibility that a sprite could be rendered on top of the player's score (evil otto could be bouncing over it, a Thargoid ship could travel outside of the Fury's range, etc.) In this case, avoiding direct writes to the video buffer makes nice for every video frame flip operation later.
There are trade-offs for "clunky & chunky" code such as this, outside of the tighty whities versus boxers argument. It all depends on your needs.
But displaying a score of whole numbers using 8-bit registers can get tricky and expensive too, especially when you cannot use any ROM integer display routine, i.e., outside of 65535 (or is it BASIC's line number limit of 63999?). BCD has benefits in this regard, outside of our conditioned thinking in base10 implications.
Code: Select all
SCORE: .byte 0,0,0 ; 6-digits, add more bytes if needed
;*********************************************************************
; Update player's score
; send A with decimal number to add
; send X with placeholder (2=1s, 1=100s, 0=10,000s)
;
SCOREUPDATE:
LDY LIVES
BEQ @fini ; no score if you're dead already ...
SED
CLC
ADC SCORE,X
STA SCORE,X
BCC @cc
@cs: DEX ; too bad if 1,000,000 is breached ... no one
LDA SCORE,X ; likes a smarty-pants! :P
CLC
ADC #$01
STA SCORE,X
BCS @cs
@cc: CLD
LDX EXTRA
BNE @show
LDA SCORE+1
CMP #$50
BCC @show
INC EXTRA ; woot!
INC LIVES
@show: JSR SCORESTATUS
@fini: RTS
This is overkill for Berzerk, because scoring is only in 10-point bonus increments or 50-points per robot kill, i.e., A=50,Y=2. Omega Fury has a much more elaborate scoring system, with end of objective bonuses up to 5000-points, i.e., A=50,Y=1 yields adding 50 to the 100-point register. So it is fairly portable between games.
The apparent weakness of this routine is that additions to the player's score is only by a single register (A), so a score of +150 is not possible (although you could call it twice: +100 and +50).
Code: Select all
;*********************************************************************
; Show player's score
;
SCORESTATUS:
LDA #$01 ; white
STA COLORCODE
LDX #1
LDY #22
JSR SSSPLOT
LDX #$00
@loop: LDA SCORE,X
LSR
LSR
LSR
LSR
CLC
ADC #$0E
JSR SSSPRINT
LDA SCORE,X
AND #$0F
CLC
ADC #$0E
JSR SSSPRINT
INX
CPX #$03
BNE @loop
;
LDA #$05 ; green
STA COLORCODE
LDX LIVES
CPX #4
BEQ @lives
LDA #$A0
JSR SSSPRINT
@lives: DEX
BEQ @fini
LDA #$00 ; life icon
JSR SSSPRINT
JMP @lives
@fini: RTS
This may look like overkill, too, using the plot/print routines provided by the software sprite stack, whereas changes are going to the PENDING video buffer, which updates visible sprites and any other changes to the ACTIVE display in one consolidated operation.
I could have directly manipulated on-screen bytes and shortened this display update, however, I have to take consideration of the possibility that a sprite could be rendered on top of the player's score (evil otto could be bouncing over it, a Thargoid ship could travel outside of the Fury's range, etc.) In this case, avoiding direct writes to the video buffer makes nice for every video frame flip operation later.
There are trade-offs for "clunky & chunky" code such as this, outside of the tighty whities versus boxers argument. It all depends on your needs.
Any technology distinguishable from magic is insufficiently advanced.
https://robert.hurst-ri.us/rob/retrocomputing
https://robert.hurst-ri.us/rob/retrocomputing
I appreciate all of your input. I went with Mike's suggestion because it more closely resembled the code I had written and was smaller than the others. Though at the moment size isn't an issue, I am going to try to keep my first all ML game within the unexpanded Vic. I added a couple of routines to it to suit my needs and call them from the program as they are needed.
I got rid of the pointer and I adjust the score increments as I need them, and then call the score routine. Its just as easy as changing the pointer and I was able to use the last 6 bytes of the screen matrix as my dedicated value holder.
And, because my program clears the screen between levels I was able to use that same area to save the score, like an increment score and recall it once the new screen was drawn. I guess I could go to a 6 place score with that. Thanks again for the knowledge.
Code: Select all
score_loc = $1e06 ;score area on screen
score_val = $1ffa ;score value
score
CLC
LDX #$04 ; number of digits in score, minus 1
lbl_02
LDA score_loc,x ; load current digit of score
ADC score_val,x ; add new points, and add in preceding carry
CMP #$3a ; digit >= 10 (i.e. > 9)?
BCC lbl_01
SBC #$0a ; yes, adjust digit-incidentally this also keeps the C flag set!
lbl_01
STA score_loc,x ; store new digit
DEX
BPL lbl_02 ; loop until all digits are done
RTS
save_score
LDY #$04
ssc_loop
CLC
LDA score_loc,y ;get screen code value from score
SBC #$2f ;off set for numbers in char set
STA score_val,y ;store number in a holding area
DEY ;next place
BPL ssc_loop ;get next screen code number
RTS
blank_value
LDY #$04 ;five cells for score
LDA #$00 ;load them with zeros
clr_value
STA score_val,y ;store them in holding area
DEY ;next
BPL clr_value ;not finished, go get another
RTS
Code: Select all
JSR blank_value
LDX #$02
LDA #$05
STA score_val,x
JSR score
; scores 500
And, because my program clears the screen between levels I was able to use that same area to save the score, like an increment score and recall it once the new screen was drawn. I guess I could go to a 6 place score with that. Thanks again for the knowledge.
Rob