processor 6502 include "vcs.h" include "macro.h" include "xmacro.h" seg.u Variables org $80 TrackFrac .byte ; fractional position along track Speed .byte ; speed of car TimeOfDay .word ; 16-bit time of day counter ; Variables for preprocessing step XPos .word ; 16-bit X position XVel .word ; 16-bit X velocity TPos .word ; 16-bit track position TrackLookahead .byte ; current fractional track increment ; Variables for track generation Random .byte ; random counter GenTarget .byte ; target of current curve GenDelta .byte ; curve increment GenCur .byte ; current curve value ZOfs .byte ; counter to draw striped center line Weather .byte ; bitmask for weather NumRoadSegments equ 28 ; Preprocessing result: X positions for all track segments RoadX0 REPEAT NumRoadSegments .byte REPEND ; Generated track curve data TrackLen equ 5 TrackData REPEAT TrackLen .byte REPEND InitialSpeed equ 10 ; starting speed ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; seg Code org $f000 Start CLEAN_START lda #1 sta Random lda #InitialSpeed sta Speed lda #0 sta TimeOfDay+1 lda #0 sta Weather NextFrame VERTICAL_SYNC ; Set up some values for road curve computation, ; since we have some scanline left over. lda #0 sta XVel sta XVel+1 sta XPos lda #70 ; approx. center of screen sta XPos+1 lda TrackFrac sta TPos lda #0 sta TPos+1 lda #10 ; initial lookahead sta TrackLookahead ; VSYNC+36+198+24 = 4+258 = 262 lines TIMER_SETUP 36 ; Initialize array with X road positions jsr PreprocessCurve TIMER_WAIT ; Now draw the main frame TIMER_SETUP 198 jsr DrawSky jsr SetupRoadComponents jsr DrawRoad ; draw the road TIMER_WAIT TIMER_SETUP 24 ; Advance position on track ; TrackFrac += Speed lda TrackFrac clc adc Speed sta TrackFrac bcc .NoGenTrack ; addition overflowed? jsr GenTrack ; yes, generate new track segment .NoGenTrack ; TimeOfDay += 1 inc TimeOfDay bne .NoTODInc inc TimeOfDay+1 lda TimeOfDay+1 ; See if it's nighttime yet, and if the stars come out clc adc #8 and #$3f cmp #$35 ror sta Weather .NoTODInc TIMER_WAIT jmp NextFrame ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Compute road curve from bottom of screen to horizon. PreprocessCurve subroutine ldx #NumRoadSegments-1 .CurveLoop ; Modify X position ; XPos += XVel (16 bit add) lda XPos clc adc XVel sta XPos lda XPos+1 adc XVel+1 sta XPos+1 sta RoadX0,x ; store in RoadX0 array ; Modify X velocity (slope) ; XVel += TrackData[TPos] ldy TPos+1 lda TrackData,y clc ; clear carry for ADC bmi .CurveLeft ; track slope negative? adc XVel sta XVel lda XVel+1 adc #0 ; carry +1 jmp .NoCurveLeft .CurveLeft adc XVel sta XVel lda XVel+1 sbc #0 ; carry -1 nop ; make the branch timings are the same .NoCurveLeft sta XVel+1 ; Advance TPos (TrackData index) ; TPos += TrackLookahead lda TPos clc adc TrackLookahead sta TPos lda TPos+1 adc #0 sta TPos+1 ; Go to next segment inc TrackLookahead ; see further along track dex bpl .CurveLoop rts ; Set road component X positions and enable registers SetupRoadComponents subroutine lda RoadX0 sta HMCLR ; clear HMOVE registers sec sta WSYNC .DivideLoop sbc #15 ; subtract 15 bcs .DivideLoop ; branch while carry still set adc #15 tay lda HMoveTable,y ; lookup HMOVE value sta HMM0 ; set missile 0 fine pos sta HMBL ; set ball fine pos sta HMM1 ; set missile 1 fine pos sta RESM0 ; set missile 0 position sta RESBL ; set ball position sta RESM1 ; set missile 1 position sta WSYNC sta HMOVE ; Make road components converge at horizon ; This will require an additional HMOVE lda #$90 ; right 7 pixels ldy #$70 ; left 7 pixels ldx #$00 ; no movement sta HMM0 sty HMM1 stx HMBL sta WSYNC sta HMOVE rts ; Draw the sky, adding clouds and time-of-day colors. DrawDaytime subroutine lda TimeOfDay+1 ; offset into sunset color table and #$3f tay lda #16 ; initial height of sky segment .SkyLoop2 tax ; height -> X pha ; push original height .SkyLoop lda SunsetColors,y ; get sunset color sta WSYNC ; start scanline sta COLUBK ; set background color lda SunsetColors+2,y ; get cloud color sta COLUPF ; set foreground color lda CloudPFData0,x ; load clouds -> playfield sta PF0 lda CloudPFData1,x sta PF1 lda CloudPFData2,x sta PF2 dex bne .SkyLoop ; repeat until sky segment done iny ; next sky color tya and #$3f ; keep sky color in range 0-63 tay ; sky color -> Y pla ; restore original segment height sec sbc #2 ; segment height - 2 cmp #2 ; done with segments? bcs .SkyLoop2 ; no, repeat ; Draw mountains ; First, load mountain color lda TimeOfDay+1 lsr lsr ; divide time-of-day by 4 and #$f ; keep in range 0-15 tax ; -> Y lda MountainColors,x ; load mountain color sta COLUPF ; set foreground lda GroundColors,x ; load ground color pha ; save it for later ldx #0 stx PF0 stx PF1 ; to avoid artifacts, we have to stx PF2 ; clear previous clouds .MtnLoop lda SunsetColors,y ; get sunset color sta WSYNC ; start scanline sta COLUBK ; set background color lda MtnPFData0,x ; load mountains -> playfield sta PF0 lda MtnPFData1,x sta PF1 lda MtnPFData2,x sta PF2 iny ; next sky color tya and #$3f ; keep sky color in range 0-63 tay ; sky color -> Y inx cpx #7 ; only 7 scanlines for the mountains bne .MtnLoop ; Setup colors and enable road components pla ; restore ground color sta COLUBK ; set background lda #0 sta PF0 sta PF1 sta PF2 rts DrawSky bit Weather bmi DrawNight jmp DrawDaytime ; Draw the night sky, with stars. DrawNight subroutine lda #6 sta ENABL sta COLUPF ldy #0 .MoreStars sta RESBL ; strobe the ball to display a star adc Start,y ; "randomize" the A register bmi .Delay1 .Delay1 ror bcc .Delay2 .Delay2 ror bcs .Delay3 .Delay3 ror bcs .Delay4 .Delay4 iny ldx INTIM cpx #$89 ; timer says we're done? bcs .MoreStars ; nope, make more stars lda #0 sta ENABL ; disable ball rts DrawRoad subroutine lda #2 sta ENAM0 sta ENAM1 ; enable missiles sta COLUPF sta COLUP0 sta COLUP1 ; set their colors too ; Draw road lda TrackFrac asl sta WSYNC ; WSYNC so scanline starts at same place each time asl ; TrackFrac * 4 sta ZOfs ; -> counter for animated stripe ldx #0 ; 0 is farthest segment .RoadLoop lda RoadColors,x ; color of sides and center line sta COLUP0 sta COLUP1 sta COLUPF lda RoadX0+1,x ; get next X coordinate sec sbc RoadX0,x ; subtract this X coordinate clc adc #7 ; add 7 tay ; -> Y lda HMoveTable-3,y ; left side biased left sta HMM0 lda HMoveTable,y ; center line sta HMBL lda HMoveTable+3,y ; right side biased right sta HMM1 sta WSYNC sta HMOVE sta WSYNC ; Make dashed road stripe by using a counter ; initialized to the fractional track position, ; then subracting the PIA timer as an approximation ; to Z value. lda ZOfs sec sbc INTIM sta ZOfs ; ZOfs -= timer rol rol rol ; shift left by 3 sta ENABL ; enable ball (bit 2) sta WSYNC lda RoadWidths,x ; lookup register for missile size sta NUSIZ0 ; store missile 0 size sta NUSIZ1 ; store missile 1 size sta WSYNC inx cpx #NumRoadSegments-1 bne .RoadLoop ; repeat until all segments done ; Clean up road objects lda #0 sta ENAM0 sta ENAM1 sta ENABL sta COLUBK sta NUSIZ0 sta NUSIZ1 rts ; Get next random number NextRandom subroutine lda Random lsr bcc .NoEor eor #$d4 .NoEor: sta Random rts ; Generate next track byte GenTrack subroutine ; Shift the existing track data one byte up ; (a[i] = a[i+1]) ldx #0 .ShiftTrackLoop lda TrackData+1,x sta TrackData,x inx cpx #TrackLen-1 bne .ShiftTrackLoop ; Modify our current track value and ; see if it intersects the target value lda GenCur clc adc GenDelta cmp GenTarget beq .ChangeTarget ; target == cur? bit GenTarget ; we need the sign flag bmi .TargetNeg ; target<0? bcs .ChangeTarget ; target>=0 && cur>=target? bcc .NoChangeTarget ; branch always taken .TargetNeg bcs .NoChangeTarget ; target<0 && cur target cmp GenCur bmi .TargetBelow ; current > target? jsr NextRandom ; get a random value and #$f ; mask to 0..15 jmp .TargetAbove .TargetBelow jsr NextRandom ora #$f0 ; mask to -16..0 .TargetAbove ora #1 ; to avoid 0 values sta GenDelta ; -> delta lda GenCur .NoChangeTarget ; Store the value in GenCur, and also ; at the end of the TrackData array sta GenCur sta TrackData+TrackLen-1 rts ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; HMOVE table from -7 to +8 HMoveTable hex 7060504030201000f0e0d0c0b0a09080 ; Sunset table SunsetColors hex 00000000a07060504446383efc0c9f9f hex aeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae hex 9f9f9f0cfc3e384644506070a0000000 hex 00000000000000000000000000000000 hex 0000 ; for overflow MountainColors hex 00021012f4f6e6264604020000000000 GroundColors hex 00c0c2c4c6c8c8e6e4e2e00000000000 RoadColors hex 020202 hex 040404 hex 060606 hex 08080808 hex 0a0a0a0a0a hex 0c0c0c0c0c0c hex 0e0e0e0e0e0e0e0e0e RoadWidths hex 000000000000 hex 10101010 hex 1010101010 hex 202020202020 hex 202020202020202020 ; Cloud data CloudPFData0 .byte #%10000000 .byte #%11100000 .byte #%10000000 .byte #%00000000 .byte #%00000000 .byte #%10000000 .byte #%11100000 .byte #%00000000 CloudPFData1 .byte #%11000000 .byte #%11110000 .byte #%11100001 .byte #%00000011 .byte #%00000111 .byte #%11000000 .byte #%00000000 .byte #%00011000 CloudPFData2 .byte #%00000000 .byte #%00001110 .byte #%00011111 .byte #%00000111 .byte #%01100000 .byte #%11000000 .byte #%00000110 .byte #%00000000 ; Mountain data MtnPFData0 .byte #%00000000 .byte #%00000000 .byte #%00000000 .byte #%00000000 .byte #%00000000 .byte #%10000000 .byte #%11100000 MtnPFData1 .byte #%00000000 .byte #%00010000 .byte #%00110000 .byte #%01111000 .byte #%11111100 .byte #%11111110 .byte #%11111110 MtnPFData2 .byte #%00000000 .byte #%00000000 .byte #%00000000 .byte #%00000100 .byte #%00101110 .byte #%01111111 .byte #%11111111 CarSprite .byte 0 .byte #%10000001;-- .byte #%10111101;-- .byte #%11111111;-- .byte #%10000001;-- .byte #%10111101;-- .byte #%01011010;-- .byte #%01011010;-- .byte #%01011010;-- .byte #%00100100;-- .byte #%11111111;-- .byte #%10111101;-- .byte #%00111100;-- ; Epilogue org $fffc .word Start .word Start