CHIP-8: Відродити класичний Pong

Написання комп’ютерної гри завжди було складним завданням. Змінюється лише характер цієї складності. Сучасна гра – це не тільки програмування, а й графіка, звук та розповідь. Але в перші дні комп’ютерів створення гри було викликом через обмежену графіку, повільні процесори та крихітну пам’ять. Якщо ви хотіли написати гру для раннього мікрокомп’ютера, зазвичай ви писали її на асемблері. BASIC був доступний на багатьох машинах, але він був занадто повільним для всього, що вимагало швидкої анімації. Асемблер був швидким, але його було важко писати, налагоджувати та переносити на іншу машину.

Компанія RCA – зокрема [Joseph Weisbecker], архітектор процесора 1802 – представила COSMAC VIP на первинному ринку домашніх відеоігор 1977 року. Окрім використання готових ігор, ідея полягала в тому, щоб ви могли створювати власні. Але написання ігор на асемблері 1802 року було б поза межами можливостей більшості клієнтів RCA. [Weisbecker] розробив CHIP-8, крихітну віртуальну машину. Це була не зовсім мова програмування. Натомість, це був простий набір інструкцій, реалізований інтерпретатором, що працював на справжньому комп’ютері. Ви писали програми для уявного процесора CHIP-8, а інтерпретатор запускав їх на реальному обладнанні. CHIP-8 ідеально підходить для ігор того часу і досі має прихильників серед тих, хто любить ретро-ігри.

Інший час: крихітні інтерпретатори

Сьогодні, коли ми думаємо про інтерпретатори, ми часто уявляємо щось на зразок Python, де інтерпретатор читає вихідний код і визначає, що робити. Однак, існує інший підхід, який був особливо привабливим, коли пам’ять і процесорний час були дефіцитними: потоковий код (threaded code).

В інтерпретаторі потокового коду програма складається з посилань (наприклад, індекси в таблиці команд) на процедури, які виконують операції. Замість декодування складних інструкцій, інтерпретатор дотримується цих посилань, виконуючи відповідні процедури. Існують різні варіації, включаючи пряме потокове виконання, непряме потокове виконання та токенне потокове виконання. Forth, ймовірно, є найвідомішим прикладом мови, яка часто використовує потоковий код. У типовій реалізації Forth програма компілюється в послідовність посилань на слова, а компактний внутрішній інтерпретатор їх виконує.

Навіщо це потрібно? Тому що реалізація може бути надзвичайно маленькою. Замість генерації нативного машинного коду, компілятор може створювати компактне представлення, яке відносно легко виконати. CHIP-8 належить до цієї ж загальної сім’ї ідей, хоча його точніше описати як байт-код інтерпретатор, ніж як звичайну систему потокового коду. Його інструкції мають довжину два байти, а інтерпретатор декодує кожну інструкцію та виконує відповідну операцію. Оригінальна реалізація була написана на машинному коді RCA 1802.

Історія CHIP-8

COSMAC VIP був цікавою машиною. Базова система мала 2 КБ оперативної пам’яті, шістнадцяткову клавіатуру та монохромний відеодисплей. Програми зазвичай вводилися у вигляді шістнадцяткових байтів за допомогою клавіатури, хоча були доступні опції розширення. Процесор 1802 всередині не був особливо швидким. Це був 8-бітний процесор із шістнадцятьма 16-бітними регістрами та незвичною архітектурою, що робило його добре придатним для певних вбудованих застосувань. Існували версії, які також добре підходили для космічних апаратів, тому цей чіп використовувався в Magellan, Galileo та навіть у деяких частинах Hubble.

CHIP-8: Відродити класичний Pong 1
VIP ([Dave Ruske] CC-BY-SA-2.0)

CHIP-8 значно полегшив програмування VIP, особливо для ігор. Замість того, щоб мати справу з особливостями 1802 та апаратного забезпечення дисплея VIP, програмісти могли використовувати невеликий, послідовний набір інструкцій, розроблений навколо графіки та введення з клавіатури.

Віртуальна машина CHIP-8 надає шістнадцять 8-бітних регістрів, які зазвичай називаються V0 до VF. Вона також має 16-бітний індексний регістр, лічильник команд, стек для викликів підпрограм та два таймери.

