add CPU hooking functionality

can be used by a frontend to implement breakpoints or tracelogging
currently hooked: M68K executing/reading/writing, VDP reading/writing (only for MegaDrive)
This commit is contained in:
vadosnaprimer 2019-04-21 20:22:06 +03:00
parent 5f8156ef72
commit d1e7cd6bea
9 changed files with 199 additions and 17 deletions

80
core/cpuhook.h Normal file
View File

@ -0,0 +1,80 @@
/***************************************************************************************
* Genesis Plus GX
* CPU hooking support
*
* Copyright DrMefistO (2018-2019)
* Copyright feos (2019)
*
* Redistribution and use of this code or any derivative works are permitted
* provided that the following conditions are met:
*
* - Redistributions may not be sold, nor may they be used in a commercial
* product or activity.
*
* - Redistributions that are modified from the original source must include the
* complete source code, including the source code for all components used by a
* binary built from the modified sources. However, as a special exception, the
* source code distributed need not include anything that is normally distributed
* (in either source or binary form) with the major components (compiler, kernel,
* and so on) of the operating system on which the executable runs, unless that
* component itself accompanies the executable.
*
* - Redistributions 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.
*
* 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.
*
****************************************************************************************/
#ifndef _CPUHOOK_H_
#define _CPUHOOK_H_
typedef enum {
HOOK_ANY = (0 << 0),
// M68K
HOOK_M68K_E = (1 << 0),
HOOK_M68K_R = (1 << 1),
HOOK_M68K_W = (1 << 2),
HOOK_M68K_RW = HOOK_M68K_R | HOOK_M68K_W,
// VDP
HOOK_VRAM_R = (1 << 3),
HOOK_VRAM_W = (1 << 4),
HOOK_VRAM_RW = HOOK_VRAM_R | HOOK_VRAM_W,
HOOK_CRAM_R = (1 << 5),
HOOK_CRAM_W = (1 << 6),
HOOK_CRAM_RW = HOOK_CRAM_R | HOOK_CRAM_W,
HOOK_VSRAM_R = (1 << 7),
HOOK_VSRAM_W = (1 << 8),
HOOK_VSRAM_RW = HOOK_VSRAM_R | HOOK_VSRAM_W,
// Z80
HOOK_Z80_E = (1 << 9),
HOOK_Z80_R = (1 << 10),
HOOK_Z80_W = (1 << 11),
HOOK_Z80_RW = HOOK_Z80_R | HOOK_Z80_W,
// REGS
HOOK_VDP_REG = (1 << 12),
HOOK_M68K_REG = (1 << 13),
} hook_type_t;
void (*cpu_hook)(hook_type_t type, int width, unsigned int address, unsigned int value);
void set_cpu_hook(void(*hook)(hook_type_t type, int width, unsigned int address, unsigned int value));
#endif /* _CPUHOOK_H_ */

View File

@ -43,6 +43,7 @@
#include <setjmp.h>
#include "macros.h"
#include "cpuhook.h"
/* ======================================================================== */
/* ==================== ARCHITECTURE-DEPENDANT DEFINES ==================== */

View File

@ -72,6 +72,19 @@ static void default_set_fc_callback(unsigned int new_fc)
#endif
/* CPU hook is called on read, write, and execute, if HOOK_CPU is defined in
* a makefile or an MSVC project. Use set_cpu_hook() to assign a callback
* that can process the data provided by cpu_hook().
*/
void (*cpu_hook)(hook_type_t type, int width, unsigned int address, unsigned int value) = NULL;
void set_cpu_hook(void (*hook)(hook_type_t type, int width, unsigned int address, unsigned int value))
{
cpu_hook = hook;
}
/* ======================================================================== */
/* ================================= API ================================== */
/* ======================================================================== */
@ -289,6 +302,12 @@ void m68k_run(unsigned int cycles)
/* Set the address space for reads */
m68ki_use_data_space() /* auto-disable (see m68kcpu.h) */
#ifdef HOOK_CPU
/* Trigger execution hook */
if (cpu_hook)
cpu_hook(HOOK_M68K_E, 0, REG_PC, 0);
#endif
/* Decode next instruction */
REG_IR = m68ki_read_imm_16();

View File

@ -849,36 +849,60 @@ INLINE uint m68ki_read_imm_32(void)
*/
INLINE uint m68ki_read_8(uint address)
{
cpu_memory_map *temp = &m68ki_cpu.memory_map[((address)>>16)&0xff];;
cpu_memory_map *temp = &m68ki_cpu.memory_map[((address)>>16)&0xff];
uint val;
m68ki_set_fc(FLAG_S | m68ki_get_address_space()) /* auto-disable (see m68kcpu.h) */
if (temp->read8) return (*temp->read8)(ADDRESS_68K(address));
else return READ_BYTE(temp->base, (address) & 0xffff);
if (temp->read8) val = (*temp->read8)(ADDRESS_68K(address));
else val = READ_BYTE(temp->base, (address) & 0xffff);
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_M68K_R, 1, address, val);
#endif
return val;
}
INLINE uint m68ki_read_16(uint address)
{
cpu_memory_map *temp;
uint val;
m68ki_set_fc(FLAG_S | m68ki_get_address_space()) /* auto-disable (see m68kcpu.h) */
m68ki_check_address_error(address, MODE_READ, FLAG_S | m68ki_get_address_space()) /* auto-disable (see m68kcpu.h) */
temp = &m68ki_cpu.memory_map[((address)>>16)&0xff];
if (temp->read16) return (*temp->read16)(ADDRESS_68K(address));
else return *(uint16 *)(temp->base + ((address) & 0xffff));
if (temp->read16) val = (*temp->read16)(ADDRESS_68K(address));
else val = *(uint16 *)(temp->base + ((address) & 0xffff));
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_M68K_R, 2, address, val);
#endif
return val;
}
INLINE uint m68ki_read_32(uint address)
{
cpu_memory_map *temp;
uint val;
m68ki_set_fc(FLAG_S | m68ki_get_address_space()) /* auto-disable (see m68kcpu.h) */
m68ki_check_address_error(address, MODE_READ, FLAG_S | m68ki_get_address_space()) /* auto-disable (see m68kcpu.h) */
temp = &m68ki_cpu.memory_map[((address)>>16)&0xff];
if (temp->read16) return ((*temp->read16)(ADDRESS_68K(address)) << 16) | ((*temp->read16)(ADDRESS_68K(address + 2)));
else return m68k_read_immediate_32(address);
if (temp->read16) val = ((*temp->read16)(ADDRESS_68K(address)) << 16) | ((*temp->read16)(ADDRESS_68K(address + 2)));
else val = m68k_read_immediate_32(address);
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_M68K_R, 4, address, val);
#endif
return val;
}
INLINE void m68ki_write_8(uint address, uint value)
@ -887,6 +911,11 @@ INLINE void m68ki_write_8(uint address, uint value)
m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_DATA) /* auto-disable (see m68kcpu.h) */
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_M68K_W, 1, address, value);
#endif
temp = &m68ki_cpu.memory_map[((address)>>16)&0xff];
if (temp->write8) (*temp->write8)(ADDRESS_68K(address),value);
else WRITE_BYTE(temp->base, (address) & 0xffff, value);
@ -899,6 +928,11 @@ INLINE void m68ki_write_16(uint address, uint value)
m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_DATA) /* auto-disable (see m68kcpu.h) */
m68ki_check_address_error(address, MODE_WRITE, FLAG_S | FUNCTION_CODE_USER_DATA); /* auto-disable (see m68kcpu.h) */
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_M68K_W, 2, address, value);
#endif
temp = &m68ki_cpu.memory_map[((address)>>16)&0xff];
if (temp->write16) (*temp->write16)(ADDRESS_68K(address),value);
else *(uint16 *)(temp->base + ((address) & 0xffff)) = value;
@ -911,6 +945,11 @@ INLINE void m68ki_write_32(uint address, uint value)
m68ki_set_fc(FLAG_S | FUNCTION_CODE_USER_DATA) /* auto-disable (see m68kcpu.h) */
m68ki_check_address_error(address, MODE_WRITE, FLAG_S | FUNCTION_CODE_USER_DATA) /* auto-disable (see m68kcpu.h) */
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_M68K_W, 4, address, value);
#endif
temp = &m68ki_cpu.memory_map[((address)>>16)&0xff];
if (temp->write16) (*temp->write16)(ADDRESS_68K(address),value>>16);
else *(uint16 *)(temp->base + ((address) & 0xffff)) = value >> 16;

View File

