Created
April 8, 2017 09:41
-
-
Save anonymous/373dd0bd0198a8d0f367a590969cccee to your computer and use it in GitHub Desktop.
A simulator of the J1 Forth CPU - running SIMPL - on an Arduino Due (Atmel ARM M3)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a cross compiler which uses the SIMPL text interpreter framework to compile short phrases of J1 | |
// machine language which are then executed on a simulated J1 | |
// J1_DUE_Sim_24 | |
// This version runs on an Arduino Due - or any of the larger RAM Arduinos | |
// Ken Boak April 2017 | |
char num_buff[11]; // buffer large enough to hold 10 digit 32 bit int | |
char *num_buff_ptr; | |
int i = 0 ; | |
int number = 0; | |
int number3 = 0; | |
int ch3 = 0; | |
int token = 0; | |
int assm = 0; | |
// int putcharx(int); | |
// int putchar1(int); | |
// SIMPL initialisation | |
#define bufRead(addr) (*(unsigned char *)(addr)) | |
#define bufWrite(addr, b) (*(unsigned char *)(addr) = (b)) | |
unsigned char bite; | |
unsigned int x = 0; | |
unsigned int y = 0; | |
int len = 48; | |
char array[26][48]; | |
int a = 0; // integer variables a,b,c,d | |
int b = 0; | |
int c = 0; | |
int e = 5; // d is used to denote the digital port pin for I/O operations | |
int z = 0; | |
char name; | |
char* parray; | |
char buf[64]; | |
char* addr; | |
// ------ J1 simulator functions | |
void setup_J1(void); | |
static void execute(int); | |
static void push(int); | |
static int pop(void); | |
//--------------------------------------------------------------------------------- | |
// Variables used in J1 CPU model | |
unsigned short m[0x1000]; // 4096 words of RAM | |
static unsigned long time_gone = 0; | |
static unsigned short t; | |
static unsigned short n; //2nd item on stack | |
static unsigned short d[0x20]; // data stack | |
static unsigned short r[0x20]; // return stack | |
static unsigned short pc; // program counter, counts 16 bit words (cells) | |
static unsigned char dsp, rsp; // point to top entry | |
// static unsigned short* memory; // ram | |
static int sx[4] = { 0, 1, -2, -1 }; // 2-bit sign extension | |
unsigned int insn; // the 16 bit encoded instruction | |
unsigned int _t; | |
unsigned int _pc; | |
unsigned int target; // 13 bit target address for jumps and calls | |
unsigned int next_t; | |
long loop_count = 0; | |
int count = 0; | |
//--------------------------------------------------------------------------------- | |
void setup() { | |
// put your setup code here, to run once: | |
Serial.begin(115200); | |
setup_J1(); | |
Serial.println("Starting J1 Simulator"); | |
pinMode(2,OUTPUT); | |
pinMode(3,OUTPUT); | |
pinMode(4,OUTPUT); | |
pinMode(5,OUTPUT); | |
pinMode(6,OUTPUT); | |
pinMode(7,OUTPUT); | |
pinMode(8,OUTPUT); | |
pinMode(9,OUTPUT); | |
pinMode(10,OUTPUT); | |
pinMode(11,OUTPUT); | |
pinMode(12,OUTPUT); | |
pinMode(13,OUTPUT); | |
parray = &array[0][0]; // parray is the pointer to the first element | |
} | |
//---------------------------------------------------------------------------------------------------------- | |
// Start of the main while loop | |
//---------------------------------------------------------------------------------------------------------- | |
void loop() { | |
txtRead(buf, 64); | |
txtChk(buf); // check if it is a colon definition | |
txtEval(buf); | |
} // End of while | |
//------------------------------------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------------------------ | |
void setup_J1(void) { | |
// Load up a simple count program into first 7 locations of memory | |
m[0] = 0x8020; // LIT 0x20 (0x20 is the address of the count variable | |
m[1] = 0x6C00; // Fetch [0020] | |
m[2] = 0x8001; // LIT 1 We are going to add 10 | |
m[3] = 0x6200; // ADD | |
m[4] = 0x8020; // LIT 0x20 | |
m[5] = 0x6020; // Store | |
m[6] = 0x0000; // JMP 0000 | |
m[7] = 0x0000; // NOP | |
m[8] = 0x6000; // NOP | |
m[9] = 0x0000; // JMP 0000 | |
m[10] = 0x0000; | |
m[11] = 0x0000; //LIT 31 | |
m[12] = 0x0000; //LIT 51 | |
m[13] = 0x0000; // OR | |
m[14] = 0x0000; | |
m[15] = 0x0000; // JMP 00 | |
m[16] = 0x0000; | |
m[17] = 0x0000; //Literal 0x0123 | |
m[18] = 0x0000; | |
m[19] = 0x0000; | |
m[20] = 0x0000; | |
m[21] = 0x6300; | |
m[22] = 0x6400; | |
m[23] = 0x6500; | |
m[24] = 0x6600; | |
m[25] = 0x6700; | |
m[26] = 0x6800; | |
m[27] = 0x6900; | |
m[28] = 0x6A00; | |
m[29] = 0x6B00; | |
m[30] = 0x6C00; | |
m[31] = 0x6D00; | |
m[32] = 0x0000; // Use this location as a counter | |
m[33] = 0x6F00; | |
m[34] = 0x0000; | |
m[35] = 0x2001; | |
m[36] = 0x4002; | |
m[37] = 0x8003; | |
m[38] = 0x8004; | |
m[39] = 0x8005; | |
m[40] = 0x8006; | |
//---------------------------------------------------------------------------------- | |
// Clear the rest of the first 255 bytes of memory | |
for(i=41; i<=255; i++) | |
{ | |
m[i] = 0; // clear memory | |
} | |
for(i=0; i<=32; i++) // clear data stack and return stack | |
{ | |
d[i] = 0; | |
r[i] = 0; | |
} | |
pc = 0; | |
} | |
// End of Setup_J1 | |
//-------------------------------------------------------------------------------------- | |
// CPU Model | |
static void push(int v) // push v onto the data stack | |
{ | |
dsp = 0x1f & (dsp + 1); | |
d[dsp] = t; | |
t = v; | |
} | |
static int pop(void) // pop value from the data stack and return it | |
{ | |
int v = t; | |
t = d[dsp]; | |
dsp = 0x1f & (dsp - 1); | |
return v; | |
} | |
// ------------------------------------------------------------------------ | |
static void execute(int instruction) // This is the CPU model | |
{ | |
insn = (instruction & 0xFFFF) ; | |
_pc = pc + 1; | |
if (insn & 0x8000) { // literal | |
push(insn & 0x7fff); | |
// Serial.println("PUSHED"); | |
} | |
else { | |
int target = insn & 0x1fff; | |
switch (insn >> 13) { | |
case 0: // jump | |
_pc = target; | |
break; | |
case 1: // conditional jump | |
if (pop() == 0) | |
_pc = target; | |
break; | |
case 2: // call | |
rsp = 31 & (rsp + 1); | |
r[rsp] = _pc << 1; | |
_pc = target; | |
break; | |
case 3: // ALU | |
if (insn & 0x1000) /* R->PC */ | |
_pc = r[rsp] >> 1; | |
n = d[dsp]; | |
switch ((insn >> 8) & 0xf) { | |
case 0: _t = t; break; | |
case 1: _t = n; break; | |
case 2: _t = t + n; break; | |
case 3: _t = t & n; break; | |
case 4: _t = t | n; break; | |
case 5: _t = t ^ n; break; | |
case 6: _t = ~t; break; | |
case 7: _t = -(t == n); break; | |
case 8: _t = -((signed short)n < (signed short)t); break; | |
case 9: _t = n >> t; break; | |
case 10: _t = t - 1; break; | |
case 11: _t = r[rsp]; break; | |
case 12: _t = m[t]; break; | |
case 13: _t = n << t; break; | |
case 14: _t = (rsp << 8) + dsp; break; | |
case 15: _t = -(n < t); break; | |
} | |
dsp = 31 & (dsp + sx[insn & 3]); | |
rsp = 31 & (rsp + sx[(insn >> 2) & 3]); | |
if (insn & 0x80) /* T->N */ | |
d[dsp] = t; | |
if (insn & 0x40) /* T->R */ | |
r[rsp] = t; | |
if (insn & 0x20) /* N->[T] */ | |
m[t] = n; | |
t = _t; | |
break; | |
} | |
} | |
pc = _pc; | |
insn = m[pc]; | |
next_t = _t; | |
} | |
// End of CPU model | |
// ------------------------------------------------------------------------ | |
// SIMPL Text Interpreter | |
// ------------------------------------------------------------------------ | |
void txtRead (char *p, byte n) { | |
byte i = 0; | |
while (i < (n-1)) { | |
while (!Serial.available()); | |
char ch = Serial.read(); | |
if (ch == '\r' || ch == '\n') break; | |
if (ch >= ' ' && ch <= '~') { | |
*p++ = ch; | |
i++; | |
} | |
} | |
*p = 0; | |
} | |
void txtChk (char *buf) { // Check if the text starts with a colon and if so store in temp[] | |
if (*buf == ':') { | |
char ch; | |
int i =0; | |
while ((ch = *buf++)){ | |
if (ch == ':') { | |
Serial.println(*buf); // get the name from the first character | |
name = *buf ; | |
buf++; | |
} | |
bufWrite((parray + (len*(name-65) +i)),*buf); | |
i++; | |
} | |
x = 1; | |
} | |
} | |
// ------------------------------------------------------------------------ | |
void txtEval (char *buf) { | |
unsigned int k = 0; | |
char *loop; | |
char ch; | |
while ((ch = *buf++)) { | |
switch (ch) { | |
case '0': | |
case '1': | |
case '2': | |
case '3': | |
case '4': | |
case '5': | |
case '6': | |
case '7': | |
case '8': | |
case '9': | |
x = ch - '0'; | |
while (*buf >= '0' && *buf <= '9') { | |
x = x*10 + (*buf++ - '0'); | |
} | |
break; | |
case 'A': // Points the interpreter to the array containing the User defined words | |
case 'B': | |
case 'C': | |
case 'D': | |
case 'E': | |
case 'F': | |
case 'G': | |
case 'H': | |
case 'I': | |
case 'J': | |
case 'K': | |
case 'L': | |
case 'M': | |
case 'N': | |
case 'O': | |
case 'P': | |
case 'Q': | |
case 'R': | |
case 'S': | |
case 'T': | |
case 'U': | |
case 'V': | |
case 'W': | |
case 'X': | |
case 'Y': | |
case 'Z': | |
name = ch - 65; | |
addr = parray + (len*name); | |
txtEval(addr); | |
break; | |
//--------------------------------------------------------------------------------------------- | |
// Decode the tokens and form the J1 machine Instructions | |
//--------------------------------------------------------------------------------------------- | |
/* | |
* Literal 8000H + Value (15 bits) | |
* | |
* Jump 0000H + destination (13 bits) | |
* | |
* Conditional Jump 2000H + destination (13 bits) | |
* | |
* Call 4000H + destination (13 bits) | |
* | |
* ALU Operations Bits 8 - 11 + 6000H | |
* | |
* 0 T | |
* 1 N | |
* 2 T+N | |
* 3 T&N | |
* 4 T|N | |
* 5 T^N | |
* 6 ~T | |
* 7 N=T | |
* 8 T>N | |
* 9 N>>T | |
* 10 T-1 | |
* 11 R | |
* 12 [T] | |
* 13 N<<T | |
* 14 Depth | |
* 15 Tu>N | |
* | |
* Stack Moves | |
* | |
* T->N Bit 7 | |
* T->R Bit 6 | |
* N->[T] Bit 5 | |
* R->PC Bit 4 | |
* | |
* ALU Return Stack Bits 3,2 | |
* | |
* r+0 0 | |
* r+1 1 | |
* r-1 3 | |
* r-2 2 | |
* | |
* ALU Stack Variable Bits 1,0 | |
* | |
* d+0 0 | |
* d+1 1 | |
* d-1 3 | |
* d-2 2 | |
* | |
* | |
*/ | |
//--------------------------------------------------------------------------------------------- | |
// Form the J1 opcodes | |
//--------------------------------------------------------------------------------------------- | |
case '\r' : | |
break; | |
case '\n' : | |
break; | |
// ALU Logical and arithmetic Operations | |
case '#' : //NOP | |
token = 0; | |
Serial.print(" NOP "); | |
Serial.print( assm = (256 * token)+ 24576,HEX); | |
break; | |
case '+' : //ADD | |
token = 2; | |
Serial.print(" ADD "); | |
Serial.print( assm = (256 * token)+ 24576+3,HEX); | |
break; | |
case '&' : //AND | |
token = 3; | |
Serial.print(" AND "); | |
Serial.print( assm = (256 * token)+ 24576+3,HEX); | |
break; | |
case '|' : //OR | |
token = 4; | |
Serial.print(" OR "); | |
Serial.print( assm = (256 * token)+ 24576+3,HEX); | |
break; | |
case '^' : //XOR | |
token = 5; | |
Serial.print(" XOR "); | |
Serial.print( assm = (256 * token)+ 24576+3,HEX); | |
break; | |
case '~' : //INV | |
token = 6; | |
Serial.print(" INV "); | |
Serial.print( assm = (256 * token)+ 24576,HEX); | |
break; | |
case '-' : //SUB | |
token = 6; | |
assm = (128 * token)+ 24576; | |
Serial.print(" SUB "); | |
Serial.print( assm = (256 * token)+ 24576,HEX); | |
break; | |
case '*' : //MUL/SHL | |
token = 13; | |
assm = (256 * token)+ 24576 +3; | |
Serial.print(" SHL "); | |
Serial.print( assm, HEX); | |
break; | |
case '/' : //DIV/SHR | |
token = 9; | |
assm = (256 * token)+ 24576 +3; | |
Serial.print(" SHR "); | |
Serial.print( assm, HEX); | |
break; | |
//-------------------------------------------------------------- | |
// Comparison Operations | |
//-------------------------------------------------------------- | |
case '<' : //LT | |
token = 8; | |
assm = (256 * token)+ 24576 +3; | |
Serial.print(" LT "); | |
Serial.print( assm, HEX); | |
break; | |
case '=' : //EQ | |
token = 7; | |
assm = (256 * token)+ 24576 +3; | |
Serial.print( assm, HEX); | |
Serial.print(" EQ "); | |
break; | |
case '>' : //GT | |
token = 0; | |
Serial.print(" GT "); | |
break; | |
//-------------------------------------------------------------- | |
// Stack Operations | |
//-------------------------------------------------------------- | |
case ',' : //PUSH | |
token = 0; | |
Serial.print(" PUSH "); | |
Serial.print( assm = (256 * token)+ 24576 +3,HEX); | |
break; | |
case '.' : //POP/EMIT | |
token = 0; | |
Serial.print(" POP "); | |
break; | |
case '"' : //DUP | |
token = 0; | |
assm = 24576 + 128 + 1; | |
Serial.print(" DUP "); | |
Serial.print( assm ,HEX); | |
break; | |
case 39 : //DROP | |
token = 1; | |
assm = (256 * token)+ 24576 +3; | |
Serial.print(" DROP "); | |
Serial.print( assm ,HEX); | |
break; | |
case '$' : // SWAP | |
token = 1; | |
assm = (256 * token)+ 24576 +128 + 0 ; | |
Serial.print(" SWAP "); | |
Serial.print( assm ,HEX); | |
break; | |
case '%' : //OVER | |
token = 1; | |
assm = (256 * token)+ 24576 +128 + 1 ; | |
Serial.print(" OVER "); | |
Serial.print( assm ,HEX); | |
break; | |
case ' ' : //LIT | |
token = 0; | |
assm = 32768 + x ; | |
Serial.print(x); | |
Serial.print(" LIT "); | |
Serial.print(assm,HEX); | |
break; | |
//---------------------------------------------- | |
// Conditional Execution - Program Flow | |
case '(' : //BEGIN | |
token = 0; | |
Serial.print(" BEGIN "); | |
break; | |
case ')' : //END | |
token = 0; | |
Serial.print(" END "); | |
break; | |
case '{' : //DO | |
token = 0; | |
Serial.print(" DO "); | |
break; | |
case '}' : //LOOP | |
token = 0; | |
Serial.print(" LOOP "); | |
break; | |
case 92 : //JUMP | |
token = 0; | |
assm = x ; | |
Serial.print(x); | |
Serial.print(" JUMP "); | |
break; | |
case ':' : //CALL | |
token = 0; | |
assm = 16384 + x; | |
Serial.print(x); | |
Serial.print(" CALL "); | |
break; | |
case ';' : //RET/EXIT | |
token = 0; | |
assm = 24576 + 4096 + 12; | |
Serial.print(" RET "); | |
Serial.print( assm ,HEX); | |
break; | |
case '?' : //?KEY | |
token = 0; | |
Serial.print("?KEY "); | |
break; | |
//-------------------------------------------------------------- | |
// Memory Operations | |
//-------------------------------------------------------------- | |
case '@' : //FETCH | |
token = 12; | |
assm = 24576 + (256 * token); | |
Serial.print(" FETCH "); | |
Serial.print(assm, HEX); | |
break; | |
case '!' : //STORE | |
token = 1; | |
assm = 24576 + (256 * token) + 32 + 3; | |
Serial.print(" STORE "); | |
Serial.print(assm, HEX); | |
break; | |
//-------------------------------------------------------------- | |
// Compound Operations - a work in progress | |
//-------------------------------------------------------------- | |
case '[' : //SWITCH | |
token = 0; | |
Serial.print(" SWITCH "); | |
break; | |
case ']' : //CASE | |
token = 0; | |
Serial.print(" CASE "); | |
break; | |
case '_' : //STRING | |
token = 0; | |
Serial.print(" STRING "); | |
break; | |
case '`' : //TOR - move T to R and vice versa | |
token = 11; | |
assm = (256 * token) + 24576 + 128 + 64 + 1 + 12; | |
Serial.print(" TOR "); | |
Serial.print(assm, HEX); | |
break; | |
case 'r' : //FROMR - move T to R and vice versa | |
token = 1; | |
assm = (256 * token) + 24576 + 64 + 3 + 8; | |
Serial.print(" FROMR "); | |
Serial.print(assm, HEX); | |
break; | |
} // End of Switch-Case | |
execute(assm); // single step the J1 instruction | |
// Some Debug output | |
Serial.print(" PC="); | |
Serial.print(pc,HEX); | |
Serial.print(" TOP="); | |
Serial.print(t,HEX); | |
Serial.print(" DS0="); | |
Serial.print(d[dsp],HEX); | |
Serial.print(" DS1="); | |
Serial.print(d[dsp-1],HEX); | |
Serial.print(" DS2="); | |
Serial.print(d[dsp-2],HEX); | |
Serial.print(" DS3="); | |
Serial.print(d[dsp-3],HEX); | |
Serial.print(" RTN="); | |
Serial.print(r[rsp],HEX); | |
Serial.print(" MEM20="); | |
Serial.print(m[32],HEX); | |
Serial.print(" MEM21="); | |
Serial.print(m[33],HEX); | |
Serial.print(" MEM22="); | |
Serial.print(m[34],HEX); | |
Serial.println(); | |
} | |
// ------------------------------------------------------------------------ | |
} // End of main loop | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment