[stella] Help (Timing problems?)

Subject: [stella] Help (Timing problems?)
From: Wade Brown <wbrown@xxxxxxxxxxxxx>
Date: Mon, 30 Jul 2001 00:37:18 -0400
Hi,
I am working on a "shell" that I can use to flesh out some ideas I have - the idea being to have basic semi-reusable functions such as: b/w color selection, screen saver timer, and score digits. (You'll probably notice I've borrowed heavily from the freeway source...)


Anyway, the problem I am having is when I move the cannon from left to right, when it reaches gets about 3/4 across the screen, it then seems to "skip" a scan line or two. Could anybody out there please take a look at my source and help me find out what I am doing wrong?

BTW, I'm new to 6502 programming, but I've got some x86 asm experience. I've had a difficult time adjusting to working with 8 bit values - I tend to think maybe the problem(s) I'm having is related to overflowing some 8 bit qty. I would especially appreciate any help in the area of boundary detection (i.e detecting when the player/enemy is at the edge of the screen).

Feel free to offer any optimizing hints as well, though I've been concentrating more on getting the code working than optimizing any particular area.
BTW, I was trying to get a fairly optimal way to read diagonals from the joystick - anyone out there see how I can improve this approach or has a different take, I would really like to see...


Thanks in advance for help/advice/criticism!

Wade Brown
;
; Kernel.asm
; Wade Brown
; Much Code From Freeway (thanks Mr. Crane!)
; 
; Use -f3 flag with DASM 2.12
; screenx = (Cycles from WSYNC to Resp0 * 3)-63
	processor 6502
	include vcs.h
	;include macros.asm
;-----------------------------------------------
;  Equates                          
;-----------------------------------------------
; bits
D0              EQU 1
D1              EQU 2
D2              EQU 4
D3              EQU 8
D4              EQU 16
D5              EQU 32
D6              EQU 64
D7              EQU 128
NUM_COLORS		EQU	1
T64_VBLANK		EQU	44	; are these values correct?
T64_OSCAN		EQU	34	; 
;
; WAIT_TIMER
; 
	mac	wait_timer	
.1		LDA	INTIM	;Wait for the proper scan line
		BNE	.1
	endm
;-----------------------------------------------
; Ram Segment (128 bytes)
;-----------------------------------------------
    	SEG.U vars
    	org $80
GameNumber	DS	1	;Current game variation being played (0-7)
FrameCounter	DS	1	;Inc'd every video frame
Polynomial	DS	1	;Random number polynomial
SelectDelay	DS	1	;Timer for select autorepeat
SaverColor	DS	1	;$00 for normal, Random for screen saver
LumMask	DS	1	;$FF for color, $0F for B&W and $07 for saver
ZColors	DS	2	;Adjusted colors (Score, Sprite)
; The order of the 4 pointers below is important!
ScoreShape01Ptr	DS	2	;Pointer to left score first digit
ScoreShape11Ptr	DS	2	;Pointer to right score first digit
ScoreShape02Ptr	DS	2	;Pointer to left score second digit
ScoreShape12Ptr	DS	2	;Pointer to right score second digit
;
; vars from here to $FF zero'ed out on reset (load address of First Variable, i.e SaverTimer)
;
SaverTimer	DS	1	;If > 127 then in screen saver mode
ScanLine	DS	1
SpriteColorPtr	DS	8
Scores	DS	2	;Player scores, (BCD) p1,p2
FrameCounterHi	DS	1	;Inc'd every 256 frames
EscortXY	DS	2
PatByte	DS	1
PatPtr	DS	1
EscortPtr	DS	1
TempX1		DS	1
; Keep these four in order
Player1X	DS	1	; x pix value
Player1Y	DS	1	; Scanline (0-192)
Player2X	DS	1
Player2Y	DS	1
; don't change order of following 2 vars
Player1Coll	DS	1	; VCS Collision Flags
Player2Coll	DS	1	; VCS Collision Flags
;-----------------------------------------------
;  Code
;-----------------------------------------------
	SEG code
	org $1000
ResetEntry
; I believe Andrew Davie wrote the mem clear code. Thanks!
	SEI
	CLD
	LDX #0
	TXS 
	PHA ; BEST WAY TO GET SP=$FF, X=0 
	TXA 
.Clear	
	PHA 
	DEX 
	BNE	.Clear
	JSR	ResetGameVars
