Обзор архитектуры Game Boy (+Color)
Восьмибитная портативная консоль от Nintendo — Game Boy вышла в 1989 году — через 6 лет после выхода домашней стационарной Famicom/NES (известной у нас под брендом Денди) и очень сильно опередила всех своих конкурентов. Фактор портативности довольно сильно урезал возможности аппарата и чёрно–белые игры на нём выглядели бледными копиями аналогов на старшем брате, но популярности это нисколько не помешало. А в 1998 году вышла обновлённая версия консоли — Game Boy Color, которая сильно раздвинула и цветовые и другие возможности при этом сохранив обратную совместимость. Сегодня мы обзорно рассмотрим эти две консоли изнутри с точки зрения программиста.
Центральный Процессор и память
Центральный процессор Game Boy (GB) и Game Boy Color (GBC) — это Sharp LR35902, который я описывал в отдельной статье.
Если вкратце, то это очень сильно урезанный 8-битный процессор Zilog Z80 который в свою очередь — сильно улучшенный Intel 8080.
В процессе урезания (видимо для экономия энергии для формата портативки) Sharp LR35902 потерял почти все нововведения и усовершенствования от Z80 и даже некоторый функционал i8080, поэтому стал несовместим ни с тем ни с другим, хотя большая часть базовых инструкций имеет тот же двоичный формат. Помимо этого были так же добавлены новые инструкции, например, позволяющие быстро работать с последними 256 байтами оперативной памяти (нечто похоже на zero-page в MOS 6502 в Famicom/NES/Денди, но в реалиях архитектуры i8080).
В GB процессор работает на частоте 4.2Мгц, а в GBC может быть переведён в ускоренный режим с 8.4МГц, хотя даже в GBC рекомендовалось работать на пониженной частоте при возможности, чтобы экономить батарею. Однако, несмотря на то, что переключение между частотами совершалось программно, но делалось это не мгновенно, а только в определенный момент VBlank и экран при этом явственно моргал, так что невозможно было переключаться несколько раз за кадр и даже просто каждый кадр во время игрового процесса — так что рекомендацию работать на пониженной частоте игры часто не могли выполнять даже частично.
В типичном для 8-биток адресном пространстве в 64 Кб на первые 32 Кб памяти консоли маппится ROM картриджа (возможно с переключением банков ROM через Memory Bank Controller (MBC) — контроллер банков памяти), а в последних 32 Кб памяти находятся 8 (GB) или 16 (GBC) Кб видеопамяти, 8 (GB) или 32 (GBC) Кб оперативной памяти, порты ввода-вывода, таблица спрайтов и 127 байт ячеек быстрой «верхней» памяти.
При этом в GBC доступ к дополнительным RAM видео- и рабочей памяти производится тоже маппингом страниц, так что раскладка памяти выглядит следующим образом:
0000-3FFF (первые 16Кб) - банк 00 ROM картриджа (неизменяемый)
4000-7FFF (вторые 16КБ) - переключаемый банк ROM картриджа (от 01 до NN в зависимости от ёмкости картриджа)
8000-9FFF (8Кб) - VRAM видеочипа (в GBC переключается между двумя банками по 8Кб)
A000-BFFF (8Кб) - дополнительное ОЗУ на картридже, если есть (возможно с переключениями банков)
C000-CFFF (4Кб) - банк 00 RAM консоли
D000-DFFF (4Кб) - банк 01 RAM консоли (в GBC можно переключать на банки 01-07)
(т.е. в GB - 8Кб внутренней RAM, а в GBC - 32Кб, но со страничным доступом)
E000-FDFF (7680 байт) - "зеркало" C000-DDFF, обычно никак не используемое
FE00-FE9F (160 байт) - таблица атрибутов спрайтов - Sprite Attribute Table (OAM)
FEA0-FEFF (96 байт) - не используется
FF00-FF7F (128 байт) - порты ввода-вывода
FF80-FFFE (127 байт) - быстрая "верхняя" память (High RAM - HRAM)
FFFF-FFFF (1 байт) - регистр управления прерываниями
Здесь можно отметить, что Nintendo видимо по привычке замапило порты ввода-вывода в адресное пространство ЦП и поэтому специализированные команды ввода-вывода в нём от Z80 и i8080 тоже пошли под нож. Зато сами порты ввода-вывода были размещены в последних 256 байтах RAM из-за чего к ним стало возможным обращаться новыми быстрыми командами.
Видеочип
Как и принято в таких консолях — единицей изображения является квадратный тайл 8x8 пикселей.
Разрешение LCD экрана — 160x144 пикселя, что вмещает ровно 20x18 тайлов.
Изображение на экране компонуется из трёх составляющих:
— задний фон (background) — квадратно-гнездовая сетка из тайлов с аппаратным скроллингом во всех направлениях.
— окно (window) — квадратно-гнездовая сетка из тайлов которую можно «приклеить» верхним-левым углом к любой координате экрана и она будет выводится в пикселях ниже и правее над задним фоном. не скроллится, а в остальном устроена как задний фон. применяется для панелей с игровой статистикой.
— спрайты — до 40 спрайтов размером 8x8 или 8x16 (размер выбирается для всех сразу) до 10 спрайтов в одной строке сканлайна (большее количество спрайтов в строке просто не рисуется)
Любую из составляющих можно включить или отключить, как и весь LCD-экран целиком. Напрашивается сравнение с Famicom/NES/Денди, где было 64 аппаратных спрайта с 8 спрайтами в строке.
У классического Game Boy (GB) монохромная цветность с четырьмя градациями черного/белого, поэтому на 1 пиксель тайла требуется 2 бита информации. Следовательно целиком описать изображение одного тайла можно 16 байтами. 8 строк хранятся последовательно по 2 байта на строку при этом первый байт в строке хранит в каждом бите нижний бит номера цвета в соответствующем пикселе, а второй — верхний.
Первые 6144 байт видеопамяти у GB отведены под последовательный массив таких изображений — 384 тайлов — тайловые данные (Tile Map Data) или, как сейчас выражаются — тайлсет.
Технически эта зона как бы поделена на две пересекающихся области по 256 тайлов — с адресов от $8000 до $8FFF и от $8800 до $97FF. При этом 128 изображений тайлов в центральной трети этих 6 Кб у них получаются общими.
Тайлы первой области жёстко привязаны к спрайтам, т.е. спрайты могут брать изображения для себя только из первых 256 тайловых данных — как тайлов с номерами от 0 до 255.
Это относится и к заднему фону и окну, но их еще можно (но только одновременно) переключить на вывод тайлов из второй области. Тут правда происходит одно усложение тайлы с номерами 0..127 лежат в её второй половине (начиная с адреса $9000), а тайлы с номерами 128...255 — в первой, той самой которая пересекается с первой областью. Так сделано чтобы общие тайлы в обеих областях имели одинаковые номера-индексы. Здесь опять можно сравнить с Famicom/NES/Денди — там было два полностью независимых банка для фона и спрайтов по 256 тайла в каждом, т.е. 512 уникальных тайла против 384 в GB.
Задний фон
Задний фон и окно в GB выводят квадратно-гнездовую сетку тайлов из так называемой карты тайлов — это квадратный массив 32x32 байт каждый элемент которого означает номер тайла который надо вывести в фоне или окне в данном месте.
Т.к. размеры экрана у GB(C) скромные, то в отличие от Famicon/NES/Денди тайловой карты 32x32 достаточно для полноценной прокрутки заднего фона по всем направлениям.
Карта тайлов (точнее даже их индексов) 32x32 занимает 1024 байта и формирует виртуальную сетку в 256x256 пикселей, часть из которых видна в заднем фоне или окне.
Последние 2048 байт видеопамяти отведены под две таких карты (адреса 9800-9BFF и 9C00-9FFF) и любую из них можно выбрать в качестве источника изображения как для заднего фона так и для окна (тут уже независимо друг от друга).
При этом для заднего фона в двух портах ввода-вывода мы задаём какой пиксель этой виртуальной пиксельной карты будет изображаться в левом-верхнем углу экрана, а остальное изображение замостит экран плиткой тайлов и если потребуется «провернётся» по краям экрана, чтобы стал возможен произвольный скроллинг (см. статью про Famicom/NES/Денди для подробностей).
А для окна аналогичные два порта ввода-вывода указывают уже координату на экране где начнётся вывод его содержимого — при этом никаких проворачиваний совершаться не будет.
Таким образом видеопамять GB (и первую страницу видеопамяти GBC) можно описать так:
8000-87FF (2 Кб) - тайлы 0..127 первого тайлсета
8800-8FFF (2 Кб) - тайлы 128..255 первого и второго тайлсетов
9000-97FF (2 Кб) - тайлы 0..127 второго тайлсета
9800-9BFF (1 Кб) - тайловая карта 0
9C00-9FFF (1 Кб) - тайловая карта 1
Интересно, что несмотря на монохромность GB в тайловых данных хранятся не непосредственные значения цвета на экране, но индексы в одной из трёх палитр, при этом каждая из палитр это всего лишь один 8-битный порт ввода-вывода, т.к. четыре группы по два бита полностью описывают одну палитру — какое из четырёх значений пикселя в тайловых данных к какой из четырёх возможных интенсивностей света приведут.
Одна палитра используется для фона и окна, а две других — для спрайтов (в каждом спрайте хранится в одном бите какую из двух он использует), но нулевой цвет в спрайтах всегда означает прозрачность, поэтому оборудованием игнорируется. Именно из-за этого наличия казалось бы не очень нужных примитивных палитр в GBC удалось несложным образом раскрасить многие из уже существующих игр для GB.
В Game Boy Color (GBC) вещи становятся сложнее — в режиме GBC он обзаводится второй страницей видеопамяти (по тем же адресам — между ней и обычной страницей надо переключаться записью в порты ввода-вывода) и полноценными палитрами с цветностью.
Существуют две палитры — для заднего фона/окна и отдельно для спрайтов. В каждой палитре 32 ячейки 16-битных цветов при этом они считаются разбитыми на 8 субпалитр по 4 цвета в каждой. Обновление цветов в палитрах осуществляется через порты ввода-вывода.
Вторая страница видеопамяти в GBC сделана довольно просто — в первых 6 Кб её в точно таком же формате как и в первой хранится еще один такой же по объёму тайлсет в 384 тайла — таким образом в GBC количество тайлов расширено до 768 штук.
А в последних 2 Кб второй страницы видеопамяти ровно «напротив» каждого байта/номера тайла в первой странице (по тому же адресу после перемаппинга) хранится байт дополнительных атрибутов, где задаются:
— номер страницы в которой изображение тайла находится (0..1)
— номер палитры для тайла (0..7)
— вертикальное и/или горизонтальное зеркалирование тайла в клетке
— подавление приоритета спрайтов (безусловный вывод тайла поверх спрайтов)
Как видно графические возможности заднего фона и окна в GBC были значительно улучшены по сравнению с GB (и даже Famicom/NES/Денди, где даже и окна не было и всяческие статус-бары надо было делать техникой HBlank-отсечения).
Спрайты
Таблица спрайтов хранится в портах ввода-вывода в 160 байтах начиная с адреса $FE00. Каждый из 40 возможных спрайтов описывается четырьмя подряд идущими байтами:
1) координата Y увеличенная на 16. правильный способ скрыть спрайт — вывести его за экран по координате Y.
2) координата X увеличенная на 8. даже если спрайт выведен за экран по координате X он влияет на максимальное число спрайтов в строке, поэтому см. пункт выше.
3) номер тайла для отображения (0..255)
4) набор флагов:
— приоритет — находится спрайт под или над задним фоном (может подавляться в GBC)
— вертикальное и/или горизонтальное зеркалирование
— для режима GB бит выбора первой или второй палитры
— для режима GBC номер палитры (0..7)
— для режима GBC выбор страницы памяти где находится тайл (0..1)
Есть глобальная настройка — отображается каждый спрайт одним тайлом (8x8) или двумя тайлами один друг под другом (8x16) — в последнем случае номер тайла должен быть чётным числом (в железе нижний бит номера просто игнорируется) — под этим номером выводится верхний тайл, а нижний берется со следующим (нечётным) номером.
Здесь можно понять почему максимальное количество разных цветов которое (без трюков) консоль может отобразить на экране — это 56. В разных плитках фона можно задать по 4 цвета в 8 разных субпалитрах, что даст нам первые 32 цвета. И почти то же самое происходит с восемью субпалитрами спрайтов, но т.к. в спрайтах нулевой цвет всегда прозрачен, то они могут дать нам только 3*8=24 цвета. Поэтому в сумме имеем 56 возможных цветов в одном кадре.
Прочее
Чтение и запись в видеопамять, палитру и таблицу спрайтов можно осуществлять только в моменты когда видеочип с ней не работает — поэтому есть порты ввода по которым можно понять наступил ли подходящий момент и развитая система генерации прерываний.
Видеочип может генерировать прерывания по VBlank, HBlank и по достижению номера текущего рисуемого сканлайна заданного в порте ввода-вывода значения (может использоваться для реализации сложных HBlank-отсечений).
Существует DMA-канал которым можно инициировать быструю передачу 160 байт из памяти в таблицу спрайтов, её полезно запускать во время VBlank заранее подготовив таблицу памяти в рабочей памяти.
В GBC так же существует дополнительный DMA-канал которым можно организовывать передачу из почти любого места в RAM или ROM в почти любое место в VRAM. При этом программа либо должна обеспечить запуск DMA-передачи в момент когда видеочип не читает видеопамять, либо задать такой режим, когда DMA-контроллер проводит передачу порциями по 16 байт в момент наступления прерываний HBlank.
За звук в GB отвечает типичный для 8-битного поколения набор генераторов звука, на котором я не буду подробно останавливаться, опишу только общие возможности:
— один генератор прямоугольного сигнала с огибающей и автоматическим изменением тона
— один генератор прямоугольного сигнала с огибающей
— 4-битный PCM-канал небольшого банка памяти
— генератор белого шума
Имелся так же звуковой вход от картриджа, видимо для потенциального расширения звуковых возможностей.
Оставшиеся порты ввода-вывода обеспечивали взаимодействие с кнопками, серийным портом для связи с другими устройствами, таймером — от всех них, кстати, могли приходить еще прерывания. В GBC так же еще был инфракрасный порт.
Как и в Famicom/NES/денди контроллеры банков памяти не встраивались в саму консоль, а всегда, по необходимости, находились на картридже. Но их число судя по всему не превышает десятка штук в отличие от того зоопарка что был на Famicom. Типовые MBC позволяли увеличить суммарный ROM картриджа до 2 Мб и встроить в картридж энергозависимую SRAM на батарейке для сохранений игр. Таковыми были мапперы MBC1 и MBC2. MBC3 кроме этого содержал еще часы реального времени в формате день: час: мин: сек, где день мог меняться в интервале от 1 до 512, что позволяло сохраняя текущий год в память сохранений автоматически поддерживать правильность даты при условии что игра на картридже включается не реже чем раз в 512 дней. Забавно, что Nintendo не выпустило маппера с маркировкой MBC4 — скорее всего из-за негативного отношения к «плохой» цифре 4 в японской культуре, а сразу перешло к MBC5. MBC5 был первым маппером умеющим работать на удвоенной частоте GBC и поддерживал до 8 Мб ROM картриджа. А вот MBC7 кроме всего прочего имел в своём составе двухосевой акселерометр ADXL202E и 256 байт энергонезависимой памяти (EEPROM)
Таким образом по моим ощущениям Game Boy заметно отставал от Famicom/NES/Денди по сумме своей технической начинки, а вот Game Boy Color при таком же отставании по некоторым статьям по другим даже заметно превосходил.
4 комментария
У меня никогда не было GBC, но почему-то мне симпатична эта платформа.
Может быть когда-нибудь я что-то сделаю под неё.
LD (HL+), A
LD HL, SP+imm8
ADD SP, imm8
то есть можно забавно адресовать локальные переменные на стеке, и
LD ($FF00+imm8), A
LD ($FF00+C), A
предполагают, что в 127 байтах быстрой памяти можно чего то интересного сохранять для более быстрой работы.
В общем было бы интересно пощупать этого мутанта в продакшене так сказать. :)
но что-то останавливает постоянно :)