d0b60129b7/vm/tiny/retro.c

User picture

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;
}