Как показать спрайты на границе на C64?

Я видел крутые демо C64, показывающие спрайты по краям экрана. Это не должно быть возможно; Думаю, им как-то удалось обмануть графический чип. Как именно они это сделали?


person Danko Durbić    schedule 25.09.2009    source источник
comment
чтобы получить больше разрешенных спрайтов, вы должны перемещать спрайт достаточно быстро по отношению к частоте обновления телевизора: спрайт рисуется на экране в месте A, вы перемещаете его в место B, и он снова перерисовывается и т. д., а затем когда экран телевизора будет отрисован, вернитесь к месту A или туда, куда когда-либо A перемещался сейчас.   -  person KM.    schedule 25.09.2009
comment
Я только что добавил c64 в свой список интересных тегов. +1 от меня!   -  person Paolo Tedesco    schedule 25.09.2009
comment
на его старшем брате (сестре?) Amiga они добавили целый сопроцессор (медь), чтобы определить, какие изменения видеооборудования должны быть сделаны на каких растровых линиях экрана. Устранение необходимости в сложных программах прерывания сборки   -  person Toad    schedule 25.09.2009
comment
как этот вопрос может быть не конструктивным? Как мне показать что-то на границе, и ответ содержит код для этого. Как это может быть более конструктивным, чем это?   -  person Peter Kofler    schedule 15.01.2013
comment
@TylerCrompton В отличие от того, как я печатаю текст у края экрана, спрайты не должны быть на границе.   -  person wizzwizz4    schedule 14.04.2017
comment
@KM (извините, что откопал старый комментарий), но на самом деле вы не перемещаете спрайт в более быстром отношении к телевизору. Вместо этого вы указываете VIC-II генерировать прерывание на определенной строке, и когда растровый луч достигает этой строки, вы снова устанавливаете указатели спрайтов. Повторите для необходимого количества строк. Просто оставьте небольшое пространство между каждой высотой спрайта (если только вы не начнете хрустеть спрайтом). VIC-II справится с остальным.   -  person cbmeeks    schedule 15.01.2019


Ответы (6)


Да, нужен ассемблер. Это трюк с синхронизацией прерываний. VIC может отображать спрайты на границе, но рамка их просто скрывает, поэтому спрайты могут скользить за ней. Он подключен к линиям сканирования, отображаемым VIC. Для нижних/верхних границ это довольно просто:

  • Запрограммируйте прерывание, синхронизированное для запуска с определенной строки развертки, 7 пикселей или что-то в этом роде до нижней границы.
  • Установите регистр в VIC, чтобы сделать границу меньше. (Есть регистр, который может это сделать.)
  • ВИК сейчас считает, что граница уже началась и не начинает ее закрашивать.
  • -> Нет границы внизу.
  • Запрограммируйте еще одно прерывание после реальной границы, чтобы вернуть ее в исходное состояние.

Для спрайтов в левой/правой границах это сложнее, потому что процесс нужно повторять для каждой строки сканирования:

  • Запрограммируйте прерывание, синхронизированное с запуском на определенной строке развертки.
  • Затем выполните несколько NOP, пока не окажетесь за 7 пикселей до правой границы.
  • Установите регистр в VIC, чтобы сделать границу меньше.
  • -> Нет границы с правой стороны.
  • Сделайте несколько NOP, пока не дойдете до реальной границы и не верните регистру исходное значение.
  • Снова сделайте несколько NOP до шага 2.

Проблема в том, что все эти NOP-операторы заняты ожиданием и крадут циклы, которые у вас есть для ваших вещей.

Мне удалось найти для вас кое-какой код из скроллера спрайтов на нижней границе. Вот код. (Это было вырвано из какой-то демо.)

C198  78        SEI
C199  20 2E C1  JSR C12E     # clear sprite area
C19C  20 48 C1  JSR C148     # init VIC
C19F  A9 BF     LDA #BF      # set up IRQ in C1BF
C1A1  A2 C1     LDX #C1
C1A3  8D 14 03  STA 0314
C1A6  8E 15 03  STX 0315
C1A9  A9 1B     LDA #1B
C1AB  8D 11 D0  STA D011
C1AE  A9 F7     LDA #F7
C1B0  8D 12 D0  STA D012
C1B3  A9 01     LDA #01
C1B5  8D 1A D0  STA D01A
C1B8  A9 7F     LDA #7F
C1BA  8D 0D DC  STA DC0D
C1BD  58        CLI
C1BE  60        RTS

----------------------------------
# init VIC
C148  A2 00     LDX #00
C14A  BD 88 C1  LDA C188,X
C14D  9D 00 D0  STA D000,X   # set first 16 values from table
C150  E8        INX
C151  E0 10     CPX #10
C153  D0 F5     BNE C14A
C155  A9 FF     LDA #FF
C157  8D 15 D0  STA D015
C15A  A9 00     LDA #00
C15C  8D 1C D0  STA D01C
C15F  A9 FF     LDA #FF
C161  8D 17 D0  STA D017
C164  8D 1D D0  STA D01D
C167  A9 C0     LDA #C0
C169  8D 10 D0  STA D010
C16C  A9 F8     LDA #F8
C16E  A2 00     LDX #00
C170  9D F8 07  STA 07F8,X
C173  18        CLC
C174  69 01     ADC #01
C176  E8        INX
C177  E0 08     CPX #08
C179  D0 F5     BNE C170
C17B  A9 0E     LDA #0E
C17D  A2 00     LDX #00
C17F  9D 27 D0  STA D027,X
C182  E8        INX
C183  E0 08     CPX #08
C185  D0 F8     BNE C17F
C187  60        RTS

----------------------------------
# data set into VIC registers
C188  00 F7 30 F7 60 F7 90 F7
C190  C0 F7 F0 F7 20 F7 50 F7

----------------------------------
# main IRQ routine
C1BF  A2 08     LDX #08
C1C1  CA        DEX
C1C2  D0 FD     BNE C1C1
C1C4  A2 28     LDX #28      # 40 or so lines
C1C6  EA        NOP          # "timing"
C1C7  EA        NOP
C1C8  EA        NOP
C1C9  EA        NOP
C1CA  CE 16 D0  DEC D016     # fiddle register
C1CD  EE 16 D0  INC D016
C1D0  AC 12 D0  LDY D012
C1D3  88        DEY
C1D4  EA        NOP
C1D5  98        TYA
C1D6  29 07     AND #07
C1D8  09 18     ORA #18
C1DA  8D 11 D0  STA D011
C1DD  24 EA     BIT   EA
C1DF  EA        NOP
C1E0  EA        NOP
C1E1  CA        DEX
C1E2  10 E4     BPL C1C8     # repeat next line
C1E4  A9 1B     LDA #1B
C1E6  8D 11 D0  STA D011
C1E9  A9 01     LDA #01
C1EB  8D 19 D0  STA D019
C1EE  20 00 C0  JSR C000   # call main code
C1F1  4C 31 EA  JMP EA31   # finish IRQ
person Peter Kofler    schedule 25.09.2009
comment
Я думаю, что код инициализации VIC по адресу 0xC148 в основном устанавливает спрайты на границе для скроллера спрайтов (это связано с отключением границы). Но спрайты должны быть там, иначе время строк VIC/растров отличается, и все это работает не работает. И вы все равно хотите быть там спрайтами :-) - person Peter Kofler; 28.09.2009

Все зависело от времени. У C64 был метод запроса точного вертикального положения электронного луча во время рисования экрана. Когда начиналась новая строка, нужно было подождать несколько тактов (это можно было засечь с помощью инструкции NOP), а затем нужно было установить аппаратный регистр видеочипа, отвечающий за установку режима экрана (и ширины границы). Точно рассчитывая время и повторяя каждую строку сканирования, вся боковая граница исчезла.

Аналогичным трюком ушла нижняя граница. На той строке сканирования, где начиналась вертикальная граница, вам также нужно было установить видеорежим, который отключил нижнюю границу для этого кадра.

На самом деле все это нужно было делать в сборе. В противном случае вы никогда не сможете точно определить время.

В качестве примечания, я думаю, что трюк с боковой границей был приписан команде 1001 Crew (голландская группа). Я не уверен, кто провернул первый трюк с нижней границей.

person Toad    schedule 25.09.2009
comment
да, но это не тот регистр, который вам нужно было установить, чтобы изменить режим экрана - person Toad; 04.02.2013
comment
Я знаю, но вы написали это довольно неконкретно. Поэтому я просто хотел добавить некоторые детали. Я полагаю, что $d011 был тем, кто выбирал, используете ли вы 25 или 24 строки, что делало границу контрактной или нет. en.wikipedia.org/wiki/MOS_Technology_VIC-II - person BerggreenDK; 06.02.2013

Чтобы получить хорошее руководство по теме открытия границ на C64, ознакомьтесь с отличной статьей Паси Ояла в C=Взлом 6.

Не вдаваясь в технические подробности, трюк использует функцию чипа VIC, позволяющую переключаться между 25/24 строками и 40/38 столбцами текста/графики, и включает в себя это переключение в нужный момент, чтобы обмануть VIC и заставить его думать, что это уже включил границы, когда на самом деле это не так. Ознакомьтесь с приведенной выше статьей для более подробного объяснения с примерами кода.

person Lars Haugseth    schedule 11.04.2010

Это было давно.

Я знаю, что было решение, основанное на частоте монитора.

С ЭЛТ текущий пиксель известен, даже если он находился за пределами обычного экрана. Таким образом, вы можете манипулировать лучом.

Где-то в моей куче мусора должно быть несколько книг C64.

Оффтоп, но графика с VIC20 (предшественник C64) была забавной. Не было возможности манипулировать каждым пикселем, но можно было изменить существующие символы. Итак, вы заполнили экран всеми символами от 0 до ... и изменили символы, чтобы установить пиксели на экране. ;-).

person Toon Krijthe    schedule 25.09.2009
comment
Также на C64 было не очень просто поменять пиксель. Графический режим по существу эмулировал экран символьного режима, заполненный символами 0, 1, .... :-) - person peterh; 23.05.2017
comment
Вы можете манипулировать каждым пикселем, используя растровый режим высокого разрешения. Или пары из них в режиме многоцветного растрового изображения, но при этом всегда используется 8000 байт (не 8 КБ), и у вас их не всегда есть. Кстати, вы не изменяете существующие символы, они находятся в ПЗУ, поэтому вы не можете этого сделать, но вы можете скопировать их в ОЗУ и затем манипулировать ими. - person ; 26.11.2020

Как уже было сказано, вам нужно обмануть ВИК, чтобы он подумал, что граница уже началась, но причина, по которой я пишу это, заключается в том, что верхний ответ немного неточен: я совершенно не смог найти регистр, чтобы сделать границу меньше, поэтому вы делаете это следующим образом (по крайней мере, для верхней и нижней части): вы ждете, пока VIC не достигнет 25-й строки символов, а затем вы включаете 24 строки ($D011, бит 3). Вы можете сделать то же самое для левой и правой границы, только с 38 столбцами ($D016, бит 3), но для этого вам нужно очень точное время, и вам также нужно устранить плохие строки, установив регистр вертикальной прокрутки. , поэтому мод 8 линии развертки никогда не равен значению прокрутки. Конечно, вы больше не можете использовать обычное отображение, потому что плохие строки на самом деле не просто плохие, я думаю, они используются для загрузки символьных данных, которые повторяются для каждой 8-й строки в неграничной области. Лично я был немного сбит с толку, когда прочитал верхний ответ, надеюсь, это поможет. (Кроме того, в верхнем ответе есть ошибка: вы не делаете границу меньше, вы делаете ее больше)

person Community    schedule 26.11.2020

Время было ключевым. Изображение было создано на границе путем изменения цвета границы (границы) по мере того, как луч ЭЛТ перемещался слева направо. Для создания изображения необходимы два синхронизирующих сигнала — обновление по вертикали и обновление по горизонтали. Обнаружив, когда происходит горизонтальное и вертикальное обновление, вы можете запустить последовательность инструкций ассемблера, чтобы изменить цвет границы для создания изображения. Вам нужно рассчитать количество тактовых импульсов ЦП на пиксель границы и использовать это для создания кода, который меняет цвет границы в нужной точке.

Это не очень хорошо работает, когда дело доходит до написания игр, поскольку накладные расходы ЦП слишком велики, чтобы можно было увеличить время для обработки пользовательского ввода и состояния игры.

person Skizz    schedule 25.09.2009
comment
это не так, как это было сделано ... Процессор был слишком медленным, чтобы изменить цвет достаточно быстро, чтобы любой пиксель мог быть освещен. В лучшем случае. Способ удалить границу состоял в том, чтобы точно определить момент начала/остановки границы и изменить режим отображения. (смотри мой ответ) - person Toad; 26.09.2009
comment
Я только что сделал некоторые расчеты: 25 кадров по 250 строк в кадре — это 6250 строк в секунду или 0,00016 секунды в строке. На частоте 1 МГц это 160 инструкций, поэтому максимум, что вы можете получить, используя этот метод, — это 160 пикселей на экране. Я видел изображения на границе, сделанные таким образом, и они были довольно блочными. - person Skizz; 28.09.2009
comment
Как только вы правильно выберете время и откроете/отключите вертикальную границу, спрайты (если они есть), размещенные там, станут видимыми. Вот как вы получаете графику там. - person BerggreenDK; 04.02.2013
comment
Не уверен, что ваши расчеты верны, Skizz. Например, инструкция по увеличению ячейки памяти не на нулевой странице занимает 6 циклов. Или инструкция по сохранению аккумулятора в память составляет 4 такта. А один машинный цикл — это 8 кадровых пикселей (или 4 многоцветных пикселя) растрового времени. - person OmarL; 30.08.2017