;-----------------------------------------------
; Main Loop Start
;-----------------------------------------------
MainLoop
	LDA	#0
	LDY	#$82		;Enable video blank flags
	STY	WSYNC		; full scan line for vsync	
	STY	VSYNC		;Enable vertical sync signal 
	STY	WSYNC		;Wait 3 scan lines
	STY	WSYNC
	STY	WSYNC
	STA	VSYNC		;Disable vertical sync signal (A=0)
	LDA	#T64_VBLANK		; total number of cycles for 37 scan-line VBLANK period
	STA	TIM64T		;Set the timer for start of screen
	;-----------------------------------------------
	; VBlank Starts (37 scanlines, 2812 cycles)
	; Note: Is the cycle count (scanlines * 76) correct?
	;-----------------------------------------------
	LDX	#2-1	; NumColors-1
.GetColors	
	LDA	BaseColors,X	;Get the colors
	EOR	SaverColor		;Screen saver
	AND	LumMask		;B&W mask
	STA	ZColors,X	;Save the adjusted colors
	DEX
	BPL	.GetColors		
	LDX	#0
	LDA	#40			;X position for score digit #1
	JSR	SetMotionRegsX	;Position digit #1
	LDA	#48			;X position for score digit #2
	INX	;X = 01
	JSR	SetMotionRegsX	;Position digit #2
	LDA	#$04	;Two copies wide 04
	STA	NUSIZ0	;Two pairs of score digits
	STA	NUSIZ1
	LDA	ZColors	;Color of the score
	STA	COLUP0	;Save the score color
	STA	COLUP1
	;-----------------------------------------------
	; Wait for End of VBlank
	;-----------------------------------------------
	wait_timer
	; Set motion registers
	; note: do not set any motion registers for 24 machine cycles! 
	STA	WSYNC
	STA	HMOVE
	;-----------------------------------------------
	;Enable video (a=0)
	;-----------------------------------------------
	STA	VBLANK	
	STA	CXCLR		;Clear collision registers
	;-----------------------------------------------
	; Display the scores (8 scan lines)
	;-----------------------------------------------
	LDY	#8-1	
.DrawScore	
	STA	WSYNC				;3 Wait for sync
	LDA	(ScoreShape01Ptr),Y	;5 Get the shape for player #0's score
	STA	GRP0	;3
	LDA	(ScoreShape02Ptr),Y	;5
	STA	GRP1	;3
	JSR	Waste18			;18 kill 18 cycles
	NOP					; kill another cycle for proper display
	LDA	(ScoreShape11Ptr),Y	;5 Get the shape for player #1's score
	STA	GRP0	;3
	LDA	(ScoreShape12Ptr),Y	;5
	STA	GRP1	;3
	DEY	;2
	BPL	.DrawScore	;3
	INY	;2 y = 0	; Clear Scores for next scanline...
	STY	GRP0 ;3
	STY	GRP1 ;3
	STY	NUSIZ0	;3
	STY	HMCLR
	;-----------------------------------------------
	; set player pos (2 scan lines)
	;-----------------------------------------------
	LDA	Player1X	
	LDX	#0
	JSR SetMotionRegsX 	; 1 scan line here
	LDA	ZColors+1		; Color of player1
	STA	COLUP0		; Save the color
	STA	WSYNC	;3		; 2nd here...
	STA	HMOVE	;3
	;-----------------------------------------------
	; Draw Loop 
	;-----------------------------------------------	
	LDA	#10			; init ScanLine to 10
	STA	ScanLine
DrawBegin
	LDA	ScanLine
	SEC
	SBC	Player1Y	; A= ScanLine - P1InitXY+1 
	TAX			; x= sprite index
	AND	#$F8		; force from 0-7
	BEQ	DrawSprite
DrawNoSprite
	LDA	#00
	JMP	DrawPlayer
DrawSprite
	LDA	Sprite,X
DrawPlayer
	STA	WSYNC
	STA	GRP0		
	INC	ScanLine
	LDA	ScanLine		
	EOR	#191		; stop at line 191
	BNE	DrawBegin
	;-----------------------------------------------
	; Screen Finished, disable video and set the oscan timer
	;-----------------------------------------------
	LDA #2
	STA WSYNC  ;Finish this scanline.
	STA VBLANK	; video OFF (important)
	LDA	#T64_OSCAN		; total number of cycles for 30 scan-line OverScan period
	STA	TIM64T		;Set the timer for start of screen
	;-----------------------------------------------
	; OVERSCAN Starts (30 scanlines, 2280 cycles)
	; Note: Crane (in Freeway) uses a lower timer value then recommended by stella.doc; find out why...   
	;-----------------------------------------------
	; Update Score Pointers
	LDA	FrameCounter	;Player 1 or 2 (Update 30 FPS)
	AND	#1
	TAX	;0/1
	ASL
	TAY	;0/2
	LDA	Scores,X	;Get the score (BCD)
	AND	#$F0	;Mask it
	LSR	;$00,$08,$10,$18,$20
	STA	ScoreShape01Ptr,Y	;Insert a space (Or digit)
	LDA	Scores,X	;Get the BCD number again
	AND	#$0F	;0-9
	ASL	;Mul by 8
	ASL
	ASL	;$00,$08,$10,$18,$20
	STA	ScoreShape02Ptr,Y	;Save the second digit
	;-----------------------------------------------
	; Check Joysticks
	;-----------------------------------------------
	LDA	SWCHA
	CMP	#$FF
	BEQ	NoJoy
	LDY	#0	;Joystick is pressed
	STY	SaverTimer
	ASL
	BCC	JoyRight
ChkJoyLeft
	ASL
	BCC	JoyLeft
ChkJoyDown
	ASL	
	BCC	JoyDown
	ASL
	BCC	JoyUp
	JMP	NoJoy
JoyRight
	INC	Player1X
	JMP	ChkJoyLeft
JoyLeft
	DEC	Player1X
	JMP	ChkJoyDown
JoyDown
	INC	Player1Y
	JMP	NoJoy
JoyUp
	DEC	Player1Y
NoJoy
	; only bounds checking the X pos currently
	LDA	Player1X
	CMP	XMaskLeft
	BEQ	PastLeft
	CMP	XMaskRight
	BNE	InRange
	LDA	#149		; restrict X to right edge
	JMP	InRange
PastLeft
	LDA	#0		; restrict X to left edge
InRange
	STA	Player1
	;-----------------------------------------------
	; Inc the frame counters and timers
	;-----------------------------------------------
	INC	FrameCounter	;Inc the frame counter
	BNE	NoRollSv		;255 frames?
	INC	FrameCounterHi	;High byte of the frame counter
	INC	SaverTimer		;Inc the screen saver timer
	BNE	NoRollSv		;Zero?
	SEC				;Keep the high byte set
	ROR	SaverTimer		;= $80
NoRollSv	
	LDY	#$FF	;Assume normal color
	LDA	SWCHB	;B&W set?
	AND	#$08
	BNE	Color
	LDY	#$0F	;Force b&w
Color	
	TYA		;Place in A
	LDY	#0	;Assume screen saver not active
	BIT	SaverTimer	;Screen saver active?
	BPL	NoSaver8
	AND	#$F7	;Halve the brightness
	LDY	SaverTimer	;Get the timer value for the random colors
NoSaver8	
	STY	SaverColor	;Save the random saver scrambler
	ASL	SaverColor	;0,2,4 etc...
	STA	LumMask	;$FF,$0F,$07
	;-----------------------------------------------
	; console switches check
	;-----------------------------------------------
DoConsole	
	JSR	ReadConsoleSwitches	;Read the console
	BMI	ChkSelect	;Reset pressed?
	LDX	#SaverTimer	; Reset the game (Set X to address of First Variable to clear)
	JMP	ResetEntry
ChkSelect	
	LDY	#0	;Was select pressed? (Init Y)
	BCS	SaveSelDelay	;Nope (Clear select delay)
	LDA	SelectDelay	;Select the next game
	BEQ	NotHeld
	DEC	SelectDelay
	BPL	NotSelTime
NotHeld	
	INC	GameNumber	;Inc the game selected
	; select pressed. Y=0
	STY	Scores+1	; set player2 score to zeros
	JSR	ResetGameVars
	LDA	GameNumber	;Make sure the game number is 0-7
	AND	#7
	STA	GameNumber	; save game number and timer
	STA	SaverTimer
	TAY			;	
	INY			; increment giving 1-8 (Low Order Digit of BCD score)
	STY	Scores	; $01-$08 0/1-8
	LDY	#30		; load the select delay for auto-repeat
SaveSelDelay	
	STY	SelectDelay	;30/60 delay (1/2 second or 0)
