Хабрахабр

Архитектура и программирование Fairchild Channel F

«Channel F homebrew would be like programming sprites via hardware jumpers...»
/ chadtower, atariage forum /

В отличии от своих предшественников типа Ping-Pong, Tennis (в том же ряду — советский «Видеоспорт»), у неё было очень существенное отличие — наличие микропроцессора и картриджей с программами. Игровая приставка Fairchild Channel F, также известная как VES, появилась в ноябре 1976 года. До этого игры в приставках реализовывались на жёсткой логике — программа, в современном понимании, там отсутствовала.

За это время было продано более четверти миллиона этих приставок и выпущено около 30-40 игр, некоторые из которых — уже в 2000-х годах. Fairchild Channel F выпускалась вплоть до 1983 года.

Впрочем, появление, менее чем через год, Atari VCS — вытеснило с рынка и Channel F. Говоря о первенстве в плане использования микропроцессора стоит отметить, что RCA Studio II, о которой я рассказывал в прошлой статье, опоздала всего на пару месяцев, но оказалась существенно слабее Channel F, ввиду чего и провалилась по продажам.

В System II и нескольких клонах, типа Saba Videoplay 2 (1979), отличия состояли преимущественно в корпусе, джойстиках (кстати, все они понимали кроме обычных положений ещё и поворот ручки) и количестве микросхем. Развития Fairchild_Channel_F, как такового, не было. Архитектурно всё было практически идентично.

Что же собой представляет Channel F?

Процессор, в традициях эпохи, производился той же компанией Fairchild, что и сама приставка и называется F8 en.wikipedia.org/wiki/Fairchild_F8. Это 8-разрядный процессор 1974 года выпуска, работающий на частоте 1.8МГц (одна инструкция занимает от 1 до 6 тактов).

Микропроцессором его можно назвать, однако, лишь с натяжкой, поскольку состоит этот процессор из двух чипов — вычислительного устройства 3850CPU, содержащего АЛУ, аккумулятор, 64 байта SRAM, логику необходимую для выполнения инструкций, два порта (шины адреса нет!) и 3851PSU (Programmable Storage Unit), который содержит 1кб ПЗУ (используется для BIOS), указатель команд, схемы адресации памяти, прерываний, таймер (прерывания и таймер конкретно в Channel F не используются).

Впрочем, это уже отдельная история. Позднее появился чип F3859, который объединял CPU и PSU на одном кристалле и Mostek 3870 — чуть усовершенствованная версия, выпускавшаяся вплоть до 1990-х. Интересно, что иногда F8 называют микроконтроллером (в частности, из-за наличия таймера и портов) и упоминают как предка семейства Intel MCS-48 (8048).

В целом удивительно, что об этом процессоре очень мало информации. Помимо Channel F, процессор F8 использовался также в компьютере VideoBrain и в шахматном компьютере CompuChess. Такое впечатление, что этот процессор больше нигде не использовали, что кажется невероятным (учитывая, что его продвинутые версии выпускались довольно долго). За исключением пары описаний от Fairchild и исходников нескольких игр и примеров конкретно для Channel F — больше ничего толком и нет. Предположу, что все остальные устройства, где он применялся — были военного назначения.

Наводят на размышления набор и функции его регистров. Теперь немного об особенностях процессора. они рассчитаны на компиляторы. Думаю, это одна из самых замороченных из существовавших в то время архитектур (наверное современные могут потягаться, но сравнение будет некорректным, т.к. С F8 же работали живые люди).

Помимо 8-разрядного аккумулятора A и регистра флагов W (I,O,Z,C,S), имеется ещё «scratchpad» — 64 восьмиразрядные ячейки с различными функциями. Регистров в процессоре много.

Обратите внимание, что буква R не указывается — пишется лишь номер регистра. Первые девять ячеек используются как регистры общего назначения R0-R8 в командах типа 'lr a, 7" (загрузить содержимое регистра R7 в аккумулятор). Скажем, в случае с инструкцией lr чисел просто не может быть. Что именно имеется ввиду, число или номер — должно быть ясно из контекста. А если это, допустим, «li 7» (загрузить константу в аккумулятор), то там это точно число, а не регистр.

Ячейки 9, 10-11 (H), 12-13 (K), 14-15 (Q) предназначены для сохранения других регистров в разных, типа вызова подпрограмм, ситуациях.

Регистры R16-R63 доступны только через специальный индексный регистр ISAR (Indirect Scratchpad Address Register), как шесть восьмибайтных буферов, которые образуются из ячеек 16-23, 24-31, 32-39, 40-47, 48-55, 56-63.