@ -2167,6 +2167,11 @@ static void vdp_bus_w(unsigned int data)
MARK_BG_DIRTY (index);
}
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_VRAM_W, 2, addr, data);
#endif
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] VRAM 0x%x write -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, addr, data, m68k_get_reg(M68K_REG_PC));
#endif
@ -2210,6 +2215,12 @@ static void vdp_bus_w(unsigned int data)
remap_line(v_counter);
}
}
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_CRAM_W, 2, addr, data);
#endif
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] CRAM 0x%x write -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, addr, data, m68k_get_reg(M68K_REG_PC));
#endif
@ -2230,6 +2241,12 @@ static void vdp_bus_w(unsigned int data)
render_line(v_counter);
}
}
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_VSRAM_W, 2, addr, data);
#endif
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] VSRAM 0x%x write -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, addr, data, m68k_get_reg(M68K_REG_PC));
#endif
@ -2434,6 +2451,11 @@ static unsigned int vdp_68k_data_r_m5(void)
/* read two bytes from VRAM */
data = *(uint16 *)&vram[addr & 0xFFFE];
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_VRAM_R, 2, addr, data);
#endif
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] VRAM 0x%x read -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, addr, data, m68k_get_reg(M68K_REG_PC));
#endif
@ -2458,6 +2480,11 @@ static unsigned int vdp_68k_data_r_m5(void)
/* Unused bits are set using data from next available FIFO entry */
data |= (fifo[fifo_idx] & ~0x7FF);
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_VSRAM_R, 2, addr, data);
#endif
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] VSRAM 0x%x read -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, addr, data, m68k_get_reg(M68K_REG_PC));
#endif
@ -2475,6 +2502,11 @@ static unsigned int vdp_68k_data_r_m5(void)
/* Unused bits are set using data from next available FIFO entry */
data |= (fifo[fifo_idx] & ~0xEEE);
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_CRAM_R, 2, addr, data);
#endif
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] CRAM 0x%x read -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, addr, data, m68k_get_reg(M68K_REG_PC));
#endif
@ -2489,6 +2521,11 @@ static unsigned int vdp_68k_data_r_m5(void)
/* Unused bits are set using data from next available FIFO entry */
data |= (fifo[fifo_idx] & ~0xFF);
#ifdef HOOK_CPU
if (cpu_hook)
cpu_hook(HOOK_VRAM_R, 2, addr, data);
#endif
#ifdef LOGVDP
error("[%d(%d)][%d(%d)] 8-bit VRAM 0x%x read -> 0x%x (%x)\n", v_counter, (v_counter + (m68k.cycles - mcycles_vdp)/MCYCLES_PER_LINE)%lines_per_frame, m68k.cycles, m68k.cycles%MCYCLES_PER_LINE, addr, data, m68k_get_reg(M68K_REG_PC));
#endif

View File

@ -308,6 +308,7 @@
<ClInclude Include="..\..\core\cd_hw\libchdr\src\huffman.h" />
<ClInclude Include="..\..\core\cd_hw\pcm.h" />
<ClInclude Include="..\..\core\cd_hw\scd.h" />
<ClInclude Include="..\..\core\cpuhook.h" />
<ClInclude Include="..\..\core\genesis.h" />
<ClInclude Include="..\..\core\input_hw\activator.h" />
<ClInclude Include="..\..\core\input_hw\gamepad.h" />

View File

@ -749,5 +749,8 @@
<ClInclude Include="..\..\core\shared.h">
<Filter>core</Filter>
</ClInclude>
<ClInclude Include="..\..\core\cpuhook.h">
<Filter>core</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -18,12 +18,13 @@
# -D15BPP_RENDERING - configure for 15-bit pixels (RGB555)
# -D16BPP_RENDERING - configure for 16-bit pixels (RGB565)
# -D32BPP_RENDERING - configure for 32-bit pixels (RGB888)
# -DUSE_LIBCHDR : enable CHD file support
# -DUSE_LIBTREMOR : enable OGG file support for CD emulation using provided TREMOR library
# -DUSE_LIBVORBIS : enable OGG file support for CD emulation using external VORBIS library
# -DUSE_LIBCHDR : enable CHD file support
# -DUSE_LIBTREMOR : enable OGG file support for CD emulation using provided TREMOR library
# -DUSE_LIBVORBIS : enable OGG file support for CD emulation using external VORBIS library
# -DISABLE_MANY_OGG_OPEN_FILES : only have one OGG file opened at once to save RAM
# -DMAXROMSIZE: defines maximal size of ROM/SRAM buffer (also shared with CD hardware) */
# -DHAVE_YM3438_CORE: enable (configurable) support for Nuked cycle-accurate YM3438 core */
# -DMAXROMSIZE : defines maximal size of ROM/SRAM buffer (also shared with CD hardware)
# -DHAVE_YM3438_CORE : enable (configurable) support for Nuked cycle-accurate YM3438 core
# -DHOOK_CPU : enable CPU hooks
NAME = gen_sdl

View File

@ -18,12 +18,13 @@
# -D15BPP_RENDERING - configure for 15-bit pixels (RGB555)
# -D16BPP_RENDERING - configure for 16-bit pixels (RGB565)
# -D32BPP_RENDERING - configure for 32-bit pixels (RGB888)
# -DUSE_LIBCHDR : enable CHD file support
# -DUSE_LIBTREMOR : enable OGG file support for CD emulation using provided TREMOR library
# -DUSE_LIBVORBIS : enable OGG file support for CD emulation using external VORBIS library
# -DUSE_LIBCHDR : enable CHD file support
# -DUSE_LIBTREMOR : enable OGG file support for CD emulation using provided TREMOR library
# -DUSE_LIBVORBIS : enable OGG file support for CD emulation using external VORBIS library
# -DISABLE_MANY_OGG_OPEN_FILES : only have one OGG file opened at once to save RAM
# -DMAXROMSIZE: defines maximal size of ROM/SRAM buffer (also shared with CD hardware) */
# -DHAVE_YM3438_CORE: enable (configurable) support for Nuked cycle-accurate YM3438 core */
# -DMAXROMSIZE : defines maximal size of ROM/SRAM buffer (also shared with CD hardware)
# -DHAVE_YM3438_CORE : enable (configurable) support for Nuked cycle-accurate YM3438 core
# -DHOOK_CPU : enable CPU hooks
NAME = gen_sdl2