Минималистическая архитектура 16-битного процессора. Ревизия 2
Формат команды и порядок флагов не изменены. Всё, что в исходном описании было не определено, определено ниже. Изменения и добавления собраны в разделе 11.
1. Общие положения
- Размер слова 16 бит, адресное пространство 64 КБ (байтовая адресация), арифметика адресов по модулю 2^16.
- 16 регистров R0..R15, каждый шириной в слово. R0..R11 универсальные и равноправные (R0 не нулевой).
- Все операции над словами, если не оговорено противное.
- Команда занимает одно или два слова. Команды и данные выровнены по границе слова.
- Обращение к слову: бит 0 адреса игнорируется. Байтовые операции работают с любым адресом.
- Порядок байт little-endian: по чётному адресу A лежит младший байт слова, по A+1 старший.
- Бит 0 регистра IP всегда равен 0: при записи в IP он обнуляется.
Специальные регистры
| Регистр |
Имя |
Назначение |
| R12 |
LR |
регистр связи (пишется командой CALL) |
| R13 |
SP |
указатель стека, растёт вниз |
| R14 |
IP |
указатель команд |
| R15 |
FLAGS |
регистр флагов |
Аппаратно особыми являются только IP (автоматическая выборка), FLAGS (флаги АЛУ, прерывания) и SP/LR/IP в ограничениях разд. 6 и командах CALL/TRAP/IRET. Остальное обычные регистры.
2. Флаги
ZF, CF, SF, OF: стандартные флаги арифметики. IF: разрешение прерываний (0 запрещены, 1 разрешены).
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ZF | CF | SF | OF | IF | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- Биты 5..15 всегда читаются как 0, запись в них игнорируется.
- FLAGS полноправный регистр для чтения и записи (LD, ST, PUSH, POP, ALU). Установка и сброс флагов делаются битовыми операциями над ним:
OR R15,#$10 (STI), AND R15,#$FFEF (CLI). Константа $10 не помещается в 4-битную, поэтому такие команды двухсловные (MOD=3, R2=IP).
- Если результатом команды записывается R15 (R1=R15), флаги АЛУ автоматически не обновляются: R15 получает ровно записанное значение. CMP и TEST регистр не пишут, поэтому при R1=R15 они обновляют флаги как обычно.
Правила установки флагов
| Команды |
ZF |
SF |
CF |
OF |
| ADD, ADC |
результат=0 |
бит 15 результата |
перенос из бита 15 |
знаковое переполнение |
| SUB, SBC, CMP |
результат=0 |
бит 15 результата |
заём: 1, если беззнаково A < B (с учётом входного CF у SBC) |
знаковое переполнение |
| NEG |
результат=0 |
бит 15 |
1, если R1≠0 (заём) |
1, если R1=$8000 |
| AND, OR, XOR, TEST |
результат=0 |
бит 15 |
0 |
0 |
| SHL, SHR, SAR, ROR (счётчик ≠ 0) |
результат=0 |
бит 15 |
последний выдвинутый бит (у ROR бит 15 результата) |
0 |
| SHL, SHR, SAR, ROR (счётчик = 0) |
не меняются |
не меняются |
не меняются |
не меняются |
ADC: A+B+CF. SBC: A−B−CF. Для многословных операций цепочка ADD/ADC и SUB/SBC работает без инверсии CF.
Флаги не меняют: LD, LDSX, LDZX, ST, STB, XCHB, NOT, BSX, Jxx, CALL, WFI. TRAP сбрасывает IF, IRET загружает флаги со стека.
3. Формат команды
Слово команды (старший бит слева):
15 11 10 7 6 3 2 0
| OPCODE:5 | R1:4 | R2:4 | R2MOD:3 |
Если команде нужно слово disp, оно следует сразу за командным словом. Длина команды:
2 слова, если R2MOD ∈ {5, 6, 7} или (R2MOD = 3 и R2 = IP); иначе 1 слово.
Это правило применяется только к командам, использующим операнд R2/MOD. Для NEG, NOT, BSX, IRET, WFI поля R2 и R2MOD игнорируются полностью: команда всегда однословная, ошибок нет (рекомендуется кодировать нули).
Допустимость команды и её длина определяются только по первому слову.
Режимы R2MOD
| MOD |
Запись |
Значение операнда |
| 0 |
reg |
значение регистра R2 |
| 1 |
#imm4 |
поле R2 как беззнаковая константа 0..15 (R2 не регистр) |
| 2 |
[reg] |
слово (байт) по адресу R2 |
| 3 |
[reg++] |
то же, затем R2 += шаг. При R2=IP: слово после команды, IP += 2 (непосредственная константа) |
| 4 |
[--reg] |
R2 −= шаг, затем обращение по R2 |
| 5 |
[disp] |
слово (байт) по абсолютному адресу disp; поле R2 игнорируется |
| 6 |
[reg+disp] |
слово (байт) по адресу R2+disp |
| 7 |
reg+disp |
сама сумма R2+disp (без обращения к памяти) |
- Шаг равен 2 для словарных команд и 1 для байтовых (STB, LDSX, LDZX, XCHB).
- Обозначение «значение R2+MOD» ниже означает результат этой таблицы.
- Для MOD=1 у команд Jxx и CALL см. разд. 7. MOD=7 с R2=IP даёт адрес относительно следующей команды.
- Команда
LD R1, R2+disp (MOD=7) выполняет роль LEA.
4. Порядок выполнения команды (микропрограмма)
- Выбрать командное слово, IP += 2.
- Проверить допустимость (разд. 6). Недопустимо: ERRCMD, никаких побочных эффектов (разд. 8).
- Если команда двухсловная, выбрать второе слово, IP += 2. С этого момента IP равен адресу следующей команды, и именно это значение видит любая команда, читающая IP (в том числе R2=IP в MOD=6, 7 и операнд IP в ALU, ST, PUSH).
- Считать значение R1, если команда его использует. Источник R1 фиксируется до любых модификаций R2.
- Вычислить операнд по MOD. Автоинкремент или автодекремент R2 происходит здесь.
- Обращение к памяти (чтение, запись или атомарный обмен).
- Выполнить операцию, записать результат в R1, обновить флаги.
Следствия:
- При совпадении R1 и R2 запись результата в R1 выигрывает у автоинкремента/автодекремента:
LD R1,[R1++] оставляет в R1 загруженное значение, POP SP (LD SP,[SP++]) загружает SP из стека.
PUSH SP кладёт старое значение SP. ST R1,[R1++] и ST R1,[--R1] записывают старое значение R1.
LD IP,[IP++] (JMP #imm) переходит на непосредственное значение.
- Автоинкремент выполняется также у Jxx при невыполненном условии, но разд. 7 запрещает такие режимы.
- Команда атомарна по отношению к прерываниям: IRQ принимается только между командами.
5. Таблица опкодов
Опкод находится в старших 5 битах слова. Декодер микропрограммный, свойства команды (байтовая, пишет R1, читает память, допустимые MOD) берутся из ПЗУ по 5-битному опкоду.
| Код |
Мнемоника |
|
Код |
Мнемоника |
| 00000 |
LD |
|
10000 |
CMP |
| 00001 |
LDZX |
|
10001 |
TEST |
| 00010 |
LDSX |
|
10010 |
SHL |
| 00011 |
XCHB |
|
10011 |
SHR |
| 00100 |
ST |
|
10100 |
SAR |
| 00101 |
STB |
|
10101 |
ROR |
| 00110 |
резерв |
|
10110 |
NEG |
| 00111 |
резерв |
|
10111 |
NOT |
| 01000 |
ADD |
|
11000 |
BSX |
| 01001 |
ADC |
|
11001 |
Jxx |
| 01010 |
SUB |
|
11010 |
CALL |
| 01011 |
SBC |
|
11011 |
TRAP |
| 01100 |
AND |
|
11100 |
IRET |
| 01101 |
OR |
|
11101 |
WFI |
| 01110 |
XOR |
|
11110 |
резерв |
| 01111 |
резерв |
|
11111 |
резерв |
Резервные опкоды вызывают ERRCMD.
6. Допустимые режимы (сводка)
6.1. Допустимые MOD по группам
| Группа |
Опкоды |
Допустимые MOD |
| Чтение слова / значение |
LD, ADD, ADC, SUB, SBC, AND, OR, XOR, CMP, TEST, SHL, SHR, SAR, ROR |
0..7 |
| Байтовая загрузка |
LDZX, LDSX |
0..6 (MOD=7 запрещён) |
| Запись, обмен |
ST, STB, XCHB |
2..6 |
| Передача управления |
Jxx, CALL, TRAP |
0, 1, 2, 3 (только R2=IP), 5, 6, 7 |
| Без операнда |
NEG, NOT, BSX, IRET, WFI |
любые (игнорируются) |
6.2. Дополнительные запреты (для всех команд с операндом)
Нарушение любого пункта вызывает ERRCMD.
- R2=R15 при MOD ∈ {2, 3, 4, 6, 7}. (При MOD=0 R15 читается как значение, при MOD=1 и 5 поле R2 не регистр.)
- R2=IP при MOD=4.
- R2=IP при MOD=3 допустим только в группах «чтение слова» и «передача управления» (непосредственная константа). Для байтовых загрузок, ST, STB, XCHB запрещён.
- R2=IP при MOD=2 запрещён для ST, STB, XCHB.
- Байтовые команды (LDZX, LDSX, STB, XCHB): MOD=3 или 4 при R2=SP или R2=IP.
- Jxx с кодом условия 1111.
Смысл MOD для байтовых загрузок: MOD=0 берёт младший байт регистра R2 и расширяет (LDZX R1,R2 это R1=R2&$FF, LDSX R1,R2 то же что BSX с копированием), MOD=1 расширяет константу, остальные читают байт из памяти.
7. Описание команд
7.1. Загрузка, выгрузка, обмен
- LD R1, op: R1 = значение операнда (слово). Флаги без изменений.
- LDZX / LDSX R1, op: R1 = байт операнда с расширением нулём / знаком. Флаги без изменений.
- ST op, R1: слово R1 по адресу операнда. Флаги без изменений.
- STB op, R1: младший байт R1 по адресу операнда. Флаги без изменений.
- XCHB op, R1: атомарный обмен: R1 = (нулевое расширение прежнего байта памяти), память = прежний младший байт R1. Шина захвачена на всё время чтения и записи, прерывания не принимаются. Флаги без изменений.
Запись байта и XCHB на 16-битной шине требуют разрешения отдельных байтов либо чтения-модификации-записи.
7.2. Арифметика и логика
- ADD, ADC, SUB, SBC, AND, OR, XOR: R1 = R1 op операнд, флаги по разд. 2.
- CMP: как SUB, но R1 не меняется. TEST: как AND, но R1 не меняется.
- SHL, SHR (логические), SAR (арифметический вправо), ROR (циклический вправо): источник и результат R1, величина сдвига равна младшим 4 битам значения операнда (0..15). Сдвиг на 16 одной командой невозможен.
- NEG: R1 = 0 − R1, влияет на все флаги. NOT: R1 = ~R1, флаги не меняет (для проверки результата нужен TEST). BSX: знаковое расширение младшего байта R1 в слово, флаги не меняет.
7.3. Передача управления
Целью является значение операнда, то есть Jxx, CALL это загрузка IP с условием или с сохранением LR:
| MOD |
Цель перехода |
| 0 |
значение регистра R2 |
| 1 |
короткое знаковое смещение: IP + 2·sext4(R2), диапазон −8..+7 слов, IP это адрес следующей (однословной) команды |
| 2 |
слово по адресу R2 |
| 3 (R2=IP) |
слово после команды (абсолютный адрес) |
| 5 |
слово по адресу disp |
| 6 |
слово по адресу R2+disp |
| 7 |
R2+disp. При R2=IP: адрес относительно следующей команды |
- Jxx: в поле R1 код условия, флаги без изменений. Условие ложно: никаких действий, IP указывает на следующую команду.
- CALL: R1 игнорируется. Цель вычисляется, затем LR = IP (адрес следующей команды), IP = цель. Стек не используется, флаги не меняются.
- TRAP: R1 игнорируется. Номер вектора n = младшие 4 бита значения R2+MOD. Для TRAP MOD=1 это обычная константа 0..15, а не смещение. Действие: PUSH IP; PUSH FLAGS; IF=0; IP = слово по адресу $FF00 + 2·n. Сохраняется IP следующей команды. Допустимы любые n 0..15.
- IRET: POP FLAGS; POP IP. Флаги берутся целиком (включая IF).
- WFI: ожидание прерывания. Процессор останавливается до появления запроса IRQ (линии 0..7) либо сброса. При IF=1 запрос обрабатывается как обычно, возврат на команду после WFI. При IF=0 процессор просто продолжает со следующей команды, не входя в обработчик. Флаги без изменений.
Коды условий Jxx (R1)
| Код |
Мнемоника |
Условие |
| 0000 |
JMP |
безусловно |
| 0001 |
JZ / JE |
ZF=1 |
| 0010 |
JNZ / JNE |
ZF=0 |
| 0011 |
JC / JB |
CF=1 (ниже, беззнак.) |
| 0100 |
JNC / JAE |
CF=0 |
| 0101 |
JA |
CF=0 и ZF=0 |
| 0110 |
JBE |
CF=1 или ZF=1 |
| 0111 |
JS |
SF=1 |
| 1000 |
JNS |
SF=0 |
| 1001 |
JO |
OF=1 |
| 1010 |
JNO |
OF=0 |
| 1011 |
JL |
SF≠OF |
| 1100 |
JGE |
SF=OF |
| 1101 |
JG |
ZF=0 и SF=OF |
| 1110 |
JLE |
ZF=1 или SF≠OF |
| 1111 |
— |
резерв, ERRCMD |
8. Прерывания и исключения
8.1. Векторы
Таблица векторов находится по адресу $FF00..$FF1F: 16 слов, вектор n по адресу $FF00 + 2n, значение адрес обработчика.
| Вектор |
Назначение |
| 0..7 |
внешние запросы IRQ0..IRQ7 |
| 8 |
ERRCMD (недопустимая команда) |
| 9..15 |
не назначены аппаратно, свободны для программных вызовов TRAP |
(TRAP может вызывать и векторы 0..8.)
8.2. Внешние прерывания
- Запросы принимаются только между командами и только при IF=1. Проверка выполняется перед выборкой каждой команды, поэтому команда, установившая IF=1, не имеет «тени»: запрос принимается сразу после неё.
- Если ожидают несколько запросов, обслуживается запрос с меньшим номером (IRQ0 высший приоритет).
- Раздельного маскирования линий в ядре нет, оно реализуется во внешнем контроллере. Рекомендуется удержание запроса (по уровню) до обслуживания устройством.
- Сохраняемый IP это адрес команды, которая должна была выполниться следующей.
8.3. Единая последовательность входа (IRQ, TRAP, ERRCMD)
- SP −= 2; [SP] = IP (сохраняемый).
- SP −= 2; [SP] = FLAGS (значение до изменения).
- FLAGS.IF = 0 (остальные флаги не меняются).
- IP = [$FF00 + 2·n].
Выход IRET: POP FLAGS (с вершины), POP IP. Порядок на стеке совпадает с TRAP.
LR, регистры и остальные флаги прерыванием не сохраняются: обработчик обязан сохранить всё, что использует, включая LR.
8.4. ERRCMD (вектор 8)
- Причины: резервный опкод, Jxx с кодом 1111, нарушение любого пункта разд. 6.
- Не маскируется флагом IF. IF сбрасывается при входе как обычно.
- Все проверки по первому слову команды и выполняются до побочных эффектов: регистры (включая IP, автоинкремент), память и флаги остаются как до команды.
- Сохраняется IP = адрес первого слова недопустимой команды, чтобы обработчик мог её прочитать, разобрать или эмулировать. Чтобы пропустить команду, обработчик вычисляет её длину по правилу разд. 3 и корректирует сохранённый IP.
- Последовательность входа сама ERRCMD вызвать не может. Недопустимая команда внутри обработчика ERRCMD снова вызывает вектор 8 (защита от зацикливания лежит на программе).
9. Сброс
- Все регистры, включая SP, и FLAGS равны нулю: IF=0, прерывания запрещены. IP=0, управление передаётся по нулевому адресу.
- Первой командой нужно инициализировать SP (например, $FF00, так что стек растёт вниз под таблицей векторов). До этого стек использовать и прерывания разрешать нельзя.
- Рекомендуемая карта памяти: ПЗУ с кодом запуска с адреса $0000, ОЗУ, таблица векторов $FF00..$FF1F (должна находиться в ОЗУ или ПЗУ), области ввода-вывода $FF20..$FFFF. Ввод-вывод отображён на память.
10. Псевдокоманды и типовые приёмы
| Запись |
Эквивалент |
| MOV Rd, Rs |
LD Rd, Rs |
| MOV Rd, #imm |
LD Rd, [IP++] (двухсловная) |
| LEA Rd, Rs+disp |
LD Rd, Rs+disp (MOD=7) |
| NOP |
LD R0, R0 |
| RET |
LD IP, LR |
| PUSH R |
ST [--SP], R |
| POP R |
LD R, [SP++] |
| JMP addr |
Jxx 0000, адрес (MOD=3 при R2=IP) |
| Короткий переход |
Jxx cond, #off (MOD=1, ±8 слов) |
| Относительный переход |
Jxx cond, IP+disp (MOD=7, R2=IP) |
| STI / CLI |
OR R15, #$0010 / AND R15, #$FFEF |
| LDZX/LDSX Rd, Rs |
расширение младшего байта регистра (MOD=0) |
11. Что изменено и добавлено относительно исходного описания
Не менялись: формат слова команды, порядок флагов, регистры, режимы MOD 0..7, набор исходных опкодов, механика прерываний и сброса.
Уточнения
- Явная раскладка полей (старший бит слева) и правило длины команды, вычисляемое по первому слову.
- Значение IP при исполнении: адрес следующей команды после выборки disp. Определены PUSH IP, ST IP, CALL, TRAP.
- Порядок микрошагов и приоритет записи R1 над автоинкрементом при совпадении R1 и R2.
- Флаги: заём в SUB/SBC/CMP, правила для NEG, сдвигов (CF = последний выдвинутый бит, OF=0, счётчик 0 флагов не трогает), поведение CMP/TEST при R1=R15.
- XCHB: результат в R1 с нулевым расширением, атомарность.
- Биты 5..15 FLAGS и бит 0 IP зафиксированы нулевыми.
- ERRCMD: сохраняется адрес недопустимой команды, проверка по первому слову без побочных эффектов, перечень причин, векторы 9..15 свободны.
- SP при сбросе нулевой (исправлено противоречие «все регистры кроме SP» и «указатель стека нулевой»).
- Приоритет и момент приёма внешних прерываний, отсутствие «тени» после установки IF.
- Карта памяти (рекомендуемая), явная запись LEA как LD с MOD=7.
Изменения поведения (проверьте)
- Байтовые команды запрещают MOD=3,4 с SP и IP единообразно (раньше для STB, XCHB запрет был только для IP, а SP допускался и ломал выравнивание).
- MOD=4 с IP запрещён для всех команд, MOD=3 с IP только для чтения слова и передачи управления, MOD=2 с IP запрещён для записи.
- У Jxx и CALL режимы 3 (кроме IP) и 4 запрещены (побочные эффекты при условном переходе недопустимы).
- LDSX/LDZX допускают MOD=0 и 1 (расширение регистра и константы), MOD=7 запрещён.
- У Jxx и CALL MOD=1 это короткое знаковое смещение относительно IP (раньше режим для переходов не определён, а абсолютный адрес 0..15 бесполезен). У TRAP MOD=1 константа.
Дополнения
- Таблица кодировки опкодов (разд. 5) и сводка допустимых режимов (разд. 6).
- Команда WFI (опкод 11101): ожидание прерывания без цикла опроса.