d0b60129b7/vm/tiny/retro.c
Commiter: Charles Childers
Author: Charles Childers
Revision: d0b60129b7
File Size: 21 KB
(April 23, 2010 00:25 UTC) About 2 years ago
add start of smaller C vm implementation
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers/* Ngaro VM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2008 - 2010 Charles Childers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#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>
/* Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#define IMAGE_SIZE 1000000
#define ADDRESSES 1024
#define STACK_DEPTH 100
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;
/* 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_CALL, 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");
if (c == 8) {
putchar(32);
putchar(8);
}
}
int dev_getch() {
int c;
if ((c = getc(input[isp])) == EOF && input[isp] != stdin) {
fclose(input[isp]);
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] != 0) {
s[i] = (char)vm->image[name];
i++; 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;
if ((fp = fopen(image, "rb")) == NULL)
return -1;
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;
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);
}
/* 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) {
int a, b, opcode;
opcode = vm->image[vm->ip];
switch(opcode) {
/***************************************************/
/* NOP Does Nothing. Used for padding */
/* Opcode: 0 Stack: - Address: - */
case VM_NOP:
break;
/***************************************************/
/* LIT Push the value in the following cell to */
/* the stack */
/* Opcode: 1 n Stack: -n Address: - */
case VM_LIT:
vm->sp++;
vm->ip++;
TOS = vm->image[vm->ip];
break;
/***************************************************/
/* DUP Duplicate the value on the top of the */
/* stack */
/* Opcode: 2 Stack: n-nn Address: - */
case VM_DUP:
vm->sp++;
vm->data[vm->sp] = NOS;
break;
/***************************************************/
/* DROP Drop the value on the top of the stack */
/* Opcode: 3 Stack: n- Address: - */
case VM_DROP:
DROP
break;
/***************************************************/
/* SWAP Exchange the top two values on the stack */
/* Opcode: 4 Stack: xy-yx Address: - */
case VM_SWAP:
a = TOS;
TOS = NOS;
NOS = a;
break;
/***************************************************/
/* PUSH Move the top value on the stack to the */
/* address stack. Remove it from the data */
/* stack. */
/* Opcode: 5 Stack: n- Address: -n */
case VM_PUSH:
vm->rsp++;
TORS = TOS;
DROP
break;
/***************************************************/
/* POP Move the top value from the address */
/* stack to the data stack. Remove it from */
/* the address stack. */
/* Opcode: 6 Stack: -n Address: n- */
case VM_POP:
vm->sp++;
TOS = TORS;
vm->rsp--;
break;
/***************************************************/
/* CALL Call a subroutine whose address is given */
/* in the following cell. Push the address */
/* following this instruction to the address*/
/* stack. */
/* Opcode: 7 a Stack: - Address: -a */
case VM_CALL:
vm->ip++;
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;
/***************************************************/
/* JUMP Unconditional jump to the address given */
/* in the following cell. */
/* Opcode: 8 a Stack: - Address: - */
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;
/***************************************************/
/* ; Return from a subroutine. Control is */
/* passed to the address on the top of the */
/* address stack. */
/* Opcode: 9 Stack: - Address: a- */
case VM_RETURN:
vm->ip = TORS;
vm->rsp--;
break;
/***************************************************/
/* >JUMP Jump to the address in the following */
/* cell if NOS > TOS. */
/* Opcode: 10 a Stack: xy- Address: - */
case VM_GT_JUMP:
vm->ip++;
if(NOS > TOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
/***************************************************/
/* <JUMP Jump to the address in the following */
/* cell if NOS < TOS. */
/* Opcode: 11 a Stack: xy- Address: - */
case VM_LT_JUMP:
vm->ip++;
if(NOS < TOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
/***************************************************/
/* !JUMP Jump to the address in the following */
/* cell if NOS <> TOS. */
/* Opcode: 12 a Stack: xy- Address: - */
case VM_NE_JUMP:
vm->ip++;
if(TOS != NOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
/***************************************************/
/* =JUMP Jump to the address in the following */
/* cell if NOS = TOS. */
/* Opcode: 13 a Stack: xy- Address: - */
case VM_EQ_JUMP:
vm->ip++;
if(TOS == NOS)
vm->ip = vm->image[vm->ip] - 1;
DROP DROP
break;
/***************************************************/
/* @ Fetch a value from a memory location */
/* Opcode: 14 Stack: a-n Address: - */
case VM_FETCH:
TOS = vm->image[TOS];
break;
/***************************************************/
/* ! Store a value to a memory location */
/* Opcode: 15 Stack: na- Address: - */
case VM_STORE:
vm->image[TOS] = NOS;
DROP DROP
break;
/***************************************************/
/* + Add TOS and NOS, leaving the result */
/* Opcode: 16 Stack: xy-z Address: - */
case VM_ADD:
NOS += TOS;
DROP
break;
/***************************************************/
/* - Subtract TOS from NOS, leaving the result*/
/* Opcode: 17 Stack: xy-z Address: - */
case VM_SUB:
NOS -= TOS;
DROP
break;
/***************************************************/
/* * Multiply TOS by NOS, leaving the result */
/* Opcode: 18 Stack: xy-z Address: - */
case VM_MUL:
NOS *= TOS;
DROP
break;
/***************************************************/
/* /MOD Divide NOS by TOS, leaving the quotient */
/* and remainder. */
/* Opcode: 19 Stack: xy-qr Address: - */
case VM_DIVMOD:
a = TOS;
b = NOS;
TOS = b / a;
NOS = b % a;
break;
/***************************************************/
/* AND Perform a bitwise and operation on TOS */
/* and NOS. */
/* Opcode: 20 Stack: xy-z Address: - */
case VM_AND:
a = TOS;
b = NOS;
DROP
TOS = a & b;
break;
/***************************************************/
/* OR Perform a bitwise or operation on TOS */
/* and NOS. */
/* Opcode: 21 Stack: xy-z Address: - */
case VM_OR:
a = TOS;
b = NOS;
DROP
TOS = a | b;
break;
/***************************************************/
/* XOR Perform a bitwise xor operation on TOS */
/* and NOS. */
/* Opcode: 22 Stack: xy-z Address: - */
case VM_XOR:
a = TOS;
b = NOS;
DROP
TOS = a ^ b;
break;
/***************************************************/
/* << Shift NOS left by TOS bits. */
/* Opcode: 23 Stack: xy-z Address: - */
case VM_SHL:
a = TOS;
b = NOS;
DROP
TOS = b << a;
break;
/***************************************************/
/* >> Shift NOS right by TOS bits. */
/* Opcode: 24 Stack: xy-z Address: - */
case VM_SHR:
a = TOS;
b = NOS;
DROP
TOS = b >>= a;
break;
/***************************************************/
/* 0; Return from a subroutine if TOS = 0. */
/* If TOS = 0, DROP TOS. */
/* If TOS <> 0, do nothing */
/* Opcode: 25 Stack: n- Address: a- */
/* Stack: n-n Address: - */
case VM_ZERO_EXIT:
if (TOS == 0) {
DROP
vm->ip = TORS;
vm->rsp--;
}
break;
/***************************************************/
/* 1+ Increase TOS by 1 */
/* Opcode: 26 Stack: x-y Address: - */
case VM_INC:
TOS += 1;
break;
/***************************************************/
/* 1- Decrease TOS by 1 */
/* Opcode: 27 Stack: x-y Address: - */
case VM_DEC:
TOS -= 1;
break;
/***************************************************/
/* IN Read a value from an I/O port */
/* Opcode: 28 Stack: p-n Address: - */
case VM_IN:
a = TOS;
TOS = vm->ports[a];
vm->ports[a] = 0;
break;
/***************************************************/
/* OUT Send a value to an I/O port */
/* Opcode: 29 Stack: np- Address: - */
case VM_OUT:
vm->ports[0] = 0;
vm->ports[TOS] = NOS;
DROP DROP
break;
/***************************************************/
/* WAIT Wait for an I/O event to occur. */
/* Opcode: 30 Stack: - Address: - */
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: vm->ports[5] = 0;
break;
case -4: vm->ports[5] = 0;
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) {
vm->ports[0] = 1;
switch (vm->ports[8]) {
case -1: rsocket(vm);
vm->ports[8] = 0;
break;
case -2: rbind(vm);
vm->ports[8] = 0;
break;
case -3: rlisten(vm);
vm->ports[8] = 0;
break;
case -4: raccept(vm);
vm->ports[8] = 0;
break;
case -5: rclose(vm);
vm->ports[8] = 0;
break;
case -6: rsend(vm);
vm->ports[8] = 0;
break;
case -7: rrecv(vm);
vm->ports[8] = 0;
break;
case -8: rconnect(vm);
vm->ports[8] = 0;
break;
default: vm->ports[8] = 0;
}
vm->ports[8] = 0;
}
break;
/***************************************************/
/* IMPLICIT CALL */
/* If we don't recognize the opcode, treat it as a */
/* subroutine address and CALL it. */
/* Opcode: * a Stack: - Address: -a */
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));
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);
/* Once done, cleanup */
dev_cleanup();
return 0;
} |