Дисплей має ширину 64 пікселів і висоту 32 пікселі. Пікселі можуть бути увімкнені або вимкнені. Немає кольору, апаратних спрайтів чи складного графічного процесора. Однак, є інструкції, спеціально призначені для полегшення написання ігор. Ви можете намалювати спрайт однією інструкцією, перевірити, чи натиснута клавіша, згенерувати випадкове число та використовувати таймери для керування анімацією.

Спрайт – це просто послідовність байтів, де кожен біт представляє піксель. Типовий спрайт може мати ширину до восьми пікселів і висоту п’ятнадцять пікселів. Набір інструкцій крихітний, лише з кількома десятками основних операцій. Проте, цього достатньо для реалізації таких ігор, як Pong, Breakout та шутери, подібні до Space Invaders.

Згодом CHIP-8 з’явився на інших системах, а пізніші варіанти розширили його можливості. Найвідомішим розширенням є Super-CHIP, яке додало такі функції, як графіка з вищою роздільною здатністю. Сьогодні CHIP-8 залишається актуальним переважно тому, що він є чудовою метою для проектів емуляторів. Написання інтерпретатора CHIP-8 – це кероване завдання, і існує тисячі невеликих програм для його тестування. Але вам не обов’язково писати інтерпретатор, щоб експериментувати з CHIP-8.

Сучасний Octo

Octo – це сучасне середовище розробки CHIP-8, створене [John Earnest]. Воно працює у веб-браузері і включає асемблер, емулятор, відлагоджувач та інструменти для роботи з програмами CHIP-8. Ви можете писати код, асемблювати його та запускати без встановлення будь-чого. Octo також підтримує розширення до оригінального набору інструкцій, хоча для цього проекту ми будемо дотримуватися звичайного CHIP-8. Використання синтаксису асемблера вищого рівня, ніж шістнадцяткові опкоди, що використовувалися ранніми програмістами CHIP-8, вже є досить зручним.

Вам все одно доведеться думати як програміст на асемблері. Є регістри, переходи, підпрограми та дуже маленький набір інструкцій. Однак, вам не доведеться пам’ятати, що 6A05 означає завантажити регістр VA значенням 5. Натомість, ви можете написати va := 5.

Це набагато легше для читання. Octo також надає мітки, константи, вирази та інші зручності, які роблять практичним написання програм розумної складності.

Трохи асемблера CHIP-8

CHIP-8 має шістнадцять регістрів загального призначення, V0 через VF. Більшість інструкцій працюють з цими регістрами.

Наприклад:

v0 := 10 v1 := 20 v0 += v1

Це завантажує 10 у V0, 20 у V1 і додає V1 до V0. Після цього V0 містить 30. Однак, Octo також дозволяє використовувати ключові слова :alias та :const для полегшення:

:const RETRY 10 :alias trycount v0 trycount := RETRY

Також є побітові операції:

v0 &= v1 v0 |= v1 v0 ^= v1

Ви можете порівнювати регістри та значення за допомогою умовних інструкцій. Одна річ, до якої потрібно звикнути, це те, що CHIP-8 насправді не має звичайних умовних переходів. Натомість, умовні інструкції пропускають наступну інструкцію, коли умова виконана. На щастя, Octo приховує частину цієї незручності за допомогою синтаксису, такого як:

if v0 == 10 then jump somewhere

Під капотом це все ще умовний пропуск, за яким слідує перехід.

Підпрограми прості:

: main   v0 := 10   do_something jump main  : do_something   v0 += 1 return

Малювання особливо цікаве. Щоб намалювати спрайт, ви спочатку завантажуєте індексний регістр i з адресою даних спрайта. Потім ви використовуєте інструкцію спрайта.

Наприклад:

i := ball_sprite sprite v0 v1 1 ; address of sprite for ball: ball_sprite 0x80

Кожен спрайт має висоту один байт і ширину вісім бітів. Оскільки встановлено лише найвищий біт, він малює один піксель. Регістри V0 і V1 вказують координати X і Y.

Зіграємо в Pong

Проста гра Pong – це гарна демонстрація програмування CHIP-8, оскільки вона вимагає дуже невеликих графічних можливостей, але задіює більшу частину цікавих частин віртуальної машини. Ця версія має дві ракетки, по одній з кожного боку екрану, і один піксель м’яча. Ліва ракетка використовує клавіші 1 і 4 для руху вгору і вниз. Права ракетка використовує C і D. Якщо ви використовуєте Octo, це відповідає клавішам 1 і Q разом з 4 і R.

