[stella] SCSIcide 1.09: Paddle, flicker, and optimization

Subject: [stella] SCSIcide 1.09: Paddle, flicker, and optimization
From: Joe Grand <jgrand@xxxxxxxxxxxxxx>
Date: Mon, 02 Jul 2001 00:12:51 -0400
Hey guys-

Attached is an update to SCSIcide (we'll call this one v1.09).. I've made lots more changes!

o Paddle control for drivehead (It's about time! Manuel, I think you will like this :) - the game plays extremely well with the paddle..
o Button starts each level - this gives the player a break (to rest his/her eyes)
o Don't draw drivehead if game hasn't started or is over - just makes it look nicer
o "Select" switch functionality enabled (for multiple game variations) - can select 8 game variations, the actually variations are TBD
o Changed the scoring to make more sense
o Points for each read bit = Level number * Latency buffer remaining (e.g. the higher the level, the more points you can possibly get)
o Score decreases by 1 if required data bit is missed
o Score decreases by 1 if incorrect data bit is read
o The 1st byte of the score is now the level number


The scoring now plays an important part of the game, since you obviously want to score as high as possible, so you won't want to miss any of the bits or read wrong data, etc.. Makes it fun :)

I have a few timing issues I need to take care of before putting this version of the game on the website.. I think it has to do with the
RandomizeColorTable and GetNextColor routines which call RandomByte (and RandomNibble).. At certain times, the routines take too long, which causes the screen to flicker (a good example of this is when you hit the "Reset" switch to start a new game or sometimes when you read the correct bit of data and the next color needs to be calculated).. Maybe the best thing to do is precalculate all the values before the level starts and disable all screen writing? Any help here would be greatly appreciated!


Also, at this stage in the project, I am willing to accept any help on optimizing the code.. :) I've had some tight spots (especially in the kernel and game calculation routines) that I've luckily been able to fix.. I'm sure my coding skills leave much to be desired..

Thanks in advance for the help! :)

Joe
;
; Project name: SCSIcide v1.09
; Game concept for the Atari 2600 VCS
;
; Copyright 2001
; By Joe Grand, jgrand@xxxxxxxxxxxxxx (http://www.mindspring.com/~jgrand/atari)
; Last update: 1.Jul.2001
; High score: $12329A
;
; dasm source.s -f3 -osource.bin to compile
;
; Tested with StellaX v1.1.3a and Atari 2600jr w/ Supercharger
;
; NEW ADDITIONS
;
; o Paddle control for drivehead
; o Button starts each level
; o Don't draw drivehead if game hasn't started or is over
; o "Select" switch functionality enabled (for multiple game variations)
; o Changed the scoring to make more sense
;   o Points for each read bit = Level number * Latency buffer remaining
;   o Score decreases by 1 if required data bit is missed
;   o Score decreases by 1 if incorrect data bit is read
;   o The 1st byte of the score is now the level (platter) number
;
;
; TO DO
;
; o Fixed all known timing/flicker issues
; o Display title screen (with screensaver)
; o Add sound
; o Easter egg :)
;
;
; [Select - Multiple Game Variations]
;
; o Bit size
; o Bad sectors (cannot touch the drivehead)
;
; [Difficulty Switch]
;
; o Smooth v. track-to-track movement
; o Normal v. oversized drivehead
; o NTSC v. PAL
;

        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             = $B8   ; 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)


        SEG.U vars
        org $80         ; Allocate memory, PIA has 128 bytes of RAM ($80-$FF). Stack from $FF

temp                    ds      1
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
selectDelay             ds      1       ; Timer for select autorepeat

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
currentDataColor        ds      1       ; Color of data to match
dataColorIndex          ds      1       ; Index to the next data color in table
color                   ds      2       ; Color flags (GetNextColor)
color2                  ds      2       ; Color flags (RandomizeColorTable)

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)

gameNumber              ds      1       ; Current game variation being played (0 = game over, 1-8)
gameHasBeenPlayed       ds      1
levelDone               ds      1       ; Flag set when level is completed

rand1                   ds      1       ; Variables for random bit/byte generation routine
rand2                   ds      1
rand3                   ds      1
rand4                   ds      1


        SEG code
        org $F800       ; Limit ourselves to 2K like the old days
        ;org $F000       ; 4K to burn 2732 ROM

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

        ; 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

;
; End the VSYNC period.
;
        LDA  #0
        STA  WSYNC ; Third line of VSYNC.
        STA  VSYNC ; (0)

        LDA  #$2
        STA  VBLANK     ; Start recharge of paddle capacitor

        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 game if reset
        ;
        LDA  gameNumber         ; If this is a new game, but game has already been selected
        BNE  Play
        CLC
        LDA  score+2            ; Game number = 1-8 (0 = game over)
        ADC  #1
Play
        STA  gameNumber
        JSR  DataInit
        JSR  BonusDone          ; Fill latency buffer
        LDA  #1
        STA  levelDone
        STA  gameHasBeenPlayed
        RTS
