fork download
  1. // ABC: A human readable byte-code language
  2. // http://t...content-available-to-author-only...d.org/techref/idea/minimalcontroller.htm
  3.  
  4. #include <stdio.h>
  5. //yeah I know. But I think these are cool, so:
  6. #define TRUE (1==1)
  7. #define FALSE (!TRUE)
  8. //registers are made up of bytes, so 1 is the default size, this is really the
  9. //spacing /between/ registers, so the maximum size each register can process.
  10. //A size of 4 means 32 bit operations will be possible. 8 would be 64.
  11. //This doesn't matter until the op width can be set.
  12. #define REG_SIZE 4
  13. //size of the program memory
  14. #define MEM_SIZE 1000
  15. //amount of memory to include after 'z' register. Stack grows here
  16. #define STACK_SIZE 100
  17. int num;
  18. char dst, src;
  19. unsigned char op;
  20. char flag;
  21. #define REG_SIZE_CHARS (1+'z'-'a')*REG_SIZE+STACK_SIZE
  22. #define REG_SIZE_INTS REG_SIZE_CHARS / sizeof(int)
  23. union reg_t { char c[REG_SIZE_CHARS]; int i[REG_SIZE_INTS]; } reg;
  24. //1 indexed array of registers. 0 is "no register found"
  25. //Code memory for programs. Could be FLASH or EEPROM
  26. char mem[MEM_SIZE];
  27. char *radix; //radix. "r" in the register array
  28. int *pc; //program counter pointer. "p" in the register array
  29. int next; //pointer to free location in memory for the next instruction.
  30. char c; //hold the current character being processed
  31.  
  32. int is_white_space(char c) {
  33. if (' '==c) return TRUE; //spaces have no effect
  34. if ('\r'==c) return TRUE; //\n ends a line \r is extra, need to skip it
  35. return FALSE;
  36. }
  37. char get_inst() { //get the next byte code instruction
  38. if (*pc) { //if the pc isn't null
  39. return c = mem[*pc++]; //get pre-loaded code from RAM
  40. //TODO when out of data from ram, switch to external input
  41. }
  42. //wait for the console.
  43. while (is_white_space(c = getchar()));//printf("got %d\n", c);
  44. if (c == EOF) c = FALSE;
  45. //TODO add external input to end of RAM for backtracking?
  46. return c;
  47. }
  48. void skip_line() {
  49. printf(" skip ");
  50. get_inst();
  51. while( c != 0 //End of File
  52. && c != '\n' //End of Line
  53. && c != '!' //"else"
  54. ) {
  55. get_inst();
  56. }
  57. printf(" past %d\n", c);
  58. get_inst();
  59. }
  60. int if_number(char c) {
  61. if ((c>='0' && c<='9') || (c>='A' && c<='F')) return TRUE;
  62. return FALSE;
  63. }
  64. void number() {
  65. while (if_number(c) ) {
  66. char digit = c-'0';
  67. if (digit>9) digit -= 'A'-'9';
  68. if (digit > *radix) {
  69. printf("\nERR: #%d > r%d\n",digit,*radix);
  70. return;
  71. }
  72. num *= *radix;
  73. num += digit;
  74. printf("#%d.",num);
  75. get_inst(); //if you used it, replace it.
  76. }
  77. }
  78. int is_register(char c) {
  79. if (c>='a' && c<='z') return TRUE;
  80. return FALSE;
  81. }
  82. int is_src_dst_reg(char sd) {
  83. if (sd >= 1 && sd <=26) return TRUE;
  84. return FALSE;
  85. }
  86. int registers() {
  87. char r = FALSE;
  88. if (is_register(c)) {//register
  89. r = 1 + c-'a'; //1 to 26
  90. r = r * REG_SIZE; //allocate some space between each register
  91. r += num; //if a number was given, offset by that amount. 4a is a+4
  92. //TODO: 'a' will end up as zero, which flags no destination?
  93. num = 0;
  94. get_inst(); //if you used it, replace it.
  95. printf(" r:%d.",r);
  96. }
  97. return r;
  98.  
  99. }
  100. #define FIRST_DEVICE 128
  101. int devices() {
  102. char d = FALSE;
  103. if (c>='A' && c<='Z') {//device
  104. d = c - 'A';
  105. d += FIRST_DEVICE;
  106. get_inst(); //if you used it, replace it.
  107. printf(" D:%d.",d);
  108. }
  109. return d;
  110. }
  111. void destination() {
  112. dst = registers();
  113. if (!dst) dst = devices();
  114. printf(" dst:%d.", dst);
  115. }
  116. void operation() {
  117. if ((c>' ' && c<'0') //after ctrl codes, but before numbers
  118. || (c>'9' && c<'A') //after numbers, but before uppercase letters
  119. || (c>'Z' && c<'a') //after uppercase, but below lowercase letters
  120. || (c>'a') //after lowercase letters.
  121. ) { //we have an operation
  122. op = c;
  123. get_inst(); //if you used it, replace it.
  124. if (c == op) { op+=128; get_inst();} //duplicate? set high bit
  125. if ('=' == c && '<' == op) { op = 'L'; get_inst();} //less than or equal
  126. if ('=' == c && '>' == op) { op = 'G'; get_inst();} //greater than or equal
  127. if ('\"' == op) { printf("\nString:\"");//quote
  128. if (is_src_dst_reg(dst)) { //move source to destination until EOF or ending "
  129. reg.i[(int)dst] = next; //point register to memory
  130. while(('\"' != c) && (EOF != c)) { printf("%c", c); //TODO also check *pc
  131. mem[next++] = c;
  132. get_inst();
  133. }
  134. get_inst(); //move past closing quote
  135. printf("\"\n");
  136. }
  137. }
  138. printf(" op:%d.c:%d",op,c);
  139. }
  140. }
  141. void source() {
  142. src = registers();
  143. if (!src) src = devices();
  144. printf(" src:%d.", src);
  145. }
  146. int read_device(int src) {
  147. //TODO read devices.
  148. return 0;
  149. }
  150. void do_it() {
  151. //convert src and dst into the values at thier addresses for width
  152. //TODO support widths
  153. int src_val = 0;
  154. if (src <= sizeof(reg.c)) { //register
  155. src_val = reg.c[(int)src] + num; printf("src:%d %c val:%d. ", src, src/REG_SIZE, src_val);
  156. } else { //otherwise it's a device
  157. src_val = read_device(src);
  158. }
  159. int dst_val = reg.c[(int)dst];
  160. //use num if no source
  161. switch(op) {
  162. case ':': dst_val = src_val; break;
  163. case '+': dst_val += src_val; break;
  164. case '-': dst_val -= src_val; break;
  165. case '*': dst_val *= src_val; break;
  166. case '/': dst_val /= src_val; break;
  167. case '|': dst_val |= src_val; break;
  168. case '&': dst_val &= src_val; break;
  169. case '>'+128: dst_val >>= src_val; break;
  170. case '<'+128: dst_val <<= src_val; break;
  171. case '=': flag = (dst_val == src_val); break;
  172. case '>': flag = (dst_val > src_val); break;
  173. case '<': flag = (dst_val < src_val); break;
  174. case 'G': flag = (dst_val >= src_val); break;
  175. case 'L': flag = (dst_val <= src_val); break;
  176. case '(': break; //parameter list (register indirection)
  177. case ')': break; //call
  178. }
  179. //TODO support widths
  180. if (dst <= sizeof(reg.c)) { //register
  181. reg.i[(int)dst] = dst_val;
  182. } else { //otherwise it's a device
  183. switch(dst-FIRST_DEVICE+'A') {
  184. case 'P': //Port/pin
  185. break;
  186. }
  187. }
  188. printf("dst:%d %c val:%d.\n", dst, dst/REG_SIZE, dst_val);
  189. //look ahead for conditionals //TODO should this be under operation?
  190. switch(c) {
  191. case '?':
  192. if (flag) get_inst(); //continue with next
  193. else skip_line(); //fail
  194. dst = op = src = num = 0;
  195. break;
  196. case '!':
  197. skip_line();
  198. break;
  199. case '.': c=0; return;//TODO up stack to top. For now, just return
  200. }
  201. }
  202.  
  203. int done() {
  204. if (dst && op && (src || num)) {
  205. printf("\nDO: %c+%d %c= %c %d +#: %d. c:%c %d\n", dst/REG_SIZE+'`', dst % REG_SIZE, op, src/REG_SIZE+'`', src % REG_SIZE, num, c, c);
  206. do_it();
  207. op = src = 0;
  208. }
  209. if ('\n' == c) { printf(" line end\n");
  210. get_inst(); //if you used it, replace it.
  211. num = dst = src = op = 0;
  212. return TRUE;
  213. }
  214. if (0 == c) { printf(" file end\n");
  215. return TRUE;
  216. }
  217. if (0 == dst) { printf(" lost dst ");
  218. return TRUE;
  219. }
  220. return FALSE;
  221.  
  222. }
  223.  
  224. //register letter to address in reg array. 1 based so 0 is false.
  225. #define REG(x) ((1+x-'a')*REG_SIZE)
  226.  
  227. int main(void) {
  228. radix = &reg.c[REG('r')]; //register 'r' stores the radix
  229. *radix = 10;
  230. printf("radix %d (%d)\n", *radix, reg.c[REG('r')]);
  231. pc = (int *)&reg.c[REG('p')]; //questionable. TODO Better way?
  232. *pc = 0; //start accepting bytcodes from console, not memory
  233. printf("program counter %d (%d)\n", *pc, reg.c[REG('p')]);
  234. //next = (int *)&reg.c[REG('n')]; //TODO Better way?
  235. next = 0;
  236. dst = src = op = 0;
  237. flag = FALSE;
  238. get_inst(); //prime the pump
  239. //return 0;
  240. while(c) {
  241. number(); //prefix numbers add to reg address. e.g. 4a is b, 2P is 2nd port
  242. destination(); //register or device to send data to.
  243. do {
  244. operation(); //operation to perform on source for dest
  245. number(); //offset for source
  246. source(); //source value
  247. } while(!done()); //do we have everything? do it! repeat op and source
  248. }
  249. printf("mem: %s.",mem);
  250. return 0;
  251. }
  252.  
