Задний план
Мне нравится мой старый 8-битный чип 6502. Здесь даже забавно решить некоторые проблемы на PPCG в машинном коде 6502. Но некоторые вещи, которые должны быть простыми (например, чтение данных или вывод в stdout), излишне громоздки в машинном коде. Так что у меня в голове есть грубая идея: изобрести собственную 8-битную виртуальную машину, которая вдохновлена 6502, но с измененным дизайном, чтобы он был более пригодным для решения задач. Начав что-то реализовывать, я понял, что это может быть хорошей задачей, если дизайн виртуальной машины сведен к минимуму :)
задача
Реализуйте 8-битную виртуальную машину, соответствующую следующей спецификации. Это код-гольф , поэтому выигрывает реализация с наименьшим количеством байтов.
вход
Ваша реализация должна принимать следующие входные данные:
Один беззнаковый байт
pc, это начальный счетчик программы (адрес в памяти, где виртуальная машина начинает выполнение, на0основе)Список байтов с максимальной длиной
256записей, это оперативная память для виртуальной машины (с ее начальным содержимым)
Вы можете принять этот вклад в любом разумном формате.
Выход
Список байтов, которые являются окончательным содержимым оперативной памяти после завершения работы виртуальной машины (см. Ниже). Вы можете предположить, что вы получаете только вход, который в конечном итоге приводит к завершению. Разрешен любой разумный формат.
Виртуальный процессор
Виртуальный процессор имеет
- 8-битный программный счетчик,
- 8-битный регистр аккумулятора называется
Aи - вызывается 8-битный индексный регистр
X.
Есть три флага состояния:
Z- нулевой флаг устанавливается после того, как какая-то операция приводит к0N- отрицательный флаг устанавливается после того, как некоторые операции приводят к отрицательному числу (установлен бит 7 результата)C- флаг переноса устанавливается добавками и сдвигами для «пропущенного» бита результата
При запуске все флаги сбрасываются, счетчик программ устанавливается на заданное значение, а содержимое Aи Xявляется неопределенным.
8-битные значения представляют либо
- беззнаковое целое число в диапазоне
[0..255] - подписал целое число, 2 в дополнение, в диапазоне
[-128..127]
в зависимости от контекста. Если операция переполняет или теряет значение, значение оборачивается (и в случае добавления затрагивается флаг переноса).
прекращение
Виртуальная машина завершает работу, когда
HLTИнструкция достигается- Доступ к несуществующему адресу памяти
- Счетчик программы работает за пределами памяти (обратите внимание, что он не оборачивается, даже если виртуальной машине предоставлено все 256 байтов памяти)
Режимы адресации
- неявный - инструкция не имеет аргумента, операнд подразумевается
- немедленный - операнд является байтом непосредственно после инструкции
- относительный - (только для ветвления) байт после подписания инструкции (дополнение 2) и определяет смещение, добавляемое к счетчику программы, если берется ветвление.
0расположение следующей инструкции - абсолютный - байт после инструкции является адресом операнда
- индексируется - байт после инструкции плюс
X(регистр) является адресом операнда
инструкции
Каждая инструкция состоит из кода операции (один байт) и, в режимах адресации, непосредственного , относительного , абсолютного и индексируемого второго байта аргумента. Когда виртуальный ЦП выполняет инструкцию, он соответственно увеличивает счетчик программы (на 1или 2).
Все коды операций, показанные здесь, в шестнадцатеричном формате.
LDA- загрузить операнд вA- Коды операций: немедленный:,
00абсолютный:,02проиндексированный:04 - Флаги:
Z,N
- Коды операций: немедленный:,
STA- сохранитьAв операнде- Коды операций: немедленный:,
08абсолютный:,0aпроиндексированный:0c
- Коды операций: немедленный:,
LDX- загрузить операнд вX- Коды операций: немедленный:,
10абсолютный:,12проиндексированный:14 - Флаги:
Z,N
- Коды операций: немедленный:,
STX- сохранитьXв операнде- Коды операций: немедленный:,
18абсолютный:,1aпроиндексированный:1c
- Коды операций: немедленный:,
AND- побитовое и изAи операнда вA- Коды операций: немедленный:,
30абсолютный:,32проиндексированный:34 - Флаги:
Z,N
- Коды операций: немедленный:,
ORA- поразрядно или изAи операнд вA- Коды операций: немедленный:,
38абсолютный:,3aпроиндексированный:3c - Флаги:
Z,N
- Коды операций: немедленный:,
EOR- побитовый xor (исключая или)Aи операнд вA- Коды операций: немедленный:,
40абсолютный:,42проиндексированный:44 - Флаги:
Z,N
- Коды операций: немедленный:,
LSR- логическое смещение вправо, сдвиг всех битов операнда на одно место вправо, бит 0 переносится- Коды операций: немедленный:,
48абсолютный:,4aпроиндексированный:4c - Флаги:
Z,N,C
- Коды операций: немедленный:,
ASL- сдвиг арифметики влево, сдвиг всех битов операнда на одну позицию влево, бит 7 идет на перенос- Коды операций: немедленный:,
50абсолютный:,52проиндексированный:54 - Флаги:
Z,N,C
- Коды операций: немедленный:,
ROR- повернуть вправо, сдвинуть все биты операнда на одно место вправо, перенос идет до бита 7, бит 0 - переносить- Коды операций: немедленный:,
58абсолютный:,5aпроиндексированный:5c - Флаги:
Z,N,C
- Коды операций: немедленный:,
ROL- поверните влево, сдвиньте все биты операнда на одну позицию влево, перенос идет в бит 0, бит 7 идет в перенос- Коды операций: немедленный:,
60абсолютный:,62проиндексированный:64 - Флаги:
Z,N,C
- Коды операций: немедленный:,
ADC- добавить с переносом, добавлен операнд плюс переносA, перенос установлен на переполнение- Коды операций: немедленный:,
68абсолютный:,6aпроиндексированный:6c - Флаги:
Z,N,C
- Коды операций: немедленный:,
INC- увеличить операнд на единицу- Коды операций: немедленный:,
78абсолютный:,7aпроиндексированный:7c - Флаги:
Z,N
- Коды операций: немедленный:,
DEC- уменьшить операнд на единицу- Коды операций: немедленный:,
80абсолютный:,82проиндексированный:84 - Флаги:
Z,N
- Коды операций: немедленный:,
CMP- сравнитеAс операндом, вычитая операнд изA, забудьте результат. Перенос очищается при потере, устанавливается иначе- Коды операций: немедленный:,
88абсолютный:,8aпроиндексированный:8c - Флаги:
Z,N,C
- Коды операций: немедленный:,
CPX- сравнитьX- так же, какCMPдляX- Коды операций: немедленный:,
90абсолютный:,92проиндексированный:94 - Флаги:
Z,N,C
- Коды операций: немедленный:,
HLT- прекратить- Коды операций: неявный:
c0
- Коды операций: неявный:
INX- увеличениеXна единицу- Коды операций: неявный:
c8 - Флаги:
Z,N
- Коды операций: неявный:
DEX- уменьшениеXна единицу- Коды операций: неявный:
c9 - Флаги:
Z,N
- Коды операций: неявный:
SEC- установить флаг переноса- Коды операций: неявный:
d0 - Флаги:
C
- Коды операций: неявный:
CLC- снять флажок- Коды операций: неявный:
d1 - Флаги:
C
- Коды операций: неявный:
BRA- ветка всегда- Коды операций: относительный:
f2
- Коды операций: относительный:
BNE- ветвь, еслиZфлаг снят- Коды операций: относительный:
f4
- Коды операций: относительный:
BEQ- ветвь, еслиZустановлен флаг- Коды операций: относительный:
f6
- Коды операций: относительный:
BPL- ветвь, еслиNфлаг снят- Коды операций: относительный:
f8
- Коды операций: относительный:
BMI- ветвь, еслиNустановлен флаг- Коды операций: относительный:
fa
- Коды операций: относительный:
BCC- ветвь, еслиCфлаг снят- Коды операций: относительный:
fc
- Коды операций: относительный:
BCS- ветвь, еслиCустановлен флаг- Коды операций: относительный:
fe
- Коды операций: относительный:
Opcodes
Поведение виртуальной машины не определено, если найден какой-либо код операции, который не соответствует действительной инструкции из приведенного выше списка.
Согласно запросу Джонатана Аллана , вы можете выбрать свой собственный набор кодов операций вместо кодов операций, показанных в разделе « Инструкции ». Если вы это сделаете, вы должны добавить полное сопоставление к кодам операций, использованным выше в вашем ответе.
Сопоставление должно быть шестнадцатеричным файлом с парами <official opcode> <your opcode>, например, если вы заменили два кода операции:
f4 f5
10 11
Новые строки здесь не имеют значения.
Тестовые случаи (официальные коды операций)
// some increments and decrements
pc: 0
ram: 10 10 7a 01 c9 f4 fb
output: 10 20 7a 01 c9 f4 fb
// a 16bit addition
pc: 4
ram: e0 08 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01
output: 0a 0b 2a 02 02 00 6a 02 0a 00 02 01 6a 03 0a 01
// a 16bit multiplication
pc: 4
ram: 5e 01 28 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
02 62 03 c9 f8 e6 c0 00 00
output: 00 00 00 00 10 10 4a 01 5a 00 fc 0d 02 02 d1 6a 21 0a 21 02 03 6a 22 0a 22 52
02 62 03 c9 f8 e6 c0 b0 36
Я мог бы добавить больше тестовых случаев позже.
Справка и тестирование
Чтобы помочь с собственными экспериментами, вот некоторая (полностью не игровая) эталонная реализация - она может выводить информацию трассировки (включая разобранные инструкции) stderrи конвертировать коды операций во время работы.
Рекомендуемый способ получения источника:
git clone https://github.com/zirias/gvm --branch challenge --single-branch --recurse-submodules
Или оформите ветку challengeи сделайте git submodule update --init --recursiveпосле клонирования, чтобы получить мою собственную систему сборки.
Создайте инструмент с помощью GNU make (просто введите makeили, gmakeесли в вашей системе, по умолчанию make не GNU make).
Использование :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram
-s startpc- начальный счетчик программы, по умолчанию0-h- ввод в шестнадцатеричном (иначе двоичный)-t- отслеживать выполнение доstderr-c convfile- конвертировать коды операций в соответствии с отображением, приведенным вconvfile-d- сбросить результирующую память в виде двоичных данных-x- дамп результирующей памяти в виде шестнадцатеричногоinitial_ram- начальное содержимое ОЗУ, либо шестнадцатеричное, либо двоичное
Обратите внимание, что функция преобразования не будет работать в программах, которые изменяют коды операций во время работы.
Отказ от ответственности: приведенные выше правила и спецификации являются авторскими для вызова, а не для этого инструмента. Это особенно относится к функции преобразования кода операции. Если вы считаете, что инструмент, представленный здесь, имеет ошибку в спецификации, сообщите об этом в комментарии :)
BRA("ветвь всегда") не вводит ветвь в потоке управления, не должна ли она быть вызвана JMP?
BRAсуществует в более поздних конструкциях чипов (у 6502 такой инструкции нет), таких как 65C02 и MC 68000. JMPСуществует также. Разница в том, что BRAиспользуется относительная адресация и JMPабсолютная адресация. Итак, я просто следовал этим проектам - действительно, это не звучит так логично;)