processor 6502 include vcs.h ; ===================================================================== ; Constants ; ===================================================================== TIM_OVERSCAN = 50 ; TIM64T, 3200 cycles = ~ 42 scanlines TIM_VBLANK = 61 ; TIM64T, 3904 cycles = ~ 51 scanlines TIM_KERNEL = 17 ; T1024T, 17408 cycles = ~229 scanlines ; ===================================================================== ; Variables ; ===================================================================== SEG.U variables ORG $80 ln_seed ds 1 ln_seed_2 ds 1 ln_lightningFrame ds 1 ; counts up from 0 after last flash ln_nextLightning ds 1 ; frame number of next lightning ln_backColor ds 1 ln_lightningType ds 1 ln_tmp ds 1 ln_curMainDir ds 1 ; Current dir of main line. $01: left 1, $ff: right 1 ln_branchState ds 1 ; 64: single branch, 128: double branch, 0: recall ln_branchDir ds 1 ; Current dir of branch ($01 or $ff). Second branch will be inverted. SEG Bank0 ORG $f000 ; ===================================================================== ; Data ; ===================================================================== ; Good-looking seeds LightningSeeds: dc.b 5, 15, 38, 47, 53, 58, 148, 180 ; ===================================================================== ; Subroutines ; ===================================================================== ; --------------------------------------------------------------------- ; Fine-position object. Updates HMx, but does no HMOVE ; IN: ; a xPos, 1..160 ; x object number, 0..4 ; OUT: ; x object number ; c clear ; --------------------------------------------------------------------- FinePosObject SUBROUTINE sec sta WSYNC FinePosDivideLoop: sbc #15 bcs FinePosDivideLoop echo "FinePosDivideLoop between", FinePosDivideLoop, "and", * eor #7 asl asl asl asl sta HMP0,x sta.w RESP0,x ; Prepare for next FinePosObject call, to save space inx rts ; --------------------------------------------------------------------- ; Generate pseudo-rng. ; IN: ; a seed with flags from lda ; OUT: ; a new seed ; --------------------------------------------------------------------- Rnd SUBROUTINE beq .doEor asl beq .noEor bcc .noEor .doEor eor #$1d .noEor rts ; ===================================================================== ; Start of code ; ===================================================================== Start SUBROUTINE ; Clear zeropage cld ldx #0 txa .clearLoop: dex tsx pha bne .clearLoop ; Init vars sta ln_backColor lda #64 sta ln_lightningFrame asl sta ln_nextLightning ; ===================================================================== ; MAIN LOOP ; ===================================================================== MainLoop: ; --------------------------------------------------------------------- ; Overscan ; --------------------------------------------------------------------- Overscan SUBROUTINE sta WSYNC lda #2 sta VBLANK lda #TIM_OVERSCAN sta TIM64T .waitForIntim lda INTIM bne .waitForIntim ; --------------------------------------------------------------------- ; VBlank ; --------------------------------------------------------------------- VBlank SUBROUTINE lda #%1110 .vsyncLoop: sta WSYNC sta VSYNC lsr bne .vsyncLoop lda #2 sta VBLANK lda #TIM_VBLANK sta TIM64T ; check for new flash ldx ln_lightningFrame inx cpx ln_nextLightning bne .noNewFlash ; new Flash: duration = 20+rnd(63) lda ln_seed_2 jsr Rnd sta ln_seed_2 and #63 adc #20 sta ln_nextLightning lda #$11 sta ln_backColor ; new form dec ln_lightningType ldx #0 .noNewFlash: stx ln_lightningFrame ; Background: Flash or black ldy ln_backColor dey bpl .noUnderflow ldy #0 .noUnderflow: sty ln_backColor sty COLUBK ; Lightning color txa ; ln_lightningFrame sbc #10 bcs .noUnderflow2 lda #0 .noUnderflow2: lsr sta ln_tmp eor #$0f cmp #$10 bcc .noUnderflow3 lda #0 .noUnderflow3: sta COLUPF lda #$0a sec sbc ln_tmp bcs .noZero2 lda #0 .dc $2c ; bit xxxx to skip ora .noZero2: ora #$d0 sta COLUP0 sta COLUP1 ; Position of lightning is 60 + ln_seed_2%31 lda ln_seed_2 and #31 clc adc #60 tay ; save for P0 and P1 ; Position ball ldx #4 stx CTRLPF ; Ball size 1, Prio above jsr FinePosObject ; Position P0 and P1 (2 less than ball) dey dey tya ldx #0 stx NUSIZ0 stx NUSIZ1 stx ln_branchState ; Start state 0: recall, missiles disabled jsr FinePosObject ; x is now 1 stx ln_curMainDir tya jsr FinePosObject ; x is now 2 sta WSYNC sta HMOVE ; Position (hide) M stx RESMP0 stx RESMP1 ; Prepare parameters lda ln_lightningType and #7 tax lda LightningSeeds,x sta ln_seed .waitForVBlank: lda INTIM bne .waitForVBlank sta WSYNC sta VBLANK ; --------------------------------------------------------------------- ; Kernel ; --------------------------------------------------------------------- Kernel SUBROUTINE lda #TIM_KERNEL sta T1024T ; Switch on objects ldx #2 stx ENABL lda #$e0 sta GRP0 ; Release missiles. Now they are 3 pixels right of the lightning. sta RESMP0 ; bit #2 is 0 sta RESMP1 ; Loop ; x is not used, so use it for constant #2 .loop: ; ---------- main line ---------- ; Get rnd number (inlined Rnd) lda ln_seed beq .doEor asl beq .noEor bcc .noEor .doEor eor #$1d .noEor sta ln_seed ; Check for direction change and #15 bne .noDirChange ; Switch direction lda ln_curMainDir eor #$fe sta ln_curMainDir .noDirChange: ; Apply wiggle to main dir ldy ln_curMainDir bit ln_seed bmi .noWiggle ; bit #7: wiggle flag bvs .doAdd ; bit #6: add or sub 1 dey dc.b $24 ; BIT to skip next instruction .doAdd: iny .noWiggle: ; Do movement and loop ; y = direction ($02, $01, $00, $ff, $fe) tya asl asl asl asl sta HMP0 sta HMP1 sta HMBL ; ---------- branches ---------- ; Multiplex state lda ln_branchState bne .noRecall ; Recall: do LEFT_3, reset state and set new branch direction for next iteration lda #$30 sta HMM0 sta HMM1 Bit6Set: lda #64 ; i.e. bit #6 is set, also used elsewhere to set v sta ln_branchState ; Direction is opposite of main branch lda ln_curMainDir eor #$fe sta ln_branchDir bne .afterBranch ; unconditional .noRecall: ; Apply wiggle to first branch ldy ln_branchDir bit ln_seed bvs .noBranchWiggle ; bit #6: wiggle flag bmi .doBranchAdd ; bit #7: add or sub 1 dey dc.b $24 ; BIT to skip next instruction .doBranchAdd: iny .noBranchWiggle: ; Set HMM0, same for both states ; y = direction ($02, $01, $00, $ff, $fe) tya asl asl asl asl sta HMM0 ; State either single or double bit ln_branchState bmi .doubleBranch ; Single branch sta HMM1 bpl .checkStateChange ; unconditional ; Double branch .doubleBranch: ; Apply wiggle to second branch ldy ln_branchDir bit ln_seed bmi .noBranchWiggle2 ; bit #7: wiggle flag bvs .doBranchAdd2 ; bit #6: add or sub 1 dey dc.b $24 ; BIT to skip next instruction .doBranchAdd2: iny .noBranchWiggle2: ; invert direction tya eor #$ff adc #$01 asl asl asl asl sta HMM1 ; Switch off one branch randomly lda ln_seed and #%00011000 bne .noSwitchOff stx RESMP0 .noSwitchOff: .checkStateChange: ; Check for state change of branch lda ln_seed and #%00001110 bne .noStateChange ; advance state asl ln_branchState bne .noStateChange ; Initiate recall sta ENAM0 sta ENAM1 stx RESMP0 stx RESMP1 .noStateChange: .afterBranch: ; ---------- move, re-enable and loop ---------- sta WSYNC sta HMOVE ; Only re-enable missiles of not in recall state lda ln_branchState beq .noReEnable sta RESMP0 ; bit #2 is 0 sta RESMP1 stx ENAM0 stx ENAM1 .noReEnable: lda INTIM cmp #4 beq .endLoop jmp .loop .endLoop: ; Switch off objects lda #0 sta ENAM0 sta ENAM1 sta ENABL sta GRP0 sta GRP1 .waitForIntim: lda INTIM bne .waitForIntim jmp MainLoop ; ===================================================================== ; Vectors ; ===================================================================== ORG $fffc .word Start .word Start