Машина: RISC5
RISC5 — процессор, который Вирт спроектировал для Project Oberon, когда решил, что коммерческие процессоры стали слишком сложными, чтобы объяснить их студентам. Описание занимает около сорока страниц, реализация — примерно двести строк Verilog.
Регистры
Шестнадцать регистров по 32 бита: R0…R15. Все равноправны на уровне
железа; разделение труда — соглашение компилятора, и оно записано прямо в
ORG.Mod:
| регистр | роль |
|---|---|
R0…R11 |
вычисления, параметры, временные значения |
R12 (MT) |
таблица модулей; через неё же уходят ловушки |
R13 (SB) |
статическая база: начало данных текущего модуля |
R14 (SP) |
вершина стека |
R15 (LNK) |
адрес возврата из процедуры |
Плюс четыре флага — N (знак), Z (ноль), C (перенос), V (переполнение) —
и отдельный регистр H, куда попадает старшее слово произведения и остаток от
деления.
Счётчик команд адресует слова, а не байты, и он двадцатидвухбитный. Значит адресное пространство — четыре миллиона слов, шестнадцать мегабайт; реальной памяти в системе один мегабайт.
Четыре формата команд
Вся система команд укладывается в четыре формата, различаемых двумя старшими битами.
F0 00uv | a | b | op | (не исп.) | c регистр-регистр
F1 01uv | a | b | op | im (16) регистр-непосредственное
F2 10uv | a | b | off (20) чтение и запись памяти
F3 11uv | cond | off (24) переход
Поля: a — приёмник, b и c — операнды, op — код операции, u и v —
модификаторы. Операций ровно шестнадцать:
MOV LSL ASR ROR AND ANN IOR XOR ADD SUB MUL DIV FAD FSB FML FDV
ANN — это «и-не»: b AND NOT c. Умножение и деление занимают десятки тактов
и на это время останавливают конвейер. Четыре последние — арифметика с
плавающей точкой, одинарной точности, прямо в железе.
Биты u и v уточняют операцию там, где это нужно:
ADDсu=1— сложение с переносом,SUBсu=1— вычитание с заёмом;MULсu=1— смешанное умножение,DIVсu=1— беззнаковое деление;MOVсu=1даёт доступ к региструHи к регистру флагов;FADсu=1— преобразование целого в плавающее, сv=1— обратно.
Для большинства операций u и v не читаются вовсе. Это не догадка: мы
проверили исполнением — запустили каждую форму рядом с базовой на одних
операндах и сложили результаты по модулю два. Из тридцати двух сравнений
двадцать семь дали ноль.
Переходы
Формат F3 задаёт условие четырьмя битами. Старший бит инвертирует, младшие
три выбирают проверяемую величину:
| код | условие | код | условие |
|---|---|---|---|
| 0 | MI знак |
8 | PL |
| 1 | EQ ноль |
9 | NE |
| 2 | CS перенос |
10 | CC |
| 3 | VS переполнение |
11 | VC |
| 4 | LS ниже или равно |
12 | HI |
| 5 | LT меньше |
13 | GE |
| 6 | LE меньше или равно |
14 | GT |
| 7 | всегда | 15 | никогда |
Переход бывает по смещению и по регистру, со связью и без. «Со связью» значит,
что адрес следующей команды кладётся в R15 — так делается вызов процедуры.
Ловушки
Отдельной команды прерывания в RISC5 нет. Ловушка — это переход по регистру
MT со связью, а в неиспользуемые биты команды компилятор кладёт полезный
груз: в разряды 23–8 позицию в исходном тексте, в разряды 7–4 номер ошибки.
Железо эти биты не читает вовсе; их достаёт обработчик — прямо из команды, на
которой споткнулся.
Поэтому сообщение об ошибке выглядит так:
pos 6734 TRAP 4 in ORB at 0001EC10
Номер 4 означает разыменование NIL. Номер 1 — выход за границу массива: именно
эту проверку кодогенератор вставляет перед каждым индексированием.
Память и устройства
Карта простая:
| диапазон | что |
|---|---|
000000…0FFFFF |
ОЗУ, один мегабайт |
0E7F00…0FFFFF |
кадровый буфер, 1024×768 точек по одному биту |
FFE000…FFFFBF |
ПЗУ с загрузчиком |
FFFFC0…FFFFFF |
регистры устройств |
Устройств немного: таймер, светодиоды, последовательный порт, SPI для SD-карты, мышь и клавиатура. Мышь читается одним словом: координаты в младших двух полях по двенадцать бит, кнопки в разрядах 24–26, готовность клавиатуры — разряд 28.
Кадровый буфер устроен непривычно: строки хранятся снизу вверх. Первое слово буфера — это нижняя строка экрана. Наивная выкладка даёт перевёрнутую картинку, и на этом спотыкаются все, кто пишет для этой машины впервые.
Чего в машине нет
Нет блока управления памятью, нет колец защиты, нет разделения на режим ядра и режим пользователя. Любая команда может записать любое слово памяти. Нет кэша, нет предсказания переходов, нет внеочередного исполнения — время исполнения команды считается по таблице и всегда одно и то же.
Это не упущение, а позиция: машина должна помещаться в голову целиком. Чем это оборачивается на практике, вы увидите в лабораторной №4.