Обзор архитектуры 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 комментария

avatar
Спасибо за разбор; лично мне было особенно полезно увидеть карту памяти.
У меня никогда не было GBC, но почему-то мне симпатична эта платформа.
Может быть когда-нибудь я что-то сделаю под неё.
avatar
Да у меня у самого руки зачесались чего нибудь сделать под это железо пока читал спеки для статьи, но главным образом из-за немного экзотичного процессора — вроде и наследник Z80/i8080, но от Z80 по большей части только JR (включая условные переходы) и битовые инструкции префикса $CB, а от i8080 даже флаги P и M отвалились. В то же время появились такие вкусности как
LD (HL+), A
LD HL, SP+imm8
ADD SP, imm8
то есть можно забавно адресовать локальные переменные на стеке, и
LD ($FF00+imm8), A
LD ($FF00+C), A
предполагают, что в 127 байтах быстрой памяти можно чего то интересного сохранять для более быстрой работы.
В общем было бы интересно пощупать этого мутанта в продакшене так сказать. :)
avatar
Да, мы уже обсуждали несколько раз, что очень многие привычные для z80 оптимизации больше не оптимальны на GBC. Нужно переосмысливать практически каждую стандартную задачу.
avatar
давно брожу кругами возле геймбоя в плане порта игры с него.
но что-то останавливает постоянно :)
  • VBI
  • +1
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.