Serial Debugger for Arduino KENBAK

36 views
Skip to first unread message

Rolf Strand

unread,
May 26, 2021, 4:49:23 AM5/26/21
to uKenbak-1

Here is the start of a serial port debugger.  It is currently added to MCP.cpp

A call to HandleConsole is added to the bottom of the MCP::Loop function

The are working:

r                     : display registers
o                    : octal dump of all mem
d [N] [addr]   :  disassemble, d, d N, d N addr
m addr data :  Modify data
g                     : go, auto stepping, enter other key to stop
s [N]               : step instruction";
p N                 : load predefined";
c                     : clear memory";
a addr            : set address to insert data
N                     : write data to insert addess and advance

enter a blank line to repeat last command

You will need to add these definitions to MCP.h

  void MCP::HandleConsole();
  void MCP::ProcessCmd(char * cmd);
  char * MCP::GetOPC(int addr);
  void MCP::WriteProgramMemoryToSerial(bool fromCmd);
  void MCP::PrintOct(byte b);
  int  Disassemble(int addr);
  void MCP::PrintBin(byte b);
  void MCP::DoStep();
  void MCP::PrintOC(byte b);
  char *MCP::GetFstr(char *buffer, char *pP);

Add to MCP.cpp

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;
      }
    }
  }
}

Vicente González

unread,
May 26, 2021, 8:24:35 AM5/26/21
to Rolf Strand, uKenbak-1
Thanks Rolf for sharing, I also followed that path last year for the #KENBAKRuler :-)

--
You received this message because you are subscribed to the Google Groups "uKenbak-1" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ukenbak-1+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ukenbak-1/f78d4287-6e16-4cbe-9e3d-66581f207a85n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages