Так, 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 такта 
				