CHIP-8: Відродити класичний Pong 2
Pong в дії!

М’яч рухається по екрану, відскакуючи від верхньої та нижньої стін. Коли він вдаряє об ракетку, він змінює напрямок. Якщо гравець пропускає, м’яч повертається до центру. М’яч не завжди відскакує прямо. Його вертикальний напрямок залежить від того, де він вдаряє об ракетку. Удар по центру призводить до горизонтальної траєкторії. Удари вище або нижче центру призводять до прогресивно крутіших кутів.

Оскільки CHIP-8 не має арифметики з плаваючою комою, ми наближаємо кути за допомогою цілочисельних лічильників. Наприклад, траєкторія в п’ять градусів рухається вертикально приблизно на один піксель за кожні одинадцять пікселів горизонтального руху. Для траєкторії в десять градусів ми рухаємося вертикально приблизно один раз на кожні шість горизонтальних пікселів. Це не складна симуляція фізики, але вона добре працює для Pong. Ви можете зіграти в неї безпосередньо за допомогою Octo у вашому браузері.

Ігровий цикл концептуально простий:

: main   erase_objects   move_paddles   move_ball   draw_objects   wait_for_frame jump main

Реальна програма трохи складніша, оскільки нам доводиться обробляти введення, зіткнення та граничні умови. Тим не менше, базова структура знайома кожному, хто писав просту відеогру.

Повний вихідний код Octo можна знайти на GitHub. Програма використовує звичайні інструкції CHIP-8 і повинна запускатися в Octo або іншому сумісному інтерпретаторі.

Виявлення зіткнень

CHIP-8 має цікаву функцію, яка робить виявлення зіткнень спрайтів особливо легким. Кожного разу, коли ви виконуєте інструкцію спрайта, інтерпретатор встановлює регістр VF, щоб вказати, чи були стерті якісь пікселі операцією XOR. Якщо жодні пікселі не були стерті, VF дорівнює нулю. Якщо один або кілька пікселів були стерті, VF дорівнює одиниці. Припустимо, на дисплеї є піксель, який вже увімкнений. Якщо ви намалюєте інший спрайт поверх цього пікселя, піксель вимкнеться. Якщо ви намалюєте поверх нього ще раз, він увімкнеться. Це дозволяє стирати спрайти, просто малюючи їх знову.

Це також забезпечує простий механізм виявлення зіткнень. Якщо ви намалюєте м’яч поверх ракетки, деякі пікселі м’яча перекриватимуться з пікселями ракетки. Операція XOR стирає ці пікселі, і VF стає одиницею. У нашій грі Pong ми можемо використовувати це, щоб визначити, чи влучив м’яч у ракетку.

Концептуально код виглядає так:

i := ball_sprite sprite v0 v1 1 if vf == 1 then jump collision

Тут є нюанс. VF не говорить нам, з чим зіткнувся м’яч, лише те, що операція малювання стерла принаймні один піксель. Для Pong ми можемо використовувати положення та напрямок м’яча, щоб визначити, яка ракетка задіяна. Потім ми можемо обчислити положення м’яча відносно ракетки, щоб визначити його нову траєкторію.

Наприклад, припустимо, що ракетка займає п’ять вертикальних пікселів. Якщо м’яч вдаряє об середній піксель, він рухається горизонтально. Якщо він вдаряє на один піксель вище центру, він рухається вгору приблизно на п’ять градусів. Два пікселі вище дають десять градусів. Те саме стосується і нижньої частини, крім того, що м’яч рухається вниз. Це простий спосіб додати трохи різноманітності до гри без необхідності складних обчислень.

Малювання XOR також дає нам спосіб уникнути мерехтіння під час малювання. Замість того, щоб очищати весь екран, ми можемо стерти старі об’єкти, намалювавши їх знову. Потім ми оновлюємо їхні координати та малюємо їх у нових положеннях. Цей підхід уникає багаторазового вимкнення всього дисплея і зазвичай дає більш плавне анімацію.

