.cpu "6502" ;; VIC colours BLACK=$0 WHITE=$1 RED=$2 CYAN=$3 PURPLE=$4 GREEN=$5 BLUE=$6 YELLOW=$7 ORANGE=$8 BROWN=$9 LRED=$A GREY1=$B GREY2=$C LGREEN=$D LBLUE=$E GREY3=$F ;; Screen memory (almost 4 pages) SCREEN=$0400 ;; Colour memory (almost 4 pages) COLOURS=$D800 ;; Frame and Background colour FRMCOL=$D020 BGCOL=$D021 ;; Joystick ports JOY1=$DC01 JOY2=$DC00 ;; Screen dimensions NUMLINES=25 NUMCOLS=40 ;; Some macros to abbreviate some operations ; move word movw .macro lda \2 sta \1 lda \2+1 sta \1+1 .endm ; move immediate word movwi .macro lda #<\2 sta \1 lda #>\2 sta \1+1 .endm ; move immediate byte movbi .macro lda #\2 sta \1 .endm ; add 8 bit immediate to 16 bit word addwbi .macro clc lda \1 adc #\2 sta \1 lda \1+1 adc #0 sta \1+1 .endm ; add 8 bit value to 16 bit word addwb .macro clc lda \1 adc \2 sta \1 lda \1+1 adc #0 sta \1+1 .endm ; sub 8 bit immediate from 16 bit word subwbi .macro sec lda \1 sbc #\2 sta \1 lda \1+1 sbc #0 sta \1+1 .endm ; sub 8 bit value from 16 bit word subwb .macro sec lda \1 sbc \2 sta \1 lda \1+1 sbc #0 sta \1+1 .endm ;; Zero page definitions *=$0 ;; temporaries A: .byte ? .byte ? ; for some reason P doesn't like to be at 1???? P: .word ? ;; Player data P1a: .word ? ; address of player 1's paddle P2a: .word ? P1p: .byte ? ; Y position of paddle P2p: .byte ? ;; Ball data ballA: .word ? ; address of ball ballLastA: .word ? ; last address of ball so we can erase ballX: .byte ? ; X position ballY: .byte ? ; Y position ballVX: .byte ? ; X speed ballVY: .byte ? ; Y speed *=$C000 start: ; init stack ldx $0 txs ; set some nice colours movbi BGCOL,BLACK movbi FRMCOL,GREY1 lda #WHITE jsr fillcolours ; clear screen lda #$20 jsr fillscreen ; init players movwi P1a,SCREEN+1 movbi P1p,0 movwi P2a,SCREEN+NUMCOLS-2 movbi P2p,0 ; init ball speed lda #1 sta ballVX sta ballVY ; draw initial paddles lda #72 ldx #0 jsr drawpaddle lda #71 ldx #$2 jsr drawpaddle ; Into the game! jmp newgame ; wait a little while for each frame. ; TODO: wait for vsync wait: ldx #$FF - ldy #$20 - dey bne - dex bne -- rts newgame: ; this is jumped into when we should be returning from a subroutine ; so just reset the stack here ldx $0 txs jsr drawball ; wait a bit between turns jsr wait jsr wait jsr wait ; set initial ball position (and pointer) ; speed is unchanged! movwi ballA,SCREEN+12*NUMCOLS+20 movbi ballX,20 movbi ballY,12 ; fallthrough ;; Game loop. Process ball, then paddles mainloop: jsr wait jsr ball lda JOY1 lsr bcc joy1up lsr bcc joy1down gamep2: lda JOY2 lsr bcc joy2up lsr bcc joy2down jmp mainloop ;; Handle player 1 input joy1up: ldx P1p beq gamep2 ; paddle can't go up, handle player 2 dex stx P1p ; decrement position ldx #0 jsr clearpaddle ; delete old paddle before drawing new ldx #0 jsr moveup ; move the pointer as well jmp paddle1 ; and draw the left paddle there joy1down: ldx P1p cpx #20 beq gamep2 ; paddle can't go down,handle player 2 inx stx P1p ; increment position ldx #0 jsr clearpaddle ldx #0 jsr movedown ; move the pointer down too paddle1: lda #72 ; left paddle character ldx #0 jsr drawpaddle jmp gamep2 ;; Handle player 2 input, same as above joy2up: ldx P2p ; load position beq mainloop ; can't go up dex stx P2p ldx #2 jsr clearpaddle ldx #2 jsr moveup jmp paddle2 joy2down: ldx P2p cpx #20 beq mainloop inx stx P2p ldx #2 jsr clearpaddle ldx #2 jsr movedown paddle2: lda #71 ; right paddle character ldx #$2 jsr drawpaddle jmp mainloop ;; draw a paddle with spaces to delete it clearpaddle: lda #$20 jmp drawpaddle ;; Move up a paddle pointer moveup: sec lda P1a,X sbc #NUMCOLS sta P1a,X lda P1a+1,X sbc #0 sta P1a+1,X rts ;; Move down a paddle pointer movedown: clc lda P1a,X adc #NUMCOLS sta P1a,X lda P1a+1,X adc #0 sta P1a+1,X rts ;; Draw a paddle with char in ACC drawpaddle: sta A lda P1a,X sta P lda P1a+1,X sta P+1 ldy #0 ldx #5 - lda A sta (P),Y addwbi P,NUMCOLS dex bne - rts ;; Process the ball ball: ;; First move vertically lda #NUMCOLS ldx ballVY jsr ballptr ; move the ball pointer vertically clc lda ballY adc ballVY sta ballY ; and the position ;; Now test collision with top or bottom wall ;; depending on direction we're moving lda ballVY bmi ballUp ballDown: lda ballY cmp #NUMLINES-1 bcs clampBottom ; branch if ACC >= jmp ballHoriz ballUp: ldx ballY dex bmi clampTop ; fallthrough ballHoriz: ;; Next move horizontally ldx ballVX lda #1 jsr ballptr ; move the ball pointer horizontally clc lda ballX adc ballVX sta ballX ; and the position ;; Test collision. ;; (NB: since x speed is always 1 or -1 we will never go beyond the paddle!!) ;; If we hit a paddle, reflect. ;; Otherwise, player loses. This is a bit simplistic perhaps lda ballVX bmi ballLeft ballRight: lda ballX cmp #NUMCOLS-3 beq hitright jmp drawball flipX: sec lda #0 sbc ballVX sta ballVX jmp drawball ballLeft: lda ballX cmp #2 beq hitleft ;; Now to draw the ball drawball: ldy #0 lda #$20 sta (ballLastA),Y ; erase the old ball lda #81 sta (ballA),Y ; draw the new ball movw ballLastA,ballA rts ;; We hit a wall, maybe even went beyond it. ;; So reposition the ball to be inside the drawing area ;; and negate its Y speed clampTop: ;; set ball Y to 0, don't change X movbi ballY,0 ;; and also set the pointer clc lda #SCREEN sta ballA+1 jmp flipY clampBottom: ;; same as above but set Y to NUMLINES-1 movbi ballY,NUMLINES-1 clc lda #SCREEN+(NUMLINES-1)*NUMCOLS sta ballA+1 ; fallthrough ;; negate Y speed cause we hit a wall flipY: sec lda #0 sbc ballVY sta ballVY jmp ballHoriz ;; Check if we hit the left or right paddle hitleft: ldx #0 jmp hitpaddle hitright: ldx #1 jmp hitpaddle ;; Y speed for each paddle position speedY: .char -2, -1, 0, 1, 2 ;; Check if he hit a given paddle hitpaddle: lda ballY sec sbc P1p,X ; subtract paddle Y bmi lose ; ball above paddle cmp #5 bpl lose ; below paddle ; Paddle hit the ball, figure out new Y speed ; flip X tax lda speedY,X sta ballVY jmp flipX lose: jmp newgame ; Move the ball pointer by repeated addition/subtraction ; A: value to add, X: multiplier ballptr: stx A ldx A ; get flags sta A bmi ballsub beq + balladd: addwb ballA,A dex bne balladd rts ballsub: subwb ballA,A inx bne ballsub + rts ;; Fill screen with char in ACC fillscreen: ldx #$00 - sta SCREEN,X sta SCREEN+$100,X sta SCREEN+$200,X sta SCREEN+$300,X inx bne - rts ;; Fill colours with colour in ACC fillcolours: ldx #$00 - sta COLOURS,X sta COLOURS+$100,X sta COLOURS+$200,X sta COLOURS+$300,X inx bne - rts