Game 1 Unit 14 of 16

High Score

Give players a target. Tracking and displaying the best score creates replayability and personal challenge.

88% of SID Symphony

The Target

A game without a high score is a game without memory. Each session exists in isolation — players have no sense of progress, no target to beat, no reason to replay.

The high score changes everything. Now there’s a goal beyond “finish the song.” Players compete against their past selves, pushing for that extra perfect hit, taking risks to maintain their multiplier.

Storage

High score needs two bytes — same as the regular score. But unlike the regular score, it persists across games within a session:

; High score variables (Unit 14)
high_score_lo:  !byte $00           ; Best score low byte (persists between games)
high_score_hi:  !byte $00           ; Best score high byte

Note what we’re NOT doing: saving to disk. That’s complexity we don’t need. The high score persists as long as the program runs. Turn off the machine, it resets. This is how most arcade games worked — and it’s enough to create that competitive tension.

Comparison

After each game ends, we compare the current score against the high score. This requires 16-bit comparison — check the high byte first, only compare low bytes if the high bytes are equal:

check_high_score:
            ; Compare high byte first
            lda score_hi
            cmp high_score_hi
            bcc chs_done            ; Current < high, done
            bne chs_new_high        ; Current > high, new record

            ; High bytes equal, compare low bytes
            lda score_lo
            cmp high_score_lo
            bcc chs_done            ; Current < high, done
            beq chs_done            ; Current = high, done

chs_new_high:
            ; New high score!
            lda score_lo
            sta high_score_lo
            lda score_hi
            sta high_score_hi

chs_done:
            rts

The logic flow:

  1. Compare high bytes
  2. If current high byte is less, we’re done (not a record)
  3. If current high byte is greater, it’s a new record
  4. If equal, compare low bytes
  5. Only update if current is strictly greater

Triggering the Check

The high score check happens at game end — both victory and game over:

            ; No notes left - victory!
            jsr silence_all
            jsr check_high_score    ; Check for new high score
            lda #STATE_VICTORY
            sta game_state
            jsr draw_victory_screen
            ; Game over!
            jsr silence_all
            jsr check_high_score    ; Check for new high score
            lda #STATE_GAMEOVER
            sta game_state
            jsr draw_gameover_screen

Display

The end screens now show both the current score and the high score. We draw the high score in yellow to distinguish it:

            ; Draw "High: NNNNNN"
            ldx #$00
dfs_high:
            lda highscore_text,x
            beq dfs_high_done
            sta SCREEN + (14 * 40) + 14,x
            lda #COL_YELLOW
            sta COLOUR + (14 * 40) + 14,x
            inx
            bne dfs_high
dfs_high_done:

Converting the high score to digits reuses our existing convert_score routine. We temporarily swap the high score into the score variables, convert, then restore:

            ; Save current score temporarily
            lda score_lo
            pha
            lda score_hi
            pha

            ; Put high score into score for conversion
            lda high_score_lo
            sta score_lo
            lda high_score_hi
            sta score_hi
            jsr convert_score

            ; Restore current score
            pla
            sta score_hi
            pla
            sta score_lo

This technique — temporarily using existing variables for a different purpose — is common in 6502 programming. It saves memory and avoids duplicating conversion logic.

The Psychology

Watch how players behave with a high score:

  1. First game: They’re learning, score doesn’t matter
  2. Second game: Now they have a target — their first score
  3. Third game: They beat it! New target set
  4. Fourth game: They fall short. Frustration drives one more try…

This loop is addictive. The high score turns a five-minute game into a thirty-minute session. Players aren’t just playing — they’re competing.

Why Not Save to Disk?

Saving to disk would make the high score truly persistent. But consider:

  • Complexity: File I/O on the C64 involves serial communication, error handling, and file management
  • Speed: Disk operations are slow and would interrupt the flow
  • Authenticity: Arcade games didn’t save high scores (mostly)
  • Scope: This is Unit 14 of 16 — we have other features to add

The simple approach works. Save disk I/O for a later lesson.

What’s Next

The game now tracks achievements across plays. In Unit 15, we add pause functionality — letting players take a break mid-song without losing progress.