Skip to content

Instantly share code, notes, and snippets.

Created April 26, 2015 06:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/4f45827b32a1fc66db27 to your computer and use it in GitHub Desktop.
Save anonymous/4f45827b32a1fc66db27 to your computer and use it in GitHub Desktop.
Emulator for J1 Forth Processor with animated VGA visualisation
// 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