From cf4685e7aa57301879650b59c7baf6e68d0aa359 Mon Sep 17 00:00:00 2001 From: diaxen <> Date: Mon, 11 May 2020 17:59:02 -0400 Subject: [PATCH] Add gdb server to debugger Here is a patch which allows using the GNU debugger as an alternative to the DOSBox built-in debugger. The built-in debugger may be more convenient to show x86 specific information about execution state, whereas Gdb is useful for C and C++ source level debugging. This patch applies against svn revision 3761. Once applied the configure script can be used with a new option: ./autogen.sh ./configure --enable-debug=gdbserver DOSBox then listen on TCP port 1234 and a Gdb client compiled for x86_64 or i686 targets can be connected: $ gdb (gdb) target remote localhost:1234 The gdb architecture must be set depending on the code your are trying to disassemble (16 or 32 bits), for instance: (gdb) set architecture i8086 (gdb) x/32i $eip Breakpoints and watchpoints are supported and some DOSBox specific features are available through the gdb "monitor" command. This patch adds the following files: - src/debug/gdb_server.cpp file - src/debug/debug_log.cpp - src/debug/debug_helpers.cpp The debug.cpp file has been split in 3 files so that the original file now contains built-in debugger stuff only and debug_log.cpp/debug_helpers.cpp files contain common code used by both built-in and Gdb debuggers. Some variables which were previously local to debug.cpp have been prefixed by DEBUG_ and debug.h has been updated accordingly. Tested under GNU/Linux. Co-authored-by: ykhwong Imported-from: https://www.vogons.org/viewtopic.php?p=259378#p259378 --- configure.in | 7 +- include/debug.h | 80 +- include/logging.h | 13 +- include/paging.h | 20 + src/cpu/core_dyn_x86.cpp | 20 +- src/cpu/core_dyn_x86/decoder.h | 2 +- src/cpu/core_dynrec.cpp | 20 +- src/cpu/core_full.cpp | 8 +- src/cpu/core_full/op.h | 6 +- src/cpu/core_normal.cpp | 14 +- src/cpu/core_normal/prefix_none.h | 8 +- src/cpu/core_prefetch.cpp | 12 +- src/cpu/core_simple.cpp | 14 +- src/cpu/cpu.cpp | 6 +- src/cpu/paging.cpp | 4 +- src/debug/Makefile.am | 3 +- src/debug/debug.cpp | 989 ++---------- src/debug/debug.cpp.orig | 2493 +++++++++++++++++++++++++++++ src/debug/debug_disasm.cpp | 2 +- src/debug/debug_gui.cpp | 93 +- src/debug/debug_helpers.cpp | 347 ++++ src/debug/debug_log.cpp | 502 ++++++ src/debug/gdb_server.cpp | 1090 +++++++++++++ src/debug/poll.cpp | 595 +++++++ src/debug/poll.h | 60 + src/dos/dos_execute.cpp | 2 +- src/dos/dos_execute.cpp.orig | 504 ++++++ src/dosbox.cpp | 6 +- src/gui/sdlmain.cpp | 6 +- src/gui/sdlmain.cpp.orig | 2057 ++++++++++++++++++++++++ src/misc/support.cpp | 2 +- 31 files changed, 7979 insertions(+), 1006 deletions(-) create mode 100644 src/debug/debug.cpp.orig create mode 100644 src/debug/debug_helpers.cpp create mode 100644 src/debug/debug_log.cpp create mode 100644 src/debug/gdb_server.cpp create mode 100644 src/debug/poll.cpp create mode 100644 src/debug/poll.h create mode 100644 src/dos/dos_execute.cpp.orig create mode 100644 src/gui/sdlmain.cpp.orig diff --git a/configure.in b/configure.in index 54b5356c..40c8e48e 100644 --- a/configure.in +++ b/configure.in @@ -188,13 +188,16 @@ AC_C_BIGENDIAN #Features to enable/disable AH_TEMPLATE(C_DEBUG,[Define to 1 to enable internal debugger, requires libcurses]) AH_TEMPLATE(C_HEAVY_DEBUG,[Define to 1 to enable heavy debugging, also have to enable C_DEBUG]) -AC_ARG_ENABLE(debug,AC_HELP_STRING([--enable-debug],[Enable debug mode]),[ +AH_TEMPLATE(C_GDBSERVER,[Define to 1 to enable GNU debugger server]) +AC_ARG_ENABLE(debug,AC_HELP_STRING([--enable-debug],[Enable debugger (yes, heavy or gdbserver)]),[ AC_CHECK_HEADER(curses.h,have_curses_h=yes,) AC_CHECK_LIB(curses, initscr, have_curses_lib=yes, , ) AC_CHECK_LIB(ncurses, initscr, have_ncurses_lib=yes, , ) AC_CHECK_LIB(pdcurses, initscr, have_pdcurses_lib=yes, , ) - if test x$enable_debug = xno; then + if test x$enable_debug = xgdbserver; then + AC_DEFINE(C_GDBSERVER,1) + elif test x$enable_debug = xno; then AC_MSG_RESULT([Debugger not enabled]) elif test x$have_curses_lib = xyes -a x$have_curses_h = xyes ; then LIBS="$LIBS -lcurses" diff --git a/include/debug.h b/include/debug.h index 031e6831..3917b15e 100644 --- a/include/debug.h +++ b/include/debug.h @@ -16,6 +16,19 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifndef DOSBOX_DEBUG_H +#define DOSBOX_DEBUG_H + +#if C_DEBUG || C_GDBSERVER + +#include + +extern Bit16u DEBUG_dataSeg; +extern Bit32u DEBUG_dataOfs; +extern bool DEBUG_showExtend; +extern char DEBUG_curSelectorName[3]; +extern bool DEBUG_exitLoop; + void DEBUG_SetupConsole(void); void DEBUG_DrawScreen(void); bool DEBUG_Breakpoint(void); @@ -25,11 +38,72 @@ void DEBUG_CheckExecuteBreakpoint(Bit16u seg, Bit32u off); bool DEBUG_ExitLoop(void); void DEBUG_RefreshPage(char scroll); Bitu DEBUG_EnableDebugger(void); +Bit32u DEBUG_GetHexValue(char* str, char*& hex); +Bit32u DEBUG_GetAddress(Bit16u seg, Bit32u offset); +char* DEBUG_AnalyzeInstruction(char* inst, bool saveSelector); +bool DEBUG_GetDescriptorInfo(char* selname, char* out1, char* out2); -extern Bitu cycle_count; -extern Bitu debugCallback; +void DEBUG_LogMCBChain(Bit16u mcb_segment); +void DEBUG_LogMCBS(void); +void DEBUG_LogGDT(void); +void DEBUG_LogLDT(void); +void DEBUG_LogIDT(void); +void DEBUG_LogPages(char* selname); +void DEBUG_LogCPUInfo(void); +void DEBUG_LogInstruction(Bit16u segValue, Bit32u eipValue, std::ofstream& out); + +extern bool DEBUG_showExtend; + +extern Bitu DEBUG_cycle_count; +extern Bitu DEBUG_debugCallback; + +#if C_HEAVY_DEBUG || C_GDBSERVER +extern std::ofstream DEBUG_cpuLogFile; +extern bool DEBUG_cpuLog; +extern int DEBUG_cpuLogCounter; +extern int DEBUG_cpuLogType; // log detail +extern bool DEBUG_zeroProtect; +extern bool DEBUG_logHeavy; -#ifdef C_HEAVY_DEBUG bool DEBUG_HeavyIsBreakpoint(void); +void DEBUG_HeavyLogInstruction(void); void DEBUG_HeavyWriteLogInstruction(void); #endif + +#ifdef C_GDBSERVER +void DEBUG_GdbMemReadHook(Bit32u address, int width); +void DEBUG_GdbMemWriteHook(Bit32u address, int width, Bit32u value); +void DEBUG_IrqBreakpoint(Bit8u intNum); +#endif + +/********************/ +/* DebugVar stuff */ +/********************/ + +#include +#include "paging.h" + +class CDebugVar +{ +public: + CDebugVar(char* _name, PhysPt _adr); + + char* GetName(void) { return name; }; + PhysPt GetAdr (void) { return adr; }; + +private: + PhysPt adr; + char name[16]; + +public: + static void InsertVariable (char* name, PhysPt adr); + static CDebugVar* FindVar (PhysPt adr); + static void DeleteAll (); + static bool SaveVars (char* name); + static bool LoadVars (char* name); + + static std::list varList; +}; + +#endif +#endif diff --git a/include/logging.h b/include/logging.h index 7faf84cd..4ef0cbaf 100644 --- a/include/logging.h +++ b/include/logging.h @@ -37,7 +37,12 @@ enum LOG_SEVERITIES { LOG_ERROR }; -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER +struct _LogGroup { + char const* front; + bool enabled; +}; + class LOG { LOG_TYPES d_type; @@ -48,14 +53,14 @@ public: d_type(type), d_severity(severity) {} - void operator() (char const* buf, ...) GCC_ATTRIBUTE(__format__(__printf__, 2, 3)); //../src/debug/debug_gui.cpp + void operator() (char const* buf, ...) GCC_ATTRIBUTE(__format__(__printf__, 2, 3)); //../src/debug/debug_log.cpp }; void DEBUG_ShowMsg(char const* format,...) GCC_ATTRIBUTE(__format__(__printf__, 1, 2)); #define LOG_MSG DEBUG_ShowMsg -#else //C_DEBUG +#else //C_DEBUG || C_GDBSERVER struct LOG { @@ -84,7 +89,7 @@ struct LOG void GFX_ShowMsg(char const* format,...) GCC_ATTRIBUTE(__format__(__printf__, 1, 2)); #define LOG_MSG GFX_ShowMsg -#endif //C_DEBUG +#endif //C_DEBUG || C_GDBSERVER #endif //DOSBOX_LOGGING_H diff --git a/include/paging.h b/include/paging.h index 13fee9b9..b13ed157 100644 --- a/include/paging.h +++ b/include/paging.h @@ -27,6 +27,8 @@ #include "mem.h" #endif +#include "debug.h" + // disable this to reduce the size of the TLB // NOTE: does not work with the dynamic core (dynrec is fine) #define USE_FULL_TLB @@ -261,12 +263,18 @@ static INLINE PhysPt PAGING_GetPhysicalAddress(PhysPt linAddr) { /* Special inlined memory reading/writing */ static INLINE Bit8u mem_readb_inline(PhysPt address) { +#ifdef C_GDBSERVER + DEBUG_GdbMemReadHook(address, 1); +#endif HostPt tlb_addr=get_tlb_read(address); if (tlb_addr) return host_readb(tlb_addr+address); else return (Bit8u)(get_tlb_readhandler(address))->readb(address); } static INLINE Bit16u mem_readw_inline(PhysPt address) { +#ifdef C_GDBSERVER + DEBUG_GdbMemReadHook(address, 1); +#endif if ((address & 0xfff)<0xfff) { HostPt tlb_addr=get_tlb_read(address); if (tlb_addr) return host_readw(tlb_addr+address); @@ -275,6 +283,9 @@ static INLINE Bit16u mem_readw_inline(PhysPt address) { } static INLINE Bit32u mem_readd_inline(PhysPt address) { +#ifdef C_GDBSERVER + DEBUG_GdbMemReadHook(address, 1); +#endif if ((address & 0xfff)<0xffd) { HostPt tlb_addr=get_tlb_read(address); if (tlb_addr) return host_readd(tlb_addr+address); @@ -283,12 +294,18 @@ static INLINE Bit32u mem_readd_inline(PhysPt address) { } static INLINE void mem_writeb_inline(PhysPt address,Bit8u val) { +#ifdef C_GDBSERVER + DEBUG_GdbMemWriteHook(address, 1, val); +#endif HostPt tlb_addr=get_tlb_write(address); if (tlb_addr) host_writeb(tlb_addr+address,val); else (get_tlb_writehandler(address))->writeb(address,val); } static INLINE void mem_writew_inline(PhysPt address,Bit16u val) { +#ifdef C_GDBSERVER + DEBUG_GdbMemWriteHook(address, 2, val); +#endif if ((address & 0xfff)<0xfff) { HostPt tlb_addr=get_tlb_write(address); if (tlb_addr) host_writew(tlb_addr+address,val); @@ -297,6 +314,9 @@ static INLINE void mem_writew_inline(PhysPt address,Bit16u val) { } static INLINE void mem_writed_inline(PhysPt address,Bit32u val) { +#ifdef C_GDBSERVER + DEBUG_GdbMemWriteHook(address, 4, val); +#endif if ((address & 0xfff)<0xffd) { HostPt tlb_addr=get_tlb_write(address); if (tlb_addr) host_writed(tlb_addr+address,val); diff --git a/src/cpu/core_dyn_x86.cpp b/src/cpu/core_dyn_x86.cpp index f63d9158..daab840a 100644 --- a/src/cpu/core_dyn_x86.cpp +++ b/src/cpu/core_dyn_x86.cpp @@ -113,7 +113,7 @@ enum BlockReturn { BR_Cycles, BR_Link1,BR_Link2, BR_Opcode, -#if (C_DEBUG) +#if C_DEBUG || C_GDBSERVER BR_OpcodeFull, #endif BR_Iret, @@ -261,8 +261,8 @@ Bits CPU_Core_Dyn_X86_Run(void) { /* Determine the linear address of CS:EIP */ restart_core: PhysPt ip_point=SegPhys(cs)+reg_eip; - #if C_HEAVY_DEBUG - if (DEBUG_HeavyIsBreakpoint()) return debugCallback; + #if C_HEAVY_DEBUG || C_GDBSERVER + if (DEBUG_HeavyIsBreakpoint()) return DEBUG_debugCallback; #endif CodePageHandler * chandler=0; if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) { @@ -296,10 +296,10 @@ run_block: BlockReturn ret=gen_runcode(block->cache.start); switch (ret) { case BR_Iret: -#if C_HEAVY_DEBUG +#if C_HEAVY_DEBUG || C_GDBSERVER if (DEBUG_HeavyIsBreakpoint()) { if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT - return debugCallback; + return DEBUG_debugCallback; } #endif if (!GETFLAG(TF)) { @@ -315,13 +315,13 @@ run_block: return CBRET_NONE; case BR_Normal: /* Maybe check if we staying in the same page? */ -#if C_HEAVY_DEBUG - if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#if C_HEAVY_DEBUG || C_GDBSERVER + if (DEBUG_HeavyIsBreakpoint()) return DEBUG_debugCallback; #endif goto restart_core; case BR_Cycles: -#if C_HEAVY_DEBUG - if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#if C_HEAVY_DEBUG || C_GDBSERVER + if (DEBUG_HeavyIsBreakpoint()) return DEBUG_debugCallback; #endif if (!dyn_dh_fpu.state_used) return CBRET_NONE; DH_FPU_SAVE_REINIT @@ -339,7 +339,7 @@ run_block: CPU_Cycles=1; if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CPU_Core_Normal_Run(); -#if (C_DEBUG) +#if C_DEBUG || C_GDBSERVER case BR_OpcodeFull: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; diff --git a/src/cpu/core_dyn_x86/decoder.h b/src/cpu/core_dyn_x86/decoder.h index a9f90d07..d02e5428 100644 --- a/src/cpu/core_dyn_x86/decoder.h +++ b/src/cpu/core_dyn_x86/decoder.h @@ -2697,7 +2697,7 @@ illegalopcode: gen_return(BR_Opcode); dyn_closeblock(); goto finish_block; -#if (C_DEBUG) +#if C_DEBUG || C_GDBSERVER dyn_set_eip_last(); dyn_reduce_cycles(); dyn_save_critical_regs(); diff --git a/src/cpu/core_dynrec.cpp b/src/cpu/core_dynrec.cpp index ed975f56..bfbef7b8 100644 --- a/src/cpu/core_dynrec.cpp +++ b/src/cpu/core_dynrec.cpp @@ -107,7 +107,7 @@ enum BlockReturn { BR_Cycles, BR_Link1,BR_Link2, BR_Opcode, -#if (C_DEBUG) +#if C_DEBUG || C_GDBSERVER BR_OpcodeFull, #endif BR_Iret, @@ -186,8 +186,8 @@ Bits CPU_Core_Dynrec_Run(void) { for (;;) { // Determine the linear address of CS:EIP PhysPt ip_point=SegPhys(cs)+reg_eip; - #if C_HEAVY_DEBUG - if (DEBUG_HeavyIsBreakpoint()) return debugCallback; + #if C_HEAVY_DEBUG || C_GDBSERVER + if (DEBUG_HeavyIsBreakpoint()) return DEBUG_debugCallback; #endif CodePageHandlerDynRec * chandler=0; @@ -231,8 +231,8 @@ run_block: switch (ret) { case BR_Iret: -#if C_HEAVY_DEBUG - if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#if C_HEAVY_DEBUG || C_GDBSERVER + if (DEBUG_HeavyIsBreakpoint()) return DEBUG_debugCallback; #endif if (!GETFLAG(TF)) { if (GETFLAG(IF) && PIC_IRQCheck) return CBRET_NONE; @@ -247,16 +247,16 @@ run_block: // modifying instruction (like ret) or some nontrivial cpu state // changing instruction (for example switch to/from pmode), // or the maximum number of instructions to translate was reached -#if C_HEAVY_DEBUG - if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#if C_HEAVY_DEBUG || C_GDBSERVER + if (DEBUG_HeavyIsBreakpoint()) return DEBUG_debugCallback; #endif break; case BR_Cycles: // cycles went negative, return from the core to handle // external events, schedule the pic... -#if C_HEAVY_DEBUG - if (DEBUG_HeavyIsBreakpoint()) return debugCallback; +#if C_HEAVY_DEBUG || C_GDBSERVER + if (DEBUG_HeavyIsBreakpoint()) return DEBUG_debugCallback; #endif return CBRET_NONE; @@ -277,7 +277,7 @@ run_block: CPU_Cycles=1; return CPU_Core_Normal_Run(); -#if (C_DEBUG) +#if C_DEBUG || C_GDBSERVER case BR_OpcodeFull: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; diff --git a/src/cpu/core_full.cpp b/src/cpu/core_full.cpp index f70f8078..6398b2b8 100644 --- a/src/cpu/core_full.cpp +++ b/src/cpu/core_full.cpp @@ -64,12 +64,12 @@ typedef PhysPt EAPoint; Bits CPU_Core_Full_Run(void) { FullData inst; while (CPU_Cycles-->0) { -#if C_DEBUG - cycle_count++; -#if C_HEAVY_DEBUG +#if C_DEBUG || C_GDBSERVER + DEBUG_cycle_count++; +#if C_HEAVY_DEBUG || C_GDBSERVER if (DEBUG_HeavyIsBreakpoint()) { FillFlags(); - return debugCallback; + return DEBUG_debugCallback; }; #endif #endif diff --git a/src/cpu/core_full/op.h b/src/cpu/core_full/op.h index ea5f517d..37297f69 100644 --- a/src/cpu/core_full/op.h +++ b/src/cpu/core_full/op.h @@ -356,12 +356,12 @@ switch (inst.code.op) { CPU_JMP(true,inst_op2_d,inst_op1_d,GetIP()); continue; case O_INT: -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER FillFlags(); if (((inst.entry & 0xFF)==0xcc) && DEBUG_Breakpoint()) - return debugCallback; + return DEBUG_debugCallback; else if (DEBUG_IntBreakpoint(inst_op1_b)) - return debugCallback; + return DEBUG_debugCallback; #endif CPU_SW_Interrupt(inst_op1_b,GetIP()); continue; diff --git a/src/cpu/core_normal.cpp b/src/cpu/core_normal.cpp index 530e0c62..04d9b257 100644 --- a/src/cpu/core_normal.cpp +++ b/src/cpu/core_normal.cpp @@ -28,7 +28,7 @@ #include "fpu.h" #include "paging.h" -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER #include "debug.h" #endif @@ -49,7 +49,7 @@ #define SaveMd(off,val) mem_writed_inline(off,val) #endif -extern Bitu cycle_count; +extern Bitu DEBUG_cycle_count; #if C_FPU #define CPU_FPU 1 //Enable FPU escape instructions @@ -145,14 +145,14 @@ Bits CPU_Core_Normal_Run(void) { BaseDS=SegBase(ds); BaseSS=SegBase(ss); core.base_val_ds=ds; -#if C_DEBUG -#if C_HEAVY_DEBUG +#if C_DEBUG || C_GDBSERVER +#if C_HEAVY_DEBUG || C_GDBSERVER if (DEBUG_HeavyIsBreakpoint()) { FillFlags(); - return debugCallback; + return DEBUG_debugCallback; }; #endif - cycle_count++; + DEBUG_cycle_count++; #endif restart_opcode: switch (core.opcode_index+Fetchb()) { @@ -162,7 +162,7 @@ restart_opcode: #include "core_normal/prefix_66_0f.h" default: illegal_opcode: -#if C_DEBUG +#if C_DEBUG { Bitu len=(GETIP-reg_eip); LOADIP; diff --git a/src/cpu/core_normal/prefix_none.h b/src/cpu/core_normal/prefix_none.h index c732d1a6..22caed46 100644 --- a/src/cpu/core_normal/prefix_none.h +++ b/src/cpu/core_normal/prefix_none.h @@ -741,10 +741,10 @@ CPU_RET(false,0,GETIP); continue; CASE_B(0xcc) /* INT3 */ -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER FillFlags(); if (DEBUG_Breakpoint()) - return debugCallback; + return DEBUG_debugCallback; #endif CPU_SW_Interrupt_NoIOPLCheck(3,GETIP); #if CPU_TRAP_CHECK @@ -754,10 +754,10 @@ CASE_B(0xcd) /* INT Ib */ { Bit8u num=Fetchb(); -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER FillFlags(); if (DEBUG_IntBreakpoint(num)) { - return debugCallback; + return DEBUG_debugCallback; } #endif CPU_SW_Interrupt(num,GETIP); diff --git a/src/cpu/core_prefetch.cpp b/src/cpu/core_prefetch.cpp index e378968a..34abdf61 100644 --- a/src/cpu/core_prefetch.cpp +++ b/src/cpu/core_prefetch.cpp @@ -29,7 +29,7 @@ #include "fpu.h" #include "paging.h" -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER #include "debug.h" #endif @@ -50,7 +50,7 @@ #define SaveMd(off,val) mem_writed_inline(off,val) #endif -extern Bitu cycle_count; +extern Bitu DEBUG_cycle_count; #if C_FPU #define CPU_FPU 1 //Enable FPU escape instructions @@ -220,14 +220,14 @@ Bits CPU_Core_Prefetch_Run(void) { BaseDS=SegBase(ds); BaseSS=SegBase(ss); core.base_val_ds=ds; -#if C_DEBUG -#if C_HEAVY_DEBUG +#if C_DEBUG || C_GDBSERVER +#if C_HEAVY_DEBUG || C_GDBSERVER if (DEBUG_HeavyIsBreakpoint()) { FillFlags(); - return debugCallback; + return DEBUG_debugCallback; }; #endif - cycle_count++; + DEBUG_cycle_count++; #endif restart_opcode: Bit8u next_opcode=Fetchb(); diff --git a/src/cpu/core_simple.cpp b/src/cpu/core_simple.cpp index e7512270..9cc167d5 100644 --- a/src/cpu/core_simple.cpp +++ b/src/cpu/core_simple.cpp @@ -27,7 +27,7 @@ #include "pic.h" #include "fpu.h" -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER #include "debug.h" #endif @@ -41,7 +41,7 @@ #define SaveMw(off,val) mem_writew(off,val) #define SaveMd(off,val) mem_writed(off,val) -extern Bitu cycle_count; +extern Bitu DEBUG_cycle_count; #if C_FPU #define CPU_FPU 1 //Enable FPU escape instructions @@ -141,14 +141,14 @@ Bits CPU_Core_Simple_Run(void) { BaseDS=SegBase(ds); BaseSS=SegBase(ss); core.base_val_ds=ds; -#if C_DEBUG -#if C_HEAVY_DEBUG +#if C_DEBUG || C_GDBSERVER +#if C_HEAVY_DEBUG || C_GDBSERVER if (DEBUG_HeavyIsBreakpoint()) { FillFlags(); - return debugCallback; + return DEBUG_debugCallback; }; #endif - cycle_count++; + DEBUG_cycle_count++; #endif restart_opcode: switch (core.opcode_index+Fetchb()) { @@ -159,7 +159,7 @@ restart_opcode: #include "core_normal/prefix_66_0f.h" default: illegal_opcode: -#if C_DEBUG +#if C_DEBUG { Bitu len=(GETIP-reg_eip); LOADIP; diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index 9c9d5668..ecd2a94f 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -90,7 +90,7 @@ void CPU_Core_Dynrec_Cache_Close(void); * In non-debug mode dosbox doesn't do detection (and hence doesn't crash at * that point). (game might crash later due to the unhandled exception) */ -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER // #define CPU_CHECK_EXCEPT 1 // #define CPU_CHECK_IGNORE 1 /* Use CHECK_EXCEPT when something doesn't work to see if a exception is @@ -546,6 +546,10 @@ Bit8u lastint; void CPU_Interrupt(Bitu num,Bitu type,Bitu oldeip) { lastint=num; FillFlags(); +#if C_GDBSERVER + if (type == 0) + DEBUG_IrqBreakpoint(num); +#endif #if C_DEBUG switch (num) { case 0xcd: diff --git a/src/cpu/paging.cpp b/src/cpu/paging.cpp index 6ac9b664..60a7a719 100644 --- a/src/cpu/paging.cpp +++ b/src/cpu/paging.cpp @@ -130,7 +130,7 @@ static Bits PageFaultCore(void) { } return 0; } -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER Bitu DEBUG_EnableDebugger(void); #endif @@ -155,7 +155,7 @@ void PAGING_PageFault(PhysPt lin_addr,Bitu page_addr,Bitu faultcode) { cpu.mpl=3; CPU_Exception(EXCEPTION_PF,faultcode); -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER // DEBUG_EnableDebugger(); #endif DOSBOX_RunMachine(); diff --git a/src/debug/Makefile.am b/src/debug/Makefile.am index dfee7924..9090707b 100644 --- a/src/debug/Makefile.am +++ b/src/debug/Makefile.am @@ -1,4 +1,5 @@ AM_CPPFLAGS = -I$(top_srcdir)/include noinst_LIBRARIES = libdebug.a -libdebug_a_SOURCES = debug.cpp debug_gui.cpp debug_disasm.cpp debug_inc.h disasm_tables.h debug_win32.cpp \ No newline at end of file +libdebug_a_SOURCES = debug.cpp debug_gui.cpp debug_disasm.cpp debug_inc.h \ + disasm_tables.h debug_win32.cpp debug_log.cpp debug_helpers.cpp gdb_server.cpp poll.cpp poll.h diff --git a/src/debug/debug.cpp b/src/debug/debug.cpp index 7b76d573..fd2da533 100644 --- a/src/debug/debug.cpp +++ b/src/debug/debug.cpp @@ -63,18 +63,9 @@ static void DrawCode(void); static void DEBUG_RaiseTimerIrq(void); static void SaveMemory(Bitu seg, Bitu ofs1, Bit32u num); static void SaveMemoryBin(Bitu seg, Bitu ofs1, Bit32u num); -static void LogMCBS(void); -static void LogGDT(void); -static void LogLDT(void); -static void LogIDT(void); -static void LogPages(char* selname); -static void LogCPUInfo(void); static void OutputVecTable(char* filename); static void DrawVariables(void); -char* AnalyzeInstruction(char* inst, bool saveSelector); -Bit32u GetHexValue(char* str, char*& hex); - #if 0 class DebugPageHandler : public PageHandler { public: @@ -97,31 +88,16 @@ public: class DEBUG; DEBUG* pDebugcom = 0; -bool exitLoop = false; - - -// Heavy Debugging Vars for logging -#if C_HEAVY_DEBUG -static ofstream cpuLogFile; -static bool cpuLog = false; -static int cpuLogCounter = 0; -static int cpuLogType = 1; // log detail -static bool zeroProtect = false; -bool logHeavy = false; -#endif - - static struct { Bit32u eax,ebx,ecx,edx,esi,edi,ebp,esp,eip; } oldregs; -static char curSelectorName[3] = { 0,0,0 }; static Segment oldsegs[6]; static Bitu oldflags,oldcpucpl; DBGBlock dbg; -Bitu cycle_count; +Bitu DEBUG_cycle_count; static bool debugging; @@ -148,10 +124,6 @@ struct SCodeViewData { int inputPos; } codeViewData; -static Bit16u dataSeg; -static Bit32u dataOfs; -static bool showExtend = true; - static void ClearInputLine(void) { codeViewData.inputStr[0] = 0; codeViewData.inputPos = 0; @@ -162,110 +134,8 @@ static void ClearInputLine(void) { static list histBuff; static list::iterator histBuffPos = histBuff.end(); -/***********/ -/* Helpers */ -/***********/ - -Bit32u PhysMakeProt(Bit16u selector, Bit32u offset) -{ - Descriptor desc; - if (cpu.gdt.GetDescriptor(selector,desc)) return desc.GetBase()+offset; - return 0; -}; - -Bit32u GetAddress(Bit16u seg, Bit32u offset) -{ - if (seg==SegValue(cs)) return SegPhys(cs)+offset; - if (cpu.pmode && !(reg_flags & FLAG_VM)) { - Descriptor desc; - if (cpu.gdt.GetDescriptor(seg,desc)) return PhysMakeProt(seg,offset); - } - return (seg<<4)+offset; -} - -static char empty_sel[] = { ' ',' ',0 }; - -bool GetDescriptorInfo(char* selname, char* out1, char* out2) -{ - Bitu sel; - Descriptor desc; - - if (strstr(selname,"cs") || strstr(selname,"CS")) sel = SegValue(cs); - else if (strstr(selname,"ds") || strstr(selname,"DS")) sel = SegValue(ds); - else if (strstr(selname,"es") || strstr(selname,"ES")) sel = SegValue(es); - else if (strstr(selname,"fs") || strstr(selname,"FS")) sel = SegValue(fs); - else if (strstr(selname,"gs") || strstr(selname,"GS")) sel = SegValue(gs); - else if (strstr(selname,"ss") || strstr(selname,"SS")) sel = SegValue(ss); - else { - sel = GetHexValue(selname,selname); - if (*selname==0) selname=empty_sel; - } - if (cpu.gdt.GetDescriptor(sel,desc)) { - switch (desc.Type()) { - case DESC_TASK_GATE: - sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type); - sprintf(out2," TaskGate dpl : %01X %1X",desc.saved.gate.dpl,desc.saved.gate.p); - return true; - case DESC_LDT: - case DESC_286_TSS_A: - case DESC_286_TSS_B: - case DESC_386_TSS_A: - case DESC_386_TSS_B: - sprintf(out1,"%s: b:%08X type:%02X pag",selname,desc.GetBase(),desc.saved.seg.type); - sprintf(out2," l:%08X dpl : %01X %1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.g); - return true; - case DESC_286_CALL_GATE: - case DESC_386_CALL_GATE: - sprintf(out1,"%s: s:%08X type:%02X p params: %02X",selname,desc.GetSelector(),desc.saved.gate.type,desc.saved.gate.paramcount); - sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p); - return true; - case DESC_286_INT_GATE: - case DESC_286_TRAP_GATE: - case DESC_386_INT_GATE: - case DESC_386_TRAP_GATE: - sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type); - sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p); - return true; - } - sprintf(out1,"%s: b:%08X type:%02X parbg",selname,desc.GetBase(),desc.saved.seg.type); - sprintf(out2," l:%08X dpl : %01X %1X%1X%1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.r,desc.saved.seg.big,desc.saved.seg.g); - return true; - } else { - strcpy(out1," "); - strcpy(out2," "); - } - return false; -}; - -/********************/ -/* DebugVar stuff */ -/********************/ - -class CDebugVar -{ -public: - CDebugVar(char* _name, PhysPt _adr) { adr=_adr; safe_strncpy(name,_name,16); }; - - char* GetName(void) { return name; }; - PhysPt GetAdr (void) { return adr; }; - -private: - PhysPt adr; - char name[16]; - -public: - static void InsertVariable (char* name, PhysPt adr); - static CDebugVar* FindVar (PhysPt adr); - static void DeleteAll (); - static bool SaveVars (char* name); - static bool LoadVars (char* name); - - static std::list varList; -}; - std::list CDebugVar::varList; - /********************/ /* Breakpoint stuff */ /********************/ @@ -281,7 +151,7 @@ class CBreakpoint public: CBreakpoint(void); - void SetAddress (Bit16u seg, Bit32u off) { location = GetAddress(seg,off); type = BKPNT_PHYSICAL; segment = seg; offset = off; }; + void SetAddress (Bit16u seg, Bit32u off) { location = DEBUG_GetAddress(seg,off); type = BKPNT_PHYSICAL; segment = seg; offset = off; }; void SetAddress (PhysPt adr) { location = adr; type = BKPNT_PHYSICAL; }; void SetInt (Bit8u _intNr, Bit16u ah) { intNr = _intNr, ahValue = ah; type = BKPNT_INTERRUPT; }; void SetOnce (bool _once) { once = _once; }; @@ -414,7 +284,7 @@ void CBreakpoint::ActivateBreakpoints(PhysPt adr, bool activate) bool CBreakpoint::CheckBreakpoint(Bitu seg, Bitu off) // Checks if breakpoint is valid and should stop execution { - if ((ignoreAddressOnce!=0) && (GetAddress(seg,off)==ignoreAddressOnce)) { + if ((ignoreAddressOnce!=0) && (DEBUG_GetAddress(seg,off)==ignoreAddressOnce)) { ignoreAddressOnce = 0; return false; } else @@ -459,7 +329,7 @@ bool CBreakpoint::CheckBreakpoint(Bitu seg, Bitu off) Bitu address; if (bp->GetType()==BKPNT_MEMORY_LINEAR) address = bp->GetOffset(); - else address = GetAddress(bp->GetSegment(),bp->GetOffset()); + else address = DEBUG_GetAddress(bp->GetSegment(),bp->GetOffset()); Bit8u value=0; if (mem_readb_checked(address,&value)) return false; if (bp->GetValue() != value) { @@ -624,7 +494,7 @@ bool DEBUG_Breakpoint(void) /* First get the phyiscal address and check for a set Breakpoint */ if (!CBreakpoint::CheckBreakpoint(SegValue(cs),reg_eip)) return false; // Found. Breakpoint is valid - PhysPt where=GetAddress(SegValue(cs),reg_eip); + PhysPt where=DEBUG_GetAddress(SegValue(cs),reg_eip); CBreakpoint::ActivateBreakpoints(where,false); // Deactivate all breakpoints return true; }; @@ -632,7 +502,7 @@ bool DEBUG_Breakpoint(void) bool DEBUG_IntBreakpoint(Bit8u intNum) { /* First get the phyiscal address and check for a set Breakpoint */ - PhysPt where=GetAddress(SegValue(cs),reg_eip); + PhysPt where=DEBUG_GetAddress(SegValue(cs),reg_eip); if (!CBreakpoint::CheckIntBreakpoint(where,intNum,reg_ah)) return false; // Found. Breakpoint is valid CBreakpoint::ActivateBreakpoints(where,false); // Deactivate all breakpoints @@ -641,8 +511,8 @@ bool DEBUG_IntBreakpoint(Bit8u intNum) static bool StepOver() { - exitLoop = false; - PhysPt start=GetAddress(SegValue(cs),reg_eip); + DEBUG_exitLoop = false; + PhysPt start=DEBUG_GetAddress(SegValue(cs),reg_eip); char dline[200];Bitu size; size=DasmI386(dline, start, reg_eip, cpu.code.big); @@ -663,8 +533,8 @@ bool DEBUG_ExitLoop(void) DrawVariables(); #endif - if (exitLoop) { - exitLoop = false; + if (DEBUG_exitLoop) { + DEBUG_exitLoop = false; return true; } return false; @@ -677,15 +547,15 @@ bool DEBUG_ExitLoop(void) static void DrawData(void) { Bit8u ch; - Bit32u add = dataOfs; + Bit32u add = DEBUG_dataOfs; Bit32u address; /* Data win */ for (int y=0; y<8; y++) { // Address - if (add<0x10000) mvwprintw (dbg.win_data,1+y,0,"%04X:%04X ",dataSeg,add); - else mvwprintw (dbg.win_data,1+y,0,"%04X:%08X ",dataSeg,add); + if (add<0x10000) mvwprintw (dbg.win_data,1+y,0,"%04X:%04X ",DEBUG_dataSeg,add); + else mvwprintw (dbg.win_data,1+y,0,"%04X:%08X ",DEBUG_dataSeg,add); for (int x=0; x<16; x++) { - address = GetAddress(dataSeg,add); + address = DEBUG_GetAddress(DEBUG_dataSeg,add); if (mem_readb_checked(address,&ch)) ch=0; mvwprintw (dbg.win_data,1+y,14+3*x,"%02X",ch); if (ch<32 || !isprint(*reinterpret_cast(&ch))) ch='.'; @@ -757,22 +627,22 @@ static void DrawRegisters(void) { mvwprintw(dbg.win_reg,0,76,"Real"); // Selector info, if available - if ((cpu.pmode) && curSelectorName[0]) { + if ((cpu.pmode) && DEBUG_curSelectorName[0]) { char out1[200], out2[200]; - GetDescriptorInfo(curSelectorName,out1,out2); + DEBUG_GetDescriptorInfo(DEBUG_curSelectorName,out1,out2); mvwprintw(dbg.win_reg,2,28,out1); mvwprintw(dbg.win_reg,3,28,out2); } wattrset(dbg.win_reg,0); - mvwprintw(dbg.win_reg,3,60,"%u ",cycle_count); + mvwprintw(dbg.win_reg,3,60,"%u ",DEBUG_cycle_count); wrefresh(dbg.win_reg); }; static void DrawCode(void) { bool saveSel; Bit32u disEIP = codeViewData.useEIP; - PhysPt start = GetAddress(codeViewData.useCS,codeViewData.useEIP); + PhysPt start = DEBUG_GetAddress(codeViewData.useCS,codeViewData.useEIP); char dline[200];Bitu size;Bitu c; static char line20[21] = " "; @@ -822,7 +692,7 @@ static void DrawCode(void) { char empty_res[] = { 0 }; char* res = empty_res; - if (showExtend) res = AnalyzeInstruction(dline, saveSel); + if (DEBUG_showExtend) res = DEBUG_AnalyzeInstruction(dline, saveSel); // Spacepad it up to 28 characters size_t dline_len = strlen(dline); if(dline_len < 28) for (c = dline_len; c < 28;c++) dline[c] = ' '; dline[28] = 0; @@ -882,86 +752,42 @@ static void SetCodeWinStart() /* User input */ /********************/ -Bit32u GetHexValue(char* str, char*& hex) -{ - Bit32u value = 0; - Bit32u regval = 0; - hex = str; - while (*hex==' ') hex++; - if (strstr(hex,"EAX")==hex) { hex+=3; regval = reg_eax; }; - if (strstr(hex,"EBX")==hex) { hex+=3; regval = reg_ebx; }; - if (strstr(hex,"ECX")==hex) { hex+=3; regval = reg_ecx; }; - if (strstr(hex,"EDX")==hex) { hex+=3; regval = reg_edx; }; - if (strstr(hex,"ESI")==hex) { hex+=3; regval = reg_esi; }; - if (strstr(hex,"EDI")==hex) { hex+=3; regval = reg_edi; }; - if (strstr(hex,"EBP")==hex) { hex+=3; regval = reg_ebp; }; - if (strstr(hex,"ESP")==hex) { hex+=3; regval = reg_esp; }; - if (strstr(hex,"EIP")==hex) { hex+=3; regval = reg_eip; }; - if (strstr(hex,"AX")==hex) { hex+=2; regval = reg_ax; }; - if (strstr(hex,"BX")==hex) { hex+=2; regval = reg_bx; }; - if (strstr(hex,"CX")==hex) { hex+=2; regval = reg_cx; }; - if (strstr(hex,"DX")==hex) { hex+=2; regval = reg_dx; }; - if (strstr(hex,"SI")==hex) { hex+=2; regval = reg_si; }; - if (strstr(hex,"DI")==hex) { hex+=2; regval = reg_di; }; - if (strstr(hex,"BP")==hex) { hex+=2; regval = reg_bp; }; - if (strstr(hex,"SP")==hex) { hex+=2; regval = reg_sp; }; - if (strstr(hex,"IP")==hex) { hex+=2; regval = reg_ip; }; - if (strstr(hex,"CS")==hex) { hex+=2; regval = SegValue(cs); }; - if (strstr(hex,"DS")==hex) { hex+=2; regval = SegValue(ds); }; - if (strstr(hex,"ES")==hex) { hex+=2; regval = SegValue(es); }; - if (strstr(hex,"FS")==hex) { hex+=2; regval = SegValue(fs); }; - if (strstr(hex,"GS")==hex) { hex+=2; regval = SegValue(gs); }; - if (strstr(hex,"SS")==hex) { hex+=2; regval = SegValue(ss); }; - - while (*hex) { - if ((*hex>='0') && (*hex<='9')) value = (value<<4)+*hex-'0'; - else if ((*hex>='A') && (*hex<='F')) value = (value<<4)+*hex-'A'+10; - else { - if(*hex == '+') {hex++;return regval + value + GetHexValue(hex,hex); }; - if(*hex == '-') {hex++;return regval + value - GetHexValue(hex,hex); }; - break; // No valid char - } - hex++; - }; - return regval + value; -}; - bool ChangeRegister(char* str) { char* hex = str; while (*hex==' ') hex++; - if (strstr(hex,"EAX")==hex) { hex+=3; reg_eax = GetHexValue(hex,hex); } else - if (strstr(hex,"EBX")==hex) { hex+=3; reg_ebx = GetHexValue(hex,hex); } else - if (strstr(hex,"ECX")==hex) { hex+=3; reg_ecx = GetHexValue(hex,hex); } else - if (strstr(hex,"EDX")==hex) { hex+=3; reg_edx = GetHexValue(hex,hex); } else - if (strstr(hex,"ESI")==hex) { hex+=3; reg_esi = GetHexValue(hex,hex); } else - if (strstr(hex,"EDI")==hex) { hex+=3; reg_edi = GetHexValue(hex,hex); } else - if (strstr(hex,"EBP")==hex) { hex+=3; reg_ebp = GetHexValue(hex,hex); } else - if (strstr(hex,"ESP")==hex) { hex+=3; reg_esp = GetHexValue(hex,hex); } else - if (strstr(hex,"EIP")==hex) { hex+=3; reg_eip = GetHexValue(hex,hex); } else - if (strstr(hex,"AX")==hex) { hex+=2; reg_ax = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"BX")==hex) { hex+=2; reg_bx = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"CX")==hex) { hex+=2; reg_cx = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"DX")==hex) { hex+=2; reg_dx = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"SI")==hex) { hex+=2; reg_si = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"DI")==hex) { hex+=2; reg_di = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"BP")==hex) { hex+=2; reg_bp = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"SP")==hex) { hex+=2; reg_sp = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"IP")==hex) { hex+=2; reg_ip = (Bit16u)GetHexValue(hex,hex); } else - if (strstr(hex,"CS")==hex) { hex+=2; SegSet16(cs,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"DS")==hex) { hex+=2; SegSet16(ds,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"ES")==hex) { hex+=2; SegSet16(es,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"FS")==hex) { hex+=2; SegSet16(fs,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"GS")==hex) { hex+=2; SegSet16(gs,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"SS")==hex) { hex+=2; SegSet16(ss,(Bit16u)GetHexValue(hex,hex)); } else - if (strstr(hex,"AF")==hex) { hex+=2; SETFLAGBIT(AF,GetHexValue(hex,hex)); } else - if (strstr(hex,"CF")==hex) { hex+=2; SETFLAGBIT(CF,GetHexValue(hex,hex)); } else - if (strstr(hex,"DF")==hex) { hex+=2; SETFLAGBIT(DF,GetHexValue(hex,hex)); } else - if (strstr(hex,"IF")==hex) { hex+=2; SETFLAGBIT(IF,GetHexValue(hex,hex)); } else - if (strstr(hex,"OF")==hex) { hex+=2; SETFLAGBIT(OF,GetHexValue(hex,hex)); } else - if (strstr(hex,"ZF")==hex) { hex+=2; SETFLAGBIT(ZF,GetHexValue(hex,hex)); } else - if (strstr(hex,"PF")==hex) { hex+=2; SETFLAGBIT(PF,GetHexValue(hex,hex)); } else - if (strstr(hex,"SF")==hex) { hex+=2; SETFLAGBIT(SF,GetHexValue(hex,hex)); } else + if (strstr(hex,"EAX")==hex) { hex+=3; reg_eax = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"EBX")==hex) { hex+=3; reg_ebx = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"ECX")==hex) { hex+=3; reg_ecx = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"EDX")==hex) { hex+=3; reg_edx = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"ESI")==hex) { hex+=3; reg_esi = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"EDI")==hex) { hex+=3; reg_edi = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"EBP")==hex) { hex+=3; reg_ebp = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"ESP")==hex) { hex+=3; reg_esp = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"EIP")==hex) { hex+=3; reg_eip = DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"AX")==hex) { hex+=2; reg_ax = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"BX")==hex) { hex+=2; reg_bx = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"CX")==hex) { hex+=2; reg_cx = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"DX")==hex) { hex+=2; reg_dx = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"SI")==hex) { hex+=2; reg_si = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"DI")==hex) { hex+=2; reg_di = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"BP")==hex) { hex+=2; reg_bp = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"SP")==hex) { hex+=2; reg_sp = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"IP")==hex) { hex+=2; reg_ip = (Bit16u)DEBUG_GetHexValue(hex,hex); } else + if (strstr(hex,"CS")==hex) { hex+=2; SegSet16(cs,(Bit16u)DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"DS")==hex) { hex+=2; SegSet16(ds,(Bit16u)DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"ES")==hex) { hex+=2; SegSet16(es,(Bit16u)DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"FS")==hex) { hex+=2; SegSet16(fs,(Bit16u)DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"GS")==hex) { hex+=2; SegSet16(gs,(Bit16u)DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"SS")==hex) { hex+=2; SegSet16(ss,(Bit16u)DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"AF")==hex) { hex+=2; SETFLAGBIT(AF,DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"CF")==hex) { hex+=2; SETFLAGBIT(CF,DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"DF")==hex) { hex+=2; SETFLAGBIT(DF,DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"IF")==hex) { hex+=2; SETFLAGBIT(IF,DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"OF")==hex) { hex+=2; SETFLAGBIT(OF,DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"ZF")==hex) { hex+=2; SETFLAGBIT(ZF,DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"PF")==hex) { hex+=2; SETFLAGBIT(PF,DEBUG_GetHexValue(hex,hex)); } else + if (strstr(hex,"SF")==hex) { hex+=2; SETFLAGBIT(SF,DEBUG_GetHexValue(hex,hex)); } else { return false; }; return true; }; @@ -982,24 +808,24 @@ bool ParseCommand(char* str) { found = const_cast(s_found.c_str()); if (command == "MEMDUMP") { // Dump memory to file - Bit16u seg = (Bit16u)GetHexValue(found,found); found++; - Bit32u ofs = GetHexValue(found,found); found++; - Bit32u num = GetHexValue(found,found); found++; + Bit16u seg = (Bit16u)DEBUG_GetHexValue(found,found); found++; + Bit32u ofs = DEBUG_GetHexValue(found,found); found++; + Bit32u num = DEBUG_GetHexValue(found,found); found++; SaveMemory(seg,ofs,num); return true; }; if (command == "MEMDUMPBIN") { // Dump memory to file bineary - Bit16u seg = (Bit16u)GetHexValue(found,found); found++; - Bit32u ofs = GetHexValue(found,found); found++; - Bit32u num = GetHexValue(found,found); found++; + Bit16u seg = (Bit16u)DEBUG_GetHexValue(found,found); found++; + Bit32u ofs = DEBUG_GetHexValue(found,found); found++; + Bit32u num = DEBUG_GetHexValue(found,found); found++; SaveMemoryBin(seg,ofs,num); return true; }; if (command == "IV") { // Insert variable - Bit16u seg = (Bit16u)GetHexValue(found,found); found++; - Bit32u ofs = (Bit16u)GetHexValue(found,found); found++; + Bit16u seg = (Bit16u)DEBUG_GetHexValue(found,found); found++; + Bit32u ofs = (Bit16u)DEBUG_GetHexValue(found,found); found++; char name[16]; for (int i=0; i<16; i++) { if (found[i] && (found[i]!=' ')) name[i] = found[i]; @@ -1009,7 +835,7 @@ bool ParseCommand(char* str) { if(!name[0]) return false; DEBUG_ShowMsg("DEBUG: Created debug var %s at %04X:%04X\n",name,seg,ofs); - CDebugVar::InsertVariable(name,GetAddress(seg,ofs)); + CDebugVar::InsertVariable(name,DEBUG_GetAddress(seg,ofs)); return true; }; @@ -1043,15 +869,15 @@ bool ParseCommand(char* str) { }; if (command == "SM") { // Set memory with following values - Bit16u seg = (Bit16u)GetHexValue(found,found); found++; - Bit32u ofs = GetHexValue(found,found); found++; + Bit16u seg = (Bit16u)DEBUG_GetHexValue(found,found); found++; + Bit32u ofs = DEBUG_GetHexValue(found,found); found++; Bit16u count = 0; while (*found) { while (*found==' ') found++; if (*found) { - Bit8u value = (Bit8u)GetHexValue(found,found); + Bit8u value = (Bit8u)DEBUG_GetHexValue(found,found); if(*found) found++; - mem_writeb_checked(GetAddress(seg,ofs+count),value); + mem_writeb_checked(DEBUG_GetAddress(seg,ofs+count),value); count++; } }; @@ -1060,8 +886,8 @@ bool ParseCommand(char* str) { }; if (command == "BP") { // Add new breakpoint - Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":" - Bit32u ofs = GetHexValue(found,found); + Bit16u seg = (Bit16u)DEBUG_GetHexValue(found,found);found++; // skip ":" + Bit32u ofs = DEBUG_GetHexValue(found,found); CBreakpoint::AddBreakpoint(seg,ofs,false); DEBUG_ShowMsg("DEBUG: Set breakpoint at %04X:%04X\n",seg,ofs); return true; @@ -1070,16 +896,16 @@ bool ParseCommand(char* str) { #if C_HEAVY_DEBUG if (command == "BPM") { // Add new breakpoint - Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":" - Bit32u ofs = GetHexValue(found,found); + Bit16u seg = (Bit16u)DEBUG_GetHexValue(found,found);found++; // skip ":" + Bit32u ofs = DEBUG_GetHexValue(found,found); CBreakpoint::AddMemBreakpoint(seg,ofs); DEBUG_ShowMsg("DEBUG: Set memory breakpoint at %04X:%04X\n",seg,ofs); return true; }; if (command == "BPPM") { // Add new breakpoint - Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":" - Bit32u ofs = GetHexValue(found,found); + Bit16u seg = (Bit16u)DEBUG_GetHexValue(found,found);found++; // skip ":" + Bit32u ofs = DEBUG_GetHexValue(found,found); CBreakpoint* bp = CBreakpoint::AddMemBreakpoint(seg,ofs); if (bp) { bp->SetType(BKPNT_MEMORY_PROT); @@ -1089,7 +915,7 @@ bool ParseCommand(char* str) { }; if (command == "BPLM") { // Add new breakpoint - Bit32u ofs = GetHexValue(found,found); + Bit32u ofs = DEBUG_GetHexValue(found,found); CBreakpoint* bp = CBreakpoint::AddMemBreakpoint(0,ofs); if (bp) bp->SetType(BKPNT_MEMORY_LINEAR); DEBUG_ShowMsg("DEBUG: Set linear memory breakpoint at %08X\n",ofs); @@ -1099,9 +925,9 @@ bool ParseCommand(char* str) { #endif if (command == "BPINT") { // Add Interrupt Breakpoint - Bit8u intNr = (Bit8u)GetHexValue(found,found); + Bit8u intNr = (Bit8u)DEBUG_GetHexValue(found,found); bool all = !(*found);found++; - Bit8u valAH = (Bit8u)GetHexValue(found,found); + Bit8u valAH = (Bit8u)DEBUG_GetHexValue(found,found); if ((valAH==0x00) && (*found=='*' || all)) { CBreakpoint::AddIntBreakpoint(intNr,BPINT_ALL,false); DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X\n",intNr); @@ -1120,7 +946,7 @@ bool ParseCommand(char* str) { }; if (command == "BPDEL") { // Delete Breakpoints - Bit8u bpNr = (Bit8u)GetHexValue(found,found); + Bit8u bpNr = (Bit8u)DEBUG_GetHexValue(found,found); if ((bpNr==0x00) && (*found=='*')) { // Delete all CBreakpoint::DeleteAll(); DEBUG_ShowMsg("DEBUG: Breakpoints deleted.\n"); @@ -1132,8 +958,8 @@ bool ParseCommand(char* str) { }; if (command == "C") { // Set code overview - Bit16u codeSeg = (Bit16u)GetHexValue(found,found); found++; - Bit32u codeOfs = GetHexValue(found,found); + Bit16u codeSeg = (Bit16u)DEBUG_GetHexValue(found,found); found++; + Bit32u codeOfs = DEBUG_GetHexValue(found,found); DEBUG_ShowMsg("DEBUG: Set code overview to %04X:%04X\n",codeSeg,codeOfs); codeViewData.useCS = codeSeg; codeViewData.useEIP = codeOfs; @@ -1142,40 +968,40 @@ bool ParseCommand(char* str) { }; if (command == "D") { // Set data overview - dataSeg = (Bit16u)GetHexValue(found,found); found++; - dataOfs = GetHexValue(found,found); - DEBUG_ShowMsg("DEBUG: Set data overview to %04X:%04X\n",dataSeg,dataOfs); + DEBUG_dataSeg = (Bit16u)DEBUG_GetHexValue(found,found); found++; + DEBUG_dataOfs = DEBUG_GetHexValue(found,found); + DEBUG_ShowMsg("DEBUG: Set data overview to %04X:%04X\n",DEBUG_dataSeg,DEBUG_dataOfs); return true; }; #if C_HEAVY_DEBUG if (command == "LOG") { // Create Cpu normal log file - cpuLogType = 1; + DEBUG_cpuLogType = 1; command = "logcode"; } if (command == "LOGS") { // Create Cpu short log file - cpuLogType = 0; + DEBUG_cpuLogType = 0; command = "logcode"; } if (command == "LOGL") { // Create Cpu long log file - cpuLogType = 2; + DEBUG_cpuLogType = 2; command = "logcode"; } if (command == "logcode") { //Shared code between all logs DEBUG_ShowMsg("DEBUG: Starting log\n"); - cpuLogFile.open("LOGCPU.TXT"); - if (!cpuLogFile.is_open()) { + DEBUG_cpuLogFile.open("LOGCPU.TXT"); + if (!DEBUG_cpuLogFile.is_open()) { DEBUG_ShowMsg("DEBUG: Logfile couldn't be created.\n"); return false; } //Initialize log object - cpuLogFile << hex << noshowbase << setfill('0') << uppercase; - cpuLog = true; - cpuLogCounter = GetHexValue(found,found); + DEBUG_cpuLogFile << hex << noshowbase << setfill('0') << uppercase; + DEBUG_cpuLog = true; + DEBUG_cpuLogCounter = DEBUG_GetHexValue(found,found); debugging = false; CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); @@ -1187,7 +1013,7 @@ bool ParseCommand(char* str) { #endif if (command == "INTT") { //trace int. - Bit8u intNr = (Bit8u)GetHexValue(found,found); + Bit8u intNr = (Bit8u)DEBUG_GetHexValue(found,found); DEBUG_ShowMsg("DEBUG: Tracing INT %02X\n",intNr); CPU_HW_Interrupt(intNr); SetCodeWinStart(); @@ -1195,7 +1021,7 @@ bool ParseCommand(char* str) { }; if (command == "INT") { // start int. - Bit8u intNr = (Bit8u)GetHexValue(found,found); + Bit8u intNr = (Bit8u)DEBUG_GetHexValue(found,found); DEBUG_ShowMsg("DEBUG: Starting INT %02X\n",intNr); CBreakpoint::AddBreakpoint(SegValue(cs),reg_eip, true); CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip-1,true); @@ -1209,26 +1035,26 @@ bool ParseCommand(char* str) { if (command == "SELINFO") { while (found[0] == ' ') found++; char out1[200],out2[200]; - GetDescriptorInfo(found,out1,out2); + DEBUG_GetDescriptorInfo(found,out1,out2); DEBUG_ShowMsg("SelectorInfo %s:\n%s\n%s\n",found,out1,out2); return true; }; if (command == "DOS") { stream >> command; - if (command == "MCBS") LogMCBS(); + if (command == "MCBS") DEBUG_LogMCBS(); return true; } - if (command == "GDT") {LogGDT(); return true;} + if (command == "GDT") {DEBUG_LogGDT(); return true;} - if (command == "LDT") {LogLDT(); return true;} + if (command == "LDT") {DEBUG_LogLDT(); return true;} - if (command == "IDT") {LogIDT(); return true;} + if (command == "IDT") {DEBUG_LogIDT(); return true;} - if (command == "PAGING") {LogPages(found); return true;} + if (command == "PAGING") {DEBUG_LogPages(found); return true;} - if (command == "CPU") {LogCPUInfo(); return true;} + if (command == "CPU") {DEBUG_LogCPUInfo(); return true;} if (command == "INTVEC") { if (found[0] != 0) { @@ -1239,7 +1065,7 @@ bool ParseCommand(char* str) { if (command == "INTHAND") { if (found[0] != 0) { - Bit8u intNr = (Bit8u)GetHexValue(found,found); + Bit8u intNr = (Bit8u)DEBUG_GetHexValue(found,found); DEBUG_ShowMsg("DEBUG: Set code overview to interrupt handler %X\n",intNr); codeViewData.useCS = mem_readw(intNr*4+2); codeViewData.useEIP = mem_readw(intNr*4); @@ -1249,7 +1075,7 @@ bool ParseCommand(char* str) { }; if(command == "EXTEND") { //Toggle additional data. - showExtend = !showExtend; + DEBUG_showExtend = !DEBUG_showExtend; return true; }; @@ -1262,14 +1088,14 @@ bool ParseCommand(char* str) { #if C_HEAVY_DEBUG if (command == "HEAVYLOG") { // Create Cpu log file - logHeavy = !logHeavy; - DEBUG_ShowMsg("DEBUG: Heavy cpu logging %s.\n",logHeavy?"on":"off"); + DEBUG_logHeavy = !DEBUG_logHeavy; + DEBUG_ShowMsg("DEBUG: Heavy cpu logging %s.\n",DEBUG_logHeavy?"on":"off"); return true; }; if (command == "ZEROPROTECT") { //toggle zero protection - zeroProtect = !zeroProtect; - DEBUG_ShowMsg("DEBUG: Zero code execution protection %s.\n",zeroProtect?"on":"off"); + DEBUG_zeroProtect = !DEBUG_zeroProtect; + DEBUG_ShowMsg("DEBUG: Zero code execution protection %s.\n",DEBUG_zeroProtect?"on":"off"); return true; }; @@ -1335,181 +1161,6 @@ bool ParseCommand(char* str) { return false; }; -char* AnalyzeInstruction(char* inst, bool saveSelector) { - static char result[256]; - - char instu[256]; - char prefix[3]; - Bit16u seg; - - strcpy(instu,inst); - upcase(instu); - - result[0] = 0; - char* pos = strchr(instu,'['); - if (pos) { - // Segment prefix ? - if (*(pos-1)==':') { - char* segpos = pos-3; - prefix[0] = tolower(*segpos); - prefix[1] = tolower(*(segpos+1)); - prefix[2] = 0; - seg = (Bit16u)GetHexValue(segpos,segpos); - } else { - if (strstr(pos,"SP") || strstr(pos,"BP")) { - seg = SegValue(ss); - strcpy(prefix,"ss"); - } else { - seg = SegValue(ds); - strcpy(prefix,"ds"); - }; - }; - - pos++; - Bit32u adr = GetHexValue(pos,pos); - while (*pos!=']') { - if (*pos=='+') { - pos++; - adr += GetHexValue(pos,pos); - } else if (*pos=='-') { - pos++; - adr -= GetHexValue(pos,pos); - } else - pos++; - }; - Bit32u address = GetAddress(seg,adr); - if (!(get_tlb_readhandler(address)->flags & PFLAG_INIT)) { - static char outmask[] = "%s:[%04X]=%02X"; - - if (cpu.pmode) outmask[6] = '8'; - switch (DasmLastOperandSize()) { - case 8 : { Bit8u val = mem_readb(address); - outmask[12] = '2'; - sprintf(result,outmask,prefix,adr,val); - } break; - case 16: { Bit16u val = mem_readw(address); - outmask[12] = '4'; - sprintf(result,outmask,prefix,adr,val); - } break; - case 32: { Bit32u val = mem_readd(address); - outmask[12] = '8'; - sprintf(result,outmask,prefix,adr,val); - } break; - } - } else { - sprintf(result,"[illegal]"); - } - // Variable found ? - CDebugVar* var = CDebugVar::FindVar(address); - if (var) { - // Replace occurence - char* pos1 = strchr(inst,'['); - char* pos2 = strchr(inst,']'); - if (pos1 && pos2) { - char temp[256]; - strcpy(temp,pos2); // save end - pos1++; *pos1 = 0; // cut after '[' - strcat(inst,var->GetName()); // add var name - strcat(inst,temp); // add end - }; - }; - // show descriptor info, if available - if ((cpu.pmode) && saveSelector) { - strcpy(curSelectorName,prefix); - }; - }; - // If it is a callback add additional info - pos = strstr(inst,"callback"); - if (pos) { - pos += 9; - Bitu nr = GetHexValue(pos,pos); - const char* descr = CALLBACK_GetDescription(nr); - if (descr) { - strcat(inst," ("); strcat(inst,descr); strcat(inst,")"); - } - }; - // Must be a jump - if (instu[0] == 'J') - { - bool jmp = false; - switch (instu[1]) { - case 'A' : { jmp = (get_CF()?false:true) && (get_ZF()?false:true); // JA - } break; - case 'B' : { if (instu[2] == 'E') { - jmp = (get_CF()?true:false) || (get_ZF()?true:false); // JBE - } else { - jmp = get_CF()?true:false; // JB - } - } break; - case 'C' : { if (instu[2] == 'X') { - jmp = reg_cx == 0; // JCXZ - } else { - jmp = get_CF()?true:false; // JC - } - } break; - case 'E' : { jmp = get_ZF()?true:false; // JE - } break; - case 'G' : { if (instu[2] == 'E') { - jmp = (get_SF()?true:false)==(get_OF()?true:false); // JGE - } else { - jmp = (get_ZF()?false:true) && ((get_SF()?true:false)==(get_OF()?true:false)); // JG - } - } break; - case 'L' : { if (instu[2] == 'E') { - jmp = (get_ZF()?true:false) || ((get_SF()?true:false)!=(get_OF()?true:false)); // JLE - } else { - jmp = (get_SF()?true:false)!=(get_OF()?true:false); // JL - } - } break; - case 'M' : { jmp = true; // JMP - } break; - case 'N' : { switch (instu[2]) { - case 'B' : - case 'C' : { jmp = get_CF()?false:true; // JNB / JNC - } break; - case 'E' : { jmp = get_ZF()?false:true; // JNE - } break; - case 'O' : { jmp = get_OF()?false:true; // JNO - } break; - case 'P' : { jmp = get_PF()?false:true; // JNP - } break; - case 'S' : { jmp = get_SF()?false:true; // JNS - } break; - case 'Z' : { jmp = get_ZF()?false:true; // JNZ - } break; - } - } break; - case 'O' : { jmp = get_OF()?true:false; // JO - } break; - case 'P' : { if (instu[2] == 'O') { - jmp = get_PF()?false:true; // JPO - } else { - jmp = get_SF()?true:false; // JP / JPE - } - } break; - case 'S' : { jmp = get_SF()?true:false; // JS - } break; - case 'Z' : { jmp = get_ZF()?true:false; // JZ - } break; - } - if (jmp) { - pos = strchr(instu,'$'); - if (pos) { - pos = strchr(instu,'+'); - if (pos) { - strcpy(result,"(down)"); - } else { - strcpy(result,"(up)"); - } - } - } else { - sprintf(result,"(no jmp)"); - } - } - return result; -}; - - Bit32u DEBUG_CheckKeys(void) { Bits ret=0; int key=getch(); @@ -1548,36 +1199,36 @@ Bit32u DEBUG_CheckKeys(void) { switch(toupper(key)) { case 'D' : // ALT - D: DS:SI - dataSeg = SegValue(ds); - if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_esi; - else dataOfs = reg_si; + DEBUG_dataSeg = SegValue(ds); + if (cpu.pmode && !(reg_flags & FLAG_VM)) DEBUG_dataOfs = reg_esi; + else DEBUG_dataOfs = reg_si; break; case 'E' : //ALT - E: es:di - dataSeg = SegValue(es); - if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_edi; - else dataOfs = reg_di; + DEBUG_dataSeg = SegValue(es); + if (cpu.pmode && !(reg_flags & FLAG_VM)) DEBUG_dataOfs = reg_edi; + else DEBUG_dataOfs = reg_di; break; case 'X': //ALT - X: ds:dx - dataSeg = SegValue(ds); - if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_edx; - else dataOfs = reg_dx; + DEBUG_dataSeg = SegValue(ds); + if (cpu.pmode && !(reg_flags & FLAG_VM)) DEBUG_dataOfs = reg_edx; + else DEBUG_dataOfs = reg_dx; break; case 'B' : //ALT -B: es:bx - dataSeg = SegValue(es); - if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_ebx; - else dataOfs = reg_bx; + DEBUG_dataSeg = SegValue(es); + if (cpu.pmode && !(reg_flags & FLAG_VM)) DEBUG_dataOfs = reg_ebx; + else DEBUG_dataOfs = reg_bx; break; case 'S': //ALT - S: ss:sp - dataSeg = SegValue(ss); - if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_esp; - else dataOfs = reg_sp; + DEBUG_dataSeg = SegValue(ss); + if (cpu.pmode && !(reg_flags & FLAG_VM)) DEBUG_dataOfs = reg_esp; + else DEBUG_dataOfs = reg_sp; break; default: break; } break; - case KEY_PPAGE : dataOfs -= 16; break; - case KEY_NPAGE : dataOfs += 16; break; + case KEY_PPAGE : DEBUG_dataOfs -= 16; break; + case KEY_NPAGE : DEBUG_dataOfs += 16; break; case KEY_DOWN: // down if (codeViewData.cursorPos<9) codeViewData.cursorPos++; @@ -1592,7 +1243,7 @@ Bit32u DEBUG_CheckKeys(void) { Bit32u newEIP = codeViewData.useEIP - 1; if(codeViewData.useEIP) { for (; bytes < 10; bytes++) { - PhysPt start = GetAddress(codeViewData.useCS,newEIP); + PhysPt start = DEBUG_GetAddress(codeViewData.useCS,newEIP); size = DasmI386(dline, start, newEIP, cpu.code.big); if(codeViewData.useEIP == newEIP+size) break; newEIP--; @@ -1645,7 +1296,7 @@ Bit32u DEBUG_CheckKeys(void) { DOSBOX_SetNormalLoop(); break; case KEY_F(9): // Set/Remove Breakpoint - { PhysPt ptr = GetAddress(codeViewData.cursorSeg,codeViewData.cursorOfs); + { PhysPt ptr = DEBUG_GetAddress(codeViewData.cursorSeg,codeViewData.cursorOfs); if (CBreakpoint::IsBreakpoint(ptr)) { CBreakpoint::DeleteBreakpoint(ptr); DEBUG_ShowMsg("DEBUG: Breakpoint deletion success.\n"); @@ -1659,7 +1310,7 @@ Bit32u DEBUG_CheckKeys(void) { case KEY_F(10): // Step over inst if (StepOver()) return 0; else { - exitLoop = false; + DEBUG_exitLoop = false; skipFirstInstruction = true; // for heavy debugger CPU_Cycles = 1; ret=(*cpudecoder)(); @@ -1668,7 +1319,7 @@ Bit32u DEBUG_CheckKeys(void) { } break; case KEY_F(11): // trace into - exitLoop = false; + DEBUG_exitLoop = false; skipFirstInstruction = true; // for heavy debugger CPU_Cycles = 1; ret = (*cpudecoder)(); @@ -1730,7 +1381,7 @@ Bit32u DEBUG_CheckKeys(void) { else ret = (*CallBack_Handlers[ret])(); if (ret) { - exitLoop=true; + DEBUG_exitLoop=true; CPU_Cycles=CPU_CycleLeft=0; return ret; } @@ -1785,239 +1436,7 @@ static void DEBUG_RaiseTimerIrq(void) { PIC_ActivateIRQ(0); } -// Display the content of the MCB chain starting with the MCB at the specified segment. -static void LogMCBChain(Bit16u mcb_segment) { - DOS_MCB mcb(mcb_segment); - char filename[9]; // 8 characters plus a terminating NUL - const char *psp_seg_note; - PhysPt dataAddr = PhysMake(dataSeg,dataOfs);// location being viewed in the "Data Overview" - // loop forever, breaking out of the loop once we've processed the last MCB - while (true) { - // verify that the type field is valid - if (mcb.GetType()!=0x4d && mcb.GetType()!=0x5a) { - LOG(LOG_MISC,LOG_ERROR)("MCB chain broken at %04X:0000!",mcb_segment); - return; - } - - mcb.GetFileName(filename); - - // some PSP segment values have special meanings - switch (mcb.GetPSPSeg()) { - case MCB_FREE: - psp_seg_note = "(free)"; - break; - case MCB_DOS: - psp_seg_note = "(DOS)"; - break; - default: - psp_seg_note = ""; - } - - LOG(LOG_MISC,LOG_ERROR)(" %04X %12u %04X %-7s %s",mcb_segment,mcb.GetSize() << 4,mcb.GetPSPSeg(), psp_seg_note, filename); - - // print a message if dataAddr is within this MCB's memory range - PhysPt mcbStartAddr = PhysMake(mcb_segment+1,0); - PhysPt mcbEndAddr = PhysMake(mcb_segment+1+mcb.GetSize(),0); - if (dataAddr >= mcbStartAddr && dataAddr < mcbEndAddr) { - LOG(LOG_MISC,LOG_ERROR)(" (data addr %04hX:%04X is %u bytes past this MCB)",dataSeg,dataOfs,dataAddr - mcbStartAddr); - } - - // if we've just processed the last MCB in the chain, break out of the loop - if (mcb.GetType()==0x5a) { - break; - } - // else, move to the next MCB in the chain - mcb_segment+=mcb.GetSize()+1; - mcb.SetPt(mcb_segment); - } -} - -// Display the content of all Memory Control Blocks. -static void LogMCBS(void) -{ - LOG(LOG_MISC,LOG_ERROR)("MCB Seg Size (bytes) PSP Seg (notes) Filename"); - LOG(LOG_MISC,LOG_ERROR)("Conventional memory:"); - LogMCBChain(dos.firstMCB); - - LOG(LOG_MISC,LOG_ERROR)("Upper memory:"); - LogMCBChain(dos_infoblock.GetStartOfUMBChain()); -} - -static void LogGDT(void) -{ - char out1[512]; - Descriptor desc; - Bitu length = cpu.gdt.GetLimit(); - PhysPt address = cpu.gdt.GetBase(); - PhysPt max = address + length; - Bitu i = 0; - LOG(LOG_MISC,LOG_ERROR)("GDT Base:%08X Limit:%08X",address,length); - while (address> 10)*4; - X86PageEntry table; - table.load=phys_readd(table_addr); - if (table.block.p) { - X86PageEntry entry; - Bitu entry_addr=(table.block.base<<12)+(i & 0x3ff)*4; - entry.load=phys_readd(entry_addr); - if (entry.block.p) { - sprintf(out1,"page %05Xxxx -> %04Xxxx flags [uw] %x:%x::%x:%x [d=%x|a=%x]", - i,entry.block.base,entry.block.us,table.block.us, - entry.block.wr,table.block.wr,entry.block.d,entry.block.a); - LOG(LOG_MISC,LOG_ERROR)(out1); - } - } - } - } else { - Bitu table_addr=(paging.base.page<<12)+(sel >> 10)*4; - X86PageEntry table; - table.load=phys_readd(table_addr); - if (table.block.p) { - X86PageEntry entry; - Bitu entry_addr=(table.block.base<<12)+(sel & 0x3ff)*4; - entry.load=phys_readd(entry_addr); - sprintf(out1,"page %05Xxxx -> %04Xxxx flags [puw] %x:%x::%x:%x::%x:%x",sel,entry.block.base,entry.block.p,table.block.p,entry.block.us,table.block.us,entry.block.wr,table.block.wr); - LOG(LOG_MISC,LOG_ERROR)(out1); - } else { - sprintf(out1,"pagetable %03X not present, flags [puw] %x::%x::%x",(sel >> 10),table.block.p,table.block.us,table.block.wr); - LOG(LOG_MISC,LOG_ERROR)(out1); - } - } - } -}; - -static void LogCPUInfo(void) { - char out1[512]; - sprintf(out1,"cr0:%08X cr2:%08X cr3:%08X cpl=%x",cpu.cr0,paging.cr2,paging.cr3,cpu.cpl); - LOG(LOG_MISC,LOG_ERROR)(out1); - sprintf(out1,"eflags:%08X [vm=%x iopl=%x nt=%x]",reg_flags,GETFLAG(VM)>>17,GETFLAG(IOPL)>>12,GETFLAG(NT)>>14); - LOG(LOG_MISC,LOG_ERROR)(out1); - sprintf(out1,"GDT base=%08X limit=%08X",cpu.gdt.GetBase(),cpu.gdt.GetLimit()); - LOG(LOG_MISC,LOG_ERROR)(out1); - sprintf(out1,"IDT base=%08X limit=%08X",cpu.idt.GetBase(),cpu.idt.GetLimit()); - LOG(LOG_MISC,LOG_ERROR)(out1); - - Bitu sel=CPU_STR(); - Descriptor desc; - if (cpu.gdt.GetDescriptor(sel,desc)) { - sprintf(out1,"TR selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); - LOG(LOG_MISC,LOG_ERROR)(out1); - } - sel=CPU_SLDT(); - if (cpu.gdt.GetDescriptor(sel,desc)) { - sprintf(out1,"LDT selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); - LOG(LOG_MISC,LOG_ERROR)(out1); - } -}; - -#if C_HEAVY_DEBUG -static void LogInstruction(Bit16u segValue, Bit32u eipValue, ofstream& out) { - static char empty[23] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0 }; - - PhysPt start = GetAddress(segValue,eipValue); - char dline[200];Bitu size; - size = DasmI386(dline, start, reg_eip, cpu.code.big); - char* res = empty; - if (showExtend && (cpuLogType > 0) ) { - res = AnalyzeInstruction(dline,false); - if (!res || !(*res)) res = empty; - Bitu reslen = strlen(res); - if (reslen<22) for (Bitu i=0; i<22-reslen; i++) res[reslen+i] = ' '; res[22] = 0; - }; - Bitu len = strlen(dline); - if (len<30) for (Bitu i=0; i<30-len; i++) dline[len + i] = ' '; dline[30] = 0; - - // Get register values - - if(cpuLogType == 0) { - out << setw(4) << SegValue(cs) << ":" << setw(4) << reg_eip << " " << dline; - } else if (cpuLogType == 1) { - out << setw(4) << SegValue(cs) << ":" << setw(8) << reg_eip << " " << dline << " " << res; - } else if (cpuLogType == 2) { - char ibytes[200]=""; char tmpc[200]; - for (Bitu i=0; i0) << " Z" << (get_ZF()>0) - << " S" << (get_SF()>0) << " O" << (get_OF()>0) << " I" << GETFLAGBOOL(IF); - } else { - out << " FS:" << setw(4) << SegValue(fs) << " GS:" << setw(4) << SegValue(gs) - << " SS:" << setw(4) << SegValue(ss) - << " CF:" << (get_CF()>0) << " ZF:" << (get_ZF()>0) << " SF:" << (get_SF()>0) - << " OF:" << (get_OF()>0) << " AF:" << (get_AF()>0) << " PF:" << (get_PF()>0) - << " IF:" << GETFLAGBOOL(IF); - } - if(cpuLogType == 2) { - out << " TF:" << GETFLAGBOOL(TF) << " VM:" << GETFLAGBOOL(VM) <<" FLG:" << setw(8) << reg_flags - << " CR0:" << setw(8) << cpu.cr0; - } - out << endl; -}; -#endif // DEBUG.COM stuff @@ -2088,7 +1507,7 @@ void DEBUG_CheckExecuteBreakpoint(Bit16u seg, Bit32u off) Bitu DEBUG_EnableDebugger(void) { - exitLoop = true; + DEBUG_exitLoop = true; DEBUG_Enable(true); CPU_Cycles=CPU_CycleLeft=0; return 0; @@ -2128,7 +1547,7 @@ void DEBUG_ShutDown(Section * /*sec*/) { #endif } -Bitu debugCallback; +Bitu DEBUG_debugCallback; void DEBUG_Init(Section* sec) { @@ -2141,14 +1560,20 @@ void DEBUG_Init(Section* sec) { /* setup debug.com */ PROGRAMS_MakeFile("DEBUG.COM",DEBUG_ProgramStart); /* Setup callback */ - debugCallback=CALLBACK_Allocate(); - CALLBACK_Setup(debugCallback,DEBUG_EnableDebugger,CB_RETF,"debugger"); + DEBUG_debugCallback=CALLBACK_Allocate(); + CALLBACK_Setup(DEBUG_debugCallback,DEBUG_EnableDebugger,CB_RETF,"debugger"); /* shutdown function */ sec->AddDestroyFunction(&DEBUG_ShutDown); } // DEBUGGING VAR STUFF +CDebugVar::CDebugVar(char* _name, PhysPt _adr) +{ + adr=_adr; + safe_strncpy(name,_name,16); +}; + void CDebugVar::InsertVariable(char* name, PhysPt adr) { varList.push_back(new CDebugVar(name,adr)); @@ -2237,7 +1662,7 @@ static void SaveMemory(Bitu seg, Bitu ofs1, Bit32u num) { sprintf(buffer,"%04X:%04X ",seg,ofs1); for (Bit16u x=0; x<16; x++) { Bit8u value; - if (mem_readb_checked(GetAddress(seg,ofs1+x),&value)) sprintf(temp,"%s","?? "); + if (mem_readb_checked(DEBUG_GetAddress(seg,ofs1+x),&value)) sprintf(temp,"%s","?? "); else sprintf(temp,"%02X ",value); strcat(buffer,temp); } @@ -2250,7 +1675,7 @@ static void SaveMemory(Bitu seg, Bitu ofs1, Bit32u num) { sprintf(buffer,"%04X:%04X ",seg,ofs1); for (Bit16u x=0; x0; - inst.z = get_ZF()>0; - inst.s = get_SF()>0; - inst.o = get_OF()>0; - inst.a = get_AF()>0; - inst.p = get_PF()>0; - inst.i = GETFLAGBOOL(IF); - - if (++logCount >= LOGCPUMAX) logCount = 0; -}; - -void DEBUG_HeavyWriteLogInstruction(void) { - if (!logHeavy) return; - logHeavy = false; - - DEBUG_ShowMsg("DEBUG: Creating cpu log LOGCPU_INT_CD.TXT\n"); - - ofstream out("LOGCPU_INT_CD.TXT"); - if (!out.is_open()) { - DEBUG_ShowMsg("DEBUG: Failed.\n"); - return; - } - out << hex << noshowbase << setfill('0') << uppercase; - Bit32u startLog = logCount; - do { - // Write Instructions - TLogInst & inst = logInst[startLog]; - out << setw(4) << inst.s_cs << ":" << setw(8) << inst.eip << " " - << inst.dline << " " << inst.res << " EAX:" << setw(8)<< inst.eax - << " EBX:" << setw(8) << inst.ebx << " ECX:" << setw(8) << inst.ecx - << " EDX:" << setw(8) << inst.edx << " ESI:" << setw(8) << inst.esi - << " EDI:" << setw(8) << inst.edi << " EBP:" << setw(8) << inst.ebp - << " ESP:" << setw(8) << inst.esp << " DS:" << setw(4) << inst.s_ds - << " ES:" << setw(4) << inst.s_es<< " FS:" << setw(4) << inst.s_fs - << " GS:" << setw(4) << inst.s_gs<< " SS:" << setw(4) << inst.s_ss - << " CF:" << inst.c << " ZF:" << inst.z << " SF:" << inst.s - << " OF:" << inst.o << " AF:" << inst.a << " PF:" << inst.p - << " IF:" << inst.i << endl; - -/* fprintf(f,"%04X:%08X %s %s EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X EBP:%08X ESP:%08X DS:%04X ES:%04X FS:%04X GS:%04X SS:%04X CF:%01X ZF:%01X SF:%01X OF:%01X AF:%01X PF:%01X IF:%01X\n", - logInst[startLog].s_cs,logInst[startLog].eip,logInst[startLog].dline,logInst[startLog].res,logInst[startLog].eax,logInst[startLog].ebx,logInst[startLog].ecx,logInst[startLog].edx,logInst[startLog].esi,logInst[startLog].edi,logInst[startLog].ebp,logInst[startLog].esp, - logInst[startLog].s_ds,logInst[startLog].s_es,logInst[startLog].s_fs,logInst[startLog].s_gs,logInst[startLog].s_ss, - logInst[startLog].c,logInst[startLog].z,logInst[startLog].s,logInst[startLog].o,logInst[startLog].a,logInst[startLog].p,logInst[startLog].i);*/ - if (++startLog >= LOGCPUMAX) startLog = 0; - } while (startLog != logCount); - - out.close(); - DEBUG_ShowMsg("DEBUG: Done.\n"); -}; - bool DEBUG_HeavyIsBreakpoint(void) { static Bitu zero_count = 0; - if (cpuLog) { - if (cpuLogCounter>0) { - LogInstruction(SegValue(cs),reg_eip,cpuLogFile); - cpuLogCounter--; + if (DEBUG_cpuLog) { + if (DEBUG_cpuLogCounter>0) { + DEBUG_LogInstruction(SegValue(cs),reg_eip,DEBUG_cpuLogFile); + DEBUG_cpuLogCounter--; } - if (cpuLogCounter<=0) { - cpuLogFile.close(); + if (DEBUG_cpuLogCounter<=0) { + DEBUG_cpuLogFile.close(); DEBUG_ShowMsg("DEBUG: cpu log LOGCPU.TXT created\n"); - cpuLog = false; + DEBUG_cpuLog = false; DEBUG_EnableDebugger(); return true; } } - // LogInstruction - if (logHeavy) DEBUG_HeavyLogInstruction(); - if (zeroProtect) { + // DEBUG_LogInstruction + if (DEBUG_logHeavy) DEBUG_HeavyLogInstruction(); + if (DEBUG_zeroProtect) { Bit32u value=0; if (!mem_readd_checked(SegPhys(cs)+reg_eip,&value)) { if (value == 0) zero_count++; diff --git a/src/debug/debug.cpp.orig b/src/debug/debug.cpp.orig new file mode 100644 index 00000000..7b76d573 --- /dev/null +++ b/src/debug/debug.cpp.orig @@ -0,0 +1,2493 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include "dosbox.h" +#if C_DEBUG + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "debug.h" +#include "cross.h" //snprintf +#include "cpu.h" +#include "video.h" +#include "pic.h" +#include "mapper.h" +#include "cpu.h" +#include "callback.h" +#include "inout.h" +#include "mixer.h" +#include "timer.h" +#include "paging.h" +#include "support.h" +#include "shell.h" +#include "programs.h" +#include "debug_inc.h" +#include "../cpu/lazyflags.h" +#include "keyboard.h" +#include "setup.h" + +#ifdef WIN32 +void WIN32_Console(); +#else +#include +#include +static struct termios consolesettings; +#endif +int old_cursor_state; + +// Forwards +static void DrawCode(void); +static void DEBUG_RaiseTimerIrq(void); +static void SaveMemory(Bitu seg, Bitu ofs1, Bit32u num); +static void SaveMemoryBin(Bitu seg, Bitu ofs1, Bit32u num); +static void LogMCBS(void); +static void LogGDT(void); +static void LogLDT(void); +static void LogIDT(void); +static void LogPages(char* selname); +static void LogCPUInfo(void); +static void OutputVecTable(char* filename); +static void DrawVariables(void); + +char* AnalyzeInstruction(char* inst, bool saveSelector); +Bit32u GetHexValue(char* str, char*& hex); + +#if 0 +class DebugPageHandler : public PageHandler { +public: + Bitu readb(PhysPt /*addr*/) { + } + Bitu readw(PhysPt /*addr*/) { + } + Bitu readd(PhysPt /*addr*/) { + } + void writeb(PhysPt /*addr*/,Bitu /*val*/) { + } + void writew(PhysPt /*addr*/,Bitu /*val*/) { + } + void writed(PhysPt /*addr*/,Bitu /*val*/) { + } +}; +#endif + + +class DEBUG; + +DEBUG* pDebugcom = 0; +bool exitLoop = false; + + +// Heavy Debugging Vars for logging +#if C_HEAVY_DEBUG +static ofstream cpuLogFile; +static bool cpuLog = false; +static int cpuLogCounter = 0; +static int cpuLogType = 1; // log detail +static bool zeroProtect = false; +bool logHeavy = false; +#endif + + + +static struct { + Bit32u eax,ebx,ecx,edx,esi,edi,ebp,esp,eip; +} oldregs; + +static char curSelectorName[3] = { 0,0,0 }; + +static Segment oldsegs[6]; +static Bitu oldflags,oldcpucpl; +DBGBlock dbg; +Bitu cycle_count; +static bool debugging; + + +static void SetColor(Bitu test) { + if (test) { + if (has_colors()) { wattrset(dbg.win_reg,COLOR_PAIR(PAIR_BYELLOW_BLACK));} + } else { + if (has_colors()) { wattrset(dbg.win_reg,0);} + } +} + +#define MAXCMDLEN 254 +struct SCodeViewData { + int cursorPos; + Bit16u firstInstSize; + Bit16u useCS; + Bit32u useEIPlast, useEIPmid; + Bit32u useEIP; + Bit16u cursorSeg; + Bit32u cursorOfs; + bool ovrMode; + char inputStr[MAXCMDLEN+1]; + char suspInputStr[MAXCMDLEN+1]; + int inputPos; +} codeViewData; + +static Bit16u dataSeg; +static Bit32u dataOfs; +static bool showExtend = true; + +static void ClearInputLine(void) { + codeViewData.inputStr[0] = 0; + codeViewData.inputPos = 0; +} + +// History stuff +#define MAX_HIST_BUFFER 50 +static list histBuff; +static list::iterator histBuffPos = histBuff.end(); + +/***********/ +/* Helpers */ +/***********/ + +Bit32u PhysMakeProt(Bit16u selector, Bit32u offset) +{ + Descriptor desc; + if (cpu.gdt.GetDescriptor(selector,desc)) return desc.GetBase()+offset; + return 0; +}; + +Bit32u GetAddress(Bit16u seg, Bit32u offset) +{ + if (seg==SegValue(cs)) return SegPhys(cs)+offset; + if (cpu.pmode && !(reg_flags & FLAG_VM)) { + Descriptor desc; + if (cpu.gdt.GetDescriptor(seg,desc)) return PhysMakeProt(seg,offset); + } + return (seg<<4)+offset; +} + +static char empty_sel[] = { ' ',' ',0 }; + +bool GetDescriptorInfo(char* selname, char* out1, char* out2) +{ + Bitu sel; + Descriptor desc; + + if (strstr(selname,"cs") || strstr(selname,"CS")) sel = SegValue(cs); + else if (strstr(selname,"ds") || strstr(selname,"DS")) sel = SegValue(ds); + else if (strstr(selname,"es") || strstr(selname,"ES")) sel = SegValue(es); + else if (strstr(selname,"fs") || strstr(selname,"FS")) sel = SegValue(fs); + else if (strstr(selname,"gs") || strstr(selname,"GS")) sel = SegValue(gs); + else if (strstr(selname,"ss") || strstr(selname,"SS")) sel = SegValue(ss); + else { + sel = GetHexValue(selname,selname); + if (*selname==0) selname=empty_sel; + } + if (cpu.gdt.GetDescriptor(sel,desc)) { + switch (desc.Type()) { + case DESC_TASK_GATE: + sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type); + sprintf(out2," TaskGate dpl : %01X %1X",desc.saved.gate.dpl,desc.saved.gate.p); + return true; + case DESC_LDT: + case DESC_286_TSS_A: + case DESC_286_TSS_B: + case DESC_386_TSS_A: + case DESC_386_TSS_B: + sprintf(out1,"%s: b:%08X type:%02X pag",selname,desc.GetBase(),desc.saved.seg.type); + sprintf(out2," l:%08X dpl : %01X %1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.g); + return true; + case DESC_286_CALL_GATE: + case DESC_386_CALL_GATE: + sprintf(out1,"%s: s:%08X type:%02X p params: %02X",selname,desc.GetSelector(),desc.saved.gate.type,desc.saved.gate.paramcount); + sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p); + return true; + case DESC_286_INT_GATE: + case DESC_286_TRAP_GATE: + case DESC_386_INT_GATE: + case DESC_386_TRAP_GATE: + sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type); + sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p); + return true; + } + sprintf(out1,"%s: b:%08X type:%02X parbg",selname,desc.GetBase(),desc.saved.seg.type); + sprintf(out2," l:%08X dpl : %01X %1X%1X%1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.r,desc.saved.seg.big,desc.saved.seg.g); + return true; + } else { + strcpy(out1," "); + strcpy(out2," "); + } + return false; +}; + +/********************/ +/* DebugVar stuff */ +/********************/ + +class CDebugVar +{ +public: + CDebugVar(char* _name, PhysPt _adr) { adr=_adr; safe_strncpy(name,_name,16); }; + + char* GetName(void) { return name; }; + PhysPt GetAdr (void) { return adr; }; + +private: + PhysPt adr; + char name[16]; + +public: + static void InsertVariable (char* name, PhysPt adr); + static CDebugVar* FindVar (PhysPt adr); + static void DeleteAll (); + static bool SaveVars (char* name); + static bool LoadVars (char* name); + + static std::list varList; +}; + +std::list CDebugVar::varList; + + +/********************/ +/* Breakpoint stuff */ +/********************/ + +bool skipFirstInstruction = false; + +enum EBreakpoint { BKPNT_UNKNOWN, BKPNT_PHYSICAL, BKPNT_INTERRUPT, BKPNT_MEMORY, BKPNT_MEMORY_PROT, BKPNT_MEMORY_LINEAR }; + +#define BPINT_ALL 0x100 + +class CBreakpoint +{ +public: + + CBreakpoint(void); + void SetAddress (Bit16u seg, Bit32u off) { location = GetAddress(seg,off); type = BKPNT_PHYSICAL; segment = seg; offset = off; }; + void SetAddress (PhysPt adr) { location = adr; type = BKPNT_PHYSICAL; }; + void SetInt (Bit8u _intNr, Bit16u ah) { intNr = _intNr, ahValue = ah; type = BKPNT_INTERRUPT; }; + void SetOnce (bool _once) { once = _once; }; + void SetType (EBreakpoint _type) { type = _type; }; + void SetValue (Bit8u value) { ahValue = value; }; + + bool IsActive (void) { return active; }; + void Activate (bool _active); + + EBreakpoint GetType (void) { return type; }; + bool GetOnce (void) { return once; }; + PhysPt GetLocation (void) { if (GetType()!=BKPNT_INTERRUPT) return location; else return 0; }; + Bit16u GetSegment (void) { return segment; }; + Bit32u GetOffset (void) { return offset; }; + Bit8u GetIntNr (void) { if (GetType()==BKPNT_INTERRUPT) return intNr; else return 0; }; + Bit16u GetValue (void) { if (GetType()!=BKPNT_PHYSICAL) return ahValue; else return 0; }; + + // statics + static CBreakpoint* AddBreakpoint (Bit16u seg, Bit32u off, bool once); + static CBreakpoint* AddIntBreakpoint (Bit8u intNum, Bit16u ah, bool once); + static CBreakpoint* AddMemBreakpoint (Bit16u seg, Bit32u off); + static void ActivateBreakpoints (PhysPt adr, bool activate); + static bool CheckBreakpoint (PhysPt adr); + static bool CheckBreakpoint (Bitu seg, Bitu off); + static bool CheckIntBreakpoint (PhysPt adr, Bit8u intNr, Bit16u ahValue); + static bool IsBreakpoint (PhysPt where); + static bool IsBreakpointDrawn (PhysPt where); + static bool DeleteBreakpoint (PhysPt where); + static bool DeleteByIndex (Bit16u index); + static void DeleteAll (void); + static void ShowList (void); + + +private: + EBreakpoint type; + // Physical + PhysPt location; + Bit8u oldData; + Bit16u segment; + Bit32u offset; + // Int + Bit8u intNr; + Bit16u ahValue; + // Shared + bool active; + bool once; + + static std::list BPoints; +public: + static CBreakpoint* ignoreOnce; +}; + +CBreakpoint::CBreakpoint(void): +location(0), +active(false),once(false), +segment(0),offset(0),intNr(0),ahValue(0), +type(BKPNT_UNKNOWN) { }; + +void CBreakpoint::Activate(bool _active) +{ +#if !C_HEAVY_DEBUG + if (GetType()==BKPNT_PHYSICAL) { + if (_active) { + // Set 0xCC and save old value + Bit8u data = mem_readb(location); + if (data!=0xCC) { + oldData = data; + mem_writeb(location,0xCC); + }; + } else { + // Remove 0xCC and set old value + if (mem_readb (location)==0xCC) { + mem_writeb(location,oldData); + }; + } + } +#endif + active = _active; +}; + +// Statics +std::list CBreakpoint::BPoints; +CBreakpoint* CBreakpoint::ignoreOnce = 0; +Bitu ignoreAddressOnce = 0; + +CBreakpoint* CBreakpoint::AddBreakpoint(Bit16u seg, Bit32u off, bool once) +{ + CBreakpoint* bp = new CBreakpoint(); + bp->SetAddress (seg,off); + bp->SetOnce (once); + BPoints.push_front (bp); + return bp; +}; + +CBreakpoint* CBreakpoint::AddIntBreakpoint(Bit8u intNum, Bit16u ah, bool once) +{ + CBreakpoint* bp = new CBreakpoint(); + bp->SetInt (intNum,ah); + bp->SetOnce (once); + BPoints.push_front (bp); + return bp; +}; + +CBreakpoint* CBreakpoint::AddMemBreakpoint(Bit16u seg, Bit32u off) +{ + CBreakpoint* bp = new CBreakpoint(); + bp->SetAddress (seg,off); + bp->SetOnce (false); + bp->SetType (BKPNT_MEMORY); + BPoints.push_front (bp); + return bp; +}; + +void CBreakpoint::ActivateBreakpoints(PhysPt adr, bool activate) +{ + // activate all breakpoints + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + bp = (*i); + // Do not activate, when bp is an actual address + if (activate && (bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==adr)) { + // Do not activate :) + continue; + } + bp->Activate(activate); + }; +}; + +bool CBreakpoint::CheckBreakpoint(Bitu seg, Bitu off) +// Checks if breakpoint is valid and should stop execution +{ + if ((ignoreAddressOnce!=0) && (GetAddress(seg,off)==ignoreAddressOnce)) { + ignoreAddressOnce = 0; + return false; + } else + ignoreAddressOnce = 0; + + // Search matching breakpoint + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + bp = (*i); + if ((bp->GetType()==BKPNT_PHYSICAL) && bp->IsActive() && (bp->GetSegment()==seg) && (bp->GetOffset()==off)) { + // Ignore Once ? + if (ignoreOnce==bp) { + ignoreOnce=0; + bp->Activate(true); + return false; + }; + // Found, + if (bp->GetOnce()) { + // delete it, if it should only be used once + (BPoints.erase)(i); + bp->Activate(false); + delete bp; + } else { + ignoreOnce = bp; + }; + return true; + } +#if C_HEAVY_DEBUG + // Memory breakpoint support + else if (bp->IsActive()) { + if ((bp->GetType()==BKPNT_MEMORY) || (bp->GetType()==BKPNT_MEMORY_PROT) || (bp->GetType()==BKPNT_MEMORY_LINEAR)) { + // Watch Protected Mode Memoryonly in pmode + if (bp->GetType()==BKPNT_MEMORY_PROT) { + // Check if pmode is active + if (!cpu.pmode) return false; + // Check if descriptor is valid + Descriptor desc; + if (!cpu.gdt.GetDescriptor(bp->GetSegment(),desc)) return false; + if (desc.GetLimit()==0) return false; + } + + Bitu address; + if (bp->GetType()==BKPNT_MEMORY_LINEAR) address = bp->GetOffset(); + else address = GetAddress(bp->GetSegment(),bp->GetOffset()); + Bit8u value=0; + if (mem_readb_checked(address,&value)) return false; + if (bp->GetValue() != value) { + // Yup, memory value changed + DEBUG_ShowMsg("DEBUG: Memory breakpoint %s: %04X:%04X - %02X -> %02X\n",(bp->GetType()==BKPNT_MEMORY_PROT)?"(Prot)":"",bp->GetSegment(),bp->GetOffset(),bp->GetValue(),value); + bp->SetValue(value); + return true; + }; + } + }; +#endif + }; + return false; +}; + +bool CBreakpoint::CheckIntBreakpoint(PhysPt adr, Bit8u intNr, Bit16u ahValue) +// Checks if interrupt breakpoint is valid and should stop execution +{ + if ((ignoreAddressOnce!=0) && (adr==ignoreAddressOnce)) { + ignoreAddressOnce = 0; + return false; + } else + ignoreAddressOnce = 0; + + // Search matching breakpoint + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + bp = (*i); + if ((bp->GetType()==BKPNT_INTERRUPT) && bp->IsActive() && (bp->GetIntNr()==intNr)) { + if ((bp->GetValue()==BPINT_ALL) || (bp->GetValue()==ahValue)) { + // Ignore it once ? + if (ignoreOnce==bp) { + ignoreOnce=0; + bp->Activate(true); + return false; + }; + // Found + if (bp->GetOnce()) { + // delete it, if it should only be used once + (BPoints.erase)(i); + bp->Activate(false); + delete bp; + } else { + ignoreOnce = bp; + } + return true; + } + }; + }; + return false; +}; + +void CBreakpoint::DeleteAll() +{ + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + bp = (*i); + bp->Activate(false); + delete bp; + }; + (BPoints.clear)(); +}; + + +bool CBreakpoint::DeleteByIndex(Bit16u index) +{ + // Search matching breakpoint + int nr = 0; + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + if (nr==index) { + bp = (*i); + (BPoints.erase)(i); + bp->Activate(false); + delete bp; + return true; + } + nr++; + }; + return false; +}; + +bool CBreakpoint::DeleteBreakpoint(PhysPt where) +{ + // Search matching breakpoint + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + bp = (*i); + if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==where)) { + (BPoints.erase)(i); + bp->Activate(false); + delete bp; + return true; + } + }; + return false; +}; + +bool CBreakpoint::IsBreakpoint(PhysPt adr) +// is there a breakpoint at address ? +{ + // Search matching breakpoint + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + bp = (*i); + if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetSegment()==adr)) { + return true; + }; + if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==adr)) { + return true; + }; + }; + return false; +}; + +bool CBreakpoint::IsBreakpointDrawn(PhysPt adr) +// valid breakpoint, that should be drawn ? +{ + // Search matching breakpoint + std::list::iterator i; + CBreakpoint* bp; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + bp = (*i); + if ((bp->GetType()==BKPNT_PHYSICAL) && (bp->GetLocation()==adr)) { + // Only draw, if breakpoint is not only once, + return !bp->GetOnce(); + }; + }; + return false; +}; + +void CBreakpoint::ShowList(void) +{ + // iterate list + int nr = 0; + std::list::iterator i; + for(i=BPoints.begin(); i != BPoints.end(); i++) { + CBreakpoint* bp = (*i); + if (bp->GetType()==BKPNT_PHYSICAL) { + DEBUG_ShowMsg("%02X. BP %04X:%04X\n",nr,bp->GetSegment(),bp->GetOffset()); + } else if (bp->GetType()==BKPNT_INTERRUPT) { + if (bp->GetValue()==BPINT_ALL) DEBUG_ShowMsg("%02X. BPINT %02X\n",nr,bp->GetIntNr()); + else DEBUG_ShowMsg("%02X. BPINT %02X AH=%02X\n",nr,bp->GetIntNr(),bp->GetValue()); + } else if (bp->GetType()==BKPNT_MEMORY) { + DEBUG_ShowMsg("%02X. BPMEM %04X:%04X (%02X)\n",nr,bp->GetSegment(),bp->GetOffset(),bp->GetValue()); + } else if (bp->GetType()==BKPNT_MEMORY_PROT) { + DEBUG_ShowMsg("%02X. BPPM %04X:%08X (%02X)\n",nr,bp->GetSegment(),bp->GetOffset(),bp->GetValue()); + } else if (bp->GetType()==BKPNT_MEMORY_LINEAR ) { + DEBUG_ShowMsg("%02X. BPLM %08X (%02X)\n",nr,bp->GetOffset(),bp->GetValue()); + }; + nr++; + } +}; + +bool DEBUG_Breakpoint(void) +{ + /* First get the phyiscal address and check for a set Breakpoint */ + if (!CBreakpoint::CheckBreakpoint(SegValue(cs),reg_eip)) return false; + // Found. Breakpoint is valid + PhysPt where=GetAddress(SegValue(cs),reg_eip); + CBreakpoint::ActivateBreakpoints(where,false); // Deactivate all breakpoints + return true; +}; + +bool DEBUG_IntBreakpoint(Bit8u intNum) +{ + /* First get the phyiscal address and check for a set Breakpoint */ + PhysPt where=GetAddress(SegValue(cs),reg_eip); + if (!CBreakpoint::CheckIntBreakpoint(where,intNum,reg_ah)) return false; + // Found. Breakpoint is valid + CBreakpoint::ActivateBreakpoints(where,false); // Deactivate all breakpoints + return true; +}; + +static bool StepOver() +{ + exitLoop = false; + PhysPt start=GetAddress(SegValue(cs),reg_eip); + char dline[200];Bitu size; + size=DasmI386(dline, start, reg_eip, cpu.code.big); + + if (strstr(dline,"call") || strstr(dline,"int") || strstr(dline,"loop") || strstr(dline,"rep")) { + CBreakpoint::AddBreakpoint (SegValue(cs),reg_eip+size, true); + CBreakpoint::ActivateBreakpoints(start, true); + debugging=false; + DrawCode(); + DOSBOX_SetNormalLoop(); + return true; + } + return false; +}; + +bool DEBUG_ExitLoop(void) +{ +#if C_HEAVY_DEBUG + DrawVariables(); +#endif + + if (exitLoop) { + exitLoop = false; + return true; + } + return false; +}; + +/********************/ +/* Draw windows */ +/********************/ + +static void DrawData(void) { + + Bit8u ch; + Bit32u add = dataOfs; + Bit32u address; + /* Data win */ + for (int y=0; y<8; y++) { + // Address + if (add<0x10000) mvwprintw (dbg.win_data,1+y,0,"%04X:%04X ",dataSeg,add); + else mvwprintw (dbg.win_data,1+y,0,"%04X:%08X ",dataSeg,add); + for (int x=0; x<16; x++) { + address = GetAddress(dataSeg,add); + if (mem_readb_checked(address,&ch)) ch=0; + mvwprintw (dbg.win_data,1+y,14+3*x,"%02X",ch); + if (ch<32 || !isprint(*reinterpret_cast(&ch))) ch='.'; + mvwprintw (dbg.win_data,1+y,63+x,"%c",ch); + add++; + }; + } + wrefresh(dbg.win_data); +}; + +static void DrawRegisters(void) { + /* Main Registers */ + SetColor(reg_eax!=oldregs.eax);oldregs.eax=reg_eax;mvwprintw (dbg.win_reg,0,4,"%08X",reg_eax); + SetColor(reg_ebx!=oldregs.ebx);oldregs.ebx=reg_ebx;mvwprintw (dbg.win_reg,1,4,"%08X",reg_ebx); + SetColor(reg_ecx!=oldregs.ecx);oldregs.ecx=reg_ecx;mvwprintw (dbg.win_reg,2,4,"%08X",reg_ecx); + SetColor(reg_edx!=oldregs.edx);oldregs.edx=reg_edx;mvwprintw (dbg.win_reg,3,4,"%08X",reg_edx); + + SetColor(reg_esi!=oldregs.esi);oldregs.esi=reg_esi;mvwprintw (dbg.win_reg,0,18,"%08X",reg_esi); + SetColor(reg_edi!=oldregs.edi);oldregs.edi=reg_edi;mvwprintw (dbg.win_reg,1,18,"%08X",reg_edi); + SetColor(reg_ebp!=oldregs.ebp);oldregs.ebp=reg_ebp;mvwprintw (dbg.win_reg,2,18,"%08X",reg_ebp); + SetColor(reg_esp!=oldregs.esp);oldregs.esp=reg_esp;mvwprintw (dbg.win_reg,3,18,"%08X",reg_esp); + SetColor(reg_eip!=oldregs.eip);oldregs.eip=reg_eip;mvwprintw (dbg.win_reg,1,42,"%08X",reg_eip); + + SetColor(SegValue(ds)!=oldsegs[ds].val);oldsegs[ds].val=SegValue(ds);mvwprintw (dbg.win_reg,0,31,"%04X",SegValue(ds)); + SetColor(SegValue(es)!=oldsegs[es].val);oldsegs[es].val=SegValue(es);mvwprintw (dbg.win_reg,0,41,"%04X",SegValue(es)); + SetColor(SegValue(fs)!=oldsegs[fs].val);oldsegs[fs].val=SegValue(fs);mvwprintw (dbg.win_reg,0,51,"%04X",SegValue(fs)); + SetColor(SegValue(gs)!=oldsegs[gs].val);oldsegs[gs].val=SegValue(gs);mvwprintw (dbg.win_reg,0,61,"%04X",SegValue(gs)); + SetColor(SegValue(ss)!=oldsegs[ss].val);oldsegs[ss].val=SegValue(ss);mvwprintw (dbg.win_reg,0,71,"%04X",SegValue(ss)); + SetColor(SegValue(cs)!=oldsegs[cs].val);oldsegs[cs].val=SegValue(cs);mvwprintw (dbg.win_reg,1,31,"%04X",SegValue(cs)); + + /*Individual flags*/ + Bitu changed_flags = reg_flags ^ oldflags; + oldflags = reg_flags; + + SetColor(changed_flags&FLAG_CF); + mvwprintw (dbg.win_reg,1,53,"%01X",GETFLAG(CF) ? 1:0); + SetColor(changed_flags&FLAG_ZF); + mvwprintw (dbg.win_reg,1,56,"%01X",GETFLAG(ZF) ? 1:0); + SetColor(changed_flags&FLAG_SF); + mvwprintw (dbg.win_reg,1,59,"%01X",GETFLAG(SF) ? 1:0); + SetColor(changed_flags&FLAG_OF); + mvwprintw (dbg.win_reg,1,62,"%01X",GETFLAG(OF) ? 1:0); + SetColor(changed_flags&FLAG_AF); + mvwprintw (dbg.win_reg,1,65,"%01X",GETFLAG(AF) ? 1:0); + SetColor(changed_flags&FLAG_PF); + mvwprintw (dbg.win_reg,1,68,"%01X",GETFLAG(PF) ? 1:0); + + + SetColor(changed_flags&FLAG_DF); + mvwprintw (dbg.win_reg,1,71,"%01X",GETFLAG(DF) ? 1:0); + SetColor(changed_flags&FLAG_IF); + mvwprintw (dbg.win_reg,1,74,"%01X",GETFLAG(IF) ? 1:0); + SetColor(changed_flags&FLAG_TF); + mvwprintw (dbg.win_reg,1,77,"%01X",GETFLAG(TF) ? 1:0); + + SetColor(changed_flags&FLAG_IOPL); + mvwprintw (dbg.win_reg,2,72,"%01X",GETFLAG(IOPL)>>12); + + + SetColor(cpu.cpl ^ oldcpucpl); + mvwprintw (dbg.win_reg,2,78,"%01X",cpu.cpl); + oldcpucpl=cpu.cpl; + + if (cpu.pmode) { + if (reg_flags & FLAG_VM) mvwprintw(dbg.win_reg,0,76,"VM86"); + else if (cpu.code.big) mvwprintw(dbg.win_reg,0,76,"Pr32"); + else mvwprintw(dbg.win_reg,0,76,"Pr16"); + } else + mvwprintw(dbg.win_reg,0,76,"Real"); + + // Selector info, if available + if ((cpu.pmode) && curSelectorName[0]) { + char out1[200], out2[200]; + GetDescriptorInfo(curSelectorName,out1,out2); + mvwprintw(dbg.win_reg,2,28,out1); + mvwprintw(dbg.win_reg,3,28,out2); + } + + wattrset(dbg.win_reg,0); + mvwprintw(dbg.win_reg,3,60,"%u ",cycle_count); + wrefresh(dbg.win_reg); +}; + +static void DrawCode(void) { + bool saveSel; + Bit32u disEIP = codeViewData.useEIP; + PhysPt start = GetAddress(codeViewData.useCS,codeViewData.useEIP); + char dline[200];Bitu size;Bitu c; + static char line20[21] = " "; + + for (int i=0;i<10;i++) { + saveSel = false; + if (has_colors()) { + if ((codeViewData.useCS==SegValue(cs)) && (disEIP == reg_eip)) { + wattrset(dbg.win_code,COLOR_PAIR(PAIR_GREEN_BLACK)); + if (codeViewData.cursorPos==-1) { + codeViewData.cursorPos = i; // Set Cursor + } + if (i == codeViewData.cursorPos) { + codeViewData.cursorSeg = SegValue(cs); + codeViewData.cursorOfs = disEIP; + } + saveSel = (i == codeViewData.cursorPos); + } else if (i == codeViewData.cursorPos) { + wattrset(dbg.win_code,COLOR_PAIR(PAIR_BLACK_GREY)); + codeViewData.cursorSeg = codeViewData.useCS; + codeViewData.cursorOfs = disEIP; + saveSel = true; + } else if (CBreakpoint::IsBreakpointDrawn(start)) { + wattrset(dbg.win_code,COLOR_PAIR(PAIR_GREY_RED)); + } else { + wattrset(dbg.win_code,0); + } + } + + + Bitu drawsize=size=DasmI386(dline, start, disEIP, cpu.code.big); + bool toolarge = false; + mvwprintw(dbg.win_code,i,0,"%04X:%04X ",codeViewData.useCS,disEIP); + + if (drawsize>10) { toolarge = true; drawsize = 9; }; + for (c=0;c %s%c", + (codeViewData.ovrMode?'O':'I'),dispPtr,(*curPtr?' ':'_')); + wclrtoeol(dbg.win_code); // not correct in pdcurses if full line + if (*curPtr) { + mvwchgat(dbg.win_code,10,(curPtr-dispPtr+4),1,0,(PAIR_BLACK_GREY),NULL); + } + } + + wrefresh(dbg.win_code); +} + +static void SetCodeWinStart() +{ + if ((SegValue(cs)==codeViewData.useCS) && (reg_eip>=codeViewData.useEIP) && (reg_eip<=codeViewData.useEIPlast)) { + // in valid window - scroll ? + if (reg_eip>=codeViewData.useEIPmid) codeViewData.useEIP += codeViewData.firstInstSize; + + } else { + // totally out of range. + codeViewData.useCS = SegValue(cs); + codeViewData.useEIP = reg_eip; + } + codeViewData.cursorPos = -1; // Recalc Cursor position +}; + +/********************/ +/* User input */ +/********************/ + +Bit32u GetHexValue(char* str, char*& hex) +{ + Bit32u value = 0; + Bit32u regval = 0; + hex = str; + while (*hex==' ') hex++; + if (strstr(hex,"EAX")==hex) { hex+=3; regval = reg_eax; }; + if (strstr(hex,"EBX")==hex) { hex+=3; regval = reg_ebx; }; + if (strstr(hex,"ECX")==hex) { hex+=3; regval = reg_ecx; }; + if (strstr(hex,"EDX")==hex) { hex+=3; regval = reg_edx; }; + if (strstr(hex,"ESI")==hex) { hex+=3; regval = reg_esi; }; + if (strstr(hex,"EDI")==hex) { hex+=3; regval = reg_edi; }; + if (strstr(hex,"EBP")==hex) { hex+=3; regval = reg_ebp; }; + if (strstr(hex,"ESP")==hex) { hex+=3; regval = reg_esp; }; + if (strstr(hex,"EIP")==hex) { hex+=3; regval = reg_eip; }; + if (strstr(hex,"AX")==hex) { hex+=2; regval = reg_ax; }; + if (strstr(hex,"BX")==hex) { hex+=2; regval = reg_bx; }; + if (strstr(hex,"CX")==hex) { hex+=2; regval = reg_cx; }; + if (strstr(hex,"DX")==hex) { hex+=2; regval = reg_dx; }; + if (strstr(hex,"SI")==hex) { hex+=2; regval = reg_si; }; + if (strstr(hex,"DI")==hex) { hex+=2; regval = reg_di; }; + if (strstr(hex,"BP")==hex) { hex+=2; regval = reg_bp; }; + if (strstr(hex,"SP")==hex) { hex+=2; regval = reg_sp; }; + if (strstr(hex,"IP")==hex) { hex+=2; regval = reg_ip; }; + if (strstr(hex,"CS")==hex) { hex+=2; regval = SegValue(cs); }; + if (strstr(hex,"DS")==hex) { hex+=2; regval = SegValue(ds); }; + if (strstr(hex,"ES")==hex) { hex+=2; regval = SegValue(es); }; + if (strstr(hex,"FS")==hex) { hex+=2; regval = SegValue(fs); }; + if (strstr(hex,"GS")==hex) { hex+=2; regval = SegValue(gs); }; + if (strstr(hex,"SS")==hex) { hex+=2; regval = SegValue(ss); }; + + while (*hex) { + if ((*hex>='0') && (*hex<='9')) value = (value<<4)+*hex-'0'; + else if ((*hex>='A') && (*hex<='F')) value = (value<<4)+*hex-'A'+10; + else { + if(*hex == '+') {hex++;return regval + value + GetHexValue(hex,hex); }; + if(*hex == '-') {hex++;return regval + value - GetHexValue(hex,hex); }; + break; // No valid char + } + hex++; + }; + return regval + value; +}; + +bool ChangeRegister(char* str) +{ + char* hex = str; + while (*hex==' ') hex++; + if (strstr(hex,"EAX")==hex) { hex+=3; reg_eax = GetHexValue(hex,hex); } else + if (strstr(hex,"EBX")==hex) { hex+=3; reg_ebx = GetHexValue(hex,hex); } else + if (strstr(hex,"ECX")==hex) { hex+=3; reg_ecx = GetHexValue(hex,hex); } else + if (strstr(hex,"EDX")==hex) { hex+=3; reg_edx = GetHexValue(hex,hex); } else + if (strstr(hex,"ESI")==hex) { hex+=3; reg_esi = GetHexValue(hex,hex); } else + if (strstr(hex,"EDI")==hex) { hex+=3; reg_edi = GetHexValue(hex,hex); } else + if (strstr(hex,"EBP")==hex) { hex+=3; reg_ebp = GetHexValue(hex,hex); } else + if (strstr(hex,"ESP")==hex) { hex+=3; reg_esp = GetHexValue(hex,hex); } else + if (strstr(hex,"EIP")==hex) { hex+=3; reg_eip = GetHexValue(hex,hex); } else + if (strstr(hex,"AX")==hex) { hex+=2; reg_ax = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"BX")==hex) { hex+=2; reg_bx = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"CX")==hex) { hex+=2; reg_cx = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"DX")==hex) { hex+=2; reg_dx = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"SI")==hex) { hex+=2; reg_si = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"DI")==hex) { hex+=2; reg_di = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"BP")==hex) { hex+=2; reg_bp = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"SP")==hex) { hex+=2; reg_sp = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"IP")==hex) { hex+=2; reg_ip = (Bit16u)GetHexValue(hex,hex); } else + if (strstr(hex,"CS")==hex) { hex+=2; SegSet16(cs,(Bit16u)GetHexValue(hex,hex)); } else + if (strstr(hex,"DS")==hex) { hex+=2; SegSet16(ds,(Bit16u)GetHexValue(hex,hex)); } else + if (strstr(hex,"ES")==hex) { hex+=2; SegSet16(es,(Bit16u)GetHexValue(hex,hex)); } else + if (strstr(hex,"FS")==hex) { hex+=2; SegSet16(fs,(Bit16u)GetHexValue(hex,hex)); } else + if (strstr(hex,"GS")==hex) { hex+=2; SegSet16(gs,(Bit16u)GetHexValue(hex,hex)); } else + if (strstr(hex,"SS")==hex) { hex+=2; SegSet16(ss,(Bit16u)GetHexValue(hex,hex)); } else + if (strstr(hex,"AF")==hex) { hex+=2; SETFLAGBIT(AF,GetHexValue(hex,hex)); } else + if (strstr(hex,"CF")==hex) { hex+=2; SETFLAGBIT(CF,GetHexValue(hex,hex)); } else + if (strstr(hex,"DF")==hex) { hex+=2; SETFLAGBIT(DF,GetHexValue(hex,hex)); } else + if (strstr(hex,"IF")==hex) { hex+=2; SETFLAGBIT(IF,GetHexValue(hex,hex)); } else + if (strstr(hex,"OF")==hex) { hex+=2; SETFLAGBIT(OF,GetHexValue(hex,hex)); } else + if (strstr(hex,"ZF")==hex) { hex+=2; SETFLAGBIT(ZF,GetHexValue(hex,hex)); } else + if (strstr(hex,"PF")==hex) { hex+=2; SETFLAGBIT(PF,GetHexValue(hex,hex)); } else + if (strstr(hex,"SF")==hex) { hex+=2; SETFLAGBIT(SF,GetHexValue(hex,hex)); } else + { return false; }; + return true; +}; + +bool ParseCommand(char* str) { + char* found = str; + for(char* idx = found;*idx != 0; idx++) + *idx = toupper(*idx); + + found = trim(found); + string s_found(found); + istringstream stream(s_found); + string command; + stream >> command; + string::size_type next = s_found.find_first_not_of(' ',command.size()); + if(next == string::npos) next = command.size(); + (s_found.erase)(0,next); + found = const_cast(s_found.c_str()); + + if (command == "MEMDUMP") { // Dump memory to file + Bit16u seg = (Bit16u)GetHexValue(found,found); found++; + Bit32u ofs = GetHexValue(found,found); found++; + Bit32u num = GetHexValue(found,found); found++; + SaveMemory(seg,ofs,num); + return true; + }; + + if (command == "MEMDUMPBIN") { // Dump memory to file bineary + Bit16u seg = (Bit16u)GetHexValue(found,found); found++; + Bit32u ofs = GetHexValue(found,found); found++; + Bit32u num = GetHexValue(found,found); found++; + SaveMemoryBin(seg,ofs,num); + return true; + }; + + if (command == "IV") { // Insert variable + Bit16u seg = (Bit16u)GetHexValue(found,found); found++; + Bit32u ofs = (Bit16u)GetHexValue(found,found); found++; + char name[16]; + for (int i=0; i<16; i++) { + if (found[i] && (found[i]!=' ')) name[i] = found[i]; + else { name[i] = 0; break; }; + }; + name[15] = 0; + + if(!name[0]) return false; + DEBUG_ShowMsg("DEBUG: Created debug var %s at %04X:%04X\n",name,seg,ofs); + CDebugVar::InsertVariable(name,GetAddress(seg,ofs)); + return true; + }; + + if (command == "SV") { // Save variables + char name[13]; + for (int i=0; i<12; i++) { + if (found[i] && (found[i]!=' ')) name[i] = found[i]; + else { name[i] = 0; break; }; + }; + name[12] = 0; + if(!name[0]) return false; + DEBUG_ShowMsg("DEBUG: Variable list save (%s) : %s.\n",name,(CDebugVar::SaveVars(name)?"ok":"failure")); + return true; + }; + + if (command == "LV") { // load variables + char name[13]; + for (int i=0; i<12; i++) { + if (found[i] && (found[i]!=' ')) name[i] = found[i]; + else { name[i] = 0; break; }; + }; + name[12] = 0; + if(!name[0]) return false; + DEBUG_ShowMsg("DEBUG: Variable list load (%s) : %s.\n",name,(CDebugVar::LoadVars(name)?"ok":"failure")); + return true; + }; + + if (command == "SR") { // Set register value + DEBUG_ShowMsg("DEBUG: Set Register %s.\n",(ChangeRegister(found)?"success":"failure")); + return true; + }; + + if (command == "SM") { // Set memory with following values + Bit16u seg = (Bit16u)GetHexValue(found,found); found++; + Bit32u ofs = GetHexValue(found,found); found++; + Bit16u count = 0; + while (*found) { + while (*found==' ') found++; + if (*found) { + Bit8u value = (Bit8u)GetHexValue(found,found); + if(*found) found++; + mem_writeb_checked(GetAddress(seg,ofs+count),value); + count++; + } + }; + DEBUG_ShowMsg("DEBUG: Memory changed.\n"); + return true; + }; + + if (command == "BP") { // Add new breakpoint + Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":" + Bit32u ofs = GetHexValue(found,found); + CBreakpoint::AddBreakpoint(seg,ofs,false); + DEBUG_ShowMsg("DEBUG: Set breakpoint at %04X:%04X\n",seg,ofs); + return true; + }; + +#if C_HEAVY_DEBUG + + if (command == "BPM") { // Add new breakpoint + Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":" + Bit32u ofs = GetHexValue(found,found); + CBreakpoint::AddMemBreakpoint(seg,ofs); + DEBUG_ShowMsg("DEBUG: Set memory breakpoint at %04X:%04X\n",seg,ofs); + return true; + }; + + if (command == "BPPM") { // Add new breakpoint + Bit16u seg = (Bit16u)GetHexValue(found,found);found++; // skip ":" + Bit32u ofs = GetHexValue(found,found); + CBreakpoint* bp = CBreakpoint::AddMemBreakpoint(seg,ofs); + if (bp) { + bp->SetType(BKPNT_MEMORY_PROT); + DEBUG_ShowMsg("DEBUG: Set prot-mode memory breakpoint at %04X:%08X\n",seg,ofs); + } + return true; + }; + + if (command == "BPLM") { // Add new breakpoint + Bit32u ofs = GetHexValue(found,found); + CBreakpoint* bp = CBreakpoint::AddMemBreakpoint(0,ofs); + if (bp) bp->SetType(BKPNT_MEMORY_LINEAR); + DEBUG_ShowMsg("DEBUG: Set linear memory breakpoint at %08X\n",ofs); + return true; + }; + +#endif + + if (command == "BPINT") { // Add Interrupt Breakpoint + Bit8u intNr = (Bit8u)GetHexValue(found,found); + bool all = !(*found);found++; + Bit8u valAH = (Bit8u)GetHexValue(found,found); + if ((valAH==0x00) && (*found=='*' || all)) { + CBreakpoint::AddIntBreakpoint(intNr,BPINT_ALL,false); + DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X\n",intNr); + } else { + CBreakpoint::AddIntBreakpoint(intNr,valAH,false); + DEBUG_ShowMsg("DEBUG: Set interrupt breakpoint at INT %02X AH=%02X\n",intNr,valAH); + } + return true; + }; + + if (command == "BPLIST") { + DEBUG_ShowMsg("Breakpoint list:\n"); + DEBUG_ShowMsg("-------------------------------------------------------------------------\n"); + CBreakpoint::ShowList(); + return true; + }; + + if (command == "BPDEL") { // Delete Breakpoints + Bit8u bpNr = (Bit8u)GetHexValue(found,found); + if ((bpNr==0x00) && (*found=='*')) { // Delete all + CBreakpoint::DeleteAll(); + DEBUG_ShowMsg("DEBUG: Breakpoints deleted.\n"); + } else { + // delete single breakpoint + DEBUG_ShowMsg("DEBUG: Breakpoint deletion %s.\n",(CBreakpoint::DeleteByIndex(bpNr)?"success":"failure")); + } + return true; + }; + + if (command == "C") { // Set code overview + Bit16u codeSeg = (Bit16u)GetHexValue(found,found); found++; + Bit32u codeOfs = GetHexValue(found,found); + DEBUG_ShowMsg("DEBUG: Set code overview to %04X:%04X\n",codeSeg,codeOfs); + codeViewData.useCS = codeSeg; + codeViewData.useEIP = codeOfs; + codeViewData.cursorPos = 0; + return true; + }; + + if (command == "D") { // Set data overview + dataSeg = (Bit16u)GetHexValue(found,found); found++; + dataOfs = GetHexValue(found,found); + DEBUG_ShowMsg("DEBUG: Set data overview to %04X:%04X\n",dataSeg,dataOfs); + return true; + }; + +#if C_HEAVY_DEBUG + + if (command == "LOG") { // Create Cpu normal log file + cpuLogType = 1; + command = "logcode"; + } + + if (command == "LOGS") { // Create Cpu short log file + cpuLogType = 0; + command = "logcode"; + } + + if (command == "LOGL") { // Create Cpu long log file + cpuLogType = 2; + command = "logcode"; + } + + if (command == "logcode") { //Shared code between all logs + DEBUG_ShowMsg("DEBUG: Starting log\n"); + cpuLogFile.open("LOGCPU.TXT"); + if (!cpuLogFile.is_open()) { + DEBUG_ShowMsg("DEBUG: Logfile couldn't be created.\n"); + return false; + } + //Initialize log object + cpuLogFile << hex << noshowbase << setfill('0') << uppercase; + cpuLog = true; + cpuLogCounter = GetHexValue(found,found); + + debugging = false; + CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); + ignoreAddressOnce = SegPhys(cs)+reg_eip; + DOSBOX_SetNormalLoop(); + return true; + }; + +#endif + + if (command == "INTT") { //trace int. + Bit8u intNr = (Bit8u)GetHexValue(found,found); + DEBUG_ShowMsg("DEBUG: Tracing INT %02X\n",intNr); + CPU_HW_Interrupt(intNr); + SetCodeWinStart(); + return true; + }; + + if (command == "INT") { // start int. + Bit8u intNr = (Bit8u)GetHexValue(found,found); + DEBUG_ShowMsg("DEBUG: Starting INT %02X\n",intNr); + CBreakpoint::AddBreakpoint(SegValue(cs),reg_eip, true); + CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip-1,true); + debugging = false; + DrawCode(); + DOSBOX_SetNormalLoop(); + CPU_HW_Interrupt(intNr); + return true; + }; + + if (command == "SELINFO") { + while (found[0] == ' ') found++; + char out1[200],out2[200]; + GetDescriptorInfo(found,out1,out2); + DEBUG_ShowMsg("SelectorInfo %s:\n%s\n%s\n",found,out1,out2); + return true; + }; + + if (command == "DOS") { + stream >> command; + if (command == "MCBS") LogMCBS(); + return true; + } + + if (command == "GDT") {LogGDT(); return true;} + + if (command == "LDT") {LogLDT(); return true;} + + if (command == "IDT") {LogIDT(); return true;} + + if (command == "PAGING") {LogPages(found); return true;} + + if (command == "CPU") {LogCPUInfo(); return true;} + + if (command == "INTVEC") { + if (found[0] != 0) { + OutputVecTable(found); + return true; + } + }; + + if (command == "INTHAND") { + if (found[0] != 0) { + Bit8u intNr = (Bit8u)GetHexValue(found,found); + DEBUG_ShowMsg("DEBUG: Set code overview to interrupt handler %X\n",intNr); + codeViewData.useCS = mem_readw(intNr*4+2); + codeViewData.useEIP = mem_readw(intNr*4); + codeViewData.cursorPos = 0; + return true; + } + }; + + if(command == "EXTEND") { //Toggle additional data. + showExtend = !showExtend; + return true; + }; + + if(command == "TIMERIRQ") { //Start a timer irq + DEBUG_RaiseTimerIrq(); + DEBUG_ShowMsg("Debug: Timer Int started.\n"); + return true; + }; + + +#if C_HEAVY_DEBUG + if (command == "HEAVYLOG") { // Create Cpu log file + logHeavy = !logHeavy; + DEBUG_ShowMsg("DEBUG: Heavy cpu logging %s.\n",logHeavy?"on":"off"); + return true; + }; + + if (command == "ZEROPROTECT") { //toggle zero protection + zeroProtect = !zeroProtect; + DEBUG_ShowMsg("DEBUG: Zero code execution protection %s.\n",zeroProtect?"on":"off"); + return true; + }; + +#endif + if (command == "HELP" || command == "?") { + DEBUG_ShowMsg("Debugger commands (enter all values in hex or as register):\n"); + DEBUG_ShowMsg("--------------------------------------------------------------------------\n"); + DEBUG_ShowMsg("F3/F6 - Previous command in history.\n"); + DEBUG_ShowMsg("F4/F7 - Next command in history.\n"); + DEBUG_ShowMsg("F5 - Run.\n"); + DEBUG_ShowMsg("F9 - Set/Remove breakpoint.\n"); + DEBUG_ShowMsg("F10/F11 - Step over / trace into instruction.\n"); + DEBUG_ShowMsg("ALT + D/E/S/X/B - Set data view to DS:SI/ES:DI/SS:SP/DS:DX/ES:BX.\n"); + DEBUG_ShowMsg("Escape - Clear input line."); + DEBUG_ShowMsg("Up/Down - Move code view cursor.\n"); + DEBUG_ShowMsg("Page Up/Down - Scroll data view.\n"); + DEBUG_ShowMsg("Home/End - Scroll log messages.\n"); + DEBUG_ShowMsg("BP [segment]:[offset] - Set breakpoint.\n"); + DEBUG_ShowMsg("BPINT [intNr] * - Set interrupt breakpoint.\n"); + DEBUG_ShowMsg("BPINT [intNr] [ah] - Set interrupt breakpoint with ah.\n"); +#if C_HEAVY_DEBUG + DEBUG_ShowMsg("BPM [segment]:[offset] - Set memory breakpoint (memory change).\n"); + DEBUG_ShowMsg("BPPM [selector]:[offset]- Set pmode-memory breakpoint (memory change).\n"); + DEBUG_ShowMsg("BPLM [linear address] - Set linear memory breakpoint (memory change).\n"); +#endif + DEBUG_ShowMsg("BPLIST - List breakpoints.\n"); + DEBUG_ShowMsg("BPDEL [bpNr] / * - Delete breakpoint nr / all.\n"); + DEBUG_ShowMsg("C / D [segment]:[offset] - Set code / data view address.\n"); + DEBUG_ShowMsg("DOS MCBS - Show Memory Control Block chain.\n"); + DEBUG_ShowMsg("INT [nr] / INTT [nr] - Execute / Trace into interrupt.\n"); +#if C_HEAVY_DEBUG + DEBUG_ShowMsg("LOG [num] - Write cpu log file.\n"); + DEBUG_ShowMsg("LOGS/LOGL [num] - Write short/long cpu log file.\n"); + DEBUG_ShowMsg("HEAVYLOG - Enable/Disable automatic cpu log when dosbox exits.\n"); + DEBUG_ShowMsg("ZEROPROTECT - Enable/Disable zero code execution detecion.\n"); +#endif + DEBUG_ShowMsg("SR [reg] [value] - Set register value.\n"); + DEBUG_ShowMsg("SM [seg]:[off] [val] [.]..- Set memory with following values.\n"); + + DEBUG_ShowMsg("IV [seg]:[off] [name] - Create var name for memory address.\n"); + DEBUG_ShowMsg("SV [filename] - Save var list in file.\n"); + DEBUG_ShowMsg("LV [filename] - Load var list from file.\n"); + + DEBUG_ShowMsg("MEMDUMP [seg]:[off] [len] - Write memory to file memdump.txt.\n"); + DEBUG_ShowMsg("MEMDUMPBIN [s]:[o] [len] - Write memory to file memdump.bin.\n"); + DEBUG_ShowMsg("SELINFO [segName] - Show selector info.\n"); + + DEBUG_ShowMsg("INTVEC [filename] - Writes interrupt vector table to file.\n"); + DEBUG_ShowMsg("INTHAND [intNum] - Set code view to interrupt handler.\n"); + + DEBUG_ShowMsg("CPU - Display CPU status information.\n"); + DEBUG_ShowMsg("GDT - Lists descriptors of the GDT.\n"); + DEBUG_ShowMsg("LDT - Lists descriptors of the LDT.\n"); + DEBUG_ShowMsg("IDT - Lists descriptors of the IDT.\n"); + DEBUG_ShowMsg("PAGING [page] - Display content of page table.\n"); + DEBUG_ShowMsg("EXTEND - Toggle additional info.\n"); + DEBUG_ShowMsg("TIMERIRQ - Run the system timer.\n"); + + DEBUG_ShowMsg("HELP - Help\n"); + + return true; + }; + return false; +}; + +char* AnalyzeInstruction(char* inst, bool saveSelector) { + static char result[256]; + + char instu[256]; + char prefix[3]; + Bit16u seg; + + strcpy(instu,inst); + upcase(instu); + + result[0] = 0; + char* pos = strchr(instu,'['); + if (pos) { + // Segment prefix ? + if (*(pos-1)==':') { + char* segpos = pos-3; + prefix[0] = tolower(*segpos); + prefix[1] = tolower(*(segpos+1)); + prefix[2] = 0; + seg = (Bit16u)GetHexValue(segpos,segpos); + } else { + if (strstr(pos,"SP") || strstr(pos,"BP")) { + seg = SegValue(ss); + strcpy(prefix,"ss"); + } else { + seg = SegValue(ds); + strcpy(prefix,"ds"); + }; + }; + + pos++; + Bit32u adr = GetHexValue(pos,pos); + while (*pos!=']') { + if (*pos=='+') { + pos++; + adr += GetHexValue(pos,pos); + } else if (*pos=='-') { + pos++; + adr -= GetHexValue(pos,pos); + } else + pos++; + }; + Bit32u address = GetAddress(seg,adr); + if (!(get_tlb_readhandler(address)->flags & PFLAG_INIT)) { + static char outmask[] = "%s:[%04X]=%02X"; + + if (cpu.pmode) outmask[6] = '8'; + switch (DasmLastOperandSize()) { + case 8 : { Bit8u val = mem_readb(address); + outmask[12] = '2'; + sprintf(result,outmask,prefix,adr,val); + } break; + case 16: { Bit16u val = mem_readw(address); + outmask[12] = '4'; + sprintf(result,outmask,prefix,adr,val); + } break; + case 32: { Bit32u val = mem_readd(address); + outmask[12] = '8'; + sprintf(result,outmask,prefix,adr,val); + } break; + } + } else { + sprintf(result,"[illegal]"); + } + // Variable found ? + CDebugVar* var = CDebugVar::FindVar(address); + if (var) { + // Replace occurence + char* pos1 = strchr(inst,'['); + char* pos2 = strchr(inst,']'); + if (pos1 && pos2) { + char temp[256]; + strcpy(temp,pos2); // save end + pos1++; *pos1 = 0; // cut after '[' + strcat(inst,var->GetName()); // add var name + strcat(inst,temp); // add end + }; + }; + // show descriptor info, if available + if ((cpu.pmode) && saveSelector) { + strcpy(curSelectorName,prefix); + }; + }; + // If it is a callback add additional info + pos = strstr(inst,"callback"); + if (pos) { + pos += 9; + Bitu nr = GetHexValue(pos,pos); + const char* descr = CALLBACK_GetDescription(nr); + if (descr) { + strcat(inst," ("); strcat(inst,descr); strcat(inst,")"); + } + }; + // Must be a jump + if (instu[0] == 'J') + { + bool jmp = false; + switch (instu[1]) { + case 'A' : { jmp = (get_CF()?false:true) && (get_ZF()?false:true); // JA + } break; + case 'B' : { if (instu[2] == 'E') { + jmp = (get_CF()?true:false) || (get_ZF()?true:false); // JBE + } else { + jmp = get_CF()?true:false; // JB + } + } break; + case 'C' : { if (instu[2] == 'X') { + jmp = reg_cx == 0; // JCXZ + } else { + jmp = get_CF()?true:false; // JC + } + } break; + case 'E' : { jmp = get_ZF()?true:false; // JE + } break; + case 'G' : { if (instu[2] == 'E') { + jmp = (get_SF()?true:false)==(get_OF()?true:false); // JGE + } else { + jmp = (get_ZF()?false:true) && ((get_SF()?true:false)==(get_OF()?true:false)); // JG + } + } break; + case 'L' : { if (instu[2] == 'E') { + jmp = (get_ZF()?true:false) || ((get_SF()?true:false)!=(get_OF()?true:false)); // JLE + } else { + jmp = (get_SF()?true:false)!=(get_OF()?true:false); // JL + } + } break; + case 'M' : { jmp = true; // JMP + } break; + case 'N' : { switch (instu[2]) { + case 'B' : + case 'C' : { jmp = get_CF()?false:true; // JNB / JNC + } break; + case 'E' : { jmp = get_ZF()?false:true; // JNE + } break; + case 'O' : { jmp = get_OF()?false:true; // JNO + } break; + case 'P' : { jmp = get_PF()?false:true; // JNP + } break; + case 'S' : { jmp = get_SF()?false:true; // JNS + } break; + case 'Z' : { jmp = get_ZF()?false:true; // JNZ + } break; + } + } break; + case 'O' : { jmp = get_OF()?true:false; // JO + } break; + case 'P' : { if (instu[2] == 'O') { + jmp = get_PF()?false:true; // JPO + } else { + jmp = get_SF()?true:false; // JP / JPE + } + } break; + case 'S' : { jmp = get_SF()?true:false; // JS + } break; + case 'Z' : { jmp = get_ZF()?true:false; // JZ + } break; + } + if (jmp) { + pos = strchr(instu,'$'); + if (pos) { + pos = strchr(instu,'+'); + if (pos) { + strcpy(result,"(down)"); + } else { + strcpy(result,"(up)"); + } + } + } else { + sprintf(result,"(no jmp)"); + } + } + return result; +}; + + +Bit32u DEBUG_CheckKeys(void) { + Bits ret=0; + int key=getch(); + if (key>0) { +#if defined(WIN32) && defined(__PDCURSES__) + switch (key) { + case PADENTER: key=0x0A; break; + case PADSLASH: key='/'; break; + case PADSTAR: key='*'; break; + case PADMINUS: key='-'; break; + case PADPLUS: key='+'; break; + case ALT_D: + if (ungetch('D') != ERR) key=27; + break; + case ALT_E: + if (ungetch('E') != ERR) key=27; + break; + case ALT_X: + if (ungetch('X') != ERR) key=27; + break; + case ALT_B: + if (ungetch('B') != ERR) key=27; + break; + case ALT_S: + if (ungetch('S') != ERR) key=27; + break; + } +#endif + switch (toupper(key)) { + case 27: // escape (a bit slow): Clears line. and processes alt commands. + key=getch(); + if(key < 0) { //Purely escape Clear line + ClearInputLine(); + break; + } + + switch(toupper(key)) { + case 'D' : // ALT - D: DS:SI + dataSeg = SegValue(ds); + if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_esi; + else dataOfs = reg_si; + break; + case 'E' : //ALT - E: es:di + dataSeg = SegValue(es); + if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_edi; + else dataOfs = reg_di; + break; + case 'X': //ALT - X: ds:dx + dataSeg = SegValue(ds); + if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_edx; + else dataOfs = reg_dx; + break; + case 'B' : //ALT -B: es:bx + dataSeg = SegValue(es); + if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_ebx; + else dataOfs = reg_bx; + break; + case 'S': //ALT - S: ss:sp + dataSeg = SegValue(ss); + if (cpu.pmode && !(reg_flags & FLAG_VM)) dataOfs = reg_esp; + else dataOfs = reg_sp; + break; + default: + break; + } + break; + case KEY_PPAGE : dataOfs -= 16; break; + case KEY_NPAGE : dataOfs += 16; break; + + case KEY_DOWN: // down + if (codeViewData.cursorPos<9) codeViewData.cursorPos++; + else codeViewData.useEIP += codeViewData.firstInstSize; + break; + case KEY_UP: // up + if (codeViewData.cursorPos>0) codeViewData.cursorPos--; + else { + Bitu bytes = 0; + char dline[200]; + Bitu size = 0; + Bit32u newEIP = codeViewData.useEIP - 1; + if(codeViewData.useEIP) { + for (; bytes < 10; bytes++) { + PhysPt start = GetAddress(codeViewData.useCS,newEIP); + size = DasmI386(dline, start, newEIP, cpu.code.big); + if(codeViewData.useEIP == newEIP+size) break; + newEIP--; + } + if (bytes>=10) newEIP = codeViewData.useEIP - 1; + } + codeViewData.useEIP = newEIP; + } + break; + case KEY_HOME: // Home: scroll log page up + DEBUG_RefreshPage(-1); + break; + case KEY_END: // End: scroll log page down + DEBUG_RefreshPage(1); + break; + case KEY_IC: // Insert: toggle insert/overwrite + codeViewData.ovrMode = !codeViewData.ovrMode; + break; + case KEY_LEFT: // move to the left in command line + if (codeViewData.inputPos > 0) codeViewData.inputPos--; + break; + case KEY_RIGHT: // move to the right in command line + if (codeViewData.inputStr[codeViewData.inputPos]) codeViewData.inputPos++; + break; + case KEY_F(6): // previous command (f1-f4 generate rubbish at my place) + case KEY_F(3): // previous command + if (histBuffPos == histBuff.begin()) break; + if (histBuffPos == histBuff.end()) { + // copy inputStr to suspInputStr so we can restore it + safe_strncpy(codeViewData.suspInputStr, codeViewData.inputStr, sizeof(codeViewData.suspInputStr)); + } + safe_strncpy(codeViewData.inputStr,(*--histBuffPos).c_str(),sizeof(codeViewData.inputStr)); + codeViewData.inputPos = strlen(codeViewData.inputStr); + break; + case KEY_F(7): // next command (f1-f4 generate rubbish at my place) + case KEY_F(4): // next command + if (histBuffPos == histBuff.end()) break; + if (++histBuffPos != histBuff.end()) { + safe_strncpy(codeViewData.inputStr,(*histBuffPos).c_str(),sizeof(codeViewData.inputStr)); + } else { + // copy suspInputStr back into inputStr + safe_strncpy(codeViewData.inputStr, codeViewData.suspInputStr, sizeof(codeViewData.inputStr)); + } + codeViewData.inputPos = strlen(codeViewData.inputStr); + break; + case KEY_F(5): // Run Program + debugging=false; + CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); + ignoreAddressOnce = SegPhys(cs)+reg_eip; + DOSBOX_SetNormalLoop(); + break; + case KEY_F(9): // Set/Remove Breakpoint + { PhysPt ptr = GetAddress(codeViewData.cursorSeg,codeViewData.cursorOfs); + if (CBreakpoint::IsBreakpoint(ptr)) { + CBreakpoint::DeleteBreakpoint(ptr); + DEBUG_ShowMsg("DEBUG: Breakpoint deletion success.\n"); + } + else { + CBreakpoint::AddBreakpoint(codeViewData.cursorSeg, codeViewData.cursorOfs, false); + DEBUG_ShowMsg("DEBUG: Set breakpoint at %04X:%04X\n",codeViewData.cursorSeg,codeViewData.cursorOfs); + } + } + break; + case KEY_F(10): // Step over inst + if (StepOver()) return 0; + else { + exitLoop = false; + skipFirstInstruction = true; // for heavy debugger + CPU_Cycles = 1; + ret=(*cpudecoder)(); + SetCodeWinStart(); + CBreakpoint::ignoreOnce = 0; + } + break; + case KEY_F(11): // trace into + exitLoop = false; + skipFirstInstruction = true; // for heavy debugger + CPU_Cycles = 1; + ret = (*cpudecoder)(); + SetCodeWinStart(); + CBreakpoint::ignoreOnce = 0; + break; + case 0x0A: //Parse typed Command + codeViewData.inputStr[MAXCMDLEN] = '\0'; + if(ParseCommand(codeViewData.inputStr)) { + char* cmd = ltrim(codeViewData.inputStr); + if (histBuff.empty() || *--histBuff.end()!=cmd) + histBuff.push_back(cmd); + if (histBuff.size() > MAX_HIST_BUFFER) histBuff.pop_front(); + histBuffPos = histBuff.end(); + ClearInputLine(); + } else { + codeViewData.inputPos = strlen(codeViewData.inputStr); + } + break; + case KEY_BACKSPACE: //backspace (linux) + case 0x7f: // backspace in some terminal emulators (linux) + case 0x08: // delete + if (codeViewData.inputPos == 0) break; + codeViewData.inputPos--; + // fallthrough + case KEY_DC: // delete character + if ((codeViewData.inputPos<0) || (codeViewData.inputPos>=MAXCMDLEN)) break; + if (codeViewData.inputStr[codeViewData.inputPos] != 0) { + codeViewData.inputStr[MAXCMDLEN] = '\0'; + for(char* p=&codeViewData.inputStr[codeViewData.inputPos];(*p=*(p+1));p++) {} + } + break; + default: + if ((key>=32) && (key<127)) { + if ((codeViewData.inputPos<0) || (codeViewData.inputPos>=MAXCMDLEN)) break; + codeViewData.inputStr[MAXCMDLEN] = '\0'; + if (codeViewData.inputStr[codeViewData.inputPos] == 0) { + codeViewData.inputStr[codeViewData.inputPos++] = char(key); + codeViewData.inputStr[codeViewData.inputPos] = '\0'; + } else if (!codeViewData.ovrMode) { + int len = (int) strlen(codeViewData.inputStr); + if (len < MAXCMDLEN) { + for(len++;len>codeViewData.inputPos;len--) + codeViewData.inputStr[len]=codeViewData.inputStr[len-1]; + codeViewData.inputStr[codeViewData.inputPos++] = char(key); + } + } else { + codeViewData.inputStr[codeViewData.inputPos++] = char(key); + } + } else if (key==killchar()) { + ClearInputLine(); + } + break; + } + if (ret<0) return ret; + if (ret>0) { + if (GCC_UNLIKELY(ret >= CB_MAX)) + ret = 0; + else + ret = (*CallBack_Handlers[ret])(); + if (ret) { + exitLoop=true; + CPU_Cycles=CPU_CycleLeft=0; + return ret; + } + } + ret=0; + DEBUG_DrawScreen(); + } + return ret; +}; + +Bitu DEBUG_Loop(void) { +//TODO Disable sound + GFX_Events(); + // Interrupt started ? - then skip it + Bit16u oldCS = SegValue(cs); + Bit32u oldEIP = reg_eip; + PIC_runIRQs(); + SDL_Delay(1); + if ((oldCS!=SegValue(cs)) || (oldEIP!=reg_eip)) { + CBreakpoint::AddBreakpoint(oldCS,oldEIP,true); + CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); + debugging=false; + DOSBOX_SetNormalLoop(); + return 0; + } + return DEBUG_CheckKeys(); +} + +void DEBUG_Enable(bool pressed) { + if (!pressed) + return; + static bool showhelp=false; + debugging=true; + SetCodeWinStart(); + DEBUG_DrawScreen(); + DOSBOX_SetLoop(&DEBUG_Loop); + if(!showhelp) { + showhelp=true; + DEBUG_ShowMsg("***| TYPE HELP (+ENTER) TO GET AN OVERVIEW OF ALL COMMANDS |***\n"); + } + KEYBOARD_ClrBuffer(); +} + +void DEBUG_DrawScreen(void) { + DrawData(); + DrawCode(); + DrawRegisters(); + DrawVariables(); +} + +static void DEBUG_RaiseTimerIrq(void) { + PIC_ActivateIRQ(0); +} + +// Display the content of the MCB chain starting with the MCB at the specified segment. +static void LogMCBChain(Bit16u mcb_segment) { + DOS_MCB mcb(mcb_segment); + char filename[9]; // 8 characters plus a terminating NUL + const char *psp_seg_note; + PhysPt dataAddr = PhysMake(dataSeg,dataOfs);// location being viewed in the "Data Overview" + + // loop forever, breaking out of the loop once we've processed the last MCB + while (true) { + // verify that the type field is valid + if (mcb.GetType()!=0x4d && mcb.GetType()!=0x5a) { + LOG(LOG_MISC,LOG_ERROR)("MCB chain broken at %04X:0000!",mcb_segment); + return; + } + + mcb.GetFileName(filename); + + // some PSP segment values have special meanings + switch (mcb.GetPSPSeg()) { + case MCB_FREE: + psp_seg_note = "(free)"; + break; + case MCB_DOS: + psp_seg_note = "(DOS)"; + break; + default: + psp_seg_note = ""; + } + + LOG(LOG_MISC,LOG_ERROR)(" %04X %12u %04X %-7s %s",mcb_segment,mcb.GetSize() << 4,mcb.GetPSPSeg(), psp_seg_note, filename); + + // print a message if dataAddr is within this MCB's memory range + PhysPt mcbStartAddr = PhysMake(mcb_segment+1,0); + PhysPt mcbEndAddr = PhysMake(mcb_segment+1+mcb.GetSize(),0); + if (dataAddr >= mcbStartAddr && dataAddr < mcbEndAddr) { + LOG(LOG_MISC,LOG_ERROR)(" (data addr %04hX:%04X is %u bytes past this MCB)",dataSeg,dataOfs,dataAddr - mcbStartAddr); + } + + // if we've just processed the last MCB in the chain, break out of the loop + if (mcb.GetType()==0x5a) { + break; + } + // else, move to the next MCB in the chain + mcb_segment+=mcb.GetSize()+1; + mcb.SetPt(mcb_segment); + } +} + +// Display the content of all Memory Control Blocks. +static void LogMCBS(void) +{ + LOG(LOG_MISC,LOG_ERROR)("MCB Seg Size (bytes) PSP Seg (notes) Filename"); + LOG(LOG_MISC,LOG_ERROR)("Conventional memory:"); + LogMCBChain(dos.firstMCB); + + LOG(LOG_MISC,LOG_ERROR)("Upper memory:"); + LogMCBChain(dos_infoblock.GetStartOfUMBChain()); +} + +static void LogGDT(void) +{ + char out1[512]; + Descriptor desc; + Bitu length = cpu.gdt.GetLimit(); + PhysPt address = cpu.gdt.GetBase(); + PhysPt max = address + length; + Bitu i = 0; + LOG(LOG_MISC,LOG_ERROR)("GDT Base:%08X Limit:%08X",address,length); + while (address> 10)*4; + X86PageEntry table; + table.load=phys_readd(table_addr); + if (table.block.p) { + X86PageEntry entry; + Bitu entry_addr=(table.block.base<<12)+(i & 0x3ff)*4; + entry.load=phys_readd(entry_addr); + if (entry.block.p) { + sprintf(out1,"page %05Xxxx -> %04Xxxx flags [uw] %x:%x::%x:%x [d=%x|a=%x]", + i,entry.block.base,entry.block.us,table.block.us, + entry.block.wr,table.block.wr,entry.block.d,entry.block.a); + LOG(LOG_MISC,LOG_ERROR)(out1); + } + } + } + } else { + Bitu table_addr=(paging.base.page<<12)+(sel >> 10)*4; + X86PageEntry table; + table.load=phys_readd(table_addr); + if (table.block.p) { + X86PageEntry entry; + Bitu entry_addr=(table.block.base<<12)+(sel & 0x3ff)*4; + entry.load=phys_readd(entry_addr); + sprintf(out1,"page %05Xxxx -> %04Xxxx flags [puw] %x:%x::%x:%x::%x:%x",sel,entry.block.base,entry.block.p,table.block.p,entry.block.us,table.block.us,entry.block.wr,table.block.wr); + LOG(LOG_MISC,LOG_ERROR)(out1); + } else { + sprintf(out1,"pagetable %03X not present, flags [puw] %x::%x::%x",(sel >> 10),table.block.p,table.block.us,table.block.wr); + LOG(LOG_MISC,LOG_ERROR)(out1); + } + } + } +}; + +static void LogCPUInfo(void) { + char out1[512]; + sprintf(out1,"cr0:%08X cr2:%08X cr3:%08X cpl=%x",cpu.cr0,paging.cr2,paging.cr3,cpu.cpl); + LOG(LOG_MISC,LOG_ERROR)(out1); + sprintf(out1,"eflags:%08X [vm=%x iopl=%x nt=%x]",reg_flags,GETFLAG(VM)>>17,GETFLAG(IOPL)>>12,GETFLAG(NT)>>14); + LOG(LOG_MISC,LOG_ERROR)(out1); + sprintf(out1,"GDT base=%08X limit=%08X",cpu.gdt.GetBase(),cpu.gdt.GetLimit()); + LOG(LOG_MISC,LOG_ERROR)(out1); + sprintf(out1,"IDT base=%08X limit=%08X",cpu.idt.GetBase(),cpu.idt.GetLimit()); + LOG(LOG_MISC,LOG_ERROR)(out1); + + Bitu sel=CPU_STR(); + Descriptor desc; + if (cpu.gdt.GetDescriptor(sel,desc)) { + sprintf(out1,"TR selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); + LOG(LOG_MISC,LOG_ERROR)(out1); + } + sel=CPU_SLDT(); + if (cpu.gdt.GetDescriptor(sel,desc)) { + sprintf(out1,"LDT selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); + LOG(LOG_MISC,LOG_ERROR)(out1); + } +}; + +#if C_HEAVY_DEBUG +static void LogInstruction(Bit16u segValue, Bit32u eipValue, ofstream& out) { + static char empty[23] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0 }; + + PhysPt start = GetAddress(segValue,eipValue); + char dline[200];Bitu size; + size = DasmI386(dline, start, reg_eip, cpu.code.big); + char* res = empty; + if (showExtend && (cpuLogType > 0) ) { + res = AnalyzeInstruction(dline,false); + if (!res || !(*res)) res = empty; + Bitu reslen = strlen(res); + if (reslen<22) for (Bitu i=0; i<22-reslen; i++) res[reslen+i] = ' '; res[22] = 0; + }; + Bitu len = strlen(dline); + if (len<30) for (Bitu i=0; i<30-len; i++) dline[len + i] = ' '; dline[30] = 0; + + // Get register values + + if(cpuLogType == 0) { + out << setw(4) << SegValue(cs) << ":" << setw(4) << reg_eip << " " << dline; + } else if (cpuLogType == 1) { + out << setw(4) << SegValue(cs) << ":" << setw(8) << reg_eip << " " << dline << " " << res; + } else if (cpuLogType == 2) { + char ibytes[200]=""; char tmpc[200]; + for (Bitu i=0; i0) << " Z" << (get_ZF()>0) + << " S" << (get_SF()>0) << " O" << (get_OF()>0) << " I" << GETFLAGBOOL(IF); + } else { + out << " FS:" << setw(4) << SegValue(fs) << " GS:" << setw(4) << SegValue(gs) + << " SS:" << setw(4) << SegValue(ss) + << " CF:" << (get_CF()>0) << " ZF:" << (get_ZF()>0) << " SF:" << (get_SF()>0) + << " OF:" << (get_OF()>0) << " AF:" << (get_AF()>0) << " PF:" << (get_PF()>0) + << " IF:" << GETFLAGBOOL(IF); + } + if(cpuLogType == 2) { + out << " TF:" << GETFLAGBOOL(TF) << " VM:" << GETFLAGBOOL(VM) <<" FLG:" << setw(8) << reg_flags + << " CR0:" << setw(8) << cpu.cr0; + } + out << endl; +}; +#endif + +// DEBUG.COM stuff + +class DEBUG : public Program { +public: + DEBUG() { pDebugcom = this; active = false; }; + ~DEBUG() { pDebugcom = 0; }; + + bool IsActive() { return active; }; + + void Run(void) + { + if(cmd->FindExist("/NOMOUSE",false)) { + real_writed(0,0x33<<2,0); + return; + } + + char filename[128]; + char args[256]; + + cmd->FindCommand(1,temp_line); + safe_strncpy(filename,temp_line.c_str(),128); + // Read commandline + Bit16u i =2; + args[0] = 0; + for (;cmd->FindCommand(i++,temp_line)==true;) { + strncat(args,temp_line.c_str(),256); + strncat(args," ",256); + } + // Start new shell and execute prog + active = true; + // Save cpu state.... + Bit16u oldcs = SegValue(cs); + Bit32u oldeip = reg_eip; + Bit16u oldss = SegValue(ss); + Bit32u oldesp = reg_esp; + + // Workaround : Allocate Stack Space + Bit16u segment; + Bit16u size = 0x200 / 0x10; + if (DOS_AllocateMemory(&segment,&size)) { + SegSet16(ss,segment); + reg_sp = 0x200; + // Start shell + DOS_Shell shell; + shell.Execute(filename,args); + DOS_FreeMemory(segment); + } + // set old reg values + SegSet16(ss,oldss); + reg_esp = oldesp; + SegSet16(cs,oldcs); + reg_eip = oldeip; + }; + +private: + bool active; +}; + +void DEBUG_CheckExecuteBreakpoint(Bit16u seg, Bit32u off) +{ + if (pDebugcom && pDebugcom->IsActive()) { + CBreakpoint::AddBreakpoint(seg,off,true); + CBreakpoint::ActivateBreakpoints(SegPhys(cs)+reg_eip,true); + pDebugcom = 0; + }; +}; + +Bitu DEBUG_EnableDebugger(void) +{ + exitLoop = true; + DEBUG_Enable(true); + CPU_Cycles=CPU_CycleLeft=0; + return 0; +}; + +static void DEBUG_ProgramStart(Program * * make) { + *make=new DEBUG; +} + +// INIT + +void DEBUG_SetupConsole(void) { + #ifdef WIN32 + WIN32_Console(); + #else + tcgetattr(0,&consolesettings); + printf("\e[8;50;80t"); //resize terminal + fflush(NULL); + #endif + memset((void *)&dbg,0,sizeof(dbg)); + debugging=false; +// dbg.active_win=3; + /* Start the Debug Gui */ + DBGUI_StartUp(); +} + +void DEBUG_ShutDown(Section * /*sec*/) { + CBreakpoint::DeleteAll(); + CDebugVar::DeleteAll(); + curs_set(old_cursor_state); + endwin(); + #ifndef WIN32 + tcsetattr(0, TCSANOW,&consolesettings); +// printf("\e[0m\e[2J"); //Seems to destroy scrolling + printf("\ec"); + fflush(NULL); + #endif +} + +Bitu debugCallback; + +void DEBUG_Init(Section* sec) { + +// MSG_Add("DEBUG_CONFIGFILE_HELP","Debugger related options.\n"); + DEBUG_DrawScreen(); + /* Add some keyhandlers */ + MAPPER_AddHandler(DEBUG_Enable,MK_pause,MMOD2,"debugger","Debugger"); + /* Reset code overview and input line */ + memset((void*)&codeViewData,0,sizeof(codeViewData)); + /* setup debug.com */ + PROGRAMS_MakeFile("DEBUG.COM",DEBUG_ProgramStart); + /* Setup callback */ + debugCallback=CALLBACK_Allocate(); + CALLBACK_Setup(debugCallback,DEBUG_EnableDebugger,CB_RETF,"debugger"); + /* shutdown function */ + sec->AddDestroyFunction(&DEBUG_ShutDown); +} + +// DEBUGGING VAR STUFF + +void CDebugVar::InsertVariable(char* name, PhysPt adr) +{ + varList.push_back(new CDebugVar(name,adr)); +}; + +void CDebugVar::DeleteAll(void) +{ + std::list::iterator i; + CDebugVar* bp; + for(i=varList.begin(); i != varList.end(); i++) { + bp = static_cast(*i); + delete bp; + }; + (varList.clear)(); +}; + +CDebugVar* CDebugVar::FindVar(PhysPt pt) +{ + std::list::iterator i; + CDebugVar* bp; + for(i=varList.begin(); i != varList.end(); i++) { + bp = static_cast(*i); + if (bp->GetAdr()==pt) return bp; + }; + return 0; +}; + +bool CDebugVar::SaveVars(char* name) { + if (varList.size()>65535) return false; + + FILE* f = fopen(name,"wb+"); + if (!f) return false; + + // write number of vars + Bit16u num = (Bit16u)varList.size(); + fwrite(&num,1,sizeof(num),f); + + std::list::iterator i; + CDebugVar* bp; + for(i=varList.begin(); i != varList.end(); i++) { + bp = static_cast(*i); + // name + fwrite(bp->GetName(),1,16,f); + // adr + PhysPt adr = bp->GetAdr(); + fwrite(&adr,1,sizeof(adr),f); + }; + fclose(f); + return true; +}; + +bool CDebugVar::LoadVars(char* name) +{ + FILE* f = fopen(name,"rb"); + if (!f) return false; + + // read number of vars + Bit16u num; + fread(&num,1,sizeof(num),f); + + for (Bit16u i=0; i16) { + sprintf(buffer,"%04X:%04X ",seg,ofs1); + for (Bit16u x=0; x<16; x++) { + Bit8u value; + if (mem_readb_checked(GetAddress(seg,ofs1+x),&value)) sprintf(temp,"%s","?? "); + else sprintf(temp,"%02X ",value); + strcat(buffer,temp); + } + ofs1+=16; + num-=16; + + fprintf(f,"%s\n",buffer); + } + if (num>0) { + sprintf(buffer,"%04X:%04X ",seg,ofs1); + for (Bit16u x=0; x::iterator i; + CDebugVar *dv; + char buffer[DEBUG_VAR_BUF_LEN]; + + int idx = 0; + for(i=CDebugVar::varList.begin(); i != CDebugVar::varList.end(); i++, idx++) { + + if (idx == 4*3) { + /* too many variables */ + break; + } + + dv = static_cast(*i); + + Bit16u value; + if (mem_readw_checked(dv->GetAdr(),&value)) + snprintf(buffer,DEBUG_VAR_BUF_LEN, "%s", "??????"); + else + snprintf(buffer,DEBUG_VAR_BUF_LEN, "0x%04x", value); + + int y = idx / 3; + int x = (idx % 3) * 26; + mvwprintw(dbg.win_var, y, x, dv->GetName()); + mvwprintw(dbg.win_var, y, (x + DEBUG_VAR_BUF_LEN + 1) , buffer); + } + + wrefresh(dbg.win_var); +}; +#undef DEBUG_VAR_BUF_LEN +// HEAVY DEBUGGING STUFF + +#if C_HEAVY_DEBUG + +const Bit32u LOGCPUMAX = 20000; + +static Bit32u logCount = 0; + +struct TLogInst { + Bit16u s_cs; + Bit32u eip; + Bit32u eax; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + Bit32u esi; + Bit32u edi; + Bit32u ebp; + Bit32u esp; + Bit16u s_ds; + Bit16u s_es; + Bit16u s_fs; + Bit16u s_gs; + Bit16u s_ss; + bool c; + bool z; + bool s; + bool o; + bool a; + bool p; + bool i; + char dline[31]; + char res[23]; +}; + +TLogInst logInst[LOGCPUMAX]; + +void DEBUG_HeavyLogInstruction(void) { + + static char empty[23] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0 }; + + PhysPt start = GetAddress(SegValue(cs),reg_eip); + char dline[200]; + DasmI386(dline, start, reg_eip, cpu.code.big); + char* res = empty; + if (showExtend) { + res = AnalyzeInstruction(dline,false); + if (!res || !(*res)) res = empty; + Bitu reslen = strlen(res); + if (reslen<22) for (Bitu i=0; i<22-reslen; i++) res[reslen+i] = ' '; res[22] = 0; + }; + + Bitu len = strlen(dline); + if (len < 30) for (Bitu i=0; i < 30-len; i++) dline[len+i] = ' '; + dline[30] = 0; + + TLogInst & inst = logInst[logCount]; + strcpy(inst.dline,dline); + inst.s_cs = SegValue(cs); + inst.eip = reg_eip; + strcpy(inst.res,res); + inst.eax = reg_eax; + inst.ebx = reg_ebx; + inst.ecx = reg_ecx; + inst.edx = reg_edx; + inst.esi = reg_esi; + inst.edi = reg_edi; + inst.ebp = reg_ebp; + inst.esp = reg_esp; + inst.s_ds = SegValue(ds); + inst.s_es = SegValue(es); + inst.s_fs = SegValue(fs); + inst.s_gs = SegValue(gs); + inst.s_ss = SegValue(ss); + inst.c = get_CF()>0; + inst.z = get_ZF()>0; + inst.s = get_SF()>0; + inst.o = get_OF()>0; + inst.a = get_AF()>0; + inst.p = get_PF()>0; + inst.i = GETFLAGBOOL(IF); + + if (++logCount >= LOGCPUMAX) logCount = 0; +}; + +void DEBUG_HeavyWriteLogInstruction(void) { + if (!logHeavy) return; + logHeavy = false; + + DEBUG_ShowMsg("DEBUG: Creating cpu log LOGCPU_INT_CD.TXT\n"); + + ofstream out("LOGCPU_INT_CD.TXT"); + if (!out.is_open()) { + DEBUG_ShowMsg("DEBUG: Failed.\n"); + return; + } + out << hex << noshowbase << setfill('0') << uppercase; + Bit32u startLog = logCount; + do { + // Write Instructions + TLogInst & inst = logInst[startLog]; + out << setw(4) << inst.s_cs << ":" << setw(8) << inst.eip << " " + << inst.dline << " " << inst.res << " EAX:" << setw(8)<< inst.eax + << " EBX:" << setw(8) << inst.ebx << " ECX:" << setw(8) << inst.ecx + << " EDX:" << setw(8) << inst.edx << " ESI:" << setw(8) << inst.esi + << " EDI:" << setw(8) << inst.edi << " EBP:" << setw(8) << inst.ebp + << " ESP:" << setw(8) << inst.esp << " DS:" << setw(4) << inst.s_ds + << " ES:" << setw(4) << inst.s_es<< " FS:" << setw(4) << inst.s_fs + << " GS:" << setw(4) << inst.s_gs<< " SS:" << setw(4) << inst.s_ss + << " CF:" << inst.c << " ZF:" << inst.z << " SF:" << inst.s + << " OF:" << inst.o << " AF:" << inst.a << " PF:" << inst.p + << " IF:" << inst.i << endl; + +/* fprintf(f,"%04X:%08X %s %s EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X EBP:%08X ESP:%08X DS:%04X ES:%04X FS:%04X GS:%04X SS:%04X CF:%01X ZF:%01X SF:%01X OF:%01X AF:%01X PF:%01X IF:%01X\n", + logInst[startLog].s_cs,logInst[startLog].eip,logInst[startLog].dline,logInst[startLog].res,logInst[startLog].eax,logInst[startLog].ebx,logInst[startLog].ecx,logInst[startLog].edx,logInst[startLog].esi,logInst[startLog].edi,logInst[startLog].ebp,logInst[startLog].esp, + logInst[startLog].s_ds,logInst[startLog].s_es,logInst[startLog].s_fs,logInst[startLog].s_gs,logInst[startLog].s_ss, + logInst[startLog].c,logInst[startLog].z,logInst[startLog].s,logInst[startLog].o,logInst[startLog].a,logInst[startLog].p,logInst[startLog].i);*/ + if (++startLog >= LOGCPUMAX) startLog = 0; + } while (startLog != logCount); + + out.close(); + DEBUG_ShowMsg("DEBUG: Done.\n"); +}; + +bool DEBUG_HeavyIsBreakpoint(void) { + static Bitu zero_count = 0; + if (cpuLog) { + if (cpuLogCounter>0) { + LogInstruction(SegValue(cs),reg_eip,cpuLogFile); + cpuLogCounter--; + } + if (cpuLogCounter<=0) { + cpuLogFile.close(); + DEBUG_ShowMsg("DEBUG: cpu log LOGCPU.TXT created\n"); + cpuLog = false; + DEBUG_EnableDebugger(); + return true; + } + } + // LogInstruction + if (logHeavy) DEBUG_HeavyLogInstruction(); + if (zeroProtect) { + Bit32u value=0; + if (!mem_readd_checked(SegPhys(cs)+reg_eip,&value)) { + if (value == 0) zero_count++; + else zero_count = 0; + } + if (GCC_UNLIKELY(zero_count == 10)) E_Exit("running zeroed code"); + } + + if (skipFirstInstruction) { + skipFirstInstruction = false; + return false; + } + if (CBreakpoint::CheckBreakpoint(SegValue(cs),reg_eip)) { + return true; + } + return false; +} + +#endif // HEAVY DEBUG + + +#endif // DEBUG + + diff --git a/src/debug/debug_disasm.cpp b/src/debug/debug_disasm.cpp index 6d8d5bc8..c5fa5f12 100644 --- a/src/debug/debug_disasm.cpp +++ b/src/debug/debug_disasm.cpp @@ -63,7 +63,7 @@ Any comments/updates/bug reports to: */ #include "dosbox.h" -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER #include #include #include diff --git a/src/debug/debug_gui.cpp b/src/debug/debug_gui.cpp index 60166d91..8f9069b7 100644 --- a/src/debug/debug_gui.cpp +++ b/src/debug/debug_gui.cpp @@ -32,10 +32,6 @@ #include "debug.h" #include "debug_inc.h" -struct _LogGroup { - char const* front; - bool enabled; -}; #include #include using namespace std; @@ -45,12 +41,10 @@ static list logBuff; static list::iterator logBuffPos = logBuff.end(); static _LogGroup loggrp[LOG_MAX]={{"",true},{0,false}}; -static FILE* debuglog; +extern FILE* debuglog; extern int old_cursor_state; - - void DEBUG_ShowMsg(char const* format,...) { char buf[512]; @@ -100,18 +94,6 @@ void DEBUG_RefreshPage(char scroll) { wrefresh(dbg.win_out); } -void LOG::operator() (char const* format, ...){ - char buf[512]; - va_list msg; - va_start(msg,format); - vsprintf(buf,format,msg); - va_end(msg); - - if (d_type>=LOG_MAX) return; - if ((d_severity!=LOG_ERROR) && (!loggrp[d_type].enabled)) return; - DEBUG_ShowMsg("%10u: %s:%s\n",cycle_count,loggrp[d_type].front,buf); -} - static void Draw_RegisterLayout(void) { mvwaddstr(dbg.win_reg,0,0,"EAX="); @@ -193,76 +175,6 @@ static void MakePairs(void) { init_pair(PAIR_BLACK_GREY, COLOR_BLACK /*| FOREGROUND_INTENSITY */, COLOR_WHITE); init_pair(PAIR_GREY_RED, COLOR_WHITE/*| FOREGROUND_INTENSITY */, COLOR_RED); } -static void LOG_Destroy(Section*) { - if(debuglog) fclose(debuglog); -} - -static void LOG_Init(Section * sec) { - Section_prop * sect=static_cast(sec); - const char * blah=sect->Get_string("logfile"); - if(blah && blah[0] &&(debuglog = fopen(blah,"wt+"))){ - }else{ - debuglog=0; - } - sect->AddDestroyFunction(&LOG_Destroy); - char buf[1024]; - for (Bitu i=1;iGet_bool(buf); - } -} - - -void LOG_StartUp(void) { - /* Setup logging groups */ - loggrp[LOG_ALL].front="ALL"; - loggrp[LOG_VGA].front="VGA"; - loggrp[LOG_VGAGFX].front="VGAGFX"; - loggrp[LOG_VGAMISC].front="VGAMISC"; - loggrp[LOG_INT10].front="INT10"; - loggrp[LOG_SB].front="SBLASTER"; - loggrp[LOG_DMACONTROL].front="DMA_CONTROL"; - - loggrp[LOG_FPU].front="FPU"; - loggrp[LOG_CPU].front="CPU"; - loggrp[LOG_PAGING].front="PAGING"; - - loggrp[LOG_FCB].front="FCB"; - loggrp[LOG_FILES].front="FILES"; - loggrp[LOG_IOCTL].front="IOCTL"; - loggrp[LOG_EXEC].front="EXEC"; - loggrp[LOG_DOSMISC].front="DOSMISC"; - - loggrp[LOG_PIT].front="PIT"; - loggrp[LOG_KEYBOARD].front="KEYBOARD"; - loggrp[LOG_PIC].front="PIC"; - - loggrp[LOG_MOUSE].front="MOUSE"; - loggrp[LOG_BIOS].front="BIOS"; - loggrp[LOG_GUI].front="GUI"; - loggrp[LOG_MISC].front="MISC"; - - loggrp[LOG_IO].front="IO"; - loggrp[LOG_PCI].front="PCI"; - - /* Register the log section */ - Section_prop * sect=control->AddSection_prop("log",LOG_Init); - Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,""); - Pstring->Set_help("file where the log messages will be saved to"); - char buf[1024]; - for (Bitu i=1;iAdd_bool(buf,Property::Changeable::Always,true); - Pbool->Set_help("Enable/Disable logging of this type."); - } -// MSG_Add("LOG_CONFIGFILE_HELP","Logging related options for the debugger.\n"); -} - - - void DBGUI_StartUp(void) { /* Start the main window */ @@ -277,10 +189,11 @@ void DBGUI_StartUp(void) { #endif old_cursor_state = curs_set(0); start_color(); - cycle_count=0; + DEBUG_cycle_count=0; MakePairs(); MakeSubWindows(); } #endif + diff --git a/src/debug/debug_helpers.cpp b/src/debug/debug_helpers.cpp new file mode 100644 index 00000000..7261a406 --- /dev/null +++ b/src/debug/debug_helpers.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "dosbox.h" +#if C_DEBUG || C_GDBSERVER + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "debug.h" +#include "cross.h" //snprintf +#include "cpu.h" +#include "video.h" +#include "pic.h" +#include "mapper.h" +#include "cpu.h" +#include "callback.h" +#include "inout.h" +#include "mixer.h" +#include "timer.h" +#include "paging.h" +#include "support.h" +#include "shell.h" +#include "programs.h" +#include "debug_inc.h" +#include "../cpu/lazyflags.h" +#include "keyboard.h" +#include "setup.h" + +bool DEBUG_exitLoop = false; +Bit16u DEBUG_dataSeg; +Bit32u DEBUG_dataOfs; +char DEBUG_curSelectorName[3] = { 0,0,0 }; + +Bit32u PhysMakeProt(Bit16u selector, Bit32u offset) +{ + Descriptor desc; + if (cpu.gdt.GetDescriptor(selector,desc)) return desc.GetBase()+offset; + return 0; +}; + +Bit32u DEBUG_GetAddress(Bit16u seg, Bit32u offset) +{ + if (seg==SegValue(cs)) return SegPhys(cs)+offset; + if (cpu.pmode && !(reg_flags & FLAG_VM)) { + Descriptor desc; + if (cpu.gdt.GetDescriptor(seg,desc)) return PhysMakeProt(seg,offset); + } + return (seg<<4)+offset; +} + +static char empty_sel[] = { ' ',' ',0 }; + +Bit32u DEBUG_GetHexValue(char* str, char*& hex) +{ + Bit32u value = 0; + Bit32u regval = 0; + hex = str; + while (*hex==' ') hex++; + if (strstr(hex,"EAX")==hex) { hex+=3; regval = reg_eax; }; + if (strstr(hex,"EBX")==hex) { hex+=3; regval = reg_ebx; }; + if (strstr(hex,"ECX")==hex) { hex+=3; regval = reg_ecx; }; + if (strstr(hex,"EDX")==hex) { hex+=3; regval = reg_edx; }; + if (strstr(hex,"ESI")==hex) { hex+=3; regval = reg_esi; }; + if (strstr(hex,"EDI")==hex) { hex+=3; regval = reg_edi; }; + if (strstr(hex,"EBP")==hex) { hex+=3; regval = reg_ebp; }; + if (strstr(hex,"ESP")==hex) { hex+=3; regval = reg_esp; }; + if (strstr(hex,"EIP")==hex) { hex+=3; regval = reg_eip; }; + if (strstr(hex,"AX")==hex) { hex+=2; regval = reg_ax; }; + if (strstr(hex,"BX")==hex) { hex+=2; regval = reg_bx; }; + if (strstr(hex,"CX")==hex) { hex+=2; regval = reg_cx; }; + if (strstr(hex,"DX")==hex) { hex+=2; regval = reg_dx; }; + if (strstr(hex,"SI")==hex) { hex+=2; regval = reg_si; }; + if (strstr(hex,"DI")==hex) { hex+=2; regval = reg_di; }; + if (strstr(hex,"BP")==hex) { hex+=2; regval = reg_bp; }; + if (strstr(hex,"SP")==hex) { hex+=2; regval = reg_sp; }; + if (strstr(hex,"IP")==hex) { hex+=2; regval = reg_ip; }; + if (strstr(hex,"CS")==hex) { hex+=2; regval = SegValue(cs); }; + if (strstr(hex,"DS")==hex) { hex+=2; regval = SegValue(ds); }; + if (strstr(hex,"ES")==hex) { hex+=2; regval = SegValue(es); }; + if (strstr(hex,"FS")==hex) { hex+=2; regval = SegValue(fs); }; + if (strstr(hex,"GS")==hex) { hex+=2; regval = SegValue(gs); }; + if (strstr(hex,"SS")==hex) { hex+=2; regval = SegValue(ss); }; + + while (*hex) { + if ((*hex>='0') && (*hex<='9')) value = (value<<4)+*hex-'0'; + else if ((*hex>='A') && (*hex<='F')) value = (value<<4)+*hex-'A'+10; + else { + if(*hex == '+') {hex++;return regval + value + DEBUG_GetHexValue(hex,hex); }; + if(*hex == '-') {hex++;return regval + value - DEBUG_GetHexValue(hex,hex); }; + break; // No valid char + } + hex++; + }; + return regval + value; +} + +bool DEBUG_GetDescriptorInfo(char* selname, char* out1, char* out2) +{ + Bitu sel; + Descriptor desc; + + if (strstr(selname,"cs") || strstr(selname,"CS")) sel = SegValue(cs); + else if (strstr(selname,"ds") || strstr(selname,"DS")) sel = SegValue(ds); + else if (strstr(selname,"es") || strstr(selname,"ES")) sel = SegValue(es); + else if (strstr(selname,"fs") || strstr(selname,"FS")) sel = SegValue(fs); + else if (strstr(selname,"gs") || strstr(selname,"GS")) sel = SegValue(gs); + else if (strstr(selname,"ss") || strstr(selname,"SS")) sel = SegValue(ss); + else { + sel = DEBUG_GetHexValue(selname,selname); + if (*selname==0) selname=empty_sel; + } + if (cpu.gdt.GetDescriptor(sel,desc)) { + switch (desc.Type()) { + case DESC_TASK_GATE: + sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type); + sprintf(out2," TaskGate dpl : %01X %1X",desc.saved.gate.dpl,desc.saved.gate.p); + return true; + case DESC_LDT: + case DESC_286_TSS_A: + case DESC_286_TSS_B: + case DESC_386_TSS_A: + case DESC_386_TSS_B: + sprintf(out1,"%s: b:%08X type:%02X pag",selname,desc.GetBase(),desc.saved.seg.type); + sprintf(out2," l:%08X dpl : %01X %1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.g); + return true; + case DESC_286_CALL_GATE: + case DESC_386_CALL_GATE: + sprintf(out1,"%s: s:%08X type:%02X p params: %02X",selname,desc.GetSelector(),desc.saved.gate.type,desc.saved.gate.paramcount); + sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p); + return true; + case DESC_286_INT_GATE: + case DESC_286_TRAP_GATE: + case DESC_386_INT_GATE: + case DESC_386_TRAP_GATE: + sprintf(out1,"%s: s:%08X type:%02X p",selname,desc.GetSelector(),desc.saved.gate.type); + sprintf(out2," o:%08X dpl : %01X %1X",desc.GetOffset(),desc.saved.gate.dpl,desc.saved.gate.p); + return true; + } + sprintf(out1,"%s: b:%08X type:%02X parbg",selname,desc.GetBase(),desc.saved.seg.type); + sprintf(out2," l:%08X dpl : %01X %1X%1X%1X%1X%1X",desc.GetLimit(),desc.saved.seg.dpl,desc.saved.seg.p,desc.saved.seg.avl,desc.saved.seg.r,desc.saved.seg.big,desc.saved.seg.g); + return true; + } else { + strcpy(out1," "); + strcpy(out2," "); + } + return false; +}; + +char* DEBUG_AnalyzeInstruction(char* inst, bool saveSelector) { + static char result[256]; + + char instu[256]; + char prefix[3]; + Bit16u seg; + + strcpy(instu,inst); + upcase(instu); + + result[0] = 0; + char* pos = strchr(instu,'['); + if (pos) { + // Segment prefix ? + if (*(pos-1)==':') { + char* segpos = pos-3; + prefix[0] = tolower(*segpos); + prefix[1] = tolower(*(segpos+1)); + prefix[2] = 0; + seg = (Bit16u)DEBUG_GetHexValue(segpos,segpos); + } else { + if (strstr(pos,"SP") || strstr(pos,"BP")) { + seg = SegValue(ss); + strcpy(prefix,"ss"); + } else { + seg = SegValue(ds); + strcpy(prefix,"ds"); + }; + }; + + pos++; + Bit32u adr = DEBUG_GetHexValue(pos,pos); + while (*pos!=']') { + if (*pos=='+') { + pos++; + adr += DEBUG_GetHexValue(pos,pos); + } else if (*pos=='-') { + pos++; + adr -= DEBUG_GetHexValue(pos,pos); + } else + pos++; + }; + Bit32u address = DEBUG_GetAddress(seg,adr); + if (!(get_tlb_readhandler(address)->flags & PFLAG_INIT)) { + static char outmask[] = "%s:[%04X]=%02X"; + + if (cpu.pmode) outmask[6] = '8'; + switch (DasmLastOperandSize()) { + case 8 : { Bit8u val = mem_readb(address); + outmask[12] = '2'; + sprintf(result,outmask,prefix,adr,val); + } break; + case 16: { Bit16u val = mem_readw(address); + outmask[12] = '4'; + sprintf(result,outmask,prefix,adr,val); + } break; + case 32: { Bit32u val = mem_readd(address); + outmask[12] = '8'; + sprintf(result,outmask,prefix,adr,val); + } break; + } + } else { + sprintf(result,"[illegal]"); + } +#ifndef C_GDBSERVER + // Variable found ? + CDebugVar* var = CDebugVar::FindVar(address); + if (var) { + // Replace occurance + char* pos1 = strchr(inst,'['); + char* pos2 = strchr(inst,']'); + if (pos1 && pos2) { + char temp[256]; + strcpy(temp,pos2); // save end + pos1++; *pos1 = 0; // cut after '[' + strcat(inst,var->GetName()); // add var name + strcat(inst,temp); // add end + }; + }; +#endif + // show descriptor info, if available + if ((cpu.pmode) && saveSelector) { + strcpy(DEBUG_curSelectorName,prefix); + }; + }; + // If it is a callback add additional info + pos = strstr(inst,"callback"); + if (pos) { + pos += 9; + Bitu nr = DEBUG_GetHexValue(pos,pos); + const char* descr = CALLBACK_GetDescription(nr); + if (descr) { + strcat(inst," ("); strcat(inst,descr); strcat(inst,")"); + } + }; + // Must be a jump + if (instu[0] == 'J') + { + bool jmp = false; + switch (instu[1]) { + case 'A' : { jmp = (get_CF()?false:true) && (get_ZF()?false:true); // JA + } break; + case 'B' : { if (instu[2] == 'E') { + jmp = (get_CF()?true:false) || (get_ZF()?true:false); // JBE + } else { + jmp = get_CF()?true:false; // JB + } + } break; + case 'C' : { if (instu[2] == 'X') { + jmp = reg_cx == 0; // JCXZ + } else { + jmp = get_CF()?true:false; // JC + } + } break; + case 'E' : { jmp = get_ZF()?true:false; // JE + } break; + case 'G' : { if (instu[2] == 'E') { + jmp = (get_SF()?true:false)==(get_OF()?true:false); // JGE + } else { + jmp = (get_ZF()?false:true) && ((get_SF()?true:false)==(get_OF()?true:false)); // JG + } + } break; + case 'L' : { if (instu[2] == 'E') { + jmp = (get_ZF()?true:false) || ((get_SF()?true:false)!=(get_OF()?true:false)); // JLE + } else { + jmp = (get_SF()?true:false)!=(get_OF()?true:false); // JL + } + } break; + case 'M' : { jmp = true; // JMP + } break; + case 'N' : { switch (instu[2]) { + case 'B' : + case 'C' : { jmp = get_CF()?false:true; // JNB / JNC + } break; + case 'E' : { jmp = get_ZF()?false:true; // JNE + } break; + case 'O' : { jmp = get_OF()?false:true; // JNO + } break; + case 'P' : { jmp = get_PF()?false:true; // JNP + } break; + case 'S' : { jmp = get_SF()?false:true; // JNS + } break; + case 'Z' : { jmp = get_ZF()?false:true; // JNZ + } break; + } + } break; + case 'O' : { jmp = get_OF()?true:false; // JO + } break; + case 'P' : { if (instu[2] == 'O') { + jmp = get_PF()?false:true; // JPO + } else { + jmp = get_SF()?true:false; // JP / JPE + } + } break; + case 'S' : { jmp = get_SF()?true:false; // JS + } break; + case 'Z' : { jmp = get_ZF()?true:false; // JZ + } break; + } + if (jmp) { + pos = strchr(instu,'$'); + if (pos) { + pos = strchr(instu,'+'); + if (pos) { + strcpy(result,"(down)"); + } else { + strcpy(result,"(up)"); + } + } + } else { + sprintf(result,"(no jmp)"); + } + } + return result; +}; + +#endif diff --git a/src/debug/debug_log.cpp b/src/debug/debug_log.cpp new file mode 100644 index 00000000..921ab20e --- /dev/null +++ b/src/debug/debug_log.cpp @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "dosbox.h" +#if C_DEBUG || C_GDBSERVER + +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +#include "control.h" +#include "debug.h" +#include "cross.h" //snprintf +#include "cpu.h" +#include "video.h" +#include "pic.h" +#include "mapper.h" +#include "cpu.h" +#include "callback.h" +#include "inout.h" +#include "mixer.h" +#include "timer.h" +#include "paging.h" +#include "support.h" +#include "shell.h" +#include "programs.h" +#include "debug_inc.h" +#include "../cpu/lazyflags.h" +#include "keyboard.h" +#include "setup.h" + +// Heavy Debugging Vars for logging +#if C_HEAVY_DEBUG || C_GDBSERVER +ofstream DEBUG_cpuLogFile; +bool DEBUG_cpuLog = false; +int DEBUG_cpuLogCounter = 0; +int DEBUG_cpuLogType = 1; // log detail +bool DEBUG_zeroProtect = false; +bool DEBUG_logHeavy = false; +#endif + +bool DEBUG_showExtend = true; + +// Display the content of the MCB chain starting with the MCB at the specified segment. +void DEBUG_LogMCBChain(Bit16u mcb_segment) { + DOS_MCB mcb(mcb_segment); + char filename[9]; // 8 characters plus a terminating NUL + const char *psp_seg_note; + PhysPt dataAddr = PhysMake(DEBUG_dataSeg,DEBUG_dataOfs);// location being viewed in the "Data Overview" + + // loop forever, breaking out of the loop once we've processed the last MCB + while (true) { + // verify that the type field is valid + if (mcb.GetType()!=0x4d && mcb.GetType()!=0x5a) { + LOG(LOG_MISC,LOG_ERROR)("MCB chain broken at %04X:0000!",mcb_segment); + return; + } + + mcb.GetFileName(filename); + + // some PSP segment values have special meanings + switch (mcb.GetPSPSeg()) { + case MCB_FREE: + psp_seg_note = "(free)"; + break; + case MCB_DOS: + psp_seg_note = "(DOS)"; + break; + default: + psp_seg_note = ""; + } + + LOG(LOG_MISC,LOG_ERROR)(" %04X %12u %04X %-7s %s",mcb_segment,mcb.GetSize() << 4,mcb.GetPSPSeg(), psp_seg_note, filename); + + // print a message if dataAddr is within this MCB's memory range + PhysPt mcbStartAddr = PhysMake(mcb_segment+1,0); + PhysPt mcbEndAddr = PhysMake(mcb_segment+1+mcb.GetSize(),0); + if (dataAddr >= mcbStartAddr && dataAddr < mcbEndAddr) { + LOG(LOG_MISC,LOG_ERROR)(" (data addr %04hX:%04X is %u bytes past this MCB)",DEBUG_dataSeg,DEBUG_dataOfs,dataAddr - mcbStartAddr); + } + + // if we've just processed the last MCB in the chain, break out of the loop + if (mcb.GetType()==0x5a) { + break; + } + // else, move to the next MCB in the chain + mcb_segment+=mcb.GetSize()+1; + mcb.SetPt(mcb_segment); + } +} + +// Display the content of all Memory Control Blocks. +void DEBUG_LogMCBS(void) +{ + LOG(LOG_MISC,LOG_ERROR)("MCB Seg Size (bytes) PSP Seg (notes) Filename"); + LOG(LOG_MISC,LOG_ERROR)("Conventional memory:"); + DEBUG_LogMCBChain(dos.firstMCB); + + LOG(LOG_MISC,LOG_ERROR)("Upper memory:"); + DEBUG_LogMCBChain(dos_infoblock.GetStartOfUMBChain()); +} + +void DEBUG_LogGDT(void) +{ + char out1[512]; + Descriptor desc; + Bitu length = cpu.gdt.GetLimit(); + PhysPt address = cpu.gdt.GetBase(); + PhysPt max = address + length; + Bitu i = 0; + LOG(LOG_MISC,LOG_ERROR)("GDT Base:%08X Limit:%08X",address,length); + while (address> 10)*4; + X86PageEntry table; + table.load=phys_readd(table_addr); + if (table.block.p) { + X86PageEntry entry; + Bitu entry_addr=(table.block.base<<12)+(i & 0x3ff)*4; + entry.load=phys_readd(entry_addr); + if (entry.block.p) { + sprintf(out1,"page %05Xxxx -> %04Xxxx flags [uw] %x:%x::%x:%x [d=%x|a=%x]", + i,entry.block.base,entry.block.us,table.block.us, + entry.block.wr,table.block.wr,entry.block.d,entry.block.a); + LOG(LOG_MISC,LOG_ERROR)(out1); + } + } + } + } else { + Bitu table_addr=(paging.base.page<<12)+(sel >> 10)*4; + X86PageEntry table; + table.load=phys_readd(table_addr); + if (table.block.p) { + X86PageEntry entry; + Bitu entry_addr=(table.block.base<<12)+(sel & 0x3ff)*4; + entry.load=phys_readd(entry_addr); + sprintf(out1,"page %05Xxxx -> %04Xxxx flags [puw] %x:%x::%x:%x::%x:%x",sel,entry.block.base,entry.block.p,table.block.p,entry.block.us,table.block.us,entry.block.wr,table.block.wr); + LOG(LOG_MISC,LOG_ERROR)(out1); + } else { + sprintf(out1,"pagetable %03X not present, flags [puw] %x::%x::%x",(sel >> 10),table.block.p,table.block.us,table.block.wr); + LOG(LOG_MISC,LOG_ERROR)(out1); + } + } + } +}; + +void DEBUG_LogCPUInfo(void) { + char out1[512]; + sprintf(out1,"cr0:%08X cr2:%08X cr3:%08X cpl=%x",cpu.cr0,paging.cr2,paging.cr3,cpu.cpl); + LOG(LOG_MISC,LOG_ERROR)(out1); + sprintf(out1,"eflags:%08X [vm=%x iopl=%x nt=%x]",reg_flags,GETFLAG(VM)>>17,GETFLAG(IOPL)>>12,GETFLAG(NT)>>14); + LOG(LOG_MISC,LOG_ERROR)(out1); + sprintf(out1,"GDT base=%08X limit=%08X",cpu.gdt.GetBase(),cpu.gdt.GetLimit()); + LOG(LOG_MISC,LOG_ERROR)(out1); + sprintf(out1,"IDT base=%08X limit=%08X",cpu.idt.GetBase(),cpu.idt.GetLimit()); + LOG(LOG_MISC,LOG_ERROR)(out1); + + Bitu sel=CPU_STR(); + Descriptor desc; + if (cpu.gdt.GetDescriptor(sel,desc)) { + sprintf(out1,"TR selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); + LOG(LOG_MISC,LOG_ERROR)(out1); + } + sel=CPU_SLDT(); + if (cpu.gdt.GetDescriptor(sel,desc)) { + sprintf(out1,"LDT selector=%04X, base=%08X limit=%08X*%X",sel,desc.GetBase(),desc.GetLimit(),desc.saved.seg.g?0x4000:1); + LOG(LOG_MISC,LOG_ERROR)(out1); + } +}; + +_LogGroup loggrp[LOG_MAX]={{"",true},{0,false}}; +FILE* debuglog; + +void LOG::operator() (char const* format, ...){ + char buf[512]; + va_list msg; + va_start(msg,format); + vsprintf(buf,format,msg); + va_end(msg); + + if (d_type>=LOG_MAX) return; + if ((d_severity!=LOG_ERROR) && (!loggrp[d_type].enabled)) return; + DEBUG_ShowMsg("%10u: %s:%s\n",DEBUG_cycle_count,loggrp[d_type].front,buf); +} + +static void LOG_Destroy(Section*) { + if(debuglog) fclose(debuglog); +} + +static void LOG_Init(Section * sec) { + Section_prop * sect=static_cast(sec); + const char * blah=sect->Get_string("logfile"); + if(blah && blah[0] &&(debuglog = fopen(blah,"wt+"))){ + }else{ + debuglog=0; + } + sect->AddDestroyFunction(&LOG_Destroy); + char buf[1024]; + for (Bitu i=1;iGet_bool(buf); + } +} + + +void LOG_StartUp(void) { + /* Setup logging groups */ + loggrp[LOG_ALL].front="ALL"; + loggrp[LOG_VGA].front="VGA"; + loggrp[LOG_VGAGFX].front="VGAGFX"; + loggrp[LOG_VGAMISC].front="VGAMISC"; + loggrp[LOG_INT10].front="INT10"; + loggrp[LOG_SB].front="SBLASTER"; + loggrp[LOG_DMACONTROL].front="DMA_CONTROL"; + + loggrp[LOG_FPU].front="FPU"; + loggrp[LOG_CPU].front="CPU"; + loggrp[LOG_PAGING].front="PAGING"; + + loggrp[LOG_FCB].front="FCB"; + loggrp[LOG_FILES].front="FILES"; + loggrp[LOG_IOCTL].front="IOCTL"; + loggrp[LOG_EXEC].front="EXEC"; + loggrp[LOG_DOSMISC].front="DOSMISC"; + + loggrp[LOG_PIT].front="PIT"; + loggrp[LOG_KEYBOARD].front="KEYBOARD"; + loggrp[LOG_PIC].front="PIC"; + + loggrp[LOG_MOUSE].front="MOUSE"; + loggrp[LOG_BIOS].front="BIOS"; + loggrp[LOG_GUI].front="GUI"; + loggrp[LOG_MISC].front="MISC"; + + loggrp[LOG_IO].front="IO"; + loggrp[LOG_PCI].front="PCI"; + + /* Register the log section */ + Section_prop * sect=control->AddSection_prop("log",LOG_Init); + Prop_string* Pstring = sect->Add_string("logfile",Property::Changeable::Always,""); + Pstring->Set_help("file where the log messages will be saved to"); + char buf[1024]; + for (Bitu i=1;iAdd_bool(buf,Property::Changeable::Always,true); + Pbool->Set_help("Enable/Disable logging of this type."); + } +// MSG_Add("LOG_CONFIGFILE_HELP","Logging related options for the debugger.\n"); +} + +#if C_HEAVY_DEBUG || C_GDBSERVER +void DEBUG_LogInstruction(Bit16u segValue, Bit32u eipValue, ofstream& out) { + static char empty[23] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0 }; + + PhysPt start = DEBUG_GetAddress(segValue,eipValue); + char dline[200];Bitu size; + size = DasmI386(dline, start, reg_eip, cpu.code.big); + char* res = empty; + if (DEBUG_showExtend && (DEBUG_cpuLogType > 0) ) { + res = DEBUG_AnalyzeInstruction(dline,false); + if (!res || !(*res)) res = empty; + Bitu reslen = strlen(res); + if (reslen<22) for (Bitu i=0; i<22-reslen; i++) res[reslen+i] = ' '; res[22] = 0; + }; + Bitu len = strlen(dline); + if (len<30) for (Bitu i=0; i<30-len; i++) dline[len + i] = ' '; dline[30] = 0; + + // Get register values + + if(DEBUG_cpuLogType == 0) { + out << setw(4) << SegValue(cs) << ":" << setw(4) << reg_eip << " " << dline; + } else if (DEBUG_cpuLogType == 1) { + out << setw(4) << SegValue(cs) << ":" << setw(8) << reg_eip << " " << dline << " " << res; + } else if (DEBUG_cpuLogType == 2) { + char ibytes[200]=""; char tmpc[200]; + for (Bitu i=0; i0) << " Z" << (get_ZF()>0) + << " S" << (get_SF()>0) << " O" << (get_OF()>0) << " I" << GETFLAGBOOL(IF); + } else { + out << " FS:" << setw(4) << SegValue(fs) << " GS:" << setw(4) << SegValue(gs) + << " SS:" << setw(4) << SegValue(ss) + << " CF:" << (get_CF()>0) << " ZF:" << (get_ZF()>0) << " SF:" << (get_SF()>0) + << " OF:" << (get_OF()>0) << " AF:" << (get_AF()>0) << " PF:" << (get_PF()>0) + << " IF:" << GETFLAGBOOL(IF); + } + if(DEBUG_cpuLogType == 2) { + out << " TF:" << GETFLAGBOOL(TF) << " VM:" << GETFLAGBOOL(VM) <<" FLG:" << setw(8) << reg_flags + << " CR0:" << setw(8) << cpu.cr0; + } + out << endl; +}; + +const Bit32u LOGCPUMAX = 20000; + +static Bit32u logCount = 0; + +struct TLogInst { + Bit16u s_cs; + Bit32u eip; + Bit32u eax; + Bit32u ebx; + Bit32u ecx; + Bit32u edx; + Bit32u esi; + Bit32u edi; + Bit32u ebp; + Bit32u esp; + Bit16u s_ds; + Bit16u s_es; + Bit16u s_fs; + Bit16u s_gs; + Bit16u s_ss; + bool c; + bool z; + bool s; + bool o; + bool a; + bool p; + bool i; + char dline[31]; + char res[23]; +}; + +TLogInst logInst[LOGCPUMAX]; + +void DEBUG_HeavyLogInstruction(void) { + + static char empty[23] = { 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0 }; + + PhysPt start = DEBUG_GetAddress(SegValue(cs),reg_eip); + char dline[200]; + DasmI386(dline, start, reg_eip, cpu.code.big); + char* res = empty; + if (DEBUG_showExtend) { + res = DEBUG_AnalyzeInstruction(dline,false); + if (!res || !(*res)) res = empty; + Bitu reslen = strlen(res); + if (reslen<22) for (Bitu i=0; i<22-reslen; i++) res[reslen+i] = ' '; res[22] = 0; + }; + + Bitu len = strlen(dline); + if (len < 30) for (Bitu i=0; i < 30-len; i++) dline[len+i] = ' '; + dline[30] = 0; + + TLogInst & inst = logInst[logCount]; + strcpy(inst.dline,dline); + inst.s_cs = SegValue(cs); + inst.eip = reg_eip; + strcpy(inst.res,res); + inst.eax = reg_eax; + inst.ebx = reg_ebx; + inst.ecx = reg_ecx; + inst.edx = reg_edx; + inst.esi = reg_esi; + inst.edi = reg_edi; + inst.ebp = reg_ebp; + inst.esp = reg_esp; + inst.s_ds = SegValue(ds); + inst.s_es = SegValue(es); + inst.s_fs = SegValue(fs); + inst.s_gs = SegValue(gs); + inst.s_ss = SegValue(ss); + inst.c = get_CF()>0; + inst.z = get_ZF()>0; + inst.s = get_SF()>0; + inst.o = get_OF()>0; + inst.a = get_AF()>0; + inst.p = get_PF()>0; + inst.i = GETFLAGBOOL(IF); + + if (++logCount >= LOGCPUMAX) logCount = 0; +}; + +void DEBUG_HeavyWriteLogInstruction(void) { + if (!DEBUG_logHeavy) return; + DEBUG_logHeavy = false; + + DEBUG_ShowMsg("DEBUG: Creating cpu log LOGCPU_INT_CD.TXT\n"); + + ofstream out("LOGCPU_INT_CD.TXT"); + if (!out.is_open()) { + DEBUG_ShowMsg("DEBUG: Failed.\n"); + return; + } + out << hex << noshowbase << setfill('0') << uppercase; + Bit32u startLog = logCount; + do { + // Write Intructions + TLogInst & inst = logInst[startLog]; + out << setw(4) << inst.s_cs << ":" << setw(8) << inst.eip << " " + << inst.dline << " " << inst.res << " EAX:" << setw(8)<< inst.eax + << " EBX:" << setw(8) << inst.ebx << " ECX:" << setw(8) << inst.ecx + << " EDX:" << setw(8) << inst.edx << " ESI:" << setw(8) << inst.esi + << " EDI:" << setw(8) << inst.edi << " EBP:" << setw(8) << inst.ebp + << " ESP:" << setw(8) << inst.esp << " DS:" << setw(4) << inst.s_ds + << " ES:" << setw(4) << inst.s_es<< " FS:" << setw(4) << inst.s_fs + << " GS:" << setw(4) << inst.s_gs<< " SS:" << setw(4) << inst.s_ss + << " CF:" << inst.c << " ZF:" << inst.z << " SF:" << inst.s + << " OF:" << inst.o << " AF:" << inst.a << " PF:" << inst.p + << " IF:" << inst.i << endl; + +/* fprintf(f,"%04X:%08X %s %s EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X EBP:%08X ESP:%08X DS:%04X ES:%04X FS:%04X GS:%04X SS:%04X CF:%01X ZF:%01X SF:%01X OF:%01X AF:%01X PF:%01X IF:%01X\n", + logInst[startLog].s_cs,logInst[startLog].eip,logInst[startLog].dline,logInst[startLog].res,logInst[startLog].eax,logInst[startLog].ebx,logInst[startLog].ecx,logInst[startLog].edx,logInst[startLog].esi,logInst[startLog].edi,logInst[startLog].ebp,logInst[startLog].esp, + logInst[startLog].s_ds,logInst[startLog].s_es,logInst[startLog].s_fs,logInst[startLog].s_gs,logInst[startLog].s_ss, + logInst[startLog].c,logInst[startLog].z,logInst[startLog].s,logInst[startLog].o,logInst[startLog].a,logInst[startLog].p,logInst[startLog].i);*/ + if (++startLog >= LOGCPUMAX) startLog = 0; + } while (startLog != logCount); + + out.close(); + DEBUG_ShowMsg("DEBUG: Done.\n"); +}; + +#endif + +#endif diff --git a/src/debug/gdb_server.cpp b/src/debug/gdb_server.cpp new file mode 100644 index 00000000..af9856c4 --- /dev/null +++ b/src/debug/gdb_server.cpp @@ -0,0 +1,1090 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * Copyright (C) 2011 Alexandre Becoulet + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "dosbox.h" +#if defined(C_GDBSERVER) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#ifdef WIN32 +//#include +#include "poll.h" +char * strtok_r (char *s, const char *delim, char **save_ptr) { + char *token; + + if (s == NULL) + s = *save_ptr; + + /* Scan leading delimiters. */ + s += strspn (s, delim); + if (*s == '\0') + { + *save_ptr = s; + return NULL; + } + + /* Find the end of the token. */ + token = s; + s = strpbrk (token, delim); + if (s == NULL) + /* This token finishes the string. */ + *save_ptr = token + strlen (token); + else + { + /* Terminate the token and make *SAVE_PTR point past it. */ + *s = '\0'; + *save_ptr = s + 1; + } + return token; +} + +#else +#include +#include +#include +#include +#endif +#include +#include + +#include "debug.h" +#include "cpu.h" +#include "video.h" +#include "pic.h" +#include "cpu.h" +#include "callback.h" +#include "paging.h" +#include "setup.h" + +#define GDB_TCP_PORT 1234 + +// remote protocol debug display, must not call DEBUG_ShowMsg in the end +#define GDB_REMOTE_LOG(...) fprintf(stderr, __VA_ARGS__) + +enum GdbState { + GdbNotConnected, + GdbRunning, + GdbStoped, + GdbStep, // set during step by step + GdbMonitor, // set during monitor command processing +}; + +static GdbState gdb_state = GdbNotConnected; +static bool gdb_remote_debug = false; +static int gdb_socket, gdb_asocket; + +static Bit32u step_eip; +static Bit16u step_cs; +GdbState step_next_state; + +// breakpoints/watchpoints maps +typedef map bp_map_t; +static bp_map_t break_points; +static bp_map_t read_watch_points; +static bp_map_t write_watch_points; + +// interrupts breakpoints bitmap +vector int_bp(256); + +Bitu DEBUG_debugCallback; +Bitu DEBUG_cycle_count; +static Bitu cycle_bp; + +extern bool DEBUG_logHeavy; + +static inline Bit32u +swapByte32(Bit32u x) +{ + return (((x >> 24) & 0x000000ff) | + ((x >> 8 ) & 0x0000ff00) | + ((x << 8 ) & 0x00ff0000) | + ((x << 24) & 0xff000000)); +} + +static inline Bit16u +swapByte16(Bit16u x) +{ + return (x >> 8) | (x << 8); +} + +/****************************/ +/* Gdb cpu registers access */ +/****************************/ + +enum GdbEipMode { + GdbFlatEip, + GdbRealEip, +}; + +static GdbEipMode gdb_eip_mode = GdbFlatEip; + +static void gdbSetEip(Bit32u gdb_eip) +{ + /* make gdb client see flat eip value */ + switch (gdb_eip_mode) { + case GdbFlatEip: + reg_eip = gdb_eip - SegPhys(cs); + break; + case GdbRealEip: + reg_eip = gdb_eip; + break; + } +} + +static Bit32u gdbGetEip(void) +{ + switch (gdb_eip_mode) { + case GdbFlatEip: + return reg_eip + SegPhys(cs); + case GdbRealEip: + return reg_eip; + } +} + +#define GDB_REGS_COUNT 16 + +Bit32u DEBUG_GdbGetRegister(int reg) +{ + switch (reg) { + case 0: + return swapByte32(reg_eax); + case 1: + return swapByte32(reg_ecx); + case 2: + return swapByte32(reg_edx); + case 3: + return swapByte32(reg_ebx); + case 4: + return swapByte32(reg_esp); + case 5: + return swapByte32(reg_ebp); + case 6: + return swapByte32(reg_esi); + case 7: + return swapByte32(reg_edi); + case 8: + return swapByte32(gdbGetEip()); + case 9: + return swapByte32(reg_flags); + case 10: + return swapByte32(SegValue(cs)); + case 11: + return swapByte32(SegValue(ss)); + case 12: + return swapByte32(SegValue(ds)); + case 13: + return swapByte32(SegValue(es)); + case 14: + return swapByte32(SegValue(fs)); + case 15: + return swapByte32(SegValue(gs)); + default: + return 0; + } +} + +int DEBUG_GdbGetRegisterSize(int reg) +{ + switch (reg) { + case 0: /* gp regs */ + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return 32; + + case 10: /* segments */ + case 11: + case 12: + case 13: + case 14: + case 15: + return 32; + + default: + return 0; + } + +} + +void DEBUG_GdbSetRegister(int reg, Bit32u value) +{ + switch (reg) { + case 0: + reg_eax = swapByte32(value); + break; + case 1: + reg_ecx = swapByte32(value); + break; + case 2: + reg_edx = swapByte32(value); + break; + case 3: + reg_ebx = swapByte32(value); + break; + case 4: + reg_esp = swapByte32(value); + break; + case 5: + reg_ebp = swapByte32(value); + break; + case 6: + reg_esi = swapByte32(value); + break; + case 7: + reg_edi = swapByte32(value); + break; + case 8: + gdbSetEip(swapByte32(value)); + break; + case 9: + reg_flags = swapByte32(value); + break; + case 10: + SegSet16(cs, swapByte32(value)); + break; + case 11: + SegSet16(ss, swapByte32(value)); + break; + case 12: + SegSet16(ds, swapByte32(value)); + break; + case 13: + SegSet16(es, swapByte32(value)); + break; + case 14: + SegSet16(fs, swapByte32(value)); + break; + case 15: + SegSet16(gs, swapByte32(value)); + break; + } +} + +/***********************/ +/* Gdb remote protocol */ +/***********************/ + +int DEBUG_GdbWritePacket(const char *data_) +{ + unsigned int i, len = strlen(data_); + char ack, end[4]; + uint8_t chksum = 0; + char data[len]; + + memcpy(data, data_, len + 1); + + unsigned char repeat = 0; + unsigned int cmplen = len; + char *cmp = data; + char *last = 0; + +#if 1 // gdb RLE data compression + + for (i = 0; ; ) { + if (i < cmplen && last && (*last == cmp[i]) && (repeat + 29 < 126)) { + repeat++; + } else { + if (repeat > 3) { + while (repeat == '#' - 29 || repeat == '$' - 29 || + repeat == '+' - 29 || repeat == '-' - 29) { + repeat--; + last++; + } + last[1] = '*'; + last[2] = 29 + repeat; + memmove(last + 3, cmp + i, cmplen - i + 1); + cmp = last + 3; + cmplen -= i; + i = 0; + last = 0; + repeat = 0; + continue; + } else { + last = cmp + i; + repeat = 0; + } + + if (i == cmplen) + break; + } + i++; + } + + cmp[cmplen] = 0; + len = cmp - data + cmplen; + +#endif + + for (i = 0; i < len; i++) + chksum += data[i]; + + sprintf(end, "#%02x", chksum); + + do { + if (gdb_remote_debug) + GDB_REMOTE_LOG("GDB: sending packet data '%s'\n", data); + + send(gdb_asocket, "$", 1, MSG_DONTWAIT | MSG_NOSIGNAL); + send(gdb_asocket, data, len, MSG_DONTWAIT | MSG_NOSIGNAL); + send(gdb_asocket, end, 3, MSG_DONTWAIT | MSG_NOSIGNAL); + + if (read(gdb_asocket, &ack, 1) < 1) { + close(gdb_asocket); + gdb_state = GdbNotConnected; + return -1; + } + } while (ack != '+'); + + return 0; +} + +char * DEBUG_GdbReadPacket(char *buffer, size_t size) +{ + int res = read(gdb_asocket, buffer, size); + + if (res <= 0) { + close(gdb_asocket); + gdb_state = GdbNotConnected; + return 0; + } + + uint8_t sum = 0, chksum = 0; + char *data = 0; + char *end = 0; + int i; + + // find data in packet + for (i = 0; i < res; i++) { + switch (buffer[i]) { + case '$': + sum = 0; + data = buffer + i + 1; + break; + + case '#': + chksum = sum; + end = buffer + i; + *end = 0; + goto end; + + default: + sum += buffer[i]; + } + } + end: + + // malformed packet + if (!end || data >= end) { + if (gdb_remote_debug) + GDB_REMOTE_LOG("GDB: malformed packet %i bytes\n", res); + + return 0; + } + + if (gdb_remote_debug) + GDB_REMOTE_LOG("GDB: packet with checksum %02x: %s\n", chksum, data); + + // verify checksum + end[3] = 0; + if (chksum != strtoul(end + 1, 0, 16)) { + send(gdb_asocket, "-", 1, MSG_DONTWAIT | MSG_NOSIGNAL); + + if (gdb_remote_debug) + GDB_REMOTE_LOG("GDB: bad packet checksum\n"); + + return 0; + } + + send(gdb_asocket, "+", 1, MSG_DONTWAIT | MSG_NOSIGNAL); + + return data; +} + +static bool DEBUG_GdbPointSet(bp_map_t &b, Bit32u addr, size_t len, bool set_) +{ + if (set_) { + pair ret = b.insert(bp_map_t::value_type(addr, len)); + + if (!ret.second) // adjust existing point + ret.first->second = max(ret.first->second, len); + } else { + b.erase(addr); + } +} + +void DEBUG_GdbProcessMonitorPacket(char *data) +{ + const char *delim = " \t,"; + char *tokens[255], *save; + unsigned int i = 0; + + for (char *t = strtok_r(data, delim, &save); + t != NULL; t = strtok_r(NULL, delim, &save)) { + tokens[i++] = t; + if (i == 255) + break; + } + + if (i >= 2 && !strcmp(tokens[0], "remote_debug")) { + gdb_remote_debug = atoi(tokens[1]); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 1 && !strcmp(tokens[0], "write_log_instruction")) { + DEBUG_HeavyWriteLogInstruction(); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 2 && !strcmp(tokens[0], "log_heavy")) { + DEBUG_logHeavy = atoi(tokens[1]); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 2 && !strcmp(tokens[0], "cycle_abs_bp")) { + cycle_bp = atoi(tokens[1]); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 2 && !strcmp(tokens[0], "cycle_bp")) { + cycle_bp = DEBUG_cycle_count + atoi(tokens[1]); + DEBUG_ShowMsg("GDB: Cycle break point set at %u.\n", cycle_bp); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 2 && !strcmp(tokens[0], "flat_eip")) { + gdb_eip_mode = atoi(tokens[1]) ? GdbFlatEip : GdbRealEip; + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 3 && !strcmp(tokens[0], "int_bp")) { + unsigned int inum = strtoul(tokens[1], 0, 0); + + if (inum < 256) { + int_bp[inum] = atoi(tokens[2]); + DEBUG_GdbWritePacket("OK"); + return; + } + } + + if (i >= 1 && !strcmp(tokens[0], "log_gdt")) { + DEBUG_LogGDT(); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 1 && !strcmp(tokens[0], "log_ldt")) { + DEBUG_LogLDT(); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 1 && !strcmp(tokens[0], "log_idt")) { + DEBUG_LogIDT(); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 1 && !strcmp(tokens[0], "log_cpuinfo")) { + DEBUG_LogCPUInfo(); + DEBUG_GdbWritePacket("OK"); + return; + } + + if (i >= 2 && !strcmp(tokens[0], "log_pages")) { + DEBUG_LogPages(tokens[1]); + DEBUG_GdbWritePacket("OK"); + return; + } + + DEBUG_ShowMsg("Supported DOSBox Gdb monitor commands:"); + DEBUG_ShowMsg(" monitor cycle_bp [value] - set relative cycle breakpoint."); + DEBUG_ShowMsg(" monitor cycle_abs_bp [value] - set absolute cycle breakpoint."); + DEBUG_ShowMsg(" monitor flat_eip [ 0 | 1 ] - enable/disable use of flat eip register value."); + DEBUG_ShowMsg(" monitor int_bp [int_num] [ 0 | 1 ] - set breakpoint on cpu interrupt."); + DEBUG_ShowMsg(" monitor log_gdt - Lists descriptors of the GDT."); + DEBUG_ShowMsg(" monitor log_ldt - Lists descriptors of the LDT."); + DEBUG_ShowMsg(" monitor log_idt - Lists descriptors of the IDT."); + DEBUG_ShowMsg(" monitor log_cpuinfo - Display CPU status information."); + DEBUG_ShowMsg(" monitor log_pages [page] - Display content of page table."); + DEBUG_ShowMsg(" monitor remote_debug [ 0 | 1 ] - enable/disable gdb remote protocol debug."); + DEBUG_ShowMsg(" monitor set_log_heavy [ 0 | 1 ] - enable/disable heavy CPU logging."); + DEBUG_ShowMsg(" monitor write_log_instruction - write instructions log to disk."); + + DEBUG_GdbWritePacket(""); +} + +void DEBUG_GdbProcessPackets() +{ + char buffer[1024]; + char *data = DEBUG_GdbReadPacket(buffer, 1024); + + if (!data) + return; + + switch (data[0]) { + case 'k': // Kill + DEBUG_GdbWritePacket("OK"); + exit(0); + return; + + case 'D': // Detach + DEBUG_GdbWritePacket("OK"); + close(gdb_asocket); + gdb_state = GdbNotConnected; + return; + + case 'q': // Query + switch (data[1]) { + case 'R': { + unsigned int i; + char byte[3] = { 0 }; + + if (strncmp(data + 2, "cmd", 3)) + break; + + assert(data[5] == ','); + + data += 6; + + for (i = 0; data[i * 2]; i++) { + memcpy(byte, data + i * 2, 2); + data[i] = strtoul(byte, 0, 16); + } + + data[i] = 0; + + if (gdb_remote_debug) + GDB_REMOTE_LOG("GDB: monitor packet: '%s'\n", data); + + gdb_state = GdbMonitor; + DEBUG_GdbProcessMonitorPacket(data); + gdb_state = GdbStoped; + return; + } + + } + DEBUG_GdbWritePacket(""); + return; + + case '?': // Indicate the reason the target halted + DEBUG_GdbWritePacket("S05"); // SIGTRAP + return; + + case 'p': { // read single register + unsigned int reg = strtoul(data + 1, 0, 16); + char fmt[32]; + size_t s = DEBUG_GdbGetRegisterSize(reg); + + if (s == 0) + break; + + sprintf(fmt, "%%0%ux", s / 4); + sprintf(buffer, fmt, DEBUG_GdbGetRegister(reg)); + DEBUG_GdbWritePacket(buffer); + return; + } + + case 'P': { // write single register + char *end; + unsigned int reg = strtoul(data + 1, &end, 16); + assert(*end == '='); + uint32_t value = strtoul(end + 1, 0, 16); + size_t s = DEBUG_GdbGetRegisterSize(reg); + + if (s == 0) + break; + + DEBUG_GdbSetRegister(reg, value); + DEBUG_GdbWritePacket("OK"); + return; + } + + case 'g': { // read multiple registers + char *b = buffer; + + for (unsigned int i = 0; i < 1 /* FIXME */; i++) { + char fmt[32]; + sprintf(fmt, "%%0%ux", DEBUG_GdbGetRegisterSize(i) / 4); + b += sprintf(b, fmt, DEBUG_GdbGetRegister(i)); + } + DEBUG_GdbWritePacket(buffer); + return; + } + + case 'G': { // write multiple registers + + data++; + for (unsigned int i = 0; i < GDB_REGS_COUNT; i++) { + size_t s = DEBUG_GdbGetRegisterSize(i) / 4; + char word[s + 1]; + word[s] = 0; + memcpy(word, data, s); + if (strlen(word) != s) + break; + DEBUG_GdbSetRegister(i, strtoul(word, 0, 16)); + data += s; + } + + DEBUG_GdbWritePacket("OK"); + return; + } + + case 'm': { // read memory + char *end; + uint32_t addr = strtoul(data + 1, &end, 16); + assert(*end == ','); + size_t len = strtoul(end + 1, 0, 16); + + char packet[len * 2 + 1]; + char *b = packet; + bool err = false; + + if (len % 4 == 0 && addr % 4 == 0) { // use dword access + for (unsigned int i = 0; i < len / 4; i++) { + Bit32u value; + err |= mem_readd_checked(addr + i * 4, &value); + b += sprintf(b, "%08x", swapByte32(value)); + } + + } else if (len % 2 == 0 && addr % 2 == 0) { // use word access + for (unsigned int i = 0; i < len / 2; i++) { + Bit16u value; + err |= mem_readw_checked(addr + i * 2, &value); + b += sprintf(b, "%04x", swapByte16(value)); + } + + } else { // use byte access + for (unsigned int i = 0; i < len ; i++) { + Bit8u value; + err |= mem_readb_checked(addr + i, &value); + b += sprintf(b, "%02x", value); + } + } + + DEBUG_GdbWritePacket(err ? "E0d" : packet); + return; + } + + case 'M': { // write memory + char *end; + uint32_t addr = strtoul(data + 1, &end, 16); + assert(*end == ','); + size_t len = strtoul(end + 1, &end, 16); + assert(*end == ':'); + + char hex[9] = { 0 }; + bool err = false; + + if (len % 4 == 0 && addr % 4 == 0) { // use dword access + for (unsigned int i = 0; i < len / 4; i++) + { + memcpy(hex, end + 1 + i * 8, 8); + err |= mem_writed_checked(addr + i, swapByte32(strtoul(hex, 0, 16))); + } + + } else if (len % 2 == 0 && addr % 2 == 0) { // use word access + for (unsigned int i = 0; i < len / 2; i++) + { + memcpy(hex, end + 1 + i * 4, 4); + err |= mem_writew_checked(addr + i, swapByte16(strtoul(hex, 0, 16))); + } + + } else { // use byte access + for (unsigned int i = 0; i < len; i++) + { + memcpy(hex, end + 1 + i * 2, 2); + err |= mem_writeb_checked(addr + i, strtoul(hex, 0, 16)); + } + } + + DEBUG_GdbWritePacket(err ? "E0d" : "OK"); + return; + } + + case 'c': { // continue [optional resume addr in hex] + if (data[1]) { + gdbSetEip(strtoul(data + 1, 0, 16)); + step_next_state = GdbRunning; + } else { + // go through single step to avoid break-point on resume address + gdb_state = GdbStep; + step_next_state = GdbRunning; + step_eip = gdbGetEip(); + step_cs = SegValue(cs); + } + return; + } + + case 's': { // continue single step [optional resume addr in hex] + uint32_t pc; + + if (data[1]) + gdbSetEip(strtoul(data + 1, 0, 16)); + + gdb_state = GdbStep; + step_next_state = GdbStoped; + step_eip = gdbGetEip(); + step_cs = SegValue(cs); + return; + } + + case 'z': // set and clean break points + case 'Z': { + char *end; + uint32_t addr = strtoul(data + 3, &end, 16); + assert(*end == ','); + size_t len = strtoul(end + 1, 0, 16); + + switch (data[1]) { + case '0': + case '1': // execution break point + DEBUG_GdbPointSet(break_points, addr, len, data[0] == 'Z'); + break; + + case '2': // write watch point + DEBUG_GdbPointSet(write_watch_points, addr, len, data[0] == 'Z'); + break; + + case '4': // access watch point + DEBUG_GdbPointSet(write_watch_points, addr, len, data[0] == 'Z'); + case '3': // read watch point + DEBUG_GdbPointSet(read_watch_points, addr, len, data[0] == 'Z'); + break; + + default: + DEBUG_GdbWritePacket(""); + return; + } + + DEBUG_GdbWritePacket("OK"); + return; + } + + default: + break; + } + + // empty reply if not supported + DEBUG_GdbWritePacket(""); +} + +static bool DEBUG_GdbCheckEvent(void) +{ + struct pollfd pf; + char c; + + switch (gdb_state) { + case GdbNotConnected: // check for incoming connection + pf.fd = gdb_socket; + pf.events = POLLIN | POLLPRI; + + if (poll(&pf, 1, 0) > 0) { + struct sockaddr_in addr; + socklen_t addr_size = sizeof(addr); + + gdb_asocket = accept(gdb_socket, (struct sockaddr*)&addr, &addr_size); + + if (gdb_asocket >= 0) { + gdb_state = GdbStoped; + return true; + } + } + break; + + case GdbStep: + case GdbRunning: // try to read CTRL-C from client + pf.fd = gdb_asocket; + pf.events = POLLIN | POLLPRI; + + if (poll(&pf, 1, 0) == 1 && read(gdb_asocket, &c, 1) == 1 && c == 3) { + DEBUG_ShowMsg("GDB: %u: break requested.\n", DEBUG_cycle_count); + + gdb_state = GdbStoped; + DEBUG_GdbWritePacket("S02"); // sigint + return true; + } + break; + } + + return false; +} + +/**********************/ +/* dosbox debug stuff */ +/**********************/ + +void DEBUG_ShowMsg(char const* format,...) +{ + char buf[256]; + va_list ap; + int i; + + va_start(ap, format); + i = vsnprintf(buf, sizeof(buf) - 1, format, ap); + buf[i] = 0; + va_end(ap); + + // trim spaces at end + for (; i > 0 && buf[i] <= 32; i--) + buf[i] = 0; + + // display message on stderr + fprintf(stderr, "%s\n", buf); + + // send message to gdb client + if (gdb_state == GdbRunning || gdb_state == GdbStep || gdb_state == GdbMonitor) { + if (buf[0]) { + char _p[sizeof(buf) * 2 + 3], *p = _p; + int i; + + *p++ = 'O'; + for (i = 0; buf[i]; i++) + p += sprintf(p, "%02x", buf[i]); + *p++ = '0'; + *p++ = 'a'; + *p++ = 0; + + DEBUG_GdbWritePacket(_p); + } + } + +} + +/* called when irq is raised */ +void DEBUG_IrqBreakpoint(Bit8u intNum) +{ + if (gdb_state == GdbRunning && int_bp[intNum]) { + DEBUG_ShowMsg("GDB: %u: processor hardware interrupt 0x%x.\n", DEBUG_cycle_count, intNum); + + gdb_state = GdbStoped; + DEBUG_GdbWritePacket("S05"); // trap + DEBUG_EnableDebugger(); + } +} + +/* called when "int n" opcode is encountered */ +bool DEBUG_IntBreakpoint(Bit8u intNum) +{ + if (gdb_state == GdbRunning && int_bp[intNum]) { + DEBUG_ShowMsg("GDB: %u: processor software interrupt 0x%x.\n", DEBUG_cycle_count, intNum); + + gdb_state = GdbStoped; + DEBUG_GdbWritePacket("S05"); // trap + return true; + } + + return false; +} + +/* called when "int3" opcode is encountered */ +bool DEBUG_Breakpoint(void) +{ + return DEBUG_IntBreakpoint(3); +} + +static inline bool DEBUG_GdbPointCheck(const bp_map_t &b, Bit32u addr) +{ + bp_map_t::const_iterator i = b.lower_bound(addr); + + return (i != b.end() && i->first <= addr && i->first + i->second > addr); +} + +/* called for each executed instruction */ +bool DEBUG_HeavyIsBreakpoint(void) +{ + static Bitu last_event_check = 0; + + void DEBUG_HeavyLogInstruction(void); + if (DEBUG_logHeavy) + DEBUG_HeavyLogInstruction(); + + /* handle single step end */ + if (gdb_state == GdbStep && (step_eip != gdbGetEip() || step_cs != SegValue(cs))) { + gdb_state = step_next_state; + + if (gdb_state == GdbStoped) { + DEBUG_GdbWritePacket("S05"); // trap + return true; + } + } + + /* check execution breakpoints */ + if (gdb_state == GdbRunning) { + + if (DEBUG_GdbPointCheck(break_points, gdbGetEip())) { + DEBUG_ShowMsg("GDB: %u: hit a breakpoint.\n", DEBUG_cycle_count); + + gdb_state = GdbStoped; + DEBUG_GdbWritePacket("S05"); // trap + return true; + } + + if (cycle_bp && cycle_bp <= DEBUG_cycle_count) { + DEBUG_ShowMsg("GDB: %u: hit a cycle breakpoint.\n", DEBUG_cycle_count); + + cycle_bp = 0; + gdb_state = GdbStoped; + DEBUG_GdbWritePacket("S05"); // trap + return true; + } + } + + /* sometimes check for incoming gdb client connections or CTRL-C from client */ + if (last_event_check + 16384 < DEBUG_cycle_count) { + last_event_check = DEBUG_cycle_count; + if (DEBUG_GdbCheckEvent()) + return true; + } + + return false; +} + +void DEBUG_GdbMemReadHook(Bit32u address, int width) +{ + if ((gdb_state == GdbRunning || gdb_state == GdbStep) && DEBUG_GdbPointCheck(read_watch_points, address)) { + DEBUG_ShowMsg("GDB: %u hit a memory read access watchpoint: address=0x%08x.\n", DEBUG_cycle_count, address); + + gdb_state = GdbStoped; + DEBUG_EnableDebugger(); + DEBUG_GdbWritePacket("S05"); // trap + } +} + +void DEBUG_GdbMemWriteHook(Bit32u address, int width, Bit32u value) +{ + if ((gdb_state == GdbRunning || gdb_state == GdbStep) && DEBUG_GdbPointCheck(write_watch_points, address)) { + DEBUG_ShowMsg("GDB: %u: hit a memory write access watchpoint: address=0x%08x, new_value=0x%x.\n", DEBUG_cycle_count, address, value); + + gdb_state = GdbStoped; + DEBUG_EnableDebugger(); + DEBUG_GdbWritePacket("S05"); // trap + } +} + +Bitu DEBUG_Loop(void) +{ + GFX_Events(); + PIC_runIRQs(); + + if (gdb_state == GdbStoped) + DEBUG_GdbProcessPackets(); + + if (gdb_state != GdbStoped) { + DEBUG_exitLoop = false; + DOSBOX_SetNormalLoop(); + } + + return 0; +} + +bool DEBUG_ExitLoop(void) +{ + if (DEBUG_exitLoop) { + DEBUG_exitLoop = false; + return true; + } + return false; +} + +Bitu DEBUG_EnableDebugger(void) +{ + DEBUG_exitLoop = true; + DOSBOX_SetLoop(&DEBUG_Loop); + return 0; +} + +void DEBUG_ShutDown(Section *sec) +{ + close(gdb_asocket); + close(gdb_socket); +} + +void DEBUG_Init(Section* sec) +{ + /* Setup callback */ + DEBUG_debugCallback=CALLBACK_Allocate(); + CALLBACK_Setup(DEBUG_debugCallback,DEBUG_EnableDebugger,CB_RETF,"debugger"); + + /* shutdown function */ + sec->AddDestroyFunction(&DEBUG_ShutDown); + + gdb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (gdb_socket < 0) { + LOG_MSG("GDB: Unable to create socket"); + return; + } + + int tmp = 1; +#ifdef WIN32 + setsockopt(gdb_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)tmp, sizeof(tmp)); +#else + setsockopt(gdb_socket, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)); +#endif + + struct sockaddr_in addr; + + int i; + for (int i = 0; ; i++) { + if (i == 10) { + LOG_MSG("GDB: Unable to bind socket"); + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_port = htons(GDB_TCP_PORT + i); + addr.sin_family = AF_INET; + + if (bind(gdb_socket, (struct sockaddr*)&addr, sizeof (struct sockaddr_in)) >= 0) + break; + } + + if (listen(gdb_socket, 1) < 0) { + LOG_MSG("GDB: Unable to listen"); + return; + } + + LOG_MSG("GDB: listening on TCP port %i", GDB_TCP_PORT + i); +} + +#endif + diff --git a/src/debug/poll.cpp b/src/debug/poll.cpp new file mode 100644 index 00000000..8d8657ac --- /dev/null +++ b/src/debug/poll.cpp @@ -0,0 +1,595 @@ +/* Emulation for poll(2) + Contributed by Paolo Bonzini. + + Copyright 2001-2003, 2006-2010 Free Software Foundation, Inc. + + This file is part of gnulib. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Tell gcc not to warn about the (nfd < 0) tests, below. */ +#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__ +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#include + +#include +#include "poll.h" +#include +#include +#include + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# define WIN32_NATIVE +# if defined (_MSC_VER) +# define _WIN32_WINNT 0x0502 +# endif +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +#endif + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif + +#include + +#ifndef INFTIM +# define INFTIM (-1) +#endif + +/* BeOS does not have MSG_PEEK. */ +#ifndef MSG_PEEK +# define MSG_PEEK 0 +#endif + +#ifdef WIN32_NATIVE + +#define IsConsoleHandle(h) (((long) (h) & 3) == 3) + +static BOOL +IsSocketHandle (HANDLE h) +{ + WSANETWORKEVENTS ev; + + if (IsConsoleHandle (h)) + return FALSE; + + /* Under Wine, it seems that getsockopt returns 0 for pipes too. + WSAEnumNetworkEvents instead distinguishes the two correctly. */ + ev.lNetworkEvents = 0xDEADBEEF; + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + return ev.lNetworkEvents != 0xDEADBEEF; +} + +/* Declare data structures for ntdll functions. */ +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _IO_STATUS_BLOCK +{ + union { + DWORD Status; + PVOID Pointer; + } u; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef enum _FILE_INFORMATION_CLASS { + FilePipeLocalInformation = 24 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef DWORD (WINAPI *PNtQueryInformationFile) + (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); + +# ifndef PIPE_BUF +# define PIPE_BUF 512 +# endif + +/* Compute revents values for file handle H. If some events cannot happen + for the handle, eliminate them from *P_SOUGHT. */ + +static int +win32_compute_revents (HANDLE h, int *p_sought) +{ + int i, ret, happened; + INPUT_RECORD *irbuffer; + DWORD avail, nbuffer; + BOOL bRet; + IO_STATUS_BLOCK iosb; + FILE_PIPE_LOCAL_INFORMATION fpli; + static PNtQueryInformationFile NtQueryInformationFile; + static BOOL once_only; + + switch (GetFileType (h)) + { + case FILE_TYPE_PIPE: + if (!once_only) + { + NtQueryInformationFile = (PNtQueryInformationFile) + GetProcAddress (GetModuleHandle ("ntdll.dll"), + "NtQueryInformationFile"); + once_only = TRUE; + } + + happened = 0; + if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) + { + if (avail) + happened |= *p_sought & (POLLIN | POLLRDNORM); + } + else if (GetLastError () == ERROR_BROKEN_PIPE) + happened |= POLLHUP; + + else + { + /* It was the write-end of the pipe. Check if it is writable. + If NtQueryInformationFile fails, optimistically assume the pipe is + writable. This could happen on Win9x, where NtQueryInformationFile + is not available, or if we inherit a pipe that doesn't permit + FILE_READ_ATTRIBUTES access on the write end (I think this should + not happen since WinXP SP2; WINE seems fine too). Otherwise, + ensure that enough space is available for atomic writes. */ + memset (&iosb, 0, sizeof (iosb)); + memset (&fpli, 0, sizeof (fpli)); + + if (!NtQueryInformationFile + || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), + FilePipeLocalInformation) + || fpli.WriteQuotaAvailable >= PIPE_BUF + || (fpli.OutboundQuota < PIPE_BUF && + fpli.WriteQuotaAvailable == fpli.OutboundQuota)) + happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); + } + return happened; + + case FILE_TYPE_CHAR: + ret = WaitForSingleObject (h, 0); + if (!IsConsoleHandle (h)) + return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0; + + nbuffer = avail = 0; + bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); + if (bRet) + { + /* Input buffer. */ + *p_sought &= POLLIN | POLLRDNORM; + if (nbuffer == 0) + return POLLHUP; + if (!*p_sought) + return 0; + + irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); + bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); + if (!bRet || avail == 0) + return POLLHUP; + + for (i = 0; i < avail; i++) + if (irbuffer[i].EventType == KEY_EVENT) + return *p_sought; + return 0; + } + else + { + /* Screen buffer. */ + *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND; + return *p_sought; + } + + default: + ret = WaitForSingleObject (h, 0); + if (ret == WAIT_OBJECT_0) + return *p_sought & ~(POLLPRI | POLLRDBAND); + + return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); + } +} + +/* Convert fd_sets returned by select into revents values. */ + +static int +win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents) +{ + int happened = 0; + + if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT) + happened |= (POLLIN | POLLRDNORM) & sought; + + else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) + { + int r, error; + + char data[64]; + WSASetLastError (0); + r = recv (h, data, sizeof (data), MSG_PEEK); + error = WSAGetLastError (); + WSASetLastError (0); + + if (r > 0 || error == WSAENOTCONN) + happened |= (POLLIN | POLLRDNORM) & sought; + + /* Distinguish hung-up sockets from other errors. */ + else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET + || error == WSAECONNABORTED || error == WSAENETRESET) + happened |= POLLHUP; + + else + happened |= POLLERR; + } + + if (lNetworkEvents & (FD_WRITE | FD_CONNECT)) + happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; + + if (lNetworkEvents & FD_OOB) + happened |= (POLLPRI | POLLRDBAND) & sought; + + return happened; +} + +#else /* !MinGW */ + +/* Convert select(2) returned fd_sets into poll(2) revents values. */ +static int +compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds) +{ + int happened = 0; + if (FD_ISSET (fd, rfds)) + { + int r; + int socket_errno; + +# if defined __MACH__ && defined __APPLE__ + /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK + for some kinds of descriptors. Detect if this descriptor is a + connected socket, a server socket, or something else using a + 0-byte recv, and use ioctl(2) to detect POLLHUP. */ + r = recv (fd, NULL, 0, MSG_PEEK); + socket_errno = (r < 0) ? errno : 0; + if (r == 0 || socket_errno == ENOTSOCK) + ioctl (fd, FIONREAD, &r); +# else + char data[64]; + r = recv (fd, data, sizeof (data), MSG_PEEK); + socket_errno = (r < 0) ? errno : 0; +# endif + if (r == 0) + happened |= POLLHUP; + + /* If the event happened on an unconnected server socket, + that's fine. */ + else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN)) + happened |= (POLLIN | POLLRDNORM) & sought; + + /* Distinguish hung-up sockets from other errors. */ + else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET + || socket_errno == ECONNABORTED || socket_errno == ENETRESET) + happened |= POLLHUP; + + else + happened |= POLLERR; + } + + if (FD_ISSET (fd, wfds)) + happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; + + if (FD_ISSET (fd, efds)) + happened |= (POLLPRI | POLLRDBAND) & sought; + + return happened; +} +#endif /* !MinGW */ + +int poll (struct pollfd *pfd, nfds_t nfd, int timeout) +{ +#ifndef WIN32_NATIVE + fd_set rfds, wfds, efds; + struct timeval tv; + struct timeval *ptv; + int maxfd, rc; + nfds_t i; + +# ifdef _SC_OPEN_MAX + static int sc_open_max = -1; + + if (nfd < 0 + || (nfd > sc_open_max + && (sc_open_max != -1 + || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX))))) + { + errno = EINVAL; + return -1; + } +# else /* !_SC_OPEN_MAX */ +# ifdef OPEN_MAX + if (nfd < 0 || nfd > OPEN_MAX) + { + errno = EINVAL; + return -1; + } +# endif /* OPEN_MAX -- else, no check is needed */ +# endif /* !_SC_OPEN_MAX */ + + /* EFAULT is not necessary to implement, but let's do it in the + simplest case. */ + if (!pfd) + { + errno = EFAULT; + return -1; + } + + /* convert timeout number into a timeval structure */ + if (timeout == 0) + { + ptv = &tv; + ptv->tv_sec = 0; + ptv->tv_usec = 0; + } + else if (timeout > 0) + { + ptv = &tv; + ptv->tv_sec = timeout / 1000; + ptv->tv_usec = (timeout % 1000) * 1000; + } + else if (timeout == INFTIM) + /* wait forever */ + ptv = NULL; + else + { + errno = EINVAL; + return -1; + } + + /* create fd sets and determine max fd */ + maxfd = -1; + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&efds); + for (i = 0; i < nfd; i++) + { + if (pfd[i].fd < 0) + continue; + + if (pfd[i].events & (POLLIN | POLLRDNORM)) + FD_SET (pfd[i].fd, &rfds); + + /* see select(2): "the only exceptional condition detectable + is out-of-band data received on a socket", hence we push + POLLWRBAND events onto wfds instead of efds. */ + if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) + FD_SET (pfd[i].fd, &wfds); + if (pfd[i].events & (POLLPRI | POLLRDBAND)) + FD_SET (pfd[i].fd, &efds); + if (pfd[i].fd >= maxfd + && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI + | POLLRDNORM | POLLRDBAND + | POLLWRNORM | POLLWRBAND))) + { + maxfd = pfd[i].fd; + if (maxfd > FD_SETSIZE) + { + errno = EOVERFLOW; + return -1; + } + } + } + + /* examine fd sets */ + rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv); + if (rc < 0) + return rc; + + /* establish results */ + rc = 0; + for (i = 0; i < nfd; i++) + if (pfd[i].fd < 0) + pfd[i].revents = 0; + else + { + int happened = compute_revents (pfd[i].fd, pfd[i].events, + &rfds, &wfds, &efds); + if (happened) + { + pfd[i].revents = happened; + rc++; + } + } + + return rc; +#else + static struct timeval tv0; + static HANDLE hEvent; + WSANETWORKEVENTS ev; + HANDLE h, handle_array[FD_SETSIZE + 2]; + DWORD ret, wait_timeout, nhandles; + fd_set rfds, wfds, xfds; + BOOL poll_again; + MSG msg; + int rc = 0; + nfds_t i; + + if (nfd < 0 || timeout < -1) + { + errno = EINVAL; + return -1; + } + + if (!hEvent) + hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + + handle_array[0] = hEvent; + nhandles = 1; + FD_ZERO (&rfds); + FD_ZERO (&wfds); + FD_ZERO (&xfds); + + /* Classify socket handles and create fd sets. */ + for (i = 0; i < nfd; i++) + { + int sought = pfd[i].events; + pfd[i].revents = 0; + if (pfd[i].fd < 0) + continue; + if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND + | POLLPRI | POLLRDBAND))) + continue; + + h = (HANDLE) _get_osfhandle (pfd[i].fd); + assert (h != NULL); + if (IsSocketHandle (h)) + { + int requested = FD_CLOSE; + + /* see above; socket handles are mapped onto select. */ + if (sought & (POLLIN | POLLRDNORM)) + { + requested |= FD_READ | FD_ACCEPT; + FD_SET ((SOCKET) h, &rfds); + } + if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND)) + { + requested |= FD_WRITE | FD_CONNECT; + FD_SET ((SOCKET) h, &wfds); + } + if (sought & (POLLPRI | POLLRDBAND)) + { + requested |= FD_OOB; + FD_SET ((SOCKET) h, &xfds); + } + + if (requested) + WSAEventSelect ((SOCKET) h, hEvent, requested); + } + else + { + /* Poll now. If we get an event, do not poll again. Also, + screen buffer handles are waitable, and they'll block until + a character is available. win32_compute_revents eliminates + bits for the "wrong" direction. */ + pfd[i].revents = win32_compute_revents (h, &sought); + if (sought) + handle_array[nhandles++] = h; + if (pfd[i].revents) + timeout = 0; + } + } + + if (select (0, &rfds, &wfds, &xfds, &tv0) > 0) + { + /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but + no need to call select again. */ + poll_again = FALSE; + wait_timeout = 0; + } + else + { + poll_again = TRUE; + if (timeout == INFTIM) + wait_timeout = INFINITE; + else + wait_timeout = timeout; + } + + for (;;) + { + ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, + wait_timeout, QS_ALLINPUT); + + if (ret == WAIT_OBJECT_0 + nhandles) + { + /* new input of some other kind */ + BOOL bRet; + while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + else + break; + } + + if (poll_again) + select (0, &rfds, &wfds, &xfds, &tv0); + + /* Place a sentinel at the end of the array. */ + handle_array[nhandles] = NULL; + nhandles = 1; + for (i = 0; i < nfd; i++) + { + int happened; + + if (pfd[i].fd < 0) + continue; + if (!(pfd[i].events & (POLLIN | POLLRDNORM | + POLLOUT | POLLWRNORM | POLLWRBAND))) + continue; + + h = (HANDLE) _get_osfhandle (pfd[i].fd); + if (h != handle_array[nhandles]) + { + /* It's a socket. */ + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + WSAEventSelect ((SOCKET) h, 0, 0); + + /* If we're lucky, WSAEnumNetworkEvents already provided a way + to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */ + if (FD_ISSET ((SOCKET) h, &rfds) + && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT))) + ev.lNetworkEvents |= FD_READ | FD_ACCEPT; + if (FD_ISSET ((SOCKET) h, &wfds)) + ev.lNetworkEvents |= FD_WRITE | FD_CONNECT; + if (FD_ISSET ((SOCKET) h, &xfds)) + ev.lNetworkEvents |= FD_OOB; + + happened = win32_compute_revents_socket ((SOCKET) h, pfd[i].events, + ev.lNetworkEvents); + } + else + { + /* Not a socket. */ + int sought = pfd[i].events; + happened = win32_compute_revents (h, &sought); + nhandles++; + } + + if ((pfd[i].revents |= happened) != 0) + rc++; + } + + return rc; +#endif +} diff --git a/src/debug/poll.h b/src/debug/poll.h new file mode 100644 index 00000000..72305368 --- /dev/null +++ b/src/debug/poll.h @@ -0,0 +1,60 @@ +/* Header for poll(2) emulation + Contributed by Paolo Bonzini. + + Copyright 2001, 2002, 2003, 2007, 2009, 2010 Free Software Foundation, Inc. + + This file is part of gnulib. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _GL_POLL_H +#define _GL_POLL_H + +#include + +/* fake a poll(2) environment */ +#define POLLIN 0x0001 /* any readable data available */ +#define POLLPRI 0x0002 /* OOB/Urgent readable data */ +#define POLLOUT 0x0004 /* file descriptor is writeable */ +#define POLLERR 0x0008 /* some poll error occurred */ +#define POLLHUP 0x0010 /* file descriptor was "hung up" */ +#define POLLNVAL 0x0020 /* requested events "invalid" */ +#define POLLRDNORM 0x0040 +#define POLLRDBAND 0x0080 +#define POLLWRNORM 0x0100 +#define POLLWRBAND 0x0200 + +#define MSG_WAITALL 0 +#define MSG_DONTWAIT 0x40 +#define MSG_NOSIGNAL 0x400 + +struct pollfd +{ + int fd; /* which file descriptor to poll */ + short events; /* events we are interested in */ + short revents; /* events found on return */ +}; + +typedef unsigned long nfds_t; +typedef int socklen_t; + +extern int poll (struct pollfd *pfd, nfds_t nfd, int timeout); + +/* Define INFTIM only if doing so conforms to POSIX. */ +#if !defined (_POSIX_C_SOURCE) && !defined (_XOPEN_SOURCE) +#define INFTIM (-1) +#endif + +#endif /* _GL_POLL_H */ diff --git a/src/dos/dos_execute.cpp b/src/dos/dos_execute.cpp index da43a405..ed1a57bd 100644 --- a/src/dos/dos_execute.cpp +++ b/src/dos/dos_execute.cpp @@ -476,7 +476,7 @@ bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { reg_di=RealOff(sssp); reg_bp=0x91c; /* DOS internal stack begin relict */ SegSet16(ds,pspseg);SegSet16(es,pspseg); -#if C_DEBUG +#if C_DEBUG /* Started from debug.com, then set breakpoint at start */ DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip)); #endif diff --git a/src/dos/dos_execute.cpp.orig b/src/dos/dos_execute.cpp.orig new file mode 100644 index 00000000..da43a405 --- /dev/null +++ b/src/dos/dos_execute.cpp.orig @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include +#include +#include "dosbox.h" +#include "mem.h" +#include "dos_inc.h" +#include "regs.h" +#include "callback.h" +#include "debug.h" +#include "cpu.h" + +const char * RunningProgram="DOSBOX"; + +#ifdef _MSC_VER +#pragma pack(1) +#endif +struct EXE_Header { + Bit16u signature; /* EXE Signature MZ or ZM */ + Bit16u extrabytes; /* Bytes on the last page */ + Bit16u pages; /* Pages in file */ + Bit16u relocations; /* Relocations in file */ + Bit16u headersize; /* Paragraphs in header */ + Bit16u minmemory; /* Minimum amount of memory */ + Bit16u maxmemory; /* Maximum amount of memory */ + Bit16u initSS; + Bit16u initSP; + Bit16u checksum; + Bit16u initIP; + Bit16u initCS; + Bit16u reloctable; + Bit16u overlay; +} GCC_ATTRIBUTE(packed); +#ifdef _MSC_VER +#pragma pack() +#endif + +#define MAGIC1 0x5a4d +#define MAGIC2 0x4d5a +#define MAXENV 32768u +#define ENV_KEEPFREE 83 /* keep unallocated by environment variables */ +#define LOADNGO 0 +#define LOAD 1 +#define OVERLAY 3 + + + +static void SaveRegisters(void) { + reg_sp-=18; + mem_writew(SegPhys(ss)+reg_sp+ 0,reg_ax); + mem_writew(SegPhys(ss)+reg_sp+ 2,reg_cx); + mem_writew(SegPhys(ss)+reg_sp+ 4,reg_dx); + mem_writew(SegPhys(ss)+reg_sp+ 6,reg_bx); + mem_writew(SegPhys(ss)+reg_sp+ 8,reg_si); + mem_writew(SegPhys(ss)+reg_sp+10,reg_di); + mem_writew(SegPhys(ss)+reg_sp+12,reg_bp); + mem_writew(SegPhys(ss)+reg_sp+14,SegValue(ds)); + mem_writew(SegPhys(ss)+reg_sp+16,SegValue(es)); +} + +static void RestoreRegisters(void) { + reg_ax=mem_readw(SegPhys(ss)+reg_sp+ 0); + reg_cx=mem_readw(SegPhys(ss)+reg_sp+ 2); + reg_dx=mem_readw(SegPhys(ss)+reg_sp+ 4); + reg_bx=mem_readw(SegPhys(ss)+reg_sp+ 6); + reg_si=mem_readw(SegPhys(ss)+reg_sp+ 8); + reg_di=mem_readw(SegPhys(ss)+reg_sp+10); + reg_bp=mem_readw(SegPhys(ss)+reg_sp+12); + SegSet16(ds,mem_readw(SegPhys(ss)+reg_sp+14)); + SegSet16(es,mem_readw(SegPhys(ss)+reg_sp+16)); + reg_sp+=18; +} + +extern void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused); +void DOS_UpdatePSPName(void) { + DOS_MCB mcb(dos.psp()-1); + static char name[9]; + mcb.GetFileName(name); + name[8] = 0; + if (!strlen(name)) strcpy(name,"DOSBOX"); + for(Bitu i = 0;i < 8;i++) { //Don't put garbage in the title bar. Mac OS X doesn't like it + if (name[i] == 0) break; + if ( !isprint(*reinterpret_cast(&name[i])) ) name[i] = '?'; + } + RunningProgram = name; + GFX_SetTitle(-1,-1,false); +} + +void DOS_Terminate(Bit16u pspseg,bool tsr,Bit8u exitcode) { + + dos.return_code=exitcode; + dos.return_mode=(tsr)?(Bit8u)RETURN_TSR:(Bit8u)RETURN_EXIT; + + DOS_PSP curpsp(pspseg); + if (pspseg==curpsp.GetParent()) return; + /* Free Files owned by process */ + if (!tsr) curpsp.CloseFiles(); + + /* Get the termination address */ + RealPt old22 = curpsp.GetInt22(); + /* Restore vector 22,23,24 */ + curpsp.RestoreVectors(); + /* Set the parent PSP */ + dos.psp(curpsp.GetParent()); + DOS_PSP parentpsp(curpsp.GetParent()); + + /* Restore the SS:SP to the previous one */ + SegSet16(ss,RealSeg(parentpsp.GetStack())); + reg_sp = RealOff(parentpsp.GetStack()); + /* Restore the old CS:IP from int 22h */ + RestoreRegisters(); + /* Set the CS:IP stored in int 0x22 back on the stack */ + mem_writew(SegPhys(ss)+reg_sp+0,RealOff(old22)); + mem_writew(SegPhys(ss)+reg_sp+2,RealSeg(old22)); + /* set IOPL=3 (Strike Commander), nested task set, + interrupts enabled, test flags cleared */ + mem_writew(SegPhys(ss)+reg_sp+4,0x7202); + // Free memory owned by process + if (!tsr) DOS_FreeProcessMemory(pspseg); + DOS_UpdatePSPName(); + + if ((!(CPU_AutoDetermineMode>>CPU_AUTODETERMINE_SHIFT)) || (cpu.pmode)) return; + + CPU_AutoDetermineMode>>=CPU_AUTODETERMINE_SHIFT; + if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) { + CPU_CycleAutoAdjust=false; + CPU_CycleLeft=0; + CPU_Cycles=0; + CPU_CycleMax=CPU_OldCycleMax; + GFX_SetTitle(CPU_OldCycleMax,-1,false); + } else { + GFX_SetTitle(-1,-1,false); + } +#if (C_DYNAMIC_X86) || (C_DYNREC) + if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) { + cpudecoder=&CPU_Core_Normal_Run; + CPU_CycleLeft=0; + CPU_Cycles=0; + } +#endif + + return; +} + +static bool MakeEnv(char * name,Bit16u * segment) { + /* If segment to copy environment is 0 copy the caller's environment */ + DOS_PSP psp(dos.psp()); + PhysPt envread,envwrite; + Bit16u envsize=1; + bool parentenv=true; + + if (*segment==0) { + if (!psp.GetEnvironment()) parentenv=false; //environment seg=0 + envread=PhysMake(psp.GetEnvironment(),0); + } else { + if (!*segment) parentenv=false; //environment seg=0 + envread=PhysMake(*segment,0); + } + + if (parentenv) { + for (envsize=0; ;envsize++) { + if (envsize>=MAXENV - ENV_KEEPFREE) { + DOS_SetError(DOSERR_ENVIRONMENT_INVALID); + return false; + } + if (mem_readw(envread+envsize)==0) break; + } + envsize += 2; /* account for trailing \0\0 */ + } + Bit16u size=long2para(envsize+ENV_KEEPFREE); + if (!DOS_AllocateMemory(segment,&size)) return false; + envwrite=PhysMake(*segment,0); + if (parentenv) { + MEM_BlockCopy(envwrite,envread,envsize); +// mem_memcpy(envwrite,envread,envsize); + envwrite+=envsize; + } else { + mem_writeb(envwrite++,0); + } + mem_writew(envwrite,1); + envwrite+=2; + char namebuf[DOS_PATHLENGTH]; + if (DOS_Canonicalize(name,namebuf)) { + MEM_BlockWrite(envwrite,namebuf,(Bitu)(strlen(namebuf)+1)); + return true; + } else return false; +} + +bool DOS_NewPSP(Bit16u segment, Bit16u size) { + DOS_PSP psp(segment); + psp.MakeNew(size); + Bit16u parent_psp_seg=psp.GetParent(); + DOS_PSP psp_parent(parent_psp_seg); + psp.CopyFileTable(&psp_parent,false); + // copy command line as well (Kings Quest AGI -cga switch) + psp.SetCommandTail(RealMake(parent_psp_seg,0x80)); + return true; +} + +bool DOS_ChildPSP(Bit16u segment, Bit16u size) { + DOS_PSP psp(segment); + psp.MakeNew(size); + Bit16u parent_psp_seg = psp.GetParent(); + DOS_PSP psp_parent(parent_psp_seg); + psp.CopyFileTable(&psp_parent,true); + psp.SetCommandTail(RealMake(parent_psp_seg,0x80)); + psp.SetFCB1(RealMake(parent_psp_seg,0x5c)); + psp.SetFCB2(RealMake(parent_psp_seg,0x6c)); + psp.SetEnvironment(psp_parent.GetEnvironment()); + psp.SetSize(size); + return true; +} + +static void SetupPSP(Bit16u pspseg,Bit16u memsize,Bit16u envseg) { + /* Fix the PSP for psp and environment MCB's */ + DOS_MCB mcb((Bit16u)(pspseg-1)); + mcb.SetPSPSeg(pspseg); + mcb.SetPt((Bit16u)(envseg-1)); + mcb.SetPSPSeg(pspseg); + + DOS_PSP psp(pspseg); + psp.MakeNew(memsize); + psp.SetEnvironment(envseg); + + /* Copy file handles */ + DOS_PSP oldpsp(dos.psp()); + psp.CopyFileTable(&oldpsp,true); + +} + +static void SetupCMDLine(Bit16u pspseg,DOS_ParamBlock & block) { + DOS_PSP psp(pspseg); + // if cmdtail==0 it will inited as empty in SetCommandTail + psp.SetCommandTail(block.exec.cmdtail); +} + +bool DOS_Execute(char * name,PhysPt block_pt,Bit8u flags) { + EXE_Header head;Bitu i; + Bit16u fhandle;Bit16u len;Bit32u pos; + Bit16u pspseg,envseg,loadseg,memsize,readsize; + PhysPt loadaddress;RealPt relocpt; + Bitu headersize=0,imagesize=0; + DOS_ParamBlock block(block_pt); + + block.LoadData(); + //Remove the loadhigh flag for the moment! + if(flags&0x80) LOG(LOG_EXEC,LOG_ERROR)("using loadhigh flag!!!!!. dropping it"); + flags &= 0x7f; + if (flags!=LOADNGO && flags!=OVERLAY && flags!=LOAD) { + DOS_SetError(DOSERR_FORMAT_INVALID); + return false; +// E_Exit("DOS:Not supported execute mode %d for file %s",flags,name); + } + /* Check for EXE or COM File */ + bool iscom=false; + if (!DOS_OpenFile(name,OPEN_READ,&fhandle)) { + DOS_SetError(DOSERR_FILE_NOT_FOUND); + return false; + } + len=sizeof(EXE_Header); + if (!DOS_ReadFile(fhandle,(Bit8u *)&head,&len)) { + DOS_CloseFile(fhandle); + return false; + } + if (len 1 MB"); + head.pages&=0x07ff; + headersize = head.headersize*16; + imagesize = head.pages*512-headersize; + if (imagesize+headersize<512) imagesize = 512-headersize; + } + } + Bit8u * loadbuf=(Bit8u *)new Bit8u[0x10000]; + if (flags!=OVERLAY) { + /* Create an environment block */ + envseg=block.exec.envseg; + if (!MakeEnv(name,&envseg)) { + DOS_CloseFile(fhandle); + return false; + } + /* Get Memory */ + Bit16u minsize,maxsize;Bit16u maxfree=0xffff;DOS_AllocateMemory(&pspseg,&maxfree); + if (iscom) { + minsize=0x1000;maxsize=0xffff; + if (machine==MCH_PCJR) { + /* try to load file into memory below 96k */ + pos=0;DOS_SeekFile(fhandle,&pos,DOS_SEEK_SET); + Bit16u dataread=0x1800; + DOS_ReadFile(fhandle,loadbuf,&dataread); + if (dataread<0x1800) maxsize=dataread; + if (minsize>maxsize) minsize=maxsize; + } + } else { /* Exe size calculated from header */ + minsize=long2para(imagesize+(head.minmemory<<4)+256); + if (head.maxmemory!=0) maxsize=long2para(imagesize+(head.maxmemory<<4)+256); + else maxsize=0xffff; + } + if (maxfree>4)+0x20; + } + if (maxfree0x7FFF) { + readsize=0x8000;DOS_ReadFile(fhandle,loadbuf,&readsize); + MEM_BlockWrite(loadaddress,loadbuf,readsize); +// if (readsize!=0x8000) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header"); + loadaddress+=0x8000;imagesize-=0x8000; + } + if (imagesize>0) { + readsize=(Bit16u)imagesize;DOS_ReadFile(fhandle,loadbuf,&readsize); + MEM_BlockWrite(loadaddress,loadbuf,readsize); +// if (readsize!=imagesize) LOG(LOG_EXEC,LOG_NORMAL)("Illegal header"); + } + /* Relocate the exe image */ + Bit16u relocate; + if (flags==OVERLAY) relocate=block.overlay.relocation; + else relocate=loadseg; + pos=head.reloctable;DOS_SeekFile(fhandle,&pos,0); + for (i=0;i0xfffe) || (reg_sp<18)) LOG(LOG_EXEC,LOG_ERROR)("stack underflow/wrap at EXEC"); + /* Get Caller's program CS:IP of the stack and set termination address to that */ + RealSetVec(0x22,RealMake(mem_readw(SegPhys(ss)+reg_sp+2),mem_readw(SegPhys(ss)+reg_sp))); + SaveRegisters(); + DOS_PSP callpsp(dos.psp()); + /* Save the SS:SP on the PSP of calling program */ + callpsp.SetStack(RealMakeSeg(ss,reg_sp)); + /* Switch the psp's and set new DTA */ + dos.psp(pspseg); + DOS_PSP newpsp(dos.psp()); + dos.dta(RealMake(newpsp.GetSegment(),0x80)); + /* save vectors */ + newpsp.SaveVectors(); + /* copy fcbs */ + newpsp.SetFCB1(block.exec.fcb1); + newpsp.SetFCB2(block.exec.fcb2); + /* Set the stack for new program */ + SegSet16(ss,RealSeg(sssp));reg_sp=RealOff(sssp); + /* Add some flags and CS:IP on the stack for the IRET */ + CPU_Push16(RealSeg(csip)); + CPU_Push16(RealOff(csip)); + /* DOS starts programs with a RETF, so critical flags + * should not be modified (IOPL in v86 mode); + * interrupt flag is set explicitly, test flags cleared */ + reg_flags=(reg_flags&(~FMASK_TEST))|FLAG_IF; + //Jump to retf so that we only need to store cs:ip on the stack + reg_ip++; + /* Setup the rest of the registers */ + reg_ax=reg_bx=0;reg_cx=0xff; + reg_dx=pspseg; + reg_si=RealOff(csip); + reg_di=RealOff(sssp); + reg_bp=0x91c; /* DOS internal stack begin relict */ + SegSet16(ds,pspseg);SegSet16(es,pspseg); +#if C_DEBUG + /* Started from debug.com, then set breakpoint at start */ + DEBUG_CheckExecuteBreakpoint(RealSeg(csip),RealOff(csip)); +#endif + /* Add the filename to PSP and environment MCB's */ + char stripname[8]= { 0 };Bitu index=0; + while (char chr=*name++) { + switch (chr) { + case ':':case '\\':case '/':index=0;break; + default:if (index<8) stripname[index++]=(char)toupper(chr); + } + } + index=0; + while (index<8) { + if (stripname[index]=='.') break; + if (!stripname[index]) break; + index++; + } + memset(&stripname[index],0,8-index); + DOS_MCB pspmcb(dos.psp()-1); + pspmcb.SetFileName(stripname); + DOS_UpdatePSPName(); + return true; + } + return false; +} diff --git a/src/dosbox.cpp b/src/dosbox.cpp index 9e31162b..e83dc1d4 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -140,7 +140,7 @@ static Bitu Normal_Loop(void) { Bitu blah = (*CallBack_Handlers[ret])(); if (GCC_UNLIKELY(blah)) return blah; } -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER if (DEBUG_ExitLoop()) return 0; #endif } else { @@ -350,7 +350,7 @@ void DOSBOX_Init(void) { Pstring = secprop->Add_path("captures",Property::Changeable::Always,"capture"); Pstring->Set_help("Directory where things like wave, midi, screenshot get captured."); -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER LOG_StartUp(); #endif @@ -496,7 +496,7 @@ void DOSBOX_Init(void) { " In that case, add 'delaysysex', for example: midiconfig=2 delaysysex\n" " See the README/Manual for more details."); -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER secprop=control->AddSection_prop("debug",&DEBUG_Init); #endif diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index 58cd8e4c..e1e1a61a 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -1334,7 +1334,7 @@ static void GUI_StartUp(Section * sec) { MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Cap Mouse"); MAPPER_AddHandler(SwitchFullScreen,MK_return,MMOD2,"fullscr","Fullscreen"); MAPPER_AddHandler(Restart,MK_home,MMOD1|MMOD2,"restart","Restart"); -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER /* Pause binds with activate-debugger */ #else MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause"); @@ -1698,7 +1698,7 @@ static void launcheditor() { printf("can't find editor(s) specified at the command line.\n"); exit(1); } -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER extern void DEBUG_ShutDown(Section * /*sec*/); #endif @@ -1712,7 +1712,7 @@ void restart_program(std::vector & parameters) { SDL_CloseAudio(); SDL_Delay(50); SDL_Quit(); -#if C_DEBUG +#if C_DEBUG || C_GDBSERVER // shutdown curses DEBUG_ShutDown(NULL); #endif diff --git a/src/gui/sdlmain.cpp.orig b/src/gui/sdlmain.cpp.orig new file mode 100644 index 00000000..58cd8e4c --- /dev/null +++ b/src/gui/sdlmain.cpp.orig @@ -0,0 +1,2057 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#endif + +#include "cross.h" +#include "SDL.h" + +#include "dosbox.h" +#include "video.h" +#include "mouse.h" +#include "pic.h" +#include "timer.h" +#include "setup.h" +#include "support.h" +#include "debug.h" +#include "mapper.h" +#include "vga.h" +#include "keyboard.h" +#include "cpu.h" +#include "cross.h" +#include "control.h" + +#define MAPPERFILE "mapper-" VERSION ".map" +//#define DISABLE_JOYSTICK + +#if C_OPENGL +#include "SDL_opengl.h" + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif + +#ifdef __WIN32__ +#define NVIDIA_PixelDataRange 1 + +#ifndef WGL_NV_allocate_memory +#define WGL_NV_allocate_memory 1 +typedef void * (APIENTRY * PFNWGLALLOCATEMEMORYNVPROC) (int size, float readfreq, float writefreq, float priority); +typedef void (APIENTRY * PFNWGLFREEMEMORYNVPROC) (void *pointer); +#endif + +PFNWGLALLOCATEMEMORYNVPROC db_glAllocateMemoryNV = NULL; +PFNWGLFREEMEMORYNVPROC db_glFreeMemoryNV = NULL; + +#else + +#endif + +#if defined(NVIDIA_PixelDataRange) + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, GLvoid *pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#endif + +PFNGLPIXELDATARANGENVPROC glPixelDataRangeNV = NULL; + +#endif + +#endif //C_OPENGL + +#if !(ENVIRON_INCLUDED) +extern char** environ; +#endif + +#ifdef WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#if (HAVE_DDRAW_H) +#include +struct private_hwdata { + LPDIRECTDRAWSURFACE3 dd_surface; + LPDIRECTDRAWSURFACE3 dd_writebuf; +}; +#endif + +#define STDOUT_FILE TEXT("stdout.txt") +#define STDERR_FILE TEXT("stderr.txt") +#define DEFAULT_CONFIG_FILE "/dosbox.conf" +#elif defined(MACOSX) +#define DEFAULT_CONFIG_FILE "/Library/Preferences/DOSBox Preferences" +#else /*linux freebsd*/ +#define DEFAULT_CONFIG_FILE "/.dosboxrc" +#endif + +#if C_SET_PRIORITY +#include +#define PRIO_TOTAL (PRIO_MAX-PRIO_MIN) +#endif + +#ifdef OS2 +#define INCL_DOS +#define INCL_WIN +#include +#endif + +enum SCREEN_TYPES { + SCREEN_SURFACE, + SCREEN_SURFACE_DDRAW, + SCREEN_OVERLAY, + SCREEN_OPENGL +}; + +enum PRIORITY_LEVELS { + PRIORITY_LEVEL_PAUSE, + PRIORITY_LEVEL_LOWEST, + PRIORITY_LEVEL_LOWER, + PRIORITY_LEVEL_NORMAL, + PRIORITY_LEVEL_HIGHER, + PRIORITY_LEVEL_HIGHEST +}; + + +struct SDL_Block { + bool inited; + bool active; //If this isn't set don't draw + bool updating; + struct { + Bit32u width; + Bit32u height; + Bit32u bpp; + Bitu flags; + double scalex,scaley; + GFX_CallBack_t callback; + } draw; + bool wait_on_error; + struct { + struct { + Bit16u width, height; + bool fixed; + } full; + struct { + Bit16u width, height; + } window; + Bit8u bpp; + bool fullscreen; + bool lazy_fullscreen; + bool lazy_fullscreen_req; + bool doublebuf; + SCREEN_TYPES type; + SCREEN_TYPES want_type; + } desktop; +#if C_OPENGL + struct { + Bitu pitch; + void * framebuf; + GLuint texture; + GLuint displaylist; + GLint max_texsize; + bool bilinear; + bool packed_pixel; + bool paletted_texture; +#if defined(NVIDIA_PixelDataRange) + bool pixel_data_range; +#endif + } opengl; +#endif + struct { + SDL_Surface * surface; +#if (HAVE_DDRAW_H) && defined(WIN32) + RECT rect; +#endif + } blit; + struct { + PRIORITY_LEVELS focus; + PRIORITY_LEVELS nofocus; + } priority; + SDL_Rect clip; + SDL_Surface * surface; + SDL_Overlay * overlay; + SDL_cond *cond; + struct { + bool autolock; + bool autoenable; + bool requestlock; + bool locked; + Bitu sensitivity; + } mouse; + SDL_Rect updateRects[1024]; + Bitu num_joysticks; +#if defined (WIN32) + bool using_windib; +#endif + // state of alt-keys for certain special handlings + Bit8u laltstate; + Bit8u raltstate; +}; + +static SDL_Block sdl; + +extern const char* RunningProgram; +extern bool CPU_CycleAutoAdjust; +//Globals for keyboard initialisation +bool startup_state_numlock=false; +bool startup_state_capslock=false; +void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused){ + char title[200]={0}; + static Bit32s internal_cycles=0; + static Bits internal_frameskip=0; + if(cycles != -1) internal_cycles = cycles; + if(frameskip != -1) internal_frameskip = frameskip; + if(CPU_CycleAutoAdjust) { + sprintf(title,"DOSBox %s, CPU speed: max %3d%% cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram); + } else { + sprintf(title,"DOSBox %s, CPU speed: %8d cycles, Frameskip %2d, Program: %8s",VERSION,internal_cycles,internal_frameskip,RunningProgram); + } + + if(paused) strcat(title," PAUSED"); + SDL_WM_SetCaption(title,VERSION); +} + +static void KillSwitch(bool pressed) { + if (!pressed) + return; + throw 1; +} + +static void PauseDOSBox(bool pressed) { + if (!pressed) + return; + GFX_SetTitle(-1,-1,true); + bool paused = true; + KEYBOARD_ClrBuffer(); + SDL_Delay(500); + SDL_Event event; + while (SDL_PollEvent(&event)) { + // flush event queue. + } + + while (paused) { + SDL_WaitEvent(&event); // since we're not polling, cpu usage drops to 0. + switch (event.type) { + + case SDL_QUIT: KillSwitch(true); break; + case SDL_KEYDOWN: // Must use Pause/Break Key to resume. + case SDL_KEYUP: + if(event.key.keysym.sym == SDLK_PAUSE) { + + paused = false; + GFX_SetTitle(-1,-1,false); + break; + } +#if defined (MACOSX) + if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) { + /* On macs, all aps exit when pressing cmd-q */ + KillSwitch(true); + break; + } +#endif + } + } +} + +#if defined (WIN32) +bool GFX_SDLUsingWinDIB(void) { + return sdl.using_windib; +} +#endif + +/* Reset the screen with current values in the sdl structure */ +Bitu GFX_GetBestMode(Bitu flags) { + Bitu testbpp,gotbpp; + switch (sdl.desktop.want_type) { + case SCREEN_SURFACE: +check_surface: + flags &= ~GFX_LOVE_8; //Disable love for 8bpp modes + /* Check if we can satisfy the depth it loves */ + if (flags & GFX_LOVE_8) testbpp=8; + else if (flags & GFX_LOVE_15) testbpp=15; + else if (flags & GFX_LOVE_16) testbpp=16; + else if (flags & GFX_LOVE_32) testbpp=32; + else testbpp=0; +#if (HAVE_DDRAW_H) && defined(WIN32) +check_gotbpp: +#endif + if (sdl.desktop.fullscreen) gotbpp=SDL_VideoModeOK(640,480,testbpp,SDL_FULLSCREEN|SDL_HWSURFACE|SDL_HWPALETTE); + else gotbpp=sdl.desktop.bpp; + /* If we can't get our favorite mode check for another working one */ + switch (gotbpp) { + case 8: + if (flags & GFX_CAN_8) flags&=~(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32); + break; + case 15: + if (flags & GFX_CAN_15) flags&=~(GFX_CAN_8|GFX_CAN_16|GFX_CAN_32); + break; + case 16: + if (flags & GFX_CAN_16) flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_32); + break; + case 24: + case 32: + if (flags & GFX_CAN_32) flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); + break; + } + flags |= GFX_CAN_RANDOM; + break; +#if (HAVE_DDRAW_H) && defined(WIN32) + case SCREEN_SURFACE_DDRAW: + if (!(flags&(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32))) goto check_surface; + if (flags & GFX_LOVE_15) testbpp=15; + else if (flags & GFX_LOVE_16) testbpp=16; + else if (flags & GFX_LOVE_32) testbpp=32; + else testbpp=0; + flags|=GFX_SCALING; + goto check_gotbpp; +#endif + case SCREEN_OVERLAY: + if (flags & GFX_RGBONLY || !(flags&GFX_CAN_32)) goto check_surface; + flags|=GFX_SCALING; + flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); + break; +#if C_OPENGL + case SCREEN_OPENGL: + if (flags & GFX_RGBONLY || !(flags&GFX_CAN_32)) goto check_surface; + flags|=GFX_SCALING; + flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16); + break; +#endif + default: + goto check_surface; + break; + } + return flags; +} + + +void GFX_ResetScreen(void) { + GFX_Stop(); + if (sdl.draw.callback) + (sdl.draw.callback)( GFX_CallBackReset ); + GFX_Start(); + CPU_Reset_AutoAdjust(); +} + +void GFX_ForceFullscreenExit(void) { + if (sdl.desktop.lazy_fullscreen) { +// sdl.desktop.lazy_fullscreen_req=true; + LOG_MSG("GFX LF: invalid screen change"); + } else { + sdl.desktop.fullscreen=false; + GFX_ResetScreen(); + } +} + +static int int_log2 (int val) { + int log = 0; + while ((val >>= 1) != 0) + log++; + return log; +} + + +static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) { + Bit16u fixedWidth; + Bit16u fixedHeight; + + if (sdl.desktop.fullscreen) { + fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0; + fixedHeight = sdl.desktop.full.fixed ? sdl.desktop.full.height : 0; + sdl_flags |= SDL_FULLSCREEN|SDL_HWSURFACE; + } else { + fixedWidth = sdl.desktop.window.width; + fixedHeight = sdl.desktop.window.height; + sdl_flags |= SDL_HWSURFACE; + } + if (fixedWidth && fixedHeight) { + double ratio_w=(double)fixedWidth/(sdl.draw.width*sdl.draw.scalex); + double ratio_h=(double)fixedHeight/(sdl.draw.height*sdl.draw.scaley); + if ( ratio_w < ratio_h) { + sdl.clip.w=fixedWidth; + sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley*ratio_w); + } else { + sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex*ratio_h); + sdl.clip.h=(Bit16u)fixedHeight; + } + if (sdl.desktop.fullscreen) + sdl.surface = SDL_SetVideoMode(fixedWidth,fixedHeight,bpp,sdl_flags); + else + sdl.surface = SDL_SetVideoMode(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); + if (sdl.surface && sdl.surface->flags & SDL_FULLSCREEN) { + sdl.clip.x=(Sint16)((sdl.surface->w-sdl.clip.w)/2); + sdl.clip.y=(Sint16)((sdl.surface->h-sdl.clip.h)/2); + } else { + sdl.clip.x = 0; + sdl.clip.y = 0; + } + return sdl.surface; + } else { + sdl.clip.x=0;sdl.clip.y=0; + sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex); + sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley); + sdl.surface=SDL_SetVideoMode(sdl.clip.w,sdl.clip.h,bpp,sdl_flags); + return sdl.surface; + } +} + +void GFX_TearDown(void) { + if (sdl.updating) + GFX_EndUpdate( 0 ); + + if (sdl.blit.surface) { + SDL_FreeSurface(sdl.blit.surface); + sdl.blit.surface=0; + } +} + +Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) { + if (sdl.updating) + GFX_EndUpdate( 0 ); + + sdl.draw.width=width; + sdl.draw.height=height; + sdl.draw.callback=callback; + sdl.draw.scalex=scalex; + sdl.draw.scaley=scaley; + + Bitu bpp=0; + Bitu retFlags = 0; + + if (sdl.blit.surface) { + SDL_FreeSurface(sdl.blit.surface); + sdl.blit.surface=0; + } + switch (sdl.desktop.want_type) { + case SCREEN_SURFACE: +dosurface: + if (flags & GFX_CAN_8) bpp=8; + if (flags & GFX_CAN_15) bpp=15; + if (flags & GFX_CAN_16) bpp=16; + if (flags & GFX_CAN_32) bpp=32; + sdl.desktop.type=SCREEN_SURFACE; + sdl.clip.w=width; + sdl.clip.h=height; + if (sdl.desktop.fullscreen) { + if (sdl.desktop.full.fixed) { + sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2); + sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2); + sdl.surface=SDL_SetVideoMode(sdl.desktop.full.width,sdl.desktop.full.height,bpp, + SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | + (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0) | SDL_HWPALETTE); + if (sdl.surface == NULL) E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,bpp,SDL_GetError()); + } else { + sdl.clip.x=0;sdl.clip.y=0; + sdl.surface=SDL_SetVideoMode(width,height,bpp, + SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) | + (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0)|SDL_HWPALETTE); + if (sdl.surface == NULL) + E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",width,height,bpp,SDL_GetError()); + } + } else { + sdl.clip.x=0;sdl.clip.y=0; + sdl.surface=SDL_SetVideoMode(width,height,bpp,(flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE); +#ifdef WIN32 + if (sdl.surface == NULL) { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + if (!sdl.using_windib) { + LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with windib enabled."); + putenv("SDL_VIDEODRIVER=windib"); + sdl.using_windib=true; + } else { + LOG_MSG("Failed to create hardware surface.\nRestarting video subsystem with directx enabled."); + putenv("SDL_VIDEODRIVER=directx"); + sdl.using_windib=false; + } + SDL_InitSubSystem(SDL_INIT_VIDEO); + sdl.surface = SDL_SetVideoMode(width,height,bpp,SDL_HWSURFACE); + } +#endif + if (sdl.surface == NULL) + E_Exit("Could not set windowed video mode %ix%i-%i: %s",width,height,bpp,SDL_GetError()); + } + if (sdl.surface) { + switch (sdl.surface->format->BitsPerPixel) { + case 8: + retFlags = GFX_CAN_8; + break; + case 15: + retFlags = GFX_CAN_15; + break; + case 16: + retFlags = GFX_CAN_16; + break; + case 32: + retFlags = GFX_CAN_32; + break; + } + if (retFlags && (sdl.surface->flags & SDL_HWSURFACE)) + retFlags |= GFX_HARDWARE; + if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF)) { + sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE, + sdl.draw.width, sdl.draw.height, + sdl.surface->format->BitsPerPixel, + sdl.surface->format->Rmask, + sdl.surface->format->Gmask, + sdl.surface->format->Bmask, + 0); + /* If this one fails be ready for some flickering... */ + } + } + break; +#if (HAVE_DDRAW_H) && defined(WIN32) + case SCREEN_SURFACE_DDRAW: + if (flags & GFX_CAN_15) bpp=15; + if (flags & GFX_CAN_16) bpp=16; + if (flags & GFX_CAN_32) bpp=32; + if (!GFX_SetupSurfaceScaled((sdl.desktop.doublebuf && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface; + sdl.blit.rect.top=sdl.clip.y; + sdl.blit.rect.left=sdl.clip.x; + sdl.blit.rect.right=sdl.clip.x+sdl.clip.w; + sdl.blit.rect.bottom=sdl.clip.y+sdl.clip.h; + sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE,sdl.draw.width,sdl.draw.height, + sdl.surface->format->BitsPerPixel, + sdl.surface->format->Rmask, + sdl.surface->format->Gmask, + sdl.surface->format->Bmask, + 0); + if (!sdl.blit.surface || (!sdl.blit.surface->flags&SDL_HWSURFACE)) { + if (sdl.blit.surface) { + SDL_FreeSurface(sdl.blit.surface); + sdl.blit.surface=0; + } + LOG_MSG("Failed to create ddraw surface, back to normal surface."); + goto dosurface; + } + switch (sdl.surface->format->BitsPerPixel) { + case 15: + retFlags = GFX_CAN_15 | GFX_SCALING | GFX_HARDWARE; + break; + case 16: + retFlags = GFX_CAN_16 | GFX_SCALING | GFX_HARDWARE; + break; + case 32: + retFlags = GFX_CAN_32 | GFX_SCALING | GFX_HARDWARE; + break; + } + sdl.desktop.type=SCREEN_SURFACE_DDRAW; + break; +#endif + case SCREEN_OVERLAY: + if (sdl.overlay) { + SDL_FreeYUVOverlay(sdl.overlay); + sdl.overlay=0; + } + if (!(flags&GFX_CAN_32) || (flags & GFX_RGBONLY)) goto dosurface; + if (!GFX_SetupSurfaceScaled(0,0)) goto dosurface; + sdl.overlay=SDL_CreateYUVOverlay(width*2,height,SDL_UYVY_OVERLAY,sdl.surface); + if (!sdl.overlay) { + LOG_MSG("SDL:Failed to create overlay, switching back to surface"); + goto dosurface; + } + sdl.desktop.type=SCREEN_OVERLAY; + retFlags = GFX_CAN_32 | GFX_SCALING | GFX_HARDWARE; + break; +#if C_OPENGL + case SCREEN_OPENGL: + { + if (sdl.opengl.framebuf) { +#if defined(NVIDIA_PixelDataRange) + if (sdl.opengl.pixel_data_range) db_glFreeMemoryNV(sdl.opengl.framebuf); + else +#endif + free(sdl.opengl.framebuf); + } + sdl.opengl.framebuf=0; + if (!(flags&GFX_CAN_32) || (flags & GFX_RGBONLY)) goto dosurface; + int texsize=2 << int_log2(width > height ? width : height); + if (texsize>sdl.opengl.max_texsize) { + LOG_MSG("SDL:OPENGL:No support for texturesize of %d, falling back to surface",texsize); + goto dosurface; + } + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); +#if defined (WIN32) && SDL_VERSION_ATLEAST(1, 2, 11) + SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 ); +#endif + GFX_SetupSurfaceScaled(SDL_OPENGL,0); + if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) { + LOG_MSG("SDL:OPENGL:Can't open drawing surface, are you running in 16bpp(or higher) mode?"); + goto dosurface; + } + /* Create the texture and display list */ +#if defined(NVIDIA_PixelDataRange) + if (sdl.opengl.pixel_data_range) { + sdl.opengl.framebuf=db_glAllocateMemoryNV(width*height*4,0.0,1.0,1.0); + glPixelDataRangeNV(GL_WRITE_PIXEL_DATA_RANGE_NV,width*height*4,sdl.opengl.framebuf); + glEnableClientState(GL_WRITE_PIXEL_DATA_RANGE_NV); + } else { +#else + { +#endif + sdl.opengl.framebuf=malloc(width*height*4); //32 bit color + } + sdl.opengl.pitch=width*4; + glViewport(sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h); + glMatrixMode (GL_PROJECTION); + glDeleteTextures(1,&sdl.opengl.texture); + glGenTextures(1,&sdl.opengl.texture); + glBindTexture(GL_TEXTURE_2D,sdl.opengl.texture); + // No borders + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + if (sdl.opengl.bilinear) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0); + + glClearColor (0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + SDL_GL_SwapBuffers(); + glClear(GL_COLOR_BUFFER_BIT); + glShadeModel (GL_FLAT); + glDisable (GL_DEPTH_TEST); + glDisable (GL_LIGHTING); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + + GLfloat tex_width=((GLfloat)(width)/(GLfloat)texsize); + GLfloat tex_height=((GLfloat)(height)/(GLfloat)texsize); + + if (glIsList(sdl.opengl.displaylist)) glDeleteLists(sdl.opengl.displaylist, 1); + sdl.opengl.displaylist = glGenLists(1); + glNewList(sdl.opengl.displaylist, GL_COMPILE); + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + glBegin(GL_QUADS); + // lower left + glTexCoord2f(0,tex_height); glVertex2f(-1.0f,-1.0f); + // lower right + glTexCoord2f(tex_width,tex_height); glVertex2f(1.0f, -1.0f); + // upper right + glTexCoord2f(tex_width,0); glVertex2f(1.0f, 1.0f); + // upper left + glTexCoord2f(0,0); glVertex2f(-1.0f, 1.0f); + glEnd(); + glEndList(); + sdl.desktop.type=SCREEN_OPENGL; + retFlags = GFX_CAN_32 | GFX_SCALING; +#if defined(NVIDIA_PixelDataRange) + if (sdl.opengl.pixel_data_range) + retFlags |= GFX_HARDWARE; +#endif + break; + }//OPENGL +#endif //C_OPENGL + default: + goto dosurface; + break; + }//CASE + if (retFlags) + GFX_Start(); + if (!sdl.mouse.autoenable) SDL_ShowCursor(sdl.mouse.autolock?SDL_DISABLE:SDL_ENABLE); + return retFlags; +} + +void GFX_CaptureMouse(void) { + sdl.mouse.locked=!sdl.mouse.locked; + if (sdl.mouse.locked) { + SDL_WM_GrabInput(SDL_GRAB_ON); + SDL_ShowCursor(SDL_DISABLE); + } else { + SDL_WM_GrabInput(SDL_GRAB_OFF); + if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE); + } + mouselocked=sdl.mouse.locked; +} + +void GFX_UpdateSDLCaptureState(void) { + if (sdl.mouse.locked) { + SDL_WM_GrabInput(SDL_GRAB_ON); + SDL_ShowCursor(SDL_DISABLE); + } else { + SDL_WM_GrabInput(SDL_GRAB_OFF); + if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE); + } + CPU_Reset_AutoAdjust(); + GFX_SetTitle(-1,-1,false); +} + +bool mouselocked; //Global variable for mapper +static void CaptureMouse(bool pressed) { + if (!pressed) + return; + GFX_CaptureMouse(); +} + +#if defined (WIN32) +STICKYKEYS stick_keys = {sizeof(STICKYKEYS), 0}; +void sticky_keys(bool restore){ + static bool inited = false; + if (!inited){ + inited = true; + SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0); + } + if (restore) { + SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &stick_keys, 0); + return; + } + //Get current sticky keys layout: + STICKYKEYS s = {sizeof(STICKYKEYS), 0}; + SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0); + if ( !(s.dwFlags & SKF_STICKYKEYSON)) { //Not on already + s.dwFlags &= ~SKF_HOTKEYACTIVE; + SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &s, 0); + } +} +#endif + +void GFX_SwitchFullScreen(void) { + sdl.desktop.fullscreen=!sdl.desktop.fullscreen; + if (sdl.desktop.fullscreen) { + if (!sdl.mouse.locked) GFX_CaptureMouse(); +#if defined (WIN32) + sticky_keys(false); //disable sticky keys in fullscreen mode +#endif + } else { + if (sdl.mouse.locked) GFX_CaptureMouse(); +#if defined (WIN32) + sticky_keys(true); //restore sticky keys to default state in windowed mode. +#endif + } + GFX_ResetScreen(); +} + +static void SwitchFullScreen(bool pressed) { + if (!pressed) + return; + + if (sdl.desktop.lazy_fullscreen) { +// sdl.desktop.lazy_fullscreen_req=true; + LOG_MSG("GFX LF: fullscreen switching not supported"); + } else { + GFX_SwitchFullScreen(); + } +} + +void GFX_SwitchLazyFullscreen(bool lazy) { + sdl.desktop.lazy_fullscreen=lazy; + sdl.desktop.lazy_fullscreen_req=false; +} + +void GFX_SwitchFullscreenNoReset(void) { + sdl.desktop.fullscreen=!sdl.desktop.fullscreen; +} + +bool GFX_LazyFullscreenRequested(void) { + if (sdl.desktop.lazy_fullscreen) return sdl.desktop.lazy_fullscreen_req; + return false; +} + +void GFX_RestoreMode(void) { + GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback); + GFX_UpdateSDLCaptureState(); +} + + +bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) { + if (!sdl.active || sdl.updating) + return false; + switch (sdl.desktop.type) { + case SCREEN_SURFACE: + if (sdl.blit.surface) { + if (SDL_MUSTLOCK(sdl.blit.surface) && SDL_LockSurface(sdl.blit.surface)) + return false; + pixels=(Bit8u *)sdl.blit.surface->pixels; + pitch=sdl.blit.surface->pitch; + } else { + if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface)) + return false; + pixels=(Bit8u *)sdl.surface->pixels; + pixels+=sdl.clip.y*sdl.surface->pitch; + pixels+=sdl.clip.x*sdl.surface->format->BytesPerPixel; + pitch=sdl.surface->pitch; + } + sdl.updating=true; + return true; +#if (HAVE_DDRAW_H) && defined(WIN32) + case SCREEN_SURFACE_DDRAW: + if (SDL_LockSurface(sdl.blit.surface)) { +// LOG_MSG("SDL Lock failed"); + return false; + } + pixels=(Bit8u *)sdl.blit.surface->pixels; + pitch=sdl.blit.surface->pitch; + sdl.updating=true; + return true; +#endif + case SCREEN_OVERLAY: + if (SDL_LockYUVOverlay(sdl.overlay)) return false; + pixels=(Bit8u *)*(sdl.overlay->pixels); + pitch=*(sdl.overlay->pitches); + sdl.updating=true; + return true; +#if C_OPENGL + case SCREEN_OPENGL: + pixels=(Bit8u *)sdl.opengl.framebuf; + pitch=sdl.opengl.pitch; + sdl.updating=true; + return true; +#endif + default: + break; + } + return false; +} + + +void GFX_EndUpdate( const Bit16u *changedLines ) { +#if (HAVE_DDRAW_H) && defined(WIN32) + int ret; +#endif + if (!sdl.updating) + return; + sdl.updating=false; + switch (sdl.desktop.type) { + case SCREEN_SURFACE: + if (SDL_MUSTLOCK(sdl.surface)) { + if (sdl.blit.surface) { + SDL_UnlockSurface(sdl.blit.surface); + int Blit = SDL_BlitSurface( sdl.blit.surface, 0, sdl.surface, &sdl.clip ); + LOG(LOG_MISC,LOG_WARN)("BlitSurface returned %d",Blit); + } else { + SDL_UnlockSurface(sdl.surface); + } + SDL_Flip(sdl.surface); + } else if (changedLines) { + Bitu y = 0, index = 0, rectCount = 0; + while (y < sdl.draw.height) { + if (!(index & 1)) { + y += changedLines[index]; + } else { + SDL_Rect *rect = &sdl.updateRects[rectCount++]; + rect->x = sdl.clip.x; + rect->y = sdl.clip.y + y; + rect->w = (Bit16u)sdl.draw.width; + rect->h = changedLines[index]; +#if 0 + if (rect->h + rect->y > sdl.surface->h) { + LOG_MSG("WTF %d + %d >%d",rect->h,rect->y,sdl.surface->h); + } +#endif + y += changedLines[index]; + } + index++; + } + if (rectCount) + SDL_UpdateRects( sdl.surface, rectCount, sdl.updateRects ); + } + break; +#if (HAVE_DDRAW_H) && defined(WIN32) + case SCREEN_SURFACE_DDRAW: + SDL_UnlockSurface(sdl.blit.surface); + ret=IDirectDrawSurface3_Blt( + sdl.surface->hwdata->dd_writebuf,&sdl.blit.rect, + sdl.blit.surface->hwdata->dd_surface,0, + DDBLT_WAIT, NULL); + switch (ret) { + case DD_OK: + break; + case DDERR_SURFACELOST: + IDirectDrawSurface3_Restore(sdl.blit.surface->hwdata->dd_surface); + IDirectDrawSurface3_Restore(sdl.surface->hwdata->dd_surface); + break; + default: + LOG_MSG("DDRAW:Failed to blit, error %X",ret); + } + SDL_Flip(sdl.surface); + break; +#endif + case SCREEN_OVERLAY: + SDL_UnlockYUVOverlay(sdl.overlay); + SDL_DisplayYUVOverlay(sdl.overlay,&sdl.clip); + break; +#if C_OPENGL + case SCREEN_OPENGL: +#if defined(NVIDIA_PixelDataRange) + if (sdl.opengl.pixel_data_range) { + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + sdl.draw.width, sdl.draw.height, GL_BGRA_EXT, + GL_UNSIGNED_INT_8_8_8_8_REV, sdl.opengl.framebuf); + glCallList(sdl.opengl.displaylist); + SDL_GL_SwapBuffers(); + } else +#endif + if (changedLines) { + Bitu y = 0, index = 0; + glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture); + while (y < sdl.draw.height) { + if (!(index & 1)) { + y += changedLines[index]; + } else { + Bit8u *pixels = (Bit8u *)sdl.opengl.framebuf + y * sdl.opengl.pitch; + Bitu height = changedLines[index]; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, + sdl.draw.width, height, GL_BGRA_EXT, + GL_UNSIGNED_INT_8_8_8_8_REV, pixels ); + y += height; + } + index++; + } + glCallList(sdl.opengl.displaylist); + SDL_GL_SwapBuffers(); + } + break; +#endif + default: + break; + } +} + + +void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) { + /* I should probably not change the GFX_PalEntry :) */ + if (sdl.surface->flags & SDL_HWPALETTE) { + if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,start,count)) { + E_Exit("SDL:Can't set palette"); + } + } else { + if (!SDL_SetPalette(sdl.surface,SDL_LOGPAL,(SDL_Color *)entries,start,count)) { + E_Exit("SDL:Can't set palette"); + } + } +} + +Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue) { + switch (sdl.desktop.type) { + case SCREEN_SURFACE: + case SCREEN_SURFACE_DDRAW: + return SDL_MapRGB(sdl.surface->format,red,green,blue); + case SCREEN_OVERLAY: + { + Bit8u y = ( 9797*(red) + 19237*(green) + 3734*(blue) ) >> 15; + Bit8u u = (18492*((blue)-(y)) >> 15) + 128; + Bit8u v = (23372*((red)-(y)) >> 15) + 128; +#ifdef WORDS_BIGENDIAN + return (y << 0) | (v << 8) | (y << 16) | (u << 24); +#else + return (u << 0) | (y << 8) | (v << 16) | (y << 24); +#endif + } + case SCREEN_OPENGL: +// return ((red << 0) | (green << 8) | (blue << 16)) | (255 << 24); + //USE BGRA + return ((blue << 0) | (green << 8) | (red << 16)) | (255 << 24); + } + return 0; +} + +void GFX_Stop() { + if (sdl.updating) + GFX_EndUpdate( 0 ); + sdl.active=false; +} + +void GFX_Start() { + sdl.active=true; +} + +static void GUI_ShutDown(Section * /*sec*/) { + GFX_Stop(); + if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop ); + if (sdl.mouse.locked) GFX_CaptureMouse(); + if (sdl.desktop.fullscreen) GFX_SwitchFullScreen(); +} + + +static void SetPriority(PRIORITY_LEVELS level) { + +#if C_SET_PRIORITY +// Do nothing if priorties are not the same and not root, else the highest +// priority can not be set as users can only lower priority (not restore it) + + if((sdl.priority.focus != sdl.priority.nofocus ) && + (getuid()!=0) ) return; + +#endif + switch (level) { +#ifdef WIN32 + case PRIORITY_LEVEL_PAUSE: // if DOSBox is paused, assume idle priority + case PRIORITY_LEVEL_LOWEST: + SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS); + break; + case PRIORITY_LEVEL_LOWER: + SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS); + break; + case PRIORITY_LEVEL_NORMAL: + SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS); + break; + case PRIORITY_LEVEL_HIGHER: + SetPriorityClass(GetCurrentProcess(),ABOVE_NORMAL_PRIORITY_CLASS); + break; + case PRIORITY_LEVEL_HIGHEST: + SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS); + break; +#elif C_SET_PRIORITY +/* Linux use group as dosbox has mulitple threads under linux */ + case PRIORITY_LEVEL_PAUSE: // if DOSBox is paused, assume idle priority + case PRIORITY_LEVEL_LOWEST: + setpriority (PRIO_PGRP, 0,PRIO_MAX); + break; + case PRIORITY_LEVEL_LOWER: + setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/3)); + break; + case PRIORITY_LEVEL_NORMAL: + setpriority (PRIO_PGRP, 0,PRIO_MAX-(PRIO_TOTAL/2)); + break; + case PRIORITY_LEVEL_HIGHER: + setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/5) ); + break; + case PRIORITY_LEVEL_HIGHEST: + setpriority (PRIO_PGRP, 0,PRIO_MAX-((3*PRIO_TOTAL)/4) ); + break; +#endif + default: + break; + } +} + +extern Bit8u int10_font_14[256 * 14]; +static void OutputString(Bitu x,Bitu y,const char * text,Bit32u color,Bit32u color2,SDL_Surface * output_surface) { + Bit32u * draw=(Bit32u*)(((Bit8u *)output_surface->pixels)+((y)*output_surface->pitch))+x; + while (*text) { + Bit8u * font=&int10_font_14[(*text)*14]; + Bitu i,j; + Bit32u * draw_line=draw; + for (i=0;i<14;i++) { + Bit8u map=*font++; + for (j=0;j<8;j++) { + if (map & 0x80) *((Bit32u*)(draw_line+j))=color; else *((Bit32u*)(draw_line+j))=color2; + map<<=1; + } + draw_line+=output_surface->pitch/4; + } + text++; + draw+=8; + } +} + +static unsigned char logo[32*32*4]= { +#include "dosbox_logo.h" +}; +#include "dosbox_splash.h" + +//extern void UI_Run(bool); +void Restart(bool pressed); + +static void GUI_StartUp(Section * sec) { + sec->AddDestroyFunction(&GUI_ShutDown); + Section_prop * section=static_cast(sec); + sdl.active=false; + sdl.updating=false; + +#if !defined(MACOSX) + /* Set Icon (must be done before any sdl_setvideomode call) */ + /* But don't set it on OS X, as we use a nicer external icon there. */ +#if WORDS_BIGENDIAN + SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0xff000000,0x00ff0000,0x0000ff00,0); +#else + SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0); +#endif + SDL_WM_SetIcon(logos,NULL); +#endif + + sdl.desktop.lazy_fullscreen=false; + sdl.desktop.lazy_fullscreen_req=false; + + sdl.desktop.fullscreen=section->Get_bool("fullscreen"); + sdl.wait_on_error=section->Get_bool("waitonerror"); + + Prop_multival* p=section->Get_multival("priority"); + std::string focus = p->GetSection()->Get_string("active"); + std::string notfocus = p->GetSection()->Get_string("inactive"); + + if (focus == "lowest") { sdl.priority.focus = PRIORITY_LEVEL_LOWEST; } + else if (focus == "lower") { sdl.priority.focus = PRIORITY_LEVEL_LOWER; } + else if (focus == "normal") { sdl.priority.focus = PRIORITY_LEVEL_NORMAL; } + else if (focus == "higher") { sdl.priority.focus = PRIORITY_LEVEL_HIGHER; } + else if (focus == "highest") { sdl.priority.focus = PRIORITY_LEVEL_HIGHEST; } + + if (notfocus == "lowest") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWEST; } + else if (notfocus == "lower") { sdl.priority.nofocus=PRIORITY_LEVEL_LOWER; } + else if (notfocus == "normal") { sdl.priority.nofocus=PRIORITY_LEVEL_NORMAL; } + else if (notfocus == "higher") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHER; } + else if (notfocus == "highest") { sdl.priority.nofocus=PRIORITY_LEVEL_HIGHEST; } + else if (notfocus == "pause") { + /* we only check for pause here, because it makes no sense + * for DOSBox to be paused while it has focus + */ + sdl.priority.nofocus=PRIORITY_LEVEL_PAUSE; + } + + SetPriority(sdl.priority.focus); //Assume focus on startup + sdl.mouse.locked=false; + mouselocked=false; //Global for mapper + sdl.mouse.requestlock=false; + sdl.desktop.full.fixed=false; + const char* fullresolution=section->Get_string("fullresolution"); + sdl.desktop.full.width = 0; + sdl.desktop.full.height = 0; + if(fullresolution && *fullresolution) { + char res[100]; + strncpy( res, fullresolution, sizeof( res )); + fullresolution = lowcase (res);//so x and X are allowed + if(strcmp(fullresolution,"original")) { + sdl.desktop.full.fixed = true; + char* height = const_cast(strchr(fullresolution,'x')); + if(height && * height) { + *height = 0; + sdl.desktop.full.height = (Bit16u)atoi(height+1); + sdl.desktop.full.width = (Bit16u)atoi(res); + } + } + } + + sdl.desktop.window.width = 0; + sdl.desktop.window.height = 0; + const char* windowresolution=section->Get_string("windowresolution"); + if(windowresolution && *windowresolution) { + char res[100]; + strncpy( res,windowresolution, sizeof( res )); + windowresolution = lowcase (res);//so x and X are allowed + if(strcmp(windowresolution,"original")) { + char* height = const_cast(strchr(windowresolution,'x')); + if(height && *height) { + *height = 0; + sdl.desktop.window.height = (Bit16u)atoi(height+1); + sdl.desktop.window.width = (Bit16u)atoi(res); + } + } + } + sdl.desktop.doublebuf=section->Get_bool("fulldouble"); + if (!sdl.desktop.full.width) { +#ifdef WIN32 + sdl.desktop.full.width=(Bit16u)GetSystemMetrics(SM_CXSCREEN); +#else + sdl.desktop.full.width=1024; +#endif + } + if (!sdl.desktop.full.height) { +#ifdef WIN32 + sdl.desktop.full.height=(Bit16u)GetSystemMetrics(SM_CYSCREEN); +#else + sdl.desktop.full.height=768; +#endif + } + sdl.mouse.autoenable=section->Get_bool("autolock"); + if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE); + sdl.mouse.autolock=false; + sdl.mouse.sensitivity=section->Get_int("sensitivity"); + std::string output=section->Get_string("output"); + + /* Setup Mouse correctly if fullscreen */ + if(sdl.desktop.fullscreen) GFX_CaptureMouse(); + + if (output == "surface") { + sdl.desktop.want_type=SCREEN_SURFACE; +#if (HAVE_DDRAW_H) && defined(WIN32) + } else if (output == "ddraw") { + sdl.desktop.want_type=SCREEN_SURFACE_DDRAW; +#endif + } else if (output == "overlay") { + sdl.desktop.want_type=SCREEN_OVERLAY; +#if C_OPENGL + } else if (output == "opengl") { + sdl.desktop.want_type=SCREEN_OPENGL; + sdl.opengl.bilinear=true; + } else if (output == "openglnb") { + sdl.desktop.want_type=SCREEN_OPENGL; + sdl.opengl.bilinear=false; +#endif + } else { + LOG_MSG("SDL:Unsupported output device %s, switching back to surface",output.c_str()); + sdl.desktop.want_type=SCREEN_SURFACE;//SHOULDN'T BE POSSIBLE anymore + } + + sdl.overlay=0; +#if C_OPENGL + if(sdl.desktop.want_type==SCREEN_OPENGL){ /* OPENGL is requested */ + sdl.surface=SDL_SetVideoMode(640,400,0,SDL_OPENGL); + if (sdl.surface == NULL) { + LOG_MSG("Could not initialize OpenGL, switching back to surface"); + sdl.desktop.want_type=SCREEN_SURFACE; + } else { + sdl.opengl.framebuf=0; + sdl.opengl.texture=0; + sdl.opengl.displaylist=0; + glGetIntegerv (GL_MAX_TEXTURE_SIZE, &sdl.opengl.max_texsize); +#if defined(__WIN32__) && defined(NVIDIA_PixelDataRange) + glPixelDataRangeNV = (PFNGLPIXELDATARANGENVPROC) wglGetProcAddress("glPixelDataRangeNV"); + db_glAllocateMemoryNV = (PFNWGLALLOCATEMEMORYNVPROC) wglGetProcAddress("wglAllocateMemoryNV"); + db_glFreeMemoryNV = (PFNWGLFREEMEMORYNVPROC) wglGetProcAddress("wglFreeMemoryNV"); +#endif + const char * gl_ext = (const char *)glGetString (GL_EXTENSIONS); + if(gl_ext && *gl_ext){ + sdl.opengl.packed_pixel=(strstr(gl_ext,"EXT_packed_pixels") > 0); + sdl.opengl.paletted_texture=(strstr(gl_ext,"EXT_paletted_texture") > 0); +#if defined(NVIDIA_PixelDataRange) + sdl.opengl.pixel_data_range=(strstr(gl_ext,"GL_NV_pixel_data_range") >0 ) && + glPixelDataRangeNV && db_glAllocateMemoryNV && db_glFreeMemoryNV; + sdl.opengl.pixel_data_range = 0; +#endif + } else { + sdl.opengl.packed_pixel=sdl.opengl.paletted_texture=false; + } + } + } /* OPENGL is requested end */ + +#endif //OPENGL + /* Initialize screen for first time */ + sdl.surface=SDL_SetVideoMode(640,400,0,0); + if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError()); + sdl.desktop.bpp=sdl.surface->format->BitsPerPixel; + if (sdl.desktop.bpp==24) { + LOG_MSG("SDL:You are running in 24 bpp mode, this will slow down things!"); + } + GFX_Stop(); + SDL_WM_SetCaption("DOSBox",VERSION); + +/* The endian part is intentionally disabled as somehow it produces correct results without according to rhoenie*/ +//#if SDL_BYTEORDER == SDL_BIG_ENDIAN +// Bit32u rmask = 0xff000000; +// Bit32u gmask = 0x00ff0000; +// Bit32u bmask = 0x0000ff00; +//#else + Bit32u rmask = 0x000000ff; + Bit32u gmask = 0x0000ff00; + Bit32u bmask = 0x00ff0000; +//#endif + +/* Please leave the Splash screen stuff in working order in DOSBox. We spend a lot of time making DOSBox. */ + SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0); + if (splash_surf) { + SDL_FillRect(splash_surf, NULL, SDL_MapRGB(splash_surf->format, 0, 0, 0)); + + Bit8u* tmpbufp = new Bit8u[640*400*3]; + GIMP_IMAGE_RUN_LENGTH_DECODE(tmpbufp,gimp_image.rle_pixel_data,640*400,3); + for (Bitu y=0; y<400; y++) { + + Bit8u* tmpbuf = tmpbufp + y*640*3; + Bit32u * draw=(Bit32u*)(((Bit8u *)splash_surf->pixels)+((y)*splash_surf->pitch)); + for (Bitu x=0; x<640; x++) { +//#if SDL_BYTEORDER == SDL_BIG_ENDIAN +// *draw++ = tmpbuf[x*3+2]+tmpbuf[x*3+1]*0x100+tmpbuf[x*3+0]*0x10000+0x00000000; +//#else + *draw++ = tmpbuf[x*3+0]+tmpbuf[x*3+1]*0x100+tmpbuf[x*3+2]*0x10000+0x00000000; +//#endif + } + } + + bool exit_splash = false; + + static Bitu max_splash_loop = 600; + static Bitu splash_fade = 100; + static bool use_fadeout = true; + + for (Bit32u ct = 0,startticks = GetTicks();ct < max_splash_loop;ct = GetTicks()-startticks) { + SDL_Event evt; + while (SDL_PollEvent(&evt)) { + if (evt.type == SDL_QUIT) { + exit_splash = true; + break; + } + } + if (exit_splash) break; + + if (ct<1) { + SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); + SDL_SetAlpha(splash_surf, SDL_SRCALPHA,255); + SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); + SDL_Flip(sdl.surface); + } else if (ct>=max_splash_loop-splash_fade) { + if (use_fadeout) { + SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); + SDL_SetAlpha(splash_surf, SDL_SRCALPHA, (Bit8u)((max_splash_loop-1-ct)*255/(splash_fade-1))); + SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); + SDL_Flip(sdl.surface); + } + } + + } + + if (use_fadeout) { + SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0)); + SDL_Flip(sdl.surface); + } + SDL_FreeSurface(splash_surf); + delete [] tmpbufp; + + } + + /* Get some Event handlers */ + MAPPER_AddHandler(KillSwitch,MK_f9,MMOD1,"shutdown","ShutDown"); + MAPPER_AddHandler(CaptureMouse,MK_f10,MMOD1,"capmouse","Cap Mouse"); + MAPPER_AddHandler(SwitchFullScreen,MK_return,MMOD2,"fullscr","Fullscreen"); + MAPPER_AddHandler(Restart,MK_home,MMOD1|MMOD2,"restart","Restart"); +#if C_DEBUG + /* Pause binds with activate-debugger */ +#else + MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause"); +#endif + /* Get Keyboard state of numlock and capslock */ + SDLMod keystate = SDL_GetModState(); + if(keystate&KMOD_NUM) startup_state_numlock = true; + if(keystate&KMOD_CAPS) startup_state_capslock = true; +} + +void Mouse_AutoLock(bool enable) { + sdl.mouse.autolock=enable; + if (sdl.mouse.autoenable) sdl.mouse.requestlock=enable; + else { + SDL_ShowCursor(enable?SDL_DISABLE:SDL_ENABLE); + sdl.mouse.requestlock=false; + } +} + +static void HandleMouseMotion(SDL_MouseMotionEvent * motion) { + if (sdl.mouse.locked || !sdl.mouse.autoenable) + Mouse_CursorMoved((float)motion->xrel*sdl.mouse.sensitivity/100.0f, + (float)motion->yrel*sdl.mouse.sensitivity/100.0f, + (float)(motion->x-sdl.clip.x)/(sdl.clip.w-1)*sdl.mouse.sensitivity/100.0f, + (float)(motion->y-sdl.clip.y)/(sdl.clip.h-1)*sdl.mouse.sensitivity/100.0f, + sdl.mouse.locked); +} + +static void HandleMouseButton(SDL_MouseButtonEvent * button) { + switch (button->state) { + case SDL_PRESSED: + if (sdl.mouse.requestlock && !sdl.mouse.locked) { + GFX_CaptureMouse(); + // Dont pass klick to mouse handler + break; + } + if (!sdl.mouse.autoenable && sdl.mouse.autolock && button->button == SDL_BUTTON_MIDDLE) { + GFX_CaptureMouse(); + break; + } + switch (button->button) { + case SDL_BUTTON_LEFT: + Mouse_ButtonPressed(0); + break; + case SDL_BUTTON_RIGHT: + Mouse_ButtonPressed(1); + break; + case SDL_BUTTON_MIDDLE: + Mouse_ButtonPressed(2); + break; + } + break; + case SDL_RELEASED: + switch (button->button) { + case SDL_BUTTON_LEFT: + Mouse_ButtonReleased(0); + break; + case SDL_BUTTON_RIGHT: + Mouse_ButtonReleased(1); + break; + case SDL_BUTTON_MIDDLE: + Mouse_ButtonReleased(2); + break; + } + break; + } +} + +void GFX_LosingFocus(void) { + sdl.laltstate=SDL_KEYUP; + sdl.raltstate=SDL_KEYUP; + MAPPER_LosingFocus(); +} + +bool GFX_IsFullscreen(void) { + return sdl.desktop.fullscreen; +} + +void GFX_Events() { + SDL_Event event; +#if defined (REDUCE_JOYSTICK_POLLING) + static int poll_delay=0; + int time=GetTicks(); + if (time-poll_delay>20) { + poll_delay=time; + if (sdl.num_joysticks>0) SDL_JoystickUpdate(); + MAPPER_UpdateJoysticks(); + } +#endif + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_ACTIVEEVENT: + if (event.active.state & SDL_APPINPUTFOCUS) { + if (event.active.gain) { + if (sdl.desktop.fullscreen && !sdl.mouse.locked) + GFX_CaptureMouse(); + SetPriority(sdl.priority.focus); + CPU_Disable_SkipAutoAdjust(); + } else { + if (sdl.mouse.locked) { +#ifdef WIN32 + if (sdl.desktop.fullscreen) { + VGA_KillDrawing(); + GFX_ForceFullscreenExit(); + } +#endif + GFX_CaptureMouse(); + } + SetPriority(sdl.priority.nofocus); + GFX_LosingFocus(); + CPU_Enable_SkipAutoAdjust(); + } + } + + /* Non-focus priority is set to pause; check to see if we've lost window or input focus + * i.e. has the window been minimised or made inactive? + */ + if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) { + if ((event.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) && (!event.active.gain)) { + /* Window has lost focus, pause the emulator. + * This is similar to what PauseDOSBox() does, but the exit criteria is different. + * Instead of waiting for the user to hit Alt-Break, we wait for the window to + * regain window or input focus. + */ + bool paused = true; + SDL_Event ev; + + GFX_SetTitle(-1,-1,true); + KEYBOARD_ClrBuffer(); +// SDL_Delay(500); +// while (SDL_PollEvent(&ev)) { + // flush event queue. +// } + + while (paused) { + // WaitEvent waits for an event rather than polling, so CPU usage drops to zero + SDL_WaitEvent(&ev); + + switch (ev.type) { + case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event. + case SDL_ACTIVEEVENT: // wait until we get window focus back + if (ev.active.state & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) { + // We've got focus back, so unpause and break out of the loop + if (ev.active.gain) { + paused = false; + GFX_SetTitle(-1,-1,false); + } + + /* Now poke a "release ALT" command into the keyboard buffer + * we have to do this, otherwise ALT will 'stick' and cause + * problems with the app running in the DOSBox. + */ + KEYBOARD_AddKey(KBD_leftalt, false); + KEYBOARD_AddKey(KBD_rightalt, false); + } + break; + } + } + } + } + break; + case SDL_MOUSEMOTION: + HandleMouseMotion(&event.motion); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + HandleMouseButton(&event.button); + break; + case SDL_VIDEORESIZE: +// HandleVideoResize(&event.resize); + break; + case SDL_QUIT: + throw(0); + break; + case SDL_VIDEOEXPOSE: + if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw ); + break; +#ifdef WIN32 + case SDL_KEYDOWN: + case SDL_KEYUP: + // ignore event alt+tab + if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type; + if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type; + if (((event.key.keysym.sym==SDLK_TAB)) && + ((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) break; +#endif +#if defined (MACOSX) + case SDL_KEYDOWN: + case SDL_KEYUP: + /* On macs CMD-Q is the default key to close an application */ + if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) { + KillSwitch(true); + break; + } +#endif + default: + void MAPPER_CheckEvent(SDL_Event * event); + MAPPER_CheckEvent(&event); + } + } +} + +#if defined (WIN32) +static BOOL WINAPI ConsoleEventHandler(DWORD event) { + switch (event) { + case CTRL_SHUTDOWN_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_BREAK_EVENT: + raise(SIGTERM); + return TRUE; + case CTRL_C_EVENT: + default: //pass to the next handler + return FALSE; + } +} +#endif + + +/* static variable to show wether there is not a valid stdout. + * Fixes some bugs when -noconsole is used in a read only directory */ +static bool no_stdout = false; +void GFX_ShowMsg(char const* format,...) { + char buf[512]; + va_list msg; + va_start(msg,format); + vsprintf(buf,format,msg); + strcat(buf,"\n"); + va_end(msg); + if(!no_stdout) printf("%s",buf); //Else buf is parsed again. +} + + +void Config_Add_SDL() { + Section_prop * sdl_sec=control->AddSection_prop("sdl",&GUI_StartUp); + sdl_sec->AddInitFunction(&MAPPER_StartUp); + Prop_bool* Pbool; + Prop_string* Pstring; + Prop_int* Pint; + Prop_multival* Pmulti; + + Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false); + Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)"); + + Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false); + Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox."); + + Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"original"); + Pstring->Set_help("What resolution to use for fullscreen: original or fixed size (e.g. 1024x768).\n" + " Using your monitor's native resolution with aspect=true might give the best results.\n" + " If you end up with small window on a large screen, try an output different from surface."); + + Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original"); + Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n" + " (output=surface does not!)"); + + const char* outputs[] = { + "surface", "overlay", +#if C_OPENGL + "opengl", "openglnb", +#endif +#if (HAVE_DDRAW_H) && defined(WIN32) + "ddraw", +#endif + 0 }; + Pstring = sdl_sec->Add_string("output",Property::Changeable::Always,"surface"); + Pstring->Set_help("What video system to use for output."); + Pstring->Set_values(outputs); + + Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always,true); + Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)"); + + Pint = sdl_sec->Add_int("sensitivity",Property::Changeable::Always,100); + Pint->SetMinMax(1,1000); + Pint->Set_help("Mouse sensitivity."); + + Pbool = sdl_sec->Add_bool("waitonerror",Property::Changeable::Always, true); + Pbool->Set_help("Wait before closing the console if dosbox has an error."); + + Pmulti = sdl_sec->Add_multi("priority", Property::Changeable::Always, ","); + Pmulti->SetValue("higher,normal"); + Pmulti->Set_help("Priority levels for dosbox. Second entry behind the comma is for when dosbox is not focused/minimized.\n" + " pause is only valid for the second entry."); + + const char* actt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0}; + Pstring = Pmulti->GetSection()->Add_string("active",Property::Changeable::Always,"higher"); + Pstring->Set_values(actt); + + const char* inactt[] = { "lowest", "lower", "normal", "higher", "highest", "pause", 0}; + Pstring = Pmulti->GetSection()->Add_string("inactive",Property::Changeable::Always,"normal"); + Pstring->Set_values(inactt); + + Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE); + Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the defaul value."); + + Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,true); + Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems."); +} + +static void show_warning(char const * const message) { + bool textonly = true; +#ifdef WIN32 + textonly = false; + if ( !sdl.inited && SDL_Init(SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE) < 0 ) textonly = true; + sdl.inited = true; +#endif + printf(message); + if(textonly) return; + if(!sdl.surface) sdl.surface = SDL_SetVideoMode(640,400,0,0); + if(!sdl.surface) return; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + Bit32u rmask = 0xff000000; + Bit32u gmask = 0x00ff0000; + Bit32u bmask = 0x0000ff00; +#else + Bit32u rmask = 0x000000ff; + Bit32u gmask = 0x0000ff00; + Bit32u bmask = 0x00ff0000; +#endif + SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0); + if (!splash_surf) return; + + int x = 120,y = 20; + std::string m(message),m2; + std::string::size_type a,b,c,d; + + while(m.size()) { //Max 50 characters. break on space before or on a newline + c = m.find('\n'); + d = m.rfind(' ',50); + if(c>d) a=b=d; else a=b=c; + if( a != std::string::npos) b++; + m2 = m.substr(0,a); m.erase(0,b); + OutputString(x,y,m2.c_str(),0xffffffff,0,splash_surf); + y += 20; + } + + SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL); + SDL_Flip(sdl.surface); + SDL_Delay(12000); +} + +static void launcheditor() { + std::string path,file; + Cross::CreatePlatformConfigDir(path); + Cross::GetPlatformConfigName(file); + path += file; + FILE* f = fopen(path.c_str(),"r"); + if(!f && !control->PrintConfig(path.c_str())) { + printf("tried creating %s. but failed.\n",path.c_str()); + exit(1); + } + if(f) fclose(f); +/* if(edit.empty()) { + printf("no editor specified.\n"); + exit(1); + }*/ + std::string edit; + while(control->cmdline->FindString("-editconf",edit,true)) //Loop until one succeeds + execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0); + //if you get here the launching failed! + printf("can't find editor(s) specified at the command line.\n"); + exit(1); +} +#if C_DEBUG +extern void DEBUG_ShutDown(Section * /*sec*/); +#endif + +void restart_program(std::vector & parameters) { + char** newargs = new char* [parameters.size()+1]; + // parameter 0 is the executable path + // contents of the vector follow + // last one is NULL + for(Bitu i = 0; i < parameters.size(); i++) newargs[i]=(char*)parameters[i].c_str(); + newargs[parameters.size()] = NULL; + SDL_CloseAudio(); + SDL_Delay(50); + SDL_Quit(); +#if C_DEBUG + // shutdown curses + DEBUG_ShutDown(NULL); +#endif + + execvp(newargs[0], newargs); + free(newargs); +} +void Restart(bool pressed) { // mapper handler + restart_program(control->startup_params); +} + +static void launchcaptures(std::string const& edit) { + std::string path,file; + Section* t = control->GetSection("dosbox"); + if(t) file = t->GetPropValue("captures"); + if(!t || file == NO_SUCH_PROPERTY) { + printf("Config system messed up.\n"); + exit(1); + } + Cross::CreatePlatformConfigDir(path); + path += file; + Cross::CreateDir(path); + struct stat cstat; + if(stat(path.c_str(),&cstat) || (cstat.st_mode & S_IFDIR) == 0) { + printf("%s doesn't exists or isn't a directory.\n",path.c_str()); + exit(1); + } +/* if(edit.empty()) { + printf("no editor specified.\n"); + exit(1); + }*/ + + execlp(edit.c_str(),edit.c_str(),path.c_str(),(char*) 0); + //if you get here the launching failed! + printf("can't find filemanager %s\n",edit.c_str()); + exit(1); +} + +static void printconfiglocation() { + std::string path,file; + Cross::CreatePlatformConfigDir(path); + Cross::GetPlatformConfigName(file); + path += file; + + FILE* f = fopen(path.c_str(),"r"); + if(!f && !control->PrintConfig(path.c_str())) { + printf("tried creating %s. but failed",path.c_str()); + exit(1); + } + if(f) fclose(f); + printf("%s\n",path.c_str()); + exit(0); +} + +static void eraseconfigfile() { + FILE* f = fopen("dosbox.conf","r"); + if(f) { + fclose(f); + show_warning("Warning: dosbox.conf exists in current working directory.\nThis will override the configuration file at runtime.\n"); + } + std::string path,file; + Cross::GetPlatformConfigDir(path); + Cross::GetPlatformConfigName(file); + path += file; + f = fopen(path.c_str(),"r"); + if(!f) exit(0); + fclose(f); + unlink(path.c_str()); + exit(0); +} + +static void erasemapperfile() { + FILE* g = fopen("dosbox.conf","r"); + if(g) { + fclose(g); + show_warning("Warning: dosbox.conf exists in current working directory.\nKeymapping might not be properly reset.\n" + "Please reset configuration as well and delete the dosbox.conf.\n"); + } + + std::string path,file=MAPPERFILE; + Cross::GetPlatformConfigDir(path); + path += file; + FILE* f = fopen(path.c_str(),"r"); + if(!f) exit(0); + fclose(f); + unlink(path.c_str()); + exit(0); +} + + +//extern void UI_Init(void); +int main(int argc, char* argv[]) { + try { + CommandLine com_line(argc,argv); + Config myconf(&com_line); + control=&myconf; + /* Init the configuration system and add default values */ + Config_Add_SDL(); + DOSBOX_Init(); + + std::string editor; + if(control->cmdline->FindString("-editconf",editor,false)) launcheditor(); + if(control->cmdline->FindString("-opencaptures",editor,true)) launchcaptures(editor); + if(control->cmdline->FindExist("-eraseconf")) eraseconfigfile(); + if(control->cmdline->FindExist("-resetconf")) eraseconfigfile(); + if(control->cmdline->FindExist("-erasemapper")) erasemapperfile(); + if(control->cmdline->FindExist("-resetmapper")) erasemapperfile(); + + /* Can't disable the console with debugger enabled */ +#if defined(WIN32) && !(C_DEBUG) + if (control->cmdline->FindExist("-noconsole")) { + FreeConsole(); + /* Redirect standard input and standard output */ + if(freopen(STDOUT_FILE, "w", stdout) == NULL) + no_stdout = true; // No stdout so don't write messages + freopen(STDERR_FILE, "w", stderr); + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* Line buffered */ + setbuf(stderr, NULL); /* No buffering */ + } else { + if (AllocConsole()) { + fclose(stdin); + fclose(stdout); + fclose(stderr); + freopen("CONIN$","r",stdin); + freopen("CONOUT$","w",stdout); + freopen("CONOUT$","w",stderr); + } + SetConsoleTitle("DOSBox Status Window"); + } +#endif //defined(WIN32) && !(C_DEBUG) + if (control->cmdline->FindExist("-version") || + control->cmdline->FindExist("--version") ) { + printf("\nDOSBox version %s, copyright 2002-2011 DOSBox Team.\n\n",VERSION); + printf("DOSBox is written by the DOSBox Team (See AUTHORS file))\n"); + printf("DOSBox comes with ABSOLUTELY NO WARRANTY. This is free software,\n"); + printf("and you are welcome to redistribute it under certain conditions;\n"); + printf("please read the COPYING file thoroughly before doing so.\n\n"); + return 0; + } + if(control->cmdline->FindExist("-printconf")) printconfiglocation(); + +#if C_DEBUG + DEBUG_SetupConsole(); +#endif + +#if defined(WIN32) + SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleEventHandler,TRUE); +#endif + +#ifdef OS2 + PPIB pib; + PTIB tib; + DosGetInfoBlocks(&tib, &pib); + if (pib->pib_ultype == 2) pib->pib_ultype = 3; + setbuf(stdout, NULL); + setbuf(stderr, NULL); +#endif + + /* Display Welcometext in the console */ + LOG_MSG("DOSBox version %s",VERSION); + LOG_MSG("Copyright 2002-2011 DOSBox Team, published under GNU GPL."); + LOG_MSG("---"); + + /* Init SDL */ +#if SDL_VERSION_ATLEAST(1, 2, 14) + /* Or debian/ubuntu with older libsdl version as they have done this themselves, but then differently. + * with this variable they will work correctly. I've only tested the 1.2.14 behaviour against the windows version + * of libsdl + */ + putenv(const_cast("SDL_DISABLE_LOCK_KEYS=1")); +#endif + if ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_CDROM + |SDL_INIT_NOPARACHUTE + ) < 0 ) E_Exit("Can't init SDL %s",SDL_GetError()); + sdl.inited = true; + +#ifndef DISABLE_JOYSTICK + //Initialise Joystick seperately. This way we can warn when it fails instead + //of exiting the application + if( SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) LOG_MSG("Failed to init joystick support"); +#endif + + sdl.laltstate = SDL_KEYUP; + sdl.raltstate = SDL_KEYUP; + +#if defined (WIN32) +#if SDL_VERSION_ATLEAST(1, 2, 10) + sdl.using_windib=true; +#else + sdl.using_windib=false; +#endif + char sdl_drv_name[128]; + if (getenv("SDL_VIDEODRIVER")==NULL) { + if (SDL_VideoDriverName(sdl_drv_name,128)!=NULL) { + sdl.using_windib=false; + if (strcmp(sdl_drv_name,"directx")!=0) { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + putenv("SDL_VIDEODRIVER=directx"); + if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) { + putenv("SDL_VIDEODRIVER=windib"); + if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) E_Exit("Can't init SDL Video %s",SDL_GetError()); + sdl.using_windib=true; + } + } + } + } else { + char* sdl_videodrv = getenv("SDL_VIDEODRIVER"); + if (strcmp(sdl_videodrv,"directx")==0) sdl.using_windib = false; + else if (strcmp(sdl_videodrv,"windib")==0) sdl.using_windib = true; + } + if (SDL_VideoDriverName(sdl_drv_name,128)!=NULL) { + if (strcmp(sdl_drv_name,"windib")==0) LOG_MSG("SDL_Init: Starting up with SDL windib video driver.\n Try to update your video card and directx drivers!"); + } +#endif + sdl.num_joysticks=SDL_NumJoysticks(); + + /* Parse configuration files */ + std::string config_file,config_path; + Cross::GetPlatformConfigDir(config_path); + + //First parse -userconf + if(control->cmdline->FindExist("-userconf",true)){ + config_file.clear(); + Cross::GetPlatformConfigDir(config_path); + Cross::GetPlatformConfigName(config_file); + config_path += config_file; + control->ParseConfigFile(config_path.c_str()); + if(!control->configfiles.size()) { + //Try to create the userlevel configfile. + config_file.clear(); + Cross::CreatePlatformConfigDir(config_path); + Cross::GetPlatformConfigName(config_file); + config_path += config_file; + if(control->PrintConfig(config_path.c_str())) { + LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_path.c_str()); + //Load them as well. Makes relative paths much easier + control->ParseConfigFile(config_path.c_str()); + } + } + } + + //Second parse -conf switches + while(control->cmdline->FindString("-conf",config_file,true)) { + if(!control->ParseConfigFile(config_file.c_str())) { + // try to load it from the user directory + control->ParseConfigFile((config_path + config_file).c_str()); + } + } + // if none found => parse localdir conf + if(!control->configfiles.size()) control->ParseConfigFile("dosbox.conf"); + + // if none found => parse userlevel conf + if(!control->configfiles.size()) { + config_file.clear(); + Cross::GetPlatformConfigName(config_file); + control->ParseConfigFile((config_path + config_file).c_str()); + } + + if(!control->configfiles.size()) { + //Try to create the userlevel configfile. + config_file.clear(); + Cross::CreatePlatformConfigDir(config_path); + Cross::GetPlatformConfigName(config_file); + config_path += config_file; + if(control->PrintConfig(config_path.c_str())) { + LOG_MSG("CONFIG: Generating default configuration.\nWriting it to %s",config_path.c_str()); + //Load them as well. Makes relative paths much easier + control->ParseConfigFile(config_path.c_str()); + } else { + LOG_MSG("CONFIG: Using default settings. Create a configfile to change them"); + } + } + + +#if (ENVIRON_LINKED) + control->ParseEnv(environ); +#endif +// UI_Init(); +// if (control->cmdline->FindExist("-startui")) UI_Run(false); + /* Init all the sections */ + control->Init(); + /* Some extra SDL Functions */ + Section_prop * sdl_sec=static_cast(control->GetSection("sdl")); + + if (control->cmdline->FindExist("-fullscreen") || sdl_sec->Get_bool("fullscreen")) { + if(!sdl.desktop.fullscreen) { //only switch if not already in fullscreen + GFX_SwitchFullScreen(); + } + } + + /* Init the keyMapper */ + MAPPER_Init(); + if (control->cmdline->FindExist("-startmapper")) MAPPER_RunInternal(); + /* Start up main machine */ + control->StartUp(); + /* Shutdown everything */ + } catch (char * error) { +#if defined (WIN32) + sticky_keys(true); +#endif + GFX_ShowMsg("Exit to error: %s",error); + fflush(NULL); + if(sdl.wait_on_error) { + //TODO Maybe look for some way to show message in linux? +#if (C_DEBUG) + GFX_ShowMsg("Press enter to continue"); + fflush(NULL); + fgetc(stdin); +#elif defined(WIN32) + Sleep(5000); +#endif + } + + } + catch (int){ + ;//nothing pressed killswitch + } + catch(...){ +#if defined (WIN32) + sticky_keys(true); +#endif + //Force visible mouse to end user. Somehow this sometimes doesn't happen + SDL_WM_GrabInput(SDL_GRAB_OFF); + SDL_ShowCursor(SDL_ENABLE); + throw;//dunno what happened. rethrow for sdl to catch + } +#if defined (WIN32) + sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt +#endif + //Force visible mouse to end user. Somehow this sometimes doesn't happen + SDL_WM_GrabInput(SDL_GRAB_OFF); + SDL_ShowCursor(SDL_ENABLE); + + SDL_Quit();//Let's hope sdl will quit as well when it catches an exception + return 0; +} + +void GFX_GetSize(int &width, int &height, bool &fullscreen) { + width = sdl.draw.width; + height = sdl.draw.height; + fullscreen = sdl.desktop.fullscreen; +} diff --git a/src/misc/support.cpp b/src/misc/support.cpp index cfcb64f6..5c4eb016 100644 --- a/src/misc/support.cpp +++ b/src/misc/support.cpp @@ -171,7 +171,7 @@ double ConvDblWord(char * word) { static char buf[1024]; //greater scope as else it doesn't always gets thrown right (linux/gcc2.95) void E_Exit(const char * format,...) { -#if C_DEBUG && C_HEAVY_DEBUG +#if C_HEAVY_DEBUG || C_GDBSERVER DEBUG_HeavyWriteLogInstruction(); #endif va_list msg;