Целью было сделать некое программируемое Тьюринг-полное устройство, пригодное к использовании в качестве Центрального Процессорного. Основной акцент делался на кодировании микропрограмм посредством реального механического дампа из шестнадцатеричных тумблеров. А чтобы не придумывать очередной бинарный машинный код, за основу были взяты тетрады этих тумблеров.
Тем самым…
Код «A1» на тумблере в дампе так и означает выборку регистра «A1»
Код «B2» означает выборку регистра «B2»
Код «AB» означает группу операндов «A,B» для АЛУ-операций. В данном случае - «A1,B2»
Код «EC» означает «End-by-Carry» - выход из подпрограммы по флагу «CF»
Тем самым, чтобы кодировать какую-нибудь небольшую программу, не нужно иметь у себя под носом даже шпаргалку, так как сами тетрады байтов машинного кода являются аббревиатурами и годятся в качестве мнемоники ассемблера сами-по-себе.
В исполнении Принстонской архитектуры на одну операцию затрачивается по 2 такта. Соответственно, Гарвардский вариант требует по 1 такту.
Разрядность контроллера может варьироваться от 4 бит до 64, в зависимости от поставленной задачи и реализации. Количество всех Регистров Общего Назначения может также варьироваться от 6 до 30.
Причём, роль РОН могут исполнять любые типовые устройства: Таймеры, ПДП, Датчики, Сервоприводы и т.п… Также, за РОН можно использовать и универсальную магазинную память в режиме FIFO или LIFO, чтобы обеспечить стековые операции. Концептуально архитектура не ограничивает конечную реализацию процессора. Так как самое главное в процессоре - дешифратор команд, который не должен менять свою логику и обязан декодировать команды в соответствии с парадигмой описываемой архитектуры.
В классическом варианте выполнение программы выполняется не спешно:
Код:
0000 A9 |REG A9 ; (Такт #1) Выборка регистра блока A с индексом #9 0001 AB |ARG A,B ; (Такт #2) Сборка группы операндов регистровых групп A и B 0002 0F|MOV A9,B0 ; (Такт #3) Фиксирование в левом операнде данных правого | | ^^ \ / || V |/ | | +----------+
Но можно ускорить весь процесс:
Код:
0000 A9 AB 0F|MOV A9,B0 ; (Такт #1) Операция выполняется за 1 такт
Если усложнить декодирование команд добавлением нескольких дешифраторов.
Вложение:
Комментарий к файлу: Четыре дешифратора команд декодируют 32-битное машинное слово в полную команду за 1 такт Koyaanisqatsi-quatro.png [70.5 KiB]
Скачиваний: 163
Минимально же весь микроконтроллер без АЛУ и РОН можно представить вот так:
Вложение:
Комментарий к файлу: Минимальный вариант схемы процессора без АЛУ и РОН Koyaanisqatsi_minimal.png [87.43 KiB]
Скачиваний: 183
Допустим, можно на «Кой-Коде» организовать подобие RISC-ядра для эмуляции выполнения команд процессора i8080. Сначала назначим все регистры i8080 на Кой-Регистры:
Код:
B - B1 C - C1 D - B2 E - C2 H - B3 L - C3 SP- B4C4 A - A1 PC- B5C5 PSW-A1A0 M - B3C3
Теперь можно описать большинство инструкций процессора i8080 примерно вот так:Спойлер
Код:
I8080 CODE|KOYAANISQATSI CODE ----------|---------- 02|STAX B |A1 10 AF|MOV B1C1,A1 ; 5 тактов 0A|LDAX B |A1 10 AE|MOV A1,B1C1 ; 5 тактов 12|STAX D |A1 20 AF|MOV B2C2,A1 ; 5 тактов 1A|LDAX D |A1 20 AE|MOV A1,B2C2 ; 5 тактов --+-------+--------+---------- 40|MOV B,B|B1 BB 1F|MOV B1,B1 ; 3 такта 41|MOV B,C|B1 BC 1F|MOV B1,C1 ; 3 такта 42|MOV B,D|B1 BB 2F|MOV B1,B2 ; 3 такта 43|MOV B,E|B1 BC 2F|MOV B1,C2 ; 3 такта 44|MOV B,H|B1 BB 3F|MOV B1,B3 ; 3 такта 45|MOV B,L|B1 BC 3F|MOV B1,C3 ; 3 такта 46|MOV B,M|B1 30 BE|MOV B1,B3C3 ; 5 тактов 47|MOV B,A|B1 BA 1F|MOV B1,A1 ; 3 такта --+-------+--------+---------- 48|MOV C,B|C1 CB 1F|MOV C1,B1 49|MOV C,C|C1 CC 1F|MOV C1,C1 4A|MOV C,D|C1 CB 2F|MOV C1,B2 4B|MOV C,E|C1 CC 2F|MOV C1,C2 4C|MOV C,H|C1 CB 3F|MOV C1,B3 4D|MOV C,L|C1 CC 3F|MOV C1,C3 4E|MOV C,M|C1 30 CE|MOV C1,B3C3 4F|MOV C,A|C1 CA 1F|MOV C1,A1 --+-------+--------+---------- 50|MOV D,B|B2 BB 1F|MOV B2,B1 51|MOV D,C|B2 BC 1F|MOV B2,C1 52|MOV D,D|B2 BB 2F|MOV B2,B2 53|MOV D,E|B2 BC 2F|MOV B2,C2 54|MOV D,H|B2 BB 3F|MOV B2,B3 55|MOV D,L|B2 BC 3F|MOV B2,C3 56|MOV D,M|B2 30 BE|MOV B2,B3C3 57|MOV D,A|B2 BA 1F|MOV B2,A1 --+-------+--------+---------- 58|MOV E,B|C2 CB 1F|MOV C2,B1 59|MOV E,C|C2 CC 1F|MOV C2,C1 5A|MOV E,D|C2 CB 2F|MOV C2,B2 5B|MOV E,E|C2 CC 2F|MOV C2,C2 5C|MOV E,H|C2 CB 3F|MOV C2,B3 5D|MOV E,L|C2 CC 3F|MOV C2,C3 5E|MOV E,M|C2 30 CE|MOV C2,B3C3 5F|MOV E,A|C2 CA 1F|MOV C2,A1 --+-------+--------+---------- 60|MOV L,B|B3 BB 1F|MOV B3,B1 61|MOV L,C|B3 BC 1F|MOV B3,C1 62|MOV L,D|B3 BB 2F|MOV B3,B2 63|MOV L,E|B3 BC 2F|MOV B3,C2 64|MOV L,H|B3 BB 3F|MOV B3,B3 65|MOV L,L|B3 BC 3F|MOV B3,C3 66|MOV L,M|B3 30 BE|MOV B3,B3C3 67|MOV L,A|B3 BA 1F|MOV B3,A1 --+-------+--------+---------- 68|MOV L,B|C3 CB 1F|MOV C3,B1 69|MOV L,C|C3 CC 1F|MOV C3,C1 6A|MOV L,D|C3 CB 2F|MOV C3,B2 6B|MOV L,E|C3 CC 2F|MOV C3,C2 6C|MOV L,H|C3 CB 3F|MOV C3,B3 6D|MOV L,L|C3 CC 3F|MOV C3,C3 6E|MOV L,M|C3 30 CE|MOV C3,B3C3 6F|MOV L,A|C3 CA 1F|MOV C3,A1 --+-------+--------+---------- 70|MOV M,B|B1 30 BF|MOV B3C3,B1 71|MOV M,C|C1 30 CF|MOV B3C3,C1 72|MOV M,D|B2 30 BF|MOV B3C3,B2 73|MOV M,E|C2 30 CF|MOV B3C3,C2 74|MOV M,H|B3 30 BF|MOV B3C3,B3 75|MOV M,L|C3 30 CF|MOV B3C3,C3 76|HLT |00 |HLT 77|MOV M,A|A1 30 AF|MOV B3C3,A1 --+-------+--------+---------- 78|MOV A,B|A1 AB 1F|MOV A1,B1 79|MOV A,C|A1 AC 1F|MOV A1,C1 7A|MOV A,D|A1 AB 2F|MOV A1,B2 7B|MOV A,E|A1 AC 2F|MOV A1,C2 7C|MOV A,H|A1 AB 3F|MOV A1,B3 7D|MOV A,L|A1 AC 3F|MOV A1,C3 7E|MOV A,M|A1 A0 CE|MOV A1,B3C3 7F|MOV A,A|A1 AA 1F|MOV A1,A1 --+-------+--------+---------- 88|ADC B |A1 AB 1A|ADD A1,B1 ; 3 такта 89|ADC C |A1 AC 1A|ADD A1,C1 ; 3 такта 8A|ADC D |A1 AB 2A|ADD A1,B2 ; 3 такта 8B|ADC E |A1 AC 2A|ADD A1,C2 ; 3 такта 8C|ADC H |A1 AB 3A|ADD A1,B3 ; 3 такта 8D|ADC L |A1 AC 3A|ADD A1,C3 ; 3 такта 8E|ADC M |A1AA301A|ADD A1,B3C3 ; 6 тактов 8F|ADC A |A1 AA 1A|ADD A1,A1 ; 3 такта --+-------+--------+---------- 98|SBB B |A1 AB 1B|SUB A1,B1 99|SBB C |A1 AC 1B|SUB A1,C1 9A|SBB D |A1 AB 2B|SUB A1,B2 9B|SBB E |A1 AC 2B|SUB A1,C2 9C|SBB H |A1 AB 3B|SUB A1,B3 9D|SBB L |A1 AC 3B|SUB A1,C3 9E|SBB M |A1AA301B|SUB A1,B3C3 9F|SBB A |A1 AA 1B|SUB A1,A1 --+-------+--------+---------- A0|ANA B |A1 AB 1C|AND A1,B1 A1|ANA C |A1 AC 1C|AND A1,C1 A2|ANA D |A1 AB 2C|AND A1,B2 A3|ANA E |A1 AC 2C|AND A1,C2 A4|ANA H |A1 AB 3C|AND A1,B3 A5|ANA L |A1 AC 3C|AND A1,C3 A6|ANA M |A1AA301C|AND A1,B3C3 A7|ANA A |A1 AA 1C|AND A1,A1 --+-------+--------+---------- A8|XRA B |A1 AB 1E|EOR A1,B1 A9|XRA C |A1 AC 1E|EOR A1,C1 AA|XRA D |A1 AB 2E|EOR A1,B2 AB|XRA E |A1 AC 2E|EOR A1,C2 AC|XRA H |A1 AB 3E|EOR A1,B3 AD|XRA L |A1 AC 3E|EOR A1,C3 AE|XRA M |A1AA301E|EOR A1,B3C3 AF|XRA A |A1 AA 1E|EOR A1,A1 --+-------+--------+---------- B0|ORA B |A1 AB 1D|OR A1,B1 B1|ORA C |A1 AC 1D|OR A1,C1 B2|ORA D |A1 AB 2D|OR A1,B2 B3|ORA E |A1 AC 2D|OR A1,C2 B4|ORA H |A1 AB 3D|OR A1,B3 B5|ORA L |A1 AC 3D|OR A1,C3 B6|ORA M |A1AA301D|OR A1,B3C3 B7|ORA A |A1 AA 1D|OR A1,A1 --+-------+--------+---------- CE|ACI IB |A1AA501A|ADD A1,B5C5 DE|SBI IB |A1AA501B|SUB A1,B5C5 E6|ANI IB |A1AA501C|AND A1,B5C5 EE|XRI IB |A1AA501E|EOR A1,B5C5 F6|ORI IB |A1AA501D|OR A1,B5C5
Откуда видно, что большинство команд легко накладываются на эмуляцию. Тем самым, если использовать ПЗУ 4 кб под прошивку микрокода и под каждую команду выделить по 16 байтов Кой-Кода, то в Гарвардском исполнении большинство из этих команд будут выполняться за 3 такта. И если на считывание кода команды в цикле M1 будет затрачено 2 такта, то на большинство этих команд в общей сложности будет затрачиваться по 5 тактов или 8 тактов, что практически сравнимо с производительностью самого i8080… Если не скупиться и использовать четвёрку дешифратора команд, на Кой-Команды будет уходить по 1 такту или по 3, а в общей сложности на чтение i8080-кода и его исполнение будет тратиться по 3 такта или 5, что уже заметно лучше.
P.S.: Здесь и далее будут использоваться сокращения вида «Кой-Дамп», «Кой-Код» и т.п…
На странице эмуляции на данный момент уже имеется ассемблер, дизассемблер, дамп памяти и графический дисплей. Среда пока очень примитивная, но даёт возможность увидеть задуманное в действии и немного вникнуть в суть.
В поле редактирования листинга можно производить любые изменения, которые будут транслированы и вступят в силу при очередном обновлении страницы.
Графический дисплей исполнен в виде симуляции ЖК-дисплея на сегментах, а в качестве демонстрации выбран эскиз с экрана Советской карманной электронной игры Автослалом: Программно можно лишь включать/отключать нужные сегменты, но нельзя попиксельно строить другое произвольное изображение.
Как и писалось выше, процессор Койянискаци разрабатывался прежде всего как микроконтроллер с интуитивно понятным машинным кодом без оглядки на производительность и прежде всего он предназначен для управления инкубатором или теплицей, когда высокая производительность не является одной из важнейших показателей системы. А для периодического обновления показаний различных датчиков на цифровом дисплее не нужно иметь сложнейшую графическую систему и можно обойтись профильным ЖК.
Управление ЖК (виртуальным) Всего до 72 сегментов ЖК управляются напрямую через девять 8-битных периферийных регистров общего назначения D₀…₈. При этом следует сначала записать в регистр D₉ значение 0xCB (Coloured Buffer) для обеспечения прямого доступа к сегментам.
Вывод на консоль (виртуальную) При печати приветственного «Hello, World!» используется вывод ASCII-последовательности в регистр D₀. Однако, перед этим в регистр D₉ записывается значение 0xC0 (Console 0ut) для обеспечения прямого канала на печать текстом.
Управление таймером Программирование микросхемы таймера i8253 (К580ВИ53) происходит через регистры общего назначения D₀…₃. Перед этим в регистр D₉ должно быть записано значение 0x53 (индекс целевой микросхемы). (В эмуляторе пока не реализовано.)
Управление периферией Программирование микросхемы ППА i8255 (К580ВИ55) производится через регистры общего назначения D₀…₃. Перед этим в регистр D₉ должно быть записано значение 0x55 (индекс целевой микросхемы). (В эмуляторе пока не реализовано.)
Управление ПДП Все регистры микросхемы ПДП i8257 (К580ВТ57) доступны через регистры D₀…₇ и D₈ для статуса ПДП. Перед программированием ПДП необходимо в регистр D₉ записать значение 0x57 (индекс целевой микросхемы) для доступа к микросхеме. (В эмуляторе пока не реализовано.)
Чтение клавиатуры Для чтения регистров микросхемы контроллера клавиатуры i8279 (К580ВВ79) следует использовать регистры общего назначения D₀ и D₁. Перед доступом к контроллеру в регистр D₉ следует записать значение 0x79 (индекс целевой микросхемы) для обеспечения её выборки. (В эмуляторе пока не реализовано.)
Буфер магазинной памяти Доступ к микросхеме M66250P осуществляется через регистры общего назначения D₀ и D₁. Перед доступом к контроллеру в регистр D₉ следует записать индекс целевой микросхемы для обеспечения её выборки. (В эмуляторе пока не реализовано.)
Код:
╔════╤════════╤════════════════════════════════════════════════════════════════╗ ║ D9 │ ИМС │ Описание доступа к ресурсам микросхемы через РОН процессора ║ ╠════╪════════╪════════════════════════════════════════════════════════════════╣ ║0x53│ i8253 │ D0/D1/D2 - Каналы Счётчиков 0/1/2 ║ ║ │К580ВИ53│ D3 - Регистр Статуса Таймера ║ ╟────┼────────┼────────────────────────────────────────────────────────────────╢ ║0x55│ i8255 │ D0/D1/D2 - Порты A/B/C ППА ║ ║ │К580ВИ55│ D3 - Регистр Статуса ППА ║ ╟────┼────────┼────────────────────────────────────────────────────────────────╢ ║0x57│ i8257 │ D0/D2/D4/D6 - Каналы 0/1/2/3: ПДП Адрес ║ ║ │ │ D1/D3/D5/D7 - Каналы 0/1/2/3: ПДП Счёт ║ ║ │К580ВТ57│ D8 - Регистр Статуса ПДП ║ ╟────┼────────┼────────────────────────────────────────────────────────────────╢ ║0x79│ i8279 │ D0 - Регистр Данных ║ ║ │К580ВВ79│ D1 - Регистр Статуса ║ ╚════╧════════╧════════════════════════════════════════════════════════════════╝
Демонстрация При открытии страницы регистры BC₀ (B₀ и C₀) сброшены, что сигнализирует о холодном старте системы. Нажатиями на кнопку «Step» можно приступить к пошаговому исполнению программы и проследить за исполнением каждой команды. Кнопкой «Run» можно запустить цикл автоматического исполнения программного кода, который может быть прерван только инструкцией HLT. Без соблюдения этого условия эмуляция может зациклиться и страничка браузера зависнет. Исходный листинг составлен так, чтобы по первому нажатию на «Run» произвести предустановку всех нужных регистров и вывести приветственное сообщение. Но последующие нажатия на «Run» будут перезаписывать регистры маскирования ЖК-сегментов для воспроизведения имитации игровой сцены известной игры.
Так уж случилось, что из-за поломки основного ПК всё оказалось на долгой паузе...
Все, кто знаком и работает с архитектурой x86 / IA32, давно в курсе, что она - далека от совершенства… Но, так как на разработку конкретной архитектуры всегда влиял так или иначе Intel, буду приводить примеры на IA-32 для доходчивости…
LEA EBX,[EBX+EBX*2+3] ; Знакомый всем трюк продвинутого ADD
ADD EBX,[EBX+EBX*2+3] ; Здесь получим исключение, так как регистр базы и регистр индекса - один. Что "из ряда вон!" ("маргинал" какой-то…)
Вот такие случаи в Intel никак не предусмотрели и оставили всё на совести программиста, переложив всю заботу на обработчик исключения.
MOV EAX,FS:[0x00000018] ; Встречается порою вполне знакомое…
MOV EAX,CS: DS: ES: FS:[0x00000018] ; Процессор проигнорирует в этой куче три лишних префикса. Но, запись - "из ряда вон!" ("маргинал" какой-то…)
И такие случаи в Intel демократично проигнорировали и оставили на совести программиста…
Теперь читатели темы достаточно подготовились к нововведению…
МАРГИНАЛЫ Как уже говорилось многим выше, префиксы кодируются с помощью любого ненулевого BCD.
код «10» означает указатель на ячейку в памяти по адресу «[D1+0]» (левый ниббл адресует регистр базы, правый ниббл определяет относительное смещение)
код «10 23» усложняет указатель добавлением к базовому ещё и индексного регистра, что получается «[D1+D2+3]»
код «10 23 45» добавляет ещё один индексный регистр и теперь получилось «[D1+D2+D4+35]» (относительное смещение ожидаемо множится на 10 и накапливает правые нибблы)
код «10 23 45 16» получился недопустимым, как «[D1+D2+D4+D1+356]»! Так какой же регистр здесь можно считать базовым, если D1 повторяется вновь?
Как уже можно догадаться, это - та самая ситуация "из ряда вон!" с условным кодовым именем "маргинал какой-то"…
Естественно, для решения этой задачи не требуется никакой вычислительной мощности, так как здесь - концептуальная проблема. На решение этой проблемы у меня ушёл, без малого, год… Год, в часы досуга, на листке бумаги карандашом расписывались подобные ситуации с поиском решения.
И решение было найдено достаточно изящное и гибкое!
код «10 23 45 16» следует интерпретировать как «[D1+(D2+D4)*2+356]», где повторяемый регистр базы служит признаком удвоения суммы индексов
код «10 23 45 16 47» здесь описывает указатель как «[D1+(D2+D4)*2+D4+3567]», что можно упростить до «[D1+2*D2+3*D4+3567]» следуя нехитрым школьным правилам
код «10 23 45 16 47 48» показывает «[D1+(D2+D4)*2+D4+D4+35678]» повторяя D4 дважды. Интерпретируем как «[D1+((D2+D4)*2+D4)*2+D4+35678]» и получаем «[D1+4*D2+7*D4+35678]»
Совсем нетрудно догадаться, что появились механизмы умножения индексных регистров через последовательно-выборочный двоичный сдвиг. Да, код при этом начинает разбухать прилично, но программист получает возможность описать указатель любой сложности.
Одним словом… Эврика!
Но, оказывается, здесь кроется один крошечный подвох…
код «10 10» как следует интерпретировать, если индексные регистры ещё не указаны, а умножение - «[D1+(0)*2+0]» - действует?
код «10 10 10» с таким механизмом это «[D1+((0)*2)*2+0]» - что это такое вообще???
Ещё месяцы ушли на решение этой концептуальной задачи.
В итоге, получилось следующее:
код «10 10» подавляет (сбрасывает) регистр базы и аккумулируется как «маргинал»: Условно, через шарп - «1#»
код «10 10 10» образует «маргинал 1#», но указатель снова в деле. Получаем маргинальный указатель - «1#[D1+0]»
код «80 80 70 70 10» уже интерпретируется как маргинальный указатель «87#[D1+0]»
Тем самым, никакие исключения, в жанре Intel, в моей архитектуре не понадобились, а в сумме - появились и комплексные векторы на ячейку памяти, и «маргинализаторы операций», выводящие код инструкций «вон из процессора» - в сопроцессор…
Verilog-модель аккумулятор эффективного адреса и детекции маргиналов:Спойлер
Код:
module vector_accumulator (input wire clk // Clock (positive edge) ,input wire vector_en // Enable parse vector (BDC detected) ,input wire vector_clr // Clear vector (after any instruction) ,input wire [7:0] operation // Operation code (BDC only) ,input wire [15:0] vector_input // Pointer input (pair B:C from register file) ,output reg [15:0] vector_output // Full vector output (summ of Base + Index + Offset) ,output reg vector_margin // Marginal flag (incorrect Index of "dry" Vector) ); reg [3:0] pointer_index; // Index of "base" in operation code reg [3:0] offset_digit; // Digit of BDC-digit for "offset" reg is_base; // Flag of "base" (Index is equal of Base) reg is_shift; // Flag of "shifting" (Index is repeated in current summ) reg [3:0] base_index; // Index of current "base" (Index of Base) reg [9:0] pointers_set; // Collection of used pointers (flags of used Indexes in current summ) reg [9:0] pointer_mask; // Mask of current pointer reg [15:0] vector_base; // Pointer of "base" reg [15:0] vector_index; // Summ of used pointers reg [15:0] vector_offset; // Decimal offset reg [15:0] offset_shift; // Offset multipled by 10
Те же Intel в своей IA-64 не стали разбираться с корректным выполнением инструкций типа «ADD DX,AX» и вообще обнуляют старшие 32 бита RDX в таких случаях. И здесь по стопам Intel мне совсем ни к чему. Просто на аппаратном уровне такие ситуации также отлавливаются и устанавливаются ещё один флаг. К тому же, регистр A0 у меня выполняет функцию PSW: Использовать его в вычислениях бессмысленно. И регистры группы D0-D9 ссылаются либо к портам УВВ (Devices), либо к регистровым парам (как DX - это DH и DL). Если «MOV BL,BL» работает как «NOP», то мне такие «пустышки» в большом количестве не нужны - такие ситуации также отлавливаются. Как результат, шесть инструкций «ADD / SUB / AND / OR / EOR / MOV» дешифратором с учётом всех комбинаций операндов синтезируются в три десятка различных инструкций. Тем самым, к АЛУ подтягивается уже не три бита кода операции, а все 11, из-за чего программно доступно уже большее число разнообразных инструкций всех видов.Спойлер
Код:
+----------> Имеется "маргинальный префикс" | | +----------> Имеется "векторный префикс" | | | | ++---------> Приёмник - либо A0/PSW, либо порт Dj, либо 16-битная регистровая пара Bn:Cn | | || | | || +-------> Приёмник и источник - один РОН или регистровая пара | | || | | | || | ++----> Приёмник - либо A0/PSW, либо порт Dj, либо 16-битная регистровая пара Bi:Ci | | || | || | | || | || +--> Признак FOR/MOV - не АЛУ-операции | | || | || | M V AD E AD F X_1_10_1_XX_1: MOV PSW,[V] ; Чтение PSW из ОЗУ X_1_10_1_XX_0: UNARY [V] ; Унарные INC/DEC/CLR/SET/NOT над ОЗУ X_0_10_1_XX_1: MOV??? PSW,PSW ; 1 шт. !!!reserved!!! X_0_10_1_XX_0: UNARY?? PSW ; 5 шт. !!!reserved!!! X_1_10_X_01_1: SWAP [V],Dn ; XCHG ОЗУ и 16-битной пары X_1_10_X_01_0: ALU_OP [V],Dn ; АЛУ-ADD/SUB/AND/OR/EOR 16-битного ОЗУ и пары X_0_10_X_01_1: INF Dj ; MOV PSW,Dj - Попытка ввода из порта (результат - в CF) X_0_10_X_01_0: UNARY Dn ; 16-битные унарные INC/DEC/CLR/SET/NOT X_1_10_X_XX_1: SWAP [V],Rn ; XCHG ОЗУ и 8-битного РОН X_1_10_X_XX_0: ALU_OP [V],Rn ; АЛУ-ADD/SUB/AND/OR/EOR ОЗУ и 8-битного РОН X_0_10_X_XX_1: MOV PSW,Rn ; Чтение PSW из РОН X_0_10_X_XX_0: UNARY Rn ; Унарные INC/DEC/CLR/SET/NOT над РОН X_1_01_X_10_X: ALU/MOV Dn,[V] ; АЛУ-ADD/SUB/AND/OR/EOR и MOV пары с 16-битным ОЗУ X_0_01_X_10_1: OUF Dj ; MOV Dj,PSW - Попытка вывода в порт (результат - в CF) X_0_01_X_10_0: UNARY Dn,CF ; Унарные INC/DEC/CLR/SET/NOT над парой при условии CF X_1_00_X_10_X: ALU/MOV Rn,[V] ; АЛУ-ADD/SUB/AND/OR/EOR и MOV РОН с ОЗУ X_0_00_X_10_1: MOV Rn,PSW ; Загрузка PSW в РОН X_0_00_X_10_0: UNARY Rn,CF ; Унарные INC/DEC/CLR/SET/NOT над РОН при условии CF X_1_01_1_XX_1: LEA Dn,[V] ; Загрузка "эффективного адреса" в регистровую пару X_1_01_0_01_1: ORD Dn,[V],Di ; Dn = (MAX(Dn, Di) - ОЗУ) >> 1 X_1_01_X_01_0: ALU_OP Dn,[V],Di ; 16-битная ADD/SUB/AND/OR/EOR ОЗУ с Di, результат в Dn X_0_XX_1_XX_1: MOV??? R,R ; 1 шт. Холостая пересылка (NOP???) X_0_01_1_XX_0: UNARY?? Dn ; 5 шт. 16-битные унарные INC/DEC/CLR/SET/NOT (повтор!!!) X_1_01_0_XX_1: LEX Dn,[V],Ri ; 16-битный XLAT: MOV Dn,[V+2*Ri] X_1_01_X_XX_0: ALU_OP Dn,[V],Ri ; 8-битная ADD/SUB/AND/OR/EOR ОЗУ с РОН, результат в 16-бит Dn X_0_01_0_00_1: MOV Dj,Ri ; Аналог OUT Dj,Ri - вывод в порт X_0_01_X_XX_0: ALU_OP Dn,Ri ; 16-битная ADD/SUB/AND/OR/EOR Dn с 8-битным Dn X_1_0X_X_01_1: MOV??? R,[V],Dj ; 1 шт. !!!СТРАННАЯ ОПЕРАЦИЯ!!! X_1_0X_X_01_0: ALU_OP R,[V],Dj ; 8-битная ADD/SUB/AND/OR/EOR ОЗУ с портом Dj, результат в РОН X_0_0X_X_01_X: ALU/MOV R,Dj ; АЛУ-ADD/SUB/AND/OR/EOR или MOV (IN R,Dj) РОН с портом Dj X_1_0X_1_0X_1: LEX R,[V] ; 8-битный XLAT: MOV R,[V+R] X_1_0X_0_0X_1: ORD R,[V],R' ; R = (MAX(R, R') - ОЗУ) >> 1 X_1_0X_X_01_X: ALU/MOV R,[V],R' ; ADD/SUB/AND/OR/EOR или MOV ОЗУ с R', результат в РОН X_0_0X_X_01_X: ALU/MOV R,R' ; ADD/SUB/AND/OR/EOR или MOV ОЗУ с R', результат в РОН
Примеры:_____ AA A0 12 0F MOV PSW,[D1+2] ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],A0 AA A0 12 0E NOT [D1+2] ; На самом деле, полная мнемоника операции - EOR A0,[D1+2],A0 AD A0 12 3F SWAP [D1+2],D3 ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],D3 AD A0 1F INF D1 ; На самом деле, полная мнемоника операции - MOV A0,D1 AD A0 1E NOT D1 ; На самом деле, полная мнемоника операции - EOR A0,D1 AB A0 12 3F SWAP [D1+2],B3 ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],B3 AB A0 12 3E EOR [D1+2],B3 ; На самом деле, полная мнемоника операции - EOR A0,[D1+2],B3 AB A0 1F MOV PSW,B1 ; На самом деле, полная мнемоника операции - MOV A0,B1 AB A0 1E NOT B1 ; На самом деле, полная мнемоника операции - EOR A0,B1 DA D1 0F OUF D1 ; На самом деле, полная мнемоника операции - MOV D1,A0 DA D1 0E NOT D1,CF ; На самом деле, полная мнемоника операции - EOR D1,A0 DD D1 23 1F LEA D1,[D2+3] ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],D1 DD D1 23 4F ORD D1,[D2+3],D4 ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],D4 DC D1 23 4F LEX D1,[D2+3],C4 ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],C4
Например, вот фрагмент логов отладчика дешифратора команд:
M# / M - последовательность "маргиналов"
VV / V - наличие корректного вектора
IC - код инструкции
E - флаг использования одного регистра за приёмник и за транслятор
AD - флаги наличия A0/PSW или D со стороны приёмника или транслятора
При прогонке отладочного кода через отладочную модель дешифратора команд выдаётся примерно такой лог:Спойлер
ТЕ ЕЩЁ МАРГИНАЛЫ Как сказано выше, указание неверного указателя на аппаратном уровне обрабатываются в особенном виде, из-за чего программист может использовать эти резервные механизмы по своему усмотрению. Однако, эти самые маргиналы местами просто слишком маргинальны. Например, рассмотрим несколько странных инструкций:
код «00» - команда останова «HLT»: Переход на адрес 0000 с сохранением точки останова в D0 (регистровой паре B0:C0)
код «10 00» - команда останова «HLT [D1+0]»: Безусловный переход на адрес [D1] с сохранением точки останова в D0
код «10 10 00» - команда останова «HLT1» или «HLT#1»: Здесь присутствует маргинал - непонятная операция…
код «10 10 10 00» - команда останова «HLT 1#D1+0» или «HLT 1#[D1+0]»: Присутствует маргинальный указатель - ещё страннее…
Нетрудно заметить, что на одной только операции Останова с частной комбинацией префиксов уже довольно неясная ситуация. Даже если браться обрабатывать такие комбинации на самом высоком уровне - эмуляцией в JavaScript, не совсем понятно, что именно с этим безобразием делать…
МАРГИНАЛЬНЫЕ КАНАЛЫ Так как «маргинальные индексы» временно помечались в ассемблере/дизассемблере с «решёткой-шарпом», то это напомнило некоторую аналогию с каналами в Бейсиках (у ZX-Spectrum также имеются). Так, есть такой GW-Basic - оператор OPEN - вполне хороший пример.
Тем самым, вполне можно:
код «10 10 00» - команду «HLT#1» можно условно обозначать за «CLOSE#1»…
код «10 10 10 00» - команду «HLT#1 [D1]» можно условно обозначать за «OPEN#1 "/dev/…"»…
Тем самым, у процессора на программном уровне в системе команд появляются две, хоть и побочные, но весьма любопытные операции. Программист получает основные операции работы с файловой системой уже на самом низком уровне.
Сам процессор может продолжать игнорировать такие операции и ничего не делать. Тогда как внешняя периферия и т.н. сопроцессор может перехватывать все эти «маргинальные конструкции инструкций» и производить соответствующую настройку. В таком случае, если в системе будут присутствовать «сетевой сопроцессор», «графический сопроцессор» или «математический сопроцессор», уже на уровне ассемблера предоставляется довольно мощное окружение…
МАРГИНАЛЬНАЯ ЭМУЛЯЦИЯ КАНАЛОВ Естественно, о поддержке «маргинального окружения» на аппаратном уровне думать не приходится в принципе! Тем самым, следует продумать механизмы прерываний, чтобы поддерживать все эти «маргинальные каналы» на программном уровне драйверов операционной системы. Ещё во времена DOS и IBM PC-XT существовали библиотеки эмуляции отсутствующего FPU. Здесь - примерно то же самое…
ПРИМЕР Если мы настроили «Маргинал #1» указателем на строку, типа "/dev/ega/320x240/" как открытия непосредственной проекции EGA-графики, то код
Код:
10 10 80 90 80 80 90 80 80 80 80 80 80 A7
будет означать «MOV 1#[D8+320D9],A7», что буквально «*(BYTE *)(D8 + 320 * D9) = A7», где D8 - координата X и D9 - координата Y. Получается одна длинная операция пиксельной записи в графическую плоскость.
P.S: Как можно подметить, термин «маргинал» в рамках текущих разработок используется как устоявшийся. Вполне возможно, что вместо привычных «эскейпов» его вполне можно использовать…
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 25
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения