This commit is contained in:
Richard
2026-03-26 18:43:01 +01:00
commit 9616a8236b
25 changed files with 2773 additions and 0 deletions

6
farm/Dockerfile Normal file
View File

@@ -0,0 +1,6 @@
FROM debian
RUN apt update && apt install -y curl vim wget xz-utils build-essential less
RUN useradd -m dev
USER dev
WORKDIR /home/dev/farm
ADD . /home/dev/farm

194
farm/Makefile Normal file
View File

@@ -0,0 +1,194 @@
#### PROJECT SETTINGS ####
# The name of the executable to be created
BIN_NAME := farm
# Compiler used
CC ?= gcc
# Extension of source files used in the project
SRC_EXT = c
# Path to the source directory, relative to the makefile
SRC_PATH = .
# Space-separated pkg-config libraries used by this project
LIBS =
# General compiler flags
COMPILE_FLAGS = -std=c99 -Wall -Wextra -g
# Additional release-specific flags
RCOMPILE_FLAGS = -D NDEBUG
# Additional debug-specific flags
DCOMPILE_FLAGS = -D DEBUG
# Add additional include paths
INCLUDES = -I $(SRC_PATH)
# General linker settings
LINK_FLAGS =
# Additional release-specific linker settings
RLINK_FLAGS =
# Additional debug-specific linker settings
DLINK_FLAGS =
# Destination directory, like a jail or mounted system
DESTDIR = /
# Install path (bin/ is appended automatically)
INSTALL_PREFIX = usr/local
#### END PROJECT SETTINGS ####
# Optionally you may move the section above to a separate config.mk file, and
# uncomment the line below
# include config.mk
# Generally should not need to edit below this line
# Obtains the OS type, either 'Darwin' (OS X) or 'Linux'
UNAME_S:=$(shell uname -s)
# Function used to check variables. Use on the command line:
# make print-VARNAME
# Useful for debugging and adding features
print-%: ; @echo $*=$($*)
# Shell used in this makefile
# bash is used for 'echo -en'
SHELL = /bin/bash
# Clear built-in rules
.SUFFIXES:
# Programs for installation
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
# Append pkg-config specific libraries if need be
ifneq ($(LIBS),)
COMPILE_FLAGS += $(shell pkg-config --cflags $(LIBS))
LINK_FLAGS += $(shell pkg-config --libs $(LIBS))
endif
# Verbose option, to output compile and link commands
export V := false
export CMD_PREFIX := @
ifeq ($(V),true)
CMD_PREFIX :=
endif
# Combine compiler and linker flags
release: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(RCOMPILE_FLAGS)
release: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(RLINK_FLAGS)
debug: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(DCOMPILE_FLAGS)
debug: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(DLINK_FLAGS)
# Build and output paths
release: export BUILD_PATH := build/release
release: export BIN_PATH := bin/release
debug: export BUILD_PATH := build/debug
debug: export BIN_PATH := bin/debug
install: export BIN_PATH := bin/release
# Find all source files in the source directory, sorted by most
# recently modified
ifeq ($(UNAME_S),Darwin)
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-)
else
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' -printf '%T@\t%p\n' \
| sort -k 1nr | cut -f2-)
endif
# fallback in case the above fails
rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) \
$(filter $(subst *,%,$2), $d))
ifeq ($(SOURCES),)
SOURCES := $(call rwildcard, $(SRC_PATH), *.$(SRC_EXT))
endif
# Set the object file names, with the source directory stripped
# from the path, and the build path prepended in its place
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
# Set the dependency files that will be used to add header dependencies
DEPS = $(OBJECTS:.o=.d)
# Version macros
# Comment/remove this section to remove versioning
USE_VERSION := false
# If this isn't a git repo or the repo has no tags, git describe will return non-zero
ifeq ($(shell git describe > /dev/null 2>&1 ; echo $$?), 0)
USE_VERSION := true
VERSION := $(shell git describe --tags --long --dirty --always | \
sed 's/v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)-\?.*-\([0-9]*\)-\(.*\)/\1 \2 \3 \4 \5/g')
VERSION_MAJOR := $(word 1, $(VERSION))
VERSION_MINOR := $(word 2, $(VERSION))
VERSION_PATCH := $(word 3, $(VERSION))
VERSION_REVISION := $(word 4, $(VERSION))
VERSION_HASH := $(word 5, $(VERSION))
VERSION_STRING := \
"$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH).$(VERSION_REVISION)-$(VERSION_HASH)"
override CFLAGS := $(CFLAGS) \
-D VERSION_MAJOR=$(VERSION_MAJOR) \
-D VERSION_MINOR=$(VERSION_MINOR) \
-D VERSION_PATCH=$(VERSION_PATCH) \
-D VERSION_REVISION=$(VERSION_REVISION) \
-D VERSION_HASH=\"$(VERSION_HASH)\"
endif
# Standard, non-optimized release build
.PHONY: release
release: dirs
ifeq ($(USE_VERSION), true)
@echo "Beginning release build v$(VERSION_STRING)"
else
@echo "Beginning release build"
endif
@$(MAKE) all --no-print-directory
# Debug build for gdb debugging
.PHONY: debug
debug: dirs
ifeq ($(USE_VERSION), true)
@echo "Beginning debug build v$(VERSION_STRING)"
else
@echo "Beginning debug build"
endif
@$(MAKE) all --no-print-directory
# Create the directories used in the build
.PHONY: dirs
dirs:
@echo "Creating directories"
@mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH)
# Installs to the set path
.PHONY: install
install:
@echo "Installing to $(DESTDIR)$(INSTALL_PREFIX)/bin"
@$(INSTALL_PROGRAM) $(BIN_PATH)/$(BIN_NAME) $(DESTDIR)$(INSTALL_PREFIX)/bin
# Uninstalls the program
.PHONY: uninstall
uninstall:
@echo "Removing $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)"
@$(RM) $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)
# Removes all build files
.PHONY: clean
clean:
@echo "Deleting $(BIN_NAME) symlink"
@$(RM) $(BIN_NAME)
@echo "Deleting directories"
@$(RM) -r build
@$(RM) -r bin
# Main rule, checks the executable and symlinks to the output
all: $(BIN_PATH)/$(BIN_NAME)
@echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
# Link the executable
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
@echo "Linking: $@"
$(CMD_PREFIX)$(CC) $(OBJECTS) $(LDFLAGS) -o $@
# Add dependency files, if they exist
-include $(DEPS)
# Source file rules
# After the first compilation they will be joined with the rules from the
# dependency files to provide header dependencies
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
@echo "Compiling: $< -> $@"
$(CMD_PREFIX)$(CC) $(CFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@

19
farm/ant.c Normal file
View File

@@ -0,0 +1,19 @@
#include "ant.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void reset_ants(struct Ant* ants, unsigned short nest) {
memset(ants, 0, sizeof(struct Ant) * NUM_ANTS);
for (int i = 0; i < NUM_ANTS; i++) {
ants[i].id = i;
ants[i].cell = nest;
}
}
struct Ant* create_ants() {
int ants_size = sizeof(struct Ant) * NUM_ANTS;
struct Ant *ants = (struct Ant *)malloc(ants_size);
return ants;
}

25
farm/ant.h Normal file
View File

@@ -0,0 +1,25 @@
#ifdef DEBUG
#define NUM_ANTS 1
#else
#define NUM_ANTS 200
#endif
struct Ant {
unsigned char id;
unsigned int pc;
unsigned short cell;
unsigned char carrying;
int r0;
int r1;
int r2;
int r3;
int r4;
int r5;
int r6;
int r7;
int jr;
};
struct Ant* create_ants();
void reset_ants(struct Ant* ants, unsigned short nest);

392
farm/farm.c Normal file
View File

@@ -0,0 +1,392 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "maps.h"
#include "program.h"
#include "ant.h"
#define TICKS 2000
#define MAX_OPS 64
#define NUM_CHANNELS 4
const char map_offsets[] = { 0, -MAP_WIDTH , 1, MAP_WIDTH, -1 };
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
#define cell_from(org, dir) (org) + map_offsets[(dir)]
#define reg_id(rid) (rid) < 8 ? (rid) : (rid) - 0x80
#define get_value(ant, v) (v) < 0x80 ? (v) : get_reg((ant), (v))
static inline unsigned int rnum(unsigned int* rstate) {
unsigned int e = *rstate + 0x9e3779b9;
*rstate = e;
e = (e ^ (e >> 16)) * 0x85ebca6b;
e = (e ^ (e >> 13)) * 0xc2b2ae35;
e = e ^ (e >> 16);
//e = e / 0x100000000;
return e;
}
// static inline unsigned int rnum(unsigned int* rstate) {
// return rand();
// }
static inline void set_reg(struct Ant* ant, unsigned char rid, unsigned int v) {
switch(reg_id(rid)) {
case 0:
ant->r0 = v;
break;
case 1:
ant->r1 = v;
break;
case 2:
ant->r2 = v;
break;
case 3:
ant->r3 = v;
break;
case 4:
ant->r4 = v;
break;
case 5:
ant->r5 = v;
break;
case 6:
ant->r6 = v;
break;
case 7:
ant->r7 = v;
break;
}
}
static inline unsigned int get_reg(struct Ant* ant, unsigned char rid) {
switch(reg_id(rid)) {
case 0:
return ant->r0;
case 1:
return ant->r1;
case 2:
return ant->r2;
case 3:
return ant->r3;
case 4:
return ant->r4;
case 5:
return ant->r5;
case 6:
return ant->r6;
case 7:
return ant->r7;
default:
return 0;
}
}
#ifdef DEBUG
#define debug(msg, ...) printf((msg), ##__VA_ARGS__)
#else
#define debug(msg, ...)
#endif
#define debug_ant(ant) debug("A%d @%d:%d R:%02x %02x %02x %02x %02x %02x %02x %02x\n", (ant)->id, (ant)->cell % MAP_WIDTH, (ant)->cell / MAP_WIDTH, (ant)->r0, (ant)->r1, (ant)->r2, (ant)->r3, (ant)->r4, (ant)->r5, (ant)->r6, (ant)->r7)
#define debug_ins(pc, ins) debug("I%02d %s %02x %02x %02x\n", pc, instruction_names[ins->op], ins->arg1, ins->arg2, ins->arg3)
int run_simulations(struct Maps* maps, struct Program* program) {
int ops;
int cost;
int score;
int dirs[4];
unsigned int rstate;
int cell;
int found_dirs = 0;
struct Map* map;
int v1, v2, v3, res;
struct Ant* ant;
unsigned int pc;
struct Instruction* ins;
struct Ant* ants = create_ants();
unsigned char food[NUM_CELLS];
unsigned char smells[NUM_CHANNELS][NUM_CELLS];
for (unsigned int mi = 0; mi < maps->num_maps; mi++) {
map = &maps->maps + mi;
debug("MAP %s\n", map->name);
memset(&smells, 0, NUM_CHANNELS * NUM_CELLS);
reset_ants(ants, map->nest);
score = 0;
rstate = 2;
for (int i = 0; i < NUM_CELLS; i++ ) {
food[i] = map->cells[i] == CELL_FOOD ? 1 : 0;
}
for (int t=0; t < TICKS; t++) {
for (int ai=0; ai < NUM_ANTS; ai++) {
ops = 0;
while (ops < MAX_OPS) {
cost = 1;
ant = &ants[ai];
pc = ant->pc;
ins = &program->instructions + pc;
debug_ins(pc, ins);
ant->pc = (pc + 1) % program->num_instructions;
switch(ins->op) {
case INS_ID:
set_reg(ant, ins->arg1, ant->id);
break;
case INS_TAG:
//noop
break;
case INS_MOVE:
v1 = get_value(ant, ins->arg1);
if (v1 == 5) {
v1 = rnum(&rstate) % 4 + 1;
}
cell = cell_from(ant->cell, v1);
if (cell > 0 && cell < NUM_CELLS && map->cells[cell] != CELL_WALL) {
ant->cell = cell;
}
cost += MAX_OPS;
break;
case INS_SENSE:
v1 = get_value(ant, ins->arg1);
v2 = ins->arg2;
found_dirs = 0;
if (map->cells[cell_from(ant->cell, 1)] == v1) dirs[found_dirs++] = 1;
if (map->cells[cell_from(ant->cell, 2)] == v1) dirs[found_dirs++] = 2;
if (map->cells[cell_from(ant->cell, 3)] == v1) dirs[found_dirs++] = 3;
if (map->cells[cell_from(ant->cell, 4)] == v1) dirs[found_dirs++] = 4;
if (found_dirs == 0) {
res = 0;
} else if (found_dirs == 1) {
res = dirs[0];
} else {
res = dirs[rnum(&rstate) % found_dirs];
}
set_reg(ant, v2, res);
break;
case INS_PROBE:
v1 = get_value(ant, ins->arg1);
v1 = cell_from(ant->cell, v1);
v1 = map->cells[v1];
set_reg(ant, ins->arg2, v1);
break;
case INS_PICKUP:
debug("PICKUP?\n");
if (!ant->carrying && food[ant->cell] > 0) {
ant->carrying = food[ant->cell]--;
debug("PICKUP!\n");
}
cost += MAX_OPS;
break;
case INS_CARRYING:
set_reg(ant, ins->arg1, ant->carrying);
break;
case INS_DROP:
debug("DROP?\n");
if (ant->carrying) {
ant -> carrying = 0;
if (map->nest == ant->cell){
debug("DROP SCORE\n");
score += 1;
} else {
food[ant->cell] += 1;
debug("DROP!\n");
}
}
cost += MAX_OPS;
break;
case INS_SMELL:
v1 = get_value(ant, ins->arg1);
v2 = ins->arg2;
res = 0;
v3 = smells[v1][cell_from(ant->cell, 1)];
if (v3 > res) found_dirs = 0;
if (v3 >= res) dirs[found_dirs++] = 1;
v3 = smells[v1][cell_from(ant->cell, 2)];
if (v3 > res) found_dirs = 0;
if (v3 >= res) dirs[found_dirs++] = 2;
v3 = smells[v1][cell_from(ant->cell, 3)];
if (v3 > res) found_dirs = 0;
if (v3 >= res) dirs[found_dirs++] = 3;
v3 = smells[v1][cell_from(ant->cell, 4)];
if (v3 > res) found_dirs = 0;
if (v3 >= res) dirs[found_dirs++] = 4;
if (res == 0) {
res = (rnum(&rstate) % 4) + 1;
}else if (found_dirs == 1) {
res = dirs[0];
} else {
res = dirs[rnum(&rstate) % found_dirs];
}
set_reg(ant, v2, res);
break;
case INS_SNIFF:
v1 = ins->arg1;
v2 = get_value(ant, ins->arg2);
v2 = cell_from(ant->cell, v2);
v1 = smells[v1][v2];
set_reg(ant, ins->arg3, v1);
break;
case INS_MARK:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
v3 = smells[v1][ant->cell];
smells[v1][ant->cell] = MAX(v3+v2, 255);
break;
case INS_SET:
set_reg(ant, ins->arg1, ins->arg2);
break;
case INS_AND:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 & v2);
break;
case INS_OR:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 | v2);
break;
case INS_XOR:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 | v2);
break;
case INS_LSHIFT:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 << v2);
break;
case INS_RSHIFT:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 >> v2);
break;
case INS_ADD:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 + v2);
break;
case INS_SUB:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 - v2);
break;
case INS_MUL:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 * v2);
break;
case INS_DIV:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
if (v2 != 0) {
set_reg(ant, ins->arg1, v1 / v2);
}
break;
case INS_MOD:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
set_reg(ant, ins->arg1, v1 % v2);
break;
case INS_RANDOM:
v2 = get_value(ant, ins->arg2);
if (v2 != 0) {
v1 = rnum(&rstate) % v2;
set_reg(ant, ins->arg1, v1);
}
break;
case INS_SJR:
ant->jr = *((int*)ins) >> 8;
break;
case INS_MJR:
ant->jr = get_value(ant, ins->arg1);
break;
case INS_JMP:
ant->pc = ant->jr;
break;
case INS_CALL:
set_reg(ant, ins->arg1, ant->pc);
ant->pc = ant->jr;
break;
case INS_JEQ:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
if (v1 == v2) {
ant->pc = ant->jr;
}
break;
case INS_JNE:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
if (v1 != v2) {
ant->pc = ant->jr;
}
break;
case INS_JGT:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
if (v1 > v2) {
ant->pc = ant->jr;
}
break;
case INS_JLT:
v1 = get_value(ant, ins->arg1);
v2 = get_value(ant, ins->arg2);
if (v1 < v2) {
ant->pc = ant->jr;
}
break;
};
debug_ant(ant);
ops += cost;
}
}
for (int pi = 0; pi < NUM_CHANNELS; pi++) {
for (int ci = 0; ci < NUM_CELLS; ci++) {
if (smells[pi][ci] > 0) smells[pi][ci]--;
}
}
}
}
return score;
}
int main(int argc, char* argv[])
{
struct Program* program = NULL;
struct Maps* maps = NULL;
int score = 0;
if (argc < 3) {
printf("Usage: farm <maps file> <program file>\n");
return 1;
}
char* maps_filename = argv[1];
char* program_filename = argv[2];
maps = load_maps(maps_filename);
program = load_program(program_filename);
int result = 0;
if (maps == NULL) {
printf("Could not load maps\n");
result = 2;
}else if (program == NULL) {
printf("Could not load program\n");
result = 3;
} else {
score = run_simulations(maps, program);
}
free(program);
free(maps);
printf("Score: %d\n", score);
return result;
}

