Что изменилось в моём ядре? Я поменял устройство скрипта. «Скрипт» у psndcj — это просто список слов, номера фреймов к которым привязаны переключения эффектов, адреса процедур которые нужно исполнить, иногда данные. Скрипт у меня — это примитивный байткод, который интерпретируется ядром. Я хотел чтобы весь фикс в деме осуществлялся на уровне ядра, а не внутри эффектов. Это означает, что эффекты никогда не получают полный контроль над машиной.
Какого рода команды есть у меня в скрипте? Самая главная команда: дождаться нужного фрейма:
kWAIT 3200
Некоторые команды чисто утилитарные: например, копирование и распаковка данных.
(первая для байтов, вторая для слов). Когда нужно слегка модифицировать эффект для достижения какого-то фикса, нет ничего проще чем добавить простую заглушку (как в первом примере) или подменить адрес данных (как во втором).
Эффекты я обычно стараюсь писать в прерываниях. Обработчик ядра, помимо своих обыкновенных обязанностей, умеет запускать процедуру пользователя. Понятно, задача порубить эффект на фреймы не всегда удобно разрешима, но я стараюсь по возможности это делать, т.к. эффекты в прерываниях позволяют ядру работать параллельно с эффектами, что даёт некую ограниченную форму многозадачности. Типичный план фрейма таков: обработчик прерывания обновляет номер текущего фрейма, играет музыку, затем запускает процедуру пользователя, после чего контроль возвращается ядру. Скрипт может сказать ядру в этом случае ждать пока номер фрейма не достигнет чего-то, т.е. крутиться в холостом цикле до следующего прерывания. Но вполне возможен вариант, когда скрипт говорит ядру распаковать данные, и тогда получается, что каждый свободный такт, оставшийся во фрейме после эффекта, будет потрачен на распаковку. Вот пример достаточно нетривиального кода из Wonderball, для переключения со сцены 2 на сцену 3 (сцена три — это шас с радиальными лучами анимацией):
kWAIT 2304-4 : kPOKE E01_ScrollerAttr, 201 ; атрибуты скроллера выключается немного заранее, чтобы нарисовать на его месте спрайты (спрайты копируются той же процедурой, что и сам скроллер)
kPPOKE E01_AniSprite1, Phase1Sprite ; настройка корректных фаз для анимации волн вокруг чанкового шара
kPPOKE E01_AniSprite2, Phase2Sprite
kPOKE E01_BlitSprite, 1 ; спрайты начинают рисоваться заранее, скрытые атрибутами
kPPOKE E01_CurTableCT3, PlasmaTabCT3_1 ; выбор таблицы для плазмы внутри шара
kINIT E01_TuneCT3 : db 5, 9, 47 ; настройки эффекта плазмы
kWAIT 2304-1 : kINIT E01_SetScene3 ; собственно переключение сцены. спрайты уже нарисованы...
kWAIT 2304+1 : kPOKE E01_ScrollerBlit, 201 ; ...поэтому скроллер можно выключить
kPOKE E01_BeamsOn, 1 ; и включить вместо него эффект радиальных лучей
kINIT E01_PaletteCT3 ; инициализация палитры для цветных чанков
db %01110110,%01010110 : db %01110110,%01010010 : db %01000110,%01000010 : db %00000110,%00000010
db %00000011,%00000001 : db %01000011,%01000001 : db %01011011,%01001001 : db %01011111,%01001011
kPPOKE E01_CurBeamColour, E01_FlashMagenta ; цвет радиальных лучей
kPOKE E01_PickPhase, 1 ; выбор фазы позволяет контролировать направление, в котором будут крутиться лучи
kEXEC E01_JumpCT3, E01_OverlayPage ; заключительная инициализация новой сцены
Поскольку эффект занимается чем-то своим, а ядро — чем-то своим, самой сложной частью для написания такого ядра оказывается менеджер памяти. При работе с таким ядром пользователь не имеет права менять страницы в обход ядра. Ядро ведёт учёт и отличает «страницу ядра», т.е. ту страницу, где хранится скрипт, «страницу музыки» (понятно), «страницу пользователя при работе прерывания» и «страницу пользователя при работе вне прерывания». Есть и другие нюансы, например, моё ядро работает не с реальными, а с виртуальными страницами, благодаря чему на классических машинах я всегда знаю, какие страницы медленные, а какие быстрые. Но я уже устал столько всего писать!
В прошлом мае я написал универсальный движок для трекмо. Прототипом движка для меня было ядро написанное psndcj для дем tbk/4d; psndcj говорил, что его ядро основано на ядре эксполодера. Т.е. родословная, типа, о-го-го.
Немного о ядре psndcj . Оно очень шустрое и легковесное. Ядро сидит чуть ниже верхней страницы. Обработчик прерывания находится в ядре и является его ключевой частью. Прерывания разрешены ВСЕГДА. Обработчик считает фреймы и, при достижении различных ключевых моментов в треке, запускает пользовательские коды. Преимущество работы с номерами фреймов (в противоположность номерам паттернов, например) заключается в том, что
1. Это позволяет синхронизироваться не с точностью до паттерна, а точнее, вплоть до каких-то звучков, индивидуально.
2. Вообще говоря, очень часто музыка играется из PSG, т.е. музыка уже полностью потеряла информацию о номерах строк, паттернах и т.д., а синхронизироваться всё ещё нужно.
Кроме этого, в ядре находился декомпрессор, процедура быстрого копирования данных и процедуры для эффективной работы со скрытым экраном. Идея в том, что по окончанию отрисовки в скрытый экран, код может начинать рисовать следующий кадр, а собственно переключение экрана осуществляет само ядро, сразу после прерывания. Это позволяет более эффективно использовать машинные ресурсы.
Недостаток, с моей точки зрения, этого ядра заключался в том, что оно было написано для работы в стиле «распаковали эффект», «показали эффект», «расчиститили место для следующего эффекта». Ядро запускает эффект и пока эффект не закончился, ядро не контролирует процесс. Конечно, так писать в чём-то проще. Но это очень сильно усложняет фикс. Если нам, допустим хочется, что-то поменять в эффекте посередине эффекта, нам придётся внутри эффекта сравнивать счётчик фреймов ядра с какими-то константами, т.е., по сути, дублировать функциональность ядра.
Другим недостатком было то, что скрипт хранился в основной памяти (насколько я знаю, psndcj недавно сделал версию ядра в которой это ограничение было снято).
Женя psndcj , у меня понятно немного пристрастный взгляд на это всё, было бы классно если бы ты прокомментировал, м.б. расставил как-то иначе акценты. Кроме того, очень интересно узнать, что у тебя в ядре пришло в наследство от эксподера.
Судя по тому, что сайт студии и сайт игры сделаны на бесплатном конструкторе — это глубокое инди. Да и за графику боязно. Еще не было ни одного четкого скриншота. Все в мыле.
Никаких сообщений по поводу разрешений не видел. Похоже, что просто ковбои. В основном графику переделывают, но видно, что враги те же самые, только изменена графика. Не удивлюсь, если и код позаимствован. Зачем иначе повторять всю логику уровней R-Type?
С тормознутостью, к сожалению, я думаю все слишком просто: оригинальные спрайты анимированы с определенным фреймрейтом (почти все они присутствуют без изменений), поэтому вся скорость игры на прежнем уровне, соответственно и скроллер тоже.
Очень инересно, что они по всей видимости решили сохранить тормознутый рваный скроллер оригинального R-Type.
Т.е. сделали ставку на 200% аутентичность. Будет интересно посмотреть как примут такой проект.
Какого рода команды есть у меня в скрипте? Самая главная команда: дождаться нужного фрейма:
Некоторые команды чисто утилитарные: например, копирование и распаковка данных.
(копируем упакованную картинку из 0й страницы вниз и потом рапаковываем её в экран на странице 7). Крайне, мегаполезные команды — kPOKE и kPPOKE:
(первая для байтов, вторая для слов). Когда нужно слегка модифицировать эффект для достижения какого-то фикса, нет ничего проще чем добавить простую заглушку (как в первом примере) или подменить адрес данных (как во втором).
Эффекты я обычно стараюсь писать в прерываниях. Обработчик ядра, помимо своих обыкновенных обязанностей, умеет запускать процедуру пользователя. Понятно, задача порубить эффект на фреймы не всегда удобно разрешима, но я стараюсь по возможности это делать, т.к. эффекты в прерываниях позволяют ядру работать параллельно с эффектами, что даёт некую ограниченную форму многозадачности. Типичный план фрейма таков: обработчик прерывания обновляет номер текущего фрейма, играет музыку, затем запускает процедуру пользователя, после чего контроль возвращается ядру. Скрипт может сказать ядру в этом случае ждать пока номер фрейма не достигнет чего-то, т.е. крутиться в холостом цикле до следующего прерывания. Но вполне возможен вариант, когда скрипт говорит ядру распаковать данные, и тогда получается, что каждый свободный такт, оставшийся во фрейме после эффекта, будет потрачен на распаковку. Вот пример достаточно нетривиального кода из Wonderball, для переключения со сцены 2 на сцену 3 (сцена три — это шас с радиальными лучами анимацией):
Поскольку эффект занимается чем-то своим, а ядро — чем-то своим, самой сложной частью для написания такого ядра оказывается менеджер памяти. При работе с таким ядром пользователь не имеет права менять страницы в обход ядра. Ядро ведёт учёт и отличает «страницу ядра», т.е. ту страницу, где хранится скрипт, «страницу музыки» (понятно), «страницу пользователя при работе прерывания» и «страницу пользователя при работе вне прерывания». Есть и другие нюансы, например, моё ядро работает не с реальными, а с виртуальными страницами, благодаря чему на классических машинах я всегда знаю, какие страницы медленные, а какие быстрые. Но я уже устал столько всего писать!
Немного о ядре psndcj . Оно очень шустрое и легковесное. Ядро сидит чуть ниже верхней страницы. Обработчик прерывания находится в ядре и является его ключевой частью. Прерывания разрешены ВСЕГДА. Обработчик считает фреймы и, при достижении различных ключевых моментов в треке, запускает пользовательские коды. Преимущество работы с номерами фреймов (в противоположность номерам паттернов, например) заключается в том, что
1. Это позволяет синхронизироваться не с точностью до паттерна, а точнее, вплоть до каких-то звучков, индивидуально.
2. Вообще говоря, очень часто музыка играется из PSG, т.е. музыка уже полностью потеряла информацию о номерах строк, паттернах и т.д., а синхронизироваться всё ещё нужно.
Кроме этого, в ядре находился декомпрессор, процедура быстрого копирования данных и процедуры для эффективной работы со скрытым экраном. Идея в том, что по окончанию отрисовки в скрытый экран, код может начинать рисовать следующий кадр, а собственно переключение экрана осуществляет само ядро, сразу после прерывания. Это позволяет более эффективно использовать машинные ресурсы.
Недостаток, с моей точки зрения, этого ядра заключался в том, что оно было написано для работы в стиле «распаковали эффект», «показали эффект», «расчиститили место для следующего эффекта». Ядро запускает эффект и пока эффект не закончился, ядро не контролирует процесс. Конечно, так писать в чём-то проще. Но это очень сильно усложняет фикс. Если нам, допустим хочется, что-то поменять в эффекте посередине эффекта, нам придётся внутри эффекта сравнивать счётчик фреймов ядра с какими-то константами, т.е., по сути, дублировать функциональность ядра.
Другим недостатком было то, что скрипт хранился в основной памяти (насколько я знаю, psndcj недавно сделал версию ядра в которой это ограничение было снято).
Женя psndcj , у меня понятно немного пристрастный взгляд на это всё, было бы классно если бы ты прокомментировал, м.б. расставил как-то иначе акценты. Кроме того, очень интересно узнать, что у тебя в ядре пришло в наследство от эксподера.
немного не по теме, но по теме :)
Т.е. сделали ставку на 200% аутентичность. Будет интересно посмотреть как примут такой проект.