Created
October 12, 2010 01:55
-
-
Save crcx/621536 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/* 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