mirror of
https://github.com/ksherlock/mpw.git
synced 2024-11-22 00:32:44 +00:00
Debugger Shell
This commit is contained in:
parent
a436e91373
commit
dccdd7cb26
@ -12,3 +12,4 @@ add_subdirectory(toolbox)
|
||||
add_subdirectory(mplite)
|
||||
add_subdirectory(mpw)
|
||||
add_subdirectory(macos)
|
||||
add_subdirectory(debugger)
|
23
debugger/CMakeLists.txt
Normal file
23
debugger/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
|
||||
set(DEBUGGER_SRC lexer.cpp parser.c)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT lexer.cpp
|
||||
COMMAND re2c -b -i -o lexer.cpp "${CMAKE_CURRENT_SOURCE_DIR}/lexer.re.cpp"
|
||||
MAIN_DEPENDENCY lexer.re.cpp
|
||||
DEPENDS commands.h parser.h
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT parser.cpp parser.h
|
||||
COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/parser.lemon" "parser.lemon"
|
||||
COMMAND lemon parser.lemon
|
||||
COMMAND cp parser.h "${CMAKE_CURRENT_SOURCE_DIR}/"
|
||||
COMMAND cp parser.out "${CMAKE_CURRENT_SOURCE_DIR}/"
|
||||
MAIN_DEPENDENCY parser.lemon
|
||||
DEPENDS commands.h
|
||||
)
|
||||
|
||||
add_library(DEBUGGER_LIB ${DEBUGGER_SRC})
|
25
debugger/commands.h
Normal file
25
debugger/commands.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef __debugger_commands__
|
||||
#define __debugger_commands__
|
||||
|
||||
enum {
|
||||
Print,
|
||||
Dump,
|
||||
List,
|
||||
Break,
|
||||
TBreak,
|
||||
Continue,
|
||||
Step,
|
||||
SetARegister,
|
||||
SetDRegister,
|
||||
SetXRegister,
|
||||
};
|
||||
|
||||
struct Command {
|
||||
bool valid;
|
||||
int action;
|
||||
uint32_t argc;
|
||||
uint32_t argv[10];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
324
debugger/lexer.re.cpp
Normal file
324
debugger/lexer.re.cpp
Normal file
@ -0,0 +1,324 @@
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "commands.h"
|
||||
#include "parser.h"
|
||||
|
||||
// re2c -b -i
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
void *ParseAlloc(void *(*mallocProc)(size_t));
|
||||
void ParseFree(void *p, void (*freeProc)(void*));
|
||||
void Parse(void *yyp, int yymajor, uint32_t yyminor, Command *command);
|
||||
|
||||
}
|
||||
|
||||
// p / print expression
|
||||
// hd / hexdump expression [:expression]
|
||||
// stack ?
|
||||
// brk expression
|
||||
// tbrk expression
|
||||
namespace {
|
||||
int tox(char c)
|
||||
{
|
||||
c |= 0x20; // lowercase it.
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t scan10(const char *begin, const char *end)
|
||||
{
|
||||
return std::accumulate(begin, end, 0,
|
||||
[](char c, uint32_t value){
|
||||
return value * 10 + c - '0';
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t scan16(const char *begin, const char *end)
|
||||
{
|
||||
return std::accumulate(begin, end, 0,
|
||||
[](char c, uint32_t value){
|
||||
return (value << 4) + tox(c);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* unordered_set of breakpoints?
|
||||
* bloom filter via std::bitset<16 * 1024> ?
|
||||
*/
|
||||
|
||||
class AddressFilter {
|
||||
std::bitset<4096> pageSet;
|
||||
std::unordered_set<uint32_t> addSet;
|
||||
|
||||
bool test(uint32_t address) const
|
||||
{
|
||||
if (address > 0xffffff) return false;
|
||||
if (!pageSet[address >> 12]) return false;
|
||||
|
||||
return addSet.find(address) != addSet.end();
|
||||
}
|
||||
|
||||
void add(uint32_t address)
|
||||
{
|
||||
if (address > 0xffffff) return;
|
||||
|
||||
pageSet[address >> 12] = true;
|
||||
addSet.insert(address);
|
||||
}
|
||||
|
||||
void remove(uint32_t address)
|
||||
{
|
||||
if (address > 0xffffff) return;
|
||||
|
||||
auto iter = addSet.find(address);
|
||||
if (iter != addSet.end())
|
||||
{
|
||||
addSet.remove();
|
||||
// need to re-scan all addresses to update the pageSet...
|
||||
uint32_t page = address >> 12;
|
||||
pageSet[page] = false;
|
||||
for (auto x : addSet)
|
||||
{
|
||||
if ((x >> 12) == page)
|
||||
{
|
||||
addSet[page] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
bool ParseLine(const char *iter, Command *command)
|
||||
{
|
||||
void *parser;
|
||||
|
||||
parser = ParseAlloc(malloc);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const char *begin = iter;
|
||||
const char *marker;
|
||||
|
||||
/*!re2c
|
||||
re2c:define:YYCURSOR = iter;
|
||||
re2c:define:YYMARKER = marker;
|
||||
re2c:define:YYCTYPE = char;
|
||||
re2c:yyfill:enable = 0;
|
||||
|
||||
[ \t\f\r\n]+ {
|
||||
// white space
|
||||
continue;
|
||||
}
|
||||
|
||||
'>=' { Parse(&parser, tkGTEQ, 0, command); continue; }
|
||||
'>>' { Parse(&parser, tkGTGT, 0, command); continue; }
|
||||
'<=' { Parse(&parser, tkLTEQ, 0, command); continue; }
|
||||
'<<' { Parse(&parser, tkLTLT, 0, command); continue; }
|
||||
'!=' { Parse(&parser, tkBANGEQ, 0, command); continue; }
|
||||
'==' { Parse(&parser, tkEQEQ, 0, command); continue; }
|
||||
'||' { Parse(&parser, tkPIPEPIPE, 0, command); continue; }
|
||||
'&&' { Parse(&parser, tkAMPAMP, 0, command); continue; }
|
||||
|
||||
|
||||
'(' { Parse(&parser, tkLPAREN, 0, command); continue; }
|
||||
')' { Parse(&parser, tkRPAREN, 0, command); continue; }
|
||||
'=' { Parse(&parser, tkEQ, 0, command); continue; }
|
||||
'+' { Parse(&parser, tkPLUS, 0, command); continue; }
|
||||
'-' { Parse(&parser, tkMINUS, 0, command); continue; }
|
||||
'*' { Parse(&parser, tkSTAR, 0, command); continue; }
|
||||
'/' { Parse(&parser, tkSLASH, 0, command); continue; }
|
||||
'%' { Parse(&parser, tkPERCENT, 0, command); continue; }
|
||||
'~' { Parse(&parser, tkTILDE, 0, command); continue; }
|
||||
'!' { Parse(&parser, tkBANG, 0, command); continue; }
|
||||
'^' { Parse(&parser, tkCARET, 0, command); continue; }
|
||||
'&' { Parse(&parser, tkAMP, 0, command); continue; }
|
||||
'|' { Parse(&parser, tkPIPE, 0, command); continue; }
|
||||
'<' { Parse(&parser, tkLT, 0, command); continue; }
|
||||
'>' { Parse(&parser, tkGT, 0, command); continue; }
|
||||
|
||||
[0-9]+ {
|
||||
// integer
|
||||
uint32_t data;
|
||||
|
||||
data = std::accumulate(begin, iter, 0,
|
||||
[](char c, uint32_t value){
|
||||
return value * 10 + c - '0';
|
||||
});
|
||||
|
||||
Parse(&parser, tkINTEGER, data, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'$' [0-9A-Fa-f]+ {
|
||||
// hex number
|
||||
uint32_t data;
|
||||
|
||||
data = std::accumulate(begin + 1, iter, 0,
|
||||
[](char c, uint32_t value){
|
||||
return (value << 4) + tox(c);
|
||||
}
|
||||
);
|
||||
Parse(&parser, tkINTEGER, data, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'0x' [0-9A-Fa-f]+ {
|
||||
// hex number
|
||||
uint32_t data;
|
||||
|
||||
data = std::accumulate(begin + 2, iter, 0,
|
||||
[](char c, uint32_t value){
|
||||
return (value << 4) + tox(c);
|
||||
}
|
||||
);
|
||||
Parse(&parser, tkINTEGER, data, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
['] [^']{1,4} ['] {
|
||||
// 4 cc code
|
||||
uint32_t data;
|
||||
|
||||
data = std::accumulate(begin + 1, iter - 1, 0,
|
||||
[](char c, uint32_t value)
|
||||
{
|
||||
return (value << 8) + (unsigned)c;
|
||||
}
|
||||
);
|
||||
Parse(&parser, tkINTEGER, data, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
'd' [0-7] {
|
||||
// data register
|
||||
uint32_t data = begin[1] - '0';
|
||||
Parse(&parser, tkDREGISTER, data, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'a' [0-7] {
|
||||
// address register
|
||||
uint32_t data = begin[1] - '0';
|
||||
Parse(&parser, tkAREGISTER, data, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'pc' {
|
||||
// program counter...
|
||||
Parse(&parser, tkXREGISTER, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'csr' {
|
||||
// condition status register.
|
||||
Parse(&parser, tkXREGISTER, 1, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'sp' {
|
||||
// stack pointer aka a7
|
||||
Parse(&parser, tkAREGISTER, 7, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'fp' {
|
||||
// frame pointer aka a6
|
||||
Parse(&parser, tkAREGISTER, 6, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
'c' | 'continue' {
|
||||
Parse(&parser, tkCONTINUE, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'hd' | 'dump' {
|
||||
Parse(&parser, tkDUMP, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'h' | 'help' {
|
||||
Parse(&parser, tkHELP, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'n' | 'next' {
|
||||
Parse(&parser, tkSTEP, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
's' | 'step' {
|
||||
Parse(&parser, tkSTEP, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'b' | 'brk' | 'break' {
|
||||
Parse(&parser, tkBREAK, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'g' | 'go' {
|
||||
Parse(&parser, tkGO, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
'r' | 'run' {
|
||||
Parse(&parser, tkRUN, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
';l' | ';list' {
|
||||
Parse(&parser, tkSEMIL, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
';h' | ';hd' | ';hexdump' {
|
||||
Parse(&parser, tkSEMIH, 0, command);
|
||||
continue;
|
||||
}
|
||||
|
||||
[_A-Za-z][_A-Za-z0-9] + {
|
||||
// identifier. lookup global address, tool number, etc.
|
||||
fprintf(stderr, "illegal identifier: %*.s\n", (int)(iter - begin), begin);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
[\x00] {
|
||||
// eol.
|
||||
Parse(&parser, tkEOL, 0, command);
|
||||
break;
|
||||
}
|
||||
|
||||
[^] {
|
||||
fprintf(stderr, "illegal character: `%c`\n", *begin);
|
||||
return false;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
Parse(&parser, 0, 0, command);
|
||||
ParseFree(&parser, free);
|
||||
|
||||
return command->valid;
|
||||
}
|
163
debugger/parser.lemon
Normal file
163
debugger/parser.lemon
Normal file
@ -0,0 +1,163 @@
|
||||
|
||||
%extra_argument { struct Command *command }
|
||||
%token_prefix tk
|
||||
|
||||
%token_type {uint32_t}
|
||||
|
||||
|
||||
%parse_failure {
|
||||
fprintf(stderr,"I don't understand.\n");
|
||||
command->valid = false;
|
||||
}
|
||||
|
||||
%parse_accept {
|
||||
command->valid = true;
|
||||
}
|
||||
|
||||
|
||||
%left PIPEPIPE.
|
||||
%left AMPAMP.
|
||||
%left PIPE.
|
||||
%left CARET.
|
||||
%left AMP.
|
||||
%left EQEQ BANGEQ.
|
||||
%left LT LTEQ GT GTEQ.
|
||||
%left LTLT GTGT.
|
||||
%left PLUS MINUS.
|
||||
%left STAR SLASH PERCENT.
|
||||
%right BANG TILDE.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
stmt ::= assignment EOL.
|
||||
stmt ::= expr(a) EOL.
|
||||
{
|
||||
// print the value.
|
||||
// hex, base 10, signed 16-bit (if appropriate)
|
||||
printf("$%08x - %u", a, a);
|
||||
|
||||
if (a & 0x80000000)
|
||||
printf(" %d", (int32_t)a);
|
||||
|
||||
if ((a & 0xffff8000) == 0x8000)
|
||||
printf(" %h", (int16_t)a);
|
||||
|
||||
printf("\n");
|
||||
|
||||
command->action = PRINT;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
stmt ::= BREAK expr(a) EOL.
|
||||
{
|
||||
command->action = tkBREAK;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
stmt ::= NEXT EOL.
|
||||
{
|
||||
command->action = tkNEXT;
|
||||
command->arg = 1;
|
||||
}
|
||||
|
||||
stmt ::= NEXT expr(a) EOL.
|
||||
{
|
||||
command->action = tkNEXT;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
stmt ::= PRINT expr(a) EOL.
|
||||
{
|
||||
command->action = tkPRINT;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
stmt ::= DUMP expr(a) EOL .
|
||||
{
|
||||
command->action = tkDUMP;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
stmt ::= LIST expr(a) EOL .
|
||||
{
|
||||
command->action = tkLIST;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
stmt ::= expr(a) SEMIL EOL.
|
||||
{
|
||||
command->action = tkLIST;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
stmt ::= expr(a) SEMIH EOL.
|
||||
{
|
||||
command->action = tkDUMP;
|
||||
command->arg = a;
|
||||
}
|
||||
|
||||
|
||||
assignment ::= DREGISTER(a) EQ expr(b). { cpuSetDReg(a, b); }
|
||||
assignment ::= AREGISTER(a) EQ expr(b). { cpuSetAReg(a, b); }
|
||||
assignment ::= XREGISTER(a) EQ expr(b).
|
||||
{
|
||||
switch(a)
|
||||
{
|
||||
case 0:
|
||||
cpuSetPC(b);
|
||||
break;
|
||||
case 1:
|
||||
cpuSetSR(b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
expr(rhs) ::= unary(a). { rhs = a; }
|
||||
expr(rhs) ::= term(a) PLUS term(b). { rhs = a + b; }
|
||||
expr(rhs) ::= term(a) MINUS term(b). { rhs = a + b; }
|
||||
expr(rhs) ::= term(a) STAR term(b). { rhs = a * b; }
|
||||
expr(rhs) ::= term(a) SLASH term(b). { rhs = a / b; }
|
||||
expr(rhs) ::= term(a) PERCENT term(b). { rhs = a % b; }
|
||||
expr(rhs) ::= term(a) LTLT term(b). { rhs = a << b; }
|
||||
expr(rhs) ::= term(a) GTGT term(b). { rhs = a >> b; }
|
||||
expr(rhs) ::= term(a) LT term(b). { rhs = a < b; }
|
||||
expr(rhs) ::= term(a) LTEQ term(b). { rhs = a <= b; }
|
||||
expr(rhs) ::= term(a) GT term(b). { rhs = a > b; }
|
||||
expr(rhs) ::= term(a) GTEQ term(b). { rhs = a >= b; }
|
||||
expr(rhs) ::= term(a) EQEQ term(b). { rhs = a == b; }
|
||||
expr(rhs) ::= term(a) BANGEQ term(b). { rhs = a != b; }
|
||||
expr(rhs) ::= term(a) AMP term(b). { rhs = a & b; }
|
||||
expr(rhs) ::= term(a) CARET term(b). { rhs = a ^ b; }
|
||||
expr(rhs) ::= term(a) PIPE term(b). { rhs = a | b; }
|
||||
expr(rhs) ::= term(a) AMPAMP term(b). { rhs = a && b; }
|
||||
expr(rhs) ::= term(a) PIPEPIPE term(b). { rhs = a || b; }
|
||||
|
||||
|
||||
unary(rhs) ::= term(a). { rhs = a; }
|
||||
unary(rhs) ::= PLUS unary(a). [BANG] { rhs = a; }
|
||||
unary(rhs) ::= MINUS unary(a). [BANG] { rhs = -a; }
|
||||
unary(rhs) ::= TILDE unary(a). { rhs = ~a; }
|
||||
unary(rhs) ::= BANG unary(a). { rhs = !a; }
|
||||
unary(rhs) ::= STAR unary(a). [BANG] { rhs = cpuMemoryReadLong(a); }
|
||||
|
||||
|
||||
term(rhs) ::= LPAREN expr(a) RPAREN. { rhs = a; }
|
||||
term(rhs) ::= INTEGER(a). { rhs = a; }
|
||||
term(rhs) ::= DREGISTER(a). { rhs = cpuGetDReg(a); }
|
||||
term(rhs) ::= AREGISTER(a). { rhs = cpuGetAReg(a); }
|
||||
term(rhs) ::= XREGISTER(a).
|
||||
{
|
||||
switch(a)
|
||||
{
|
||||
case 0:
|
||||
rhs = cpuGetPC();
|
||||
break;
|
||||
case 1:
|
||||
rhs = cpuGetSR();
|
||||
break;
|
||||
default:
|
||||
rhs = 0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user