Import from tarball created on 2024-03-14

This commit is contained in:
John Lorentzson 2025-03-26 10:28:23 +01:00
commit 9206086b8a
11 changed files with 924 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*.o
*.prg
*.lst

3
build.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
cl65 -o pong.prg -l pong.lst -t c64 -C c64-asm.cfg -u__EXEHDR__ main.asm

84
c64.inc Normal file
View file

@ -0,0 +1,84 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
PROGSTART = $080D ; just after BASIC header
;; Misc
CINV = $0314 ; Hardware IRQ vector
;; CIA registers
PRA = $DC00
PRB = $DC01
DDRA = $DC02
DDRB = $DC03
CIA_ICR = $DC0D
PRA2 = $DD00
;; VIC-II registers
VICBASE = $D000
SPRITE_0X = VICBASE+0
SPRITE_0Y = VICBASE+1
SPRITE_1X = VICBASE+2
SPRITE_1Y = VICBASE+3
SPRITE_2X = VICBASE+4
SPRITE_2Y = VICBASE+5
SPRITE_3X = VICBASE+6
SPRITE_3Y = VICBASE+7
SPRITE_4X = VICBASE+8
SPRITE_4Y = VICBASE+9
SPRITE_5X = VICBASE+10
SPRITE_5Y = VICBASE+11
SPRITE_6X = VICBASE+12
SPRITE_6Y = VICBASE+13
SPRITE_7X = VICBASE+14
SPRITE_7Y = VICBASE+15
SPRITE_X_MSB = VICBASE+16
YSCROLL_MODE = VICBASE+17
RASTER = VICBASE+18
;; ...
SPRITE_ENABLE = VICBASE+21
XSCROLL_MODE = VICBASE+22
SPRITE_XPAND_Y = VICBASE+23
;; ...
MEMPTR = VICBASE+24
VICINT = VICBASE+25
VICINTMASK = VICBASE+26
SPRITE_BG_PRIO = VICBASE+27
;; ...
SPRITE_XPAND_X = VICBASE+29
SSCOL = VICBASE+30
;; ...
BORDER = VICBASE+32
BGCOL0 = VICBASE+33
BGCOL1 = VICBASE+34
BGCOL2 = VICBASE+35
BGCOL3 = VICBASE+36
;; ...
SPRITE_0C = VICBASE+39
SPRITE_1C = VICBASE+40
SPRITE_2C = VICBASE+41
SPRITE_3C = VICBASE+42
SPRITE_4C = VICBASE+43
SPRITE_5C = VICBASE+44
SPRITE_6C = VICBASE+45
SPRITE_7C = VICBASE+46
COLORMEM = $D800
;; SID registers
SIDBASE = $D400
SIDF1H = SIDBASE+1
SIDF1L = SIDBASE+2
SIDCR1 = SIDBASE+4
SIDAD1 = SIDBASE+5
SIDSR1 = SIDBASE+6
SIDFMV = SIDBASE+24
;; Macros
.macro padto addr
.repeat (addr - *)
.byte 0
.endrep
.endmacro

8
enum.asm Normal file
View file

@ -0,0 +1,8 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
.enum mode
StartScreen = 0
MainGame
ScoreHighlight
Win
.endenum

183
gameloop.asm Normal file
View file

