//ABC.c A minimal interpreted human readable bytecode language
//http://t...content-available-to-author-only...d.org/techref/idea/minimalcontroller.htm

#include <stdio.h>

#define NUM_PORTS 3
//number of IO ports to support.
#define VAR_SIZE 4
//bytes of storage in each variable

#define STACK_SIZE 10
#define NUM_VARS ( (VAR_SIZE*('z'-'a')) + STACK_SIZE + 2 )
//number of memory spaces needed for variables. 
//some number of bytes per variable address (a-z) plus stack size after that.
#define START_STACK ( NUM_VARS - STACK_SIZE + 1 )
//stack starts inside the variable sapce, but at the end, after 'z'
#define NUM_LCDS 40

#define START_PORTS ( NUM_VARS + 1 )
//memory reserved to shadow IO ports
#define START_LCD ( START_PORTS + NUM_PORTS + 1 )
//memory reserved to shadow local display
#define NUM_MEMS ( START_LCD + NUM_LCDS )
//amount of RAM memory required. 
//TODO: add a check to verify this uC has that much available.

#define NUM_FLASH 2048
//amount of FLASH or EEPROM memory

//use the first line for debug output, the second to remove it from any optimizing compiler
#define DEBUG if(1) 
//#define DEBUG if(0) 

char abc; //the incomming character
int skip_lines=0; //count of the number of lines to skip in branches.
unsigned char dst,src;
/* SRC and DST reference up to 128 8 bit "registers" or, when the high bit is set,
one of the devices (LCD/Keys, Serial, EEPROM, IO). Defaults to LCD/Keys. */
bool src_dst=false; //set when the SRC register is to be loaded, clear for DST
/* The DST is set first, and the SRC/DST flag toggled so that SRC is loaded next at 
which point the current operation and comparison are performed. */ 
int num=0; //numeric accumulator. Hex digits (0-9, A-F) are added into this
/* If SRC is not loaded (zero), NUM is the SRC. */
unsigned char op=0; //Operation
/* OP stores the operation to perform once we have a SRC (we should already have 
a DST) or when the line ends. After each operation SRC is cleared. 
At the end of each line, SRC/DST and DST is cleared. Since there is only one SRC 
at a time, binary operations like addition always accumulate into DST. i.e. DST 
is the implied second source in binary operations. The default operation, when 
no other is specified, is copy. i.e. if only a DST, then a SRC are specified, 
the value of the SRC is simply written to the DST. OP is not cleared so that " 
can escape ". In other words, so that quote can escape quote. */
bool true_flag; //set when the comparison finds truth, clear when false.
unsigned char vars[NUM_MEMS];
unsigned char flash[NUM_FLASH];
int flashptr=1; 
//can't start at address 0 in flash because a call to that address looks like console
int pcptr=0; //just a temp var to hold the current PC pointer

unsigned char pcp=START_STACK; 
//program counter pointer starts at TOS, in var space, after 'z'

