processor 6502 include "vcs.h" include "macro.h" ARENA_HEIGHT = 80 SEG.U vars ORG $80 ; DoDraw Graphic Pointers in $87-8a Player0Ptr: ds 2 ; used for drawing player0 Player1Ptr: ds 2 ; used for drawing player1 Player0Draw: ds 1 ; used for drawing player0 Player1Draw: ds 1 ; used for drawing player1 Color0Ptr: ds 2 ; used for coloring player0 DGS33 Color0: ds 1 ; used for coloring player0 DGS33 OverscanValue: ds 1 Frame: ds 1 ; stored in $9C Score: ds 2 ; stored in $80-81 ScoreTimer: ds 1 Temp ds 1 Rand ds 1 Lives ds 1 LivesDisplay ds 5 ScoreDisplayLeft ds 5 ScoreDisplayRight ds 5 LiveColor ds 1 PlayfieldColor ds 1 Rand16 ds 1 SpriteXPosition ds 1 PlayerX ds 2 ; x coordinate PlayerY ds 2 FrancisFeet ds 1 MissileFired ds 1 Missile1X ds 1 SEG code ORG $F800 Random: lda Rand lsr ifconst Rand16 rol Rand16 endif bcc noeor eor #$B4 noeor sta Rand ifconst Rand16 eor Rand16 endif rts Reset ; Clear RAM and all TIA registers CLEAN_START lda #80 sta PlayerX lda #40 sta PlayerY lda #11 sta OverscanValue GameLoop: lda INPT4 ; read the player's fire button value bmi IHateThis ldx MissileFired cpx #1 bcs IHateThis lda PlayerX sta Missile1X lda #1 sta MissileFired lda Missile1X ldx #3 jsr PosPlayer ;Strobe HMOVE sta WSYNC sta HMOVE ;Wait a scanline and strobe HMCLR ;You must not strobe HMCLR until at least 23 cycles after HMOVE or positions will be wrong sta WSYNC sta HMCLR lda PlayerY adc #240 sta FrancisFeet lda #$E0 sta HMM1 IHateThis lda #14 sta COLUPF sta PlayfieldColor LDA SWCHB ; Check console switches AND #$08 ; Black & white mode? BEQ BlackWhite ; Y: B/W colors lda #$9C sta COLUBK jmp GameLoop1 BlackWhite: lda #10 sta COLUBK GameLoop1: jsr VerticalSync jsr VerticalBlank jsr Kernel jsr Overscan1 jmp GameLoop VerticalSync: lda #$82 ; DGS - LoaD Accumulator with $82 so D7=1 and D1=1 ldx #48 ; LoaD X with 49 sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) sta VSYNC ; Accumulator D1=1, turns on Vertical Sync signal sta VBLANK ; DGS - turn off video, dump paddles to ground stx TIM64T ; set timer to go off in 41 scanlines (49 * 64) / 76 ;sta CTRLPF ; D1=1, playfield now in SCORE mode lda Frame and #$3f bne VSskip VSskip: inc Frame ; increment Frame count sta WSYNC ; Wait for Sync - halts CPU until end of 1st scanline of VSYNC lda #$30 ; sta NUSIZ0 ; set missile0 to be 8x sta NUSIZ1 ; set missile1 to be 8x sta WSYNC ; wait until end of 2nd scanline of VSYNC lda #0 ; LoaD Accumulator with 0 so D1=0 sta PF0 ; blank the playfield sta PF1 ; blank the playfield sta PF2 ; blank the playfield sta GRP0 ; blanks player0 if VDELP0 was off sta GRP1 ; blanks player0 if VDELP0 was on, player1 if VDELP1 was off sta GRP0 ; blanks player1 if VDELP1 was on sta VDELP0 ; turn off Vertical Delay sta VDELP1 ; turn off Vertical Delay sta CXCLR ; clear collision detection latches sta WSYNC ; wait until end of 3rd scanline of VSYNC sta VSYNC ; Accumulator D1=0, turns off Vertical Sync signal Sleep12: ; jsr here to sleep for 12 cycles rts ; ReTurn from Subroutine VerticalBlank: jsr Random jsr Joystick PositionPlayers: ldx #1 ; position players 0 and 1 POloop2: lda #(ARENA_HEIGHT + Francis_HEIGHT) sec sbc PlayerY sta Player0Draw lda PlayerX,x ; get the Player's X position jsr PosPlayer ; set coarse X position and fine-tune amount dex ; DEcrement X bpl POloop2 ; Branch PLus so we position all Players sta WSYNC ; wait for end of scanline sta HMOVE ; Tell TIA to use fine-tune values to set final X positions ; Player0Draw = ARENA_HEIGHT + HUMAN_HEIGHT - Y_position lda #<(FrancisGFX + Francis_HEIGHT - 1) sec sbc PlayerY sta Player0Ptr lda #>(FrancisGFX + Francis_HEIGHT - 1) sbc #0 sta Player0Ptr+1 jsr DrawFrancisColor jmp POLoop4 DrawFrancisColor LDA SWCHB ; Check console switches AND #$08 ; Black & white mode? BEQ DrawFrancisInBW ; Y: B/W colors lda #<(FrancisColors + Francis_HEIGHT - 1) ; DGS33 sec ; DGS33 sbc PlayerY ; DGS33 sta Color0Ptr ; DGS33 lda #>(FrancisColors + Francis_HEIGHT - 1) ; DGS33 sbc #0 ; DGS33 sta Color0Ptr+1 ; DGS33 rts DrawFrancisInBW lda #<(FrancisBWColors + Francis_HEIGHT - 1) ; DGS33 sec ; DGS33 sbc PlayerY ; DGS33 sta Color0Ptr ; DGS33 lda #>(FrancisBWColors + Francis_HEIGHT - 1) ; DGS33 sbc #0 ; DGS33 sta Color0Ptr+1 ; DGS33 rts POLoop4: ; Player1Draw = ARENA_HEIGHT + BOX_HEIGHT - Y_position lda #(ARENA_HEIGHT + AnotherObject_HEIGHT) sec sbc PlayerY+1 sta Player1Draw ; Set Player1Ptr to proper value for drawing player1 lda #<(AnotherObjectGfx + AnotherObject_HEIGHT - 1) sec sbc PlayerY+1 sta Player1Ptr lda #>(AnotherObjectGfx + AnotherObject_HEIGHT - 1) sbc #0 sta Player1Ptr+1 ldx #4 PSFDloop: lda Lives ;Lives digit and #$0F sta Temp asl asl adc Temp sta Temp txa adc Temp tay lda DigitFlippedGfx,y and #$F0 sta LivesDisplay,x lda Score ; thousandths digit and #$F0 lsr lsr sta Temp lsr lsr adc Temp sta Temp txa adc Temp tay lda DigitFlippedGfx,y and #$0F sta ScoreDisplayLeft,x lda Score ;hundredths digit and #$0F sta Temp asl asl adc Temp sta Temp txa adc Temp tay lda DigitFlippedGfx,y and #$F0 ora ScoreDisplayLeft,x sta ScoreDisplayLeft,x lda Score+1 ; tenths digit and #$F0 lsr lsr sta Temp lsr lsr adc Temp sta Temp txa adc Temp tay lda DigitGfx,y and #$F0 sta ScoreDisplayRight,x lda Score+1 ;ones digit and #$0F sta Temp asl asl adc Temp sta Temp txa adc Temp tay lda DigitGfx,y and #$0F ora ScoreDisplayRight,x sta ScoreDisplayRight,x dex bpl PSFDloop2 rts PSFDloop2: ; this is being dumb. jmp PSFDloop Kernel: sta WSYNC ; Wait for SYNC (halts CPU until end of scanline) ;--------------------------------------- lda INTIM ; 4 4 - check the timer bne Kernel ; 2 6 - (3 7) Branch if its Not Equal to 0 ; turn on the display sta VBLANK ; 3 9 - Accumulator D1=0, turns off Vertical Blank signal (image output on) lda #$11 ; 2 11 sta CTRLPF ; 3 14 - turn on playfield mirroring ldx #0 ScoreLoop2: sta WSYNC ;--------------------------------------- lda LivesDisplay,x ; 4 4 sta PF0 ; 3 7 - update PF0 for left side of screen lda LiveColor ; 2 9 sta COLUPF ; 3 12 lda ScoreDisplayLeft,x ; 4 16 sta PF2 ; 3 19 - update PF2 for left side of screen jsr Sleep12 ;12 31 lda PlayfieldColor ; 2 33 sta COLUPF ; 3 36 SLEEP 3 ; 5 41 lda ScoreDisplayRight,x ; 4 45 sta PF2 ; 3 48 - @48 - update PF2 for right side of screen lda #0 ; 2 50 sta PF0 ; 3 53 - update PF0 for right side of screen sta WSYNC ;--------------------------------------- lda LivesDisplay,x ; 4 4 sta PF0 ; 3 7 - update PF0 for left side of screen lda LiveColor ; 2 9 sta COLUPF ; 3 12 lda ScoreDisplayLeft,x ; 4 11 sta PF2 ; 3 14 - update PF2 for left side of screen jsr Sleep12 ;12 26 lda PlayfieldColor ; 2 33 sta COLUPF ; 3 36 SLEEP 3 ; 5 41 lda ScoreDisplayRight,x ; 4 45 sta PF2 ; 3 48 - @48 - update PF2 for right side of screen lda #0 ; 2 50 sta PF0 ; 3 53 - update PF0 for right side of screen inx ; 2 55 cpx #5 ; bne ScoreLoop2 ; 3 58 sta WSYNC ; 3 45 - wait for end of scanline ;--------------------------------------- lda #0 ; 2 2 sta PF1 sta PF2 tax ; used by Draw_MrCelery loop sta WSYNC ; 3 6 - wait for end of scanline DrawClouds ; cloud pattern will be here eventually. lda #0 sta PF0 sta PF1 sta PF2 inx cpx #162 bne DrawClouds lda #0 ldy #ARENA_HEIGHT ; init loop counter ArenaLoop: ; - 11 - time of longest path here cpy FrancisFeet bne DontDrawMissile lda #2 sta ENAM1 lda #4 sta COLUP1 DontDrawMissile lda #AnotherObject_HEIGHT-1 ; 2 30 - height of the box graphics, subtract 1 due to starting with 0 cmp Player1Draw ; 5 35 - Decrement Player1Draw and compare with height bne ArenaLoop2 ; 2 37 - (3 38) if Carry is Set, then player1 is on current scanline lda #2 sta ENABL ArenaLoop2: lda #Francis_HEIGHT-1 ; 2 13 - height of the human graphics, dcp Player0Draw ; 5 18 - Decrement Player0Draw and compare with height bcs DoDrawGrp0 ; 2 20 - (3 21) if Carry is Set then player0 is on current scanline lda #0 ; 2 22 - otherwise use 0 to turn off player0 .byte $2C ; 4 26 - $2C = BIT with absolute addressing, trick that ; causes the lda (Player0Ptr),y to be skipped DoDrawGrp0: ; 21 - from bcs DoDrawGRP0 lda (Player0Ptr),y ; 5 26 - load the shape for player0 tax ; 2 28 - save in X for update during Horizontal Blanking lda (Color0Ptr),y ; DGS33 sta Color0 ; DGS33 lda #AnotherObject_HEIGHT-1 ; 2 30 - height of the box graphics, subtract 1 due to starting with 0 dcp Player1Draw ; 5 35 - Decrement Player1Draw and compare with height bcs DoDrawGrp1 ; 2 37 - (3 38) if Carry is Set, then player1 is on current scanline lda #0 ; 2 39 - otherwise use 0 to turn off player1 sta ENABL STA ENAM1 .byte $2C ; 4 43 - $2C = BIT with absolute addressing, trick that ; causes the lda (Player1Ptr),y to be skipped DoDrawGrp1: ; 38 - from bcs DoDrawGrp1 lda (Player1Ptr),y ; 5 43 - load the shape for player1 sta WSYNC ; 3 46/0 - halts CPU until start of next scanline stx GRP0 ; 3 3 - draw the human sta GRP1 ; 3 6 - draw the box lda Color0 ; DGS33 sta COLUP0 ; DGS33 dey ; 2 8 - update loop counter bne ArenaLoop ; 2 10 - 3 11 if taken rts ; 6 16 PosPlayer: sec sta WSYNC DivideLoop sbc #15 ; 2 2 - each time thru this loop takes 5 cycles, which is bcs DivideLoop ; 2 4 - the same amount of time it takes to draw 15 pixels eor #7 ; 2 6 - The EOR & ASL statements convert the remainder asl ; 2 8 - of position/15 to the value needed to fine tune asl ; 2 10 - the X position asl ; 2 12 asl ; 2 14 sta.wx HMP0,X ; 5 19 - store fine tuning of X sta RESP0,X ; 4 23 - set coarse X position of Player rts Overscan1 sta WSYNC ; Wait for SYNC (start of next scanline) lda #2 ; LoaD Accumulator with 2 sta VBLANK ; STore Accumulator to VBLANK, D1=1 turns image output off lda OverscanValue sta TIM64T ; set timer for end of Overscan ;jsr Move_Man_or_celery_y_pos jsr ProcessCollisions ;jsr DrawPlayer0XPos OSwait: sta WSYNC bit TIMINT bpl OSwait ; wait for the timer to denote end of Overscan rts Joystick: lda SWCHA ; fetch state of both joysticks asl ; shift A bits left, R is now in carry bit bcs CheckLeft ; branch if joystick is not held right ldy PlayerX ; get position of player iny ; and move it right cpy #160 ; test for edge of screen bne SaveX ; save value as is if we're not at edge ldy #0 ; else wrap to left edge of screen SaveX: sty PlayerX ; save player's new X position ldy #0 ; turn off reflect of player, which sty REFP0 ; makes player image face right CheckLeft: asl ; shift A bits left, L is now in the carry bit bcs CheckDown ; branch if joystick not held left ldy PlayerX ; get the Player's X position dey ; and move it left cpy #255 ; test for edge of screen bne SaveX2 ; save X if we're not at the edge ldy #159 ; else wrap to right edge SaveX2: sty PlayerX ; save player's new X position ldy #8 ; turn on reflect of player, which sty REFP0 ; makes player image face left CheckDown: asl ; shift A bits left, D is now in the carry bit bcs CheckUp ; branch if joystick not held down ldy PlayerY ; get the Player's Y position dey ; move it down cpy #20 ; test for bottom of screen bne SaveY ; save Y if we're not at the bottom jmp CheckUp SaveY: sty PlayerY ; save Y CheckUp: asl ; shift A bits left, U is now in the carry bit bcs DoneWithJoystick ; branch if joystick not held up ldy PlayerY ; get the Player's Y position iny ; move it up cpy #80 ; test for top of screen bne SaveY2 ; save Y if we're not at the top jmp DoneWithJoystick SaveY2: sty PlayerY ; save Y DoneWithJoystick: rts ProcessCollisions: ; will be filled later rts DigitGfx: .byte %01110111 .byte %01010101 .byte %01010101 .byte %01010101 .byte %01110111 .byte %00100010 .byte %01100110 .byte %00100010 .byte %00100010 .byte %01110111 .byte %01110111 .byte %00010001 .byte %01110111 .byte %01000100 .byte %01110111 .byte %01110111 .byte %00010001 .byte %00110011 .byte %00010001 .byte %01110111 .byte %01010101 .byte %01010101 .byte %01110111 .byte %00010001 .byte %00010001 .byte %01110111 .byte %01000100 .byte %01110111 .byte %00010001 .byte %01110111 .byte %01110111 .byte %01000100 .byte %01110111 .byte %01010101 .byte %01110111 .byte %01110111 .byte %00010001 .byte %00010001 .byte %00010001 .byte %00010001 .byte %01110111 .byte %01010101 .byte %01110111 .byte %01010101 .byte %01110111 .byte %01110111 .byte %01010101 .byte %01110111 .byte %00010001 .byte %01110111 DigitFlippedGfx: .byte %11101110 .byte %10101010 .byte %10101010 .byte %10101010 .byte %11101110 .byte %01000100 .byte %01100110 .byte %01000100 .byte %01000100 .byte %11101110 .byte %11101110 .byte %10001000 .byte %11101110 .byte %00100010 .byte %11101110 .byte %11101110 .byte %10001000 .byte %11001100 .byte %10001000 .byte %11101110 .byte %10101010 .byte %10101010 .byte %11101110 .byte %10001000 .byte %10001000 .byte %11101110 .byte %00100010 .byte %11101110 .byte %10001000 .byte %11101110 .byte %11101110 .byte %00100010 .byte %11101110 .byte %10101010 .byte %11101110 .byte %11101110 .byte %10001000 .byte %10001000 .byte %10001000 .byte %10001000 .byte %11101110 .byte %10101010 .byte %11101110 .byte %10101010 .byte %11101110 .byte %11101110 .byte %10101010 .byte %11101110 .byte %10001000 .byte %11101110 FrancisGFX .byte #%11111110 .byte #%11111110 .byte #%01101100 .byte #%00101000 .byte #%00101000 .byte #%00101000 .byte #%00111000 .byte #%00111000 .byte #%00111000 .byte #%01111100 .byte #%10010010 .byte #%10111010 .byte #%00111000 .byte #%00111000 Francis_HEIGHT = * - FrancisGFX FrancisColors .byte #$0e .byte #$0e .byte #$2c .byte #$2c .byte #$92 .byte #$92 .byte #$92 .byte #$c6 .byte #$c6 .byte #$c6 .byte #$2c .byte #$2c .byte #$2c .byte #$24 FrancisBWColors .byte #14 .byte #14 .byte #12 .byte #12 .byte #4 .byte #4 .byte #4 .byte #6 .byte #6 .byte #6 .byte #12 .byte #12 .byte #12 .byte #4 AnotherObjectGfx .byte #0 AnotherObject_HEIGHT = * - AnotherObjectGfx AnotherObjectColors .byte #$9A echo "------", [$FFFA - *]d, "bytes free before end of cartridge" ORG $FFFA InterruptVectors .word Reset ; NMI .word Reset ; RESET .word Reset ; IRQ END