Initial commit
This commit is contained in:
commit
c58c8faac3
7 changed files with 1540 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
build/
|
53
Makefile
Normal file
53
Makefile
Normal file
|
@ -0,0 +1,53 @@
|
|||
CFLAGS?=-O3 --opt-code-speed
|
||||
PLATFORM?=ti8x
|
||||
|
||||
CC=$(shell which zcc z88dk.zcc | head -1)
|
||||
LD=$(CC)
|
||||
SHELL=bash
|
||||
|
||||
BUILD=build
|
||||
|
||||
wilder_card=$(wildcard $(1)/**/$(2)) $(wildcard $(1)/$(2))
|
||||
define source_directory
|
||||
$(eval $(1)=$(2))
|
||||
$(eval $(1)_FILES=$(call wilder_card,$(2),*.s) $(call wilder_card,$(2),*.c))
|
||||
$(eval $(1)_OBJECT_FILES=$$(subst $(2)/,$$(BUILD)/$(2)/,$$(patsubst %.s,%.o,$$(patsubst %.c,%.o,$$($(1)_FILES)))))
|
||||
$(eval vpath %.c $(2))
|
||||
$(eval vpath %.s $(2))
|
||||
endef
|
||||
|
||||
$(call source_directory,SRC,src)
|
||||
|
||||
# It's extremely important that the source file paths are absolute, otherwise the
|
||||
# extension will have trouble mapping the paths.
|
||||
define source_compile
|
||||
mkdir -p "$(dir $@)"
|
||||
$(CC) +$(PLATFORM) $(CFLAGS) $(1) -o "$@" "$(realpath $<)"
|
||||
endef
|
||||
|
||||
.ONESHELL:
|
||||
|
||||
all: $(BUILD)/gdb.lib
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
$(BUILD):
|
||||
@mkdir -p "$@"
|
||||
|
||||
$(BUILD)/gdb.lib: $(BUILD)/gdb.lst $(SRC_OBJECT_FILES)
|
||||
cd $(BUILD) && z88dk.z88dk-z80asm -d -xgdb "@$(subst $(BUILD)/,,$<)"
|
||||
|
||||
$(BUILD)/gdb.lst: | $(BUILD)
|
||||
echo > "$@"
|
||||
for each in $(SRC_FILES) ; do
|
||||
echo $${each%.*} >> "$@"
|
||||
done
|
||||
|
||||
.PRECIOUS: $(BUILD)/%.o
|
||||
$(BUILD)/%.o: %.s | $(BUILD)
|
||||
$(call source_compile,-c)
|
||||
|
||||
.PRECIOUS: $(BUILD)/%.o
|
||||
$(BUILD)/%.o: %.c | $(BUILD)
|
||||
$(call source_compile,-c)
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# z88dk-gdbstub
|
||||
|
||||
A GDB stub library for z88dk z80 projects. You must implement the following
|
||||
functions in your program for it to compile:
|
||||
|
||||
* unsigned char gdb_getDebugChar (void)
|
||||
* void gdb_putDebugChar (unsigned char ch)
|
||||
|
||||
Other functions will also be needed to do anything useful.
|
||||
|
||||
See [The template project for TI 8x calculators](https://github.com/empathicqubit/z88dk-ti8xp-template) for an example implementation.
|
142
src/gdb.c
Normal file
142
src/gdb.c
Normal file
|
@ -0,0 +1,142 @@
|
|||
#include "lib.h"
|
||||
#include "gdb.h"
|
||||
|
||||
// All publicly exported functions go here
|
||||
|
||||
void gdb_exception (int ex) __naked {
|
||||
__asm
|
||||
ld (__gdb_state+R_SP), sp
|
||||
LOAD_SP
|
||||
call __gdb_save_cpu_state
|
||||
ld hl, 0
|
||||
push hl
|
||||
#ifdef __SDCC_gbz80
|
||||
ld hl, __gdb_state + R_SP
|
||||
ld a, (hl+)
|
||||
ld h, (hl)
|
||||
ld l, a
|
||||
#else
|
||||
ld hl, (__gdb_state + R_SP)
|
||||
#endif
|
||||
inc hl
|
||||
inc hl
|
||||
ld e, (hl)
|
||||
inc hl
|
||||
ld d, (hl)
|
||||
push de
|
||||
call __gdb_stub_main
|
||||
__endasm;
|
||||
(void)ex;
|
||||
}
|
||||
|
||||
#ifdef DBG_HWBREAK
|
||||
#ifndef DBG_HWBREAK_SIZE
|
||||
#define DBG_HWBREAK_SIZE 0
|
||||
#endif /* DBG_HWBREAK_SIZE */
|
||||
void
|
||||
gdb_hwbreak (void) __naked
|
||||
{
|
||||
__asm
|
||||
ld (__gdb_state + R_SP), sp
|
||||
LOAD_SP
|
||||
call __gdb_save_cpu_state
|
||||
ld hl, -DBG_HWBREAK_SIZE
|
||||
push hl
|
||||
ld hl, EX_HWBREAK
|
||||
push hl
|
||||
call __gdb_stub_main
|
||||
__endasm;
|
||||
}
|
||||
#endif /* DBG_HWBREAK_SET */
|
||||
|
||||
void gdb_int(void) __naked {
|
||||
__asm
|
||||
ld (__gdb_state + R_SP), sp
|
||||
LOAD_SP
|
||||
call __gdb_save_cpu_state
|
||||
ld hl, 0 ;pc_adj
|
||||
push hl
|
||||
ld hl, DBG_INT_EX
|
||||
push hl
|
||||
ld hl, __gdb_stub_main
|
||||
push hl
|
||||
push hl
|
||||
ei
|
||||
reti
|
||||
__endasm;
|
||||
}
|
||||
|
||||
#ifndef __SDCC_gbz80
|
||||
void gdb_nmi(void) __naked {
|
||||
__asm
|
||||
ld (__gdb_state + R_SP), sp
|
||||
LOAD_SP
|
||||
call __gdb_save_cpu_state
|
||||
ld hl, 0 ;pc_adj
|
||||
push hl
|
||||
ld hl, DBG_NMI_EX
|
||||
push hl
|
||||
ld hl, __gdb_stub_main
|
||||
push hl
|
||||
push hl
|
||||
retn
|
||||
__endasm;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DBG_SWBREAK
|
||||
#ifdef DBG_SWBREAK_RST
|
||||
#define DBG_SWBREAK_SIZE 1
|
||||
#else
|
||||
#define DBG_SWBREAK_SIZE 3
|
||||
#endif
|
||||
|
||||
void gdb_swbreak (void) __naked {
|
||||
__asm
|
||||
ld (__gdb_state + R_SP), sp
|
||||
LOAD_SP
|
||||
call __gdb_save_cpu_state
|
||||
ld hl, -DBG_SWBREAK_SIZE
|
||||
push hl
|
||||
ld hl, EX_SWBREAK
|
||||
push hl
|
||||
call __gdb_stub_main
|
||||
;.globl _break_handler
|
||||
#ifdef DBG_SWBREAK_RST
|
||||
_break_handler = DBG_SWBREAK_RST
|
||||
#else
|
||||
_break_handler = _gdb_swbreak
|
||||
#endif
|
||||
__endasm;
|
||||
}
|
||||
#endif /* DBG_SWBREAK */
|
||||
|
||||
void gdb_set_enter(void (*func)(void)) {
|
||||
_gdb_enter_func = func;
|
||||
}
|
||||
|
||||
void gdb_set_hwbreak_toggle(int (*func)(int set, void *addr)) {
|
||||
_gdb_toggle_hwbreak = func;
|
||||
}
|
||||
|
||||
#ifdef DBG_TOGGLESTEP
|
||||
void gdb_set_step_toggle(int (*func)(int set)) {
|
||||
_gdb_toggle_step = func;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DGB_SWBREAK
|
||||
void gdb_set_swbreak_toggle(int (*func)(int set, void *addr)) {
|
||||
_gdb_toggle_swbreak = func;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set calls to here in the holes, then remove them when we
|
||||
// reenter
|
||||
#if defined(DBG_TOGGLESTEP) && defined(DBG_SWBREAK)
|
||||
void gdb_step (void) __naked {
|
||||
__asm
|
||||
jp _gdb_swbreak
|
||||
__endasm
|
||||
}
|
||||
#endif
|
68
src/gdb.h
Normal file
68
src/gdb.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
#ifndef __GDB_GDB_H__
|
||||
#define __GDB_GDB_H__
|
||||
|
||||
#include "lib.h"
|
||||
|
||||
#define export(returntype, signature) { \
|
||||
returntype signature; \
|
||||
extern returntype __LIB__ signature; \
|
||||
}
|
||||
|
||||
/* These functions must be defined by the application */
|
||||
unsigned char gdb_getDebugChar(void);
|
||||
void gdb_putDebugChar(unsigned char ch);
|
||||
|
||||
/* This file contains the library exports. */
|
||||
|
||||
/* Enter to debug mode from software or hardware breakpoint.
|
||||
Assume address of next instruction after breakpoint call is on top of stack.
|
||||
Do JP _gdb_swbreak or JP _gdb_hwbreak from RST handler, for example.
|
||||
*/
|
||||
export(void, gdb_swbreak(void) __naked);
|
||||
export(void, gdb_hwbreak(void) __naked);
|
||||
export(void, gdb_step(void) __naked);
|
||||
export(void, gdb_step (void));
|
||||
export(void, gdb_exception (int ex) __naked);
|
||||
|
||||
/* Jump to this function from NMI handler. Just replace RETN instruction by
|
||||
JP _gdb_nmi
|
||||
Use if NMI detects request to enter to debug mode.
|
||||
*/
|
||||
export(void, gdb_nmi (void) __naked);
|
||||
|
||||
/* Jump to this function from INT handler. Just replace EI+RETI instructions by
|
||||
JP _gdb_int
|
||||
Use if INT detects request to enter to debug mode.
|
||||
*/
|
||||
export(void, gdb_int (void) __naked);
|
||||
|
||||
/* Prints to debugger console. */
|
||||
export(void, gdb_print(const char *str));
|
||||
|
||||
/* Set the function which gets a packet character. This is required. */
|
||||
export(void, gdb_set_get_char(unsigned char (*getter)(void)));
|
||||
|
||||
/* Set the function which puts a packet character. This is required. */
|
||||
export(void, gdb_set_put_char(void (*putter)(unsigned char)));
|
||||
|
||||
/* Set the function which turns a software break in a particular location on or off */
|
||||
export(void, gdb_set_swbreak_toggle(int (*func)(int set, void *addr)));
|
||||
|
||||
/* Set the function which turns a hardware break in a particular location on or off */
|
||||
export(void, gdb_set_hwbreak_toggle(int (*func)(int set, void *addr)));
|
||||
|
||||
/* Set the function which turns line stepping on or off */
|
||||
export(void, gdb_set_step_toggle(int (*func)(int set)));
|
||||
|
||||
/* Set the function which is called when the debugger is entered */
|
||||
export(void, gdb_set_enter(void (*func)(void)));
|
||||
|
||||
/* Enter to debug mode (after receiving BREAK from GDB, for example)
|
||||
* Assume:
|
||||
* program PC in (SP+0)
|
||||
* caught signal in (SP+2)
|
||||
* program SP is SP+4
|
||||
*/
|
||||
export(void, gdb_exception (int ex));
|
||||
|
||||
#endif // __GDB_GDB_H__
|
254
src/lib.h
Normal file
254
src/lib.h
Normal file
|
@ -0,0 +1,254 @@
|
|||
#ifndef __GDB_LIB_H__
|
||||
#define __GDB_LIB_H__
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned short word;
|
||||
|
||||
/* This file contains stuff internal to the library */
|
||||
|
||||
/* Usage:
|
||||
1. Copy this file to project directory
|
||||
2. Configure it commenting/uncommenting macros below or define DBG_CONFIGURED
|
||||
and all required macros and then include this file to one of your C-source
|
||||
files.
|
||||
3. Implement gdb_getDebugChar() and gdb_putDebugChar(), functions must not return
|
||||
until data received or sent.
|
||||
4. Implement all optional functions used to toggle breakpoints/watchpoints,
|
||||
if supported. Do not write fuctions to toggle software breakpoints if
|
||||
you unsure (GDB will do itself).
|
||||
5. Implement serial port initialization routine called at program start.
|
||||
6. Add necessary debugger entry points to your program, for example:
|
||||
.org 0x08 ;RST 8 handler
|
||||
jp _gdb_swbreak
|
||||
...
|
||||
.org 0x66 ;NMI handler
|
||||
jp _gdb_nmi
|
||||
...
|
||||
main_loop:
|
||||
halt
|
||||
call isDbgInterrupt
|
||||
jr z,101$
|
||||
ld hl, 2 ;EX_SIGINT
|
||||
push hl
|
||||
call _debug_exception
|
||||
101$:
|
||||
...
|
||||
7. Compile file using SDCC (supported ports are: z80, z180, z80n, gbz80 and
|
||||
ez80_z80), do not use --peep-asm option. For example:
|
||||
$ sdcc -mz80 --opt-code-size --max-allocs-per-node 50000 z80-stub.c
|
||||
*/
|
||||
/******************************************************************************\
|
||||
Configuration
|
||||
\******************************************************************************/
|
||||
#ifndef DBG_CONFIGURED
|
||||
/* Uncomment this line, if stub size is critical for you */
|
||||
//#define DBG_MIN_SIZE
|
||||
|
||||
/* Comment this line out if software breakpoints are unsupported.
|
||||
If you have special function to toggle software breakpoints, then provide
|
||||
here name of these function. Expected prototype:
|
||||
int _gdb_toggle_swbreak(int set, void *addr);
|
||||
function must return 0 on success. */
|
||||
#define DBG_SWBREAK _gdb_toggle_swbreak
|
||||
|
||||
/* Define the function to disable and enable software stepping
|
||||
int _gdb_toggle_step(int set);
|
||||
*/
|
||||
#define DBG_TOGGLESTEP _gdb_toggle_step
|
||||
|
||||
/* Define if one of standard RST handlers is used as software
|
||||
breakpoint entry point */
|
||||
//#define DBG_SWBREAK_RST 0x08
|
||||
|
||||
/* if platform supports hardware breakpoints then define following macro
|
||||
by name of function. Fuction must have next prototype:
|
||||
int _gdb_toggle_hwbreak(int set, void *addr);
|
||||
function must return 0 on success. */
|
||||
#define DBG_HWBREAK _gdb_toggle_hwbreak
|
||||
|
||||
/* if platform supports hardware watchpoints then define all or some of
|
||||
following macros by names of functions. Fuctions prototypes:
|
||||
int toggle_watch(int set, void *addr, size_t size); // memory write watch
|
||||
int toggle_rwatch(int set, void *addr, size_t size); // memory read watch
|
||||
int toggle_awatch(int set, void *addr, size_t size); // memory access watch
|
||||
function must return 0 on success. */
|
||||
//#define DBG_WWATCH toggle_watch
|
||||
//#define DBG_RWATCH toggle_rwatch
|
||||
//#define DBG_AWATCH toggle_awatch
|
||||
|
||||
/* Size of hardware breakpoint. Required to correct PC. */
|
||||
#define DBG_HWBREAK_SIZE 0
|
||||
|
||||
/* Define following macro if you need custom memory read/write routine.
|
||||
Function should return non-zero on success, and zero on failure
|
||||
(for example, write to ROM area).
|
||||
Useful with overlays (bank switching).
|
||||
Do not forget to define:
|
||||
_ovly_table - overlay table
|
||||
_novlys - number of items in _ovly_table
|
||||
or
|
||||
_ovly_region_table - overlay regions table
|
||||
_novly_regions - number of items in _ovly_region_table
|
||||
|
||||
_ovly_debug_prepare - function is called before overlay mapping
|
||||
_ovly_debug_event - function is called after overlay mapping
|
||||
*/
|
||||
//#define DBG_MEMCPY memcpy
|
||||
|
||||
/* define dedicated stack size if required */
|
||||
//#define DBG_STACK_SIZE 256
|
||||
|
||||
/* max GDB packet size
|
||||
should be much less that DBG_STACK_SIZE because it will be allocated on stack
|
||||
*/
|
||||
#define DBG_PACKET_SIZE 800
|
||||
|
||||
/* Uncomment if required to use trampoline when resuming operation.
|
||||
Useful with dedicated stack when stack pointer do not point to the stack or
|
||||
stack is not writable */
|
||||
//#define DBG_USE_TRAMPOLINE
|
||||
|
||||
/* Uncomment following macro to enable debug printing to debugger console */
|
||||
#define DBG_PRINT
|
||||
|
||||
#define DBG_NMI_EX EX_HWBREAK
|
||||
#define DBG_INT_EX EX_SIGINT
|
||||
|
||||
/* Define following macro to statement, which will be exectuted after entering to
|
||||
_gdb_stub_main function. Statement should include semicolon. */
|
||||
#define DBG_ENTER if(_gdb_enter_func) { _gdb_enter_func(); };
|
||||
|
||||
/* Define following macro to instruction(s), which will be execute before return
|
||||
control to the program. It is useful when gdb-stub is placed in one of overlays.
|
||||
This procedure must not change any register. On top of stack before invocation
|
||||
will be return address of the program. */
|
||||
//#define DBG_RESUME jp _restore_bank
|
||||
|
||||
/* Define following macro to the string containing memory map definition XML.
|
||||
GDB will use it to select proper breakpoint type (HW or SW). */
|
||||
/*#define DBG_MEMORY_MAP "\
|
||||
<memory-map>\
|
||||
<memory type=\"rom\" start=\"0x0000\" length=\"0x4000\"/>\
|
||||
<!-- <memory type=\"flash\" start=\"0x4000\" length=\"0x4000\">\
|
||||
<property name=\"blocksize\">128</property>\
|
||||
</memory> -->\
|
||||
<memory type=\"ram\" start=\"0x8000\" length=\"0x8000\"/>\
|
||||
</memory-map>\
|
||||
"
|
||||
*/
|
||||
|
||||
/* Define following macro to the string containing feature definition XML. */
|
||||
#define DBG_FEATURE_STR "<target version=\"1.0\">"\
|
||||
"<feature name=\"org.gnu.gdb.z80.cpu\">"\
|
||||
"<reg name=\"af\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"bc\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"de\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"hl\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"sp\" bitsize=\"16\" type=\"data_ptr\"/>"\
|
||||
"<reg name=\"pc\" bitsize=\"16\" type=\"code_ptr\"/>"\
|
||||
"<reg name=\"ix\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"iy\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"af'\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"bc'\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"de'\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"hl'\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"<reg name=\"ir\" bitsize=\"16\" type=\"int\"/>"\
|
||||
"</feature>"\
|
||||
"<architecture>z80</architecture>"\
|
||||
"</target>"
|
||||
|
||||
#endif /* DBG_CONFIGURED */
|
||||
/******************************************************************************\
|
||||
Public Interface
|
||||
\******************************************************************************/
|
||||
|
||||
#ifndef NULL
|
||||
# define NULL ((void*)0)
|
||||
#endif
|
||||
|
||||
#define EX_SWBREAK 0 /* sw breakpoint */
|
||||
#define EX_HWBREAK -1 /* hw breakpoint */
|
||||
#define EX_WWATCH -2 /* memory write watch */
|
||||
#define EX_RWATCH -3 /* memory read watch */
|
||||
#define EX_AWATCH -4 /* memory access watch */
|
||||
#define EX_SIGINT 2
|
||||
#define EX_SIGTRAP 5
|
||||
#define EX_SIGABRT 6
|
||||
#define EX_SIGBUS 10
|
||||
#define EX_SIGSEGV 11
|
||||
/* or any standard *nix signal value */
|
||||
|
||||
#endif // __GDB_LIB_H__
|
||||
|
||||
/******************************************************************************\
|
||||
IMPLEMENTATION
|
||||
\******************************************************************************/
|
||||
|
||||
/* CPU state */
|
||||
#ifdef __SDCC_ez80_adl
|
||||
# define REG_SIZE 3
|
||||
#else
|
||||
# define REG_SIZE 2
|
||||
#endif /* __SDCC_ez80_adl */
|
||||
|
||||
#define R_AF (0*REG_SIZE)
|
||||
#define R_BC (1*REG_SIZE)
|
||||
#define R_DE (2*REG_SIZE)
|
||||
#define R_HL (3*REG_SIZE)
|
||||
#define R_SP (4*REG_SIZE)
|
||||
#define R_PC (5*REG_SIZE)
|
||||
|
||||
#ifndef __SDCC_gbz80
|
||||
#define R_IX (6*REG_SIZE)
|
||||
#define R_IY (7*REG_SIZE)
|
||||
#define R_AF_ (8*REG_SIZE)
|
||||
#define R_BC_ (9*REG_SIZE)
|
||||
#define R_DE_ (10*REG_SIZE)
|
||||
#define R_HL_ (11*REG_SIZE)
|
||||
#define R_IR (12*REG_SIZE)
|
||||
|
||||
#ifdef __SDCC_ez80_adl
|
||||
#define R_SPS (13*REG_SIZE)
|
||||
#define NUMREGBYTES (14*REG_SIZE)
|
||||
#else
|
||||
#define NUMREGBYTES (13*REG_SIZE)
|
||||
#endif /* __SDCC_ez80_adl */
|
||||
#else
|
||||
#define NUMREGBYTES (6*REG_SIZE)
|
||||
#define FASTCALL
|
||||
#endif /*__SDCC_gbz80 */
|
||||
extern byte _gdb_state[NUMREGBYTES];
|
||||
|
||||
void _gdb_save_cpu_state (void);
|
||||
void _gdb_rest_cpu_state (void);
|
||||
void _gdb_stub_main (int sigval, int pc_adj);
|
||||
|
||||
/* dedicated stack */
|
||||
#ifdef DBG_STACK_SIZE
|
||||
|
||||
#define LOAD_SP ld sp, __gdb_stack + DBG_STACK_SIZE
|
||||
|
||||
static char _gdb_stack[DBG_STACK_SIZE];
|
||||
|
||||
#else
|
||||
|
||||
#undef DBG_USE_TRAMPOLINE
|
||||
#define LOAD_SP
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DBG_HWBREAK
|
||||
extern int (*_gdb_toggle_hwbreak)(int set, void *addr);
|
||||
#endif
|
||||
|
||||
#if defined(DBG_TOGGLESTEP)
|
||||
extern int (*_gdb_toggle_step)(int set);
|
||||
#endif
|
||||
|
||||
#ifdef DBG_SWBREAK
|
||||
extern int (*_gdb_toggle_swbreak)(int set, void *addr);
|
||||
#endif
|
||||
|
||||
#ifdef DBG_ENTER
|
||||
extern void (*_gdb_enter_func)(void);
|
||||
#endif
|
Loading…
Add table
Reference in a new issue