SYNCHRONIZATION - XORED CYRCLES
Привет всем.
На последнем собрании true zx'еров, мы все много общались. Ваня ПирогFlexx и Валtsl просили меня объяснить, как же был сделан эффект «XOR-CYRCLES» в SYNCHRONIZATION. Но я как не шуршал мозгами замаринованные в коньяке, так и не смог вспомнить.
Речь идёт вот про этот эффект Xored Cyrcles
1. Вначале прерывание, которое происходит 240 раз. Оно нужно, что бы в первую очередь, задать новую отображаемую линию для текстового режима, и во вторую очередь выпендриться и покрасить бордюр в новый цвет. Но главная фишка именно в обновлении отображение новой линии. У меня есть зарезервированные страницы с какого-то там места, выделенного автоматически под именем VTX_PAGE. В VTX_PAGE лежит экранная область отображаемая TSLDac'ом, но линейно. То есть вначале отобразятся первый 64 символа, потом вторые, ну и так 240 раз… В итоге у нас получается некая видео-область размером в 240*64, что является 8-ми tslстраниц. Нужно помнить, что у Вала каждая новая строчка пересчитывается с учётом смещения по Y, в итоге каждое прерывание обновляет новое смещение по Y и высчитывает новый адрес страницы для VideoPage. Выглядит это так:
2. Далее подготовка графики для текста. В предыдущем пункте решена проблема с отображением, теперь когда у нас есть линейное отображение памяти в пикселях, можно заниматься магией. Итак, у нас высвечивается 480 по 8 пикселей, код текста с адреса VTX_PAGE+64*LINE. Реальных, а точнее привычных нам пикселей не 8 а 4, по скольку текст в двое плотнее стандартного графического режима. Теперь перед всем этим эффектом, один раз заполняем графику текста, так что бы код буквы соответствовал XORу верхней и нижней тетрады кода символа.. То есть например код отображаемого символа в бинари такой «10010011» это значит что я хочу сделать «1001» XOR «0011» = «1010». Отлично, теперь символ с кодом «10010011», заполняем графикой восемь раз «11001100», что равно «1010». Однако, сделаем эффект более интересный, где пиксели будут преобладать над пустотой, тогда появится «жирность». Итак сегенируем табличку ITEG_TBL из 16-ти значений утолщённых пикселей. Графика текста складывается в страницу (VTX_PAGE+1). Генератор графики получается таким:
3. Далее подготовка кругов. Я не буду расписывать сам генератор, но смысл прост, нам нужно сгенерировать 8-мь гигантских экранов размером 128х512 байт, где графика будет хранить в верхней тетраде, и ещё 8-мь экранов точно таких же, где графика будет в нижней тетраде. Получится что нужно 128*512*8*2 = 1 мегабайт памяти. Его и заполняем графикой кругов. Вот пока вылетает череп с шариками вместо глаз, это и происходит на заднем плане…
4. Далее DMA копирование, которое будет формировать изображение. Тут всё просто… Вначале нужно скопировать с помощью DMA, с флагом RAM_RAM, из первой области гигантского экрана в VTX_PAGE видео-области. Следующим шагом скопировать в то же самое место VTX_PAGE видео-области из второй области гигантского экрана но с флагом BLT_RAM. Получается что вы скрестите верхние и нижние тетрады двух гигантских экранов с кругами в видео-области. И по-скольку графика каждого кода символа уже раскранчена XOR'ом, у вас получается аппарат, который сам за вас XOR'ит. Ваша задача только задавать DMA аппарату, что, куда и откуда копировать, ну и процессором ничего не делать. Вот выдержка процедур, которые копируют и ждут:
Если присмотритесь, то пока DMA что там копирует я не сильно парясь обновляю координаты шариков — «ShowBallA» и «ShowBallB», где-то в конце ещё и музыку играю «MUSIC.PLAYSTEP».
Это стандартный аппарат который крутит всю дему или игру. Процедура SUPER_PUPER_EFFECT это ваши действия, где вы показываете графику или что-то ещё. В этой процедуре можно вызвать процедуру «MUSIC.PLAYSTEP», которая за вас переключит страницу, проиграет музыку, и вернёт страницу назад. Далее, выйдя из процедуры вашего эффекта, исполнится процедура «Mas_LOA», которая сама подготовит буфер для отправки на AY и проверит нужно-ли плееру новые данные на следующее прерывание выставляя флаг ZF, что даёт сигнал для вот этой команды «CALL Z,MUSIC.PLAY». Таким образом вы в прерывании можете вызвать 10-ть раз процедуру «MUSIC.PLAYSTEP», и автоматически на 10-ть прерываний вперёд сохранятся данные для AY'ка. Догадайтесь сами какие возможности у вас появляются? Кстати, можно вызвать процедуру «MUSIC.PLAY0», и тогда в буфер упадёт только данные канала «А», ну а если вызвать «MUSIC.PLAY1», то данные канала «В»! Можете делать это в любой момент, когда вам это удобно и главное, все эти вызовы стабильны в прерывании(по желанию), то есть всегда будут занимать одно и то же количество тактов, при любых данных музыки(в зависимости от музыканта). В зависимости от музыканта имеется в виду, что если музыкант использует на левом канале какой-то инструмент с тяжёлыми по тактам процедурами, то автоматически левый канал по тактам уровняется со всеми вызовами для левого канала. конечно, если используется проигрывание всех трёх каналов, то просто увеличится время в тактах. Хочу обратить внимание, что речь идёт о небольшом количестве тактов, например 30-40. Конечно, если использовать во всех каналах все возможности инструментов, все виды циклов и все виды всего-всего, то плеер увеличится до 4000 тактов. Но практика показывает, что для этого нужен музыкант, который привык писать музыку в ASMе, а по скольку львиная доля музыкантов использует PT3, то плеер не вылетает за пределы 2500 тактов. Так же отмечу, что музыка для 1xAY и 2xAY занимает ровно столько же тактов. То есть ваши мультиколоры не изменятся в зависимости от использования TurboSound'а. Конечно при написании под TS-Conf'у это всё не актуально, и тут важнее не использовать стек(SP), что безусловно есть и плеер можно, вызовом одной процедуры, модифицировать и убрать какое либо обращение к стеку. Однако, что бы добиться максимум эффекта от качества работы плеера, нужно что бы музыкант понимал все эти тонкости, и в нужные моменты, не использовал ту или иную возможность музыкального редактора, конечно, тогда пострадает звук, но пока вы ни разу этого не заметили, хотя один раз PSB, заметил недочёт, который был убран, но всё же один раз было такое.
На последнем собрании true zx'еров, мы все много общались. Ваня ПирогFlexx и Валtsl просили меня объяснить, как же был сделан эффект «XOR-CYRCLES» в SYNCHRONIZATION. Но я как не шуршал мозгами замаринованные в коньяке, так и не смог вспомнить.
Речь идёт вот про этот эффект Xored Cyrcles
---[Lets GO]---
Итак, как же оно работает… Всё очень просто, но нужно вчитаться… Итак есть текстовый режим и прерывания на каждую строчку, начиная с минус один от первой отображаемой линии.1. Вначале прерывание, которое происходит 240 раз. Оно нужно, что бы в первую очередь, задать новую отображаемую линию для текстового режима, и во вторую очередь выпендриться и покрасить бордюр в новый цвет. Но главная фишка именно в обновлении отображение новой линии. У меня есть зарезервированные страницы с какого-то там места, выделенного автоматически под именем VTX_PAGE. В VTX_PAGE лежит экранная область отображаемая TSLDac'ом, но линейно. То есть вначале отобразятся первый 64 символа, потом вторые, ну и так 240 раз… В итоге у нас получается некая видео-область размером в 240*64, что является 8-ми tslстраниц. Нужно помнить, что у Вала каждая новая строчка пересчитывается с учётом смещения по Y, в итоге каждое прерывание обновляет новое смещение по Y и высчитывает новый адрес страницы для VideoPage. Выглядит это так:
INPEx2 LD HL,0
LD BC,8
ADD HL,BC
LD (INPEx2+1),HL
LD BC,TSL.GYOffsL
OUT (C),L
INC B
OUT (C),H
LD B,HIGH(TSL.Vpage)
LD A,H
AND 00000110B
ADD A,VTX_PAGE
OUT (C),A
2. Далее подготовка графики для текста. В предыдущем пункте решена проблема с отображением, теперь когда у нас есть линейное отображение памяти в пикселях, можно заниматься магией. Итак, у нас высвечивается 480 по 8 пикселей, код текста с адреса VTX_PAGE+64*LINE. Реальных, а точнее привычных нам пикселей не 8 а 4, по скольку текст в двое плотнее стандартного графического режима. Теперь перед всем этим эффектом, один раз заполняем графику текста, так что бы код буквы соответствовал XORу верхней и нижней тетрады кода символа.. То есть например код отображаемого символа в бинари такой «10010011» это значит что я хочу сделать «1001» XOR «0011» = «1010». Отлично, теперь символ с кодом «10010011», заполняем графикой восемь раз «11001100», что равно «1010». Однако, сделаем эффект более интересный, где пиксели будут преобладать над пустотой, тогда появится «жирность». Итак сегенируем табличку ITEG_TBL из 16-ти значений утолщённых пикселей. Графика текста складывается в страницу (VTX_PAGE+1). Генератор графики получается таким:
InitTextGFX:
LD A,VTX_PAGE+1
CALL IORQTS.RAMPage3
LD HL,49152
LD BC,0000H
LD D,HIGH(ITEG_TBL)
ITEGx1 LD A,C
RRCA
RRCA
RRCA
RRCA
XOR C
AND 00001111B
ADD A,LOW(ITEG_TBL)
LD E,A
LD A,(DE)
.8
LD (HL),A
INC L
..
ORG $-1
INC HL
INC C
DJNZ ITEGx1
RET
ALIGN 16
ITEG_TBL ; 0 0 0 0
DB 00000000B
DB 00000011B
DB 00001110B
DB 00001111B
DB 00111000B
DB 00111111B
DB 00111110B
DB 00111111B
DB 11100000B
DB 11110111B
DB 11101110B
DB 11101111B
DB 11111000B
DB 11111011B
DB 11111110B
DB 11111111B
3. Далее подготовка кругов. Я не буду расписывать сам генератор, но смысл прост, нам нужно сгенерировать 8-мь гигантских экранов размером 128х512 байт, где графика будет хранить в верхней тетраде, и ещё 8-мь экранов точно таких же, где графика будет в нижней тетраде. Получится что нужно 128*512*8*2 = 1 мегабайт памяти. Его и заполняем графикой кругов. Вот пока вылетает череп с шариками вместо глаз, это и происходит на заднем плане…
4. Далее DMA копирование, которое будет формировать изображение. Тут всё просто… Вначале нужно скопировать с помощью DMA, с флагом RAM_RAM, из первой области гигантского экрана в VTX_PAGE видео-области. Следующим шагом скопировать в то же самое место VTX_PAGE видео-области из второй области гигантского экрана но с флагом BLT_RAM. Получается что вы скрестите верхние и нижние тетрады двух гигантских экранов с кругами в видео-области. И по-скольку графика каждого кода символа уже раскранчена XOR'ом, у вас получается аппарат, который сам за вас XOR'ит. Ваша задача только задавать DMA аппарату, что, куда и откуда копировать, ну и процессором ничего не делать. Вот выдержка процедур, которые копируют и ждут:
CALL INTDMA0
CALL IORQTS.DMA_WAIT
CALL INTDMA4
CALL ShowBallA
CALL IORQTS.DMA_WAIT
CALL INTDMA1
CALL ShowBallB
CALL IORQTS.DMA_WAIT
CALL INTDMA5
CALL IORQTS.DMA_WAIT
CALL INTDMA2
CALL IORQTS.DMA_WAIT
CALL INTDMA6
CALL MUSIC.PLAYSTEP
CALL IORQTS.DMA_WAIT
CALL INTDMA3
CALL IORQTS.DMA_WAIT
CALL INTDMA7
RET
Если присмотритесь, то пока DMA что там копирует я не сильно парясь обновляю координаты шариков — «ShowBallA» и «ShowBallB», где-то в конце ещё и музыку играю «MUSIC.PLAYSTEP».
---[Advertising]---
А теперь похвастаюсь своим MASONом. Сверху виден пример, что в середине прерывания я вызываю «MUSIC.PLAYSTEP». Может показаться, что в каждом эффекте нужно вызывать руками эту процедуру? Это не так! В этом и прелесть этого плеера. Каждое прерывание происходит вот такое:
CALL MUSIC.OUT
CALL SUPER_PUPER_EFFECT
LD A,MUSIC.PAGE
CALL IORQ.MUSIC_PAGE_SWITCH_A
CALL Mas_LOA
CALL Z,MUSIC.PLAY
Это стандартный аппарат который крутит всю дему или игру. Процедура SUPER_PUPER_EFFECT это ваши действия, где вы показываете графику или что-то ещё. В этой процедуре можно вызвать процедуру «MUSIC.PLAYSTEP», которая за вас переключит страницу, проиграет музыку, и вернёт страницу назад. Далее, выйдя из процедуры вашего эффекта, исполнится процедура «Mas_LOA», которая сама подготовит буфер для отправки на AY и проверит нужно-ли плееру новые данные на следующее прерывание выставляя флаг ZF, что даёт сигнал для вот этой команды «CALL Z,MUSIC.PLAY». Таким образом вы в прерывании можете вызвать 10-ть раз процедуру «MUSIC.PLAYSTEP», и автоматически на 10-ть прерываний вперёд сохранятся данные для AY'ка. Догадайтесь сами какие возможности у вас появляются? Кстати, можно вызвать процедуру «MUSIC.PLAY0», и тогда в буфер упадёт только данные канала «А», ну а если вызвать «MUSIC.PLAY1», то данные канала «В»! Можете делать это в любой момент, когда вам это удобно и главное, все эти вызовы стабильны в прерывании(по желанию), то есть всегда будут занимать одно и то же количество тактов, при любых данных музыки(в зависимости от музыканта). В зависимости от музыканта имеется в виду, что если музыкант использует на левом канале какой-то инструмент с тяжёлыми по тактам процедурами, то автоматически левый канал по тактам уровняется со всеми вызовами для левого канала. конечно, если используется проигрывание всех трёх каналов, то просто увеличится время в тактах. Хочу обратить внимание, что речь идёт о небольшом количестве тактов, например 30-40. Конечно, если использовать во всех каналах все возможности инструментов, все виды циклов и все виды всего-всего, то плеер увеличится до 4000 тактов. Но практика показывает, что для этого нужен музыкант, который привык писать музыку в ASMе, а по скольку львиная доля музыкантов использует PT3, то плеер не вылетает за пределы 2500 тактов. Так же отмечу, что музыка для 1xAY и 2xAY занимает ровно столько же тактов. То есть ваши мультиколоры не изменятся в зависимости от использования TurboSound'а. Конечно при написании под TS-Conf'у это всё не актуально, и тут важнее не использовать стек(SP), что безусловно есть и плеер можно, вызовом одной процедуры, модифицировать и убрать какое либо обращение к стеку. Однако, что бы добиться максимум эффекта от качества работы плеера, нужно что бы музыкант понимал все эти тонкости, и в нужные моменты, не использовал ту или иную возможность музыкального редактора, конечно, тогда пострадает звук, но пока вы ни разу этого не заметили, хотя один раз PSB, заметил недочёт, который был убран, но всё же один раз было такое.
10 комментариев
Про плейер Масона лично мне особенно интересно потому, что прочитав когда-то давно на zx-pk.ru краткую сводку о возможностях Масона, я во многом вдохновлялся его идеями для своих плейеров PSG. Технология у меня развилась в итоге не совсем так, я ушёл в скорость и вместо ровных тактов выбрал кратность на 4 (так как это бережёт число тактов и в пике, и в среднем). Во многом мои плейеры тупее, например, я не могу распаковывать каналы независимо друг от друга, хотя вариант, немного устаревший, буферизованного плейера у меня тоже есть. С другой стороны, у меня есть опции проигрывания, которые в итоге в разы быстрее Масона. Но в целом, очень полезно знать, как ты развивал (и развил) интерфейс своего плейера, т.к. это подсказывает решения. Вот такой взгляд со стороны всегда очень крут.
А вообще, да. Я пока что не решился на кардинальную смену формата музыки, который потребуется для более эффективной работы с орнаментами, но я думаю что в самом пакере есть ещё запас по коэффициенту сжатия где-то на 10-20%, который я надеюсь выбрать переписав упаковщик. Не в 3-4 раза, увы, но тоже ощутимо.
шрифт лежит в следующей за видео (текстовой) паге
давно с тхт не игрался, забыл
pp — адрес окна, куда замаплена процессору страница (в данном случае пофиг), X — номер столбца, Y — номер строки, A = 0 — код символа, если 1, то его атрибут.
Атрибуты от пцшного текстмода не отличаются, разве что нет мерцания, поэтому биты 0..3 — цвет символа, 4..7 — цвет фона (старшие 4 бита индекса палитры в PalSel, здесь еще одна фишка — поскольку в буфере строк у VGA-скандаблера внутри FPGA нет памяти для хранения полного 8-битного индекса палитры, то все слои TSU используют ту же палитру, что и слой текстмода)
в странице VPage XOR 1 (!) лежит обычный шрифт 8x8 пикселов 256 цветов, символ за символом, как на пц :)
по организации текстмода — очень удобно для вывода символов одном цветом, например (да и с раскраской проблем нет, можно сделать set 7, h: ld [hl], : res 7, h), но честно скажу, пцшный вариант с чередованием символов\атрибутов удобнее и иногда быстрее (медленнее для монохромных строк из-за лишнего inc l, но можно сразу раскрашивать строку по ходу, да и очистку можно сделать одним вызовом DMA)