mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
8782 lines
208 KiB
C++
8782 lines
208 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* Debugger
|
|
*
|
|
* (c) 1995 Bernd Schmidt
|
|
* (c) 2006 Toni Wilen
|
|
*
|
|
*/
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
|
|
#include "options.h"
|
|
#include "uae.h"
|
|
#include "memory.h"
|
|
#include "custom.h"
|
|
#include "newcpu.h"
|
|
#include "cpu_prefetch.h"
|
|
#include "debug.h"
|
|
#include "disasm.h"
|
|
#include "debugmem.h"
|
|
#include "cia.h"
|
|
#include "xwin.h"
|
|
#include "identify.h"
|
|
#include "audio.h"
|
|
#include "sounddep/sound.h"
|
|
#include "disk.h"
|
|
#include "savestate.h"
|
|
#include "autoconf.h"
|
|
#include "akiko.h"
|
|
#include "inputdevice.h"
|
|
#include "crc32.h"
|
|
#include "cpummu.h"
|
|
#include "rommgr.h"
|
|
#include "inputrecord.h"
|
|
#include "calc.h"
|
|
#include "cpummu.h"
|
|
#include "cpummu030.h"
|
|
#include "ar.h"
|
|
#include "pci.h"
|
|
#include "ppc/ppcd.h"
|
|
#include "uae/io.h"
|
|
#include "uae/ppc.h"
|
|
#include "drawing.h"
|
|
#include "devices.h"
|
|
#include "blitter.h"
|
|
#include "ini.h"
|
|
#include "readcpu.h"
|
|
#include "cputbl.h"
|
|
#include "keybuf.h"
|
|
|
|
static int trace_mode;
|
|
static uae_u32 trace_param[3];
|
|
|
|
int debugger_active;
|
|
static int debug_rewind;
|
|
static int memwatch_triggered;
|
|
static int inside_debugger;
|
|
int debugger_used;
|
|
int memwatch_access_validator;
|
|
int memwatch_enabled;
|
|
int debugging;
|
|
int exception_debugging;
|
|
int no_trace_exceptions;
|
|
int debug_copper = 0;
|
|
int debug_dma = 0, debug_heatmap = 0;
|
|
int debug_sprite_mask = 0xff;
|
|
int debug_illegal = 0;
|
|
uae_u64 debug_illegal_mask;
|
|
static int debug_mmu_mode;
|
|
static bool break_if_enforcer;
|
|
static uaecptr debug_pc;
|
|
|
|
static int trace_cycles;
|
|
static int last_hpos1, last_hpos2;
|
|
static int last_vpos1, last_vpos2;
|
|
static int last_frame = -1;
|
|
static evt_t last_cycles1, last_cycles2;
|
|
|
|
static uaecptr processptr;
|
|
static uae_char *processname;
|
|
|
|
static uaecptr debug_copper_pc;
|
|
|
|
extern int audio_channel_mask;
|
|
extern int inputdevice_logging;
|
|
|
|
static void debug_cycles(int mode)
|
|
{
|
|
trace_cycles = mode;
|
|
last_cycles2 = get_cycles();
|
|
last_vpos2 = vpos;
|
|
last_hpos2 = current_hpos();
|
|
}
|
|
|
|
void deactivate_debugger (void)
|
|
{
|
|
inside_debugger = 0;
|
|
debugger_active = 0;
|
|
debugging = 0;
|
|
exception_debugging = 0;
|
|
processptr = 0;
|
|
xfree (processname);
|
|
processname = NULL;
|
|
debugmem_enable();
|
|
debug_pc = 0xffffffff;
|
|
keybuf_ignore_next_release();
|
|
}
|
|
|
|
void activate_debugger (void)
|
|
{
|
|
disasm_init();
|
|
if (isfullscreen() > 0)
|
|
return;
|
|
|
|
debugger_load_libraries();
|
|
|
|
debugger_used = 1;
|
|
inside_debugger = 1;
|
|
debug_pc = 0xffffffff;
|
|
trace_mode = 0;
|
|
if (debugger_active) {
|
|
// already in debugger but some break point triggered
|
|
// during disassembly etc..
|
|
return;
|
|
}
|
|
debug_cycles(1);
|
|
debugger_active = 1;
|
|
set_special (SPCFLAG_BRK);
|
|
debugging = 1;
|
|
mmu_triggered = 0;
|
|
debugmem_disable();
|
|
}
|
|
|
|
void activate_debugger_new(void)
|
|
{
|
|
activate_debugger();
|
|
debug_pc = M68K_GETPC;
|
|
}
|
|
|
|
void activate_debugger_new_pc(uaecptr pc, int len)
|
|
{
|
|
activate_debugger();
|
|
trace_mode = TRACE_RANGE_PC;
|
|
trace_param[0] = pc;
|
|
trace_param[1] = pc + len;
|
|
}
|
|
|
|
static void debug_continue(void)
|
|
{
|
|
set_special(SPCFLAG_BRK);
|
|
}
|
|
|
|
bool debug_enforcer(void)
|
|
{
|
|
if (!break_if_enforcer)
|
|
return false;
|
|
activate_debugger();
|
|
return true;
|
|
}
|
|
|
|
|
|
int firsthist = 0;
|
|
int lasthist = 0;
|
|
struct cpuhistory {
|
|
struct regstruct regs;
|
|
int fp, vpos, hpos;
|
|
};
|
|
static struct cpuhistory history[MAX_HIST];
|
|
|
|
static const TCHAR help[] = {
|
|
_T(" HELP for UAE Debugger\n")
|
|
_T(" -----------------------\n\n")
|
|
_T(" g [<address>] Start execution at the current address or <address>.\n")
|
|
_T(" c Dump state of the CIA, disk drives and custom registers.\n")
|
|
_T(" r Dump state of the CPU.\n")
|
|
_T(" r <reg> <value> Modify CPU registers (Dx,Ax,USP,ISP,VBR,...).\n")
|
|
_T(" rc[d] Show CPU instruction or data cache contents.\n")
|
|
_T(" m <address> [<lines>] Memory dump starting at <address>.\n")
|
|
_T(" a <address> Assembler.\n")
|
|
_T(" d <address> [<lines>] Disassembly starting at <address>.\n")
|
|
_T(" t [instructions] Step one or more instructions.\n")
|
|
_T(" tx Break when any exception.\n")
|
|
_T(" z Step through one instruction - useful for JSR, DBRA etc.\n")
|
|
_T(" f Step forward until PC in RAM (\"boot block finder\").\n")
|
|
_T(" f <address> [Nx] Add/remove breakpoint.\n")
|
|
_T(" fa <address> [<start>] [<end>]\n")
|
|
_T(" Find effective address <address>.\n")
|
|
_T(" fi Step forward until PC points to RTS, RTD or RTE.\n")
|
|
_T(" fi <opcode> [<w2>] [<w3>] Step forward until PC points to <opcode>.\n")
|
|
_T(" fp \"<name>\"/<addr> Step forward until process <name> or <addr> is active.\n")
|
|
_T(" fl List breakpoints.\n")
|
|
_T(" fd Remove all breakpoints.\n")
|
|
_T(" fs <lines to wait> | <vpos> <hpos> Wait n scanlines/position.\n")
|
|
_T(" fc <CCKs to wait> Wait n color clocks.\n")
|
|
_T(" fo <num> <reg> <oper> <val> [<mask> <val2>] Conditional register breakpoint [Nx] [Hx].\n")
|
|
_T(" reg=Dx,Ax,PC,USP,ISP,VBR,SR. oper:!=,==,<,>,>=,<=,-,!- (-=val to val2 range).\n")
|
|
_T(" f <addr1> <addr2> Step forward until <addr1> <= PC <= <addr2>.\n")
|
|
_T(" e[x] Dump contents of all custom registers, ea = AGA colors.\n")
|
|
_T(" i [<addr>] Dump contents of interrupt and trap vectors.\n")
|
|
_T(" il [<mask>] Exception breakpoint.\n")
|
|
_T(" o <0-2|addr> [<lines>]View memory as Copper instructions.\n")
|
|
_T(" od Enable/disable Copper vpos/hpos tracing.\n")
|
|
_T(" ot Copper single step trace.\n")
|
|
_T(" ob <addr> Copper breakpoint.\n")
|
|
_T(" H[H] <cnt> Show PC history (HH=full CPU info) <cnt> instructions.\n")
|
|
_T(" C <value> Search for values like energy or lifes in games.\n")
|
|
_T(" Cl List currently found trainer addresses.\n")
|
|
_T(" D[idxzs <[max diff]>] Deep trainer. i=new value must be larger, d=smaller,\n")
|
|
_T(" x = must be same, z = must be different, s = restart.\n")
|
|
_T(" W <addr> <values[.x] separated by space> Write into Amiga memory.\n")
|
|
_T(" W <addr> 'string' Write into Amiga memory.\n")
|
|
_T(" Wf <addr> <endaddr> <bytes or string like above>, fill memory.\n")
|
|
_T(" Wc <addr> <endaddr> <destaddr>, copy memory.\n")
|
|
_T(" w <num> <address> <length> <R/W/I> <F/C/L/N> [V<value>[.x]] (read/write/opcode) (freeze/mustchange/logonly/nobreak).\n")
|
|
_T(" Add/remove memory watchpoints.\n")
|
|
_T(" wd [<0-1>] Enable illegal access logger. 1 = enable break.\n")
|
|
_T(" L <file> <addr> [<n>] Load a block of Amiga memory.\n")
|
|
_T(" S <file> <addr> <n> Save a block of Amiga memory.\n")
|
|
_T(" s \"<string>\"/<values> [<addr>] [<length>]\n")
|
|
_T(" Search for string/bytes.\n")
|
|
_T(" T or Tt Show exec tasks and their PCs.\n")
|
|
_T(" Td,Tl,Tr,Tp,Ts,TS,Ti,TO,TM,Tf Show devs, libs, resources, ports, semaphores,\n")
|
|
_T(" residents, interrupts, doslist, memorylist, fsres.\n")
|
|
_T(" b Step to previous state capture position.\n")
|
|
_T(" M<a/b/s> <val> Enable or disable audio channels, bitplanes or sprites.\n")
|
|
_T(" sp <addr> [<addr2][<size>] Dump sprite information.\n")
|
|
_T(" di <mode> [<track>] Break on disk access. R=DMA read,W=write,RW=both,P=PIO.\n")
|
|
_T(" Also enables level 1 disk logging.\n")
|
|
_T(" did <log level> Enable disk logging.\n")
|
|
_T(" dj [<level bitmask>] Enable joystick/mouse input debugging.\n")
|
|
_T(" smc [<0-1>] Enable self-modifying code detector. 1 = enable break.\n")
|
|
_T(" dm Dump current address space map.\n")
|
|
_T(" v <vpos> [<hpos>] [<lines>]\n")
|
|
_T(" Show DMA data (accurate only in cycle-exact mode).\n")
|
|
_T(" v [-1 to -4] = enable visual DMA debugger.\n")
|
|
_T(" vh [<ratio> <lines>] \"Heat map\"\n")
|
|
_T(" I <custom event> Send custom event string\n")
|
|
_T(" ?<value> Hex ($ and 0x)/Bin (%)/Dec (!) converter and calculator.\n")
|
|
#ifdef _WIN32
|
|
_T(" x Close debugger.\n")
|
|
_T(" xx Switch between console and GUI debugger.\n")
|
|
_T(" mg <address> Memory dump starting at <address> in GUI.\n")
|
|
_T(" dg <address> Disassembly starting at <address> in GUI.\n")
|
|
#endif
|
|
_T(" q Quit the emulator. You don't want to use this command.\n\n")
|
|
};
|
|
|
|
void debug_help (void)
|
|
{
|
|
console_out (help);
|
|
}
|
|
|
|
|
|
struct mw_acc {
|
|
uae_u32 mask;
|
|
const TCHAR *name;
|
|
};
|
|
|
|
static const struct mw_acc memwatch_access_masks[] =
|
|
{
|
|
{ MW_MASK_ALL, _T("ALL") },
|
|
{ MW_MASK_NONE, _T("NONE") },
|
|
{ MW_MASK_ALL & ~(MW_MASK_CPU_I | MW_MASK_CPU_D_R | MW_MASK_CPU_D_W), _T("DMA") },
|
|
|
|
{ MW_MASK_BLITTER_A | MW_MASK_BLITTER_B | MW_MASK_BLITTER_C | MW_MASK_BLITTER_D_N | MW_MASK_BLITTER_D_L | MW_MASK_BLITTER_D_F, _T("BLT") },
|
|
{ MW_MASK_BLITTER_D_N | MW_MASK_BLITTER_D_L | MW_MASK_BLITTER_D_F, _T("BLTD") },
|
|
|
|
{ MW_MASK_AUDIO_0 | MW_MASK_AUDIO_1 | MW_MASK_AUDIO_2 | MW_MASK_AUDIO_3, _T("AUD") },
|
|
|
|
{ MW_MASK_BPL_0 | MW_MASK_BPL_1 | MW_MASK_BPL_2 | MW_MASK_BPL_3 |
|
|
MW_MASK_BPL_4 | MW_MASK_BPL_5 | MW_MASK_BPL_6 | MW_MASK_BPL_7, _T("BPL") },
|
|
|
|
{ MW_MASK_SPR_0 | MW_MASK_SPR_1 | MW_MASK_SPR_2 | MW_MASK_SPR_3 |
|
|
MW_MASK_SPR_4 | MW_MASK_SPR_5 | MW_MASK_SPR_6 | MW_MASK_SPR_7, _T("SPR") },
|
|
|
|
{ MW_MASK_CPU_I | MW_MASK_CPU_D_R | MW_MASK_CPU_D_W, _T("CPU") },
|
|
{ MW_MASK_CPU_D_R | MW_MASK_CPU_D_W, _T("CPUD") },
|
|
{ MW_MASK_CPU_I, _T("CPUI") },
|
|
{ MW_MASK_CPU_D_R, _T("CPUDR") },
|
|
{ MW_MASK_CPU_D_W, _T("CPUDW") },
|
|
|
|
{ MW_MASK_COPPER, _T("COP") },
|
|
|
|
{ MW_MASK_BLITTER_A, _T("BLTA") },
|
|
{ MW_MASK_BLITTER_B, _T("BLTB") },
|
|
{ MW_MASK_BLITTER_C, _T("BLTC") },
|
|
{ MW_MASK_BLITTER_D_N, _T("BLTDN") },
|
|
{ MW_MASK_BLITTER_D_L, _T("BLTDL") },
|
|
{ MW_MASK_BLITTER_D_F, _T("BLTDF") },
|
|
|
|
{ MW_MASK_DISK, _T("DSK") },
|
|
|
|
{ MW_MASK_AUDIO_0, _T("AUD0") },
|
|
{ MW_MASK_AUDIO_1, _T("AUD1") },
|
|
{ MW_MASK_AUDIO_2, _T("AUD2") },
|
|
{ MW_MASK_AUDIO_3, _T("AUD3") },
|
|
|
|
{ MW_MASK_BPL_0, _T("BPL0") },
|
|
{ MW_MASK_BPL_1, _T("BPL1") },
|
|
{ MW_MASK_BPL_2, _T("BPL2") },
|
|
{ MW_MASK_BPL_3, _T("BPL3") },
|
|
{ MW_MASK_BPL_4, _T("BPL4") },
|
|
{ MW_MASK_BPL_5, _T("BPL5") },
|
|
{ MW_MASK_BPL_6, _T("BPL6") },
|
|
{ MW_MASK_BPL_7, _T("BPL7") },
|
|
|
|
{ MW_MASK_SPR_0, _T("SPR0") },
|
|
{ MW_MASK_SPR_1, _T("SPR1") },
|
|
{ MW_MASK_SPR_2, _T("SPR2") },
|
|
{ MW_MASK_SPR_3, _T("SPR3") },
|
|
{ MW_MASK_SPR_4, _T("SPR4") },
|
|
{ MW_MASK_SPR_5, _T("SPR5") },
|
|
{ MW_MASK_SPR_6, _T("SPR6") },
|
|
{ MW_MASK_SPR_7, _T("SPR7") },
|
|
|
|
{ 0, NULL },
|
|
};
|
|
|
|
static void mw_help(void)
|
|
{
|
|
for (int i = 0; memwatch_access_masks[i].mask; i++) {
|
|
console_out_f(_T("%s "), memwatch_access_masks[i].name);
|
|
}
|
|
console_out_f(_T("\n"));
|
|
}
|
|
|
|
static int debug_linecounter;
|
|
#define MAX_LINECOUNTER 1000
|
|
|
|
static int debug_out (const TCHAR *format, ...)
|
|
{
|
|
va_list parms;
|
|
TCHAR buffer[4000];
|
|
|
|
va_start (parms, format);
|
|
_vsntprintf (buffer, 4000 - 1, format, parms);
|
|
va_end (parms);
|
|
|
|
console_out (buffer);
|
|
if (debug_linecounter < MAX_LINECOUNTER)
|
|
debug_linecounter++;
|
|
if (debug_linecounter >= MAX_LINECOUNTER)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
uae_u32 get_byte_debug (uaecptr addr)
|
|
{
|
|
uae_u32 v = 0xff;
|
|
if (debug_mmu_mode) {
|
|
flagtype olds = regs.s;
|
|
regs.s = (debug_mmu_mode & 4) != 0;
|
|
TRY(p) {
|
|
if (currprefs.mmu_model == 68030) {
|
|
v = mmu030_get_generic (addr, debug_mmu_mode, sz_byte, MMU030_SSW_SIZE_B);
|
|
} else {
|
|
if (debug_mmu_mode & 1) {
|
|
bool odd = (addr & 1) != 0;
|
|
addr &= ~1;
|
|
v = mmu_get_iword(addr, sz_byte);
|
|
if (!odd)
|
|
v >>= 8;
|
|
else
|
|
v &= 0xff;
|
|
} else {
|
|
v = mmu_get_user_byte (addr, regs.s != 0, false, sz_byte, false);
|
|
}
|
|
}
|
|
} CATCH(p) {
|
|
} ENDTRY
|
|
regs.s = olds;
|
|
} else {
|
|
v = get_byte (addr);
|
|
}
|
|
return v;
|
|
}
|
|
uae_u32 get_word_debug (uaecptr addr)
|
|
{
|
|
uae_u32 v = 0xffff;
|
|
if (debug_mmu_mode) {
|
|
flagtype olds = regs.s;
|
|
regs.s = (debug_mmu_mode & 4) != 0;
|
|
TRY(p) {
|
|
if (currprefs.mmu_model == 68030) {
|
|
v = mmu030_get_generic (addr, debug_mmu_mode, sz_word, MMU030_SSW_SIZE_W);
|
|
} else {
|
|
if (debug_mmu_mode & 1) {
|
|
v = mmu_get_iword(addr, sz_word);
|
|
} else {
|
|
v = mmu_get_user_word (addr, regs.s != 0, false, sz_word, false);
|
|
}
|
|
}
|
|
} CATCH(p) {
|
|
} ENDTRY
|
|
regs.s = olds;
|
|
} else {
|
|
v = get_word (addr);
|
|
}
|
|
return v;
|
|
}
|
|
uae_u32 get_long_debug (uaecptr addr)
|
|
{
|
|
uae_u32 v = 0xffffffff;
|
|
if (debug_mmu_mode) {
|
|
flagtype olds = regs.s;
|
|
regs.s = (debug_mmu_mode & 4) != 0;
|
|
TRY(p) {
|
|
if (currprefs.mmu_model == 68030) {
|
|
v = mmu030_get_generic (addr, debug_mmu_mode, sz_long, MMU030_SSW_SIZE_L);
|
|
} else {
|
|
if (debug_mmu_mode & 1) {
|
|
v = mmu_get_ilong(addr, sz_long);
|
|
} else {
|
|
v = mmu_get_user_long (addr, regs.s != 0, false, sz_long, false);
|
|
}
|
|
}
|
|
} CATCH(p) {
|
|
} ENDTRY
|
|
regs.s = olds;
|
|
} else {
|
|
v = get_long (addr);
|
|
}
|
|
return v;
|
|
}
|
|
uae_u32 get_iword_debug (uaecptr addr)
|
|
{
|
|
if (debug_mmu_mode) {
|
|
return get_word_debug (addr);
|
|
} else {
|
|
if (valid_address (addr, 2))
|
|
return get_word (addr);
|
|
return 0xffff;
|
|
}
|
|
}
|
|
uae_u32 get_ilong_debug (uaecptr addr)
|
|
{
|
|
if (debug_mmu_mode) {
|
|
return get_long_debug (addr);
|
|
} else {
|
|
if (valid_address (addr, 4))
|
|
return get_long (addr);
|
|
return 0xffffffff;
|
|
}
|
|
}
|
|
uae_u8 *get_real_address_debug(uaecptr addr)
|
|
{
|
|
if (debug_mmu_mode) {
|
|
flagtype olds = regs.s;
|
|
TRY(p) {
|
|
if (currprefs.mmu_model >= 68040)
|
|
addr = mmu_translate(addr, 0, regs.s != 0, (debug_mmu_mode & 1), false, 0);
|
|
else
|
|
addr = mmu030_translate(addr, regs.s != 0, (debug_mmu_mode & 1), false);
|
|
} CATCH(p) {
|
|
} ENDTRY
|
|
}
|
|
return get_real_address(addr);
|
|
}
|
|
|
|
int debug_safe_addr (uaecptr addr, int size)
|
|
{
|
|
if (debug_mmu_mode) {
|
|
flagtype olds = regs.s;
|
|
regs.s = (debug_mmu_mode & 4) != 0;
|
|
TRY(p) {
|
|
if (currprefs.mmu_model >= 68040)
|
|
addr = mmu_translate (addr, 0, regs.s != 0, (debug_mmu_mode & 1), false, size);
|
|
else
|
|
addr = mmu030_translate (addr, regs.s != 0, (debug_mmu_mode & 1), false);
|
|
} CATCH(p) {
|
|
STOPTRY;
|
|
return 0;
|
|
} ENDTRY
|
|
regs.s = olds;
|
|
}
|
|
addrbank *ab = &get_mem_bank (addr);
|
|
if (!ab)
|
|
return 0;
|
|
if (ab->flags & ABFLAG_SAFE)
|
|
return 1;
|
|
if (!ab->check (addr, size))
|
|
return 0;
|
|
if (ab->flags & (ABFLAG_RAM | ABFLAG_ROM | ABFLAG_ROMIN | ABFLAG_SAFE))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static bool iscancel (int counter)
|
|
{
|
|
static int cnt;
|
|
|
|
cnt++;
|
|
if (cnt < counter)
|
|
return false;
|
|
cnt = 0;
|
|
if (!console_isch ())
|
|
return false;
|
|
console_getch ();
|
|
return true;
|
|
}
|
|
|
|
static bool isoperator(TCHAR **cp)
|
|
{
|
|
TCHAR c = _totupper(**cp);
|
|
TCHAR c1 = _totupper((*cp)[1]);
|
|
return c == '+' || c == '-' || c == '/' || c == '*' || c == '(' || c == ')' ||
|
|
c == '|' || c == '&' || c == '^' || c == '=' || c == '>' || c == '<' ||
|
|
(c == 'R' && (c1 == 'L' || c1 == 'W' || c1 == 'B'));
|
|
}
|
|
|
|
static void ignore_ws (TCHAR **c)
|
|
{
|
|
while (**c && _istspace(**c))
|
|
(*c)++;
|
|
}
|
|
static TCHAR peekchar (TCHAR **c)
|
|
{
|
|
return **c;
|
|
}
|
|
static TCHAR readchar (TCHAR **c)
|
|
{
|
|
TCHAR cc = **c;
|
|
(*c)++;
|
|
return cc;
|
|
}
|
|
static TCHAR next_char(TCHAR **c)
|
|
{
|
|
ignore_ws (c);
|
|
return *(*c)++;
|
|
}
|
|
static TCHAR next_char2(TCHAR **c)
|
|
{
|
|
return *(*c)++;
|
|
}
|
|
static TCHAR peek_next_char (TCHAR **c)
|
|
{
|
|
TCHAR *pc = *c;
|
|
return pc[1];
|
|
}
|
|
static int more_params(TCHAR **c)
|
|
{
|
|
ignore_ws(c);
|
|
return (**c) != 0;
|
|
}
|
|
static int more_params2(TCHAR **c)
|
|
{
|
|
return (**c) != 0;
|
|
}
|
|
|
|
static uae_u32 readint(TCHAR **c, bool *err);
|
|
static uae_u32 readbin(TCHAR **c, bool *err);
|
|
static uae_u32 readhex(TCHAR **c, bool *err);
|
|
|
|
static const TCHAR *debugoper[] = {
|
|
_T("=="),
|
|
_T("!="),
|
|
_T("<="),
|
|
_T(">="),
|
|
_T("<"),
|
|
_T(">"),
|
|
_T("-"),
|
|
_T("!-"),
|
|
NULL
|
|
};
|
|
|
|
static int getoperidx(TCHAR **c, bool *opersigned)
|
|
{
|
|
int i;
|
|
TCHAR *p = *c;
|
|
TCHAR tmp[10];
|
|
int extra = 0;
|
|
|
|
i = 0;
|
|
while (p[i]) {
|
|
tmp[i] = _totupper(p[i]);
|
|
if (i >= sizeof(tmp) / sizeof(TCHAR) - 1)
|
|
break;
|
|
i++;
|
|
}
|
|
tmp[i] = 0;
|
|
if (!_tcsncmp(tmp, _T("!="), 2)) {
|
|
(*c) += 2;
|
|
return BREAKPOINT_CMP_NEQUAL;
|
|
} else if (!_tcsncmp(tmp, _T("=="), 2)) {
|
|
(*c) += 2;
|
|
return BREAKPOINT_CMP_EQUAL;
|
|
} else if (!_tcsncmp(tmp, _T(">="), 2)) {
|
|
(*c) += 2;
|
|
return BREAKPOINT_CMP_LARGER_EQUAL;
|
|
} else if (!_tcsncmp(tmp, _T("<="), 2)) {
|
|
(*c) += 2;
|
|
return BREAKPOINT_CMP_SMALLER_EQUAL;
|
|
} else if (!_tcsncmp(tmp, _T(">"), 1)) {
|
|
(*c) += 1;
|
|
return BREAKPOINT_CMP_LARGER;
|
|
} else if (!_tcsncmp(tmp, _T("<"), 1)) {
|
|
(*c) += 1;
|
|
return BREAKPOINT_CMP_SMALLER;
|
|
} else if (!_tcsncmp(tmp, _T("-"), 1)) {
|
|
(*c) += 1;
|
|
return BREAKPOINT_CMP_RANGE;
|
|
} else if (!_tcsncmp(tmp, _T("!-"), 2)) {
|
|
(*c) += 2;
|
|
return BREAKPOINT_CMP_NRANGE;
|
|
}
|
|
*opersigned = false;
|
|
if (**c == 's') {
|
|
(*c)++;
|
|
*opersigned = true;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static const TCHAR *debugregs[] = {
|
|
_T("D0"),
|
|
_T("D1"),
|
|
_T("D2"),
|
|
_T("D3"),
|
|
_T("D4"),
|
|
_T("D5"),
|
|
_T("D6"),
|
|
_T("D7"),
|
|
_T("A0"),
|
|
_T("A1"),
|
|
_T("A2"),
|
|
_T("A3"),
|
|
_T("A4"),
|
|
_T("A5"),
|
|
_T("A6"),
|
|
_T("A7"),
|
|
_T("PC"),
|
|
_T("USP"),
|
|
_T("MSP"),
|
|
_T("ISP"),
|
|
_T("VBR"),
|
|
_T("SR"),
|
|
_T("CCR"),
|
|
_T("CACR"),
|
|
_T("CAAR"),
|
|
_T("SFC"),
|
|
_T("DFC"),
|
|
_T("TC"),
|
|
_T("ITT0"),
|
|
_T("ITT1"),
|
|
_T("DTT0"),
|
|
_T("DTT1"),
|
|
_T("BUSC"),
|
|
_T("PCR"),
|
|
_T("FPIAR"),
|
|
_T("FPCR"),
|
|
_T("FPSR"),
|
|
NULL
|
|
};
|
|
|
|
int getregidx(TCHAR **c)
|
|
{
|
|
int i;
|
|
TCHAR *p = *c;
|
|
TCHAR tmp[10];
|
|
int extra = 0;
|
|
|
|
i = 0;
|
|
while (p[i]) {
|
|
tmp[i] = _totupper(p[i]);
|
|
if (i >= sizeof(tmp) / sizeof(TCHAR) - 1)
|
|
break;
|
|
i++;
|
|
}
|
|
tmp[i] = 0;
|
|
for (int i = 0; debugregs[i]; i++) {
|
|
if (!_tcsncmp(tmp, debugregs[i], _tcslen(debugregs[i]))) {
|
|
(*c) += _tcslen(debugregs[i]);
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
uae_u32 returnregx(int regid)
|
|
{
|
|
if (regid < BREAKPOINT_REG_PC)
|
|
return regs.regs[regid];
|
|
switch(regid)
|
|
{
|
|
case BREAKPOINT_REG_PC:
|
|
return M68K_GETPC;
|
|
case BREAKPOINT_REG_USP:
|
|
return regs.usp;
|
|
case BREAKPOINT_REG_MSP:
|
|
return regs.msp;
|
|
case BREAKPOINT_REG_ISP:
|
|
return regs.isp;
|
|
case BREAKPOINT_REG_VBR:
|
|
return regs.vbr;
|
|
case BREAKPOINT_REG_SR:
|
|
MakeSR();
|
|
return regs.sr;
|
|
case BREAKPOINT_REG_CCR:
|
|
MakeSR();
|
|
return regs.sr & 31;
|
|
case BREAKPOINT_REG_CACR:
|
|
return regs.cacr;
|
|
case BREAKPOINT_REG_CAAR:
|
|
return regs.caar;
|
|
case BREAKPOINT_REG_SFC:
|
|
return regs.sfc;
|
|
case BREAKPOINT_REG_DFC:
|
|
return regs.dfc;
|
|
case BREAKPOINT_REG_TC:
|
|
if (currprefs.cpu_model == 68030)
|
|
return tc_030;
|
|
return regs.tcr;
|
|
case BREAKPOINT_REG_ITT0:
|
|
if (currprefs.cpu_model == 68030)
|
|
return tt0_030;
|
|
return regs.itt0;
|
|
case BREAKPOINT_REG_ITT1:
|
|
if (currprefs.cpu_model == 68030)
|
|
return tt1_030;
|
|
return regs.itt1;
|
|
case BREAKPOINT_REG_DTT0:
|
|
return regs.dtt0;
|
|
case BREAKPOINT_REG_DTT1:
|
|
return regs.dtt1;
|
|
case BREAKPOINT_REG_BUSC:
|
|
return regs.buscr;
|
|
case BREAKPOINT_REG_PCR:
|
|
return regs.pcr;
|
|
case BREAKPOINT_REG_FPIAR:
|
|
return regs.fpiar;
|
|
case BREAKPOINT_REG_FPCR:
|
|
return regs.fpcr;
|
|
case BREAKPOINT_REG_FPSR:
|
|
return regs.fpsr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int readregx(TCHAR **c, uae_u32 *valp)
|
|
{
|
|
int idx;
|
|
uae_u32 addr;
|
|
TCHAR *old = *c;
|
|
|
|
addr = 0;
|
|
if (_totupper(**c) == 'R') {
|
|
(*c)++;
|
|
}
|
|
idx = getregidx(c);
|
|
if (idx < 0) {
|
|
*c = old;
|
|
return 0;
|
|
}
|
|
addr = returnregx(idx);
|
|
*valp = addr;
|
|
return 1;
|
|
}
|
|
|
|
static bool checkisneg(TCHAR **c)
|
|
{
|
|
TCHAR nc = peekchar(c);
|
|
if (nc == '-') {
|
|
(*c)++;
|
|
return true;
|
|
} else if (nc == '+') {
|
|
(*c)++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool readbinx (TCHAR **c, uae_u32 *valp)
|
|
{
|
|
uae_u32 val = 0;
|
|
bool first = true;
|
|
bool negative = false;
|
|
|
|
ignore_ws (c);
|
|
negative = checkisneg(c);
|
|
for (;;) {
|
|
TCHAR nc = **c;
|
|
if (nc != '1' && nc != '0' && nc != '`') {
|
|
if (first)
|
|
return false;
|
|
break;
|
|
}
|
|
first = false;
|
|
(*c)++;
|
|
if (nc != '`') {
|
|
val <<= 1;
|
|
if (nc == '1') {
|
|
val |= 1;
|
|
}
|
|
}
|
|
}
|
|
*valp = val * (negative ? -1 : 1);
|
|
return true;
|
|
}
|
|
|
|
static bool readhexx (TCHAR **c, uae_u32 *valp)
|
|
{
|
|
uae_u32 val = 0;
|
|
TCHAR nc;
|
|
bool negative = false;
|
|
|
|
ignore_ws(c);
|
|
negative = checkisneg(c);
|
|
if (!isxdigit(peekchar(c)))
|
|
return false;
|
|
while (isxdigit (nc = **c)) {
|
|
(*c)++;
|
|
val *= 16;
|
|
nc = _totupper (nc);
|
|
if (isdigit (nc)) {
|
|
val += nc - '0';
|
|
} else {
|
|
val += nc - 'A' + 10;
|
|
}
|
|
}
|
|
*valp = val * (negative ? -1 : 1);
|
|
return true;
|
|
}
|
|
|
|
static bool readintx (TCHAR **c, uae_u32 *valp)
|
|
{
|
|
uae_u32 val = 0;
|
|
TCHAR nc;
|
|
int negative = 0;
|
|
|
|
ignore_ws (c);
|
|
negative = checkisneg(c);
|
|
if (!isdigit (peekchar (c)))
|
|
return false;
|
|
while (isdigit (nc = **c)) {
|
|
(*c)++;
|
|
val *= 10;
|
|
val += nc - '0';
|
|
}
|
|
*valp = val * (negative ? -1 : 1);
|
|
return true;
|
|
}
|
|
|
|
static int checkvaltype2 (TCHAR **c, uae_u32 *val, TCHAR def)
|
|
{
|
|
TCHAR nc;
|
|
|
|
ignore_ws (c);
|
|
nc = _totupper (**c);
|
|
if (nc == '!') {
|
|
(*c)++;
|
|
return readintx (c, val) ? 1 : 0;
|
|
}
|
|
if (nc == '$') {
|
|
(*c)++;
|
|
return readhexx (c, val) ? 1 : 0;
|
|
}
|
|
if (nc == '0' && _totupper ((*c)[1]) == 'X') {
|
|
(*c)+= 2;
|
|
return readhexx (c, val) ? 1 : 0;
|
|
}
|
|
if (nc == '%') {
|
|
(*c)++;
|
|
return readbinx (c, val) ? 1: 0;
|
|
}
|
|
if (nc >= 'A' && nc <= 'Z' && nc != 'A' && nc != 'D') {
|
|
if (readregx (c, val))
|
|
return 1;
|
|
}
|
|
TCHAR name[256];
|
|
name[0] = 0;
|
|
for (int i = 0; i < sizeof name / sizeof(TCHAR) - 1; i++) {
|
|
nc = (*c)[i];
|
|
if (nc == 0 || nc == ' ')
|
|
break;
|
|
name[i] = nc;
|
|
name[i + 1] = 0;
|
|
}
|
|
if (name[0]) {
|
|
TCHAR *np = name;
|
|
if (*np == '#')
|
|
np++;
|
|
if (debugmem_get_symbol_value(np, val)) {
|
|
(*c) += _tcslen(name);
|
|
return 1;
|
|
}
|
|
}
|
|
if (def == '!') {
|
|
return readintx (c, val) ? -1 : 0;
|
|
} else if (def == '$') {
|
|
return readhexx (c, val) ? -1 : 0;
|
|
} else if (def == '%') {
|
|
return readbinx (c, val) ? -1 : 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int readsize (int val, TCHAR **c)
|
|
{
|
|
TCHAR cc = _totupper (readchar(c));
|
|
if (cc == 'B')
|
|
return 1;
|
|
if (cc == 'W')
|
|
return 2;
|
|
if (cc == '3')
|
|
return 3;
|
|
if (cc == 'L')
|
|
return 4;
|
|
return 0;
|
|
}
|
|
|
|
static int checkvaltype(TCHAR **cp, uae_u32 *val, int *size, TCHAR def)
|
|
{
|
|
TCHAR form[256], *p;
|
|
bool gotop = false;
|
|
bool copyrest = false;
|
|
double out;
|
|
|
|
form[0] = 0;
|
|
if (size)
|
|
*size = 0;
|
|
p = form;
|
|
for (;;) {
|
|
uae_u32 v;
|
|
if (!checkvaltype2(cp, &v, def)) {
|
|
if (isoperator(cp) || gotop || **cp == '\"' || **cp == '\'') {
|
|
goto docalc;
|
|
}
|
|
return 0;
|
|
}
|
|
*val = v;
|
|
// stupid but works!
|
|
_stprintf(p, _T("%u"), v);
|
|
p += _tcslen (p);
|
|
*p = 0;
|
|
if (peekchar(cp) == '.') {
|
|
readchar(cp);
|
|
if (size) {
|
|
*size = readsize(v, cp);
|
|
}
|
|
}
|
|
TCHAR *cpb = *cp;
|
|
ignore_ws(cp);
|
|
if (!isoperator(cp)) {
|
|
*cp = cpb;
|
|
break;
|
|
}
|
|
gotop = true;
|
|
*p++= readchar(cp);
|
|
*p = 0;
|
|
}
|
|
if (!gotop) {
|
|
if (size && *size == 0) {
|
|
uae_s32 v = (uae_s32)(*val);
|
|
if (v > 65535 || v < -32767) {
|
|
*size = 4;
|
|
} else if (v > 255 || v < -127) {
|
|
*size = 2;
|
|
} else {
|
|
*size = 1;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
docalc:
|
|
while (more_params2(cp)) {
|
|
TCHAR c = readchar(cp);
|
|
if (c == ' ') {
|
|
break;
|
|
}
|
|
*p++ = c;
|
|
}
|
|
*p = 0;
|
|
TCHAR tmp[MAX_DPATH];
|
|
int v = calc(form, &out, tmp, sizeof(tmp) / sizeof(TCHAR));
|
|
if (v > 0) {
|
|
*val = (uae_u32)out;
|
|
if (size && *size == 0) {
|
|
uae_s32 v = (uae_s32)(*val);
|
|
if (v > 255 || v < -127) {
|
|
*size = 2;
|
|
} else if (v > 65535 || v < -32767) {
|
|
*size = 4;
|
|
} else {
|
|
*size = 1;
|
|
}
|
|
}
|
|
return 1;
|
|
} else if (v < 0) {
|
|
console_out_f(_T("String returned: '%s'\n"), tmp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static uae_u32 readnum(TCHAR **c, int *size, TCHAR def, bool *err)
|
|
{
|
|
uae_u32 val;
|
|
if (err) {
|
|
*err = 0;
|
|
}
|
|
if (checkvaltype(c, &val, size, def)) {
|
|
return val;
|
|
}
|
|
if (err) {
|
|
*err = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uae_u32 readint(TCHAR **c, bool *err)
|
|
{
|
|
int size;
|
|
return readnum(c, &size, '!', err);
|
|
}
|
|
static uae_u32 readhex(TCHAR **c, bool *err)
|
|
{
|
|
int size;
|
|
return readnum(c, &size, '$', err);
|
|
}
|
|
static uae_u32 readbin(TCHAR **c, bool *err)
|
|
{
|
|
int size;
|
|
return readnum(c, &size, '%', err);
|
|
}
|
|
static uae_u32 readint(TCHAR **c, int *size, bool *err)
|
|
{
|
|
return readnum(c, size, '!', err);
|
|
}
|
|
static uae_u32 readhex(TCHAR **c, int *size, bool *err)
|
|
{
|
|
return readnum(c, size, '$', err);
|
|
}
|
|
|
|
static size_t next_string (TCHAR **c, TCHAR *out, int max, int forceupper)
|
|
{
|
|
TCHAR *p = out;
|
|
int startmarker = 0;
|
|
|
|
if (**c == '\"') {
|
|
startmarker = 1;
|
|
(*c)++;
|
|
}
|
|
*p = 0;
|
|
while (**c != 0) {
|
|
if (**c == '\"' && startmarker)
|
|
break;
|
|
if (**c == 32 && !startmarker) {
|
|
ignore_ws (c);
|
|
break;
|
|
}
|
|
*p = next_char (c);
|
|
if (forceupper)
|
|
*p = _totupper(*p);
|
|
*++p = 0;
|
|
max--;
|
|
if (max <= 1)
|
|
break;
|
|
}
|
|
return _tcslen (out);
|
|
}
|
|
|
|
static void converter(TCHAR **c)
|
|
{
|
|
bool err;
|
|
uae_u32 v = readint(c, &err);
|
|
TCHAR s[100];
|
|
int i, j;
|
|
|
|
if (err) {
|
|
return;
|
|
}
|
|
for (i = 0, j = 0; i < 32; i++) {
|
|
s[j++] = (v & (1 << (31 - i))) ? '1' : '0';
|
|
if (i < 31 && (i & 7) == 7) {
|
|
s[j++] = '`';
|
|
}
|
|
}
|
|
s[j] = 0;
|
|
console_out_f (_T("$%08X = %%%s = %u = %d\n"), v, s, v, (uae_s32)v);
|
|
}
|
|
|
|
static bool isrom(uaecptr addr)
|
|
{
|
|
addrbank *ab = &get_mem_bank(addr);
|
|
if (ab->flags & ABFLAG_ROM) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static uae_u32 lastaddr(uae_u32 start)
|
|
{
|
|
int lastbank = currprefs.address_space_24 ? 255 : 65535;
|
|
|
|
addrbank *ab2 = get_mem_bank_real(start + 1);
|
|
uae_u32 flags = ab2->flags & (ABFLAG_RAM | ABFLAG_ROM);
|
|
if (start == 0xffffffff) {
|
|
flags = ABFLAG_RAM;
|
|
}
|
|
for (int i = lastbank; i >= 0; i--) {
|
|
addrbank *ab = get_mem_bank_real(i << 16);
|
|
if (ab->baseaddr && (ab->flags & (ABFLAG_RAM | ABFLAG_ROM)) == flags) {
|
|
return (i + 1) << 16;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uae_u32 nextaddr_ab_flags = ABFLAG_RAM;
|
|
static uae_u32 nextaddr_ab_flags_mask = ABFLAG_RAM;
|
|
|
|
static void nextaddr_init(uaecptr addr)
|
|
{
|
|
addrbank *ab = get_mem_bank_real(addr + 1);
|
|
if (addr != 0xffffffff && (ab->flags & ABFLAG_ROM)) {
|
|
nextaddr_ab_flags = ABFLAG_ROM;
|
|
nextaddr_ab_flags_mask = ABFLAG_ROM;
|
|
} else {
|
|
nextaddr_ab_flags = ABFLAG_RAM;
|
|
nextaddr_ab_flags_mask = ABFLAG_RAM;
|
|
}
|
|
}
|
|
|
|
static uaecptr nextaddr(uaecptr addr, uaecptr last, uaecptr *endp, bool verbose, bool *lfp)
|
|
{
|
|
addrbank *ab;
|
|
int lastbank = currprefs.address_space_24 ? 255 : 65535;
|
|
|
|
if (addr != 0xffffffff) {
|
|
addrbank *ab2 = get_mem_bank_real(addr);
|
|
addr++;
|
|
ab = get_mem_bank_real(addr);
|
|
if (ab->baseaddr && (ab->flags & nextaddr_ab_flags_mask) == nextaddr_ab_flags && ab == ab2) {
|
|
return addr;
|
|
}
|
|
} else {
|
|
addr = 0;
|
|
}
|
|
|
|
while (addr < (lastbank << 16)) {
|
|
ab = get_mem_bank_real(addr);
|
|
if (ab->baseaddr && ((ab->flags & nextaddr_ab_flags_mask) == nextaddr_ab_flags))
|
|
break;
|
|
addr += 65536;
|
|
}
|
|
if (addr >= (lastbank << 16)) {
|
|
if (endp)
|
|
*endp = 0xffffffff;
|
|
return 0xffffffff;
|
|
}
|
|
|
|
uaecptr start = addr;
|
|
|
|
while (addr <= (lastbank << 16)) {
|
|
addrbank *ab2 = get_mem_bank_real(addr);
|
|
if ((last && last != 0xffffffff && addr >= last) || !ab2->baseaddr || ((ab2->flags & nextaddr_ab_flags_mask) != nextaddr_ab_flags) || ab != ab2) {
|
|
if (endp)
|
|
*endp = addr;
|
|
break;
|
|
}
|
|
addr += 65536;
|
|
}
|
|
|
|
if (verbose) {
|
|
if (lfp && *lfp) {
|
|
console_out_f(_T("\n"));
|
|
*lfp = false;
|
|
}
|
|
console_out_f(_T("Scanning.. %08x - %08x (%s)\n"), start, addr, get_mem_bank(start).name);
|
|
}
|
|
|
|
return start;
|
|
}
|
|
|
|
uaecptr dumpmem2 (uaecptr addr, TCHAR *out, int osize)
|
|
{
|
|
int i, cols = 8;
|
|
int nonsafe = 0;
|
|
|
|
if (osize <= (9 + cols * 5 + 1 + 2 * cols))
|
|
return addr;
|
|
_stprintf (out, _T("%08X "), addr);
|
|
for (i = 0; i < cols; i++) {
|
|
uae_u8 b1, b2;
|
|
b1 = b2 = 0;
|
|
if (debug_safe_addr (addr, 1)) {
|
|
b1 = get_byte_debug (addr + 0);
|
|
b2 = get_byte_debug (addr + 1);
|
|
_stprintf (out + 9 + i * 5, _T("%02X%02X "), b1, b2);
|
|
out[9 + cols * 5 + 1 + i * 2 + 0] = b1 >= 32 && b1 < 127 ? b1 : '.';
|
|
out[9 + cols * 5 + 1 + i * 2 + 1] = b2 >= 32 && b2 < 127 ? b2 : '.';
|
|
} else {
|
|
nonsafe++;
|
|
_tcscpy (out + 9 + i * 5, _T("**** "));
|
|
out[9 + cols * 5 + 1 + i * 2 + 0] = '*';
|
|
out[9 + cols * 5 + 1 + i * 2 + 1] = '*';
|
|
}
|
|
addr += 2;
|
|
}
|
|
out[9 + cols * 5] = ' ';
|
|
out[9 + cols * 5 + 1 + 2 * cols] = 0;
|
|
if (nonsafe == cols) {
|
|
addrbank *ab = &get_mem_bank (addr);
|
|
if (ab->name)
|
|
memcpy (out + (9 + 4 + 1) * sizeof (TCHAR), ab->name, _tcslen (ab->name) * sizeof (TCHAR));
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
static TCHAR dumpmemline[MAX_LINEWIDTH + 1];
|
|
|
|
static void dumpmem (uaecptr addr, uaecptr *nxmem, int lines)
|
|
{
|
|
for (;lines--;) {
|
|
addr = dumpmem2 (addr, dumpmemline, sizeof(dumpmemline) / sizeof(TCHAR));
|
|
debug_out (_T("%s"), dumpmemline);
|
|
if (!debug_out (_T("\n")))
|
|
break;
|
|
}
|
|
*nxmem = addr;
|
|
}
|
|
|
|
static void dump_custom_regs(bool aga, bool ext)
|
|
{
|
|
size_t len;
|
|
uae_u8 *p1, *p2, *p3, *p4;
|
|
TCHAR extra1[256], extra2[256];
|
|
|
|
extra1[0] = 0;
|
|
extra2[0] = 0;
|
|
if (aga) {
|
|
dump_aga_custom();
|
|
return;
|
|
}
|
|
|
|
p1 = p2 = save_custom (&len, 0, 1);
|
|
p1 += 4; // skip chipset type
|
|
for (int i = 0; i < 4; i++) {
|
|
p4 = p1 + 0xa0 + i * 16;
|
|
p3 = save_audio (i, &len, 0);
|
|
p4[0] = p3[12];
|
|
p4[1] = p3[13];
|
|
p4[2] = p3[14];
|
|
p4[3] = p3[15];
|
|
p4[4] = p3[4];
|
|
p4[5] = p3[5];
|
|
p4[6] = p3[8];
|
|
p4[7] = p3[9];
|
|
p4[8] = 0;
|
|
p4[9] = p3[1];
|
|
p4[10] = p3[10];
|
|
p4[11] = p3[11];
|
|
free (p3);
|
|
}
|
|
int total = 0;
|
|
int i = 0;
|
|
while (custd[i].name) {
|
|
if (!(custd[i].special & CD_NONE))
|
|
total++;
|
|
i++;
|
|
}
|
|
int cnt1 = 0;
|
|
int cnt2 = 0;
|
|
i = 0;
|
|
while (i < total / 2 + 1) {
|
|
for (;;) {
|
|
cnt2++;
|
|
if (!(custd[cnt2].special & CD_NONE))
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
for (int i = 0; i < total / 2 + 1; i++) {
|
|
uae_u16 v1, v2;
|
|
int addr1, addr2;
|
|
addr1 = custd[cnt1].adr & 0x1ff;
|
|
addr2 = custd[cnt2].adr & 0x1ff;
|
|
v1 = (p1[addr1 + 0] << 8) | p1[addr1 + 1];
|
|
v2 = (p1[addr2 + 0] << 8) | p1[addr2 + 1];
|
|
if (ext) {
|
|
struct custom_store *cs;
|
|
cs = &custom_storage[addr1 >> 1];
|
|
_stprintf(extra1, _T("\t%04X %08X %s"), cs->value, cs->pc & ~1, (cs->pc & 1) ? _T("COP") : _T("CPU"));
|
|
cs = &custom_storage[addr2 >> 1];
|
|
_stprintf(extra2, _T("\t%04X %08X %s"), cs->value, cs->pc & ~1, (cs->pc & 1) ? _T("COP") : _T("CPU"));
|
|
}
|
|
console_out_f (_T("%03X %s\t%04X%s\t%03X %s\t%04X%s\n"),
|
|
addr1, custd[cnt1].name, v1, extra1,
|
|
addr2, custd[cnt2].name, v2, extra2);
|
|
for (;;) {
|
|
cnt1++;
|
|
if (!(custd[cnt1].special & CD_NONE))
|
|
break;
|
|
}
|
|
for (;;) {
|
|
cnt2++;
|
|
if (!(custd[cnt2].special & CD_NONE))
|
|
break;
|
|
}
|
|
}
|
|
xfree(p2);
|
|
}
|
|
|
|
static void dump_vectors (uaecptr addr)
|
|
{
|
|
int i = 0, j = 0;
|
|
|
|
if (addr == 0xffffffff)
|
|
addr = regs.vbr;
|
|
|
|
while (int_labels[i].name || trap_labels[j].name) {
|
|
if (int_labels[i].name) {
|
|
console_out_f (_T("$%08X %02d: %12s $%08X "), int_labels[i].adr + addr, int_labels[i].adr / 4,
|
|
int_labels[i].name, get_long_debug (int_labels[i].adr + addr));
|
|
i++;
|
|
}
|
|
if (trap_labels[j].name) {
|
|
console_out_f (_T("$%08X %02d: %12s $%08X"), trap_labels[j].adr + addr, trap_labels[j].adr / 4,
|
|
trap_labels[j].name, get_long_debug (trap_labels[j].adr + addr));
|
|
j++;
|
|
}
|
|
console_out (_T("\n"));
|
|
}
|
|
}
|
|
|
|
static void disassemble_wait (FILE *file, unsigned long insn)
|
|
{
|
|
int vp, hp, ve, he, bfd, v_mask, h_mask;
|
|
int doout = 0;
|
|
|
|
vp = (insn & 0xff000000) >> 24;
|
|
hp = (insn & 0x00fe0000) >> 16;
|
|
ve = (insn & 0x00007f00) >> 8;
|
|
he = (insn & 0x000000fe);
|
|
bfd = (insn & 0x00008000) >> 15;
|
|
|
|
/* bit15 can never be masked out*/
|
|
v_mask = vp & (ve | 0x80);
|
|
h_mask = hp & he;
|
|
if (v_mask > 0) {
|
|
doout = 1;
|
|
console_out (_T("vpos "));
|
|
if (ve != 0x7f) {
|
|
console_out_f (_T("& 0x%02x "), ve);
|
|
}
|
|
console_out_f (_T(">= 0x%02x"), v_mask);
|
|
}
|
|
if (he > 0) {
|
|
if (v_mask > 0) {
|
|
console_out (_T(" and"));
|
|
}
|
|
console_out (_T(" hpos "));
|
|
if (he != 0xfe) {
|
|
console_out_f (_T("& 0x%02x "), he);
|
|
}
|
|
console_out_f (_T(">= 0x%02x"), h_mask);
|
|
} else {
|
|
if (doout)
|
|
console_out (_T(", "));
|
|
console_out (_T(", ignore horizontal"));
|
|
}
|
|
|
|
console_out_f (_T("\n \t; VP %02x, VE %02x; HP %02x, HE %02x; BFD %d\n"),
|
|
vp, ve, hp, he, bfd);
|
|
}
|
|
|
|
#define NR_COPPER_RECORDS 100000
|
|
/* Record copper activity for the debugger. */
|
|
struct cop_rec
|
|
{
|
|
uae_u16 w1, w2;
|
|
int hpos, vpos;
|
|
int bhpos, bvpos;
|
|
uaecptr addr, nextaddr;
|
|
};
|
|
static struct cop_rec *cop_record[2];
|
|
static int nr_cop_records[2], curr_cop_set, selected_cop_set;
|
|
|
|
#define NR_DMA_REC_HPOS 288
|
|
#define NR_DMA_REC_VPOS 1000
|
|
static struct dma_rec *dma_record[2];
|
|
static int dma_record_toggle, dma_record_frame[2];
|
|
|
|
static void record_dma_clear(int r)
|
|
{
|
|
struct dma_rec *dr = dma_record[r];
|
|
for (int v = 0; v < NR_DMA_REC_VPOS; v++) {
|
|
for (int h = 0; h < NR_DMA_REC_HPOS; h++) {
|
|
struct dma_rec *dr2 = &dr[v * NR_DMA_REC_HPOS + h];
|
|
memset(dr2, 0, sizeof(struct dma_rec));
|
|
dr2->reg = 0xffff;
|
|
dr2->hpos = -1;
|
|
dr2->cf_reg = 0xffff;
|
|
dr2->addr = 0xffffffff;
|
|
}
|
|
}
|
|
dma_record_frame[r] = -1;
|
|
}
|
|
|
|
static void dma_record_init(void)
|
|
{
|
|
if (!dma_record[0]) {
|
|
dma_record[0] = xcalloc(struct dma_rec, NR_DMA_REC_HPOS * NR_DMA_REC_VPOS + 2);
|
|
dma_record[1] = xcalloc(struct dma_rec, NR_DMA_REC_HPOS * NR_DMA_REC_VPOS + 2);
|
|
dma_record[0]->vpos = -1;
|
|
dma_record[1]->vpos = -1;
|
|
dma_record[0]->end = 1;
|
|
dma_record[1]->end = 1;
|
|
dma_record[0]++;
|
|
dma_record[1]++;
|
|
record_dma_reset(0);
|
|
dma_record_toggle = 0;
|
|
dma_record_frame[0] = -1;
|
|
dma_record_frame[1] = -1;
|
|
record_dma_clear(0);
|
|
record_dma_clear(1);
|
|
}
|
|
}
|
|
|
|
void record_dma_reset(int start)
|
|
{
|
|
if (start && !dma_record[0]) {
|
|
dma_record_init();
|
|
}
|
|
if (!dma_record[0]) {
|
|
return;
|
|
}
|
|
dma_record_toggle ^= 1;
|
|
record_dma_clear(dma_record_toggle);
|
|
if (start && !debug_dma) {
|
|
debug_dma = start;
|
|
}
|
|
}
|
|
|
|
void record_copper_reset (void)
|
|
{
|
|
/* Start a new set of copper records. */
|
|
curr_cop_set ^= 1;
|
|
nr_cop_records[curr_cop_set] = 0;
|
|
}
|
|
|
|
STATIC_INLINE uae_u32 ledcolor (uae_u32 c, uae_u32 *rc, uae_u32 *gc, uae_u32 *bc, uae_u32 *a)
|
|
{
|
|
uae_u32 v = rc[(c >> 16) & 0xff] | gc[(c >> 8) & 0xff] | bc[(c >> 0) & 0xff];
|
|
if (a)
|
|
v |= a[255 - ((c >> 24) & 0xff)];
|
|
return v;
|
|
}
|
|
|
|
STATIC_INLINE void putpixel (uae_u8 *buf, int bpp, int x, xcolnr c8)
|
|
{
|
|
if (x <= 0)
|
|
return;
|
|
|
|
switch (bpp) {
|
|
case 1:
|
|
buf[x] = (uae_u8)c8;
|
|
break;
|
|
case 2:
|
|
{
|
|
uae_u16 *p = (uae_u16*)buf + x;
|
|
*p = (uae_u16)c8;
|
|
break;
|
|
}
|
|
case 3:
|
|
/* no 24 bit yet */
|
|
break;
|
|
case 4:
|
|
{
|
|
uae_u32 *p = (uae_u32*)buf + x;
|
|
*p = c8;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define lc(x) ledcolor (x, xredcolors, xgreencolors, xbluecolors, NULL)
|
|
#define DMARECORD_SUBITEMS 8
|
|
struct dmadebug
|
|
{
|
|
uae_u32 l[DMARECORD_SUBITEMS];
|
|
uae_u8 r, g, b;
|
|
bool enabled;
|
|
int max;
|
|
const TCHAR *name;
|
|
};
|
|
|
|
static uae_u32 intlevc[] = { 0x000000, 0x444444, 0x008800, 0xffff00, 0x000088, 0x880000, 0xff0000, 0xffffff };
|
|
static struct dmadebug debug_colors[DMARECORD_MAX];
|
|
static bool debug_colors_set;
|
|
|
|
static void set_dbg_color(int index, int extra, uae_u8 r, uae_u8 g, uae_u8 b, int max, const TCHAR *name)
|
|
{
|
|
if (extra <= 0) {
|
|
debug_colors[index].r = r;
|
|
debug_colors[index].g = g;
|
|
debug_colors[index].b = b;
|
|
debug_colors[index].enabled = true;
|
|
}
|
|
if (name != NULL)
|
|
debug_colors[index].name = name;
|
|
if (max > 0)
|
|
debug_colors[index].max = max;
|
|
if (extra >= 0) {
|
|
debug_colors[index].l[extra] = lc((r << 16) | (g << 8) | (b << 0));
|
|
} else {
|
|
for (int i = 0; i < DMARECORD_SUBITEMS; i++) {
|
|
debug_colors[index].l[i] = lc((r << 16) | (g << 8) | (b << 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void set_debug_colors(void)
|
|
{
|
|
if (debug_colors_set)
|
|
return;
|
|
debug_colors_set = true;
|
|
set_dbg_color(0, 0, 0x22, 0x22, 0x22, 1, _T("-"));
|
|
set_dbg_color(DMARECORD_REFRESH, 0, 0x44, 0x44, 0x44, 4, _T("Refresh"));
|
|
set_dbg_color(DMARECORD_CPU, 0, 0xa2, 0x53, 0x42, 2, _T("CPU")); // code
|
|
set_dbg_color(DMARECORD_COPPER, 0, 0xee, 0xee, 0x00, 3, _T("Copper"));
|
|
set_dbg_color(DMARECORD_AUDIO, 0, 0xff, 0x00, 0x00, 4, _T("Audio"));
|
|
set_dbg_color(DMARECORD_BLITTER, 0, 0x00, 0x88, 0x88, 2, _T("Blitter")); // blit A
|
|
set_dbg_color(DMARECORD_BITPLANE, 0, 0x00, 0x00, 0xff, 8, _T("Bitplane"));
|
|
set_dbg_color(DMARECORD_SPRITE, 0, 0xff, 0x00, 0xff, 8, _T("Sprite"));
|
|
set_dbg_color(DMARECORD_DISK, 0, 0xff, 0xff, 0xff, 3, _T("Disk"));
|
|
set_dbg_color(DMARECORD_CONFLICT, 0, 0xff, 0xb8, 0x40, 0, _T("Conflict"));
|
|
|
|
for (int i = 0; i < DMARECORD_MAX; i++) {
|
|
for (int j = 1; j < DMARECORD_SUBITEMS; j++) {
|
|
debug_colors[i].l[j] = debug_colors[i].l[0];
|
|
}
|
|
}
|
|
|
|
set_dbg_color(DMARECORD_CPU, 1, 0xad, 0x98, 0xd6, 0, NULL); // data
|
|
set_dbg_color(DMARECORD_COPPER, 1, 0xaa, 0xaa, 0x22, 0, NULL); // wait
|
|
set_dbg_color(DMARECORD_COPPER, 2, 0x66, 0x66, 0x44, 0, NULL); // special
|
|
set_dbg_color(DMARECORD_BLITTER, 1, 0x00, 0x88, 0x88, 0, NULL); // blit B
|
|
set_dbg_color(DMARECORD_BLITTER, 2, 0x00, 0x88, 0x88, 0, NULL); // blit C
|
|
set_dbg_color(DMARECORD_BLITTER, 3, 0x00, 0xaa, 0x88, 0, NULL); // blit D (write)
|
|
set_dbg_color(DMARECORD_BLITTER, 4, 0x00, 0x88, 0xff, 0, NULL); // fill A-D
|
|
set_dbg_color(DMARECORD_BLITTER, 6, 0x00, 0xff, 0x00, 0, NULL); // line A-D
|
|
}
|
|
|
|
static int cycles_toggle;
|
|
static int record_dma_maxhpos, record_dma_maxvpos;
|
|
static int dma_record_hoffset;
|
|
|
|
static void debug_draw_cycles(uae_u8 *buf, int bpp, int line, int width, int height, uae_u32 *xredcolors, uae_u32 *xgreencolors, uae_u32 *xbluescolors)
|
|
{
|
|
int y, x, xx, dx, xplus, yplus;
|
|
struct dma_rec *dr;
|
|
int t;
|
|
|
|
if (debug_dma >= 4)
|
|
yplus = 2;
|
|
else
|
|
yplus = 1;
|
|
if (debug_dma >= 5)
|
|
xplus = 3;
|
|
else if (debug_dma >= 3)
|
|
xplus = 2;
|
|
else
|
|
xplus = 1;
|
|
|
|
t = dma_record_toggle ^ 1;
|
|
y = line / yplus;
|
|
if (yplus < 2)
|
|
y -= 8;
|
|
|
|
if (y < 0)
|
|
return;
|
|
if (y > record_dma_maxvpos)
|
|
return;
|
|
if (y >= height)
|
|
return;
|
|
|
|
dx = width - xplus * ((record_dma_maxhpos + 1) & ~1) - 16;
|
|
|
|
bool ended = false;
|
|
uae_s8 intlev = 0;
|
|
for (x = 0; x < record_dma_maxhpos; x++) {
|
|
uae_u32 c = debug_colors[0].l[0];
|
|
xx = x * xplus + dx;
|
|
dr = &dma_record[t][y * NR_DMA_REC_HPOS + x];
|
|
|
|
if (dr->end) {
|
|
ended = true;
|
|
}
|
|
if (ended) {
|
|
c = 0;
|
|
} else {
|
|
if (dr->reg != 0xffff && debug_colors[dr->type].enabled) {
|
|
// General DMA slots
|
|
c = debug_colors[dr->type].l[dr->extra & 7];
|
|
|
|
// Special cases
|
|
if (dr->cf_reg != 0xffff && ((cycles_toggle ^ line) & 1)) {
|
|
c = debug_colors[DMARECORD_CONFLICT].l[0];
|
|
} else if (dr->extra > 0xF) {
|
|
// High bits of "extra" contain additional blitter state.
|
|
if (dr->extra & 0x10)
|
|
c = debug_colors[dr->type].l[4]; // blit fill, channels A-D
|
|
else if (dr->extra & 0x20)
|
|
c = debug_colors[dr->type].l[6]; // blit line, channels A-D
|
|
}
|
|
}
|
|
}
|
|
if (dr->intlev > intlev)
|
|
intlev = dr->intlev;
|
|
putpixel(buf, bpp, xx + 4, c);
|
|
if (xplus > 1)
|
|
putpixel(buf, bpp, xx + 4 + 1, c);
|
|
if (xplus > 2)
|
|
putpixel(buf, bpp, xx + 4 + 2, c);
|
|
}
|
|
putpixel (buf, bpp, dx + 0, 0);
|
|
putpixel (buf, bpp, dx + 1, lc(intlevc[intlev]));
|
|
putpixel (buf, bpp, dx + 2, lc(intlevc[intlev]));
|
|
putpixel (buf, bpp, dx + 3, 0);
|
|
}
|
|
|
|
#define HEATMAP_WIDTH 256
|
|
#define HEATMAP_HEIGHT 256
|
|
#define HEATMAP_COUNT 32
|
|
#define HEATMAP_DIV 8
|
|
static const int max_heatmap = 16 * 1048576; // 16M
|
|
static uae_u32 *heatmap_debug_colors;
|
|
|
|
static struct memory_heatmap *heatmap;
|
|
struct memory_heatmap
|
|
{
|
|
uae_u32 mask;
|
|
uae_u32 cpucnt;
|
|
uae_u16 cnt;
|
|
uae_u16 type, extra;
|
|
};
|
|
|
|
static void debug_draw_heatmap(uae_u8 *buf, int bpp, int line, int width, int height, uae_u32 *xredcolors, uae_u32 *xgreencolors, uae_u32 *xbluescolors)
|
|
{
|
|
struct memory_heatmap *mht = heatmap;
|
|
int dx = 16;
|
|
int y = line;
|
|
|
|
if (y < 0 || y >= HEATMAP_HEIGHT)
|
|
return;
|
|
|
|
mht += y * HEATMAP_WIDTH;
|
|
|
|
for (int x = 0; x < HEATMAP_WIDTH; x++) {
|
|
uae_u32 c = heatmap_debug_colors[mht->cnt * DMARECORD_MAX + mht->type];
|
|
//c = heatmap_debug_colors[(HEATMAP_COUNT - 1) * DMARECORD_MAX + DMARECORD_CPU_I];
|
|
int xx = x + dx;
|
|
putpixel(buf, bpp, xx, c);
|
|
if (mht->cnt > 0)
|
|
mht->cnt--;
|
|
mht++;
|
|
}
|
|
}
|
|
|
|
void debug_draw(uae_u8 *buf, int bpp, int line, int width, int height, uae_u32 *xredcolors, uae_u32 *xgreencolors, uae_u32 *xbluescolors)
|
|
{
|
|
if (!heatmap_debug_colors) {
|
|
heatmap_debug_colors = xcalloc(uae_u32, DMARECORD_MAX * HEATMAP_COUNT);
|
|
set_debug_colors();
|
|
for (int i = 0; i < HEATMAP_COUNT; i++) {
|
|
uae_u32 *cp = heatmap_debug_colors + i * DMARECORD_MAX;
|
|
for (int j = 0; j < DMARECORD_MAX; j++) {
|
|
uae_u8 r = debug_colors[j].r;
|
|
uae_u8 g = debug_colors[j].g;
|
|
uae_u8 b = debug_colors[j].b;
|
|
r = r * i / HEATMAP_COUNT;
|
|
g = g * i / HEATMAP_COUNT;
|
|
b = b * i / HEATMAP_COUNT;
|
|
cp[j] = lc((r << 16) | (g << 8) | (b << 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (heatmap) {
|
|
debug_draw_heatmap(buf, bpp, line, width, height, xredcolors, xgreencolors, xbluecolors);
|
|
} else if (dma_record[0]) {
|
|
debug_draw_cycles(buf, bpp, line, width, height, xredcolors, xgreencolors, xbluecolors);
|
|
}
|
|
}
|
|
|
|
struct heatmapstore
|
|
{
|
|
TCHAR *s;
|
|
double v;
|
|
};
|
|
|
|
static void heatmap_stats(TCHAR **c)
|
|
{
|
|
int range = 95;
|
|
int maxlines = 30;
|
|
double max;
|
|
int maxcnt;
|
|
uae_u32 mask = MW_MASK_CPU_I;
|
|
const TCHAR *maskname = NULL;
|
|
|
|
if (more_params(c)) {
|
|
if (**c == 'c' && peek_next_char(c) == 0) {
|
|
for (int i = 0; i < max_heatmap / HEATMAP_DIV; i++) {
|
|
struct memory_heatmap *hm = &heatmap[i];
|
|
memset(hm, 0, sizeof(struct memory_heatmap));
|
|
}
|
|
console_out(_T("heatmap data cleared\n"));
|
|
return;
|
|
}
|
|
if (!isdigit(peek_next_char(c))) {
|
|
TCHAR str[100];
|
|
if (next_string(c, str, sizeof str / sizeof (TCHAR), true)) {
|
|
for (int j = 0; memwatch_access_masks[j].mask; j++) {
|
|
if (!_tcsicmp(str, memwatch_access_masks[j].name)) {
|
|
mask = memwatch_access_masks[j].mask;
|
|
maskname = memwatch_access_masks[j].name;
|
|
console_out_f(_T("Mask %08x Name %s\n"), mask, maskname);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (more_params(c)) {
|
|
maxlines = readint(c, NULL);
|
|
}
|
|
} else {
|
|
range = readint(c, NULL);
|
|
if (more_params(c)) {
|
|
maxlines = readint(c, NULL);
|
|
}
|
|
}
|
|
}
|
|
if (maxlines <= 0)
|
|
maxlines = 10000;
|
|
|
|
if (mask != MW_MASK_CPU_I) {
|
|
|
|
int found = -1;
|
|
int firstaddress = 0;
|
|
for (int lines = 0; lines < maxlines; lines++) {
|
|
|
|
for (; firstaddress < max_heatmap / HEATMAP_DIV; firstaddress++) {
|
|
struct memory_heatmap *hm = &heatmap[firstaddress];
|
|
if (hm->mask & mask)
|
|
break;
|
|
}
|
|
|
|
if (firstaddress == max_heatmap / HEATMAP_DIV)
|
|
return;
|
|
|
|
int lastaddress;
|
|
for (lastaddress = firstaddress; lastaddress < max_heatmap / HEATMAP_DIV; lastaddress++) {
|
|
struct memory_heatmap *hm = &heatmap[lastaddress];
|
|
if (!(hm->mask & mask))
|
|
break;
|
|
}
|
|
lastaddress--;
|
|
|
|
console_out_f(_T("%03d: %08x - %08x %08x (%d) %s\n"),
|
|
lines,
|
|
firstaddress * HEATMAP_DIV, lastaddress * HEATMAP_DIV + HEATMAP_DIV - 1,
|
|
lastaddress * HEATMAP_DIV - firstaddress * HEATMAP_DIV + HEATMAP_DIV - 1,
|
|
lastaddress * HEATMAP_DIV - firstaddress * HEATMAP_DIV + HEATMAP_DIV - 1,
|
|
maskname);
|
|
|
|
firstaddress = lastaddress + 1;
|
|
}
|
|
|
|
} else {
|
|
#define MAX_HEATMAP_LINES 1000
|
|
struct heatmapstore linestore[MAX_HEATMAP_LINES] = { 0 };
|
|
int storecnt = 0;
|
|
uae_u32 maxlimit = 0xffffffff;
|
|
|
|
max = 0;
|
|
maxcnt = 0;
|
|
for (int i = 0; i < max_heatmap / HEATMAP_DIV; i++) {
|
|
struct memory_heatmap *hm = &heatmap[i];
|
|
if (hm->cpucnt > 0) {
|
|
max += hm->cpucnt;
|
|
maxcnt++;
|
|
}
|
|
}
|
|
|
|
if (!maxcnt) {
|
|
console_out(_T("No CPU accesses found\n"));
|
|
return;
|
|
}
|
|
|
|
for (int lines = 0; lines < maxlines; lines++) {
|
|
|
|
int found = -1;
|
|
int foundcnt = 0;
|
|
|
|
for (int i = 0; i < max_heatmap / HEATMAP_DIV; i++) {
|
|
struct memory_heatmap *hm = &heatmap[i];
|
|
if (hm->cpucnt > 0 && hm->cpucnt > foundcnt && hm->cpucnt < maxlimit) {
|
|
foundcnt = hm->cpucnt;
|
|
found = i;
|
|
}
|
|
}
|
|
if (found < 0)
|
|
break;
|
|
|
|
int totalcnt = 0;
|
|
int cntrange = foundcnt * range / 100;
|
|
if (cntrange <= 0)
|
|
cntrange = 1;
|
|
|
|
int lastaddress;
|
|
for (lastaddress = found; lastaddress < max_heatmap / HEATMAP_DIV; lastaddress++) {
|
|
struct memory_heatmap *hm = &heatmap[lastaddress];
|
|
if (hm->cpucnt == 0 || hm->cpucnt < cntrange || hm->cpucnt >= maxlimit)
|
|
break;
|
|
totalcnt += hm->cpucnt;
|
|
}
|
|
lastaddress--;
|
|
|
|
int firstaddress;
|
|
for (firstaddress = found - 1; firstaddress >= 0; firstaddress--) {
|
|
struct memory_heatmap *hm = &heatmap[firstaddress];
|
|
if (hm->cpucnt == 0 || hm->cpucnt < cntrange || hm->cpucnt >= maxlimit)
|
|
break;
|
|
totalcnt += hm->cpucnt;
|
|
}
|
|
firstaddress--;
|
|
|
|
firstaddress *= HEATMAP_DIV;
|
|
lastaddress *= HEATMAP_DIV;
|
|
|
|
TCHAR tmp[100];
|
|
double pct = totalcnt / max * 100.0;
|
|
_stprintf(tmp, _T("%03d: %08x - %08x %08x (%d) %.5f%%\n"), lines + 1,
|
|
firstaddress, lastaddress + HEATMAP_DIV - 1,
|
|
lastaddress - firstaddress + HEATMAP_DIV - 1,
|
|
lastaddress - firstaddress + HEATMAP_DIV - 1,
|
|
pct);
|
|
linestore[storecnt].s = my_strdup(tmp);
|
|
linestore[storecnt].v = pct;
|
|
|
|
storecnt++;
|
|
if (storecnt >= MAX_HEATMAP_LINES)
|
|
break;
|
|
|
|
maxlimit = foundcnt;
|
|
}
|
|
|
|
for (int lines1 = 0; lines1 < storecnt; lines1++) {
|
|
for (int lines2 = lines1 + 1; lines2 < storecnt; lines2++) {
|
|
if (linestore[lines1].v < linestore[lines2].v) {
|
|
struct heatmapstore hms;
|
|
memcpy(&hms, &linestore[lines1], sizeof(struct heatmapstore));
|
|
memcpy(&linestore[lines1], &linestore[lines2], sizeof(struct heatmapstore));
|
|
memcpy(&linestore[lines2], &hms, sizeof(struct heatmapstore));
|
|
}
|
|
}
|
|
}
|
|
for (int lines1 = 0; lines1 < storecnt; lines1++) {
|
|
console_out(linestore[lines1].s);
|
|
xfree(linestore[lines1].s);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void free_heatmap(void)
|
|
{
|
|
xfree(heatmap);
|
|
heatmap = NULL;
|
|
debug_heatmap = 0;
|
|
}
|
|
|
|
static void init_heatmap(void)
|
|
{
|
|
if (!heatmap)
|
|
heatmap = xcalloc(struct memory_heatmap, max_heatmap / HEATMAP_DIV);
|
|
}
|
|
|
|
static void memwatch_heatmap(uaecptr addr, int rwi, int size, uae_u32 accessmask)
|
|
{
|
|
if (addr >= max_heatmap || !heatmap)
|
|
return;
|
|
struct memory_heatmap *hm = &heatmap[addr / HEATMAP_DIV];
|
|
if (accessmask & MW_MASK_CPU_I) {
|
|
hm->cpucnt++;
|
|
}
|
|
hm->cnt = HEATMAP_COUNT - 1;
|
|
int type = 0;
|
|
int extra = 0;
|
|
for (int i = 0; i < 32; i++) {
|
|
if (accessmask & (1 << i)) {
|
|
switch (1 << i)
|
|
{
|
|
case MW_MASK_BPL_0:
|
|
case MW_MASK_BPL_1:
|
|
case MW_MASK_BPL_2:
|
|
case MW_MASK_BPL_3:
|
|
case MW_MASK_BPL_4:
|
|
case MW_MASK_BPL_5:
|
|
case MW_MASK_BPL_6:
|
|
case MW_MASK_BPL_7:
|
|
type = DMARECORD_BITPLANE;
|
|
break;
|
|
case MW_MASK_AUDIO_0:
|
|
case MW_MASK_AUDIO_1:
|
|
case MW_MASK_AUDIO_2:
|
|
case MW_MASK_AUDIO_3:
|
|
type = DMARECORD_AUDIO;
|
|
break;
|
|
case MW_MASK_BLITTER_A:
|
|
case MW_MASK_BLITTER_B:
|
|
case MW_MASK_BLITTER_C:
|
|
case MW_MASK_BLITTER_D_N:
|
|
case MW_MASK_BLITTER_D_F:
|
|
case MW_MASK_BLITTER_D_L:
|
|
type = DMARECORD_BLITTER;
|
|
break;
|
|
case MW_MASK_COPPER:
|
|
type = DMARECORD_COPPER;
|
|
break;
|
|
case MW_MASK_DISK:
|
|
type = DMARECORD_DISK;
|
|
break;
|
|
case MW_MASK_CPU_I:
|
|
type = DMARECORD_CPU;
|
|
break;
|
|
case MW_MASK_CPU_D_R:
|
|
case MW_MASK_CPU_D_W:
|
|
type = DMARECORD_CPU;
|
|
extra = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
hm->type = type;
|
|
hm->extra = extra;
|
|
hm->mask |= accessmask;
|
|
}
|
|
|
|
struct refdata
|
|
{
|
|
evt_t c;
|
|
uae_u32 cnt;
|
|
};
|
|
static struct refdata refreshtable[1024];
|
|
static int refcheck_count;
|
|
#define REFRESH_LINES 64
|
|
|
|
static void check_refreshed(void)
|
|
{
|
|
int max = ecs_agnus ? 512 : 256;
|
|
int reffail = 0;
|
|
evt_t c = get_cycles();
|
|
for (int i = 0; i < max; i++) {
|
|
struct refdata *rd = &refreshtable[i];
|
|
if (rd->cnt < 10) {
|
|
rd->cnt++;
|
|
}
|
|
if (rd->cnt == 10) {
|
|
reffail++;
|
|
rd->cnt = 0;
|
|
}
|
|
if (rd->c && (int)c - (int)rd->c >= CYCLE_UNIT * maxhpos * REFRESH_LINES) {
|
|
reffail++;
|
|
rd->c = 0;
|
|
rd->cnt = 0;
|
|
}
|
|
if (reffail) {
|
|
write_log("%03u ", i);
|
|
}
|
|
}
|
|
if (reffail) {
|
|
write_log("%d memory rows not refreshed fast enough!\n", reffail);
|
|
}
|
|
}
|
|
|
|
void debug_mark_refreshed(uaecptr rp)
|
|
{
|
|
int ras;
|
|
if (ecs_agnus && currprefs.chipmem.size > 0x100000) {
|
|
ras = (rp >> 9) & 0x3ff;
|
|
} else if (ecs_agnus) {
|
|
ras = (rp >> 9) & 0x1ff;
|
|
} else {
|
|
ras = (rp >> 1) & 0xff;
|
|
}
|
|
struct refdata *rd = &refreshtable[ras];
|
|
evt_t c = get_cycles();
|
|
rd->c = c;
|
|
rd->cnt = 0;
|
|
}
|
|
|
|
void record_dma_vsync(int vp)
|
|
{
|
|
struct dma_rec *dr;
|
|
|
|
if (!dma_record[0])
|
|
return;
|
|
if (vp >= NR_DMA_REC_VPOS)
|
|
return;
|
|
|
|
dr = &dma_record[dma_record_toggle][vp * NR_DMA_REC_HPOS];
|
|
dr->end = true;
|
|
|
|
record_dma_maxvpos = vp;
|
|
dma_record_hoffset = 0;
|
|
cycles_toggle = cycles_toggle ? 0 : 1;
|
|
}
|
|
|
|
void record_dma_reoffset(int vp, int oldhpos, int newhpos)
|
|
{
|
|
if (!dma_record[0])
|
|
return;
|
|
|
|
int hp = newhpos + dma_record_hoffset;
|
|
struct dma_rec *dr = &dma_record[dma_record_toggle][vp * NR_DMA_REC_HPOS + hp];
|
|
dma_record_hoffset -= newhpos - oldhpos;
|
|
#if 0
|
|
dr->vpos = vp;
|
|
dr->hpos = oldhpos;
|
|
#endif
|
|
if (dma_record_hoffset >= NR_DMA_REC_HPOS) {
|
|
dma_record_hoffset = NR_DMA_REC_HPOS - 1;
|
|
}
|
|
if (dma_record_hoffset <= -NR_DMA_REC_HPOS) {
|
|
dma_record_hoffset = -NR_DMA_REC_HPOS + 1;
|
|
}
|
|
}
|
|
|
|
void record_dma_hsync(int lasthpos)
|
|
{
|
|
struct dma_rec *dr;
|
|
|
|
if (!dma_record[0])
|
|
return;
|
|
|
|
lasthpos += dma_record_hoffset;
|
|
if (lasthpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS) {
|
|
dma_record_hoffset = 0;
|
|
return;
|
|
}
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + lasthpos];
|
|
dr->end = true;
|
|
lasthpos -= dma_record_hoffset;
|
|
dma_record_hoffset = 0;
|
|
|
|
if (vpos == 0) {
|
|
record_dma_maxhpos = lasthpos;
|
|
} else {
|
|
if (lasthpos > record_dma_maxhpos) {
|
|
record_dma_maxhpos = lasthpos;
|
|
}
|
|
if (vpos > record_dma_maxvpos) {
|
|
record_dma_maxvpos = vpos;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
refcheck_count++;
|
|
if (refcheck_count >= REFRESH_LINES / 8) {
|
|
refcheck_count = 0;
|
|
check_refreshed();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void record_dma_ipl(int hpos, int vpos)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
if (!dma_record[0])
|
|
return;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->intlev = regs.intmask;
|
|
dr->ipl = regs.ipl_pin;
|
|
dr->evt2 |= DMA_EVENT2_IPL;
|
|
}
|
|
|
|
void record_dma_ipl_sample(int hpos, int vpos)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
if (!dma_record[0])
|
|
return;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->intlev = regs.intmask;
|
|
dr->ipl2 = regs.ipl_pin;
|
|
dr->evt2 |= DMA_EVENT2_IPLSAMPLE;
|
|
}
|
|
|
|
void record_dma_event(uae_u32 evt, int hpos, int vpos)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
if (!dma_record[0])
|
|
return;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->evt |= evt;
|
|
dr->ipl = regs.ipl_pin;
|
|
}
|
|
|
|
void record_dma_event2(uae_u32 evt2, int hpos, int vpos)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
if (!dma_record[0])
|
|
return;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->evt2 |= evt2;
|
|
dr->ipl = regs.ipl_pin;
|
|
}
|
|
|
|
void record_dma_event_data(uae_u32 evt, int hpos, int vpos, uae_u32 data)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
if (!dma_record[0])
|
|
return;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->evt |= evt;
|
|
dr->evtdata = data;
|
|
dr->evtdataset = true;
|
|
dr->ipl = regs.ipl_pin;
|
|
}
|
|
|
|
void record_dma_replace(int hpos, int vpos, int type, int extra)
|
|
{
|
|
struct dma_rec *dr;
|
|
if (!dma_record[0])
|
|
return;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
if (dr->reg == 0xffff) {
|
|
write_log(_T("DMA record replace without old data!\n"));
|
|
return;
|
|
}
|
|
if (dr->type != type) {
|
|
write_log(_T("DMA record replace type change %d -> %d!\n"), dr->type, type);
|
|
return;
|
|
}
|
|
dr->extra = extra;
|
|
}
|
|
|
|
static void dma_conflict(int vpos, int hpos, struct dma_rec *dr, int reg, bool write)
|
|
{
|
|
write_log(_T("DMA conflict %c: v=%d h=%d OREG=%04X NREG=%04X\n"), write ? 'W' : 'R', vpos, hpos, dr->reg, reg);
|
|
}
|
|
|
|
void record_dma_write(uae_u16 reg, uae_u32 dat, uae_u32 addr, int hpos, int vpos, int type, int extra)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
if (!dma_record[0]) {
|
|
dma_record_init();
|
|
}
|
|
|
|
last_dma_rec = NULL;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dma_record_frame[dma_record_toggle] = vsync_counter;
|
|
if (dr->reg != 0xffff) {
|
|
dr->cf_reg = reg;
|
|
dr->cf_dat = dat;
|
|
dr->cf_addr = addr;
|
|
dma_conflict(vpos, hp, dr, reg, false);
|
|
return;
|
|
}
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->reg = reg;
|
|
dr->dat = dat;
|
|
dr->addr = addr;
|
|
dr->type = type;
|
|
dr->extra = extra;
|
|
dr->intlev = regs.intmask;
|
|
dr->ipl = regs.ipl_pin;
|
|
dr->size = 2;
|
|
dr->end = false;
|
|
last_dma_rec = dr;
|
|
debug_mark_refreshed(dr->addr);
|
|
}
|
|
struct dma_rec *last_dma_rec;
|
|
void record_dma_read_value_pos(uae_u32 v, int hpos, int vpos)
|
|
{
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
struct dma_rec *dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
last_dma_rec = dr;
|
|
record_dma_read_value(v);
|
|
}
|
|
|
|
void record_dma_read_value(uae_u32 v)
|
|
{
|
|
if (last_dma_rec) {
|
|
if (last_dma_rec->cf_reg != 0xffff) {
|
|
last_dma_rec->cf_dat = v;
|
|
} else {
|
|
last_dma_rec->dat = v;
|
|
}
|
|
last_dma_rec->size = 2;
|
|
}
|
|
}
|
|
|
|
void record_dma_read_value_wide(uae_u64 v, bool quad)
|
|
{
|
|
if (last_dma_rec) {
|
|
if (last_dma_rec->cf_reg != 0xffff) {
|
|
last_dma_rec->cf_dat = (uae_u16)v;
|
|
} else {
|
|
last_dma_rec->dat = v;
|
|
}
|
|
last_dma_rec->size = quad ? 8 : 4;
|
|
}
|
|
}
|
|
|
|
bool record_dma_check(int hpos, int vpos)
|
|
{
|
|
if (!dma_record[0]) {
|
|
return false;
|
|
}
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS) {
|
|
return false;
|
|
}
|
|
struct dma_rec *dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
return dr->reg != 0xffff;
|
|
}
|
|
|
|
void record_dma_denise(int hpos, int dhpos)
|
|
{
|
|
if (!dma_record[0]) {
|
|
return;
|
|
}
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS) {
|
|
return;
|
|
}
|
|
struct dma_rec *dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dr->dhpos = dhpos;
|
|
}
|
|
|
|
void record_dma_clear(int hpos, int vpos)
|
|
{
|
|
if (!dma_record[0]) {
|
|
return;
|
|
}
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS) {
|
|
return;
|
|
}
|
|
struct dma_rec *dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dr->reg = 0xffff;
|
|
dr->cf_reg = 0xffff;
|
|
}
|
|
|
|
void record_cia_access(int r, int mask, uae_u16 value, bool rw, int hpos, int vpos, int phase)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
dma_record_init();
|
|
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dma_record_frame[dma_record_toggle] = vsync_counter;
|
|
|
|
if (dr->ciaphase < 0) {
|
|
return;
|
|
}
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->ciamask = mask;
|
|
dr->ciareg = r;
|
|
dr->ciavalue = value;
|
|
dr->ciarw = rw;
|
|
dr->ciaphase = phase;
|
|
}
|
|
|
|
void record_dma_read(uae_u16 reg, uae_u32 addr, int hpos, int vpos, int type, int extra)
|
|
{
|
|
struct dma_rec *dr;
|
|
int hp = hpos;
|
|
|
|
dma_record_init();
|
|
|
|
last_dma_rec = NULL;
|
|
hpos += dma_record_hoffset;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
|
|
dr = &dma_record[dma_record_toggle][vpos * NR_DMA_REC_HPOS + hpos];
|
|
dma_record_frame[dma_record_toggle] = vsync_counter;
|
|
if (dr->reg != 0xffff) {
|
|
if (dr->reg != reg) {
|
|
dma_conflict(vpos, hp, dr, reg, false);
|
|
dr->cf_reg = reg;
|
|
dr->cf_addr = addr;
|
|
}
|
|
return;
|
|
}
|
|
dr->hpos = hp;
|
|
dr->vpos = vpos;
|
|
dr->reg = reg;
|
|
dr->dat = 0;
|
|
dr->addr = addr;
|
|
dr->type = type;
|
|
dr->extra = extra;
|
|
dr->intlev = regs.intmask;
|
|
dr->ipl = regs.ipl_pin;
|
|
dr->end = false;
|
|
|
|
last_dma_rec = dr;
|
|
debug_mark_refreshed(dr->addr);
|
|
}
|
|
|
|
static bool get_record_dma_info(struct dma_rec *drs, struct dma_rec *dr, TCHAR *l1, TCHAR *l2, TCHAR *l3, TCHAR *l4, TCHAR *l5, TCHAR *l6, uae_u32 *split, int *iplp)
|
|
{
|
|
int longsize = dr->size;
|
|
bool got = false;
|
|
int r = dr->reg;
|
|
int regsize = 3;
|
|
const TCHAR *sr;
|
|
int br = dr->extra & 7;
|
|
int chcnt = -1;
|
|
TCHAR srtext[10];
|
|
bool extra64 = false;
|
|
uae_u32 extraval;
|
|
bool noval = false;
|
|
|
|
if (l1)
|
|
l1[0] = 0;
|
|
if (l2)
|
|
l2[0] = 0;
|
|
if (l3)
|
|
l3[0] = 0;
|
|
if (l4)
|
|
l4[0] = 0;
|
|
if (l5)
|
|
l5[0] = 0;
|
|
if (l6)
|
|
l6[0] = 0;
|
|
|
|
int hpos = dr->hpos;
|
|
int dhpos = dr->dhpos;
|
|
if (hpos < 0) {
|
|
struct dma_rec *dr2 = dr;
|
|
int cnt = 0;
|
|
while (!dr2->end) {
|
|
if (dr2 == drs) {
|
|
hpos = addrdiff(dr, drs);
|
|
break;
|
|
}
|
|
if (dr2->hpos >= 0) {
|
|
hpos = dr2->hpos + cnt;
|
|
break;
|
|
}
|
|
cnt++;
|
|
dr2--;
|
|
}
|
|
}
|
|
if (hpos < 0) {
|
|
hpos = 0;
|
|
}
|
|
|
|
if (split) {
|
|
if ((dr->evt & DMA_EVENT_CPUINS) && dr->evtdataset) {
|
|
*split = dr->evtdata;
|
|
}
|
|
}
|
|
|
|
if (dr->type != 0 || dr->reg != 0xffff || dr->evt)
|
|
got = true;
|
|
|
|
sr = _T(" ");
|
|
if (dr->type == DMARECORD_COPPER) {
|
|
if (br == 2)
|
|
sr = _T("COPS");
|
|
else if (br == 1)
|
|
sr = _T("COPW");
|
|
else
|
|
sr = _T("COP ");
|
|
} else if (dr->type == DMARECORD_BLITTER) {
|
|
if (dr->extra & 0x20) {
|
|
if (br == 0)
|
|
sr = _T("BLL-A");
|
|
if (br == 1)
|
|
sr = _T("BLL-B");
|
|
if (br == 2)
|
|
sr = _T("BLL-C");
|
|
if (br == 3)
|
|
sr = _T("BLL-D");
|
|
} else if (dr->extra & 0x10) {
|
|
if (br == 0)
|
|
sr = _T("BLF-A");
|
|
if (br == 1)
|
|
sr = _T("BLF-B");
|
|
if (br == 2)
|
|
sr = _T("BLF-C");
|
|
if (br == 3)
|
|
sr = _T("BLF-D");
|
|
} else {
|
|
if (br == 0)
|
|
sr = _T("BLT-A");
|
|
if (br == 1)
|
|
sr = _T("BLT-B");
|
|
if (br == 2)
|
|
sr = _T("BLT-C");
|
|
if (br == 3)
|
|
sr = _T("BLT-D");
|
|
}
|
|
regsize = 2;
|
|
} else if (dr->type == DMARECORD_REFRESH) {
|
|
sr = _T("RFS");
|
|
chcnt = br;
|
|
noval = true;
|
|
} else if (dr->type == DMARECORD_AUDIO) {
|
|
sr = _T("AUD");
|
|
chcnt = br;
|
|
} else if (dr->type == DMARECORD_DISK) {
|
|
sr = _T("DSK");
|
|
chcnt = br;
|
|
} else if (dr->type == DMARECORD_SPRITE) {
|
|
sr = _T("SPR");
|
|
chcnt = br;
|
|
} else if (dr->type == DMARECORD_BITPLANE) {
|
|
sr = _T("BPL");
|
|
chcnt = br + 1;
|
|
}
|
|
if (dr->cf_reg != 0xffff) {
|
|
_stprintf(srtext, _T("!%03x"), dr->cf_reg);
|
|
chcnt = -1;
|
|
regsize--;
|
|
} else {
|
|
_tcscpy(srtext, sr);
|
|
}
|
|
int ipl = 0;
|
|
if (iplp) {
|
|
ipl = *iplp;
|
|
if (dr->ipl > 0) {
|
|
ipl = dr->ipl;
|
|
} else if (dr->ipl < 0) {
|
|
ipl = 0;
|
|
}
|
|
*iplp = ipl;
|
|
if (dr->ipl2 > 0) {
|
|
}
|
|
}
|
|
if (ipl >= 0) {
|
|
_stprintf(l1, _T("[%02X %03X %d]"), hpos, dhpos, ipl);
|
|
} else if (ipl == -2) {
|
|
_stprintf(l1, _T("[%02X -]"), hpos);
|
|
} else {
|
|
_stprintf(l1, _T("[%02X ]"), hpos);
|
|
}
|
|
if (l4) {
|
|
_tcscpy(l4, _T(" "));
|
|
}
|
|
if (l2) {
|
|
_tcscpy(l2, _T(" "));
|
|
}
|
|
if (l3) {
|
|
_tcscpy(l3, _T(" "));
|
|
}
|
|
if (r != 0xffff) {
|
|
if (r & 0x1000) {
|
|
if ((r & 0x0100) == 0x0000)
|
|
_tcscpy(l2, _T("CPU-R "));
|
|
else if ((r & 0x0100) == 0x0100)
|
|
_tcscpy(l2, _T("CPU-W "));
|
|
if ((r & 0xff) == 4) {
|
|
l2[5] = 'L';
|
|
longsize = 4;
|
|
}
|
|
if ((r & 0xff) == 2) {
|
|
l2[5] = 'W';
|
|
}
|
|
if ((r & 0xff) == 1) {
|
|
l2[5] = 'B';
|
|
}
|
|
if (br) {
|
|
l2[6] = 'D';
|
|
} else {
|
|
l2[6] = 'I';
|
|
}
|
|
} else {
|
|
if (chcnt >= 0) {
|
|
if (regsize == 3)
|
|
_stprintf(l2, _T("%3s%d %03X"), srtext, chcnt, r);
|
|
else if (regsize == 2)
|
|
_stprintf(l2, _T("%4s%d %02X"), srtext, chcnt, r);
|
|
else
|
|
_stprintf(l2, _T("%5s%d %02X"), srtext, chcnt, r);
|
|
} else {
|
|
if (regsize == 3)
|
|
_stprintf(l2, _T("%4s %03X"), srtext, r);
|
|
else if (regsize == 2)
|
|
_stprintf(l2, _T("%5s %02X"), srtext, r);
|
|
else
|
|
_stprintf(l2, _T("%6s %02X"), srtext, r);
|
|
}
|
|
}
|
|
if (l3 && !noval) {
|
|
uae_u64 v = dr->dat;
|
|
if (longsize == 4) {
|
|
_stprintf(l3, _T("%08X"), (uae_u32)v);
|
|
} else if (longsize == 8) {
|
|
_stprintf(l3, _T("%08X"), (uae_u32)(v >> 32));
|
|
extra64 = true;
|
|
extraval = (uae_u32)v;
|
|
} else {
|
|
_stprintf(l3, _T(" %04X"), (uae_u32)(v & 0xffff));
|
|
}
|
|
}
|
|
if (l4 && dr->addr != 0xffffffff)
|
|
_stprintf (l4, _T("%08X"), dr->addr & 0x00ffffff);
|
|
}
|
|
if (l3) {
|
|
int cl2 = 0;
|
|
if (dr->evt & DMA_EVENT_BLITFINALD)
|
|
l3[cl2++] = 'D';
|
|
if (dr->evt & DMA_EVENT_BLITSTARTFINISH)
|
|
l3[cl2++] = 'B';
|
|
if (dr->evt & DMA_EVENT_CPUBLITTERSTEAL)
|
|
l3[cl2++] = 's';
|
|
if (dr->evt & DMA_EVENT_CPUBLITTERSTOLEN)
|
|
l3[cl2++] = 'S';
|
|
if (dr->evt & DMA_EVENT_BLITIRQ)
|
|
l3[cl2++] = 'b';
|
|
if (dr->evt & DMA_EVENT_BPLFETCHUPDATE)
|
|
l3[cl2++] = 'p';
|
|
if (dr->evt & (DMA_EVENT_COPPERWAKE | DMA_EVENT_COPPERSKIP))
|
|
l3[cl2++] = 'W';
|
|
if (dr->evt & DMA_EVENT_NOONEGETS) {
|
|
l3[cl2++] = '#';
|
|
} else if (dr->evt & DMA_EVENT_COPPERWANTED) {
|
|
l3[cl2++] = 'c';
|
|
}
|
|
if (dr->evt & DMA_EVENT_CPUIRQ)
|
|
l3[cl2++] = 'I';
|
|
if (dr->evt & DMA_EVENT_CPUSTOP)
|
|
l3[cl2++] = '|';
|
|
if (dr->evt & DMA_EVENT_CPUSTOPIPL)
|
|
l3[cl2++] = '+';
|
|
if (dr->evt & DMA_EVENT_INTREQ)
|
|
l3[cl2++] = 'i';
|
|
if (dr->evt & DMA_EVENT_SPECIAL)
|
|
l3[cl2++] = 'X';
|
|
if (dr->evt & DMA_EVENT_DDFSTRT)
|
|
l3[cl2++] = '0';
|
|
if (dr->evt & DMA_EVENT_DDFSTOP)
|
|
l3[cl2++] = '1';
|
|
if (dr->evt & DMA_EVENT_DDFSTOP2)
|
|
l3[cl2++] = '2';
|
|
|
|
if (dr->evt & DMA_EVENT_HDIWS) {
|
|
l3[cl2++] = '(';
|
|
}
|
|
if (dr->evt & DMA_EVENT_HDIWE) {
|
|
l3[cl2++] = ')';
|
|
}
|
|
|
|
if (dr->evt & DMA_EVENT_HBS) {
|
|
l3[cl2++] = '[';
|
|
}
|
|
if (dr->evt & DMA_EVENT_HBE) {
|
|
l3[cl2++] = ']';
|
|
}
|
|
if (dr->evt & DMA_EVENT_HSS) {
|
|
l3[cl2++] = '{';
|
|
}
|
|
if (dr->evt & DMA_EVENT_HSE) {
|
|
l3[cl2++] = '}';
|
|
}
|
|
|
|
if (dr->evt & (DMA_EVENT_VB | DMA_EVENT_VS | DMA_EVENT_LOL | DMA_EVENT_LOF | DMA_EVENT_VDIW)) {
|
|
l3[cl2++] = '*';
|
|
}
|
|
if (dr->evt & DMA_EVENT_VS) {
|
|
l3[cl2++] = 'S';
|
|
}
|
|
if (dr->evt & DMA_EVENT_VB) {
|
|
l3[cl2++] = 'B';
|
|
}
|
|
if (dr->evt & DMA_EVENT_LOL) {
|
|
l3[cl2++] = 'L';
|
|
}
|
|
if (dr->evt & DMA_EVENT_LOF) {
|
|
l3[cl2++] = 'F';
|
|
}
|
|
if (dr->evt & DMA_EVENT_VDIW) {
|
|
l3[cl2++] = '=';
|
|
}
|
|
if (dr->evt & (DMA_EVENT_VB | DMA_EVENT_VS | DMA_EVENT_LOL | DMA_EVENT_LOF | DMA_EVENT_VDIW)) {
|
|
l3[cl2++] = 0;
|
|
}
|
|
|
|
if (dr->evt & (DMA_EVENT_CIAA_IRQ | DMA_EVENT_CIAB_IRQ)) {
|
|
l3[cl2++] = '#';
|
|
}
|
|
if (dr->evt & DMA_EVENT_CIAA_IRQ) {
|
|
l3[cl2++] = 'A';
|
|
}
|
|
if (dr->evt & DMA_EVENT_CIAB_IRQ) {
|
|
l3[cl2++] = 'B';
|
|
}
|
|
|
|
if (dr->evt2 & DMA_EVENT2_IPLSAMPLE) {
|
|
l3[cl2++] = '^';
|
|
}
|
|
if (dr->evt2 & DMA_EVENT2_COPPERUSE) {
|
|
l3[cl2++] = 'C';
|
|
}
|
|
|
|
}
|
|
if (l5) {
|
|
if (dr->ciaphase) {
|
|
if (dr->ciamask) {
|
|
_stprintf(l5, _T("%c%s%X %04X"), dr->ciarw ? 'W' : 'R',
|
|
dr->ciamask == 1 ? _T("A") : (dr->ciamask == 2 ? _T("B") : _T("X")),
|
|
dr->ciareg, dr->ciavalue);
|
|
} else {
|
|
int ph = dr->ciaphase;
|
|
if (ph >= 100) {
|
|
_tcscpy(l5, _T(" - "));
|
|
} else {
|
|
_stprintf(l5, _T(" %u "), ph - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (l6) {
|
|
if (dr->addr != 0xffffffff) {
|
|
int ras, cas;
|
|
TCHAR xtra = ' ';
|
|
bool ret = get_ras_cas(dr->addr, &ras, &cas);
|
|
if (ret) {
|
|
xtra = '+';
|
|
}
|
|
_stprintf(l6, _T("%c%03X %03X"), xtra, ras, cas);
|
|
} else {
|
|
l6[0] = 0;
|
|
}
|
|
}
|
|
if (extra64) {
|
|
_tcscpy(l6, l4);
|
|
_stprintf(l4, _T("%08X"), extraval);
|
|
}
|
|
return got;
|
|
}
|
|
|
|
|
|
|
|
static void decode_dma_record(int hpos, int vpos, int count, int toggle, bool logfile)
|
|
{
|
|
struct dma_rec *dr, *dr_start;
|
|
int h, i, maxh = 0;
|
|
|
|
if (!dma_record[0] || hpos < 0 || vpos < 0)
|
|
return;
|
|
if (hpos >= NR_DMA_REC_HPOS || vpos >= NR_DMA_REC_VPOS)
|
|
return;
|
|
dr_start = dr = &dma_record[dma_record_toggle ^ toggle][vpos * NR_DMA_REC_HPOS];
|
|
if (logfile)
|
|
write_dlog (_T("Line: %02X %3d HPOS %02X %3d:\n"), vpos, vpos, hpos, hpos);
|
|
else
|
|
console_out_f (_T("Line: %02X %3d HPOS %02X %3d:\n"), vpos, vpos, hpos, hpos);
|
|
h = hpos;
|
|
while (maxh < NR_DMA_REC_HPOS) {
|
|
if (dr->end)
|
|
break;
|
|
maxh++;
|
|
dr++;
|
|
}
|
|
dr = dr_start + hpos;
|
|
if (!logfile && maxh - h > 80) {
|
|
int maxh2 = maxh;
|
|
maxh = h + 80;
|
|
if (maxh > maxh2) {
|
|
maxh = maxh2;
|
|
}
|
|
}
|
|
int ipl = -2;
|
|
while (h < maxh) {
|
|
int cols = (logfile ? 16 : 8);
|
|
TCHAR l1[400];
|
|
TCHAR l2[400];
|
|
TCHAR l3[400];
|
|
TCHAR l4[400];
|
|
TCHAR l5[400];
|
|
TCHAR l6[400];
|
|
l1[0] = 0;
|
|
l2[0] = 0;
|
|
l3[0] = 0;
|
|
l4[0] = 0;
|
|
l5[0] = 0;
|
|
l6[0] = 0;
|
|
for (i = 0; i < cols && h < maxh; i++, h++, dr++) {
|
|
TCHAR l1l[16], l2l[16], l3l[16], l4l[16], l5l[16], l6l[16];
|
|
uae_u32 split = 0xffffffff;
|
|
|
|
get_record_dma_info(dr_start, dr, l1l, l2l, l3l, l4l, l5l, l6l, &split, &ipl);
|
|
|
|
TCHAR *p = l1 + _tcslen(l1);
|
|
_stprintf(p, _T("%11s "), l1l);
|
|
p = l2 + _tcslen(l2);
|
|
_stprintf(p, _T("%11s "), l2l);
|
|
p = l3 + _tcslen(l3);
|
|
_stprintf(p, _T("%11s "), l3l);
|
|
p = l4 + _tcslen(l4);
|
|
_stprintf(p, _T("%11s "), l4l);
|
|
p = l5 + _tcslen(l5);
|
|
_stprintf(p, _T("%11s "), l5l);
|
|
p = l6 + _tcslen(l6);
|
|
_stprintf(p, _T("%11s "), l6l);
|
|
|
|
if (split != 0xffffffff) {
|
|
if (split < 0x10000) {
|
|
struct instr *dp = table68k + split;
|
|
if (dp->mnemo == i_ILLG) {
|
|
split = 0x4AFC;
|
|
dp = table68k + split;
|
|
}
|
|
struct mnemolookup *lookup;
|
|
for (lookup = lookuptab; lookup->mnemo != dp->mnemo; lookup++)
|
|
;
|
|
const TCHAR *opcodename = lookup->friendlyname;
|
|
if (!opcodename) {
|
|
opcodename = lookup->name;
|
|
}
|
|
TCHAR *ptrs[7];
|
|
ptrs[0] = &l1[_tcslen(l1)];
|
|
ptrs[1] = &l2[_tcslen(l2)];
|
|
ptrs[2] = &l3[_tcslen(l3)];
|
|
ptrs[3] = &l4[_tcslen(l4)];
|
|
ptrs[4] = &l5[_tcslen(l5)];
|
|
ptrs[5] = &l6[_tcslen(l6)];
|
|
for (int i = 0; i < 6; i++) {
|
|
if (!opcodename[i]) {
|
|
break;
|
|
}
|
|
TCHAR *p = ptrs[i];
|
|
p[-1] = opcodename[i];
|
|
}
|
|
} else {
|
|
l1[_tcslen(l1) - 1] = '*';
|
|
}
|
|
}
|
|
}
|
|
if (logfile) {
|
|
write_dlog(_T("%s\n"), l1);
|
|
write_dlog(_T("%s\n"), l2);
|
|
write_dlog(_T("%s\n"), l3);
|
|
write_dlog(_T("%s\n"), l4);
|
|
write_dlog(_T("%s\n"), l5);
|
|
write_dlog(_T("%s\n"), l6);
|
|
write_dlog(_T("\n"));
|
|
} else {
|
|
console_out_f(_T("%s\n"), l1);
|
|
console_out_f(_T("%s\n"), l2);
|
|
console_out_f(_T("%s\n"), l3);
|
|
console_out_f(_T("%s\n"), l4);
|
|
console_out_f(_T("%s\n"), l5);
|
|
console_out_f(_T("%s\n"), l6);
|
|
console_out_f(_T("\n"));
|
|
}
|
|
if (count > 0) {
|
|
count--;
|
|
if (!count) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (logfile)
|
|
flush_log();
|
|
}
|
|
|
|
void log_dma_record (void)
|
|
{
|
|
if (!input_record && !input_play)
|
|
return;
|
|
if (!debug_dma)
|
|
return;
|
|
decode_dma_record (0, 0, 0, 0, true);
|
|
}
|
|
|
|
static void init_record_copper(void)
|
|
{
|
|
if (!cop_record[0]) {
|
|
cop_record[0] = xmalloc(struct cop_rec, NR_COPPER_RECORDS);
|
|
cop_record[1] = xmalloc(struct cop_rec, NR_COPPER_RECORDS);
|
|
}
|
|
}
|
|
|
|
void record_copper_blitwait (uaecptr addr, int hpos, int vpos)
|
|
{
|
|
int t = nr_cop_records[curr_cop_set];
|
|
init_record_copper();
|
|
cop_record[curr_cop_set][t].bhpos = hpos;
|
|
cop_record[curr_cop_set][t].bvpos = vpos;
|
|
}
|
|
|
|
void record_copper (uaecptr addr, uaecptr nextaddr, uae_u16 word1, uae_u16 word2, int hpos, int vpos)
|
|
{
|
|
int t = nr_cop_records[curr_cop_set];
|
|
init_record_copper();
|
|
if (t < NR_COPPER_RECORDS) {
|
|
cop_record[curr_cop_set][t].addr = addr;
|
|
cop_record[curr_cop_set][t].nextaddr = nextaddr;
|
|
cop_record[curr_cop_set][t].w1 = word1;
|
|
cop_record[curr_cop_set][t].w2 = word2;
|
|
cop_record[curr_cop_set][t].hpos = hpos;
|
|
cop_record[curr_cop_set][t].vpos = vpos;
|
|
cop_record[curr_cop_set][t].bvpos = -1;
|
|
nr_cop_records[curr_cop_set] = t + 1;
|
|
}
|
|
if (debug_copper & 2) { /* trace */
|
|
debug_copper &= ~2;
|
|
activate_debugger_new();
|
|
}
|
|
if ((debug_copper & 4) && addr >= debug_copper_pc && addr <= debug_copper_pc + 3) {
|
|
debug_copper &= ~4;
|
|
activate_debugger_new();
|
|
}
|
|
}
|
|
|
|
static struct cop_rec *find_copper_records(uaecptr addr)
|
|
{
|
|
int s = selected_cop_set;
|
|
int t = nr_cop_records[s];
|
|
int i;
|
|
for (i = 0; i < t; i++) {
|
|
if (cop_record[s][i].addr == addr)
|
|
return &cop_record[s][i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* simple decode copper by Mark Cox */
|
|
static uaecptr decode_copper_insn(FILE *file, uae_u16 mword1, uae_u16 mword2, uaecptr addr)
|
|
{
|
|
struct cop_rec *cr = NULL;
|
|
uae_u32 insn_type, insn;
|
|
TCHAR here = ' ';
|
|
TCHAR record[] = _T(" ");
|
|
|
|
if ((cr = find_copper_records(addr))) {
|
|
_stprintf(record, _T(" [%03x %03x]"), cr->vpos, cr->hpos);
|
|
insn = (cr->w1 << 16) | cr->w2;
|
|
} else {
|
|
insn = (mword1 << 16) | mword2;
|
|
}
|
|
|
|
insn_type = insn & 0x00010001;
|
|
|
|
if (get_copper_address(-1) >= addr && get_copper_address(-1) <= addr + 3)
|
|
here = '*';
|
|
|
|
console_out_f (_T("%c%08x: %04x %04x%s\t;%c "), here, addr, insn >> 16, insn & 0xFFFF, record, insn != ((mword1 << 16) | mword2) ? '!' : ' ');
|
|
|
|
switch (insn_type) {
|
|
case 0x00010000: /* WAIT insn */
|
|
console_out(_T("Wait for "));
|
|
disassemble_wait(file, insn);
|
|
|
|
if (insn == 0xfffffffe)
|
|
console_out(_T(" \t; End of Copperlist\n"));
|
|
|
|
break;
|
|
|
|
case 0x00010001: /* SKIP insn */
|
|
console_out(_T("Skip if "));
|
|
disassemble_wait(file, insn);
|
|
break;
|
|
|
|
case 0x00000000:
|
|
case 0x00000001: /* MOVE insn */
|
|
{
|
|
int addr = (insn >> 16) & 0x1fe;
|
|
int i = 0;
|
|
while (custd[i].name) {
|
|
if (custd[i].adr == addr + 0xdff000)
|
|
break;
|
|
i++;
|
|
}
|
|
if (custd[i].name)
|
|
console_out_f(_T("%s := 0x%04x\n"), custd[i].name, insn & 0xffff);
|
|
else
|
|
console_out_f(_T("%04x := 0x%04x\n"), addr, insn & 0xffff);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
abort ();
|
|
}
|
|
|
|
if (cr && cr->bvpos >= 0) {
|
|
console_out_f(_T(" BLT [%03x %03x]\n"), cr->bvpos, cr->bhpos);
|
|
}
|
|
if (cr && cr->nextaddr != 0xffffffff && cr->nextaddr != addr + 4) {
|
|
console_out_f(_T(" %08x: Copper jump\n"), cr->nextaddr);
|
|
return cr->nextaddr;
|
|
}
|
|
return addr + 4;
|
|
}
|
|
|
|
static uaecptr decode_copperlist(FILE *file, uaecptr address, int nolines)
|
|
{
|
|
uaecptr next;
|
|
while (nolines-- > 0) {
|
|
next = decode_copper_insn(file, chipmem_wget_indirect(address), chipmem_wget_indirect(address + 2), address);
|
|
address = next;
|
|
}
|
|
return address;
|
|
/* You may wonder why I don't stop this at the end of the copperlist?
|
|
* Well, often nice things are hidden at the end and it is debatable the actual
|
|
* values that mean the end of the copperlist */
|
|
}
|
|
|
|
static int copper_debugger (TCHAR **c)
|
|
{
|
|
static uaecptr nxcopper;
|
|
uae_u32 maddr;
|
|
int lines;
|
|
|
|
if (**c == 'd') {
|
|
next_char (c);
|
|
if (debug_copper)
|
|
debug_copper = 0;
|
|
else
|
|
debug_copper = 1;
|
|
console_out_f (_T("Copper debugger %s.\n"), debug_copper ? _T("enabled") : _T("disabled"));
|
|
} else if (**c == 't') {
|
|
debug_copper = 1|2;
|
|
return 1;
|
|
} else if (**c == 'b') {
|
|
(*c)++;
|
|
debug_copper = 1|4;
|
|
if (more_params (c)) {
|
|
debug_copper_pc = readhex(c, NULL);
|
|
console_out_f (_T("Copper breakpoint @0x%08x\n"), debug_copper_pc);
|
|
} else {
|
|
debug_copper &= ~4;
|
|
}
|
|
} else {
|
|
if (more_params(c)) {
|
|
maddr = readhex(c, NULL);
|
|
if (maddr == 1 || maddr == 2 || maddr == 3)
|
|
maddr = get_copper_address(maddr);
|
|
else if (maddr == 0)
|
|
maddr = get_copper_address(-1);
|
|
} else {
|
|
maddr = nxcopper;
|
|
}
|
|
selected_cop_set = curr_cop_set;
|
|
if (!find_copper_records(maddr)) {
|
|
selected_cop_set = curr_cop_set ^ 1;
|
|
}
|
|
|
|
if (more_params(c))
|
|
lines = readhex(c, NULL);
|
|
else
|
|
lines = 20;
|
|
|
|
nxcopper = decode_copperlist (stdout, maddr, lines);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define MAX_CHEAT_VIEW 100
|
|
struct trainerstruct {
|
|
uaecptr addr;
|
|
int size;
|
|
};
|
|
|
|
static struct trainerstruct *trainerdata;
|
|
static int totaltrainers;
|
|
|
|
static void clearcheater(void)
|
|
{
|
|
if (!trainerdata)
|
|
trainerdata = xmalloc(struct trainerstruct, MAX_CHEAT_VIEW);
|
|
memset(trainerdata, 0, sizeof (struct trainerstruct) * MAX_CHEAT_VIEW);
|
|
totaltrainers = 0;
|
|
}
|
|
static int addcheater(uaecptr addr, int size)
|
|
{
|
|
if (totaltrainers >= MAX_CHEAT_VIEW)
|
|
return 0;
|
|
trainerdata[totaltrainers].addr = addr;
|
|
trainerdata[totaltrainers].size = size;
|
|
totaltrainers++;
|
|
return 1;
|
|
}
|
|
static void listcheater(int mode, int size)
|
|
{
|
|
int i, skip;
|
|
|
|
if (!trainerdata)
|
|
return;
|
|
if (mode)
|
|
skip = 4;
|
|
else
|
|
skip = 8;
|
|
for(i = 0; i < totaltrainers; i++) {
|
|
struct trainerstruct *ts = &trainerdata[i];
|
|
uae_u16 b;
|
|
|
|
if (size) {
|
|
b = get_byte_debug(ts->addr);
|
|
} else {
|
|
b = get_word_debug(ts->addr);
|
|
}
|
|
if (mode)
|
|
console_out_f(_T("%08X=%04X "), ts->addr, b);
|
|
else
|
|
console_out_f(_T("%08X "), ts->addr);
|
|
if ((i % skip) == skip)
|
|
console_out(_T("\n"));
|
|
}
|
|
}
|
|
|
|
static void deepcheatsearch(TCHAR **c)
|
|
{
|
|
static int first = 1;
|
|
static uae_u8 *memtmp;
|
|
static int memsize, memsize2;
|
|
uae_u8 *p1, *p2;
|
|
uaecptr addr, end;
|
|
int i, wasmodified, nonmodified;
|
|
static int size;
|
|
static int inconly, deconly, maxdiff;
|
|
int addrcnt, cnt;
|
|
TCHAR v;
|
|
|
|
v = _totupper(**c);
|
|
|
|
if(!memtmp || v == 'S') {
|
|
maxdiff = 0x10000;
|
|
inconly = 0;
|
|
deconly = 0;
|
|
size = 1;
|
|
}
|
|
|
|
if (**c)
|
|
(*c)++;
|
|
ignore_ws(c);
|
|
if ((**c) == '1' || (**c) == '2') {
|
|
size = **c - '0';
|
|
(*c)++;
|
|
}
|
|
if (more_params(c))
|
|
maxdiff = readint(c, NULL);
|
|
|
|
if (!memtmp || v == 'S') {
|
|
first = 1;
|
|
xfree (memtmp);
|
|
memsize = 0;
|
|
addr = 0xffffffff;
|
|
nextaddr_init(addr);
|
|
while ((addr = nextaddr(addr, 0, &end, false, NULL)) != 0xffffffff) {
|
|
memsize += end - addr;
|
|
addr = end - 1;
|
|
}
|
|
memsize2 = (memsize + 7) / 8;
|
|
memtmp = xmalloc(uae_u8, memsize + memsize2);
|
|
if (!memtmp)
|
|
return;
|
|
memset(memtmp + memsize, 0xff, memsize2);
|
|
p1 = memtmp;
|
|
addr = 0xffffffff;
|
|
nextaddr_init(addr);
|
|
while ((addr = nextaddr(addr, 0, &end, true, NULL)) != 0xffffffff) {
|
|
for (i = addr; i < end; i++)
|
|
*p1++ = get_byte_debug(i);
|
|
addr = end - 1;
|
|
}
|
|
console_out(_T("Deep trainer first pass complete.\n"));
|
|
return;
|
|
}
|
|
inconly = deconly = 0;
|
|
wasmodified = v == 'X' ? 0 : 1;
|
|
nonmodified = v == 'Z' ? 1 : 0;
|
|
if (v == 'I')
|
|
inconly = 1;
|
|
if (v == 'D')
|
|
deconly = 1;
|
|
p1 = memtmp;
|
|
p2 = memtmp + memsize;
|
|
addrcnt = 0;
|
|
cnt = 0;
|
|
addr = 0xffffffff;
|
|
nextaddr_init(addr);
|
|
while ((addr = nextaddr(addr, 0, NULL, true, NULL)) != 0xffffffff) {
|
|
uae_s32 b, b2;
|
|
int doremove = 0;
|
|
int addroff;
|
|
int addrmask ;
|
|
|
|
if (size == 1) {
|
|
b = (uae_s8)get_byte_debug(addr);
|
|
b2 = (uae_s8)p1[addrcnt];
|
|
addroff = addrcnt >> 3;
|
|
addrmask = 1 << (addrcnt & 7);
|
|
} else {
|
|
b = (uae_s16)get_word_debug(addr);
|
|
b2 = (uae_s16)((p1[addrcnt] << 8) | p1[addrcnt + 1]);
|
|
addroff = addrcnt >> 2;
|
|
addrmask = 3 << (addrcnt & 3);
|
|
}
|
|
|
|
if (p2[addroff] & addrmask) {
|
|
if (wasmodified && !nonmodified) {
|
|
int diff = b - b2;
|
|
if (b == b2)
|
|
doremove = 1;
|
|
if (abs(diff) > maxdiff)
|
|
doremove = 1;
|
|
if (inconly && diff < 0)
|
|
doremove = 1;
|
|
if (deconly && diff > 0)
|
|
doremove = 1;
|
|
} else if (nonmodified && b == b2) {
|
|
doremove = 1;
|
|
} else if (!wasmodified && b != b2) {
|
|
doremove = 1;
|
|
}
|
|
if (doremove)
|
|
p2[addroff] &= ~addrmask;
|
|
else
|
|
cnt++;
|
|
}
|
|
if (size == 1) {
|
|
p1[addrcnt] = b;
|
|
addrcnt++;
|
|
} else {
|
|
p1[addrcnt] = b >> 8;
|
|
p1[addrcnt + 1] = b >> 0;
|
|
addr = nextaddr(addr, 0, NULL, true, NULL);
|
|
if (addr == 0xffffffff)
|
|
break;
|
|
addrcnt++;
|
|
}
|
|
if (iscancel(65536)) {
|
|
console_out_f(_T("Aborted at %08X\n"), addr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
console_out_f(_T("%d addresses found\n"), cnt);
|
|
if (cnt <= MAX_CHEAT_VIEW) {
|
|
clearcheater();
|
|
cnt = 0;
|
|
addrcnt = 0;
|
|
addr = 0xffffffff;
|
|
while ((addr = nextaddr(addr, 0, NULL, true, NULL)) != 0xffffffff) {
|
|
int addroff = addrcnt >> (size == 1 ? 3 : 2);
|
|
int addrmask = (size == 1 ? 1 : 3) << (addrcnt & (size == 1 ? 7 : 3));
|
|
if (p2[addroff] & addrmask)
|
|
addcheater(addr, size);
|
|
if (size == 2) {
|
|
addr = nextaddr(addr, 0, NULL, true, NULL);
|
|
if (addr == 0xffffffff) {
|
|
break;
|
|
}
|
|
}
|
|
addrcnt++;
|
|
cnt++;
|
|
}
|
|
if (cnt > 0)
|
|
console_out(_T("\n"));
|
|
listcheater(1, size);
|
|
} else {
|
|
console_out(_T("Now continue with 'g' and use 'D' again after you have lost another life\n"));
|
|
}
|
|
}
|
|
|
|
/* cheat-search by Toni Wilen (originally by Holger Jakob) */
|
|
static void cheatsearch (TCHAR **c)
|
|
{
|
|
static uae_u8 *vlist;
|
|
static int listsize;
|
|
static int first = 1;
|
|
static int size = 1;
|
|
uae_u32 val, memcnt, prevmemcnt;
|
|
int i, count, vcnt, memsize;
|
|
uaecptr addr, end;
|
|
bool err;
|
|
|
|
memsize = 0;
|
|
addr = 0xffffffff;
|
|
nextaddr_init(addr);
|
|
while ((addr = nextaddr(addr, 0, &end, false, NULL)) != 0xffffffff) {
|
|
memsize += end - addr;
|
|
addr = end - 1;
|
|
}
|
|
|
|
if (_totupper (**c) == 'L') {
|
|
listcheater (1, size);
|
|
return;
|
|
}
|
|
ignore_ws (c);
|
|
if (!more_params (c)) {
|
|
first = 1;
|
|
console_out (_T("Search reset\n"));
|
|
xfree (vlist);
|
|
listsize = memsize;
|
|
vlist = xcalloc (uae_u8, listsize >> 3);
|
|
return;
|
|
}
|
|
if (first)
|
|
val = readint(c, &size, &err);
|
|
else
|
|
val = readint(c, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
|
|
if (vlist == NULL) {
|
|
listsize = memsize;
|
|
vlist = xcalloc (uae_u8, listsize >> 3);
|
|
}
|
|
|
|
count = 0;
|
|
vcnt = 0;
|
|
|
|
clearcheater ();
|
|
addr = 0xffffffff;
|
|
nextaddr_init(addr);
|
|
prevmemcnt = memcnt = 0;
|
|
while ((addr = nextaddr(addr, 0, &end, true, NULL)) != 0xffffffff) {
|
|
if (addr + size < end) {
|
|
for (i = 0; i < size; i++) {
|
|
int shift = (size - i - 1) * 8;
|
|
if (get_byte_debug (addr + i) != ((val >> shift) & 0xff))
|
|
break;
|
|
}
|
|
if (i == size) {
|
|
int voffset = memcnt >> 3;
|
|
int vmask = 1 << (memcnt & 7);
|
|
if (!first) {
|
|
while (prevmemcnt < memcnt) {
|
|
vlist[prevmemcnt >> 3] &= ~(1 << (prevmemcnt & 7));
|
|
prevmemcnt++;
|
|
}
|
|
if (vlist[voffset] & vmask) {
|
|
count++;
|
|
addcheater(addr, size);
|
|
} else {
|
|
vlist[voffset] &= ~vmask;
|
|
}
|
|
prevmemcnt = memcnt + 1;
|
|
} else {
|
|
vlist[voffset] |= vmask;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
memcnt++;
|
|
if (iscancel (65536)) {
|
|
console_out_f (_T("Aborted at %08X\n"), addr);
|
|
break;
|
|
}
|
|
}
|
|
if (!first) {
|
|
while (prevmemcnt < memcnt) {
|
|
vlist[prevmemcnt >> 3] &= ~(1 << (prevmemcnt & 7));
|
|
prevmemcnt++;
|
|
}
|
|
listcheater (0, size);
|
|
}
|
|
console_out_f (_T("Found %d possible addresses with 0x%X (%u) (%d bytes)\n"), count, val, val, size);
|
|
if (count > 0)
|
|
console_out (_T("Now continue with 'g' and use 'C' with a different value\n"));
|
|
first = 0;
|
|
}
|
|
|
|
struct breakpoint_node bpnodes[BREAKPOINT_TOTAL];
|
|
static addrbank **debug_mem_banks;
|
|
static addrbank *debug_mem_area;
|
|
struct memwatch_node mwnodes[MEMWATCH_TOTAL];
|
|
static int mwnodes_start, mwnodes_end;
|
|
static struct memwatch_node mwhit;
|
|
|
|
#define MUNGWALL_SLOTS 16
|
|
struct mungwall_data
|
|
{
|
|
int slots;
|
|
uae_u32 start[MUNGWALL_SLOTS], end[MUNGWALL_SLOTS];
|
|
};
|
|
static struct mungwall_data **mungwall;
|
|
|
|
static uae_u8 *illgdebug, *illghdebug;
|
|
static int illgdebug_break;
|
|
|
|
static void illg_free (void)
|
|
{
|
|
xfree (illgdebug);
|
|
illgdebug = NULL;
|
|
xfree (illghdebug);
|
|
illghdebug = NULL;
|
|
}
|
|
|
|
static void illg_init (void)
|
|
{
|
|
int i;
|
|
uae_u8 c = 3;
|
|
uaecptr addr, end;
|
|
|
|
illgdebug = xcalloc (uae_u8, 0x01000000);
|
|
illghdebug = xcalloc (uae_u8, 65536);
|
|
if (!illgdebug || !illghdebug) {
|
|
illg_free();
|
|
return;
|
|
}
|
|
addr = 0xffffffff;
|
|
nextaddr_init(addr);
|
|
while ((addr = nextaddr(addr, 0, &end, false, NULL)) != 0xffffffff) {
|
|
if (end < 0x01000000) {
|
|
memset (illgdebug + addr, c, end - addr);
|
|
} else {
|
|
uae_u32 s = addr >> 16;
|
|
uae_u32 e = end >> 16;
|
|
memset (illghdebug + s, c, e - s);
|
|
}
|
|
addr = end - 1;
|
|
}
|
|
for (int i = 0; i < MAX_RTG_BOARDS; i++) {
|
|
if (currprefs.rtgboards[i].rtgmem_size)
|
|
memset (illghdebug + (gfxmem_banks[i]->start >> 16), 3, currprefs.rtgboards[i].rtgmem_size >> 16);
|
|
}
|
|
|
|
i = 0;
|
|
while (custd[i].name) {
|
|
int rw = (custd[i].special & CD_WO) ? 2 : 1;
|
|
illgdebug[custd[i].adr] = rw;
|
|
illgdebug[custd[i].adr + 1] = rw;
|
|
i++;
|
|
}
|
|
for (i = 0; i < 16; i++) { /* CIAs */
|
|
if (i == 11)
|
|
continue;
|
|
illgdebug[0xbfe001 + i * 0x100] = c;
|
|
illgdebug[0xbfd000 + i * 0x100] = c;
|
|
}
|
|
memset (illgdebug + 0xf80000, 1, 512 * 1024); /* KS ROM */
|
|
memset (illgdebug + 0xdc0000, c, 0x3f); /* clock */
|
|
#ifdef CDTV
|
|
if (currprefs.cs_cdtvram) {
|
|
memset (illgdebug + 0xdc8000, c, 4096); /* CDTV batt RAM */
|
|
memset (illgdebug + 0xf00000, 1, 256 * 1024); /* CDTV ext ROM */
|
|
}
|
|
#endif
|
|
#ifdef CD32
|
|
if (currprefs.cs_cd32cd) {
|
|
memset (illgdebug + AKIKO_BASE, c, AKIKO_BASE_END - AKIKO_BASE);
|
|
memset (illgdebug + 0xe00000, 1, 512 * 1024); /* CD32 ext ROM */
|
|
}
|
|
#endif
|
|
if (currprefs.cs_ksmirror_e0)
|
|
memset (illgdebug + 0xe00000, 1, 512 * 1024);
|
|
if (currprefs.cs_ksmirror_a8)
|
|
memset (illgdebug + 0xa80000, 1, 2 * 512 * 1024);
|
|
#ifdef FILESYS
|
|
if (uae_boot_rom_type) /* filesys "rom" */
|
|
memset (illgdebug + rtarea_base, 1, 0x10000);
|
|
#endif
|
|
if (currprefs.cs_ide > 0)
|
|
memset (illgdebug + 0xdd0000, 3, 65536);
|
|
}
|
|
|
|
/* add special custom register check here */
|
|
static void illg_debug_check (uaecptr addr, int rwi, int size, uae_u32 val)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static void illg_debug_do (uaecptr addr, int rwi, int size, uae_u32 val)
|
|
{
|
|
uae_u8 mask;
|
|
uae_u32 pc = m68k_getpc ();
|
|
int i;
|
|
|
|
for (i = size - 1; i >= 0; i--) {
|
|
uae_u8 v = val >> (i * 8);
|
|
uae_u32 ad = addr + i;
|
|
if (ad >= 0x01000000)
|
|
mask = illghdebug[ad >> 16];
|
|
else
|
|
mask = illgdebug[ad];
|
|
if ((mask & 3) == 3)
|
|
return;
|
|
if (mask & 0x80) {
|
|
illg_debug_check (ad, rwi, size, val);
|
|
} else if ((mask & 3) == 0) {
|
|
if (rwi & 2)
|
|
console_out_f (_T("W: %08X=%02X PC=%08X\n"), ad, v, pc);
|
|
else if (rwi & 1)
|
|
console_out_f (_T("R: %08X PC=%08X\n"), ad, pc);
|
|
if (illgdebug_break)
|
|
activate_debugger_new();
|
|
} else if (!(mask & 1) && (rwi & 1)) {
|
|
console_out_f (_T("RO: %08X=%02X PC=%08X\n"), ad, v, pc);
|
|
if (illgdebug_break)
|
|
activate_debugger_new();
|
|
} else if (!(mask & 2) && (rwi & 2)) {
|
|
console_out_f (_T("WO: %08X PC=%08X\n"), ad, pc);
|
|
if (illgdebug_break)
|
|
activate_debugger_new();
|
|
}
|
|
}
|
|
}
|
|
|
|
static int debug_mem_off (uaecptr *addrp)
|
|
{
|
|
uaecptr addr = *addrp;
|
|
addrbank *ba;
|
|
int offset = munge24 (addr) >> 16;
|
|
if (!debug_mem_banks)
|
|
return offset;
|
|
ba = debug_mem_banks[offset];
|
|
if (!ba)
|
|
return offset;
|
|
if (ba->mask || ba->startmask) {
|
|
uae_u32 start = ba->startmask ? ba->startmask : ba->start;
|
|
addr -= start;
|
|
addr &= ba->mask;
|
|
addr += start;
|
|
}
|
|
*addrp = addr;
|
|
return offset;
|
|
}
|
|
|
|
struct smc_item {
|
|
uae_u32 addr;
|
|
uae_u16 version;
|
|
uae_u8 cnt;
|
|
};
|
|
|
|
static uae_u32 smc_size, smc_mode;
|
|
static struct smc_item *smc_table;
|
|
static uae_u16 smc_version;
|
|
|
|
static void smc_free (void)
|
|
{
|
|
if (smc_table)
|
|
console_out (_T("SMCD disabled\n"));
|
|
xfree(smc_table);
|
|
smc_mode = 0;
|
|
smc_table = NULL;
|
|
}
|
|
|
|
static void initialize_memwatch(int mode);
|
|
static void smc_detect_init(TCHAR **c)
|
|
{
|
|
int v;
|
|
|
|
ignore_ws(c);
|
|
v = readint(c, NULL);
|
|
smc_free();
|
|
smc_size = 1 << 24;
|
|
if (highest_ram > smc_size) {
|
|
smc_size = highest_ram;
|
|
}
|
|
smc_table = xcalloc(struct smc_item, smc_size + 4);
|
|
if (!smc_table) {
|
|
console_out_f(_T("Failed to allocated SMCD buffers, %d bytes needed\n."), sizeof(struct smc_item) * smc_size + 4);
|
|
return;
|
|
}
|
|
smc_version = 0xffff;
|
|
debug_smc_clear(-1, 0);
|
|
if (!memwatch_enabled)
|
|
initialize_memwatch(0);
|
|
if (v)
|
|
smc_mode = 1;
|
|
console_out_f(_T("SMCD enabled. Break=%d. Last address=%08x\n"), smc_mode, smc_size);
|
|
}
|
|
|
|
void debug_smc_clear(uaecptr addr, int size)
|
|
{
|
|
if (!smc_table)
|
|
return;
|
|
smc_version++;
|
|
if (smc_version == 0) {
|
|
for (uae_u32 i = 0; i < smc_size; i += 65536) {
|
|
addrbank *ab = &get_mem_bank(i);
|
|
if (ab->flags & (ABFLAG_RAM | ABFLAG_ROM)) {
|
|
for (uae_u32 j = 0; j < 65536; j++) {
|
|
struct smc_item *si = &smc_table[i + j];
|
|
if (size < 0 || (si->addr >= addr && si->addr < addr + size)) {
|
|
si->addr = 0xffffffff;
|
|
si->cnt = 0;
|
|
si->version = smc_version;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SMC_MAXHITS 8
|
|
static void smc_detector(uaecptr addr, int rwi, int size, uae_u32 *valp)
|
|
{
|
|
int hitcnt;
|
|
uaecptr hitaddr, hitpc;
|
|
|
|
if (!smc_table)
|
|
return;
|
|
if (addr + size > smc_size)
|
|
return;
|
|
|
|
if (rwi == 2) {
|
|
for (int i = 0; i < size; i++) {
|
|
struct smc_item *si = &smc_table[addr + i];
|
|
if (si->version != smc_version) {
|
|
si->version = smc_version;
|
|
si->addr = 0xffffffff;
|
|
si->cnt = 0;
|
|
}
|
|
if (si->cnt < SMC_MAXHITS) {
|
|
si->addr = m68k_getpc();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
hitpc = 0xffffffff;
|
|
for (int i = 0; i < size && hitpc == 0xffffffff && addr + i < smc_size; i += 2) {
|
|
struct smc_item *si = &smc_table[addr + i];
|
|
if (si->version == smc_version) {
|
|
hitpc = si->addr;
|
|
}
|
|
}
|
|
if (hitpc == 0xffffffff) {
|
|
return;
|
|
}
|
|
if ((hitpc & 0xFFF80000) == 0xF80000) {
|
|
return;
|
|
}
|
|
hitaddr = addr;
|
|
hitcnt = 0;
|
|
while (addr < smc_size) {
|
|
struct smc_item *si = &smc_table[addr];
|
|
if (si->addr == 0xffffffff || si->version != smc_version) {
|
|
break;
|
|
}
|
|
si->addr = 0xffffffff;
|
|
hitcnt++;
|
|
addr++;
|
|
}
|
|
if (currprefs.cpu_model <= 68010 && currprefs.cpu_compatible) {
|
|
/* ignore single-word unconditional jump instructions
|
|
* (instruction prefetch from PC+2 can cause false positives) */
|
|
if (regs.irc == 0x4e75 || regs.irc == 0x4e74 || regs.irc == 0x4e73 || regs.irc == 0x4e77)
|
|
return; /* RTS, RTD, RTE, RTR */
|
|
if ((regs.irc & 0xff00) == 0x6000 && (regs.irc & 0x00ff) != 0 && (regs.irc & 0x00ff) != 0xff)
|
|
return; /* BRA.B */
|
|
}
|
|
if (hitcnt < 100) {
|
|
struct smc_item *si = &smc_table[hitaddr];
|
|
si->cnt++;
|
|
console_out_f(_T("SMC at %08X - %08X (%d) from %08X\n"), hitaddr, hitaddr + hitcnt, hitcnt, hitpc);
|
|
if (smc_mode) {
|
|
activate_debugger_new();
|
|
}
|
|
if (si->cnt >= SMC_MAXHITS) {
|
|
console_out_f(_T("* hit count >= %d, future hits ignored\n"), SMC_MAXHITS);
|
|
}
|
|
}
|
|
}
|
|
|
|
uae_u8 *save_debug_memwatch (size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
int total;
|
|
|
|
total = 0;
|
|
for (int i = 0; i < MEMWATCH_TOTAL; i++) {
|
|
if (mwnodes[i].size > 0)
|
|
total++;
|
|
}
|
|
if (!total)
|
|
return NULL;
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc (uae_u8, 1000);
|
|
save_u32 (1);
|
|
save_u8 (total);
|
|
for (int i = 0; i < MEMWATCH_TOTAL; i++) {
|
|
struct memwatch_node *m = &mwnodes[i];
|
|
if (m->size <= 0)
|
|
continue;
|
|
save_store_pos ();
|
|
save_u8 (i);
|
|
save_u8 (m->modval_written);
|
|
save_u8 (m->mustchange);
|
|
save_u8 (m->frozen);
|
|
save_u8 (m->val_enabled);
|
|
save_u8 (m->rwi);
|
|
save_u32 (m->addr);
|
|
save_u32 (m->size);
|
|
save_u32 (m->modval);
|
|
save_u32 (m->val_mask);
|
|
save_u32 (m->val_size);
|
|
save_u32 (m->val);
|
|
save_u32 (m->pc);
|
|
save_u32 (m->access_mask);
|
|
save_u32 (m->reg);
|
|
save_u8(m->nobreak);
|
|
save_u8(m->reportonly);
|
|
save_store_size ();
|
|
}
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_debug_memwatch (uae_u8 *src)
|
|
{
|
|
if (restore_u32 () != 1)
|
|
return src;
|
|
int total = restore_u8 ();
|
|
for (int i = 0; i < total; i++) {
|
|
restore_store_pos ();
|
|
int idx = restore_u8 ();
|
|
struct memwatch_node *m = &mwnodes[idx];
|
|
m->modval_written = restore_u8 ();
|
|
m->mustchange = restore_u8 ();
|
|
m->frozen = restore_u8 ();
|
|
m->val_enabled = restore_u8 ();
|
|
m->rwi = restore_u8 ();
|
|
m->addr = restore_u32 ();
|
|
m->size = restore_u32 ();
|
|
m->modval = restore_u32 ();
|
|
m->val_mask = restore_u32 ();
|
|
m->val_size = restore_u32 ();
|
|
m->val = restore_u32 ();
|
|
m->pc = restore_u32 ();
|
|
m->access_mask = restore_u32();
|
|
m->reg = restore_u32();
|
|
m->nobreak = restore_u8();
|
|
m->reportonly = restore_u8();
|
|
restore_store_size ();
|
|
}
|
|
return src;
|
|
}
|
|
|
|
void restore_debug_memwatch_finish (void)
|
|
{
|
|
for (int i = 0; i < MEMWATCH_TOTAL; i++) {
|
|
struct memwatch_node *m = &mwnodes[i];
|
|
if (m->size) {
|
|
if (!memwatch_enabled)
|
|
initialize_memwatch (0);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void debug_check_reg(uae_u32 addr, int write, uae_u16 v)
|
|
{
|
|
if (!memwatch_access_validator)
|
|
return;
|
|
int reg = addr & 0x1ff;
|
|
const struct customData *cd = &custd[reg >> 1];
|
|
|
|
if (((addr & 0xfe00) != 0xf000 && (addr & 0xffff0000) != 0) || ((addr & 0xffff0000) != 0 && (addr & 0xffff0000) != 0x00df0000) || (addr & 0x0600)) {
|
|
write_log(_T("Mirror custom register %08x (%s) %s access. PC=%08x\n"), addr, cd->name, write ? _T("write") : _T("read"), M68K_GETPC);
|
|
}
|
|
|
|
int spc = cd->special;
|
|
if ((spc & CD_AGA) && !(currprefs.chipset_mask & CSMASK_AGA))
|
|
spc |= CD_NONE;
|
|
if ((spc & CD_ECS_DENISE) && !(currprefs.chipset_mask & CSMASK_ECS_DENISE))
|
|
spc |= CD_NONE;
|
|
if ((spc & CD_ECS_AGNUS) && !(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
|
|
spc |= CD_NONE;
|
|
if (spc & CD_NONE) {
|
|
write_log(_T("Non-existing custom register %04x (%s) %s access. PC=%08x\n"), reg, cd->name, write ? _T("write") : _T("read"), M68K_GETPC);
|
|
return;
|
|
}
|
|
|
|
if (spc & CD_COLOR) {
|
|
if (currprefs.chipset_mask & CSMASK_AGA)
|
|
return;
|
|
}
|
|
|
|
if (write & !(spc & CD_WO)) {
|
|
write_log(_T("Write access to read-only custom register %04x (%s). PC=%08x\n"), reg, cd->name, M68K_GETPC);
|
|
return;
|
|
} else if (!write && (spc & CD_WO)) {
|
|
write_log(_T("Read access from write-only custom register %04x (%s). PC=%08x\n"), reg, cd->name, M68K_GETPC);
|
|
return;
|
|
}
|
|
|
|
if (write && cd->mask[2]) {
|
|
int idx = (currprefs.chipset_mask & CSMASK_AGA) ? 2 : (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 1 : 0;
|
|
uae_u16 mask = cd->mask[idx];
|
|
if (v & ~mask) {
|
|
write_log(_T("Unuset bits set %04x when writing custom register %04x (%s) PC=%08x\n"), v & ~mask, reg, cd->name, M68K_GETPC);
|
|
}
|
|
}
|
|
|
|
if (spc & CD_DMA_PTR) {
|
|
uae_u32 addr = (custom_storage[((reg & ~2) >> 1)].value << 16) | custom_storage[((reg | 2) >> 1)].value;
|
|
if (currprefs.z3chipmem.size) {
|
|
if (addr >= currprefs.z3chipmem.start_address && addr < currprefs.z3chipmem.start_address + currprefs.z3chipmem.size)
|
|
return;
|
|
}
|
|
if(addr >= currprefs.chipmem.size)
|
|
write_log(_T("DMA pointer %04x (%s) set to invalid value %08x %s=%08x\n"), reg, cd->name, addr,
|
|
custom_storage[reg >> 1].pc & 1 ? _T("COP") : _T("PC"), custom_storage[reg >> 1].pc);
|
|
}
|
|
}
|
|
|
|
void debug_invalid_reg(int reg, int size, uae_u16 v)
|
|
{
|
|
if (!memwatch_access_validator)
|
|
return;
|
|
reg &= 0x1ff;
|
|
if (size == 1) {
|
|
if (reg == 2) // DMACONR low byte
|
|
return;
|
|
if (reg == 6) // VPOS
|
|
return;
|
|
}
|
|
const struct customData *cd = &custd[reg >> 1];
|
|
if (size == -2 && (reg & 1)) {
|
|
write_log(_T("Unaligned word write to register %04x (%s) val %04x PC=%08x\n"), reg, cd->name, v, M68K_GETPC);
|
|
} else if (size == -1) {
|
|
write_log(_T("Byte write to register %04x (%s) val %02x PC=%08x\n"), reg, cd->name, v & 0xff, M68K_GETPC);
|
|
} else if (size == 2 && (reg & 1)) {
|
|
write_log(_T("Unaligned word read from register %04x (%s) PC=%08x\n"), reg, cd->name, M68K_GETPC);
|
|
} else if (size == 1) {
|
|
write_log(_T("Byte read from register %04x (%s) PC=%08x\n"), reg, cd->name, M68K_GETPC);
|
|
}
|
|
}
|
|
|
|
static void is_valid_dma(int reg, int ptrreg, uaecptr addr)
|
|
{
|
|
if (!memwatch_access_validator)
|
|
return;
|
|
if (reg == 0x1fe) // refresh
|
|
return;
|
|
if (currprefs.z3chipmem.size) {
|
|
if (addr >= currprefs.z3chipmem.start_address && addr < currprefs.z3chipmem.start_address + currprefs.z3chipmem.size)
|
|
return;
|
|
}
|
|
if (!(addr & ~(currprefs.chipmem.size - 1)))
|
|
return;
|
|
const struct customData *cdreg = &custd[reg >> 1];
|
|
const struct customData *cdptr = &custd[ptrreg >> 1];
|
|
write_log(_T("DMA DAT %04x (%s), PT %04x (%s) accessed invalid memory %08x. Init: %08x, PC/COP=%08x\n"),
|
|
reg, cdreg->name, ptrreg, cdptr->name, addr,
|
|
(custom_storage[ptrreg >> 1].value << 16) | (custom_storage[(ptrreg >> 1) + 1].value),
|
|
custom_storage[ptrreg >> 1].pc);
|
|
}
|
|
|
|
static void mungwall_memwatch(uaecptr addr, int rwi, int size, uae_u32 valp)
|
|
{
|
|
struct mungwall_data *mwd = mungwall[addr >> 16];
|
|
if (!mwd)
|
|
return;
|
|
for (int i = 0; i < mwd->slots; i++) {
|
|
if (!mwd->end[i])
|
|
continue;
|
|
if (addr + size > mwd->start[i] && addr < mwd->end[i]) {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
static void memwatch_hit_msg(int mw)
|
|
{
|
|
console_out_f(_T("Memwatch %d: break at %08X.%c %c%c%c %08X PC=%08X "), mw, mwhit.addr,
|
|
mwhit.size == 1 ? 'B' : (mwhit.size == 2 ? 'W' : 'L'),
|
|
(mwhit.rwi & 1) ? 'R' : ' ', (mwhit.rwi & 2) ? 'W' : ' ', (mwhit.rwi & 4) ? 'I' : ' ',
|
|
mwhit.val, mwhit.pc);
|
|
for (int i = 0; memwatch_access_masks[i].mask; i++) {
|
|
if (mwhit.access_mask == memwatch_access_masks[i].mask)
|
|
console_out_f(_T("%s (%03x)\n"), memwatch_access_masks[i].name, mwhit.reg);
|
|
}
|
|
if (mwhit.access_mask & (MW_MASK_BLITTER_A | MW_MASK_BLITTER_B | MW_MASK_BLITTER_C | MW_MASK_BLITTER_D_N | MW_MASK_BLITTER_D_L | MW_MASK_BLITTER_D_F)) {
|
|
blitter_debugdump();
|
|
}
|
|
}
|
|
|
|
static int memwatch_func (uaecptr addr, int rwi, int size, uae_u32 *valp, uae_u32 accessmask, uae_u32 reg)
|
|
{
|
|
uae_u32 val = *valp;
|
|
|
|
if (inside_debugger)
|
|
return 1;
|
|
|
|
if (mungwall)
|
|
mungwall_memwatch(addr, rwi, size, val);
|
|
|
|
if (illgdebug)
|
|
illg_debug_do (addr, rwi, size, val);
|
|
|
|
if (heatmap)
|
|
memwatch_heatmap (addr, rwi, size, accessmask);
|
|
|
|
addr = munge24 (addr);
|
|
|
|
if (smc_table && (rwi >= 2))
|
|
smc_detector (addr, rwi, size, valp);
|
|
|
|
for (int i = mwnodes_start; i <= mwnodes_end; i++) {
|
|
struct memwatch_node *m = &mwnodes[i];
|
|
uaecptr addr2 = m->addr;
|
|
uaecptr addr3 = addr2 + m->size;
|
|
int rwi2 = m->rwi;
|
|
uae_u32 oldval = 0;
|
|
int isoldval = 0;
|
|
int brk = 0;
|
|
|
|
if (m->size == 0)
|
|
continue;
|
|
if (!(rwi & rwi2))
|
|
continue;
|
|
if (!(m->access_mask & accessmask))
|
|
continue;
|
|
|
|
if (addr >= addr2 && addr < addr3)
|
|
brk = 1;
|
|
if (!brk && size == 2 && (addr + 1 >= addr2 && addr + 1 < addr3))
|
|
brk = 1;
|
|
if (!brk && size == 4 && ((addr + 2 >= addr2 && addr + 2 < addr3) || (addr + 3 >= addr2 && addr + 3 < addr3)))
|
|
brk = 1;
|
|
|
|
if (!brk)
|
|
continue;
|
|
|
|
if (m->bus_error) {
|
|
if (((m->bus_error & 1) && (rwi & 1)) || ((m->bus_error & 4) && (rwi & 4)) || ((m->bus_error & 2) && (rwi & 2))) {
|
|
hardware_exception2(addr, val, (rwi & 2) != 0, (rwi & 4) != 0, size == 4 ? sz_long : (size == 2 ? sz_word : sz_byte));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (mem_banks[addr >> 16]->check (addr, size)) {
|
|
uae_u8 *p = mem_banks[addr >> 16]->xlateaddr (addr);
|
|
if (size == 1)
|
|
oldval = p[0];
|
|
else if (size == 2)
|
|
oldval = (p[0] << 8) | p[1];
|
|
else
|
|
oldval = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
|
|
isoldval = 1;
|
|
}
|
|
|
|
if (m->pc != 0xffffffff) {
|
|
if (m->pc != regs.instruction_pc)
|
|
continue;
|
|
}
|
|
|
|
if (!m->frozen && m->val_enabled) {
|
|
int trigger = 0;
|
|
uae_u32 mask = m->size == 4 ? 0xffffffff : (1 << (m->size * 8)) - 1;
|
|
uae_u32 mval = m->val;
|
|
int scnt = size;
|
|
for (;;) {
|
|
if (((mval & mask) & m->val_mask) == ((val & mask) & m->val_mask))
|
|
trigger = 1;
|
|
if (mask & 0x80000000)
|
|
break;
|
|
if (m->size == 1) {
|
|
mask <<= 8;
|
|
mval <<= 8;
|
|
scnt--;
|
|
} else if (m->size == 2) {
|
|
mask <<= 16;
|
|
scnt -= 2;
|
|
mval <<= 16;
|
|
} else {
|
|
scnt -= 4;
|
|
}
|
|
if (scnt <= 0)
|
|
break;
|
|
}
|
|
if (!trigger)
|
|
continue;
|
|
}
|
|
|
|
if (m->mustchange && rwi == 2 && isoldval) {
|
|
if (oldval == *valp)
|
|
continue;
|
|
}
|
|
|
|
if (m->modval_written) {
|
|
if (!rwi) {
|
|
brk = 0;
|
|
} else if (m->modval_written == 1) {
|
|
m->modval_written = 2;
|
|
m->modval = val;
|
|
brk = 0;
|
|
} else if (m->modval == val) {
|
|
brk = 0;
|
|
}
|
|
}
|
|
if (m->frozen) {
|
|
if (m->val_enabled) {
|
|
int shift = (addr + size - 1) - (m->addr + m->val_size - 1);
|
|
uae_u32 sval;
|
|
uae_u32 mask;
|
|
|
|
if (m->val_size == 4)
|
|
mask = 0xffffffff;
|
|
else if (m->val_size == 2)
|
|
mask = 0x0000ffff;
|
|
else
|
|
mask = 0x000000ff;
|
|
|
|
sval = m->val;
|
|
if (shift < 0) {
|
|
shift = -8 * shift;
|
|
sval >>= shift;
|
|
mask >>= shift;
|
|
} else {
|
|
shift = 8 * shift;
|
|
sval <<= shift;
|
|
mask <<= shift;
|
|
}
|
|
*valp = (sval & mask) | ((*valp) & ~mask);
|
|
//write_log (_T("%08x %08x %08x %08x %d\n"), addr, m->addr, *valp, mask, shift);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
mwhit.addr = addr;
|
|
mwhit.rwi = rwi;
|
|
mwhit.size = size;
|
|
mwhit.val = 0;
|
|
mwhit.access_mask = accessmask;
|
|
mwhit.reg = reg;
|
|
if (mwhit.rwi & 2)
|
|
mwhit.val = val;
|
|
mwhit.pc = M68K_GETPC;
|
|
memwatch_triggered = i + 1;
|
|
if (m->reportonly) {
|
|
memwatch_hit_msg(memwatch_triggered - 1);
|
|
}
|
|
if (!m->nobreak && !m->reportonly) {
|
|
debugging = 1;
|
|
debug_pc = M68K_GETPC;
|
|
debug_cycles(1);
|
|
set_special(SPCFLAG_BRK);
|
|
}
|
|
return 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int mmu_hit (uaecptr addr, int size, int rwi, uae_u32 *v);
|
|
|
|
static uae_u32 REGPARAM2 mmu_lget (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v = 0;
|
|
if (!mmu_hit (addr, 4, 0, &v))
|
|
v = debug_mem_banks[off]->lget (addr);
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 mmu_wget (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v = 0;
|
|
if (!mmu_hit (addr, 2, 0, &v))
|
|
v = debug_mem_banks[off]->wget (addr);
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 mmu_bget (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v = 0;
|
|
if (!mmu_hit(addr, 1, 0, &v))
|
|
v = debug_mem_banks[off]->bget (addr);
|
|
return v;
|
|
}
|
|
static void REGPARAM2 mmu_lput (uaecptr addr, uae_u32 v)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
if (!mmu_hit (addr, 4, 1, &v))
|
|
debug_mem_banks[off]->lput (addr, v);
|
|
}
|
|
static void REGPARAM2 mmu_wput (uaecptr addr, uae_u32 v)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
if (!mmu_hit (addr, 2, 1, &v))
|
|
debug_mem_banks[off]->wput (addr, v);
|
|
}
|
|
static void REGPARAM2 mmu_bput (uaecptr addr, uae_u32 v)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
if (!mmu_hit (addr, 1, 1, &v))
|
|
debug_mem_banks[off]->bput (addr, v);
|
|
}
|
|
static uae_u32 REGPARAM2 mmu_lgeti (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v = 0;
|
|
if (!mmu_hit (addr, 4, 4, &v))
|
|
v = debug_mem_banks[off]->lgeti (addr);
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 mmu_wgeti (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v = 0;
|
|
if (!mmu_hit (addr, 2, 4, &v))
|
|
v = debug_mem_banks[off]->wgeti (addr);
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 debug_lget(uaecptr addr)
|
|
{
|
|
uae_u32 off = debug_mem_off(&addr);
|
|
uae_u32 v;
|
|
v = debug_mem_banks[off]->lget(addr);
|
|
memwatch_func(addr, 1, 4, &v, MW_MASK_CPU_D_R, 0);
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 debug_wget (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v;
|
|
v = debug_mem_banks[off]->wget (addr);
|
|
memwatch_func (addr, 1, 2, &v, MW_MASK_CPU_D_R, 0);
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 debug_bget (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v;
|
|
v = debug_mem_banks[off]->bget (addr);
|
|
memwatch_func (addr, 1, 1, &v, MW_MASK_CPU_D_R, 0);
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 debug_lgeti (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v;
|
|
v = debug_mem_banks[off]->lgeti (addr);
|
|
memwatch_func (addr, 4, 4, &v, MW_MASK_CPU_I, 0);
|
|
return v;
|
|
}
|
|
static uae_u32 REGPARAM2 debug_wgeti (uaecptr addr)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
uae_u32 v;
|
|
v = debug_mem_banks[off]->wgeti (addr);
|
|
memwatch_func (addr, 4, 2, &v, MW_MASK_CPU_I, 0);
|
|
return v;
|
|
}
|
|
static void REGPARAM2 debug_lput (uaecptr addr, uae_u32 v)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
if (memwatch_func (addr, 2, 4, &v, MW_MASK_CPU_D_W, 0))
|
|
debug_mem_banks[off]->lput (addr, v);
|
|
}
|
|
static void REGPARAM2 debug_wput (uaecptr addr, uae_u32 v)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
if (memwatch_func (addr, 2, 2, &v, MW_MASK_CPU_D_W, 0))
|
|
debug_mem_banks[off]->wput (addr, v);
|
|
}
|
|
static void REGPARAM2 debug_bput (uaecptr addr, uae_u32 v)
|
|
{
|
|
int off = debug_mem_off (&addr);
|
|
if (memwatch_func (addr, 2, 1, &v, MW_MASK_CPU_D_W, 0))
|
|
debug_mem_banks[off]->bput (addr, v);
|
|
}
|
|
static int REGPARAM2 debug_check (uaecptr addr, uae_u32 size)
|
|
{
|
|
return debug_mem_banks[munge24 (addr) >> 16]->check (addr, size);
|
|
}
|
|
static uae_u8 *REGPARAM2 debug_xlate (uaecptr addr)
|
|
{
|
|
return debug_mem_banks[munge24 (addr) >> 16]->xlateaddr (addr);
|
|
}
|
|
|
|
struct peekdma peekdma_data;
|
|
|
|
static void peekdma_save(int type, uaecptr addr, uae_u32 mask, int reg, int ptrreg)
|
|
{
|
|
peekdma_data.type = type;
|
|
peekdma_data.addr = addr;
|
|
peekdma_data.mask = mask;
|
|
peekdma_data.reg = reg;
|
|
peekdma_data.ptrreg = ptrreg;
|
|
}
|
|
|
|
void debug_getpeekdma_value(uae_u32 v)
|
|
{
|
|
uae_u32 vv = v;
|
|
if (!memwatch_enabled) {
|
|
return;
|
|
}
|
|
is_valid_dma(peekdma_data.reg, peekdma_data.ptrreg, peekdma_data.addr);
|
|
if (debug_mem_banks[peekdma_data.addr >> 16] == NULL) {
|
|
return;
|
|
}
|
|
if (!currprefs.z3chipmem.size) {
|
|
peekdma_data.addr &= chipmem_bank.mask;
|
|
}
|
|
memwatch_func(peekdma_data.addr, 1, 2, &vv, peekdma_data.mask, peekdma_data.reg);
|
|
}
|
|
void debug_getpeekdma_value_long(uae_u32 v, int offset)
|
|
{
|
|
uae_u32 vv = v;
|
|
uae_u32 mask = 0xffffffff;
|
|
if (!memwatch_enabled) {
|
|
return;
|
|
}
|
|
is_valid_dma(peekdma_data.reg, peekdma_data.ptrreg, peekdma_data.addr + offset);
|
|
if (debug_mem_banks[(peekdma_data.addr + offset) >> 16] == NULL) {
|
|
return;
|
|
}
|
|
if (!currprefs.z3chipmem.size) {
|
|
mask = chipmem_bank.mask;
|
|
}
|
|
memwatch_func((peekdma_data.addr + offset) & mask, 1, 4, &vv, peekdma_data.mask, peekdma_data.reg);
|
|
}
|
|
|
|
|
|
uae_u32 debug_putpeekdma_chipset(uaecptr addr, uae_u32 v, uae_u32 mask, int reg)
|
|
{
|
|
peekdma_save(0, addr, mask, reg, 0);
|
|
if (!memwatch_enabled)
|
|
return v;
|
|
peekdma_data.addr &= 0x1fe;
|
|
peekdma_data.addr += 0xdff000;
|
|
memwatch_func(peekdma_data.addr, 2, 2, &v, peekdma_data.mask, peekdma_data.reg);
|
|
return v;
|
|
}
|
|
|
|
uae_u32 debug_putpeekdma_chipram(uaecptr addr, uae_u32 v, uae_u32 mask, int reg, int ptrreg)
|
|
{
|
|
peekdma_save(1, addr, mask, reg, ptrreg);
|
|
if (!memwatch_enabled)
|
|
return v;
|
|
is_valid_dma(peekdma_data.reg, peekdma_data.ptrreg, peekdma_data.addr);
|
|
if (debug_mem_banks[peekdma_data.addr >> 16] == NULL)
|
|
return v;
|
|
if (!currprefs.z3chipmem.size)
|
|
peekdma_data.addr &= chipmem_bank.mask;
|
|
memwatch_func(peekdma_data.addr & chipmem_bank.mask, 2, 2, &v, peekdma_data.mask, peekdma_data.reg);
|
|
return v;
|
|
}
|
|
|
|
void debug_getpeekdma_chipram(uaecptr addr, uae_u32 mask, int reg, int ptrreg)
|
|
{
|
|
peekdma_save(2, addr, mask, reg, ptrreg);
|
|
}
|
|
|
|
static void debug_putlpeek (uaecptr addr, uae_u32 v)
|
|
{
|
|
if (!memwatch_enabled)
|
|
return;
|
|
memwatch_func (addr, 2, 4, &v, MW_MASK_CPU_D_W, 0);
|
|
}
|
|
void debug_wputpeek (uaecptr addr, uae_u32 v)
|
|
{
|
|
if (!memwatch_enabled)
|
|
return;
|
|
memwatch_func (addr, 2, 2, &v, MW_MASK_CPU_D_W, 0);
|
|
}
|
|
void debug_bputpeek (uaecptr addr, uae_u32 v)
|
|
{
|
|
if (!memwatch_enabled)
|
|
return;
|
|
memwatch_func (addr, 2, 1, &v, MW_MASK_CPU_D_W, 0);
|
|
}
|
|
void debug_bgetpeek (uaecptr addr, uae_u32 v)
|
|
{
|
|
uae_u32 vv = v;
|
|
if (!memwatch_enabled)
|
|
return;
|
|
memwatch_func (addr, 1, 1, &vv, MW_MASK_CPU_D_R, 0);
|
|
}
|
|
void debug_wgetpeek (uaecptr addr, uae_u32 v)
|
|
{
|
|
uae_u32 vv = v;
|
|
if (!memwatch_enabled)
|
|
return;
|
|
memwatch_func (addr, 1, 2, &vv, MW_MASK_CPU_D_R, 0);
|
|
}
|
|
void debug_lgetpeek (uaecptr addr, uae_u32 v)
|
|
{
|
|
uae_u32 vv = v;
|
|
if (!memwatch_enabled)
|
|
return;
|
|
memwatch_func (addr, 1, 4, &vv, MW_MASK_CPU_D_R, 0);
|
|
}
|
|
|
|
struct membank_store
|
|
{
|
|
addrbank *addr;
|
|
addrbank newbank;
|
|
int banknr;
|
|
};
|
|
|
|
static struct membank_store *membank_stores;
|
|
static int membank_total;
|
|
#define MEMWATCH_STORE_SLOTS 32
|
|
|
|
static void memwatch_reset (void)
|
|
{
|
|
for (int i = 0; i < membank_total; i++) {
|
|
addrbank *ab = debug_mem_banks[i];
|
|
if (!ab)
|
|
continue;
|
|
map_banks_quick (ab, i, 1, 1);
|
|
}
|
|
for (int i = 0; membank_stores[i].addr; i++) {
|
|
struct membank_store *ms = &membank_stores[i];
|
|
/* name was allocated in memwatch_remap */
|
|
xfree ((char*)ms->newbank.name);
|
|
memset (ms, 0, sizeof (struct membank_store));
|
|
ms->addr = NULL;
|
|
}
|
|
memset (debug_mem_banks, 0, membank_total * sizeof (addrbank*));
|
|
}
|
|
|
|
static void memwatch_remap (uaecptr addr)
|
|
{
|
|
int mode = 0;
|
|
int i;
|
|
int banknr;
|
|
struct membank_store *ms;
|
|
addrbank *bank;
|
|
addrbank *newbank = NULL;
|
|
|
|
addr &= ~65535;
|
|
banknr = addr >> 16;
|
|
if (debug_mem_banks[banknr])
|
|
return;
|
|
bank = mem_banks[banknr];
|
|
for (i = 0 ; i < MEMWATCH_STORE_SLOTS; i++) {
|
|
ms = &membank_stores[i];
|
|
if (ms->addr == NULL)
|
|
break;
|
|
if (ms->addr == bank) {
|
|
newbank = &ms->newbank;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= MEMWATCH_STORE_SLOTS)
|
|
return;
|
|
if (!newbank) {
|
|
TCHAR tmp[200];
|
|
_stprintf (tmp, _T("%s [D]"), bank->name);
|
|
ms->addr = bank;
|
|
ms->banknr = banknr;
|
|
newbank = &ms->newbank;
|
|
memcpy (newbank, bank, sizeof(addrbank));
|
|
newbank->bget = mode ? mmu_bget : debug_bget;
|
|
newbank->wget = mode ? mmu_wget : debug_wget;
|
|
newbank->lget = mode ? mmu_lget : debug_lget;
|
|
newbank->bput = mode ? mmu_bput : debug_bput;
|
|
newbank->wput = mode ? mmu_wput : debug_wput;
|
|
newbank->lput = mode ? mmu_lput : debug_lput;
|
|
newbank->check = debug_check;
|
|
newbank->xlateaddr = debug_xlate;
|
|
newbank->wgeti = mode ? mmu_wgeti : debug_wgeti;
|
|
newbank->lgeti = mode ? mmu_lgeti : debug_lgeti;
|
|
/* name will be freed by memwatch_reset */
|
|
newbank->name = my_strdup (tmp);
|
|
if (!newbank->mask)
|
|
newbank->mask = -1;
|
|
newbank->baseaddr_direct_r = 0;
|
|
newbank->baseaddr_direct_w = 0;
|
|
}
|
|
debug_mem_banks[banknr] = bank;
|
|
map_banks_quick (newbank, banknr, 1, 1);
|
|
// map aliases
|
|
for (i = 0; i < membank_total; i++) {
|
|
uaecptr addr2 = i << 16;
|
|
addrbank *ab = &get_mem_bank(addr2);
|
|
if (ab != ms->addr)
|
|
continue;
|
|
if ((addr2 & ab->mask) == (addr & bank->mask)) {
|
|
debug_mem_banks[i] = ms->addr;
|
|
map_banks_quick (newbank, i, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void memwatch_setup(void)
|
|
{
|
|
memwatch_reset();
|
|
mwnodes_start = MEMWATCH_TOTAL - 1;
|
|
mwnodes_end = 0;
|
|
for (int i = 0; i < MEMWATCH_TOTAL; i++) {
|
|
struct memwatch_node *m = &mwnodes[i];
|
|
if (!m->size)
|
|
continue;
|
|
if (mwnodes_start > i)
|
|
mwnodes_start = i;
|
|
if (mwnodes_end < i)
|
|
mwnodes_end = i;
|
|
int addr = m->addr & ~65535;
|
|
int eaddr = (m->addr + m->size + 65535) & ~65535;
|
|
while (addr < eaddr) {
|
|
memwatch_remap(addr);
|
|
addr += 65536;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int deinitialize_memwatch (void)
|
|
{
|
|
int oldmode;
|
|
|
|
if (!memwatch_enabled && !mmu_enabled)
|
|
return -1;
|
|
memwatch_reset ();
|
|
oldmode = mmu_enabled ? 1 : 0;
|
|
xfree (debug_mem_banks);
|
|
debug_mem_banks = NULL;
|
|
xfree (debug_mem_area);
|
|
debug_mem_area = NULL;
|
|
xfree (membank_stores);
|
|
membank_stores = NULL;
|
|
memwatch_enabled = 0;
|
|
mmu_enabled = 0;
|
|
xfree (illgdebug);
|
|
illgdebug = 0;
|
|
return oldmode;
|
|
}
|
|
|
|
static void initialize_memwatch (int mode)
|
|
{
|
|
membank_total = currprefs.address_space_24 ? 256 : 65536;
|
|
deinitialize_memwatch ();
|
|
debug_mem_banks = xcalloc (addrbank*, 65536);
|
|
if (!debug_mem_banks)
|
|
return;
|
|
if (membank_total < 65536) {
|
|
for (int i = 256; i < 65536; i++) {
|
|
debug_mem_banks[i] = &dummy_bank;
|
|
}
|
|
}
|
|
debug_mem_area = xcalloc (addrbank, membank_total);
|
|
membank_stores = xcalloc (struct membank_store, MEMWATCH_STORE_SLOTS);
|
|
for (int i = 0; i < MEMWATCH_TOTAL; i++) {
|
|
struct memwatch_node *m = &mwnodes[i];
|
|
m->pc = 0xffffffff;
|
|
}
|
|
#if 0
|
|
int i, j, as;
|
|
addrbank *a1, *a2, *oa;
|
|
oa = NULL;
|
|
for (i = 0; i < as; i++) {
|
|
a1 = debug_mem_banks[i] = debug_mem_area + i;
|
|
a2 = mem_banks[i];
|
|
if (a2 != oa) {
|
|
for (j = 0; membank_stores[j].addr; j++) {
|
|
if (membank_stores[j].addr == a2)
|
|
break;
|
|
}
|
|
if (membank_stores[j].addr == NULL) {
|
|
membank_stores[j].addr = a2;
|
|
memcpy (&membank_stores[j].store, a2, sizeof (addrbank));
|
|
}
|
|
}
|
|
memcpy (a1, a2, sizeof (addrbank));
|
|
}
|
|
for (i = 0; i < as; i++) {
|
|
a2 = mem_banks[i];
|
|
a2->bget = mode ? mmu_bget : debug_bget;
|
|
a2->wget = mode ? mmu_wget : debug_wget;
|
|
a2->lget = mode ? mmu_lget : debug_lget;
|
|
a2->bput = mode ? mmu_bput : debug_bput;
|
|
a2->wput = mode ? mmu_wput : debug_wput;
|
|
a2->lput = mode ? mmu_lput : debug_lput;
|
|
a2->check = debug_check;
|
|
a2->xlateaddr = debug_xlate;
|
|
a2->wgeti = mode ? mmu_wgeti : debug_wgeti;
|
|
a2->lgeti = mode ? mmu_lgeti : debug_lgeti;
|
|
}
|
|
#endif
|
|
if (mode)
|
|
mmu_enabled = 1;
|
|
else
|
|
memwatch_enabled = 1;
|
|
}
|
|
|
|
int debug_bankchange (int mode)
|
|
{
|
|
if (mode == -1) {
|
|
int v = deinitialize_memwatch ();
|
|
if (v < 0)
|
|
return -2;
|
|
return v;
|
|
}
|
|
if (mode >= 0) {
|
|
initialize_memwatch (mode);
|
|
memwatch_setup ();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
addrbank *get_mem_bank_real(uaecptr addr)
|
|
{
|
|
addrbank *ab = &get_mem_bank(addr);
|
|
if (!memwatch_enabled)
|
|
return ab;
|
|
addrbank *ab2 = debug_mem_banks[addr >> 16];
|
|
if (ab2)
|
|
return ab2;
|
|
return ab;
|
|
}
|
|
|
|
static const TCHAR *getsizechar (int size)
|
|
{
|
|
if (size == 4)
|
|
return _T(".l");
|
|
if (size == 3)
|
|
return _T(".3");
|
|
if (size == 2)
|
|
return _T(".w");
|
|
if (size == 1)
|
|
return _T(".b");
|
|
return _T("");
|
|
}
|
|
|
|
void memwatch_dump2 (TCHAR *buf, int bufsize, int num)
|
|
{
|
|
int i;
|
|
struct memwatch_node *mwn;
|
|
|
|
if (buf)
|
|
memset (buf, 0, bufsize * sizeof (TCHAR));
|
|
for (i = 0; i < MEMWATCH_TOTAL; i++) {
|
|
if ((num >= 0 && num == i) || (num < 0)) {
|
|
uae_u32 usedmask = 0;
|
|
mwn = &mwnodes[i];
|
|
if (mwn->size == 0)
|
|
continue;
|
|
buf = buf_out (buf, &bufsize, _T("%2d: %08X - %08X (%d) %c%c%c"),
|
|
i, mwn->addr, mwn->addr + (mwn->size - 1), mwn->size,
|
|
(mwn->rwi & 1) ? 'R' : ' ', (mwn->rwi & 2) ? 'W' : ' ', (mwn->rwi & 4) ? 'I' : ' ');
|
|
if (mwn->frozen)
|
|
buf = buf_out (buf, &bufsize, _T(" F"));
|
|
if (mwn->val_enabled)
|
|
buf = buf_out (buf, &bufsize, _T(" =%X%s"), mwn->val, getsizechar (mwn->val_size));
|
|
if (mwn->modval_written)
|
|
buf = buf_out (buf, &bufsize, _T(" =M"));
|
|
if (mwn->mustchange)
|
|
buf = buf_out(buf, &bufsize, _T(" C"));
|
|
if (mwn->pc != 0xffffffff)
|
|
buf = buf_out(buf, &bufsize, _T(" PC=%08x"), mwn->pc);
|
|
if (mwn->reportonly)
|
|
buf = buf_out(buf, &bufsize, _T(" L"));
|
|
if (mwn->nobreak)
|
|
buf = buf_out(buf, &bufsize, _T(" N"));
|
|
if (mwn->bus_error) {
|
|
buf = buf_out(buf, &bufsize, _T(" BER%s%s%s"),
|
|
(mwn->bus_error & 1) ? _T("R") : _T(""),
|
|
(mwn->bus_error & 2) ? _T("W") : _T(""),
|
|
(mwn->bus_error & 4) ? _T("P") : _T(""));
|
|
}
|
|
for (int j = 0; memwatch_access_masks[j].mask; j++) {
|
|
uae_u32 mask = memwatch_access_masks[j].mask;
|
|
if ((mwn->access_mask & mask) == mask && (usedmask & mask) == 0) {
|
|
buf = buf_out(buf, &bufsize, _T(" "));
|
|
buf = buf_out(buf, &bufsize, memwatch_access_masks[j].name);
|
|
usedmask |= mask;
|
|
}
|
|
}
|
|
buf = buf_out (buf, &bufsize, _T("\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void memwatch_dump (int num)
|
|
{
|
|
TCHAR *buf;
|
|
int multiplier = num < 0 ? MEMWATCH_TOTAL : 1;
|
|
|
|
buf = xmalloc (TCHAR, 50 * multiplier);
|
|
if (!buf)
|
|
return;
|
|
memwatch_dump2 (buf, 50 * multiplier, num);
|
|
f_out (stdout, _T("%s"), buf);
|
|
xfree (buf);
|
|
}
|
|
|
|
static void memwatch (TCHAR **c)
|
|
{
|
|
int num;
|
|
struct memwatch_node *mwn;
|
|
TCHAR nc, *cp;
|
|
bool err;
|
|
|
|
if (!memwatch_enabled) {
|
|
initialize_memwatch (0);
|
|
console_out (_T("Memwatch breakpoints enabled\n"));
|
|
memwatch_access_validator = 0;
|
|
}
|
|
|
|
cp = *c;
|
|
ignore_ws (c);
|
|
if (!more_params (c)) {
|
|
memwatch_dump (-1);
|
|
return;
|
|
}
|
|
nc = next_char (c);
|
|
if (nc == '-') {
|
|
deinitialize_memwatch ();
|
|
console_out (_T("Memwatch breakpoints disabled\n"));
|
|
return;
|
|
}
|
|
if (nc == 'l') {
|
|
memwatch_access_validator = !memwatch_access_validator;
|
|
console_out_f(_T("Memwatch DMA validator %s\n"), memwatch_access_validator ? _T("enabled") : _T("disabled"));
|
|
return;
|
|
}
|
|
|
|
if (nc == 'd') {
|
|
if (illgdebug) {
|
|
ignore_ws (c);
|
|
if (more_params (c)) {
|
|
uae_u32 addr = readhex(c, NULL);
|
|
uae_u32 len = 1;
|
|
if (more_params (c))
|
|
len = readhex(c, NULL);
|
|
console_out_f (_T("Cleared logging addresses %08X - %08X\n"), addr, addr + len);
|
|
while (len > 0) {
|
|
addr &= 0xffffff;
|
|
illgdebug[addr] = 7;
|
|
addr++;
|
|
len--;
|
|
}
|
|
} else {
|
|
illg_free();
|
|
console_out (_T("Illegal memory access logging disabled\n"));
|
|
}
|
|
} else {
|
|
illg_init ();
|
|
ignore_ws (c);
|
|
illgdebug_break = 0;
|
|
if (more_params (c))
|
|
illgdebug_break = 1;
|
|
console_out_f (_T("Illegal memory access logging enabled. Break=%d\n"), illgdebug_break);
|
|
}
|
|
return;
|
|
}
|
|
*c = cp;
|
|
num = readint(c, NULL);
|
|
if (num < 0 || num >= MEMWATCH_TOTAL)
|
|
return;
|
|
mwn = &mwnodes[num];
|
|
mwn->size = 0;
|
|
ignore_ws (c);
|
|
if (!more_params (c)) {
|
|
console_out_f (_T("Memwatch %d removed\n"), num);
|
|
memwatch_setup ();
|
|
return;
|
|
}
|
|
mwn->addr = readhex(c, NULL);
|
|
mwn->size = 1;
|
|
mwn->rwi = 7;
|
|
mwn->val_enabled = 0;
|
|
mwn->val_mask = 0xffffffff;
|
|
mwn->val = 0;
|
|
mwn->access_mask = 0;
|
|
mwn->reg = 0xffffffff;
|
|
mwn->frozen = 0;
|
|
mwn->modval_written = 0;
|
|
mwn->mustchange = 0;
|
|
mwn->bus_error = 0;
|
|
mwn->reportonly = false;
|
|
mwn->nobreak = false;
|
|
ignore_ws (c);
|
|
if (more_params (c)) {
|
|
mwn->size = readhex(c, NULL);
|
|
ignore_ws (c);
|
|
if (more_params (c)) {
|
|
TCHAR *cs = *c;
|
|
while (*cs) {
|
|
for (int i = 0; memwatch_access_masks[i].mask; i++) {
|
|
const TCHAR *n = memwatch_access_masks[i].name;
|
|
int len = uaetcslen(n);
|
|
if (!_tcsnicmp(cs, n, len)) {
|
|
if (cs[len] == 0 || cs[len] == 10 || cs[len] == 13 || cs[len] == ' ') {
|
|
mwn->access_mask |= memwatch_access_masks[i].mask;
|
|
while (len > 0) {
|
|
len--;
|
|
cs[len] = ' ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cs++;
|
|
}
|
|
ignore_ws (c);
|
|
if (more_params(c)) {
|
|
for (;;) {
|
|
TCHAR ncc = _totupper(peek_next_char(c));
|
|
TCHAR nc = _totupper(next_char(c));
|
|
if (mwn->rwi == 7 && (nc == 'W' || nc == 'R' || nc == 'I'))
|
|
mwn->rwi = 0;
|
|
if (nc == 'F')
|
|
mwn->frozen = 1;
|
|
if (nc == 'W')
|
|
mwn->rwi |= 2;
|
|
if (nc == 'I')
|
|
mwn->rwi |= 4;
|
|
if (nc == 'R')
|
|
mwn->rwi |= 1;
|
|
if (nc == 'B') {
|
|
mwn->bus_error = 0;
|
|
for (;;) {
|
|
ncc = next_char2(c);
|
|
if (ncc == ' ' || ncc == 0)
|
|
break;
|
|
if (ncc == 'R') {
|
|
mwn->bus_error |= 1;
|
|
mwn->rwi |= 1;
|
|
} else if (ncc == 'W') {
|
|
mwn->bus_error |= 2;
|
|
mwn->rwi |= 2;
|
|
} else if (ncc == 'P') {
|
|
mwn->bus_error |= 4;
|
|
mwn->rwi |= 4;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (!mwn->rwi)
|
|
mwn->rwi = 7;
|
|
if (!mwn->bus_error)
|
|
mwn->bus_error = 7;
|
|
}
|
|
if (nc == 'L')
|
|
mwn->reportonly = true;
|
|
if (nc == 'N')
|
|
mwn->nobreak = true;
|
|
if (nc == 'P' && ncc == 'C') {
|
|
next_char(c);
|
|
mwn->pc = readhex(c, NULL);
|
|
}
|
|
if (nc == 'M') {
|
|
mwn->modval_written = 1;
|
|
}
|
|
if (nc == 'C') {
|
|
mwn->mustchange = 1;
|
|
}
|
|
if (nc == 'V') {
|
|
mwn->val = readhex(c, &mwn->val_size, &err);
|
|
mwn->val_enabled = 1;
|
|
}
|
|
if (!more_params(c))
|
|
break;
|
|
}
|
|
ignore_ws (c);
|
|
}
|
|
}
|
|
}
|
|
if (!mwn->access_mask)
|
|
mwn->access_mask = MW_MASK_CPU_D_R | MW_MASK_CPU_D_W | MW_MASK_CPU_I;
|
|
if (mwn->frozen && mwn->rwi == 0)
|
|
mwn->rwi = 3;
|
|
memwatch_setup ();
|
|
memwatch_dump (num);
|
|
}
|
|
|
|
static void copymem(TCHAR **c)
|
|
{
|
|
uae_u32 addr = 0, eaddr = 0, dst = 0;
|
|
bool err;
|
|
|
|
ignore_ws(c);
|
|
if (!more_params(c))
|
|
return;
|
|
addr = readhex(c, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
ignore_ws (c);
|
|
if (!more_params(c))
|
|
return;
|
|
eaddr = readhex(c, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
ignore_ws (c);
|
|
if (!more_params(c))
|
|
return;
|
|
dst = readhex(c, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
|
|
if (addr >= eaddr)
|
|
return;
|
|
uae_u32 addrb = addr;
|
|
uae_u32 dstb = dst;
|
|
uae_u32 len = eaddr - addr;
|
|
if (dst <= addr) {
|
|
while (addr < eaddr) {
|
|
put_byte(dst, get_byte(addr));
|
|
addr++;
|
|
dst++;
|
|
}
|
|
} else {
|
|
dst += eaddr - addr;
|
|
while (addr < eaddr) {
|
|
dst--;
|
|
eaddr--;
|
|
put_byte(dst, get_byte(eaddr));
|
|
}
|
|
}
|
|
console_out_f(_T("Copied from %08x - %08x to %08x - %08x\n"), addrb, addrb + len - 1, dstb, dstb + len - 1);
|
|
}
|
|
|
|
static void writeintomem (TCHAR **c)
|
|
{
|
|
uae_u32 addr = 0;
|
|
uae_u32 eaddr = 0xffffffff;
|
|
uae_u32 val = 0;
|
|
TCHAR cc;
|
|
int len = 1;
|
|
bool fillmode = false;
|
|
bool err;
|
|
|
|
if (**c == 'f') {
|
|
fillmode = true;
|
|
(*c)++;
|
|
} else if (**c == 'c') {
|
|
(*c)++;
|
|
copymem(c);
|
|
return;
|
|
}
|
|
|
|
ignore_ws(c);
|
|
addr = readhex(c, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
ignore_ws (c);
|
|
|
|
if (fillmode) {
|
|
if (!more_params(c))
|
|
return;
|
|
eaddr = readhex(c, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
ignore_ws(c);
|
|
}
|
|
|
|
if (!more_params (c))
|
|
return;
|
|
TCHAR *cb = *c;
|
|
uae_u32 addrc = addr;
|
|
bool retry = false;
|
|
for(;;) {
|
|
cc = peekchar(c);
|
|
retry = false;
|
|
uae_u32 addrb = addr;
|
|
if (cc == '\'' || cc == '\"') {
|
|
TCHAR quoted = cc;
|
|
next_char2(c);
|
|
while (more_params2(c)) {
|
|
TCHAR str[2];
|
|
char *astr;
|
|
cc = next_char2(c);
|
|
if (quoted == cc) {
|
|
ignore_ws(c);
|
|
retry = true;
|
|
break;
|
|
}
|
|
if (addr >= eaddr) {
|
|
break;
|
|
}
|
|
str[0] = cc;
|
|
str[1] = 0;
|
|
astr = ua(str);
|
|
put_byte(addr, astr[0]);
|
|
if (!fillmode) {
|
|
char c = astr[0];
|
|
if (c < 32) {
|
|
c = '.';
|
|
}
|
|
console_out_f(_T("Wrote '%c' (%02X, %02u) at %08X.B\n"), c, c, c, addr);
|
|
}
|
|
xfree(astr);
|
|
addr++;
|
|
}
|
|
if (fillmode && peekchar(c) == 0) {
|
|
*c = cb;
|
|
}
|
|
} else {
|
|
for (;;) {
|
|
bool err;
|
|
ignore_ws(c);
|
|
if (!more_params(c))
|
|
break;
|
|
val = readhex(c, &len, &err);
|
|
if (err) {
|
|
goto end;
|
|
}
|
|
if (len == 4) {
|
|
put_long(addr, val);
|
|
cc = 'L';
|
|
} else if (len == 2) {
|
|
put_word(addr, val);
|
|
cc = 'W';
|
|
} else if (len == 1) {
|
|
put_byte(addr, val);
|
|
cc = 'B';
|
|
} else {
|
|
cc = peekchar(c);
|
|
if (cc == '\'' || cc == '\"') {
|
|
retry = true;
|
|
} else {
|
|
next_char(c);
|
|
retry = true;
|
|
}
|
|
break;
|
|
}
|
|
if (!fillmode) {
|
|
console_out_f(_T("Wrote %X (%u) at %08X.%c\n"), val, val, addr, cc);
|
|
}
|
|
addr += len;
|
|
if (addr >= eaddr)
|
|
break;
|
|
}
|
|
if (fillmode && peekchar(c) == 0) {
|
|
*c = cb;
|
|
}
|
|
}
|
|
if (retry) {
|
|
continue;
|
|
}
|
|
if (addr >= eaddr) {
|
|
break;
|
|
}
|
|
if (eaddr == 0xffffffff || addr <= addrb) {
|
|
break;
|
|
}
|
|
}
|
|
end:
|
|
if (eaddr != 0xffffffff)
|
|
console_out_f(_T("Wrote data to %08x - %08x\n"), addrc, addr);
|
|
}
|
|
|
|
static uae_u8 *dump_xlate (uae_u32 addr)
|
|
{
|
|
if (!mem_banks[addr >> 16]->check (addr, 1))
|
|
return NULL;
|
|
return mem_banks[addr >> 16]->xlateaddr (addr);
|
|
}
|
|
|
|
#if 0
|
|
#define UAE_MEMORY_REGIONS_MAX 64
|
|
#define UAE_MEMORY_REGION_NAME_LENGTH 64
|
|
|
|
#define UAE_MEMORY_REGION_RAM (1 << 0)
|
|
#define UAE_MEMORY_REGION_ALIAS (1 << 1)
|
|
#define UAE_MEMORY_REGION_MIRROR (1 << 2)
|
|
|
|
typedef struct UaeMemoryRegion {
|
|
uaecptr start;
|
|
int size;
|
|
TCHAR name[UAE_MEMORY_REGION_NAME_LENGTH];
|
|
TCHAR rom_name[UAE_MEMORY_REGION_NAME_LENGTH];
|
|
uaecptr alias;
|
|
int flags;
|
|
} UaeMemoryRegion;
|
|
|
|
typedef struct UaeMemoryMap {
|
|
UaeMemoryRegion regions[UAE_MEMORY_REGIONS_MAX];
|
|
int num_regions;
|
|
} UaeMemoryMap;
|
|
#endif
|
|
|
|
static const TCHAR *bankmodes[] = { _T("F32"), _T("C16"), _T("C32"), _T("CIA"), _T("F16"), _T("F16X") };
|
|
|
|
static void memory_map_dump_3(UaeMemoryMap *map, int log)
|
|
{
|
|
bool imold;
|
|
int i, j, max;
|
|
addrbank *a1 = mem_banks[0];
|
|
TCHAR txt[256];
|
|
|
|
map->num_regions = 0;
|
|
imold = currprefs.illegal_mem;
|
|
currprefs.illegal_mem = false;
|
|
max = currprefs.address_space_24 ? 256 : 65536;
|
|
j = 0;
|
|
for (i = 0; i < max + 1; i++) {
|
|
addrbank *a2 = NULL;
|
|
if (i < max)
|
|
a2 = mem_banks[i];
|
|
if (a1 != a2) {
|
|
int k, mirrored, mirrored2, size, size_out;
|
|
TCHAR size_ext;
|
|
uae_u8 *caddr;
|
|
TCHAR tmp[MAX_DPATH];
|
|
const TCHAR *name = a1->name;
|
|
struct addrbank_sub *sb = a1->sub_banks;
|
|
int bankoffset = 0;
|
|
int region_size;
|
|
|
|
k = j;
|
|
caddr = dump_xlate (k << 16);
|
|
mirrored = caddr ? 1 : 0;
|
|
k++;
|
|
while (k < i && caddr) {
|
|
if (dump_xlate (k << 16) == caddr) {
|
|
mirrored++;
|
|
}
|
|
k++;
|
|
}
|
|
mirrored2 = mirrored;
|
|
if (mirrored2 == 0)
|
|
mirrored2 = 1;
|
|
|
|
while (bankoffset < 65536) {
|
|
int bankoffset2 = bankoffset;
|
|
if (sb) {
|
|
uaecptr daddr;
|
|
if (!sb->bank)
|
|
break;
|
|
daddr = (j << 16) | bankoffset;
|
|
a1 = get_sub_bank(&daddr);
|
|
name = a1->name;
|
|
for (;;) {
|
|
bankoffset2 += MEMORY_MIN_SUBBANK;
|
|
if (bankoffset2 >= 65536)
|
|
break;
|
|
daddr = (j << 16) | bankoffset2;
|
|
addrbank *dab = get_sub_bank(&daddr);
|
|
if (dab != a1)
|
|
break;
|
|
}
|
|
sb++;
|
|
size = (bankoffset2 - bankoffset) / 1024;
|
|
region_size = size * 1024;
|
|
} else {
|
|
size = (i - j) << (16 - 10);
|
|
region_size = ((i - j) << 16) / mirrored2;
|
|
}
|
|
|
|
if (name == NULL)
|
|
name = _T("<none>");
|
|
|
|
size_out = size;
|
|
size_ext = 'K';
|
|
if (j >= 256 && (size_out / mirrored2 >= 1024) && !((size_out / mirrored2) & 1023)) {
|
|
size_out /= 1024;
|
|
size_ext = 'M';
|
|
}
|
|
_stprintf (txt, _T("%08X %7d%c/%d = %7d%c %s%s%c %s %s"), (j << 16) | bankoffset, size_out, size_ext,
|
|
mirrored, mirrored ? size_out / mirrored : size_out, size_ext,
|
|
(a1->flags & ABFLAG_CACHE_ENABLE_INS) ? _T("I") : _T("-"),
|
|
(a1->flags & ABFLAG_CACHE_ENABLE_DATA) ? _T("D") : _T("-"),
|
|
a1->baseaddr == NULL ? ' ' : '*',
|
|
bankmodes[ce_banktype[j]],
|
|
name);
|
|
tmp[0] = 0;
|
|
if ((a1->flags & ABFLAG_ROM) && mirrored) {
|
|
TCHAR *p = txt + _tcslen (txt);
|
|
uae_u32 crc = 0xffffffff;
|
|
if (a1->check(((j << 16) | bankoffset), (size * 1024) / mirrored))
|
|
crc = get_crc32 (a1->xlateaddr((j << 16) | bankoffset), (size * 1024) / mirrored);
|
|
struct romdata *rd = getromdatabycrc (crc);
|
|
_stprintf (p, _T(" (%08X)"), crc);
|
|
if (rd) {
|
|
tmp[0] = '=';
|
|
getromname (rd, tmp + 1);
|
|
_tcscat (tmp, _T("\n"));
|
|
}
|
|
}
|
|
|
|
if (a1 != &dummy_bank) {
|
|
for (int m = 0; m < mirrored2; m++) {
|
|
if (map->num_regions >= UAE_MEMORY_REGIONS_MAX)
|
|
break;
|
|
UaeMemoryRegion *r = &map->regions[map->num_regions];
|
|
r->start = (j << 16) + bankoffset + region_size * m;
|
|
r->size = region_size;
|
|
r->flags = 0;
|
|
r->memory = NULL;
|
|
if (!(a1->flags & ABFLAG_PPCIOSPACE)) {
|
|
r->memory = dump_xlate((j << 16) | bankoffset);
|
|
if (r->memory)
|
|
r->flags |= UAE_MEMORY_REGION_RAM;
|
|
}
|
|
/* just to make it easier to spot in debugger */
|
|
r->alias = 0xffffffff;
|
|
if (m >= 0) {
|
|
r->alias = j << 16;
|
|
r->flags |= UAE_MEMORY_REGION_ALIAS | UAE_MEMORY_REGION_MIRROR;
|
|
}
|
|
_stprintf(r->name, _T("%s"), name);
|
|
_stprintf(r->rom_name, _T("%s"), tmp);
|
|
map->num_regions += 1;
|
|
}
|
|
}
|
|
_tcscat (txt, _T("\n"));
|
|
if (log > 0)
|
|
write_log (_T("%s"), txt);
|
|
else if (log == 0)
|
|
console_out (txt);
|
|
if (tmp[0]) {
|
|
if (log > 0)
|
|
write_log (_T("%s"), tmp);
|
|
else if (log == 0)
|
|
console_out (tmp);
|
|
}
|
|
if (!sb)
|
|
break;
|
|
bankoffset = bankoffset2;
|
|
}
|
|
j = i;
|
|
a1 = a2;
|
|
}
|
|
}
|
|
pci_dump(log);
|
|
currprefs.illegal_mem = imold;
|
|
}
|
|
|
|
void uae_memory_map(UaeMemoryMap *map)
|
|
{
|
|
memory_map_dump_3(map, -1);
|
|
}
|
|
|
|
static void memory_map_dump_2 (int log)
|
|
{
|
|
UaeMemoryMap map;
|
|
memory_map_dump_3(&map, log);
|
|
#if 0
|
|
for (int i = 0; i < map.num_regions; i++) {
|
|
TCHAR txt[256];
|
|
UaeMemoryRegion *r = &map.regions[i];
|
|
int size = r->size / 1024;
|
|
TCHAR size_ext = 'K';
|
|
int mirrored = 1;
|
|
int size_out = 0;
|
|
_stprintf (txt, _T("%08X %7u%c/%d = %7u%c %s\n"), r->start, size, size_ext,
|
|
r->flags & UAE_MEMORY_REGION_RAM, size, size_ext, r->name);
|
|
if (log)
|
|
write_log (_T("%s"), txt);
|
|
else
|
|
console_out (txt);
|
|
if (r->rom_name[0]) {
|
|
if (log)
|
|
write_log (_T("%s"), r->rom_name);
|
|
else
|
|
console_out (r->rom_name);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void memory_map_dump (void)
|
|
{
|
|
memory_map_dump_2(1);
|
|
}
|
|
|
|
STATIC_INLINE uaecptr BPTR2APTR (uaecptr addr)
|
|
{
|
|
return addr << 2;
|
|
}
|
|
static TCHAR *BSTR2CSTR (uae_u8 *bstr)
|
|
{
|
|
TCHAR *s;
|
|
char *cstr = xmalloc (char, bstr[0] + 1);
|
|
if (cstr) {
|
|
memcpy (cstr, bstr + 1, bstr[0]);
|
|
cstr[bstr[0]] = 0;
|
|
}
|
|
s = au (cstr);
|
|
xfree (cstr);
|
|
return s;
|
|
}
|
|
|
|
static void print_task_info (uaecptr node, bool nonactive)
|
|
{
|
|
TCHAR *s;
|
|
int process = get_byte_debug (node + 8) == 13 ? 1 : 0;
|
|
|
|
console_out_f (_T("%08X: "), node);
|
|
s = au ((char*)get_real_address_debug(get_long_debug (node + 10)));
|
|
console_out_f (process ? _T("PROCESS '%s'\n") : _T("TASK '%s'\n"), s);
|
|
xfree (s);
|
|
if (process) {
|
|
uaecptr cli = BPTR2APTR (get_long_debug (node + 172));
|
|
int tasknum = get_long_debug (node + 140);
|
|
if (cli && tasknum) {
|
|
uae_u8 *command_bstr = get_real_address_debug(BPTR2APTR (get_long_debug (cli + 16)));
|
|
TCHAR *command = BSTR2CSTR (command_bstr);
|
|
console_out_f (_T(" [%d, '%s']\n"), tasknum, command);
|
|
xfree (command);
|
|
} else {
|
|
console_out (_T("\n"));
|
|
}
|
|
}
|
|
if (nonactive) {
|
|
uae_u32 sigwait = get_long_debug(node + 22);
|
|
if (sigwait)
|
|
console_out_f(_T(" Waiting signals: %08x\n"), sigwait);
|
|
int offset = kickstart_version >= 37 ? 74 : 70;
|
|
uae_u32 sp = get_long_debug(node + 54) + offset;
|
|
uae_u32 pc = get_long_debug(sp);
|
|
console_out_f(_T(" SP: %08x PC: %08x\n"), sp, pc);
|
|
}
|
|
}
|
|
|
|
static void show_exec_tasks (void)
|
|
{
|
|
uaecptr execbase = get_long_debug (4);
|
|
uaecptr taskready = execbase + 406;
|
|
uaecptr taskwait = execbase + 420;
|
|
uaecptr node;
|
|
console_out_f (_T("Execbase at 0x%08X\n"), execbase);
|
|
console_out (_T("Current:\n"));
|
|
node = get_long_debug (execbase + 276);
|
|
print_task_info (node, false);
|
|
console_out_f (_T("Ready:\n"));
|
|
node = get_long_debug (taskready);
|
|
while (node && get_long_debug(node)) {
|
|
print_task_info (node, true);
|
|
node = get_long_debug (node);
|
|
}
|
|
console_out (_T("Waiting:\n"));
|
|
node = get_long_debug (taskwait);
|
|
while (node && get_long_debug(node)) {
|
|
print_task_info (node, true);
|
|
node = get_long_debug (node);
|
|
}
|
|
}
|
|
|
|
static uaecptr get_base (const uae_char *name, int offset)
|
|
{
|
|
uaecptr v = get_long_debug (4);
|
|
addrbank *b = &get_mem_bank(v);
|
|
|
|
if (!b || !b->check (v, 400) || !(b->flags & ABFLAG_RAM))
|
|
return 0;
|
|
v += offset;
|
|
while ((v = get_long_debug (v))) {
|
|
uae_u32 v2;
|
|
uae_u8 *p;
|
|
b = &get_mem_bank (v);
|
|
if (!b || !b->check (v, 32) || (!(b->flags & ABFLAG_RAM) && !(b->flags & ABFLAG_ROMIN)))
|
|
goto fail;
|
|
v2 = get_long_debug (v + 10); // name
|
|
b = &get_mem_bank (v2);
|
|
if (!b || !b->check (v2, 20))
|
|
goto fail;
|
|
if ((b->flags & ABFLAG_ROM) || (b->flags & ABFLAG_RAM) || (b->flags & ABFLAG_ROMIN)) {
|
|
p = get_real_address_debug(v2);
|
|
if (!memcmp (p, name, strlen (name) + 1))
|
|
return v;
|
|
}
|
|
}
|
|
return 0;
|
|
fail:
|
|
return 0xffffffff;
|
|
}
|
|
|
|
static TCHAR *getfrombstr(uaecptr pp)
|
|
{
|
|
uae_u8 len = get_byte(pp << 2);
|
|
TCHAR *s = xcalloc (TCHAR, len + 1);
|
|
char data[256];
|
|
for (int i = 0; i < len; i++) {
|
|
data[i] = get_byte((pp << 2) + 1 + i);
|
|
data[i + 1] = 0;
|
|
}
|
|
return au_copy (s, len + 1, data);
|
|
}
|
|
|
|
// read one byte from expansion autoconfig ROM
|
|
static void copyromdata(uae_u8 bustype, uaecptr rom, int offset, uae_u8 *out, int size)
|
|
{
|
|
switch (bustype & 0xc0)
|
|
{
|
|
case 0x00: // nibble
|
|
while (size-- > 0) {
|
|
*out++ = (get_byte_debug(rom + offset * 4 + 0) & 0xf0) | ((get_byte_debug(rom + offset * 4 + 2) & 0xf0) >> 4);
|
|
offset++;
|
|
}
|
|
break;
|
|
case 0x40: // byte
|
|
while (size-- > 0) {
|
|
*out++ = get_byte_debug(rom + offset * 2);
|
|
offset++;
|
|
}
|
|
break;
|
|
case 0x80: // word
|
|
default:
|
|
while (size-- > 0) {
|
|
*out++ = get_byte_debug(rom + offset);
|
|
offset++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void show_exec_lists (TCHAR *t)
|
|
{
|
|
uaecptr execbase = get_long_debug (4);
|
|
uaecptr list = 0, node;
|
|
TCHAR c = t[0];
|
|
|
|
if (c == 'o' || c == 'O') { // doslist
|
|
uaecptr dosbase = get_base ("dos.library", 378);
|
|
if (dosbase) {
|
|
uaecptr rootnode = get_long_debug (dosbase + 34);
|
|
uaecptr dosinfo = get_long_debug (rootnode + 24) << 2;
|
|
console_out_f (_T("ROOTNODE: %08x DOSINFO: %08x\n"), rootnode, dosinfo);
|
|
uaecptr doslist = get_long_debug (dosinfo + 4) << 2;
|
|
while (doslist) {
|
|
int type = get_long_debug (doslist + 4);
|
|
uaecptr msgport = get_long_debug (doslist + 8);
|
|
uaecptr lock = get_long_debug(doslist + 12);
|
|
TCHAR *name = getfrombstr(get_long_debug(doslist + 40));
|
|
console_out_f(_T("%08x: Type=%d Port=%08x Lock=%08x '%s'\n"), doslist, type, msgport, lock, name);
|
|
if (type == 0) {
|
|
uaecptr fssm = get_long_debug(doslist + 28) << 2;
|
|
console_out_f (_T(" - H=%08x Stack=%5d Pri=%2d Start=%08x Seg=%08x GV=%08x\n"),
|
|
get_long_debug (doslist + 16) << 2, get_long_debug (doslist + 20),
|
|
get_long_debug (doslist + 24), fssm,
|
|
get_long_debug (doslist + 32) << 2, get_long_debug (doslist + 36));
|
|
if (fssm >= 0x100 && (fssm & 3) == 0) {
|
|
TCHAR *unitname = getfrombstr(get_long_debug(fssm + 4));
|
|
console_out_f (_T(" %s:%d %08x\n"), unitname, get_long_debug(fssm), get_long_debug(fssm + 8));
|
|
uaecptr de = get_long_debug(fssm + 8) << 2;
|
|
if (de) {
|
|
console_out_f (_T(" TableSize %u\n"), get_long_debug(de + 0));
|
|
console_out_f (_T(" SizeBlock %u\n"), get_long_debug(de + 4));
|
|
console_out_f (_T(" SecOrg %u\n"), get_long_debug(de + 8));
|
|
console_out_f (_T(" Surfaces %u\n"), get_long_debug(de + 12));
|
|
console_out_f (_T(" SectorPerBlock %u\n"), get_long_debug(de + 16));
|
|
console_out_f (_T(" BlocksPerTrack %u\n"), get_long_debug(de + 20));
|
|
console_out_f (_T(" Reserved %u\n"), get_long_debug(de + 24));
|
|
console_out_f (_T(" PreAlloc %u\n"), get_long_debug(de + 28));
|
|
console_out_f (_T(" Interleave %u\n"), get_long_debug(de + 32));
|
|
console_out_f (_T(" LowCyl %u\n"), get_long_debug(de + 36));
|
|
console_out_f (_T(" HighCyl %u (Total %u)\n"), get_long_debug(de + 40), get_long_debug(de + 40) - get_long_debug(de + 36) + 1);
|
|
console_out_f (_T(" NumBuffers %u\n"), get_long_debug(de + 44));
|
|
console_out_f (_T(" BufMemType 0x%08x\n"), get_long_debug(de + 48));
|
|
console_out_f (_T(" MaxTransfer 0x%08x\n"), get_long_debug(de + 52));
|
|
console_out_f (_T(" Mask 0x%08x\n"), get_long_debug(de + 56));
|
|
console_out_f (_T(" BootPri %d\n"), get_long_debug(de + 60));
|
|
console_out_f (_T(" DosType 0x%08x\n"), get_long_debug(de + 64));
|
|
}
|
|
xfree(unitname);
|
|
}
|
|
} else if (type == 2) {
|
|
console_out_f(_T(" - VolumeDate=%08x %08x %08x LockList=%08x DiskType=%08x\n"),
|
|
get_long_debug(doslist + 16), get_long_debug(doslist + 20), get_long_debug(doslist + 24),
|
|
get_long_debug(doslist + 28),
|
|
get_long_debug(doslist + 32));
|
|
}
|
|
xfree (name);
|
|
doslist = get_long_debug (doslist) << 2;
|
|
}
|
|
} else {
|
|
console_out_f (_T("can't find dos.library\n"));
|
|
}
|
|
return;
|
|
} else if (c == 'i' || c == 'I') { // interrupts
|
|
static const int it[] = { 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0 };
|
|
static const int it2[] = { 1, 1, 1, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 7 };
|
|
list = execbase + 84;
|
|
for (int i = 0; i < 16; i++) {
|
|
console_out_f (_T("%2d %d: %08X\n"), i + 1, it2[i], list);
|
|
if (it[i]) {
|
|
console_out_f (_T(" [H] %08X\n"), get_long_debug (list));
|
|
node = get_long_debug (list + 8);
|
|
if (node) {
|
|
uae_u8 *addr = get_real_address_debug(get_long_debug (node + 10));
|
|
TCHAR *name = addr ? au ((char*)addr) : au("<null>");
|
|
console_out_f (_T(" %08X (C=%08X D=%08X) '%s'\n"), node, get_long_debug (list + 4), get_long_debug (list), name);
|
|
xfree (name);
|
|
}
|
|
} else {
|
|
int cnt = 0;
|
|
node = get_long_debug (list);
|
|
node = get_long_debug (node);
|
|
while (get_long_debug (node)) {
|
|
uae_u8 *addr = get_real_address_debug(get_long_debug (node + 10));
|
|
TCHAR *name = addr ? au ((char*)addr) : au("<null>");
|
|
uae_s8 pri = get_byte_debug(node + 9);
|
|
console_out_f (_T(" [S] %08x %+03d (C=%08x D=%08X) '%s'\n"), node, pri, get_long_debug (node + 18), get_long_debug (node + 14), name);
|
|
if (i == 4 - 1 || i == 14 - 1) {
|
|
if (!_tcsicmp (name, _T("cia-a")) || !_tcsicmp (name, _T("cia-b"))) {
|
|
static const TCHAR *ciai[] = { _T("A"), _T("B"), _T("ALRM"), _T("SP"), _T("FLG") };
|
|
uaecptr cia = node + 22;
|
|
for (int j = 0; j < 5; j++) {
|
|
uaecptr ciap = get_long_debug (cia);
|
|
console_out_f (_T(" %5s: %08x"), ciai[j], ciap);
|
|
if (ciap) {
|
|
uae_u8 *addr2 = get_real_address_debug(get_long_debug (ciap + 10));
|
|
TCHAR *name2 = addr ? au ((char*)addr2) : au("<null>");
|
|
console_out_f (_T(" (C=%08x D=%08X) '%s'"), get_long_debug (ciap + 18), get_long_debug (ciap + 14), name2);
|
|
xfree (name2);
|
|
}
|
|
console_out_f (_T("\n"));
|
|
cia += 4;
|
|
}
|
|
}
|
|
}
|
|
xfree (name);
|
|
node = get_long_debug (node);
|
|
cnt++;
|
|
}
|
|
if (!cnt)
|
|
console_out_f (_T(" [S] <none>\n"));
|
|
}
|
|
list += 12;
|
|
}
|
|
return;
|
|
} else if (c == 'e') { // expansion
|
|
uaecptr expbase = get_base("expansion.library", 378);
|
|
if (expbase) {
|
|
if (t[1] == 'm') {
|
|
uaecptr list = get_long_debug(expbase + 74);
|
|
while (list && get_long_debug(list)) {
|
|
uaecptr name = get_long(list + 10);
|
|
uae_s8 pri = get_byte(list + 9);
|
|
uae_u16 flags = get_word_debug(list + 14);
|
|
uae_u32 dn = get_long_debug(list + 16);
|
|
uae_u8 *addr = get_real_address_debug(name);
|
|
TCHAR *name1 = addr ? au((char*)addr) : au("<null>");
|
|
my_trim(name1);
|
|
console_out_f(_T("%08x %04x %08x %d %s\n"), list, flags, dn, pri, name1);
|
|
xfree(name1);
|
|
list = get_long_debug(list);
|
|
}
|
|
} else {
|
|
list = get_long_debug(expbase + 60);
|
|
while (list && get_long_debug(list)) {
|
|
uae_u32 addr = get_long_debug(list + 32);
|
|
uae_u16 rom_vector = get_word_debug(list + 16 + 10);
|
|
uae_u8 type = get_byte_debug(list + 16 + 0);
|
|
console_out_f(_T("%02x %02x %08x %08x %04x %02x %08x %04x (%u/%u)\n"),
|
|
type, get_byte_debug(list + 16 + 2),
|
|
addr, get_long_debug(list + 36),
|
|
get_word_debug(list + 16 + 4), get_byte_debug(list + 16 + 1),
|
|
get_long_debug(list + 16 + 6), rom_vector,
|
|
get_word_debug(list + 16 + 4), get_byte_debug(list + 16 + 1));
|
|
for (int i = 0; i < 16; i++) {
|
|
console_out_f(_T("%02x"), get_byte_debug(list + 16 + i));
|
|
if (i < 15)
|
|
console_out_f(_T("."));
|
|
}
|
|
console_out_f(_T("\n"));
|
|
if ((type & 0x10)) {
|
|
uae_u8 diagarea[256];
|
|
uae_u16 nameoffset;
|
|
uaecptr rom = addr + rom_vector;
|
|
uae_u8 config = get_byte_debug(rom);
|
|
copyromdata(config, rom, 0, diagarea, 16);
|
|
nameoffset = (diagarea[8] << 8) | diagarea[9];
|
|
console_out_f(_T(" %02x %02x Size %04x Diag %04x Boot %04x Name %04x %04x %04x\n"),
|
|
diagarea[0], diagarea[1],
|
|
(diagarea[2] << 8) | diagarea[3],
|
|
(diagarea[4] << 8) | diagarea[5],
|
|
(diagarea[6] << 8) | diagarea[7],
|
|
nameoffset,
|
|
(diagarea[10] << 8) | diagarea[11],
|
|
(diagarea[12] << 8) | diagarea[13]);
|
|
if (nameoffset != 0 && nameoffset != 0xffff) {
|
|
copyromdata(config, rom, nameoffset, diagarea, 256);
|
|
diagarea[sizeof diagarea - 1] = 0;
|
|
TCHAR *str = au((char*)diagarea);
|
|
console_out_f(_T(" '%s'\n"), str);
|
|
xfree(str);
|
|
}
|
|
}
|
|
list = get_long_debug(list);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
} else if (c == 'R') { // residents
|
|
list = get_long_debug(execbase + 300);
|
|
while (list) {
|
|
uaecptr resident = get_long_debug (list);
|
|
if (!resident)
|
|
break;
|
|
if (resident & 0x80000000) {
|
|
console_out_f (_T("-> %08X\n"), resident & 0x7fffffff);
|
|
list = resident & 0x7fffffff;
|
|
continue;
|
|
}
|
|
uae_u8 *addr;
|
|
addr = get_real_address_debug(get_long_debug (resident + 14));
|
|
TCHAR *name1 = addr ? au ((char*)addr) : au("<null>");
|
|
my_trim (name1);
|
|
addr = get_real_address_debug(get_long_debug (resident + 18));
|
|
TCHAR *name2 = addr ? au ((char*)addr) : au("<null>");
|
|
my_trim (name2);
|
|
console_out_f (_T("%08X %08X: %02X %3d %02X %+3.3d '%s' ('%s')\n"),
|
|
list, resident,
|
|
get_byte_debug (resident + 10), get_byte_debug (resident + 11),
|
|
get_byte_debug (resident + 12), (uae_s8)get_byte_debug (resident + 13),
|
|
name1, name2);
|
|
xfree (name2);
|
|
xfree (name1);
|
|
list += 4;
|
|
}
|
|
return;
|
|
} else if (c == 'f' || c== 'F') { // filesystem.resource
|
|
uaecptr fs = get_base ("FileSystem.resource", 336);
|
|
if (fs) {
|
|
static const TCHAR *fsnames[] = {
|
|
_T("DosType"),
|
|
_T("Version"),
|
|
_T("PatchFlags"),
|
|
_T("Type"),
|
|
_T("Task"),
|
|
_T("Lock"),
|
|
_T("Handler"),
|
|
_T("StackSize"),
|
|
_T("Priority"),
|
|
_T("Startup"),
|
|
_T("SegList"),
|
|
_T("GlobalVec"),
|
|
NULL
|
|
};
|
|
uae_u8 *addr = get_real_address_debug(get_long_debug (fs + 14));
|
|
TCHAR *name = addr ? au ((char*)addr) : au ("<null>");
|
|
my_trim (name);
|
|
console_out_f (_T("%08x: '%s'\n"), fs, name);
|
|
xfree (name);
|
|
node = get_long_debug (fs + 18);
|
|
while (get_long_debug (node)) {
|
|
TCHAR *name = au ((char*)get_real_address_debug(get_long_debug (node + 10)));
|
|
my_trim (name);
|
|
console_out_f (_T("%08x: '%s'\n"), node, name);
|
|
xfree (name);
|
|
for (int i = 0; fsnames[i]; i++) {
|
|
uae_u32 v = get_long_debug (node + 14 + i * 4);
|
|
console_out_f (_T("%16s = %08x %d\n"), fsnames[i], v, v);
|
|
}
|
|
console_out_f (_T("\n"));
|
|
node = get_long_debug (node);
|
|
}
|
|
|
|
} else {
|
|
console_out_f (_T("FileSystem.resource not found.\n"));
|
|
}
|
|
return;
|
|
} else if (c == 'm' || c == 'M') { // memory
|
|
list = execbase + 322;
|
|
node = get_long_debug (list);
|
|
while (get_long_debug (node)) {
|
|
TCHAR *name = au ((char*)get_real_address_debug(get_long_debug (node + 10)));
|
|
uae_u16 v = get_word_debug (node + 8);
|
|
console_out_f (_T("%08x %d %d %s\n"), node, (int)((v >> 8) & 0xff), (uae_s8)(v & 0xff), name);
|
|
xfree (name);
|
|
console_out_f (_T("Attributes %04x First %08x Lower %08x Upper %08x Free %d\n"),
|
|
get_word_debug (node + 14), get_long_debug (node + 16), get_long_debug (node + 20),
|
|
get_long_debug (node + 24), get_long_debug (node + 28));
|
|
uaecptr mc = get_long_debug (node + 16);
|
|
while (mc) {
|
|
uae_u32 mc1 = get_long_debug (mc);
|
|
uae_u32 mc2 = get_long_debug (mc + 4);
|
|
console_out_f (_T(" %08x: %08x-%08x,%08x,%08x (%d)\n"), mc, mc, mc + mc2, mc1, mc2, mc2);
|
|
mc = mc1;
|
|
}
|
|
console_out_f (_T("\n"));
|
|
node = get_long_debug (node);
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool full = false;
|
|
switch (c)
|
|
{
|
|
case 'r': // resources
|
|
list = execbase + 336;
|
|
break;
|
|
case 'd': // devices
|
|
list = execbase + 350;
|
|
full = true;
|
|
break;
|
|
case 'l': // libraries
|
|
list = execbase + 378;
|
|
full = true;
|
|
break;
|
|
case 'p': // ports
|
|
list = execbase + 392;
|
|
break;
|
|
case 's': // semaphores
|
|
list = execbase + 532;
|
|
break;
|
|
}
|
|
if (list == 0)
|
|
return;
|
|
node = get_long_debug (list);
|
|
while (get_long_debug (node)) {
|
|
TCHAR *name = au ((char*)get_real_address_debug(get_long_debug (node + 10)));
|
|
uae_u16 v = get_word_debug (node + 8);
|
|
console_out_f (_T("%08x %d %d"), node, (int)((v >> 8) & 0xff), (uae_s8)(v & 0xff));
|
|
if (full) {
|
|
uae_u16 ver = get_word_debug(node + 20);
|
|
uae_u16 rev = get_word_debug(node + 22);
|
|
uae_u32 op = get_word_debug(node + 32);
|
|
console_out_f(_T(" %d.%d %d"), ver, rev, op);
|
|
}
|
|
console_out_f(_T(" %s"), name);
|
|
xfree (name);
|
|
if (full) {
|
|
uaecptr idstring = get_long_debug(node + 24);
|
|
if (idstring) {
|
|
name = au((char*)get_real_address_debug(idstring));
|
|
console_out_f(_T(" (%s)"), name);
|
|
xfree(name);
|
|
}
|
|
}
|
|
console_out_f(_T("\n"));
|
|
node = get_long_debug (node);
|
|
}
|
|
}
|
|
|
|
static int debug_vpos = -1;
|
|
static int debug_hpos = -1;
|
|
|
|
static void breakfunc(uae_u32 v)
|
|
{
|
|
write_log(_T("Cycle breakpoint hit\n"));
|
|
debugging = 1;
|
|
debug_vpos = -1;
|
|
debug_hpos = -1;
|
|
debug_cycles(2);
|
|
set_special(SPCFLAG_BRK);
|
|
}
|
|
|
|
void debug_hsync(void)
|
|
{
|
|
if (debug_vpos < 0) {
|
|
return;
|
|
}
|
|
if (debug_vpos != vpos) {
|
|
return;
|
|
}
|
|
if (debug_hpos <= 0) {
|
|
breakfunc(0);
|
|
} else {
|
|
if (current_hpos() < debug_hpos) {
|
|
event2_newevent_x(-1, debug_hpos - current_hpos(), 0, breakfunc);
|
|
} else {
|
|
breakfunc(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int cycle_breakpoint(TCHAR **c)
|
|
{
|
|
TCHAR nc = (*c)[0];
|
|
next_char(c);
|
|
if (more_params(c)) {
|
|
int count = readint(c, NULL);
|
|
if (nc == 's') {
|
|
if (more_params(c)) {
|
|
debug_vpos = count;
|
|
debug_hpos = readint(c, NULL);
|
|
if (debug_vpos == vpos && debug_hpos > current_hpos()) {
|
|
debug_vpos = -1;
|
|
count = debug_hpos - current_hpos();
|
|
debug_hpos = -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
} else {
|
|
count *= maxhpos;
|
|
}
|
|
}
|
|
if (count > 0) {
|
|
event2_newevent_x(-1, count, 0, breakfunc);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
static int trace_same_insn_count;
|
|
static uae_u8 trace_insn_copy[10];
|
|
static struct regstruct trace_prev_regs;
|
|
#endif
|
|
static uaecptr nextpc;
|
|
|
|
static void check_breakpoint_extra(TCHAR **c, struct breakpoint_node *bpn)
|
|
{
|
|
bpn->cnt = 0;
|
|
bpn->chain = -1;
|
|
if (more_params(c)) {
|
|
TCHAR nc = _totupper((*c)[0]);
|
|
if (nc == 'N') {
|
|
next_char(c);
|
|
bpn->cnt = readint(c, NULL);
|
|
}
|
|
}
|
|
if (more_params(c)) {
|
|
TCHAR nc = _totupper((*c)[0]);
|
|
if (nc == 'H') {
|
|
next_char(c);
|
|
bpn->chain = readint(c, NULL);
|
|
if (bpn->chain < 0 || bpn->chain >= BREAKPOINT_TOTAL) {
|
|
bpn->chain = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int instruction_breakpoint(TCHAR **c)
|
|
{
|
|
struct breakpoint_node *bpn;
|
|
int i;
|
|
TCHAR next = 0;
|
|
|
|
if (more_params (c)) {
|
|
TCHAR nc = _totupper ((*c)[0]);
|
|
if (nc == 'O') {
|
|
// bpnum register operation value1 [mask value2]
|
|
next_char(c);
|
|
if (more_params(c)) {
|
|
int bpidx = readint(c, NULL);
|
|
if (more_params(c) && bpidx >= 0 && bpidx < BREAKPOINT_TOTAL) {
|
|
bpn = &bpnodes[bpidx];
|
|
int regid = getregidx(c);
|
|
if (regid >= 0) {
|
|
bpn->type = regid;
|
|
bpn->mask = 0xffffffff;
|
|
if (more_params(c)) {
|
|
int operid = getoperidx(c, &bpn->opersigned);
|
|
if (more_params(c) && operid >= 0) {
|
|
bpn->oper = operid;
|
|
bpn->value1 = readhex(c, NULL);
|
|
bpn->enabled = 1;
|
|
if (more_params(c)) {
|
|
bpn->mask = readhex(c, NULL);
|
|
if (more_params(c)) {
|
|
bpn->value2 = readhex(c, NULL);
|
|
}
|
|
}
|
|
check_breakpoint_extra(c, bpn);
|
|
console_out(_T("Breakpoint added.\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
} else if (nc == 'I') {
|
|
uae_u16 opcodes[32];
|
|
next_char(c);
|
|
ignore_ws(c);
|
|
trace_param[1] = 0x10000;
|
|
trace_param[2] = 0x10000;
|
|
|
|
int w = m68k_asm(*c, opcodes, 0);
|
|
if (w > 0) {
|
|
trace_param[0] = opcodes[0];
|
|
if (w > 1) {
|
|
trace_param[1] = opcodes[1];
|
|
if (w > 2) {
|
|
trace_param[2] = opcodes[2];
|
|
}
|
|
}
|
|
} else {
|
|
if (more_params(c)) {
|
|
trace_param[0] = readhex(c, NULL);
|
|
if (more_params(c)) {
|
|
trace_param[1] = readhex(c, NULL);
|
|
}
|
|
if (more_params(c)) {
|
|
trace_param[2] = readhex(c, NULL);
|
|
}
|
|
} else {
|
|
trace_param[0] = 0x10000;
|
|
}
|
|
}
|
|
trace_mode = TRACE_MATCH_INS;
|
|
return 1;
|
|
} else if (nc == 'D' && (*c)[1] == 0) {
|
|
for (i = 0; i < BREAKPOINT_TOTAL; i++) {
|
|
bpnodes[i].enabled = 0;
|
|
}
|
|
console_out(_T("All breakpoints removed.\n"));
|
|
return 0;
|
|
} else if (nc == 'R' && (*c)[1] == 0) {
|
|
if (more_params(c)) {
|
|
int bpnum = readint(c, NULL);
|
|
if (bpnum >= 0 && bpnum < BREAKPOINT_TOTAL) {
|
|
bpnodes[bpnum].enabled = 0;
|
|
console_out_f(_T("Breakpoint %d removed.\n"), bpnum);
|
|
}
|
|
}
|
|
return 0;
|
|
} else if (nc == 'L') {
|
|
int got = 0;
|
|
for (i = 0; i < BREAKPOINT_TOTAL; i++) {
|
|
bpn = &bpnodes[i];
|
|
if (!bpn->enabled)
|
|
continue;
|
|
console_out_f (_T("%d: %s %s %08x [%08x %08x]"), i, debugregs[bpn->type], debugoper[bpn->oper], bpn->value1, bpn->mask, bpn->value2);
|
|
if (bpn->cnt > 0) {
|
|
console_out_f(_T(" N=%d"), bpn->cnt);
|
|
}
|
|
if (bpn->chain > 0) {
|
|
console_out_f(_T(" H=%d"), bpn->chain);
|
|
}
|
|
console_out_f(_T("\n"));
|
|
got = 1;
|
|
}
|
|
if (!got)
|
|
console_out (_T("No breakpoints.\n"));
|
|
else
|
|
console_out (_T("\n"));
|
|
return 0;
|
|
}
|
|
trace_mode = TRACE_RANGE_PC;
|
|
trace_param[0] = readhex(c, NULL);
|
|
if (more_params (c)) {
|
|
trace_param[1] = readhex(c, NULL);
|
|
return 1;
|
|
} else {
|
|
for (i = 0; i < BREAKPOINT_TOTAL; i++) {
|
|
bpn = &bpnodes[i];
|
|
if (bpn->enabled && bpn->value1 == trace_param[0]) {
|
|
bpn->enabled = 0;
|
|
console_out (_T("Breakpoint removed.\n"));
|
|
trace_mode = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
for (i = 0; i < BREAKPOINT_TOTAL; i++) {
|
|
bpn = &bpnodes[i];
|
|
if (bpn->enabled)
|
|
continue;
|
|
bpn->value1 = trace_param[0];
|
|
bpn->type = BREAKPOINT_REG_PC;
|
|
bpn->oper = BREAKPOINT_CMP_EQUAL;
|
|
bpn->enabled = 1;
|
|
check_breakpoint_extra(c, bpn);
|
|
console_out (_T("Breakpoint added.\n"));
|
|
trace_mode = 0;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
trace_mode = TRACE_RAM_PC;
|
|
return 1;
|
|
}
|
|
|
|
static int process_breakpoint(TCHAR **c)
|
|
{
|
|
bool err;
|
|
processptr = 0;
|
|
xfree(processname);
|
|
processname = NULL;
|
|
if (!more_params(c))
|
|
return 0;
|
|
if (**c == '\"') {
|
|
TCHAR pn[200];
|
|
next_string(c, pn, sizeof(pn) / sizeof(TCHAR), 0);
|
|
processname = ua(pn);
|
|
} else {
|
|
processptr = readhex(c, &err);
|
|
if (err) {
|
|
return 0;
|
|
}
|
|
}
|
|
trace_mode = TRACE_CHECKONLY;
|
|
return 1;
|
|
}
|
|
|
|
static void saveloadmem (TCHAR **cc, bool save)
|
|
{
|
|
uae_u8 b;
|
|
uae_u32 src, src2;
|
|
int len, len2;
|
|
TCHAR *name;
|
|
FILE *fp;
|
|
|
|
if (!more_params (cc))
|
|
goto S_argh;
|
|
|
|
name = *cc;
|
|
while (**cc != '\0' && !isspace (**cc))
|
|
(*cc)++;
|
|
if (!isspace (**cc))
|
|
goto S_argh;
|
|
|
|
**cc = '\0';
|
|
(*cc)++;
|
|
if (!more_params (cc))
|
|
goto S_argh;
|
|
src2 = src = readhex(cc, NULL);
|
|
if (save) {
|
|
if (!more_params(cc))
|
|
goto S_argh;
|
|
}
|
|
len2 = len = -1;
|
|
if (more_params(cc)) {
|
|
len2 = len = readhex(cc, NULL);
|
|
}
|
|
fp = uae_tfopen (name, save ? _T("wb") : _T("rb"));
|
|
if (fp == NULL) {
|
|
console_out_f (_T("Couldn't open file '%s'.\n"), name);
|
|
return;
|
|
}
|
|
if (save) {
|
|
while (len > 0) {
|
|
b = get_byte_debug (src);
|
|
src++;
|
|
len--;
|
|
if (fwrite (&b, 1, 1, fp) != 1) {
|
|
console_out (_T("Error writing file.\n"));
|
|
break;
|
|
}
|
|
}
|
|
if (len == 0)
|
|
console_out_f (_T("Wrote %08X - %08X (%d bytes) to '%s'.\n"),
|
|
src2, src2 + len2, len2, name);
|
|
} else {
|
|
len2 = 0;
|
|
while (len != 0) {
|
|
if (fread(&b, 1, 1, fp) != 1) {
|
|
if (len > 0)
|
|
console_out (_T("Unexpected end of file.\n"));
|
|
len = 0;
|
|
break;
|
|
}
|
|
put_byte (src, b);
|
|
src++;
|
|
if (len > 0)
|
|
len--;
|
|
len2++;
|
|
}
|
|
if (len == 0)
|
|
console_out_f (_T("Read %08X - %08X (%d bytes) to '%s'.\n"),
|
|
src2, src2 + len2, len2, name);
|
|
}
|
|
fclose (fp);
|
|
return;
|
|
S_argh:
|
|
console_out (_T("Command needs more arguments!\n"));
|
|
}
|
|
|
|
static void searchmem (TCHAR **cc)
|
|
{
|
|
int i, sslen, got, val, stringmode;
|
|
uae_u8 ss[256];
|
|
uae_u32 addr, endaddr;
|
|
TCHAR nc;
|
|
|
|
got = 0;
|
|
sslen = 0;
|
|
stringmode = 0;
|
|
ignore_ws (cc);
|
|
while(more_params(cc)) {
|
|
if (**cc == '"' || **cc == '\'') {
|
|
TCHAR quoted = **cc;
|
|
stringmode = 1;
|
|
(*cc)++;
|
|
while (**cc != quoted && **cc != 0) {
|
|
ss[sslen++] = tolower(**cc);
|
|
(*cc)++;
|
|
}
|
|
if (**cc != 0) {
|
|
(*cc)++;
|
|
}
|
|
} else {
|
|
for (;;) {
|
|
if (**cc == 32 || **cc == 0) {
|
|
break;
|
|
}
|
|
if (**cc == '"' || **cc == '\'') {
|
|
break;
|
|
}
|
|
nc = _totupper(next_char(cc));
|
|
if (isspace(nc))
|
|
break;
|
|
if (isdigit(nc))
|
|
val = nc - '0';
|
|
else
|
|
val = nc - 'A' + 10;
|
|
if (val < 0 || val > 15)
|
|
return;
|
|
val *= 16;
|
|
if (**cc == 32 || **cc == 0)
|
|
break;
|
|
nc = _totupper(next_char(cc));
|
|
if (isspace(nc))
|
|
break;
|
|
if (isdigit(nc))
|
|
val += nc - '0';
|
|
else
|
|
val += nc - 'A' + 10;
|
|
if (val < 0 || val > 255)
|
|
return;
|
|
ss[sslen++] = (uae_u8)val;
|
|
}
|
|
}
|
|
if (**cc == 0 || **cc == 32) {
|
|
break;
|
|
}
|
|
if (**cc != '"' && **cc != '\'') {
|
|
(*cc)++;
|
|
}
|
|
}
|
|
if (sslen == 0)
|
|
return;
|
|
ignore_ws (cc);
|
|
addr = 0xffffffff;
|
|
endaddr = lastaddr(addr);
|
|
if (more_params (cc)) {
|
|
addr = readhex(cc, NULL);
|
|
addr--;
|
|
endaddr = lastaddr(addr);
|
|
if (more_params(cc)) {
|
|
endaddr = readhex(cc, NULL);
|
|
}
|
|
}
|
|
console_out_f (_T("Searching from %08X to %08X..\n"), addr + 1, endaddr);
|
|
nextaddr_init(addr);
|
|
bool out = false;
|
|
while ((addr = nextaddr(addr, endaddr, NULL, true, &out)) != 0xffffffff) {
|
|
if (addr == endaddr)
|
|
break;
|
|
for (i = 0; i < sslen; i++) {
|
|
uae_u8 b = get_byte_debug (addr + i);
|
|
if (stringmode) {
|
|
if (tolower (b) != ss[i])
|
|
break;
|
|
} else {
|
|
if (b != ss[i])
|
|
break;
|
|
}
|
|
}
|
|
if (i == sslen) {
|
|
got++;
|
|
console_out_f (_T(" %08X"), addr);
|
|
out = true;
|
|
if (got > 100) {
|
|
console_out (_T("\nMore than 100 results, aborting.."));
|
|
break;
|
|
}
|
|
}
|
|
if (iscancel (65536)) {
|
|
console_out_f (_T("Aborted at %08X\n"), addr);
|
|
break;
|
|
}
|
|
}
|
|
if (!got)
|
|
console_out (_T("nothing found"));
|
|
console_out (_T("\n"));
|
|
}
|
|
|
|
static int staterecorder (TCHAR **cc)
|
|
{
|
|
#if 0
|
|
TCHAR nc;
|
|
|
|
if (!more_params (cc)) {
|
|
if (savestate_dorewind (1)) {
|
|
debug_rewind = 1;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
nc = next_char (cc);
|
|
if (nc == 'l') {
|
|
savestate_listrewind ();
|
|
return 0;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int debugtest_modes[DEBUGTEST_MAX];
|
|
static const TCHAR *debugtest_names[] = {
|
|
_T("Blitter"), _T("Keyboard"), _T("Floppy")
|
|
};
|
|
|
|
void debugtest (enum debugtest_item di, const TCHAR *format, ...)
|
|
{
|
|
va_list parms;
|
|
TCHAR buffer[1000];
|
|
|
|
if (!debugtest_modes[di])
|
|
return;
|
|
va_start (parms, format);
|
|
_vsntprintf (buffer, 1000 - 1, format, parms);
|
|
va_end (parms);
|
|
write_log (_T("%s PC=%08X: %s\n"), debugtest_names[di], M68K_GETPC, buffer);
|
|
if (debugtest_modes[di] == 2)
|
|
activate_debugger_new();
|
|
}
|
|
|
|
static void debugtest_set (TCHAR **inptr)
|
|
{
|
|
int i, val, val2;
|
|
ignore_ws (inptr);
|
|
|
|
val2 = 1;
|
|
if (!more_params (inptr)) {
|
|
for (i = 0; i < DEBUGTEST_MAX; i++)
|
|
debugtest_modes[i] = 0;
|
|
console_out (_T("All debugtests disabled\n"));
|
|
return;
|
|
}
|
|
val = readint(inptr, NULL);
|
|
if (more_params (inptr)) {
|
|
val2 = readint(inptr, NULL);
|
|
if (val2 > 0)
|
|
val2 = 2;
|
|
}
|
|
if (val < 0) {
|
|
for (i = 0; i < DEBUGTEST_MAX; i++)
|
|
debugtest_modes[i] = val2;
|
|
console_out (_T("All debugtests enabled\n"));
|
|
return;
|
|
}
|
|
if (val >= 0 && val < DEBUGTEST_MAX) {
|
|
if (debugtest_modes[val])
|
|
debugtest_modes[val] = 0;
|
|
else
|
|
debugtest_modes[val] = val2;
|
|
console_out_f (_T("Debugtest '%s': %s. break = %s\n"),
|
|
debugtest_names[val], debugtest_modes[val] ? _T("on") :_T("off"), val2 == 2 ? _T("on") : _T("off"));
|
|
}
|
|
}
|
|
|
|
static void debug_sprite (TCHAR **inptr)
|
|
{
|
|
uaecptr saddr, addr, addr2;
|
|
int xpos, xpos_ecs;
|
|
int ypos, ypos_ecs;
|
|
int ypose, ypose_ecs;
|
|
int attach;
|
|
uae_u64 w1, w2, ww1, ww2;
|
|
int size = 1, width;
|
|
int ecs, sh10;
|
|
int y, i;
|
|
TCHAR tmp[80];
|
|
int max = 14;
|
|
|
|
addr2 = 0;
|
|
ignore_ws(inptr);
|
|
addr = readhex(inptr, NULL);
|
|
ignore_ws(inptr);
|
|
if (more_params (inptr))
|
|
size = readhex(inptr, NULL);
|
|
if (size != 1 && size != 2 && size != 4) {
|
|
addr2 = size;
|
|
ignore_ws(inptr);
|
|
if (more_params(inptr))
|
|
size = readint(inptr, NULL);
|
|
if (size != 1 && size != 2 && size != 4)
|
|
size = 1;
|
|
}
|
|
for (;;) {
|
|
ecs = 0;
|
|
sh10 = 0;
|
|
saddr = addr;
|
|
width = size * 16;
|
|
w1 = get_word_debug (addr);
|
|
w2 = get_word_debug (addr + size * 2);
|
|
console_out_f (_T(" %06X "), addr);
|
|
for (i = 0; i < size * 2; i++)
|
|
console_out_f (_T("%04X "), get_word_debug (addr + i * 2));
|
|
console_out_f (_T("\n"));
|
|
|
|
ypos = (int)(w1 >> 8);
|
|
xpos = w1 & 255;
|
|
ypose = (int)(w2 >> 8);
|
|
attach = (w2 & 0x80) ? 1 : 0;
|
|
if (w2 & 4)
|
|
ypos |= 256;
|
|
if (w2 & 2)
|
|
ypose |= 256;
|
|
ypos_ecs = ypos;
|
|
ypose_ecs = ypose;
|
|
if (w2 & 0x40)
|
|
ypos_ecs |= 512;
|
|
if (w2 & 0x20)
|
|
ypose_ecs |= 512;
|
|
xpos <<= 1;
|
|
if (w2 & 0x01)
|
|
xpos |= 1;
|
|
xpos_ecs = xpos << 2;
|
|
if (w2 & 0x10)
|
|
xpos_ecs |= 2;
|
|
if (w2 & 0x08)
|
|
xpos_ecs |= 1;
|
|
if (w2 & (0x40 | 0x20 | 0x10 | 0x08))
|
|
ecs = 1;
|
|
if (w1 & 0x80)
|
|
sh10 = 1;
|
|
if (ypose < ypos)
|
|
ypose += 256;
|
|
|
|
if (ecs_agnus) {
|
|
ypos = ypos_ecs;
|
|
ypose = ypose_ecs;
|
|
}
|
|
|
|
for (y = ypos; y < ypose; y++) {
|
|
int x;
|
|
addr += size * 4;
|
|
if (addr2)
|
|
addr2 += size * 4;
|
|
if (size == 1) {
|
|
w1 = get_word_debug (addr);
|
|
w2 = get_word_debug (addr + 2);
|
|
if (addr2) {
|
|
ww1 = get_word_debug (addr2);
|
|
ww2 = get_word_debug (addr2 + 2);
|
|
}
|
|
} else if (size == 2) {
|
|
w1 = get_long_debug (addr);
|
|
w2 = get_long_debug (addr + 4);
|
|
if (addr2) {
|
|
ww1 = get_long_debug (addr2);
|
|
ww2 = get_long_debug (addr2 + 4);
|
|
}
|
|
} else if (size == 4) {
|
|
w1 = get_long_debug (addr + 0);
|
|
w2 = get_long_debug (addr + 8);
|
|
w1 <<= 32;
|
|
w2 <<= 32;
|
|
w1 |= get_long_debug (addr + 4);
|
|
w2 |= get_long_debug (addr + 12);
|
|
if (addr2) {
|
|
ww1 = get_long_debug (addr2 + 0);
|
|
ww2 = get_long_debug (addr2 + 8);
|
|
ww1 <<= 32;
|
|
ww2 <<= 32;
|
|
ww1 |= get_long_debug (addr2 + 4);
|
|
ww2 |= get_long_debug (addr2 + 12);
|
|
}
|
|
}
|
|
width = size * 16;
|
|
for (x = 0; x < width; x++) {
|
|
int v1 = w1 & 1;
|
|
int v2 = w2 & 1;
|
|
int v = v2 * 2 + v1;
|
|
w1 >>= 1;
|
|
w2 >>= 1;
|
|
if (addr2) {
|
|
int vv1 = ww1 & 1;
|
|
int vv2 = ww2 & 1;
|
|
int vv = vv2 * 2 + vv1;
|
|
ww1 >>= 1;
|
|
ww2 >>= 1;
|
|
v *= 4;
|
|
v += vv;
|
|
tmp[width - (x + 1)] = v >= 10 ? 'A' + v - 10 : v + '0';
|
|
} else {
|
|
tmp[width - (x + 1)] = v + '0';
|
|
}
|
|
}
|
|
tmp[width] = 0;
|
|
console_out_f (_T("%3d %06X %s\n"), y, addr, tmp);
|
|
}
|
|
|
|
console_out_f (_T("Sprite address %08X, width = %d\n"), saddr, size * 16);
|
|
console_out_f (_T("OCS: StartX=%d StartY=%d EndY=%d\n"), xpos, ypos, ypose);
|
|
console_out_f (_T("ECS: StartX=%d (%d.%d) StartY=%d EndY=%d%s\n"), xpos_ecs, xpos_ecs / 4, xpos_ecs & 3, ypos_ecs, ypose_ecs, ecs ? _T(" (*)") : _T(""));
|
|
console_out_f (_T("Attach: %d. AGA SSCAN/SH10 bit: %d\n"), attach, sh10);
|
|
|
|
addr += size * 4;
|
|
if (get_word_debug (addr) == 0 && get_word_debug (addr + size * 4) == 0)
|
|
break;
|
|
max--;
|
|
if (max <= 0) {
|
|
console_out_f(_T("Max sprite count reached.\n"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
int debug_write_memory_16 (uaecptr addr, uae_u16 v)
|
|
{
|
|
addrbank *ad;
|
|
|
|
ad = &get_mem_bank (addr);
|
|
if (ad) {
|
|
ad->wput (addr, v);
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
int debug_write_memory_8 (uaecptr addr, uae_u8 v)
|
|
{
|
|
addrbank *ad;
|
|
|
|
ad = &get_mem_bank (addr);
|
|
if (ad) {
|
|
ad->bput (addr, v);
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
int debug_peek_memory_16 (uaecptr addr)
|
|
{
|
|
addrbank *ad;
|
|
|
|
ad = &get_mem_bank (addr);
|
|
if (ad->flags & (ABFLAG_RAM | ABFLAG_ROM | ABFLAG_ROMIN | ABFLAG_SAFE))
|
|
return ad->wget (addr);
|
|
if (ad == &custom_bank) {
|
|
addr &= 0x1fe;
|
|
return (ar_custom[addr + 0] << 8) | ar_custom[addr + 1];
|
|
}
|
|
return -1;
|
|
}
|
|
int debug_read_memory_16 (uaecptr addr)
|
|
{
|
|
addrbank *ad;
|
|
|
|
ad = &get_mem_bank (addr);
|
|
if (ad)
|
|
return ad->wget (addr);
|
|
return -1;
|
|
}
|
|
int debug_read_memory_8 (uaecptr addr)
|
|
{
|
|
addrbank *ad;
|
|
|
|
ad = &get_mem_bank (addr);
|
|
if (ad)
|
|
return ad->bget (addr);
|
|
return -1;
|
|
}
|
|
|
|
static void disk_debug (TCHAR **inptr)
|
|
{
|
|
TCHAR parm[10];
|
|
int i;
|
|
|
|
if (**inptr == 'd') {
|
|
(*inptr)++;
|
|
ignore_ws (inptr);
|
|
disk_debug_logging = readint(inptr, NULL);
|
|
console_out_f (_T("Disk logging level %d\n"), disk_debug_logging);
|
|
return;
|
|
}
|
|
disk_debug_mode = 0;
|
|
disk_debug_track = -1;
|
|
ignore_ws (inptr);
|
|
if (!next_string (inptr, parm, sizeof (parm) / sizeof (TCHAR), 1))
|
|
goto end;
|
|
for (i = 0; i < _tcslen(parm); i++) {
|
|
if (parm[i] == 'R')
|
|
disk_debug_mode |= DISK_DEBUG_DMA_READ;
|
|
if (parm[i] == 'W')
|
|
disk_debug_mode |= DISK_DEBUG_DMA_WRITE;
|
|
if (parm[i] == 'P')
|
|
disk_debug_mode |= DISK_DEBUG_PIO;
|
|
}
|
|
if (more_params(inptr))
|
|
disk_debug_track = readint(inptr, NULL);
|
|
if (disk_debug_track < 0 || disk_debug_track > 2 * 83)
|
|
disk_debug_track = -1;
|
|
if (disk_debug_logging == 0)
|
|
disk_debug_logging = 1;
|
|
end:
|
|
console_out_f (_T("Disk breakpoint mode %c%c%c track %d\n"),
|
|
disk_debug_mode & DISK_DEBUG_DMA_READ ? 'R' : '-',
|
|
disk_debug_mode & DISK_DEBUG_DMA_WRITE ? 'W' : '-',
|
|
disk_debug_mode & DISK_DEBUG_PIO ? 'P' : '-',
|
|
disk_debug_track);
|
|
}
|
|
|
|
static void find_ea (TCHAR **inptr)
|
|
{
|
|
uae_u32 ea, sea, dea;
|
|
uaecptr addr, end, end2;
|
|
int hits = 0;
|
|
bool err;
|
|
|
|
addr = 0xffffffff;
|
|
end = lastaddr(addr);
|
|
ea = readhex(inptr, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
if (more_params(inptr)) {
|
|
addr = readhex(inptr, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
addr--;
|
|
end = lastaddr(addr);
|
|
if (more_params(inptr)) {
|
|
end = readhex(inptr, &err);
|
|
if (err) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
console_out_f (_T("Searching from %08X to %08X\n"), addr + 1, end);
|
|
end2 = 0;
|
|
nextaddr_init(addr);
|
|
bool out = false;
|
|
while((addr = nextaddr(addr, end, &end2, true, &out)) != 0xffffffff) {
|
|
if ((addr & 1) == 0 && addr + 6 <= end2) {
|
|
sea = 0xffffffff;
|
|
dea = 0xffffffff;
|
|
m68k_disasm_ea (addr, NULL, 1, &sea, &dea, 0xffffffff);
|
|
if (ea == sea || ea == dea) {
|
|
m68k_disasm (addr, NULL, 0xffffffff, 1);
|
|
out = true;
|
|
hits++;
|
|
if (hits > 100) {
|
|
console_out_f (_T("Too many hits. End addr = %08X\n"), addr);
|
|
break;
|
|
}
|
|
}
|
|
if (iscancel (65536)) {
|
|
console_out_f (_T("Aborted at %08X\n"), addr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void m68k_modify (TCHAR **inptr)
|
|
{
|
|
uae_u32 v;
|
|
TCHAR parm[10];
|
|
TCHAR c1, c2;
|
|
int i;
|
|
|
|
if (!next_string (inptr, parm, sizeof (parm) / sizeof (TCHAR), 1))
|
|
return;
|
|
c1 = _totupper (parm[0]);
|
|
c2 = 99;
|
|
if (c1 == 'A' || c1 == 'D' || c1 == 'P') {
|
|
c2 = _totupper (parm[1]);
|
|
if (isdigit (c2))
|
|
c2 -= '0';
|
|
else
|
|
c2 = 99;
|
|
}
|
|
v = readhex(inptr, NULL);
|
|
if (c1 == 'A' && c2 < 8)
|
|
regs.regs[8 + c2] = v;
|
|
else if (c1 == 'D' && c2 < 8)
|
|
regs.regs[c2] = v;
|
|
else if (c1 == 'P' && c2 == 0)
|
|
regs.irc = v;
|
|
else if (c1 == 'P' && c2 == 1)
|
|
regs.ir = v;
|
|
else if (!_tcscmp (parm, _T("SR"))) {
|
|
regs.sr = v;
|
|
MakeFromSR ();
|
|
} else if (!_tcscmp (parm, _T("CCR"))) {
|
|
regs.sr = (regs.sr & ~31) | (v & 31);
|
|
MakeFromSR ();
|
|
} else if (!_tcscmp (parm, _T("USP"))) {
|
|
regs.usp = v;
|
|
} else if (!_tcscmp (parm, _T("ISP"))) {
|
|
regs.isp = v;
|
|
} else if (!_tcscmp (parm, _T("PC"))) {
|
|
m68k_setpc (v);
|
|
fill_prefetch ();
|
|
} else {
|
|
for (i = 0; m2cregs[i].regname; i++) {
|
|
if (!_tcscmp (parm, m2cregs[i].regname))
|
|
val_move2c2 (m2cregs[i].regno, v);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ppc_disasm(uaecptr addr, uaecptr *nextpc, int cnt)
|
|
{
|
|
PPCD_CB disa;
|
|
|
|
while(cnt-- > 0) {
|
|
uae_u32 instr = get_long_debug(addr);
|
|
disa.pc = addr;
|
|
disa.instr = instr;
|
|
PPCDisasm(&disa);
|
|
TCHAR *mnemo = au(disa.mnemonic);
|
|
TCHAR *ops = au(disa.operands);
|
|
console_out_f(_T("%08X %08X %-12s%-30s\n"), addr, instr, mnemo, ops);
|
|
xfree(ops);
|
|
xfree(mnemo);
|
|
addr += 4;
|
|
}
|
|
if (nextpc)
|
|
*nextpc = addr;
|
|
}
|
|
|
|
static void dma_disasm(int frames, int vp, int hp, int frames_end, int vp_end, int hp_end)
|
|
{
|
|
if (!dma_record[0] || frames < 0 || vp < 0 || hp < 0)
|
|
return;
|
|
for (;;) {
|
|
struct dma_rec *dr = NULL, *drs = NULL;
|
|
if (dma_record_frame[0] == frames) {
|
|
drs = &dma_record[0][vp * NR_DMA_REC_HPOS];
|
|
dr = &dma_record[0][vp * NR_DMA_REC_HPOS + hp];
|
|
} else if (dma_record_frame[1] == frames) {
|
|
drs = &dma_record[1][vp * NR_DMA_REC_HPOS];
|
|
dr = &dma_record[1][vp * NR_DMA_REC_HPOS + hp];
|
|
}
|
|
if (!dr)
|
|
return;
|
|
TCHAR l1[16], l2[16], l3[16], l4[16];
|
|
if (get_record_dma_info(drs, dr, l1, l2, l3, l4, NULL, NULL, NULL, NULL)) {
|
|
TCHAR tmp[256];
|
|
_stprintf(tmp, _T(" - %02d %02X %s"), dr->ipl, dr->hpos, l2);
|
|
while (_tcslen(tmp) < 20) {
|
|
_tcscat(tmp, _T(" "));
|
|
}
|
|
console_out_f(_T("%s %11s %11s\n"), tmp, l3, l4);
|
|
}
|
|
hp++;
|
|
if (dr->end || hp >= NR_DMA_REC_HPOS) {
|
|
hp = 0;
|
|
vp++;
|
|
if (vp >= maxvpos + 1) {
|
|
vp = 0;
|
|
frames++;
|
|
break;
|
|
}
|
|
}
|
|
if ((frames == frames_end && vp == vp_end && hp == hp_end) || frames > frames_end)
|
|
break;
|
|
if (vp_end < 0 || hp_end < 0 || frames_end < 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uaecptr nxdis, nxmem, asmaddr;
|
|
static bool ppcmode, asmmode;
|
|
|
|
static bool parsecmd(TCHAR *cmd, bool *out)
|
|
{
|
|
if (!_tcsicmp(cmd, _T("reset"))) {
|
|
deactivate_debugger();
|
|
debug_continue();
|
|
uae_reset(0, 0);
|
|
return true;
|
|
}
|
|
if (!_tcsicmp(cmd, _T("reseth"))) {
|
|
deactivate_debugger();
|
|
debug_continue();
|
|
uae_reset(1, 0);
|
|
return true;
|
|
}
|
|
if (!_tcsicmp(cmd, _T("resetk"))) {
|
|
deactivate_debugger();
|
|
debug_continue();
|
|
uae_reset(0, 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool debug_line (TCHAR *input)
|
|
{
|
|
TCHAR cmd, *inptr;
|
|
uaecptr addr;
|
|
bool err;
|
|
|
|
inptr = input;
|
|
|
|
if (asmmode) {
|
|
if (more_params(&inptr)) {
|
|
if (!_tcsicmp(inptr, _T("x"))) {
|
|
asmmode = false;
|
|
return false;
|
|
}
|
|
uae_u16 asmout[16];
|
|
int inss = m68k_asm(inptr, asmout, asmaddr);
|
|
if (inss > 0) {
|
|
for (int i = 0; i < inss; i++) {
|
|
put_word(asmaddr + i * 2, asmout[i]);
|
|
}
|
|
m68k_disasm(asmaddr, &nxdis, 0xffffffff, 1);
|
|
asmaddr = nxdis;
|
|
}
|
|
console_out_f(_T("%08X "), asmaddr);
|
|
return false;
|
|
} else {
|
|
asmmode = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ignore_ws(&inptr);
|
|
if (parsecmd(inptr, &err)) {
|
|
return err;
|
|
}
|
|
cmd = next_char (&inptr);
|
|
|
|
switch (cmd)
|
|
{
|
|
case 'I':
|
|
if (more_params (&inptr)) {
|
|
static int recursive;
|
|
if (!recursive) {
|
|
recursive++;
|
|
handle_custom_event(inptr, 0);
|
|
device_check_config();
|
|
recursive--;
|
|
}
|
|
}
|
|
break;
|
|
case 'c': dumpcia (); dumpdisk (_T("DEBUG")); dumpcustom (); break;
|
|
case 'i':
|
|
{
|
|
if (*inptr == 'l') {
|
|
next_char (&inptr);
|
|
if (more_params (&inptr)) {
|
|
debug_illegal_mask = readhex(&inptr, NULL);
|
|
if (more_params(&inptr))
|
|
debug_illegal_mask |= ((uae_u64)readhex(&inptr, NULL)) << 32;
|
|
} else {
|
|
debug_illegal_mask = debug_illegal ? 0 : -1;
|
|
debug_illegal_mask &= ~((uae_u64)255 << 24); // mask interrupts
|
|
}
|
|
console_out_f (_T("Exception breakpoint mask: %08X %08X\n"), (uae_u32)(debug_illegal_mask >> 32), (uae_u32)debug_illegal_mask);
|
|
debug_illegal = debug_illegal_mask ? 1 : 0;
|
|
} else {
|
|
addr = 0xffffffff;
|
|
if (more_params (&inptr))
|
|
addr = readhex (&inptr, NULL);
|
|
dump_vectors (addr);
|
|
}
|
|
break;
|
|
}
|
|
case 'e':
|
|
{
|
|
bool aga = tolower(*inptr) == 'a';
|
|
if (aga)
|
|
next_char(&inptr);
|
|
bool ext = tolower(*inptr) == 'x';
|
|
dump_custom_regs(aga, ext);
|
|
}
|
|
break;
|
|
case 'r':
|
|
{
|
|
if (*inptr == 'c') {
|
|
next_char(&inptr);
|
|
m68k_dumpcache(*inptr == 'd');
|
|
} else if (*inptr == 's') {
|
|
if (*(inptr + 1) == 's')
|
|
debugmem_list_stackframe(true);
|
|
else
|
|
debugmem_list_stackframe(false);
|
|
} else if (more_params(&inptr)) {
|
|
m68k_modify(&inptr);
|
|
} else {
|
|
custom_dumpstate(0);
|
|
m68k_dumpstate(&nextpc, 0xffffffff);
|
|
}
|
|
}
|
|
break;
|
|
case 'D': deepcheatsearch (&inptr); break;
|
|
case 'C': cheatsearch (&inptr); break;
|
|
case 'W': writeintomem (&inptr); break;
|
|
case 'w': memwatch (&inptr); break;
|
|
case 'S': saveloadmem (&inptr, true); break;
|
|
case 'L': saveloadmem (&inptr, false); break;
|
|
case 's':
|
|
if (*inptr == 'e' && *(inptr + 1) == 'g') {
|
|
next_char(&inptr);
|
|
next_char(&inptr);
|
|
addr = 0xffffffff;
|
|
if (*inptr == 's') {
|
|
debugmem_list_segment(1, addr);
|
|
} else {
|
|
if (more_params(&inptr)) {
|
|
addr = readhex(&inptr, NULL);
|
|
}
|
|
debugmem_list_segment(0, addr);
|
|
}
|
|
} else if (*inptr == 'c') {
|
|
screenshot(-1, 1, 1);
|
|
} else if (*inptr == 'p') {
|
|
inptr++;
|
|
debug_sprite (&inptr);
|
|
} else if (*inptr == 'm') {
|
|
if (*(inptr + 1) == 'c') {
|
|
next_char (&inptr);
|
|
next_char (&inptr);
|
|
if (!smc_table)
|
|
smc_detect_init (&inptr);
|
|
else
|
|
smc_free ();
|
|
}
|
|
} else {
|
|
searchmem (&inptr);
|
|
}
|
|
break;
|
|
case 'a':
|
|
asmaddr = nxdis;
|
|
if (more_params(&inptr)) {
|
|
asmaddr = readhex(&inptr, &err);
|
|
if (err) {
|
|
break;
|
|
}
|
|
if (more_params(&inptr)) {
|
|
uae_u16 asmout[16];
|
|
int inss = m68k_asm(inptr, asmout, asmaddr);
|
|
if (inss > 0) {
|
|
for (int i = 0; i < inss; i++) {
|
|
put_word(asmaddr + i * 2, asmout[i]);
|
|
}
|
|
m68k_disasm(asmaddr, &nxdis, 1, 0xffffffff);
|
|
asmaddr = nxdis;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
asmmode = true;
|
|
console_out_f(_T("%08X "), asmaddr);
|
|
break;
|
|
case 'd':
|
|
{
|
|
if (*inptr == 'i') {
|
|
next_char (&inptr);
|
|
disk_debug (&inptr);
|
|
} else if (*inptr == 'j') {
|
|
inptr++;
|
|
inputdevice_logging = 1 | 2;
|
|
if (more_params (&inptr))
|
|
inputdevice_logging = readint(&inptr, NULL);
|
|
console_out_f (_T("Input logging level %d\n"), inputdevice_logging);
|
|
} else if (*inptr == 'm') {
|
|
memory_map_dump_2 (0);
|
|
} else if (*inptr == 't') {
|
|
next_char (&inptr);
|
|
debugtest_set (&inptr);
|
|
#ifdef _WIN32
|
|
} else if (*inptr == 'g') {
|
|
extern void update_disassembly (uae_u32);
|
|
next_char (&inptr);
|
|
if (more_params (&inptr))
|
|
update_disassembly(readhex (&inptr, NULL));
|
|
#endif
|
|
} else {
|
|
uae_u32 daddr;
|
|
int count;
|
|
if (*inptr == 'p' && inptr[1] == 'p' && inptr[2] == 'c') {
|
|
ppcmode = true;
|
|
next_char(&inptr);
|
|
} else if(*inptr == 'o') {
|
|
ppcmode = false;
|
|
next_char(&inptr);
|
|
}
|
|
if (more_params (&inptr))
|
|
daddr = readhex(&inptr, NULL);
|
|
else
|
|
daddr = nxdis;
|
|
if (more_params (&inptr))
|
|
count = readhex(&inptr, NULL);
|
|
else
|
|
count = 10;
|
|
if (ppcmode) {
|
|
ppc_disasm(daddr, &nxdis, count);
|
|
} else {
|
|
m68k_disasm (daddr, &nxdis, 0xffffffff, count);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'T':
|
|
if (inptr[0] == 'L')
|
|
debugger_scan_libraries();
|
|
else if (inptr[0] == 't' || inptr[0] == 0)
|
|
show_exec_tasks ();
|
|
else
|
|
show_exec_lists (&inptr[0]);
|
|
break;
|
|
case 't':
|
|
no_trace_exceptions = 0;
|
|
debug_cycles(2);
|
|
trace_param[0] = trace_param[1] = 0;
|
|
if (*inptr == 't') {
|
|
no_trace_exceptions = 1;
|
|
inptr++;
|
|
}
|
|
if (*inptr == 'r') {
|
|
// break when PC in debugmem
|
|
if (debugmem_get_range(&trace_param[0], &trace_param[1])) {
|
|
trace_mode = TRACE_RANGE_PC;
|
|
return true;
|
|
}
|
|
} else if (*inptr == 's') {
|
|
if (*(inptr + 1) == 'e') {
|
|
debugmem_enable_stackframe(true);
|
|
} else if (*(inptr + 1) == 'd') {
|
|
debugmem_enable_stackframe(false);
|
|
} else if (*(inptr + 1) == 'p') {
|
|
if (debugmem_break_stack_pop()) {
|
|
debugger_active = 0;
|
|
return true;
|
|
}
|
|
} else {
|
|
if (debugmem_break_stack_pop()) {
|
|
debugger_active = 0;
|
|
return true;
|
|
}
|
|
}
|
|
} else if (*inptr == 'l') {
|
|
// skip next source line
|
|
if (debugmem_isactive()) {
|
|
trace_mode = TRACE_SKIP_LINE;
|
|
trace_param[0] = 1;
|
|
trace_param[1] = debugmem_get_sourceline(M68K_GETPC, NULL, 0);
|
|
return true;
|
|
}
|
|
} else if (*inptr == 'x') {
|
|
trace_mode = TRACE_SKIP_INS;
|
|
trace_param[0] = 0xffffffff;
|
|
exception_debugging = 1;
|
|
return true;
|
|
} else {
|
|
if (more_params(&inptr))
|
|
trace_param[0] = readint(&inptr, NULL);
|
|
if (trace_param[0] <= 0 || trace_param[0] > 10000)
|
|
trace_param[0] = 1;
|
|
trace_mode = TRACE_SKIP_INS;
|
|
exception_debugging = 1;
|
|
return true;
|
|
}
|
|
break;
|
|
case 'z':
|
|
trace_mode = TRACE_MATCH_PC;
|
|
trace_param[0] = nextpc;
|
|
exception_debugging = 1;
|
|
debug_cycles(2);
|
|
return true;
|
|
|
|
case 'f':
|
|
if (inptr[0] == 'a') {
|
|
next_char (&inptr);
|
|
find_ea (&inptr);
|
|
} else if (inptr[0] == 'p') {
|
|
inptr++;
|
|
if (process_breakpoint (&inptr))
|
|
return true;
|
|
} else if (inptr[0] == 'c' || inptr[0] == 's') {
|
|
if (cycle_breakpoint(&inptr)) {
|
|
return true;
|
|
}
|
|
} else if (inptr[0] == 'e' && inptr[1] == 'n') {
|
|
break_if_enforcer = break_if_enforcer ? false : true;
|
|
console_out_f(_T("Break when enforcer hit: %s\n"), break_if_enforcer ? _T("enabled") : _T("disabled"));
|
|
} else {
|
|
if (instruction_breakpoint(&inptr)) {
|
|
debug_cycles(1);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'q':
|
|
uae_quit();
|
|
deactivate_debugger();
|
|
return true;
|
|
|
|
case 'g':
|
|
if (more_params (&inptr)) {
|
|
uaecptr addr = readhex(&inptr, &err);
|
|
if (err) {
|
|
break;
|
|
}
|
|
m68k_setpc(addr);
|
|
fill_prefetch();
|
|
}
|
|
deactivate_debugger();
|
|
return true;
|
|
|
|
case 'x':
|
|
if (_totupper(inptr[0]) == 'X') {
|
|
debugger_change(-1);
|
|
} else {
|
|
deactivate_debugger();
|
|
close_console();
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case 'H':
|
|
{
|
|
int count, temp, badly, skip;
|
|
uae_u32 addr = 0;
|
|
uae_u32 oldpc = m68k_getpc ();
|
|
int lastframes, lastvpos, lasthpos;
|
|
struct regstruct save_regs = regs;
|
|
|
|
badly = 0;
|
|
if (inptr[0] == 'H') {
|
|
badly = 1;
|
|
inptr++;
|
|
}
|
|
|
|
if (more_params(&inptr))
|
|
count = readint(&inptr, NULL);
|
|
else
|
|
count = 10;
|
|
if (count > 1000) {
|
|
addr = count;
|
|
count = MAX_HIST;
|
|
}
|
|
if (count < 0)
|
|
break;
|
|
skip = count;
|
|
if (more_params (&inptr))
|
|
skip = count - readint(&inptr, NULL);
|
|
|
|
temp = lasthist;
|
|
while (count-- > 0 && temp != firsthist) {
|
|
if (temp == 0)
|
|
temp = MAX_HIST - 1;
|
|
else
|
|
temp--;
|
|
}
|
|
lastframes = lastvpos = lasthpos = -1;
|
|
while (temp != lasthist) {
|
|
regs = history[temp].regs;
|
|
if (regs.pc == addr || addr == 0) {
|
|
m68k_setpc (regs.pc);
|
|
if (badly) {
|
|
m68k_dumpstate(NULL, 0xffffffff);
|
|
} else {
|
|
if (lastvpos >= 0) {
|
|
dma_disasm(lastframes, lastvpos, lasthpos, history[temp].fp, history[temp].vpos, history[temp].hpos);
|
|
}
|
|
lastframes = history[temp].fp;
|
|
lastvpos = history[temp].vpos;
|
|
lasthpos = history[temp].hpos;
|
|
console_out_f(_T("%2d %03d/%03d "), regs.intmask ? regs.intmask : (regs.s ? -1 : 0), lasthpos, lastvpos);
|
|
m68k_disasm (regs.pc, NULL, 0xffffffff, 1);
|
|
}
|
|
if (addr && regs.pc == addr)
|
|
break;
|
|
}
|
|
if (skip-- < 0)
|
|
break;
|
|
if (++temp == MAX_HIST)
|
|
temp = 0;
|
|
}
|
|
regs = save_regs;
|
|
m68k_setpc (oldpc);
|
|
}
|
|
break;
|
|
case 'M':
|
|
if (more_params (&inptr)) {
|
|
switch (next_char (&inptr))
|
|
{
|
|
case 'a':
|
|
if (more_params (&inptr))
|
|
audio_channel_mask = readhex(&inptr, NULL);
|
|
console_out_f (_T("Audio mask = %02X\n"), audio_channel_mask);
|
|
break;
|
|
case 's':
|
|
if (more_params (&inptr))
|
|
debug_sprite_mask = readhex(&inptr, NULL);
|
|
console_out_f (_T("Sprite mask: %02X\n"), debug_sprite_mask);
|
|
break;
|
|
case 'b':
|
|
if (more_params(&inptr)) {
|
|
debug_bpl_mask = readhex(&inptr, NULL) & 0xff;
|
|
if (more_params(&inptr))
|
|
debug_bpl_mask_one = readhex(&inptr, NULL) & 0xff;
|
|
notice_screen_contents_lost(0);
|
|
}
|
|
console_out_f (_T("Bitplane mask: %02X (%02X)\n"), debug_bpl_mask, debug_bpl_mask_one);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'm':
|
|
{
|
|
uae_u32 maddr;
|
|
int lines;
|
|
#ifdef _WIN32
|
|
if (*inptr == 'g') {
|
|
extern void update_memdump (uae_u32);
|
|
next_char (&inptr);
|
|
if (more_params(&inptr)) {
|
|
uaecptr addr = readhex(&inptr, &err);
|
|
if (!err) {
|
|
update_memdump(addr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
if (*inptr == 'm' && inptr[1] == 'u') {
|
|
inptr += 2;
|
|
if (inptr[0] == 'd') {
|
|
if (currprefs.mmu_model >= 68040)
|
|
mmu_dump_tables();
|
|
} else {
|
|
if (currprefs.mmu_model) {
|
|
if (more_params (&inptr))
|
|
debug_mmu_mode = readint(&inptr, NULL);
|
|
else
|
|
debug_mmu_mode = 0;
|
|
console_out_f (_T("MMU translation function code = %d\n"), debug_mmu_mode);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
err = false;
|
|
if (more_params (&inptr)) {
|
|
maddr = readhex(&inptr, &err);
|
|
} else {
|
|
maddr = nxmem;
|
|
}
|
|
if (more_params (&inptr))
|
|
lines = readhex(&inptr, &err);
|
|
else
|
|
lines = 20;
|
|
if (!err) {
|
|
dumpmem(maddr, &nxmem, lines);
|
|
}
|
|
}
|
|
break;
|
|
case 'v':
|
|
case 'V':
|
|
{
|
|
static int v1 = 0, v2 = 0, v3 = 0;
|
|
if (*inptr == 'h') {
|
|
inptr++;
|
|
if (more_params(&inptr) && *inptr == '?') {
|
|
mw_help();
|
|
} else if (!heatmap) {
|
|
debug_heatmap = 1;
|
|
init_heatmap();
|
|
if (more_params(&inptr)) {
|
|
v1 = readint(&inptr, NULL);
|
|
if (v1 < 0) {
|
|
debug_heatmap = 2;
|
|
}
|
|
}
|
|
TCHAR buf[200];
|
|
TCHAR *pbuf;
|
|
_stprintf(buf, _T("0 dff000 200 NONE"));
|
|
pbuf = buf;
|
|
memwatch(&pbuf);
|
|
_stprintf(buf, _T("1 0 %08x NONE"), currprefs.chipmem.size);
|
|
pbuf = buf;
|
|
memwatch(&pbuf);
|
|
if (currprefs.bogomem.size) {
|
|
_stprintf(buf, _T("2 c00000 %08x NONE"), currprefs.bogomem.size);
|
|
pbuf = buf;
|
|
memwatch(&pbuf);
|
|
}
|
|
console_out_f(_T("Heatmap enabled\n"));
|
|
} else {
|
|
if (*inptr == 'd') {
|
|
console_out_f(_T("Heatmap disabled\n"));
|
|
free_heatmap();
|
|
} else {
|
|
heatmap_stats(&inptr);
|
|
}
|
|
}
|
|
} else if (*inptr == 'o') {
|
|
if (debug_dma) {
|
|
console_out_f (_T("DMA debugger disabled\n"), debug_dma);
|
|
record_dma_reset(0);
|
|
reset_drawing();
|
|
debug_dma = 0;
|
|
}
|
|
} else if (*inptr == 'm') {
|
|
set_debug_colors();
|
|
inptr++;
|
|
if (more_params(&inptr)) {
|
|
v1 = readint(&inptr, &err);
|
|
if (!err && v1 >= 0 && v1 < DMARECORD_MAX) {
|
|
v2 = readint(&inptr, &err);
|
|
if (!err && v2 >= 0 && v2 <= DMARECORD_SUBITEMS) {
|
|
if (more_params(&inptr)) {
|
|
uae_u32 rgb = readhex(&inptr, NULL);
|
|
if (v2 == 0) {
|
|
for (int i = 0; i < DMARECORD_SUBITEMS; i++) {
|
|
debug_colors[v1].l[i] = rgb;
|
|
}
|
|
} else {
|
|
v2--;
|
|
debug_colors[v1].l[v2] = rgb;
|
|
}
|
|
debug_colors[v1].enabled = true;
|
|
} else {
|
|
debug_colors[v1].enabled = !debug_colors[v1].enabled;
|
|
}
|
|
console_out_f(_T("%d,%d: %08x %s %s\n"), v1, v2, debug_colors[v1].l[v2], debug_colors[v1].enabled ? _T("*") : _T(" "), debug_colors[v1].name);
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = 0; i < DMARECORD_MAX; i++) {
|
|
for (int j = 0; j < DMARECORD_SUBITEMS; j++) {
|
|
if (j < debug_colors[i].max) {
|
|
console_out_f(_T("%d,%d: %08x %s %s\n"), i, j, debug_colors[i].l[j], debug_colors[i].enabled ? _T("*") : _T(" "), debug_colors[i].name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (more_params(&inptr) && *inptr == '?') {
|
|
mw_help();
|
|
} else {
|
|
free_heatmap();
|
|
int nextcmd = peekchar(&inptr);
|
|
if (nextcmd == 'l') {
|
|
next_char(&inptr);
|
|
}
|
|
if (more_params(&inptr))
|
|
v1 = readint(&inptr, NULL);
|
|
if (more_params(&inptr))
|
|
v2 = readint(&inptr, NULL);
|
|
if (more_params(&inptr))
|
|
v3 = readint(&inptr, NULL);
|
|
if (debug_dma && v1 >= 0 && v2 >= 0) {
|
|
decode_dma_record(v2, v1, v3, cmd == 'v', nextcmd == 'l');
|
|
} else {
|
|
if (debug_dma) {
|
|
record_dma_reset(0);
|
|
reset_drawing();
|
|
}
|
|
debug_dma = v1 < 0 ? -v1 : 1;
|
|
console_out_f(_T("DMA debugger enabled, mode=%d.\n"), debug_dma);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'o':
|
|
{
|
|
if (copper_debugger (&inptr)) {
|
|
debugger_active = 0;
|
|
debugging = 0;
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case 'O':
|
|
break;
|
|
case 'b':
|
|
if (staterecorder (&inptr))
|
|
return true;
|
|
break;
|
|
case 'u':
|
|
{
|
|
if (more_params(&inptr)) {
|
|
if (*inptr == 'a') {
|
|
debugmem_inhibit_break(1);
|
|
console_out(_T("All break to debugger methods inhibited.\n"));
|
|
} else if (*inptr == 'c') {
|
|
debugmem_inhibit_break(-1);
|
|
console_out(_T("All break to debugger methods allowed.\n"));
|
|
}
|
|
} else {
|
|
if (debugmem_inhibit_break(0)) {
|
|
console_out(_T("Current break to debugger method inhibited.\n"));
|
|
} else {
|
|
console_out(_T("Current break to debugger method allowed.\n"));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'U':
|
|
if (currprefs.mmu_model && more_params (&inptr)) {
|
|
int i;
|
|
uaecptr addrl = readhex(&inptr, NULL);
|
|
uaecptr addrp;
|
|
console_out_f (_T("%08X translates to:\n"), addrl);
|
|
for (i = 0; i < 4; i++) {
|
|
bool super = (i & 2) != 0;
|
|
bool data = (i & 1) != 0;
|
|
console_out_f (_T("S%dD%d="), super, data);
|
|
TRY(prb) {
|
|
if (currprefs.mmu_model >= 68040)
|
|
addrp = mmu_translate (addrl, 0, super, data, false, sz_long);
|
|
else
|
|
addrp = mmu030_translate (addrl, super, data, false);
|
|
console_out_f (_T("%08X"), addrp);
|
|
TRY(prb2) {
|
|
if (currprefs.mmu_model >= 68040)
|
|
addrp = mmu_translate (addrl, 0, super, data, true, sz_long);
|
|
else
|
|
addrp = mmu030_translate (addrl, super, data, true);
|
|
console_out_f (_T(" RW"));
|
|
} CATCH(prb2) {
|
|
console_out_f (_T(" RO"));
|
|
} ENDTRY
|
|
} CATCH(prb) {
|
|
console_out_f (_T("***********"));
|
|
} ENDTRY
|
|
console_out_f (_T(" "));
|
|
}
|
|
console_out_f (_T("\n"));
|
|
}
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
if (more_params (&inptr))
|
|
converter (&inptr);
|
|
else
|
|
debug_help ();
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static TCHAR input[MAX_LINEWIDTH];
|
|
|
|
static void debug_1 (void)
|
|
{
|
|
custom_dumpstate(0);
|
|
m68k_dumpstate(&nextpc, debug_pc);
|
|
debug_pc = 0xffffffff;
|
|
nxdis = nextpc; nxmem = 0;
|
|
debugger_active = 1;
|
|
|
|
for (;;) {
|
|
int v;
|
|
|
|
if (!debugger_active)
|
|
return;
|
|
update_debug_info ();
|
|
console_out (_T(">"));
|
|
console_flush ();
|
|
debug_linecounter = 0;
|
|
v = console_get (input, MAX_LINEWIDTH);
|
|
if (v < 0)
|
|
return;
|
|
if (v == 0)
|
|
continue;
|
|
if (debug_line (input))
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void addhistory(void)
|
|
{
|
|
uae_u32 pc = currprefs.cpu_model >= 68020 && currprefs.cpu_compatible ? regs.instruction_pc : m68k_getpc();
|
|
int prevhist = lasthist == 0 ? MAX_HIST - 1 : lasthist - 1;
|
|
if (history[prevhist].regs.pc == pc) {
|
|
return;
|
|
}
|
|
history[lasthist].regs = regs;
|
|
history[lasthist].regs.pc = pc;
|
|
history[lasthist].vpos = vpos;
|
|
history[lasthist].hpos = current_hpos();
|
|
history[lasthist].fp = vsync_counter;
|
|
|
|
if (++lasthist == MAX_HIST) {
|
|
lasthist = 0;
|
|
}
|
|
if (lasthist == firsthist) {
|
|
if (++firsthist == MAX_HIST) {
|
|
firsthist = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void debug_exception(int nr)
|
|
{
|
|
if (debug_illegal) {
|
|
if (nr <= 63 && (debug_illegal_mask & ((uae_u64)1 << nr))) {
|
|
write_log(_T("Exception %d breakpoint\n"), nr);
|
|
activate_debugger();
|
|
}
|
|
}
|
|
if (trace_param[0] == 0xffffffff && trace_mode == TRACE_SKIP_INS) {
|
|
activate_debugger();
|
|
}
|
|
}
|
|
|
|
static bool check_breakpoint(struct breakpoint_node *bpn, uaecptr pc)
|
|
{
|
|
int bpnum = -1;
|
|
|
|
if (!bpn->enabled) {
|
|
return false;
|
|
}
|
|
if (bpn->type == BREAKPOINT_REG_PC) {
|
|
if (bpn->value1 == pc) {
|
|
return true;
|
|
}
|
|
} else if (bpn->type >= 0 && bpn->type < BREAKPOINT_REG_END) {
|
|
uae_u32 value1 = bpn->value1 & bpn->mask;
|
|
uae_u32 value2 = bpn->value2 & bpn->mask;
|
|
uae_u32 cval = returnregx(bpn->type) & bpn->mask;
|
|
int opersize = bpn->mask == 0xff ? 1 : (bpn->mask == 0xffff) ? 2 : 4;
|
|
bool opersigned = bpn->opersigned;
|
|
uae_s32 value1s = (uae_s32)value1;
|
|
uae_s32 value2s = (uae_s32)value2;
|
|
uae_s32 cvals = (uae_s32)cval;
|
|
if (opersize == 2) {
|
|
cvals = (uae_s32)(uae_s16)cvals;
|
|
value1s = (uae_s32)(uae_s16)value1s;
|
|
value2s = (uae_s32)(uae_s16)value2s;
|
|
} else if (opersize == 1) {
|
|
cvals = (uae_s32)(uae_s8)cvals;
|
|
value1s = (uae_s32)(uae_s8)value1s;
|
|
value2s = (uae_s32)(uae_s8)value2s;
|
|
}
|
|
switch (bpn->oper)
|
|
{
|
|
case BREAKPOINT_CMP_EQUAL:
|
|
if (cval == value1)
|
|
return true;
|
|
break;
|
|
case BREAKPOINT_CMP_NEQUAL:
|
|
if (cval != value1)
|
|
return true;
|
|
break;
|
|
case BREAKPOINT_CMP_SMALLER:
|
|
if (opersigned) {
|
|
if (cvals <= value1s)
|
|
return true;
|
|
} else {
|
|
if (cval <= value1)
|
|
return true;
|
|
}
|
|
break;
|
|
case BREAKPOINT_CMP_LARGER:
|
|
if (opersigned) {
|
|
if (cvals >= value1s)
|
|
return true;
|
|
} else {
|
|
if (cval >= value1)
|
|
return true;
|
|
}
|
|
break;
|
|
case BREAKPOINT_CMP_RANGE:
|
|
if (opersigned) {
|
|
if (cvals >= value1s && cvals <= value2s)
|
|
return true;
|
|
} else {
|
|
if (cval >= value1 && cval <= value2)
|
|
return true;
|
|
}
|
|
break;
|
|
case BREAKPOINT_CMP_NRANGE:
|
|
if (opersigned) {
|
|
if (cvals <= value1s || cvals >= value2s)
|
|
return true;
|
|
} else {
|
|
if (cval <= value1 || cval >= value2)
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool check_breakpoint_count(struct breakpoint_node *bpn, uaecptr pc)
|
|
{
|
|
if (bpn->cnt <= 0) {
|
|
return true;
|
|
}
|
|
console_out_f(_T("Breakpoint %d hit: PC=%08x, count=%d.\n"), bpn - bpnodes, pc, bpn->cnt);
|
|
bpn->cnt--;
|
|
return false;
|
|
}
|
|
|
|
void debug (void)
|
|
{
|
|
int wasactive;
|
|
|
|
if (savestate_state)
|
|
return;
|
|
|
|
bogusframe = 1;
|
|
disasm_init();
|
|
addhistory ();
|
|
|
|
#if 0
|
|
if (do_skip && skipaddr_start == 0xC0DEDBAD) {
|
|
if (trace_same_insn_count > 0) {
|
|
if (memcmp (trace_insn_copy, regs.pc_p, 10) == 0
|
|
&& memcmp (trace_prev_regs.regs, regs.regs, sizeof regs.regs) == 0)
|
|
{
|
|
trace_same_insn_count++;
|
|
return;
|
|
}
|
|
}
|
|
if (trace_same_insn_count > 1)
|
|
fprintf (logfile, "[ repeated %d times ]\n", trace_same_insn_count);
|
|
m68k_dumpstate (logfile, &nextpc);
|
|
trace_same_insn_count = 1;
|
|
memcpy (trace_insn_copy, regs.pc_p, 10);
|
|
memcpy (&trace_prev_regs, ®s, sizeof regs);
|
|
}
|
|
#endif
|
|
|
|
if (!memwatch_triggered) {
|
|
if (trace_mode) {
|
|
uae_u32 pc;
|
|
uae_u16 opcode;
|
|
int bpnum = -1;
|
|
int bp = 0;
|
|
|
|
pc = munge24 (m68k_getpc ());
|
|
opcode = currprefs.cpu_model < 68020 && (currprefs.cpu_compatible || currprefs.cpu_cycle_exact) ? regs.ir : get_word_debug (pc);
|
|
|
|
for (int i = 0; i < BREAKPOINT_TOTAL; i++) {
|
|
struct breakpoint_node *bpn = &bpnodes[i];
|
|
if (check_breakpoint(bpn, pc)) {
|
|
int j;
|
|
// if this breakpoint is chained, ignore it
|
|
for (j = 0; j < BREAKPOINT_TOTAL; j++) {
|
|
struct breakpoint_node *bpn2 = &bpnodes[j];
|
|
if (bpn2->enabled && bpn2->chain == i) {
|
|
break;
|
|
}
|
|
}
|
|
if (j >= BREAKPOINT_TOTAL) {
|
|
if (!check_breakpoint_count(bpn, pc)) {
|
|
break;
|
|
}
|
|
int max = BREAKPOINT_TOTAL;
|
|
bpnum = i;
|
|
// check breakpoint chain
|
|
while (bpnum >= 0 && bpnodes[bpnum].chain >= 0 && bpnodes[bpnum].chain != bpnum && max > 0) {
|
|
bpnum = bpnodes[bpnum].chain;
|
|
struct breakpoint_node *bpn = &bpnodes[bpnum];
|
|
if (!check_breakpoint(bpn, pc)) {
|
|
bpnum = -1;
|
|
break;
|
|
}
|
|
if (!check_breakpoint_count(bpn, pc)) {
|
|
bpnum = -1;
|
|
break;
|
|
}
|
|
max--;
|
|
}
|
|
if (bpnum >= 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (trace_mode) {
|
|
if (trace_mode == TRACE_MATCH_PC && trace_param[0] == pc)
|
|
bp = -1;
|
|
if (trace_mode == TRACE_RAM_PC) {
|
|
addrbank *ab = &get_mem_bank(pc);
|
|
if (ab->flags & ABFLAG_RAM) {
|
|
uae_u16 ins = get_word_debug(pc);
|
|
// skip JMP xxxxxx (LVOs)
|
|
if (ins != 0x4ef9) {
|
|
bp = -1;
|
|
}
|
|
}
|
|
}
|
|
if ((processptr || processname) && !isrom(m68k_getpc())) {
|
|
uaecptr execbase = get_long_debug (4);
|
|
uaecptr activetask = get_long_debug (execbase + 276);
|
|
int process = get_byte_debug (activetask + 8) == 13 ? 1 : 0;
|
|
char *name = (char*)get_real_address_debug(get_long_debug (activetask + 10));
|
|
if (process) {
|
|
uaecptr cli = BPTR2APTR(get_long_debug (activetask + 172));
|
|
uaecptr seglist = 0;
|
|
|
|
uae_char *command = NULL;
|
|
if (cli) {
|
|
if (processname)
|
|
command = (char*)get_real_address_debug(BPTR2APTR(get_long_debug (cli + 16)));
|
|
seglist = BPTR2APTR(get_long_debug (cli + 60));
|
|
} else {
|
|
seglist = BPTR2APTR(get_long_debug (activetask + 128));
|
|
seglist = BPTR2APTR(get_long_debug (seglist + 12));
|
|
}
|
|
if (activetask == processptr || (processname && (!stricmp (name, processname) || (command && command[0] && !strnicmp (command + 1, processname, command[0]) && processname[command[0]] == 0)))) {
|
|
while (seglist) {
|
|
uae_u32 size = get_long_debug (seglist - 4) - 4;
|
|
if (pc >= (seglist + 4) && pc < (seglist + size)) {
|
|
bp = -1;
|
|
break;
|
|
}
|
|
seglist = BPTR2APTR(get_long_debug (seglist));
|
|
}
|
|
}
|
|
}
|
|
} else if (trace_mode == TRACE_MATCH_INS) {
|
|
if (trace_param[0] == 0x10000) {
|
|
if (opcode == 0x4e75 || opcode == 0x4e73 || opcode == 0x4e77)
|
|
bp = -1;
|
|
} else if (opcode == trace_param[0]) {
|
|
bp = -1;
|
|
for (int op = 1; op < 3; op++) {
|
|
if (trace_param[op] != 0x10000) {
|
|
uae_u16 w = 0xffff;
|
|
debug_get_prefetch(op, &w);
|
|
if (w != trace_param[op])
|
|
bp = 0;
|
|
}
|
|
}
|
|
}
|
|
} else if (trace_mode == TRACE_SKIP_INS) {
|
|
if (trace_param[0] != 0 && trace_param[0] != 0xffffffff)
|
|
trace_param[0]--;
|
|
if (trace_param[0] == 0) {
|
|
bp = -1;
|
|
}
|
|
#if 0
|
|
} else if (skipaddr_start == 0xffffffff && skipaddr_doskip > 0) {
|
|
bp = -1;
|
|
#endif
|
|
} else if (trace_mode == TRACE_RANGE_PC) {
|
|
if (pc >= trace_param[0] && pc < trace_param[1])
|
|
bp = -1;
|
|
} else if (trace_mode == TRACE_SKIP_LINE) {
|
|
if (trace_param[0] != 0)
|
|
trace_param[0]--;
|
|
if (trace_param[0] == 0) {
|
|
int line = debugmem_get_sourceline(pc, NULL, 0);
|
|
if (line > 0 && line != trace_param[1])
|
|
bp = -1;
|
|
}
|
|
}
|
|
}
|
|
if (!bp && bpnum < 0) {
|
|
debug_continue();
|
|
return;
|
|
}
|
|
if (bpnum >= 0) {
|
|
console_out_f(_T("Breakpoint %d triggered.\n"), bpnum);
|
|
}
|
|
debug_cycles(1);
|
|
}
|
|
} else {
|
|
memwatch_hit_msg(memwatch_triggered - 1);
|
|
memwatch_triggered = 0;
|
|
}
|
|
|
|
wasactive = ismouseactive ();
|
|
#ifdef WITH_PPC
|
|
uae_ppc_pause(1);
|
|
#endif
|
|
inputdevice_unacquire ();
|
|
pause_sound ();
|
|
setmouseactive(0, 0);
|
|
activate_console ();
|
|
trace_mode = 0;
|
|
exception_debugging = 0;
|
|
debug_rewind = 0;
|
|
processptr = 0;
|
|
#if 0
|
|
if (!currprefs.statecapture) {
|
|
changed_prefs.statecapture = currprefs.statecapture = 1;
|
|
savestate_init ();
|
|
}
|
|
#endif
|
|
debugmem_disable();
|
|
|
|
if (trace_cycles && last_frame >= 0) {
|
|
if (last_frame + 2 >= vsync_counter || trace_cycles > 1) {
|
|
evt_t c = last_cycles2 - last_cycles1;
|
|
uae_u32 cc;
|
|
if (c >= 0x7fffffff) {
|
|
cc = 0x7fffffff;
|
|
} else {
|
|
cc = (uae_u32)c;
|
|
}
|
|
console_out_f(_T("Cycles: %d Chip, %d CPU. (V=%d H=%d -> V=%d H=%d)\n"),
|
|
cc / CYCLE_UNIT,
|
|
cc / cpucycleunit,
|
|
last_vpos1, last_hpos1,
|
|
last_vpos2, last_hpos2);
|
|
}
|
|
}
|
|
trace_cycles = 0;
|
|
|
|
debug_1 ();
|
|
debugmem_enable();
|
|
if (!debug_rewind && !currprefs.cachesize
|
|
#ifdef FILESYS
|
|
&& nr_units () == 0
|
|
#endif
|
|
) {
|
|
savestate_capture (1);
|
|
}
|
|
if (!trace_mode) {
|
|
for (int i = 0; i < BREAKPOINT_TOTAL; i++) {
|
|
if (bpnodes[i].enabled)
|
|
trace_mode = TRACE_CHECKONLY;
|
|
}
|
|
}
|
|
if (trace_mode) {
|
|
set_special (SPCFLAG_BRK);
|
|
debugging = -1;
|
|
}
|
|
resume_sound ();
|
|
inputdevice_acquire (TRUE);
|
|
#ifdef WITH_PPC
|
|
uae_ppc_pause(0);
|
|
#endif
|
|
setmouseactive(0, wasactive ? 2 : 0);
|
|
|
|
last_cycles1 = get_cycles();
|
|
last_vpos1 = vpos;
|
|
last_hpos1 = current_hpos();
|
|
last_frame = timeframes;
|
|
}
|
|
|
|
const TCHAR *debuginfo (int mode)
|
|
{
|
|
static TCHAR txt[100];
|
|
uae_u32 pc = M68K_GETPC;
|
|
_stprintf (txt, _T("PC=%08X INS=%04X %04X %04X"),
|
|
pc, get_word_debug (pc), get_word_debug (pc + 2), get_word_debug (pc + 4));
|
|
return txt;
|
|
}
|
|
|
|
void mmu_disasm (uaecptr pc, int lines)
|
|
{
|
|
debug_mmu_mode = regs.s ? 6 : 2;
|
|
m68k_dumpstate(NULL, 0xffffffff);
|
|
m68k_disasm (pc, NULL, 0xffffffff, lines);
|
|
}
|
|
|
|
static int mmu_logging;
|
|
|
|
#define MMU_PAGE_SHIFT 16
|
|
|
|
struct mmudata {
|
|
uae_u32 flags;
|
|
uae_u32 addr;
|
|
uae_u32 len;
|
|
uae_u32 remap;
|
|
uae_u32 p_addr;
|
|
};
|
|
|
|
static struct mmudata *mmubanks;
|
|
static uae_u32 mmu_struct, mmu_callback, mmu_regs;
|
|
static uae_u32 mmu_fault_bank_addr, mmu_fault_addr;
|
|
static int mmu_fault_size, mmu_fault_rw;
|
|
static int mmu_slots;
|
|
static struct regstruct mmur;
|
|
|
|
struct mmunode {
|
|
struct mmudata *mmubank;
|
|
struct mmunode *next;
|
|
};
|
|
static struct mmunode **mmunl;
|
|
extern regstruct mmu_backup_regs;
|
|
|
|
#define MMU_READ_U (1 << 0)
|
|
#define MMU_WRITE_U (1 << 1)
|
|
#define MMU_READ_S (1 << 2)
|
|
#define MMU_WRITE_S (1 << 3)
|
|
#define MMU_READI_U (1 << 4)
|
|
#define MMU_READI_S (1 << 5)
|
|
|
|
#define MMU_MAP_READ_U (1 << 8)
|
|
#define MMU_MAP_WRITE_U (1 << 9)
|
|
#define MMU_MAP_READ_S (1 << 10)
|
|
#define MMU_MAP_WRITE_S (1 << 11)
|
|
#define MMU_MAP_READI_U (1 << 12)
|
|
#define MMU_MAP_READI_S (1 << 13)
|
|
|
|
void mmu_do_hit (void)
|
|
{
|
|
int i;
|
|
uaecptr p;
|
|
uae_u32 pc;
|
|
|
|
mmu_triggered = 0;
|
|
pc = m68k_getpc ();
|
|
p = mmu_regs + 18 * 4;
|
|
put_long (p, pc);
|
|
regs = mmu_backup_regs;
|
|
regs.intmask = 7;
|
|
regs.t0 = regs.t1 = 0;
|
|
if (!regs.s) {
|
|
regs.usp = m68k_areg (regs, 7);
|
|
if (currprefs.cpu_model >= 68020)
|
|
m68k_areg (regs, 7) = regs.m ? regs.msp : regs.isp;
|
|
else
|
|
m68k_areg (regs, 7) = regs.isp;
|
|
regs.s = 1;
|
|
}
|
|
MakeSR ();
|
|
m68k_setpc (mmu_callback);
|
|
fill_prefetch ();
|
|
|
|
if (currprefs.cpu_model > 68000) {
|
|
for (i = 0 ; i < 9; i++) {
|
|
m68k_areg (regs, 7) -= 4;
|
|
put_long (m68k_areg (regs, 7), 0);
|
|
}
|
|
m68k_areg (regs, 7) -= 4;
|
|
put_long (m68k_areg (regs, 7), mmu_fault_addr);
|
|
m68k_areg (regs, 7) -= 2;
|
|
put_word (m68k_areg (regs, 7), 0); /* WB1S */
|
|
m68k_areg (regs, 7) -= 2;
|
|
put_word (m68k_areg (regs, 7), 0); /* WB2S */
|
|
m68k_areg (regs, 7) -= 2;
|
|
put_word (m68k_areg (regs, 7), 0); /* WB3S */
|
|
m68k_areg (regs, 7) -= 2;
|
|
put_word (m68k_areg (regs, 7),
|
|
(mmu_fault_rw ? 0 : 0x100) | (mmu_fault_size << 5)); /* SSW */
|
|
m68k_areg (regs, 7) -= 4;
|
|
put_long (m68k_areg (regs, 7), mmu_fault_bank_addr);
|
|
m68k_areg (regs, 7) -= 2;
|
|
put_word (m68k_areg (regs, 7), 0x7002);
|
|
}
|
|
m68k_areg (regs, 7) -= 4;
|
|
put_long (m68k_areg (regs, 7), get_long_debug (p - 4));
|
|
m68k_areg (regs, 7) -= 2;
|
|
put_word (m68k_areg (regs, 7), mmur.sr);
|
|
#ifdef JIT
|
|
if (currprefs.cachesize) {
|
|
set_special(SPCFLAG_END_COMPILE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void mmu_do_hit_pre (struct mmudata *md, uaecptr addr, int size, int rwi, uae_u32 v)
|
|
{
|
|
uae_u32 p, pc;
|
|
int i;
|
|
|
|
mmur = regs;
|
|
pc = m68k_getpc ();
|
|
if (mmu_logging)
|
|
console_out_f (_T("MMU: hit %08X SZ=%d RW=%d V=%08X PC=%08X\n"), addr, size, rwi, v, pc);
|
|
|
|
p = mmu_regs;
|
|
put_long (p, 0); p += 4;
|
|
for (i = 0; i < 16; i++) {
|
|
put_long (p, regs.regs[i]);
|
|
p += 4;
|
|
}
|
|
put_long (p, pc); p += 4;
|
|
put_long (p, 0); p += 4;
|
|
put_long (p, regs.usp); p += 4;
|
|
put_long (p, regs.isp); p += 4;
|
|
put_long (p, regs.msp); p += 4;
|
|
put_word (p, regs.sr); p += 2;
|
|
put_word (p, (size << 1) | ((rwi & 2) ? 1 : 0)); /* size and rw */ p += 2;
|
|
put_long (p, addr); /* fault address */ p += 4;
|
|
put_long (p, md->p_addr); /* bank address */ p += 4;
|
|
put_long (p, v); p += 4;
|
|
mmu_fault_addr = addr;
|
|
mmu_fault_bank_addr = md->p_addr;
|
|
mmu_fault_size = size;
|
|
mmu_fault_rw = rwi;
|
|
mmu_triggered = 1;
|
|
}
|
|
|
|
static int mmu_hit (uaecptr addr, int size, int rwi, uae_u32 *v)
|
|
{
|
|
int s, trig;
|
|
uae_u32 flags;
|
|
struct mmudata *md;
|
|
struct mmunode *mn;
|
|
|
|
if (mmu_triggered)
|
|
return 1;
|
|
|
|
mn = mmunl[addr >> MMU_PAGE_SHIFT];
|
|
if (mn == NULL)
|
|
return 0;
|
|
|
|
s = regs.s;
|
|
while (mn) {
|
|
md = mn->mmubank;
|
|
if (addr >= md->addr && addr < md->addr + md->len) {
|
|
flags = md->flags;
|
|
if (flags & (MMU_MAP_READ_U | MMU_MAP_WRITE_U | MMU_MAP_READ_S | MMU_MAP_WRITE_S | MMU_MAP_READI_U | MMU_MAP_READI_S)) {
|
|
trig = 0;
|
|
if (!s && (flags & MMU_MAP_READ_U) && (rwi & 1))
|
|
trig = 1;
|
|
if (!s && (flags & MMU_MAP_WRITE_U) && (rwi & 2))
|
|
trig = 1;
|
|
if (s && (flags & MMU_MAP_READ_S) && (rwi & 1))
|
|
trig = 1;
|
|
if (s && (flags & MMU_MAP_WRITE_S) && (rwi & 2))
|
|
trig = 1;
|
|
if (!s && (flags & MMU_MAP_READI_U) && (rwi & 4))
|
|
trig = 1;
|
|
if (s && (flags & MMU_MAP_READI_S) && (rwi & 4))
|
|
trig = 1;
|
|
if (trig) {
|
|
uaecptr maddr = md->remap + (addr - md->addr);
|
|
if (maddr == addr) /* infinite mmu hit loop? no thanks.. */
|
|
return 1;
|
|
if (mmu_logging)
|
|
console_out_f (_T("MMU: remap %08X -> %08X SZ=%d RW=%d\n"), addr, maddr, size, rwi);
|
|
if ((rwi & 2)) {
|
|
switch (size)
|
|
{
|
|
case 4:
|
|
put_long (maddr, *v);
|
|
break;
|
|
case 2:
|
|
put_word (maddr, *v);
|
|
break;
|
|
case 1:
|
|
put_byte (maddr, *v);
|
|
break;
|
|
}
|
|
} else {
|
|
switch (size)
|
|
{
|
|
case 4:
|
|
*v = get_long_debug (maddr);
|
|
break;
|
|
case 2:
|
|
*v = get_word_debug (maddr);
|
|
break;
|
|
case 1:
|
|
*v = get_byte_debug (maddr);
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
if (flags & (MMU_READ_U | MMU_WRITE_U | MMU_READ_S | MMU_WRITE_S | MMU_READI_U | MMU_READI_S)) {
|
|
trig = 0;
|
|
if (!s && (flags & MMU_READ_U) && (rwi & 1))
|
|
trig = 1;
|
|
if (!s && (flags & MMU_WRITE_U) && (rwi & 2))
|
|
trig = 1;
|
|
if (s && (flags & MMU_READ_S) && (rwi & 1))
|
|
trig = 1;
|
|
if (s && (flags & MMU_WRITE_S) && (rwi & 2))
|
|
trig = 1;
|
|
if (!s && (flags & MMU_READI_U) && (rwi & 4))
|
|
trig = 1;
|
|
if (s && (flags & MMU_READI_S) && (rwi & 4))
|
|
trig = 1;
|
|
if (trig) {
|
|
mmu_do_hit_pre (md, addr, size, rwi, *v);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
mn = mn->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef JIT
|
|
|
|
static void mmu_free_node(struct mmunode *mn)
|
|
{
|
|
if (!mn)
|
|
return;
|
|
mmu_free_node (mn->next);
|
|
xfree (mn);
|
|
}
|
|
|
|
static void mmu_free(void)
|
|
{
|
|
struct mmunode *mn;
|
|
int i;
|
|
|
|
for (i = 0; i < mmu_slots; i++) {
|
|
mn = mmunl[i];
|
|
mmu_free_node (mn);
|
|
}
|
|
xfree (mmunl);
|
|
mmunl = NULL;
|
|
xfree (mmubanks);
|
|
mmubanks = NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int getmmubank(struct mmudata *snptr, uaecptr p)
|
|
{
|
|
snptr->flags = get_long_debug (p);
|
|
if (snptr->flags == 0xffffffff)
|
|
return 1;
|
|
snptr->addr = get_long_debug (p + 4);
|
|
snptr->len = get_long_debug (p + 8);
|
|
snptr->remap = get_long_debug (p + 12);
|
|
snptr->p_addr = p;
|
|
return 0;
|
|
}
|
|
|
|
int mmu_init(int mode, uaecptr parm, uaecptr parm2)
|
|
{
|
|
uaecptr p, p2, banks;
|
|
int size;
|
|
struct mmudata *snptr;
|
|
struct mmunode *mn;
|
|
#ifdef JIT
|
|
static int wasjit;
|
|
if (currprefs.cachesize) {
|
|
wasjit = currprefs.cachesize;
|
|
changed_prefs.cachesize = 0;
|
|
console_out (_T("MMU: JIT disabled\n"));
|
|
check_prefs_changed_comp(false);
|
|
}
|
|
if (mode == 0) {
|
|
if (mmu_enabled) {
|
|
mmu_free ();
|
|
deinitialize_memwatch ();
|
|
console_out (_T("MMU: disabled\n"));
|
|
changed_prefs.cachesize = wasjit;
|
|
}
|
|
mmu_logging = 0;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
if (mode == 1) {
|
|
if (!mmu_enabled)
|
|
return 0xffffffff;
|
|
return mmu_struct;
|
|
}
|
|
|
|
p = parm;
|
|
mmu_struct = p;
|
|
if (get_long_debug (p) != 1) {
|
|
console_out_f (_T("MMU: version mismatch %d <> %d\n"), get_long_debug (p), 1);
|
|
return 0;
|
|
}
|
|
p += 4;
|
|
mmu_logging = get_long_debug (p) & 1;
|
|
p += 4;
|
|
mmu_callback = get_long_debug (p);
|
|
p += 4;
|
|
mmu_regs = get_long_debug (p);
|
|
p += 4;
|
|
|
|
if (mode == 3) {
|
|
int off;
|
|
uaecptr addr = get_long_debug (parm2 + 4);
|
|
if (!mmu_enabled)
|
|
return 0;
|
|
off = addr >> MMU_PAGE_SHIFT;
|
|
mn = mmunl[off];
|
|
while (mn) {
|
|
if (mn->mmubank->p_addr == parm2) {
|
|
getmmubank(mn->mmubank, parm2);
|
|
if (mmu_logging)
|
|
console_out_f (_T("MMU: bank update %08X: %08X - %08X %08X\n"),
|
|
mn->mmubank->flags, mn->mmubank->addr, mn->mmubank->len + mn->mmubank->addr,
|
|
mn->mmubank->remap);
|
|
}
|
|
mn = mn->next;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
mmu_slots = 1 << ((currprefs.address_space_24 ? 24 : 32) - MMU_PAGE_SHIFT);
|
|
mmunl = xcalloc (struct mmunode*, mmu_slots);
|
|
size = 1;
|
|
p2 = get_long_debug (p);
|
|
while (get_long_debug (p2) != 0xffffffff) {
|
|
p2 += 16;
|
|
size++;
|
|
}
|
|
p = banks = get_long_debug (p);
|
|
snptr = mmubanks = xmalloc (struct mmudata, size);
|
|
for (;;) {
|
|
int off;
|
|
if (getmmubank(snptr, p))
|
|
break;
|
|
p += 16;
|
|
off = snptr->addr >> MMU_PAGE_SHIFT;
|
|
if (mmunl[off] == NULL) {
|
|
mn = mmunl[off] = xcalloc (struct mmunode, 1);
|
|
} else {
|
|
mn = mmunl[off];
|
|
while (mn->next)
|
|
mn = mn->next;
|
|
mn = mn->next = xcalloc (struct mmunode, 1);
|
|
}
|
|
mn->mmubank = snptr;
|
|
snptr++;
|
|
}
|
|
|
|
initialize_memwatch (1);
|
|
console_out_f (_T("MMU: enabled, %d banks, CB=%08X S=%08X BNK=%08X SF=%08X, %d*%d\n"),
|
|
size - 1, mmu_callback, parm, banks, mmu_regs, mmu_slots, 1 << MMU_PAGE_SHIFT);
|
|
set_special (SPCFLAG_BRK);
|
|
return 1;
|
|
}
|
|
|
|
void debug_parser (const TCHAR *cmd, TCHAR *out, uae_u32 outsize)
|
|
{
|
|
TCHAR empty[2] = { 0 };
|
|
TCHAR *input = my_strdup (cmd);
|
|
if (out == NULL && outsize == 0) {
|
|
setconsolemode (empty, 1);
|
|
} else if (out != NULL && outsize > 0) {
|
|
out[0] = 0;
|
|
setconsolemode (out, outsize);
|
|
}
|
|
debug_line (input);
|
|
setconsolemode (NULL, 0);
|
|
xfree (input);
|
|
}
|
|
|
|
/*
|
|
|
|
trainer file is .ini file with following one or more [patch] sections.
|
|
Each [patch] section describes single trainer option.
|
|
|
|
After [patch] section must come at least one patch descriptor.
|
|
|
|
[patch]
|
|
name=name
|
|
enable=true/false
|
|
event=KEY_F1
|
|
|
|
; patch descriptor
|
|
data=200e46802d400026200cxx02 ; this is comment
|
|
offset=2
|
|
access=write
|
|
setvalue=<value>
|
|
type=nop/freeze/set/setonce
|
|
|
|
; patch descriptor
|
|
data=11223344556677889900
|
|
offset=10
|
|
replacedata=4e71
|
|
replaceoffset=4
|
|
|
|
; next patch section
|
|
[patch]
|
|
|
|
|
|
name: name of the option (appears in GUI in the future)
|
|
enable: true = automatically enabled at startup. (false=manually activated using key shortcut etc.., will be implemented later)
|
|
event: inputevents.def event name
|
|
|
|
data: match data, when emulated CPU executes first opcode of this data and following words also match: match is detected. x = anything.
|
|
offset: word offset from beginning of "data" that points to memory read/write instruction that you want to "patch". Default=0.
|
|
access: read=read access, write=write access. Default: write if instruction does both memory read and write, read if read-only.
|
|
|
|
setvalue: value to write if type is set or setonce.
|
|
type=nop: found instruction's write does nothing. This instruction only. Other instruction(s) modifying same memory location are not skipped.
|
|
type=freeze: found instruction's memory read always returns value in memory. Write does nothing.
|
|
type=set: found instruction's memory read always returns "setvalue" contents. Write works normally.
|
|
type=setonce: "setvalue" contents are written to memory when patch is detected.
|
|
|
|
replacedata: data to be copied over data + replaceoffset. x masking is also supported. Memory is modified.
|
|
replaceoffset: word offset from data.
|
|
|
|
---
|
|
|
|
Internally it uses debugger memory watch points to modify/freeze memory contents. No memory or code is modified.
|
|
Only type=setonce and replacedata modifies memory.
|
|
|
|
When CPU emulator current to be executed instruction's matches contents of data[offset], other words of data are also checked.
|
|
If all words match: instruction's effective address(es) are calculated and matching (read/write) EA is stored. Matching part
|
|
of patch is now done.
|
|
|
|
Reason for this complexity is to enable single patch to work even if game is relocatable or it uses different memory
|
|
locations depending on hardware config.
|
|
|
|
If type=nop/freeze/set: debugger memwatch point is set that handles faking of read/write access.
|
|
If type=setonce: "setvalue" contents gets written to detected effective address.
|
|
If replacedata is set: copy code.
|
|
|
|
Detection phase may cause increased CPU load, this may get optimized more but it shouldn't be (too) noticeable in basic
|
|
A500 or A1200 modes.
|
|
|
|
*/
|
|
|
|
#define TRAINER_NOP 0
|
|
#define TRAINER_FREEZE 1
|
|
#define TRAINER_SET 2
|
|
#define TRAINER_SETONCE 3
|
|
|
|
struct trainerpatch
|
|
{
|
|
TCHAR *name;
|
|
uae_u16 *data;
|
|
uae_u16 *maskdata;
|
|
uae_u16 *replacedata;
|
|
uae_u16 *replacemaskdata;
|
|
uae_u16 *replacedata_original;
|
|
uae_u16 first;
|
|
int length;
|
|
int offset;
|
|
int access;
|
|
int replacelength;
|
|
int replaceoffset;
|
|
uaecptr addr;
|
|
uaecptr varaddr;
|
|
int varsize;
|
|
uae_u32 oldval;
|
|
int patchtype;
|
|
int setvalue;
|
|
int *events;
|
|
int eventcount;
|
|
int memwatchindex;
|
|
bool enabledatstart;
|
|
bool enabled;
|
|
};
|
|
|
|
static struct trainerpatch **tpptr;
|
|
static int tpptrcnt;
|
|
bool debug_opcode_watch;
|
|
|
|
static int debug_trainer_get_ea(struct trainerpatch *tp, uaecptr pc, uae_u16 opcode, uaecptr *addr)
|
|
{
|
|
struct instr *dp = table68k + opcode;
|
|
uae_u32 sea = 0, dea = 0;
|
|
uaecptr spc = 0, dpc = 0;
|
|
uaecptr pc2 = pc + 2;
|
|
if (dp->suse) {
|
|
spc = pc2;
|
|
pc2 = ShowEA(NULL, pc2, opcode, dp->sreg, dp->smode, dp->size, NULL, &sea, NULL, 1);
|
|
if (sea == spc)
|
|
spc = 0xffffffff;
|
|
}
|
|
if (dp->duse) {
|
|
dpc = pc2;
|
|
pc2 = ShowEA(NULL, pc2, opcode, dp->dreg, dp->dmode, dp->size, NULL, &dea, NULL, 1);
|
|
if (dea == dpc)
|
|
dpc = 0xffffffff;
|
|
}
|
|
if (dea && dpc != 0xffffffff && tp->access == 1) {
|
|
*addr = dea;
|
|
return 1 << dp->size;
|
|
}
|
|
if (sea && spc != 0xffffffff && tp->access == 0) {
|
|
*addr = sea;
|
|
return 1 << dp->size;
|
|
}
|
|
if (dea && tp->access > 1) {
|
|
*addr = dea;
|
|
return 1 << dp->size;
|
|
}
|
|
if (sea && tp->access > 1) {
|
|
*addr = sea;
|
|
return 1 << dp->size;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void debug_trainer_enable(struct trainerpatch *tp, bool enable)
|
|
{
|
|
if (tp->enabled == enable)
|
|
return;
|
|
|
|
if (tp->replacedata) {
|
|
if (enable) {
|
|
bool first = false;
|
|
if (!tp->replacedata_original) {
|
|
tp->replacedata_original = xcalloc(uae_u16, tp->replacelength);
|
|
first = true;
|
|
}
|
|
for (int j = 0; j < tp->replacelength; j++) {
|
|
uae_u16 v = tp->replacedata[j];
|
|
uae_u16 m = tp->replacemaskdata[j];
|
|
uaecptr addr = (tp->addr - tp->offset * 2) + j * 2 + tp->replaceoffset * 2;
|
|
if (m == 0xffff) {
|
|
x_put_word(addr, v);
|
|
} else {
|
|
uae_u16 vo = x_get_word(addr);
|
|
x_put_word(addr, (vo & ~m) | (v & m));
|
|
if (first)
|
|
tp->replacedata_original[j] = vo;
|
|
}
|
|
}
|
|
} else if (tp->replacedata_original) {
|
|
for (int j = 0; j < tp->replacelength; j++) {
|
|
uae_u16 m = tp->replacemaskdata[j];
|
|
uaecptr addr = (tp->addr - tp->offset * 2) + j * 2 + tp->replaceoffset * 2;
|
|
if (m != 0xffff) {
|
|
x_put_word(addr, tp->replacedata_original[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tp->patchtype == TRAINER_SETONCE && tp->varaddr != 0xffffffff) {
|
|
uae_u32 v = enable ? tp->setvalue : tp->oldval;
|
|
switch (tp->varsize)
|
|
{
|
|
case 1:
|
|
x_put_byte(tp->varaddr, tp->setvalue);
|
|
break;
|
|
case 2:
|
|
x_put_word(tp->varaddr, tp->setvalue);
|
|
break;
|
|
case 4:
|
|
x_put_long(tp->varaddr, tp->setvalue);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((tp->patchtype == TRAINER_NOP || tp->patchtype == TRAINER_FREEZE || tp->patchtype == TRAINER_SET) && tp->varaddr != 0xffffffff) {
|
|
struct memwatch_node *mwn;
|
|
if (!memwatch_enabled)
|
|
initialize_memwatch(0);
|
|
if (enable) {
|
|
int i;
|
|
for (i = MEMWATCH_TOTAL - 1; i >= 0; i--) {
|
|
mwn = &mwnodes[i];
|
|
if (!mwn->size)
|
|
break;
|
|
}
|
|
if (i < 0) {
|
|
write_log(_T("Trainer out of free memwatchpoints ('%s' %08x\n).\n"), tp->name, tp->addr);
|
|
} else {
|
|
mwn->addr = tp->varaddr;
|
|
mwn->size = tp->varsize;
|
|
mwn->rwi = 1 | 2;
|
|
mwn->access_mask = MW_MASK_CPU_D_R | MW_MASK_CPU_D_W;
|
|
mwn->reg = 0xffffffff;
|
|
mwn->pc = tp->patchtype == TRAINER_NOP ? tp->addr : 0xffffffff;
|
|
mwn->frozen = tp->patchtype == TRAINER_FREEZE || tp->patchtype == TRAINER_NOP;
|
|
mwn->modval_written = 0;
|
|
mwn->val_enabled = 0;
|
|
mwn->val_mask = 0xffffffff;
|
|
mwn->val = 0;
|
|
mwn->reportonly = false;
|
|
if (tp->patchtype == TRAINER_SET) {
|
|
mwn->val_enabled = 1;
|
|
mwn->val = tp->setvalue;
|
|
}
|
|
mwn->nobreak = true;
|
|
memwatch_setup();
|
|
TCHAR buf[256];
|
|
memwatch_dump2(buf, sizeof(buf) / sizeof(TCHAR), i);
|
|
write_log(_T("%s"), buf);
|
|
}
|
|
} else {
|
|
mwn = &mwnodes[tp->memwatchindex];
|
|
mwn->size = 0;
|
|
memwatch_setup();
|
|
}
|
|
}
|
|
|
|
write_log(_T("Trainer '%s' %s (addr=%08x)\n"), tp->name, enable ? _T("enabled") : _T("disabled"), tp->addr);
|
|
tp->enabled = enable;
|
|
}
|
|
|
|
void debug_trainer_match(void)
|
|
{
|
|
uaecptr pc = m68k_getpc();
|
|
uae_u16 opcode = x_get_word(pc);
|
|
for (int i = 0; i < tpptrcnt; i++) {
|
|
struct trainerpatch *tp = tpptr[i];
|
|
if (tp->first != opcode)
|
|
continue;
|
|
if (tp->addr)
|
|
continue;
|
|
int j;
|
|
for (j = 0; j < tp->length; j++) {
|
|
uae_u16 d = x_get_word(pc + (j - tp->offset) * 2);
|
|
if ((d & tp->maskdata[j]) != tp->data[j])
|
|
break;
|
|
}
|
|
if (j < tp->length)
|
|
continue;
|
|
tp->first = 0xffff;
|
|
tp->addr = pc;
|
|
tp->varsize = -1;
|
|
tp->varaddr = 0xffffffff;
|
|
tp->oldval = 0xffffffff;
|
|
if (tp->access >= 0) {
|
|
tp->varsize = debug_trainer_get_ea(tp, pc, opcode, &tp->varaddr);
|
|
switch (tp->varsize)
|
|
{
|
|
case 1:
|
|
tp->oldval = x_get_byte(tp->varaddr);
|
|
break;
|
|
case 2:
|
|
tp->oldval = x_get_word(tp->varaddr);
|
|
break;
|
|
case 4:
|
|
tp->oldval = x_get_long(tp->varaddr);
|
|
break;
|
|
}
|
|
}
|
|
write_log(_T("Patch %d match at %08x. Addr %08x, size %d, val %08x\n"), i, pc, tp->varaddr, tp->varsize, tp->oldval);
|
|
|
|
if (tp->enabledatstart)
|
|
debug_trainer_enable(tp, true);
|
|
|
|
// all detected?
|
|
for (j = 0; j < tpptrcnt; j++) {
|
|
struct trainerpatch *tp = tpptr[j];
|
|
if (!tp->addr)
|
|
break;
|
|
}
|
|
if (j == tpptrcnt)
|
|
debug_opcode_watch = false;
|
|
}
|
|
}
|
|
|
|
static int parsetrainerdata(const TCHAR *data, uae_u16 *outdata, uae_u16 *outmask)
|
|
{
|
|
int len = uaetcslen(data);
|
|
uae_u16 v = 0, vm = 0;
|
|
int j = 0;
|
|
for (int i = 0; i < len; ) {
|
|
TCHAR c1 = _totupper(data[i + 0]);
|
|
TCHAR c2 = _totupper(data[i + 1]);
|
|
if (c1 > 0 && c1 <= ' ') {
|
|
i++;
|
|
continue;
|
|
}
|
|
if (i + 1 >= len)
|
|
return 0;
|
|
|
|
vm <<= 8;
|
|
vm |= 0xff;
|
|
if (c1 == 'X' || c1 == '?')
|
|
vm &= 0x0f;
|
|
if (c2 == 'X' || c2 == '?')
|
|
vm &= 0xf0;
|
|
|
|
if (c1 >= 'A')
|
|
c1 -= 'A' - 10;
|
|
else if (c1 >= '0')
|
|
c1 -= '0';
|
|
if (c2 >= 'A')
|
|
c2 -= 'A' - 10;
|
|
else if (c2 >= '0')
|
|
c2 -= '0';
|
|
|
|
v <<= 8;
|
|
if (c1 >= 0 && c1 < 16)
|
|
v |= c1 << 4;
|
|
if (c2 >= 0 && c2 < 16)
|
|
v |= c2;
|
|
|
|
if (i & 2) {
|
|
outdata[j] = v;
|
|
outmask[j] = vm;
|
|
j++;
|
|
}
|
|
|
|
i += 2;
|
|
}
|
|
return j;
|
|
}
|
|
|
|
void debug_init_trainer(const TCHAR *file)
|
|
{
|
|
TCHAR section[256];
|
|
int cnt = 1;
|
|
|
|
struct ini_data *ini = ini_load(file, false);
|
|
if (!ini)
|
|
return;
|
|
|
|
write_log(_T("Loaded '%s'\n"), file);
|
|
|
|
_tcscpy(section, _T("patch"));
|
|
|
|
for (;;) {
|
|
struct ini_context ictx;
|
|
ini_initcontext(ini, &ictx);
|
|
|
|
for (;;) {
|
|
TCHAR *name = NULL;
|
|
TCHAR *data;
|
|
|
|
ini_getstring_multi(ini, section, _T("name"), &name, &ictx);
|
|
|
|
if (!ini_getstring_multi(ini, section, _T("data"), &data, &ictx))
|
|
break;
|
|
ini_setcurrentasstart(ini, &ictx);
|
|
ini_setlast(ini, section, _T("data"), &ictx);
|
|
|
|
TCHAR *p = _tcschr(data, ';');
|
|
if (p)
|
|
*p = 0;
|
|
my_trim(data);
|
|
|
|
struct trainerpatch *tp = xcalloc(struct trainerpatch, 1);
|
|
|
|
int datalen = (uaetcslen(data) + 3) / 4;
|
|
tp->data = xcalloc(uae_u16, datalen);
|
|
tp->maskdata = xcalloc(uae_u16, datalen);
|
|
tp->length = parsetrainerdata(data, tp->data, tp->maskdata);
|
|
xfree(data);
|
|
|
|
ini_getval_multi(ini, section, _T("offset"), &tp->offset, &ictx);
|
|
if (tp->offset < 0 || tp->offset >= tp->length)
|
|
tp->offset = 0;
|
|
|
|
if (ini_getstring_multi(ini, section, _T("replacedata"), &data, &ictx)) {
|
|
int replacedatalen = (uaetcslen(data) + 3) / 4;
|
|
tp->replacedata = xcalloc(uae_u16, replacedatalen);
|
|
tp->replacemaskdata = xcalloc(uae_u16, replacedatalen);
|
|
tp->replacelength = parsetrainerdata(data, tp->replacedata, tp->replacemaskdata);
|
|
ini_getval_multi(ini, section, _T("replaceoffset"), &tp->offset, &ictx);
|
|
if (tp->replaceoffset < 0 || tp->replaceoffset >= tp->length)
|
|
tp->replaceoffset = 0;
|
|
tp->access = -1;
|
|
xfree(data);
|
|
}
|
|
|
|
tp->access = 2;
|
|
if (ini_getstring_multi(ini, section, _T("access"), &data, &ictx)) {
|
|
if (!_tcsicmp(data, _T("read")))
|
|
tp->access = 0;
|
|
else if (!_tcsicmp(data, _T("write")))
|
|
tp->access = 1;
|
|
xfree(data);
|
|
}
|
|
|
|
if (ini_getstring_multi(ini, section, _T("type"), &data, &ictx)) {
|
|
if (!_tcsicmp(data, _T("freeze")))
|
|
tp->patchtype = TRAINER_FREEZE;
|
|
else if (!_tcsicmp(data, _T("nop")))
|
|
tp->patchtype = TRAINER_NOP;
|
|
else if (!_tcsicmp(data, _T("set")))
|
|
tp->patchtype = TRAINER_SET;
|
|
else if (!_tcsicmp(data, _T("setonce")))
|
|
tp->patchtype = TRAINER_SETONCE;
|
|
xfree(data);
|
|
}
|
|
|
|
if (ini_getstring_multi(ini, section, _T("setvalue"), &data, &ictx)) {
|
|
TCHAR *endptr;
|
|
if (data[0] == '$') {
|
|
tp->setvalue = _tcstol(data + 1, &endptr, 16);
|
|
} else if (_tcslen(data) > 2 && data[0] == '0' && _totupper(data[1]) == 'x') {
|
|
tp->setvalue = _tcstol(data + 2, &endptr, 16);
|
|
} else {
|
|
tp->setvalue = _tcstol(data, &endptr, 10);
|
|
}
|
|
xfree(data);
|
|
}
|
|
|
|
if (ini_getstring(ini, section, _T("enable"), &data)) {
|
|
if (!_tcsicmp(data, _T("true")))
|
|
tp->enabledatstart = true;
|
|
xfree(data);
|
|
}
|
|
|
|
if (ini_getstring(ini, section, _T("event"), &data)) {
|
|
TCHAR *s = data;
|
|
_tcscat(s, _T(","));
|
|
while (*s) {
|
|
bool end = false;
|
|
while (*s == ' ')
|
|
s++;
|
|
TCHAR *se = _tcschr(s, ',');
|
|
if (se) {
|
|
*se = 0;
|
|
} else {
|
|
end = true;
|
|
}
|
|
TCHAR *se2 = se - 1;
|
|
while (se2 > s) {
|
|
if (*se2 != ' ')
|
|
break;
|
|
*se2 = 0;
|
|
se2--;
|
|
}
|
|
int evt = inputdevice_geteventid(s);
|
|
if (evt > 0) {
|
|
if (tp->events) {
|
|
tp->events = xrealloc(int, tp->events, tp->eventcount + 1);
|
|
} else {
|
|
tp->events = xmalloc(int, 1);
|
|
}
|
|
tp->events[tp->eventcount++] = evt;
|
|
} else {
|
|
write_log(_T("Unknown event '%s'\n"), s);
|
|
}
|
|
if (end)
|
|
break;
|
|
s = se + 1;
|
|
}
|
|
xfree(data);
|
|
}
|
|
|
|
tp->first = tp->data[tp->offset];
|
|
tp->name = name;
|
|
|
|
if (tpptrcnt)
|
|
tpptr = xrealloc(struct trainerpatch*, tpptr, tpptrcnt + 1);
|
|
else
|
|
tpptr = xcalloc(struct trainerpatch*, tpptrcnt + 1);
|
|
tpptr[tpptrcnt++] = tp;
|
|
|
|
write_log(_T("%d: '%s' parsed and enabled\n"), cnt, tp->name ? tp->name : _T("<no name>"));
|
|
cnt++;
|
|
|
|
ini_setlastasstart(ini, &ictx);
|
|
}
|
|
|
|
if (!ini_nextsection(ini, section))
|
|
break;
|
|
|
|
}
|
|
|
|
if (tpptrcnt > 0)
|
|
debug_opcode_watch = true;
|
|
|
|
ini_free(ini);
|
|
}
|
|
|
|
bool debug_trainer_event(int evt, int state)
|
|
{
|
|
for (int i = 0; i < tpptrcnt; i++) {
|
|
struct trainerpatch *tp = tpptr[i];
|
|
for (int j = 0; j < tp->eventcount; j++) {
|
|
if (tp->events[j] <= 0)
|
|
continue;
|
|
if (tp->events[j] == evt) {
|
|
if (!state)
|
|
return true;
|
|
write_log(_T("Trainer %d ('%s') -> %s\n"), i, tp->name, tp->enabled ? _T("off") : _T("on"));
|
|
debug_trainer_enable(tp, !tp->enabled);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool debug_get_prefetch(int idx, uae_u16 *opword)
|
|
{
|
|
if (currprefs.cpu_compatible) {
|
|
if (currprefs.cpu_model < 68020) {
|
|
if (idx == 0) {
|
|
*opword = regs.ir;
|
|
return true;
|
|
}
|
|
if (idx == 1) {
|
|
*opword = regs.irc;
|
|
return true;
|
|
}
|
|
*opword = get_word_debug(m68k_getpc() + idx * 2);
|
|
return false;
|
|
} else {
|
|
if (regs.prefetch020_valid[idx]) {
|
|
*opword = regs.prefetch020[idx];
|
|
return true;
|
|
}
|
|
*opword = get_word_debug(m68k_getpc() + idx * 2);
|
|
return false;
|
|
}
|
|
|
|
} else {
|
|
*opword = get_word_debug(m68k_getpc() + idx * 2);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#define DEBUGSPRINTF_SIZE 32
|
|
static int debugsprintf_cnt;
|
|
struct dsprintfstack
|
|
{
|
|
uae_u32 val;
|
|
int size;
|
|
};
|
|
static dsprintfstack debugsprintf_stack[DEBUGSPRINTF_SIZE];
|
|
static uae_u16 debugsprintf_latch, debugsprintf_latched;
|
|
static evt_t debugsprintf_cycles, debugsprintf_cycles_set;
|
|
static uaecptr debugsprintf_va;
|
|
static int debugsprintf_mode;
|
|
|
|
static void read_bstring(char *out, int max, uae_u32 addr)
|
|
{
|
|
out[0] = 0;
|
|
if (!valid_address(addr, 1))
|
|
return;
|
|
uae_u8 l = get_byte(addr);
|
|
if (l > max)
|
|
l = max;
|
|
addr++;
|
|
for (int i = 0; i < l && i < max; i++) {
|
|
uae_u8 c = 0;
|
|
if (valid_address(addr, 1)) {
|
|
c = get_byte(addr);
|
|
}
|
|
if (c == 0) {
|
|
c = '.';
|
|
}
|
|
addr++;
|
|
out[i] = c;
|
|
out[i + 1] = 0;
|
|
}
|
|
}
|
|
|
|
static void read_string(char *out, int max, uae_u32 addr)
|
|
{
|
|
out[0] = 0;
|
|
for (int i = 0; i < max; i++) {
|
|
uae_u8 c = 0;
|
|
if (valid_address(addr, 1)) {
|
|
c = get_byte(addr);
|
|
}
|
|
addr++;
|
|
out[i] = c;
|
|
out[i + 1] = 0;
|
|
if (!c)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void parse_custom(char *out, int buffersize, char *format, char *p, char c)
|
|
{
|
|
bool gotv = false;
|
|
bool gots = false;
|
|
out[0] = 0;
|
|
uae_u32 v = 0;
|
|
char s[256];
|
|
if (!strcmp(p, "CYCLES")) {
|
|
if (debugsprintf_cycles_set) {
|
|
v = (uae_u32)((get_cycles() - debugsprintf_cycles) / CYCLE_UNIT);
|
|
} else {
|
|
v = 0xffffffff;
|
|
}
|
|
gotv = true;
|
|
}
|
|
if (gotv) {
|
|
if (c == 'x' || c == 'X' || c == 'd' || c == 'i' || c == 'u' || c == 'o') {
|
|
char *fs = format + strlen(format);
|
|
*fs++ = c;
|
|
*fs = 0;
|
|
snprintf(out, buffersize, format, v);
|
|
} else {
|
|
strcpy(s, "****");
|
|
gots = true;
|
|
}
|
|
}
|
|
if (gots) {
|
|
char *fs = format + strlen(format);
|
|
*fs++ = 's';
|
|
*fs = 0;
|
|
snprintf(out, buffersize, format, s);
|
|
}
|
|
}
|
|
|
|
static uae_u32 get_value(struct dsprintfstack **stackp, uae_u32 *sizep, uaecptr *ptrp, uae_u32 size)
|
|
{
|
|
if (debugsprintf_mode) {
|
|
uae_u32 v;
|
|
uaecptr ptr = *ptrp;
|
|
if (size == sz_long) {
|
|
v = get_long_debug(ptr);
|
|
ptr += 4;
|
|
} else if (size == sz_word) {
|
|
v = get_word_debug(ptr);
|
|
ptr += 2;
|
|
} else {
|
|
v = get_byte_debug(ptr);
|
|
ptr++;
|
|
}
|
|
*ptrp = ptr;
|
|
*sizep = size;
|
|
return v;
|
|
} else {
|
|
struct dsprintfstack *stack = *stackp;
|
|
uae_u32 v = stack->val;
|
|
if (stack->size == 0)
|
|
v &= 0xff;
|
|
else if (stack->size == 1)
|
|
v &= 0xffff;
|
|
if (size == 1)
|
|
v &= 0xffff;
|
|
*sizep = size;
|
|
stack++;
|
|
*stackp = stack;
|
|
return v;
|
|
}
|
|
}
|
|
|
|
static void debug_sprintf_do(uae_u32 s)
|
|
{
|
|
int cnt = 0;
|
|
char format[MAX_DPATH];
|
|
char out[MAX_DPATH];
|
|
read_string(format, MAX_DPATH - 1, s);
|
|
char *p = format;
|
|
char *d = out;
|
|
bool gotm = false;
|
|
bool l = false;
|
|
uaecptr ptr = debugsprintf_va;
|
|
struct dsprintfstack *stack = debugsprintf_stack;
|
|
char fstr[100], *fstrp;
|
|
int buffersize = MAX_DPATH - 1;
|
|
fstrp = fstr;
|
|
*d = 0;
|
|
for (;;) {
|
|
char c = *p++;
|
|
if (c == 0)
|
|
break;
|
|
if (gotm) {
|
|
bool got = false;
|
|
buffersize = MAX_DPATH - uaestrlen(out);
|
|
if (buffersize <= 1)
|
|
break;
|
|
if (c == '%') {
|
|
*d++ = '%';
|
|
gotm = false;
|
|
} else if (c == 'l') {
|
|
l = true;
|
|
} else if (c == 'c') {
|
|
uae_u32 size;
|
|
uae_u32 val = get_value(&stack, &size, &ptr, l ? sz_long : sz_word);
|
|
*fstrp++ = c;
|
|
*fstrp = 0;
|
|
snprintf(d, buffersize, fstr, val);
|
|
got = true;
|
|
} else if (c == 'b') {
|
|
uae_u32 size;
|
|
uae_u32 val = get_value(&stack, &size, &ptr, sz_long);
|
|
char tmp[MAX_DPATH];
|
|
read_bstring(tmp, MAX_DPATH - 1, val);
|
|
*fstrp++ = 's';
|
|
*fstrp = 0;
|
|
snprintf(d, buffersize, fstr, tmp);
|
|
got = true;
|
|
} else if (c == 's') {
|
|
uae_u32 size;
|
|
uae_u32 val = get_value(&stack, &size, &ptr, sz_long);
|
|
char tmp[MAX_DPATH];
|
|
read_string(tmp, MAX_DPATH - 1, val);
|
|
*fstrp++ = c;
|
|
*fstrp = 0;
|
|
snprintf(d, buffersize, fstr, tmp);
|
|
got = true;
|
|
} else if (c == 'p') {
|
|
uae_u32 size;
|
|
uae_u32 val = get_value(&stack, &size, &ptr, sz_long);
|
|
snprintf(d, buffersize, "$%08x", val);
|
|
got = true;
|
|
} else if (c == 'x' || c == 'X' || c == 'd' || c == 'i' || c == 'u' || c == 'o') {
|
|
uae_u32 size;
|
|
uae_u32 val = get_value(&stack, &size, &ptr, l ? sz_long : sz_word);
|
|
if (c == 'd' || c == 'i') {
|
|
if (size == sz_word && (val & 0x8000)) {
|
|
val = (uae_s32)(uae_s16)val;
|
|
}
|
|
}
|
|
*fstrp++ = c;
|
|
*fstrp = 0;
|
|
snprintf(d, buffersize, fstr, val);
|
|
got = true;
|
|
} else if (c == '[') {
|
|
char *next = strchr(p, ']');
|
|
if (next && next[1]) {
|
|
char customout[MAX_DPATH];
|
|
customout[0] = 0;
|
|
*next = 0;
|
|
parse_custom(d, buffersize, fstr, p, next[1]);
|
|
p = next + 2;
|
|
got = true;
|
|
} else {
|
|
gotm = false;
|
|
}
|
|
} else {
|
|
if (fstrp - fstr < sizeof(fstr) - 1) {
|
|
*fstrp++ = c;
|
|
*fstrp = 0;
|
|
}
|
|
}
|
|
if (got) {
|
|
d += strlen(d);
|
|
gotm = false;
|
|
}
|
|
} else if (c == '%') {
|
|
l = false;
|
|
fstrp = fstr;
|
|
*fstrp++ = c;
|
|
*fstrp = 0;
|
|
gotm = true;
|
|
} else {
|
|
*d++ = c;
|
|
}
|
|
*d = 0;
|
|
}
|
|
write_log("%s", out);
|
|
}
|
|
|
|
bool debug_sprintf(uaecptr addr, uae_u32 val, int size)
|
|
{
|
|
if (!currprefs.debug_mem)
|
|
return false;
|
|
|
|
uae_u32 v = val;
|
|
if (size == sz_word && currprefs.cpu_model < 68020) {
|
|
v &= 0xffff;
|
|
if (!(addr & 2)) {
|
|
debugsprintf_latch = v;
|
|
debugsprintf_latched = 1;
|
|
} else if (debugsprintf_latched) {
|
|
v |= debugsprintf_latch << 16;
|
|
size = sz_long;
|
|
if (!(addr & 4) && debugsprintf_cnt > 0) {
|
|
debugsprintf_cnt--;
|
|
}
|
|
}
|
|
}
|
|
if (size != sz_word) {
|
|
debugsprintf_latched = 0;
|
|
}
|
|
if ((addr & (8 | 4)) == 4) {
|
|
if (size != sz_long)
|
|
return true;
|
|
debug_sprintf_do(v);
|
|
debugsprintf_cnt = 0;
|
|
debugsprintf_latched = 0;
|
|
debugsprintf_cycles = get_cycles();
|
|
debugsprintf_cycles_set = 1;
|
|
} else if ((addr & (8 | 4)) == 8) {
|
|
if (size != sz_long)
|
|
return true;
|
|
debugsprintf_va = val;
|
|
debugsprintf_mode = 1;
|
|
} else {
|
|
if (debugsprintf_cnt < DEBUGSPRINTF_SIZE) {
|
|
debugsprintf_stack[debugsprintf_cnt].val = v;
|
|
debugsprintf_stack[debugsprintf_cnt].size = size;
|
|
debugsprintf_cnt++;
|
|
}
|
|
debugsprintf_mode = 0;
|
|
}
|
|
return true;
|
|
}
|