; ; ; SSSSSSSSSSS CCCCCCCCCCCC SSSSSSSSSSS III dd ; SSSSSSSSSSS CC CCCC SSSSSSSSSSS III dd ; SS SS CC CCCC SS SS III iii dd ; SS SS CC CCCC SS SS III iii dd ; SS SS CC CCCC SS SS III dd ; SS SS CC CCCC SS SS III dd ; SS CC SS III cccccccc ii ddddddddd eeeeeeeee ; SSSSSSSSSSS CC SSSSSSSSSSS III cccccccc ii ddddddddd eeeeeeeee ; SSSSSSSSSSS CC SSSSSSSSSSS IIII cc cccc ii dddd dd eeee ee ; SSSS CC CC SSSS IIII cc cccc ii dddd dd eeee ee ; SSSS CC CC SSSS IIII cc iiii dddd dd eeeeeeeee ; SS SSSS CC CC SS SSSS IIII cc iiii dddd dd eeeeeeeee ; SS SSSS CC CC SS SSSS IIII cc iiii dddd dd eeee ; SS SSSS CC CC SS SSSS IIII cc cc iiii dddd dd eeee ee ; SS SSSS CC CC SS SSSS IIII cc cc iiii dddd dd eeee ee ; SSSSSSSSSSS CCCCCCCCCCCC SSSSSSSSSSS IIII cccccccc iiii ddddddddd eeeeeeeee ; SSSSSSSSSSS CCCCCCCCCCCC SSSSSSSSSSS IIII cccccccc iiii ddddddddd eeeeeeeee ; ; The Atari VCS Game Concept ; ; Copyright 2001 ; By Joe Grand, jgrand@mindspring.com (http://www.mindspring.com/~jgrand/atari) ; ; dasm source.s -f3 -osource.bin to compile ; ; Tested with StellaX v1.1.3a and Atari 2600jr w/ Supercharger ; ; NEW ADDITIONS ; ; o Nothing! ; ; ; TO DO ; ; o Nothing! ; ; processor 6502 include vcs.h SEG.U defines NUMGRP = 10 ; Number of graphics (player 1 data) GRPHEIGHT = 7 ; Height of each graphic XMIN = 8 XMAX = 158 HORPOS_P0 = 148 ; Horizontal position player 0 (drivehead) - fixed COLOR_SCORE = $C6 ; Score/Time Remaining color (Green) COLOR_GAUGE = $34 ; Gas gauge background color (Red) COLOR_LINE = $F4 ; Line color (Light Brown) COLOR_TRACK = $F2 ; Track color (Brown) COLOR_BG = $00 ; Background color (Black) SOUND_CORRECT = $8F ; Correct data bit SOUND_INCORRECT = $5C ; Incorrect data bit SOUND_MISSED = $2E ; Data bit missed COLOR_REPEAT = 10 ; Number of times to repeat with the same data for title drawing SEG.U vars org $80 ; Allocate memory, PIA has 128 bytes of RAM ($80-$FF). Stack from $FF ; Total RAM used = 115 bytes temp ds 1 ; Temporary variables temp_stack ds 1 loopCount ds 1 ; Counter for loops pot0 ds 1 ; Player 0's paddle value startDelay ds 1 ; Timer for paddle switch debounce horPosP1 ds NUMGRP ; Horizontal position player 1 (data) horPosP1_FC ds NUMGRP horMotP1 ds NUMGRP ; Motion values player 1 (data) verPosP0 ds 1 ; Vertical position player 0 (drivehead) grpCount ds 1 ; Group counter grpY ds NUMGRP grpY_Next ds 1 ; First line of next group grpDistance ds 1 ; Distance between player0 and top of group nodata ds 1 nocc ds 1 coll ds NUMGRP ; Collision flags dataColorTable ds NUMGRP ; Color order of data for current level nextColorTable ds NUMGRP ; Color order of data to match for current level currentDataColor ds 1 ; Color of data to match dataColorIndex ds 1 ; Index to the next data color in table score ds 3 ; Player score (6 hex digits: 00 00 00) ; / | \ ; score +1 +2 ; Platter Level Actual Score scorePtr0 ds 2 ; Pointer to score shapes scorePtr1 ds 2 scorePtr2 ds 2 scorePtr3 ds 2 scorePtr4 ds 2 scorePtr5 ds 2 gauge_PF ds 2 ; Data latency buffer, PF0 and PF1 (1st half of screen) bitCount ds 1 ; Number of data bits read bitCount_PF ds 3 ; PF0, PF1 and PF2 (2nd half of screen) gameInProgress ds 1 ; Flag set when game is in progress levelDone ds 1 ; Flag set when level is completed titleScreen ds 1 ; Flag set when title screen should be disabled rand1 ds 1 ; Variables for random bit/byte generation routine rand2 ds 1 rand3 ds 1 rand4 ds 1 soundType ds 1 ambientPitch ds 1 ; Current pitch for ambient noise headSize ds 1 ; Size of drivehead based on difficulty switch color0 ds 1 ; For color cycling, top of the current line color1 ds 1 SEG code org $F000 ; 4K to burn 2732 ROM .byte "SCSIcide v1.31, " .byte "Joe Spandex Limited Edition, " .byte "August 25, 2001, " .byte "Copyright J. Grand " Start ; ; The 2600 powers up in a completely random state, except for the PC which ; is set to the location at $FFC. Therefore the first thing we must do is ; to set everything up inside the 6502. ; SEI ; Disable interrupts, if there are any. CLD ; Clear BCD math bit. LDX #$FF TXS ; Set stack to beginning. ; Loop to clear memory and TIA registers LDA #0 B1 STA 0,X DEX BNE B1 ; The above routine does not clear location 0, which is VSYNC JSR GameInit ; Initial variable and register setup MainLoop JSR VerticalBlank ; Execute the vertical blank. JSR CheckSwitches ; Check console switches. JSR GameCalc ; Do calculations during VBLANK JSR DrawScreen ; Draw the screen JSR OverScan ; Do more calculations during overscan JMP MainLoop ; Continue forever. VerticalBlank ;*********************** VERTICAL BLANK HANDLER ; ; Beginning of the frame - at the end of overscan. ; LDA #$82 ; VB_DumpPots & VB_Enable (Thanks Eckhard!) STA VBLANK ; Discharge paddle potentiometer LDA #2 ; VBLANK was set at the beginning of overscan. STA WSYNC STA WSYNC STA WSYNC STA VSYNC ; Begin vertical sync STA WSYNC ; First line of VSYNC STA WSYNC ; Second line of VSYNC ; ; End the VSYNC period. ; LDA #0 STA WSYNC ; Third line of VSYNC. STA VSYNC ; (0) LDA #$2 STA VBLANK ; Start recharge of paddle capacitor ; Set the timer to go off just before the end of vertical blank space ; 37 scanlines * 76 cycles = 2812 cycles / 64 = approx. 44 LDA #44 STA TIM64T RTS CheckSwitches ;*************************** CONSOLE SWITCH HANDLER LDA SWCHB ; Read the switches LSR ; Shift the Reset switch ROR ; Rotate Reset to Negative flag and Carry has Select BMI NoReset Go ; ; Start new game if reset ; LDA #1 STA gameInProgress STA titleScreen JSR DataInit JSR BonusDone ; Fill latency buffer LDA #1 STA levelDone RTS NoReset LDA SWCHB ; Read the switches ASL ; Shift the Player 1 Difficulty switch into Carry ; ; Player 0 (Left) ; BMI P0Easy ; Set size of drivehead LDA #P_Single ; B JMP P0Done P0Easy LDA #P_Double ; A P0Done STA headSize RTS GameCalc ;******************************* GAME CALCULATION ROUTINES JSR RandomBit ; Seed the PRNG LDA #1 STA pot0 ; ; Check to see if latency buffer is empty. If it is, end game ; LDA gauge_PF BNE NotEmpty JSR EndGame NotEmpty ; ; Start new level when paddle button is pressed ; LDA startDelay BNE Delay2 LDA levelDone ; If flag is set... BEQ NotReady LDA #15 ; Delay... STA startDelay Delay2 DEC startDelay BNE NotReady LDA SWCHA ; Then check for button press... BMI NotReady JSR NextLevel ; ...to begin next level NotReady ; ; Move data objects horizontally (Courtesy of Piero Cavina PCMSD 1.1 source) ; LDX #NUMGRP-1 MoveLP LDA horPosP1,X ; Get current position CLC ADC horMotP1,X ; Increment/decrement by motion value STA horPosP1,X CMP #XMIN ; Are we at the minimum X value (left edge of screen)? BCC SwapX0 CMP #XMAX ; Are we at the maximum X value (right edge of screen)? BCS SwapX JMP MoveDone ; We must be somewhere in between SwapX0 LDA #XMIN ; Minimum SEC SBC horPosP1,X CLC ADC #XMIN STA horPosP1,X SwapX ; Maximum LDA #XMIN ; Wrap around to the beginning of the track STA horPosP1,X ; Is this the current data bit? If so, decrement latency buffer LDA currentDataColor CMP dataColorTable,X BNE MoveDone LDA levelDone ; If level is not done, game is in progress... BNE MoveDone LDA #SOUND_MISSED ; Play sound STA soundType JSR DecGauge ; ...so gauge can be decremented JSR DecScore MoveDone LDA horPosP1,X ; Convert X position to FC format JSR Convert STA horPosP1_FC,X DEX BPL MoveLP LDA #0 STA grpCount ; Initialize group counter STA grpY ; First line of first group LDA #GRPHEIGHT+1 STA grpY_Next ; First line of next (second) group LDA verPosP0 STA grpDistance ; ; Is it time to display easter egg? ; LDA SWCHA ; Have both right paddle buttons been pressed? AND #$0C BNE RealScore ; If so, set score to easter egg text ; ; Text depends on version of cartridge ; ; ; CGE2K1 ; ; ; LDA #C ; STA scorePtr0+1 ; ; LDA #G ; STA scorePtr1+1 ; ; LDA #E ; STA scorePtr2+1 ; ; LDA #Two ; STA scorePtr3+1 ; ; LDA #K ; STA scorePtr4+1 ; ; LDA #One ; STA scorePtr5+1 ; ; BOSTON ; LDA #B STA scorePtr0+1 LDA #O STA scorePtr1+1 STA scorePtr4+1 LDA #S STA scorePtr2+1 LDA #T STA scorePtr3+1 LDA #N STA scorePtr5+1 ; ; HOZER ; ; LDA #H ; STA scorePtr0+1 ; ; LDA #O ; STA scorePtr1+1 ; ; LDA #Z ; STA scorePtr2+1 ; ; LDA #E ; STA scorePtr3+1 ; ; LDA #R ; STA scorePtr4+1 ; ; LDA #HeadFrame ; STA scorePtr5+1 RTS RealScore ; ; Set pointers to number data (based on current score) ; score score+1 score+2 ; LDX #3-1 LDY #10 ScoreLoop LDA score,X AND #$0F ; Mask it ASL ; Multiply by 8 ASL ASL CLC ADC #FontPage STA scorePtr0+1,Y DEY DEY LDA score,X AND #$F0 ; Mask it LSR ; $00,$08,$10,$18,$20, ... ( / 8 = value) ADC #FontPage STA scorePtr0+1,Y DEY DEY DEX BPL ScoreLoop RTS DrawScreen ;**************************** SCREEN DRAWING ROUTINES ; ; Initialize display variables. ; LDA #COLOR_BG STA COLUBK ; Background will be black WaitVBlank LDA INTIM ; Loop until timer is done - wait for the proper scanline ; (i.e. somewhere in the last line of VBLANK) BNE WaitVBlank ; Whew! We're at the beginning of the frame LDA #0 STA WSYNC ; End the current scanline STA HMOVE ; Add horizontal motion to position sprites STA VBLANK ; End the VBLANK period with a zero, enable video, charge pot. STA WSYNC ; Wait 2 scanlines before drawing score LDA titleScreen BEQ DoTitleScreen JMP NoTitleScreen ; Branch is out of range, so need to use JMP DoTitleScreen ; Set the timer to go off just before the end of actual TV picture to prevent flicker ; 192 scanlines * 76 cycles = 14592 cycles / 64 = 228 LDA #228 STA TIM64T LDX #30 PreDraw STA WSYNC ; Move down to position title DEX BNE PreDraw CLC LDA color0 ; What is the color at the top of current line? ADC #$02 STA color0 ; Store the new top color STA color1 STA COLUPF ; Set new playfield color ; ; Draw SCSIcide title ; LDY #COLOR_REPEAT LDX #8-1 NxtLine STA WSYNC ; Wait for line to finish LDA PFData0,X ; Get the Xth line for playfield 0 (left half of screen) STA PF0 LDA PFData1,X ; Get the Xth line for playfield 1 STA PF1 LDA PFData2,X ; Get the Xth line for playfield 2 STA PF2 INC color1 ; Increase color of current line by 1 NOP ; Wait LDA PFData3,X ; Get the Xth line for playfield 0 (right half of screen) STA PF0 LDA PFData4,X ; Get the Xth line for playfield 1 STA PF1 LDA PFData5,X ; Get the Xth line for playfield 2 STA PF2 LDA color1 ; Used in place of NOP for extra cycle LDA color1 STA COLUPF ; Set new playfield color DEY BNE NxtLine LDY #COLOR_REPEAT ; Number of times to repeat with the same data DEX ; Done with data yet? BPL NxtLine ; If not, do next line LDY #0 STY PF0 STY PF1 STY PF2 LDX #32 PostDraw STA WSYNC ; Finish drawing the rest of the screen to prevent flicker DEX BNE PostDraw ; ; Draw name and date ; LDX #P_TwoClose STX NUSIZ0 ; 2 shapes, close together STX NUSIZ1 LDA #$0E ; White STA COLUP0 STA COLUP1 ; ; Horizontally position P0 and P1 ; STA HMOVE NOP NOP NOP NOP NOP NOP NOP STA HMCLR ;Clear horizontal motion registers STA RESP0 ;Set the X coordinate STA RESP1 LDA #$10 ;Move P1 one pixel to the left STA HMP1 LDA #$00 STA HMP0 ; ; All the shapes end with a shape of zero. This way, I can assume ; the video display is pure black after the loop is finished. ; LDX #17-1 DrawLogo STA WSYNC ; 3 Wait for sync STA HMOVE ; 3 Move the players (Only first time) LDA NameData0,X ; 4 Draw the shape STA GRP0 ; 3 LDA NameData1,X ; 4 Draw in player #1 STA GRP1 ; 3 NOP ; 2 Waste cycles for horizontal position NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 LDA NameData3,X ; 4 Preload the final 2 bytes TAY ; 2 LDA NameData2,X ; 4 STA GRP0 ; 3 Store in shape STY GRP1 ; 3 STA HMCLR ; 3 Clear horiz. motion for future loops ; Repeat for double height STA WSYNC ; 3 STA HMOVE LDA NameData0,X ; 4 Draw the shape STA GRP0 ; 3 LDA NameData1,X ; 4 Draw in player #1 STA GRP1 ; 3 NOP ; 2 Waste cycles for horizontal position NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 NOP ; 2 LDA NameData3,X ; 4 Preload the final 2 bytes TAY ; 2 LDA NameData2,X ; 4 STA GRP0 ; 3 Store in shape STY GRP1 ; 3 STA HMCLR DEX ; 2 All done? BPL DrawLogo ; 3 Loop WaitOverscan2 LDA INTIM ; Loop until timer is done - wait for the proper scanline BNE WaitOverscan2 RTS NoTitleScreen LDA currentDataColor ; Set the score color (P0 and P1) STA COLUP0 STA COLUP1 LDX #P_ThreeClose STX NUSIZ0 ; Three copies of player, close together (for score) STX NUSIZ1 STA WSYNC ; ; Horizontally position P0 and P1 for score ; LDX #22 Pause1 DEX BNE Pause1 STA RESP0 STA WSYNC LDX #22 Pause2 DEX BNE Pause2 NOP STA RESP1 STA HMCLR LDA #%10100000 ; P1 position 2 less than P0 STA HMP1 LDA #%11000000 STA HMP0 STA WSYNC STA HMOVE STA WSYNC ScanLoop ; ================================== SCANNING LOOP ; ; There are 228/3 cycles per line, so we have a maximum of 73 cycles ; after STA WSYNC ; ; Display the scores - 6 digit, courtesy of Robin Harbron ; LDA #8-1 ; Lines to display STA loopCount LDX #9 Pause3 DEX BNE Pause3 NOP NOP NOP TSX STX temp_stack ; Save the stack pointer (Andrew Davie) DrawScore LDY loopCount ; 3 LDA (scorePtr0),Y ; 5 STA GRP0 ; 3 = 11 BIT $00 LDA (scorePtr1),Y ; 5 STA GRP1 ; 3 LDA (scorePtr5),Y ; 5 TAX ; 2 TXS ; 2 LDA (scorePtr2),Y ; 5 STA temp ; 3 LDA (scorePtr3),Y ; 5 TAX ; 2 LDA (scorePtr4),Y ; 5 = 37 LDY temp ; 3 STY GRP0 ; 3 STX GRP1 ; 3 STA GRP0 ; 3 TSX ; 2 STX GRP1 ; 3 = 17 DEC loopCount ; 5 BPL DrawScore ; 3 = 8 LDX temp_stack TXS LDA #0 STA GRP0 STA GRP1 ; ; Position drivehead (horizontally) ; LDA #HORPOS_P0 ; Convert Player0 X position to FC format JSR Convert STA WSYNC ; Prepare to position Player0 STA HMP0 ; Remember, we're still doing VBLANK now AND #$0F TAY PosWait DEY BPL PosWait STA RESP0 STA WSYNC STA HMOVE LDX headSize ; Set size of drivehead based on difficulty switch STX NUSIZ0 LDX #P_Single ; One copy of data STX NUSIZ1 LDA currentDataColor STA COLUP0 STA HMCLR ; Clear horizontal motion (of Player 0) LDA #0 ; Use playfield (black) to shorten length of track STA COLUPF STA PF1 STA PF2 LDA #$30 STA PF0 LDA #$05 ; Reflect,No score,High Priority,1 pixel-wide ball STA CTRLPF ; ; Start drawing the actual disk playfield (10 lanes) ; ; We're going to draw #NUMGRP groups, each made of: ; 2 scanlines for Player1 positioning with Player0, plus ; #GRPHEIGHT*2 scanlines with Player1 and Player0. ; ; (Courtesy of Piero Cavina PCMSD 1.1 source) ; Kernel LDA grpDistance ; Distance between Player0 <-> top of group CMP #GRPHEIGHT+1 ; Is Player0 inside current group? BCC DrawP0 ; Yes, we'll draw it... LDX #0 ; No, draw instead a blank sprite BEQ BlankP0 DrawP0 LDA grpY_Next ; We must draw Player0, and we'll start SEC ; from the (grpY_Next-verPosP0)th byte. SBC verPosP0 TAX ; Put the index to the first byte into X BlankP0 STX temp ; and remember it. LDY grpCount ; Store any collision between Player0 and LDA CXPPMM ; Player1 happened while drawing the last group. STA coll,Y LDA dataColorTable,Y STA COLUP1 LDA horPosP1_FC,Y ; Get Player1 position LDY gameInProgress ; If game over, don't draw drivehead BEQ NoHead LDY HeadFrame,X ; Get Player0 pattern NoHead STA WSYNC ; Start with a new scanline. STY GRP0 ; Set Player0 pattern STA HMP1 ; Prepare Player1 fine motion AND #$0F ; Prepare Player1 coarse positioning TAY PosWait2 DEY ; Waste time BPL PosWait2 STA RESP1 ; Position Player1 STA WSYNC ; Wait for next scanline STA HMOVE ; Apply fine motion LDA #COLOR_LINE STA COLUBK ; Draw track seperator ; ; Now prepare various things for the next group ; LDA grpY_Next ; Update this group and next group STA grpY ; top line numbers CLC ADC #GRPHEIGHT+1 STA grpY_Next LDA verPosP0 ; Find out which 'slice' SEC ; of Player0 we'll have to draw. SBC grpY ; We need the distance of Player0 BPL DPos ; from the top of the group. EOR #$FF CLC ADC #1 ; A = ABS(verPosP0-grpY) DPos STA grpDistance LDX temp ; Pointer to the next byte of Player0 INX ; pattern. Use X while drawing the group LDA #0 ; Clear collisions STA CXCLR LDY #GRPHEIGHT-1 ; Initialize line counter (going backwards) DrawGrp LDA #0 STA WSYNC ; Wait for a new line LDA #COLOR_TRACK STA COLUBK ; Set background color LDA DataFrame,Y STA GRP1 ; Set Player1 shape LDA gameInProgress ; If game over, don't draw drivehead BEQ NoHead2 LDA HeadFrame,X NoHead2 STA GRP0 ; Set Player0 shape LDA INPT0 ; Read paddle 0 BMI Charged ; Capacitor already charged, skip increment INC pot0 ; Increment paddle position value Charged STA WSYNC ; Wait for a new scanline INX ; Update the index to next byte of Player0 DEY ; Decrement line counter BPL DrawGrp ; Go on with this group if needed INC grpCount ; Increment current group number LDA grpCount ; CMP #NUMGRP ; Is there another group to do? BCS OuterKernel ; No, exit JMP Kernel ; Yes, go back. (Using JMP because a branch ; would be out of range) OuterKernel STA WSYNC ; Finish current scanline LDA #0 ; Avoid bleeding STA GRP0 STA GRP1 LDA #COLOR_LINE ; Draw bottom track seperator STA COLUBK STA WSYNC LDA #COLOR_BG ; Black line STA COLUBK LDY #7 DrawWait2 STA WSYNC DEY BNE DrawWait2 ; ; Draw data latency buffer and bit counter ; LDA #$30 STA CTRLPF ; No Reflect,No score,Low Priority,8 pixel-wide ball LDX #$07 ; Number of lines alike NxtGaugeLine STA WSYNC ; Wait for line to finish LDA #COLOR_SCORE ; [0] +2 green STA COLUPF ; [2] +3 LDA #COLOR_GAUGE ; [5] +2 red STA COLUBK ; [7] +3 set background ; First half of screen LDA gauge_PF ; [10] +3 STA PF0 ; [13] +3 LDA gauge_PF+1 ; [16] +3 STA PF1 ; [19] +3 LDA #0 ; [22] +2 STA PF2 ; [24] +3 STA PF2 ; [27] +3 Used in place of NOP for extra cycle NOP ; [30] +2 LDY currentDataColor ; [32] +3 STA COLUBK ; [35] +3 end of gauge -> background color back to black ; Second half of screen - display number of bytes currently retrieved STY COLUPF ; [38] +3 LDA bitCount_PF ; [41] +3 STA PF0 ; [44] +3 LDA bitCount_PF+1 ; [47] +3 STA PF1 ; [50] +3 LDA bitCount_PF+2 ; [53] +3 STA PF2 ; [56] +3 DEX ; [59] +5 BNE NxtGaugeLine ; [64] +3 (take branch) ; ; Clear all registers here to prevent any possible bleeding. ; LDA #2 STA WSYNC ; Finish this scanline. STA VBLANK ; Make TIA output invisible, ; Now we need to worry about it bleeding when we turn ; the TIA output back on. LDY #0 STY PF0 STY PF1 STY PF2 RTS OverScan ;***************************** OVERSCAN CALCULATIONS (2280 cycles) ; Set the timer to go off just before the end of overscan ; 30 scanlines * 76 cycles = 2280 cycles / 64 = approx. 35 LDA #35 STA TIM64T ; If game is over, disable paddle LDA gameInProgress BNE GameInPlay JMP NoStick GameInPlay ; ; Set new vertical position of drivehead based on paddle value ; LDX pot0 LDA SWCHB ; Read the Player 1 (Right) Difficulty switch BPL P1Done ; ; Check paddle position and set for track-to-track (non-smooth) motion ; CPX #14 BPL Next LDX #0 ; If position is anywhere within the track, keep it at the beginning JMP P1Done Next CPX #21 BPL Next2 LDX #8 JMP P1Done Next2 CPX #28 BPL Next3 LDX #16 JMP P1Done Next3 CPX #35 BPL Next4 LDX #24 JMP P1Done Next4 CPX #42 BPL Next5 LDX #32 JMP P1Done Next5 CPX #49 BPL Next6 LDX #40 JMP P1Done Next6 CPX #56 BPL Next7 LDX #48 JMP P1Done Next7 CPX #63 BPL Next8 LDX #56 JMP P1Done Next8 CPX #70 BPL Next9 LDX #64 JMP P1Done Next9 CPX #77 BPL P1Done LDX #72 P1Done STX verPosP0 NoStick ; ; Check and handle any collisions between P1 (data) and P0 (drivehead) ; LDA gameInProgress ; Don't read button if game is over BEQ NoButton LDA levelDone ; If level is completed and we're waiting for a new one to start BNE NoButton LDA nodata BNE Delay LDA SWCHA ; Has fire button been pressed? BMI NoButton ; If yes, check for collisions LDX #NUMGRP-1 LDA #0 LoopColl ; Check all collision flags ORA coll,X DEX BPL LoopColl CMP #0 ; If there are no collisions... BMI DataExists JSR DecGauge ; There is NO data to read, so decrement latency buffer LDA #20 ; Delay to prevent multiple collisions STA nodata JMP NoButton Delay DEC nodata DataExists LDX #NUMGRP-1 CheckCollisions LDA nocc BNE CollisionDelay LDA coll,X BPL NoCollision ; Button has been pressed and there is data to read STX temp DEX BPL NoRoll ; We are looking at the previous groups collision LDX #NUMGRP-1 ; So if X=0, need to set to X=9 NoRoll ; Is the color correct? LDA currentDataColor CMP dataColorTable,X BEQ SameColor ; If not, decrement score JSR DecScore LDA #SOUND_INCORRECT ; Play sound STA soundType JMP NoButton SameColor LDA #0 ; Hide/remove block STA horPosP1,X STA horMotP1,X LDA #SOUND_CORRECT ; Play sound STA soundType JSR IncCount ; Increase bit counter JSR IncBonus ; Refill latency buffer and give bonus points JSR GetNextColor ; Change to next color LDA #20 ; Delay to prevent multiple collisions STA nocc LDX temp CollisionDelay DEC nocc NoCollision LDA #0 ; Clear collision flag STA coll,X DEX BPL CheckCollisions NoButton JSR DoSound WaitOverscan LDA INTIM ; Loop until timer is done - wait for the proper scanline BNE WaitOverscan RTS GameInit ;***************************** INITIAL VARIABLE & REGISTER SETUP ; ; Called at initial power on ; LDA #$9A ; Need to be initialized to any non-zero value STA rand1 STA rand2 STA rand3 STA rand4 JSR DataInit ; We need to set up some of the data before the game starts ; ; Clear data pointers ; LDX #12-1 LDA #0 Clear2 STA scorePtr0,X DEX BPL Clear2 STA WSYNC LDY #3 Pos0 DEY BNE Pos0 STA RESP0 ; A = 0 STA gameInProgress STA titleScreen STA color0 LDA #COLOR_SCORE ; Color of score and drivehead before the game starts STA currentDataColor RTS DataInit ; ; Called when Reset switch is pressed ; LDA #0 STA nodata STA nocc STA dataColorIndex STA startDelay ; Score STA score STA score+1 STA score+2 ; Clear bit counter STA bitCount STA bitCount_PF STA bitCount_PF+1 STA bitCount_PF+2 ; Clear latency buffer STA gauge_PF STA gauge_PF+1 ; Disable sound STA soundType STA AUDC0 ; Control (Type) STA AUDF0 ; Frequency (Pitch) STA AUDV0 ; Volume STA AUDC1 STA AUDF1 STA AUDV1 LDX #NUMGRP-1 NoMotion2 STA horPosP1,X STA horMotP1,X DEX BPL NoMotion2 LDA #25 STA ambientPitch RTS ; ; Set the next color of data block to retrieve ; GetNextColor ; ; If all data bits have been read, go to next level ; LDA bitCount CMP #NUMGRP BNE MoreData JSR LevelDone MoreData ; Change to next color LDY bitCount LDA nextColorTable,Y STA currentDataColor RTS ; ; 1) Randomly arrange the color order of data bits: dataColorTable ; 2) Precalculate the color order of data to match: nextColorTable ; RandomizeColorTables ; Clear flags LDY #NUMGRP-1 LDA #0 ClearFlags STA horPosP1,Y ; Use horPosP1 and horMotP1 as color flags STA horMotP1,Y DEY BPL ClearFlags LDY #NUMGRP-1 Table JSR RandomByte ; Use random number to select next color (to prevent patterns) STA temp ; Random number returned in A GetNext AND #$0F ; Use the LSB for calculating dataColorTable ; Mod 10 CMP #10 BMI NoMod CLC SBC #9 NoMod TAX LDA horPosP1,X BEQ Good TXA ; Else, color has already been done, so... CLC ; ...increment random number and try again ADC #1 JMP GetNext Good LDA DataColorsNTSC,X STA dataColorTable,Y STA horPosP1,X ; Set flag LDA temp ; Now use the MSB for calculating nextColorTable LSR LSR LSR LSR GetNext2 AND #$0F ; Mod 10 CMP #10 BMI NoMod2 CLC SBC #9 NoMod2 TAX LDA horMotP1,X BEQ Good2 TXA ; Else, color has already been done, so... CLC ; ...increment random number and try again ADC #1 JMP GetNext2 Good2 LDA DataColorsNTSC,X STA nextColorTable,Y STA horMotP1,X ; Set flag DEY BPL Table RTS ; ; Give extra points: Platter level number (score) * remaining latency buffer ; IncBonus LDX gauge_PF BEQ BonusDone JSR DecGauge LDA score JSR IncScore JMP IncBonus BonusDone ; Refill latency buffer LDA #$F0 STA gauge_PF LDA #$FF STA gauge_PF+1 RTS ; ; Level is complete, clear variables and set flag ; LevelDone ; Clear bit counter LDA #0 STA bitCount STA bitCount_PF STA bitCount_PF+1 STA bitCount_PF+2 ; Disable sound STA AUDC0 ; Control (Type) STA AUDF0 ; Frequency (Pitch) STA AUDV0 ; Volume STA AUDC1 STA AUDF1 STA AUDV1 LDA #1 STA levelDone RTS ; ; Go to next level ; NextLevel LDA #0 STA levelDone JSR RandomizeColorTables JSR GetNextColor ; ; Set objects position and motion ; LDY #NUMGRP-1 NewMotion JSR RandomByte ; Random number returned in A AND #$7F ; Choose random positioning of data bits STA horPosP1,Y CLC LDA score ; Increase speed every other level ADC #2 LSR ; /2 STA horMotP1,Y DEY BPL NewMotion INC score ; Increment level number ; Increase pitch of ambient sound (Channel 0, hard drive spinning) LDA #15 STA AUDC0 ; 5 bit poly div 6 LDA #9 STA AUDV0 ; Minimum pitch = 5 LDA ambientPitch CMP #5 BNE IncreasePitch LDA #6 IncreasePitch SEC SBC #1 STA ambientPitch STA AUDF0 RTS ; ; End the game ; Stop all data bits, prevent drive head from moving ; EndGame LDA #0 STA gameInProgress ; Disable sound STA AUDC0 ; Control (Type) STA AUDF0 ; Frequency (Pitch) STA AUDV0 ; Volume STA AUDC1 STA AUDF1 STA AUDV1 LDX #NUMGRP-1 NoMotion STA horMotP1,X DEX BPL NoMotion RTS ; ; Sound routine for handling Channel 1 sounds (non-ambient) ; Play sound effect depending on value of soundType ; ; (Used Ed Federmeyer's SoundX utility to experiment with sound effects) ; DoSound LDA soundType BEQ EndSound AND #$40 BNE NextSound1 LDA soundType AND #$20 BNE NextSound2 ; SOUND_CORRECT LDA soundType STA AUDV1 LDA #12 ; Div 31 pure tone STA AUDC1 LDA #6 STA AUDF1 DEC soundType ; Count down the sound timer LDA soundType ; Done? AND #$0F BNE EndSound LDA #0 ; Kill the sound STA AUDV1 STA soundType JMP EndSound NextSound1 ; SOUND_INCORRECT LDA soundType STA AUDV1 LDA #11 STA AUDF1 LDA #7 STA AUDC1 ; 5 bit poly -> div 2 DEC soundType ; Count down the sound timer LDA soundType ; Done? AND #$0F BNE EndSound LDA #0 ; Kill the sound STA AUDV1 STA soundType JMP EndSound NextSound2 ; SOUND_MISSED LDA soundType STA AUDV1 EOR #$FF STA AUDF1 LDA #5 ; Div 2 pure tone STA AUDC1 DEC soundType ; Count down the sound timer LDA soundType ; Done? AND #$0F BNE EndSound LDA #0 ; Kill the sound STA AUDV1 STA soundType EndSound RTS ; ; Increase the score by A (up to $FF) ; Handles roll of score from 00FF to 0100, etc. ; IncScore CLC ; Clear carry LDX #2-1 Loop ADC score+1,X ; Add LSB STA score+1,X LDA #0 ; MSB 2 bytes = 0 DEX BPL Loop RTS ; ; Decrement the score by 1 point ; Handle roll of score from 0100 to 00FF, etc. ; Prevent roll if score is already at 0000 ; DecScore ; Check for 0000 LDA score+2 BNE Decrement ; If not 0, just decrement LSB ORA score+1 BEQ DecDone ; If score has rolled, decrement MSB, too DEC score+1 Decrement DEC score+2 DecDone RTS ; ; Increase/decrease the value of the data latency buffer by 1 pixel ; IncGauge LDA gauge_PF CMP #$F0 BEQ IncPF ASL ORA #$10 STA gauge_PF JMP IncGaugeDone IncPF LDA gauge_PF+1 LSR ORA #$80 STA gauge_PF+1 IncGaugeDone RTS DecGauge LDA gauge_PF+1 CMP #$0 BEQ DecPF0 ASL STA gauge_PF+1 JMP DecGaugeDone DecPF0 LDA gauge_PF LSR AND #$F0 STA gauge_PF DecGaugeDone RTS ; ; Increase the value of the bit counter by 2 pixels, increment counter variable ; IncCount INC bitCount LDA bitCount_PF CMP #$F0 BEQ IncPF1 ASL ASL ORA #$30 STA bitCount_PF JMP IncDone IncPF1 LDA bitCount_PF+1 CMP #$FF BEQ IncPF2 LSR LSR ORA #$C0 STA bitCount_PF+1 JMP IncDone IncPF2 LDA bitCount_PF+2 ASL ASL ORA #$03 STA bitCount_PF+2 IncDone RTS ; ; Random number generation (courtesy of Erik Mooney, March 1997) ; http://www.biglist.com/lists/stella/archives/199703/msg00296.html ; ; rand1, rand2, rand3, rand4 are RAM locations, initialized to any non-zero ; value at program start ; ; RandomBit generates one random bit. ; RandomByte generates one random byte and returns it in the accumulator. ; RandomBit LDA rand4 ASL ASL ASL EOR rand4 ; New bit is now in bit 6 of A ASL ASL ; New bit is now in carry ROL rand1 ; Shift new bit into bit 0 of register, bit 7 goes into carry ROL rand2 ; Shift old bit 7 into bit 8, etc. ROL rand3 ROL rand4 RTS RandomByte LDX #8 RandomByte1 JSR RandomBit DEX BNE RandomByte1 LDA rand1 RTS ; ; Straight from "Air-Sea Battle", here's the routine ; to convert from standard X positions to FC positions ; Convert STA temp BPL LF34B CMP #$9E BCC LF34B LDA #$00 STA temp LF34B LSR LSR LSR LSR TAY LDA temp AND #$0F STY temp CLC ADC temp CMP #$0F BCC LF360 SBC #$0F INY LF360 CMP #$08 EOR #$0F BCS LF369 ADC #$01 DEY LF369 INY ASL ASL ASL ASL STA temp TYA ORA temp RTS org $FD00 ; *********************** GRAPHICS DATA ; ; All shapes are upside down to allow decrementing by Y as both ; a counter and a shape index ; ; ; Numeric font ; FontPage ;Zero .byte %00111100 ; | XXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %00111100 ; | XXXX | ;One .byte %00111100 ; | XXXX | .byte %00011000 ; | XX | .byte %00011000 ; | XX | .byte %00011000 ; | XX | .byte %00011000 ; | XX | .byte %00011000 ; | XX | .byte %00111000 ; | XXX | .byte %00011000 ; | XX | ;Two .byte %01111110 ; | XXXXXX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %00111100 ; | XXXX | .byte %00000110 ; | XX | .byte %00000110 ; | XX | .byte %01000110 ; | X XX | .byte %00111100 ; | XXXX | ;Three .byte %00111100 ; | XXXX | .byte %01000110 ; | X XX | .byte %00000110 ; | XX | .byte %00001100 ; | XX | .byte %00001100 ; | XX | .byte %00000110 ; | XX | .byte %01000110 ; | X XX | .byte %00111100 ; | XXXX | ;Four .byte %00001100 ; | XX | .byte %00001100 ; | XX | .byte %00001100 ; | XX | .byte %01111110 ; | XXXXXX | .byte %01001100 ; | X XX | .byte %00101100 ; | X XX | .byte %00011100 ; | XXX | .byte %00001100 ; | XX | ;Five .byte %01111100 ; | XXXXX | .byte %01000110 ; | X XX | .byte %00000110 ; | XX | .byte %00000110 ; | XX | .byte %01111100 ; | XXXXX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01111110 ; | XXXXXX | ;Six .byte %00111100 ; | XXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01111100 ; | XXXXX | .byte %01100000 ; | XX | .byte %01100010 ; | XX X | .byte %00111100 ; | XXXX | ;Seven .byte %00011000 ; | XX | .byte %00011000 ; | XX | .byte %00011000 ; | XX | .byte %00011000 ; | XX | .byte %00001100 ; | XX | .byte %00000110 ; | XX | .byte %01000010 ; | X X | .byte %01111110 ; | XXXXXX | ;Eight .byte %00111100 ; | XXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %00111100 ; | XXXX | .byte %00111100 ; | XXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %00111100 ; | XXXX | ;Nine .byte %00111100 ; | XXXX | .byte %01000110 ; | X XX | .byte %00000110 ; | XX | .byte %00111110 ; | XXXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %00111100 ; | XXXX | ;A .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01111110 ; | XXXXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %00111100 ; | XXXX | ;B .byte %01111100 ; | XXXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01111100 ; | XXXXX | .byte %01111100 ; | XXXXX | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01111100 ; | XXXXX | ;C .byte %00111100 ; | XXXX | .byte %01100110 ; | XX XX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01100110 ; | XX XX | .byte %00111100 ; | XXXX | ;D .byte %01111100 ; | XXXXX | .byte %01100110 ; | XX XX | .byte %01100010 ; | XX X | .byte %01100010 ; | XX X | .byte %01100010 ; | XX X | .byte %01100010 ; | XX X | .byte %01100110 ; | XX XX | .byte %01111100 ; | XXXXX | ;E .byte %01111110 ; | XXXXXX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01111110 ; | XXXXXX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01111110 ; | XXXXXX | ;F .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01111110 ; | XXXXXX | .byte %01100000 ; | XX | .byte %01100000 ; | XX | .byte %01111110 ; | XXXXXX | ; ; Easter Egg Characters ; ;One ; .byte %00011000 ; | XX | ; .byte %00011000 ; | XX | ; .byte %00011000 ; | XX | ; .byte %00011000 ; | XX | ; .byte %00010000 ; | X | ; .byte %00010000 ; | X | ; .byte %00010000 ; | X | ; .byte %00010000 ; | X | ;Two ; .byte %01111100 ; | XXXXX | ; .byte %01100000 ; | XX | ; .byte %01100000 ; | XX | ; .byte %01100000 ; | XX | ; .byte %01111100 ; | XXXXX | ; .byte %00000100 ; | X | ; .byte %00000100 ; | X | ; .byte %01111100 ; | XXXXX | B .byte %01111100 ; | XXXXX | .byte %01100100 ; | XX X | .byte %01100100 ; | XX X | .byte %01100100 ; | XX X | .byte %01111100 ; | XXXXX | .byte %01010000 ; | X X | .byte %01010000 ; | X X | .byte %01110000 ; | XXX | ;C ; .byte %01111100 ; | XXXXX | ; .byte %01000100 ; | X X | ; .byte %01000100 ; | X X | ; .byte %01000000 ; | X | ; .byte %01000000 ; | X | ; .byte %01001100 ; | X XX | ; .byte %01001100 ; | X XX | ; .byte %01111100 ; | XXXXX | ;E ; .byte %01111100 ; | XXXXX | ; .byte %01100000 ; | XX | ; .byte %01100000 ; | XX | ; .byte %01100000 ; | XX | ; .byte %01111100 ; | XXXXX | ; .byte %01000000 ; | X | ; .byte %01000000 ; | X | ; .byte %01111100 ; | XXXXX | ;G ; .byte %01111110 ; | XXXXXX | ; .byte %01000110 ; | X XX | ; .byte %01000110 ; | X XX | ; .byte %01001110 ; | X XXX | ; .byte %01000000 ; | X | ; .byte %01000000 ; | X X | ; .byte %01000010 ; | X X | ; .byte %01111110 ; | XXXXXX | ;H ; .byte %01100010 ; | XX X | ; .byte %01100010 ; | XX X | ; .byte %01100010 ; | XX X | ; .byte %01100010 ; | XX X | ; .byte %01111110 ; | XXXXXX | ; .byte %01000010 ; | X X | ; .byte %01000010 ; | X X | ; .byte %01000010 ; | X X | ;K ; .byte %00110010 ; | XX X | ; .byte %00110010 ; | XX X | ; .byte %00110010 ; | XX X | ; .byte %00110010 ; | XX X | ; .byte %00111110 ; | XXXXX | ; .byte %00100100 ; | X X | ; .byte %00100100 ; | X X | ; .byte %00100100 ; | X X | N .byte %01100010 ; | XX X | .byte %01100110 ; | XX XX | .byte %01100110 ; | XX XX | .byte %01001010 ; | X X X | .byte %01001010 ; | X X X | .byte %01010010 ; | X X X | .byte %01010010 ; | X X X | .byte %01100010 ; | XX X | O .byte %01111110 ; | XXXXXX | .byte %01100010 ; | XX X | .byte %01100010 ; | XX X | .byte %01100010 ; | XX X | .byte %01000010 ; | X X | .byte %01000010 ; | X X | .byte %01000010 ; | X X | .byte %01111110 ; | XXXXXX | ;R ; .byte %11001000 ; |XX X | ; .byte %11001000 ; |XX X | ; .byte %11001000 ; |XX X | ; .byte %11001000 ; |XX X | ; .byte %11111000 ; |XXXXX | ; .byte %10010000 ; |X X | ; .byte %10010000 ; |X X | ; .byte %11110000 ; |XXXX | S .byte %01111110 ; | XXXXXX | .byte %01000110 ; | X XX | .byte %00000110 ; | XX | .byte %00000110 ; | XX | .byte %01111110 ; | XXXXXX | .byte %01000000 ; | X | .byte %01000010 ; | X X | .byte %01111110 ; | XXXXXX | T .byte %00010000 ; | X | .byte %00010000 ; | X | .byte %00010000 ; | X | .byte %00010000 ; | X | .byte %00010000 ; | X | .byte %00010000 ; | X | .byte %00011110 ; | XXXX | .byte %01111110 ; | XXXXXX | ;Z ; .byte %01111110 ; | XXXXXX | ; .byte %01100010 ; | XX X | ; .byte %01100010 ; | XX X | ; .byte %00110000 ; | XX | ; .byte %00011000 ; | XX | ; .byte %00001100 ; | X XX | ; .byte %01000110 ; | X XX | ; .byte %01111110 ; | XXXXXX | DataColorsNTSC ; Need 10 distinct colors to avoid confusion .byte $06, $18, $24, $34, $48, $54, $84, $A6, $C4, $EE HeadFrame ; Note the leading and trailing $00's .byte $00, $00, $00, $00, $00, $00, $00, $00, $00 ; 9 .byte %00111110 ; | XXXXX | .byte %01101011 ; | XX X XX| .byte %00001000 ; | X | .byte %00001000 ; | X | .byte %00001000 ; | X | .byte %01101011 ; | XX X XX| .byte %00111110 ; | XXXXX | .byte $00, $00, $00, $00, $00, $00, $00 ; 7 DataFrame .byte %00000000 ; | | .byte %01111110 ; | XXXXXX | .byte %11111111 ; |XXXXXXXX| .byte %11111111 ; |XXXXXXXX| .byte %11111111 ; |XXXXXXXX| .byte %01111110 ; | XXXXXX | .byte %00000000 ; | | ; ; Playfield graphics for title screen (40 x 8, generated using Stella-Graph) ; PFData0: ; S .byte $E0, $20, $20, $00, $E0, $20, $20, $E0 PFData1: ; C .byte $DF, $D1, $D1, $D0, $D0, $13, $53, $DF PFData2: ; SI .byte $BE, $B2, $B2, $B0, $BE, $82, $A2, $BE PFData3: ; c .byte $D0, $50, $50, $50, $C0, $00, $00, $00 PFData4: ; i .byte $DB, $5B, $1B, $D3, $D3, $00, $10, $00 PFData5: ; de .byte $7B, $1A, $7A, $5A, $7B, $02, $02, $02 ; ; Name and date bitmap, 32 x 17 ; NameData0 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %11110100 .byte %10010000 .byte %00010000 .byte %00010000 .byte %00110000 .byte %00110000 .byte %00000000 NameData1 .byte %00000000 .byte %11101111 .byte %11001101 .byte %11101101 .byte %00101101 .byte %00101101 .byte %11101111 .byte %00000000 .byte %00000000 .byte %00000000 .byte %11111011 .byte %10011011 .byte %10111010 .byte %10000010 .byte %10001011 .byte %11111000 .byte %00000000 NameData2 .byte %00000000 .byte %01111011 .byte %01101011 .byte %01101011 .byte %01101010 .byte %01101010 .byte %01111010 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00111101 .byte %00110101 .byte %00111101 .byte %10000101 .byte %10111101 .byte %00000000 .byte %00000000 NameData3 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %00000000 .byte %10101111 .byte %10101101 .byte %10101101 .byte %00101101 .byte %11101111 .byte %00000001 .byte %00000001 .byte "Running a race hasn't changed much in a thousand years. " .byte "It is still a supreme test of the individual. When the " .byte "starter's pistol cracks, runners are alone, dependent on " .byte "themselves. The greater their physical and mental conditioning, " .byte "the greater the runners. The immortals of the track become " .byte "chariots of fire, driven by a burning desire to win. " ; ; Avoid $FFF8 to allow use with Supercharger ; ; ; Set up the 6502 interrupt vector table ; org $FFFC .word Start ; Reset .word Start ; IRQ END