Диалектика разрядности

На размышления по теме меня сподвигла статья aa-dav под заголовком 8/16-битный компьютер мечты (процессор Simpleton). Статья, без дураков, замечательная. Понравилась своим рассудительным изложением материала. Мой особенный интерес вызвали мысли автора, оказавшись близкими по духу. Множество раз размышляя на подобную тему “с карандашом в руках” очень похоже изводил себя вопросами озвученными автором. Процесс созидания чего бы то ни было итеративный, наблюдения и заключения меняются по времени, в силу приобретения дополнительного опыта и знаний. Иногда достаточно попробовать сложившуюся на бумаге архитектуру “на зуб” написав в ней десяток другой строк кода и, казалось бы, с железобетонных утверждений приходится слезать. Неизменным всегда остается то, что любой вопрос, касающийся архитектуры, содержит в себе массу противоречий. По большому счету выбор решения в итоге заключается в том какие же из них являются меньшим злом. Так и в случае разрядности появляется масса вопросов и проблем. Для меня в итоге, при решении вопросов разрядности в разработке архитектуры (архитектуры мечты), потеря байта как единицы оперативной информации, оказалась болезненной.

Главная неразрешимая проблема состоит в том что адресное пространство для 8-битного или 16-битного вычислительного устройства ограничено одним и тем же числом ячеек — 65536. Я не рассматриваю сейчас способы преодоления данного ограничения, важно что они есть и они достаточно разнообразны, но при этом неизменны для платформ любой разрядности. А вот что действительно важно это “узость” имеющегося пространства в 64К слов. Особенно для Фон Неймановской архитектуры, когда код и данные зависят от одного и того же 64К слов адресного пространства.
Выбор системы команд RISC способен это противоречие только усугубить. Эволюцию размена простоты декодирования инструкции, а значит и частотного потенциала вычислительного ядра, на плотность машинного кода можно прочувствовать на примере системы команд встраиваемых ядер ARM. Изначально довольно изысканная по своей непротиворечивости и складности 32-битная инструкция довольно быстро скатилась к многочисленной разновидности THUMB. Переход к THUMB и уплотнению инструкции, ломает внутреннюю красоту, описательность и простоту первичной системы команд. Такое можно стерпеть, при условии, что ассемблер и оптимизация это удел компиляторов. В случае появления THUMB в АРМ первую скрипку играет ограниченность ресурсов в накристальных системах, прежде всего в ПЗУ и ОЗУ, а использование кросс средств и ЯВУ вопрос давно решенный. Допустимо ли это для “архитектуры мечты”? В некотором смысле система команд PDP-11 произведение искусства, а программирование в ассемблере для него, многим доставляет эстетическое удовольствие. Возможно ли этим пренебречь?
В любой архитектуре вычислительного ядра отказаться от индеректной константы крайне сложно. В RISC такая боль непринужденно вылезая из головы проектировщика декодера, превращается в мигрень программиста. Посудите сами, как одной 16 битной инструкцией, загрузить 16 битную же константу? Можно грузить слова из памяти по укороченному смещению или адресу, но на индиректную загрузку константы это совсем не похоже. Можно грузить что то меньшее 16 бит и операцию расширяющую это число. Что называется оба метода хуже — в первом случае рыхлый код становится еще рыхлее на адрес и данные к нему, а во втором случае в один прекрасный момент понадобится константа для получения которой потребуются затратить две инструкции. Впрочем, можно пойти на игнорирование принципов RISC и допустить команду с операндом в следующем слове (прощай мигрень программиста), но плотность кода все еще страдает. Как только начнется адресная арифметика со смещениями чьи значения по разрядности находятся в районе 5-4 бит, а для адреса в 64К трудно представить себе смещения между соседними элементами массивов больше чем 16 байт, так рыхлый код не заставит себя ждать. И это пока только код, нужно ли говорить какими аппетитами на адресное пространство будут обладать сами массивы с 16-битной грануляцией данных в них? Достаточно показательно, в качестве иллюстрации, хранение PCM сэмпла, да и вообще любых данных, чье сжатие основанное на учете приращения к предыдущему отсчету. Конечно всегда можно воспользоваться практикой сдвигов и маскировок битовых полей, но стоит ли это отказа от байта?
В системе команд почти всегда имеется группа инструкций применяющихся наиболее часто, но в случае с RISC где инструкции одинаковы по разрядности, использовать такую особенность на увеличение плотности бессмысленно. Другое дело CISC. Взамен CISC одаряет разработчика ядра сногсшибательной головной болью. Нерегулярная система команд требует совсем непростого автомата декодера, секвенсера и микрокода. Впрочем можно и без микрокода. А если уж так, то почему бы не усугубить, ввести очень короткую команду размером в байт, в которую засунуть наиболее часто используемые инструкции? Все остальные пойдут через префиксное расширение, которым могут являться бессмысленные инструкции, например пересылка из одного и того же регистра MOV Ri,Ri.

