Retro is an concatenative, stack based language with roots in Forth. This implementation of the VM, when coupled with a 16-bit image, allows for a complete, interactive programming environment running on the mbed microcontroller. This requires an image file (grab the latest at [[http://rx-core.org/retroImg]]) to operate. The image file contains the actual Retro language and environment, and also includes support for interactive control of the digital I/O pins on the mbed. == Quickstart == Throw the rx_mbed .bin on your mbed, along with the retroImg file. Reset, and connect in your terminal emulator. Then do: <<code>> with mbed' 1 LED1 p! <</code>> And the first onboard LED should light up. Then: <<code>> 0 LED1 p! <</code>> And it should turn back off. == Notes == Pins 5 - 30 are exposed via constants //P5// - //P30// in the //mbed'// vocabulary. These are mapped as DigitalInOut, and can be read using //p@// and written to using //p!//. The four onboard LED's are exposed via constants //LED1// - //LED4// and are mapped as DigitalOut devices. With the standard image loaded, you get around 3800 cells of memory to play with. (Cells are 16-bit values; the VM does not allow for addressing smaller or larger data sizes directly). This is not a lot, but should be enough to allow a fair amount of experimenting. == Tested I/O Devices == I have only a limited amount of hardware at this point. All I've tested are: * LEDs * Piezo Buzzer I'm hoping to be able to test other components in the near future, but first need to locate, purchase, and assemble them.
main.cpp
- Committer:
- crc
- Date:
- 2011-03-17
- Revision:
- 2:b7a8bbb38e2f
- Parent:
- 1:659c8f87b4bc
File content as of revision 2:b7a8bbb38e2f:
/* 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 24 #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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ DigitalInOut P5(p5); DigitalInOut P6(p6); DigitalInOut P7(p7); DigitalInOut P8(p8); DigitalInOut P9(p9); DigitalInOut P10(p10); DigitalInOut P11(p11); DigitalInOut P12(p12); DigitalInOut P13(p13); DigitalInOut P14(p14); DigitalInOut P15(p15); DigitalInOut P16(p16); DigitalInOut P17(p17); DigitalInOut P18(p18); DigitalInOut P19(p19); DigitalInOut P20(p20); DigitalInOut P21(p21); DigitalInOut P22(p22); DigitalInOut P23(p23); DigitalInOut P24(p24); DigitalInOut P25(p25); DigitalInOut P26(p26); DigitalInOut P27(p27); DigitalInOut P28(p28); DigitalInOut P29(p29); DigitalInOut P30(p30); DigitalOut L1(LED1); DigitalOut L2(LED2); DigitalOut L3(LED3); DigitalOut L4(LED4); void handle_mbed(VM *vm) { switch (vm->ports[13]) { case -1: switch (TOS) { case 5: P5.output(); break; case 6: P6.output(); break; case 7: P7.output(); break; case 8: P8.output(); break; case 9: P9.output(); break; case 10: P10.output(); break; case 11: P11.output(); break; case 12: P12.output(); break; case 13: P13.output(); break; case 14: P14.output(); break; case 15: P15.output(); break; case 16: P16.output(); break; case 17: P17.output(); break; case 18: P18.output(); break; case 19: P19.output(); break; case 20: P20.output(); break; case 21: P21.output(); break; case 22: P22.output(); break; case 23: P23.output(); break; case 24: P24.output(); break; case 25: P25.output(); break; case 26: P26.output(); break; case 27: P27.output(); break; case 28: P28.output(); break; case 29: P29.output(); break; case 30: P30.output(); break; } vm->ports[13] = 0; break; case -2: switch (TOS) { case 5: P5.input(); break; case 6: P6.input(); break; case 7: P7.input(); break; case 8: P8.input(); break; case 9: P9.input(); break; case 10: P10.input(); break; case 11: P11.input(); break; case 12: P12.input(); break; case 13: P13.input(); break; case 14: P14.input(); break; case 15: P15.input(); break; case 16: P16.input(); break; case 17: P17.input(); break; case 18: P18.input(); break; case 19: P19.input(); break; case 20: P20.input(); break; case 21: P21.input(); break; case 22: P22.input(); break; case 23: P23.input(); break; case 24: P24.input(); break; case 25: P25.input(); break; case 26: P26.input(); break; case 27: P27.input(); break; case 28: P28.input(); break; case 29: P29.input(); break; case 30: P30.input(); break; } vm->ports[13] = 0; break; case 5: P5 = TOS; DROP; vm->ports[13] = 0; break; case 6: P6 = TOS; DROP; vm->ports[13] = 0; break; case 7: P7 = TOS; DROP; vm->ports[13] = 0; break; case 8: P8 = TOS; DROP; vm->ports[13] = 0; break; case 9: P9 = TOS; DROP; vm->ports[13] = 0; break; case 10: P10 = TOS; DROP; vm->ports[13] = 0; break; case 11: P11 = TOS; DROP; vm->ports[13] = 0; break; case 12: P12 = TOS; DROP; vm->ports[13] = 0; break; case 13: P13 = TOS; DROP; vm->ports[13] = 0; break; case 14: P14 = TOS; DROP; vm->ports[13] = 0; break; case 15: P15 = TOS; DROP; vm->ports[13] = 0; break; case 16: P16 = TOS; DROP; vm->ports[13] = 0; break; case 17: P17 = TOS; DROP; vm->ports[13] = 0; break; case 18: P18 = TOS; DROP; vm->ports[13] = 0; break; case 19: P19 = TOS; DROP; vm->ports[13] = 0; break; case 20: P20 = TOS; DROP; vm->ports[13] = 0; break; case 21: P21 = TOS; DROP; vm->ports[13] = 0; break; case 22: P22 = TOS; DROP; vm->ports[13] = 0; break; case 23: P23 = TOS; DROP; vm->ports[13] = 0; break; case 24: P24 = TOS; DROP; vm->ports[13] = 0; break; case 25: P25 = TOS; DROP; vm->ports[13] = 0; break; case 26: P26 = TOS; DROP; vm->ports[13] = 0; break; case 27: P27 = TOS; DROP; vm->ports[13] = 0; break; case 28: P28 = TOS; DROP; vm->ports[13] = 0; break; case 29: P29 = TOS; DROP; vm->ports[13] = 0; break; case 30: P30 = TOS; DROP; vm->ports[13] = 0; break; case 31: L1 = TOS; DROP; vm->ports[13] = 0; break; case 32: L2 = TOS; DROP; vm->ports[13] = 0; break; case 33: L3 = TOS; DROP; vm->ports[13] = 0; break; case 34: L4 = TOS; DROP; vm->ports[13] = 0; break; default: vm->ports[13] = 0; } switch (vm->ports[14]) { case 5: SP++; TOS = P5; vm->ports[14] = 0; break; case 6: SP++; TOS = P6; vm->ports[14] = 0; break; case 7: SP++; TOS = P7; vm->ports[14] = 0; break; case 8: SP++; TOS = P8; vm->ports[14] = 0; break; case 9: SP++; TOS = P9; vm->ports[14] = 0; break; case 10: SP++; TOS = P10; vm->ports[14] = 0; break; case 11: SP++; TOS = P11; vm->ports[14] = 0; break; case 12: SP++; TOS = P12; vm->ports[14] = 0; break; case 13: SP++; TOS = P13; vm->ports[14] = 0; break; case 14: SP++; TOS = P14; vm->ports[14] = 0; break; case 15: SP++; TOS = P15; vm->ports[14] = 0; break; case 16: SP++; TOS = P16; vm->ports[14] = 0; break; case 17: SP++; TOS = P17; vm->ports[14] = 0; break; case 18: SP++; TOS = P18; vm->ports[14] = 0; break; case 19: SP++; TOS = P19; vm->ports[14] = 0; break; case 20: SP++; TOS = P20; vm->ports[14] = 0; break; case 21: SP++; TOS = P21; vm->ports[14] = 0; break; case 22: SP++; TOS = P22; vm->ports[14] = 0; break; case 23: SP++; TOS = P23; vm->ports[14] = 0; break; case 24: SP++; TOS = P24; vm->ports[14] = 0; break; case 25: SP++; TOS = P25; vm->ports[14] = 0; break; case 26: SP++; TOS = P26; vm->ports[14] = 0; break; case 27: SP++; TOS = P27; vm->ports[14] = 0; break; case 28: SP++; TOS = P28; vm->ports[14] = 0; break; case 29: SP++; TOS = P29; vm->ports[14] = 0; break; case 30: SP++; TOS = P30; vm->ports[14] = 0; break; default: vm->ports[14] = 0; } } /* 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[13] != 0 || vm->ports[14] != 0) { vm->ports[0] = 1; handle_mbed(vm); } } } /* 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(); } }