====== Any Given Screen Positioning (AGSP) VSP with a line crunch ====== A 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 VSP (Variable 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 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 VSP 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 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