void doop() {
	if (dst && op && (src || num) ) { //got enough to work with
		if ('"'==op) { //Quote? but don't update vars[dst] if it's a variable.
			if (NUM_VARS>dst) { //'cause it's a value quoted to a variable ptr
				flash[flashptr]=src; //so actually write it to flash instead
				DEBUG printf("\nflash[%i]=%c",flashptr,src);
				flashptr++;         //and move to next space in flash
				return;             //and that's it.
				}
			}
		else if (src>=START_PORTS) {
            //on a real uC, we would update vars[src] with the value of the port 
			//so add code here to actually read the port.
			DEBUG printf("   reading %i from port %i",vars[src],src-NUM_VARS); 
			};
		num += vars[src]; 
		switch(op) {
			case ':': vars[dst]=num; 		break;
			case '+': vars[dst]+=num;		break;
			case '-': vars[dst]-=num;		break;
			case '*': vars[dst]*=num;		break;
			case '/': vars[dst]/=num;		break;
			case '=': true_flag=(vars[dst]==num);	break;
			case '<': true_flag=(vars[dst]<num);	break;
			case '>': true_flag=(vars[dst]>num);	break;
			case '{': true_flag=(vars[dst]<=num);	break;
			case '}': true_flag=(vars[dst]>=num);	break;
			case '|': true_flag=(vars[dst]|=num);	break;
			case '&': true_flag=(vars[dst]&=num);	break;
			case '`': true_flag=(vars[dst]!=num);	break;
			}
		DEBUG {
			switch(op) { 
				case ':': case '+': case '-': case '*': case '/':
					printf("\n   vars\t[dst]\t%c=\tvars\t[src]\t+num;",op);
					printf("\n \\\\%i\t[%i]\t%c=\t%i\t[%i]\t+%i",vars[dst],dst,op,vars[src],src,num); 
					break;
				case '"':
					break;
				default:
					printf("\n  flag=\t(vars\t[dst]\t%c=\tvars\t[src]\t+num);",op);
					printf("\n \\\\%c\t %i\t[%i]\t%c=\t%i\t[%i]\t+%i",true_flag?'Y':'N',vars[dst],dst,op,vars[src],src,num); 
					break;
				}
			}
		if (dst>=NUM_VARS && dst<NUM_LCDS) {
			DEBUG printf("   writing %i to port %i",src,dst-START_PORTS);
            //on a real uC, we would update the port with the value of vars[dst]
			//so add code here to actually write to the port.
			};
		if (dst>=NUM_LCDS) {
			DEBUG printf("   writing %c to LCD @ %i",src,dst-START_LCD);
			//on a real uC, we would actually write the character to the LCD
			 dst++; //move to the next character on the LCD.
			};
		num=0;
		DEBUG printf("\n   num=0;src=0;");
		src = 0; //clear source leave dst and op.
		}
	}
	
int main () {
	printf("Ready %i\n", NUM_MEMS);
	vars[pcp+0] = 0;
	vars[pcp+1] = 0;
	vars[pcp+2] = 0;
	//initialize the current PC to zero...
	pcptr = 0;
	//which flags that we are executing from the console
	while (	(pcptr=((vars[pcp+2]<<16) + (vars[pcp+1]<<8) + (vars[pcp+0]<<0))) //Not executing from FLASH?
		|| EOF != (abc=getchar())	//get a character from the console, until EOF
		) {
		//DEBUG printf("pcptr=%u, abc=%c",pcptr,abc);
		if (!abc) { //we didn't get a character from the console
			abc = flash[pcptr];
			//get abc from flash at PC, and increment PC
			//DEBUG printf("GOT %c from %u via pcp %u",abc,vars[pcp],pcp);
			if(!(++vars[pcp+0])) if (!(++vars[pcp+1])) if (!(++vars[pcp+2]));
			//increment must roll over to pcp+1, etc... Note: pcptr updated next time around
			}
		//can we process a ready operation here first? Helps with quote. TODO: test.
		if (src) doop();	// if we already had a source, process it first
		if ('\n'==abc) {
			// DEBUG printf("\n   ");
			doop();
			src_dst=false; //reset back to looking for destinations at EOL
			num=op=0;
			if (skip_lines) {
				skip_lines--;
				DEBUG printf("\n----------------");
				continue;
				}
			}
		else printf("\n%c  ",abc);
		if (skip_lines) {
			DEBUG printf("skipped");
			continue;
			}
		if ('\"'==abc) {
		            DEBUG printf("quote, op==%c",op);
			if ('\"'==op) {//ending quote?
				op=' '; //secret op means we were quoting.
		            	abc=' ';
				DEBUG printf(" ending quote");
				}
		            else if (' '==op) { //double quote
				op='"'; //just pickup where we left off
				DEBUG printf(" double quote");
				}
		            else if (NUM_VARS>dst && ':'==op) {
				vars[dst+0]=flashptr>> 0 & 0xFF;
				vars[dst+1]=flashptr>> 8 & 0xFF;
				vars[dst+2]=flashptr>>16 & 0xFF;
				//This may be a 3 or 4 byte value. Could be more efficient.
				DEBUG printf("vars[%i]=%i //assign variable to flashptr", dst, flashptr);
				}
			//continue;
			} //done with input was a quote
		if ('"'==op) {  //If we are quoting
		            src=abc;    //value of input in source, just pass it on
			DEBUG printf(" src=%i;",src);
		            }
		else if ((abc>='A' && abc<='F') || (abc>='0' && abc<='9')) { //number
			num *= 16;
			num += (abc>'9' ? abc-'A'+10 : abc-'0');
			DEBUG printf(" num=%i;",num);
			}
		else if (('a'<=abc && 'z'>=abc) || (abc>='G' && abc<='Z')) { //variable or function
			switch(abc) {
				case 'P':	//Port and pin. 
					num+=START_PORTS;	//correct P to past the last variable
					DEBUG printf(" port=%i;",num);
					break;
				case 'L':	//Local display 
					DEBUG printf("local display @%i",num);
					num+=START_LCD;
					break;
				default: //it's a variable
					num += ((abc-'a')*VAR_SIZE)+1; //spread out in var space by VAR_SIZE
					break;
				}
			//variables or functions can be sources or destinations.
			if (!src_dst) { //looking for a destination
				dst = num;
				DEBUG printf(" dst=%i;vars[dst]=%i",dst,vars[dst]);
				src_dst=true;
				}
			else {	//looking for a source
				src = num;
				DEBUG printf(" src=%i;",src);
				//src_dst=true; //stay on sources until EOL.
				}
			num = 0;
			}
		else switch(abc) { //must be an operator
			case '@':
				if (src) { //if we have a source
					num+=vars[src];
					src=0;
					DEBUG printf(" num=%i;src=0;",num);
					}
				else {
					num+=vars[dst];
					dst=0;
					DEBUG printf(" num=%i;dst=0;",num);
					src_dst=false;//look for another destination.
					};					
				break;
			case '?':
				doop();
				if (!true_flag) {
					skip_lines=1;
					DEBUG printf(" FALSE! (skip the line)");
					}
				else {
					DEBUG printf(" TRUE! {continue}");
					};
				src_dst=false; //reset back to looking for destinations at EOL
				break;
			case ')': //call. PCP++, (DST)->var(PCP)
				//Check if dst is a variable.
				if (NUM_VARS < dst) {
					DEBUG printf("ERROR: Call to other than variable address!");
					return 1;
					}
				pcp+=VAR_SIZE; //increment PCP to the next variable space
				// check if PCP > NUM_MEMS. Stack overflow
				if (NUM_VARS < pcp) {
					DEBUG printf("ERROR: Stack Overflow!");
					return 1;
					}
				//copy the value of dst to the new pcp
				vars[pcp+0]=vars[dst+0]; 
				vars[pcp+1]=vars[dst+1]; 
				vars[pcp+2]=vars[dst+2]; 
				src_dst=false;
				DEBUG printf(" CALL %c at %u, via pcp %u", dst+'a'-1, ((vars[pcp+2]<<16)+(vars[pcp+1]<<8)+(vars[pcp+0]<<0)), pcp);
				break;
			case '.': //return. 0!=var(PCP)? PCP--
				//TODO: check if we are already on the console
				//Check if PCP >= VAR_SIZE
				DEBUG printf(" RETURN to %u, via pcp %u",vars[pcp],pcp);
				if (START_STACK >= pcp) {
					DEBUG printf("ERROR: Return stack underflow. Return while not in subroutine?");
					break;
					}
				doop();	//finish up whatever was done in the subroutine
				pcp-=VAR_SIZE; //decrement PCP to the prior variable space
				break;
			case '\n':
				DEBUG printf("\n----------------");
				break;
			default:
				doop();	//in case we already have a destination, operation and source.
				if ('='==abc && op) { // an equal might come after < or >
					abc=op+('{'-'<'); 
					// add 123 - 60 or 63. This maps <, =, > to {, |, }
					// <= is {, == is |, >= is }, ?= is ~, != is ` (back single quote)
					// so don't use {} in the future! () and [] are ok.
					}
                
				op=abc;
				DEBUG printf(" op=\'%c\';",op);
				break;
			}
		//putchar(abc);
		abc=0; //done with this character.	
		}	
	}