[stella] Unending VDEL hell

Subject: [stella] Unending VDEL hell
From: Glenn Saunders <cybpunks2@xxxxxxxxxxxxx>
Date: Mon, 29 Apr 2002 00:49:38 -0700
Well, I give up.

I tried to take Thomas' suggestions from a while back and implement them as literally as I could, and no matter what I do I either get 1-scanline precision with the 2nd car down a line or both cars offset by 2 scanlines or on the same scanline but animating in 2-scanline jumps.

I've done a lot of experimentation that should have at least accidentally allowed me to find the right combination of logic and nothing is working so I've got to just dump this onto the list and beg for help, preferably for someone to just fix the actual sourcecode ;) I've spent hours tinkering with this and I just can't figure it out, so it's not like I haven't put in the effort. If nobody comes forward to fix this I'll have to probably go back to the drawing board and do some sprite isolated demos based on analyzing various sourcecode out there because obviously I do not understand this enough to be able to diagnose this behavior. The trial and error worked okay when tweaking the playfield but it is now leading me nowhere.

I still think that after both cars and both men can be moved around the screen without tearing that the rest will be downhill. I managed to handle the playfield okay, and should be able to code in the I/O, animation, sound, and AI okay, but I'm afraid, due to time-constraints, the only way I may be able to finish this game is if someone works over my sourcecode to get over this roadblock. If I can't solve this on my own in the next few months it will be a sign that I simply don't have the time to focus enough concentration on this. Each time I come back to the sourcecode after a long absence I have to shake off the rust so I'm always 1 step forward, 2 steps back. This CAN'T be as hard as it seems, but it is eluding me.

I added a simple framecounter in order to get the cars to move up the screen in order to see whether they move in single or double scanline jumps.

As it is, I'm not that happy with the idea of having to have both GRPx writes be time-critical. If Combat and other games use an approach that allows one of them to be non-critical, then I may need to use that because I see no way to wedge the missile stuff anywhere but in the middle of the scanline which will lead me into having to have multiple kernels to intelligently write to ENAMx ahead or after the X positions of the missiles. Am I going to have to put both writes on the same scanline maybe? I've got enough free cycles to move things around somewhat since abandoning the no-WSYNC approach in favor of (ultimately) only doing 1 missile routine per line-pair.


Glenn Saunders - Producer - Cyberpunks Entertainment Personal homepage: http://www.geocities.com/Hollywood/1698 Cyberpunks Entertainment: http://cyberpunks.uni.cc
;Death Derby 2002
; By Glenn Saunders

;LINEAGE:

; How to Draw an Asymmetric Reflected Playfield
; by Roger Williams

; with a lot of help from Nick Bensema
; (this started out as his "How to Draw a Playfield")

; Extra help from Thomas Jentzsch

	processor 6502
	include vcs.h

; CONSTANTS

; SCREEN GEOMETRY 	 CTR  ACTUAL 	SUBTOTAL
;----------------------- ---- --------- --------
ScoreHeight 		= 20; 20	20
; 	TopBorder	= 2   2		22
PlayfieldHeight		= 88; (176)	198
;	BottomBorder	= 2   2		200
;			 		---
;					200 visible lines

OverScanHeight		= 22; 22	222
;			 		---
;					222 total lines

;FIXED COLORS
Color0 = $34
Color1 = $C8
ColorPF = $0F;White
ColorSafeZone = $1E;Yellow

;INITIAL COUNTER VALUES
TombstoneHeight 	= 8  ; 8*2 = 16 (single scanlines) high per tombstone
TombstoneRowsPerScreen 	= 11-1 ; 11 rows of tombstones per screen
CarHeight 	= 8-1  ; 8 doubled scanlines (used as offset)



; constants that reference rotation index pointers
PointUp 	= 0
PointRight 	= 4
PointDown 	= 8
PointLeft 	= 12

TurnOnSpriteReflect = %00001000
TurnOffSpriteReflect = 0

CarPage		= $FF;00
CarDataAlign	=    $65

InitialSafeZone	= %10010000 ; initial safezone border graphics
SafeZone2	= %10000000 
FlipThreshold	= #9 ; flip sprite when
		    ;P0_CurrentRotationIndex >= 9









    SEG.U vars
    org $80

;TEMPORARY VARIABLES (OVERLAYS)

Temp       	ds 1
LastController_temp_State = Temp

Temp2		ds 1
CurrentControllerState = Temp2
Compensate ds 1; determines whether to compensate for VDEL

P0_P1_PARAMETER ds 1


;CurrentScanLine ds 1
; use Y instead

;CurrentTombstoneRow	ds 1; 11->0 outer loop counter
; use X instead

CurrentTombstoneSubRow 	ds 1; 7->0 inner loop counter


; ALTERNATING SAFEZONE BORDER STRIP
CurrentSafeZone ds 1
; PLAYFIELD BITMAP
PF1DataRAM	ds TombstoneRowsPerScreen+1
PF2DataRAM	ds TombstoneRowsPerScreen+1
PF1bDataRAM	ds TombstoneRowsPerScreen+1
PF2bDataRAM	ds TombstoneRowsPerScreen+1

;sprite positiong

P0_Y		ds 1
P1_Y		ds 1

P0_X		ds 1
P1_X		ds 1

P0_YFullres	ds 1
P1_YFullres	ds 1

M0_Y		ds 1
M1_Y		ds 1

M0_X		ds 1
M1_X		ds 1




FrameCounter1	ds 1; use this for timing purposes

;sprite graphics pointers
P0_CurrentRotationSequence ds 1; 0-15
P1_CurrentRotationSequence ds 1; 0-15

;these above reference addresses that get stored below

P0_CurrentFramePtr ds 2; this will wind up being a constant $FF in the high byte
P1_CurrentFramePtr ds 2; this will wind up being a constant $FF in the high byte

M0_CurrentFramePtr ds 2
M1_CurrentFramePtr ds 2


LastControllerState ds 1; store previous state of the controller ports

;CurrentController_P0_State ds 1





;P0_CurrentRowCounter ds 1
;P1_CurrentRowCounter ds 1
;M0_CurrentRowCounter ds 1
;M1_CurrentRowCounter ds 1


; stolen from OUTLAW source
;PF0Array            ds 1   ; PF0 array, 18 Bytes
;ScoreshapeLow01     ds 1   ; Shape of player 1 lower score digit
;ScoreshapeLow02     ds 1   ; Shape of player 2 lower score digit
;ScoreshapeHi01      ds 1   ; Shape of player 1 higher score digit
;ScoreshapeHi02      ds 1   ; Shape of player 2 higher score digit
;bcdScore01Backup    ds 1   ; Backups score for player 1
;rightScoreOnOff     ds 1   ; 0F for Right score on, 00 for off
gameState           ds 1   ; 0F running/EF saver/00 select/XX counter



    SEG code
    org  $F000

;FrameCounter = $93 was going to try some color cycling


;                BRANCH CHEAT SHEET                  
;                                                    
;        opcode          flags           integer type
;======= =============== =============== ============
;A == M  BEQ             N=0, Z=1, C=1               
;A != M  BNE             N=?, Z=0, C=?               
;A >= M  BCS             N=?, Z=?, C=1   (unsigned)  
;A > M   BCS and BNE     N=?, Z=?, C=1   (unsigned)  
;A < M   BCC             N=?, Z=0, C=0   (unsigned)  

;A >= M  BPL             N=0,;Z=?, C=?   (signed)    
;A > M   BPL and BNE     N=0,;Z=0, C=?   (signed)    
;A < M   BMI             N=1;;Z=0; C=?   (signed)    


Start
	SEI  ; Disable interrupts, if there are any.
	CLD  ; Clear BCD math bit.
	LDX  #$FF
	TXS  ; Set stack to beginning.


	; CLEAR OUT RAM
	LDA #0

KeepZeroing
	STA 0,X
	DEX
	BNE KeepZeroing

SetColors
	LDA #$00
	STA COLUBK  ; Background will be black.
	
	LDA #ColorPF
	STA COLUPF
	
	LDA #PF_Reflect
	STA CTRLPF  ; set playfield to reflect

	LDA #Color0
	STA COLUP0
	LDA #Color1
	STA COLUP1
	
	LDA #60
	STA FrameCounter1

InitializeControllerState
	LDA #0
	STA SWACNT; set controller ports for input first



InitializeSprites
; set high byte for frame pointers
	; this should only have to be done once for the whole game
	; since all the car graphics are on the same page of ROM
	LDA #CarPage; $FF
	STA P0_CurrentFramePtr+1
	STA P1_CurrentFramePtr+1

	;LDA #0
	;STA Compensate

	
; set initial X,Y of P0, P1
	LDA #27; start P0 +10 from the left
	STA P0_X
	LDA #94; start P0 halfway down
	STA P0_YFullres
	SEC
	SBC Compensate
	LSR
	STA P0_Y


	;LDA #127; start P1 -10 the right
	LDA #27
	STA P1_X
	
	LDA #94; start P1 halfway down
	STA P1_YFullres
	LSR
	STA P1_Y