При увеличении или уменьшении ISAR на единицу, затрагиваются только младшие 3 разряда — т.е. Шестиразрядный ISAR разделён на две части по 3 разряда. адресация осуществляется в пределах одного из шести упомянутых восьмибайтных буферов (конец буфера при этом можно отследить специальной инструкций условного перехода br7).

clr ; 0 -> A ; set ISAR to full address Ox27 (octal 27) lisu 2 ; buffer N2 lisl 7 ; index within buffer N2 loop: lr d,a ; A-> buffer N2[index], than decrement ISAR (27, 26, 25, ... ) br7 loop ; go further if low part of ISAR contains 7 (end of buffer N2). if not, go loop again

В данном случае «d» в инструкции «lr d,a» — это не название регистра, а признак того, что ISAR надо будет уменьшить («i» — увеличивать, «s» — оставлять неизменным).

Есть и другой способ косвенной адресации — через регистр DC0 (Data Counter):

dci data_addr ; data_addr -> DC0 lm ; [data_addr] -> A, DC0 + 1 -> DC0 ...
data_addr: db 0,1,2,3,...

Если нужно переносить данные из одной области памяти в другую, то дополнительно используется команда xdc, которая меняет местами содержимое регистров DC0 и DC1. Т.е. читаем из адреса на который указывает DC0, потом делаем xdc и пишем по адресу, в который указывает теперь уже DC1. Потом снова xdc, и т.д. Т.е. DC1 — это некий теневой регистр для хранения копии DC0. Напрямую к нему обращаться (кроме как командной xdc) — нельзя.

Приведённые примеры иллюстрируют лишь часть возможностей в плане адресации, на самом деле их больше.

В Channel F порты используются для вывода графики, звука и чтения состояния джойстика. Также в F8 имеются четыре порта — 0,1,4,5, в которые можно писать командой out и читать командой in.

Отмечу лишь, что выбор регистров над которыми можно производить действия очень ограничен, поэтому код быстро разрастается из-за необходимости постоянно перемещать значения туда-сюда. На прочих инструкциях типа арифметики, переходов и прочего особо останавливаться не буду — они достаточно стандартны.

Инструкции уменьшения (ds) и увеличения (inc) на единицу — несимметричны. Отсутствует вычитание (есть только сложение). ds работает только с регистрами r0-r8, inc — только с аккумулятором.

Безусловный переход портит аккумулятор.

Пример обычного цикла:

li 25 ; (r4) number of iterations lr 4,a
next: ds 4 ; r4-- bnz next ; until r4 == 0

Поскольку в F8 нет аппаратного стека, имеются сложности с вложенными вызовами подпрограмм. Обычный вызов и возврат выглядит так:

; ...code pi sub ; Pushes address of next instruction to PC1 ; address of sub is stored in PC0 (jump to subroutine) ; ...code continues sub: ; ... often used code pop ; Move return address from PC1 to PC0

Здесь PC0 — это обычный указатель команд. PC1 — так называемый «регистр стека». К стеку он отношения не имеет, просто в него сохраняется PC0 при вызове подпрограммы.

Если из sub вызывается ещё одна подпрограмма, адрес возврата затирается и нужно усложнять (сохранять первый адрес возврата в регистр K):

prog: ; ...do something... pi sub1 ; Address of next instruction stored in PC1 ; sub1 is stored in PC0 (jump to subroutine) ; ...do more... sub1: lr k,p ; Copy PC1 to K, original jump address to K ; ...do something... pi sub2 ; Pushes address of next instruction to PC1 ; sub1 is stored in PC0 (jump to subroutine) ; ...do more... pk ; Store address of next instruction in PC1 ; Copy value in K to PC0 (jump back to main) sub2: ; ...do something... pop ; Move return address from PC1 to PC0

Если же нужен ещё и третий уровень вложенности, то всё становится совсем грустно (в BIOS для этого есть даже две специальных подпрограммы — pushk и popk).

Конечно, если нет жёстких ограничений на размер кода. Общая рекомендация — стараться заменять вызовы подпрограмм макросами.

При необходимости он реализуется программно — через ISAR и буферы. Аппаратного стека в F8 нет.

Запись в ОЗУ не очень актуальна (за неимением такового), но выглядит так:

li $FF ; set value dci $3800 ; set target address st ; write

Хотя это прозвучит несколько странно, но ОЗУ в Fairchild Channel F нет. Имеющиеся два килобайта видеопамяти (MK4027) не отображаются в адресное пространство, да и вообще — недоступны для чтения, запись в них осуществляется через порты. Регистры же микропроцессора, хоть их аж целых 64, за ОЗУ считать вряд ли корректно.

