Стабильный растр на 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+ лет назад)..
Из-за незначительного дрожания прерывания не совсем возможно получить абсолютно стабильную синхронизацию для растровых эффектов; так что независимо от того, что вы делаете в программном обеспечении, у вас будет "мерцающее" пятно в начале линии сканирования.
Чтобы исправить это, используйте спрайт, чтобы скрыть мерцающее пятно.