int MCP::Disassemble(int addr)
{
byte Instruction = CPU::cpu->Read(addr);
byte operand = CPU::cpu->Read((addr+1) & 0xFF);
int n = 1;
// decode Instruction, processes the next byte if required
byte P__ = (Instruction >> 6) & 0x3;
byte _Q_ = (Instruction >> 3) & 0x7;
byte __R = (Instruction >> 0) & 0x7;
if (__R == 0) // ==================== Miscellaneous (one byte only)
{
if (P__ == 0 || P__ == 1) // HALT
Serial.print(F("HALT "));
else // NOOP
{
if (_Q_ == 0)
Serial.print(F("NOOP "));
else if (_Q_ == 6)
Serial.print(F("NOOP_EXT "));
}
}
else if (__R == 1) // ==================== Shifts, Rotates (one byte only)
{
byte Places = _Q_ & 0x03;
byte Rotate = P__ & 0x01;
byte Left = P__ & 0x02;
n = 2;
if (Places == 0)
Places = 4;
if (Left) // left
{
if (Rotate) // or-in the bit that rolled off
{
Serial.print(F("SFTL ")); Serial.print(Places);
}
else
{
Serial.print(F("ROTL ")); Serial.print(Places);
}
}
else // right
{
if (Rotate) // or-in the bit that rolled off
{
Serial.print(F("SFTR ")); Serial.print(Places);
}
else
{
Serial.print(F("ROTR ")); Serial.print(Places);
}
}
}
else if (__R == 2) // ==================== Bit Test and Manipulation
{
byte One = P__ & 0x01;
n = 2;
if (P__ & 0x02) // SKIP
{
Serial.print(F("SKP "));
}
else // SET
{
Serial.print(F("SET "));
}
Serial.print(_Q_);
if (One)
Serial.print(F(",1,"));
else
Serial.print(F(",0,"));
Serial.print(addr);
}
else if (_Q_ > 3) // ==================== jumps
{
byte JumpAndMark = _Q_ & 0x02;
n = 2;
if (JumpAndMark)
{
Serial.print(F("JMK "));
}
else
{
Serial.print(F("JMP "));
}
switch(P__)
{
case 0:
Serial.print(F("A,"));
break;
case 1:
Serial.print(F("B,"));
break;
case 2:
Serial.print(F("X,"));
break;
}
if (P__ == 3)
Serial.print(F(""));
else
{
switch (__R)
{
case 3:
Serial.print(F("NE,"));
break;
case 4:
Serial.print(F("EQ,"));
break;
case 5:
Serial.print(F("LT,"));
break;
case 6:
Serial.print(F("GE,"));
break;
case 7:
Serial.print(F("GT,"));
break;
}
}
if ( (_Q_ == 4) || (_Q_ == 6) )
{
Serial.print(operand);
}
else
{
Serial.print(F("("));Serial.print(operand);Serial.print(F(")"));
}
}
else if (P__ == 3) // ==================== Or, And, Lneg, Noop
{
n = 2;
if (_Q_ == 1) // (NOOP)
{
Serial.print(F("NOOP "));
}
else
{
if (_Q_ == 0) // OR
Serial.print(F("OR "));
else if (_Q_ == 2) // AND
Serial.print(F("AND "));
else if (_Q_ == 3) // LNEG
Serial.print(F("LNEG "));
switch(__R)
{
case 3:
Serial.print(F("#"));Serial.print(operand);
break;
case 4:
Serial.print(operand);
break;
case 5:
Serial.print(F("("));Serial.print(operand);Serial.print(F(")"));
break;
case 6:
Serial.print(operand);Serial.print(F(",X"));
break;
case 7:
Serial.print(F("("));Serial.print(operand);Serial.print(F("),X"));
break;
}
}
}
else // ==================== Add, Sub, Load, Store
{
n = 2;
if (_Q_ == 0) // ADD
Serial.print(F("ADD "));
else if (_Q_ == 1) // SUB
Serial.print(F("SUB "));
else if (_Q_ == 2) // LOAD
Serial.print(F("LOAD "));
else if (_Q_ == 3) // STORE
Serial.print(F("STORE "));
switch(P__)
{
case 0:
Serial.print(F("A,"));
break;
case 1:
Serial.print(F("B,"));
break;
case 2:
Serial.print(F("X,"));
break;
}
switch(__R)
{
case 3:
Serial.print(F("#"));Serial.print(operand);
break;
case 4:
Serial.print(operand);
break;
case 5:
Serial.print(F("("));Serial.print(operand);Serial.print(F(")"));
break;
case 6:
Serial.print(operand);Serial.print(F(",X"));
break;
case 7:
Serial.print(F("("));Serial.print(operand);Serial.print(F("),X"));
break;
}
}
return n;
}
const char cmd1[] PROGMEM = "r"; //dump registers
const char cmd2[] PROGMEM = "o"; //octal dump
const char cmd3[] PROGMEM = "d"; //disassemble
const char cmd4[] PROGMEM = "m"; //modify
const char cmd5[] PROGMEM = "g"; //go, auto stepping, send any string to stop
const char cmd6[] PROGMEM = "s"; //step
const char cmd7[] PROGMEM = "p"; //predefined + n
const char cmd8[] PROGMEM = "e"; //eeprom + n
const char cmd9[] PROGMEM = "w"; //eeprom + n
const char cmd10[] PROGMEM = "c"; //clear
const char cmd11[] PROGMEM = "a"; //set address write pointer, now lines of octal write
const char *const cmds[] PROGMEM = {
cmd1, cmd2, cmd3, cmd4, cmd5, cmd6, cmd7, cmd8, cmd9, cmd10, cmd11,
NULL
};
const char help1[] PROGMEM = "r registers";
const char help2[] PROGMEM = "o octal dump";
const char help3[] PROGMEM = "d [N] [aaa] disassemble";
const char help4[] PROGMEM = "m aaa ddd";
const char help5[] PROGMEM = "g go, auto stepping";
const char help6[] PROGMEM = "s N step";
const char help7[] PROGMEM = "p N load predefined";
const char help8[] PROGMEM = "e N load eeprom";
const char help9[] PROGMEM = "w N write eeprom";
const char help10[] PROGMEM = "c clear memory";
const char help11[] PROGMEM = "a aaa set address";
const char *const help[] PROGMEM=
{
help1, help2, help3, help4, help5, help6, help7, help8,
help9, help10, help11, NULL
};
char * MCP::GetFstr(char *buffer, char *pP)
{
if(pP == NULL) return NULL;
strcpy_P(buffer, pP);
return buffer;
}
enum {
CMD_REGISTERS,
CLD_DUMP_OCT,
CMD_DISASSEMBLE,
CMD_MODIFY,
CMD_GO,
CMD_STEP,
CMD_PREDEF,
CMD_READ_EE,
CMD_WRITE_EE,
CMD_CLEAR,
CMD_SET_ADDR,
};
char strOPC[5];
void MCP::PrintOct(byte b)
{
Serial.print('0');
Serial.print((b >> 6) & 0x07, OCT);
Serial.print((b >> 3) & 0x07, OCT);
Serial.print((b >> 0) & 0x07, OCT);
}
void MCP::PrintOC(byte b)
{
Serial.print(' ');
Serial.print((b >> 1) & 0x01, BIN);
Serial.print((b >> 0) & 0x01, BIN);
Serial.print(' ');
}
void MCP::PrintBin(byte b)
{
Serial.print('b');
Serial.print((b >> 7) & 0x01, BIN);
Serial.print((b >> 6) & 0x01, BIN);
Serial.print((b >> 5) & 0x01, BIN);
Serial.print((b >> 4) & 0x01, BIN);
Serial.print((b >> 3) & 0x01, BIN);
Serial.print((b >> 2) & 0x01, BIN);
Serial.print((b >> 1) & 0x01, BIN);
Serial.print((b >> 0) & 0x01, BIN);
}
int insertAddr = 4;
int disassAddr = 0;
void MCP::DoStep()
{
CPU::cpu->Step();
leds.Display(m_Data, m_Control);
byte A = CPU::cpu->Read(REG_A_IDX);
byte B = CPU::cpu->Read(REG_B_IDX);
byte X = CPU::cpu->Read(REG_X_IDX);
byte P = CPU::cpu->Read(REG_P_IDX);
byte Output = CPU::cpu->Read(REG_OUTPUT_IDX);
byte OCA = CPU::cpu->Read(REG_FLAGS_A_IDX);
byte OCB = CPU::cpu->Read(REG_FLAGS_B_IDX);
byte OCX = CPU::cpu->Read(REG_FLAGS_X_IDX);
byte Input = CPU::cpu->Read(REG_INPUT_IDX);
PrintOct(A); PrintOC(OCA); PrintOct(B); PrintOC(OCB); PrintOct(X); PrintOC(OCX); Serial.println(' ');
PrintBin(Output); Serial.print(' '); PrintBin(Input); Serial.print(' ');
Serial.print(F("P: ")); PrintOct(P); Serial.print(' '); Serial.print(P);
Serial.print(F(" opc: ")); PrintOct(P); Serial.print(' ');
Disassemble(P); Serial.println();
}
void MCP::ProcessCmd(char * cmd)
{
int i = 0;
char *pCmd = NULL;
word State;
word Pressed;
char buf[16];
char lCmd[128];
strcpy(lCmd, cmd);
strtok(lCmd, " ");
char * p1 = strtok(NULL, " ");
char * p2 = strtok(NULL, " ");
pCmd = GetFstr(buf, (char*)pgm_read_word(&(cmds[i])));
while(pCmd != NULL)
{
if (pCmd[0] == cmd[0])
{
break;
}
++i;
pCmd = GetFstr(buf, (char*)pgm_read_word(&(cmds[i])));
}
if (isDigit(cmd[0]))
{
if( (cmd[0] == '0') && (strlen(cmd) > 1))
{
//octal
}
else
{
int data = atoi(cmd) & 0xFF;
CPU::cpu->Write(insertAddr, data);
PrintOct(insertAddr);
Serial.print(F(" "));
PrintOct(data);
Serial.println(F(""));
insertAddr = (insertAddr + 1) & 0xFF;
}
}
else if (pCmd != NULL)
{
byte A = CPU::cpu->Read(REG_A_IDX);
byte B = CPU::cpu->Read(REG_B_IDX);
byte X = CPU::cpu->Read(REG_X_IDX);
byte P = CPU::cpu->Read(REG_P_IDX);
byte Output = CPU::cpu->Read(REG_OUTPUT_IDX);
byte OCA = CPU::cpu->Read(REG_FLAGS_A_IDX);
byte OCB = CPU::cpu->Read(REG_FLAGS_B_IDX);
byte OCX = CPU::cpu->Read(REG_FLAGS_X_IDX);
byte Input = CPU::cpu->Read(REG_INPUT_IDX);
int n;
switch (i)
{
case CMD_REGISTERS:
{
Serial.print(F("A: ")); PrintOct(A); Serial.print(' '); Serial.print(A);
Serial.print(F(" C:")); Serial.print(OCA >> 1); Serial.print(" O:"); Serial.println(OCA & 1);
Serial.print(F("B: ")); PrintOct(B); Serial.print(' '); Serial.print(B);
Serial.print(F(" C:")); Serial.print(OCB >> 1); Serial.print(" O:"); Serial.println(OCB & 1);
Serial.print(F("X: ")); PrintOct(X); Serial.print(' '); Serial.print(X);
Serial.print(F(" C:")); Serial.print(OCX >> 1); Serial.print(" O:"); Serial.println(OCX & 1);
Serial.print(F("P: ")); PrintOct(P); Serial.print(' '); Serial.print(P);
Serial.print(F(" opc: ")); PrintOct(P); Serial.print(' '); Disassemble(P); Serial.println();
Serial.print(F("O: ")); PrintOct(Output); Serial.print(' '); Serial.println(Output);
Serial.print(F("I: ")); PrintOct(Input); Serial.print(' '); Serial.println(Input);
break;
}
case CLD_DUMP_OCT:
{
// should add parameters to choose format
SerializeMemory(false, 3); //Maintain 38K
break;
}
case CMD_DISASSEMBLE:
{
// Need to all ranges d N addr, d N
int len = 256;
int start = disassAddr;
int addr;
if (p1 != NULL) len = atoi(p1);
if (p2 != NULL) start = atoi(p2);
for (addr = start; addr < start + len;)
{
PrintOct(addr & 0xFF);
int opc = CPU::cpu->Read(addr & 0xFF);
Serial.print(": ");
n = 1;
switch (addr & 0xFF)
{
case REG_A_IDX:
PrintOct(opc);
Serial.println(F(" REG_A"));
break;
case REG_B_IDX:
PrintOct(opc);
Serial.println(F(" REG_B"));
break;
case REG_X_IDX:
PrintOct(opc);
Serial.println(F(" REG_X"));
break;
case REG_P_IDX:
PrintOct(opc);
Serial.println(F(" REG_P"));
break;
case REG_OUTPUT_IDX:
PrintOct(opc);
Serial.println(F(" REG_OUTPUT"));
break;
case REG_FLAGS_A_IDX:
PrintOct(opc);
Serial.println(F(" REG_FLAGS_A"));
break;
case REG_FLAGS_B_IDX:
PrintOct(opc);
Serial.println(F(" REG_FLAGS_B"));
break;
case REG_FLAGS_X_IDX:
PrintOct(opc);
Serial.println(F(" REG_FLAGS_X"));
break;
case REG_INPUT_IDX:
PrintOct(opc);
Serial.println(F(" REG_INPUT"));
break;
default:
PrintOct(addr & 0xFF);
Serial.print(F(" "));
n = Disassemble(addr & 0xFF);
Serial.println();
}
addr += n;
}
disassAddr = addr & 0xFF;
break;
}
case CMD_MODIFY:
{
int addr = 0;
int data = 0;
if ( (p1 != NULL) && (p2 != NULL) )
{
addr = atoi(p1) & 0xFF;
data = atoi(p2) & 0xFF;
CPU::cpu->Write(addr, data);
}
break;
}
case CMD_GO:
{
while (Serial.available() == 0)
{
DoStep();
}
while (Serial.available() != 0)
{
Serial.read();
}
break;
}
case CMD_STEP:
{
// TODO add N for number of steps
int steps = 1;
if (p1 != NULL) steps = atoi(p1);
for(int i = 0; i < steps; ++i)
DoStep();
break;
}
case CMD_PREDEF:
{
int index = 0;
if (p1 != NULL) index = atoi(p1);
if ( (p1 == NULL) || (index > 6) )
{
Serial.println(F("Load Predefined Usage: p N"));
break;
}
Serial.print(F("Loading predefined program "));
Serial.println(index);
memory.LoadStandardProgram(index);
break;
}
case CMD_READ_EE:
{
break;
}
case CMD_WRITE_EE:
{
break;
}
case CMD_CLEAR:
{
byte* pMem = CPU::cpu->Memory();
for(int addr = 0; addr < 256; ++addr)
{
pMem[addr] = 0;
}
pMem[REG_P_IDX] = 4;
Serial.println(F("Memmory cleared, PC = 004"));
Serial.println(F("Address pointer 004"));
insertAddr = 4;
break;
}
case CMD_SET_ADDR:
{
if (p1 != NULL)
{
insertAddr = atoi(p1) & 0xFF;
}
Serial.print(F("Insert at "));
Serial.println(insertAddr);
break;
}
default:
{
Serial.println(F("???"));
}
}
}
}
bool consoleInit = false;
void MCP::HandleConsole()
{
static int len = 0;
static bool chkRepeat = false;
static char cmd[128];
if (consoleInit == false)
{
consoleInit = true;
int i = 0;
Serial.println(F("Console commands:\r\n"));
char *pHelp = GetFstr(cmd, (char*)pgm_read_word(&(help[i])));
while (pHelp != NULL)
{
Serial.println(pHelp);
++i;
pHelp = GetFstr(cmd, (char*)pgm_read_word(&(help[i])));
}
Serial.println();
}
while (Serial.available() > 0)
{
int ch = Serial.read();
if (len < sizeof(cmd)-2)
{
switch (ch)
{
case '\r':
break;
case '\n':
ProcessCmd((char *)cmd);
chkRepeat = true;
break;
default:
if (chkRepeat == true)
{
len = 0;
chkRepeat = false;
}
cmd[len] = ch;
++len;
cmd[len] = 0;
break;
}
}
}
}