; set initial rotations
	LDX #PointRight; start P0 car pointing right
	STX P0_CurrentRotationSequence; store for later


	LDX #PointLeft; start P1 car pointing left
	STX P1_CurrentRotationSequence; store for later



	LDX #TombstoneRowsPerScreen
InitializeTombstones; copy from ROM to RAM

	LDA #InitialSafeZone
	STA CurrentSafeZone

	LDA PF1Data,X
	STA PF1DataRAM,X

	LDA PF2Data,X
	STA PF2DataRAM,X

	LDA PF1bData,X
	STA PF1bDataRAM,X

	LDA PF2bData,X
	STA PF2bDataRAM,X
	
    	DEX
    	BPL InitializeTombstones
    	
InitializeControllerParse
	LDA SWCHA; read ports once
	STA CurrentControllerState; store to temp variable
	STA LastControllerState
MainLoop
	JSR VerticalBlank
	JSR GameCalc
	JSR DrawScreen
	JSR OverScan
	JMP MainLoop      ;Continue forever.









ReadController1
        LDA CurrentControllerState
	AND %00000011
	TAY
	

	LDA LastControllerState
	AND %00000011
	STA LastController_temp_State
	
	CPY LastController_temp_State; see if there was no change
	BNE .Merge1
	RTS

.Merge1
	ASL
	ASL ; move it over two to the left
	STA LastController_temp_State
	TYA
	ORA LastController_temp_State; merge
	TAY

	JMP .MainParse

ReadController0

	LDA CurrentControllerState
	AND %00111111
;DO CONTROLLER 0 FIRST
	LSR
	LSR
	LSR
	LSR; move the 4 bits of P0 data over
	;AND %00000011
	TAY ; move it out of the way

	
	; Y will hold the current controller state
	; A or LastController_temp_State will hold previous controller state
	
	LDA LastControllerState
	AND %00111111
	LSR
	LSR
	LSR
	LSR; move the 4 bits of P0 data over
	STA LastController_temp_State; temp variable

	; test for no change

	CPY LastController_temp_State; see if there was no change
	BNE .Merge0
	RTS

.Merge0	
	ASL
	ASL ; move it back over two to the left
	STA LastController_temp_State
	TYA
	ORA LastController_temp_State; merge
	TAY

.MainParse:
	; scan through both tables.
	LDX #7
	TYA
.KeepScanning:
	CMP EncodingTable,X
	BEQ .PickDirection
	DEX
	BPL .KeepScanning
	;abrupt twist because no match
	RTS

.PickDirection:
	CLC
	CPX #4
	BCS .FoundCurrentMatchRight; if X >= 4 then turning right

        ;else left
.FoundCurrentMatchLeft:
	LDX P0_P1_PARAMETER
	;LDX #0
	
	; decrement rotation counter and exit
	LDA P0_CurrentRotationSequence,X
	; else decrement
	CMP #0
	BEQ .SetHigh; if A = 0 then set loop back to 15
	DEC P0_CurrentRotationSequence,X; else go ahead and decrement
	RTS 
.SetHigh
	LDA #15
	STA P0_CurrentRotationSequence,X
	RTS

.FoundCurrentMatchRight
	LDX P0_P1_PARAMETER
	;LDX #0
	
	; decrement rotation counter and exit
	LDA P0_CurrentRotationSequence,X
	; else decrement
	CMP #15
	BEQ .SetLow; if A = 0 then set loop back to 15
	INC P0_CurrentRotationSequence,X; else go ahead and decrement
	RTS
.SetLow
	LDA #0
	STA P0_CurrentRotationSequence,X
	RTS
; end of controller check



VerticalBlank
	LDX  #0
	LDA  #2
	STA WSYNC  
	STA WSYNC
	STA WSYNC
	STA VSYNC ; Begin vertical sync.
	STA WSYNC ; First line of VSYNC
	STA WSYNC ; Second line of VSYNC.
	LDA #44
	STA TIM64T
	LDA #0
	STA CXCLR
	STA WSYNC ; Third line of VSYNC.
	STA VSYNC ; (0)
	RTS 




;SUB
ClearTombstones
	LDX #TombstoneRowsPerScreen
KeepClearing 
	DEX
	STA <PF1DataRAM,X
	STA <PF2DataRAM,X
	STA <PF1bDataRAM,X
	STA <PF2bDataRAM,X
	BNE KeepClearing
	RTS  

;SUB
OverScan
	LDX #OverScanHeight

.KillLines
	STA WSYNC
	DEX
	BNE .KillLines

	RTS



