Вложений: 1
x80: CISC - уже не i8080, ещё не i8086
Легенда
Eщё со школы имею мечту построить собственный компьютер на собственном процессоре. Причём, архитектуру процессора 15 лет безуспешно пытался разработать с нуля. Но дальше RISC-процессора в LogiSim дело не сдвинулось.
Хотя, схему выборки CISC-команды из памяти разработать удалось.
Сейчас разрабатываю эмулятор средствами HTML5 на JavaScript, чтобы никому ничего не нужно было скачивать и работало онлайн.
Если первый эмулятор строился на «switch-case» и это значительно усложняло разработку системы команд, то потом разработал движок с использованием шаблона на принципах «casex» Verilog, где вся система команд описывается в таблице, а скрипт при инициализации парсит её и генерирует массив из тысяч комбинаций команд. Конечно, память расходуется значительно и наблюдаются существенные просадки производительности, но именно такой эмулятор удобен, когда окончательная таблица команд всё ещё не готова и постоянно модифицируется.
Идеология
Опыт коллег (PICO-8, MegaProcessor, MyCPU, Gigatron и т.д…) вдохновляет меня и собственными окольными путями пытаюсь построить собственный процессор без предрассудков: Мне не нужно экономить на простоте, чтобы где-то какой-то проводочек стал короче на миллиметр. И я не тороплюсь запустить архитектуру с колена, чтобы состряпать хоть что-нибудь и это поехало лампочками мигать или фракталы Мандельброта строить…
План прост по-идее, но сложен в перспективе, так как локально планируется переразработать всю линейку архитектуры Intel с нуля. А именно, так как i8080 и i8086 программно никак не совместимы, а система команд обоих оставляет желать лучшего, то поставил план разработать 8-битную архитектуру с расширением до 16 бит или до 32, причём с программной совместимостью в обе стороны…
Ошибки Intel прослеживаются с самого начала:
- На уровне машинного кода i8080 и i8086 абсолютно несовместимы
- На уровне ассемблера совместимость частичная, хоть и заявлялось о ней
- Поддерживаются рудиментарные команды (i8080: 32/3A - STA/LDA; i8086: A0/A1/A2/A3), которые все не перечислишь, но в современном защищённом режиме их наличие - бессмысленно
- Гибриды типа NEC V20 пытались поправить ситуацию, но всё оказалось безнадёжным
Тем самым, был просто взят процессор i8080 во всей его красе и система команд была перераспределена из восьмеричной в шестнадцатеричную и удалены команды с 16-битными константами. И даже можно увидеть ту же систему команд i8080:
Если внимательно присмотреться, то за основу я взял тактику i8080, где инструкцию «MOV M,M» превратили в «HLT»: Всю группу MOV-блока я сдвинул в начало и перегруппировал, чтобы та самая «HLT» заняла код «00». Так как в i8086 код «00» печальным образом занимает команда «ADD», что позднее в Windows разрослось в кучу ловушек, это послужило ценным опытом, как не надо делать на стадии проектирования дешифратора команд в угоду экономии проводочков, так как сейчас дешифрацией x86-кода занимается RISC-ядро и тактические экономические трюки инженеров в 70-х просто навсегда изувечили всю систему команд!
Также и имена регистров переименованы в стиль i8086, что значительно упрощает написание кода под мой процессор в стандартных отладчиках, как та же Visual Studio…
Тем самым, у меня получилось, что:- 00: MOV M,M = HLT
- 11: MOV BH,BH = Prefix BH/BP
- 22:MOV CH,CH = Prefix CH/SI
- 33:MOV DH,DH = Prefix DH/DI
- 44:MOV AL,AL = Prefix SP
- 55:MOV BL,BL = Prefix BL/BX
- 66:MOV CL,CL = Prefix CL/CX
- 77:MOV DL,DL = Prefix DL,DX
Чем для приложения даны 7 префиксов.
Для супервизора имеется «супер-префикс» с кодом «00», так как в «режиме ядра» операция «HLT» не нужна. Чем я убил двух зайцев:- Нету никаких привилегированных команд, которые приложению нельзя использовать. Тем самым, все коды таблицы команд приложение может использовать
- Только в «режиме ядра» этот «супер-префикс» доступен и не болтается мусорным защищённым кодом в системе команд у приложений
Архитектура
Хоть процессор и походит на продвинутый вариант i8080, но это не совсем так.
В режиме супервизора «супер-префикс» 00 даёт доступ к служебному регистру #0, который переключает регистровый файл процессора. Всего предусмотрено до 128 страниц регистрового файла и процессор может выполнять до 128 задач в кольце.
Регистровый файл предусмотрен как внешняя память статического ОЗУ из отдельных регистров. Можно использовать как интегральное ОЗУ одной микросхемой памяти на 32 Кб, а можно и организовать 256 отдельных регистров/ОЗУ на 128 байтов. То есть, в отладочном стенде файл контекста можно организовать из 256 регистров, чтобы в любой момент видеть его содержимое (как в Мегапроцессоре). А в практическом исполнении просто обойтись одной микросхемой памяти…
Как уже понятно, для доступа к РОН хранящихся во внешнем статическом ОЗУ требуются такты. В этом плане процессор работает как тот же 6502 и на простые операции требуется много тактов. С другой стороны, я над этим не беспокоюсь, так как систему кешов и конвейеров никто не отменял, что никак не мешает в перспективе ускорить архитектуру в разы.
(Я уже выше сказал, что не буду совершать ошибки Intel и искать оптимальные пути на этапе проектирования, так как RISC-ядро с лёгкостью может сгладить любые издержки производительности.
А так как это - CISC-процессор, то по-любому архитектура может быть довольно сложной и затратной. Оптимизировать я её не собираюсь!)
Процессор имеет несколько особенностей, которые усложняют его архитектуру, но приближает его к уровню 16-битных.
Так, используются «запретные комбинации» статуса АЛУ, которые никогда в нормальных условиях не встречаются:- ZF PF - SKIP-режим холостого чтения команд
- ZF SF - LOOP-режим выполнения команды несколько раз, один из РОН используется за счётчик
- ZF SF PF - WAIT-режим, тот же LOOP-режим с прерыванием
Так, операции с портами IN/OUT доступны лишь под режимом WAIT и порт, если он существует, вернёт сигнал готовности и прервёт WAIT-цикл. Если же порта аппаратно не существует и сигнала готовности нет, режимом WAIT будет использован один из РОН в качестве счётчика. Программа по обнулённому счётчику может знать, ответил порт или нет…
Так и цикл WAIT+SUB может использоваться как операция деления, так как WAIT прерывается по флагу CF. Аппаратно легко перехватить комбинацию WAIT+SUB или LOOP+ADD внешним сопроцессором и выполнить операции DIV/MUL быстро аппаратно. Тем самым, системой команд предусматривается внешний сопроцессор, но для его операций используются трюковые комбинации стандартных команд.
Режим SKIP упрощает описание «ленивых выражений» и условных кейсов. Если Вы помните DOS с его «INT 21h» с подфункцией через код в AH, то здесь SKIP работает примерно также. Например, комбинация «LOOP 5 + INT 21» не станет вызывать «INT 21» пять раз, а вызовет один раз, но переключится в режим «SKIP 5». Тем самым, если в подпрограмме имеется стопка из «JMP», то будет пропущено 5 «JMP» режимом «SKIP». Это делает решение гораздо изящнее…
Прерывания
Процессор при любом событии всегда переходит в одну заданную точку программы, но с разной SKIP-величиной.
- Обращение к INT 0-79
- Переключение памяти
- Переключение задач
- Запрос к устройству ввода-вывода
- Внешние маскируемые прерывания
- Сигнал немаскируемых прерываний
- Запуск ядра по сигналу RESET
Тем самым, по сигналу СБРОС процессор перепрыгнет в стандартную точку входу и пропустит семь JMP'ов в режиме «SKIP 7». Причём, СБРОС происходит не сразу, а по истечению интервала в служебных регистрах с отсчётом до 65535 тактов. Тем самым, сначала RESET срабатывает как немаскируемое прерывание и управление получает ядро. Если ядро нормально функционирует, то оно предустановит служебные счётчики и предотвратит СБРОС системы. Иначе, спустя указанное число от счётов управление снова получит ядро, но через точку запуска.
Приложения не могут напрямую обращаться к устройствам и операции «WAIT+IN/OUT» генерируют событие #4 ядру, которое уже само должно разбираться.
RISC vs RISC
Хоть процессор разрабатывается по принципам CISC-технологии (городи что хочешь), но он из неё умудрился вылезти!
И я просто переименовал его в RISC, который нужно читать не как «Reduced Instruction Set Computer», а «Rebused Instruction Set Computer». То есть, «Ребусная Система Команд», так как комбинации префиксов достигают уровня головоломки, которую тяжело переварить ассемблером и дизассемблером. Хотя эмулятор их исполняет корректно.
Потому, если Вы любитель простоты, то Вам моя технология не понравится и её можете игнорировать.
С другой стороны, если Вы - любитель головоломок и не прочь сломать мозг машинным кодом, то Вам может понравится сахар…
Сахар в машинном коде
- LOOP n + SKIP => SKIP n ; Цикл из n-раз превращается в игнорирование n-инструкций
- LOOP n + JMP => JMP & SKIP n ; Переход на метку и игнорирование n-инструкций
- LOOP n + CALL => CALL & SKIP n ; Вызов подпрограммы и игнорирование n-инструкций внутри подпрограммы
- LOOP n + INT => INT & SKIP n ; Вызов программного прерывания и игнорирование n-инструкций внутри подпрограммы
- LOOP n + RET => RET & SKIP n ; Возврат из подпрограммы и игнорирование n-инструкций
- LOOP n + MOV R,[IX±offset] => MOV R,[IX+Rn±offset] ; Вместо цикла индексный адрес формируется в индексно-относительный
- LOOP n + MOV [IX±offset],R => MOV [IX+Rn±offset],R ; Вместо цикла индексный адрес формируется в индексно-относительный
- LOOP n + NOP m => NOP n×m ; Команда NOP с задержкой на m-тактов выполняется n-раз
- LOOP n + ADD => MUL n ; Без сопроцессора умножение достигается сложением в цикле
- WAIT n + SUB => DIV n ; Без сопроцессора деление достигается циклом вычитания с прерыванием
- WAIT n + MOV => IN/OUT ; В ожидании команды межрегистровых пересылок превращаются в команды обращения к УВВ с ожиданием готовности
- LOOP n + MOV => MOV ctx ; В цикле команды межрегистровых пересылок обращаются к ячейкам регистрового файла активного контекста
- HLT + MOV M,R => MOV ctrl,R ; Под «супер-префиксом» можно записать регистр страницы контекста и переключить задачу
- LOOP n + HLT => EXIT n ; Запрос системы с выходом и передачей кода результата
- WAIT n + HLT => SYSCALL n ; Обращение к API системы
- LOOP + LOOP => reserved ; Теоретически работает, но назначения и логики не имеет
- LOOP + WAIT => reserved ; Теоретически работает, но назначения и логики не имеет
- WAIT + LOOP => reserved ; Теоретически работает, но назначения и логики не имеет
- WAIT + WAIT => reserved ; Теоретически работает, но назначения и логики не имеет
Можно видеть, что подобные трюки увели концепцию далеко от i8080/z80 и подтянули возможности до i80286…
Система команд напоминает ребус и не понравится тем, кто любит простые решения…
Код:
LOOP 3 ; Режим повтора 3 раза
CALL MyFn ; Повторить вызов нельзя - будет пропуск SubFn0, SubFn1, SubFn2
JMP Error ; Если подфункция отсутствует, RET вернётся на этот JMP
... ; Иначе - нормальное продолжение программы
MyFn:
JMP SubFn0 ; Выполнится по CALL MyFn
JMP SubFn1 ; Выполнится по LOOP 1 + CALL MyFn
JMP SubFn2 ; Выполнится по LOOP 2 + CALL MyFn
ADD AL,DL ; Выполнится по LOOP 3 + CALL MyFn
LOOP 1 ; Пропустим JMP Error
RET
Эмуляция
- Одна из первых версий эмулятора с Монитором в стиле РАДИО-86РК. Автоматически выводится дамп, прыгает шарик с очищением поля и построением рамки, после чего выводится фигурка Тетриса, управляемая клавиатурой
- Другая версия эмулятора. Клавиша F4 запускает эмуляцию. Сначала выводится дамп по «D000,3BF», затем выводится подсказка по директивам, а потом запускается режим эха клавиатуры, которое глючит. Баг очень сложен и связан с неполным пониманием механизма переключения контекста от Супервизора к приложению. Ядро эмуляции спланировано неверно и его нужно полностью перерабатывать. (Если буфер клавиатуры чист, чтение порта приводит к ожиданию циклом Wait без флага CF. Приложению этот флаг нужно передать в контекст, но на уровне JavaScript теряется контроль над синхронностью и приложению возвращаются неверные флаги…)
- В данный период дорабатывается версия эмулятора с путаницей переключения контекстов, так как я занимался внедрением «супер-префикса». Пустяковая доработка заняла три дня отладки и доработок алгоритмов ассемблера, дизассемблера и эмулятора. Но сама проблема пока не решена. Лишь упростились некоторые команды…
Наращивание
Как выше уже сказано, процессор спланирован как восьмибитный, но с перспективой расширения до 16 и 32.
Уже на восьми битах планируются трюки, где АЛУ может работать с 16 и 32 битами. Разница лишь в том, что в 8-битном прототипе на 32-битную операцию может уйти десятки тактов, тогда как в 32-битном прототипе - один такт.
Тем самым, на 8-битном прототипе уже можно разрабатывать 32-битные приложения и запускать их. Разница будет лишь в производительности, так как размер команд останется тем же: В отличии от i8080/z80 здесь нету 16-битных операндов, типа «LD SP,addr». Всё - 8-битное. И на 32-битном прототипе все индексы операндов - один байт.
Способы адресации
Код:
07 :MOV DL,[BX] ; Косвенно-регистровая адресация
67 :MOV DL,CL ; Регистровая адресация
A7 89:MOV DL,0x89 ; Непосредственная адресация
55 A7 89:MOV DL,[BX-119] ; Относительная адресация
E4 55 A7 89:MOV DL,[BX+AL-119] ; Индексная адресация
B8 FE 55 A7 89:MOVX DL,[BX+XX-119] ; Чтение в режиме X по индексу XX
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
0A :ADD AL,[BX] ; Косвенно-регистровая адресация
6A :ADD AL,CL ; Регистровая адресация
AA 89:ADD AL,0x89 ; Непосредственная адресация
55 AA 89:ADD BL,0x89 ; Непосредственная адресация
E4 55 AA 89:ADD4 BL,0x89 ; Сложение в режиме #4
B8 FE 55 AA 89:ADDX BL,0x89 ; Сложение в цикле режима X
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
81 23:PUSH 0x0123 ; Помещение константы в стек
91 23:PUSH 0xF123 ; Помещение константы в стек
55 81 23:PUSH 0x5123 ; Помещение константы в стек
55 91 23:PUSH 0xA123 ; Помещение константы в стек
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
55 A0 23:PUSH [BX+35] ; Относительная адресация
55 B0 23:POP [BX+35] ; Относительная адресация
E4 55 A0 23:PUSH [BX+AL+35] ; Индексная адресация
E4 55 B0 23:POP [BX+AL+35] ; Индексная адресация
B8 FE 55 A0 23:PUSX [BX+XX+35] ; Индексная адресация X по XX
B8 FE 55 B0 23:POPX [BX+XX+35] ; Индексная адресация X по XX
FPGA
Естественно, есть и Verilog-модель, выполняющая несколько команд.
Но, из-за плохого владения принципами Verilog, эскиз модели получился тупиковым и всё нужно переписывать с нуля…
Почему x80?
Почему из всего богатства архитектур я выбрал именно Intel?
Во-первых, с i8080 я знаком с самого детства через ВМ80 в любимом РАДИО-86РК.
Во-вторых, тот же ZX-Spectrum и GameBoy построены на Z80.
В-третьих, система команд CISC более дружелюбна, а i8086 намного легче реализовать, чем изучать 68000, так как с ним я никогда не работал непосредственно.
В-четвёртых, я не так силён в электронике, чтобы городить свой RISC с перспективой конвейерного исполнения 32 команд за такт.
Сотрудничество
Если Вы имеете возможность и способность управиться с Verilog/FPGA/CPLD, буду очень рад формированию хоть какого-то коллектива разработчиков линейки x80, x180, x280, x380…
Вложений: 6
Инструментальный Эмулятор x80 - alpha
Цитата:
Сообщение от
HardWareMan
Эм...
Всe инструкции видеть можно только в эмуляторе.
В архиве выложил скриншоты с таблицей команд, где можно чуточку посмотреть на некоторые комбинации и сочетания с префиксами.
Скриншоты, естественно, не все, так как комбинаций очень много. Некоторые инструкции я вручную перечеркнул графически, так как эмулятором они представлены как недокументированные и их нужно глушить как побочные.
Ещё в архив добавил весь файл эмулятора, где…- в строках 4160…4274 описывается вся система команд шаблоном
- в строках 4276…4377 описываются регистры, флаги и АЛУ-операции
- в строках 4653…4780 представлен знакогенератор РАДИО-86РК
- строками 4867…6386 описывается ассемблером некий программный код, но много под комментарием, так как скопировано из прошлой версии эмулятора и сейчас может не работать
В общем, если браузер позволит использовать функциональные клавиши, то…- F1 делает один шаг в отладке
- F4 запускает эмуляцию до точек отладки, помеченных розовым
- F6/F7/F8 настраивают производительность эмуляции
- ALT+P и ALT+T позволяет редактировать память или ячейки контекста
- клавиш много, но они полезны только мне, как активному разработчику…
Думаю, если просто любопытно глянуть на всё это, то увидите, сколько вложено усилий и никаких «copy-paste» из интернета. Успел понаписать столько, что самому жутко всё это отлаживать.
А если вздумаете вникнуть в этот исходник, то ничего не поймёте, так как и сам уже в нём теряюсь и правлю с опаской по принципу «работает - не трожь!»: Написано местами изящно (парсер шаблона всех команд), но в общем - очень безобразно!
RISC'ов много, а x80 - один
Цитата:
Сообщение от
Hunta
Можете не отвечать, это было моё последнее сообщение здесь.
Тeм не менее, я отвечу, как минимум для тех, кто думает так же, но не ставит крест на интересе к данной теме…
Цитата:
Сообщение от
Hunta
Поэтому это Вам привычно. А другим сходу будет нихера не понятно.
Пример понятного
Здесь критикуете именно синтаксис, так как в RISC-архитектурах традиционно и регистры не именуются, а индексируются. Так поступили и в IA-64 вводом регистров R8…R15.
Однако, здесь важно внимательнее присмотреться к примеру:
Код:
A4 7F|MOV R4,0x7F
55 A4 7F|MOV R4,[P5+0x7F]
E6 55 A4 7F|MOV R4,[P5+R6+0x7F]
Причём, в исходниках это прямо так и указано:
Код:
// Register descriptions
// Pointers
P1 BP BP BP
P2 SI SI SI
P3 DI DI DI
P4 SP SP SP
P5 BX BX DX
P6 CX CX CX
P7 DX DX DX
// Regular
R0 [BX] _BX_ [BX]
R1 BH BH BH
R2 CH CH CH
R3 DH DH DH
R4 AL AL AL
R5 BL BL BL
R6 CL CL CL
R7 DL DL DL
Тем самым, в эмуляторе синтаксис «ALU7 R4,R0» заменяется на «CMP AL,[BX]». И алгоритм там прорабатывался несколько лет /!\, чтобы индексы регистров привести к Intel-стилю и АЛУ-код представить именами операций.
Потому и код
Код:
MOV R0, R1
ADD R0, R1
изначально генерируется, но я приложил максимум усилий и трюков JavaScript с регулярными выражениями, чтобы нигде не чувствовался привкус RISC.
В этом плане, моя система команд не хуже того же байт-кода Java-машины. Но, если Java-апплет голым дампом вручную очень сложно набить, то как виртуальная байт-машина мой x80 всё же на порядок дружелюбнее!
Кстати, сейчас добавил пару строчек для экспорта шаблона…
Как результат, вот дешифратор всех команд на:
C/JavaScript: if…else
Код:
// 18b'0XX_0XX_0000_0000_0000:
if(0x0000 == ($IC & 0x4FFF)} {
// HLT
(CR(0,CR(0)|128)),_.EV = DO_ACCLAIM;
} else
// 18b'XXX_1XX_0000_0000_0000:
if(0x4000 == ($IC & 0x4FFF)} {
// PREFIX SUPER
return 0;
} else
// 18b'1XX_XXX_0000_0XXX_0XXX:
if(0x0000 == ($IC & 0x0F88)} {
// PREFIX R$X/P$Y
return 0;
} else
// 18b'XXX_X00_0XXX_0000_0000:
if(0x0000 == ($IC & 0x38FF)} {
// HLT R$Z
FH($Z | 0);
} else
// 18b'111_X00_0XXX_0XXX_0XXX:
if(0x8000 == ($IC & 0xB888)} {
// HLT P$Z/$Z
FH($Z | 8);
} else
// 18b'XXX_X00_0100_0000_0000:
if(0x0400 == ($IC & 0x3FFF)} {
// .$Y$X
return 0;
} else
// 18b'XXX_X00_1000_0000_0000:
if(0x0800 == ($IC & 0x3FFF)} {
// .$Y$X
return 0;
} else
// 18b'XXX_X00_1000_0XXX_0000:
if(0x0800 == ($IC & 0x3F8F)} {
// MOV [0],R$Y
CR(0,R$Y())+ FL(0),_.IE=0;
} else
// 18b'XXX_X10_0100_0XXX_0000:
if(0x2400 == ($IC & 0x3F8F)} {
// .$Y$X
return 0;
} else
// 18b'XXX_X10_1000_0XXX_0000:
if(0x2800 == ($IC & 0x3F8F)} {
// .$Y$X
return 0;
} else
// 18b'XXX_X10_0100_0000_0XXX:
if(0x2400 == ($IC & 0x3FF8)} {
// MOV R$X,[$Y]
R$X(CR($Y))+ FL(0),_.IE=0;
} else
// 18b'1XX_X10_0100_0XXX_0XXX:
if(0x2400 == ($IC & 0x3F88)} {
// MOV R$X,[R$Y]
R$X(CTX(R$Y()))+ FL(0),_.IE=0;
} else
// 18b'XXX_X10_0100_0XXX_0XXX:
if(0x2400 == ($IC & 0x3F88)} {
// MOV [R$X],R$Y
CTX(R$X(), R$Y())+ FL(0),_.IE=0;
} else
// 18b'101_X00_0XXX_0XXX_0XXX:
if(0x8000 == ($IC & 0xB888)} {
// XCHG R$Z,R$Y
$1=R$Z(),R$Z(R$Y()),R$Y($1);
} else
// 18b'110_X00_0XXX_0XXX_0XXX:
if(0x0000 == ($IC & 0xB888)} {
// XCHG P$Z,P$Y
$1=P$Z(),P$Z(P$Y()),P$Y($1);
} else
// 18b'XXX_X11_X100_0XXX_0000:
if(0x3400 == ($IC & 0x378F)} {
// .$Y$X
return 0;
} else
// 18b'XXX_X11_X100_0000_0XXX:
if(0x3400 == ($IC & 0x37F8)} {
// .$Y$X
return 0;
} else
// 18b'1XX_X11_X100_0XXX_0XXX:
if(0x3400 == ($IC & 0x3788)} {
// IN R$X
R$X(PORT(R$X()));
} else
// 18b'XXX_X11_X100_0XXX_0XXX:
if(0x3400 == ($IC & 0x3788)} {
// OUT R$X,R$Y
PORT(R$X(),R$Y());
} else
// 18b'XXX_XXX_0100_0XXX_1XXX:
if(0x0408 == ($IC & 0x0F88)} {
// BIT R$X,$Y
$1=FL(),FL($1&0xFE)|((R$X()>>$Y)&1),R$X(~(~R$X()|(1<<$Y)))/*>*/;
} else
// 18b'0XX_X00_0000_0XXX_0XXX:
if(0x0000 == ($IC & 0x3F88)} {
// MOV R$X,R$Y
R$X(R$Y());
} else
// 18b'XXX_X00_0XXX_0000_0XXX:
if(0x0000 == ($IC & 0x38F8)} {
// MOV R$X,[$Z]
R$X(CR($Z));
} else
// 18b'XXX_X00_0XXX_0XXX_0000:
if(0x0000 == ($IC & 0x388F)} {
// MOV [$Z],R$Y
CR($Z,R$Y());
} else
// 18b'0XX_X00_0100_0XXX_0XXX:
if(0x0400 == ($IC & 0x3F88)} {
// MOV P$X,P$Y
P$X(P$Y());
} else
// 18b'XXX_X10_0100_0XXX_1XXX:
if(0x2408 == ($IC & 0x3F88)} {
// -- Z$Y
$1=ALU$X(DROP($Y),REG($Y)),REG($Y,$1),FL($1.hi());
} else
// 18b'XXX_XXX_0XXX_0XXX_1XXX:
if(0x0008 == ($IC & 0x0888)} {
// ALU$X Z$Z,R$Y
$1=ALU$X(Z$Z(),R$Y()),Z$Z($1),FL($1.hi());
} else
// 18b'XXX_XXX_0100_1010_1XXX:
if(0x04A8 == ($IC & 0x0FF8)} {
// ADC P$X,IB
$1=ALU$X(DROP(FH()),_.B),ACC($1),FL($1.hi());
} else
// 18b'XXX_XXX_0XXX_1010_1XXX:
if(0x00A8 == ($IC & 0x08F8)} {
// ALU$X Z$Z,IB
$1=ALU$X(Z$Z(),_.B),Z$Z($1),FL($1.hi());
} else
// 18b'XXX_X00_0XXX_100X_XXXX:
if(0x0080 == ($IC & 0x38E0)} {
// PUSH $VIB
HEAP(0x$V00 + _.B);
} else
// 18b'XXX_XXX_0000_1110_1110:
if(0x00EE == ($IC & 0x0FFF)} {
// NOP
$Z;
} else
// 18b'XXX_XXX_0XXX_1110_1110:
if(0x00EE == ($IC & 0x08FF)} {
// NOP $Z
$Z;
} else
// 18b'XXX_X0X_0000_1111_1110:
if(0x00FE == ($IC & 0x2FFF)} {
// DBG 0
HEAP(_.IP)+IP(JP(10>$T?0:1));
_.IF=true;
} else
// 18b'XXX_X0X_0000_1111_1111:
if(0x00FF == ($IC & 0x2FFF)} {
// DBG
HEAP(_.IP)+IP(JP(10>$T?0:1));
_.IF=true;
} else
// 18b'111_XXX_0000_1100_1XXX:
if(0x80C8 == ($IC & 0x8FF8)} {
// INC Q$X
Q$X(Q$X()+1);
} else
// 18b'111_XXX_0000_1101_1XXX:
if(0x80D8 == ($IC & 0x8FF8)} {
// DEC Q$X
Q$X(Q$X()-1);
} else
// 18b'XXX_X00_0000_1100_1111:
if(0x00CF == ($IC & 0x3FFF)} {
// CMC
FL(FL() ^ 2);
} else
// 18b'111_XXX_0XXX_1100_1XXX:
if(0x80C8 == ($IC & 0x88F8)} {
// ADD Q$X,P$Z
$1=Q$X()+P$Z()+(_CF?0:0),$2=($1>>15)&2,FL((FL()& 0xD)|$2), Q$X($1);
} else
// 18b'111_XXX_0XXX_1101_1XXX:
if(0x80D8 == ($IC & 0x88F8)} {
// SUB Q$X,P$Z
$1=Q$X()-P$Z()-(_CF?0:0),$2=($1>>15)&2,FL((FL()& 0xD)|$2), Q$X($1);
} else
// 18b'XXX_XXX_0000_1101_1110:
if(0x00DE == ($IC & 0x0FFF)} {
// DOZ
CND7?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
} else
// 18b'XXX_XXX_0000_1101_1111:
if(0x00DF == ($IC & 0x0FFF)} {
// DONZ
CND6?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
} else
// 18b'XXX_XXX_0XXX_1101_111X:
if(0x00DE == ($IC & 0x08FE)} {
// ---
return 0;
} else
// 18b'XXX_XXX_0100_110X_1XXX:
if(0x04C8 == ($IC & 0x0FE8)} {
// ---
$1=ACC(ALU$W(ACC())),FL($1.hi());
} else
// 18b'XXX_XXX_0XXX_1100_1110:
if(0x00CE == ($IC & 0x08FF)} {
// ALU1F Z$Z
$1=Z$Z(ALU1F(Z$Z())),FL($1.hi());
} else
// 18b'XXX_XXX_0XXX_110X_1XXX:
if(0x00C8 == ($IC & 0x08E8)} {
// ALU$W Z$Z
$1=Z$Z(ALU$W(Z$Z())),FL($1.hi());
} else
// 18b'XXX_X00_0000_1010_0XXX:
if(0x00A0 == ($IC & 0x3FF8)} {
// MOV R$X,IB
R$X(_.B);
} else
// 18b'XXX_X00_X100_1011_0000:
if(0x04B0 == ($IC & 0x37FF)} {
// PUSH $+IB
HEAP(IP()+_.A);
} else
// 18b'XXX_X0X_0000_1011_0XXX:
if(0x00B0 == ($IC & 0x2FF8)} {
// CCND$X $+IB
CND$X?HEAP(IP())+IP(IP()+_.A):0;
_.IF=true;
} else
// 18b'XXX_XXX_0000_1100_0XXX:
if(0x00C0 == ($IC & 0x0FF8)} {
// INC R$X
$1=ADD(R$X(),1),R$X($1),FL($1.hi());
} else
// 18b'XXX_XXX_0000_1101_0XXX:
if(0x00D0 == ($IC & 0x0FF8)} {
// DEC R$X
$1=SUB(R$X(),1),R$X($1),FL($1.hi());
} else
// 18b'XXX_XXX_0XXX_1100_0XXX:
if(0x00C0 == ($IC & 0x08F8)} {
// ADD P$Z,R$X
$1=(P$Z()+R$X()),P$X($1),FL($1.hi());
} else
// 18b'XXX_XXX_0XXX_1101_0XXX:
if(0x00D0 == ($IC & 0x08F8)} {
// SUB P$Z,R$X
$1=(P$Z()+R$X()),P$X($1),FL($1.hi());
} else
// 18b'XXX_XXX_0XXX_1101_0XXX:
if(0x00D0 == ($IC & 0x08F8)} {
// DEC R$X
$1=SUB(R$X(),1),R$X($1),FL($1.hi());
} else
// 18b'XXX_X00_0XXX_1010_0000:
if(0x00A0 == ($IC & 0x38FF)} {
// POP [P$Z+IB]
DW(P$Z()+_.C,HEAP());
} else
// 18b'XXX_X00_0XXX_1010_0XXX:
if(0x00A0 == ($IC & 0x38F8)} {
// MOV R$X,[P$Z+IB]
R$X(DB(P$Z()+_.C));
} else
// 18b'XXX_X00_111X_1011_0000:
if(0x0EB0 == ($IC & 0x3EFF)} {
// PUSH [P$Z+IB]
HEAP(DW(P$Z()+_.C));
} else
// 18b'XXX_X00_0XXX_1011_0000:
if(0x00B0 == ($IC & 0x38FF)} {
// PUSH [P$Z+IB]
HEAP(DW(P$Z()+_.C));
} else
// 18b'XXX_X00_0XXX_1011_0XXX:
if(0x00B0 == ($IC & 0x38F8)} {
// MOV [P$Z+IB],R$X
DB(P$Z()+_.C,R$X());
} else
// 18b'XXX_X10_0000_1010_0XXX:
if(0x20A0 == ($IC & 0x3FF8)} {
// --- IB
return 0;
} else
// 18b'XXX_X10_0000_1011_0XXX:
if(0x20B0 == ($IC & 0x3FF8)} {
// --- IB
return 0;
} else
// 18b'XXX_X10_0XXX_1010_0XXX:
if(0x20A0 == ($IC & 0x38F8)} {
// MOV R$X,[P$Z+ACC+IB]
R$X(DB(P$Z()+ACC()+_.C)),FL(0),_.IE=0;
} else
// 18b'XXX_X10_0XXX_1011_0XXX:
if(0x20B0 == ($IC & 0x38F8)} {
// MOV [P$Z+ACC+IB],R$X
DB(P$Z()+ACC()+_.C,R$X()),FL(0),_.IE=0;
} else
// 18b'XXX_XXX_X100_1011_1XXX:
if(0x04B8 == ($IC & 0x07F8)} {
// --- IB
return 0;
} else
// 18b'XXX_X00_X100_1011_1010:
if(0x04BA == ($IC & 0x37FF)} {
// LEA +IB
DST(DST()+_.B);
} else
// 18b'XXX_X00_X100_1011_1XXX:
if(0x04B8 == ($IC & 0x37F8)} {
// MOV [U$X],IB
DB(U$X(),_.B);
} else
// 18b'XXX_X00_1110_1011_1000:
if(0x0EB8 == ($IC & 0x3FFF)} {
// WAIT
FL((FL() & 0x02) | 0x0D),trace.expression=0;
} else
// 18b'XXX_X00_1111_1011_1000:
if(0x0FB8 == ($IC & 0x3FFF)} {
// RET
IP(HEAP());
} else
// 18b'XXX_X0X_0XXX_1011_1000:
if(0x00B8 == ($IC & 0x28FF)} {
// JMP $+$UIB
IP(IP()+_.A);
_.IF=true;
} else
// 18b'XXX_XXX_0XXX_1011_1XXX:
if(0x00B8 == ($IC & 0x08F8)} {
// JCND$X $+$UIB
CND$X?IP(IP()+_.A):0;
_.IF=true;
} else
// 18b'XXX_XXX_1111_1011_1XXX:
if(0x0FB8 == ($IC & 0x0FF8)} {
// RCND$X
CND$X?IP(HEAP()):0;
} else
// 18b'1X1_X00_0000_1110_1XXX:
if(0x80E8 == ($IC & 0xBFF8)} {
// PUSH U$X
HEAP(U$X());
} else
// 18b'1X1_X00_0000_1111_1XXX:
if(0x80F8 == ($IC & 0xBFF8)} {
// POP U$X
U$X(HEAP());
} else
// 18b'XXX_X00_XXXX_1110_1111:
if(0x00EF == ($IC & 0x30FF)} {
// XCHG P$Z,[SP]
$1=P$Z(),P$Z(DW(SP())), DW(SP(), $1);
} else
// 18b'XXX_X00_0000_1110_0000:
if(0x00E0 == ($IC & 0x3FFF)} {
// SKIP
FH(1 | 8), FL((FL() & 0x02) | 0x05);
} else
// 18b'XXX_X10_0000_1110_0000:
if(0x20E0 == ($IC & 0x3FFF)} {
// SKIP ACC
FH(1 | 8), FL((FL() & 0x02) | 0x05);
} else
// 18b'XXX_X00_0000_1110_0XXX:
if(0x00E0 == ($IC & 0x3FF8)} {
// LOOP $X/R$X
FH($X | 8), FL((FL() & 0x02) | 0x09);
} else
// 18b'XXX_X00_0000_1110_1000:
if(0x00E8 == ($IC & 0x3FFF)} {
// DOC
CND5?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
} else
// 18b'XXX_X00_0000_1110_1001:
if(0x00E9 == ($IC & 0x3FFF)} {
// DONC
CND4?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
} else
// 18b'XXX_X00_0XXX_1110_1011:
if(0x00EB == ($IC & 0x38FF)} {
// LOOP [R$Z]
FH(R$Z()|8),FL((FL() & 0x02) | 0x09);
} else
// 18b'XXX_X00_0XXX_1110_1100:
if(0x00EC == ($IC & 0x38FF)} {
// LOOP R$Z
FH($Z), FL((FL() & 0x02) | 0x09);
} else
// 18b'XXX_X00_0XXX_1111_1011:
if(0x00FB == ($IC & 0x38FF)} {
// SKIP [R$Z]
FH(R$Z()|8),FL((FL() & 0x02) | 0x05);
} else
// 18b'XXX_X00_0XXX_1111_1100:
if(0x00FC == ($IC & 0x38FF)} {
// SKIP R$Z
FH($Z), FL((FL() & 0x02) | 0x05);
} else
// 18b'XXX_X00_0XXX_1111_1101:
if(0x00FD == ($IC & 0x38FF)} {
// SKIP $Z
FH($Z | 8), FL((FL() & 0x02) | 0x05);
} else
// 18b'100_X0X_0XXX_1111_XXXX:
if(0x00F0 == ($IC & 0xA8F0)} {
// INT $T
HEAP(_.IP)+IP(JP(10>$T?0:1));
_.IF=true;
} else
// 18b'XXX_XXX_1000_10XX_XXXX:
if(0x0880 == ($IC & 0x0FC0)} {
// Y$Y_X$X IB
return 0;
} else
// 18b'XXX_XXX_XXXX_XXXX_XXXX:
if(0x0000 == ($IC & 0x0000)} {
// Y$Y_X$X ($M)
0;
}
[свернуть]
Или на System Verilog:
Verilog
Код:
case(IC)
18b'0XX_0XX_0000_0000_0000: // HLT
//(CR(0,CR(0)|128)),_.EV = DO_ACCLAIM;
18b'XXX_1XX_0000_0000_0000: // PREFIX SUPER
//return 0;
18b'1XX_XXX_0000_0XXX_0XXX: // PREFIX R$X/P$Y
//return 0;
18b'XXX_X00_0XXX_0000_0000: // HLT R$Z
//FH($Z | 0);
18b'111_X00_0XXX_0XXX_0XXX: // HLT P$Z/$Z
//FH($Z | 8);
18b'XXX_X00_0100_0000_0000: // .$Y$X
//return 0;
18b'XXX_X00_1000_0000_0000: // .$Y$X
//return 0;
18b'XXX_X00_1000_0XXX_0000: // MOV [0],R$Y
//CR(0,R$Y())+ FL(0),_.IE=0;
18b'XXX_X10_0100_0XXX_0000: // .$Y$X
//return 0;
18b'XXX_X10_1000_0XXX_0000: // .$Y$X
//return 0;
18b'XXX_X10_0100_0000_0XXX: // MOV R$X,[$Y]
//R$X(CR($Y))+ FL(0),_.IE=0;
18b'1XX_X10_0100_0XXX_0XXX: // MOV R$X,[R$Y]
//R$X(CTX(R$Y()))+ FL(0),_.IE=0;
18b'XXX_X10_0100_0XXX_0XXX: // MOV [R$X],R$Y
//CTX(R$X(), R$Y())+ FL(0),_.IE=0;
18b'101_X00_0XXX_0XXX_0XXX: // XCHG R$Z,R$Y
//$1=R$Z(),R$Z(R$Y()),R$Y($1);
18b'110_X00_0XXX_0XXX_0XXX: // XCHG P$Z,P$Y
//$1=P$Z(),P$Z(P$Y()),P$Y($1);
18b'XXX_X11_X100_0XXX_0000: // .$Y$X
//return 0;
18b'XXX_X11_X100_0000_0XXX: // .$Y$X
//return 0;
18b'1XX_X11_X100_0XXX_0XXX: // IN R$X
//R$X(PORT(R$X()));
18b'XXX_X11_X100_0XXX_0XXX: // OUT R$X,R$Y
//PORT(R$X(),R$Y());
18b'XXX_XXX_0100_0XXX_1XXX: // BIT R$X,$Y
//$1=FL(),FL($1&0xFE)|((R$X()>>$Y)&1),R$X(~(~R$X()|(1<<$Y)))/*>*/;
18b'0XX_X00_0000_0XXX_0XXX: // MOV R$X,R$Y
//R$X(R$Y());
18b'XXX_X00_0XXX_0000_0XXX: // MOV R$X,[$Z]
//R$X(CR($Z));
18b'XXX_X00_0XXX_0XXX_0000: // MOV [$Z],R$Y
//CR($Z,R$Y());
18b'0XX_X00_0100_0XXX_0XXX: // MOV P$X,P$Y
//P$X(P$Y());
18b'XXX_X10_0100_0XXX_1XXX: // -- Z$Y
//$1=ALU$X(DROP($Y),REG($Y)),REG($Y,$1),FL($1.hi());
18b'XXX_XXX_0XXX_0XXX_1XXX: // ALU$X Z$Z,R$Y
//$1=ALU$X(Z$Z(),R$Y()),Z$Z($1),FL($1.hi());
18b'XXX_XXX_0100_1010_1XXX: // ADC P$X,IB
//$1=ALU$X(DROP(FH()),_.B),ACC($1),FL($1.hi());
18b'XXX_XXX_0XXX_1010_1XXX: // ALU$X Z$Z,IB
//$1=ALU$X(Z$Z(),_.B),Z$Z($1),FL($1.hi());
18b'XXX_X00_0XXX_100X_XXXX: // PUSH $VIB
//HEAP(0x$V00 + _.B);
18b'XXX_XXX_0000_1110_1110: // NOP
//$Z;
18b'XXX_XXX_0XXX_1110_1110: // NOP $Z
//$Z;
18b'XXX_X0X_0000_1111_1110: // DBG 0
//HEAP(_.IP)+IP(JP(10>$T?0:1));
//_.IF=true;
18b'XXX_X0X_0000_1111_1111: // DBG
//HEAP(_.IP)+IP(JP(10>$T?0:1));
//_.IF=true;
18b'111_XXX_0000_1100_1XXX: // INC Q$X
//Q$X(Q$X()+1);
18b'111_XXX_0000_1101_1XXX: // DEC Q$X
//Q$X(Q$X()-1);
18b'XXX_X00_0000_1100_1111: // CMC
//FL(FL() ^ 2);
18b'111_XXX_0XXX_1100_1XXX: // ADD Q$X,P$Z
//$1=Q$X()+P$Z()+(_CF?0:0),$2=($1>>15)&2,FL((FL()& 0xD)|$2), Q$X($1);
18b'111_XXX_0XXX_1101_1XXX: // SUB Q$X,P$Z
//$1=Q$X()-P$Z()-(_CF?0:0),$2=($1>>15)&2,FL((FL()& 0xD)|$2), Q$X($1);
18b'XXX_XXX_0000_1101_1110: // DOZ
//CND7?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
18b'XXX_XXX_0000_1101_1111: // DONZ
//CND6?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
18b'XXX_XXX_0XXX_1101_111X: // ---
//return 0;
18b'XXX_XXX_0100_110X_1XXX: // ---
//$1=ACC(ALU$W(ACC())),FL($1.hi());
18b'XXX_XXX_0XXX_1100_1110: // ALU1F Z$Z
//$1=Z$Z(ALU1F(Z$Z())),FL($1.hi());
18b'XXX_XXX_0XXX_110X_1XXX: // ALU$W Z$Z
//$1=Z$Z(ALU$W(Z$Z())),FL($1.hi());
18b'XXX_X00_0000_1010_0XXX: // MOV R$X,IB
//R$X(_.B);
18b'XXX_X00_X100_1011_0000: // PUSH $+IB
//HEAP(IP()+_.A);
18b'XXX_X0X_0000_1011_0XXX: // CCND$X $+IB
//CND$X?HEAP(IP())+IP(IP()+_.A):0;
//_.IF=true;
18b'XXX_XXX_0000_1100_0XXX: // INC R$X
//$1=ADD(R$X(),1),R$X($1),FL($1.hi());
18b'XXX_XXX_0000_1101_0XXX: // DEC R$X
//$1=SUB(R$X(),1),R$X($1),FL($1.hi());
18b'XXX_XXX_0XXX_1100_0XXX: // ADD P$Z,R$X
//$1=(P$Z()+R$X()),P$X($1),FL($1.hi());
18b'XXX_XXX_0XXX_1101_0XXX: // SUB P$Z,R$X
//$1=(P$Z()+R$X()),P$X($1),FL($1.hi());
18b'XXX_XXX_0XXX_1101_0XXX: // DEC R$X
//$1=SUB(R$X(),1),R$X($1),FL($1.hi());
18b'XXX_X00_0XXX_1010_0000: // POP [P$Z+IB]
//DW(P$Z()+_.C,HEAP());
18b'XXX_X00_0XXX_1010_0XXX: // MOV R$X,[P$Z+IB]
//R$X(DB(P$Z()+_.C));
18b'XXX_X00_111X_1011_0000: // PUSH [P$Z+IB]
//HEAP(DW(P$Z()+_.C));
18b'XXX_X00_0XXX_1011_0000: // PUSH [P$Z+IB]
//HEAP(DW(P$Z()+_.C));
18b'XXX_X00_0XXX_1011_0XXX: // MOV [P$Z+IB],R$X
//DB(P$Z()+_.C,R$X());
18b'XXX_X10_0000_1010_0XXX: // --- IB
//return 0;
18b'XXX_X10_0000_1011_0XXX: // --- IB
//return 0;
18b'XXX_X10_0XXX_1010_0XXX: // MOV R$X,[P$Z+ACC+IB]
//R$X(DB(P$Z()+ACC()+_.C)),FL(0),_.IE=0;
18b'XXX_X10_0XXX_1011_0XXX: // MOV [P$Z+ACC+IB],R$X
//DB(P$Z()+ACC()+_.C,R$X()),FL(0),_.IE=0;
18b'XXX_XXX_X100_1011_1XXX: // --- IB
//return 0;
18b'XXX_X00_X100_1011_1010: // LEA +IB
//DST(DST()+_.B);
18b'XXX_X00_X100_1011_1XXX: // MOV [U$X],IB
//DB(U$X(),_.B);
18b'XXX_X00_1110_1011_1000: // WAIT
//FL((FL() & 0x02) | 0x0D),trace.expression=0;
18b'XXX_X00_1111_1011_1000: // RET
//IP(HEAP());
18b'XXX_X0X_0XXX_1011_1000: // JMP $+$UIB
//IP(IP()+_.A);
//_.IF=true;
18b'XXX_XXX_0XXX_1011_1XXX: // JCND$X $+$UIB
//CND$X?IP(IP()+_.A):0;
//_.IF=true;
18b'XXX_XXX_1111_1011_1XXX: // RCND$X
//CND$X?IP(HEAP()):0;
18b'1X1_X00_0000_1110_1XXX: // PUSH U$X
//HEAP(U$X());
18b'1X1_X00_0000_1111_1XXX: // POP U$X
//U$X(HEAP());
18b'XXX_X00_XXXX_1110_1111: // XCHG P$Z,[SP]
//$1=P$Z(),P$Z(DW(SP())), DW(SP(), $1);
18b'XXX_X00_0000_1110_0000: // SKIP
//FH(1 | 8), FL((FL() & 0x02) | 0x05);
18b'XXX_X10_0000_1110_0000: // SKIP ACC
//FH(1 | 8), FL((FL() & 0x02) | 0x05);
18b'XXX_X00_0000_1110_0XXX: // LOOP $X/R$X
//FH($X | 8), FL((FL() & 0x02) | 0x09);
18b'XXX_X00_0000_1110_1000: // DOC
//CND5?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
18b'XXX_X00_0000_1110_1001: // DONC
//CND4?(FH(1 | 8), FL((FL() & 0x02) | 0x05)):0;
18b'XXX_X00_0XXX_1110_1011: // LOOP [R$Z]
//FH(R$Z()|8),FL((FL() & 0x02) | 0x09);
18b'XXX_X00_0XXX_1110_1100: // LOOP R$Z
//FH($Z), FL((FL() & 0x02) | 0x09);
18b'XXX_X00_0XXX_1111_1011: // SKIP [R$Z]
//FH(R$Z()|8),FL((FL() & 0x02) | 0x05);
18b'XXX_X00_0XXX_1111_1100: // SKIP R$Z
//FH($Z), FL((FL() & 0x02) | 0x05);
18b'XXX_X00_0XXX_1111_1101: // SKIP $Z
//FH($Z | 8), FL((FL() & 0x02) | 0x05);
18b'100_X0X_0XXX_1111_XXXX: // INT $T
//HEAP(_.IP)+IP(JP(10>$T?0:1));
//_.IF=true;
18b'XXX_XXX_1000_10XX_XXXX: // Y$Y_X$X IB
//return 0;
18b'XXX_XXX_XXXX_XXXX_XXXX: // Y$Y_X$X ($M)
//0;
[свернуть]
Естественно, это лишь примерный набросок / скелет, по которому уже нужно, так или иначе, описывать алгоритм работы каждой операции.
(Хотя, знатоки JavaScript уже поняли, что «R$X(…)» синтаксически легальна и может учитывать «X» как младшие 3 бита кода инструкции. Тем самым, немного пошаманив, можно допилить эмулятор так, чтобы всё работало именно как и описано в шаблоне - без всяких конвертаций «R$X()» -> «R4()» -> «AL()», что занимает уйму времени при открытии эмулятора.
Просто многие фишки JavaScript я не знал на момент разработки эмулятора и не догадался их использовать…)
Естественно, это значительно упростит в дальнейшем разработку, так как от порядка описания команд зависит вся работа в целом и нельзя перепутать местами несколько условий из-за строжайшего мажоритарного приоритета.
(В LogiSim-модели я немало мучался с индексированием инструкций, так как вручную тяжело придерживаться нужного логического порядка кодирования их…)
P.S.: Чтобы было легче проникнуться затеей, относитесь к ней к подобию виртуальной Java-машины с дружественным байт-кодом и легко реализуемой в FPGA или ТТЛ.
(В отличии от Java-процессора, который разрабатывали монстры индустрии и так до конца не справились с изначально поставленной задачей. Хоть первоначально и нацеливались на аппаратную реализацию с конкуренцией с Intel. Здесь я ни с кем конкурировать не собираюсь и систему команд продумываю без оглядки на ограничения в FPGA/ТТЛ. Хоть и реализация на ТТЛ-рассыпухие в перспективе и предусматривается…)
x80 - Процессор i8080 глазами Вангога
Напримeр, данный машинный код в 80-х любой школьник мог бы усвоить на раз-два:
Код:
x80: .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F
0000 A1 76 A5 D0 A4 4E CB D4 BF FC 00
Если лень думать…
Код:
Z80: .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F
0000 26 76 2E D0 3E 4E 23 3D 20 FC 76
[свернуть]
Пояснение…
Код:
0000 A1 76:ARG R1,0x76
0002 A5 D0:ARG R5,0xD0
0004 A4 4E:ARG R4,0x4E
0006 CB :INC BX
0007 D4 :DEC R4
0008 BF FC:BRF 0x0006
000A 00 :NUL/HLT
[свернуть]
(Всё равно, что на Z80 в цикле M1 код команды пропускать через ПЗУ с таблицей перекодировки - x80 так и планировался на первых парах…)
То есть, я как любитель дампов потратил десятилетия на проработку системы команд по принципу паззла: Смысл и логику не менял, но переставлял по ячейкам таблицы команд.
Просто, если человек не хочет ради прикола программировать дампом - это личный интерес каждого…
P.S.: Я всего лишь сделал то, как было бы в древние времена, когда микроскоп украшался узорами (пруф) и радиоприёмники были украшением (пруф).
Я подошёл к принципу реорганизации таблицы команд i8080 как эстет. Не более!
За это критикуете?
(Кто-то любит систему команд по-Пикассо, а я собрал систему команд по-Вангогу!)
i8080/8086 - перезагрузка!
Цитата:
Сообщение от
andrews
В чём её преимущества перед другими?
Кaк выше я и сказал: x80 - архитектура i8080, но с красивой таблицей команд.
Тем самым, преимущество, в первую очередь, именно в красоте.
А так как я никаким боком не инженер, то просто реинженерю i8080. Сначала я переделал известный эмулятор и переписывался с его автором. В его эмуляторе я просто добавил «case 0x40: case 0x49: case 0x52: …», то есть все холостые MOV-пересылки: mov b,b; mov c,c; mov d,d и т.д…
И эти холостые MOV я превратил в префиксы, чтобы код «40 86» работал как «add b,m» без участия аккумулятора.
Кто-то сказал, что идея - не плохая, так как куролесица с аккумуляторным АЛУ жутко напрягала. Но, на кристалле это выполнить в 70-е невозможно. Слишком дорого бы обошлось перехватывать коды холостых MOV. Потому обошлись инженеры лишь тем, что «mov m,m» превратили в «hlt».
Цитата:
Сообщение от
andrews
Для чего создается?
Чисто из интереса!
Сначала я пробовал накидать схему перехвата MOV-префиксов, чтобы проверить, правда ли сложно?
Потом решил причесать таблицу команд и вскоре понял, что переделывать эмулятор перебивкой сотен case каждый раз - очень тяжело.
Написал свой эмулятор с генератором команд по шаблону.
Так и «mov m,m» (hlt) переместился в позицию 00 и я начал активно прорабатывать идею уже как отдельную архитектуру.
Я выбросил все команды с адресом: «ld sp,addr», «ld hl,addr», «ld bc,addr», «ld de,addr», «ld a,addr», «ld a,(addr)», «ld hl,(addr)», «ld (addr),a», «ld (addr),hl» и «jp addr».
Если в мою таблицу вглядеться внимательнее, то по-своему - это совершенный i8080, каким он мог быть в 70-е с самого начала…
Система команд более ортогональна, чем у z80, так как префиксами там достигается вся гибкость почти уровня i8086.
Цитата:
Сообщение от
andrews
Проходит ли данная архитектура по "лезвию Оккама"?
С трудом догадываюсь, в чём вопрос.
Если не обращать внимания на префиксы, то x80 - тот же i8080, но, как выше сказал, без команд с кодами 01, 11, 21, 22, 2A, 31, 32, 3A, C2, C3, C4, CA, CC, D2, D4, DA, D4, E2, E4, EA, EC, F2, F4, FA, FC.
А так же и без кодов C0, C8, D0, D8, E0, E8, F0, F8 и D3, DB, F3, FB, 09, 19, 29, 39.
То есть, слишком редуцированный i8080, так как все те команды доступны уже через префикс.
Не проверял, проходит ли тест по Тьюрингу мой процессор в этом случае, так как схематически получается гораздо проще i8080 в реализации собачником.
По теме
Тему можно было назвать «i8080/8086 - перезагрузка!», так как я именно занимаюсь перезагрузкой этих архитектур, как Вы уже поняли.
Это как из i8086 вытрясти все 16-битные команды и сделать ремейк i8080, который может выполнять программы i8086.
То есть, из 16-битного i8086 сделать 8-битник (не i8088).
(А не гибрид типа NEC V20…)
Регистровый файл
Все регистры у аппаратного x80 должны быть во внешнем регистровом файле в виде статического ОЗУ.
Тем самым, даже команда «NOP» будет занимать десятки тактов из-за активного доступа к внешней статике.
То есть, сначала прочитать младший байт регистра адреса команды IP, затем - старший байт. Уже 4 такта.
Затем, выдать IP на шину адреса, прочитать код команды - ещё 8 тактов.
Потом выполнить инкремент IP и записать его в статику - ещё 12 тактов.
Как видите, прототип x80 в FPGA или ТТЛ будет очень медленным.
Здесь в пору заметить: Если Вы ждёте от прототипа x80 супер скорости, можете выходить из темы…
Резюме
Цель: Перезагрузить саму систему команд i8080, чтобы она могла в последствии стать 32-битной или 64-битной.
Причём, совместимость в обе стороны: 64-битные x80-программы можно запускать и на 8-битном x80, но производительность упадёт в сто раз!!!
Это сделано с той целью, что, используя современные технологии с супер-кешом, по-идее в перспективе можно будет добиться доступа к регистровому файла за 1 такт. А введя конвейер, ускорить исполнение команд в несколько раз. То есть, это уже уровень мастерства.
Сейчас главное (для меня) - просто добиться функциональной полноты системы команд, чтобы хотя бы в эмуляторе работало гладко.
x80 - член клуба шестнадцатеричного счисления
Цитата:
Сообщение от
b2m
Самое прикольное, что и система команд 8080 тоже хорошо ложится на восьмеричные коды.
O чём я и говорю. Когда в справочниках отца по микропроцессорам изучал комплекты серий типа 1801 и т.д…, то путался всегда в этих их восьмеричных адресах и вообще не понимал ничего.
(Вроде бы операции умножения проглядываются, но выглядело (после дампов РК) всё как-то несерьёзно: Спорное заявление, но это - дело вкуса…)
А с РАДИО-86РК и ASCII-таблицу вызубрил быстро, и в адресации стал разбираться.
Это наверное дело вкуса. Но, как я уже сказал, здесь x80 имеет редуцированную систему команд i8080, которая из восьмеричного порядка комплектации команд в таблице была приведена к шестнадцатеричному виду.
Не забывайте, что…
Таблица команд публикаций
…представлялась именно шестнадцатеричной таблицей. Да и дамп в Hex-представлении куда удобнее и компактнее на экране с 52 символами на строку.
И адрес F803 выглядит красивее и запоминается легче, чем 174003.
Человек, прежде всего, мыслит словами и звуками: F803 - Функция #8-03.
Наверное, здесь ещё играет роль особенности памяти индивидуума: Кто-то номера телефонов запоминает легко цифрами, а кто-то - буквами:
Например, у моего знакомого номер сотового я помню только потому, что он представляется как «-BABNIK» - «-222645». А своей племяннице сим-карту покупал на «-DOLLY-» - «-36559-».
(На владельцев блатных номеров, типа 111-11-11, я смотрю как на безграмотных гопников из 90-х…)
Даже в системе команд x86 меня напрягает поле «md-reg-r/m» как восьмеричное.
Пытался делать эмулятор i8086 с шестнадцатеричным полем операндов как «h-reg-l-r/m», чтобы голым дампом не нужно было в уме индекс регистра из восьмеричной системы переводить в нормальный Hex-вид…
P.S.: Потому, как ответвление от i8080, этот x80 именно шестнадцатеричный.