[stella] Ho ho ho #2 (horizontal scrolling playfield thing)

Subject: [stella] Ho ho ho #2 (horizontal scrolling playfield thing)
From: Rob <kudla@xxxxxxxxx>
Date: Wed, 22 Dec 1999 23:59:46 -0500
This is my first attempt at doing a horizontally scrolling playfield.  I
haven't seen too much of this so I figured I'd give it a try.  This one I
did more or less from scratch but obviously I used Nick's Playfield source
as a reference.  I'd love any ideas you have for tightening things up since
I'm sure my code is still pretty ghastly and I imagine, completely bloated.
 It's only a couple hours old.

What I'm hoping to do next is vertical scrolling and then, dare I say it,
first-person perspective (faked, no vanishing point, just a horizon.)  I'm
trying to come up with a way to make my scanline loop skip 2 lines between
XOR's, then 4 lines, then 8 etc until the bottom of the screen (or region,
I also want to try a split screen kind of thing - OK, by now you can
probably guess what game I want to do) in as few cycles as possible so I
have room to add sprites and stuff.  I have some ideas but I just wanted to
get this out as proof of concept.  I'm sure it can be done, with the tricky
part being to make it look good.  I also want to make the pattern finer,
but this was the best I could do without having to discover writing the PF
registers mid-scanline.

Anyway, since boing26 was my first real 2600 project following the hacks
and I didn't know about the stella-list then, I figured this one might make
a good introduction.  It's even tangentially related to boing26, you could
think of it as an extreme close-up of a really large boing ball ;)

Happy holidays to all.

Rob
; Horizontally Scrolling Playfield Thing
; Started 22 December 1999
; by Rob Kudla

                processor 6502

; equates from vcs.h since it took me so long to find one that worked

;
; VCS system equates
;
; Vertical blank registers
;
VSYNC   =  $00
VS_Enable = 2
VBLANK  =  $01
VB_Enable      = 2
VB_Disable     = 0
VB_LatchEnable = 64
VB_LatchDisable = 0
VB_DumpPots    = 128
WSYNC   =  $02
RSYNC   =  $03   
;
; Size registers for players and missiles
;
NUSIZ0  =  $04
NUSIZ1  =  $05
P_Single      = 0
P_TwoClose    = 1
P_TwoMedium   = 2
P_ThreeClose  = 3
P_TwoFar      = 4
P_Double      = 5
P_ThreeMedium = 6
P_Quad        = 7
M_Single      = $00
M_Double      = $10
M_Quad        = $20
M_Oct         = $40

;
; Color registers
;
COLUP0  =  $06
COLUP1  =  $07
COLUPF  =  $08
COLUBK  =  $09

;
; Playfield Control
;
CTRLPF  =  $0A
PF_Reflect  = $01
PF_Score    = $02 
PF_Priority = $04
REFP0   =  $0B
REFP1   =  $0C
P_Reflect = $08
PF0     =  $0D
PF1     =  $0E
PF2     =  $0F
RESP0   =  $10
RESP1   =  $11
AUDC0   =  $15
AUDC1   =  $16
AUDF0   =  $17
AUDF1   =  $18
AUDV0   =  $19
AUDV1   =  $1A  ;duh

;
; Players
;
GRP0    =  $1B
GRP1    =  $1C

;
; Single-bit objects
;
ENAM0   =  $1D
ENAM1   =  $1E
ENABL   =  $1F
M_Enable = 2
HMP0    =  $20
HMP1    =  $21

; Miscellaneous
VDELP0  =  $25
VDEL01  =  $26
VDELP1  =  $26
VDELBL  =  $27
RESMP0  =  $28
RESMP1  =  $29
HMOVE   =  $2A
HMCLR   =  $2B
CXCLR   =  $2C
CXM0P   =  $30
CXM1P   =  $31
CXP0FB  =  $32
CXP1FB  =  $33
CXM0FB  =  $34
CXM1FB  =  $35
CXBLPF  =  $36
CXPPMM  =  $37
INPT0   =  $38
INPT1   =  $39
INPT2   =  $3A
INPT3   =  $3B
INPT4   =  $3C
INPT5   =  $3D

;
; Switch A equates.
;
SWCHA   =  $0280
J0_Right = $80
J0_Left  = $40
J0_Down  = $20
J0_Up    = $10
J1_Right = $08
J1_Left  = $04
J1_Down  = $02
J1_up    = $01
;
; Switch B equates
;
SWCHB   =  $0282
P0_Diff = $80
P1_Diff = $40
Con_Color  = $08
Con_Select = $02
Con_Start  = $01