;HORIZONTAL POSITIONING SUBROUTINE FROM MANUEL POLIK's GUNFIGHT	
;SUB (alternate beginning of below)
PosP1
            CLC 
            ADC #$00; changed from 08
;SUB
PosP0
            CMP #$A0
            BCC .NH2
            SBC #$A0
.NH2
            TAY
            LSR
            LSR
            LSR
            LSR
            STA Temp
            TYA
            AND #15
            CLC
            ADC Temp
            LDY Temp
            CMP #15
            BCC .NH
            SBC #15
            INY
.NH
            EOR #7
            ASL
            ASL
            ASL
            ASL
            STA HMP0,X
            INY                         ; Waste 5 cycles, 1 Byte
            STA WSYNC
            INY                         ; Waste 7(!) cycles, 1 Byte
            BIT 0                       ; Waste 3 cycles, 2 Byte
.PosDelay   DEY
            BPL .PosDelay
            STA RESP0,X
            RTS
;/HORIZONTAL POSITIONING SUBROUTINE	


	;this will have been modified by the controller routines
UpdateCurrentFramePtr
	LDY #TurnOffSpriteReflect; initialize to no flip
	
	LDX P0_P1_PARAMETER
	
	;this will load either P0 or P1 depending on P0_P1_PARAMETER
	LDA P0_CurrentRotationSequence,X;0-15
	TAX

	CPX #FlipThreshold
	BMI .Continue0   ; if A < 9 then sigN will be 1
			; so do not flip, just .Continue
.FlipSprite0
	LDY #TurnOnSpriteReflect

.Continue0
	STX Temp
	;THIS WILL STORE TO P0 or P1 depending on P0_P1_PARAMETER
	LDX P0_P1_PARAMETER
	STY REFP0,X
	LDX Temp

	; load low byte pointer to the start
	; of gfx for the current frame
	LDA RotationSequence,X
        SEC ; set carry bit

       	LDX P0_P1_PARAMETER
        SBC P0_Y,X; subtract the Y position of P0 or P1

        ADC #CarHeight
	; POINTER is initialized as offset -PO_Y away from the last scanline
	; of CarRotation0 graphics.
	; STORE THE LOW BYTE POINTER

	TAY; get RotationSequence out of the way

	;THIS WILL STORE TO P0 or P1 depending on P0_P1_PARAMETER
	LDA P0_P1_PARAMETER
	ASL ; multiply by 2, yielding either 0 or 2
	TAX ; use as an offset index
	
	TYA; restore RotationSequence
        STA P0_CurrentFramePtr,X

	RTS

;SUB
GameCalc
	;BRK
	DEC FrameCounter1
	BNE .NoMove

	LDA #60
	STA FrameCounter1
	INC P0_YFullres
	INC P1_YFullres
.NoMove

	

        LDX #0 ; store to HMP0
        LDA P0_X; load current X value of P0
        JSR PosP0; call coarse pos calc routine for P0

	LDA #2
	STA WSYNC

        LDX #1; store to HMP1
        LDA P1_X; load current X value of P1
        JSR PosP1; call coarse pos calc routine for P1
        
	LDA #2
	STA WSYNC
	STA HMOVE

	;later on we need to add the horizontal positioning
	;of the two missiles



	LDA P0_YFullres
	LSR
	;SEC
	;SBC #1
	STA P0_Y

	LDA P1_YFullres
	LSR
	STA P1_Y
	
;	LDA P1_YFullres
;	TAX
;	EOR #1
;	BEQ .NoCompensation
;	TXA
;	SEC
;	SBC #1
;	LSR
;	JMP .SetY
;.NoCompensation
;	TXA
;	LSR
;.SetY
;	STA P1_Y
	
; NOW ON : ODD


;assume that car 0 is on the same line with car 1, when it's
;P0_YFullres is *one* below P1_YFullres.'

;----CAR0--------------
;------------------CAR1

;IF Y = EVEN (for both cars)
; NO COMPENSATION
;-> VDELP0 = 1
;-> VDELP1 = 0

;IF Y = ODD (for both cars)
; P0_Y = P0_Y + 1 (compensate)
;-> VDELP0 = 0
;-> VDELP1 = 1

	LDA P0_YFullres		;of player position
	EOR #1
	; IF ODD,THEN VDEL
	BNE .SetVDELP0
	LDA 1
	JMP .ClearVDELP0
.SetVDELP0
	LDA 0
.ClearVDELP0
	STA VDELP0

	LDA P1_YFullres		;of player position
	EOR #1
	BNE .SetVDELP1
	LDA 0
	JMP .ClearVDELP1
