Score, etc

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Score, etc

Post by JoeGtake2 »

I've seen lots of posts on this. Trying to keep it very simple. Was wondering if anyone had any thoughts.

Trying to essentially keep a score tallying (no problem with drawing or using a digit-based solution). I can make it work by seeding the additive...like, if I'm adding a three digit number to a three digit number, if I:

Code: Select all


    LDA #$0
    STA add
    LDA #$5
    STA add+1
    LDA #$1
    STA add+2
...and run the routine, I can successfully appear to add 150 to the score, no problem there.

The problem I'm having is using this simple, quick method with variable numbers. So for instance, if I had a table with point-values for various objects (I'm ok with everything being worth <256, so I only need a byte), what would be the simplest way to get a hex value broken into 'places'?

For instance...hex value $64 = 100. What would be the best method or converting $64 to seed a 1 in the 'hundreds' additive, a zero in the 'tens' additive, and a zero in the 'ones' additive?

I can imagine creating three separate 256 byte LUTs to handle this, but that seems dreadfully inefficient and wasteful. Surely, there has to be some better method, and maybe I'm just looking at it too close?

Thanks!
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Score, etc

Post by tepples »

There's a small, reasonably fast (80 cycles) byte-to-digits converter in the source code for Zap Ruder.
hackfresh
Posts: 100
Joined: Sun May 03, 2015 8:19 pm

Re: Score, etc

Post by hackfresh »

This is how its done in TSB. But probably not the most efficient way possible. All of these variables are temporary variables.

Code: Select all


INPUT:  A= hex number to convert

RESULT:
$45=  high digits
$44=  low digits

hex_to_bcd_8_bit						; CONVERT HEX NUMBER TO BCD(A= HEX) 
	STA $45
	LDA #$0A						; SETS TO BASE 10
	STA $44
	JSR divide_8_bit					; 8-BIT DIVIDE ($45/$44) QUOTIENT =$45 REMAINDER =$42
	LDA $42                                             ;
	STA $43                                             ;
	JSR divide_8_bit					; 8-BIT DIVIDE ($45/$44) QUOTIENT =$45 REMAINDER =$42
	LDA $42						;
	ASL                                                   ;
	ASL                                                   ;
	ASL                                                   ;
	ASL                                                   ;
	ORA $43                                             ;
	STA $43                                             ;
	JSR divide_8_bit					; 8-BIT DIVIDE ($45/$44) QUOTIENT =$45 REMAINDER =$42
	LDA $42						; SAVE HIGH DIGIT IN $45
	STA $45                                             ;
	LDA $43                                             ; SAVE LOW DIGIT IN $44
	STA $44                                             ;
	RTS							; RETURN

divide_8_bit:						; 8 BIT DIVIDE ($45/$44) QUOTIENT $45 REMAINDER $42
	LDA #$00
	STA  $42
	LDX #$08                                            ; do 8 bits
@div:
	ASL $45
	ROL $42
	LDA $42
	SEC
	SBC $44
	BCC @next
	STA $42
	INC $45
@next:
	DEX
	BNE @div
	RTS	



Last edited by hackfresh on Wed Dec 09, 2015 2:46 pm, edited 2 times in total.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Score, etc

Post by JoeGtake2 »

Hackfresh - I'm guessing those JSRs are supposed to be to divide_8_bit, and there's not some other divide_8 routine being called, correct?

And I'm guessing $42-$45 are just your memory locations for the 'score' variable or whatever? I'm a bit confused by that part of it.

Thanks!
hackfresh
Posts: 100
Joined: Sun May 03, 2015 8:19 pm

Re: Score, etc

Post by hackfresh »

Yes, I posted this fast so yes the JSR's should have been divide_8_bit. I edited my post. In this game $3E-$45 are used as local variables for routines.

The 'A" register holds the hex number that is to be converted. The result of the conversion gets saved in $44,$45 where 44 is the low digit and $45 is the high digit.

Ex

Input to hex_to_bcd_8_bit A= #$82 = 130 decimal

Result
$44 = #$30
$45 = #$01
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Score, etc

Post by JoeGtake2 »

Perfect - that makes it a lot easier to read. Thanks for the clarification...I'll try to implement this. It's simple enough to navigate and should work just fine for my purpose.
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Score, etc

Post by thefox »

Do you really need 256 unique score values? If you're willing to settle for less (which is very, very common in games), your lookup table doesn't have to be large. E.g. you could decide that possible scores are 5, 10, 15, 30, 50, 100, 200, 350, 500, 800 => only 30 bytes of lookup tables.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
hackfresh
Posts: 100
Joined: Sun May 03, 2015 8:19 pm

Re: Score, etc

Post by hackfresh »

JoeGtake2 wrote:Perfect - that makes it a lot easier to read. Thanks for the clarification...I'll try to implement this. It's simple enough to navigate and should work just fine for my purpose.

Here is a full example. Sorry about the formatting.

EXAMPLE A= #$80 = 128 DECIMAL

Code: Select all