;
; Timer
;
INTIM   =  $0284
TIM1T   =  $0294
TIM8T   =  $0295
TIM64T  =  $0296
TIM1024T = $0297


; local equates
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

CurrentFrame = $80
CurrentPFPtr = $81
PF0Data = $82
PF1Data = $83
PF2Data = $84
PFCarry = $85




; and away we go
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

                ORG     $F000


Init2600:
		SEI				; Disable interrupts.
		CLD				; Clear BCD mode.
		LDX	#$FF
		TXS				; Clear the stack.
       
		LDA	#$00
ClearZeroPage:
		STA	$00,X
		DEX
                BNE     ClearZeroPage           ; clear zero page except for VSYNC
	
                JSR InitGame

MainLoop:       
                JSR NewScreen
                JSR DrawScreen
                JSR Animate
                JSR Overscan
                JMP MainLoop

InitGame:       LDA     #$36      ; a nice shade of red
                STA     COLUPF
                LDA     #$0f
                STA     COLUBK
                LDA     PFData
                STA     PF0Data
                LDA     PFData+1
                STA     PF1Data
                LDA     PFData+2
                STA     PF2Data   ; set up initial playfield
                RTS


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; NewScreen
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

NewScreen:
                LDA     #$02
		STA	WSYNC		; Wait for horizontal sync
		STA	VBLANK		; Turn on VBLANK
                STA	VSYNC		; Turn on VSYNC
		STA	WSYNC		; Skip 3 lines
		STA	WSYNC
		STA	WSYNC
                LDA     #$00
		STA	VSYNC		; Turn VSYNC off

                LDA     #$2C            ; 37 lines of VBLANK
		STA	TIM64T		; 44 * 64 = 2816 (need 2876 cycles of vblank)

                RTS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Animate
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Animate:
                INC     CurrentFrame    ; general all purpose frame counter

                LDA     CurrentFrame
                AND     #$01
                BNE     EndAnimate      ; only animate every other frame

                LDA     #$00
                STA     PFCarry         ; start assuming we don't carry

                LDA     CurrentFrame
                BMI     DoRotateRight   ; switch directions every 4 seconds or so
DoRotateLeft:
                JSR     RotateLeft
                JMP     EndAnimate
DoRotateRight:
                JSR     RotateRight

EndAnimate:
                RTS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; RotateLeft
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

RotateLeft:
                LDA     PF0Data    
                LSR                     ; rotate PF0 to left
                TAY
                AND     #$08
                BNE     LCarry0          ; did we lose a bit?
                JMP     LNoCarry0        ; if not, skip this next
LCarry0:
                LDA     PFCarry
                ORA     #$01            ; turn on bit 0 of PFCarry
                STA     PFCarry
LNoCarry0:
                TYA
                AND     #$F0
                STA     PF0Data

                LDA     PF1Data    
                CLC
                ASL                     ; rotate PF1 to left
                BCS     LCarry1
                JMP     LNoCarry1
LCarry1:
                TAY
                LDA     PFCarry
                ORA     #$02            ; turn on bit 1 of PFCarry
                STA     PFCarry
                TYA
LNoCarry1:
                STA     PF1Data

                LDA     PF2Data    
                CLC
                LSR                     ; rotate PF2 to left
                BCS     LCarry2
                JMP     LNoCarry2
LCarry2:
                TAY
                LDA     PFCarry
                ORA     #$04            ; turn on bit 2 of PFCarry
                STA     PFCarry
                TYA
LNoCarry2:
                STA     PF2Data

                LDA     PFCarry
                AND     #$02            ; mask out all but carry bit 1 (from PF1Data)
                ASL
                ASL
                ASL
                ASL
                ASL
                ASL
                ORA     PF0Data         ; add in bits from PF0Data
                STA     PF0Data         ; and put back in PF0Data

                LDA     PFCarry
                AND     #$04            ; mask out all but carry bit 2 (from PF2Data)
                LSR
                LSR
                ORA     PF1Data
                STA     PF1Data         ; put into PF1Data

                LDA     PFCarry
                AND     #$01            ; mask out all but carry bit 0 (from PF0Data)
                ASL
                ASL
                ASL
                ASL
                ASL
                ASL
                ASL
                ORA     PF2Data
                STA     PF2Data         ; put into PF2Data
                RTS


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; RotateRight
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

RotateRight:
                LDA     PF0Data    
                CLC
                ASL                     ; rotate PF0 to right
                BCS     RCarry0         ; did we lose a bit?
                JMP     RNoCarry0       ; if not, skip this next
RCarry0:
                TAY
                LDA     PFCarry
                ORA     #$01            ; turn on bit 0 of PFCarry
                STA     PFCarry
                TYA
RNoCarry0:
                AND     #$F0
                STA     PF0Data

                LDA     PF1Data    
                CLC
                LSR                     ; rotate PF1 to left
                BCS     RCarry1
                JMP     RNoCarry1
RCarry1:
                TAY
                LDA     PFCarry
                ORA     #$02            ; turn on bit 1 of PFCarry
                STA     PFCarry
                TYA
RNoCarry1:
                STA     PF1Data

                LDA     PF2Data    
                CLC
                ASL                     ; rotate PF2 to left
                BCS     RCarry2
                JMP     RNoCarry2
RCarry2:
                TAY
                LDA     PFCarry
                ORA     #$04            ; turn on bit 2 of PFCarry
                STA     PFCarry
                TYA
RNoCarry2:
                STA     PF2Data

                LDA     PFCarry
                AND     #$04            ; mask out all but carry bit 2 (from PF2Data)
                ASL                     ; need to set bit 4 of PF0
                ASL
                ORA     PF0Data         ; add in bits from PF0Data
                STA     PF0Data         ; and put back in PF0Data

                LDA     PFCarry
                AND     #$01            ; mask out all but carry bit 0 (from PF0Data)
                ASL                     ; need to set bit 7 of PF1
                ASL
                ASL
                ASL
                ASL
                ASL
                ASL
                ORA     PF1Data
                STA     PF1Data         ; put into PF1Data

                LDA     PFCarry
                AND     #$02            ; mask out all but carry bit 1 (from PF1Data)
                LSR                     ; need to set bit 0 of PF2
                ORA     PF2Data
                STA     PF2Data         ; put into PF2Data
                RTS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; DrawScreen
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DrawScreen:                     

                LDA     INTIM 
                BNE     DrawScreen      ; wait till vblank is done
                STA     WSYNC           ; skip rest of current scanline
                STA     VBLANK          ; turn off vblank

                LDA     #$00
                STA     CTRLPF          ; set up playfield (repeat, not reflect)

                LDY     #$BE            ; let's do 190 lines of this

Scanline:
                STA     WSYNC           
                LDA     PF0Data
                STA     PF0
                LDA     PF1Data
                STA     PF1
                LDA     PF2Data
                STA     PF2

                ; now that that's out of the way see what else needs to be done
                TYA
                AND     #$1F            ; XOR every 32nd line
                BNE     DontXOR
                LDA     PF0Data
                EOR     #$FF
                AND     #$F0
                STA     PF0Data         ; reverse PF0
                LDA     PF1Data
                EOR     #$FF
                STA     PF1Data         ; reverse PF1
                LDA     PF2Data
                EOR     #$FF
                STA     PF2Data         ; reverse PF2

DontXOR:        
                DEY
                BNE     Scanline

                LDA     PF0Data



                LDA     #$02
                STA     WSYNC           ; skip rest of current scanline
                STA     WSYNC           ; and another because we're only doing 190
                STA     VBLANK          ; turn VBLANK back on

                ; always ends up reversed - put back
                LDA     PF0Data
                EOR     #$FF
                AND     #$F0
                STA     PF0Data         ; reverse PF0
                LDA     PF1Data
                EOR     #$FF
                STA     PF1Data         ; reverse PF1
                LDA     PF2Data
                EOR     #$FF
                STA     PF2Data         ; reverse PF2

                RTS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Overscan
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Overscan:
                LDY     #$1E
SkipLine:       STA     WSYNC
                DEY
                BNE     SkipLine
                RTS                

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

                ORG     $FF00
PFData:

                .byte $f0,$83,$07 ; we'll get fancier later


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Vectors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

		ORG	$FFFC
Reset           .word   Init2600
IRQ             .word   Init2600
        
;		END

Attachment: hspt.bin
Description: Binary data

kudla@xxxxxxxxx ... http://kudla.org/raindog ... Rob
Current Thread