Однак, є нюанс. Пам’ятаєте, що малювання спрайта встановлює VF, коли пікселі стираються? Коли ми навмисно стираємо старі спрайти, VF також може стати одиницею. Це не означатиме зіткнення. Це просто означає, що операція XOR зробила саме те, що ми хотіли. Отже, ми ігноруємо VF під час проходу стирання. Ми розглядаємо його лише під час виконання операції малювання, призначеної для виявлення зіткнення. Ще одна важлива деталь: VF оновлюється кожною інструкцією спрайта. Якщо ви малюєте м’яч, а потім малюєте ракетку, друге малювання перезаписує VF. Тому вам потрібно перевірити або зберегти VF одразу після відповідної інструкції.

Наскільки швидкий CHIP-8?

Оригінальний інтерпретатор CHIP-8 працював на мікропроцесорі 1970-х років, тому ніхто не очікував вражаючої продуктивності. Сучасні комп’ютери можуть виконувати інструкції CHIP-8 майже миттєво. Однак, запуск інтерпретатора з максимально можливою швидкістю зробить багато старих ігор неграбельними.

CHIP-8 має два таймери, таймер затримки та звуковий таймер, які зменшуються зі швидкістю 60 Гц, коли вони не нульові. Програми можуть використовувати таймер затримки для керування анімацією незалежно від частоти виконання інструкцій інтерпретатором. Наприклад:

v0 := 2 delay := v0 : wait   v0 := delay   if v0 != 0 then jump wait

Це чекає, доки таймер затримки не стане нульовим. З таймером 60 Гц два відліки відповідають приблизно 33 мілісекундам, або 30 кадрам на секунду.

Octo також дозволяє регулювати швидкість виконання інтерпретатора. Це важливо, оскільки CHIP-8 ніколи не мав єдиної універсально стандартизованої частоти виконання інструкцій. Різні історичні реалізації працювали з різною швидкістю, і програми іноді залежали від цих відмінностей. Якщо ви запускаєте інтерпретатор занадто повільно, гра може стати млявою. Якщо ви запускаєте його занадто швидко, гра може працювати набагато швидше, ніж було задумано.

Для нашої гри Pong таймер затримки забезпечує основне тактування кадру, тому точна частота виконання інструкцій менш критична. Однак, інтерпретатор все ще повинен виконати достатньо інструкцій між тактами таймера, щоб завершити кожен кадр. Існує також цікава взаємодія між швидкістю інтерпретатора та поведінкою дисплея. Деякі реалізації CHIP-8 синхронізують операції малювання з оновленням дисплея, а інші – ні. Ці відмінності можуть вплинути на плавність анімації та видиму швидкість програми.

Якщо ви експериментуєте з Octo, варто налаштувати параметри швидкості, щоб побачити, як вони впливають на гру.

Все старе…

CHIP-8 майже п’ятдесят років, але основна ідея залишається знайомою. Розглянемо Java Virtual Machine. Вихідний код Java компілюється в байт-код, який виконується на віртуальній машині, а не безпосередньо на процесорі хоста. CHIP-8 робить по суті те саме, але в набагато меншому масштабі. Звичайно, JVM набагато складніша. Вона підтримує великий набір інструкцій, складні типи даних, об’єктно-орієнтоване програмування, збирання сміття, механізми безпеки та часто компіляцію “на льоту” (just-in-time compilation). До цього система UCSD P-code пропонувала більшу абстракцію для ранніх комп’ютерів.

CHIP-8 має лише шістнадцять 8-бітних регістрів, кілька десятків інструкцій і монохромний дисплей 64×32. Але всі ці системи намагаються відокремити погляд програміста на машину від фактичного обладнання. CHIP-8 також демонструє, як багато можна досягти з дуже малим. Його набір інструкцій достатньо малий, щоб вивчити його за півдня, а написання інтерпретатора для нього – це керований проект на вихідні.

Якщо ви хочете зрозуміти віртуальні машини, емулятори або навіть дизайн наборів інструкцій, CHIP-8 – це особливо доступне місце для початку. І якщо нічого іншого, це нагадування про те, що вам не потрібні мільйони рядків коду, гігабайти пам’яті або графічний процесор, щоб написати відеогру. Він навіть надихнув деякі нові системи.

Головне зображення: “Iconic COSMAC Elf spaceship scene” від [William Donnelly]

  • CHIP-8: Відродити класичний Pong 3
  • CHIP-8: Відродити класичний Pong 4
  • CHIP-8: Відродити класичний Pong 5
  • CHIP-8: Відродити класичний Pong 6

Posted in Hackaday Columns, Slider Post navigation ← The Drawer Every Standing Desk Needs Модульні, 3D-друкні поїзди-свистуни для всіх  →

7 думок про “CHIP 8: The Pong Game’s Afoot!”

  1. Gösta каже: Листопад 11, 2026 о 1:58 вечора

    Чудова стаття 🙂

    Reply

    1. make piece not war каже: Листопад 11, 2026 о 4:20 вечора

      Так, і вона також закінчується!

      Reply

      1. dremu каже: Листопад 11, 2026 о 4:59 вечора

        Little endian чи big endian?

        Reply

  2. Joshua каже: Листопад 11, 2026 о 10:08 вечора

    1802 (не версія для космічних умов) також використовувався в аматорському радіосупутниковому сервісі.

    AO-10 та 13 мали його як HCU (house keeping unit), який керував системою програмування IPS.

    https://amsat-dl.org/en/amsat-phase-3-b-oscar-10/

    https://en.wikipedia.org/wiki/AMSAT-OSCAR_10

    Reply

    1. Joshua каже: Листопад 11, 2026 о 10:27 вечора

      Якщо ви хочете дізнатися більше про IPS..

      https://amsat-dl.org/en/ips-high-level-programming-of-small-systems-for-the-amsat-space-projects/

      Мова програмування спочатку була німецькою, але існують і англійські переклади.

      Востаннє IPS використовувався на аматорському супутнику в колишній зірці серії OSCAR, AO-40.

      https://en.wikipedia.org/wiki/OSCAR_40

      https://www.amsat-bda.org/IPS_Home.html

      IPS був портований на кілька платформ, включаючи ті, які зараз вважаються “ретро” або вінтажними.

      PS: Раніше я написав HCU, але правильний термін, можливо, IHU (Internal Housekeeping Unit).

      Reply

  3. Jamieson каже: Листопад 12, 2026 о 9:02 ранку

    Ігри CHIP пережили невелике відродження на початку 90-х, коли для калькуляторів HP 48 були написані інтерпретатори CHIP-48 та SCHIP-48. Я досі граю в тетріс і реверсі на своєму ’48.

    Reply

    1. Joshua каже: Листопад 12, 2026 о 11:22 ранку

      Привіт, дійсно! Існують також вищі версії CHIP-8!

      Reply

Залишити відповідь Скасувати відповідь

Будь ласка, будьте ввічливі та шанобливі, щоб зробити розділ коментарів чудовим. (Політика коментарів)

Цей сайт використовує Akismet для зменшення спаму. Дізнайтеся, як обробляються дані ваших коментарів.

Пошук

Ніколи не пропустіть хак

Слідкуйте на facebook

Слідкуйте на twitter

Слідкуйте на youtube

Слідкуйте на rss

Зв’язатися з нами

Підписатися

Якщо ви пропустили

  • CHIP-8: Відродити класичний Pong 7

    Секстант: навігація старим способом

    16 Коментарів

  • CHIP-8: Відродити класичний Pong 8

    EV батареї виявляються витривалішими, ніж очікувалося

    53 Коментарів

  • CHIP-8: Відродити класичний Pong 9

    DeviceTree для хакерів: Вступ

    21 Коментарів

  • CHIP-8: Відродити класичний Pong 10

    Радон Активний

    30 Коментарів

  • CHIP-8: Відродити класичний Pong 11

    Думаю, нам слід побачити інші SBC

    76 Коментарів

Більше з цієї категорії

Наші колонки

  • Плата INA219 з роз'ємом QWIIC та кабелем

    Просто використовуйте QWIIC вже

    10 Коментарів

  • CHIP-8: Відродити класичний Pong 12

    FLOSS Weekly Епізод 883: Telnet нас зламав

    2 Коментарів

  • CHIP-8: Відродити класичний Pong 13

    CHIP 8: The Pong Game’s Afoot!

    7 Коментарів

  • CHIP-8: Відродити класичний Pong 14

    Як CRISPR може позначити мутовані ракові клітини для знищення

    11 Коментарів

  • CHIP-8: Відродити класичний Pong 15

    UDP-бродкаст зустрічає більш сфокусований мультикаст

    8 Коментарів

Більше з цієї категорії

Оригінал статті: hackaday.com

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *