Цією публікацією хочу розпочати серію статей про программування для ігрової консолі Atari 2600. Atari випустила її у 1977 році, і це одна з перших ігрових систем, побудована на мікропроцесорі та картриджах, які містили код програми. Технічно, першою є Fairchild Channel F , але це окрема історія. Більше про саму історію створення Atari 2600 можна почитати в моїй публікації 4-річної давнини на play.ua. А тут, все ж таки, давайте про программування 🙂
Почати пропоную з двох, не дуже пов’язаних на перший погляд, питань.
Перше - Як формується зображення в телевізорі з CRT (Cathode Ray Tube або електронно-промінева трубка).
Кінескоп телевізора має електронну пушку яка “стріляє” електронами, та відклоняючу систему, яка за допомогою магнітного поля, що формується катушками, може змінювати напрямок цього променя, створюючи на покритій люмінофором поверхні кінескопа зображення. В телевізорах зображення формується построково, тобто промінь спочатку малює одну строку зображення, а потім перемикається на іншу і так до кінця кадру. Коли кадр завершено, він знову малює першу строку зображення.
В залежності від телевізійного формату (NTSC, PAL або SECAM) швидкість та параметри роботи цієї системи відрізняються. Для NTSC це 525 строк зображення при швидкості 30 кадрів на секунду, тобто одна строка зображення формується 1/30/525 = 63,5 мкс. Давайте поки запам’ятаємо цю цифру. Для формату PAL це 625 строк, але 25 кадрів на секунду, тобто це 1/25/625 = 64 мкс., майже те саме.
Друге питання, на яке нам треба відповісти - як схематехнічно побудована Atari 2600. Існує легенда, що ця консоль взагалі не має оперативної пам’яті, хоча насправді це не так.
Уважно подивившись на схему консолі ми можемо побачити, що вона складається з 4 основних компонентів.
Перший (блакитний) - це мікропроцесор MOS6507, спрощена версія легендарного MOS6502, на якому побудовані Apple II, Commodore 64, Bender з Футурами та навіть Terminator T101. Спрощеність полягала в меншій кількості виводів (28 проти 40 в MOS6502) та лише 13-бітній шині адрес, що дозволяє адресувати лише 8 кБ адресного простору.
Другий компонент (зелений) - це мікросхема 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, з його двома наборами 16-бітних регістрів загального призанчення, індескними регістрами та ще купою крутих штук (він стоїть в ZX Spectrum, Sega Master System і ще багатьох 8-бітних платформах), у MOS6502 можливості значно скромніші. Він має лише 8-бітний акумулятор А та два 8-бітних індексних регістри X та Y, але і з цим можна працювати.
Щоб не запам’ятовувати адреси всіх регістрів 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 байтах 4-кілобайтного блоку має лежати таблиця переривань, то ж компілятор створює файл на всю доступну пам’ять.
Stella це не тільки емулятор, це ще й чудовий дебаггер, який можна відкрити настснувши тільду (під Esc). За допомогою кнопок Step, Trace, Scan+1 та Frame+1 можна детально подивитись, що відбувається з регістрами процесора та TIA, як формується зображення на екрані. Раджу додатково з цим погратися.
Думаю, для першої публікації достатньо. В наступних статтях будемо далі досліджувати регістри TIA, розбирати на прикладах випущених ігор, як саме працює ця консоль і які складнощі долали розробники ігор більше 40 років тому.
Дякую за увагу. Слідкуйте за цікавими історіями в моєму телеграм-блозі