Чем короче инструкция и плотней код, тем более сложные и пространные программы можно писать для 64К не прибегая к оверлеям. Идя по этой дороге можно добраться до разновидности OISC — с инструкцией в 4 бита и арифметическим стеком, где мы снова окажемся на развилке противоречий. Стек заменяя многочисленные регистры общего назначения (в архитектуре мечты их может быть и более 16) расшивает узкое горло полей для их представительства в машинной инструкции, взамен награждает неторопливостью при вычислениях на нем. Дарит реверсивную стековую нотацию и сильно упрощает разбор выражений, однако лишает оптимизации по скорости вычисления.

Еще один шаг по тропе OISC и мы в области тьмы :) в области архитектуры ТТА (transport triggered architecture) состоящей из действительно одной инструкции, инструкции перемещения MOV. Команда шириной 8-бит тут к месту.
8-битная  инструкция ТТА
список источников и приемников
Использовать индиректно 8-битную константу пересылкой из источника CODE8, #200 -> R0, где #200 это 8-битная константа следующая за 8-битной инструкцией. 16-битную, меняем источник на CODE16, #2000h -> IP. Само собой IP при чтении с CODE8/CODE16 смещается на 1/2 байта, и указывает на следующую за константой инструкцию к моменту исполнения. Кодируя 16 источников и 16 приемников, легко и непринужденно можно их префиксировать не меняя основы, например так #1 -> RPAGE. Где RPAGE указатель страниц регистров.
Максимально простой декодер, нет микрокода. Внутренняя архитектура — мультиплексор данных из источников, управляемый полем машинного слова “SRC” и дешифратор записи управляемый полем “DST”. Быстра. Взамен… Совсем не простое кодирование, ведь инструкция сложения ADD #4, R0 превращается в набор пересылок: #4 -> ALU.A, R0 -> ALU.B, ADD -> R0. Опять здравствуй, хоть и небольшая, рыхлость кода. Продемонстрирую некоторые варианты использования:

Условное исполнение:
; псевдокод
; ADD = R0 + 8;
; IF ADD == 0 THEN R0 = ADD;
; IF ADD != 0 THEN goto M1;

#8 -> ALU.A
R0 -> ALU.B
ADD -> IFZ#     ; передача Флагов сумматора в модуль условного исполнения по Z-флагу
ADD -> R0	; Z флаг подключен на разрешение записи в приемник, действует в течении текущей инструкции
ADD -> IFNZ#    ; передача Флагов сумматора в модуль условного исполнения по Z-флагу
IP  -> R0	; инверсия Z флага подключена на разрешение записи в приемник, действует в течении текущей инструкции


Авто инкремент и декремент адреса:

; addr = 2000
; mem[addr]  = R0 + mem[addr]; 
; addr += 100;

#100  -> OFFS
#2000 -> ADDR
DATA  -> ALU.A
R0    -> ALU.B
ADD   -> DATA+	; постинкремент адреса на OFFS


P.S. Пожалуй моделировать такой процессор в Digital, это идея для следующей статьи. :)

12 комментариев

avatar
Недавно были опубликованы исходники порта Doom на SNES созданного Рэнди Линденом считай что своими силами (id software вообще поначалу не была в курсе): gbx.ru/?showtopic=142214
По ссылке я новость написал с минимумом технических подробностей (но история сама, имхо, интересная), но сам же заинтересовался чипом SuperFX — это 16-битный процессор с архитектурой созданной под влиянием идей RISC который на 16-битной SNES позволял эффективно рисовать 3Д-графику — у ЦП SNES с этим есть ряд проблем. А у SuperFX прям как у Z80Next есть команда PutPixel и много прочего. Чип SuperFX впаивался в картридж SNES аки маппер в денди и рулил и педалил 3Д-графоний на 16 битах.

Думаю краткий обзор архитектуры SuperFX станет предметом моей следующей статьи, но тут распишу идею из него которая мне тоже понравилась и которую в таком виде нигде не видел.
Опкоды там как правило однобайтовые, но бывают префиксы.
Шестнадцать 16-битных регистров во многих инструкциях кодируются в четырёх битах как в статье: 0xAB, где A — код инструкции, а B — код регистра. Из этого правила есть немало исключений, но рассмотрим, например, операцию сложения:

ADD R5

В своей первичной форме она рассматривает R0 как аккумулятор и к нему прибавляет указанный в инструкции регистр R5. Т.е. берёт аккумулятор и R5, складывает их и результат записывает обратно в аккумулятор. Окей.
Однако можно временно на одну эффективную инструкцию сменить что будет являться приёмником операции — это делает инструкция TO:

TO R4
ADD R5

Здесь эффект будет таков, что сумма R0 и R5 будет записана в R4. После выполнения ADD приёмник по умолчанию опять станет R0.
Точно так же есть однобайтовая инструкция FROM которая так же меняет временно какой регистр будет служить первым операндом:

FROM R3
TO R4
ADD R5

Выполнит следующее: сумма R5 и R3 запишется в R4, а аккумулятор окажется вообще не при делах.
Для еще больше краткости есть однобайтовая же инструкция WITH которая сразу выставляет и FROM и TO в один и тот же указанный регистр.
Но и это еще не всё — WITH взводит еще один внутренний флаг который модифицирует поведение инструкций FROM и TO если они встречаются после неё. В SuperFX отсутствует специализированная команда MOVE, зато если после WITH сразу же идёт FROM, то происходит копирование из регистра указанного во FROM в регистре запомненном в WITH. И наоборот — инструкция TO скопирует из регистра запомненного по FROM в регистр указанный в себе.

FROM Rn ; код 0xBn - выставляет Sreg в n
TO Rn   ; код 0x1n - выставляет Dreg в n
WITH Rn ; код 0x2n - выставляет и Sreg и Dreg в n

Довольно забавная система команд — байтово-ориентированная, но постоянно на каких то полухаках, префиксах и сменах текущих целей и назначений.
Каждый боролся за плотность кода как мог. :)))
avatar
А вот очень хороший пример! Действительно либо адрес в 16бит и борьба за каждый байт (ЕМНИП, Стив Возняк неплохо это показал запихнув в 256 байт монитор для Apple-I), либо 24/32 бита — «счастье всем и никто не уйдет обиженным». Адресного пространства так много что ARM выделяет в нем области для манипуляцией битами. :)

P.S. Статья про SuperFX будет крайне интересна!
  • SAA
  • +1
avatar
Стив Возняк неплохо это показал запихнув в 256 байт монитор для Apple-I
О! Спасибо за напоминание про плотность кода и виртуальный процессор Возняка — у меня ведь есть новый материал на эту тему. :)
avatar
Монитор попроще чем виртуальная машина Возняка. Ваше исследование почитаю с удовольствием! :)
avatar
P.S.
И еще по вашему процессору я не понял как делается CALL — LR вроде есть, но непонятно что в него записывать.
avatar
Хорошее замечание, включу в пример, часть кода. LINK — это регистр в который попадает значение IP для следующей инструкции, в случае записи в IP. То есть в случае с переходом (записью адреса перехода в IP) следующая инструкция на которую будет указывать IP это туда куда надо вернутся.

#label -> IP  ; Переход на адрес label и запись адреса следующей инструкции в LINK
R1 -> ALU.A
.........
label:
..............
LINK -> IP         ; возврат на команду R1 -> ALU.A


Можно сохранять LINK как и в случае с LR в ARM либо в ОЗУ, либо один из регистров общего назначения. Допустим R4 это стек.

; push LINK
R4 -> ADDR
LINK -> DATA    ; LINK 16 битный регистр, будет записан в память как слово за два такта
R4 -> ALU.A
#2 -> ALU.B
ADD -> R4       ; R4 += 2
; pop LINK
R4 -> ALU.A
#-2 -> ALU.B
ADD -> R4
ADD -> ADDR
DATA -> IP       ;  IP - 16-битный регистр, буден считан за два такта
avatar
Мне представляется, что [естественное] ограничение в 64к для восьмибитных платформ, по теперешним временам — это плюс, а не минус. Это примерно из той же серии, что ограничения в 256b, 4k, 640k и т.д. в intro/demo конкурсах. Если не хватает 64к, есть абсолютно доступные PC, Mac, смартфоны.
К слову — даже если посмотреть на Амигу, снятие ограничений пошло, на мой взгляд, ей непосредственно во вред. В среднем качество софта (демо и интро в первую очередь) сильно ниже, чем таковое у 8-битных платформ (того же C64).
Потому что адекватный человек (даже, хе-хе, демомейкер) не будет САМ СЕБЕ создавать сложности — он будет полностью использовать те возможности, которые ему даёт платформа. Это касается и памяти и отсутствий ограничения на число цветов на знакоместо и лёгкость проигрывания сэмплов и пр. Счастье, кстати, что REU для C64 не были широко доступны в своё время и не стали стандартом. Сейчас иногда делают демки для REU (ram expansion unit, до 512K обычно) но, как правило, бросается в глаза, что они написаны просто чтобы как-то использовать REU. Ну там картинку большего размера покрутить, к примеру. Или не возиться с непрерывной подгрузкой с диска, а загрузить сразу всё в буфер.
  • frog
  • +1
avatar
Трудно не согласится! Любое ограничение играет в пользу творческого процесса. Можно по взрослым платформам лицезреть как доступность ресурсов работает против них… черт возьми снова законы диалектики :) Было бы интересно увидеть размышления на тему полезного ограничения в разрешающей способности видеоадаптеров для платформы 64К. Есть ли золотая середина?
avatar
В плане разрешающей способности — я думаю, что не столько важно количество точек, сколько цветовое разрешение. Скажем, если брать тот же C64, при формальном разрешении 320x200, фактически используемое в большинстве картинок — обычно 160x200 (не считая спрайтов) и никого это особо не беспокоит, поскольку в 160x200 с цветами довольно неплохо (4 цвета на знакоместо из 16-ти доступных, в отличии от 320x200, где вроде два из 16). При этом, если вспомнить платформы с разрешением 320x200 и честным «цвет на точку» (например, PC EGA), то уровень графики там в среднем невысок. Другими словами, это уже слишком много. Я думаю, что идеалом было бы что-то типа 256x256 квадратных точек при четырёх цветах на знакоместо из 16 доступных (т.е. слева и справа рамки, картинка «высокая»). 256 — потому что в байт влезает, в отличии от 320. В жизни такое разрешение не использовалось, поскольку неоптимально для текстовых применений — практически тот же объём памяти выгоднее использовать для 320x200, чтобы получить хотя бы 40 столбцов в текстовых редакторах.
avatar
Да с текстом претензии к видеоадаптерам возрастают. Если предположить что 256х256 это всего лишь массив для отображения на экране, тогда можно пойти чуть дальше и представить, что для его отображения используется 2х2 точки в растре разрешением 800х600. Ну а раз есть такое разрешение то можно сделать еще один шажок с наложением текстового слоя с знакоместом 8х8 как 80х60 из области ~5Кб но точкой для него 1х1. Два растра точно конечно не совмещаются, но в этом может быть и нет необходимости. В принципе добиться включения слоя с текстом в слой с графикой можно меняя размеры знакоместа.
avatar
На 8 битах, имхо, оптимум — 256x192 точек как в спектруме. Ну во первых 256x256 это перебор с точки зрения стандартного соотношения сторон картинки 4:3. А 256x192 это как раз 4:3. Во вторых насколько я понял телевизионный сигнал что NTSC что PAL имеет предел эффективного разрешения по цвету в строке и потому о 640 например уже речи быть не может. Вроде бы предел это где то от ~400 что ли, но не скажу тут точно.

