Изначально всё проектировалось, чтобы ПЗУ с программой заменить на макетные платы и шестнадцатеричные тумблеры (ссылка) в количестве от 512 штук (под табло механического дампа на ввод программы из 256 байтов).
Очень любопытный вопрос!
С одной стороны, под NOP'ом может использоваться любая операция:Но, это - только в изначальном варианте. Так как сейчас работаю над "строгим дешифратором команд", который не допускает повторения однотипных инструкций:
- коды A0..D9 (REG A0..D0) для активации левого операнда-приёмника могут повторяться много раз
- коды AA..DD (ARG A,A..D,D) для определения АЛУ-операндов могут повторяться много раз
- коды AE..DF (CLC/CMC и т.д.) могут повторяться много раз
То есть, процессор хоть и не "нейросеть", но вполне способен в таком контексте выполнять "скрытые операции".
- последовательность операций REG A7 и REG A5 составляются в одну - REG A75 (нужен регистровый файл побольше)
- последовательность операций CMC и CLC, так как CMC перед CLC бессмысленна, образует новую операцию
- последовательность операций CMC и CMC, так как двойная CMC бессмысленна, образует новую операцию
Выше я привёл пример же:То есть, CMC с префиксом - условный переход.
- код "CF" означал "NOT CF" (CMC), а с префиксом "54 CF" означает "JCF [D5+4]"
Так как имеется 32 INT'а (коды E0..FF), в подпрограммах (в JavaScript-эмуляторе с "Гонками") реализованы "нормальные" JC/JNC на абсолютные адреса (или относительные).
МАРГИНАЛЫ
В знакомой всем IA-32 / x86 имеются механизмы вычисления абсолютного адреса:Инженеры Intel переложили всю ответственность за использование подобных указателей на программиста. Хотя, можно было бы подправить их интерпретацию:
- LEA EBX,[EBX+EBX*2+3] - эквивалент EBX += EBX * 2 + 3
- ADD EBX,[EBX+EBX*2+3] - а вот здесь такой указатель не сработает и выдаст исключение
В таких случаях, так как "ADD EBX,[EBX+EBX*2]" использует адресацию "из ряда вон!", условно она стала обозначаться как "маргинальная".
- ADD EBX,[EBX+EBX*2+3] мог бы работать как ADD EBX,(EBX+EBX*2+3) - EBX += EBX*3+3
- ADD [EBX+EBX*2+3],EBX - вот тут проблема, так как нельзя выполнить как EBX*3+3 += EBX
Также в x86 инструкцию можно составить искусственно и так:
Программе это ничего путного не даст, но внутреннему отладчику исключений может нести некую закодированную информацию (в зависимости от порядка префиксов).
- MOV EAX,CS: DS: ES: FS: GS:[EBX] - указано целых пять префиксов, четыре из которых - игнорируются (маргинальные префиксы?)
И я много внимания уделил механизмам отлова "маргиналов", чтобы исключить вообще такие неприятные ситуации.
То есть, из таблицы выше, вот такие примеры:То есть, количество префиксов перед инструкцией ничем не ограничивается и может достигать десятков и сотен. Правая тетрада префикса, как уже можно догадаться, как часть BCD накапливается в аккумуляторе смещения.
- код "CB C1 2A" означал "ADD C1,B2", а с префиксом "CB C1 54_2A" означает "ADD C1,[D5+4],B2"
- код "CB C1 2A" означал "ADD C1,B2", а с префиксами "CB C1 54_58_2A" означает "ADD C1,[D5+D5+48],B2" с "маргиналом"
- код "BA B2 3E" означал "EOR B2,A3", а с префиксом "BA B2 54_3E" означает "EOR B2,[D5+4],A3"
- код "BA B2 3E" означал "EOR B2,A3", а с префиксами "BA B2 54_58_3E" означает "EOR B2,[D5+D5+48],A3" с "маргиналом"
А вот левая - указывает на регистровую пару B:C под псевдонимом D. И если указывать нулевое смещение, можно "маргинализировать" адрес (подавлять его):Думаю, основную суть уже уловить можно.
- код "CB C1 50_2A" работает как "ADD C1,[D5+0],B2"
- код "CB C1 50_50_2A" как "ADD C1,[D5+D5+0],B2" уже не работает: Цепочка "50_50" - "маргинальна" и указатель "подавлен". Операция работает как "ADD5 C1,B2"
- код "CB C1 50_50_50_2A" как "ADD C1,[D5+D5+D5+0],B2" уже не работает: Цепочка "50_50" - "маргинальна" и указатель "подавлен", но указан в третьем префиксе. Операция работает как "ADD5 C1,[D5+0],B2"
Но, что такое этот "ADD5"? Как работает эта "маргинализованная" инструкция?
Лично я пока ещё не определился. Формально - это команда "внешнего АЛУ#5". Обращение к сопроцессору.
То есть, технически код "80_80_70_70_0A" как "ADD87" можно использовать с сопроцессором i8087 как "FADD" - все "маргиналы" процессор (модель на Verilog) пока никак не обрабатывает, а лишь сигнализирует на выводе "MRG" для внешнего буфера, как "M1" - машинный цикл выборки операции.
То есть, масштабируемость архитектуры уже поддерживается на самом базовом уровне.
ДЕШИФРАТОР АЛУ
Обычно в процессорах допускаются бессмысленные операции, типа "MOV A,A", "CMP A" и т.д.
Так как у меня регистр A0 выполняет функцию PSW, операции типа "ADD A0,A0" как "ADD PSW,PSW" бессмысленны и на аппаратном уровне помечаются особыми признаками.
Всего получается восемь таких признаков:
- M - наличие "маргинала" перед инструкцией
- V - наличие "нормального указателя"
- A_rcv - используется A0/PSW как приёмник результата
- D_rcv - используется Порт (Device) как приёмник результата
- E - признак эквивалентности операндов ("приёмник" и "источник" - один регистр/порт)
- A_trs - используется A0/PSW как источник
- D_trs - используется Порт (Device) как источник
- F_bit - признак не АЛУ операции - FOR/MOV
На этом фоне, с учётом всех исключений, получается солидная таблица АЛУ-команд.
Изначально их было шесть - ADD / SUB / AND / OR / EOR и MOV. А стало:
Вот скриншот прогона отладки дешифратора команд на Verilog HDL:
Как можно заметить, многие инструкции (ситуации) ещё не определены и появились инструкции LEA, LEX (аналог XLAT) и ORD (аналог сортировочной перестановки регистров совмещённой с SHR).
Ознакомиться с кодированием инструкций, используя сложные "указатели" и "маргиналы", можно по ссылке.
P.S.: Нетрудно заметить, что с префиксами и "маргиналами" система команд стала значительно сложнее и серьёзнее. А как 8-битная архитектура уже близко подошла к i8086.





Ответить с цитированием