SYNCHRONIZATION - XORED CYRCLES

Привет всем.

На последнем собрании true zx'еров, мы все много общались. Ваня ПирогFlexx и Валtsl просили меня объяснить, как же был сделан эффект «XOR-CYRCLES» в SYNCHRONIZATION. Но я как не шуршал мозгами замаринованные в коньяке, так и не смог вспомнить.
TO XOR OR NOT TO XOR THAT IS THE REAL QUESTION


Речь идёт вот про этот эффект 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, заметил недочёт, который был убран, но всё же один раз было такое.

---[P.S.]---
Главная идея SYNCHRONIZATION была в том, что бы показать фокусы именно аппаратной части, а не программной. Хотя изначально была цель сделать один эффект за одни сутки, и этот эффект был туннель. Туннель был сделан из принципа потратить именно сутки ничего вообще не зная о платформе TS-CONFIG. Поэтому единственный эффект, который не дорабатывался с момента начала создания демы, это NEDO TUNNEL, ровно в 00:00 я начал писать этот эффект и в 23:59 остановился, оставив всё в том виде в котором он попал в демо, за исключением тайм-лайна окончания эффекта, который согласовывался с Олегомn1k-o .
---[P.S.S.]---
Планируется написать по статье о интересных эффектах в SYNCHRONIZATION. Вот, как бы, оно так.

10 комментариев

avatar
Очень интересно и поучительно. Я лично не знал о режиме копирования BLT_RAM, и это клёвый способ его использования.

Про плейер Масона лично мне особенно интересно потому, что прочитав когда-то давно на zx-pk.ru краткую сводку о возможностях Масона, я во многом вдохновлялся его идеями для своих плейеров PSG. Технология у меня развилась в итоге не совсем так, я ушёл в скорость и вместо ровных тактов выбрал кратность на 4 (так как это бережёт число тактов и в пике, и в среднем). Во многом мои плейеры тупее, например, я не могу распаковывать каналы независимо друг от друга, хотя вариант, немного устаревший, буферизованного плейера у меня тоже есть. С другой стороны, у меня есть опции проигрывания, которые в итоге в разы быстрее Масона. Но в целом, очень полезно знать, как ты развивал (и развил) интерфейс своего плейера, т.к. это подсказывает решения. Вот такой взгляд со стороны всегда очень крут.
avatar
В масоновском плеере, в не оптимизированном варианте, это когда нет юстировки по выравниванию всех процедур в тактах под самую тяжёлую процедуру, автоматически идёт выравнивание на 4 такта. Ты выбрал путь работы с конечным потоком на AY, конечно твой плеер автоматом будет кушать меньше, но размер мелодии становится ооочень большим. Главная проблема это превышение 16384, ещё можно согласиться вне страницы положить плеер, но данные очень не удобно. У меня в масоне отделен канал и верхняя обработка его. То есть я по сути храню два стрима, это основной тон и постобработка, и конечно же каждый канал в отдельности сброшен в одну кучу и проходит анализ на повторения одним пакером. Эти задержки, о которых я писал, что типа музыкант использует навороты в инструменте, и есть отдельные каналы. То есть, если музыкант добавит обработку звука поверх текущего звука, просто на одну точку добавится вход в декомпрессию, так плееру придётся выполнить вытаскивание на Х шагов больше данных из одного архива. Это сильно экономит память, сильно это в 3-4 раза, что очень существенно. Хотя и у меня бывают моменты, когда коткомпилированная музыка вылазит за 16384. Я когда увидел твой плеер, а точнее компилятор PSG, первое, что захотел, это применить и тут такую технологию, тебе просто надо было прогнать через фильтр-анализатор тоны, таким образом, что бы назад получить орнаменты. А так получается сильный проигрыш в размере. Согласись, что это очень важно?! Лёша, в твоём плеере не хватило только одного для идеала, если бы перед стартом можно было «раскранчить» мелодию из PT3. Вот один маленький нюанс не даёт возможность его для массового использования, только размер.
avatar
Да, я согласен по всем пунктам, кроме одного — меня не очень сильно беспокоит выход за пределы 16384. Обычно я готов потратить и 1.5-2 страницы под музыку, моё демоядро эту ситуацию учитывает и смена музыкальной страницы на лету у меня реализуется вполне просто, хотя и не до конца автоматически. Применительно к Synchronization написанной для машин с 4Мб памяти, конечно, задача впихнуть музыку в страничку звучит почти смешно :)

А вообще, да. Я пока что не решился на кардинальную смену формата музыки, который потребуется для более эффективной работы с орнаментами, но я думаю что в самом пакере есть ещё запас по коэффициенту сжатия где-то на 10-20%, который я надеюсь выбрать переписав упаковщик. Не в 3-4 раза, увы, но тоже ощутимо.
avatar
Для полного понимания не хватает ссылки на доку по текстовому режиму.
avatar
там довольно просто — байт кода символа, байт аттрибутов (цвет).
шрифт лежит в следующей за видео (текстовой) паге
avatar
Спасибо, теперь я понял суть эффекта, хитро :)
avatar
а не 128 байт символов и 128 байт атрибутов?
avatar
в точку.
давно с тхт не игрался, забыл
avatar
вкратце — текстовый режим использует две страницы памяти. В первой (указанной в реге VPage) лежат 64 строки текстового экрана по 256 байт — последовательно 128 байт символов и 128 байт атрибутов к ним в строке. Адрес выглядит примерно так:
%ppYYYYYY AXXXXXXX
pp — адрес окна, куда замаплена процессору страница (в данном случае пофиг), X — номер столбца, Y — номер строки, A = 0 — код символа, если 1, то его атрибут.
Атрибуты от пцшного текстмода не отличаются, разве что нет мерцания, поэтому биты 0..3 — цвет символа, 4..7 — цвет фона (старшие 4 бита индекса палитры в PalSel, здесь еще одна фишка — поскольку в буфере строк у VGA-скандаблера внутри FPGA нет памяти для хранения полного 8-битного индекса палитры, то все слои TSU используют ту же палитру, что и слой текстмода)

в странице VPage XOR 1 (!) лежит обычный шрифт 8x8 пикселов 256 цветов, символ за символом, как на пц :)
avatar
*поправка про шрифт — 256 символов, не цветов :))

по организации текстмода — очень удобно для вывода символов одном цветом, например (да и с раскраской проблем нет, можно сделать set 7, h: ld [hl], : res 7, h), но честно скажу, пцшный вариант с чередованием символов\атрибутов удобнее и иногда быстрее (медленнее для монохромных строк из-за лишнего inc l, но можно сразу раскрашивать строку по ходу, да и очистку можно сделать одним вызовом DMA)
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.