NoReset

        BCS  NotSelTime         ; Was select pressed?

        LDA  gameHasBeenPlayed  ; If a game is in action or has been played once,
        BNE  NotSelTime         ; disable Select so it doesn't affect game or overwrite previous score

        LDA  selectDelay
        BNE  SaveSelDelay

        CLC
        LDA  score+2            ; Game number is stored in score while it is being selected
        ADC  #1                 ; Increment the game selected
        AND  #7                 ; Make sure the game number is 0-7
        STA  score+2

        LDA  #15                ; Delay
        STA  selectDelay
        JMP  NotSelTime
SaveSelDelay
        DEC  selectDelay
NotSelTime

        ;
        ; Examine Difficulty Switch settings
        ;

        RTS


GameCalc ;******************************* GAME CALCULATION ROUTINES

        LDA  #1
        STA  pot0

        ;
        ; 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

;
; Check to see if latency buffer is empty. If it is, end game
;
        LDA  gauge_PF
        BNE  NotEmpty
        JSR  EndGame
NotEmpty

;
; 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
        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

        ;
        ; 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         ; Store the 2-byte pointer
        STA  scorePtr0,Y
        LDA  #0
        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         ; Store the 2-byte pointer
        STA  scorePtr0,Y
        LDA  #0
        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
        LDA  currentDataColor   ; Set the score color (P0 and P1)
        STA  COLUP0
        STA  COLUP1
        LDX  #3
        STX  NUSIZ0             ; Three copies of player, close together (for score)
        STX  NUSIZ1

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
        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

        ;
        ; Skip down a few lines
        ;
        LDY  #4
DrawWait
        STA  WSYNC
        DEY
        BNE  DrawWait

        ;
        ; 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  #P_Single
        STX  NUSIZ0             ; One copy of player
        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  gameNumber         ; 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  gameNumber         ; 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  #8
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
NxtLine
        STA  WSYNC              ; Wait for line to finish


        LDA  #COLOR_SCORE       ; [0] +4 green
        STA  COLUPF             ; [4] +3
        LDA  #COLOR_GAUGE       ; [7] +4 red
        STA  COLUBK             ; [11] +3 set background

        ; First half of screen
        LDA  gauge_PF           ; [14] +3
        STA  PF0                ; [17] +3
        LDA  gauge_PF+1         ; [20] +3
        STA  PF1                ; [23] +3
        LDA  #0                 ; [26] +3
        STA  PF2                ; [29] +3

        STA  PF2                ; [32] +3  Used in place of NOP for extra cycle
        NOP                     ; [35] +2

        LDY  currentDataColor   ; [37] +4
        STA  COLUBK             ; [41] +3 end of gauge -> background color back to black

        ; Second half of screen - display number of bytes currently retrieved
        STY  COLUPF             ; [44] +3
        LDA  bitCount_PF        ; [47] +4
        STA  PF0                ; [51] +3
        LDA  bitCount_PF+1      ; [54] +4
        STA  PF1                ; [58] +3
        LDA  bitCount_PF+2      ; [61] +4
        STA  PF2                ; [65] +3

        DEX                     ; [68] +5
        BNE  NxtLine            ; [73] +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  gameNumber
        BNE  GameInPlay
        JMP  NoStick
GameInPlay
        ;
        ; Set new vertical position of drivehead based on paddle value
        ;
        LDX  pot0
        STX  verPosP0
NoStick

;
; Check and handle any collisions between P1 (data) and P0 (drivehead)
;
        LDA  gameNumber         ; 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
        JMP  NoButton
SameColor
        LDA  #0                 ; Hide/remove block
        STA  horPosP1,X
        STA  horMotP1,X
        JSR  IncCount           ; Increase bit counter
        JSR  GetNextColor       ; Change to next color
        JSR  IncBonus           ; Refilll latency buffer and give bonus points

        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

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  #$6D               ; 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  gameNumber
        STA  gameHasBeenPlayed

        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
        STA  selectDelay

        ; Color flags
        STA  color
        STA  color+1
        STA  color2
        STA  color2+1

        ; 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

        LDX  #NUMGRP-1
NoMotion2
        STA  horPosP1,X
        STA  horMotP1,X
        DEX
        BPL  NoMotion2

        ; Player 0 (drivehead)
        LDA  #36                        ; Start positioned in center
        STA  verPosP0

        JSR  RandomizeColorTable        ; Set color order of data bits to begin
        JSR  GetNextColor               ; Set color of each data block

        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
        ; Use random number to select next color (to prevent patterns)
        JSR  RandomNibble       ; Random number returned in A

        ; Mod 10
        AND  #$0F
        CMP  #10
        BPL  Mod
        JMP  NoMod
Mod
        CLC
        SBC  #9
NoMod
        STA  temp

        ; If random number < 8, use color+1, else use color
        ; xxxx xx00 0000 0000
        ; Bit set = color has been used
        CMP  #8
        BPL  Msb

        TAX
        LDA  #0
        SEC                     ; Set Carry
Shift
        ROL
        DEX
        BPL  Shift
        STA  temp_stack
        AND  color+1
        BNE  MoreData           ; If color has already been done, get a new value
        LDA  temp_stack
        ORA  color+1            ; Set flag
        STA  color+1
        JMP  ColorDone