@ -0,0 +1,183 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
;;; Main gameloop routine.
game:
lda p1score
ldx #4
jsr printbyte
lda p2score
ldx #(40 - 6)
jsr printbyte
lda col
beq @nocol
jsr handle_collision
@nocol:
;; Player input:
;; F1 (restart)
ldx #%11111110
ldy #%00010000
jsr keydown
bne @norestart
jmp PROGSTART
@norestart:
;; Q (player 1 up)
ldx #%01111111
ldy #%01000000
jsr keydown
bne @skip1
dec p1y
dec p1y
;; A (player 1 down)
@skip1:
ldx #%11111101
ldy #%00000100
jsr keydown
bne @skip2
inc p1y
inc p1y
@skip2:
;; O (player 2 up)
ldx #%11101111
ldy #%01000000
jsr keydown
bne @skip3
dec p2y
dec p2y
@skip3:
;; L (player 2 down)
ldx #%11011111
ldy #%00000100
jsr keydown
bne @skip4
inc p2y
inc p2y
@skip4:
;; Clamp paddles to stay on-screen.
lda #$32 ;player 1 top
cmp p1y
bcc @dontclamp1
sta p1y
@dontclamp1:
lda #($fa - 42) ;player 1 bottom
cmp p1y
bcs @dontclamp2
sta p1y
@dontclamp2:
lda #$32 ;player 2 top
cmp p2y
bcc @dontclamp3
sta p2y
@dontclamp3:
lda #($fa - 42) ;player 2 bottom
cmp p2y
bcs @dontclamp4
sta p2y
@dontclamp4:
update_paddles:
lda p1y
sta SPRITE_0Y
lda p2y
sta SPRITE_1Y
ball_physics:
lda #BALLVERT
and balldata
beq @down ; 0 = down, 1 = up
txa
clc
dec ball_y
lda #BALLVSPEED
bit balldata
beq @slowballup
dec ball_y ;else we have a fast down ball
@slowballup:
;; Top bounce check
lda ball_y
cmp #$32
beq @topbouncestill
bcs @horizontal
@topbouncestill:
;; If it's <= 32 then we bounce off the top
jsr ball_bounce_vert
jmp @horizontal
@down:
inc ball_y
lda #BALLVSPEED
bit balldata
beq @slowballdown
inc ball_y
@slowballdown:
;; Bottom bounce check
lda ball_y
cmp #($fa - 8) ;$fa is bottom of the screen, - 8 for ball height
bcc @horizontal
jsr ball_bounce_vert
;; fallthrough to horizontal
@horizontal:
lda #BALLHORI
and balldata
beq @left ; 0 = left, 1 = right
;; Move ball right
jsr ball_x_inc ;first increment
lda #BALLHSPEED
bit balldata
beq @done
;; if bit BALLHSPEED is set, we have a horizontally fast ball
jsr ball_x_inc ;so we increment again
jmp @done
@left:
;; Move ball left
jsr ball_x_dec ;first decrement
lda #BALLHSPEED
bit balldata
beq @done
;; if bit BALLHSPEED is set, we have a horizontally fast ball
jsr ball_x_dec ;so we dec again
@done:
check_ball_goal:
;; Check to see if it's time to change score and reset the ball
lda #%00000100
bit SPRITE_X_MSB ;check if it's on the left or right side
bne @right
lda ball_x
cmp #$10
bcs @nope
inc p2score
lda p2score
jsr clampscore
sta p2score
jsr reset_ball
jsr highlightscore
jmp @nope
@right:
lda ball_x
cmp #$50
bcc @nope
inc p1score
lda p1score
jsr clampscore
sta p1score
jsr reset_ball
jsr highlightscore
@nope:
ball_sprite:
lda ball_x
sta SPRITE_2X
lda ball_y
sta SPRITE_2Y
;; MAIN GAME ROUTINE ENDS HERE, PUT SUBROUTINES AFTER
rts

73
graphics.asm Normal file
View file

@ -0,0 +1,73 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
SPRITEBASE = $3400
sprite_data:
.repeat 21
.byte $ff, 0, 0 ;defines 8x21 paddle sprite
.endrep
.byte 0
.repeat 8
.byte $ff, 0, 0
.endrep
.repeat 13
.byte 0, 0, 0
.endrep
.byte 0
sprite_data_end:
scrlinelo:
.byte $00
.byte $28
.byte $50
.byte $78
.byte $A0
.byte $C8
.byte $F0
.byte $18
.byte $40
.byte $68
.byte $90
.byte $B8
.byte $E0
.byte $08
.byte $30
.byte $58
.byte $80
.byte $A8
.byte $D0
.byte $F8
.byte $20
.byte $48
.byte $70
.byte $98
.byte $C0
scrlinehi:
.byte $04
.byte $04
.byte $04
.byte $04
.byte $04
.byte $04
.byte $04
.byte $05
.byte $05
.byte $05
.byte $05
.byte $05
.byte $05
.byte $06
.byte $06
.byte $06
.byte $06
.byte $06
.byte $06
.byte $06
.byte $07
.byte $07
.byte $07
.byte $07
.byte $07

215
init.asm Normal file
View file

@ -0,0 +1,215 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
init:
;; Set our custom NMI vector so that pressing restore quits
lda #<nmi
sta $0318
lda #>nmi
sta $0319
;; Reset gamemode
lda #mode::StartScreen
sta gamemode
;; Reset variables
lda #0
sta p1score
sta p2score
sta p1y
sta p2y
sta exit
lda #64
sta ball_x
sta ball_y
sei ;turn off IRQ while setting up
lda #$7f
sta CIA_ICR ;disable CIA IRQ
bit CIA_ICR ;acknowledge it
;; Set up our graphics
ldx #$00
stx BGCOL0 ;black background, appropriate for pong
stx BORDER
jsr loadtitlescreen
lda #%00000111
sta SPRITE_ENABLE ;enable sprite 0,1,2
sta SSCOL
sta SPRITE_BG_PRIO
lda #%00000011
sta SPRITE_XPAND_Y
lda #$01
sta SPRITE_0C
sta SPRITE_1C
sta SPRITE_2C
lda #%00000010
sta SPRITE_X_MSB
lda #$18
sta SPRITE_0X
lda #$50
sta SPRITE_1X
lda #$32
sta SPRITE_0Y
sta SPRITE_1Y
lda #$64
sta SPRITE_2X
sta SPRITE_2Y
;; Load sprites
;; Note: breaks if we have more than 255 bytes of sprite gfx
ldx #(sprite_data_end - sprite_data) ;DANGER
ldy #$00
@loadloop:
lda sprite_data,y
sta SPRITEBASE,y
iny
dex
bne @loadloop
;; done when X is zero
;; lda #<scorestr
;; sta z:zscratch0
;; lda #>scorestr
;; sta z:zscratch1
;; ldx #4
;; jsr printstr
;; Set up our own IRQ routine
lda #<irq
sta CINV
lda #>irq
sta CINV+1
ldx #$0
stx RASTER ;trigger IRQ on scanline 0 (vblank)
ldx #%00000101 ;raster & sprite-sprite col IRQs
stx VICINTMASK ;enable VIC IRQ
cli ;turn IRQ back on, we're ready
idle:
jmp idle ;do nothing forever, game is driven by IRQ
nmi:
brk
clearscreen:
;; Let's clear the background
lda #<VMBASE
sta z:zscratch0
lda #>VMBASE
sta z:zscratch1
lda #<COLORMEM
sta z:zscratch2
lda #>COLORMEM
sta z:zscratch3
ldx #$04
@loopouter:
ldy #$00
@loopinner:
lda #$20
sta (zscratch0),y
lda #$01 ;write the color white
sta (zscratch2),y ;to every part of color RAM
iny
bne @loopinner
inc z:zscratch1 ;increment page
inc z:zscratch3
dex
bne @loopouter
;; Set up sprites
;; sprite ptr $d0 = memory location $3400
ldx #$d0
stx VMBASE+($400 - 8)
stx VMBASE+($400 - 7)
inx
stx VMBASE+($400 - 6)
rts
loadtitlescreen:
jsr clearscreen
lda #<title
sta z:zscratch0
lda #>title
sta z:zscratch1
ldx #$0a
ldy #$0b
jsr printstr
lda #<credits
sta z:zscratch0
lda #>credits
sta z:zscratch1
ldx #$0c
ldy #$0e
jsr printstr
lda #<tutorial2
sta z:zscratch0
lda #>tutorial2
sta z:zscratch1
ldx #$09
ldy #$15
jsr printstr
lda #<tutorial1
sta z:zscratch0
lda #>tutorial1
sta z:zscratch1
ldx #$0a
ldy #$17
jsr printstr
rts
title:
.byte $10, $0F, $0E, $07, $2C, $20, $0D, $0F, $12, $05, $20, $0F, $12, $20, $0C, $05, $13, $13, 0
credits:
.byte $0D, $01, $04, $05, $20, $02, $19, $20, $04, $15, $15, $11, $0E, $04, 0
tutorial1:
.byte $03, $0F, $0E, $14, $12, $0F, $0C, $20, $17, $09, $14, $08, $20, $11, $01, $20, $0F, $0C, 0
tutorial2:
.byte $10, $12, $05, $13, $13, $20, $13, $10, $01, $03, $05, $20, $14, $0F, $20, $13, $14, $01, $12, $14, 0
loadgamefield:
jsr clearscreen
;; now we'll draw the center column
ldx #$25
@lineloop:
lda scrlinelo,x
sta z:zscratch0
lda scrlinehi,x
sta z:zscratch1
txa
and #%00000001
bne @skipdot
lda #103
ldy #20
sta (zscratch0),y
jmp @next
@skipdot:
lda #116
ldy #19
sta (zscratch0),y
@next:
dex
bpl @lineloop
rts

