px4-firmware/misc/pascal/insn16/prun/pdbg.c

749 lines
21 KiB
C

/**********************************************************************
* pdbg.c
* P-Code Debugger
*
* Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
**********************************************************************/
/**********************************************************************
* Included Files
**********************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
#include "keywords.h"
#include "pdefs.h"
#include "podefs.h"
#include "pinsn16.h"
#include "pxdefs.h"
#include "pedefs.h"
#include "paslib.h"
#include "pinsn.h"
#include "pexec.h"
#include "pdbg.h"
/**********************************************************************
* Pre-processor Definitions
**********************************************************************/
#define TRACE_ARRAY_SIZE 16
#define MAX_BREAK_POINTS 8
#define DISPLAY_STACK_SIZE 16
#define DISPLAY_INST_SIZE 16
/**********************************************************************
* Private Type Definitions
**********************************************************************/
enum command_e
{
eCMD_NONE = 0,
eCMD_RESET,
eCMD_RUN,
eCMD_STEP,
eCMD_NEXT,
eCMD_GO,
eCMD_BS,
eCMD_BC,
eCMD_DP,
eCMD_DT,
eCMD_DS,
eCMD_DI,
eCMD_DB,
eCMD_HELP,
eCMD_QUIT
};
struct trace_s
{
paddr_t pc;
paddr_t sp;
ustack_t tos;
};
typedef struct trace_s trace_t;
/**********************************************************************
* Private Constant Data
**********************************************************************/
/**********************************************************************
* Private Data
**********************************************************************/
static enum command_e g_lastcmd = eCMD_NONE;
static uint32_t g_lastvalue;
/**********************************************************************
* Private Function Prototypes
**********************************************************************/
static void pdbg_showcommands(void);
static void pdbg_execcommand(struct pexec_s *st, enum command_e cmd, uint32_t value);
static int32_t pdbg_readdecimal(char *ptr);
static int32_t pdbg_readhex(char *ptr, int32_t defaultvalue);
static void pdbg_programstatus(struct pexec_s *st);
static paddr_t pdbg_printpcode(struct pexec_s *st, paddr_t pc, int16_t nitems);
static paddr_t pdbg_printstack(struct pexec_s *st, paddr_t sp, int16_t nitems);
static void pdbg_printregisters(struct pexec_s *st);
static void pdbg_printtracearray(struct pexec_s *st);
static void pdbg_addbreakpoint(paddr_t pc);
static void pdbg_deletebreakpoint(int16_t bpno);
static void pdbg_printbreakpoints(struct pexec_s *st);
static void pdbg_checkbreakpoint(struct pexec_s *st);
static void pdbg_initdebugger(void);
static void pdbg_debugpcode(struct pexec_s *st);
/**********************************************************************
* Global Variables
**********************************************************************/
/**********************************************************************
* Private Variables
**********************************************************************/
/* Debugging variables */
static trace_t g_tracearray[TRACE_ARRAY_SIZE];
/* Holds execution histor */
static uint16_t g_tracendx;
/* This is the index into the circular g_tracearray */
static uint16_t g_ntracepoints;
/* This is the number of valid enties in g_tracearray */
static paddr_t g_breakpoint[MAX_BREAK_POINTS];
/* Contains address associated with all active */
/* break points. */
static paddr_t g_untilpoint;
/* The 'g_untilpoint' is a temporary breakpoint */
static uint16_t g_nbreakpoints;
/* Number of items in breakPoints[] */
static paddr_t g_displayloc;
/* P-code display location display */
static bool g_bstopexecution;
/* true means to stop program execution */
/* I/O variables */
static char g_inline[LINE_SIZE+1];
/* Command line buffer */
/**********************************************************************
* Public Functions
**********************************************************************/
void dbg_run(struct pexec_s *st)
{
paddr_t pc;
int i;
pdbg_showcommands();
pdbg_initdebugger();
pdbg_programstatus(st);
while (true)
{
printf("CMD: ");
(void) fgets(g_inline, LINE_SIZE, stdin);
switch (toupper(g_inline[0]))
{
case 'R' :
switch (toupper(g_inline[1])) {
case 'E' : /* Reset */
pdbg_execcommand(st, eCMD_RESET, 0);
break;
case 'U' : /* Run */
pdbg_execcommand(st, eCMD_RUN, 0);
break;
default :
printf("Unrecognized Command\n");
pdbg_execcommand(st, eCMD_HELP, 0);
break;
} /* end switch */
break;
case 'S' : /* Single Step (into) */
pdbg_execcommand(st, eCMD_STEP, 0);
break;
case 'N' : /* Single Step (over) */
pdbg_execcommand(st, eCMD_NEXT, 0);
break;
case 'G' : /* Go */
pdbg_execcommand(st, eCMD_GO, 0);
break;
case 'B' :
switch (toupper(g_inline[1])) {
case 'S' : /* Set Breakpoint */
pc = pdbg_readhex(&g_inline[2], st->pc);
pdbg_execcommand(st, eCMD_BS, pc);
break;
case 'C' : /* Clear Breakpoint */
i = pdbg_readdecimal(&g_inline[2]);
pdbg_execcommand(st, eCMD_BC, i);
break;
default :
printf("Unrecognized Command\n");
pdbg_execcommand(st, eCMD_HELP, 0);
break;
} /* end switch */
break;
case 'D' :
switch (toupper(g_inline[1])) {
case 'P' : /* Display Program Status */
pdbg_execcommand(st, eCMD_DP, 0);
break;
case 'T' : /* Display Program Trace */
pdbg_execcommand(st, eCMD_DT, 0);
break;
case 'S' : /* Display Stack */
pc = pdbg_readhex(&g_inline[2], st->sp);
pdbg_execcommand(st, eCMD_DS, pc);
break;
case 'I' : /* Display Instructions */
pc = pdbg_readhex(&g_inline[2], st->pc);
pdbg_execcommand(st, eCMD_DI, pc);
break;
case 'B' : /* Display Breakpoints */
pdbg_execcommand(st, eCMD_DB, pc);
break;
default :
printf("Unrecognized Command\n");
pdbg_execcommand(st, eCMD_HELP, 0);
break;
} /* end switch */
break;
case 'Q' : /* Quit */
pdbg_execcommand(st, eCMD_QUIT, pc);
break;
case 'H' : /* Help */
case '?' :
pdbg_execcommand(st, eCMD_HELP, 0);
break;
case '\0' : /* Repeat last command */
case '\n' : /* Repeat last command */
pdbg_execcommand(st, g_lastcmd, g_lastvalue);
break;
default :
printf("Unrecognized Command\n");
pdbg_execcommand(st, eCMD_HELP, 0);
break;
} /* end switch */
} /* end while */
} /* end pdbg_debugpcodeProgram */
/**********************************************************************
* Private Functions
**********************************************************************/
/* Show command characters */
static void pdbg_showcommands(void)
{
printf("Commands:\n");
printf(" RE[set] - Reset\n");
printf(" RU[n] - Run\n");
printf(" S[tep] - Single Step (Into)\n");
printf(" N[ext] - Single Step (Over)\n");
printf(" G[o] - Go\n");
printf(" BS xxxx - Set Breakpoint\n");
printf(" BC n - Clear Breakpoint\n");
printf(" DP - Display Program Status\n");
printf(" DT - Display Program Trace\n");
printf(" DS [xxxx] - Display Stack\n");
printf(" DI [xxxx] - Display Instructions\n");
printf(" DB - Display Breakpoints\n");
printf(" H or ? - Shows this list\n");
printf(" Q[uit] - Quit\n");
} /* end pdbg_showcommands */
/***********************************************************************/
static void pdbg_execcommand(struct pexec_s *st, enum command_e cmd, uint32_t value)
{
/* Save the command to resuse if the user enters nothing */
g_lastcmd = cmd;
g_lastvalue = value;
switch (cmd)
{
case eCMD_NONE: /* Do nothing */
break;
case eCMD_RESET: /* Reset */
pexec_reset(st);
pdbg_initdebugger();
pdbg_programstatus(st);
g_lastcmd = eCMD_NONE;
break;
case eCMD_RUN: /* Run */
pexec_reset(st);
pdbg_initdebugger();
pdbg_debugpcode(st);
pdbg_programstatus(st);
break;
case eCMD_STEP: /* Single Step (into)*/
g_bstopexecution = true;
pdbg_debugpcode(st);
pdbg_programstatus(st);
break;
case eCMD_NEXT: /* Single Step (over) */
if (st->ispace[st->pc] == oPCAL)
{
g_bstopexecution = false;
g_untilpoint = st->pc + 4;
}
else
{
g_bstopexecution = true;
}
pdbg_debugpcode(st);
g_untilpoint = 0;
pdbg_programstatus(st);
break;
case eCMD_GO: /* Go */
g_bstopexecution = false;
pdbg_debugpcode(st);
pdbg_programstatus(st);
break;
case eCMD_BS: /* Set Breakpoint */
if (g_nbreakpoints >= MAX_BREAK_POINTS)
{
printf("Too many breakpoints\n");
g_lastcmd = eCMD_NONE;
}
else if (value >= st->maxpc)
{
printf("Invalid address for breakpoint\n");
g_lastcmd = eCMD_NONE;
}
else
{
pdbg_addbreakpoint(value);
pdbg_printbreakpoints(st);
} /* end else */
break;
case eCMD_BC: /* Clear Breakpoint */
if ((value >= 1) && (value <= g_nbreakpoints))
{
pdbg_deletebreakpoint(value);
}
else
{
printf("Invalid breakpoint number\n");
g_lastcmd = eCMD_NONE;
}
pdbg_printbreakpoints(st);
break;
case eCMD_DP: /* Display Program Status */
pdbg_programstatus(st);
break;
case eCMD_DT: /* Display Program Trace */
pdbg_printtracearray(st);
break;
case eCMD_DS: /* Display Stack */
if (value > st->sp)
{
printf("Invalid stack address\n");
g_lastcmd = eCMD_NONE;
}
else
{
g_lastvalue = pdbg_printstack(st, value, DISPLAY_STACK_SIZE);
} /* end else */
break;
case eCMD_DI: /* Display Instructions */
if (value >= st->maxpc)
{
printf("Invalid instruction address\n");
g_lastcmd = eCMD_NONE;
}
else
{
g_lastvalue = pdbg_printpcode(st, value, DISPLAY_INST_SIZE);
} /* end else */
break;
case eCMD_DB: /* Display Breakpoints */
pdbg_printbreakpoints(st);
break;
case eCMD_QUIT: /* Quit */
printf("Goodbye\n");
exit(0);
break;
case eCMD_HELP: /* Help */
default: /* Internal error */
pdbg_showcommands();
g_lastcmd = eCMD_NONE;
break;
} /* end switch */
} /* end pdbg_execcommand */
/***********************************************************************/
/* Read a decimal value from the input string */
static int32_t pdbg_readdecimal(char *ptr)
{
int32_t decimal = 0;
while (!isspace(*ptr)) ptr++;
while (isspace(*ptr)) ptr++;
for (; ((*ptr >= '0') && (*ptr <= '9')); ptr++)
decimal = 10*decimal + (int32_t)*ptr - (int32_t)'0';
return decimal;
} /* end pdbg_readdecimal */
/***********************************************************************/
/* Read a hexadecimal value from the input string */
static int32_t pdbg_readhex(char *ptr, int32_t defaultvalue)
{
char c;
int32_t hex = 0;
bool found = false;
while (!isspace(*ptr)) ptr++;
while (isspace(*ptr)) ptr++;
while (true) {
c = toupper(*ptr);
if ((c >= '0') && (c <= '9')) {
hex = ((hex << 4) | ((int32_t)c - (int32_t)'0'));
found = true;
} /* end if */
else if ((c >= 'A') && (c <= 'F')) {
hex = ((hex << 4) | ((int32_t)c - (int32_t)'A' + 10));
found = true;
} /* end else if */
else {
if (found)
return hex;
else
return defaultvalue;
} /* end else */
ptr++;
} /* end while */
} /* end pdbg_readhex */
/***********************************************************************/
/* Print the disassembled P-Code at PC */
static void pdbg_programstatus(struct pexec_s *st)
{
(void)pdbg_printpcode(st, st->pc, 1);
(void)pdbg_printstack(st, st->sp, 2);
pdbg_printregisters(st);
} /* end pdbg_programstatus */
/***********************************************************************/
/* Print the disassembled P-Code at PC */
static paddr_t pdbg_printpcode(struct pexec_s *st, paddr_t pc, int16_t nitems)
{
OPTYPE op;
paddr_t opsize;
uint8_t *address;
for (; ((pc < st->maxpc) && (nitems > 0)); nitems--)
{
address = &st->ispace[pc];
op.op = *address++;
op.arg1 = 0;
op.arg2 = 0;
opsize = 1;
printf("PC:%04x %02x", pc, op.op);
if ((op.op & o8) != 0)
{
op.arg1 = *address++;
printf("%02x", op.arg1);
opsize++;
} /* end if */
else
printf("..");
if ((op.op & o16) != 0)
{
op.arg2 = ((*address++) << 8);
op.arg2 |= *address++;
printf("%04x", op.arg2);
opsize += 2;
} /* end if */
else
printf("....");
/* The disassemble it to stdout */
printf(" ");
insn_DisassemblePCode(stdout, &op);
/* Get the address of the next P-Code */
pc += opsize;
} /* end for */
return pc;
} /* end pdbg_printpcode */
/***********************************************************************/
/* Print the stack value at SP */
static paddr_t pdbg_printstack(struct pexec_s *st, paddr_t sp, int16_t nitems)
{
int32_t isp;
if ((st->sp < st->stacksize) && (sp <= st->sp))
{
isp = BTOISTACK(sp);
printf("SP:%04x %04x\n", sp, st->dstack.i[isp]);
for (isp--, sp -= BPERI, nitems--;
((isp >= 0) && (nitems > 0));
isp--, sp -= BPERI, nitems--)
printf(" %04x %04x\n", sp, st->dstack.i[isp] & 0xffff);
} /* end if */
else
{
printf("SP:%04x BAD\n", sp);
} /* end else */
return sp;
} /* end pdbg_printstack */
/***********************************************************************/
/* Print the base register */
static void pdbg_printregisters(struct pexec_s *st)
{
if (st->fp <= st->sp)
printf("FP:%04x ", st->fp);
printf("CSP:%04x\n", st->csp);
} /* end pdbg_printregisters */
/***********************************************************************/
/* Print the g_tracearray */
static void pdbg_printtracearray(struct pexec_s *st)
{
int nprinted;
int index;
index = g_tracendx + TRACE_ARRAY_SIZE - g_ntracepoints;
if (index >= TRACE_ARRAY_SIZE)
index -= TRACE_ARRAY_SIZE;
for (nprinted = 0; nprinted < g_ntracepoints; nprinted++) {
printf("SP:%04x %04x ",
g_tracearray[ index ].sp, g_tracearray[ index ].tos);
/* Print the instruction executed at this traced address */
(void)pdbg_printpcode(st, g_tracearray[ index ].pc, 1);
/* Index to the next trace entry */
if (++index >= TRACE_ARRAY_SIZE)
index = 0;
} /* end for */
} /* end pdbg_printtracearray */
/***********************************************************************/
/* Add a breakpoint to the breakpoint array */
static void pdbg_addbreakpoint(paddr_t pc)
{
int i;
/* Is there room for another breakpoint? */
if (g_nbreakpoints < MAX_BREAK_POINTS)
{
/* Yes..Check if the breakpoint already exists */
for (i = 0; i < g_nbreakpoints; i++)
{
if (g_breakpoint[i] == pc)
{
/* It is already set. Return without doing anything */
return;
}
}
/* The breakpoint is not already set -- set it */
g_breakpoint[g_nbreakpoints++] = pc;
} /* end if */
} /* end pdbg_addbreakpoint */
/***********************************************************************/
/* Remove a breakpoint from the breakpoint array */
static void pdbg_deletebreakpoint(int16_t bpno)
{
if ((bpno >= 1) && (bpno <= g_nbreakpoints)) {
for (; (bpno < g_nbreakpoints); bpno++)
g_breakpoint[bpno-1] = g_breakpoint[bpno];
g_nbreakpoints--;
} /* end if */
} /* end pdbg_deletebreakpoint */
/***********************************************************************/
/* Print the breakpoint array */
static void pdbg_printbreakpoints(struct pexec_s *st)
{
int i;
printf("BP:# Address P-Code\n");
for (i = 0; i < g_nbreakpoints; i++)
{
printf("BP:%d ", (i+1));
(void)pdbg_printpcode(st, g_breakpoint[i], 1);
} /* end for */
} /* end pdbg_printbreakpoints */
/***********************************************************************/
/* Check if a breakpoint is set at the current value of program counter.
* If so, print the instruction and stop execution. */
static void pdbg_checkbreakpoint(struct pexec_s *st)
{
uint16_t bpIndex;
/* Check for a user breakpoint */
for (bpIndex = 0;
((bpIndex < g_nbreakpoints) && (!g_bstopexecution));
bpIndex++)
{
if (g_breakpoint[bpIndex] == st->pc)
{
printf("Breakpoint #%d -- Execution Stopped\n", (bpIndex+1));
g_bstopexecution = true;
return;
} /* end if */
} /* end for */
} /* end pdbg_checkbreakpoint */
/***********************************************************************/
/* Initialize Debugger variables */
static void pdbg_initdebugger(void)
{
g_bstopexecution = false;
g_displayloc = 0;
g_tracendx = 0;
g_ntracepoints = 0;
}
/***********************************************************************/
/* This function executes the P-Code program until a stopping condition
* is encountered. */
static void pdbg_debugpcode(struct pexec_s *st)
{
uint16_t errno;
do {
/* Trace the next instruction execution */
g_tracearray[g_tracendx].pc = st->pc;
g_tracearray[g_tracendx].sp = st->sp;
if (st->sp < st->stacksize)
g_tracearray[g_tracendx].tos = st->dstack.i[BTOISTACK(st->sp)];
else
g_tracearray[g_tracendx].tos = 0;
if (++g_tracendx >= TRACE_ARRAY_SIZE)
g_tracendx = 0;
if (g_ntracepoints < TRACE_ARRAY_SIZE)
g_ntracepoints++;
/* Execute the instruction */
errno = pexec(st);
/* Check for exceptional stopping conditions */
if (errno != eNOERROR)
{
if (errno == eEXIT)
printf("Normal Termination\n");
else
printf("Runtime error 0x%02x -- Execution Stopped\n", errno);
g_bstopexecution = true;
} /* end if */
/* Check for normal stopping conditions */
if (!g_bstopexecution)
{
/* Check for attempt to execute code outside of legal range */
if (st->pc >= st->maxpc)
g_bstopexecution = true;
/* Check for a temporary breakpoint */
else if ((g_untilpoint > 0) && (g_untilpoint == st->pc))
g_bstopexecution = true;
/* Check if there is a breakpoint at the next instruction */
else if (g_nbreakpoints > 0)
pdbg_checkbreakpoint(st);
}
} while (!g_bstopexecution);
} /* end pdbg_debugpcode */