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,0basada)Una lista de bytes con una longitud máxima de
256entradas, 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
Ay - 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ón0N- 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 Ay Xson 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
HLTalcanza 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.
0es 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 1o 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:,
00absoluto:,02indexado:04 - Banderas:
Z,N
- Códigos de operación: inmediato:,
STA- almacenarAen operando- Códigos de operación: inmediato:,
08absoluto:,0aindexado:0c
- Códigos de operación: inmediato:,
LDX- cargar operando enX- Códigos de operación: inmediato:,
10absoluto:,12indexado:14 - Banderas:
Z,N
- Códigos de operación: inmediato:,
STX- almacenarXen operando- Códigos de operación: inmediato:,
18absoluto:,1aindexado:1c
- Códigos de operación: inmediato:,
AND- bit a bit y deAy operando enA- Códigos de operación: inmediato:,
30absoluto:,32indexado:34 - Banderas:
Z,N
- Códigos de operación: inmediato:,
ORA- bit a bit o deAy operando enA- Códigos de operación: inmediato:,
38absoluto:,3aindexado:3c - Banderas:
Z,N
- Códigos de operación: inmediato:,
EOR- bitor xor (exclusivo o) deAy operando enA- Códigos de operación: inmediato:,
40absoluto:,42indexado: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:,
48absoluto:,4aindexado: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:,
50absoluto:,52indexado: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:,
58absoluto:,5aindexado: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:,
60absoluto:,62indexado: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:,
68absoluto:,6aindexado:6c - Banderas:
Z,N,C
- Códigos de operación: inmediato:,
INC- incrementa el operando en uno- Códigos de operación: inmediato:,
78absoluto:,7aindexado:7c - Banderas:
Z,N
- Códigos de operación: inmediato:,
DEC- decremento operando por uno- Códigos de operación: inmediato:,
80absoluto:,82indexado:84 - Banderas:
Z,N
- Códigos de operación: inmediato:,
CMP- compararAcon operando restando operando deA, olvidar resultado. El transporte se borra por debajo del flujo, configurado de otra manera- Códigos de operación: inmediato:,
88absoluto:,8aindexado:8c - Banderas:
Z,N,C
- Códigos de operación: inmediato:,
CPX- compararX- igual queCMPparaX- Códigos de operación: inmediato:,
90absoluto:,92indexado: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- incrementarXen uno- Códigos de operación: implícito:
c8 - Banderas:
Z,N
- Códigos de operación: implícito:
DEX- decrementoXpor 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 seZborró la bandera- Códigos de operación: relativo:
f4
- Códigos de operación: relativo:
BEQ- rama si seZestablece la bandera- Códigos de operación: relativo:
f6
- Códigos de operación: relativo:
BPL- bifurcar si seNborró la bandera- Códigos de operación: relativo:
f8
- Códigos de operación: relativo:
BMI- rama si seNestablece la bandera- Códigos de operación: relativo:
fa
- Códigos de operación: relativo:
BCC- bifurcar si seCborró la bandera- Códigos de operación: relativo:
fc
- Códigos de operación: relativo:
BCS- rama si seCestablece 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) stderry 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 challengerealice un pago en la rama y realice una git submodule update --init --recursiveclonación posterior para obtener mi sistema de compilación personalizado.
Construya la herramienta con GNU make (solo escriba make, o gmakesi 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?
BRAexiste en diseños de chips posteriores (el 6502 no tiene esa instrucción) como el 65C02 y el MC 68000. también JMPexiste. La diferencia es que BRAusa direccionamiento relativo y JMPusa direccionamiento absoluto. Entonces, solo seguí estos diseños; de hecho, no suena tan lógico;)