Msb
        CLC
        SBC  #7
        TAX
        LDA  #0
        SEC                     ; Set Carry
Shift2
        ROL
        DEX
        BPL  Shift2
        STA  temp_stack
        AND  color
        BNE  MoreData           ; If color has already been done, get a new value
        LDA  temp_stack
        ORA  color              ; Set flag
        STA  color

ColorDone
        LDX  temp
        LDA  dataColorTable,X
        STA  currentDataColor

        RTS


;
; Randomly arrange the color order of data bits
;
RandomizeColorTable
        LDY  #NUMGRP-1
Table
        JSR  RandomNibble       ; Random number returned in A

        ; Mod 10
        AND  #$0F
        CMP  #10
        BPL  Mod2
        JMP  NoMod2
Mod2
        CLC
        SBC  #9
NoMod2
        STA  temp

        ; If random number < 8, use color2+1, else use color2
        ; xxxx xx00 0000 0000
        ; Bit set = color has been used
        CMP  #8
        BPL  Msb2

        TAX
        LDA  #0
        SEC                     ; Set Carry
Shift3
        ROL
        DEX
        BPL  Shift3
        STA  temp_stack
        AND  color2+1
        BNE  Table              ; If color has already been done, get a new value
        LDA  temp_stack
        ORA  color2+1           ; Set flag
        STA  color2+1
        JMP  ColorTableDone

Msb2
        CLC
        SBC  #7
        TAX
        LDA  #0
        SEC                     ; Set Carry
Shift4
        ROL
        DEX
        BPL  Shift4
        STA  temp_stack
        AND  color2
        BNE  Table              ; If color has already been done, get a new value
        LDA  temp_stack
        ORA  color2             ; Set flag
        STA  color2

ColorTableDone
        LDX  temp
        LDA  DataColors,X
        STA  dataColorTable,Y

        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

        ; Clear color flags
        STA  color
        STA  color+1
        STA  color2
        STA  color2+1

        JSR  RandomizeColorTable

        ;
        ; Play tune?
        ;

        LDA  #1
        STA  levelDone

        RTS


;
; Go to next level
;
NextLevel
        LDA  #0
        STA  levelDone

        ;
        ; 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

        RTS


;
; End the game
; Stop all data bits, prevent drive head from moving
; Play tune?
;
EndGame
        LDA  #0
        STA  gameNumber

        LDX  #NUMGRP-1
NoMotion
        STA  horMotP1,X
        DEX
        BPL  NoMotion
        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.
; RandomNibble generates one random nibble (in LSB) 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
        JMP  RandomByte1
RandomNibble
        LDX  #4
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



DataColors      ; Need 10 distinct colors to avoid confusion
                .byte $04, $16, $2E, $34, $44, $6C, $74, $B8, $D4, $1C

HeadFrame
                ; Note the leading and trailing $00's
                .byte $00, $00, $00, $00, $00, $00, $00, $00, $00 ; 9

                .byte   %00011110 ; |   XXXX |
                .byte   %00111011 ; |  XXX XX|
                .byte   %00110000 ; |  XX    |
                .byte   %11111000 ; |XXXXX   |
                .byte   %00110000 ; |  XX    |
                .byte   %00111011 ; |  XXX XX|
                .byte   %00011110 ; |   XXXX |

                .byte $00, $00, $00, $00, $00, $00, $00 ; 7

DataFrame
                 .byte   %00000000 ; |        |
                 .byte   %11111111 ; |XXXXXXXX|
                 .byte   %11111111 ; |XXXXXXXX|
                 .byte   %11111111 ; |XXXXXXXX|
                 .byte   %11111111 ; |XXXXXXXX|
                 .byte   %11111111 ; |XXXXXXXX|
                 .byte   %00000000 ; |        |


; All shapes are upside down to allow decrementing by Y as both
; a counter and a shape index
;
       org $FE00 ; *********************** GRAPHICS DATA

;
; Numeric font
;
FontPage
Zero
;                .byte   %01111100 ; | XXXXX  |
;                .byte   %01100100 ; | XX  X  |
;                .byte   %01100100 ; | XX  X  |
;                .byte   %01100100 ; | XX  X  |
;                .byte   %01100100 ; | XX  X  |
;                .byte   %01100100 ; | XX  X  |
;                .byte   %01100100 ; | XX  X  |
;                .byte   %01111100 ; | XXXXX  |

                .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   %00011000 ; |   XX   |
;                .byte   %00011000 ; |   XX   |
;                .byte   %00011000 ; |   XX   |
;                .byte   %00011000 ; |   XX   |
;                .byte   %00010000 ; |   X    |
;                .byte   %00010000 ; |   X    |
;                .byte   %00010000 ; |   X    |
;                .byte   %00010000 ; |   X    |

                .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 |


        .byte "SCSIcide"
        .byte "J. Grand"
        .byte "2001"

;
; Avoid $FFF8 to allow use with Supercharger
;

;
; Set up the 6502 interrupt vector table
;
        org $FFFC

        .word Start     ; Reset
        .word Start     ; IRQ

        END

Attachment: scsi109.bin
Description: Binary data

Current Thread