246
main.asm Normal file
View file

@ -0,0 +1,246 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
.include "c64.inc"
.include "enum.asm"
VMBASE = $0400
.org PROGSTART
.include "init.asm" ;must come first
.include "utilities.asm"
;; Start screen, waits for space key to be pressed.
titlescreen:
;; More visually interesting sprite positioning for
;; the title screen. Overrides default from init.
lda #$82
sta SPRITE_0Y
ldx #%01111111
ldy #%00010000
jsr keydown
bne @dontstart
;; Start the game here.
jsr loadgamefield ;set the background
lda #mode::MainGame ;don't forget that #
sta gamemode ;set the game mode to MainGame
@dontstart:
rts
;; Wait and highlight the score change for half a second.
;; Happens every time a player scores or whatever it's called in tennis
scorewait:
;; While waiting we play a buzz
lda #$0f
sta SIDFMV
lda #$08
sta SIDAD1
lda #$68
sta SIDSR1
lda #$45
sta SIDF1L
lda #$1d
sta SIDF1H
lda #$11
sta SIDCR1
dec timer
bne @dontresume
;; Reset gamemode
lda #mode::MainGame
sta gamemode
;; Reset background color
lda #0
sta BGCOL0
;; Stop sound
lda #$10
sta SIDCR1
@dontresume:
rts
;; Call after a player scores to highlight the win
highlightscore:
lda #mode::ScoreHighlight
sta gamemode
lda #25
sta timer
lda #2
sta BGCOL0
rts
.include "gameloop.asm"
;; "Victory" mode when 100 points has been reached.
win:
dec timer
bne @skip
dec timer2
bne @continue
;; After five seconds (10 half-seconds) we reset the game
jmp PROGSTART
@continue:
lda #25
sta timer
lda BGCOL0
eor #8
sta BGCOL0
@skip:
rts
winstop:
lda #25
sta timer
lda #10
sta timer2
lda #$0d
sta BGCOL0
lda #mode::Win ;DO NOT FORGET THE #
sta gamemode
rts
ball_x_inc:
inc ball_x
bne @done
;; If ball_x incremented to zero we have either looped around
;; the screen (shouldn't happen) or crossed the bit 8 boundry.
;; So we set the MSB to one, since we're going right.
lda #%00000100
ora SPRITE_X_MSB
sta SPRITE_X_MSB
@done:
rts
ball_x_dec:
dec ball_x
lda ball_x
cmp #$ff
bne @done
;; If ball_x incremented to zero we have either looped around
;; the screen (shouldn't happen) or crossed the bit 8 boundry.
;; So we set the MSB to one, since we're going right.
lda #%11111011
and SPRITE_X_MSB
sta SPRITE_X_MSB
@done:
rts
ball_bounce_vert:
jsr color_change
lda #%10000000
eor balldata
sta balldata
rts
ball_bounce_hori:
jsr color_change
lda #%01000000
eor balldata
sta balldata
rts
reset_ball:
jsr ball_bounce_hori
lda #$90
sta ball_y
lda #$c0
sta ball_x
lda #%11111011
and SPRITE_X_MSB
sta SPRITE_X_MSB
rts
color_change:
inc bordercol
lda bordercol
and #$0f
cmp #2
bcs @fine
lda #2
sta bordercol
@fine:
lda bordercol
sta BORDER
rts
handle_collision:
jsr ball_bounce_hori ;obvious, flip the ball's horizontal direction
lda #$00
sta col ;clear the collision flag
rts
;; Cheap trick to make a hexprinted number look decimal.
;; Score comes in the A register, clamped score returned in A
clampscore:
tax
and #%00001111
cmp #$0a
bcc @noclamplower
;; yes clamp, inc upper nybble, ex. $19 -> $20
txa
and #%11110000
clc
adc #$10
tax
@noclamplower:
txa
and #%11110000
cmp #$a0
bcc @noclamp
;; over 99? just stop at that point, we'll flash some colors then reset
jsr winstop
pla
pla
@noclamp:
txa
rts
modedispatch:
jsr jumpwithtable
.addr titlescreen
.addr game ;normal gameplay
.addr scorewait
.addr win
;; Main IRQ subroutine. CPU gets sent here by the interrupts from the
;; VIC-II's raster and collision interrupts, and is responsible for
;; calling everything else. Since the game is interrupt-driven, you
;; could add a RTS to the init code and have the game run together
;; with BASIC. Would be pretty stupid but you *can* do it. That does
;; give me an idea though...
irq:
lda SSCOL ;need to do this for collision checking to work
lda #%00000100 ;check if irq is for collision or not
and VICINT
beq @nocol
;; Collision
sta col ;set the collision flag
lda #$7f
and VICINT
sta VICINT
jmp @skipgame
@nocol:
lda gamemode
jsr modedispatch
@skipgame:
lda #$7f
and VICINT
sta VICINT
jmp $ea31
.include "variables.asm"
BALLVERT = %10000000
BALLHORI = %01000000
BALLVSPEED = %00000010
BALLHSPEED = %00000001
.include "graphics.asm"
.include "zeropage.asm"

88
utilities.asm Normal file
View file

@ -0,0 +1,88 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
;; Jumps to a routine via a table. Index goes in A register.
jumpwithtable:
tay
;; Set up pointer to jump table
pla
sta z:zscratch0
pla
sta z:zscratch1
tya
asl
tay
iny
lda (zscratch0),y
sta z:zscratch2
iny
lda (zscratch0),y
sta z:zscratch3
jmp (zscratch2)
printbyte:
inx
jsr printnyb
.repeat 4
lsr
.endrep
dex
jsr printnyb
rts
printnyb:
pha
and #$0f
cmp #$0a
bcc @number ;if nybble<$A then print a number, else letter
sec
sbc #$09 ;letters start at $01, so sub away 9
jmp @done
@number:
clc
adc #$30 ;print a number, starting at $30
@done:
sta VMBASE,x
pla
rts
;; Put string pointer into zscratch1+2
;; X position into X, y pos into Y
printstr:
lda scrlinelo,y
sta z:zscratch2
lda scrlinehi,y
sta z:zscratch3
txa
clc
adc z:zscratch2
sta z:zscratch2
lda z:zscratch3
adc #0
sta z:zscratch3
ldy #$00
@loop:
lda (zscratch0),y
beq @done
sta (zscratch2),y
iny
inx
jmp @loop
@done:
rts
keydown:
lda #$ff
sta DDRA
lda #$00
sta DDRB
stx PRA
tya
and PRB
rts

14
variables.asm Normal file
View file

@ -0,0 +1,14 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
gamemode: .res 1, mode::StartScreen
p1y: .res 1, 0
p1score: .res 1, $90
p2score: .res 1, 0
p2y: .res 1, 0
col: .res 1, 0
balldata: .res 1, %01000011 ;ball starts going right, fast vert & hori
ball_x: .res 1, 64
ball_y: .res 1, 64
bordercol: .res 1, 0
timer: .res 1, 0
timer2: .res 1, 0
exit: .res 1, 0

7
zeropage.asm Normal file
View file

@ -0,0 +1,7 @@
;;; -*- Mode: asm; indent-tabs-mode: t; tab-width: 8 -*-
.zeropage
.org $50
zscratch0: .res 1
zscratch1: .res 1
zscratch2: .res 1
zscratch3: .res 1