.SetVDELP1
	LDA 1
.ClearVDELP1
	STA VDELP1	



	LDA SWCHA; read ports once
	STA CurrentControllerState; store to temp variable

	LDA #0
	STA P0_P1_PARAMETER
	;JSR ApplyMotion
	JSR ReadController0
	JSR UpdateCurrentFramePtr

	LDA #1
	STA P0_P1_PARAMETER
	;JSR ApplyMotion
	JSR ReadController1
	JSR UpdateCurrentFramePtr
	
	LDA CurrentControllerState; read back stored ports
	STA LastControllerState; store for next time
	
	RTS
	
	;END GAMECALC




DrawScreen
	LDA INTIM
	BNE DrawScreen ; FINISH WAITING OUT GAMECALC AND THEN DRAW
	
	STA WSYNC
	STA VBLANK  ;End the VBLANK period with a zero.
	

	LDA #0
	STA PF0
	STA PF1
	STA PF2; zero out playfield


	; reset fine-control on sprites so that they don't get
	; repositioned with the missiles
	STA HMP0
	STA HMP1

	LDX #ScoreHeight; burn through 20 total scanlines
DrawScore
	STA WSYNC
	DEX
	BNE DrawScore

TopBorder ; 2 lines
	LDA #$FF 
	STA PF0 ; draw a solid line
	STA PF1
	STA PF2
	LDA #InitialSafeZone
	STA WSYNC
	; draw a solid line except for the safezone border
	STA PF0 

	;ScanGo will finish the line off

FinalInitialization
	;INITIALIZE TOMBSTONE ROW COUNTERS
	LDX #TombstoneRowsPerScreen+1
	
	;TombstoneRowsPerScreen held in X register through kernel
	LDA #TombstoneHeight+1
	; NOTE: pad these by one because the first time through it decrements
	; at the top of the loop

	STA CurrentTombstoneSubRow; store TombstoneHeight to RAM

	

	LDY #PlayfieldHeight ; store scanline counter in Y throughout
		; the whole kernel.  Decrement every other scanline





;THIS AREA IS A 2-LINE KERNEL

;START FIRST LINE OF LINE-PAIR
ScanLoop1
	;INNER LOOP
	DEC CurrentTombstoneSubRow ;next row of current tombstone
	BNE ScanGo1

	LDA #TombstoneHeight; if counter reaches zero, reset to 7
	STA CurrentTombstoneSubRow; and store back to RAM
	DEX ;CurrentTombstoneRow = CurrentTombstoneRow - 1
	    ;outer loop - indexes next tombstone row




ScanGo1
        TYA                     ;+2     
        SEC                     ;+2
        SBC  P0_Y               ;+3
        ADC #CarHeight+1        ;+2
        BCS .drawP0             ;+2/+3
        LDA #0                  ;+2
        BEQ .setP0              ;+3
.drawP0:
        LDA (P0_CurrentFramePtr),Y ; +5
.setP0:

	                              ;MAX 20 cycles
	STA WSYNC
        STA.W GRP0                ;+3
	; Time to begin cycle counting

				;	0
	;STA HMOVE		;+4	4
	; in order for a proper 2-line kernel w/VDEL to operate properly,
	; it is necessary to write to GRP0 on the first of the two line-pairs
	; right after WSYNC.  You can then update GRP1 at any time between then
	; and the end of the 2nd line-pair.
	STA GRP0                ;+3


	;will probably have to do an HMOVE on every line to 
	;simplify logic.  HMOVE is only necessary for the missiles

	LDA <PF1DataRAM-1,X       ;+4	8

	;I'm not sure why the -1 is necessary here to make it work


	STA PF1               	;+3  = *11*  < 29	;PF1 visible
	
	LDA <PF2DataRAM-1,X       ;+4	15
	STA.W PF2                 ;+3  = *19*  < 40	;PF2 visible
	;NOP
	NOP			;+2	20
	NOP			;+2	22
	NOP			;+2	24
	NOP			;+2	26
	NOP			;+2	28
	NOP			;+2 	30
	NOP			;+2	32
	;NOP			;+2	34
	
	LDA <PF1bDataRAM-1,X	;+4	38

	;PF1 no longer visible, safe to rewrite
	STA PF1			;+3 = *41*


	LDA <PF2bDataRAM-1,X	;+3     44
	
	;PF2 rewrite must begin at exactly cycle 45!!, no more, no less
	STA PF2			;+3  = *47*  ; > 46 PF2 no longer visible
	;TXS			;+2	49
	STX Temp
	NOP			;+2	51

	;preserve X register
			
	NOP			;+2	53
	NOP			;+2	55
	NOP			;+2	57
	NOP			;+2	59
	NOP			;+2	61
	NOP			;+2	63
	;NOP			;+2	65
	;NOP			;+2	67
	LDA #0			;+2	69
	; CLEAR OUT PLAYFIELD FOR SPRITE HANDLING BELOW
	STA PF2			;+3	72
	STA PF1			;+3	75	
	STA HMP0		;+3
	STA HMP1		;+3
	STA HMOVE		;+4	4

	
	;PERFECT TIMING, NO WSYNC NECESSARY
	; PUT ONE BACK IN IF THE NOPs are replaced with code
	; that has varying timing
	