26
farm/maps.c Normal file
View File

@@ -0,0 +1,26 @@
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include "maps.h"
struct Maps* load_maps(char* filename) {
FILE* f = fopen(filename, "r");
if (f == NULL) {
return NULL;
}
int num_maps;
int num_read = fread(&num_maps, 4, 1, f);
if (num_read != 1 || num_maps > 10000) {
return NULL;
}
int mapfile_size = sizeof(struct Map) * num_maps;
struct Maps *maps = (struct Maps *)malloc(mapfile_size + 4);
maps->num_maps = num_maps;
num_read = fread(&maps->maps, 1, mapfile_size, f);
if (num_read != mapfile_size) {
return NULL;
}
fclose(f);
return maps;
}

23
farm/maps.h Normal file
View File

@@ -0,0 +1,23 @@
#define MAP_WIDTH 128
#define MAP_HEIGHT 128
#define NUM_CELLS MAP_WIDTH * MAP_HEIGHT
#define MAP_NAME_LENGTH 32
#define CELL_EMPTY 0
#define CELL_WALL 1
#define CELL_FOOD 2
#define CELL_NEST 3
struct Map {
char name[MAP_NAME_LENGTH];
unsigned short nest;
unsigned char cells[MAP_WIDTH * MAP_HEIGHT];
};
struct Maps {
unsigned int num_maps;
struct Map maps;
};
struct Maps* load_maps(char* filename);

