Background change at Raster Location

Basic and Machine Language

Moderator: Moderators

User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Background change at Raster Location

Post by chysn »

Let me be clear: The code I provided in the OP is not the right way to do this. It was my first draft. I have another draft, and I'm trying to find it for you. It's not on my SD card for some reason. It was marginally better than the first draft. Ultimately, I didn't use it in my game because the game would sometimes just freeze for seconds at a time, and I didn't really have the time or the remaining memory to diagnose it.

I sort of came to the conclusion that getting a raster split to be totally solid would require more counting than I'll ever be willing to do.

But if I manage to track down my "marginally-better" version, I'll post it here. Maybe I saved it on a different SD card...?

Out of curiosity, does your program have interrupt activities unrelated to the screen that must be processed?

Edit: Damn it all, this is embarrassing. I save everything I do on the VIC, on the basis that I'll never run out of space. Every time I snap the shutter on my DSLR, it takes like three thousand VIC-20s worth of disk space. I'll see if I can reconstruct it from wet memory...
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
beamrider
Vic 20 Scientist
Posts: 1452
Joined: Sun Oct 17, 2010 2:28 pm
Location: UK

Re: Background change at Raster Location

Post by beamrider »

I used Markos raster routine in my Mario Demo.

I found it worked pretty much out-of-the box, although as Mike mentioned it wastes a few cycles. Suggest starting with a new program, then incorporating your code into his.

Mine has four stable horizontal splits (one of which does nothing).

As you can see, it's doing smooth scrolling, music and sprite rendering (very much a WIP at the moment) concurrently.
vicassembly
Vic 20 Devotee
Posts: 253
Joined: Fri Mar 19, 2010 1:40 pm

Re: Background change at Raster Location

Post by vicassembly »

Do you have a cc65 version of the routine or did you use the same compiler that Marko uses?

Could I see JUST the code you used?
beamrider wrote: Sat Jun 06, 2020 3:09 pm I used Markos raster routine in my Mario Demo.

I found it worked pretty much out-of-the box, although as Mike mentioned it wastes a few cycles. Suggest starting with a new program, then incorporating your code into his.

Mine has four stable horizontal splits (one of which does nothing).

As you can see, it's doing smooth scrolling, music and sprite rendering (very much a WIP at the moment) concurrently.
(mod: quote repaired)
vicassembly
Vic 20 Devotee
Posts: 253
Joined: Fri Mar 19, 2010 1:40 pm

Re: Background change at Raster Location

Post by vicassembly »

I am just starting an unexpanded game. Yes, it will do other interrupt activities for moving objects around.
chysn wrote: Sat Jun 06, 2020 1:32 pm Let me be clear: The code I provided in the OP is not the right way to do this. It was my first draft. I have another draft, and I'm trying to find it for you. It's not on my SD card for some reason. It was marginally better than the first draft. Ultimately, I didn't use it in my game because the game would sometimes just freeze for seconds at a time, and I didn't really have the time or the remaining memory to diagnose it.

I sort of came to the conclusion that getting a raster split to be totally solid would require more counting than I'll ever be willing to do.

But if I manage to track down my "marginally-better" version, I'll post it here. Maybe I saved it on a different SD card...?

Out of curiosity, does your program have interrupt activities unrelated to the screen that must be processed?

Edit: Damn it all, this is embarrassing. I save everything I do on the VIC, on the basis that I'll never run out of space. Every time I snap the shutter on my DSLR, it takes like three thousand VIC-20s worth of disk space. I'll see if I can reconstruct it from wet memory...
User avatar
beamrider
Vic 20 Scientist
Posts: 1452
Joined: Sun Oct 17, 2010 2:28 pm
Location: UK

Re: Background change at Raster Location

Post by beamrider »

vicassembly wrote: Sat Jun 06, 2020 4:00 pm Do you have a cc65 version of the routine or did you use the same compiler that Marko uses?

Could I see JUST the code you used?
Here you go. I've prepped it without testing so take your chances. As I mentioned it's not very efficient at the moment. I will convert to Mike's routine eventually when I get the other higher priority pieces sorted.

Just call InstallRaterSplit from your game start and then add you hooks to the various points. If you only want one split, you can revert to the stock timing from Markos original.

Code: Select all

							.segment "CODE"
; http://www.retrocomputing.net/parts/commodore/vic20/docs/6561c.txt

; ------------------------------ exports  -----------------------------
.global InstallRaterSplit
;------------------------------ Constants ---------------------

NTSC    = 1
PAL     = 2
DEFAULT_HSPLIT_DELAY = 37

;SYSTEM = NTSC  ; 6560-101: 65 cycles per raster line, 261 lines
SYSTEM  = PAL   ; 6561-101: 71 cycles per raster line, 312 lines

.if SYSTEM & PAL
	LINES = 312
	CYCLES_PER_LINE = 71
.endif
.if SYSTEM & NTSC
	LINES = 261
	CYCLES_PER_LINE = 65
.endif

SPLIT_BASELOCATION = 111 + 08  ; Start at 3rd split....
BANDS = 4
TIMER_VALUE = (LINES / BANDS * CYCLES_PER_LINE) - 2
splitCounter: .byte  0
hSplitDelay: .byte DEFAULT_HSPLIT_DELAY

; IRQ Handler -------------------------------------------------------------------------------
irq:
					; 38 to 45 IRQ prep cycles delay at this stage

		lda $9114     ; get the NMI timer A value
						; (42 to 49 cycles delay at this stage)
		cmp #8        ; are we more than 7 cycles ahead of time?
		bcc @0
		pha           ; yes, spend 8 extra cycles
		pla
		and #7        ; and reset the high bit
@0:
		cmp #4
		bcc @1
		bit $24       ; waste 4 cycles
		and #3
@1:
		cmp #2        ; spend the rest of the cycles
		bcs *+2
		bcs *+2
		lsr
		bcs *+2       ; now it has taken 82 cycles from the beginning of the IRQ


@effect:

		lda splitCounter
		inc splitCounter
		and #3
		cmp #0; 
		beq @split0
		cmp #1 
		beq @split1
		cmp #2
		beq @bottomarea
		cmp #3
		beq @revertColor
		JMP $EB15				

@split0:	; ~25% of way down lines after top of screen
		lda #59
		sta 36879
		jmp $EABF ; - if kb scan needed each frame
		;jmp $EB15 ; - if no kb scan needed 

@split1:	; ~50% of way down lines after top of screen
		lda #42
		sta 36879
		JMP $EB15;  

@bottomarea:	; ~75% of way down lines after top of screen
		
		lda #25
		sta 36879
		; setup off-screen scroll area
		JMP $EB15;  

@revertColor:; ~100% = VBlank 
		lda #8
		sta 36879
		jmp $EB15     ; return to normal IRQ


@loop:
		dey
		bpl @loop
		rts

; Initialisation of raster splits-----------------------------------------------------------------------------
InstallRaterSplit:	

		lda #3
		sta splitCounter
		lda #$7f
		sta $912e     ; disable and acknowledge interrupts
		sta $912d
		sta $911e     ; disable NMIs (Restore key)

;synchronize with the screen
sync:
		ldx #SPLIT_BASELOCATION       ; wait for this raster line (times 2)
@0:
		cpx $9004
		bne @0        ; at this stage, the inaccuracy is 7 clock cycles
						; the processor is in this place 2 to 9 cycles
						; after $9004 has changed
		ldy #9
		bit $24
@1:
		ldx $9004
		txa
		bit $24
.if SYSTEM & PAL
		ldx #24
.elseif SYSTEM & NTSC
		bit $24
		ldx #21
.endif
		dex
		bne *-1       ; first spend some time (so that the whole
		cmp $9004     ; loop will be 2 raster lines)
		bcs *+2       ; save one cycle if $9004 changed too late
		dey
		bne @1
						; now it is fully synchronized
						; 6 cycles have passed since last $9004 change
						; and we are on line 2(116+9)=250

