Здравствуй, мир! Сегодня у нас перевод спецификации языка CHIP8. Это статья содержит только теоретическую часть.  CHIP8 это интерпретируемый язык программирования, который был разработан Джозефом Вейзбекером (прим. перевод Joseph Weisbecker) в семидесятых для использования в RCA COSMAC VIP. В дальнейшем был использован в COSMAC ELF, Telmac 1800, ETI 660, DREAM 6800. Тридцать одна (35?) инструкция давали возможности для вывода простого звука, монохромной графики в разрешении 64 на 32 пикселя, а также позволяло использовать 16 пользовательских кнопок. Сегодня CHIP-8 часто используется для обучения базовым навыком эмуляции (не интерпретации). Интерпретаторы CHIP-8, часто по ошибке называемые „эмуляторами“, существуют на все более расширяющемся множестве платформ. Это обилие интерпретаторов связано со сходством дизайна интерпретатора CHIP-8 и эмулятора системы. Те, кто хочет разобраться в эмуляторах, нередко начинают с написания интерпретатора CHIP-8. Из-за его простоты большое количество игр и программ были написаны на CHIP-8. Это доказывает, что программист часто не ограничен языком программирования. Инструкции CHIP-8 хранились напрямую в памяти. Современные компьютеры позволяют хранить бинарные данные без надобности вводить их вручную в память. Спецификация COSMAC VIP предполагает, что код загружается в памяти со смещением в 512 байтов (0x200). Большинство игр и программ в CHIP-8 во время работы с памятью предполагают именно такое смещение. Надо отметить, что программы в памяти CHIP-8 хранятся в Big-Endian, предполагая хранение MSB First (Most Significant Byte First — Самый "значимый" байт храниться первым). Инструкции исполняются по два байта последовательно если не было иных инструкций. Так как инструкции CHIP-8 содержат указатели на данные или инструкции в памяти изменение кода требовало бы изменения адреса в инструкциях. К счастью псевдо-ассемблер решает эту проблему. Большое количество документации к CHIP-8 не содержат описания некоторый инструкций (8XY3, 8XY6, 8XY7 и 8XYE), но будут описаны здесь. Все арифметические операции используют регистры. В CHIP-8 описаны 16 регистров. Все регистры без знаковые, 8-и битные и могут использоваться в инструкциях принимающие регистры общего назначения в качестве аргумента, но стоит помнить, что некоторые инструкции могут модифицировать последний регистр (V[0xF]) (Регистр переполнения). Псевдокод: Не совершайте моих ошибок: Последний регистр это полноценный 8-и битный регистр, несмотря на использование как спец регистр в некоторых инструкциях. Хотя спецификация рекомендует не использовать последний регистр в операциях. Регистр I это 16-битный регистр. Несмотря на это в нем используется только первые 12 бит. Псевдокод: Упущение в спецификации: Что будет если произойдет переполнение? Регистр PC это аналогичный регистру I, только указывает на инструкции.
Упущение в спецификации: Что будет если произойдет переполнение? В CHIP-8 Описан стек глубиной 12 ячеек. Прямого доступа к стеку нету (PUSH/POP/etc), но есть инструкции вызова и возврата, которые используют стек.
NOTE: Тут Указанно 16 ячеек. А тут — 12. Все инструкции это шестнадцатеричная запись.
Обозначения смотрите в таблице: 6XNN — Загрузить в регистр константу.
Самая простая инструкция для регистров это 6XNN (шестнадцатеричная запись). Где X это регистр, а NN это константа загружаемая в регистр. Например 6ABB — Загрузить в регистр под номером 10 (V[0xA], регистров 16 от нуля до пятнадцати) значение BB (187). Псевдокод: 7XNN — Добавить константу к регистру (ADDI)
Добавляет константу NN к регистру под номером X и сохраняет в регистре под номером X. Не меняет регистр переполнения. 8XY0 — Сохранить регистр в другой регистр (MOV)
Еще одна инструкция работающая с регистрами. Имеет запись 8XY0. Где X это номер регистра куда будет скопирован регистр под номером Y. Псевдокод: 8XY4 — Сложить два регистра (ADD)
Добавляет значение регистра под номером Y к регистру X и сохраняет значение в регистр X. Если переполнение произошло регистр переполнения будет установлено в значение 1. Если переполнения не произошло регистр переполнения будет сброшен в значение 0. Не совершайте моих ошибок: Регистр переполнения будет модифицирован в любом случае.
Упущение в спецификации: Что будет если регистр X будет регистром переполнения? 8XY5 — Вычесть из регистра (SUB)
Вычитает из регистра под номером X значение регистра Y и если произошло заимствование (прим. перевод Borrow) установить регистр переполнения в 1. И установить регистр переполнения в 0 если заимствование не произошло. 8XY7 — "Обратное" вычитание (SUB)
Установить регистр под номером X в результат вычитания значения регистра X из регистра Y. И если произошло заимствование установить регистр переполнения в 1. И установить регистр переполнения в 0 если заимствование не произошло. 8XY2, 8XY1 и 8XY3 — Логические операции (AND, OR, XOR)
Установить регистр X в результат операции
8XY2 — Логической "И",
8XY1 — Логической "ИЛИ",
8XY3 — Исключающее "ИЛИ"
двух операндов: регистра X и регистра Y. Не модифицирует регистр переполнения.
Не совершайте моих ошибок: Эти операции НЕ МОДИФИЦИРУЮТ регистр переполнения.
NOTE: Здесь нет опечатки. 8XY2 — AND. 8XY1 OR. 8XY3 XOR. 8XY6 — Сдвиг Вправо (Shift Right)
Сохранить в регистр X результат сдвига регистра Y вправо.
Установить регистр переполнения в значение младшего бита регистра Y.
Не совершайте моих ошибок: Результат сдвига регистра Y сохраняется в регистр X, а не в регистр Y. Хотя многие интерпретаторы это правило игнорируют. 8XYE — Сдвиг Влево (Shift Right)
Сохранить старший бит регистра Y в регистр переполнения.
Сохранить результат сдвига регистра Y в регистр X. CXNN — Рандом Случайное число
Установить значение регистра X в результат логической "И" константы NN и рандомного случайного числа. 1NNN — Прыжок в NNN
Ставит PC в значение NNN.
Следующая инструкция будет исполнена из адреса NNN BNNN — Прыжок в NNN+V0
Ставит PC в значение NNN+V0.
Следующая инструкция будет исполнена из адреса NNN+V0 2NNN — Вызов функции (Call Subroutine)
Вызывает функции по адресу 2NNN. В стек записывается значение PC + 2. 00EE — Возврат из функции (Return from Subroutine)
Регистр PC будет установлен в значение последнего элемента стека. 3XNN — Пропустить инструкцию, если константа и регистр равны.
Пропускает инструкцию (PC+4) если константа NN и регистр X равны. Иначе не пропускать (PC+2). 5XY0 — Пропустить инструкцию, если оба регистра равны.
Пропускает инструкцию (PC+4) если регистр Y и регистр X равны. Иначе не пропускать (PC+2). 4XNN — Пропустить инструкцию, если константа и регистр не равны.
Пропускает инструкцию (PC+4) если константа NN и регистр X НЕ равны. Иначе не пропускать (PC+2). 9XY0 — Пропустить инструкцию, если регистры не равны.
Пропускает инструкцию (PC+4) если регистр Y и регистр X НЕ равны. Иначе не пропускать (PC+2). В CHIP-8 есть два таймера. Один таймер отсчитывает задержку (прим. перевод Delay Timer), другой "Звуковой таймер" (прим. перевод Sound Timer) воспроизводит звук пока значение таймера больше нуля. Оба таймера уменьшают собственные значения с частотой 60Hz (60 раз в секунду). Из таймера задержек можно читать, а из "Звукового таймера" нельзя. FX15 — Установить значение таймера задержек в значения регистра X.
FX07 — Установить значение регистра X в значение таймера задержек.
Здесь все понятно :)
FX18 — Установить значение звукового таймера в значения регистра X.
NOTE: Стоит помнить, что в COSMAC VIP указанно, что значение 1 не даст никакого эффекта. Для ввода используется 16 кнопок 0-9 и A-F. Упущение в спецификации: Что будет если в кнопки под этим номером не будет. Например:17.
FX0A — Ожидание нажатия. Приостанавливает исполнение до нажатия клавиши указанной в регистре X.
Не совершайте моих ошибок: Не любых нажатий, а только нажатий именно этой клавиши. EX9E — Пропустить следующую инструкцию если кнопка соответствующая значению регистра X нажата.
EXA1 — Пропустить следующую инструкцию если кнопка соответствующая значению регистра X не нажата. Здесь думаю все понятно. ANNN — Записать в регистр I значение NNN. FX1E — Добавить значение регистра X к регистру I.
Регистр переполнения будет установлено в 1 если произошло переполнение, иначе в 0. NOTE: Графика будет подробно описана в практической части.
DXYN — Нарисовать спрайт. Рисуем спрайт размером N байт (Не нулевое значение) в позиции на экране: (Vx,Vy). Спрайт находиться в памяти по адресу I. Спрайт рисуется логически исключающим ИЛИ (XOR). Если мы перерисовали пиксель (1,1 -> 0) регистр переполнения будет установлен в 1, иначе в 0. Упущение в спецификации: 00E0 — Очистить экран. FX29 — Установить значение I в адрес спрайта для числа указанного в регистре X.
Спрайт храниться в первых 512 байтах.  Эта таблица в C: Взято отсюда. FX33 — Сохранить значения регистра в двоично десятичном формате в I, I+1 и I+2.
Смотрите: Двоично десятичный код (прим. перевод BCD — Binary-Coded Decimal) FX55 — Сохранить значения регистров V0 до VX включительно в память начиная адреса I.
После выполнения регистр I будет равен I + X + 1. Хотя некоторые интерпретаторы игнорируют это правило. FX65 — Загрузить регистры V0 до VX включительно в значения сохраненный в памяти начиная с адреса I.
После выполнения регистр I будет равен I + X + 1. Хотя некоторые интерпретаторы игнорируют это правило. На какой частоте работает интерпретатор? Про это ничего не могу найти в оригинале. Из интернета были получены самые разные частоты: 1000Hz, 840Hz, 540Hz, 500Hz, даже 60Hz. Что будет если прыжок (Jump) будет не выровнен?
Никакой информации об этом я не нашел, но думаю что инструкция будет загружаться и исполняться. Что будет если прочитать или записать из первых 512 байт?
Снова ничего не найдено. Думаю надо отдавать 0 а при записи игнорировать. На этом конец. При опечатках писать в личные сообщения. Буду рад любым замечаниям. Практическая часть находиться в процессе создания. Практическая часть будет на C (не C++) и SDL2. Тут можно найти оригинал. Еще чуть-чуть информации тут. Еще практический туториал тут.