Score Keeping in ML

Basic and Machine Language

Moderator: Moderators

Post Reply
User avatar
GreyGhost
Vic 20 Nerd
Posts: 525
Joined: Wed Oct 05, 2005 11:10 pm

Score Keeping in ML

Post by GreyGhost »

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
tlr
Vic 20 Nerd
Posts: 567
Joined: Mon Oct 04, 2004 10:53 am

Post by tlr »

It could actually be cheaper to store the score as full bytes per digit because the code to handle it gets simpler.
You can also keep the score directly on the screen if you need to save space.
Kananga
Vic 20 Afficionado
Posts: 317
Joined: Mon Mar 08, 2010 2:11 pm

Post by Kananga »

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! :-)
Buy the new Bug-Wizard, the first 100 bugs are free!
User avatar
GreyGhost
Vic 20 Nerd
Posts: 525
Joined: Wed Oct 05, 2005 11:10 pm

Post by GreyGhost »

I gave tlr's suggestion a try and came up with this:

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
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.

Kananga wrote:Jungle Hunt uses BCD for score-keeping. Take a look at code starting from $A7EF.
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.
Rob
User avatar
Mike
Herr VC
Posts: 4845
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Post by Mike »

My version:

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
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,

Code: Select all

>02B6 00 00 00 00 01
increases the score by 1 each call,

Code: Select all

>02B6 00 00 00 02 05
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).
rhurst
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

Post by rhurst »

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.

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
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).

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
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. :P
Any technology distinguishable from magic is insufficiently advanced.
https://robert.hurst-ri.us/rob/retrocomputing
User avatar
GreyGhost
Vic 20 Nerd
Posts: 525
Joined: Wed Oct 05, 2005 11:10 pm

Post by GreyGhost »

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.

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
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.

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
Post Reply