185 lines
5.0 KiB
C++
185 lines
5.0 KiB
C++
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdint.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <assert.h>
|
||
|
#include <string.h>
|
||
|
#include <locale.h>
|
||
|
#include <climits>
|
||
|
#include <unordered_set>
|
||
|
|
||
|
typedef struct {
|
||
|
uint8_t opcode;
|
||
|
int32_t a, b, c;
|
||
|
} Instruction;
|
||
|
|
||
|
Instruction instructions[1024];
|
||
|
|
||
|
enum {
|
||
|
ADDR, ADDI, MULR, MULI, BANR, BANI, BORR, BORI,
|
||
|
SETR, SETI, GTIR, GTRI, GTRR, EQIR, EQRI, EQRR,
|
||
|
IP,
|
||
|
};
|
||
|
|
||
|
const char *names[] = {
|
||
|
"addr", "addi", "mulr", "muli", "banr", "bani", "borr", "bori",
|
||
|
"setr", "seti", "gtir", "gtri", "gtrr", "eqir", "eqri", "eqrr",
|
||
|
"#ip",
|
||
|
};
|
||
|
|
||
|
int instruction_read(Instruction *i, FILE *file) {
|
||
|
char mnemonic[64] = {0};
|
||
|
size_t count = fscanf(file, "%s %u %u %u", mnemonic, &i->a, &i->b, &i->c);
|
||
|
if (count != 4 && count != 2)
|
||
|
return 0;
|
||
|
|
||
|
for (int opcode = 0; opcode <= IP; opcode++) {
|
||
|
if (strcmp(names[opcode], mnemonic) == 0) {
|
||
|
i->opcode = opcode;
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
printf("Unknown mnemonic '%s'\n", mnemonic);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void instruction_execute(const Instruction *i, int32_t *r, int32_t *ip, int32_t *ip_r) {
|
||
|
if (*ip_r != -1)
|
||
|
r[*ip_r] = *ip;
|
||
|
|
||
|
switch (i->opcode) {
|
||
|
// Addition
|
||
|
case ADDR: r[i->c] = r[i->a] + r[i->b]; break;
|
||
|
case ADDI: r[i->c] = r[i->a] + i->b; break;
|
||
|
|
||
|
// Multiplication
|
||
|
case MULR: r[i->c] = r[i->a] * r[i->b]; break;
|
||
|
case MULI: r[i->c] = r[i->a] * i->b; break;
|
||
|
|
||
|
// Bitwise AND
|
||
|
case BANR: r[i->c] = (r[i->a] & r[i->b]); break;
|
||
|
case BANI: r[i->c] = (r[i->a] & i->b); break;
|
||
|
|
||
|
// Bitwise OR
|
||
|
case BORR: r[i->c] = (r[i->a] | r[i->b]); break;
|
||
|
case BORI: r[i->c] = (r[i->a] | i->b); break;
|
||
|
|
||
|
// Assignment
|
||
|
case SETR: r[i->c] = r[i->a]; break;
|
||
|
case SETI: r[i->c] = i->a; break;
|
||
|
|
||
|
// Greater-than testing
|
||
|
case GTIR: r[i->c] = (i->a > r[i->b]); break;
|
||
|
case GTRI: r[i->c] = (r[i->a] > i->b); break;
|
||
|
case GTRR: r[i->c] = (r[i->a] > r[i->b]); break;
|
||
|
|
||
|
// Equality testing
|
||
|
case EQIR: r[i->c] = (i->a == r[i->b]); break;
|
||
|
case EQRI: r[i->c] = (r[i->a] == i->b); break;
|
||
|
case EQRR: r[i->c] = (r[i->a] == r[i->b]); break;
|
||
|
|
||
|
case IP: *ip_r = i->a; *ip -= 1; break;
|
||
|
|
||
|
default: assert(0);
|
||
|
}
|
||
|
|
||
|
if (*ip_r != -1)
|
||
|
*ip = r[*ip_r];
|
||
|
|
||
|
*ip += 1;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char **argv) {
|
||
|
setlocale(LC_NUMERIC, "");
|
||
|
if (argc != 2) {
|
||
|
printf("Usage: %s [input]\n", argv[0]);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
FILE *file = fopen(argv[1], "r");
|
||
|
|
||
|
int32_t jump_regs[6] = {-1};
|
||
|
int32_t registers[6] = {INT_MAX, 0};
|
||
|
int32_t ip_reg = -1;
|
||
|
size_t instr_count = 0;
|
||
|
for (instr_count = 0; instr_count < 1024 && instruction_read(&instructions[instr_count], file); instr_count++) {
|
||
|
if (instructions[instr_count].opcode == IP) {
|
||
|
ip_reg = instructions[instr_count].a;
|
||
|
instr_count -= 1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
printf("[%2zu] %s %d %d %d\n", instr_count, names[instructions[instr_count].opcode],
|
||
|
instructions[instr_count].a, instructions[instr_count].b,
|
||
|
instructions[instr_count].c);
|
||
|
}
|
||
|
printf("\n");
|
||
|
|
||
|
std::unordered_set<size_t> breakpoints;
|
||
|
bool stepping = true;
|
||
|
|
||
|
uint64_t instructions_executed = 0;
|
||
|
for (int32_t i = 0; i < instr_count;) {
|
||
|
Instruction instruction = instructions[i];
|
||
|
|
||
|
if (breakpoints.find(i) != breakpoints.end())
|
||
|
stepping = true;
|
||
|
|
||
|
if (stepping) {
|
||
|
const char *name = names[instruction.opcode];
|
||
|
printf("[%2d] \033[33m%s %d %d %d\033[0m\r", i,
|
||
|
name, instruction.a, instruction.b, instruction.c);
|
||
|
}
|
||
|
instruction_execute(&instruction, registers, &i, &ip_reg);
|
||
|
if (stepping) {
|
||
|
printf("\033[30C[%d, %d, %d, %d, %d, %d]\n", registers[0], registers[1],
|
||
|
registers[2], registers[3], registers[4], registers[5]);
|
||
|
|
||
|
read:
|
||
|
printf("> ");
|
||
|
char c;
|
||
|
scanf(" %c", &c);
|
||
|
switch (c) {
|
||
|
case 0:
|
||
|
printf("q\n");
|
||
|
[[fallthrough]];
|
||
|
|
||
|
case 'q':
|
||
|
i = instr_count;
|
||
|
break;
|
||
|
|
||
|
case 'c':
|
||
|
stepping = false;
|
||
|
break;
|
||
|
|
||
|
case 'b':
|
||
|
breakpoints.insert(i);
|
||
|
break;
|
||
|
|
||
|
case 's':
|
||
|
break;
|
||
|
|
||
|
case '\n':
|
||
|
goto read;
|
||
|
|
||
|
default:
|
||
|
printf("Unknown command '%c'\n", c);
|
||
|
goto read;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
instructions_executed += 1;
|
||
|
if (!stepping && instructions_executed % (1 << 20) == 0) {
|
||
|
printf("%'lu %d ", instructions_executed, i);
|
||
|
printf("[%'d, %'d, %'d, %'d, %'d, %'d]\033[K\r", registers[0], registers[1],
|
||
|
registers[2], registers[3], registers[4], registers[5]);
|
||
|
}
|
||
|
}
|
||
|
printf("instructions: %lu\n", instructions_executed);
|
||
|
|
||
|
fclose(file);
|
||
|
|
||
|
printf("%d\n", registers[0]);
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|