24
farm/program.c Normal file
View File

@@ -0,0 +1,24 @@
#include "program.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
struct Program* load_program(char* filename) {
FILE* f = fopen(filename, "r");
if (f == NULL) {
return NULL;
}
int num_instructions;
int num_read = fread(&num_instructions, 4, 1, f);
if (num_read != 1) {
return NULL;
}
int program_size = 4 + num_instructions * 4;
struct Program* program = (struct Program *)malloc(program_size);
program->num_instructions = num_instructions;
num_read = fread(&program->instructions, 1, program_size-4, f);
if (num_read != program_size-4) {
return NULL;
}
fclose(f);
return program;
}

70
farm/program.h Normal file
View File

@@ -0,0 +1,70 @@
#define INS_NOOP 0
#define INS_ID 1
#define INS_TAG 2
#define INS_MOVE 3
#define INS_SENSE 4
#define INS_PROBE 5
#define INS_PICKUP 6
#define INS_CARRYING 7
#define INS_DROP 8
#define INS_SMELL 9
#define INS_SNIFF 0xa
#define INS_MARK 0xb
#define INS_SET 0x10
#define INS_AND 0x11
#define INS_OR 0x12
#define INS_XOR 0x13
#define INS_LSHIFT 0x14
#define INS_RSHIFT 0x15
#define INS_ADD 0x16
#define INS_SUB 0x17
#define INS_MUL 0x18
#define INS_DIV 0x19
#define INS_MOD 0x1a
#define INS_RANDOM 0x1b
#define INS_SJR 0x20
#define INS_MJR 0x21
#define INS_JMP 0x22
#define INS_CALL 0x23
#define INS_JEQ 0x24
#define INS_JNE 0x25
#define INS_JGT 0x26
#define INS_JLT 0x27
static char* instruction_names[256] = {
/* 0x00 */ "NOOP", "ID ", "TAG ", "MOVE", "SENS", "PROB", "PICK", "CARY", "DROP", "SMEL", "SNIF", "MARK", "?", "?", "?", "?",
/* 0x10 */ "SET ", "AND ", "OR ", "XOR ", "SHL ", "SHR ", "ADD ", "SUB ", "MUL ", "DIV ", "MOD", "RAND", "?", "?", "?", "?",
/* 0x20 */ "SJR ", "MJR ", "JMP ", "CALL", "JEQ", "JNE ", "JGT ", "JLT ", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0x30 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0x40 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0x50 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0x60 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0x70 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0x80 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0x90 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0xa0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0xb0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0xc0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0xd0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0xe0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
/* 0xf0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
};
struct Instruction {
unsigned char op;
unsigned char arg1;
unsigned char arg2;
unsigned char arg3;
};
struct Program {
unsigned int num_instructions;
struct Instruction instructions;
};
struct Program* load_program(char* filename);