Charles Childers / Mbed 2 deprecated rx_mbed

Dependencies:   mbed

main.cpp

Committer:
crc
Date:
2011-03-16
Revision:
1:659c8f87b4bc
Parent:
0:5fbb38eaed90
Child:
2:b7a8bbb38e2f

File content as of revision 1:659c8f87b4bc:

/* Ngaro VM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   Copyright (c) 2008 - 2011, Charles Childers
   Copyright (c) 2009 - 2010, Luke Parrish
   Copyright (c) 2010,        Marc Simpson
   Copyright (c) 2010,        Jay Skeer
   Copyright (c) 2011,        Kenneth Keating
   Copyright (c) 2011,        Erturk Kocalar
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

#include "mbed.h"
#include <stdint.h>

Serial pc(USBTX, USBRX);
LocalFileSystem local("local");

/* Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                +---------+---------+---------+
                | 16 bit  | 32 bit  | 64 bit  |
   +------------+---------+---------+---------+
   | IMAGE_SIZE | 32000   | 1000000 | 1000000 |
   +------------+---------+---------+---------+
   | CELL       | int16_t | int32_t | int64_t |
   +------------+---------+---------+---------+

   If memory is tight, cut the MAX_FILE_NAME and MAX_ENV_QUERY. For
   most purposes, these can be much smaller.

   You can also cut the ADDRESSES stack size down, but if you have
   heavy nesting or recursion this may cause problems. If you do modify
   it and experience odd problems, try raising it a bit higher.
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#define CELL          int16_t
#define IMAGE_SIZE      14000              /* This can be raised to 15k */
#define ADDRESSES         256
#define STACK_DEPTH        50
#define PORTS              13
#define MAX_FILE_NAME      48
#define MAX_ENV_QUERY       1
#define MAX_OPEN_FILES      4

typedef struct {
  CELL sp, rsp, ip;
  CELL data[STACK_DEPTH];
  CELL address[ADDRESSES];
  CELL ports[PORTS];
  CELL image[IMAGE_SIZE];
  char filename[MAX_FILE_NAME];
} VM;

/* Macros ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
#define IP   vm->ip
#define SP   vm->sp
#define RSP  vm->rsp
#define DROP vm->data[SP] = 0; if (--SP < 0) IP = IMAGE_SIZE;
#define TOS  vm->data[SP]
#define NOS  vm->data[SP-1]
#define TORS vm->address[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

/* Console I/O Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
FILE *input[MAX_OPEN_FILES];
CELL isp=0;

void dev_putch(CELL c) {
  if (c > 0) {
    putc((char)c, stdout);
    if (c == '\n') {
      putc('\r', stdout);
    }
  }
  else
    printf("\033[2J\033[1;1H");
  /* Erase the previous character if c = backspace */
  if (c == 8) {
    putc(32, stdout);
    putc(8, stdout);
  }
}

CELL dev_getch() {
  CELL 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() {
}

void dev_cleanup() {
}

/* File I/O Support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
FILE *files[MAX_OPEN_FILES];

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

void file_add(VM *vm) {
  char s[MAX_FILE_NAME];
  CELL name = TOS; DROP;
  CELL i = 0;
  while(vm->image[name])
    s[i++] = (char)vm->image[name++];
  s[i] = 0;
  dev_include(s);
}

CELL file_handle(VM *vm) {
  CELL slot = file_free();
  CELL mode = TOS; DROP;
  CELL i, address = TOS; DROP;
  char filename[MAX_FILE_NAME];
  for (i = 0; i < MAX_FILE_NAME; 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 = 0;
  }
  return slot;
}

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

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

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

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

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

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

CELL file_delete(VM *vm) {
  CELL i, address;
  char filename[MAX_FILE_NAME];
  address = TOS; DROP;
  for (i = 0; i < MAX_FILE_NAME; i++) {
    filename[i] = vm->image[address+i];
    if (! filename[i]) break;
  }
  if (remove(filename) == 0)
    return -1;
  else
    return 0;
}

CELL vm_load_image(VM *vm, char *image) {
  FILE *fp;
  CELL x = -1;

  if ((fp = fopen(image, "rb")) != NULL) {
    x = fread(&vm->image, sizeof(CELL), IMAGE_SIZE, fp);
    fclose(fp);
  }
  return x;
}

CELL vm_save_image(VM *vm, char *image) {
  FILE *fp;
  CELL x = -1;

  if ((fp = fopen(image, "wb")) == NULL)
  {
    fprintf(stderr, "Sorry, but I couldn't open %s\n", image);
    dev_cleanup();
    exit(-1);
  }

  x = fwrite(&vm->image, sizeof(CELL), vm->image[3], fp);
  fclose(fp);

  return x;
}

/* Environment Query ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void dev_getenv(VM *vm) {
  DROP; DROP;
}

/* mbed devices ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
DigitalOut L1(LED1);
DigitalOut L2(LED2);
DigitalOut L3(LED3);
DigitalOut L4(LED4);

/* Device I/O Handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void handle_devices(VM *vm) {
  if (vm->ports[0] != 1) {
    /* 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;
    }

    /* File IO and Image Saving */
    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] = SP;
                  break;
        case -6:  vm->ports[5] = RSP;
                  break;
        case -7:  vm->ports[5] = 0;
                  break;
        case -8:  vm->ports[5] = time(NULL);
                  break;
        case -9:  vm->ports[5] = 0;
                  IP = IMAGE_SIZE;
                  break;
        case -10: vm->ports[5] = 0;
                  dev_getenv(vm);
                  break;
        case -11: vm->ports[5] = 80;
                  break;
        case -12: vm->ports[5] = 25;
                  break;
        default:  vm->ports[5] = 0;
      }
    }

    /* mbed io devices */
    if (vm->ports[12] != 0) {
      vm->ports[0] = 1;
      switch (vm->ports[12]) {
        case  1: L1 = TOS; DROP;
                 vm->ports[12] = 0;
                 break;
        case  2: L2 = TOS; DROP;
                 vm->ports[12] = 0;
                 break;
        case  3: L3 = TOS; DROP;
                 vm->ports[12] = 0;
                 break;
        case  4: L4 = TOS; DROP;
                 vm->ports[12] = 0;
                 break;
        default: vm->ports[12] = 0;
      }
    }

  }
}

