User Tools

Site Tools


base:agsp_any_given_screen_position

Any Given Screen Positioning (AGSP) HSP/VSP with a line crunch

A HSP (VSP/Wanker/DMA delay) is used for the horizontal position and a line crunch is used for the vertical position.

This code is for PAL machines. To make it work for NTSC you have to do some minor timing modifications for each scan-line.

The code is in ACME assembler format.

; This demonstrates AGSP (any given screen position). This uses HSP (Horizontal Screen Positioning) (DMA delay)
; with a stable raster plus a line crunch to scroll the screen vertically to any position.

; Use a joystick in port two to move the screen around.

; The top five and a bit lines are "wasted" by the line crunch, if you see any demos with the top
; few lines blanked out and the screen below is moving at a fast rate then you can probably guess
; it is using a similar method.

!to "RasterTest.prg", cbm
!cpu 6510
!ct pet

ZPProcessorPortDDR			= $00
ProcessorPortDDRDefault		= %101111
ZPProcessorPort			= $01
ProcessorPortAllRAMWithIO		= %100101
CIA1InterruptControl			= $dc0d
CIA1TimerAControl			= $dc0e
CIA1TimerBControl			= $dc0f
CIA2InterruptControl			= $dd0d
CIA2TimerAControl			= $dd0e
CIA2TimerBControl			= $dd0f
VIC2InteruptControl			= $d01a
VIC2InteruptStatus			= $d019
VIC2BorderColour			= $d020
VIC2ScreenColour			= $d021
VIC2ScreenControlV			= $d011
VIC2SpriteEnable			= $d015
VIC2Raster				= $d012
CIA1KeyboardColumnJoystickA		= $dc00
KERNALNMIServiceRoutineLo		= $fffa
KERNALNMIServiceRoutineHi		= $fffb
KERNALIRQServiceRoutineLo		= $fffe
KERNALIRQServiceRoutineHi		= $ffff
VIC2Colour_Black = 0
VIC2Colour_Red = 2
VIC2Colour_Green = 5

!macro MACROAckRasterIRQ_A {
	lda #1
	sta VIC2InteruptStatus				; Ack Raster interupt
}

!macro MACROAckAllIRQs_A {
	; Ack any interrupts that might have happened from the CIAs
	lda CIA1InterruptControl
	lda CIA2InterruptControl
	; Ack any interrupts that have happened from the VIC2
	lda #$ff
	sta VIC2InteruptStatus
}


*= $0801
!byte $0b,$08,$01,$00,$9e		; Line 1 SYS2061
!convtab pet
!tx "2061"						; Address for sys start in text
!byte $00,$00,$00

!zn
.theLine = 45
	; Stop interrupts, clear decimal mode and backup the previous stack entries
	lda #ProcessorPortAllRAMWithIO
	sei
	cld
	; Grab everything on the stack
	ldx #$ff
	txs
	; Init the processor port
	ldx #ProcessorPortDDRDefault
	stx ZPProcessorPortDDR
	; Set the user requested ROM state
	sta ZPProcessorPort
	; Clear all CIA to known state, interrupts off.
	lda #$7f
	sta CIA1InterruptControl
	sta CIA2InterruptControl
	lda #0
	sta VIC2InteruptControl
	sta CIA1TimerAControl
	sta CIA1TimerBControl
	sta CIA2TimerAControl
	sta CIA2TimerBControl
	+MACROAckAllIRQs_A

	; Setup kernal and user mode IRQ vectors to point to a blank routine
	lda #<.initIRQ
	sta KERNALIRQServiceRoutineLo
	lda #>.initIRQ
	sta KERNALIRQServiceRoutineHi

	lda #<.initNMI
	sta KERNALNMIServiceRoutineLo
	lda #>.initNMI
	sta KERNALNMIServiceRoutineHi

	; Turn off various bits in the VIC2 and SID chips
	; Screen, sprites and volume are disabled.
	lda #0
	sta VIC2ScreenColour
	sta VIC2BorderColour
	sta VIC2ScreenControlV
	sta VIC2SpriteEnable

	; Setup raster IRQ
	lda #<IrqTopOfScreen
	sta KERNALIRQServiceRoutineLo
	lda #>IrqTopOfScreen
	sta KERNALIRQServiceRoutineHi
	lda #1
	sta VIC2InteruptControl
	lda #.theLine
	sta VIC2Raster
	lda #$1b
	sta VIC2ScreenControlV

	lda #VIC2Colour_Red
	sta VIC2BorderColour

	+MACROAckAllIRQs_A

	ldx #39
.fs1
	lda #$30
	sta $400 + 00*40 ,x
	sta $400 + 10*40 ,x
	sta $400 + 20*40 ,x
	lda #$31
	sta $400 + 01*40 ,x
	sta $400 + 11*40 ,x
	sta $400 + 21*40 ,x
	lda #$32
	sta $400 + 02*40 ,x
	sta $400 + 12*40 ,x
	sta $400 + 22*40 ,x
	lda #$33
	sta $400 + 03*40 ,x
	sta $400 + 13*40 ,x
	sta $400 + 23*40 ,x
	lda #$34
	sta $400 + 04*40 ,x
	sta $400 + 14*40 ,x
	sta $400 + 24*40 ,x
	lda #$35
	sta $400 + 05*40 ,x
	sta $400 + 15*40 ,x
	lda #$36
	sta $400 + 06*40 ,x
	sta $400 + 16*40 ,x
	lda #$37
	sta $400 + 07*40 ,x
	sta $400 + 17*40 ,x
	lda #$38
	sta $400 + 08*40 ,x
	sta $400 + 18*40 ,x
	lda #$39
	sta $400 + 09*40 ,x
	sta $400 + 19*40 ,x

	dex
	bpl .fs1

	cli
