; RAM-PONG ; Copyright 2009-2017, Thomas Jentzsch ; ; Version 1.00, 2009-05-10 ; Version 1.01, 2015-02-25 (no major changes) ; Version 0.99, 2015-01-02 (1K version) LIST OFF processor 6502 include "vcs.h" include "macro.h" LIST ON ;=============================================================================== ; A S S E M B L E R - S W I T C H E S ;=============================================================================== VERSION = $0100 BASE_ADR = $fc00 NTSC_TIM = 1 NTSC_COL = 1 DEMO_MODE = 0 ; game doesn't stop ILLEGAL = 1 DEBUG = 0 VERBOSE = 1 ; 1 = more verbose output during assembling ;=============================================================================== ; C O N S T A N T S ;=============================================================================== ; RAM game constants: BK_COL = GREEN FG_COL = $0e IF NTSC_TIM KERNEL_H = 200/2 + 1 ; first two lines are invisible PADDLE_H = 20/2 BALL_H = 4/2 Y_BALL = 82 ; KERNEL_H - x must be odd! ELSE KERNEL_H = 238/2 + 1 PADDLE_H = 20/2 BALL_H = 4/2 Y_BALL = 91 ; KERNEL_H - x must be odd! ENDIF Y_SPEED = <-1 START_RAM = $80 ; title screen constants: NUM_ANGLES = 15 ; 0°..90° in 6° steps TITLE_H = 9 IF NTSC_TIM ROT_SPEED = 160 ELSE ROT_SPEED = 192 ENDIF IF NTSC_COL NUM_COLORS = 7 BROWN = $20 RED = $40 BLUE = $80 GREEN = $c0 ELSE NUM_COLORS = 6 BROWN = $20 RED = $60 BLUE = $d0 GREEN = $50 ENDIF ;=============================================================================== ; Z P - V A R I A B L E S ;=============================================================================== SEG.U variables ORG $80 tmpVar ds 15 side ds 1 angle ds 1 colorIdx ds 1 rotSum ds 1 pattern ds 1 ;=============================================================================== ; M A C R O S ;=============================================================================== DEBUG_BYTES SET 0 MAC DEBUG_BRK IF DEBUG DEBUG_BYTES SET DEBUG_BYTES + 1 brk ; ENDIF ENDM MAC NOP_IMM .byte $82 ENDM MAC BIT_B .byte $24 ENDM MAC BIT_W .byte $2c ENDM MAC SLEEP IF {1} = 1 ECHO "ERROR: SLEEP 1 not allowed !" END ENDIF IF {1} & 1 nop $00 REPEAT ({1}-3)/2 nop REPEND ELSE REPEAT ({1})/2 nop REPEND ENDIF ENDM MAC CHECKPAGE IF >. != >{1} ECHO "" ECHO "ERROR: different pages! (", {1}, ",", ., ")" ECHO "" ERR ENDIF ENDM MAC ALIGN_FREE LIST OFF _FREE SET _FREE - . align {1} _FREE SET _FREE + . IF VERBOSE ECHO "INFO: ALIGN_FREE", {1}, "@", ., ":", (_FREE)d ENDIF LIST ON ENDM MAC ORG_FREE _FREE SET _FREE - . org {1}, 0 _FREE SET _FREE + {1} IF VERBOSE ECHO "INFO: ORG_FREE ", {1}, "@", ., ":", (_FREE)d ENDIF ENDM ;=============================================================================== ; R O M - C O D E ;=============================================================================== SEG Bank0 ORG BASE_ADR _FREE SET 0 ;--------------------------------------------------------------- ; RAM code start ;--------------------------------------------------------------- startRAM: RORG START_RAM DELTA = startRAM - . yPosBall .byte Y_BALL ySpeedBall .byte Y_SPEED ; +/-0..3 y-pixel/frame yPaddleLst ds 2 yPaddle0 = yPaddleLst yPaddle1 = yPaddleLst+1 MainRAMLoop: ; @08..14 ; OverScan SUBROUTINE sty VBLANK ; 3 = 3 disable display ; check for game over: txa ; 2 a, x = 0; y = $ff bit CXM1FB ; 3 net at left or right border? IF DEMO_MODE ldy #<-1 ; 2 waste 2 bytes and cycles ELSE bvs .skipGamePlay ; 2/3= 7/8 ENDIF ; check paddle bouncing: bit CXM0P ; 3 paddle hit? bvs .paddle0 ; 2/3 yes, paddle 0 hit bpl .skipBounceX ; 2/3 no, no paddle hit inx ; 2 paddle 1 hit .paddle0: ; = 6/9 lda yPosBall ; 3 KERNEL_H - x must be odd! hPaddle0 = . + 1 adc #PADDLE_H/2+BALL_H*2; 2 sbc yPaddleLst,x ; 4 cmp #$80 ; 2 ror ; 2 sta ySpeedBall ; 3 eorSpeed = . + 1 lda #$c0 ; 2 xSpeedBall = . + 1 ; +/-2/3 x-pixel/frame eor #$ef ; 2 = 20 also enable audio 0 .skipGamePlay: sta xSpeedBall ; 3 sta HMM0 ; 3 = 6 .skipBounceX: ; total: 17..45 ; make paddle noise: sta AUDV0 ; 3 = 3 a = $00/$xf ; check missed ball: bit CXM0FB ; 3 left/right border hit? bvc .skipBorder ; 2/3 ldx xSpeedBall ; 3 also enable audio 1 .skipBorder: ; ; move net left or right: stx HMM1 ; 3 = 9/11 y = $00/$xf ; check vertical bouncing: pla ; 4 lda yPosBall cmp #KERNEL_H-BALL_H-1 ; 2 pla ; 4 lda ySpeedBall bcc .skipBounceY ; 2/3 dex ; 2 enable audio 1 eor #$ff ; 2 .skipBounceY; adc #0 ; 2 pha ; 3 sta ySpeedBall ; update vertical ball position: adc yPosBall ; 3 pha ; 3 = 24/27 sta yPosBall ; make border hit noise: stx AUDV1 ; 3 = 3 y = $00/$xf ; time overscan, v-sync and v-blank: IF NTSC_TIM ldx #58|$80 ; 2 ELSE ldx #70|$80 ; 2 = 2 ENDIF EnterRAMLoop: .waitFrame: txa ; 2 IF NTSC_TIM sbc #31|$80 ; 2 ELSE sbc #37|$80 ; 2 ENDIF adc #3 ; 2 rol ; 2 asl ; 2 sta VSYNC ; 3 dex ; 2 sta WSYNC ; 3 = 18 @10.. ;--------------------------------------- bmi .waitFrame ; 2/3 x = $7f at loop end sta HMOVE ; 3 sta CXCLR ; 3 ; DrawScreen SUBROUTINE ldy #KERNEL_H-1 ; 2 .loopKernel: ; @05..11 ; draw ball (1/2): tya ; 2 sbc yPosBall ; 3 C = 0! sta WSYNC ; 3 = 8 ;--------------------------------------- stx VBLANK ; 3 disabled in 2nd loop, loads yPaddleLst ; draw ball (2/2): sbc #BALL_H ; 2 txa ; 2 x = 0 adc #$03 ; 2 sta ENAM0 ; 3 sty ENAM1 ; 3 draw dotted net ldx #2 ; 2 = 17 .loopPlayer: dex ; 2 = 2 ; check paddles: lda INPT0,x ; 4 paddle pos reached? bmi .skipPos ; 2/3 no, skip sty yPaddleLst,x ; 4 .skipPos: ; = 7/10 ; draw paddles: tya ; 2 sec ; 2 sbc yPaddleLst,x ; 4 hPaddle1 = . + 1 adc #PADDLE_H ; 2 txa ; 2 ror ; 2 a = $00/$80 sta GRP0,x ; 4 = 18 ´ @44/74 (>=72) ; loop player: bcs .loopPlayer ; 2/3= 2/3 dey ; 2 bpl .loopKernel ; 2/3= 5 bmi MainRAMLoop ; 3 @08..14 RAM_SIZE = . - START_RAM IF RAM_SIZE > $80 echo "Error: Out of RAM memory!", RAM_SIZE, "bytes" ERR ENDIF ;--------------------------------------------------------------- ; RAM code end ;--------------------------------------------------------------- ; ORG . + DELTA RORG . + DELTA echo "Start:", . SetXPos SUBROUTINE dec $fe ; 5 adjust return address (fixed!) sec sta WSYNC WaitObject: sbc #$0f ; 2 bcs WaitObject ; 2³ CHECKPAGE WaitObject eor #$07 ; 2 asl ; 2 asl ; 2 asl ; 2 asl ; 2 sta HMP0,x ; 4 sta.wx RESP0,x ; 5 @23! dex rti ; SetXPos Start SUBROUTINE cld ; Clear BCD math bit. lda #0 tax dex txs .clearLoop: tsx pha bne .clearLoop ; *** Game init *** lda #FG_COL sta COLUP0 sta COLUP1 MainLoop: ; *** vertical blank *** lda #%00001110 .loopVSync: sta WSYNC sta VSYNC lsr bne .loopVSync IF NTSC_TIM lda #38 ELSE lda #45 ENDIF sta TIM64T ; *** GameCalc *** lda rotSum clc adc #ROT_SPEED sta rotSum bcc .skipAngle ; *** handle rotation: *** ldy angle lda side bmi .frontSide ; angle = 1..15 iny cpy #NUM_ANGLES bcc .setAngle ora #$80 ; reverse direction bne .setSide .frontSide: ; angle = 14..0 eor #$c0 ; reverse direction and side dey bne .setAngle cmp #$00 bne .skipColor ldx colorIdx inx cpx #NUM_COLORS bcc .skipResetIdx tax ; ldx #0 .skipResetIdx stx colorIdx .skipColor: .setSide: sta side .setAngle: sty angle .skipAngle: ; jsr DrawScreen DrawScreen SUBROUTINE .endY = tmpVar .color = tmpVar+1 .addVal = tmpVar+2 .sum = tmpVar+3 .addVal2 = tmpVar+4 .addVal3 = tmpVar+5 .sum23 = tmpVar+6 .topLoop = tmpVar+7 .btmLoop = tmpVar+8 ;--------------------------------------------------------------- ; position menu option sprites: ldx #1 lda #59 brk lda #74 brk IF NTSC_TIM ldx #228 ELSE ldx #255 ENDIF .waitTim: lda INTIM bne .waitTim sta WSYNC sta HMOVE sta VBLANK stx TIM64T ;--------------------------------------------------------------- ldy angle lda TopTbl+NUM_ANGLES+1,y ldx BtmTbl+NUM_ANGLES+1,y .posDir3: sta .topLoop stx .btmLoop lda AddTbl,y sta .addVal lsr sta .sum ; invert angle: tya eor #$0f tax ; setup top and bottom values: lda #0 sta .addVal2 sta .addVal3 lda AddTbl,x ldx .addVal ; always draw at least the side border beq .skipTop bit side bpl .skipTop sta .addVal2 .byte $0c .skipTop: sta .addVal3 lsr sta .sum23 ; center display: ldx DelayTbl,y .waitCenter: dex sta WSYNC bne .waitCenter tya ; beq .endDrawJmp bne .contKernel .endDrawJmp jmp .endDraw ; ALIGN_FREE 256 .contKernel: bit side ; ldx #0 ldy #TITLE_H-1 bvc .posDir1 ldy #TITLE_H*(6+1)-1 ldx #TITLE_H*6 .posDir1: stx .endY ldx colorIdx lda ColorTbl,x bvc .posDir2 lda ColorTbl+NUM_COLORS,x .posDir2: sta .color ; draw top border: lda .addVal2 ; 3 beq .exitLoop2 ; 2³ ldx #$00 ; 2 .loopLine2: sta WSYNC ; 3 ;--------------------------------------- lda #0 sta COLUBK stx COLUPF ; 3 lda #$ff sta PF0 ; 3 @75 sta PF1 ; 3 @75 sta PF2 ; 3 @75 lda .sum23 ; 3 adc .addVal2 ; 3 sta .sum23 ; 3 = 9 @42 bcs .exitLoop2 ; 2³ inx ; 2 inx ; 2 bcc .loopLine2 ; 3³= 9 @65 .exitLoop2: ldx .topLoop beq .skipTopLoop .loopTop: sta WSYNC lda #0 sta COLUBK sta PF0 sta PF1 sta PF2 dex bne .loopTop .skipTopLoop: ; draw main title: .loopBlock: ; @53 cpy .endY ; 3 beq .endLoop ; 2³ dey ; 2 ldx .color ; 3 lda #0 sta COLUBK .loopLine: lda TitlePat0L,y ; 4 sta WSYNC ; 3 = 3 @68/76 ;--------------------------------------- stx COLUPF ; 3 = 3 @03 sta PF0 ; 3 lda TitlePat1L,y ; 4 sta PF1 ; 3 @13 lda TitlePat2L,y ; 4 sta PF2 ; 3 = 21 @20 ; nop ; 2 lda TitlePat0R,y ; 4 sta PF0 ; 3 = 7 @27 lda .sum ; 3 adc .addVal ; 3 sta .sum ; 3 = 9 @36 lda TitlePat1R,y ; 4 sta PF1 ; 3 @43 lda TitlePat2R,y ; 4 sta PF2 ; 3 = 14 @50 bcs .loopBlock ; 2³ inx ; 2 inx ; 2 bcc .loopLine ; 3 = 9 @59 ;--------------------------------------------------------------- .endLoop: ; @59 ldx .btmLoop ; 3 beq .skiploopBtm ; 2³ .loopBtm: sta WSYNC lda #0 sta PF0 sta PF1 sta PF2 dex bne .loopBtm .endDraw: ; ; draw bottom border: ldx #$00 ; 2 .skiploopBtm; lda .addVal3 ; 3 beq .exitLoop3 ; 2³ .loopLine3: sta WSYNC ; 3 ;--------------------------------------- stx COLUPF ; 3 lda #$ff sta PF0 ; 3 sta PF1 ; 3 sta PF2 ; 3 lda .sum23 ; 3 adc .addVal3 ; 3 sta .sum23 ; 3 = 9 @42 bcs .exitLoop3 ; 2³ inx ; 2 inx ; 2 bcc .loopLine3 ; 3³= 9 @65 .exitLoop3: sta WSYNC lda #BROWN;GREEN+2 sta COLUBK ldy #0 ; 2 sty PF0 sty PF1 sty PF2 sty COLUPF ; 3 .waitMiddle: lda INTIM IF NTSC_TIM eor #58+21+5 ELSE eor #58+24+10-8 ENDIF bne .waitMiddle sta WSYNC ; display some simplyfied options: tay lda #$08 bit SWCHB beq .colorSW ldy #GREEN .colorSW: sty COLUPF lda #1 sta CTRLPF asl sta NUSIZ1 ldy #15 .loopOptions lda #%10110000 bit SWCHB bpl .slow lda #%10010011 .slow: cpy #7 beq .drawBall lda #0 .drawBall ldx #0 bvc .large cpy #9 bcs .drawPaddle cpy #7 bcc .drawPaddle bcs .showPaddle .large cpy #10 bcs .drawPaddle cpy #6 bcc .drawPaddle .showPaddle ldx #%1100 .drawPaddle ;--------------------------------------- sta WSYNC stx GRP1 sta GRP0 lda #%11111000 sta PF2 dey sta WSYNC sta WSYNC sta WSYNC bne .loopOptions sty PF2 sty CTRLPF .waitScreen: lda INTIM bne .waitScreen ; DrawScreen IF NTSC_TIM = 0 ldx #15 .loopWait0: sta WSYNC dex bne .loopWait0 ENDIF ldy #2 sta WSYNC sty VBLANK ; lsr SWCHB ; bcc .startGame bit SWCHA bpl .startGame bvc .startGame IF NTSC_TIM ldx #7 ELSE ldx #7 ENDIF .waitBottom: dex sta WSYNC bne .waitBottom ; *** OverScan *** IF NTSC_TIM lda #33 ELSE lda #40 ENDIF sta TIM64T .waitOverScan: lda INTIM bne .waitOverScan jmp MainLoop .startGame ; prepare to start the RAM game: ; position invisible left and right borders (BL): ldx #4 lda #159-2;0;159 ; x = 159 brk ; position middle line with M1 lda #80 ; x = 79 brk ; position ball (M0) at start: lda #45 brk ; position right paddle (P1): ; 2 pixel M0 (ball), double/quad width P0 (left paddle): ldy #%000101 lda #157-14 bit SWCHB bpl .amateur1 ldy #%000111 .amateur1: sty NUSIZ1 brk ; position left paddle (P0): ; 2 pixel M0 (ball), double/quad width P0 (left paddle): ldy #%010101 lda #-1+14 bit SWCHB bpl .amateur0 ldy #%010111 lda #-1+14-2 .amateur0: sty NUSIZ0 brk sta WSYNC sta HMOVE ldy #2 sty VBLANK ; sta WSYNC lda SWCHB and #$08 beq .noColor lda #BK_COL .noColor: sta COLUBK ; lda #BK_COL+4 ; sta COLUPF ; lda #FG_COL ; sta COLUP0 ; sta COLUP1 ; 8 pixel BL for left/right borders lda #%110001 sta CTRLPF ; lda #1 sta VDELP1 ; sound data copied from Video Olympics: lda #$04 sta AUDC0 ldx #$15 stx AUDF0 lda #$ec sta AUDC1 ; ldx #$15 stx AUDF1 ; enable ball, net, left and right borders: inx stx ENABL stx ENAM0 stx ENAM1 sta HMCLR ; RAMGameInit ldx #RAM_SIZE .copyRAM: lda START_RAM + DELTA - 1,x sta START_RAM - 1,x dex bne .copyRAM ; check right difficulty switch for game speed: bit SWCHB bpl .slowSpeed inx .slowSpeed lda SpeedTbl,x sta xSpeedBall ; enable initial horizontal movement sta HMM0 lda EorTbl,x sta eorSpeed ; check left difficulty switch for paddle height: lda #PADDLE_H ; 10 bvc .widePaddles lda #PADDLE_H-4 ; 6 .widePaddles sta hPaddle1 ; lsr ; 5/3 adc #BALL_H*2 ; 4 sta hPaddle0 ; 9/7 5+4, 3+4 ; adjust stack pointer for variable access: ldx #yPosBall-1 txs ; initial waiting to keep frame in sync: IF NTSC_TIM ldx #32|$80 ELSE ldx #44|$80 ENDIF DEBUG1 jmp EnterRAMLoop SpeedTbl: .byte $ef, $df EorTbl: .byte $c0, $e0 ;=============================================================================== ; R O M - T A B L E S (Bank 0) ;=============================================================================== ; ORG_FREE BASE_ADR + $700 ; RORG BASE_ADR + $700 IF NTSC_TIM TH = 20 ELSE TH = 24*6/5 ENDIF DelayTbl: ; TITLE_H*256 = 2560: -> (TITLE_H*8-(2560-AddTbl/2)AddTbl)/2 .byte 40+TH, 35+TH, 32+TH, 28+TH, 24+TH, 20+TH, 17+TH, 14+TH .byte 11+TH, 8+TH, 6+TH, 4+TH, 3+TH, 1+TH .byte 0+TH, 0+TH AddTbl: ; 0..84°: .byte 0,255,154,104, 79, 64, 54+1, 48 .byte 43, 40, 37, 35, 34, 33 .byte 32, 32 TopTbl: .byte 0, 0, 1, 1, 1, 2, 2, 2 .byte 3, 3, 3, 3, 4, 4, 4, 4 .byte 0, 0, 1, 2, 3, 4, 4, 5 .byte 6, 6, 7, 7, 7, 8, 8, 8 BtmTbl: .byte 0, 1-1, 1-1, 1-1, 2-1, 2-1, 3-1, 3-1 .byte 3-1, 3-1, 4-1, 4-1, 4-1, 4-1, 4-1, 4-1 .byte 0, 1-1, 2-1, 3-1, 4-1, 4-1, 5-1, 6-1 .byte 6-1, 7-1, 7-1, 8-1, 8-1, 8-1, 8-1, 8-1 ColorTbl: IF NTSC_COL .byte $f0, $20, $40, $60, $50, $30, $10 .byte $70, $90, $b0, $d0, $c0, $a0, $80 ELSE .byte $20, $60, $a0, $c0, $80, $40 .byte $90, $50, $30, $70, $b0, $d0 ENDIF ; RAM TitlePat0L: .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte 0 TitlePat1L: .byte %00001100 .byte %00001100 .byte %00001111 .byte %01101111 .byte %01101100 .byte %00001100 .byte %00001111 .byte %00001111 .byte 0 TitlePat2L: .byte %10011011 .byte %10011011 .byte %11111001 .byte %11111011 .byte %10011011 .byte %10011011 .byte %11111011 .byte %11110001 .byte 0 TitlePat0R: .byte %11010000 .byte %11010000 .byte %11010000 .byte %11010000 .byte %11010000 .byte %11010000 .byte %11010000 .byte %10000000 .byte 0 TitlePat1R: .byte %00110011 .byte %00110011 .byte %00110011 .byte %00110011 .byte %00110011 .byte %00110011 .byte %11111111 .byte %11001110 .byte 0 TitlePat2R: .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000110 .byte %00000110 .byte %00000000 .byte %00000000 .byte %00000000 .byte 0 ; PONG TitlePat0La: .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte 0 TitlePat1La: .byte %01100000 .byte %01100000 .byte %01111100 .byte %01111110 .byte %01100110 .byte %01100110 .byte %01111110 .byte %01111100 .byte 0 TitlePat2La: .byte %00111100 .byte %01111110 .byte %11100111 .byte %11000011 .byte %11000011 .byte %11100111 .byte %01111110 .byte %00111100 .byte 0 TitlePat0Ra: .byte %01100000 .byte %01100000 .byte %01100000 .byte %01100000 .byte %01100000 .byte %01100000 .byte %11100000 .byte %11000000 .byte 0 TitlePat1Ra: .byte %01100011 .byte %01100111 .byte %01101110 .byte %01101100 .byte %01101100 .byte %01101110 .byte %11100111 .byte %11000011 .byte 0 TitlePat2Ra: .byte %00000111 .byte %00000111 .byte %00000110 .byte %00000111 .byte %00000111 .byte %00000000 .byte %00000111 .byte %00000111 ; .byte " RAM-PONG v1.01 - (C) 2015 Thomas Jentzsch " .byte "JTZ" ORG_FREE BASE_ADR + $3fc .word Start .word SetXPos ;VERSION echo "*** Free RAM:", ($80 - RAM_SIZE)d, "bytes" ECHO "*** Free ROM:", (_FREE)d, "bytes"