/* The VM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
void init_vm(VM *vm) {
   CELL a;
   IP = 0;  SP = 0;  RSP = 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 < PORTS; a++)
      vm->ports[a] = 0;
}

void vm_process(VM *vm) {
  CELL a, b, opcode;
  opcode = vm->image[IP];

  switch(opcode) {
    case VM_NOP:
         break;
    case VM_LIT:
         SP++;
         IP++;
         TOS = vm->image[IP];
         break;
    case VM_DUP:
         SP++;
         vm->data[SP] = NOS;
         break;
    case VM_DROP:
         DROP
         break;
    case VM_SWAP:
         a = TOS;
         TOS = NOS;
         NOS = a;
         break;
    case VM_PUSH:
         RSP++;
         TORS = TOS;
         DROP
         break;
    case VM_POP:
         SP++;
         TOS = TORS;
         RSP--;
         break;
    case VM_LOOP:
         TOS--;
         if (TOS != 0 && TOS > -1)
         {
           IP++;
           IP = vm->image[IP] - 1;
         }
         else
         {
           IP++;
           DROP;
         }
         break;
    case VM_JUMP:
         IP++;
         IP = vm->image[IP] - 1;
         if (IP < 0)
           IP = IMAGE_SIZE;
         else {
           if (vm->image[IP+1] == 0)
             IP++;
           if (vm->image[IP+1] == 0)
             IP++;
         }
         break;
    case VM_RETURN:
         IP = TORS;
         RSP--;
         if (IP < 0)
           IP = IMAGE_SIZE;
         else {
           if (vm->image[IP+1] == 0)
             IP++;
           if (vm->image[IP+1] == 0)
             IP++;
         }
         break;
    case VM_GT_JUMP:
         IP++;
         if(NOS > TOS)
           IP = vm->image[IP] - 1;
         DROP DROP
         break;
    case VM_LT_JUMP:
         IP++;
         if(NOS < TOS)
           IP = vm->image[IP] - 1;
         DROP DROP
         break;
    case VM_NE_JUMP:
         IP++;
         if(TOS != NOS)
           IP = vm->image[IP] - 1;
         DROP DROP
         break;
    case VM_EQ_JUMP:
         IP++;
         if(TOS == NOS)
           IP = vm->image[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
           IP = TORS;
           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:
         handle_devices(vm);
         break;
    default:
         RSP++;
         TORS = IP;
         IP = vm->image[IP] - 1;

         if (IP < 0)
           IP = IMAGE_SIZE;
         else {
           if (vm->image[IP+1] == 0)
             IP++;
           if (vm->image[IP+1] == 0)
             IP++;
         }
         break;
  }
  vm->ports[3] = 1;
}

/* Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
int main(int argc, char **argv)
{
  VM static vmm;
  VM *vm = &vmm;
  strcpy(vm->filename, "/local/retroImg");

  while (1) {
    init_vm(vm);
    dev_init_input();

    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 (IP = 0; IP < IMAGE_SIZE; IP++)
      vm_process(vm);

    dev_cleanup();
  }
}