Кроме того есть, встроенное в микросхему процессора, ПЗУ BIOS объемом 1кб, содержащее простую игру типа Tennis, пару-тройку полезных подпрограмм и образы нескольких символов. Исполняемая программа хранится в сменных ROM картриджах, чаще всего имеющих ёмкость 2кб (некоторые современные игры используют 3к, 4к и 5к картриджи).

В адресном пространстве BIOS расположен с $0000 по $07ff, ПЗУ картриджа — с $0800.

Графические возможности Channel F весьма примитивны, поскольку видеоконтроллера в виде отдельного чипа там просто нет — всё реализовано на обычной логике типа сдвиговых регистров, вентилей и операционных усилителей. Довольно необычно, что в разных источниках упоминается разное разрешение, причём вариантов — множество. Дело в том, что имеющиеся 2 килобайта видеопамяти подрузамевают разрешение 128 x 64, но в реальности это далеко не так. Во-первых, многое зависит от того, какая область формируемого изображения на данном конкретном телевизоре видна (из-за чего первые 4 столбца официально вообще не используются). Во-вторых, последние несколько столбцов используются под палитру. В третьих, часть памяти не используется вообще.

В итоге — фактическое разрешение можно примерно оценить как 95 x 58 пикселов при 8 цветах (что, впрочем, сильно лучше, чем RCA Studio II с её черно-белыми 64x32).

Практических различий, как пишут, нет (количество строк тоже самое). Большинство приставок выпущено в NTSC варианте, однако PAL тоже существуют.

Хотя общее количество отображаемых цветов — 8, однако в пределах одной строки могут быть отображены лишь четыре (можно условно назвать это палитрой). По существу, такое простое железо позволяет лишь рисовать на экране точки. Изменение палитры производится, соответственно, рисованием пикселов в этих двух столбцах. Палитра устанавливается индивидуально для каждой строки, причём хранится в довольно странном месте — в столбцах 125 и 126 каждой из строк (которые в любом случае находятся за пределами видимой области).

(жёлтым выделены область видеопамяти, которая фактически видна на экране и область, где устанавливается палитра)

При этом указывается цвет, столбец, строка: Как уже отмечалось выше, записать данные в VRAM можно лишь одним способом — через порты.

X записывается в порт 4, Y в порт 5, цвет в порт 1, после чего записью константы в порт 0 осуществляется передача данных:

; set color (2 bit per pixel) li $00 ; color ($00 = green, $40 = red, $80 = blue, $C0 = background) outs 1 li 104 ; X com outs 4 ; set the row li 61 ; Y com outs 5 ; transfer data to VRAM li $60 outs 0 li $50 outs 0 ; wait for update lis 6
delay: ai $ff bnz delay

Задержка требуется, чтобы всё успело записаться, иначе следующая точка может не нарисоваться. В официальной версии к столбцу и строке ещё добавляется 4 (для простоты опущено)

Важно отметить, что к чрезвычайно медленному обновлению экрана добавляется ещё и то, что нет способа подождать обратного хода луча по кадру. Заполнить весь экран точками (в цикле) занимает таким образом порядка секунды. Соответственно, даже небольшая перерисовка неизбежно сопровождается мерцанием.

Строго говоря, для каждой строки есть два режима — чёрно-белый (когда фон чёрный, а передний план — только белый, какой бы пиксел не нарисовали) и цветной. Теперь о палитре.

Плюс один из трёх цветов фона — серый, светло-синий, светло-зелёный. В цветном режиме цветов переднего плана всегда три — красный, зелёный, голубой (rgb).

Для установки палитры в столбцы 125 и 126 нужно записать следующие значения:

x=125 x=126 palette
---------------------------------------------------------------------
00 00 COLOR: rgb, light green bg
00 ff COLOR: rgb, light blue bg
ff 00 COLOR: rgb, gray bg
ff ff B/W: www, black bg

В типовых играх обычно поступают так: сначала задают некий общий фон, для чего можно воспользоваться готовой процедурой BIOS:

li $c6 ; $21 - b/w palette, fill with black. $c6 - color palette, fill with gray lr 3, A pi clrscrn ; clrscrn BIOS call

Затем, при необходимости, делают полосу с чёрно-белой палитрой (к примеру, для вывода счёта игры белыми цифрами на чёрном фоне)

И далее уже ставят пикселы одного из трёх цветов (если фон серый, соответственно — r,g,b). В итоге, цветов-то конечно 8, но поставить в конкретное место точку произвольного цвета так вот запросто — нельзя. Вот набор картинок, которые дают некоторое представление о цветах и их сочетаниях.

Из полезных подпрограмм, в BIOS есть вывод символов. Собственно, на этом вся графика, как таковая, заканчивается — всё, что хочется делать, делается рисованием пикселов, причём вручную. Однако, из соображений экономии места, в ПЗУ имеются лишь изображения цифр и отдельных знаков, размером 5x8 точек:

Тем не менее, процедура их вывода полезна и может быть использована следующим образом:

li 25 ; column lr 1,a li 25 ; row lr 2,a li %11000000 ; e.g. $c0 - green "0" lr 0,a ; a -> r0 pi drawchar ; call subroutine

В вышеприведённом варианте старшие два разряда в регистре r0 определяют цвет (10 — red, 11 — green, 01 — blue, 00 — transparent), остальные — порядковый номер символа ( 0 1 2 3 4 5 6 7 8 9 G? T SPACE M X BLOCK: — center|| left|| ` ), начиная с нуля. В регистры r1 и r2 помещаются, соответственно, столбец и строка.

Более практичный вариант, чтобы не думать о разрядах:

li 20 ; x lr 1,a ; a -> r1 li 10 ; y lr 2,a ; a -> r2 li $40 ; char color in bits 6,7: $80 (%10000000) - red, $c0 (%11000000) - green, $40 (%01000000) - blue, $00 (%00000000) - transparent oi 1 ; index of char ( e.g. 3 for "3" ) lr 0,a ; combined color + char index -> r0 pi drawchar ; call subroutine

Чтобы немного прочувствовать платформу, я написал для конкурса tiny intro на Chaos Constructions'2019 256-байтное интро. Ничего особенного, но обратите внимание, для чего там использовано построчное изменение палитры. Ползущая вертикальная полоска как бы подсвечивает строчку, которая под ней находится, временно заменяя весь фон на чёрный, а все пикселы на белые. Поскольку для этого не надо перезаписывать сами пикселы (и не надо их потом за собой восстанавливать), можно делать такую «подсветку» очень быстрой и без мерцаний.

Поскольку букв «C» нет в BIOS, использовано наложение друг на друга букв GG и цифры 1, чтобы получить инверсные «CC». Второй момент — буквы «CC».

Со звуком всё плохо. Официально есть три звука — 120Гц, 500 Гц и 1 КГц. Фактически, получить что-то кроме щелчков и сдавленного писка — проблематично. Вдобавок, говорят что между PAL и NTSC машинами, равно как и между старыми и новыми версиями звук ещё и отличается. Впрочем, для типовых игр — вполне достаточно. Включается и выключается звук через порты:

li %01000000 ; 1khz beep $40 outs 5 li %10000000 ; 500hz beep $80 outs 5 li %11000000 ; 120hz beep $c0 outs 5 ; some pause clr outs 5 ; turn off sound

Народ развлекался даже проигрыванием музыки. Лучшие образцы отдалённо напоминают PC-Speaker. Правда, практического смысла всё равно нет — все ресурсы процессора уходят на музыку, ничего особо уже не порисуешь.
В настоящее время существует готовая подборка необходимого софта под названием «Development Pack».

Всё это без проблем работает как минимум под Windows 7. Туда входит ассемблер DASM, дизассемблер, эмулятор MESS (с отладчиком).

Параметры для ассемблирования и запуска:

dasm.exe test.asm -f3 -otest.bin
messd channelf -cartridge %cartPath%\test.bin -w -effect sharp -r 640x480 -ka

Эмулятор вполне неплох, хотя отладчик там чрезвычайно странный. Новую версию MAME/MESS мне настроить сходу не удалось (заметил, что настроить MAME под непопулярную платформу, которую он, якобы, поддерживает — каждый раз нетривиальная задача).

В эмуляторе предполагается, что разрешение видимой области соответствует minx=5,minY=5, maxX=105,maxY=61

По моей просьбе tnt23 сделал специальный картридж (Алексей Новожилов напечатал к нему корпус), в который вставляется EEPROM 28C16A. Поскольку нет никакого удовольствия что-то писать под эмулятор, не опробовав результат вживую, пришлось решать проблему с эмулятором ПЗУ. В итоге, появилась возможность, запрограммировав EEPROM, смотреть, как выглядит код на живой машинке. Из-за особенностей адресации F8, пришлось ещё покупать на eBay древнюю микросхему Fairchild 3853.

Кроме того, tnt23 приделал к Channel F S-Video выход (штатно её можно было подключить лишь к телевизору через антенный вход), что сильно улучшило качество изображения и цветопередачу.

Рассказ про Fairchild Channel F:

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть