2015-06-07 10:19:33 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <avr/pgmspace.h>
|
|
|
|
|
|
|
|
#include "hd44780.h"
|
|
|
|
#include "status.h"
|
|
|
|
|
|
|
|
#define CTRL_PORT PORTB
|
|
|
|
#define CTRL_DDR DDRB
|
2015-06-07 16:58:14 +00:00
|
|
|
#define CTRL_DIN PINB
|
|
|
|
|
|
|
|
// Outputs
|
2015-06-07 10:19:33 +00:00
|
|
|
#define STEP_MASK 0x01
|
2015-06-07 10:45:34 +00:00
|
|
|
#define SINGLE_MASK 0x02
|
|
|
|
#define RESET_MASK 0x04
|
2015-06-07 16:58:14 +00:00
|
|
|
#define BRKPT_ENABLE_MASK 0x08
|
|
|
|
#define BRKPT_CLOCK_MASK 0x10
|
|
|
|
#define BRKPT_DATA_MASK 0x20
|
|
|
|
// Inputs
|
|
|
|
#define BRKPT_INTERRUPTED_MASK 0x40
|
|
|
|
#define BRKPT_ACTIVE_MASK 0x80
|
|
|
|
|
2015-06-10 10:55:39 +00:00
|
|
|
// Breakpoint Modes
|
|
|
|
#define BRKPT_INSTR 0x01
|
|
|
|
#define BRKPT_READ 0x02
|
|
|
|
#define BRKPT_WRITE 0x04
|
|
|
|
|
|
|
|
char *brkptStrings[8] = {
|
|
|
|
"No breakpoint",
|
|
|
|
"Instruction breakpoint",
|
|
|
|
"Read breakpoint",
|
|
|
|
"Instruction, read breakpoints",
|
|
|
|
"Write breakpoint",
|
|
|
|
"Instruction, write breakpoints",
|
|
|
|
"Read, write breakpoints",
|
|
|
|
"Instruction, read, write breakpoints"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-06-07 16:58:14 +00:00
|
|
|
#define CTRL_MASK (SINGLE_MASK | STEP_MASK | RESET_MASK | BRKPT_ENABLE_MASK | BRKPT_CLOCK_MASK | BRKPT_DATA_MASK)
|
2015-06-07 10:19:33 +00:00
|
|
|
|
|
|
|
#define AL_PORT PORTD
|
|
|
|
#define AL_DIN PIND
|
|
|
|
#define AL_MASK 0x00
|
|
|
|
#define AL_DDR DDRD
|
|
|
|
|
|
|
|
#define AH_PORT PORTE
|
|
|
|
#define AH_DIN PINE
|
|
|
|
#define AH_MASK 0x00
|
|
|
|
#define AH_DDR DDRE
|
|
|
|
|
2015-06-07 16:58:14 +00:00
|
|
|
#define VERSION "0.11"
|
2015-06-07 10:19:33 +00:00
|
|
|
|
2015-06-10 10:55:39 +00:00
|
|
|
#define NUMCMDS 12
|
2015-06-07 10:19:33 +00:00
|
|
|
#define MAXBKPTS 4
|
|
|
|
|
|
|
|
int numbkpts = 0;
|
|
|
|
|
|
|
|
int single;
|
|
|
|
long trace;
|
|
|
|
long instructions = 1;
|
|
|
|
|
|
|
|
unsigned int breakpoints[MAXBKPTS] = {
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2015-06-10 10:55:39 +00:00
|
|
|
unsigned int modes[MAXBKPTS] = {
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-06-07 10:19:33 +00:00
|
|
|
char *cmdStrings[NUMCMDS] = {
|
|
|
|
"help",
|
|
|
|
"reset",
|
2015-06-07 16:58:14 +00:00
|
|
|
"interrupt",
|
2015-06-07 10:19:33 +00:00
|
|
|
"address",
|
|
|
|
"step",
|
|
|
|
"trace",
|
|
|
|
"blist",
|
2015-06-10 10:55:39 +00:00
|
|
|
"breaki",
|
|
|
|
"breakr",
|
|
|
|
"breakw",
|
2015-06-07 16:58:14 +00:00
|
|
|
"bclear",
|
|
|
|
"continue",
|
2015-06-07 10:19:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define Delay_us(__us) \
|
|
|
|
if((unsigned long) (F_CPU/1000000.0 * __us) != F_CPU/1000000.0 * __us)\
|
|
|
|
__builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000000.0 * __us)+1);\
|
|
|
|
else __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000000.0 * __us))
|
|
|
|
|
|
|
|
#define Delay_ms(__ms) \
|
|
|
|
if((unsigned long) (F_CPU/1000.0 * __ms) != F_CPU/1000.0 * __ms)\
|
|
|
|
__builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000.0 * __ms)+1);\
|
|
|
|
else __builtin_avr_delay_cycles((unsigned long) ( F_CPU/1000.0 * __ms))
|
|
|
|
|
|
|
|
char message[32];
|
|
|
|
char command[32];
|
|
|
|
|
|
|
|
void readCmd(char *cmd) {
|
|
|
|
char c;
|
|
|
|
int i = 0;
|
|
|
|
log0(">> ");
|
|
|
|
while (1) {
|
|
|
|
c = Serial_RxByte0();
|
|
|
|
if (c == 8) {
|
|
|
|
// Handle backspace/delete
|
|
|
|
if (i > 0) {
|
|
|
|
i--;
|
|
|
|
Serial_TxByte0(c);
|
|
|
|
Serial_TxByte0(32);
|
|
|
|
Serial_TxByte0(c);
|
|
|
|
}
|
|
|
|
} else if (c == 13) {
|
|
|
|
// Handle return
|
|
|
|
if (i == 0) {
|
|
|
|
while (cmd[i]) {
|
|
|
|
Serial_TxByte0(cmd[i++]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cmd[i] = 0;
|
|
|
|
}
|
|
|
|
Serial_TxByte0(10);
|
|
|
|
Serial_TxByte0(13);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
// Handle any other character
|
|
|
|
Serial_TxByte0(c);
|
|
|
|
cmd[i] = c;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setSingle(int i) {
|
|
|
|
single = i;
|
|
|
|
if (single) {
|
|
|
|
CTRL_PORT |= SINGLE_MASK;
|
|
|
|
} else {
|
|
|
|
CTRL_PORT &= ~SINGLE_MASK;
|
|
|
|
}
|
2015-06-07 16:58:14 +00:00
|
|
|
Delay_us(10);
|
2015-06-07 10:19:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void setTrace(long i) {
|
|
|
|
trace = i;
|
|
|
|
if (trace) {
|
2015-06-07 16:58:14 +00:00
|
|
|
log0("Tracing every %ld instructions while single stepping\n", trace);
|
2015-06-07 10:19:33 +00:00
|
|
|
} else {
|
|
|
|
log0("Tracing disabled\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-07 16:58:14 +00:00
|
|
|
void version() {
|
|
|
|
log0("Atom Bus Monitor version %s\n", VERSION);
|
|
|
|
log0("Compiled at %s on %s\n",__TIME__,__DATE__);
|
|
|
|
}
|
2015-06-07 10:19:33 +00:00
|
|
|
|
2015-06-10 10:55:39 +00:00
|
|
|
|
2015-06-07 10:19:33 +00:00
|
|
|
/*******************************************
|
|
|
|
* Commands
|
|
|
|
*******************************************/
|
|
|
|
|
|
|
|
void doCmdHelp(char *params) {
|
|
|
|
int i;
|
2015-06-07 16:58:14 +00:00
|
|
|
version();
|
2015-06-07 10:19:33 +00:00
|
|
|
log0("Commands:\n");
|
|
|
|
for (i = 0; i < NUMCMDS; i++) {
|
|
|
|
log0(" %s\n", cmdStrings[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-10 11:09:56 +00:00
|
|
|
|
2015-06-07 10:19:33 +00:00
|
|
|
void doCmdAddr() {
|
2015-06-10 11:09:56 +00:00
|
|
|
int i, nibble;
|
2015-06-07 10:19:33 +00:00
|
|
|
unsigned int addr = AH_DIN << 8 | AL_DIN;
|
2015-06-10 11:09:56 +00:00
|
|
|
// Update the serial port
|
|
|
|
log0("%04X\n", addr);
|
|
|
|
// Update the LCD display
|
2015-06-07 10:19:33 +00:00
|
|
|
lcd_goto(6);
|
2015-06-10 11:09:56 +00:00
|
|
|
// Avoid using sprintf, as it adds quite a lot of code
|
|
|
|
for (i = 3; i >= 0; i--) {
|
|
|
|
nibble = addr >> (i * 4);
|
|
|
|
nibble &= 0x0F;
|
|
|
|
nibble += '0';
|
|
|
|
if (nibble > '9') {
|
|
|
|
nibble += 'A' - '9' - 1;
|
|
|
|
}
|
|
|
|
lcd_putc(nibble);
|
|
|
|
}
|
2015-06-07 10:19:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void doCmdStep(char *params) {
|
|
|
|
long i;
|
|
|
|
long j;
|
|
|
|
|
|
|
|
if (!single) {
|
|
|
|
log0("Use the break command to stop the 6502\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sscanf(params, "%ld", &instructions);
|
|
|
|
if (instructions <= 0) {
|
|
|
|
log0("Number of instuctions must be positive\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
log0("Stepping %ld instructions\n", instructions);
|
|
|
|
|
|
|
|
j = trace;
|
|
|
|
for (i = 1; i <= instructions; i++) {
|
|
|
|
// Step the 6502
|
|
|
|
CTRL_PORT &= ~STEP_MASK;
|
2015-06-07 16:58:14 +00:00
|
|
|
Delay_us(2);
|
2015-06-07 10:19:33 +00:00
|
|
|
CTRL_PORT |= STEP_MASK;
|
2015-06-07 16:58:14 +00:00
|
|
|
Delay_us(2);
|
2015-06-07 10:19:33 +00:00
|
|
|
if (i == instructions || (trace && (--j == 0))) {
|
2015-06-07 16:58:14 +00:00
|
|
|
Delay_us(10);
|
2015-06-07 10:19:33 +00:00
|
|
|
doCmdAddr();
|
|
|
|
j = trace;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void doCmdReset(char *params) {
|
2015-06-07 10:45:34 +00:00
|
|
|
log0("Resetting 6502\n");
|
|
|
|
CTRL_PORT |= RESET_MASK;
|
|
|
|
Delay_us(100);
|
|
|
|
CTRL_PORT &= ~RESET_MASK;
|
2015-06-07 10:19:33 +00:00
|
|
|
}
|
|
|
|
|
2015-06-07 16:58:14 +00:00
|
|
|
void doCmdInterrupt(char *params) {
|
|
|
|
setSingle(1);
|
2015-06-07 10:19:33 +00:00
|
|
|
doCmdAddr();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void doCmdTrace(char *params) {
|
|
|
|
long i;
|
|
|
|
sscanf(params, "%ld", &i);
|
|
|
|
setTrace(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void doCmdBList(char *params) {
|
|
|
|
int i;
|
|
|
|
if (numbkpts) {
|
|
|
|
for (i = 0; i < numbkpts; i++) {
|
2015-06-10 10:55:39 +00:00
|
|
|
log0("%d: %04X %s\n", i, breakpoints[i], brkptStrings[modes[i]]);
|
2015-06-07 10:19:33 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log0("No breakpoints set\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-10 10:55:39 +00:00
|
|
|
void setBreakpoint(int i, int addr, int mode) {
|
|
|
|
log0("%s set at %04X\n", brkptStrings[mode], addr);
|
|
|
|
breakpoints[i] = addr;
|
|
|
|
modes[i] = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void doCmdBreak(char *params, unsigned int mode) {
|
2015-06-07 10:19:33 +00:00
|
|
|
int i;
|
|
|
|
unsigned int addr;
|
|
|
|
sscanf(params, "%x", &addr);
|
2015-06-07 16:58:14 +00:00
|
|
|
for (i = 0; i < numbkpts; i++) {
|
|
|
|
if (breakpoints[i] == addr) {
|
2015-06-10 10:55:39 +00:00
|
|
|
if (modes[i] & mode) {
|
|
|
|
log0("%s already set at %04X\n", brkptStrings[mode], addr);
|
|
|
|
} else {
|
|
|
|
setBreakpoint(i, addr, modes[i] | mode);
|
|
|
|
}
|
2015-06-07 16:58:14 +00:00
|
|
|
doCmdBList(NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2015-06-10 10:55:39 +00:00
|
|
|
if (numbkpts == MAXBKPTS) {
|
|
|
|
log0("All breakpoints are already set\n");
|
|
|
|
doCmdBList(NULL);
|
|
|
|
return;
|
|
|
|
}
|
2015-06-07 10:19:33 +00:00
|
|
|
numbkpts++;
|
|
|
|
for (i = numbkpts - 2; i >= -1; i--) {
|
|
|
|
if (i == -1 || breakpoints[i] < addr) {
|
2015-06-10 10:55:39 +00:00
|
|
|
setBreakpoint(i + 1, addr, mode);
|
2015-06-07 10:19:33 +00:00
|
|
|
doCmdBList(NULL);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
breakpoints[i + 1] = breakpoints[i];
|
2015-06-10 10:55:39 +00:00
|
|
|
modes[i + 1] = modes[i];
|
2015-06-07 10:19:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-10 10:55:39 +00:00
|
|
|
void doCmdBreakI(char *params) {
|
|
|
|
doCmdBreak(params, BRKPT_INSTR);
|
|
|
|
}
|
|
|
|
|
|
|
|
void doCmdBreakR(char *params) {
|
|
|
|
doCmdBreak(params, BRKPT_READ);
|
|
|
|
}
|
|
|
|
|
|
|
|
void doCmdBreakW(char *params) {
|
|
|
|
doCmdBreak(params, BRKPT_WRITE);
|
|
|
|
}
|
|
|
|
|
2015-06-07 10:19:33 +00:00
|
|
|
void doCmdBClear(char *params) {
|
|
|
|
int i;
|
2015-06-07 16:58:14 +00:00
|
|
|
int n = 0;
|
2015-06-07 10:19:33 +00:00
|
|
|
sscanf(params, "%d", &n);
|
|
|
|
if (n >= numbkpts) {
|
|
|
|
log0("Breakpoint %d not set\n", n);
|
|
|
|
} else {
|
2015-06-10 11:09:56 +00:00
|
|
|
log0("Removing %s at %04X\n", brkptStrings[modes[n]], breakpoints[n]);
|
2015-06-07 10:19:33 +00:00
|
|
|
for (i = n; i < numbkpts; i++) {
|
|
|
|
breakpoints[i] = breakpoints[i + 1];
|
2015-06-10 10:55:39 +00:00
|
|
|
modes[i] = modes[i + 1];
|
2015-06-07 10:19:33 +00:00
|
|
|
}
|
|
|
|
numbkpts--;
|
|
|
|
}
|
|
|
|
doCmdBList(NULL);
|
|
|
|
}
|
|
|
|
|
2015-06-10 10:55:39 +00:00
|
|
|
void shiftBreakpointRegister(unsigned int addr, unsigned int mode) {
|
2015-06-07 16:58:14 +00:00
|
|
|
int i;
|
2015-06-10 10:55:39 +00:00
|
|
|
long reg = mode;
|
|
|
|
reg <<= 16;
|
|
|
|
reg |= addr;
|
|
|
|
for (i = 0; i < 20; i++) {
|
2015-06-07 16:58:14 +00:00
|
|
|
CTRL_PORT &= ~BRKPT_CLOCK_MASK;
|
2015-06-10 10:55:39 +00:00
|
|
|
if (reg & 1) {
|
2015-06-07 16:58:14 +00:00
|
|
|
CTRL_PORT |= BRKPT_DATA_MASK;
|
|
|
|
} else {
|
|
|
|
CTRL_PORT &= ~BRKPT_DATA_MASK;
|
|
|
|
}
|
|
|
|
Delay_us(10);
|
|
|
|
CTRL_PORT |= BRKPT_CLOCK_MASK;
|
|
|
|
Delay_us(10);
|
2015-06-10 10:55:39 +00:00
|
|
|
reg >>= 1;
|
2015-06-07 16:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void doCmdContinue(char *params) {
|
|
|
|
int i;
|
|
|
|
int status;
|
|
|
|
doCmdBList(NULL);
|
|
|
|
|
|
|
|
// Disable breakpoints to allow loading
|
|
|
|
CTRL_PORT &= ~BRKPT_ENABLE_MASK;
|
|
|
|
|
|
|
|
// Load breakpoints into comparators
|
2015-06-10 10:55:39 +00:00
|
|
|
for (i = 0; i < numbkpts; i++) {
|
|
|
|
shiftBreakpointRegister(breakpoints[i], modes[i]);
|
|
|
|
}
|
|
|
|
for (i = numbkpts; i < MAXBKPTS; i++) {
|
|
|
|
shiftBreakpointRegister(0, 0);
|
2015-06-07 16:58:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enable breakpoints
|
|
|
|
CTRL_PORT |= BRKPT_ENABLE_MASK;
|
|
|
|
|
|
|
|
// Disable single stepping
|
|
|
|
setSingle(0);
|
|
|
|
|
|
|
|
// Wait for breakpoint to become active
|
|
|
|
log0("6502 free running...\n");
|
|
|
|
do {
|
|
|
|
status = CTRL_DIN;
|
|
|
|
} while (!(status & BRKPT_ACTIVE_MASK) && !(status && BRKPT_INTERRUPTED_MASK));
|
|
|
|
|
|
|
|
// Output cause
|
|
|
|
if (status & BRKPT_ACTIVE_MASK) {
|
|
|
|
log0("Breakpoint hit at ");
|
|
|
|
} else {
|
|
|
|
log0("Interrupted at ");
|
|
|
|
}
|
|
|
|
doCmdAddr();
|
|
|
|
|
|
|
|
// Enable single stepping
|
|
|
|
setSingle(1);
|
|
|
|
|
|
|
|
// Disable breakpoints
|
|
|
|
CTRL_PORT &= ~BRKPT_ENABLE_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-07 10:19:33 +00:00
|
|
|
void initialize() {
|
|
|
|
CTRL_DDR = CTRL_MASK;
|
|
|
|
AL_DDR = AL_MASK;
|
|
|
|
AH_DDR = AH_MASK;
|
2015-06-07 16:58:14 +00:00
|
|
|
CTRL_PORT &= 0;
|
2015-06-07 10:19:33 +00:00
|
|
|
Serial_Init(57600,57600);
|
|
|
|
lcd_init();
|
|
|
|
lcd_puts("Addr: xxxx");
|
2015-06-07 16:58:14 +00:00
|
|
|
version();
|
|
|
|
setSingle(1);
|
|
|
|
setTrace(1);
|
|
|
|
log0("6502 paused...\n");
|
|
|
|
doCmdAddr();
|
2015-06-07 10:19:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void (*cmdFuncs[NUMCMDS])(char *params) = {
|
|
|
|
doCmdHelp,
|
|
|
|
doCmdReset,
|
2015-06-07 16:58:14 +00:00
|
|
|
doCmdInterrupt,
|
2015-06-07 10:19:33 +00:00
|
|
|
doCmdAddr,
|
|
|
|
doCmdStep,
|
|
|
|
doCmdTrace,
|
|
|
|
doCmdBList,
|
2015-06-10 10:55:39 +00:00
|
|
|
doCmdBreakI,
|
|
|
|
doCmdBreakR,
|
|
|
|
doCmdBreakW,
|
2015-06-07 16:58:14 +00:00
|
|
|
doCmdBClear,
|
|
|
|
doCmdContinue
|
2015-06-07 10:19:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void dispatchCmd(char *cmd) {
|
|
|
|
int i;
|
|
|
|
char *cmdString;
|
|
|
|
|
|
|
|
|
|
|
|
int minLen;
|
|
|
|
int cmdStringLen;
|
|
|
|
|
|
|
|
int cmdLen = 0;
|
|
|
|
while (cmd[cmdLen] >= 'a' && cmd[cmdLen] <= 'z') {
|
|
|
|
cmdLen++;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < NUMCMDS; i++) {
|
|
|
|
cmdString = cmdStrings[i];
|
|
|
|
cmdStringLen = strlen(cmdString);
|
|
|
|
minLen = cmdLen < cmdStringLen ? cmdLen : cmdStringLen;
|
|
|
|
if (strncmp(cmdString, cmd, minLen) == 0) {
|
|
|
|
(*cmdFuncs[i])(command + cmdLen);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log0("Unknown command %s\n", cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(void) {
|
|
|
|
initialize();
|
|
|
|
while (1) {
|
|
|
|
readCmd(command);
|
|
|
|
dispatchCmd(command);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|