Фон
Мені подобається мій старий 8-бітний чіп 6502. Це навіть цікаво вирішити деякі завдання тут на PPCG в машинному коді 6502. Але деякі речі, які повинні бути простими (наприклад, читання даних або вихід у stdout), надмірно громіздко робити в машинному коді. Тож у мене на думці є груба ідея: Винайдіть власну 8-бітну віртуальну машину, яка надихається 6502, але з модифікованим дизайном, щоб бути більш придатним для вирішення проблем. Почавши щось реалізовувати, я зрозумів, що це може бути приємним завданням, якщо дизайн VM буде зведений до мінімуму :)
Завдання
Реалізуйте 8-бітну віртуальну машину відповідно до наведених нижче специфікацій. Це код-гольф , тому реалізація з найменшими байтами виграє.
Вхідні дані
Ваша реалізація повинна мати такі входи:
Єдиний непідписаний байт
pc
, це початковий лічильник програми (адреса в пам'яті, де VM починає виконання, на0
базі)Список байтів з максимальною довжиною
256
записів, це ОЗУ для віртуальної машини (з її початковим вмістом)
Ви можете взяти цей ввід у будь-якому розумному форматі.
Вихід
Список байтів, які є кінцевим вмістом оперативної пам’яті після завершення роботи VM (див. Нижче). Можна припустити, що ви отримуєте лише вхід, який призводить до припинення в кінцевому рахунку. Дозволений будь-який розумний формат.
Віртуальний процесор
Віртуальний процесор має
- 8-бітний програмний лічильник,
- 8-бітний реєстр акумуляторів, що називається
A
і - 8-бітний регістр індексів, що називається
X
.
Є три прапори стану:
Z
- нульовий прапор встановлюється після деякої операції в0
N
- від'ємний прапор встановлюється після деякої операції, яка призводить до від'ємного числа (встановлено iow біт 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
- Опкоди: відносні:
Опкоди
Поведінка VM не визначена, якщо знайдено який-небудь опкод, який не відповідає дійсній інструкції із наведеного вище списку.
Відповідно до запиту Джонатана Аллана , ви можете вибрати власний набір кодів замість опкодів, показаних у розділі Інструкції . Якщо ви це зробите, ви повинні додати повне відображення до описаних вище кодів у своїй відповіді.
Відображення має бути шістнадцятковим файлом з парами <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
використовується абсолютна адресація. Отже, я просто дотримувався цих конструкцій - дійсно, це не так логічно звучить;)