;initialize the timers
timers:
		lda #$40      ; enable Timer A free run of both VIAs
		sta $911b
		sta $912b

		lda #<TIMER_VALUE
		ldx #>TIMER_VALUE
		sta $9116     ; load the timer low byte latches
		sta $9126

.if SYSTEM & PAL

		ldy #7        ; make a little delay to get the raster effect to the
		dey           ; right place
		bne *-1
		nop
		nop

.endif
.if SYSTEM & NTSC

		ldy #6
		dey
		bne *-1
		bit $24

.endif

		stx $9125     ; start the IRQ timer A
					  ; 6560-101: 65 cycles from $9004 change
					   ; 6561-101: 77 cycles from $9004 change
		ldy #10       ; spend some time (1+5*9+4=55 cycles)
		dey           ; before starting the reference timer
		bne *-1
		stx $9115     ; start the reference timer

pointers:

		lda #<irq     ; set the raster IRQ routine pointer
		sta $314
		lda #>irq
		sta $315
		lda #$c0
		sta $912e     ; enable Timer A underflow interrupts
		rts           ; return


User avatar
beamrider
Vic 20 Scientist
Posts: 1452
Joined: Sun Oct 17, 2010 2:28 pm
Location: UK

Re: Background change at Raster Location

Post by beamrider »

btw... it would be nice to have some kind of code-generator for Mike's routine, where you enter the number of splits, PAL/NTSC and desired scan-lines and it outputs a CA65 compatible file.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Background change at Raster Location

Post by Mike »

vicassembly wrote:3. I looked for a printout of Mike's stable routine and could not find it. Mike: Could you help me with a text version of it or point me to the right place?
I posted a link to the source of my routine already in the first page of the thread here, but to ease things, here's the source quoted:

Code: Select all

DIM code 511
:
ntsc=TRUE
:
cycles=&4243:line=17:phase=21:delay=21:pos=5
IF NOT ntsc THEN cycles=&5686:line=30:phase=88:delay=23:pos=1
:
aux_extbrd_1=&3C00
bck_intbrd_1=&3D00
aux_extbrd_2=&3E00
bck_intbrd_2=&3F00
:
FOR pass=4 TO 7 STEP 3
P%=&3B00:O%=code
[OPT pass
.Init
 SEI
 LDA #Server MOD 256:STA &0314
 LDA #Server DIV 256:STA &0315
 LDA #cycles MOD 256:STA &9124
 LDY #line
.Init_00
 CPY &9004:BNE Init_00:INY:INY
.Init_01
 CPY &9004:BNE Init_01:JSR Init_06
 INY:CPY &9004:BEQ Init_02:NOP:NOP
.Init_02
 JSR Init_06:NOP
 INY:CPY &9004:BEQ Init_03:BIT &24
.Init_03
 JSR Init_06:NOP
 INY:CPY &9004:BNE Init_04
.Init_04
 LDX #pos
.Init_05
 DEX:BNE Init_05
]
IF ntsc THEN [OPT pass:NOP:NOP:]
[OPT pass
 LDA #cycles DIV 256:STA &9125
 CLI
 RTS
.Init_06
 LDX #delay
.Init_07
 DEX:BNE Init_07
]
IF NOT ntsc THEN [OPT pass:NOP:]
[OPT pass
 RTS

.Server
 CLD:SEC:LDA #phase:SBC &9124
 CMP #10:BCC Server_00:JMP &EABF
.Server_00
 STA Server_01+1
.Server_01
 BCC Server_01
 LDA #&A9
 LDA #&A9
 LDA #&A9
 LDA #&A9
 LDA #&A5
 NOP
 LDA &FB:PHA
 LDX &900F
 LDY #0
.Server_02
 JMP Frame1

.Frame1
 LDA &900E:EOR aux_extbrd_1,Y:AND #&0F:EOR aux_extbrd_1,Y:STA &FB
 LDA aux_extbrd_1,Y:EOR bck_intbrd_1,Y:AND #&0F:STX &900F:EOR bck_intbrd_1,Y:TAX
 LDA &FB:STA &900E:STX &900F:]:IF NOT ntsc THEN [OPT pass:CMP (&00,X):]
[OPT pass:LDA bck_intbrd_1,Y:STA &900F:NOP:INY:CPY #&C1:BCC Frame1
 LDX #Frame2 MOD 256
 LDY #Frame2 DIV 256
 JMP Exit

.Frame2
 LDA &900E:EOR aux_extbrd_2,Y:AND #&0F:EOR aux_extbrd_2,Y:STA &FB
 LDA aux_extbrd_2,Y:EOR bck_intbrd_2,Y:AND #&0F:STX &900F:EOR bck_intbrd_2,Y:TAX
 LDA &FB:STA &900E:STX &900F:]:IF NOT ntsc THEN [OPT pass:CMP (&00,X):]
[OPT pass:LDA bck_intbrd_2,Y:STA &900F:NOP:INY:CPY #&C1:BCC Frame2
 LDX #Frame1 MOD 256
 LDY #Frame1 DIV 256

.Exit
 STX Server_02+1
 STY Server_02+2
 PLA:STA &FB
 JMP &EABF
]
NEXT
Note: both Marko's and my routine first of all only provide for the stabilization of raster IRQ processing for a single interrupt per frame. If you want multiple, stable, interrupts per frame, this needs a lot more code than what you see here.

Also, a stable raster does not merely "not flicker around vertically for one or two raster lines". It SITS on the raster beam and is also exactly defined in the horizontal position, accurate to a single CPU cycle!

If you just want to have three or more regions on screen, vertically stacked, with different background colours, that can be done without the necessity to use Marko's or my routine:
  • Setup timer 1 to fire once per frame at a given raster line one or two rasters before the first change is supposed to happen. This interrupt provides the base position from which all other interrupts are derived.
  • In the interrupt routine, first check whether timer 1 or timer 2 fired.
  • If timer 1 fired, enter a loop to wait for the correct raster line, "nudge" the CPU with NOPs into the side border, do the background change, and setup timer 2 to fire one or two raster lines *before* the 2nd register change should happen.
  • If timer 2 fired, and the 2nd register change is bound to happen, again enter a loop to wait for the correct raster line, "nudge" the CPU, do the background change and setup timer 2 again to fire one or two raster lines before the 3rd register change.
  • timer 2 fires, and 3rd register change? Again, enter a loop for the correct raster line, CPU NOP "nudge", background change, eventually setup of timer 2 for further interrupts.
What you *shouldn't* do is: use a high frequency IRQ, and poll the VIC raster register inside that interrupt. That's neither stable nor fast, and will grind the foreground process to a halt. Especially, when the interrupt period is on the order of <100 µs.
User avatar
beamrider
Vic 20 Scientist
Posts: 1452
Joined: Sun Oct 17, 2010 2:28 pm
Location: UK

Re: Background change at Raster Location

Post by beamrider »

Using Markos routine as I posted above I got 4 irqs each of which were stable and didn't wander around although some required a few NOPs to get the colour change off screen but I didn't have to poll. I can shift them all up or down based relative to the first split.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Background change at Raster Location

Post by Mike »

beamrider wrote:Using Markos routine as I posted above I got 4 irqs each of which were stable and didn't wander around although some required a few NOPs to get the colour change off screen but I didn't have to poll. I can shift them all up or down based relative to the first split.
That method then only works for equidistant vertical splits.

And, as I said, for just laying out a few vertical regions, both Marko's and my routine are completely over the top.

I use my routine, when I need to split colour registers several times within a single raster, necessarily over a span of several rasters (192 for the height of a MINIGRAFIK bitmap in my example). There, I split the background/border register for a new colour during horizontal beam flyback, at the left edge of the display window, at the right edge of the display window and last, I prepare the auxiliary colour for the next raster before the horizontal flyback comes.

Especially, the changes at the left and right edge of the display window need to be done cycle exact. One cycle off and you're half a character wrong! There's still a one hires pixel difference, but that's due to hardware reasons in the pixel serializer of the VIC chip and nothing can be done to correct this.
User avatar
beamrider
Vic 20 Scientist
Posts: 1452
Joined: Sun Oct 17, 2010 2:28 pm
Location: UK

Re: Background change at Raster Location

Post by beamrider »

Isn't the most important things how long it takes to synchronise? With your simple approach where you suggested setting an IRQ 2 lines prior, won't this be 140 cycles or so? Markos routine is fixed at 82 cycles.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Background change at Raster Location

Post by Mike »

beamrider wrote:Isn't the most important things how long it takes to synchronise? With your simple approach where you suggested setting an IRQ 2 lines prior, won't this be 140 cycles or so? Markos routine is fixed at 82 cycles.
If you want different sized regions, then Marko's method may need to waste a lot more than either 140 or 82 cycles to wait from the 'nearest' interrupt above up to a given colour change.

And, again, the stabilization for cycle-exactness is absolutely unnecessary for the task of just stacking some different coloured regions vertically.