Success #stdin #stdout 0s 5476KB
stdin
a"b:1"
a:1
b:a+1
c:a+b+10
d:0
c=14?d:1!d:2
stdout
radix 10 (10)
program counter 0 (0)
 r:4. dst:4.
String:"b:1"
 op:34.c:10 src:0. line end
 r:4. dst:4. op:58.c:49#1. src:0.
DO: a+0 := ` 0 +#: 1. c:
 10
src:0  val:1. dst:4  val:1.
 line end
 r:8. dst:8. op:58.c:97 r:4. src:4.
DO: b+0 := a 0 +#: 0. c:+ 43
src:4  val:0. dst:8  val:0.
 op:43.c:49#1. src:0.
DO: b+0 += ` 0 +#: 1. c:
 10
src:0  val:1. dst:8  val:1.
 line end
 r:12. dst:12. op:58.c:97 r:4. src:4.
DO: c+0 := a 0 +#: 0. c:+ 43
src:4  val:0. dst:12  val:0.
 op:43.c:98 r:8. src:8.
DO: c+0 += b 0 +#: 0. c:+ 43
src:8  val:0. dst:12  val:0.
 op:43.c:49#1.#10. src:0.
DO: c+0 += ` 0 +#: 10. c:
 10
src:0  val:10. dst:12  val:10.
 line end
 r:16. dst:16. op:58.c:48#0. src:0. line end
 r:12. dst:12. op:61.c:49#1.#14. src:0.
DO: c+0 == ` 0 +#: 14. c:? 63
src:0  val:14. dst:12  val:0.
 skip  past 33
 lost dst  r:16. dst:16. op:58.c:50#2. src:0.
DO: d+0 := ` 0 +#: 2. c:
 10
src:0  val:2. dst:16  val:2.
 line end
#1. dst:0. src:0. file end
mem: b:1.