Однако действительно вопрос глубже — как «выдавить» при этом нормальную цветность.
Монохром как мы знаем в спектруме том же отнимает 6144 байта и раскраска знакоместами порождает клэшинг при попытке графонить графоний. Да и скроллинг в играх, а давайте уже не лукавить — игры это для нас очень важно — просто убивает и производительность и всё на свете — апдейтить 6Кб с приличным фпс — трудно для восьми бит.

Поэтому, имхо, оптимальность картинки в целом надо искать на пересечении текста и консольных принципов тайловой графики, причём одно плавно вытекает из другого, ибо не зря в Famicom/NES/Денди память тайлов офдоки называют CHR (character) ROM/RAM, ибо технически это действительно 256-значные символы квадратно-гнездовой сетки 8x8.
Однако Famicom, имхо, пример как немного перемудрили и ради призрачной экономии зарезали немало хорошего.
Главное — это аппаратный скроллинг по как минимум двум страницам видеотекста бесшовно перетекающий с прокрутной одна в другую.
Причём вот именно страницы текста имеют разрешение 32x32 символа, т.е. по вертикали виртуальное разрешение 256 пикселей, но оно используется чтобы организовывать бесшовный скроллинг с «проворотом» между границ.
Две экранных области текста пристыковани друг к другу слева-направо, над ними как бы «летает» видеоокно 256x192 на кинескоп управляемое одним 8-битным регистром прокрутки по вертикали и одним 9-битным регистром прокрутки по горизонтали (512 эффективных пикселей).
Но конечно чтобы игры на таком тексте делать еще нужны аппаратные спрайты.
В общем по лекалам денди я бы сделал так:
4Кб — память A изображений 256 тайлов 8x8 2bpp
4Кб — память B изображений 256 тайлов 8x8 2bpp
(одна область формирует тайлы для фона, другая — для спрайтов)
1Кб — текстовая видеостраница X 32x32 символа
1Кб — текстовая видеостраница Y 32x32 символа
(если произвольно скроллить, то видеостраницы пристыкованы прямоугольником «XY»)
256 байт — цветовые атрибуты видеостраницы X
256 байт — цветовые атрибуты видеостраницы Y
(2 бита на знакоместо/тайл выбирают ему палитру из 4-х возможных, в одном байте описывается 4 соседних символа/тайла квадратиком, что удобно для концепции метатайлов).
Но с таким потайловым и удобным для скроллинга фоном нужно конечно еще спрайты. Именно для них нужна память изображений Б ибо одной там реально маловато.
А чтобы всякие Elite делать нужно иметь второй видеорежим где первые 8Кб можно превратить в монохром, т.е. уже чистую графику.
avatar
стандарты разложения растров
Как Вы и отметили растра 4:3, получить количество точек по горизонтали можно умножив кол-во строк на 4/3. Для PAL и SECAM это 800 элементов в строке. Не спорю конечно кинескоп и качество исполнения видеотракта дадут свои ограничения, на эти возможности.

У меня есть несколько вопросов, буду признателен за ответы.
1. Почему именно телевизионный растр?
2. Чем вызваны Ваши предпочтения накладываемые на видеоадаптер по разрешению и цветности, только ли ограничениями ТВ растра?
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.