If, for whatever reasons, you want to do two or more distinct raster spans, that additionally are supposed to be stable (in the sense of Marko's or my routine), you'd need to do the IRQ jitter compensation for each of them anew. It is already difficult enough to start a timer at a given cycle-exact position (which is the first thing necessary to get the whole thing working, but normally is only done once, at the setup of the interrupt), the IRQ jitter compensation is critically dependent on which of the raster spans you are using, but would need to be the first thing to be done when entering the IRQ server. But you can't just reuse the same jitter compensation for non-equidistant interrupts, as they're likely to use different offsets for the branch calculation of the NOP slide.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Background change at Raster Location

Post by Mike »

Here's a blueprint of how I would implement the interrupt server using the method I laid out above:

Code: Select all

.IRQ
 LDA $912D
 AND #$40
 BEQ IRQ_00
 LDA #$00
 STA State        ; reset State to 0 if timer 1 fired
.IRQ_00
 LDY State
 INC State

 STY $900F        ; <- Only used during development to see where each interrupt
                  ; happens. You can remove it, when everything works.

 LDA Raster,Y
.IRQ_01
 CMP $9004
 BNE IRQ_01       ; sync to given raster line

 LDX Wait,Y
.IRQ_02
 DEX
 BNE IRQ_02       ; "nudge" workload into right side border or horizontal sync.

 ; the real workload starts here. Note the change of $9004 in IRQ_01 happens
 ; just a few cycles before the display windows starts (on PAL) or even in the
 ; middle of the raster line (on NTSC), so you'll have to position the IRQ well
 ; before the wanted raster (at least two 'double'-lines above), wait for
 ; (double-)raster minus 1, and finally, "nudge" the workload to happen in the
 ; right border or horizontal sync before the wanted raster.
 ;
 ; As this routine isn't fully stabilized, the accuracy will be within 7 cycles
 ; ($9004 wait) +/- 2 cycles (5 cycle DEX loop), which should be sufficient to
 ; even hide border colour changes.

 LDA Colour,Y
 STA $900F

 ; unless this is the last timer 2 interrupt for the frame, (re-)start timer 2
 ; by loading the time into it when it's supposed to fire next.

 CPY #xx          ; "xx" is the number of vertical interrupts, minus 1.
 BEQ IRQ_03
 LDA Timer2Lo,Y
 STA $9128
 LDA Timer2Hi,Y
 STA $9129        ; use the interrupt "display" of STY $900F to place the next
                  ; timer 2 interrupt two (double-)lines above where the next
                  ; colour change is supposed to happen.
 
.IRQ_03
 CPY #00
 BEQ IRQ_04
 JMP $EB18        ; timer 2 interrupts just return to the foreground process.
.IRQ_04
 JMP $EABF        ; the timer 1 interrupt calls the KERNAL IRQ routine to
                  ; process the keyboard. The KERNAL also clears the timer 1
                  ; interrupt in the VIA IFR.
                  ;
                  ; This interrupt should not happen at an arbitrary Y position,
                  ; rather at one, where the next (timer 2) IRQ is supposed to
                  ; have the biggest distance to. This is supposed to give the
                  ; keyboard scan routine enough time (~40 rasters) to do its
                  ; work when a key is pressed.
If you use VICE to build the whole routine to your specification (i.e. colour changes at certain Y-positions), be aware that certain VICE revisions suffer from regression bugs in the VIA implementation. Especially the implementation of timer 2 unfortunately is bugged in those.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Background change at Raster Location

Post by Mike »

The blueprint proved fully functional with just a few changes. Here's a small teaser:

Image
vicassembly
Vic 20 Devotee
Posts: 253
Joined: Fri Mar 19, 2010 1:40 pm

Re: Background change at Raster Location

Post by vicassembly »

Mike:

First of all, THANK you for your time. When I saw your reply with the algorithm I told my wife that I feel so lost and inadequate with understanding this stuff. She smiled and guffawed at me.

Your nice screen print is what I want...... I really want to learn so I am going to ask questions.

"Setup timer 1 to fire once per frame at a given raster line one or two rasters before the first change is supposed to happen. This interrupt provides the base position from which all other interrupts are derived."

I see your words and I even see the code that you gave me. I simply don't comprehend this. Set timer 1 up to fire once per frame one or two rasters before? I guess I don't understand timers? How do timers work? Is that what this code does?

.IRQ
LDA $912D
AND #$40
BEQ IRQ_00
LDA #$00
STA State ; reset State to 0 if timer 1 fired
.IRQ_00

I looked at my memory map and it says "this bit is set by an expiration of timer 1 and reset when a read of timer usb or write of timer 1 msb takes place."



"In the interrupt routine, first check whether timer 1 or timer 2 fired."

It appears that this is what you are checking above? I have no idea. When you say interrupt are you speaking of the IRQ interrupt that hits and goes to vector $0314/5?

"If timer 1 fired, enter a loop to wait for the correct raster line, "nudge" the CPU with NOPs into the side border, do the background change, and setup timer 2 to fire one or two raster lines *before* the 2nd register change should happen.
If timer 2 fired, and the 2nd register change is bound to happen, again enter a loop to wait for the correct raster line, "nudge" the CPU, do the background change and setup timer 2 again to fire one or two raster line before the 3rd register change.
timer 2 fires, and 3rd register change? Again, enter a loop for the correct raster line, CPU NOP "nudge", background change, eventually setup of timer 2 for further interrupts."


I am so lost by this. How is timer 1 fired? What does that mean? I get that it should be fired prior to a given raster line. I am getting frustrated by my lack of understanding.
Same questions for timer 2.

Sigh... I need an education on this. Can I pay for a Skype lesson? :-).

I could probably look at your vice code and convert that to a CC65 routine. I don't even know how to enter code in vice but that is not the issue.
I'm delving into things that are brand new.
User avatar
srowe
Vic 20 Scientist
Posts: 1340
Joined: Mon Jun 16, 2014 3:19 pm

Re: Background change at Raster Location

Post by srowe »

Best home work is to read the 6522 datasheet. There are details in there such as the order of writing to the 16 bit timer registers and interrupt enabling that just aren't obvious from looking at code.

In short you load the timer register with a value and when it crosses from 0>FFFF it flags an event. If that event is defined to cause a CPU interrupt then one is raised. Depending on which VIA it is this is either an IRQ or an NMI.

The tricky bit with raster interrupts is that the cycles to set up the timer and those needed to reach the interrupt handler code are significant.
Post Reply