Розробка ігор для Atari 2600. Частина 1
Цією публікацією хочу розпочати серію статей про програмування для ігрової консолі Atari 2600. Atari випустила її у 1977 році, і це одна з перших ігрових систем, побудована на мікропроцесорі та картриджах, які містили код програми. Технічно, першою є Fairchild Channel F , але це окрема історія. Більше про саму історію створення Atari 2600 можна почитати в моїй публікації
Почати пропоную з двох, не дуже пов’язаних, на перший погляд, питань.
Як формується зображення в телевізорі з CRT
Перше — Як формується зображення в телевізорі з CRT (Cathode Ray Tube або електронно-променева трубка).
Кінескоп телевізора має електронну пушку яка «стріляє» електронами, та відклоняючу систему, яка за допомогою магнітного поля, що формується котушками, може змінювати напрямок цього променя, створюючи на покритій люмінофором поверхні кінескопа зображення. В телевізорах зображення формується построково, тобто промінь спочатку малює одну строку зображення, а потім перемикається на іншу і так до кінця кадру. Коли кадр завершено, він знову малює першу строку зображення.
Залежно від телевізійного формату (NTSC, PAL або SECAM) швидкість та параметри роботи цієї системи відрізняються. Для NTSC це 525 строк зображення при швидкості 30 кадрів на секунду, тобто одна строка зображення формується 1/30/525 = 63,5 мкс. Давайте поки запам’ятаємо цю цифру. Для формату PAL це 625 строк, але 25 кадрів на секунду, тобто це 1/25/625 = 64 мкс., майже те саме.
Як схематехнічно побудована Atari 2600
Друге питання, на яке нам треба відповісти — як схематехнічно побудована Atari 2600. Існує легенда, що ця консоль взагалі не має оперативної пам’яті, хоча насправді це не так.
Уважно подивившись на схему консолі ми можемо побачити, що вона складається з 4 основних компонентів.
Перший (блакитний) — це мікропроцесор MOS6507, спрощена версія легендарного MOS6502, на якому побудовані Apple II, Commodore 64, Bender з Футурами та навіть Terminator T101. Спрощеність полягала в меншій кількості виводів (28 проти 40 в MOS6502) та лише
Другий компонент (зелений) — це мікросхема MOS6532 RAM-I/O-Timer (RIOT), яка має 128 байт статичної пам’яті, програмований таймер та порти вводу-виводу. То ж 128 байт пам’яті в консолі все ж таки є
Третій компонент (рожевий) — це слот картриджа. Сюди підключається мікросхема ROM з «прошивкою» гри. Максимальний розмір картриджа, який мала підтримувати Atari 2600, становив лише 4 кБ. Може виникнути питання, а чому так, бо процесор вміє працювати з 8 кБ адресного простору. Все вірно, але окрім ROM треба ще адресувати ту саму статичну пам’ять, порти та іншу периферію.
І от тут ми переходимо до останнього компоненту (жовтого) — це Television Interface Adapter (TIA), спеціалізована мікросхема, яку розробив геніальний інженер Jay Miner. Він також розробляв чипсет для Commodore Amiga. Власне завдяки цьому чіпу і відбувається вся магія, яку ми бачимо на екрані. Як видно зі схеми, вихідні сигнали TIA подаються на RF модулятор, що підключається безпосередньо до антенного входу телевізора.
То ж як це працює. До Atari 2600 я мав досвід низькорівневого програмування лише для ZX Spectrum. Книга «Як написати гру на Асемблері» в свій час дуже мені допомогла. І в тому ж ZX Spectrum (1982 рік) все доволі просто, бо в нього є цілих 6 кілобайт відеопам’яті. Можна взяти монохромне зображення 256×192 пікселі (256*192/8=6144 байт), помістити його в область пам’яті $4000...$57FF та отримати це зображення на екрані.
В Atari ми маємо лише 128 байт оперативної пам’яті, але вона не є відеобуфером, проте в TIA є цілих 44 програмовані регістри, через які й відбувається все програмування графіки.
А зараз пригадаймо, як зображення формується на екрані телевізора і складемо цілісну картину.
У 2009 році вийшла книга, назва якої дуже точно описує цей процес. Racing the Beam: The Atari Video Computer System — в гонитві за променем . Нам дійсно треба слідкувати за променем електронно-променевої трубки і записувати дані в відповідні регістри TIA щоб отримати зображення на екрані.
TIA формує зображення на екрані протягом двох проходів CRT-трубки, тобто для NTSC з її 525 строк ми отримуємо 525/2 = 262 лінії зображення. Назвемо їх сканлайнами (scanlines). На електричній схемі (обведено червоним) видно, що частота тактового кварцу дорівнює 3.5795 МГц, але процесор працює на половині цієї частоти — 1.7897 МГц. Відповідно, один такт дорівнює 1/1.7879 = 0.5587 мкс.
То ж скільки тактів процесора ми маємо, поки малюється один сканлайн?
63,5 мкс (одна строка зображення в NTSC) множимо на 2 (2 строки на сканлайн) ділимо на 0.5587 мкс (тривалість одного такту процесора), отримуємо 63.5*2/0.5587 = 228 тактів.
Тобто, якщо ми хочемо, построково змінювати зображення на екрані, нам треба встигнути зробити це протягом 228 тактів процесора, поки малюється сканлайн.
За специфікацією для розробників Stella Programmer’s Guide, є певні буферні зони, які не потрапляють на екран, то ж фінальна картина виглядає отак:
Перші 3 сканлайни використовуються для кадрової синхронізації.
Потім в нас є 37 сканлайнів «порожнього місця» які не потрапляють на екран і протягом цього часу ми можемо робити якусь ігрову логіку.
Далі нам треба почекати 68 тактів процесора на початку кожного сканлайну, поки промінь добіжить до видимої частини зображення, і тоді почати писати щось в регістри TIA яка й буде малювати це на екрані. За рекомендацією для розробників, видима зона має бути 192 сканлайни. Останні 30 рекомендується теж залишати порожніми і ця частина зветься overscan. Це зроблено тому, що всі телевізорі різні, і щоб картинка гарантовано потрапила в видиму частину кінескопа і ніде не обрізалася, прийняті такі стандартні параметри.
То ж спробуймо нарешті написати наш Hello World — намалюємо прапор України :) В цьому простому прикладі ми будемо використовувати лише один графічний регістр TIA COLUBK
, який відповідає за колір фону зображення. Поки ми не чіпаємо оперативну пам’ять, порти контролерів та більш складні регістри.
На щастя, майже все потрібне вже є у розширені Atari Dev Studio для VS Code. Це розширення містить асемблер DASM та емулятор Stella. (В мене емулятор вже був встановлений, то ж якщо щось не працює, встановіть його окремо за посиланням).
Більшість моїх прикладів базуються на репозиторії пана Кайла, в якого є багато відео про програмування для Atari 2600
Також нам треба розуміти карту пам’яті (memory map). Пам’ятаємо, що наш «обрізаний» процесор вміє адресувати лише 8 кБ пам’яті $0000…$1F00
.
$0000-$003F = TIA Addresses $00-$3F (zero page) - тут в нас лежать регістри TIA
$0080-$00FF = RIOT RAM (zero page) - тут в нас 128 байт оперативної пам’яті
$0100-$01FF = CPU Stack - тут знаходиться стек процесора
$0280-$029F = RIOT Addresses $00-$1F - це регістри портів вводу/виводу
$1000-$1FFF = ROM Addresses $000-$FFF - а це наші 4 кБ пам’яті картриджа.
То ж, якщо ми щось запишемо в адресу $0009
, це буде регістр TIA COLUBK
, який відповідає за колір фону, а якщо в адресу $0081
, то збережемо ці дані в другій комірці оперативної пам’яті. Писати щось в адресу $1001
сенсу нема, бо то наш картридж, Read-Only Memory.
На відміну від процесора Z80, з його двома наборами
Щоб не запам’ятовувати адреси всіх регістрів TIA можна використати готовий header-файл vcs.h
Створимо чисту папку, закинемо туди header та створимо там новий файл ukr_flag.asm
Можна забрати готові файли з гітхабу
Кожна строка детально прокоментована в самому коді, то ж сюди я додам скріни з VS Code щоб зберегти форматування та підсвітку синтаксису
Щодо палітри, тут ситуація наступна. Для кожного регіону, залежно від прийнятого в країні стандарту телебачення, випускалася своя версія TIA. Найбільш насичена палітра доступна для NTSC, там ми маємо 128 унікальних кольорів. Для PAL це лише 104 кольори, а найгірша ситуація з SECAM, лише 8 кольорів. Ані консолей, ані картриджів SECAM я ніколи наживо не бачив.
Atari 2600 не має регіональних обмежень і картридж, розроблений для NTSC системи без проблем запуститься на PAL консолі, але саме через суттєву відмінність палітр, гра буде виглядати не так, як задумали автори. До речі, колись це дуже зіпсувало моє враження, коли я запускав NTSC картриджі на європейській консолі.
Якщо подивитись на таблицю кольорів, то буде видно, що замість жовтого (0xEE
) ми отримаємо сірий, а замість рожевого (0x5A
) — зелений. На PAL консолі наш прапор буде сіро-рожевим. Перемикати відеостандарти в емуляторі можна за допомогою Ctrl+Shift+F
Час запустити наш код. Натискаємо F5
, компілятор збере нам bin-файл розміром 4 кБ і запустить його в емуляторі Stella.
Насправді наш код займає цілих 67 байтів, що можна побачити в будь-якому HEX-редакторі. Мені, до речі, сподобався HxD. Але в останніх 6 байтах
Stella це не тільки емулятор, це ще й чудовий дебаггер, який можна відкрити натиснувши тільду (під Esc). За допомогою кнопок Step, Trace, Scan+1 та Frame+1 можна детально подивитись, що відбувається з регістрами процесора та TIA, як формується зображення на екрані. Раджу додатково з цим погратися.
Думаю, для першої публікації достатньо. В наступних статтях будемо далі досліджувати регістри TIA, розбирати на прикладах випущених ігор, як саме працює ця консоль і які складнощі долали розробники ігор понад 40 років тому.
Дякую за увагу. Слідкуйте за цікавими історіями в моєму телеграм-блозі
P.S. хто дочитав до цього місця, маленький дисклеймер. Оригінал публікації запостив на Друкарню, але вона, схоже, не настільки популярна, тим більше, правила обох ресурсів, наче, не забороняють кросс-постінг :)
3 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів