Not logged in

Artifact e04eec81edecc3a3971bfaf725410b6456550a64

File retro.c part of check-in [90c0b418f9] - Update to 11.0-2010.12.19 and latest VM; fix editor.rx and debug.rx to work with new image by charleschilders on 2010-12-20 00:46:13. [annotate]


/* 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 <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;

/* 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 + 1

int stats[NUM_OPS];

/* 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
FILE *files[8];

int file_free()
{
  int i;
  for(i = 1; i < 8; i++)
  {
    if (files[i] == 0)
      return i;
  }
  return 0;
}

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) {
  int slot = file_free();
  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;
  }
  if (slot > 0)
  {
    if (mode == 0)
      files[slot] = fopen(filename, "r");
    if (mode == 1)
      files[slot] = fopen(filename, "w");
    if (mode == 2)
      files[slot] = fopen(filename, "a");
    if (mode == 3)
      files[slot] = fopen(filename, "r+");
  }
  if (files[slot] == NULL)
  {
    files[slot] = 0;
    slot = -1;
  }
  return slot;
}

int file_readc(VM *vm) {
  int c = fgetc(files[TOS]); DROP;
  if ( c == EOF )
    return 0;
  else
    return c;
}

int file_writec(VM *vm) {
  int slot = TOS; DROP;
  int c = TOS; DROP;
  int r = fputc(c, files[slot]);
  if ( r == EOF )
    return 0;
  else
    return 1;
}

int file_closehandle(VM *vm) {
  fclose(files[TOS]);
  files[TOS] = 0;
  DROP;
  return 1;
}

int file_getpos(VM *vm) {
  int slot = TOS; DROP;
  return (int) ftell(files[slot]);
}

int file_seek(VM *vm) {
  int slot = TOS; DROP;
  int pos  = TOS; DROP;
  int r = fseek(files[slot], pos, SEEK_SET);
  return r;
}

int file_size(VM *vm) {
  int slot = TOS; DROP;
  int current = ftell(files[slot]);
  int r = fseek(files[slot], 0, SEEK_END);
  int size = ftell(files[slot]);
  fseek(files[slot], current, SEEK_SET);
  if ( r == 0 )
    return size;
  else
    return 0;
}

int file_delete(VM *vm) {
  int i, address;
  char filename[256];
  address = TOS; DROP;
  for (i = 0; i < 256; i++) {
    filename[i] = vm->image[address+i];
    if (! filename[i]) break;
  }
  return unlink(filename);
}

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

/* Environment Query ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
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:
         stats[VM_NOP]++;
         break;
    case VM_LIT:
         stats[VM_LIT]++;
         vm->sp++;
         vm->ip++;
         TOS = vm->image[vm->ip];
         break;
    case VM_DUP:
         stats[VM_DUP]++;
         vm->sp++;
         vm->data[vm->sp] = NOS;
         break;
    case VM_DROP:
         stats[VM_DROP]++;
         DROP
         break;
    case VM_SWAP:
         stats[VM_SWAP]++;
         a = TOS;
         TOS = NOS;
         NOS = a;
         break;
    case VM_PUSH:
         stats[VM_PUSH]++;
         vm->rsp++;
         TORS = TOS;
         DROP
         break;
    case VM_POP:
         stats[VM_POP]++;
         vm->sp++;
         TOS = TORS;
         vm->rsp--;
         break;
    case VM_LOOP:
         stats[VM_LOOP]++;
         TOS--;
         if (TOS != 0 && TOS > -1)
         {
           vm->ip++;
           vm->ip = vm->image[vm->ip] - 1;
         }
         else
         {
           vm->ip++;
           DROP;
         }
         break;
    case VM_JUMP:
         stats[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:
         stats[VM_RETURN]++;
         vm->ip = TORS;
         vm->rsp--;
         break;
    case VM_GT_JUMP:
         stats[VM_GT_JUMP]++;
         vm->ip++;
         if(NOS > TOS)
           vm->ip = vm->image[vm->ip] - 1;
         DROP DROP
         break;
    case VM_LT_JUMP:
         stats[VM_LT_JUMP]++;
         vm->ip++;
         if(NOS < TOS)
           vm->ip = vm->image[vm->ip] - 1;
         DROP DROP
         break;
    case VM_NE_JUMP:
         stats[VM_NE_JUMP]++;
         vm->ip++;
         if(TOS != NOS)
           vm->ip = vm->image[vm->ip] - 1;
         DROP DROP
         break;
    case VM_EQ_JUMP:
         stats[VM_EQ_JUMP]++;
         vm->ip++;
         if(TOS == NOS)
           vm->ip = vm->image[vm->ip] - 1;
         DROP DROP
         break;
    case VM_FETCH:
         stats[VM_FETCH]++;
         TOS = vm->image[TOS];
         break;
    case VM_STORE:
         stats[VM_STORE]++;
         vm->image[TOS] = NOS;
         DROP DROP
         break;
    case VM_ADD:
         stats[VM_ADD]++;
         NOS += TOS;
         DROP
         break;
    case VM_SUB:
         stats[VM_SUB]++;
         NOS -= TOS;
         DROP
         break;
    case VM_MUL:
         stats[VM_MUL]++;
         NOS *= TOS;
         DROP
         break;
    case VM_DIVMOD:
         stats[VM_DIVMOD]++;
         a = TOS;
         b = NOS;
         TOS = b / a;
         NOS = b % a;
         break;
    case VM_AND:
         stats[VM_AND]++;
         a = TOS;
         b = NOS;
         DROP
         TOS = a & b;
         break;
    case VM_OR:
         stats[VM_OR]++;
         a = TOS;
         b = NOS;
         DROP
         TOS = a | b;
         break;
    case VM_XOR:
         stats[VM_XOR]++;
         a = TOS;
         b = NOS;
         DROP
         TOS = a ^ b;
         break;
    case VM_SHL:
         stats[VM_SHL]++;
         a = TOS;
         b = NOS;
         DROP
         TOS = b << a;
         break;
    case VM_SHR:
         stats[VM_SHR]++;
         a = TOS;
         b = NOS;
         DROP
         TOS = b >>= a;
         break;
    case VM_ZERO_EXIT:
         stats[VM_ZERO_EXIT]++;
         if (TOS == 0) {
           DROP
           vm->ip = TORS;
           vm->rsp--;
         }
         break;
    case VM_INC:
         stats[VM_INC]++;
         TOS += 1;
         break;
    case VM_DEC:
         stats[VM_DEC]++;
         TOS -= 1;
         break;
    case VM_IN:
         stats[VM_IN]++;
         a = TOS;
         TOS = vm->ports[a];
         vm->ports[a] = 0;
         break;
    case VM_OUT:
         stats[VM_OUT]++;
         vm->ports[0] = 0;
         vm->ports[TOS] = NOS;
         DROP DROP
         break;
    case VM_WAIT:
         stats[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;
             case -8: vm->ports[4] = file_delete(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;
             case -10: vm->ports[5] = 0;
                       dev_getenv(vm);
                       break;
             case -11: ioctl(0, TIOCGWINSZ, &w);
                       vm->ports[5] = w.ws_col;
                       break;
             case -12: ioctl(0, TIOCGWINSZ, &w);
                       vm->ports[5] = w.ws_row;
                       break;
             default:  vm->ports[5] = 0;
           }
         }
         break;
    default:
         stats[NUM_OPS]++;
         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, s;
  VM *vm = malloc(sizeof(VM));
  strcpy(vm->filename, "retroImage");

  init_vm(vm);
  dev_init_input();

  s = 0;
  for (i = 0; i < NUM_OPS; i++)
    stats[i] = 0;

  /* 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;
    if (strcmp(argv[i], "--track") == 0)
      s = -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();
  if (s == -1)
  {
    printf("Runtime Statistics\n");
    printf("NOP:     %d\n", stats[VM_NOP]);
    printf("LIT:     %d\n", stats[VM_LIT]);
    printf("DUP:     %d\n", stats[VM_DUP]);
    printf("DROP:    %d\n", stats[VM_DROP]);
    printf("SWAP:    %d\n", stats[VM_SWAP]);
    printf("PUSH:    %d\n", stats[VM_PUSH]);
    printf("POP:     %d\n", stats[VM_POP]);
    printf("LOOP:    %d\n", stats[VM_LOOP]);
    printf("JUMP:    %d\n", stats[VM_JUMP]);
    printf("RETURN:  %d\n", stats[VM_RETURN]);
    printf(">JUMP:   %d\n", stats[VM_GT_JUMP]);
    printf("<JUMP:   %d\n", stats[VM_LT_JUMP]);
    printf("!JUMP:   %d\n", stats[VM_NE_JUMP]);
    printf("=JUMP:   %d\n", stats[VM_EQ_JUMP]);
    printf("FETCH:   %d\n", stats[VM_FETCH]);
    printf("STORE:   %d\n", stats[VM_STORE]);
    printf("ADD:     %d\n", stats[VM_ADD]);
    printf("SUB:     %d\n", stats[VM_SUB]);
    printf("MUL:     %d\n", stats[VM_MUL]);
    printf("DIVMOD:  %d\n", stats[VM_DIVMOD]);
    printf("AND:     %d\n", stats[VM_AND]);
    printf("OR:      %d\n", stats[VM_OR]);
    printf("XOR:     %d\n", stats[VM_XOR]);
    printf("SHL:     %d\n", stats[VM_SHL]);
    printf("SHR:     %d\n", stats[VM_SHR]);
    printf("0;:      %d\n", stats[VM_ZERO_EXIT]);
    printf("INC:     %d\n", stats[VM_INC]);
    printf("DEC:     %d\n", stats[VM_DEC]);
    printf("IN:      %d\n", stats[VM_IN]);
    printf("OUT:     %d\n", stats[VM_OUT]);
    printf("WAIT:    %d\n", stats[VM_WAIT]);
    printf("CALL:    %d\n", stats[NUM_OPS]);

    i = 0;
    for (s = 0; s < NUM_OPS; s++)
    {
      i += stats[s];
    }
    printf("Total opcodes processed: %d\n", i);
  }
  return 0;
}