hex_to_bcd_8_bit		; 
	STA $45			        ; $45=#$80
	LDA #$0A		                ; SETS TO BASE 10

				                ; GET ONES PLACE
	STA $44                             ; $44= #$0A
	JSR divide_8_bit	                ; = #$80/#$0A   RESULT = QUOTIENt $45= #$0C, REM $42 = #$08 
	LDA $42                             ; 
	STA $43                             ; $43= #$08
				
				               ; GET 10'S PLACE
	JSR divide_8_biT                 ; =#$0C/#$0A    RESULT = QUOTIENt $45= #$01, REM $42 = #$02 
	LDA $42		               ; A= $42= #$02 
	ASL                                  ; 
	ASL                                  ;
	ASL                                  ;
	ASL                                  ; A= #$20 
	
				               ; COMBINE 10'S AND 1'S PLACE	
	ORA $43                            ; = #$20 + #$08
	STA $43                           ; $43 =#$28
	
				               ; GET 100'S PLACE
	JSR divide_8_bit                  ; = #$01/#$0A   RESULT = QUOTIENT $45= #$00, REM $42 = #$01 
	
				               ; SAVE RESULT IN $44,$45
	LDA $42	                      ; SAVE HIGH DIGIT IN $45 = #$01
	STA $45                           ;
	LDA $43                            ; SAVE LOW DIGIT IN $44 = #$28
	STA $44                            ;
	RTS			               ; RETURN
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Score, etc

Post by tepples »

It depends on how fast the division by 10 is.

For reference, here's a copy of the one I was referring to:

Code: Select all

.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 80 cycles.
; @param a the number to change
; @return a: low digit; 0: upper digits as nibbles
; No other memory or register is touched.
.proc bcd8bit
highDigits = 0

  ; First clear out two bits of highDigits.  (The conversion will
  ; fill in the other six.)
  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, then 100, 80, and one of 40 and 20 aren't.
  ; 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
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Score, etc

Post by rainwarrior »

I'm sure I'd just keep it decimal the whole time. I wouldn't bother to convert, and I probably wouldn't use a lookup table. It doesn't seem terribly inefficient to me at all to just do it one digit per byte. Probably something along the lines of this:

Code: Select all

.segment "BSS"
score: .res SCORE_SIZE

.segment "CODE"

; decimal addition code

score_add100: ; add A * 100 to score (A = 0-9)
	ldx #2
	jmp score_add_loop

score_add10: ; add A * 10 to score (A = 0-9)
	ldx #1
	jmp score_add_loop

score_add1: ; add A to score (A = 0-9)
	ldx #0
score_add_loop:
	clc
	adc score, X
	cmp #10
	bcc :+
		sec
		sbc #10
		sta score, X
		inx
		cpx #SCORE_SIZE
		bcs :++ ; overflow, maybe set all digits to 9 instead?
		lda #1
		jmp score_add_loop
	:
	sta score, X
	:
	rts

; example use

goomba_kill:
	; add 300 to score
	lda #3
	jsr score_add100
	rts

power_up:
	; add 150 to score
	lda #1
	jsr score_add100
	lda #5
	jsr score_add10
	rts
Last edited by rainwarrior on Wed Dec 09, 2015 6:30 pm, edited 2 times in total.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Score, etc

Post by JoeGtake2 »

Rain - that's pretty simple, but now if I had 50 possible 'increase amounts', this would require 50 mini routines, right? KillMonster0-KillMonster50. Again, that seems a little bloated...like the LUTs. Not that it's a huge issue, just wondering what method makes more sense.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Score, etc

Post by rainwarrior »

Whether or not to use a table of values to add is kind of a completely independent question from how to store/display/work with decimal numbers.

If all of your monsters share the same code for dying, sure, use tables? If each has unique code for it, there might not be an advantage. I've got no advice on that part without knowing what you're working with. Doesn't seem like a difficult decision, though.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Score, etc

Post by JoeGtake2 »

Thanks for the input (and that's to everyone!).

Point taken, Rain. I think maybe the best way to handle it in this scenario would be to use your method, make a LUT table for all point-yielding objects (since they all have an identifying object type number anyway) that will point to different 'amounts' to increase, and then just set up routines that are "addOne", "addFive", "addTen", "addTwentyFive" and so on.

Definitely helped me sort that out in my head and simplified it a good bit.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Score, etc

Post by rainwarrior »

Maybe my example wasn't clear. The functions score_add1/add10/add100 were just meant to add a value from A (0 to 9) starting at a specific digit (i.e. multiplied by 1/10/100). I wasn't suggesting to add a subroutine entry for every possible value, just one for each digit you want to add to.
JoeGtake2
Posts: 333
Joined: Tue Jul 01, 2014 4:02 pm

Re: Score, etc

Post by JoeGtake2 »

No, I got it. But if Monster1 adds 5 points, monster2 to adds 250 points, monster3 adds 27 points...etc...I'd need subroutines for each to populate those values, and then feed them into the routine.

ie:

Code: Select all

add5:   ;; for monster one and any other thing that adds five points
    LDA #$05
    JSR score_addOnes
   RTS

add250:  ;; for monster two and any other thing that adds two hundred and fifty points
   LDA #$5
   JSR score_addTens
   LDA #$2
   JSR score_addHundreds
   RTS
etc.
Post Reply