В предыдущей своей небольшой заметке я описывал принцип построения эмулятора старой игровой платформы CHIP-8 из далеких 70-х. Здесь же речь пойдет о своего рода наследнице – CHIP16. Итак, что же такое CHIP16?
CHIP16 – “вымышленная” игровая приставка, которой никогда не существовало в “железе”. Всю спецификацию на нее разрабатывали (-ют) энтузиасты с одного англоязычного форума. Смысл в том, чтобы максимально упростить написание эмулятора, иметь хорошую документацию и поддержку комьюнити. Тем самым позволяя даже новичкам в программировании создать полностью рабочий эмулятор с нуля на фактически любом языке программирования. Сразу оговорюсь, что здесь я не буду приводить примеры кода эмулятора, цель – просто рассказать об этой платформе. И да, конечно все Just for fun!
Предыстория
А все началось где-то в 2010 году, с тех пор спецификация приобрела номер версии 1.1, основные недочёты были устранены, были написаны основные инструменты для создания игр и других программ под эту платформу (ассемблер, отладчик, конвертер изображений и прочее). Основное отличие от той-же CHIP-8 это наличие большего разрешения экрана, большего количества цветов, большего количества арифметических и других инструкций, улучшенной поддержки звукового сопровождения и отсутствие недокументированных фич.
Приставка имеет 16-ти разрядный процессор, память, два устройства ввода (джойстики типа Dendy), звуковую и видео подсистему. Рассмотрим всю эту кучу подробнее.
Процессор
Процессор включает:
- Один 16-разрядный счетчик команд (PC)
- Один 16-разрядный указатель стека (SP)
- Шестнадцать 16-разрядных регистра общего назначения (R0 – RF)
- Один 8-разрядный регистр флагов
Выполнение каждого опкода (команды процессора) занимает ровно один цикл, процессор работает на частоте 1 Mhz. Здесь я оговорюсь, что на самом деле, пока частота не ясна до конца, и каждый выполняет на эмуляторе команды быстрее, чем 1 Mhz. Иногда с максимальной скоростью эмуляции. В общем пока можно не заморачиваться по этому поводу.
Память
Всего 64Kb (65536 байт). Память распределяется следующим образом:
0x0000 – Начало бинарных данных
0xFDF0 – Начало стека (512 байт).
0xFFF0 – Порты ввода/вывода (I/O, для отслеживания состояния джойстиков).
Видео
Разрешение экрана 320x240 пикселей. Одновременное количество отображаемых цветов – 16 из стандартной палитры.
Индекс в палитре | Шестнадцат. значение | Цвет |
---|---|---|
0x0 | 0x000000 | Черный, прозрачный на слое фона |
0x1 | 0x000000 | Черный |
0x2 | 0x888888 | Серый |
0x3 | 0xBF3932 | Красный |
0x4 | 0xDE7AAE | Розовый |
0x5 | 0x4C3D21 | Темно-коричневый |
0x6 | 0x905F25 | Коричневый |
0x7 | 0xE49452 | Оранжевый |
0x8 | 0xEAD979 | Желтый |
0x9 | 0x537A3B | Зеленый |
0xA | 0xABD54A | Светло-зеленый |
0xB | 0x252E38 | Темно-синий |
0xC | 0x00467F | Синий |
0xD | 0x68ABCC | Светло-синий |
0xE | 0xBCDEE4 | Sky blue |
0xF | 0xFFFFFF | Белый |
Есть возможность устанавливать собственную цветовую палитру. Частота обновления экрана 60 кадров в секунду, при этом каждый кадр выставляется внутренний флаг Vblank (через каждые ~16ms). Процессор имеет возможность ожидать завершения отрисовки кадра с помощью инструкции VBLNK. Изображение формируется из спрайтов, прямого доступа к видеопамяти нет. Помимо основного слоя, где рисуются спрайты, присутствует слой фона, который заполняет весь экран одним из 16-ти цветов.
Экран зеркально не отображается, таким образом все, что не влезло в физические координаты – остается за экраном и не отображается. Из этого следует, что спрайты могут иметь отрицательные координаты (либо превышающие 320x200). Если взять спрайт 4x4 пикселя и, к примеру, отдать команду нарисовать его по координатам (-2,-2), то в верхнем левом углу экрана отобразиться часть спрайта размером 2x2 пикселя.
Так, как всего возможных цветов в палитре 16, то чтобы закодировать одну точку на экране необходимо 4 бита. Один байт является блоком из двух точек. Минимальный спрайт состоит из одного байта – это спрайт размером 2x1 (две точки). Давайте для примера посмотрим как закодировать спрайт размером 8x5 пикселей, если учесть что белый цвет в стандартной палитре это цвет с индексом 0xFh, а черный — с индексом 0x1h
Итого, 20 байт. То есть (8 x 5) / 2 = 20
Если новый спрайт перекрывает любые уже существующие пиксели на экране (за исключением пикселей с нулевым цветом – они прозрачны), то устанавливается флаг переполнения (carry flag). Таким образом, можно отслеживать столкновения и коллизии объектов на экране в играх.
Для назначения собственной цветовой палитры вместо стандартной существует специальная команда процессора PAL. Каждый цвет палитры состоит из 3-х байт, в формате RGB. Так как используется всего 16 цветов, то команда PAL прочитает из памяти массив в 48 байт и назначит RGB компоненты цветам с индексами 0x0, 0x1,..,0xF. Палитра сменяется после получения процессором команды VBLNK.
Звук
В начальных версиях присутствовала возможность проигрывать только три фиксированных тона (500Hz, 1000Hz, 1500Hz) определенное количество миллисекунд, но затем добавилась возможность использовать звуковой генератор типа ADSR
Устройства ввода (джойстики)
Доступ к информации с джойстиков осуществляется посредством отображаемых в памяти портов ввода-вывода. Первый джойстик по адресу 0xFFF0, второй – 0xFFF2.
Bit[0] – Up (Вверх)
Bit[1] — Down (Вниз)
Bit[2] — Left (Влево)
Bit[3] — Right (Вправо)
Bit[4] — Select (Выбор)
Bit[5] — Start (Старт)
Bit[6] — A
Bit[7] — B
Bit[8 — 15] — Не используются, всегда равны нулю.
Таким образом, прочитав значение из памяти по адресу 0xFFF0 в регистр и проверяя соответствующие установленные биты можно отслеживать какие кнопки в данный момент нажаты на первом джойстике.
Формат файлов-образов (ROM-файлов)
ROM файл (стандартное расширение .c16) содержит в себе заголовок и бинарные данные сразу после заголовка. Информация из заголовка может быть использована для определения версии спецификации и пр. Заголовок имеет постоянный размер 16 байт. Его формат:
Смещение | Назначение |
---|---|
0x00 | Магическое число 'CH16' |
0x04 | Зарезервированно |
0x05 | Версия спецификации (первые 4 бита=главная версия, вторые 4 бита=подверсия, т.е 0.7 = 0x07 и 1.0 = 0x10) |
0x06 | Размер ROM-файла (не включая заголовок, в байтах) |
0x0A | Начальный адрес (Значение счетчика команд PC) |
0x0C | CRC32 контрольная сумма (не включая заголовок, полином = 0x04c11db7) |
Сразу за ним начинаются бинарные данные, которые всегда должны быть прочитаны в память начиная с адреса 0x0000. Счетчик команд должен быть установлен в соответствии со значением в заголовке. Как правило это 0x0000.
ROM-файлы могут и не иметь заголовка вовсе, так как он был введен в спецификацию сравнительно недавно.
Регистр флагов
Bit[0] | Зарезервирован |
Bit[1] | c — carry flag (беззнаковое переполнение) |
Bit[2] | z — zero flag |
Bit[3] | Зарезервирован |
Bit[4] | Зарезервирован |
Bit[5] | Зарезервирован |
Bit[6] | o — Overflow (переполнение чисел со знаком) |
Bit[7] | n — negative (флаг отрицательного знака) |
Типы условий для команд условного перехода
Условия используются для команд условного перехода (прыжков) или команд условного вызова подпрограмм. К примеру: «jle метка» или «cno some_label2». В квадратных скобках приводится состояние флагов, когда условие срабатывает.
Z | 0x0 | [z==1] | Equal (Zero) |
NZ | 0x1 | [z==0] | Not Equal (Non-Zero) |
N | 0x2 | [n==1] | Negative |
NN | 0x3 | [n==0] | Not-Negative (Positive or Zero) |
P | 0x4 | [n==0 && z==0] | Positive |
O | 0x5 | [o==1] | Overflow |
NO | 0x6 | [o==0] | No Overflow |
A | 0x7 | [c==0 && z==0] | Above (Unsigned Greater Than) |
AE | 0x8 | [c==0] | Above Equal (Unsigned Greater Than or Equal) |
B | 0x9 | [c==1] | Below (Unsigned Less Than) |
BE | 0xA | [c==1 || z==1] | Below Equal (Unsigned Less Than or Equal) |
G | 0xB | [o==n && z==0] | Signed Greater Than |
GE | 0xC | [o==n] | Signed Greater Than or Equal |
L | 0xD | [o!=n] | Signed Less Than |
LE | 0xE | [o!=n || z==1] | Signed Less Than or Equal |
Так же можно использовать альтернативные мнемоники:
NC | 0x8 | [c==0] | Not Carry (Same as AE) |
C | 0x9 | [c==1] | Carry (Same as B) |
Команды процессора
Любой опкод CHIP16 занимает ровно 4 байта (32 бита).
HH — старший байт.
LL — младший файт.
N — nibble (4х-битное значение).
X, Y, Z — 4х-битный идентификатор регистра.
Опкод | Мнемоника | Использование | |||||||
---|---|---|---|---|---|---|---|---|---|
00 00 00 00 | NOP | Нет операции, просто один цикл процессора | |||||||
01 00 00 00 | CLS | Очистка экрана (основной слой очищается, цвет фона устанавливается в цвет с индексом 0) | |||||||
02 00 00 00 | VBLNK | Ожидать вертикальной синхронизации. Если кадр не успел отрисоваться, то PC-=4 | |||||||
03 00 0N 00 | BGC N | Установить цвет фона с индексом N. Если индекс равен 0, то цвет фона — черный | |||||||
04 00 LL HH | SPR HHLL | Установить размер спрайта: ширину (LL) и высоту (HH) | |||||||
05 YX LL HH | DRW RX, RY, HHLL | Нарисовать спрайт из адреса в памяти HHLL по координатам, заданным в регистрах X и Y. Результат влияет на carry flag | |||||||
06 YX 0Z 00 | DRW RX, RY, RZ | Нарисовать спрайт из адреса в памяти, на который указывает регистр Z по координатам, заданным в регистрах X и Y. Результат влияет на carry flag | |||||||
07 0X LL HH | RND RX, HHLL | Поместить случайное число в регистр X. Максимальное значение задается HHLL | |||||||
08 00 00 00 | FLIP 0, 0 | Задать ориентацию отображения спрайта. Горизонтальный переворот = НЕТ, вертикальный переворот = НЕТ | |||||||
08 00 00 01 | FLIP 0, 1 | Задать ориентацию отображения спрайта. Горизонтальный переворот = НЕТ, вертикальный переворот = ДА | |||||||
08 00 00 02 | FLIP 1, 0 | Задать ориентацию отображения спрайта. Горизонтальный переворот = ДА, вертикальный переворот = НЕТ | |||||||
08 00 00 03 | FLIP 1, 1 | Задать ориентацию отображения спрайта. Горизонтальный переворот = ДА, вертикальный переворот = ДА | |||||||
09 00 00 00 | SND0 | Остановить воспроизведение звука | |||||||
0A 00 LL HH | SND1 HHLL | Воспроизводить 500Hz тон HHLL миллисекунд | |||||||
0B 00 LL HH | SND2 HHLL | Воспроизводить 1000Hz тон HHLL миллисекунд | |||||||
0C 00 LL HH | SND3 HHLL | Воспроизводить 1500Hz тон HHLL миллисекунд | |||||||
0D 0X LL HH | SNP RX, HHLL | Воспроизводить звуковой тон в течении HHLL миллисекунд, в соответствии с текущим звуковым генератором, заданным по адресу HHLL | |||||||
0E AD SR VT | SNG AD, VTSR | Звуковой генератор ADSR
|
|||||||
10 00 LL HH | JMP HHLL | Перейти на указанный адрес HHLL | |||||||
12 0x LL HH | Jx HHLL | Перейти на указанный адрес HHLL с учетом условия 'x'. (см. Типы условий) | |||||||
13 YX LL HH | JME RX, RY, HHLL | Перейти на указанный адрес HHLL если регистр X равен регистру Y | |||||||
16 0X 00 00 | JMP RX | Перейти на адрес, заданный в регистре X | |||||||
14 00 LL HH | CALL HHLL | Вызвать подпрограмму по адресу HHLL. Сохраняет PC в [SP], увеличивает SP на 2 | |||||||
15 00 00 00 | RET | Возврат из подпрограммы. Уменьшает SP на 2 и восстанавливает PC из [SP] | |||||||
17 0x LL HH | Cx HHLL | Если выполняется условие 'x', тогда вызов подпрограммы. (см. Типы условий) | |||||||
18 0X 00 00 | CALL RX | Вызвать подпрограмму по адресу, находящемуся в регистре X. Сохраняет PC в [SP], увеличивает SP на 2 | |||||||
20 0X LL HH | LDI RX, HHLL | Поместить в регистр X непосредственное значение HHLL | |||||||
21 00 LL HH | LDI SP, HHLL | Установить указатель стека на адрес HHLL. Не перемещает старые значения в стеке на новый адрес | |||||||
22 0X LL HH | LDM RX, HHLL | Поместить в регистр X 16-битное значение из памяти по адресу HHLL | |||||||
22 YX 00 00 | LDM RX, RY | Поместить в регистр X 16-битное значение из памяти по адресу, на который указывает регистр Y | |||||||
24 YX 00 00 | MOV RX, RY | Скопировать значение регистра Y в регистр X | |||||||
30 0X LL HH | STM RX, HHLL | Сохранить значение регистра X в памяти по адресу HHLL | |||||||
31 YX 00 00 | STM RX, RY | Сохранить значение регистра X в памяти по адресу, находящемуся в регистре Y | |||||||
40 0X LL HH | ADDI RX, HHLL | Добавить непосредственное значение HHLL к регистру X. Влияет на флаги [c,z,o,n] | |||||||
41 YX 00 00 | ADD RX, RY | Добавить значение регистра Y к регистру X. Результат помещается в регистр X. Влияет на флаги [c,z,o,n] | |||||||
42 YX 0Z 00 | ADD RX, RY, RZ | Добавить значение регистра Y к регистру X. Результат помещается в регистр Z. Влияет на флаги [c,z,o,n] | |||||||
50 0X LL HH | SUBI RX, HHLL | Вычесть непосредственное значение HHLL из регистра X. Результат в регистре X. Влияет на флаги [c,z,o,n] | |||||||
51 YX 00 00 | SUB RX, RY | Вычесть значение регистра Y из регистра X. Результат помещается в регистр X. Влияет на флаги [c,z,o,n] | |||||||
52 YX 0Z 00 | SUB RX, RY, RZ | Вычесть значение регистра Y из регистра X. Результат помещается в регистр Z. Влияет на флаги [c,z,o,n] | |||||||
53 0X LL HH | CMPI RX, HHLL | Вычесть непосредственное значение HHLL из регистра X. Результат не сохраняется. Влияет на флаги [c,z,o,n] | |||||||
54 YX 00 00 | CMP RX, RY | Вычесть значение регистра Y из регистра X. Результат не сохраняется. Влияет на флаги [c,z,o,n] | |||||||
60 0X LL HH | ANDI RX, HHLL | Логическая операция 'И' непосредственного значения HHLL к регистру X. Результат в регистре X. Влияет на флаги [z,n] | |||||||
61 YX 00 00 | AND RX, RY | Логическая операция 'И' значения в регистре Y к регистру X. Результат помещается в регистр X. Влияет на флаги [z,n] | |||||||
62 YX 0Z 00 | AND RX, RY, RZ | Логическая операция 'И' значения в регистре Y к регистру X. Результат помещается в регистр Z. Влияет на флаги [z,n] | |||||||
63 0X LL HH | TSTI RX, HHLL | Логическая операция 'И' непосредственного значения HHLL к регистру X. Результат не сохраняется. Влияет на флаги [z,n] | |||||||
64 YX 00 00 | TST RX, RY | Логическая операция 'И' значения в регистре Y к регистру X. Результат не сохраняется. Влияет на флаги [z,n] | |||||||
70 0X LL HH | ORI RX, HHLL | Логическая операция 'ИЛИ' непосредственного значения HHLL к регистру X. Результат в регистре X. Влияет на флаги [z,n] | |||||||
71 YX 00 00 | OR RX, RY | Логическая операция 'ИЛИ' значения в регистре Y к регистру X. Результат помещается в регистр X. Влияет на флаги [z,n] | |||||||
72 YX 0Z 00 | OR RX, RY, RZ | Логическая операция 'ИЛИ' значения в регистре Y к регистру X. Результат помещается в регистр Z. Влияет на флаги [z,n] | |||||||
80 0X LL HH | XORI RX, HHLL | Логическая операция 'XOR' непосредственного значения HHLL к регистру X. Результат в регистре X. Влияет на флаги [z,n] | |||||||
81 YX 00 00 | XOR RX, RY | Логическая операция 'XOR' значения в регистре Y к регистру X. Результат помещается в регистр X. Влияет на флаги [z,n] | |||||||
82 YX 0Z 00 | XOR RX, RY, RZ | Логическая операция 'XOR' значения в регистре Y к регистру X. Результат помещается в регистр Z. Влияет на флаги [z,n] | |||||||
90 0X LL HH | MULI RX, HHLL | Умножение непосредственного значения HHLL на регистр X. Результат в регистре X. Влияет на флаги [c,z,n] | |||||||
91 YX 00 00 | MUL RX, RY | Умножение значения в регистре Y на регистр X. Результат помещается в регистр X. Влияет на флаги [c,z,n] | |||||||
92 YX 0Z 00 | MUL RX, RY, RZ | Умножение значения в регистре Y на регистр X. Результат помещается в регистр Z. Влияет на флаги [c,z,n] | |||||||
A0 0X LL HH | DIVI RX, HHLL | Деление регистра X на непосредственное значение HHLL. Результат в регистре X. Влияет на флаги [c,z,n] | |||||||
A1 YX 00 00 | DIV RX, RY | Деление регистра X на значение в регистре Y. Результат помещается в регистр X. Влияет на флаги [c,z,n] | |||||||
A2 YX 0Z 00 | DIV RX, RY, RZ | Деление регистра X на значение в регистре Y. Результат помещается в регистр X. Влияет на флаги [c,z,n] | |||||||
B0 0X 0N 00 | SHL RX, N | Логический сдвиг значения в регистре X влево N-раз. Влияет на флаги [z,n] | |||||||
B1 0X 0N 00 | SHR RX, N | Логический сдвиг значения в регистре X вправо N-раз. Влияет на флаги [z,n] | |||||||
B0 0X 0N 00 | SAL RX, N | Арифметический сдвиг значения в регистре X влево N-раз. Влияет на флаги [z,n]. Аналогична команде SHL | |||||||
B2 0X 0N 00 | SAR RX, N | Арифметический сдвиг значения в регистре X вправо N-раз. Влияет на флаги [z,n] | |||||||
B3 YX 00 00 | SHL RX, RY | Логический сдвиг значения в регистре X влево на значение, находящееся в регистре Y. Влияет на флаги [z,n] | |||||||
B4 YX 00 00 | SHR RX, RY | Логический сдвиг значения в регистре X вправо на значение, находящееся в регистре Y. Влияет на флаги [z,n] | |||||||
B3 YX 00 00 | SAL RX, RY | Арифметический сдвиг значения в регистре X влево на значение, находящееся в регистре Y. Влияет на флаги [z,n]. Аналогична команде SHL | |||||||
B5 YX 00 00 | SAR RX, RY | Арифметический сдвиг значения в регистре X вправо на значение, находящееся в регистре Y. Влияет на флаги [z,n] | |||||||
C0 0X 00 00 | PUSH RX | Поместить значение регистра X в стек. Увеличивает SP на 2 | |||||||
C1 0X 00 00 | POP RX | Уменьшает SP на 2. Восстановить значение регистра X из стека. | |||||||
C2 00 00 00 | PUSHALL | Сохранить значения всех регистров общего назначения (r0-rf) в стеке. Увеличивает SP на 32 | |||||||
C3 00 00 00 | POPALL | Уменьшает SP на 32. Восстановить значения всех регистров общего назначения (r0-rf) из стека. | |||||||
C4 00 00 00 | PUSHF | Сохранить состояние регистра флагов в стеке. Биты 0-7 основные флаги, биты 8-15 пусты (всегда ноль). Увеличивает SP на 2 | |||||||
C5 00 00 00 | PUSHF | Уменьшает SP на 2. Восстановить состояние регистра флагов из стека | |||||||
D0 00 LL HH | PAL HHLL | Загрузить палитру находящуюся по адресу HHLL, 16*3 байт, RGB-формат; начнет действовать сразу после последнего VBlank | |||||||
D1 0x 00 00 | PAL Rx | Загрузить палитру находящуюся по адресу в регистре X, 16*3 байт, RGB-формат; начнет действовать сразу после последнего VBlank |
Где почитать подробнее, как «пощупать»?
Почитать подробнее можно, как я уже говорил, на англоязычном форуме.
— Первая (и уже устаревшая и закрытая) тема с обсуждением CHIP16
— Вторая тема с обсуждением (основная). Здесь вся информация собирается в первом посте темы. Там спецификация, инструменты, примеры программ. Нужна регистрация, что бы что-то скачивать с форума.
Хороший эмулятор RefCHIP16: code.google.com/p/refchip16/downloads/list Исходники на Си++, имеется возможность как простой интерпретации, так и AOT (Ahead Of Time) компиляции, что несомненно доставляет. Пожалуй единственный нормальный эмулятор, который корректно обрабатывает спецификацию 1.1 (в особенности ADSR звук).
Немного устаревший набор игр, демок и тестовых образов для CHIP16. Там же исходники на ассемблере для большинства программ: rghost.ru/38862474 Новые игры и программы выкладываются в теме на форуме.
Разработка
Программы и игры для CHIP16 пока в подавляющем большинстве пишутся на ассемблере. Скачать его можно здесь: code.google.com/p/tchip16/downloads/list Для примера, возьмем наш спрайт из этого топика (стрелочку) и выведем его на экран. Для этого открываем любой текстовый редактор, создаем пустой файл habr.asm и пишем туда команды:
spr #0504 ; установим размер спрайта 8x5
ldi r0,10 ; в регистр r0 - X координата
ldi r1,10 ; в регистр r1 - Y координата
drw r0,r1,arrow ; выведем спрайт на экран по координатам (10,10)
end: jmp end ; бесконечный цикл
; Спрайт
arrow: db #f1, #11, #11, #ff
db #f1, #1f, #ff, #ff
db #f1, #f1, #ff, #ff
db #f1, #ff, #1f, #ff
db #f1, #ff, #f1, #ff
После чего компилируем программу с помощью данной команды:
tchip16.exe habr.asm -o habr.c16
Затем открываем получившийся файл habr.c16 в эмуляторе и наслаждаемся видом черной стрелочки на белом фоне :)
Для отладки сложных алгоритмов можно использовать мой отладчик — chip16debugger: code.google.com/p/chip16debugger/downloads/list
Пока альфа версия, говнокод, наверняка с багами, но лучше чем ничего. Иногда реально помогает словить баг.
Эмулятор… А что еще прикольного можно замутить?
Ну, например какой-нибудь компилятор (или транслятор) с языка высокого уровня. Я, например, пытался написать транслятор с паскале-подобного языка. Кое-что конечно получилось, но до полноценного языка явно не дотягивает. Вот такие программки можно было на нем писать:
var
xPixels, yPixels, xStart, yStart, Xsize, YSize, maxiter : integer;
xStep, yStep : integer;
ix,iy,x,y,x0,y0,iteration,xtemp : integer;
dist : byte;
temp : byte;
xx,yy : byte;
begin
XPixels := 160;
YPixels := 100;
XStart := $FF9c;
YStart := $FFce;
XSize := 160;
YSize := 100;
MaxIter := 16;
XStep := XSize div XPixels;
YStep := YSize div YPixels;
yy := 20;
For iy := 0 to yPixels do
begin
xx := 0;
For ix := 0 to xPixels do
begin
x := xStart + ix * xStep;
y := yStart + iy * yStep;
x0 := x;
y0 := y;
iteration := 0;
Repeat
xtemp := ((x*x) div 48) - ((y*y) div 48) + x0;
y := 2*((x*y) div 48) + y0;
x := xtemp;
iteration := iteration + 1;
dist := ((x*x) div 48) + ((y*y) div 48);
If iteration = maxiter then dist := 4000;
Until dist > 192;
If iteration <> maxiter then
If iteration > 1 then
begin
temp := ((iteration shl 4) or iteration) shl 8;
temp := temp or ((iteration shl 4) or iteration);
DrawSprite(xx,yy,$0201,^temp);
end;
xx := xx + 2;
end;
yy := yy + 2;
end;
end.
На выходе получалась тонна ассемблерного говнокода, который компилился вышеуказанным ассемблером tchip16. В итоге это как-то работало и давало такую картинку:
К сожалению забросил, не хватило скила довести все до релиза или хотя бы беты.
Что еще… Ах да, был случай интересный — хотели замутить CHIP16 демо-компо, типа написание демок под сабж. Ну а что, олдскульно, ресурсы ограниченны, это вам не шойдеры с гигами оперативы. В теории можно вполне крутить старые эффекты, так, как хоть тут нет фрейм-буффера, зато есть 32Kb оперативы под спрайт размером с весь экран. И 32Kb на код остается. Будет даже без морганий экрана. Моя небольшая демка sinedots (такие точки крутятся в пространстве, получается как-бы трехмерные). Правда я тут ступил, и выводил точки именно точками (спрайтами в один байт) из-за чего получается моргание.
Еще можно создать всю платформу аппаратно (для любителей ПЛИС и прочих транзисторов). А я быть может напишу свой эмулятор под эту железяку:
И пускай не совсем аппаратно с точки зрения аппаратности (там обычный MIPS процессор), зато все равно интересно.
Всем удачи!
Автор: tronix286