Created
March 16, 2012 07:35
-
-
Save magcius/2049013 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <glib.h> | |
typedef struct { | |
GSList *stack; | |
char *bytecode; | |
char *pc; | |
} MachineState; | |
static void machine_push (MachineState *state, int v); | |
static int machine_pop (MachineState *state); | |
/* pushes an operand onto the operand stack */ | |
static void | |
op_pushint (MachineState *state, | |
int v) | |
{ | |
machine_push (state, v); | |
} | |
static void | |
op_add (MachineState *state) | |
{ | |
int a, b, res; | |
a = machine_pop (state); | |
b = machine_pop (state); | |
res = a + b; | |
machine_push (state, res); | |
} | |
static void | |
op_sub (MachineState *state) | |
{ | |
int a, b, res; | |
a = machine_pop (state); | |
b = machine_pop (state); | |
res = a - b; | |
machine_push (state, res); | |
} | |
static void | |
op_mul (MachineState *state) | |
{ | |
int a, b, res; | |
a = machine_pop (state); | |
b = machine_pop (state); | |
res = a * b; | |
machine_push (state, res); | |
} | |
static void | |
op_mod (MachineState *state) | |
{ | |
int a, b, res; | |
a = machine_pop (state); | |
b = machine_pop (state); | |
res = a % b; | |
machine_push (state, res); | |
} | |
static void | |
op_div (MachineState *state) | |
{ | |
int a, b, res; | |
a = machine_pop (state); | |
b = machine_pop (state); | |
res = a / b; | |
machine_push (state, res); | |
} | |
static void | |
op_pop (MachineState *state) | |
{ | |
machine_pop (state); | |
} | |
static void | |
op_dup (MachineState *state) | |
{ | |
int a; | |
a = machine_pop (state); | |
machine_push (state, a); | |
machine_push (state, a); | |
} | |
static void | |
op_swap (MachineState *state) | |
{ | |
int a, b; | |
a = machine_pop (state); | |
b = machine_pop (state); | |
machine_push (state, a); | |
machine_push (state, b); | |
} | |
static void | |
op_print (MachineState *state) | |
{ | |
int a; | |
a = machine_pop (state); | |
g_print ("%d\n", a); | |
} | |
static void | |
op_jmp (MachineState *state, | |
int loc) | |
{ | |
/* minus one because pc is unconditionally | |
* incremented after each op */ | |
state->pc = state->bytecode + loc - 1; | |
} | |
static void | |
op_jz (MachineState *state, | |
int loc) | |
{ | |
int test; | |
test = machine_pop (state); | |
if (test == 0) | |
op_jmp (state, loc); | |
} | |
static void | |
op_jnz (MachineState *state, | |
int loc) | |
{ | |
int test; | |
test = machine_pop (state); | |
if (test != 0) | |
op_jmp (state, loc); | |
} | |
typedef enum { | |
/* NUL marks the end of the bytecode stream */ | |
OP_NUL = 0, | |
OP_SWAP, | |
OP_DUP, | |
OP_ADD, | |
OP_SUB, | |
OP_MUL, | |
OP_DIV, | |
OP_MOD, | |
OP_PRINT, | |
OP_PUSHINT, | |
OP_JMP, | |
OP_JZ, | |
OP_JNZ, | |
OP_LAST | |
} MachineOperation; | |
/* Because we have different signatures, use a | |
* void * pointer that we'll cast in the runner. */ | |
static void * operation_funcs[] = { | |
NULL, | |
op_swap, | |
op_dup, | |
op_add, | |
op_sub, | |
op_mul, | |
op_div, | |
op_mod, | |
op_print, | |
op_pushint, | |
op_jmp, | |
op_jz, | |
op_jnz | |
}; | |
typedef void (*NoArgumentOp) (MachineState *state); | |
typedef void (*OneArgumentOp) (MachineState *state, int argument); | |
static void | |
machine_run (MachineState *state) | |
{ | |
state->pc = state->bytecode; | |
while (*state->pc != OP_NUL) { | |
char op = *state->pc; | |
if (op >= OP_LAST) { | |
g_print ("Operation %d is invalid", op); | |
return; | |
} else if (op >= OP_PUSHINT) { | |
OneArgumentOp func = (OneArgumentOp) operation_funcs[op]; | |
int argument = *(++ state->pc); | |
func (state, argument); | |
} else { | |
NoArgumentOp func = (NoArgumentOp) operation_funcs[op]; | |
func (state); | |
} | |
++ state->pc; | |
} | |
} | |
int | |
main (int argc, char **argv) | |
{ | |
MachineState state; | |
int end = 28; | |
int odd = 23; | |
char bytecode[] = { | |
/* Print the current value. */ | |
OP_DUP, | |
OP_PRINT, | |
/* If the value is 1, we're finished. */ | |
OP_DUP, | |
OP_PUSHINT, 1, | |
OP_SUB, | |
OP_JZ, end, | |
/* At the top of the loop, we have a value | |
* on the stack that's our current Collatz value. | |
* Do an even/odd test. */ | |
OP_DUP, | |
OP_PUSHINT, 2, | |
OP_SWAP, | |
OP_MOD, | |
OP_JNZ, odd, | |
/* Value is even. */ | |
OP_PUSHINT, 3, | |
OP_MUL, | |
OP_PUSHINT, 1, | |
OP_ADD, | |
OP_JMP, 0, | |
/* Value is odd. */ | |
OP_PUSHINT, 2, | |
OP_SWAP, | |
OP_DIV, | |
OP_JMP, 0, | |
OP_NUL | |
}; | |
state.stack = NULL; | |
state.bytecode = bytecode; | |
machine_push (&state, 23); | |
machine_run (&state); | |
return 0; | |
} | |
static void | |
machine_push (MachineState *state, | |
int v) | |
{ | |
state->stack = g_slist_prepend (state->stack, GINT_TO_POINTER (v)); | |
} | |
static int | |
machine_pop (MachineState *state) | |
{ | |
int v; | |
v = GPOINTER_TO_INT (state->stack->data); | |
state->stack = g_slist_delete_link (state->stack, state->stack); | |
return v; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment