13

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      
}
4

3 回答 3

14

据我了解,您的问题不在于您的光栅条在闪​​烁(即您的光栅中断是稳定的),而是您在屏幕上绘制的光栅条的第二行不是全红的。

你的问题是线条不好。(见[1])

在您稳定光栅中断后,使用您发布的代码,您的“实际代码”将在光栅线 $3A 的第 4 周期开始运行。

您希望将背景颜色和边框颜色设置为红色的光栅条的第二行是一条坏线。(它是光栅线 $3B。由于 $D011 = $1B,这是一条坏线,因为 $D011 和 $D012 的低 3 位相同)

在这条坏线上,第一个 INC (INC $D020) 成功运行,因此边框颜色变为红色。然后第二个 INC (INC $D021) 开始运行,但 VIC 在它完成之前接管,因此您的 INC $D021 直到 VIC 将总线归还后才完成。(这是 43 个周期之后 - 即将背景颜色设置为红色会延迟 43 个周期)。

您几乎拥有它,但是 badline 位于与您的代码预期不同的光栅线上,您需要“推几个周期”,以便在被 VIC 中断之前,两个 INC 都将在 badline 上执行。(如果您希望在 VIC 接管之前执行这两个 INC,那么在 badline 的周期 4 开始执行两个 INC 有点太晚了)

更新示例:

尝试替换这部分代码:

.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)

有了这个:

// 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
  }
}

(警告:这段代码在内存方面非常浪费 - 使用普通循环很容易产生相同的效果)(除了改变延迟之外,您还可以通过修改 $D011 将坏线推开,如果你不这样做' t 计划显示字符图形)

尝试检查 HOXS64 模拟器中的机器代码监视器。它非常适合调试与时序相关的问题。它向您显示您在任何给定时间处于哪个光栅线的哪个周期(+它可以在中断时中断)。

希望这有帮助:)


请注意,我还没有彻底查看您的稳定光栅例程中的陷阱,但它似乎没问题 - 方法是正确的并且您没有任何闪烁。如果您开始出现闪烁的光栅条,您就知道要解决什么问题。;)


如果有人阅读本文不知道什么是坏话:

酷参考:

[1]:阅读 vic-article(或称其为“vic-bible”)中有关“坏线”的更多信息:http ://csdb.dk/release/?id=44685 (PDF) 或http://vice-emu.sourceforge.net/plain/VIC-Article.txt (TXT)。另见附录:http: //vice-emu.sourceforge.net/plain/VIC-Addendum.txt

基本上,当 VIC 芯片开始绘制文本行的第一条光栅线时,它会从 CPU 中窃取 40-43 个周期(见下文,了解为什么不总是 43 个)。这些光栅线称为“坏线”。因此,在一条坏线上,只有 20-23 个周期可用,而不是 63 个。

(更准确地说,当 $D011 的 3 个最低位等于 $D012 的 3 个最低位时(并且我们不在边界中并且屏幕没有被 $ 的第 4 位“关闭” D011))

VIC 芯片使用这 43 个周期中的最后 40 个周期来读取要在文本行上显示的 40 个字符。CPU 在这 40 个周期内不能执行任何指令。

然而,在这 43 个周期的前 3 个周期中,CPU 实际上可以执行其指令的“写周期”——但只能执行写周期,而不是读周期。(请参阅 [2])因此,如果您正确计时操作码,则可以在这 3 个周期内执行指令的某些周期。(注意:具有 3 个写周期的唯一指令是“brk”,这通常是无用的,因此在实践中,您最多只能使用这 3 个周期中的 2 个来获得有用的东西)。

请注意,除了在坏线上窃取周期外,VIC 还将在带有精灵的光栅线上从 CPU 窃取周期。

[2]:请参阅“64doc”以了解 C64 不同指令的哪些周期是 write-cycles:http: //vice-emu.sourceforge.net/plain/64doc.txt
(Write-cycles 在表和读取周期标记为“R”)

[X]: ...而且在http://codebase64.org上有很多好文章

于 2014-09-01T22:10:10.523 回答
1

为了使光栅线无闪烁,需要一些额外的技巧来稳定时间。原因是,您永远无法确定您的光栅例程是在一行的最开头执行的,但取决于 CPU“离开”主程序代码的位置,会浪费未知数量的周期来执行最后一个操作。有不同的方法可以达到这个目标,你应该查看Codebase64 上的这个页面来了解更多关于这个主题并获取一些示例代码。但是,一旦您建立了稳定的光栅计时,您的方法对我来说看起来不错。

于 2014-07-01T07:47:48.500 回答
-3

如果我没记错的话(从 20 多年前开始)..

由于较小的中断抖动,不太可能获得完全稳定的光栅效果时序;所以无论你在软件中做什么,你都会在扫描线的开头有一个“闪烁”的地方。

要解决此问题,请使用精灵隐藏闪烁点。

于 2014-06-24T02:31:45.573 回答