;START SECOND LINE OF LINE-PAIR
ScanGo2	; do all the sprite routines here
	; WSYNC PUT BACK IN IF NECESSARY
	; start cycle counting again
				; 	0



P1Loop

        TYA                     ;+2     
        SEC                     ;+2
        SBC  P1_Y               ;+3
        ADC #CarHeight+1        ;+2
        BCS .drawP1             ;+2/+3
        LDA #0                  ;+2
        BEQ .setP1              ;+3
.drawP1:
        LDA (P1_CurrentFramePtr),Y 
.setP1:
        STA GRP1                ;+3
        



.endLoop
	LDX Temp
	;TSX; grab tombstone counter back again
	DEY ; Decrement ScanlinePair counter
	BNE ScanLoop1; keep going until scanline counter reaches 0
DoBorder
	LDA #$FF ; this may overload the timing of the sprite kernel line
		 ; and have to be moved under WSYNC!!

	STA WSYNC; finish last line of sprites
	STA PF0 ; draw a straight line
	STA PF1
	STA PF2
	STA WSYNC; line 1 of bottom border
	STA WSYNC; line 2 of bottom border

EndKernel
	STA VBLANK ; Make TIA output invisible,
	; Y is already zero
	STY PF0
	STY PF1
	STY PF2
	STY GRP0
	STY GRP1
	STY ENAM0
	STY ENAM1
	STY ENABL
	RTS; END DrawScreen

;THE DATA        
	org $FE00

;stores first 2 bits of quadrature encoding data
;and the expected next set


EncodingTable
              ;        
;RotatingLeft  ;   RLRL
	.byte %00000001
	.byte %00000111
	.byte %00001110	
	.byte %00001000; X < 4 = left

	      ;        
;RotatingRight ;   RLRL; X > 3 = right
	.byte %00000010
	.byte %00001011
	.byte %00001101	
	.byte %00000100

scoreshapedata: 
		.byte %01000100 ; | X   X  |
                .byte %10101010 ; |X X X X |
                .byte %10101010 ; |X X X X |
                .byte %10101010 ; |X X X X |
                .byte %01000100 ; | X   X  |

                .byte %11001100 ; |XX  XX  |
                .byte %01000100 ; | X   X  |
                .byte %01000100 ; | X   X  |
                .byte %01000100 ; | X   X  |
                .byte %11101110 ; |XXX XXX |

                .byte %11101110 ; |XXX XXX |
                .byte %00100010 ; |  X   X |
                .byte %01100110 ; | XX  XX |
                .byte %10001000 ; |X   X   |
                .byte $11101110 ; |XXX XXX |

                .byte %11101110 ; |XXX XXX |
                .byte %00100010 ; |  X   X |
                .byte %01000100 ; | X   X  |
                .byte %00100010 ; |  X   X |
                .byte %11001100 ; |XX  XX  |

                .byte %10001000 ; |X   X   |
                .byte %10101010 ; |X X X X |
                .byte %11101110 ; |XXX XXX |
                .byte %00100010 ; |  X   X |
                .byte %00100010 ; |  X   X |

                .byte %11101110 ; |XXX XXX |
                .byte %10001000 ; |X   X   |
                .byte %11001100 ; |XX  XX  |
                .byte %00100010 ; |  X   X |
                .byte %11001100 ; |XX  XX  |

                .byte %01000100 ; | X   X  |
                .byte %10001000 ; |X   X   |
                .byte %11101110 ; |XXX XXX |
                .byte %10101010 ; |X X X X |
                .byte %11101110 ; |XXX XXX |

                .byte %11101110 ; |XXX XXX |
                .byte %00100010 ; |  X   X |
                .byte %01000100 ; | X   X  |
                .byte %10001000 ; |X   X   |
                .byte %10001000 ; |X   X   |

                .byte %11101110 ; |XXX XXX |
                .byte %10101010 ; |X X X X |
                .byte %11101110 ; |XXX XXX |
                .byte %10101010 ; |X X X X |
                .byte %11101110 ; |XXX XXX |

                .byte %11101110 ; |XXX XXX |
                .byte %10101010 ; |X X X X |
                .byte %11101110 ; |XXX XXX |
                .byte %00100010 ; |  X   X |
                .byte %01000100 ; | X   X  |

	org $FF00
