Skip to content

Instantly share code, notes, and snippets.

@crcx
Created October 12, 2010 01:55
Show Gist options
  • Save crcx/621536 to your computer and use it in GitHub Desktop.
Save crcx/621536 to your computer and use it in GitHub Desktop.
/* Ngaro VM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2008 - 2010 Charles Childers
Copyright (c) 2009 - 2010, Luke Parrish
Copyright (c) 2010, Marc Simpson
Copyright (c) 2010, Jay Skeer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <termios.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
/* Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#define IMAGE_SIZE 1000000
#define ADDRESSES 1024
#define STACK_DEPTH 100
#define GLOBAL "/usr/local/share/retro/retroImage"
typedef struct {
int sp, rsp, ip;
int data[STACK_DEPTH], address[ADDRESSES], ports[1024];
int image[IMAGE_SIZE];
int shrink, padding;
char filename[2048];
} VM;
s_cgi **cgi;
/* Macros ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#define CELL int
#define DROP vm->data[vm->sp] = 0; if (--vm->sp < 0) vm->ip = IMAGE_SIZE;
#define TOS vm->data[vm->sp]
#define NOS vm->data[vm->sp-1]
#define TORS vm->address[vm->rsp]
enum vm_opcode {VM_NOP, VM_LIT, VM_DUP, VM_DROP, VM_SWAP, VM_PUSH, VM_POP,
VM_LOOP, VM_JUMP, VM_RETURN, VM_GT_JUMP, VM_LT_JUMP,
VM_NE_JUMP,VM_EQ_JUMP, VM_FETCH, VM_STORE, VM_ADD,
VM_SUB, VM_MUL, VM_DIVMOD, VM_AND, VM_OR, VM_XOR, VM_SHL,
VM_SHR, VM_ZERO_EXIT, VM_INC, VM_DEC, VM_IN, VM_OUT,
VM_WAIT };
#define NUM_OPS VM_WAIT
/* Console I/O Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
struct termios new_termios, old_termios;
FILE *input[12];
int isp=0;
void dev_putch(int c) {
if (c > 0)
putchar((char)c);
else
printf("\033[2J\033[1;1H");
/* Erase the previous character if c = backspace */
if (c == 8) {
putchar(32);
putchar(8);
}
}
int dev_getch() {
int c;
if ((c = getc(input[isp])) == EOF && input[isp] != stdin) {
fclose(input[isp--]);
return 0;
}
if (c == EOF && input[isp] == stdin)
exit(0);
return c;
}
void dev_include(char *s) {
FILE *file;
file = fopen(s, "r");
if (file)
input[++isp] = file;
}
void dev_init_input() {
isp = 0;
input[isp] = stdin;
}
void dev_init_output() {
tcgetattr(0, &old_termios);
new_termios = old_termios;
new_termios.c_iflag &= ~(BRKINT+ISTRIP+IXON+IXOFF);
new_termios.c_iflag |= (IGNBRK+IGNPAR);
new_termios.c_lflag &= ~(ICANON+ISIG+IEXTEN+ECHO);
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &new_termios);
}
void dev_cleanup() {
tcsetattr(0, TCSANOW, &old_termios);
}
/* File I/O Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void file_add(VM *vm) {
char s[1024];
int name = TOS; DROP;
int i = 0;
while(vm->image[name])
s[i++] = (char)vm->image[name++];
s[i] = 0;
dev_include(s);
}
int file_handle(VM *vm) {
char *modes[] = { "r", "r+", "w", "w+", "a", "a+" };
int mode = TOS; DROP;
int i, address = TOS; DROP;
char filename[256];
for (i = 0; i < 256; i++) {
filename[i] = vm->image[address+i];
if (! filename[i]) break;
}
FILE *handle = fopen(filename, modes[mode]);
return (int)handle;
}
int file_readc(VM *vm) {
FILE *handle = (FILE *) TOS; DROP;
int c = fgetc(handle);
if ( c == EOF )
return 0;
else
return c;
}
int file_writec(VM *vm) {
FILE *handle = (FILE *)TOS; DROP;
int c = TOS; DROP;
int r = fputc(c, handle);
if ( r == EOF )
return 0;
else
return -1;
}
int file_closehandle(VM *vm) {
fclose((FILE *)TOS); DROP;
return 1;
}
int file_getpos(VM *vm) {
FILE *handle = (FILE *)TOS; DROP;
int pos = (int) ftell(handle);
return pos;
}
int file_seek(VM *vm) {
FILE *handle = (FILE *)TOS; DROP;
int pos = TOS; DROP;
int r = fseek(handle, pos, SEEK_SET);
if ( r == 0 )
return -1;
else
return 0;
}
int file_size(VM *vm) {
FILE *handle = (FILE *)TOS; DROP;
int current = ftell(handle);
int r = fseek(handle, 0, SEEK_END);
int size = ftell(handle);
fseek(handle, current, SEEK_SET);
if ( r == 0 )
return size;
else
return 0;
}
int vm_load_image(VM *vm, char *image) {
FILE *fp;
int x = -1;
if ((fp = fopen(image, "rb")) != NULL) {
x = fread(&vm->image, sizeof(int), IMAGE_SIZE, fp);
fclose(fp);
}
if (x == -1)
{
if ((fp = fopen(GLOBAL, "rb")) != NULL) {
x = fread(&vm->image, sizeof(int), IMAGE_SIZE, fp);
fclose(fp);
}
}
return x;
}
int vm_save_image(VM *vm, char *image) {
FILE *fp;
int x = -1;
if ((fp = fopen(image, "wb")) == NULL)
{
fprintf(stderr, "Sorry, but I couldn't open %s\n", image);
dev_cleanup();
exit(-1);
}
if (vm->shrink == 0)
x = fwrite(&vm->image, sizeof(int), IMAGE_SIZE, fp);
else
x = fwrite(&vm->image, sizeof(int), vm->image[3], fp);
fclose(fp);
return x;
}
/* Socket I/O Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void rsocket(VM *vm) {
vm->sp++;
TOS = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
}
void rbind(VM *vm) {
struct sockaddr_in address;
int port = TOS; DROP;
int sock = TOS;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
TOS = bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr));
}
void rlisten(VM *vm) {
TOS = listen(TOS, 3);
}
void raccept(VM *vm) {
int sock = TOS;
int addrlen;
struct sockaddr_in address;
addrlen = sizeof(struct sockaddr_in);
TOS = accept(sock, (struct sockaddr *)&address, (socklen_t *)&addrlen);
}
void rclose(VM *vm) {
shutdown(TOS, SHUT_RDWR);
TOS = close(TOS);
}
void rsend(VM *vm) {
int sock = TOS; DROP;
int data = TOS;
char s[65535];
int c;
for (c = 0; c < 65535; c++)
s[c] = '\0';
for (c = 0; vm->image[data] != 0; c++, data++)
s[c] = (char)vm->image[data];
TOS = send(sock, s, strlen(s), 0);
}
void rrecv(VM *vm) {
int sock = TOS;
char s[2] = { 0, 0 };
recv(sock, s, 1, 0);
TOS = (int)s[0];
}
void rconnect(VM *vm) {
int sock = TOS; DROP;
int port = TOS; DROP;
int data = TOS;
struct sockaddr_in address;
struct hostent *server;
char s[1024];
int c, addrlen;
addrlen = sizeof(struct sockaddr_in);
for (c = 0; c < 1024; c++)
s[c] = '\0';
for (c = 0; vm->image[data] != 0; c++, data++)
s[c] = (char)vm->image[data];
server = gethostbyname(s);
bzero((char *) &address, sizeof(address));
address.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&address.sin_addr.s_addr, server->h_length);
address.sin_port = htons(port);
TOS = connect(sock, (struct sockaddr *)&address, (socklen_t)addrlen);
}
/* CGI ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void dev_getenv(VM *vm) {
int req, dest, c;
char s[65535];
char *r;
req = TOS; DROP;
dest = TOS; DROP;
for (c = 0; c < 65535; c++)
s[c] = '\0';
for (c = 0; vm->image[req] != 0; c++, req++)
s[c] = (char)vm->image[req];
r = getenv(s);
if (r != 0)
while (*r != '\0')
{
vm->image[dest] = *r;
dest++;
r++;
}
else
vm->image[dest] = 0;
}
/* The VM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void init_vm(VM *vm) {
int a;
vm->ip = 0; vm->sp = 0; vm->rsp = 0;
vm->shrink = 0;
for (a = 0; a < STACK_DEPTH; a++)
vm->data[a] = 0;
for (a = 0; a < ADDRESSES; a++)
vm->address[a] = 0;
for (a = 0; a < IMAGE_SIZE; a++)
vm->image[a] = 0;
for (a = 0; a < 1024; a++)
vm->ports[a] = 0;
}
void vm_process(VM *vm) {
struct winsize w;
int a, b, opcode;
opcode = vm->image[vm->ip];
switch(opcode) {
case VM_NOP:
break;
case VM_LIT:
vm->sp++;
vm->ip++;
TOS = vm->image[vm->ip];
break;
case VM_DUP:
vm->sp++;
vm->data[vm->sp] = NOS;
break;
case VM_DROP:
DROP
break;
case VM_SWAP:
a = TOS;
TOS = NOS;
NOS = a;
break;
case VM_PUSH:
vm->rsp++;
TORS = TOS;
DROP
break;
case VM_POP:
vm->sp++;
TOS = TORS;
vm->rsp--;
break;
case VM_LOOP:
TOS--;
if (TOS != 0)
{
vm->ip++;
vm->ip = vm->image[vm->ip] - 1;
}
else
{
vm->ip++;
DROP;
}
break;
case VM_JUMP:
vm->ip++;
vm->ip = vm->image[vm->ip] - 1;
if (vm->ip < 0)
vm->ip = IMAGE_SIZE;
else {
if (vm->image[vm->ip+1] == 0)
vm->ip++;
if (vm->image[vm->ip+1] == 0)
vm->ip++;
}
break;
case VM_RETURN:
vm->ip = TORS;
vm->rsp--;
if (vm->image[vm->ip+1] == 0)
vm->ip++;
if (vm->image[vm->ip+1] == 0)
vm->ip++;
break;
case VM_GT_JUMP:
vm->ip++;
if(NOS > TOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
case VM_LT_JUMP:
vm->ip++;
if(NOS < TOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
case VM_NE_JUMP:
vm->ip++;
if(TOS != NOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
case VM_EQ_JUMP:
vm->ip++;
if(TOS == NOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
case VM_FETCH:
TOS = vm->image[TOS];
break;
case VM_STORE:
vm->image[TOS] = NOS;
DROP DROP
break;
case VM_ADD:
NOS += TOS;
DROP
break;
case VM_SUB:
NOS -= TOS;
DROP
break;
case VM_MUL:
NOS *= TOS;
DROP
break;
case VM_DIVMOD:
a = TOS;
b = NOS;
TOS = b / a;
NOS = b % a;
break;
case VM_AND:
a = TOS;
b = NOS;
DROP
TOS = a & b;
break;
case VM_OR:
a = TOS;
b = NOS;
DROP
TOS = a | b;
break;
case VM_XOR:
a = TOS;
b = NOS;
DROP
TOS = a ^ b;
break;
case VM_SHL:
a = TOS;
b = NOS;
DROP
TOS = b << a;
break;
case VM_SHR:
a = TOS;
b = NOS;
DROP
TOS = b >>= a;
break;
case VM_ZERO_EXIT:
if (TOS == 0) {
DROP
vm->ip = TORS;
vm->rsp--;
}
break;
case VM_INC:
TOS += 1;
break;
case VM_DEC:
TOS -= 1;
break;
case VM_IN:
a = TOS;
TOS = vm->ports[a];
vm->ports[a] = 0;
break;
case VM_OUT:
vm->ports[0] = 0;
vm->ports[TOS] = NOS;
DROP DROP
break;
case VM_WAIT:
if (vm->ports[0] == 1)
break;
/* Input */
if (vm->ports[0] == 0 && vm->ports[1] == 1) {
vm->ports[1] = dev_getch();
vm->ports[0] = 1;
}
/* Output (character generator) */
if (vm->ports[2] == 1) {
dev_putch(TOS); DROP
vm->ports[2] = 0;
vm->ports[0] = 1;
}
if (vm->ports[4] != 0) {
vm->ports[0] = 1;
switch (vm->ports[4]) {
case 1: vm_save_image(vm, vm->filename);
vm->ports[4] = 0;
break;
case 2: file_add(vm);
vm->ports[4] = 0;
break;
case -1: vm->ports[4] = file_handle(vm);
break;
case -2: vm->ports[4] = file_readc(vm);
break;
case -3: vm->ports[4] = file_writec(vm);
break;
case -4: vm->ports[4] = file_closehandle(vm);
break;
case -5: vm->ports[4] = file_getpos(vm);
break;
case -6: vm->ports[4] = file_seek(vm);
break;
case -7: vm->ports[4] = file_size(vm);
break;
default: vm->ports[4] = 0;
}
}
/* Capabilities */
if (vm->ports[5] != 0) {
vm->ports[0] = 1;
switch(vm->ports[5]) {
case -1: vm->ports[5] = IMAGE_SIZE;
break;
case -2: vm->ports[5] = 0;
break;
case -3: ioctl(0, TIOCGWINSZ, &w);
vm->ports[5] = w.ws_col;
break;
case -4: ioctl(0, TIOCGWINSZ, &w);
vm->ports[5] = w.ws_row;
break;
case -5: vm->ports[5] = vm->sp;
break;
case -6: vm->ports[5] = vm->rsp;
break;
case -7: vm->ports[5] = 0;
break;
case -8: vm->ports[5] = time(NULL);
break;
case -9: vm->ports[5] = 0;
vm->ip = IMAGE_SIZE;
break;
default: vm->ports[5] = 0;
}
}
if (vm->ports[8] != 0) {
a = vm->ports[8];
vm->ports[0] = 1;
switch (a) {
case -1: rsocket(vm);
break;
case -2: rbind(vm);
break;
case -3: rlisten(vm);
break;
case -4: raccept(vm);
break;
case -5: rclose(vm);
break;
case -6: rsend(vm);
break;
case -7: rrecv(vm);
break;
case -8: rconnect(vm);
break;
}
vm->ports[8] = 0;
}
if (vm->ports[9] != 0) {
switch(vm->ports[9])
{
case 1:
vm->ports[9] = 0;
vm->ports[0] = 1;
dev_getenv(vm);
break;
}
}
break;
default:
vm->rsp++;
TORS = vm->ip;
vm->ip = vm->image[vm->ip] - 1;
if (vm->ip < 0)
vm->ip = IMAGE_SIZE;
else {
if (vm->image[vm->ip+1] == 0)
vm->ip++;
if (vm->image[vm->ip+1] == 0)
vm->ip++;
}
break;
}
vm->ports[3] = 1;
}
/* Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int main(int argc, char **argv)
{
int i;
VM *vm = malloc(sizeof(VM));
cgiDebug(0, 0);
cgi = cgiInit();
strcpy(vm->filename, "retroImage");
init_vm(vm);
dev_init_input();
/* Parse the command line arguments */
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "--with") == 0)
dev_include(argv[++i]);
if (strcmp(argv[i], "--image") == 0)
strcpy(vm->filename, argv[++i]);
if (strcmp(argv[i], "--shrink") == 0)
vm->shrink = 1;
}
dev_init_output();
if (vm_load_image(vm, vm->filename) == -1) {
dev_cleanup();
printf("Sorry, unable to find %s\n", vm->filename);
exit(1);
}
for (vm->ip = 0; vm->ip < IMAGE_SIZE; vm->ip++)
vm_process(vm);
dev_cleanup();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment