- Учебная платформа и симулятор для курса архитектуры компьтера - Разработана для практического изучения 5 принципиально разных архитектурных парадигм в рамках единого инструментария - [Сайт](https://wrench.edu.swampbuds.me) - [Исходники](https://github.com/ryukzak/wrench) ## Файлы конфигов Каждый тест программы описывается декларативным `.yaml` файлом - `limit` - максимальное число инструкций (защита от зацикливаний) - `memory_size` - размер адресного пространства (по умолчанию 8 Кб) - `memory_mapped_io` - начальное состояние портов - `0x80` - входной поток - `0x84` - выходной поток - `reports` - секция для вывода в логи ### slice Определяет какую часть журнала симуляции выводить - `slice: all` - состояние процессора на каждом шаге - `slice: last` - только финальное состояние на момент завершения ### Доступные метрики Указываются в секции `view` в `reports` - `{sim:instruction-count}` - число выполненных инструкций - `{layout:sections-size}` - декларированный размер секций `.text` и `.data` - `{mem:*-ranges}` - дипазоны адресов памяти, к которым реально обращался процессор - `{memory:table}` - полная карта памяти с процентом покрытия ### Блок проверки - Блок `assert` в секции `reports` отвечает за автоматическую проверку результата работы программы - `[Состояние до запуска] >>> [Ожидаемое состояние после остановки]` - Например, `numio[0x80]: [72, 101, 10] >>> []` (до запуска лежали три байта, после работы должен стать пустым) - Или, `symio[0x84]: "" >>> "HELLO"` - Что можно проверять - Потоки ввода-вывода (числовое представление `numio` или строковое представление `symio` в ASCII) Непечатные символы (ASCII код меньше 32) заменяются на знаки вопроса `?` - Состояние памяти (`mem[0..31]: 05 48 45 ...`) - Регистры и счетчики процессора ## Стандарт ввода-вывода MMIO Во всех архитектурах чтение/запись внешних устройств настроены абсолютно одинаково - Адрес `0x80` - входной поток - очередь FIFO. При чтении процессором выдает очередной элемент из тестового массива - Адрес `0x84` - выходной поток. Принимает 32-битные слова. Запись любого числа отправляет его в выходной поток ## Порядок байтов и адресация Адресация побайтовая, то есть один адрес - один байт. Например, в 32-битной архитектуре чтение по `0x80` захватывает сразу `0x80, 0x81, 0x82, 0x83`. Порядок - `Little Endian` ## Крайние случаи ### Запись во входной порт (0x80) - `load_imm 42` $\to$ `store_addr 0x80` - Программа не упала, продолжила работу. Данные исчезли навсегда - Контроллер порта `0x80` работает только на чтение ### Чтение из выходного порта (0x84) - Попытались сделать `load_addr 0x84` (или `load_acc` по этому адресу) - Аварийное завершение `ERROR: input is depleted` по адресу `132` (`0x84`) - Порт `0x84` — это write-only устройство. У него нет входящей очереди (FIFO), из которой процессор мог бы прочитать байт ### Исполнение кода из порта ввода (JMP 0x80) - jmp 0x80` - Аварийное завершение `ERROR: instruction in memory corrupted` - `PC` выставил адрес `0x80`. Процессор забрал из очереди ввода пользовательский байт (например, букву `'H'` = 72) и попытался декодировать его как машинную команду (Opcode). Команды с таким кодом нет ### Размещение данных прямо в портах (`.org 0x80`) - Написали `.org 0x80`, положили `.word 42`, а следом секцию `.text` с командой `halt` - Аварийное завершение при старте: `ERROR: iomemory[132]: instruction corrupted` - Данные `.word 42` (4 байта) заняли адреса `0x80, 0x81, 0x82, 0x83`. - Код программы (`halt`) "перелился" в следующую свободную ячейку — `0x84` (132) - Процессор попытался стартовать с адреса `0x84` (считая его началом `.text`) - Но `0x84` - это порт только для записи! Процессор не смог прочитать оттуда код и умер при старте ### Несколько `.org` - Ошибка компилятора `multiple .org directives not allowed` - В одной секции - один `.org` (один в `.text`, один в `.data`) ### Чтение из пустого ввода - `ERROR: memory access error: input is depleted` ### Сдвиги - Если сдвинуть числа больше чем на 32, то просто зануление - для сдвига влево - Для сдвигов вправо, если исходное число было положительным - зануление - Для сдвигов вправо, если иходное число было отрицательным - `-1` ### Переходы - В отличие от БЭВМ переходы тут работают иначе. Переходы, которые сравнивают с нулем, учитывают только знак (в бэвм учитывали еще `n+v(mod2)`). Поэтому надо самому реализовывать переходы, которые учитывают специфику доп. кода через `bvz+bltz` например