;               REFLECTED  PLAYFIELD            
;                                               
; PF0|   PF1  |  PF2   ||   PF2b |   PF1b |PF0b|
;4567|76543210|01234567||76543210|01234567|7654|


;INITIAL TITLE GRAPHICS
;TO BE COPIED TO RAM

;SafeZone ;     7654----
;	.byte %10011001


PF1Data  ;D    76543210
	.byte %01111001
	.byte %01000101
	.byte %01000101
	.byte %01000101
	.byte %01111001
	.byte %00000000
	.byte %01111001
	.byte %01000101
	.byte %01000101
	.byte %01000101
	.byte %01111001

	
PF2Data  ;EA   01234567
	.byte %00101111
	.byte %10100000
	.byte %11100111
	.byte %00100000
	.byte %11101111
	.byte %00000000
	.byte %00101111
	.byte %00100000
	.byte %11100111
	.byte %00100000
	.byte %11101111	


PF2bData ;     76543210;<--- scanning order

	.byte %11011110
	.byte %00010001
	.byte %10011110
	.byte %01010001
	.byte %10011110
	.byte %00000000
	.byte %01000100
	.byte %01000100
	.byte %11000100
	.byte %01000100
	.byte %11011111

	
PF1bData;      01234567;<--- scanning order


	.byte %00111110
	.byte %00100000
	.byte %00111110
	.byte %00100010
	.byte %00100010
	.byte %00000000
	.byte %00100010
	.byte %00100010
	.byte %00111110
	.byte %00100010	
	.byte %00100010	


; THE ROTATION SEQUENCE POINTERS

; when writing the driving controller code, I will
; index through this table of pointers which rotates
; through the frames.  These pointers only store the low byte
; value.  The high value is always going to be $FF anyway.


; since the shapes are stored upside down, the actual orientation
; is indicated in the notes.  the shapes in the sourcecode will
; appear upside-down

RotationSequence
	;NO FLIP BIT - FACE RIGHT
	.byte <CarRotation0;0  - point up
	.byte <CarRotation1;1  - 
	.byte <CarRotation2;2  - 
	.byte <CarRotation3;3  - 
	.byte <CarRotation4;4  - point right
	.byte <CarRotation5;5  - 
	.byte <CarRotation6;6  - 
	.byte <CarRotation7;7  - 
	.byte <CarRotation8;8  - point down
	; WITH FlipThreshold - FACE LEFT
	.byte <CarRotation7;9  - 
	.byte <CarRotation6;10 - 
	.byte <CarRotation5;11 - 
	.byte <CarRotation4;12 - point left
	.byte <CarRotation3;13 - 
	.byte <CarRotation2;14 - 
	.byte <CarRotation1;15 - 

	
	org $FF00+CarDataAlign
	; make sure all sprite graphics are at least 'CarDataAlign' bytes
	; above the page in order to make sure that the vertical
	; logic routine will work properly


; THIS STORES HALF OF THE ROTATIONS.
; THE OTHER HALF ARE GENERATED BY USING THE HARDWARE PLAYER FLIP FUNCTION

; note that this rotation frame doesn't need an upside down version because it is symmetrical


;9 frames * 8 bytes per frame = 56 bytes used

CarRotation0;UP (ACTUAL)
  ;.byte %00000000
  .byte %01011010; .X.XX.X.
  .byte %01111110; .XXXXXX.
  .byte %01100110; .XX..XX.
  .byte %00011000; ...XX...
  .byte %01011010; .X.XX.X.
  .byte %01111110; .XXXXXX.
  .byte %01011010; .X.XX.X.
  .byte %00011000; ...XX...

CarRotation1
  ;.byte %00000000
  .byte %00000110; ....XX..
  .byte %00011110; ..XXXX..
  .byte %01110100; XXX.X...
  .byte %00011010; XX.XX.X.
  .byte %00011010; ...XX.X.
  .byte %01011111; .X.XXXXX
  .byte %00111100; ..XXXX..
  .byte %00101100; ..X.XX..

CarRotation2
  ;.byte %00000000
  .byte %00011000; ...XX...
  .byte %01111000; .XXXX...
  .byte %01000010; .X....X.
  .byte %11011011; XX.XX.XX
  .byte %11011100; XX.XXX..
  .byte %00011110; ...XXXX.
  .byte %00110110; ..XX.XX.
  .byte %00010000; ...X....

CarRotation3
  ;.byte %00000000
  .byte %00110000; ..XX....
  .byte %00110010; ..XX.X..
  .byte %01100011; .XX...XX
  .byte %01011110; .X.XXXX.
  .byte %11111111; XXXXXXXX
  .byte %11000111; XX...XXX
  .byte %00011100; ...XXX..
  .byte %00000100; .....X..

CarRotation4;RIGHT|LEFT  
  ;.byte %00000000
  .byte %00000000; ........
  .byte %11101110; XXX.XXX.
  .byte %01100100; .XX..X..
  .byte %11011111; XX.XXXXX
  .byte %11011111; XX.XXXXX
  .byte %01100100; .XX..X..
  .byte %11101110; XXX.XXX.
  .byte %00000000; ........

CarRotation5   
  ;.byte %00000000
  .byte %00000100; .....X..
  .byte %00011100; ...XXX..
  .byte %11000111; XX...XXX
  .byte %11111111; XXXXXXXX
  .byte %01011110; .X.XXXX.
  .byte %01100011; .XX...XX
  .byte %00110010; ..XX.X..
  .byte %00110000; ..XX....

CarRotation6
  ;.byte %00000000
  .byte %00010000; ...X....
  .byte %00110110; ..XX.XX.
  .byte %00011110; ...XXXX.
  .byte %11011100; XX.XXX..
  .byte %11011011; XX.XX.XX
  .byte %01000010; .X....X.
  .byte %01111000; .XXXX...
  .byte %00011000; ...XX...

CarRotation7   
  ;.byte %00000000
  .byte %00101100; ..X.XX..
  .byte %00111100; ..XXXX..
  .byte %01011111; .X.XXXXX
  .byte %00011010; ...XX.X.
  .byte %11011010; XX.XX.X.
  .byte %11101000; XXX.X...
  .byte %00111100; ..XXXX..
  .byte %00001100; ....XX..

CarRotation8;DOWN (ACTUAL)
  ;.byte %00000000
  .byte %00011000; ...XX...
  .byte %01011010; .X.XX.X.
  .byte %01111110; .XXXXXX.
  .byte %01011010; .X.XX.X.
  .byte %00011000; ...XX...
  .byte %01100110; .XX..XX.
  .byte %01111110; .XXXXXX.
  .byte %01011010; .X.XX.X.


  
  
;missiles are used as pseudoplayers
;data is encoded as width and pixel offsets.

;76543210
;XXXX    <- OFFSET (typically 0 through +8 pixels)
;    XX  <- NUSIZ data

;EXAMPLE:
;LDA (M0_ShapeFrame),Y	;
;STA HMM0		;
;LSL			;
;LSL			;
;STA NUSIZ0		;

;32 bytes used

Ped1_1;   OOOOWW__
   .byte %11110100 ; .XX.....
   .byte %11100000 ; ..X.....
   .byte %11011000 ; ...XXXX.
   .byte %11010100 ; ...XX...
   .byte %11111000 ; .XXXX...
   .byte %00000000 ; ........
   .byte %11010100 ; ...XX...
   .byte %11010100 ; ...XX...
   
Ped1_2;   OOOOWW__
   .byte %11010100 ; ...XX...
   .byte %11010100 ; ...XX...
   .byte %11010100 ; ...XX...
   .byte %11010100 ; ...XX...
   .byte %11010100 ; ...XX...
   .byte %00000000 ; ........
   .byte %11010100 ; ...XX...
   .byte %11010100 ; ...XX...

Ped1_3;   OOOOWW__
   .byte %11110100 ; .XX.....
   .byte %00000000 ; ..X.....
   .byte %11011100 ; ...XXXX.
   .byte %11100100 ; ..XX....
   .byte %11010100 ; ...XX...
   .byte %00000000 ; ........
   .byte %11010100 ; ...XX...
   .byte %11010100 ; ...XX...

Ped1_4;   OOOOWW__
   .byte %11011100 ; ...XXXX.
   .byte %11010000 ; ...X....
   .byte %11011100 ; ...XXXX.
   .byte %11010100 ; ...XX...
   .byte %00001100 ; XXXX....
   .byte %11101100 ; ..XXXX..
   .byte %11110100 ; .XX.....
   .byte %11110100 ; .XX.....





;add score graphics later	
	org $FFFC
        .word Start
        .word Start

Attachment: DD_0016.bin
Description: Binary data

Current Thread