Eсли x80 мною разрабатывается долго и нудно как CISC с системой команд интуитивного кодирования, то данный здесь процессор представляется RISC-архитектурой со «сквозным интуитивным» набором команд.
Процессор выполнен в русле акына - «что вижу, то имею» - WYSYWYG на уровне байт-кода. Тем самым, для программирования на нём не нужен никакой транслятор.
Система команд- 0x00 означает HLT (как и в x80)
- 0x01…0x09, 0x10…0x19, 0x20…0x29, 0x30…0x39, 0x40…0x49, 0x50…0x59, 0x60…0x69, 0x70…0x79, 0x80…0x89, 0x90…0x99 - непосредственный двоично-десятичный операнд 1…99, заносимый в текущий аккумулятор двоичной величиной 0x01…0x63
- 0x0A - ADD; 0x0B - SUB; 0x0E - EOR
- 0x0C - AND (Conjunction)
- 0x0D - OR (Disjunction)
- 0x0F - CMP (Flags)
- 0xA1…0xA9 - выбор индекса регистра A (A₁…A₉)
- 0xB1…0xB9 - выбор индекса регистра B (B₁…B₉)
- 0xC1…0xC9 - выбор индекса регистра C (C₁…C₉)
- 0xAA / 0xAB / 0xAC - выбор группы операндов: A,A; A,B; A,C
- 0xBA / 0xBB / 0xBC - выбор группы операндов: B,A; B,B; B,C
- 0xCA / 0xCB / 0xCC - выбор группы операндов: C,A; C,B; C,C
- 0xAD - занесение значений операндов в регистр адреса ADdress
- 0xBE - извлечение данных из буфера (Buffer Extract: register = [ADdress])
- 0xBF - заполнение буфера данными (Buffer Fill: [ADdress] = register)
- 0xCF - инверсия флага CF (Carry Flag)
- 0xD0 - метка цикла DO (ADDR = IP)
- 0xDA / 0xDB / 0xDC / 0xDD / 0xDE / 0xDF - условное выполнение (Do if Among / Bigger / Carry / Discarry / Equal / Fiction)
- 0xEB - переход на адрес (Execute Buffer pointed by ADdress)
То есть, как и в x80, здесь та же самая концепция: Какими символами машинный код кодируется, такая и аббревиатура команды.
Практически, это и есть вся система команд!
(Инструкция EB комбинированная: Вместе с переходом на ADdress, она помещает адрес возврата в ADdress. А так как этот регистр доступен только на запись командой AD, то сбрасываются индексы регистров на B₀ и C₀, через которые можно и прочитать регистр ADdress.)
Например, такая программа:
Код:
// Исходный код
*(BYTE *)0x76D0 = '*';
// Промежуточный код
*(BYTE *)MAKEWORD(0xD0, 0x76) = 0x2A;
// Развёрнутый код
*(BYTE *)(AD = MAKEWORD(B8 = 52 << 2, A9 = 59 << 1)) = (A1 = 40);
кодируется таким дампом:
Код:
.0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F
0000 A9 B8 AA 59 0A BB 52 0A 0A AB AD A1 40 BF 00 ..
И по байтам её можно представить вот так:
Код:
0000 A9 : A₉ ; Using A#9
0001 B8 : B₈ ; Using B#8
0002 AA : A₉,A₉ ; Reciever A#9, Translator A#9
0003 59 :SET A₉,59 ; Load 59 to receiver A#9
0004 0A :ADD A₉,A₉ ; A₉=118=0x76
0005 BB : B₈,B₈ ; Receiver B#8, Translator B#8
0006 52 :SET B₈,52 ; Load 52 to receiver B#8
0007 0A :ADD B₈,B₈ ; B₈=104=0x68
0008 0A :ADD B₈,B₈ ; B₈=208=0xD0
0009 AB : A₉,B₈ ; Hi-Byte A#9, Lo-Byte B#8
000A AD :ADR A₉,B₈ ; ADDR=MAKEWORD(B₈,A₉)
000B A1 : A₁ ; Receiver A#1
000C 40 :SET A₁,40 ; A₁ = 0x2A = '*'
000D BF :BF A₁ ; Buffer Flush
000E 00 :HLT ; Halt
Ниже в архиве исходные файлы для LogiSim.
У кого симулятора нет - снимок схемы.
(Сначала я начал писать JavaScript-эмулятор. Но идея задуманного процессора показалась настолько простой, что вместо часов писанины в Блокноте я потратил недели в LogiSim и остался доволен.
Потому, могу огорчить далёких от электроники пользователей: Эмулятора нет, так как занимаюсь эмулятором x80 и нет сил заниматься другими проектами! Хотя если я решу таки строить x80 на RISC-ядре, то эмулятор данного простейшего дружелюбного процессора войдёт в состав эмулятора.)
Некоторые нюансы
Сначала схема была выполнена по Гарвадской архитектуре, код программы хранился в отдельном банке памяти и на выполнение одной инструкции уходило по одному такту. Естественно, подобных готовых схем в интернете пруд пруди и я решил немного усложнить себе жизнь. Собственно, Пристонскую архитектуру работы с памятью я реализовывал несколько недель, так как очень долго въезжал в тему и отлаживал схемы. Естественно, я не пользовался никаким готовыми решения и гордо шагал по граблям!
Регистровый файл выполнен максимально развёрнуто из трёх дешифраторов и 27 регистров. Опять таки, можно было просто использовать готовые микросхемы памяти, но это было скучно и хотелось сделать максимально развёрнутый вариант процессора, где всё элементарно, понятно и просто, где каждый регистр подсвечивается лампочкой и может иметь отдельную шину данных для управления каким-нибудь устройством.
То есть, если собрать данный процессор собачником на ТТЛ-рассыпухе, то можно сократить размер регистрового файла и регистр C7 использовать драйвером двигателей, а регистр B6 заменить джойстиком!
Дешифратор команд выполнен так, чтобы обойтись одним вентилем 3-ИЛИ на команду. Тем самым, система команд легко наращивается программированием ПЗУ с добавлением любой группы команд в любой участок таблицы. Всего можно закодировать 24 различные группы команд. В схеме используется лишь 12.
Особенностью системы команд является именно сквозное смысловое кодирование, где шестнадцатеричное представление команды напоминает аббревиатуру команды. Если бы ни стремление достичь этого, данную схему я бы просто не взялся разрабатывать!
Это нужно понимать, если решите добавлять свои команды/!\
Была идея добавить ещё группу команд - 0x1A…0x1F, 0x2A…0x2F, 0x3A…0x3F, … … … … …, 0x9A…0x9F.
Они могут служить для повышения гибкости АЛУ-операций, где цифра выбирает индекс регистра-источника.
Например, сейчас нельзя выполнить сложение двух регистров одной группы, типа «ADD A₆,A₁».
Чтобы разрешить данные операции, можно было бы закодировать эту инструкцию кодом 0x1A: 0xAA 0xA6 0x1A.
Но, это требует добавления дополнительных мультиплексеров и усложняет схему.
А так как данная схема выполнена в стиле простоты и минимализма, то данной группы операций в ней не предусмотрено.
P.S.: Тем самым, как автор CISC-системы команд с эстетическим расположением инструкций, я реализовал и RISC-вариант с эстетическим набором команд.
Это я к тому, что и простое можно сделать красивым, не уродуя таблицу команд в угоду каких-то аппаратных издержек.
И на ТТЛ-макетке такой процессор можно собрать при практическом желании.