Created
April 26, 2015 06:52
-
-
Save anonymous/4f45827b32a1fc66db27 to your computer and use it in GitHub Desktop.
Emulator for J1 Forth Processor with animated VGA visualisation
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
// An animated simulator for James Bowman's J1 Forth Processr | |
// Runs on ZPUino and produces animated VGA window into memory | |
// Ken Boak April 2015 | |
// ZPUino by Alvaro Lopez | |
// Papilio Duo and Logic Start Shield by Gadget Factory | |
// J1 CPU Model - courtesy of ddb https://github.com/ddb/j1tools/blob/master/J1GUI/J1GUI/j1.c | |
//------------------------------------------------------------------------------------------------ | |
// Set up the Papilio Duo with ZPUino processor, VGA and Adafruit GFX Library | |
//Uncomment to use with a Computing Shield on Papilio DUO | |
//#define circuit Computing_Shield2 | |
//Uncomment to use with a LogicStart Shield on Papilio DUO | |
#define circuit LogicStart_Shield2 | |
#include <Adafruit_GFX.h> | |
#include <ZPUino_GFX.h> | |
#include <PLL.h> | |
// Assign human-readable names to some common 16-bit color values: | |
#define BLACK 0x0000 | |
#define BLUE 0x001F | |
#define RED 0xF800 | |
#define GREEN 0x07E0 | |
#define CYAN 0x07FF | |
#define MAGENTA 0xF81F | |
#define YELLOW 0xFFE0 | |
#define WHITE 0xFFFF | |
#define min(x,y) ((x)>(y) ? (y):(x)) | |
ZPUino_GFX gfx; | |
//------------------------------------------------------------------------------------------------ | |
// Variables used in J1 CPU model | |
unsigned short m[0x4000]; // 32kb of RAM | |
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; | |
//------------------------------------------------------------------------------------------------ | |
void setup(void) { | |
Serial.begin(115200); | |
gfx.begin( &modeline_800x600_60 ); //Highest mode supported by Papilio DUO 2MB | |
gfx.fillScreen(WHITE); | |
gfx.setTextColor(BLACK); | |
// Load up a simple program into first 40 locations of memory | |
m[0] = 0x8010; // LIT 10 | |
m[1] = 0x6C00; // Fetch | |
m[2] = 0x6000; // NOP | |
m[3] = 0x8001; // LIT 1 | |
m[4] = 0x6200; // ADD | |
m[5] = 0x8010; // LIT 10 | |
m[6] = 0x6020; // Store | |
m[7] = 0x0000; // JMP 0000 | |
m[8] = 0x0000; // | |
m[9] = 0x0000; | |
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] = 0x6E00; | |
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 1K of memory | |
for(int i=32; i<=511; i++) | |
{ | |
m[i] = 0; // clear memory | |
} | |
for(int i=0; i<=32; i++) // clear data stack and return stack | |
{ | |
d[i] = 0; | |
r[i] = 0; | |
} | |
dump_memory(0,511); // show the first 1024 words of memory in dump window | |
disassemble(0,40, 0xFFF0); // Produce the disaassembler window | |
pc = 0; | |
} | |
// End of Setup | |
//----------------------------------------------------------------------- | |
void loop(void) { | |
gfx.setCursor(0,0); | |
execute(m[pc] ); // get the next instruction and execute it using the cpu model | |
dump_memory(0,31); | |
highlight_address(pc,CYAN); | |
disassemble(pc,0, CYAN); | |
data_stack_print(0,0,GREEN); | |
return_stack_print(0,0,YELLOW); | |
display_regs(); | |
highlight_address(pc,0xFFF0); // back to pale yellow | |
disassemble(0,0x20, 0xFFF0); | |
} // end of loop | |
//------------------------------------------------------------------------------------ | |
void clear_address(int address, int colour) | |
{ | |
int row; | |
int column; | |
int page; | |
int pagestart; | |
page = address/1024; // defines the 1K page start | |
pagestart = 1024 * page; | |
row = (address -pagestart)/16; | |
column = (address - pagestart)%16; | |
row = row * 8; | |
column = (column +2) * 30; | |
Serial.print(row); | |
Serial.print(" "); | |
Serial.println(column); | |
gfx.setTextColor(BLACK,colour); | |
gfx.setCursor(column,row); | |
gfx.setTextSize(1); | |
gfx.print(" "); | |
} | |
//------------------------------------------------------------------------------------ | |
void clear_address_range(int address, int range, int colour) | |
{ | |
int row; | |
int column; | |
int page; | |
int pagestart; | |
for (int i = address; i <= address+range; i++) | |
{ | |
page = i/1024; // defines the 1K page start | |
pagestart = 1024 * page; | |
row = (i -pagestart)/16; | |
column = (i - pagestart)%16; | |
row = row * 8; | |
column = (column +2) * 30; | |
Serial.print(row); | |
Serial.print(" "); | |
Serial.println(column); | |
gfx.setTextColor(BLACK,colour); | |
gfx.setCursor(column,row); | |
gfx.setTextSize(1); | |
gfx.print(" "); | |
} | |
} | |
//------------------------------------------------------------------------------------- | |
void highlight_address(int address, int colour) | |
{ | |
int row; | |
int column; | |
int page; | |
int pagestart; | |
page = address/1024; // defines the 1K page start | |
pagestart = 1024 * page; | |
row = (address -pagestart)/16; | |
column = (address - pagestart)%16; | |
row = row * 8; | |
column = (column +2) * 30; | |
Serial.print(row); | |
Serial.print(" "); | |
Serial.println(column); | |
gfx.setTextColor(BLACK,colour); | |
gfx.setCursor(column,row); | |
gfx.setTextSize(1); | |
gfx.print(m[address],HEX); | |
} | |
//------------------------------------------------------------------------------------------------ | |
void data_stack_print(int x_pos, int y_pos, int colour) | |
{ | |
for(int k = 0; k<=31; k++) | |
{ | |
gfx.setTextColor(BLACK,WHITE); | |
gfx.setCursor(570, (y_pos + 8 *k)); | |
gfx.setTextSize(1); | |
gfx.print(k, HEX); | |
gfx.setTextColor(BLACK,colour); | |
gfx.setCursor(600, (y_pos + 8 *k)); | |
gfx.setTextSize(1); | |
if(d[k] >=0 && d[k] < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(d[k] >=16 && d[k] < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if(d[k] >=256 && d[k] < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(d[k], HEX); | |
} | |
} | |
//------------------------------------------------------------------------------------- | |
void return_stack_print(int x_pos, int y_pos, int colour) | |
{ | |
for(int k = 0; k<=31; k++) | |
{ | |
gfx.setTextColor(BLACK,colour); | |
gfx.setCursor(660, (y_pos + 8 *k)); | |
gfx.setTextSize(1); | |
if(r[k] >=0 && r[k] < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(r[k] >=16 && r[k] < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if(r[k] >=256 && r[k] < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(r[k], HEX); | |
} | |
} | |
//-------------------------------------------------------------------------------------- | |
void disassemble(int address, int range, int colour) // Produce a dissasmbler display output | |
{ | |
int mode; | |
int insn; | |
int target; | |
int literal; | |
for(int i = 0; i <=range; i++) // define the instruction range for disassembly | |
{ | |
if(m[address+i] & 0x8000) | |
{ | |
mode = 8; | |
} | |
else if (m[address+i] & 0x7000) | |
{ | |
mode = (m[address+i] & 0x7000) >>12; | |
} | |
else if (!(m[address+i] & 0xF000)) | |
{ | |
mode = 0; | |
} | |
gfx.setTextColor(BLACK,colour); | |
gfx.setTextSize(1); | |
gfx.setCursor(570, (256 + 8 *i)); | |
if (i == 0) | |
{ | |
gfx.setCursor(570, (256 + 8 *address)); | |
} | |
//-------------------------------------------------------------- | |
// Print out the Address | |
if(address+i >= 0 && address+i < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(address+i >=16 && address+i < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if(address+i >=256 && address+i < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(address+i,HEX); | |
gfx.print(" "); | |
// Serial.print(address+i,HEX); | |
// Serial.print(" "); | |
//---------------------------------------------------------- | |
//Print out the data | |
if(m[address+i] >=0 && m[address+i] < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(m[address+i] >=16 && m[address+i] < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if(m[address+i] >=256 && m[address+i] < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(m[address +i], HEX); | |
//Serial.print(m[address +i], HEX); | |
//Serial.print(" "); | |
//----------------------------------------------------------- | |
// Print out the Mode | |
Serial.println(mode); | |
gfx.print(" "); | |
gfx.print(mode); | |
gfx.print(" "); | |
//----------------------------------------------------------- | |
//Print out literals | |
if(mode == 8) | |
{ | |
gfx.print("LIT "); | |
literal = (m[address +i] & 0x7fff); | |
gfx.print(" "); | |
if(literal >=0 && literal < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(literal >=16 && literal < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if (literal >=256 && literal < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(literal, HEX); | |
} | |
//------------------------------------------------------------ | |
if(mode == 0) | |
{ | |
gfx.print("JMP "); | |
} | |
if(mode == 2) | |
{ | |
gfx.print("BRZ "); | |
} | |
if(mode == 4) | |
{ | |
gfx.print("CALL"); | |
} | |
if (mode == 0 | mode == 2 | mode == 4) | |
{ | |
target = (m[address +i] & 0x1fff); | |
gfx.print(" "); | |
if(target >=0 && target < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(target >=16 && target < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if (target >=256 && target < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(target, HEX); | |
} | |
//------------------------------------------------------------- | |
// Decode the ALU Instructions | |
if (mode == 6) | |
{ | |
insn = (m[address+i] & 0x0F00)>>8; | |
switch(insn) | |
{ | |
case 0: | |
gfx.print("NOP "); | |
break; | |
case 1: | |
gfx.print("COPY"); | |
break; | |
case 2: | |
gfx.print("ADD "); | |
break; | |
case 3: | |
gfx.print("AND "); | |
break; | |
case 4: | |
gfx.print("OR "); | |
break; | |
case 5: | |
gfx.print("XOR "); | |
break; | |
case 6: | |
gfx.print("NEG "); | |
break; | |
case 7: | |
gfx.print("= "); | |
break; | |
case 8: | |
gfx.print("< "); | |
break; | |
case 9: | |
gfx.print("SRA "); | |
break; | |
case 10: | |
gfx.print("1- "); | |
break; | |
case 11: | |
gfx.print("R@ "); | |
break; | |
case 12: | |
gfx.print("MEM "); | |
break; | |
case 13: | |
gfx.print("SLA "); | |
break; | |
case 14: | |
gfx.print("DSP "); | |
break; | |
case 15: | |
gfx.print("U< "); | |
break; | |
} | |
} | |
gfx.print(" "); | |
} | |
} | |
//------------------------------------------------------------------------------- | |
void display_regs(void) // Display the PC TOP 2ND DSP RSP INSN | |
{ | |
gfx.setTextColor(BLACK,0xFFF0); | |
gfx.setTextSize(1); | |
gfx.setCursor(60, (544)); | |
gfx.println("PC TOP 2ND DSP RSP INST"); | |
gfx.setCursor(60, (552)); | |
gfx.print(" "); // Clear the last reg_dump | |
gfx.setCursor(60, (552)); | |
gfx.print(pc,HEX); | |
gfx.setCursor(120, (552)); | |
gfx.print(_t,HEX); | |
gfx.setCursor(180, (552)); | |
gfx.print(n,HEX); | |
gfx.setCursor(240, (552)); | |
gfx.print(dsp,HEX); | |
gfx.setCursor(300, (552)); | |
gfx.print(rsp,HEX); | |
gfx.setCursor(360, (552)); | |
gfx.print(m[pc],HEX); | |
} | |
//-------------------------------------------------------------------------------------- | |
// 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); | |
} | |
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] */ | |
// clear_address(t,0xFFF0); // back to pale yellow | |
m[t] = n; | |
t = _t; | |
break; | |
} | |
} | |
pc = _pc; | |
insn = m[pc]; | |
next_t = _t; | |
} | |
// End of CPU model | |
// ------------------------------------------------------------------------ | |
void dump_memory(int start, int range) | |
{ | |
for(int i=start; i<=start+range; i=i+16) | |
{ | |
// m[i] = random(0,65535); | |
gfx.setTextColor(BLACK, (i%32)*1+0xFFF0); //Subtle yellow background on every other line | |
//----------------------------------------------------------------------- | |
// Print out the Row Address | |
if(i >=0 && i < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(i >=16 && i < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if(i >=256 && i < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(i,HEX); | |
gfx.print(" "); | |
//------------------------------------------------------------------------- | |
// Print out the data | |
for(int j=0; j<=15; j++) | |
{ | |
if(m[i+j] >=0 && m[i+j] < 16) //Pad all hex out to 4 digits | |
{ | |
gfx.print("000"); | |
} | |
else if(m[i+j] >=16 && m[i+j] < 256) | |
{ | |
gfx.print("00"); | |
} | |
else if(m[i+j] >=256 && m[i+j] < 4096) | |
{ | |
gfx.print("0"); | |
} | |
gfx.print(m[i+j],HEX); | |
gfx.print(" "); | |
} | |
// if(j=15) | |
gfx.println(); | |
} | |
} | |
//------------------------------------------------------------------- | |
// Highlight the pc in the disassembler window | |
void highlight_pc(int colour) | |
{ | |
gfx.setTextColor(BLACK,colour); | |
gfx.setTextSize(1); | |
gfx.setCursor(570, 256 + (8 *pc)); | |
gfx.print(" "); //Print a block of highlighted colour at pc location | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment