Initial commit

This commit is contained in:
Empathic Qubit 2022-12-31 22:09:30 +01:00
commit c58c8faac3
7 changed files with 1540 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

53
Makefile Normal file
View 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
View 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
View 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
View 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__

1011
src/lib.c Normal file

File diff suppressed because it is too large Load diff

254
src/lib.h Normal file
View 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