Так, x80 задумывался мною как нечто среднее между z80 и i8086. Но CISC довольно сложна в освоении реализации и там я наткнулся на множество подводных камней, хотя первые шаги и сделал.
Решил наконец перешагнуть через некоторые свои принципы и заняться RISC всерьёз.
А именно, сделать набросок простого (не простейшего) процессора, но с оригинальной системой команд и интуитивно ясным байт-кодом, чтобы не нужно было зубрить таблицу системы команд.
И я нашёл достаточно оригинальный вариант…
Мой подход максимально прост: Шестнадцатиричный код инструкции и является мнемоникой команды.
Code: Select all
Bytes|Assm | Краткое описание
----- ----- ------------------
00 HALT - Куда без неё?
01…99 1…99 - Шестнадцатиричные цифры используются как десятичные
0A…0F ALU - 0xA:Add; 0xB:suB; 0xC:Conjunction (AND); 0xD:Disjunction (OR); 0xE:Eor; 0xF:Flags (CMP)
A1…A9 A1…9 - Выбор одного из девяти регистров A
B1…B9 B1…9 - Выбор одного из девяти регистров B
C1…C9 C1…9 - Выбор одного из девяти регистров C
AA…CC R,T - Выбор пары приёмник-источник (Receiver,Translator pair)
AD ADDR - Фиксация значений приёмника-транслятора в регистре адреса
BE BE - Чтение из буфера памяти (Buffer Extract)
BF BF - Запись в буфер памяти (Buffer Flush)
DA…DF DA…F - Skip 1 Instruction by: Amount; Bigger; Carry; Dis-Carry; Equal; False
EB EB - Execute Buffer (Jump to Buffer Address)
Сначала схему я строил в Atanua, но потом перешёл в Logisim, так как он удобнее в работе с шинами. За неделю мне удалось набросать более-менее приличную схему, если сравнивать с предыдущим безобразием из путаницы проводников и логики по всему экрану. Сейчас выглядит схема более приличнее и многое расположено гораздо ровнее, чем было в первых вариантах.
А главное - это работает.
Самым сложным моментом было разработка узла чтения/записи памяти, так как первый вариант был аналогичным Гарвадскому. Теперь набор вентилей и сдвиговый регистр выполняют нормальный Принстонский функционал, на отладку которого ушло несколько ночей. Думаю, опыт пригодится в моём x80, когда руки дойдут до него.
Как это работает…
Можно из схемы заметить, что значительную часть составляет регистровый файл.
Конечно, можно было использовать готовый элемент памяти, но схему я разрабатывал так, чтобы можно было на ТТЛ всё собрать при возможности.
В Logisim все дешифраторы выдают активную логическую единицу, тогда как подавляющее большинство физических микросхем активным используют логический ноль.
Всего данный процессор способен выполнить пока 140 инструкций.
И на данный момент АЛУ имеет только функцию сумматора под кодом операции 0A, так как пока нет нужды во всех операциях. Хотя, как и в x80, операции будут те же…
(0A:Add, 0B:suB, 0C:Conjunction/AND, 0D:Disjunction/OR, 0E:Eor, 0F:diFference/CMP)
Например:
Code: Select all
0000 BA ; Выбор пары B,A
0001 B9 ; Выбор регистра B9
0002 64 ; Загрузить десятичное 64 (0x40) в регистр B9
0003 CB ; Выбор пары C,B
0004 C8 ; Выбор регистра C8
0005 30 ; Загрузить десятичное 30 (0x1E) в регистр C8
0006 AD ; Поместить в указатель адреса значения C8 и B9: Addr = (C8 << 8) | B9
0007 A1 ; Выбор регистра A1
0008 AB ; Выбор пары AB
0009 BE ; Прочитать память и сохранить данные: A1 = [Addr] или A1 = [0x1E40]
000A 00 ; Останов
Реализовать стек программно теоретически возможно уже сейчас.
Code: Select all
0000 A9 ; A9 ; Чтобы организовать цикл, нужно настроить указатель
0001 B9 ; B9 ; Выбираем регистры A9 B9
0002 AA ; A,A ; Эти аргументы помогут очистить регистр A9
0003 0E ; EOR ; (EOR A9,A9) посредством Исключающего ИЛИ
0004 BA ; B,A ; Теперь загрузим адрес единственной метки
0005 16 ; 16 ; Сейчас B9,A9 имеют значения 0x10,0x00
0006 AB ; A,B ; Формируем корректный адрес: 0x00,0x10
0007 AD ; ADDRESS ; Загружаем в указатель буфера 0x0010 из A9,B9
0008 A8 ; A8 ; Теперь настроим регистры для циклического счёта
0009 B8 ; B8 ; A8 будет счётчиком, а B8 будет хранить предел
000A AA ; A,A ; Очистим будущий счётчик
000B 0E ; EOR ; (EOR A8,A8) традиционным способом
000C BA ; B,A ; Чтобы загрузить B8, нужно выбрать аргументы
000D 99 ; 99 ; (MOV B8,99) Теперь B8 == 0x63
000E B1 ; B1 ; Здесь будет храниться шаг инкремента
000F 01 ; 1 ; (MOV B1,1)
0010 ;;;; ; Этот адрес мы указали в начале программы - прошло 32 такта
0010 AB ; A,B ; Указываем порядок аргументов
0011 A8 ; A8 ; Чтобы обеспечить инкремент
0012 B1 ; B1 ; как A8 += B1
0013 0A ; ADD ; (ADD A8,B1)
0014 B8 ; B8 ; Регистр предела
0015 0F ; CMP ; (CMP A8,B8) Операция сравнения
0016 DC ; DC ; (Do if Carry) Операция проверки флага переноса
0017 EB ; EB ; Если переноса нет, эта команда (Execute Buffer) будет пропущена - 16 тактов + 2
0018 00 ; HLT ; Останов - всегда 2 такта