.mainLine
	; Wait for the top IRQ to be triggered
	lda .topIRQDone
	beq .mainLine

	lda #0
	sta .topIRQDone

	lda #%00001
	bit CIA1KeyboardColumnJoystickA
	bne .notUp
	ldx .yPosOffset
	inx
	cpx #26
	bne .s1
	ldx #25
.s1
	stx .yPosOffset
.notUp
	lda #%00010
	bit CIA1KeyboardColumnJoystickA
	bne .notDown
	ldx .yPosOffset
	dex
	bpl .s2
	ldx #0
.s2
	stx .yPosOffset
.notDown
	; If fire is not pressed then don't update the counter
	lda #%01000
	bit CIA1KeyboardColumnJoystickA
	bne .notLeft
	ldx .xPosOffset
	inx
	cpx #40
	bne .o1
	ldx #39
.o1
	stx .xPosOffset

.notLeft
	lda #%00100
	bit CIA1KeyboardColumnJoystickA
	bne .notRight
	ldx .xPosOffset
	dex
	bpl .o2
	ldx #0
.o2
	stx .xPosOffset

.notRight
	jmp .mainLine

.topIRQDone !by 0
.xPosOffset !by 0
.yPosOffset !by 0

; Remove all possibility that the timings will change due to previous code
!align 255,0
IrqTopOfScreen
	; Line 45
	pha
	txa
	pha
	tya
	pha

	inc .topIRQDone

	lda #<.irq2
	ldx #>.irq2

	sta KERNALIRQServiceRoutineLo
	stx KERNALIRQServiceRoutineHi
	inc VIC2Raster
	+MACROAckRasterIRQ_A

	; Begin the raster stabilisation code
	tsx
	cli
	; These nops never really finish due to the raster IRQ triggering again
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
.irq2
	; Line 46
	txs

	; Delay for a while
	ldx #8
.l1
	dex
	bne .l1
	bit $ea

	; Final cycle wobble check.
	lda VIC2Raster
	cmp VIC2Raster
	beq .start
.start

	; The raster is now stable

	; Line 47
	lda #$11
	sta VIC2ScreenControlV

	; Waste some time
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	bit $ea

	; Still in line 47
	; Calculate a variable offset to delay by branching over nops
	lda #39
	sec
	sbc .xPosOffset
	; divide by 2 to get the number of nops to skip
	lsr
	sta .sm1+1
	; Force branch always
	clv

	; Line 48

	; Introduce a 1 cycle extra delay depending on the least significant bit of the x offset
	bcc .sm1
.sm1
	bvc *
	; The above branches somewhere into these nops depending on the x offset position
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop

	; Show the raster position is stable and varies as a function of x offset
	; If these border colour changes are removed then they need to be replaced with the same
	; cycle count of nops
	inc VIC2BorderColour
	dec VIC2BorderColour

	; Do the HSP by tweaking the VIC2ScreenControlV register at the correct time
	lda #%01111011	; Plus ECM/Bitmap to disable the screen output
;	lda #%00011011	; Enable this line to see what is going on at the top of the screen
	dec VIC2ScreenControlV
	inc VIC2ScreenControlV
	sta VIC2ScreenControlV

	; Change the below to "!if 0" to turn off the line crunch
!if 1 {
	; Display a red border at the start of this routine
	lda #VIC2Colour_Red
	sta VIC2BorderColour

	; The number of lines to crunch
	ldy .yPosOffset
	beq .s3

	; The raster line to start crunching on
	ldx #67
.crunchLoop
	; Load the next screen value
	lda .table-67,x
	; Wait for the line...
	cpx VIC2Raster
	bne *-3
	; Produces a one line crunch
	; The timing of this store is quite critical, hence why the load is done before the raster line test
	sta VIC2ScreenControlV
	inx
	dey
	bne .crunchLoop

	; Display green until the screen is enabled
	lda #VIC2Colour_Green
	sta VIC2BorderColour

.s3

	; Turn off ECM/Bitmap to enable the screen
	lda VIC2ScreenControlV
	and #%00011111
	; Wait for this raster line
	ldx #67+26
	cpx VIC2Raster
	bne *-3
	; Enable the screen
	sta VIC2ScreenControlV

	lda #VIC2Colour_Black
	sta VIC2BorderColour
} else {
	lda #%00011011
	sta VIC2ScreenControlV
}

	; Restart the IRQ chain
	lda #<IrqTopOfScreen
	ldx #>IrqTopOfScreen
	ldy #.theLine
	sta KERNALIRQServiceRoutineLo
	stx KERNALIRQServiceRoutineHi
	sty VIC2Raster
	+MACROAckRasterIRQ_A

	; Exit the IRQ
	pla
	tay
	pla
	tax
	pla
.initIRQ
.initNMI
	rti

; With ECM + bitmap enabled to turn off the top of the screen
.table
!by %01111100
!by %01111101
!by %01111110
!by %01111111
!by %01111000
!by %01111001
!by %01111010
!by %01111011

!by %01111100
!by %01111101
!by %01111110
!by %01111111
!by %01111000
!by %01111001
!by %01111010
!by %01111011

!by %01111100
!by %01111101
!by %01111110
!by %01111111
!by %01111000
!by %01111001
!by %01111010
!by %01111011

!by %01111100
!by %01111101
!by %01111110
!by %01111111
!by %01111000
!by %01111001
!by %01111010
!by %01111011
base/agsp_any_given_screen_position.txt · Last modified: 2015-04-17 04:30 (external edit)