| /* Debug stub for Z80. | 
 |  | 
 |    Copyright (C) 2021-2022 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | /* 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 getDebugChar() and 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 _debug_swbreak | 
 | 	... | 
 | 	.org	0x66	;NMI handler | 
 | 	jp	_debug_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 toggle_swbreak(int set, void *addr); | 
 |    function must return 0 on success. */ | 
 | //#define DBG_SWBREAK toggle_swbreak | 
 | #define DBG_SWBREAK | 
 |  | 
 | /* 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 toggle_hwbreak(int set, void *addr); | 
 |    function must return 0 on success. */ | 
 | //#define DBG_HWBREAK 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 150 | 
 |  | 
 | /* 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 | 
 |    stub_main function. Statement should include semicolon. */ | 
 | //#define DBG_ENTER debug_enter(); | 
 |  | 
 | /* 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>\ | 
 | " | 
 | */ | 
 | #endif /* DBG_CONFIGURED */ | 
 | /******************************************************************************\ | 
 | 			     Public Interface | 
 | \******************************************************************************/ | 
 |  | 
 | /* Enter to debug mode from software or hardware breakpoint. | 
 |    Assume address of next instruction after breakpoint call is on top of stack. | 
 |    Do JP _debug_swbreak or JP _debug_hwbreak from RST handler, for example. | 
 |  */ | 
 | void debug_swbreak (void); | 
 | void debug_hwbreak (void); | 
 |  | 
 | /* Jump to this function from NMI handler. Just replace RETN instruction by | 
 |    JP _debug_nmi | 
 |    Use if NMI detects request to enter to debug mode. | 
 |  */ | 
 | void debug_nmi (void); | 
 |  | 
 | /* Jump to this function from INT handler. Just replace EI+RETI instructions by | 
 |    JP _debug_int | 
 |    Use if INT detects request to enter to debug mode. | 
 |  */ | 
 | void debug_int (void); | 
 |  | 
 | #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 */ | 
 |  | 
 | /* 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 | 
 |  */ | 
 | void debug_exception (int ex); | 
 |  | 
 | /* Prints to debugger console. */ | 
 | void debug_print(const char *str); | 
 | /******************************************************************************\ | 
 | 			      Required functions | 
 | \******************************************************************************/ | 
 |  | 
 | extern int getDebugChar (void); | 
 | extern void putDebugChar (int ch); | 
 |  | 
 | #ifdef DBG_SWBREAK | 
 | #define DO_EXPAND(VAL)  VAL ## 123456 | 
 | #define EXPAND(VAL)     DO_EXPAND(VAL) | 
 |  | 
 | #if EXPAND(DBG_SWBREAK) != 123456 | 
 | #define DBG_SWBREAK_PROC DBG_SWBREAK | 
 | extern int DBG_SWBREAK(int set, void *addr); | 
 | #endif | 
 |  | 
 | #undef EXPAND | 
 | #undef DO_EXPAND | 
 | #endif /* DBG_SWBREAK */ | 
 |  | 
 | #ifdef DBG_HWBREAK | 
 | extern int DBG_HWBREAK(int set, void *addr); | 
 | #endif | 
 |  | 
 | #ifdef DBG_MEMCPY | 
 | extern void* DBG_MEMCPY (void *dest, const void *src, unsigned n); | 
 | #endif | 
 |  | 
 | #ifdef DBG_WWATCH | 
 | extern int DBG_WWATCH(int set, void *addr, unsigned size); | 
 | #endif | 
 |  | 
 | #ifdef DBG_RWATCH | 
 | extern int DBG_RWATCH(int set, void *addr, unsigned size); | 
 | #endif | 
 |  | 
 | #ifdef DBG_AWATCH | 
 | extern int DBG_AWATCH(int set, void *addr, unsigned size); | 
 | #endif | 
 |  | 
 | /******************************************************************************\ | 
 | 			       IMPLEMENTATION | 
 | \******************************************************************************/ | 
 |  | 
 | #include <string.h> | 
 |  | 
 | #ifndef NULL | 
 | # define NULL (void*)0 | 
 | #endif | 
 |  | 
 | typedef unsigned char byte; | 
 | typedef unsigned short word; | 
 |  | 
 | /* 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 */ | 
 | static byte state[NUMREGBYTES]; | 
 |  | 
 | #if DBG_PACKET_SIZE < (NUMREGBYTES*2+5) | 
 | #error "Too small DBG_PACKET_SIZE" | 
 | #endif | 
 |  | 
 | #ifndef FASTCALL | 
 | #define FASTCALL __z88dk_fastcall | 
 | #endif | 
 |  | 
 | /* dedicated stack */ | 
 | #ifdef DBG_STACK_SIZE | 
 |  | 
 | #define LOAD_SP	ld	sp, #_stack + DBG_STACK_SIZE | 
 |  | 
 | static char stack[DBG_STACK_SIZE]; | 
 |  | 
 | #else | 
 |  | 
 | #undef DBG_USE_TRAMPOLINE | 
 | #define LOAD_SP | 
 |  | 
 | #endif | 
 |  | 
 | #ifndef DBG_ENTER | 
 | #define DBG_ENTER | 
 | #endif | 
 |  | 
 | #ifndef DBG_RESUME | 
 | #define DBG_RESUME ret | 
 | #endif | 
 |  | 
 | static signed char sigval; | 
 |  | 
 | static void stub_main (int sigval, int pc_adj); | 
 | static char high_hex (byte v) FASTCALL; | 
 | static char low_hex (byte v) FASTCALL; | 
 | static char put_packet_info (const char *buffer) FASTCALL; | 
 | static void save_cpu_state (void); | 
 | static void rest_cpu_state (void); | 
 |  | 
 | /******************************************************************************/ | 
 | #ifdef DBG_SWBREAK | 
 | #ifdef DBG_SWBREAK_RST | 
 | #define DBG_SWBREAK_SIZE 1 | 
 | #else | 
 | #define DBG_SWBREAK_SIZE 3 | 
 | #endif | 
 | void | 
 | debug_swbreak (void) __naked | 
 | { | 
 |   __asm | 
 | 	ld	(#_state + R_SP), sp | 
 | 	LOAD_SP | 
 | 	call	_save_cpu_state | 
 | 	ld	hl, #-DBG_SWBREAK_SIZE | 
 | 	push	hl | 
 | 	ld	hl, #EX_SWBREAK | 
 | 	push	hl | 
 | 	call	_stub_main | 
 | 	.globl	_break_handler | 
 | #ifdef DBG_SWBREAK_RST | 
 | _break_handler = DBG_SWBREAK_RST | 
 | #else | 
 | _break_handler = _debug_swbreak | 
 | #endif | 
 |   __endasm; | 
 | } | 
 | #endif /* DBG_SWBREAK */ | 
 | /******************************************************************************/ | 
 | #ifdef DBG_HWBREAK | 
 | #ifndef DBG_HWBREAK_SIZE | 
 | #define DBG_HWBREAK_SIZE 0 | 
 | #endif /* DBG_HWBREAK_SIZE */ | 
 | void | 
 | debug_hwbreak (void) __naked | 
 | { | 
 |   __asm | 
 | 	ld	(#_state + R_SP), sp | 
 | 	LOAD_SP | 
 | 	call	_save_cpu_state | 
 | 	ld	hl, #-DBG_HWBREAK_SIZE | 
 | 	push	hl | 
 | 	ld	hl, #EX_HWBREAK | 
 | 	push	hl | 
 | 	call	_stub_main | 
 |   __endasm; | 
 | } | 
 | #endif /* DBG_HWBREAK_SET */ | 
 | /******************************************************************************/ | 
 | void | 
 | debug_exception (int ex) __naked | 
 | { | 
 |   __asm | 
 | 	ld	(#_state + R_SP), sp | 
 | 	LOAD_SP | 
 | 	call	_save_cpu_state | 
 | 	ld	hl, #0 | 
 | 	push	hl | 
 | #ifdef __SDCC_gbz80 | 
 | 	ld	hl, #_state + R_SP | 
 | 	ld	a, (hl+) | 
 | 	ld	h, (hl) | 
 | 	ld	l, a | 
 | #else | 
 | 	ld	hl, (#_state + R_SP) | 
 | #endif | 
 | 	inc	hl | 
 | 	inc	hl | 
 | 	ld	e, (hl) | 
 | 	inc	hl | 
 | 	ld	d, (hl) | 
 | 	push	de | 
 | 	call	_stub_main | 
 |   __endasm; | 
 |   (void)ex; | 
 | } | 
 | /******************************************************************************/ | 
 | #ifndef __SDCC_gbz80 | 
 | void | 
 | debug_nmi(void) __naked | 
 | { | 
 |   __asm | 
 | 	ld	(#_state + R_SP), sp | 
 | 	LOAD_SP | 
 | 	call	_save_cpu_state | 
 | 	ld	hl, #0	;pc_adj | 
 | 	push	hl | 
 | 	ld	hl, #DBG_NMI_EX | 
 | 	push	hl | 
 | 	ld	hl, #_stub_main | 
 | 	push	hl | 
 | 	push	hl | 
 | 	retn | 
 |   __endasm; | 
 | } | 
 | #endif | 
 | /******************************************************************************/ | 
 | void | 
 | debug_int(void) __naked | 
 | { | 
 |   __asm | 
 | 	ld	(#_state + R_SP), sp | 
 | 	LOAD_SP | 
 | 	call	_save_cpu_state | 
 | 	ld	hl, #0	;pc_adj | 
 | 	push	hl | 
 | 	ld	hl, #DBG_INT_EX | 
 | 	push	hl | 
 | 	ld	hl, #_stub_main | 
 | 	push	hl | 
 | 	push	hl | 
 | 	ei | 
 | 	reti | 
 |   __endasm; | 
 | } | 
 | /******************************************************************************/ | 
 | #ifdef DBG_PRINT | 
 | void | 
 | debug_print(const char *str) | 
 | { | 
 |   putDebugChar ('$'); | 
 |   putDebugChar ('O'); | 
 |   char csum = 'O'; | 
 |   for (; *str != '\0'; ) | 
 |     { | 
 |       char c = high_hex (*str); | 
 |       csum += c; | 
 |       putDebugChar (c); | 
 |       c = low_hex (*str++); | 
 |       csum += c; | 
 |       putDebugChar (c); | 
 |     } | 
 |   putDebugChar ('#'); | 
 |   putDebugChar (high_hex (csum)); | 
 |   putDebugChar (low_hex (csum)); | 
 | } | 
 | #endif /* DBG_PRINT */ | 
 | /******************************************************************************/ | 
 | static void store_pc_sp (int pc_adj) FASTCALL; | 
 | #define get_reg_value(mem) (*(void* const*)(mem)) | 
 | #define set_reg_value(mem,val) do { (*(void**)(mem) = (val)); } while (0) | 
 | static char* byte2hex(char *buf, byte val); | 
 | static int hex2int (const char **buf) FASTCALL; | 
 | static char* int2hex (char *buf, int v); | 
 | static void get_packet (char *buffer); | 
 | static void put_packet (const char *buffer); | 
 | static char process (char *buffer) FASTCALL; | 
 | static void rest_cpu_state (void); | 
 |  | 
 | static void | 
 | stub_main (int ex, int pc_adj) | 
 | { | 
 |   char buffer[DBG_PACKET_SIZE+1]; | 
 |   sigval = (signed char)ex; | 
 |   store_pc_sp (pc_adj); | 
 |  | 
 |   DBG_ENTER | 
 |  | 
 |   /* after starting gdb_stub must always return stop reason */ | 
 |   *buffer = '?'; | 
 |   for (; process (buffer);) | 
 |     { | 
 |       put_packet (buffer); | 
 |       get_packet (buffer); | 
 |     } | 
 |   put_packet (buffer); | 
 |   rest_cpu_state (); | 
 | } | 
 |  | 
 | static void | 
 | get_packet (char *buffer) | 
 | { | 
 |   byte csum; | 
 |   char ch; | 
 |   char *p; | 
 |   byte esc; | 
 | #if DBG_PACKET_SIZE <= 256 | 
 |   byte count; /* it is OK to use up to 256 here */ | 
 | #else | 
 |   unsigned count; | 
 | #endif | 
 |   for (;; putDebugChar ('-')) | 
 |     { | 
 |       /* wait for packet start character */ | 
 |       while (getDebugChar () != '$'); | 
 | retry: | 
 |       csum = 0; | 
 |       esc = 0; | 
 |       p = buffer; | 
 |       count = DBG_PACKET_SIZE; | 
 |       do | 
 | 	{ | 
 | 	  ch = getDebugChar (); | 
 | 	  switch (ch) | 
 | 	    { | 
 | 	    case '$': | 
 | 	      goto retry; | 
 | 	    case '#': | 
 | 	      goto finish; | 
 | 	    case '}': | 
 | 	      esc = 0x20; | 
 | 	      break; | 
 | 	    default: | 
 | 	      *p++ = ch ^ esc; | 
 | 	      esc = 0; | 
 | 	      --count; | 
 | 	    } | 
 | 	  csum += ch; | 
 | 	} | 
 |       while (count != 0); | 
 | finish: | 
 |       *p = '\0'; | 
 |       if (ch != '#') /* packet is too large */ | 
 | 	continue; | 
 |       ch = getDebugChar (); | 
 |       if (ch != high_hex (csum)) | 
 | 	continue; | 
 |       ch = getDebugChar (); | 
 |       if (ch != low_hex (csum)) | 
 | 	continue; | 
 |       break; | 
 |     } | 
 |   putDebugChar ('+'); | 
 | } | 
 |  | 
 | static void | 
 | put_packet (const char *buffer) | 
 | { | 
 |   /*  $<packet info>#<checksum>. */ | 
 |   for (;;) | 
 |     { | 
 |       putDebugChar ('$'); | 
 |       char checksum = put_packet_info (buffer); | 
 |       putDebugChar ('#'); | 
 |       putDebugChar (high_hex(checksum)); | 
 |       putDebugChar (low_hex(checksum)); | 
 |       for (;;) | 
 | 	{ | 
 | 	  char c = getDebugChar (); | 
 | 	  switch (c) | 
 | 	    { | 
 | 	    case '+': return; | 
 | 	    case '-': break; | 
 | 	    default: | 
 | 	      putDebugChar (c); | 
 | 	      continue; | 
 | 	    } | 
 | 	  break; | 
 | 	} | 
 |     } | 
 | } | 
 |  | 
 | static char | 
 | put_packet_info (const char *src) FASTCALL | 
 | { | 
 |   char ch; | 
 |   char checksum = 0; | 
 |   for (;;) | 
 |     { | 
 |       ch = *src++; | 
 |       if (ch == '\0') | 
 | 	break; | 
 |       if (ch == '}' || ch == '*' || ch == '#' || ch == '$') | 
 | 	{ | 
 | 	  /* escape special characters */ | 
 | 	  putDebugChar ('}'); | 
 | 	  checksum += '}'; | 
 | 	  ch ^= 0x20; | 
 | 	} | 
 |       putDebugChar (ch); | 
 |       checksum += ch; | 
 |     } | 
 |   return checksum; | 
 | } | 
 |  | 
 | static void | 
 | store_pc_sp (int pc_adj) FASTCALL | 
 | { | 
 |   byte *sp = get_reg_value (&state[R_SP]); | 
 |   byte *pc = get_reg_value (sp); | 
 |   pc += pc_adj; | 
 |   set_reg_value (&state[R_PC], pc); | 
 |   set_reg_value (&state[R_SP], sp + REG_SIZE); | 
 | } | 
 |  | 
 | static char *mem2hex (char *buf, const byte *mem, unsigned bytes); | 
 | static char *hex2mem (byte *mem, const char *buf, unsigned bytes); | 
 |  | 
 | /* Command processors. Takes pointer to buffer (begins from command symbol), | 
 |    modifies buffer, returns: -1 - empty response (ignore), 0 - success, | 
 |    positive: error code. */ | 
 |  | 
 | #ifdef DBG_MIN_SIZE | 
 | static signed char | 
 | process_question (char *p) FASTCALL | 
 | { | 
 |   signed char sig; | 
 |   *p++ = 'S'; | 
 |   sig = sigval; | 
 |   if (sig <= 0) | 
 |     sig = EX_SIGTRAP; | 
 |   p = byte2hex (p, (byte)sig); | 
 |   *p = '\0'; | 
 |   return 0; | 
 | } | 
 | #else /* DBG_MIN_SIZE */ | 
 | static char *format_reg_value (char *p, unsigned reg_num, const byte *value); | 
 |  | 
 | static signed char | 
 | process_question (char *p) FASTCALL | 
 | { | 
 |   signed char sig; | 
 |   *p++ = 'T'; | 
 |   sig = sigval; | 
 |   if (sig <= 0) | 
 |     sig = EX_SIGTRAP; | 
 |   p = byte2hex (p, (byte)sig); | 
 |   p = format_reg_value(p, R_AF/REG_SIZE, &state[R_AF]); | 
 |   p = format_reg_value(p, R_SP/REG_SIZE, &state[R_SP]); | 
 |   p = format_reg_value(p, R_PC/REG_SIZE, &state[R_PC]); | 
 | #if defined(DBG_SWBREAK_PROC) || defined(DBG_HWBREAK) || defined(DBG_WWATCH) || defined(DBG_RWATCH) || defined(DBG_AWATCH) | 
 |   const char *reason; | 
 |   unsigned addr = 0; | 
 |   switch (sigval) | 
 |     { | 
 | #ifdef DBG_SWBREAK_PROC | 
 |     case EX_SWBREAK: | 
 |       reason = "swbreak"; | 
 |       break; | 
 | #endif | 
 | #ifdef DBG_HWBREAK | 
 |     case EX_HWBREAK: | 
 |       reason = "hwbreak"; | 
 |       break; | 
 | #endif | 
 | #ifdef DBG_WWATCH | 
 |     case EX_WWATCH: | 
 |       reason = "watch"; | 
 |       addr = 1; | 
 |       break; | 
 | #endif | 
 | #ifdef DBG_RWATCH | 
 |     case EX_RWATCH: | 
 |       reason = "rwatch"; | 
 |       addr = 1; | 
 |       break; | 
 | #endif | 
 | #ifdef DBG_AWATCH | 
 |     case EX_AWATCH: | 
 |       reason = "awatch"; | 
 |       addr = 1; | 
 |       break; | 
 | #endif | 
 |     default: | 
 |       goto finish; | 
 |     } | 
 |   while ((*p++ = *reason++)) | 
 |     ; | 
 |   --p; | 
 |   *p++ = ':'; | 
 |   if (addr != 0) | 
 |     p = int2hex(p, addr); | 
 |   *p++ = ';'; | 
 | finish: | 
 | #endif /* DBG_HWBREAK, DBG_WWATCH, DBG_RWATCH, DBG_AWATCH */ | 
 |   *p++ = '\0'; | 
 |   return 0; | 
 | } | 
 | #endif /* DBG_MINSIZE */ | 
 |  | 
 | #define STRING2(x) #x | 
 | #define STRING1(x) STRING2(x) | 
 | #define STRING(x) STRING1(x) | 
 | #ifdef DBG_MEMORY_MAP | 
 | static void read_memory_map (char *buffer, unsigned offset, unsigned length); | 
 | #endif | 
 |  | 
 | static signed char | 
 | process_q (char *buffer) FASTCALL | 
 | { | 
 |   char *p; | 
 |   if (memcmp (buffer + 1, "Supported", 9) == 0) | 
 |     { | 
 |       memcpy (buffer, "PacketSize=", 11); | 
 |       p = int2hex (&buffer[11], DBG_PACKET_SIZE); | 
 | #ifndef DBG_MIN_SIZE | 
 | #ifdef DBG_SWBREAK_PROC | 
 |       memcpy (p, ";swbreak+", 9); | 
 |       p += 9; | 
 | #endif | 
 | #ifdef DBG_HWBREAK | 
 |       memcpy (p, ";hwbreak+", 9); | 
 |       p += 9; | 
 | #endif | 
 | #endif /* DBG_MIN_SIZE */ | 
 |  | 
 | #ifdef DBG_MEMORY_MAP | 
 |       memcpy (p, ";qXfer:memory-map:read+", 23); | 
 |       p += 23; | 
 | #endif | 
 |       *p = '\0'; | 
 |       return 0; | 
 |     } | 
 | #ifdef DBG_MEMORY_MAP | 
 |   if (memcmp (buffer + 1, "Xfer:memory-map:read:", 21) == 0) | 
 |     { | 
 |       p = strchr (buffer + 1 + 21, ':'); | 
 |       if (p == NULL) | 
 | 	return 1; | 
 |       ++p; | 
 |       unsigned offset = hex2int (&p); | 
 |       if (*p++ != ',') | 
 | 	return 2; | 
 |       unsigned length = hex2int (&p); | 
 |       if (length == 0) | 
 | 	return 3; | 
 |       if (length > DBG_PACKET_SIZE) | 
 | 	return 4; | 
 |       read_memory_map (buffer, offset, length); | 
 |       return 0; | 
 |     } | 
 | #endif | 
 | #ifndef DBG_MIN_SIZE | 
 |   if (memcmp (&buffer[1], "Attached", 9) == 0) | 
 |     { | 
 |       /* Just report that GDB attached to existing process | 
 | 	 if it is not applicable for you, then send patches */ | 
 |       memcpy(buffer, "1", 2); | 
 |       return 0; | 
 |     } | 
 | #endif /* DBG_MIN_SIZE */ | 
 |   *buffer = '\0'; | 
 |   return -1; | 
 | } | 
 |  | 
 | static signed char | 
 | process_g (char *buffer) FASTCALL | 
 | { | 
 |   mem2hex (buffer, state, NUMREGBYTES); | 
 |   return 0; | 
 | } | 
 |  | 
 | static signed char | 
 | process_G (char *buffer) FASTCALL | 
 | { | 
 |   hex2mem (state, &buffer[1], NUMREGBYTES); | 
 |   /* OK response */ | 
 |   *buffer = '\0'; | 
 |   return 0; | 
 | } | 
 |  | 
 | static signed char | 
 | process_m (char *buffer) FASTCALL | 
 | {/* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */ | 
 |   char *p = &buffer[1]; | 
 |   byte *addr = (void*)hex2int(&p); | 
 |   if (*p++ != ',') | 
 |     return 1; | 
 |   unsigned len = (unsigned)hex2int(&p); | 
 |   if (len == 0) | 
 |     return 2; | 
 |   if (len > DBG_PACKET_SIZE/2) | 
 |     return 3; | 
 |   p = buffer; | 
 | #ifdef DBG_MEMCPY | 
 |   do | 
 |     { | 
 |       byte tmp[16]; | 
 |       unsigned tlen = sizeof(tmp); | 
 |       if (tlen > len) | 
 | 	tlen = len; | 
 |       if (!DBG_MEMCPY(tmp, addr, tlen)) | 
 | 	return 4; | 
 |       p = mem2hex (p, tmp, tlen); | 
 |       addr += tlen; | 
 |       len -= tlen; | 
 |     } | 
 |   while (len); | 
 | #else | 
 |   p = mem2hex (p, addr, len); | 
 | #endif | 
 |   return 0; | 
 | } | 
 |  | 
 | static signed char | 
 | process_M (char *buffer) FASTCALL | 
 | {/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ | 
 |   char *p = &buffer[1]; | 
 |   byte *addr = (void*)hex2int(&p); | 
 |   if (*p != ',') | 
 |     return 1; | 
 |   ++p; | 
 |   unsigned len = (unsigned)hex2int(&p); | 
 |   if (*p++ != ':') | 
 |     return 2; | 
 |   if (len == 0) | 
 |     goto end; | 
 |   if (len*2 + (p - buffer) > DBG_PACKET_SIZE) | 
 |     return 3; | 
 | #ifdef DBG_MEMCPY | 
 |   do | 
 |     { | 
 |       byte tmp[16]; | 
 |       unsigned tlen = sizeof(tmp); | 
 |       if (tlen > len) | 
 | 	tlen = len; | 
 |       p = hex2mem (tmp, p, tlen); | 
 |       if (!DBG_MEMCPY(addr, tmp, tlen)) | 
 | 	return 4; | 
 |       addr += tlen; | 
 | 	len -= tlen; | 
 |     } | 
 |   while (len); | 
 | #else | 
 |   hex2mem (addr, p, len); | 
 | #endif | 
 | end: | 
 |   /* OK response */ | 
 |   *buffer = '\0'; | 
 |   return 0; | 
 | } | 
 |  | 
 | #ifndef DBG_MIN_SIZE | 
 | static signed char | 
 | process_X (char *buffer) FASTCALL | 
 | {/* XAA..AA,LLLL: Write LLLL binary bytes at address AA.AA return OK */ | 
 |   char *p = &buffer[1]; | 
 |   byte *addr = (void*)hex2int(&p); | 
 |   if (*p != ',') | 
 |     return 1; | 
 |   ++p; | 
 |   unsigned len = (unsigned)hex2int(&p); | 
 |   if (*p++ != ':') | 
 |     return 2; | 
 |   if (len == 0) | 
 |     goto end; | 
 |   if (len + (p - buffer) > DBG_PACKET_SIZE) | 
 |     return 3; | 
 | #ifdef DBG_MEMCPY | 
 |   if (!DBG_MEMCPY(addr, p, len)) | 
 |     return 4; | 
 | #else | 
 |   memcpy (addr, p, len); | 
 | #endif | 
 | end: | 
 |   /* OK response */ | 
 |   *buffer = '\0'; | 
 |   return 0; | 
 | } | 
 | #else /* DBG_MIN_SIZE */ | 
 | static signed char | 
 | process_X (char *buffer) FASTCALL | 
 | { | 
 |   (void)buffer; | 
 |   return -1; | 
 | } | 
 | #endif /* DBG_MIN_SIZE */ | 
 |  | 
 | static signed char | 
 | process_c (char *buffer) FASTCALL | 
 | {/* 'cAAAA' - Continue at address AAAA(optional) */ | 
 |   const char *p = &buffer[1]; | 
 |   if (*p != '\0') | 
 |     { | 
 |       void *addr = (void*)hex2int(&p); | 
 |       set_reg_value (&state[R_PC], addr); | 
 |     } | 
 |   rest_cpu_state (); | 
 |   return 0; | 
 | } | 
 |  | 
 | static signed char | 
 | process_D (char *buffer) FASTCALL | 
 | {/* 'D' - detach the program: continue execution */ | 
 |   *buffer = '\0'; | 
 |   return -2; | 
 | } | 
 |  | 
 | static signed char | 
 | process_k (char *buffer) FASTCALL | 
 | {/* 'k' - Kill the program */ | 
 |   set_reg_value (&state[R_PC], 0); | 
 |   rest_cpu_state (); | 
 |   (void)buffer; | 
 |   return 0; | 
 | } | 
 |  | 
 | static signed char | 
 | process_v (char *buffer) FASTCALL | 
 | { | 
 | #ifndef DBG_MIN_SIZE | 
 |   if (memcmp (&buffer[1], "Cont", 4) == 0) | 
 |     { | 
 |       if (buffer[5] == '?') | 
 | 	{ | 
 | 	  /* result response will be "vCont;c;C"; C action must be | 
 | 	     supported too, because GDB reguires at lease both of them */ | 
 | 	  memcpy (&buffer[5], ";c;C", 5); | 
 | 	  return 0; | 
 | 	} | 
 |       buffer[0] = '\0'; | 
 |       if (buffer[5] == ';' && (buffer[6] == 'c' || buffer[6] == 'C')) | 
 | 	return -2; /* resume execution */ | 
 |       return 1; | 
 |   } | 
 | #endif /* DBG_MIN_SIZE */ | 
 |   return -1; | 
 | } | 
 |  | 
 | static signed char | 
 | process_zZ (char *buffer) FASTCALL | 
 | { /* insert/remove breakpoint */ | 
 | #if defined(DBG_SWBREAK_PROC) || defined(DBG_HWBREAK) || \ | 
 |     defined(DBG_WWATCH) || defined(DBG_RWATCH) || defined(DBG_AWATCH) | 
 |   const byte set = (*buffer == 'Z'); | 
 |   const char *p = &buffer[3]; | 
 |   void *addr = (void*)hex2int(&p); | 
 |   if (*p != ',') | 
 |     return 1; | 
 |   p++; | 
 |   int kind = hex2int(&p); | 
 |   *buffer = '\0'; | 
 |   switch (buffer[1]) | 
 |     { | 
 | #ifdef DBG_SWBREAK_PROC | 
 |     case '0': /* sw break */ | 
 |       return DBG_SWBREAK_PROC(set, addr); | 
 | #endif | 
 | #ifdef DBG_HWBREAK | 
 |     case '1': /* hw break */ | 
 |       return DBG_HWBREAK(set, addr); | 
 | #endif | 
 | #ifdef DBG_WWATCH | 
 |     case '2': /* write watch */ | 
 |       return DBG_WWATCH(set, addr, kind); | 
 | #endif | 
 | #ifdef DBG_RWATCH | 
 |     case '3': /* read watch */ | 
 |       return DBG_RWATCH(set, addr, kind); | 
 | #endif | 
 | #ifdef DBG_AWATCH | 
 |     case '4': /* access watch */ | 
 |       return DBG_AWATCH(set, addr, kind); | 
 | #endif | 
 |     default:; /* not supported */ | 
 |     } | 
 | #endif | 
 |   (void)buffer; | 
 |   return -1; | 
 | } | 
 |  | 
 | static signed char | 
 | do_process (char *buffer) FASTCALL | 
 | { | 
 |   switch (*buffer) | 
 |     { | 
 |     case '?': return process_question (buffer); | 
 |     case 'G': return process_G (buffer); | 
 |     case 'k': return process_k (buffer); | 
 |     case 'M': return process_M (buffer); | 
 |     case 'X': return process_X (buffer); | 
 |     case 'Z': return process_zZ (buffer); | 
 |     case 'c': return process_c (buffer); | 
 |     case 'D': return process_D (buffer); | 
 |     case 'g': return process_g (buffer); | 
 |     case 'm': return process_m (buffer); | 
 |     case 'q': return process_q (buffer); | 
 |     case 'v': return process_v (buffer); | 
 |     case 'z': return process_zZ (buffer); | 
 |     default:  return -1; /* empty response */ | 
 |     } | 
 | } | 
 |  | 
 | static char | 
 | process (char *buffer) FASTCALL | 
 | { | 
 |   signed char err = do_process (buffer); | 
 |   char *p = buffer; | 
 |   char ret = 1; | 
 |   if (err == -2) | 
 |     { | 
 |       ret = 0; | 
 |       err = 0; | 
 |     } | 
 |   if (err > 0) | 
 |     { | 
 |       *p++ = 'E'; | 
 |       p = byte2hex (p, err); | 
 |       *p = '\0'; | 
 |     } | 
 |   else if (err < 0) | 
 |     { | 
 |       *p = '\0'; | 
 |     } | 
 |   else if (*p == '\0') | 
 |     memcpy(p, "OK", 3); | 
 |   return ret; | 
 | } | 
 |  | 
 | static char * | 
 | byte2hex (char *p, byte v) | 
 | { | 
 |   *p++ = high_hex (v); | 
 |   *p++ = low_hex (v); | 
 |   return p; | 
 | } | 
 |  | 
 | static signed char | 
 | hex2val (unsigned char hex) FASTCALL | 
 | { | 
 |   if (hex <= '9') | 
 |     return hex - '0'; | 
 |   hex &= 0xdf; /* make uppercase */ | 
 |   hex -= 'A' - 10; | 
 |   return (hex >= 10 && hex < 16) ? hex : -1; | 
 | } | 
 |  | 
 | static int | 
 | hex2byte (const char *p) FASTCALL | 
 | { | 
 |   signed char h = hex2val (p[0]); | 
 |   signed char l = hex2val (p[1]); | 
 |   if (h < 0 || l < 0) | 
 |     return -1; | 
 |   return (byte)((byte)h << 4) | (byte)l; | 
 | } | 
 |  | 
 | static int | 
 | hex2int (const char **buf) FASTCALL | 
 | { | 
 |   word r = 0; | 
 |   for (;; (*buf)++) | 
 |     { | 
 |       signed char a = hex2val(**buf); | 
 |       if (a < 0) | 
 | 	break; | 
 |       r <<= 4; | 
 |       r += (byte)a; | 
 |     } | 
 |   return (int)r; | 
 | } | 
 |  | 
 | static char * | 
 | int2hex (char *buf, int v) | 
 | { | 
 |   buf = byte2hex(buf, (word)v >> 8); | 
 |   return byte2hex(buf, (byte)v); | 
 | } | 
 |  | 
 | static char | 
 | high_hex (byte v) FASTCALL | 
 | { | 
 |   return low_hex(v >> 4); | 
 | } | 
 |  | 
 | static char | 
 | low_hex (byte v) FASTCALL | 
 | { | 
 | /* | 
 |   __asm | 
 | 	ld	a, l | 
 | 	and	a, #0x0f | 
 | 	add	a, #0x90 | 
 | 	daa | 
 | 	adc	a, #0x40 | 
 | 	daa | 
 | 	ld	l, a | 
 |   __endasm; | 
 |   (void)v; | 
 | */ | 
 |   v &= 0x0f; | 
 |   v += '0'; | 
 |   if (v < '9'+1) | 
 |     return v; | 
 |   return v + 'a' - '0' - 10; | 
 | } | 
 |  | 
 | /* convert the memory, pointed to by mem into hex, placing result in buf */ | 
 | /* return a pointer to the last char put in buf (null) */ | 
 | static char * | 
 | mem2hex (char *buf, const byte *mem, unsigned bytes) | 
 | { | 
 |   char *d = buf; | 
 |   if (bytes != 0) | 
 |     { | 
 |       do | 
 | 	{ | 
 | 	  d = byte2hex (d, *mem++); | 
 | 	} | 
 |       while (--bytes); | 
 |     } | 
 |   *d = 0; | 
 |   return d; | 
 | } | 
 |  | 
 | /* convert the hex array pointed to by buf into binary, to be placed in mem | 
 |    return a pointer to the character after the last byte written */ | 
 |  | 
 | static const char * | 
 | hex2mem (byte *mem, const char *buf, unsigned bytes) | 
 | { | 
 |   if (bytes != 0) | 
 |     { | 
 |       do | 
 | 	{ | 
 | 	  *mem++ = hex2byte (buf); | 
 | 	  buf += 2; | 
 | 	} | 
 |       while (--bytes); | 
 |     } | 
 |   return buf; | 
 | } | 
 |  | 
 | #ifdef DBG_MEMORY_MAP | 
 | static void | 
 | read_memory_map (char *buffer, unsigned offset, unsigned length) | 
 | { | 
 |   const char *map = DBG_MEMORY_MAP; | 
 |   const unsigned map_sz = strlen(map); | 
 |   if (offset >= map_sz) | 
 |     { | 
 |       buffer[0] = 'l'; | 
 |       buffer[1] = '\0'; | 
 |       return; | 
 |     } | 
 |   if (offset + length > map_sz) | 
 |     length = map_sz - offset; | 
 |   buffer[0] = 'm'; | 
 |   memcpy (&buffer[1], &map[offset], length); | 
 |   buffer[1+length] = '\0'; | 
 | } | 
 | #endif | 
 |  | 
 | /* write string like " nn:0123" and return pointer after it */ | 
 | #ifndef DBG_MIN_SIZE | 
 | static char * | 
 | format_reg_value (char *p, unsigned reg_num, const byte *value) | 
 | { | 
 |   char *d = p; | 
 |   unsigned char i; | 
 |   d = byte2hex(d, reg_num); | 
 |   *d++ = ':'; | 
 |   value += REG_SIZE; | 
 |   i = REG_SIZE; | 
 |   do | 
 |     { | 
 |       d = byte2hex(d, *--value); | 
 |     } | 
 |   while (--i != 0); | 
 |   *d++ = ';'; | 
 |   return d; | 
 | } | 
 | #endif /* DBG_MIN_SIZE */ | 
 |  | 
 | #ifdef __SDCC_gbz80 | 
 | /* saves all state.except PC and SP */ | 
 | static void | 
 | save_cpu_state() __naked | 
 | { | 
 |   __asm | 
 | 	push	af | 
 | 	ld	a, l | 
 | 	ld	(#_state + R_HL + 0), a | 
 | 	ld	a, h | 
 | 	ld	(#_state + R_HL + 1), a | 
 | 	ld	hl, #_state + R_HL - 1 | 
 | 	ld	(hl), d | 
 | 	dec	hl | 
 | 	ld	(hl), e | 
 | 	dec	hl | 
 | 	ld	(hl), b | 
 | 	dec	hl | 
 | 	ld	(hl), c | 
 | 	dec	hl | 
 | 	pop	bc | 
 | 	ld	(hl), b | 
 | 	dec	hl | 
 | 	ld	(hl), c | 
 | 	ret | 
 |   __endasm; | 
 | } | 
 |  | 
 | /* restore CPU state and continue execution */ | 
 | static void | 
 | rest_cpu_state() __naked | 
 | { | 
 |   __asm | 
 | ;restore SP | 
 | 	ld	a, (#_state + R_SP + 0) | 
 | 	ld	l,a | 
 | 	ld	a, (#_state + R_SP + 1) | 
 | 	ld	h,a | 
 | 	ld	sp, hl | 
 | ;push PC value as return address | 
 | 	ld	a, (#_state + R_PC + 0) | 
 | 	ld	l, a | 
 | 	ld	a, (#_state + R_PC + 1) | 
 | 	ld	h, a | 
 | 	push	hl | 
 | ;restore registers | 
 | 	ld	hl, #_state + R_AF | 
 | 	ld	c, (hl) | 
 | 	inc	hl | 
 | 	ld	b, (hl) | 
 | 	inc	hl | 
 | 	push	bc | 
 | 	ld	c, (hl) | 
 | 	inc	hl | 
 | 	ld	b, (hl) | 
 | 	inc	hl | 
 | 	ld	e, (hl) | 
 | 	inc	hl | 
 | 	ld	d, (hl) | 
 | 	inc	hl | 
 | 	ld	a, (hl) | 
 | 	inc	hl | 
 | 	ld	h, (hl) | 
 | 	ld	l, a | 
 | 	pop	af | 
 | 	ret | 
 |   __endasm; | 
 | } | 
 | #else | 
 | /* saves all state.except PC and SP */ | 
 | static void | 
 | save_cpu_state() __naked | 
 | { | 
 |   __asm | 
 | 	ld	(#_state + R_HL), hl | 
 | 	ld	(#_state + R_DE), de | 
 | 	ld	(#_state + R_BC), bc | 
 | 	push	af | 
 | 	pop	hl | 
 | 	ld	(#_state + R_AF), hl | 
 | 	ld	a, r	;R is increased by 7 or by 8 if called via RST | 
 | 	ld	l, a | 
 | 	sub	a, #7 | 
 | 	xor	a, l | 
 | 	and	a, #0x7f | 
 | 	xor	a, l | 
 | #ifdef __SDCC_ez80_adl | 
 | 	ld	hl, i | 
 | 	ex	de, hl | 
 | 	ld	hl, #_state + R_IR | 
 | 	ld	(hl), a | 
 | 	inc	hl | 
 | 	ld	(hl), e | 
 | 	inc	hl | 
 | 	ld	(hl), d | 
 | 	ld	a, MB | 
 | 	ld	(#_state + R_AF+2), a | 
 | #else | 
 | 	ld	l, a | 
 | 	ld	a, i | 
 | 	ld	h, a | 
 | 	ld	(#_state + R_IR), hl | 
 | #endif /* __SDCC_ez80_adl */ | 
 | 	ld	(#_state + R_IX), ix | 
 | 	ld	(#_state + R_IY), iy | 
 | 	ex	af, af'	;' | 
 | 	exx | 
 | 	ld	(#_state + R_HL_), hl | 
 | 	ld	(#_state + R_DE_), de | 
 | 	ld	(#_state + R_BC_), bc | 
 | 	push	af | 
 | 	pop	hl | 
 | 	ld	(#_state + R_AF_), hl | 
 | 	ret | 
 |   __endasm; | 
 | } | 
 |  | 
 | /* restore CPU state and continue execution */ | 
 | static void | 
 | rest_cpu_state() __naked | 
 | { | 
 |   __asm | 
 | #ifdef DBG_USE_TRAMPOLINE | 
 | 	ld	sp, _stack + DBG_STACK_SIZE | 
 | 	ld	hl, (#_state + R_PC) | 
 | 	push	hl	/* resume address */ | 
 | #ifdef __SDCC_ez80_adl | 
 | 	ld	hl, 0xc30000 ; use 0xc34000 for jp.s | 
 | #else | 
 | 	ld	hl, 0xc300 | 
 | #endif | 
 | 	push	hl	/* JP opcode */ | 
 | #endif /* DBG_USE_TRAMPOLINE */ | 
 | 	ld	hl, (#_state + R_AF_) | 
 | 	push	hl | 
 | 	pop	af | 
 | 	ld	bc, (#_state + R_BC_) | 
 | 	ld	de, (#_state + R_DE_) | 
 | 	ld	hl, (#_state + R_HL_) | 
 | 	exx | 
 | 	ex	af, af'	;' | 
 | 	ld	iy, (#_state + R_IY) | 
 | 	ld	ix, (#_state + R_IX) | 
 | #ifdef __SDCC_ez80_adl | 
 | 	ld	a, (#_state + R_AF + 2) | 
 | 	ld	MB, a | 
 | 	ld	hl, (#_state + R_IR + 1) ;I register | 
 | 	ld	i, hl | 
 | 	ld	a, (#_state + R_IR + 0) ; R register | 
 | 	ld	l, a | 
 | #else | 
 | 	ld	hl, (#_state + R_IR) | 
 | 	ld	a, h | 
 | 	ld	i, a | 
 | 	ld	a, l | 
 | #endif /* __SDCC_ez80_adl */ | 
 | 	sub	a, #10	;number of M1 cycles after ld r,a | 
 | 	xor	a, l | 
 | 	and	a, #0x7f | 
 | 	xor	a, l | 
 | 	ld	r, a | 
 | 	ld	de, (#_state + R_DE) | 
 | 	ld	bc, (#_state + R_BC) | 
 | 	ld	hl, (#_state + R_AF) | 
 | 	push	hl | 
 | 	pop	af | 
 | 	ld	sp, (#_state + R_SP) | 
 | #ifndef DBG_USE_TRAMPOLINE | 
 | 	ld	hl, (#_state + R_PC) | 
 | 	push	hl | 
 | 	ld	hl, (#_state + R_HL) | 
 | 	DBG_RESUME | 
 | #else | 
 | 	ld	hl, (#_state + R_HL) | 
 | #ifdef __SDCC_ez80_adl | 
 | 	jp	#_stack + DBG_STACK_SIZE - 4 | 
 | #else | 
 | 	jp	#_stack + DBG_STACK_SIZE - 3 | 
 | #endif | 
 | #endif /* DBG_USE_TRAMPOLINE */ | 
 |   __endasm; | 
 | } | 
 | #endif /* __SDCC_gbz80 */ |