Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить

Койяанискаци - Процессор Радиолюбителя

Чт дек 24, 2020 19:17:22

Основнaя Хабр-Статья.


Целью было сделать некое программируемое Тьюринг-полное устройство, пригодное к использовании в качестве Центрального Процессорного.
Основной акцент делался на кодировании микропрограмм посредством реального механического дампа из шестнадцатеричных тумблеров.
А чтобы не придумывать очередной бинарный машинный код, за основу были взяты тетрады этих тумблеров.

Тем самым…
  • Код «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 такт
Если усложнить декодирование команд добавлением нескольких дешифраторов.
Koyaanisqatsi-quatro.png
Четыре дешифратора команд декодируют 32-битное машинное слово в полную команду за 1 такт
(70.5 KiB) Скачиваний: 142


Минимально же весь микроконтроллер без АЛУ и РОН можно представить вот так:
Koyaanisqatsi_minimal.png
Минимальный вариант схемы процессора без АЛУ и РОН
(87.43 KiB) Скачиваний: 158


Допустим, можно на «Кой-Коде» организовать подобие 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.: Здесь и далее будут использоваться сокращения вида «Кой-Дамп», «Кой-Код» и т.п…

Койяанискаци - Процессор Радиолюбителя

Вт июн 08, 2021 22:00:01

За всё это время удалось выделить силы на разработку более-менее инструментальной среды для Койяанискаци.

Ссылка запуска онлайн эмулятора

На странице эмуляции на данный момент уже имеется ассемблер, дизассемблер, дамп памяти и графический дисплей.
Среда пока очень примитивная, но даёт возможность увидеть задуманное в действии и немного вникнуть в суть.

В поле редактирования листинга можно производить любые изменения, которые будут транслированы и вступят в силу при очередном обновлении страницы.

Графический дисплей исполнен в виде симуляции ЖК-дисплея на сегментах, а в качестве демонстрации выбран эскиз с экрана Советской карманной электронной игры Автослалом: Программно можно лишь включать/отключать нужные сегменты, но нельзя попиксельно строить другое произвольное изображение.

Как и писалось выше, процессор Койянискаци разрабатывался прежде всего как микроконтроллер с интуитивно понятным машинным кодом без оглядки на производительность и прежде всего он предназначен для управления инкубатором или теплицей, когда высокая производительность не является одной из важнейших показателей системы.
А для периодического обновления показаний различных датчиков на цифровом дисплее не нужно иметь сложнейшую графическую систему и можно обойтись профильным ЖК.

Управление ЖК (виртуальным)
Всего до 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» будут перезаписывать регистры маскирования ЖК-сегментов для воспроизведения имитации игровой сцены известной игры.

Маргинальное кодирование

Пт июн 30, 2023 16:00:26

Так уж случилось, что из-за поломки основного ПК всё оказалось на долгой паузе...

Все, кто знаком и работает с архитектурой 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, в моей архитектуре не понадобились, а в сумме - появились и комплексные векторы на ячейку памяти, и «маргинализаторы операций», выводящие код инструкций «вон из процессора» - в сопроцессор…

Естественно, всё это не ограничилось JavaScript-опытами (ссылка на JavaScript кодировщик инструкции).

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

   always   @*
   begin
      pointer_index = operation[7:4];
      pointer_mask = 10'b1 << pointer_index;
      is_base = &(base_index ^ pointer_index);
      is_shift = |(pointers_set & pointer_mask);
      offset_digit = operation[3:0];
      offset_shift = (((vector_offset << 2) + vector_offset) << 1) + {12'd0, offset_digit};
      vector_output = vector_base + vector_index + vector_offset;
      vector_margin = vector_en && ~|offset_digit && is_base && ~|vector_offset && ~|pointers_set;
   end

   always   @(posedge clk)
   begin
      if(vector_clr)
      begin
         base_index <= 0;
         vector_base <= 0;
         vector_index <= 0;
         vector_offset <= 0;
         pointers_set <= 0;
      end else
      if(vector_margin)
         base_index <= 0;
      else
      if(vector_en)
      begin
         vector_offset <= offset_shift;
         if(~|base_index)
         begin
            base_index <= ~pointer_index;
            vector_base <= vector_input;
         end else
         if(is_base)
         begin
            vector_index <= vector_index << 1;
            pointers_set <= pointer_mask;
         end else
         if(is_shift)
         begin
            vector_index <= (vector_index << 1) + vector_input;
            pointers_set <= pointer_mask;
         end else begin
            vector_index <= vector_index + vector_input;
            pointers_set <= pointers_set | pointer_mask;
         end
      end
   end
endmodule
BDC-Accumulator-part#1.png
Незаконченная схема вычислителя "эффективного адреса"
(183.69 KiB) Скачиваний: 0

Те же 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 со стороны приёмника или транслятора
При прогонке отладочного кода через отладочную модель дешифратора команд выдаётся примерно такой лог:
Спойлер
Код:
VCD info: dumpfile testbench.vcd opened for output.
_IP_:M#[VV]IC  M_V_AD_E_AD_F RC<>TR _ASM ..OPERANDS..   _REAL INSTRUCTION_
0000:      AA  0_0_x0_x_0x_0:A?!=0?  ARG   A,A
0001:      A0  0_0_x0_x_00_0:A?!=AA  REG   A0
0002:86#59 A0  1_1_10_0_00_0:A0!=AA  MOV86 D1+2D2+3D3+4D4+5D5+67D6+789,A0
0016:03#   A0  1_0_10_0_00_0:A0!=AA  REG3  A0      ; Marginaled
0019:      0F  0_0_10_1_10_1:A0==A0  ---   ???      ; MOV   A0,A0
001A:      0E  0_0_10_1_10_0:A0==A0  ---   ???      ; EOR   A0,A0
001B:      0D  0_0_10_1_10_0:A0==A0  ---   ???      ; DIS   A0,A0
001C:      0C  0_0_10_1_10_0:A0==A0  ---   ???      ; CON   A0,A0
001D:      0B  0_0_10_1_10_0:A0==A0  ---   ???      ; SUB   A0,A0
001E:      0A  0_0_10_1_10_0:A0==A0  ---   ???      ; ADD   A0,A0
001F:   34 0F  0_1_10_1_10_1:A0==A0  MOV   PSW,D3+4   ; MOV   A0,D3+4,A0
0021:   34 0E  0_1_10_1_10_0:A0==A0  NOT   D3+4      ; EOR   A0,D3+4,A0
0023:      AD  0_0_10_0_00_0:A0!=AA  ARG   A,D
0024:      5F  0_0_10_0_01_1:A0!=D5  INF   D5      ; MOV   A0,D5
0025:      5E  0_0_10_0_01_0:A0!=D5  NOT   D5      ; EOR   A0,D5
0026:   34 5F  0_1_10_0_01_1:A0!=D5  SWP   D3+4,D5   ; MOV   A0,D3+4,D5
0028:   34 5E  0_1_10_0_01_0:A0!=D5  EOR   D3+4,D5   ; EOR   A0,D3+4,D5
002A:      AC  0_0_10_0_01_0:A0!=Da  ARG   A,C
002B:      5F  0_0_10_0_00_1:A0!=C5  MOV   PSW,C5   ; MOV   A0,C5
002C:      5E  0_0_10_0_00_0:A0!=C5  NOT   C5      ; EOR   A0,C5
002D:   34 5F  0_1_10_0_00_1:A0!=C5  SWP   D3+4,C5   ; MOV   A0,D3+4,C5
002F:   34 5E  0_1_10_0_00_0:A0!=C5  EOR   D3+4,C5   ; EOR   A0,D3+4,C5
0031:      DA  0_0_10_0_00_0:A0!=CD  ARG   D,A
0032:      D1  0_0_01_0_00_0:Dx!=AD  REG   D1
0033:      0F  0_0_01_0_10_1:D1!=A0  OUF   D1      ; MOV   D1,A0
0034:      0E  0_0_01_0_10_0:D1!=A0  NOT   D1,CF    ; EOR   D1,A0
0035:   34 0F  0_1_01_0_10_1:D1!=A0  MOV   D1,D3+4   ; MOV   D1,D3+4,A0
0037:   34 0E  0_1_01_0_10_0:D1!=A0  EOR   D1,D3+4   ; EOR   D1,D3+4,A0
0039:      CA  0_0_01_0_00_0:D1!=AC  ARG   C,A
003A:      C2  0_0_00_0_00_0:C?!=AC  REG   C2
003B:      0F  0_0_00_0_10_1:C2!=A0  MOV   C2,PSW   ; MOV   C2,A0
003C:      0E  0_0_00_0_10_0:C2!=A0  NOT   C2,CF    ; EOR   C2,A0
003D:   34 0F  0_1_00_0_10_1:C2!=A0  MOV   C2,D3+4   ; MOV   C2,D3+4,A0
003F:   34 0E  0_1_00_0_10_0:C2!=A0  EOR   C2,D3+4   ; EOR   C2,D3+4,A0
0041:      DD  0_0_00_0_00_0:C2!=AD  ARG   D,D
0042:      1F  0_0_01_1_01_1:D1==D1  ???   D1      ; MOV   D1,D1
0043:      1E  0_0_01_1_01_0:D1==D1  NEG   D1      ; EOR   D1,D1
0044:      1D  0_0_01_1_01_0:D1==D1  STD   D1      ; DIS   D1,D1
0045:      1C  0_0_01_1_01_0:D1==D1  CLN   D1      ; CON   D1,D1
0046:      1B  0_0_01_1_01_0:D1==D1  SUB   D1      ; SUB   D1,D1
0047:      1A  0_0_01_1_01_0:D1==D1  ADD   D1      ; ADD   D1,D1
0048:06#34 5F  1_1_01_0_01_1:D1!=D5  ORD6  D1,D3+4,D5   ; MOV   D1,D3+4,D5
004C:   34 5E  0_1_01_0_01_0:D1!=D5  EOR   D1,D3+4,D5   ; EOR   D1,D3+4,D5
004E:   34 1F  0_1_01_1_01_1:D1==D1  LEA   D1,D3+4   ; MOV   D1,D3+4,D1
0050:   34 1E  0_1_01_1_01_0:D1==D1  RRC   D1,D3+4,D1   ; EOR   D1,D3+4,D1
0052:   34 1D  0_1_01_1_01_0:D1==D1  SAL   D1,D3+4,D1   ; DIS   D1,D3+4,D1
0054:   34 1C  0_1_01_1_01_0:D1==D1  SAR   D1,D3+4,D1   ; CON   D1,D3+4,D1
0056:   34 1B  0_1_01_1_01_0:D1==D1  SUB   D1,D3+4,D1   ; SUB   D1,D3+4,D1
0058:   34 1A  0_1_01_1_01_0:D1==D1  MUL   D1,D3+4,D1   ; ADD   D1,D3+4,D1
005A:      DB  0_0_01_0_01_0:D1!=Dd  ARG   D,B
005B:      5F  0_0_01_0_00_1:D1!=B5  OUT   D1,B5   ; MOV   D1,B5
005C:      5E  0_0_01_0_00_0:D1!=B5  EOR   D1,B5   ; EOR   D1,B5
005D:   34 5F  0_1_01_0_00_1:D1!=B5  LEX   D1,D3+4,B5   ; MOV   D1,D3+4,B5
005F:   34 5E  0_1_01_0_00_0:D1!=B5  EOR   D1,D3+4,B5   ; EOR   D1,D3+4,B5
0061:      CD  0_0_01_0_00_0:D1!=BC  ARG   C,D
0062:      5F  0_0_00_0_01_1:C2!=D5  MOV   C2,D5   ; MOV   C2,D5
0063:      5E  0_0_00_0_01_0:C2!=D5  EOR   C2,D5   ; EOR   C2,D5
0064:   34 5F  0_1_00_0_01_1:C2!=D5  ???   C2,D3+4,D5   ; MOV   C2,D3+4,D5
0066:   34 5E  0_1_00_0_01_0:C2!=D5  EOR   C2,D3+4,D5   ; EOR   C2,D3+4,D5
0068:      CC  0_0_00_0_01_0:C2!=Dc  ARG   C,C
0069:      2F  0_0_00_1_00_1:C2==C2  MOV   C2,C2   ; MOV   C2,C2
006A:      2E  0_0_00_1_00_0:C2==C2  NEG   C2,C2   ; EOR   C2,C2
006B:      2D  0_0_00_1_00_0:C2==C2  STD   C2,C2   ; DIS   C2,C2
006C:      2C  0_0_00_1_00_0:C2==C2  CLN   C2,C2   ; CON   C2,C2
006D:      2B  0_0_00_1_00_0:C2==C2  SUB   C2,C2   ; SUB   C2,C2
006E:      2A  0_0_00_1_00_0:C2==C2  ADD   C2,C2   ; ADD   C2,C2
006F:   34 2F  0_1_00_1_00_1:C2==C2  LEX   C2,D3+4,C2   ; MOV   C2,D3+4,C2
0071:   34 2E  0_1_00_1_00_0:C2==C2  RRC   C2,D3+4,C2   ; EOR   C2,D3+4,C2
0073:   34 2D  0_1_00_1_00_0:C2==C2  SAL   C2,D3+4,C2   ; DIS   C2,D3+4,C2
0075:   34 2C  0_1_00_1_00_0:C2==C2  SAR   C2,D3+4,C2   ; CON   C2,D3+4,C2

ТЕ ЕЩЁ МАРГИНАЛЫ
Как сказано выше, указание неверного указателя на аппаратном уровне обрабатываются в особенном виде, из-за чего программист может использовать эти резервные механизмы по своему усмотрению.
Однако, эти самые маргиналы местами просто слишком маргинальны.
Например, рассмотрим несколько странных инструкций:

  • код «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: Как можно подметить, термин «маргинал» в рамках текущих разработок используется как устоявшийся.
Вполне возможно, что вместо привычных «эскейпов» его вполне можно использовать…
Ответить