Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stable raster on C64

Tags:

assembly

6510

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:

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      
}
like image 931
Kristoffer Jälén Avatar asked Jun 23 '14 21:06

Kristoffer Jälén


2 Answers

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

like image 144
DisplayName Avatar answered Nov 18 '22 11:11

DisplayName


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.

like image 36
Nith Avatar answered Nov 18 '22 11:11

Nith