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