I usually use c++ so it may not be best practice for c.
This is a stack based toy vm and as a result it is very primitive, and it has no bitwise
instructions
64k might be a bit overkill for a toy vm.
coding is hard
#include <stdio.h>
#include "vm.h"
int main() {
Vm *vm = new_vm();
i32 buffer[] = {
0x00000A01, /* push 0x0A(\n) */
0x00004301, /* push 0x43(C) */
0x00004201, /* push 0x42(B) */
0x00004101, /* push 0x41(A) */
0x00000009, /* output */
0x00000002, /* pop */
0x00000009,
0x00000002,
0x00000009,
0x00000002,
0x00000009,
0x00000000 /* halt */
};
for (int i = 0; i < sizeof(buffer); i++) {
vm->mem[vm->pc+i] = buffer[i];
}
run_vm(vm);
free_vm(vm);
return 0;
}
vm.h
#ifndef VM_H_
#define VM_H_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
typedef uint32_t i32; /* other numbers */
typedef int32_t si32; /* stack pointer */
typedef unsigned char byte;
typedef struct {
i32 mem[0xffff]; /* approx. 64k */
si32 sp;
i32 pc;
i32 raw;
byte opc;
i32 param;
bool running;
} Vm;
Vm *new_vm();
void reset_vm(Vm *vm);
void free_vm(Vm *vm);
void run_vm(Vm *vm);
#endif
vm.c
#include <stdio.h>
#include "vm.h"
Vm *new_vm() {
Vm *ret = (Vm*)malloc(sizeof(Vm));
ret->pc = 1024; /* add space for the stack */
ret->sp = -1;
ret->running = true;
return ret;
}
void reset_vm(Vm *vm) {
vm->running = true;
for (int i = 0; i < 0xffff; i++) {
vm->mem[i] = 0;
}
vm->sp = -1;
vm->pc = 1024;
}
void free_vm(Vm *vm) {
free(vm);
vm = NULL;
}
static void fetch(Vm *vm) {
vm->raw = vm->mem[vm->pc++];
}
static void decode(Vm *vm) {
/* style of opcode
* 24 bits for parameter
* a byte for the opcode
*/
vm->opc = vm->raw & 0xff;
vm->param = (vm->raw & 0xffffff00) >> 8;
}
static void execute(Vm *vm) {
switch(vm->opc) {
case 0x00: /* halt */
vm->running = false;
printf("Halt\n");
break;
case 0x01: /* push */
vm->mem[++vm->sp] = vm->param;
break;
case 0x02: /* pop */
vm->mem[vm->sp--] = 0;
break;
case 0x03: /* store */
vm->mem[ vm->mem[vm->sp - 1] ] = vm->mem[vm->sp];
break;
case 0x04: /* load */
vm->mem[vm->sp + 1] = vm->mem[ vm->mem[vm->sp] ];
++vm->sp;
break;
case 0x05: /* add */
vm->mem[vm->sp + 1] = vm->mem[vm->sp] + vm->mem[vm->sp - 1];
++vm->sp;
break;
case 0x06: /* sub */
vm->mem[vm->sp + 1] = vm->mem[vm->sp - 1] - vm->mem[vm->sp];
++vm->sp;
break;
case 0x07: /* mul */
vm->mem[vm->sp + 1] = vm->mem[vm->sp] * vm->mem[vm->sp - 1];
++vm->sp;
break;
case 0x08: /* div */
vm->mem[vm->sp + 1] = vm->mem[vm->sp - 1] / vm->mem[vm->sp];
++vm->sp;
break;
case 0x09: /* outc */
printf("%c", vm->mem[vm->sp]);
break;
case 0x0A: /* inpc */
vm->mem[++vm->sp] = getchar();
break;
}
}
void run_vm(Vm *vm) {
while(vm->running) {
fetch(vm);
decode(vm);
execute(vm);
}
}
foo(void);
when declaring a function that takes no arguments, asfoo();
declares a function with unspecified parameters. \$\endgroup\$ – ljrk Jun 27 '20 at 10:09