Using 6510 assembly on the Commodore 64, I am trying to create a stable raster effect. Using the double IRQ principle I draw some raster lines on the screen. I pad with NOPs to match 63 cycles for every normal scanline, and to 23 cycles for every badline. I realise that there is a specific start line I need to set, in order to match my 8th iteration with a badline, but no matter on what line I put the first line or what combination of NOPs I use, I can't get the timing right. I want complete lines that are not "broken". Can anyone see what I am doing wrong? Code is in Kick Assembler format. And here is a screenshot:
.pc = $0801 "Basic upstart"
:BasicUpstart($8000)
.pc = $8000 "Program"
jsr $ff81
sei
lda #$35
sta $01
jsr setupInterrupts
cli
jmp *
setupInterrupts:
lda #<int1
ldy #>int1
sta $fffe
sty $ffff
lda #$01
sta $d01a
lda #$7f
sta $dc0d
sta $dd0d
lda $dc0d
lda $dd0d
lda #$1b
sta $d011
lda #$01
sta $d019
lda start
sta $d012
rts
start:
.byte 56
int1:
pha txa pha tya pha
:STABILIZE()
.for (var i=0; i<7; i++) {
inc $d020 // 6 cycles
inc $d021 // 6 cycles
nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop // 24*2=48 cycles
bit $ea // 3 cycles
// = 63 cycles
}
inc $d020 // 6 cycles
inc $d021 // 6 cycles
nop nop nop nop // 4*2=8 cycles
bit $ea // 3 cycles
// = 23 cycles (badline)
lda #$00
sta $d020
sta $d021
lda start
sta $d012
lda #<int1
ldy #>int1
sta $fffe
sty $ffff
lda #$01
sta $d019
pla tay pla tax pla
rti
.macro STABILIZE() {
lda #<nextRasterLineIRQ
sta $fffe
lda #>nextRasterLineIRQ
sta $ffff
inc $d012
lda #$01
sta $d019
tsx
cli
nop nop nop nop nop nop nop nop
nextRasterLineIRQ:
txs
ldx #$08
dex
bne *-1
bit $00
lda $d012
cmp $d012
beq *+2
}
As i understand you, your problem isn't that your raster bars are flickering (i.e. your raster interrupt is stable), but that the second line of the raster-bar you are drawing on the screen isn't fully red.
Your problem is bad lines. (See [1])
After you have stabilized your raster interrupt, with the code you posted, your "actual code" will start running at cycle 4 of rasterline $3A.
The second line of your raster-bar, where you want to the background color and the border color to be red, is a bad line. (It is raster-line $3B. Since $D011 = $1B, this is a bad-line, since the lower 3 bits of $D011 and $D012 are the same)
On this bad line, the first INC (INC $D020) manages to run, so the border color becomes red. Then the second INC (INC $D021) starts running, but the VIC takes over before it completes, and your INC $D021 is thus not completed until after the VIC has given the bus back. (This is 43 cycles later - i.e. setting the background-color to red is delayed by 43 cycles).
You almost had it, but the badline was on a different raster-line than what your code expected and you needed to "push a few cycles" so that both INCs would be executed on badlines before being interrupted by the VIC. (It is a bit too late to start executing the two INCs at cycle 4 of a badline, if you want both of them to be executed before the VIC takes over)
Updated Example:
Try replacing this section of your code:
.for (var i=0; i<7; i++) {
inc $d020 // 6 cycles
inc $d021 // 6 cycles
nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop // 24*2=48 cycles
bit $ea // 3 cycles
// = 63 cycles
}
inc $d020 // 6 cycles
inc $d021 // 6 cycles
nop nop nop nop // 4*2=8 cycles
bit $ea // 3 cycles
// = 23 cycles (badline)
With this:
// a delay to get to some cycle at the end of the raster-line, so we have time to execute both inc's on
// each successive raster-line - in particular on the badlines before the VIC takes over the bus.
.for (var i=0; i<28; i++) nop
// just for illustrative purposes - not cool code :)
.for (var i=0; i<8*6; i++) {
inc $d020 // 6 cycles
inc $d021 // 6 cycles
.if ([i & %111] == 0) {
// badline
nop nop nop nop // 4*2=8 cycles
} else {
// non-badline
nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop // 24*2=48 cycles
bit $ea // 3 cycles
// = 63 cycles
}
}
(Warning: This code is quite wasteful memory-wise - the same effect could easily be made with a normal loop) (Instead of varying the delay, you could alternatively push the bad-lines away, by modifying $D011, if you don't plan to display character graphics)
Try checking out the machine-code monitor in the HOXS64 emulator. It is perfect for debugging timing related issues. It shows you which cycle of which raster-line you are on at any given time (+it can break on interrupt taken).
Hope this helped :)
Note, i haven't throughly looked through your stable-raster routine for pitfalls, but it seems ok - the approach is correct and you don't have any flickering. If you start getting flickering raster-bars, you know what to fix. ;)
In case someone reading this does not know what badlines are:
Cool References:
[1]: Read more about "bad lines" in the vic-article (or "the vic-bible" as it deserves to be called): http://csdb.dk/release/?id=44685 (PDF) or http://vice-emu.sourceforge.net/plain/VIC-Article.txt (TXT). Also see the addendum: http://vice-emu.sourceforge.net/plain/VIC-Addendum.txt
Basically, when the VIC-chip starts drawing the first raster-line of a text-line, it steals 40-43 cycles from the CPU (see below for why not always 43). These raster-lines are called "bad lines". On a bad line there are thus only 20-23 cycles available, instead of 63.
(To be more precise, a badline occurs when the 3 lowermost bits of $D011 equals the 3 lowermost bits of $D012 (and we're not in the border and the screen haven't been "switched off" by bit 4 of $D011))
The VIC-chip uses the last 40 of these 43 cycles to read the 40 characters which is to be displayed on the text-line. The CPU can't execute any instructions during these 40 cycles.
During the first 3 cycles of these 43 cycles, however, the CPU can actually execute "write cycles" of its instructions - but only write cycles, not read cycles. (See [2]) So if you time your opcodes correctly, you can execute some of the cycles of your instructions during these 3 cycles. (Note: the only instruction with 3 write-cycles is "brk", which is usually useless, so in practice you will only be able to use at most 2 of these 3 cycles for something useful).
Note that in addition to stealing cycles on badlines, the VIC will also steal cycles from the CPU on raster-lines with sprites.
[2]: See "64doc" to learn which cycles of the C64's different instructions are write-cycles: http://vice-emu.sourceforge.net/plain/64doc.txt
(Write-cycles are marked as "W" in the tables, and read-cycles are marked as "R")
[X]: ...And there are lots of good articles at http://codebase64.org
In order to get the raster lines flicker free there is some extra trickery needed to stabilize the timing. The reason is, that you can never be sure that your raster routine is executed at the very beginning of a line but depending on where the CPU "leaves" the main program code an unknown amount of cycles is wasted to execute the last operation. There are different approaches to reach this goal, you should check out this page on Codebase64 to learn more about this topic and get some sample code. However, once you have established a stable raster timing your approach looks good to me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With