Стабильный растр на C64

Используя сборку 6510 на Commodore 64, я пытаюсь создать стабильный растровый эффект. Используя принцип двойного IRQ, я рисую несколько растровых линий на экране. Я дополняю NOP, чтобы они соответствовали 63 циклам для каждой обычной линии сканирования и 23 циклам для каждой плохой линии. Я понимаю, что есть конкретная стартовая строка, которую мне нужно установить, чтобы сопоставить восьмую итерацию с плохой линией, но независимо от того, на какую строку я ставлю первую строку или какую комбинацию NOP я использую, я не могу получить Правильный выбор времени. Я хочу полные строки, которые не "сломаны". Кто-нибудь может увидеть, что я делаю не так? Код в формате Kick Assembler. И вот скриншот:

Скриншот

.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 ответа

Решение

Как я понимаю, ваша проблема не в том, что ваши растровые полосы мерцают (т.е. ваше растровое прерывание стабильно), а в том, что вторая линия растровой панели, которую вы рисуете на экране, не полностью красная.

Ваша проблема в плохих линиях. (См. [1])

После того, как вы стабилизировали свое растровое прерывание с помощью кода, который вы разместили, ваш "фактический код" начнет работать с цикла 4 растровой линии $3A.

Вторая строка вашего растра, где вы хотите, чтобы цвет фона и границы были красными, это плохая линия. (Это растровая линия $3B. Поскольку $D011 = $1B, это плохая линия, так как младшие 3 бита $ D011 и $D012 одинаковы)

В этой плохой строке первый INC (INC $D020) удается запустить, поэтому цвет границы становится красным. Затем запускается второй INC (INC $D021), но VIC вступает во владение до его завершения, и, таким образом, ваш INC $ D021 не завершается, пока VIC не вернет шину. (Это спустя 43 цикла - т.е. установка цвета фона на красный задерживается на 43 цикла).

У вас почти это было, но плохая линия была на другой растровой линии, чем ожидалось в вашем коде, и вам нужно было "протолкнуть несколько циклов", чтобы оба INC были выполнены на плохих линиях до того, как VIC их прервет. (Слишком поздно начинать выполнение двух INC в 4-м цикле плохой линии, если вы хотите, чтобы они оба были выполнены до того, как VIC вступит во владение)

Обновленный пример:

Попробуйте заменить этот раздел вашего кода:

.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, если вы не не планирую отображать графику персонажей)

Попробуйте проверить монитор машинного кода в эмуляторе HOXS64. Он идеально подходит для устранения проблем, связанных с синхронизацией. Он показывает вам, на каком цикле какой растровой линии вы находитесь в данный момент времени (+ он может прерваться при получении прерывания).

Надеюсь, это помогло:)


Заметьте, я не досконально изучил вашу процедуру стабильного растра на предмет подводных камней, но, похоже, все в порядке - подход правильный, и у вас нет мерцания. Если вы начинаете получать мерцающие растровые полосы, вы знаете, что нужно исправить. ;)


Если кто-то читает это, он не знает, что такое плохие линии:

Классные ссылки:

[1]: Подробнее о "плохих строках" читайте в статье vic (или "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 начинает рисовать первую растровую строку текстовой строки, она отнимает у процессора 40-43 цикла (см. Ниже, почему не всегда 43). Эти растровые линии называются "плохими". Таким образом, на плохой линии доступно только 20-23 цикла вместо 63.

(Точнее, плохая линия возникает, когда 3 самых младших бита $ D011 равняются 3 самым младшим битам $D012 (и мы не на границе, и экран не был "выключен" битом 4 из $D011))

Чип VIC использует последние 40 из этих 43 циклов для считывания 40 символов, которые должны отображаться в текстовой строке. Процессор не может выполнить никаких инструкций в течение этих 40 циклов.

Однако в течение первых 3 циклов этих 43 циклов ЦП может фактически выполнять "циклы записи" своих инструкций - но только циклы записи, а не циклы чтения. (См. [2]). Поэтому, если вы правильно рассчитали свои коды операций, вы можете выполнить некоторые из циклов ваших инструкций в течение этих 3 циклов. (Примечание: единственная инструкция с 3 циклами записи - это "brk", которая обычно бесполезна, поэтому на практике вы сможете использовать не более 2 из этих 3 циклов для чего-то полезного).

Обратите внимание, что в дополнение к циклам кражи на плохих линиях, VIC также будет кражу циклов из ЦП на линиях растра со спрайтами.

[2]: См. "64doc", чтобы узнать, какие циклы различных инструкций C64 являются циклами записи: http://vice-emu.sourceforge.net/plain/64doc.txt
(Циклы записи отмечены как "W" в таблицах, а циклы чтения отмечены как "R")

[X]:... И на http://codebase64.org/ есть много хороших статей.

Для того, чтобы растровые линии не мерцали, есть некоторая хитрость, необходимая для стабилизации времени. Причина в том, что вы никогда не можете быть уверены, что ваша растровая подпрограмма выполняется в самом начале строки, но в зависимости от того, где процессор "оставляет" основной программный код, неизвестное количество циклов теряется для выполнения последней операции. Существуют различные подходы для достижения этой цели, вы должны проверить эту страницу на Codebase64, чтобы узнать больше об этой теме и получить пример кода. Однако, как только вы установили стабильное время растра, ваш подход выглядит хорошо для меня.

Возможно, вы захотите проверить это: https://github.com/Zibri/C64_Stable_Raster .

Суть процедуры HSYNC заключается в следующем:

      DEC $DC03         ; trigger the light pen
INC $DC03         ; restore port B to input
LDA $D013         ; read the raster X position
STA $2
LSR A
LDA $2
ADC #$00          ; if carry is set this is a 8565 i f it's clear it's a 6569
CMP #$0B          ; this is just sheer magic :D
ADC #$11          ; this is just sheer magic :D
LSR A
LSR A
STA SS+1          ; A will be: 0-6
SS:
BVC *
; the following 1 cycle clock slide does not affect any registers nor the cpu status.

.BYTE $80
.BYTE $80, $80
.BYTE $80, $80
.BYTE $44,$5A
RTS  ; Here we we always be at cycle 52 of a scanline.

Если я правильно помню (от 20+ лет назад)..

Из-за незначительного дрожания прерывания не совсем возможно получить абсолютно стабильную синхронизацию для растровых эффектов; так что независимо от того, что вы делаете в программном обеспечении, у вас будет "мерцающее" пятно в начале линии сканирования.

Чтобы исправить это, используйте спрайт, чтобы скрыть мерцающее пятно.

Другие вопросы по тегам