Fondo
Me gusta mi viejo chip 6502 de 8 bits. Incluso es divertido resolver algunos de los desafíos aquí en PPCG en el código de máquina 6502. Pero algunas cosas que deberían ser simples (como leer datos o enviar a stdout) son innecesariamente engorrosas de hacer en el código de máquina. Así que tengo una idea aproximada: inventar mi propia máquina virtual de 8 bits inspirada en el 6502, pero con el diseño modificado para que sea más útil para los desafíos. Comenzando a implementar algo, me di cuenta de que este podría ser un buen desafío en sí mismo si el diseño de la VM se reduce al mínimo :)
Tarea
Implemente una máquina virtual de 8 bits conforme a la siguiente especificación. Este es el código de golf , por lo que gana la implementación con la menor cantidad de bytes.
Entrada
Su implementación debe tomar las siguientes entradas:
Un solo byte sin signo
pc
, este es el contador inicial del programa (la dirección en la memoria donde la VM comienza la ejecución,0
basada)Una lista de bytes con una longitud máxima de
256
entradas, esta es la RAM para la máquina virtual (con su contenido inicial)
Puede tomar esta entrada en cualquier formato razonable.
Salida
Una lista de bytes que son los contenidos finales de la RAM después de que la VM termina (ver más abajo). Puede suponer que solo obtiene información que conduce a la terminación eventualmente. Se permite cualquier formato sensible.
CPU virtual
La CPU virtual tiene
- un contador de programa de 8 bits,
- un registro acumulador de 8 bits llamado
A
y - un registro de índice de 8 bits llamado
X
.
Hay tres banderas de estado:
Z
- el indicador de cero se establece después de que algunos resultados de operación0
N
- el indicador negativo se establece después de que algunas operaciones den como resultado un número negativo (se establece el bit 7 del resultado)C
- el indicador de acarreo se establece mediante adiciones y cambios para el bit "faltante" del resultado
Al inicio, todos los indicadores se borran, el contador del programa se establece en un valor dado y los contenidos de A
y X
son indeterminados.
Los valores de 8 bits representan
- un entero sin signo en el rango
[0..255]
- una firmada número entero, complemento a 2, en el rango
[-128..127]
Dependiendo del contexto. Si una operación se desborda o se desborda, el valor se ajusta (y en caso de una adición, el indicador de acarreo se ve afectado).
Terminación
La máquina virtual termina cuando
- Se
HLT
alcanza una instrucción. - Se accede a una dirección de memoria no existente
- El contador del programa se ejecuta fuera de la memoria (tenga en cuenta que no se ajusta incluso si la VM recibe los 256 bytes completos de memoria)
Modos de direccionamiento
- implícito : la instrucción no tiene argumento, el operando está implícito
- inmediato : el operando es el byte directamente después de la instrucción
- relativo : (solo para bifurcación) el byte después de que se firma la instrucción (complemento de 2) y determina el desplazamiento que se agregará al contador del programa si se toma la bifurcación.
0
es la ubicación de las siguientes instrucciones - absoluto : el byte después de la instrucción es la dirección del operando
- indexado : el byte después de la instrucción plus
X
(el registro) es la dirección del operando
Instrucciones
Cada instrucción consta de un código de operación (un byte) y, en los modos de direccionamiento inmediato , relativo , absoluto e indexado, un segundo byte de argumento. Cuando la CPU virtual ejecuta una instrucción, incrementa el contador del programa en consecuencia (por 1
o 2
).
Todos los códigos de operación que se muestran aquí están en hexadecimal.
LDA
- cargar operando enA
- Códigos de operación: inmediato:,
00
absoluto:,02
indexado:04
- Banderas:
Z
,N
- Códigos de operación: inmediato:,
STA
- almacenarA
en operando- Códigos de operación: inmediato:,
08
absoluto:,0a
indexado:0c
- Códigos de operación: inmediato:,
LDX
- cargar operando enX
- Códigos de operación: inmediato:,
10
absoluto:,12
indexado:14
- Banderas:
Z
,N
- Códigos de operación: inmediato:,
STX
- almacenarX
en operando- Códigos de operación: inmediato:,
18
absoluto:,1a
indexado:1c
- Códigos de operación: inmediato:,
AND
- bit a bit y deA
y operando enA
- Códigos de operación: inmediato:,
30
absoluto:,32
indexado:34
- Banderas:
Z
,N
- Códigos de operación: inmediato:,
ORA
- bit a bit o deA
y operando enA
- Códigos de operación: inmediato:,
38
absoluto:,3a
indexado:3c
- Banderas:
Z
,N
- Códigos de operación: inmediato:,
EOR
- bitor xor (exclusivo o) deA
y operando enA
- Códigos de operación: inmediato:,
40
absoluto:,42
indexado:44
- Banderas:
Z
,N
- Códigos de operación: inmediato:,
LSR
- desplazamiento lógico a la derecha, desplazar todos los bits de operando un lugar a la derecha, el bit 0 va a llevar- Códigos de operación: inmediato:,
48
absoluto:,4a
indexado:4c
- Banderas:
Z
,N
,C
- Códigos de operación: inmediato:,
ASL
- desplazamiento aritmético a la izquierda, desplazar todos los bits del operando un lugar a la izquierda, el bit 7 va a llevar- Códigos de operación: inmediato:,
50
absoluto:,52
indexado:54
- Banderas:
Z
,N
,C
- Códigos de operación: inmediato:,
ROR
- rotar a la derecha, desplazar todos los bits de operando un lugar a la derecha, llevar va al bit 7, bit 0 va a llevar- Códigos de operación: inmediato:,
58
absoluto:,5a
indexado:5c
- Banderas:
Z
,N
,C
- Códigos de operación: inmediato:,
ROL
- rotar a la izquierda, desplazar todos los bits del operando un lugar a la izquierda, llevar va al bit 0, bit 7 va a llevar- Códigos de operación: inmediato:,
60
absoluto:,62
indexado:64
- Banderas:
Z
,N
,C
- Códigos de operación: inmediato:,
ADC
- agregar con carry, operand plus carry se agrega aA
, carry se establece en desbordamiento- Códigos de operación: inmediato:,
68
absoluto:,6a
indexado:6c
- Banderas:
Z
,N
,C
- Códigos de operación: inmediato:,
INC
- incrementa el operando en uno- Códigos de operación: inmediato:,
78
absoluto:,7a
indexado:7c
- Banderas:
Z
,N
- Códigos de operación: inmediato:,
DEC
- decremento operando por uno- Códigos de operación: inmediato:,
80
absoluto:,82
indexado:84
- Banderas:
Z
,N
- Códigos de operación: inmediato:,
CMP
- compararA
con operando restando operando deA
, olvidar resultado. El transporte se borra por debajo del flujo, configurado de otra manera- Códigos de operación: inmediato:,
88
absoluto:,8a
indexado:8c
- Banderas:
Z
,N
,C
- Códigos de operación: inmediato:,
CPX
- compararX
- igual queCMP
paraX
- Códigos de operación: inmediato:,
90
absoluto:,92
indexado:94
- Banderas:
Z
,N
,C
- Códigos de operación: inmediato:,
HLT
-- Terminar- Códigos de operación: implícito:
c0
- Códigos de operación: implícito:
INX
- incrementarX
en uno- Códigos de operación: implícito:
c8
- Banderas:
Z
,N
- Códigos de operación: implícito:
DEX
- decrementoX
por uno- Códigos de operación: implícito:
c9
- Banderas:
Z
,N
- Códigos de operación: implícito:
SEC
- establecer la bandera de transporte- Códigos de operación: implícito:
d0
- Banderas
C
- Códigos de operación: implícito:
CLC
- claro llevar la bandera- Códigos de operación: implícito:
d1
- Banderas
C
- Códigos de operación: implícito:
BRA
- rama siempre- Códigos de operación: relativo:
f2
- Códigos de operación: relativo:
BNE
- bifurcar si seZ
borró la bandera- Códigos de operación: relativo:
f4
- Códigos de operación: relativo:
BEQ
- rama si seZ
establece la bandera- Códigos de operación: relativo:
f6
- Códigos de operación: relativo:
BPL
- bifurcar si seN
borró la bandera- Códigos de operación: relativo:
f8
- Códigos de operación: relativo:
BMI
- rama si seN
establece la bandera- Códigos de operación: relativo:
fa
- Códigos de operación: relativo:
BCC
- bifurcar si seC
borró la bandera- Códigos de operación: relativo:
fc
- Códigos de operación: relativo:
BCS
- rama si seC
establece la bandera- Códigos de operación: relativo:
fe
- Códigos de operación: relativo:
Opcodes
El comportamiento de la VM no está definido si se encuentra algún código de operación que no se correlacione con una instrucción válida de la lista anterior.
Según la solicitud de Jonathan Allan , puede elegir su propio conjunto de códigos de operación en lugar de los códigos de operación que se muestran en la sección de Instrucciones . Si lo hace, debe agregar una asignación completa a los códigos de operación utilizados anteriormente en su respuesta.
La asignación debe ser un archivo hexadecimal con pares <official opcode> <your opcode>
, por ejemplo, si reemplazó dos códigos de operación:
f4 f5
10 11
Las nuevas líneas no importan aquí.
Casos de prueba (códigos de operación oficiales)
// 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
Podría agregar más casos de prueba más tarde.
Referencia y prueba
Para ayudar con sus propios experimentos, aquí hay alguna implementación de referencia (totalmente no desarrollada) : puede generar información de rastreo (incluidas las instrucciones desensambladas) stderr
y convertir códigos de operación mientras se ejecuta.
Forma recomendada de obtener la fuente:
git clone https://github.com/zirias/gvm --branch challenge --single-branch --recurse-submodules
O challenge
realice un pago en la rama y realice una git submodule update --init --recursive
clonación posterior para obtener mi sistema de compilación personalizado.
Construya la herramienta con GNU make (solo escriba make
, o gmake
si en su sistema, el make predeterminado no es GNU make).
Uso :gvm [-s startpc] [-h] [-t] [-c convfile] [-d] [-x] <initial_ram
-s startpc
- el contador inicial del programa, por defecto0
-h
- la entrada está en hexadecimal (de lo contrario binaria)-t
- seguimiento de ejecución astderr
-c convfile
- convertir códigos de operación de acuerdo con una asignación dada enconvfile
-d
- volcar la memoria resultante como datos binarios-x
- volcar la memoria resultante como hexadecimalinitial_ram
- el contenido inicial de RAM, ya sea hexadecimal o binario
Tenga en cuenta que la función de conversión fallará en los programas que modifican los códigos de operación mientras se ejecutan.
Descargo de responsabilidad: las reglas y especificaciones anteriores son autorizadas para el desafío, no esta herramienta. Esto se aplica especialmente a la función de conversión de opcode. Si cree que la herramienta presentada aquí tiene un error con las especificaciones, informe en un comentario :)
BRA
("rama siempre") no introduce una rama en el flujo de control, ¿no debería llamarse JMP
?
BRA
existe en diseños de chips posteriores (el 6502 no tiene esa instrucción) como el 65C02 y el MC 68000. también JMP
existe. La diferencia es que BRA
usa direccionamiento relativo y JMP
usa direccionamiento absoluto. Entonces, solo seguí estos diseños; de hecho, no suena tan lógico;)