NotSelTime	
	;-----------------------------------------------
	; wait for rest of overscan period
	;-----------------------------------------------
	wait_timer		
	JMP 	MainLoop
;-----------------------------------------------
; Main Loop End
;-----------------------------------------------
;---------------------------------------------------------
;
; Sub-Routines
;
;---------------------------------------------------------
; ResetGameVars
; Out: A=GameNumber (0-7)
;	 X=FF
ResetGameVars SUBROUTINE
	LDA	#0	;Reset the frame counter
	STA	FrameCounter
	LDX	#8-1
	LDA	#>FontPage
	LDY	SpriteColors
.SetVidPtr	
	STA	ScoreShape01Ptr,X	;Init the high byte for video pointers
	STY 	SpriteColorPtr,X
	DEX
	STY 	SpriteColorPtr,X
	DEX
	BPL	.SetVidPtr
	
	LDX	#03
; set x,y positions of both players
.PlayPtrTop
	LDA	P1InitXY,X
	STA	Player1X,X
	DEX
	BPL	.PlayPtrTop
	INX			; X=0
	TXA
	INX			; X=1
.VolClrTop
	STA	AUDV0,X 	;Reset the volume of the audio chip
	DEX
	BPL	.VolClrTop	
; Load Tables here
;	LDA	GameNumber	; Might need this to init tables based on the game number
	RTS
;
;ReadConsoleSwitches
;
; Read the console switches from the 6532 RIOT chip
; Bits are... $01 Reset, $02 Select, $08 Color
; $40 Difficulty Left, $80 Difficulty Right
; Out:
; Carry = Select
; Negative = Reset
;
ReadConsoleSwitches SUBROUTINE
	LDA	SWCHB	;Read the switches
	LSR	;Shift the reset switch
	ROR	;Rotate reset to negative flag
	RTS	;and Carry has select
;
;CalcXRegs
; Given a requested X coord in A,
; return in A and Y and Horizonal offset and the cycle delay
;
CalcXRegs SUBROUTINE	
	CLC
	ADC	#46
	TAY
	AND	#$0F	;Mask the 4 bit offset
	STA	TempX1	;Save temp
	TYA	;Get value again
	LSR	;Isolate the upper 4 bits
	LSR
	LSR
	LSR
	TAY	;Save as a WSYNC Delay
	CLC
	ADC	TempX1	;Mod 15
	CMP	#15
	BMI 	NoExcess	; (2/3)
	;BLT	NoExcess
	SBC	#15	;Remove one step
	INY	;5 more cycles
NoExcess	
	EOR	#7	; Negate horizontal offset
	ASL	; (2) Move to upper 4 bits (Hardware)
Waste18	
	ASL	;(2) Call to waste 18 cycles
	ASL	;(2)
Waste14	
	ASL	;(2) Call to waste 14 cycles
Waste12	
	RTS	;(6) Call to waste 12 cycles

;SetMotionRegsX 
; Set the horizontal motion registers
; In: x coordinate in A
;	offset from HMPO in x (0=p0, 1=p1)
;
SetMotionRegsX SUBROUTINE
	STA	WSYNC	;Sync to video
	TAY			; x co-ord in Y
	LDA	HorzTable,Y
	STA	HMP0,X	;Save the horizontal motion register
	AND	#$0F
	TAY
.delay	
	DEY	;Wait for cycle delay (5 cycles per)
	BPL	.delay
	STA	RESP0,X	;Hit the register for course adjustment
	RTS
;-----------------------------------------------
; We have some padding here, put it to use... 	
;-----------------------------------------------
XMaskLeft	
	.byte	$F0
XMaskRight
	.byte	$96
;-----------------------------------------------
; Numeric font (0-9) (88 bytes total)
;-----------------------------------------------
FontPage ALIGN 256
	.byte	%00111100
	.byte	%01100110
	.byte	%01100110
	.byte	%01100110
	.byte	%01100110
	.byte	%01100110
	.byte	%01100110
	.byte	%00111100

	.byte	%00111100
	.byte	%00011000
	.byte	%00011000
	.byte	%00011000
	.byte	%00011000
	.byte	%00011000
	.byte	%00111000
	.byte	%00011000

	.byte	%01111110
	.byte	%01100000
	.byte	%01100000
	.byte	%00111100
	.byte	%00000110
	.byte	%00000110
	.byte	%01000110
	.byte	%00111100

	.byte	%00111100
	.byte	%01000110
	.byte	%00000110
	.byte	%00001100
	.byte	%00001100
	.byte	%00000110
	.byte	%01000110
	.byte	%00111100

	.byte	%00001100
	.byte	%00001100
	.byte	%00001100
	.byte	%01111110
	.byte	%01001100
	.byte	%00101100
	.byte	%00011100
	.byte	%00001100

	.byte	%01111100
	.byte	%01000110
	.byte	%00000110
	.byte	%00000110
	.byte	%01111100
	.byte	%01100000
	.byte	%01100000
	.byte	%01111110

	.byte	%00111100
	.byte	%01100110
	.byte	%01100110
	.byte	%01100110
	.byte	%01111100
	.byte	%01100000
	.byte	%01100010
	.byte	%00111100

	.byte	%00011000
	.byte	%00011000
	.byte	%00011000
	.byte	%00011000
	.byte	%00001100
	.byte	%00000110
	.byte	%01000010
	.byte	%01111110

	.byte	%00111100
	.byte	%01100110
	.byte	%01100110
	.byte	%00111100
	.byte	%00111100
	.byte	%01100110
	.byte	%01100110
	.byte	%00111100

	.byte	%00111100
	.byte	%01000110
	.byte	%00000110
	.byte	%00111110
	.byte	%01100110
	.byte	%01100110
	.byte	%01100110
	.byte	%00111100
Sprite				
	.byte %00011000
	.byte	%01011010
	.byte %11111111
	.byte %11011011
	.byte %11011011
	.byte %11111111
	.byte %01011010
	.byte %00011000
FontEnd
;-----------------------------------------------
; Constant Segment
; 166 bytes left in this page
;-----------------------------------------------
	SEG consts
	ORG FontEnd
HorzTable 	ALIGN 256; this must not cross a page boundary (158 Bytes)
	.byte $00,$F0,$E0,$D0,$C0,$B0,$A0,$90 
	.byte $71,$61,$51,$41,$31,$21,$11,$01,$F1,$E1,$D1,$C1,$B1,$A1,$91 
	.byte $72,$62,$52,$42,$32,$22,$12,$02,$F2,$E2,$D2,$C2,$B2,$A2,$92 
	.byte $73,$63,$53,$43,$33,$23,$13,$03,$F3,$E3,$D3,$C3,$B3,$A3,$93 
	.byte $74,$64,$54,$44,$34,$24,$14,$04,$F4,$E4,$D4,$C4,$B4,$A4,$94 
	.byte $75,$65,$55,$45,$35,$25,$15,$05,$F5,$E5,$D5,$C5,$B5,$A5,$95 
	.byte $76,$66,$56,$46,$36,$26,$16,$06,$F6,$E6,$D6,$C6,$B6,$A6,$96 
	.byte $77,$67,$57,$47,$37,$27,$17,$07,$F7,$E7,$D7,$C7,$B7,$A7,$97 
	.byte $78,$68,$58,$48,$38,$28,$18,$08,$F8,$E8,$D8,$C8,$B8,$A8,$98 
	.byte $79,$69,$59,$49,$39,$29,$19,$09,$F9,$E9,$D9,$C9,$B9,$A9,$99 
	.byte $7A,$6A,$5A,$4A,$3A,$2A,$1A,$0A,$FA,$EA,$DA,$CA,$BA,$AA,$9A 
; 8 bytes follow
BaseColors	HEX	4A		; SCORE
		HEX	88		; SPRITE
SpriteColors
		HEX	88		; LINE_ODD_COLOR
		HEX	90		; LINE_EVEN_COLOR
P1InitXY	.byte	149
		.byte	50
P2InitXY	.byte	60
		.byte	88
; only 1 byte left in page
EscortPattern	ALIGN 256	; 1st byte = frame count, 2nd byte: bit 7 x direction (1 negative), bits 4-6 x offset
		HEX	0F 		; ditto for y: bit 3 y direction (1 negative), bits 0-2 y offset
		HEX	A1		; -x, -2 offset +y, 1 offset
		HEX	07
		HEX	2A
;
; Reset vector. Note we have two free bytes after in the rom, if we pad to 2k
; 
	org $1FFC
	.word ResetEntry
;
; Difficulty switch flags, for padding
; 
DiffSwitchTbl	HEX	4080	;Left,Right difficulty flags

	
Current Thread