/* AppleWin : An Apple //e emulator for Windows Copyright (C) 1994-1996, Michael O'Brien Copyright (C) 1999-2001, Oliver Schmidt Copyright (C) 2002-2005, Tom Charlesworth Copyright (C) 2006-2014, Tom Charlesworth, Michael Pohoreski AppleWin 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. AppleWin 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 AppleWin; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Description: Debugger * * Author: Copyright (C) 2006-2010 Michael Pohoreski */ // disable warning C4786: symbol greater than 255 character: //#pragma warning(disable: 4786) #include "StdAfx.h" #include "Debug.h" #include "DebugDefs.h" #include "Debugger_Win32.h" #include "../Windows/AppleWin.h" #include "../Core.h" #include "../Interface.h" #include "../CardManager.h" #include "../CPU.h" #include "../Disk.h" #include "../Keyboard.h" #include "../Memory.h" #include "../NTSC.h" #include "../SoundCore.h" // SoundCore_SetFade() // #define DEBUG_COMMAND_HELP 1 // #define DEBUG_ASM_HASH 1 #define ALLOW_INPUT_LOWERCASE 1 #define MAKE_VERSION(a,b,c,d) ((a<<24) | (b<<16) | (c<<8) | (d)) // See /docs/Debugger_Changelog.txt for full details const int DEBUGGER_VERSION = MAKE_VERSION(2,9,1,14); // Public _________________________________________________________________________________________ // All (Global) bool g_bDebuggerEatKey = false; // Bookmarks __________________________________________________________________ int g_nBookmarks = 0; Bookmark_t g_aBookmarks[ MAX_BOOKMARKS ]; // Breakpoints ________________________________________________________________ // Any Speed Breakpoints int g_nDebugBreakOnInvalid = 0; // Bit Flags of Invalid Opcode to break on: // iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3 int g_iDebugBreakOnOpcode = 0; bool g_bDebugBreakOnInterrupt = false; struct DebugBreakOnDMA { DebugBreakOnDMA() : isToOrFromMemory(0), memoryAddr(0), memoryAddrEnd(0), BPid(0) {} int isToOrFromMemory; WORD memoryAddr; WORD memoryAddrEnd; int BPid; }; static const uint32_t NUM_BREAK_ON_DMA = 3; // A 512-byte block misaligned touching 3 pages static DebugBreakOnDMA g_DebugBreakOnDMA[NUM_BREAK_ON_DMA]; static DebugBreakOnDMA g_DebugBreakOnDMAIO; int g_bDebugBreakpointHit = 0; // See: BreakpointHit_t int g_nBreakpoints = 0; Breakpoint_t g_aBreakpoints[ MAX_BREAKPOINTS ]; // NOTE: BreakpointSource_t and g_aBreakpointSource must match! const char *g_aBreakpointSource[ NUM_BREAKPOINT_SOURCES ] = { // Used to be one char, since ArgsCook also uses // TODO/FIXME: Parser use Param[] ? // Used for both Input & Output! // Regs "A", // Reg A "X", // Reg X "Y", // Reg Y // Special "PC", // Program Counter -- could use "$" "S" , // Stack Pointer // Flags -- .8 Moved: Flag names from g_aFlagNames[] to "inlined" g_aBreakpointSource[] "P", // Processor Status "C", // ---- ---1 Carry "Z", // ---- --1- Zero "I", // ---- -1-- Interrupt "D", // ---- 1--- Decimal "B", // ---1 ---- Break "R", // --1- ---- Reserved "V", // -1-- ---- Overflow "N", // 1--- ---- Sign // Misc "OP", // Opcode/Instruction/Mnemonic "M", // Mem RW "M", // Mem READ_ONLY "M", // Mem WRITE_ONLY "V", // Video Scanner // TODO: M0 ram bank 0, M1 aux ram ? }; // Note: BreakpointOperator_t, _PARAM_BREAKPOINT_, and g_aBreakpointSymbols must match! const char *g_aBreakpointSymbols[ NUM_BREAKPOINT_OPERATORS ] = { // Output: Must be 2 chars! "<=", // LESS_EQUAL "< ", // LESS_THAN "= ", // EQUAL "!=", // NOT_EQUAL // "! ", // NOT_EQUAL_1 "> ", // GREATER_THAN ">=", // GREATER_EQUAL "? ", // READ // Q. IO Read use 'I'? A. No, since I=Interrupt // Also can't use: 'r' reserver "@ ", // WRITE // Q. IO Write use 'O'? A. No, since O=Opcode // This is free: 'w' "* ", // Read/Write }; static WORD g_uBreakMemoryAddress = 0; // Commands _______________________________________________________________________________________ int g_iCommand; // last command (enum) // used for consecutive commands std::vector g_vPotentialCommands; // global, since TAB-completion also needs std::vector g_vSortedCommands; // static const char g_aFlagNames[_6502_NUM_FLAGS+1] = TEXT("CZIDBRVN");// Reversed since arrays are from left-to-right // Cursor (Console Input) _____________________________________________________ // char g_aInputCursor[] = "\|/-"; enum InputCursor { CURSOR_INSERT, CURSOR_OVERSTRIKE, NUM_INPUT_CURSORS }; const char g_aInputCursor[] = "_\x7F"; // insert over-write bool g_bInputCursor = false; int g_iInputCursor = CURSOR_OVERSTRIKE; // which cursor to use const int g_nInputCursor = sizeof( g_aInputCursor ); void DebuggerCursorUpdate(); char DebuggerCursorGet(); // Cursor (Disasm) ____________________________________________________________ WORD g_nDisasmTopAddress = 0; WORD g_nDisasmBotAddress = 0; WORD g_nDisasmCurAddress = 0; bool g_bDisasmCurBad = false; int g_nDisasmCurLine = 0; // Aligned to Top or Center int g_iDisasmCurState = CURSOR_NORMAL; int g_nDisasmWinHeight = 0; // char g_aConfigDisasmAddressColon[] = TEXT(" :"); extern const int WINDOW_DATA_BYTES_PER_LINE = 8; #if OLD_FONT // Font TCHAR g_sFontNameDefault[ MAX_FONT_NAME ] = TEXT("Courier New"); TCHAR g_sFontNameConsole[ MAX_FONT_NAME ] = TEXT("Courier New"); TCHAR g_sFontNameDisasm [ MAX_FONT_NAME ] = TEXT("Courier New"); TCHAR g_sFontNameInfo [ MAX_FONT_NAME ] = TEXT("Courier New"); TCHAR g_sFontNameBranch [ MAX_FONT_NAME ] = TEXT("Webdings"); HFONT g_hFontWebDings = (HFONT)0; #endif int g_iFontSpacing = FONT_SPACING_CLEAN; // TODO: This really needs to be phased out, and use the ConfigFont[] settings #if USE_APPLE_FONT int g_nFontHeight = CONSOLE_FONT_HEIGHT; // 13 -> 12 Lucida Console is readable #else int g_nFontHeight = 15; // 13 -> 12 Lucida Console is readable #endif const int MIN_DISPLAY_CONSOLE_LINES = 5; // doesn't include ConsoleInput int g_nDisasmDisplayLines = 0; // Config _____________________________________________________________________ // Config - Disassembly bool g_bConfigDisasmAddressView = true; int g_bConfigDisasmClick = 4; // GH#462 alt=1, ctrl=2, shift=4 bitmask (default to Shift-Click) bool g_bConfigDisasmAddressColon = true; bool g_bConfigDisasmOpcodesView = true; bool g_bConfigDisasmOpcodeSpaces = true; int g_iConfigDisasmTargets = DISASM_TARGET_BOTH; int g_iConfigDisasmBranchType = DISASM_BRANCH_FANCY; int g_bConfigDisasmImmediateChar = DISASM_IMMED_BOTH; int g_iConfigDisasmScroll = 3; // favor 3 byte opcodes // Config - Info bool g_bConfigInfoTargetPointer = false; MemoryTextFile_t g_ConfigState; static bool g_bDebugFullSpeed = false; static bool g_bLastGoCmdWasFullSpeed = false; static bool g_bGoCmd_ReinitFlag = false; // Display ____________________________________________________________________ void UpdateDisplay( Update_t bUpdate ); // Memory _____________________________________________________________________ MemoryDump_t g_aMemDump[ NUM_MEM_DUMPS ]; // Made global so operator @# can be used with other commands. MemorySearchResults_t g_vMemorySearchResults; // Profile const int NUM_PROFILE_LINES = NUM_OPCODES + NUM_OPMODES + 16; ProfileOpcode_t g_aProfileOpcodes[ NUM_OPCODES ]; ProfileOpmode_t g_aProfileOpmodes[ NUM_OPMODES ]; unsigned __int64 g_nProfileBeginCycles = 0; // g_nCumulativeCycles // PROFILE RESET const std::string g_FileNameProfile = TEXT("Profile.txt"); // changed from .csv to .txt since Excel doesn't give import options. int g_nProfileLine = 0; char g_aProfileLine[ NUM_PROFILE_LINES ][ CONSOLE_WIDTH ]; void ProfileReset (); bool ProfileSave (); void ProfileFormat( bool bSeperateColumns, ProfileFormat_e eFormatMode ); // TODO: Things would be much simpler if g_aProfileLine is just a container of std::string. struct ProfileLine_t { ProfileLine_t() : buf(NULL), bufsz(0) {} ProfileLine_t(char* _buf, size_t _bufsz) : buf(_buf), bufsz(_bufsz) {} void Assign(std::string const& str) { if (!buf || bufsz <= 0) return; strncpy_s(buf, bufsz, str.c_str(), _TRUNCATE); } ATTRIBUTE_FORMAT_PRINTF(2, 3) /* 1 is "this" */ void Format(const char* fmt, ...) { if (!buf || bufsz <= 0) return; va_list va; va_start(va, fmt); Assign(StrFormatV(fmt, va)); va_end(va); } char* buf; size_t bufsz; }; ProfileLine_t ProfileLinePeek ( int iLine ); ProfileLine_t ProfileLinePush (); void ProfileLineReset (); // Soft-switches __________________________________________________________________________________ // Source Level Debugging _________________________________________________________________________ bool g_bSourceLevelDebugging = false; bool g_bSourceAddSymbols = false; bool g_bSourceAddMemory = false; std::string g_aSourceFileName; MemoryTextFile_t g_AssemblerSourceBuffer; int g_iSourceDisplayStart = 0; int g_nSourceAssembleBytes = 0; int g_nSourceAssemblySymbols = 0; // TODO: Support multiple source filenames SourceAssembly_t g_aSourceDebug; // Watches ________________________________________________________________________________________ int g_nWatches = 0; Watches_t g_aWatches[ MAX_WATCHES ]; // TODO: use vector ?? // Window _________________________________________________________________________________________ int g_iWindowLast = WINDOW_CODE; // TODO: FIXME! should be offset into WindowConfig!!! // Who has active focus int g_iWindowThis = WINDOW_CODE; // TODO: FIXME! should be offset into WindowConfig!!! WindowSplit_t g_aWindowConfig[ NUM_WINDOWS ]; // Zero Page Pointers _____________________________________________________________________________ int g_nZeroPagePointers = 0; ZeroPagePointers_t g_aZeroPagePointers[ MAX_ZEROPAGE_POINTERS ]; // TODO: use vector<> ? // TODO: // CONFIG SAVE --> VERSION # enum DebugConfigVersion_e { VERSION_0, CURRENT_VERSION = VERSION_0 }; // Misc. __________________________________________________________________________________________ std::string g_sFileNameConfig = #ifdef MSDOS "AWDEBUGR.CFG"; #else "AppleWinDebugger.cfg"; #endif static char g_sFileNameTrace [] = "Trace.txt"; static bool g_bBenchmarking = false; static BOOL g_bProfiling = 0; static int g_nDebugSteps = 0; static DWORD g_nDebugStepCycles = 0; static int g_nDebugStepStart = 0; static int g_nDebugStepUntil = -1; // HACK: MAGIC # static int g_nDebugSkipStart = 0; static int g_nDebugSkipLen = 0; static FILE *g_hTraceFile = NULL; static bool g_bTraceHeader = false; // semaphore, flag header to be printed static bool g_bTraceFileWithVideoScanner = false; DWORD extbench = 0; static bool g_bIgnoreNextKey = false; const UINT LBR_UNDEFINED = -1; static UINT g_LBR = LBR_UNDEFINED; // Last Branch Record static bool g_bScriptReadOk = false; // Private ________________________________________________________________________________________ // Prototypes _______________________________________________________________ static void DebugEnd (); static int ParseInput ( LPTSTR pConsoleInput, bool bCook = true ); static Update_t ExecuteCommand ( int nArgs ); // Breakpoints void _BWZ_List ( const Breakpoint_t * aBreakWatchZero, const int iBWZ ); // bool bZeroBased = true ); void _BWZ_ListAll ( const Breakpoint_t * aBreakWatchZero, const int nMax ); // bool CheckBreakpoint (WORD address, BOOL memory); bool _CmdBreakpointAddReg ( Breakpoint_t *pBP, BreakpointSource_t iSrc, BreakpointOperator_t iCmp, WORD nAddress, int nLen, bool bIsTempBreakpoint ); int _CmdBreakpointAddCommonArg ( int iArg, int nArg, BreakpointSource_t iSrc, BreakpointOperator_t iCmp, bool bIsTempBreakpoint=false ); void _BWZ_RemoveOne( Breakpoint_t *aBreakWatchZero, const int iSlot, int & nTotal ); // Config - Save bool ConfigSave_BufferToDisk ( const char *pFileName, ConfigSave_t eConfigSave ); void ConfigSave_PrepareHeader ( const Parameters_e eCategory, const Commands_e eCommandClear ); // Drawing static void _CmdColorGet ( const int iScheme, const int iColor ); // Source Level Debugging static bool BufferAssemblyListing ( const std::string & pFileName ); static bool ParseAssemblyListing ( bool bBytesToMemory, bool bAddSymbols ); // Window void _WindowJoin (); void _WindowSplit (Window_e eNewBottomWindow ); void _WindowLast (); void _WindowSwitch ( int eNewWindow ); int WindowGetHeight ( int iWindow ); void WindowUpdateDisasmSize (); void WindowUpdateConsoleDisplayedSize (); void WindowUpdateSizes (); Update_t _CmdWindowViewFull (int iNewWindow); Update_t _CmdWindowViewCommon (int iNewWindow); // Utility void _CursorMoveDownAligned( int nDelta ); void _CursorMoveUpAligned( int nDelta ); // DebugVideoMode _____________________________________________________________ // Fix for GH#345 // Wrap & protect the debugger's video mode in its own class: // . This may seem like overkill but it stops the video mode being (erroneously) additionally used as a flag. // . VideoMode is a bitmap of video flags and a VideoMode value of zero is a valid video mode (GR,PAGE1,non-mixed). class DebugVideoMode // NB. Implemented as a singleton { protected: DebugVideoMode() { Reset(); } public: ~DebugVideoMode(){} static DebugVideoMode& Instance() { return m_Instance; } void Reset(void) { m_bIsVideoModeValid = false; m_uVideoMode = 0; } bool IsSet(void) { return m_bIsVideoModeValid; } bool Get(UINT* pVideoMode) { if (pVideoMode) *pVideoMode = m_bIsVideoModeValid ? m_uVideoMode : 0; return m_bIsVideoModeValid; } void Set(UINT videoMode) { m_bIsVideoModeValid = true; m_uVideoMode = videoMode; } private: bool m_bIsVideoModeValid; uint32_t m_uVideoMode; static DebugVideoMode m_Instance; }; DebugVideoMode DebugVideoMode::m_Instance; bool DebugGetVideoMode(UINT* pVideoMode) { return DebugVideoMode::Instance().Get(pVideoMode); } // File _______________________________________________________________________ size_t _GetFileSize( FILE *hFile ) { fseek( hFile, 0, SEEK_END ); size_t nFileBytes = ftell( hFile ); fseek( hFile, 0, SEEK_SET ); return nFileBytes; } // Bookmarks __________________________________________________________________ //=========================================================================== bool _Bookmark_Add( const int iBookmark, const WORD nAddress ) { if (iBookmark < MAX_BOOKMARKS) { // g_aBookmarks.push_back( nAddress ); // g_aBookmarks.at( iBookmark ) = nAddress; g_aBookmarks[ iBookmark ].nAddress = nAddress; g_aBookmarks[ iBookmark ].bSet = true; g_nBookmarks++; return true; } return false; } //=========================================================================== bool _Bookmark_Del( const WORD nAddress ) { bool bDeleted = false; // int nSize = g_aBookmarks.size(); int iBookmark; for (iBookmark = 0; iBookmark < MAX_BOOKMARKS; iBookmark++ ) { if (g_aBookmarks[ iBookmark ].nAddress == nAddress) { // g_aBookmarks.at( iBookmark ) = NO_6502_TARGET; g_aBookmarks[ iBookmark ].bSet = false; g_nBookmarks--; bDeleted = true; } } return bDeleted; } // Returns: // 0 if address does not have a bookmark set // N+1 if there is an existing bookmark that has this address int Bookmark_Find( const WORD nAddress ) { // Ugh, linear search // int nSize = g_aBookmarks.size(); int iBookmark; for (iBookmark = 0; iBookmark < MAX_BOOKMARKS; iBookmark++ ) { if (g_aBookmarks[ iBookmark ].nAddress == nAddress) { if (g_aBookmarks[ iBookmark ].bSet) return iBookmark + 1; } } return 0; } //=========================================================================== bool _Bookmark_Get( const int iBookmark, WORD & nAddress ) { // int nSize = g_aBookmarks.size(); if (iBookmark >= MAX_BOOKMARKS) return false; if (g_aBookmarks[ iBookmark ].bSet) { nAddress = g_aBookmarks[ iBookmark ].nAddress; return true; } return false; } //=========================================================================== void _Bookmark_Reset() { // g_aBookmarks.reserve( MAX_BOOKMARKS ); // g_aBookmarks.insert( g_aBookma int iBookmark = 0; int iBookmark = 0; for (iBookmark = 0; iBookmark < MAX_BOOKMARKS; iBookmark++ ) { g_aBookmarks[ iBookmark ].bSet = false; } g_nBookmarks = 0; } //=========================================================================== int _Bookmark_Size() { g_nBookmarks = 0; int iBookmark; for (iBookmark = 0; iBookmark < MAX_BOOKMARKS; iBookmark++ ) { if (g_aBookmarks[ iBookmark ].bSet) g_nBookmarks++; } return g_nBookmarks; } //=========================================================================== Update_t CmdBookmark (int nArgs) { return CmdBookmarkAdd( nArgs ); } //=========================================================================== Update_t CmdBookmarkAdd (int nArgs ) { // BMA address // BMA # address ; where # is [0...9] if (! nArgs) { return CmdBookmarkList( 0 ); } int iBookmark = 0; int iArg = 1; if (nArgs > 1) { iBookmark = g_aArgs[ iArg ].nValue; iArg++; } else { while ((iBookmark < MAX_BOOKMARKS) && g_aBookmarks[iBookmark].bSet) iBookmark++; } WORD nAddress = g_aArgs[ iArg ].nValue; if (iBookmark >= MAX_BOOKMARKS) { ConsoleDisplayPushFormat( "All bookmarks are currently in use. (Max: %d)", MAX_BOOKMARKS ); return ConsoleUpdate(); } if (Bookmark_Find( nAddress )) _Bookmark_Del( nAddress ); bool bAdded = _Bookmark_Add( iBookmark, nAddress ); if (!bAdded) return Help_Arg_1( CMD_BOOKMARK_ADD ); return UPDATE_DISASM | ConsoleUpdate(); } //=========================================================================== Update_t CmdBookmarkClear (int nArgs) { int iBookmark = 0; int iArg; for (iArg = 1; iArg <= nArgs; iArg++ ) { if (! _tcscmp(g_aArgs[nArgs].sArg, g_aParameters[ PARAM_WILDSTAR ].m_sName)) { _Bookmark_Reset(); break; } iBookmark = g_aArgs[ iArg ].nValue; if (g_aBookmarks[ iBookmark ].bSet) _Bookmark_Del(g_aBookmarks[ iBookmark ].nAddress); } return UPDATE_DISASM; } //=========================================================================== Update_t CmdBookmarkGoto ( int nArgs ) { if (! nArgs) return Help_Arg_1( CMD_BOOKMARK_GOTO ); int iBookmark = g_aArgs[ 1 ].nValue; WORD nAddress; if (_Bookmark_Get( iBookmark, nAddress )) { g_nDisasmCurAddress = nAddress; g_nDisasmCurLine = 0; DisasmCalcTopBotAddress(); } return UPDATE_DISASM; } //=========================================================================== Update_t CmdBookmarkList (int nArgs) { if (! g_nBookmarks) { ConsoleBufferPushFormat( " There are no current bookmarks. (Max: %d)", MAX_BOOKMARKS ); } else { _BWZ_ListAll( g_aBookmarks, MAX_BOOKMARKS ); } return ConsoleUpdate(); } //=========================================================================== Update_t CmdBookmarkLoad (int nArgs) { if (nArgs == 1) { // strcpy( sMiniFileName, pFileName ); // strcat( sMiniFileName, ".aws" ); // HACK: MAGIC STRING // _tcscpy(sFileName, g_sCurrentDir); // // strcat(sFileName, sMiniFileName); } return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdBookmarkSave (int nArgs) { g_ConfigState.Reset(); ConfigSave_PrepareHeader( PARAM_CAT_BOOKMARKS, CMD_BOOKMARK_CLEAR ); int iBookmark = 0; while (iBookmark < MAX_BOOKMARKS) { if (g_aBookmarks[ iBookmark ].bSet) { g_ConfigState.PushLineFormat( "%s %x %04X\n" , g_aCommands[ CMD_BOOKMARK_ADD ].m_sName , iBookmark , g_aBookmarks[ iBookmark ].nAddress ); } iBookmark++; } if (nArgs) { if (! (g_aArgs[ 1 ].bType & TYPE_QUOTED_2)) return Help_Arg_1( CMD_BOOKMARK_SAVE ); if (ConfigSave_BufferToDisk( g_aArgs[ 1 ].sArg, CONFIG_SAVE_FILE_CREATE )) { ConsoleBufferPush( TEXT( "Saved." ) ); return ConsoleUpdate(); } } return UPDATE_CONSOLE_DISPLAY; } // Benchmark ______________________________________________________________________________________ //=========================================================================== Update_t CmdBenchmark (int nArgs) { if (g_bBenchmarking) CmdBenchmarkStart(0); else CmdBenchmarkStop(0); return UPDATE_ALL; // TODO/FIXME Verify } //=========================================================================== Update_t CmdBenchmarkStart (int nArgs) { CpuSetupBenchmark(); g_nDisasmCurAddress = regs.pc; DisasmCalcTopBotAddress(); g_bBenchmarking = true; return UPDATE_ALL; // 1; } //=========================================================================== Update_t CmdBenchmarkStop (int nArgs) { g_bBenchmarking = false; DebugEnd(); GetFrame().FrameRefreshStatus(DRAW_TITLE | DRAW_DISK_STATUS); GetFrame().VideoRedrawScreen(); DWORD currtime = GetTickCount(); while ((extbench = GetTickCount()) != currtime) ; // intentional busy-waiting KeybQueueKeypress(TEXT(' ') ,ASCII); return UPDATE_ALL; // 0; } //=========================================================================== Update_t CmdProfile (int nArgs) { if (! nArgs) { strncpy_s( g_aArgs[ 1 ].sArg, g_aParameters[ PARAM_RESET ].m_sName, _TRUNCATE ); nArgs = 1; } if (nArgs == 1) { int iParam; int nFound = FindParam( g_aArgs[ 1 ].sArg, MATCH_EXACT, iParam, _PARAM_GENERAL_BEGIN, _PARAM_GENERAL_END ); if (! nFound) goto _Help; if (iParam == PARAM_RESET) { ProfileReset(); g_bProfiling = 1; ConsoleBufferPush( TEXT(" Resetting profile data." ) ); } else { if ((iParam != PARAM_SAVE) && (iParam != PARAM_LIST)) goto _Help; bool bExport = true; if (iParam == PARAM_LIST) bExport = false; // .csv (Comma Seperated Value) // ProfileFormat( bExport, bExport ? PROFILE_FORMAT_COMMA : PROFILE_FORMAT_SPACE ); // .txt (Tab Seperated Value) ProfileFormat( bExport, bExport ? PROFILE_FORMAT_TAB : PROFILE_FORMAT_SPACE ); // Dump to console if (iParam == PARAM_LIST) { const int nLine = g_nProfileLine; for ( int iLine = 0; iLine < nLine; iLine++ ) { ProfileLine_t prfline = ProfileLinePeek( iLine ); if (prfline.buf) { char sText[ CONSOLE_WIDTH ]; TextConvertTabsToSpaces( sText, prfline.buf, CONSOLE_WIDTH, 4 ); // ConsoleBufferPush( sText ); ConsolePrint( sText ); } } } if (iParam == PARAM_SAVE) { if (ProfileSave()) { ConsoleBufferPushFormat( " Saved: %s", g_FileNameProfile.c_str() ); } else ConsoleBufferPush( " ERROR: Couldn't save file. (In use?)" ); } } } else goto _Help; return ConsoleUpdate(); // UPDATE_CONSOLE_DISPLAY; _Help: return Help_Arg_1( CMD_PROFILE ); } // Breakpoints ____________________________________________________________________________________ //=========================================================================== // iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3 static bool IsDebugBreakOnInvalid(int iOpcodeType) { return ((g_nDebugBreakOnInvalid >> iOpcodeType) & 1) ? true : false; } // iOpcodeType = AM_IMPLIED (BRK), AM_1, AM_2, AM_3 static void SetDebugBreakOnInvalid( int iOpcodeType, int nValue ) { if (iOpcodeType <= AM_3) { g_nDebugBreakOnInvalid &= ~ ( 1 << iOpcodeType); g_nDebugBreakOnInvalid |= ((nValue & 1) << iOpcodeType); } } Update_t CmdBreakInvalid (int nArgs) // Breakpoint IFF Full-speed! { if (nArgs > 2) // || (nArgs == 0)) return HelpLastCommand(); int iType = AM_IMPLIED; // default to BRK int nActive = 0; if (nArgs == 0) { nArgs = 1; g_aArgs[ 1 ].nValue = AM_IMPLIED; g_aArgs[ 1 ].sArg[0] = 0; } iType = g_aArgs[ 1 ].nValue; // Cases: // 0. CMD // display // 1a. CMD # // display // 1b. CMD ON | OFF // set // 1c. CMD ? // error // 2a. CMD # ON | OFF // set // 2b. CMD ALL ON | OFF // set all // 2c. CMD # ? // error bool bValidParam = true; int iParamArg = nArgs; // last arg is the 'ON' / 'OFF' param int iParam; int nFound = FindParam( g_aArgs[ iParamArg ].sArg, MATCH_EXACT, iParam, _PARAM_GENERAL_BEGIN, _PARAM_GENERAL_END ); if (nFound) { if (iParam == PARAM_ON) nActive = 1; else if (iParam == PARAM_OFF) nActive = 0; else { bValidParam = false; nFound = 0; } } else bValidParam = false; if (nArgs == 1) { if (! nFound) { if ((iType < AM_IMPLIED) || (iType > AM_3)) goto _Help; if ( IsDebugBreakOnInvalid( iType ) ) iParam = PARAM_ON; else iParam = PARAM_OFF; } else // case 1b { SetDebugBreakOnInvalid( iType, nActive ); } if (iType == 0) ConsoleBufferPushFormat( "Enter debugger on BRK opcode: %s", g_aParameters[ iParam ].m_sName ); else ConsoleBufferPushFormat( "Enter debugger on INVALID %1X opcode: %s", iType, g_aParameters[ iParam ].m_sName ); return ConsoleUpdate(); } else if (nArgs == 2) { int iParam1; if (FindParam(g_aArgs[1].sArg, MATCH_EXACT, iParam1, PARAM_ALL, PARAM_ALL)) // case 2b { for (iType = 0; iType <= AM_3; iType++) SetDebugBreakOnInvalid(iType, nActive); ConsoleBufferPushFormat("Enter debugger on BRK opcode and INVALID opcodes: %s", g_aParameters[iParam].m_sName); return ConsoleUpdate(); } else if (! bValidParam) // case 2c { goto _Help; } else // case 2a { if ((iType < 0) || (iType > AM_3)) goto _Help; SetDebugBreakOnInvalid( iType, nActive ); if (iType == 0) ConsoleBufferPushFormat( "Enter debugger on BRK opcode: %s", g_aParameters[ iParam ].m_sName ); else ConsoleBufferPushFormat( "Enter debugger on INVALID %1X opcode: %s", iType, g_aParameters[ iParam ].m_sName ); return ConsoleUpdate(); } } return UPDATE_CONSOLE_DISPLAY; _Help: return HelpLastCommand(); } //=========================================================================== Update_t CmdBreakOpcode (int nArgs) // Breakpoint IFF Full-speed! { if (nArgs > 1) return HelpLastCommand(); TCHAR sAction[ CONSOLE_WIDTH ] = TEXT("Current"); // default to display if (nArgs == 1) { int iOpcode = g_aArgs[ 1] .nValue; g_iDebugBreakOnOpcode = iOpcode & 0xFF; _tcscpy( sAction, TEXT("Setting") ); if (iOpcode >= NUM_OPCODES) { ConsoleBufferPushFormat( "Warning: clamping opcode: %02X", g_iDebugBreakOnOpcode ); return ConsoleUpdate(); } } if (g_iDebugBreakOnOpcode == 0) // Show what the current break opcode is ConsoleBufferPushFormat( "%s Break on Opcode: None" , sAction ); else // Show what the current break opcode is ConsoleBufferPushFormat( "%s Break on Opcode: %02X %s" , sAction , g_iDebugBreakOnOpcode , g_aOpcodes65C02[ g_iDebugBreakOnOpcode ].sMnemonic ); return ConsoleUpdate(); } //=========================================================================== Update_t CmdBreakOnInterrupt(int nArgs) { if (nArgs > 1) return HelpLastCommand(); int iParamArg = nArgs; int iParam; int nFound = FindParam(g_aArgs[iParamArg].sArg, MATCH_EXACT, iParam, _PARAM_GENERAL_BEGIN, _PARAM_GENERAL_END); int nActive = -1; if (nFound) { if (iParam == PARAM_ON) nActive = 1; else if (iParam == PARAM_OFF) nActive = 0; } if (nArgs == 1 && nActive == -1) return HelpLastCommand(); TCHAR sAction[CONSOLE_WIDTH] = TEXT("Current"); // default to display if (nArgs == 1) { g_bDebugBreakOnInterrupt = (iParam == PARAM_ON) ? true : false; _tcscpy(sAction, TEXT("Setting")); } ConsoleBufferPushFormat("%s Break on Interrupt: %s" , sAction , g_bDebugBreakOnInterrupt ? "Enabled" : "Disabled" ); return ConsoleUpdate(); } // bool bBP = g_nBreakpoints && CheckBreakpoint(nOffset,nOffset == regs.pc); //=========================================================================== bool GetBreakpointInfo ( WORD nOffset, bool & bBreakpointActive_, bool & bBreakpointEnable_ ) { for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t *pBP = &g_aBreakpoints[ iBreakpoint ]; if ((pBP->nLength) // && (pBP->bEnabled) // not bSet && (nOffset >= pBP->nAddress) && (nOffset < (pBP->nAddress + pBP->nLength))) // [nAddress,nAddress+nLength] { bBreakpointActive_ = pBP->bSet; bBreakpointEnable_ = pBP->bEnabled; return true; } // if (g_aBreakpoints[iBreakpoint].nLength && g_aBreakpoints[iBreakpoint].bEnabled && // (g_aBreakpoints[iBreakpoint].nAddress <= targetaddr) && // (g_aBreakpoints[iBreakpoint].nAddress + g_aBreakpoints[iBreakpoint].nLength > targetaddr)) } bBreakpointActive_ = false; bBreakpointEnable_ = false; return false; } // returns the hit type if the breakpoint stops static BreakpointHit_t hitBreakpoint(Breakpoint_t * pBP, BreakpointHit_t eHitType) { pBP->bHit = true; ++pBP->nHitCount; return pBP->bStop ? eHitType : BP_HIT_NONE; } // Returns true if we should continue checking breakpoint details, else false //=========================================================================== bool _BreakpointValid( Breakpoint_t *pBP ) //, BreakpointSource_t iSrc ) { bool bStatus = false; if (! pBP->bEnabled) return bStatus; // if (pBP->eSource != iSrc) // return bStatus; if (! pBP->nLength) return bStatus; return true; } // Stepping void ClearTempBreakpoints() { for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t *pBP = &g_aBreakpoints[iBreakpoint]; if (! _BreakpointValid( pBP )) continue; if (pBP->bHit && pBP->bTemp) _BWZ_RemoveOne(g_aBreakpoints, iBreakpoint, g_nBreakpoints); pBP->bHit = false; } } static void DebugEnterStepping() { ClearTempBreakpoints(); g_nAppMode = MODE_STEPPING; GetFrame().FrameRefreshStatus(DRAW_TITLE | DRAW_DISK_STATUS); } //=========================================================================== bool _CheckBreakpointValue( Breakpoint_t *pBP, int nVal ) { bool bStatus = false; int iCmp = pBP->eOperator; switch (iCmp) { case BP_OP_LESS_EQUAL : if (nVal <= pBP->nAddress) bStatus = true; break; case BP_OP_LESS_THAN : if (nVal < pBP->nAddress) bStatus = true; break; case BP_OP_EQUAL : // Range is like C++ STL: [,) (inclusive,not-inclusive) if ((nVal >= pBP->nAddress) && ((UINT)nVal < (pBP->nAddress + pBP->nLength))) bStatus = true; break; case BP_OP_NOT_EQUAL : // Range is: (,] (not-inclusive, inclusive) if ((nVal < pBP->nAddress) || ((UINT)nVal >= (pBP->nAddress + pBP->nLength))) bStatus = true; break; case BP_OP_GREATER_THAN : if (nVal > pBP->nAddress) bStatus = true; break; case BP_OP_GREATER_EQUAL: if (nVal >= pBP->nAddress) bStatus = true; break; default: break; } return bStatus; } //=========================================================================== bool _CheckBreakpointRange(Breakpoint_t* pBP, int nVal, int nSize) { bool bStatus = false; int iCmp = pBP->eOperator; switch (iCmp) { case BP_OP_EQUAL: // Range is like C++ STL: [,) (inclusive,not-inclusive) if ( ((nVal >= pBP->nAddress) && ((UINT)nVal < (pBP->nAddress + pBP->nLength))) || ((pBP->nAddress >= nVal) && (pBP->nAddress < ((UINT)nVal + nSize))) ) bStatus = true; break; default: _ASSERT(0); break; } return bStatus; } //=========================================================================== static void DebuggerBreakOnDma(WORD nAddress, WORD nSize, bool isDmaToMemory, int iBreakpoint); bool DebuggerCheckMemBreakpoints(WORD nAddress, WORD nSize, bool isDmaToMemory) { // NB. Caller handles when (addr+size) wraps on 64K for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t* pBP = &g_aBreakpoints[iBreakpoint]; if (_BreakpointValid(pBP)) { if (pBP->eSource == BP_SRC_MEM_RW || (pBP->eSource == BP_SRC_MEM_READ_ONLY && !isDmaToMemory) || (pBP->eSource == BP_SRC_MEM_WRITE_ONLY && isDmaToMemory)) { if (_CheckBreakpointRange(pBP, nAddress, nSize)) { DebuggerBreakOnDma(nAddress, nSize, isDmaToMemory, iBreakpoint); return true; } } } } return false; } //=========================================================================== int CheckBreakpointsIO () { const int NUM_TARGETS = 3; int aTarget[ NUM_TARGETS ] = { NO_6502_TARGET, NO_6502_TARGET, NO_6502_TARGET }; int nBytes; int bBreakpointHit = 0; int iTarget; int nAddress; // bIncludeNextOpcodeAddress == false: // . JSR addr16: ignore addr16 as a target // . BRK/RTS/RTI: ignore return (or vector) addr16 as a target _6502_GetTargets( regs.pc, &aTarget[0], &aTarget[1], &aTarget[2], &nBytes, true, false ); if (nBytes) { for (iTarget = 0; iTarget < NUM_TARGETS; iTarget++ ) { nAddress = aTarget[ iTarget ]; if (nAddress != NO_6502_TARGET) { for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t *pBP = &g_aBreakpoints[iBreakpoint]; if (_BreakpointValid( pBP )) { if (pBP->eSource == BP_SRC_MEM_RW || pBP->eSource == BP_SRC_MEM_READ_ONLY || pBP->eSource == BP_SRC_MEM_WRITE_ONLY) { if (_CheckBreakpointValue( pBP, nAddress )) { g_uBreakMemoryAddress = (WORD) nAddress; BYTE opcode = mem[regs.pc]; if (pBP->eSource == BP_SRC_MEM_RW) { bBreakpointHit |= hitBreakpoint(pBP, BP_HIT_MEM); } else if (pBP->eSource == BP_SRC_MEM_READ_ONLY) { if (g_aOpcodes[opcode].nMemoryAccess & (MEM_RI|MEM_R)) { bBreakpointHit |= hitBreakpoint(pBP, BP_HIT_MEMR); } } else if (pBP->eSource == BP_SRC_MEM_WRITE_ONLY) { if (g_aOpcodes[opcode].nMemoryAccess & (MEM_WI|MEM_W)) { bBreakpointHit |= hitBreakpoint(pBP, BP_HIT_MEMW); } } else { _ASSERT(0); } } } } } } } } return bBreakpointHit; } // Returns true if a register breakpoint is triggered //=========================================================================== int CheckBreakpointsReg () { int iAnyBreakpointHit = 0; for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t *pBP = &g_aBreakpoints[iBreakpoint]; if (! _BreakpointValid( pBP )) continue; bool bBreakpointHit = 0; switch (pBP->eSource) { case BP_SRC_REG_PC: bBreakpointHit = _CheckBreakpointValue( pBP, regs.pc ); break; case BP_SRC_REG_A: bBreakpointHit = _CheckBreakpointValue( pBP, regs.a ); break; case BP_SRC_REG_X: bBreakpointHit = _CheckBreakpointValue( pBP, regs.x ); break; case BP_SRC_REG_Y: bBreakpointHit = _CheckBreakpointValue( pBP, regs.y ); break; case BP_SRC_REG_P: bBreakpointHit = _CheckBreakpointValue( pBP, regs.ps ); break; case BP_SRC_REG_S: bBreakpointHit = _CheckBreakpointValue( pBP, regs.sp ); break; default: break; } if (bBreakpointHit) { iAnyBreakpointHit = hitBreakpoint(pBP, BP_HIT_REG); } } return iAnyBreakpointHit; } // Returns true if a video breakpoint is triggered //=========================================================================== int CheckBreakpointsVideo() { int iBreakpointHit = 0; for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t* pBP = &g_aBreakpoints[iBreakpoint]; if (!_BreakpointValid(pBP)) continue; if (pBP->eSource != BP_SRC_VIDEO_SCANNER) continue; uint16_t vert = NTSC_GetVideoVertForDebugger(); // update video scanner's vert/horz position - needed for when in fullspeed (GH#1164) if (_CheckBreakpointValue(pBP, vert)) { iBreakpointHit = hitBreakpoint(pBP, BP_HIT_VIDEO_POS); pBP->bEnabled = false; // Disable, otherwise it'll trigger many times on this scan-line } } return iBreakpointHit; } //=========================================================================== static int CheckBreakpointsDmaToOrFromIOMemory(void) { int res = g_DebugBreakOnDMAIO.isToOrFromMemory; g_DebugBreakOnDMAIO.isToOrFromMemory = 0; return res; } void DebuggerBreakOnDmaToOrFromIoMemory(WORD nAddress, bool isDmaToMemory) { g_DebugBreakOnDMAIO.isToOrFromMemory = isDmaToMemory ? BP_DMA_TO_IO_MEM : BP_DMA_FROM_IO_MEM; g_DebugBreakOnDMAIO.memoryAddr = nAddress; } static int CheckBreakpointsDmaToOrFromMemory(int idx) { if (idx == -1) { int res = 0; for (int i = 0; i < NUM_BREAK_ON_DMA; i++) res |= g_DebugBreakOnDMA[i].isToOrFromMemory; return res; } _ASSERT(idx < NUM_BREAK_ON_DMA); if (idx >= NUM_BREAK_ON_DMA) return 0; int res = g_DebugBreakOnDMA[idx].isToOrFromMemory; g_DebugBreakOnDMA[idx].isToOrFromMemory = 0; return res; } static void DebuggerBreakOnDma(WORD nAddress, WORD nSize, bool isDmaToMemory, int iBreakpoint) { for (int i = 0; i < NUM_BREAK_ON_DMA; i++) { if (g_DebugBreakOnDMA[i].isToOrFromMemory != 0) continue; g_DebugBreakOnDMA[i].isToOrFromMemory = isDmaToMemory ? BP_DMA_TO_MEM : BP_DMA_FROM_MEM; g_DebugBreakOnDMA[i].memoryAddr = nAddress; g_DebugBreakOnDMA[i].memoryAddrEnd = nAddress + nSize - 1; g_DebugBreakOnDMA[i].BPid = iBreakpoint; return; } _ASSERT(0); } //=========================================================================== Update_t CmdBreakpoint (int nArgs) { return CmdBreakpointAddPC( nArgs ); } // smart breakpoint //=========================================================================== Update_t CmdBreakpointAddSmart (int nArgs) { unsigned int nAddress = g_aArgs[1].nValue; if (! nArgs) { nArgs = 1; g_aArgs[ nArgs ].nValue = g_nDisasmCurAddress; } if ((nAddress >= _6502_IO_BEGIN) && (nAddress <= _6502_IO_END)) { return CmdBreakpointAddIO( nArgs ); } else { CmdBreakpointAddReg( nArgs ); CmdBreakpointAddMem( nArgs ); return UPDATE_BREAKPOINTS; } } //=========================================================================== Update_t CmdBreakpointAddReg (int nArgs) { if (! nArgs) { return Help_Arg_1( CMD_BREAKPOINT_ADD_REG ); } BreakpointSource_t iSrc = BP_SRC_REG_PC; BreakpointOperator_t iCmp = BP_OP_EQUAL ; bool bHaveSrc = false; bool bHaveCmp = false; int iParamSrc; int iParamCmp; int nFound; int iArg = 0; while (iArg++ < nArgs) { char *sArg = g_aArgs[iArg].sArg; bHaveSrc = false; bHaveCmp = false; nFound = FindParam( sArg, MATCH_EXACT, iParamSrc, _PARAM_REGS_BEGIN, _PARAM_REGS_END ); if (nFound) { switch (iParamSrc) { case PARAM_REG_A : iSrc = BP_SRC_REG_A ; bHaveSrc = true; break; case PARAM_FLAGS : iSrc = BP_SRC_REG_P ; bHaveSrc = true; break; case PARAM_REG_X : iSrc = BP_SRC_REG_X ; bHaveSrc = true; break; case PARAM_REG_Y : iSrc = BP_SRC_REG_Y ; bHaveSrc = true; break; case PARAM_REG_PC: iSrc = BP_SRC_REG_PC; bHaveSrc = true; break; case PARAM_REG_SP: iSrc = BP_SRC_REG_S ; bHaveSrc = true; break; default: break; } } nFound = FindParam( sArg, MATCH_EXACT, iParamCmp, _PARAM_BREAKPOINT_BEGIN, _PARAM_BREAKPOINT_END ); if (nFound) { switch (iParamCmp) { case PARAM_BP_LESS_EQUAL : iCmp = BP_OP_LESS_EQUAL ; bHaveCmp = true; break; case PARAM_BP_LESS_THAN : iCmp = BP_OP_LESS_THAN ; bHaveCmp = true; break; case PARAM_BP_EQUAL : iCmp = BP_OP_EQUAL ; bHaveCmp = true; break; case PARAM_BP_NOT_EQUAL : iCmp = BP_OP_NOT_EQUAL ; bHaveCmp = true; break; case PARAM_BP_NOT_EQUAL_1 : iCmp = BP_OP_NOT_EQUAL ; bHaveCmp = true; break; case PARAM_BP_GREATER_THAN : iCmp = BP_OP_GREATER_THAN ; bHaveCmp = true; break; case PARAM_BP_GREATER_EQUAL: iCmp = BP_OP_GREATER_EQUAL; bHaveCmp = true; break; default: break; } } if ((! bHaveSrc) && (! bHaveCmp)) // Inverted/Convoluted logic: didn't find BOTH this pass, so we must have already found them. { int dArgs = _CmdBreakpointAddCommonArg( iArg, nArgs, iSrc, iCmp ); if (!dArgs) { return Help_Arg_1( CMD_BREAKPOINT_ADD_REG ); } iArg += dArgs; } } return UPDATE_ALL; } //=========================================================================== bool _CmdBreakpointAddReg( Breakpoint_t *pBP, BreakpointSource_t iSrc, BreakpointOperator_t iCmp, WORD nAddress, int nLen, bool bIsTempBreakpoint ) { bool bStatus = false; if (pBP) { _ASSERT(nLen <= _6502_MEM_LEN); if (nLen > (int) _6502_MEM_LEN) nLen = (int) _6502_MEM_LEN; if (iSrc == BP_SRC_VIDEO_SCANNER) { if (nAddress >= NTSC_GetVideoLines()) nAddress = NTSC_GetVideoLines() - 1; if ((nAddress + (UINT)nLen) >= NTSC_GetVideoLines()) nLen = NTSC_GetVideoLines() - nAddress; } pBP->eSource = iSrc; pBP->eOperator = iCmp; pBP->nAddress = nAddress; pBP->nLength = nLen; pBP->bSet = true; pBP->bEnabled = true; pBP->bTemp = bIsTempBreakpoint; pBP->bStop = true; pBP->bHit = false; pBP->nHitCount = 0; bStatus = true; } return bStatus; } // @return Number of args processed //=========================================================================== int _CmdBreakpointAddCommonArg ( int iArg, int nArg, BreakpointSource_t iSrc, BreakpointOperator_t iCmp, bool bIsTempBreakpoint ) { int dArg = 0; int iBreakpoint = 0; Breakpoint_t *pBP = & g_aBreakpoints[ iBreakpoint ]; while ((iBreakpoint < MAX_BREAKPOINTS) && g_aBreakpoints[iBreakpoint].bSet) //g_aBreakpoints[iBreakpoint].nLength) { iBreakpoint++; pBP++; } if (iBreakpoint >= MAX_BREAKPOINTS) { ConsoleDisplayError("All Breakpoints slots are currently in use."); return dArg; } if (iArg <= nArg) { #if DEBUG_VAL_2 int nLen = g_aArgs[iArg].nVal2; #endif WORD nAddress = 0; WORD nAddress2 = 0; WORD nEnd = 0; int nLen = 0; dArg = 1; RangeType_t eRange = Range_Get( nAddress, nAddress2, iArg); if ((eRange == RANGE_HAS_END) || (eRange == RANGE_HAS_LEN)) { Range_CalcEndLen( eRange, nAddress, nAddress2, nEnd, nLen ); dArg = 2; } if ( !nLen) { nLen = 1; } if (! _CmdBreakpointAddReg( pBP, iSrc, iCmp, nAddress, nLen, bIsTempBreakpoint )) { dArg = 0; } g_nBreakpoints++; } return dArg; } //=========================================================================== Update_t CmdBreakpointAddPC (int nArgs) { BreakpointSource_t iSrc = BP_SRC_REG_PC; BreakpointOperator_t iCmp = BP_OP_EQUAL ; if (!nArgs) { nArgs = 1; // g_aArgs[1].nValue = regs.pc; g_aArgs[1].nValue = g_nDisasmCurAddress; } // int iParamSrc; int iParamCmp; int nFound = 0; int iArg = 0; while (iArg++ < nArgs) { char *sArg = g_aArgs[iArg].sArg; if (g_aArgs[iArg].bType & TYPE_OPERATOR) { nFound = FindParam( sArg, MATCH_EXACT, iParamCmp, _PARAM_BREAKPOINT_BEGIN, _PARAM_BREAKPOINT_END ); if (nFound) { switch (iParamCmp) { case PARAM_BP_LESS_EQUAL : iCmp = BP_OP_LESS_EQUAL ; break; case PARAM_BP_LESS_THAN : iCmp = BP_OP_LESS_THAN ; break; case PARAM_BP_EQUAL : iCmp = BP_OP_EQUAL ; break; case PARAM_BP_NOT_EQUAL : iCmp = BP_OP_NOT_EQUAL ; break; case PARAM_BP_GREATER_THAN : iCmp = BP_OP_GREATER_THAN ; break; case PARAM_BP_GREATER_EQUAL: iCmp = BP_OP_GREATER_EQUAL; break; default: break; } } } else { int dArg = _CmdBreakpointAddCommonArg( iArg, nArgs, iSrc, iCmp ); if (! dArg) { return Help_Arg_1( CMD_BREAKPOINT_ADD_PC ); } iArg += dArg; } } return UPDATE_BREAKPOINTS | UPDATE_CONSOLE_DISPLAY; // 1; } //=========================================================================== Update_t CmdBreakpointAddIO (int nArgs) { return CmdBreakpointAddMem( nArgs ); // return UPDATE_BREAKPOINTS | UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdBreakpointAddMemA(int nArgs) { return CmdBreakpointAddMem(nArgs); } //=========================================================================== Update_t CmdBreakpointAddMemR(int nArgs) { return CmdBreakpointAddMem(nArgs, BP_SRC_MEM_READ_ONLY); } //=========================================================================== Update_t CmdBreakpointAddMemW(int nArgs) { return CmdBreakpointAddMem(nArgs, BP_SRC_MEM_WRITE_ONLY); } //=========================================================================== Update_t CmdBreakpointAddMem(int nArgs, BreakpointSource_t bpSrc /*= BP_SRC_MEM_RW*/) { BreakpointSource_t iSrc = bpSrc; BreakpointOperator_t iCmp = BP_OP_EQUAL; int iArg = 0; while (iArg++ < nArgs) { if (g_aArgs[iArg].bType & TYPE_OPERATOR) { return Help_Arg_1( CMD_BREAKPOINT_ADD_MEM ); } else { int dArg = _CmdBreakpointAddCommonArg( iArg, nArgs, iSrc, iCmp ); if (! dArg) { return Help_Arg_1( CMD_BREAKPOINT_ADD_MEM ); } iArg += dArg; } } return UPDATE_BREAKPOINTS | UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdBreakpointAddVideo(int nArgs) { BreakpointSource_t iSrc = BP_SRC_VIDEO_SCANNER; BreakpointOperator_t iCmp = BP_OP_EQUAL; int iArg = 0; while (iArg++ < nArgs) { if (g_aArgs[iArg].bType & TYPE_OPERATOR) { return Help_Arg_1(CMD_BREAKPOINT_ADD_VIDEO); } else { int dArg = _CmdBreakpointAddCommonArg(iArg, nArgs, iSrc, iCmp); if (!dArg) { return Help_Arg_1(CMD_BREAKPOINT_ADD_VIDEO); } iArg += dArg; } } return UPDATE_BREAKPOINTS | UPDATE_CONSOLE_DISPLAY; } //=========================================================================== void _BWZ_RemoveOne( Breakpoint_t *aBreakWatchZero, const int iSlot, int & nTotal ) { if (aBreakWatchZero[iSlot].bSet) { aBreakWatchZero[ iSlot ].bSet = false; aBreakWatchZero[ iSlot ].bEnabled = false; aBreakWatchZero[ iSlot ].nLength = 0; nTotal--; } } void _BWZ_RemoveAll( Breakpoint_t *aBreakWatchZero, const int nMax, int & nTotal ) { for ( int iSlot = 0; iSlot < nMax; iSlot++ ) { _BWZ_RemoveOne( aBreakWatchZero, iSlot, nTotal ); } } // called by BreakpointsClear, WatchesClear, ZeroPagePointersClear //=========================================================================== void _BWZ_ClearViaArgs( int nArgs, Breakpoint_t * aBreakWatchZero, const int nMax, int & nTotal ) { int iSlot = 0; // Clear specified breakpoints while (nArgs) { iSlot = g_aArgs[nArgs].nValue; if (! _tcscmp(g_aArgs[nArgs].sArg, g_aParameters[ PARAM_WILDSTAR ].m_sName)) { _BWZ_RemoveAll( aBreakWatchZero, nMax, nTotal ); break; } else if ((iSlot >= 0) && (iSlot < nMax)) { _BWZ_RemoveOne( aBreakWatchZero, iSlot, nTotal ); } nArgs--; } } // called by BreakpointsEnable, WatchesEnable, ZeroPagePointersEnable // called by BreakpointsDisable, WatchesDisable, ZeroPagePointersDisable void _BWZ_EnableDisableViaArgs( int nArgs, Breakpoint_t * aBreakWatchZero, const int nMax, const bool bEnabled ) { int iSlot = 0; // Enable each breakpoint in the list while (nArgs) { iSlot = g_aArgs[nArgs].nValue; if (! _tcscmp(g_aArgs[nArgs].sArg, g_aParameters[ PARAM_WILDSTAR ].m_sName)) { for ( ; iSlot < nMax; iSlot++ ) { aBreakWatchZero[ iSlot ].bEnabled = bEnabled; } } else if ((iSlot >= 0) && (iSlot < nMax)) { aBreakWatchZero[ iSlot ].bEnabled = bEnabled; } nArgs--; } } //=========================================================================== Update_t CmdBreakpointClear (int nArgs) { if (!g_nBreakpoints) return ConsoleDisplayError("There are no breakpoints defined."); if (!nArgs) { _BWZ_RemoveAll( g_aBreakpoints, MAX_BREAKPOINTS, g_nBreakpoints ); } else { _BWZ_ClearViaArgs( nArgs, g_aBreakpoints, MAX_BREAKPOINTS, g_nBreakpoints ); } return UPDATE_DISASM | UPDATE_BREAKPOINTS | UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdBreakpointDisable (int nArgs) { if (! g_nBreakpoints) return ConsoleDisplayError("There are no (PC) Breakpoints defined."); if (! nArgs) return Help_Arg_1( CMD_BREAKPOINT_DISABLE ); _BWZ_EnableDisableViaArgs( nArgs, g_aBreakpoints, MAX_BREAKPOINTS, false ); return UPDATE_BREAKPOINTS; } //=========================================================================== Update_t CmdBreakpointEdit (int nArgs) { return (UPDATE_DISASM | UPDATE_BREAKPOINTS); } //=========================================================================== Update_t CmdBreakpointEnable (int nArgs) { if (! g_nBreakpoints) return ConsoleDisplayError("There are no (PC) Breakpoints defined."); if (! nArgs) return Help_Arg_1( CMD_BREAKPOINT_ENABLE ); _BWZ_EnableDisableViaArgs( nArgs, g_aBreakpoints, MAX_BREAKPOINTS, true ); return UPDATE_BREAKPOINTS; } Update_t CmdBreakpointChange (int nArgs) { if (! g_nBreakpoints) return ConsoleDisplayError("There are no (PC) Breakpoints defined."); if (nArgs != 2) return Help_Arg_1( CMD_BREAKPOINT_CHANGE ); const int iSlot = g_aArgs[1].nValue; if (iSlot >= 0 && iSlot < MAX_BREAKPOINTS && g_aBreakpoints[iSlot].bSet) { Breakpoint_t & bp = g_aBreakpoints[iSlot]; const char * sArg = g_aArgs[2].sArg; const int nArgLen = g_aArgs[2].nArgLen; for (int i = 0; i < nArgLen; ++i) { switch (sArg[i]) { case 'E': bp.bEnabled = true; break; case 'e': bp.bEnabled = false; break; case 'T': bp.bTemp = true; break; case 't': bp.bTemp = false; break; case 'S': bp.bStop = true; break; case 's': bp.bStop = false; break; } } } return UPDATE_BREAKPOINTS; } void _BWZ_List( const Breakpoint_t * aBreakWatchZero, const int iBWZ ) //, bool bZeroBased ) { static const char sEnabledFlags[] = "-E"; static const char sStopFlags[] = "-S"; static const char sTempFlags[] = "-T"; static const char sHitFlags[] = " *"; std::string sAddressBuf; std::string const& sSymbol = GetSymbol(aBreakWatchZero[iBWZ].nAddress, 2, sAddressBuf); char cBPM = aBreakWatchZero[iBWZ].eSource == BP_SRC_MEM_READ_ONLY ? 'R' : aBreakWatchZero[iBWZ].eSource == BP_SRC_MEM_WRITE_ONLY ? 'W' : ' '; ConsoleBufferPushFormat( " #%d %c %c %c %c %08X %04X %c %s", // (bZeroBased ? iBWZ + 1 : iBWZ), iBWZ, sEnabledFlags[ aBreakWatchZero[ iBWZ ].bEnabled ? 1 : 0 ], sStopFlags [ aBreakWatchZero[ iBWZ ].bStop ? 1 : 0 ], sTempFlags [ aBreakWatchZero[ iBWZ ].bTemp ? 1 : 0 ], sHitFlags [ aBreakWatchZero[ iBWZ ].bHit ? 1 : 0 ], aBreakWatchZero[ iBWZ ].nHitCount, aBreakWatchZero[ iBWZ ].nAddress, cBPM, sSymbol.c_str() ); } void _BWZ_ListAll( const Breakpoint_t * aBreakWatchZero, const int nMax ) { int iBWZ = 0; while (iBWZ < nMax) // { if (aBreakWatchZero[ iBWZ ].bSet) { _BWZ_List( aBreakWatchZero, iBWZ ); } iBWZ++; } } //=========================================================================== Update_t CmdBreakpointList (int nArgs) { // ConsoleBufferPush( ); // std::vector vBreakpoints; // int iBreakpoint = MAX_BREAKPOINTS; // while (iBreakpoint--) // { // if (g_aBreakpoints[iBreakpoint].enabled) // { // vBreakpoints.push_back( g_aBreakpoints[iBreakpoint].address ); // } // } // std::sort( vBreakpoints.begin(), vBreakpoints.end() ); // iBreakpoint = vBreakPoints.size(); if (! g_nBreakpoints) { ConsoleBufferPushFormat( " There are no current breakpoints. (Max: %d)", MAX_BREAKPOINTS ); } else { _BWZ_ListAll( g_aBreakpoints, MAX_BREAKPOINTS ); } return ConsoleUpdate(); } //=========================================================================== Update_t CmdBreakpointLoad (int nArgs) { return UPDATE_ALL; } //=========================================================================== Update_t CmdBreakpointSave (int nArgs) { g_ConfigState.Reset(); ConfigSave_PrepareHeader( PARAM_CAT_BREAKPOINTS, CMD_BREAKPOINT_CLEAR ); int iBreakpoint = 0; while (iBreakpoint < MAX_BREAKPOINTS) { if (g_aBreakpoints[ iBreakpoint ].bSet) { g_ConfigState.PushLineFormat( "%s %x %04X,%04X\n" , g_aCommands[ CMD_BREAKPOINT_ADD_REG ].m_sName , iBreakpoint , g_aBreakpoints[ iBreakpoint ].nAddress , g_aBreakpoints[ iBreakpoint ].nLength ); } if (! g_aBreakpoints[ iBreakpoint ].bEnabled) { g_ConfigState.PushLineFormat( "%s %x\n" , g_aCommands[ CMD_BREAKPOINT_DISABLE ].m_sName , iBreakpoint ); } iBreakpoint++; } if (nArgs) { if (! (g_aArgs[ 1 ].bType & TYPE_QUOTED_2)) return Help_Arg_1( CMD_BREAKPOINT_SAVE ); if (ConfigSave_BufferToDisk( g_aArgs[ 1 ].sArg, CONFIG_SAVE_FILE_CREATE )) { ConsoleBufferPush( TEXT( "Saved." ) ); return ConsoleUpdate(); } } return UPDATE_CONSOLE_DISPLAY; } // Assembler ______________________________________________________________________________________ //=========================================================================== Update_t _CmdAssemble( WORD nAddress, int iArg, int nArgs ) { // if AlphaNumeric ArgToken_e iTokenSrc = NO_TOKEN; ParserFindToken( g_pConsoleInput, g_aTokens, NUM_TOKENS, &iTokenSrc ); if (iTokenSrc == NO_TOKEN) // is TOKEN_ALPHANUMERIC if (g_pConsoleInput[0] != CHAR_SPACE) { // Symbol char *pSymbolName = g_aArgs[ iArg ].sArg; // pArg->sArg; SymbolUpdate( SYMBOLS_ASSEMBLY, pSymbolName, nAddress, false, true ); // bool bRemoveSymbol, bool bUpdateSymbol ) iArg++; } bool bStatus = Assemble( iArg, nArgs, nAddress ); if ( bStatus) { // move disassembler to current address g_nDisasmCurAddress = g_nAssemblerAddress; WindowUpdateDisasmSize(); // calc cur line DisasmCalcTopBotAddress(); return UPDATE_ALL; } return UPDATE_CONSOLE_DISPLAY; // UPDATE_NOTHING; } //=========================================================================== Update_t CmdAssemble (int nArgs) { if (! g_bAssemblerOpcodesHashed) { AssemblerStartup(); g_bAssemblerOpcodesHashed = true; } // 0 : A // 1 : A address // 2+: A address mnemonic... if (nArgs > 0) g_nAssemblerAddress = g_aArgs[1].nValue; // move disassembler window to current assembler address g_nDisasmCurAddress = g_nAssemblerAddress; // (2) WindowUpdateDisasmSize(); // calc cur line DisasmCalcTopBotAddress(); if (! nArgs) { // return Help_Arg_1( CMD_ASSEMBLE ); // Start assembler, continue with last assembled address AssemblerOn(); return UPDATE_CONSOLE_DISPLAY; } if (nArgs == 1) { int iArg = 1; // undocumented ASM * if ((! _tcscmp( g_aArgs[ iArg ].sArg, g_aParameters[ PARAM_WILDSTAR ].m_sName )) || (! _tcscmp( g_aArgs[ iArg ].sArg, g_aParameters[ PARAM_MEM_SEARCH_WILD ].m_sName )) ) { _CmdAssembleHashDump(); } AssemblerOn(); return UPDATE_CONSOLE_DISPLAY; // return Help_Arg_1( CMD_ASSEMBLE ); } if (nArgs > 1) { return _CmdAssemble( g_nAssemblerAddress, 2, nArgs ); // disasm, memory, watches, zeropage } // return Help_Arg_1( CMD_ASSEMBLE ); // g_nAssemblerAddress; // g_aArgs[1].nValue; // return ConsoleUpdate(); return UPDATE_CONSOLE_DISPLAY; } // CPU ____________________________________________________________________________________________ // CPU Step, Trace ________________________________________________________________________________ //=========================================================================== static Update_t CmdGo (int nArgs, const bool bFullSpeed) { // G StopAddress [SkipAddress,Length] // Example: // G C600 FA00,FFFF // TODO: G addr1,len addr3,len // TODO: G addr1:addr2 addr3:addr4 const int kCmdGo = !bFullSpeed ? CMD_GO_NORMAL_SPEED : CMD_GO_FULL_SPEED; g_nDebugSteps = -1; g_nDebugStepCycles = 0; g_nDebugStepStart = regs.pc; g_nDebugStepUntil = nArgs ? g_aArgs[1].nValue : -1; g_nDebugSkipStart = -1; g_nDebugSkipLen = -1; if (nArgs > 4) return Help_Arg_1( kCmdGo ); // G StopAddress [SkipAddress,Len] // Old 1 2 2 // G addr addr [, len] // New 1 2 3 4 if (nArgs > 1) { int iArg = 2; g_nDebugSkipStart = g_aArgs[ iArg ].nValue; #if DEBUG_VAL_2 WORD nAddress = g_aArgs[ iArg ].nVal2; #endif int nLen = 0; int nEnd = 0; if (nArgs > 2) { if (g_aArgs[ iArg + 1 ].eToken == TOKEN_COMMA) { if (nArgs > 3) { nLen = g_aArgs[ iArg + 2 ].nValue; nEnd = g_nDebugSkipStart + nLen; if (nEnd > (int) _6502_MEM_END) nEnd = _6502_MEM_END + 1; } else { return Help_Arg_1( kCmdGo ); } } else if (g_aArgs[ iArg+ 1 ].eToken == TOKEN_COLON) { nEnd = g_aArgs[ iArg + 2 ].nValue + 1; } else return Help_Arg_1( kCmdGo ); } else return Help_Arg_1( kCmdGo ); nLen = nEnd - g_nDebugSkipStart; if (nLen < 0) nLen = -nLen; g_nDebugSkipLen = nLen; g_nDebugSkipLen &= _6502_MEM_END; #if _DEBUG ConsoleBufferPushFormat( "Start: %04X,%04X End: %04X Len: %04X", g_nDebugSkipStart, g_nDebugSkipLen, nEnd, nLen ); ConsoleBufferToDisplay(); #endif } // WORD nAddressSymbol = 0; // bool bFoundSymbol = FindAddressFromSymbol( g_aArgs[1].sArg, & nAddressSymbol ); // if (bFoundSymbol) // g_nDebugStepUntil = nAddressSymbol; // if (!g_nDebugStepUntil) // g_nDebugStepUntil = GetAddress(g_aArgs[1].sArg); g_bDebuggerEatKey = true; g_bDebugFullSpeed = bFullSpeed; g_bLastGoCmdWasFullSpeed = bFullSpeed; g_bGoCmd_ReinitFlag = true; DebugEnterStepping(); SoundCore_SetFade(FADE_IN); return UPDATE_CONSOLE_DISPLAY; } Update_t CmdGoNormalSpeed (int nArgs) { return CmdGo(nArgs, false); } Update_t CmdGoFullSpeed (int nArgs) { return CmdGo(nArgs, true); } //=========================================================================== Update_t CmdStepOver (int nArgs) { // assert( g_nDisasmCurAddress == regs.pc ); // g_nDebugSteps = nArgs ? g_aArgs[1].nValue : 1; WORD nDebugSteps = nArgs ? g_aArgs[1].nValue : 1; while (nDebugSteps -- > 0) { int nOpcode = *(mem + regs.pc); // g_nDisasmCurAddress // int eMode = g_aOpcodes[ nOpcode ].addrmode; // int nByte = g_aOpmodes[eMode]._nBytes; // if ((eMode == AM_A) && CmdTrace(0); if (nOpcode == OPCODE_JSR) { CmdStepOut(0); g_nDebugSteps = 0xFFFF; while (g_nDebugSteps != 0) DebugContinueStepping(true); } } return UPDATE_ALL; } //=========================================================================== Update_t CmdStepOut (int nArgs) { // TODO: "RET" should probably pop the Call stack // Also see: CmdCursorJumpRetAddr WORD nAddress; if (_6502_GetStackReturnAddress( nAddress )) { nArgs = _Arg_1( nAddress ); g_aArgs[1].sArg[0] = 0; CmdGo( 1, true ); } return UPDATE_ALL; } //=========================================================================== Update_t CmdTrace (int nArgs) { g_nDebugSteps = nArgs ? g_aArgs[1].nValue : 1; g_nDebugStepCycles = 0; g_nDebugStepStart = regs.pc; g_nDebugStepUntil = -1; DebugEnterStepping(); DebugContinueStepping(true); return UPDATE_ALL; // TODO: Verify // 0 } //=========================================================================== Update_t CmdTraceFile (int nArgs) { if (g_hTraceFile) { fclose( g_hTraceFile ); g_hTraceFile = NULL; ConsoleBufferPush( "Trace stopped." ); } else { std::string sFileName; if (nArgs) sFileName = g_aArgs[1].sArg; else sFileName = g_sFileNameTrace; g_bTraceFileWithVideoScanner = (nArgs >= 2); const std::string sFilePath = g_sCurrentDir + sFileName; g_hTraceFile = fopen( sFilePath.c_str(), "wt" ); if (g_hTraceFile) { const char* pTextHdr = g_bTraceFileWithVideoScanner ? "Trace (with video info) started: %s" : "Trace started: %s"; ConsoleBufferPushFormat( pTextHdr, sFilePath.c_str() ); g_bTraceHeader = true; } else { ConsoleBufferPushFormat( "Trace ERROR: %s", sFilePath.c_str() ); } } ConsoleBufferToDisplay(); return UPDATE_ALL; // TODO: Verify // 0 } //=========================================================================== Update_t CmdTraceLine (int nArgs) { g_nDebugSteps = nArgs ? g_aArgs[1].nValue : 1; g_nDebugStepCycles = 1; g_nDebugStepStart = regs.pc; g_nDebugStepUntil = -1; DebugEnterStepping(); DebugContinueStepping(true); return UPDATE_ALL; // TODO: Verify // 0 } // Unassemble //=========================================================================== Update_t CmdUnassemble (int nArgs) { if (! nArgs) return Help_Arg_1( CMD_UNASSEMBLE ); WORD nAddress = g_aArgs[1].nValue; g_nDisasmTopAddress = nAddress; DisasmCalcCurFromTopAddress(); DisasmCalcBotFromTopAddress(); return UPDATE_DISASM; } //=========================================================================== Update_t CmdKey (int nArgs) { KeybQueueKeypress( nArgs ? g_aArgs[1].nValue ? g_aArgs[1].nValue : g_aArgs[1].sArg[0] : TEXT(' '), ASCII); // FIXME!!! return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdIn (int nArgs) { if (!nArgs) return Help_Arg_1( CMD_IN ); WORD nAddress = g_aArgs[1].nValue; IORead[ (nAddress>>4) & 0xF ](regs.pc, nAddress & 0xFF, 0, 0, 0); // g_aArgs[1].nValue return UPDATE_CONSOLE_DISPLAY; // TODO: Verify // 1 } //=========================================================================== Update_t CmdJSR (int nArgs) { if (! nArgs) return Help_Arg_1( CMD_JSR ); WORD nAddress = g_aArgs[1].nValue & _6502_MEM_END; // Mark Stack Page as dirty *(memdirty+(regs.sp >> 8)) = 1; // Push PC onto stack *(mem + regs.sp) = ((regs.pc >> 8) & 0xFF); regs.sp--; *(mem + regs.sp) = ((regs.pc >> 0) - 1) & 0xFF; regs.sp--; // Jump to new address regs.pc = nAddress; return UPDATE_ALL; } //=========================================================================== Update_t CmdNOP (int nArgs) { int iOpcode; int iOpmode; int nOpbytes; _6502_GetOpcodeOpmodeOpbyte( iOpcode, iOpmode, nOpbytes ); while (nOpbytes--) { *(mem+regs.pc + nOpbytes) = 0xEA; } return UPDATE_ALL; } //=========================================================================== Update_t CmdOut (int nArgs) { // if ((!nArgs) || // ((g_aArgs[1].sArg[0] != TEXT('0')) && (!g_aArgs[1].nValue) && (!GetAddress(g_aArgs[1].sArg)))) // return DisplayHelp(CmdInput); if (!nArgs) Help_Arg_1( CMD_OUT ); WORD nAddress = g_aArgs[1].nValue; IOWrite[ (nAddress>>4) & 0xF ] (regs.pc, nAddress & 0xFF, 1, g_aArgs[2].nValue & 0xFF, 0); return UPDATE_CONSOLE_DISPLAY; // TODO: Verify // 1 } //=========================================================================== Update_t CmdLBR(int nArgs) { if (g_LBR == LBR_UNDEFINED) ConsolePrintFormat(" LBR not set yet. Hint: Run from the debugger via 'g' command."); else ConsolePrintFormat(" LBR = $%04X", g_LBR); return ConsoleUpdate(); } // Color __________________________________________________________________________________________ void _ColorPrint( int iColor, COLORREF nColor ) { int R = (nColor >> 0) & 0xFF; int G = (nColor >> 8) & 0xFF; int B = (nColor >> 16) & 0xFF; ConsoleBufferPushFormat( " Color %01X: %02X %02X %02X", iColor, R, G, B ); // TODO: print name of colors! } void _CmdColorGet( const int iScheme, const int iColor ) { if (iColor < NUM_DEBUG_COLORS) { // COLORREF nColor = g_aColors[ iScheme ][ iColor ]; DebugColors_e eColor = static_cast( iColor ); COLORREF nColor = DebuggerGetColor( eColor ); _ColorPrint( iColor, nColor ); } else { std::string sText = StrFormat( "Color: %d\nOut of range!", iColor ); GetFrame().FrameMessageBox(sText.c_str(), "ERROR", MB_OK); } } //=========================================================================== Update_t CmdConfigColorMono (int nArgs) { int iScheme = 0; if (g_iCommand == CMD_CONFIG_COLOR) iScheme = SCHEME_COLOR; if (g_iCommand == CMD_CONFIG_MONOCHROME) iScheme = SCHEME_MONO; if (g_iCommand == CMD_CONFIG_BW) iScheme = SCHEME_BW; if ((iScheme < 0) || (iScheme > NUM_COLOR_SCHEMES)) // sanity check iScheme = SCHEME_COLOR; if (! nArgs) { g_iColorScheme = iScheme; UpdateDisplay( UPDATE_BACKGROUND ); return UPDATE_ALL; } // if ((nArgs != 1) && (nArgs != 4)) if (nArgs > 4) return HelpLastCommand(); int iColor = g_aArgs[ 1 ].nValue; if ((iColor < 0) || iColor >= NUM_DEBUG_COLORS) return HelpLastCommand(); int iParam; int nFound = FindParam( g_aArgs[ 1 ].sArg, MATCH_EXACT, iParam, _PARAM_GENERAL_BEGIN, _PARAM_GENERAL_END ); if (nFound) { if (iParam == PARAM_RESET) { ConfigColorsReset(); ConsoleBufferPush( TEXT(" Resetting colors." ) ); } else if (iParam == PARAM_SAVE) { } else if (iParam == PARAM_LOAD) { } else return HelpLastCommand(); } else { if (nArgs == 1) { // Dump Color _CmdColorGet( iScheme, iColor ); return ConsoleUpdate(); } else if (nArgs == 4) { // Set Color int R = g_aArgs[2].nValue & 0xFF; int G = g_aArgs[3].nValue & 0xFF; int B = g_aArgs[4].nValue & 0xFF; COLORREF nColor = RGB(R,G,B); DebuggerSetColor( iScheme, iColor, nColor ); } else return HelpLastCommand(); } return UPDATE_ALL; } Update_t CmdConfigHColor (int nArgs) { if ((nArgs != 1) && (nArgs != 4)) return Help_Arg_1( g_iCommand ); int iColor = g_aArgs[ 1 ].nValue; if ((iColor < 0) || iColor >= NUM_DEBUG_COLORS) return Help_Arg_1( g_iCommand ); if (nArgs == 1) { // Dump Color // _CmdColorGet( iScheme, iColor ); // TODO/FIXME: must export AW_Video.cpp: static LPBITMAPINFO framebufferinfo; // COLORREF nColor = g_aColors[ iScheme ][ iColor ]; // _ColorPrint( iColor, nColor ); return ConsoleUpdate(); } else { // Set Color // DebuggerSetColor( iScheme, iColor ); return UPDATE_ALL; } } // Config _________________________________________________________________________________________ //=========================================================================== Update_t CmdConfigLoad (int nArgs) { // TODO: CmdConfigRun( gaFileNameConfig ) // TCHAR sFileNameConfig[ MAX_PATH ]; if (! nArgs) { } // gDebugConfigName // DEBUGLOAD file // load debugger setting return UPDATE_ALL; } //=========================================================================== bool ConfigSave_BufferToDisk ( const char *pFileName, ConfigSave_t eConfigSave ) { bool bStatus = false; char sModeCreate[] = "w+t"; char sModeAppend[] = "a+t"; char *pMode = NULL; if (eConfigSave == CONFIG_SAVE_FILE_CREATE) pMode = sModeCreate; else if (eConfigSave == CONFIG_SAVE_FILE_APPEND) pMode = sModeAppend; const std::string sFileName = g_sCurrentDir + pFileName; // TODO: g_sDebugDir FILE *hFile = fopen( pFileName, pMode ); if (hFile) { const int nLine = g_ConfigState.GetNumLines(); for ( int iLine = 0; iLine < nLine; iLine++ ) { const char *pText = g_ConfigState.GetLine( iLine ); if ( pText ) { fputs( pText, hFile ); } } fclose( hFile ); bStatus = true; } return bStatus; } //=========================================================================== void ConfigSave_PrepareHeader ( const Parameters_e eCategory, const Commands_e eCommandClear ) { g_ConfigState.PushLineFormat( "%s %s = %s\n" , g_aTokens[ TOKEN_COMMENT_EOL ].sToken , g_aParameters[ PARAM_CATEGORY ].m_sName , g_aParameters[ eCategory ].m_sName ); g_ConfigState.PushLineFormat( "%s %s\n" , g_aCommands[ eCommandClear ].m_sName , g_aParameters[ PARAM_WILDSTAR ].m_sName ); } // Save Debugger Settings //=========================================================================== Update_t CmdConfigSave (int nArgs) { const std::string sFilename = g_sProgramDir + g_sFileNameConfig; /* HANDLE hFile = CreateFile( sfilename, GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES)NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile != INVALID_HANDLE_VALUE) { void *pSrc; int nLen; DWORD nPut; // FIXME: Should be saving in Text format, not binary! int nVersion = CURRENT_VERSION; pSrc = (void *) &nVersion; nLen = sizeof( nVersion ); WriteFile( hFile, pSrc, nLen, &nPut, NULL ); pSrc = (void *) & g_aColorPalette; nLen = sizeof( g_aColorPalette ); WriteFile( hFile, pSrc, nLen, &nPut, NULL ); pSrc = (void *) & g_aColorIndex; nLen = sizeof( g_aColorIndex ); WriteFile( hFile, pSrc, nLen, &nPut, NULL ); CloseHandle( hFile ); } */ // Bookmarks CmdBookmarkSave( 0 ); // Breakpoints CmdBreakpointSave( 0 ); // Watches CmdWatchSave( 0 ); // Zeropage pointers CmdZeroPageSave( 0 ); // Color Palete // Color Index // CmdColorSave( 0 ); // UserSymbol // History return UPDATE_CONSOLE_DISPLAY; } // Config - Disasm ________________________________________________________________________________ //=========================================================================== Update_t CmdConfigDisasm( int nArgs ) { int iParam = 0; bool bDisplayCurrentSettings = false; // if (! _tcscmp( g_aArgs[ 1 ].sArg, g_aParameters[ PARAM_WILDSTAR ].m_sName )) if (! nArgs) { bDisplayCurrentSettings = true; nArgs = PARAM_CONFIG_NUM; } else { if (nArgs > 2) return Help_Arg_1( CMD_CONFIG_DISASM ); } for (int iArg = 1; iArg <= nArgs; iArg++ ) { if (bDisplayCurrentSettings) iParam = _PARAM_CONFIG_BEGIN + iArg - 1; else if (FindParam( g_aArgs[iArg].sArg, MATCH_FUZZY, iParam )) { } switch (iParam) { case PARAM_CONFIG_BRANCH: if ((nArgs > 1) && (! bDisplayCurrentSettings)) // set { iArg++; g_iConfigDisasmBranchType = g_aArgs[ iArg ].nValue; if (g_iConfigDisasmBranchType < 0) g_iConfigDisasmBranchType = 0; if (g_iConfigDisasmBranchType >= NUM_DISASM_BRANCH_TYPES) g_iConfigDisasmBranchType = NUM_DISASM_BRANCH_TYPES - 1; } else // show current setting { ConsoleBufferPushFormat( "Branch Type: %d", g_iConfigDisasmBranchType ); ConsoleBufferToDisplay(); } break; case PARAM_CONFIG_CLICK: // GH#462 if ((nArgs > 1) && (! bDisplayCurrentSettings)) // set { iArg++; g_bConfigDisasmClick = (g_aArgs[ iArg ].nValue) & 7; // MAGIC NUMBER } // else // Always show current setting -- TODO: Fix remaining disasm to show current setting when set { const char *aClickKey[8] = { "" // 0 ,"Alt " // 1 ,"Ctrl " // 2 ,"Alt+Ctrl " // 3 ,"Shift " // 4 ,"Shift+Alt " // 5 ,"Shift+Ctrl " // 6 ,"Shift+Ctarl+Alt " // 7 }; ConsoleBufferPushFormat( "Click: %d = %sLeft click", g_bConfigDisasmClick, aClickKey[ g_bConfigDisasmClick & 7 ] ); ConsoleBufferToDisplay(); } break; case PARAM_CONFIG_COLON: if ((nArgs > 1) && (! bDisplayCurrentSettings)) // set { iArg++; g_bConfigDisasmAddressColon = (g_aArgs[ iArg ].nValue) ? true : false; } else // show current setting { int iState = g_bConfigDisasmAddressColon ? PARAM_ON : PARAM_OFF; ConsoleBufferPushFormat( "Colon: %s", g_aParameters[ iState ].m_sName ); ConsoleBufferToDisplay(); } break; case PARAM_CONFIG_OPCODE: if ((nArgs > 1) && (! bDisplayCurrentSettings)) // set { iArg++; g_bConfigDisasmOpcodesView = (g_aArgs[ iArg ].nValue) ? true : false; } else { int iState = g_bConfigDisasmOpcodesView ? PARAM_ON : PARAM_OFF; ConsoleBufferPushFormat( "Opcodes: %s", g_aParameters[ iState ].m_sName ); ConsoleBufferToDisplay(); } break; case PARAM_CONFIG_POINTER: if ((nArgs > 1) && (! bDisplayCurrentSettings)) // set { iArg++; g_bConfigInfoTargetPointer = (g_aArgs[ iArg ].nValue) ? true : false; } else { int iState = g_bConfigInfoTargetPointer ? PARAM_ON : PARAM_OFF; ConsoleBufferPushFormat( "Info Target Pointer: %s", g_aParameters[ iState ].m_sName ); ConsoleBufferToDisplay(); } break; case PARAM_CONFIG_SPACES: if ((nArgs > 1) && (! bDisplayCurrentSettings)) // set { iArg++; g_bConfigDisasmOpcodeSpaces = (g_aArgs[ iArg ].nValue) ? true : false; } else { int iState = g_bConfigDisasmOpcodeSpaces ? PARAM_ON : PARAM_OFF; ConsoleBufferPushFormat( "Opcode spaces: %s", g_aParameters[ iState ].m_sName ); ConsoleBufferToDisplay(); } break; case PARAM_CONFIG_TARGET: if ((nArgs > 1) && (! bDisplayCurrentSettings)) // set { iArg++; g_iConfigDisasmTargets = g_aArgs[ iArg ].nValue; if (g_iConfigDisasmTargets < 0) g_iConfigDisasmTargets = 0; if (g_iConfigDisasmTargets >= NUM_DISASM_TARGET_TYPES) g_iConfigDisasmTargets = NUM_DISASM_TARGET_TYPES - 1; } else // show current setting { ConsoleBufferPushFormat( "Target: %d", g_iConfigDisasmTargets ); ConsoleBufferToDisplay(); } break; default: return Help_Arg_1( CMD_CONFIG_DISASM ); // CMD_CONFIG_DISASM_OPCODE ); } // } // else // return Help_Arg_1( CMD_CONFIG_DISASM ); } return UPDATE_CONSOLE_DISPLAY | UPDATE_DISASM; } // Cursor _________________________________________________________________________________________ //=========================================================================== Update_t CmdCursorFollowTarget ( int nArgs ) { WORD nAddress = 0; if (_6502_GetTargetAddress( g_nDisasmCurAddress, nAddress )) { g_nDisasmCurAddress = nAddress; if (CURSOR_ALIGN_CENTER == nArgs) { WindowUpdateDisasmSize(); } else if (CURSOR_ALIGN_TOP == nArgs) { g_nDisasmCurLine = 0; } DisasmCalcTopBotAddress(); } return UPDATE_ALL; } //=========================================================================== Update_t CmdCursorLineDown (int nArgs) { int iOpmode; int nOpbytes; _6502_GetOpmodeOpbyte( g_nDisasmCurAddress, iOpmode, nOpbytes ); // g_nDisasmTopAddress if (g_iWindowThis == WINDOW_DATA) { _CursorMoveDownAligned( WINDOW_DATA_BYTES_PER_LINE ); DisasmCalcTopBotAddress(); } else if (nArgs) // scroll down by 'n' bytes { nOpbytes = nArgs; // HACKL g_aArgs[1].val g_nDisasmTopAddress += nOpbytes; g_nDisasmCurAddress += nOpbytes; g_nDisasmBotAddress += nOpbytes; DisasmCalcTopBotAddress(); } else { #if DEBUG_SCROLL == 6 // Works except on one case: G FB53, SPACE, DOWN WORD nTop = g_nDisasmTopAddress; WORD nCur = g_nDisasmCurAddress + nOpbytes; if (g_bDisasmCurBad) { g_nDisasmCurAddress = nCur; g_bDisasmCurBad = false; DisasmCalcTopFromCurAddress(); return UPDATE_DISASM; } // Adjust Top until nNewCur is at > Cur do { g_nDisasmTopAddress++; DisasmCalcCurFromTopAddress(); } while (g_nDisasmCurAddress < nCur); DisasmCalcCurFromTopAddress(); DisasmCalcBotFromTopAddress(); g_bDisasmCurBad = false; #endif g_nDisasmCurAddress += nOpbytes; _6502_GetOpmodeOpbyte( g_nDisasmTopAddress, iOpmode, nOpbytes ); g_nDisasmTopAddress += nOpbytes; _6502_GetOpmodeOpbyte( g_nDisasmBotAddress, iOpmode, nOpbytes ); g_nDisasmBotAddress += nOpbytes; if (g_bDisasmCurBad) { // MessageBox( NULL, TEXT("Bad Disassembly of opcodes"), TEXT("Debugger"), MB_OK ); // g_nDisasmCurAddress = nCur; // g_bDisasmCurBad = false; // DisasmCalcTopFromCurAddress(); DisasmCalcTopBotAddress(); // return UPDATE_DISASM; } g_bDisasmCurBad = false; } // Can't use use + nBytes due to Disasm Singularity // DisasmCalcTopBotAddress(); return UPDATE_DISASM; } // C++ Bug, can't have local structs used in STL containers struct LookAhead_t { int _nAddress; int _iOpcode; int _iOpmode; int _nOpbytes; }; //=========================================================================== Update_t CmdCursorLineUp (int nArgs) { int nBytes = 1; if (g_iWindowThis == WINDOW_DATA) { _CursorMoveUpAligned( WINDOW_DATA_BYTES_PER_LINE ); } else if (nArgs) { nBytes = nArgs; // HACK: g_aArgs[1].nValue g_nDisasmTopAddress--; DisasmCalcCurFromTopAddress(); DisasmCalcBotFromTopAddress(); } else { // if (! g_nDisasmCurLine) // { // g_nDisasmCurLine = 1; // DisasmCalcTopFromCurAddress( false ); // g_nDisasmCurLine = 0; // DisasmCalcCurFromTopAddress(); // DisasmCalcBotFromTopAddress(); // return UPDATE_DISASM; // } // SmartLineUp() // Figure out if we should move up 1, 2, or 3 bytes since we have 2 possible cases: // // a) Scroll up by 2 bytes // xx-2: A9 yy LDA #xx // xxxx: top // // b) Scroll up by 3 bytes // xx-3: 20 A9 xx JSR $00A9 // xxxx: top of window // #define DEBUG_SCROLL 3 #if DEBUG_SCROLL == 1 WORD nCur = g_nDisasmCurAddress - nBytes; // Adjust Top until nNewCur is at > Cur do { g_nDisasmTopAddress--; DisasmCalcCurFromTopAddress(); } while (g_nDisasmCurAddress > nCur); #endif #if DEBUG_SCROLL == 2 WORD nCur = g_nDisasmCurAddress - nBytes; int iOpcode; int iOpmode; int nOpbytes; int aOpBytes[ 4 ]; // index is relative offset from cursor int nLeastDesiredTopAddress = NO_6502_TARGET; do { g_nDisasmTopAddress--; // _6502_GetOpcodeOpmodeOpbyte( iOpcode, iOpmode, nOpbytes ); iOpcode = _6502_GetOpmodeOpbyte( g_nDisasmTopAddress, iOpmode, nOpbytes ); aOpBytes[ 1 ] = nOpbytes; // Disasm is kept in sync. Maybe bad opcode, but if no other choices... if (nOpbytes == 1) nLeastDesiredTopAddress = g_nDisasmTopAddress; if ( (iOpmode == AM_1) || (iOpmode == AM_2) || (iOpmode == AM_3) || ! _6502_IsOpcodeValid( iOpcode) || (nOpbytes != 1) ) { g_nDisasmTopAddress--; DisasmCalcCurFromTopAddress(); iOpcode = _6502_GetOpmodeOpbyte( g_nDisasmTopAddress, iOpmode, nOpbytes ); aOpBytes[ 2 ] = nOpbytes; if ( (iOpmode == AM_1) || (iOpmode == AM_2) || (iOpmode == AM_3) || ! _6502_IsOpcodeValid( iOpcode) || (nOpbytes != 2) ) { g_nDisasmTopAddress--; DisasmCalcCurFromTopAddress(); iOpcode = _6502_GetOpmodeOpbyte( g_nDisasmTopAddress, iOpmode, nOpbytes ); aOpBytes[ 3 ] = nOpbytes; if ( (iOpmode == AM_1) || (iOpmode == AM_2) || (iOpmode == AM_3) || (nOpbytes != 3) ) g_nDisasmTopAddress--; DisasmCalcCurFromTopAddress(); } } DisasmCalcCurFromTopAddress(); } while (g_nDisasmCurAddress > nCur); #endif #if DEBUG_SCROLL == 3 // Isn't this the new DisasmCalcTopFromCurAddress() ?? int iOpcode; int iOpmode; int nOpbytes; const int MAX_LOOK_AHEAD = g_nDisasmWinHeight; static std::vector aTopCandidates; LookAhead_t tCandidate; aTopCandidates.clear(); aTopCandidates.reserve( MAX_LOOK_AHEAD ); WORD nTop = g_nDisasmTopAddress; WORD iTop = 0; WORD nCur = 0; do { nTop--; nCur = nTop; iTop = (g_nDisasmTopAddress - nTop); for (int iLine = 0; iLine < MAX_LOOK_AHEAD; iLine++ ) { iOpcode = _6502_GetOpmodeOpbyte( nCur, iOpmode, nOpbytes ); // If address on iLine = g_nDisasmCurLine + 1 if (iLine == (g_nDisasmCurLine + 1)) { if (nCur == (g_nDisasmCurAddress)) { iOpcode = _6502_GetOpmodeOpbyte( nTop, iOpmode, nOpbytes ); tCandidate._nAddress = nTop; tCandidate._iOpcode = iOpcode; tCandidate._iOpmode = iOpmode; tCandidate._nOpbytes = nOpbytes; aTopCandidates.push_back( tCandidate ); } } nCur += nOpbytes; if (nCur > g_nDisasmCurAddress) break; } } while (iTop < MAX_LOOK_AHEAD); int nCandidates = aTopCandidates.size(); if (nCandidates) { int iBest = NO_6502_TARGET; int iCandidate = 0; for ( ; iCandidate < nCandidates; iCandidate++ ) { tCandidate = aTopCandidates.at( iCandidate ); iOpcode = tCandidate._iOpcode; iOpmode = tCandidate._iOpmode; if ( (iOpmode != AM_1) && (iOpmode != AM_2) && (iOpmode != AM_3) && _6502_IsOpcodeValid( iOpcode ) ) { if (g_iConfigDisasmScroll == 1) { // Favor min opbytes if (iBest != NO_6502_TARGET) iBest = iCandidate; } else if (g_iConfigDisasmScroll == 3) { // Favor max opbytes iBest = iCandidate; } } } // All were "invalid", pick first choice if (iBest == NO_6502_TARGET) iBest = 0; tCandidate = aTopCandidates.at( iBest ); g_nDisasmTopAddress = tCandidate._nAddress; DisasmCalcCurFromTopAddress(); DisasmCalcBotFromTopAddress(); g_bDisasmCurBad = false; } else { // Singularity g_bDisasmCurBad = true; // g_nDisasmTopAddress--; g_nDisasmCurAddress--; // g_nDisasmBotAddress--; DisasmCalcTopBotAddress(); } #endif } return UPDATE_DISASM; } //=========================================================================== Update_t CmdCursorJumpPC (int nArgs) { // TODO: Allow user to decide if they want next g_aOpcodes at // 1) Centered (traditionaly), or // 2) Top of the screen // if (UserPrefs.bNextInstructionCentered) if (CURSOR_ALIGN_CENTER == nArgs) { g_nDisasmCurAddress = regs.pc; // (2) WindowUpdateDisasmSize(); // calc cur line } else if (CURSOR_ALIGN_TOP == nArgs) { g_nDisasmCurAddress = regs.pc; // (2) g_nDisasmCurLine = 0; } DisasmCalcTopBotAddress(); return UPDATE_ALL; } //=========================================================================== Update_t CmdCursorJumpRetAddr (int nArgs) { WORD nAddress = 0; if (_6502_GetStackReturnAddress( nAddress )) { g_nDisasmCurAddress = nAddress; if (CURSOR_ALIGN_CENTER == nArgs) { WindowUpdateDisasmSize(); } else if (CURSOR_ALIGN_TOP == nArgs) { g_nDisasmCurLine = 0; } DisasmCalcTopBotAddress(); } return UPDATE_ALL; } //=========================================================================== Update_t CmdCursorRunUntil (int nArgs) { nArgs = _Arg_1( g_nDisasmCurAddress ); return CmdGo( nArgs, true ); } // nDelta must be a power of 2 //=========================================================================== void _CursorMoveDownAligned( int nDelta ) { if (g_iWindowThis == WINDOW_DATA) { if (g_aMemDump[0].eDevice == DEV_MEMORY) { g_aMemDump[0].nAddress += nDelta; g_aMemDump[0].nAddress &= _6502_MEM_END; } } else { int nNewAddress = g_nDisasmTopAddress; // BUGFIX: g_nDisasmCurAddress; if ((nNewAddress & (nDelta-1)) == 0) nNewAddress += nDelta; else nNewAddress += (nDelta - (nNewAddress & (nDelta-1))); // .22 Fixed: Shift-PageUp Shift-PageDown Ctrl-PageUp Ctrl-PageDown -> _CursorMoveUpAligned() & _CursorMoveDownAligned() g_nDisasmTopAddress = nNewAddress & _6502_MEM_END; // .21 Fixed: _CursorMoveUpAligned() & _CursorMoveDownAligned() not wrapping around past FF00 to 0, and wrapping around past 0 to FF00 } } // nDelta must be a power of 2 //=========================================================================== void _CursorMoveUpAligned( int nDelta ) { if (g_iWindowThis == WINDOW_DATA) { if (g_aMemDump[0].eDevice == DEV_MEMORY) { g_aMemDump[0].nAddress -= nDelta; g_aMemDump[0].nAddress &= _6502_MEM_END; } } else { int nNewAddress = g_nDisasmTopAddress; // BUGFIX: g_nDisasmCurAddress; if ((nNewAddress & (nDelta-1)) == 0) nNewAddress -= nDelta; else nNewAddress -= (nNewAddress & (nDelta-1)); // .22 Fixed: Shift-PageUp Shift-PageDown Ctrl-PageUp Ctrl-PageDown -> _CursorMoveUpAligned() & _CursorMoveDownAligned() g_nDisasmTopAddress = nNewAddress & _6502_MEM_END; // .21 Fixed: _CursorMoveUpAligned() & _CursorMoveDownAligned() not wrapping around past FF00 to 0, and wrapping around past 0 to FF00 } } //=========================================================================== Update_t CmdCursorPageDown (int nArgs) { int iLines = 0; // show at least 1 line from previous display int nLines = WindowGetHeight( g_iWindowThis ); if (nLines < 2) nLines = 2; if (g_iWindowThis == WINDOW_DATA) { const int nStep = 128; _CursorMoveDownAligned( nStep ); } else { // 4 // while (++iLines < nLines) // CmdCursorLineDown(nArgs); // 5 nLines -= (g_nDisasmCurLine + 1); if (nLines < 1) nLines = 1; while (iLines++ < nLines) { CmdCursorLineDown( 0 ); // nArgs } // 6 } return UPDATE_DISASM; } //=========================================================================== Update_t CmdCursorPageDown256 (int nArgs) { const int nStep = 256; _CursorMoveDownAligned( nStep ); return UPDATE_DISASM; } //=========================================================================== Update_t CmdCursorPageDown4K (int nArgs) { const int nStep = 4096; _CursorMoveDownAligned( nStep ); return UPDATE_DISASM; } //=========================================================================== Update_t CmdCursorPageUp (int nArgs) { int iLines = 0; // show at least 1 line from previous display int nLines = WindowGetHeight( g_iWindowThis ); if (nLines < 2) nLines = 2; if (g_iWindowThis == WINDOW_DATA) { const int nStep = 128; _CursorMoveUpAligned( nStep ); } else { // while (++iLines < nLines) // CmdCursorLineUp(nArgs); nLines -= (g_nDisasmCurLine + 1); if (nLines < 1) nLines = 1; while (iLines++ < nLines) { CmdCursorLineUp( 0 ); // smart line up // CmdCursorLineUp( -nLines ); } } return UPDATE_DISASM; } //=========================================================================== Update_t CmdCursorPageUp256 (int nArgs) { const int nStep = 256; _CursorMoveUpAligned( nStep ); return UPDATE_DISASM; } //=========================================================================== Update_t CmdCursorPageUp4K (int nArgs) { const int nStep = 4096; _CursorMoveUpAligned( nStep ); return UPDATE_DISASM; } //=========================================================================== Update_t CmdCursorSetPC(int) { regs.pc = g_nDisasmCurAddress; // set PC to current cursor address return UPDATE_DISASM; } // Flags __________________________________________________________________________________________ //=========================================================================== Update_t CmdFlagClear (int nArgs) { int iFlag = (g_iCommand - CMD_FLAG_CLR_C); if (g_iCommand == CMD_FLAG_CLEAR) // Undocumented: "cl f f ... f", eg: "se n v c" (TODO: Conflicts with monitor command #L -> 000CL) { int iArg = nArgs; while (iArg) { iFlag = 0; while (iFlag < _6502_NUM_FLAGS) { // if (g_aFlagNames[iFlag] == g_aArgs[iArg].sArg[0]) if (g_aBreakpointSource[ BP_SRC_FLAG_N - iFlag ][0] == toupper(g_aArgs[iArg].sArg[0])) { regs.ps &= ~(1 << (7-iFlag)); break; } iFlag++; } iArg--; } } else { regs.ps &= ~(1 << iFlag); } return UPDATE_FLAGS; // 1; } //=========================================================================== Update_t CmdFlagSet (int nArgs) { int iFlag = (g_iCommand - CMD_FLAG_SET_C); if (g_iCommand == CMD_FLAG_SET) // Undocumented: "se f f ... f", eg: "se n v c" { int iArg = nArgs; while (iArg) { iFlag = 0; while (iFlag < _6502_NUM_FLAGS) { // if (g_aFlagNames[iFlag] == g_aArgs[iArg].sArg[0]) if (g_aBreakpointSource[ BP_SRC_FLAG_N - iFlag ][0] == toupper(g_aArgs[iArg].sArg[0])) { regs.ps |= (1 << (7-iFlag)); break; } iFlag++; } iArg--; } } else { regs.ps |= (1 << iFlag); } return UPDATE_FLAGS; // 1; } //=========================================================================== Update_t CmdFlag (int nArgs) { // if (g_aArgs[0].sArg[0] == g_aParameters[PARAM_FLAG_CLEAR].aName[0] ) // TEXT('R') if (g_iCommand == CMD_FLAG_CLEAR) return CmdFlagClear( nArgs ); else if (g_iCommand == CMD_FLAG_SET) // if (g_aArgs[0].sArg[0] == g_aParameters[PARAM_FLAG_SET].aName[0] ) // TEXT('S') return CmdFlagSet( nArgs ); return UPDATE_ALL; // 0; } // Disk ___________________________________________________________________________________________ // Usage: // DISK SLOT [#] // Show [or set] the current slot of the Disk II I/F card (for all other cmds to act on) // DISK INFO // Info for current drive // DISK # EJECT // Unmount disk // DISK # PROTECT # // Write-protect disk on/off // DISK # "" // Mount filename as floppy disk // TODO: // DISK # READ // Read Track/Sector(s) // DISK # READ Addr:Addr // Read Track/Sector(s) // DISK # WRITE Addr:Addr // Write Track/Sector(s) // Examples: // DISK INFO Update_t CmdDisk (int nArgs) { static UINT currentSlot = SLOT6; if (! nArgs) return HelpLastCommand(); // check for info or slot command int iParam = 0; FindParam(g_aArgs[1].sArg, MATCH_EXACT, iParam, _PARAM_DISK_BEGIN, _PARAM_DISK_END); if (iParam == PARAM_DISK_SET_SLOT) { if (nArgs > 2) return HelpLastCommand(); if (nArgs > 1) { UINT slot = g_aArgs[2].nValue; if (slot < SLOT1 || slot > SLOT7) return HelpLastCommand(); currentSlot = slot; } ConsoleBufferPushFormat("Current Disk II slot = %d", currentSlot); return ConsoleUpdate(); } if (GetCardMgr().QuerySlot(currentSlot) != CT_Disk2) return ConsoleDisplayErrorFormat("No Disk II card in slot-%d", currentSlot); Disk2InterfaceCard& diskCard = dynamic_cast(GetCardMgr().GetRef(currentSlot)); if (iParam == PARAM_DISK_INFO) { if (nArgs > 2) return HelpLastCommand(); ConsoleBufferPushFormat("FW%2d: D%d at T$%s, phase $%s, bitOffset $%04X, extraCycles %.2f, %s", diskCard.GetCurrentFirmware(), diskCard.GetCurrentDrive() + 1, diskCard.GetCurrentTrackString().c_str(), diskCard.GetCurrentPhaseString().c_str(), diskCard.GetCurrentBitOffset(), diskCard.GetCurrentExtraCycles(), diskCard.GetCurrentState() ); return ConsoleUpdate(); } if (nArgs < 2) return HelpLastCommand(); // first param should be drive int iDrive = g_aArgs[ 1 ].nValue; if ((iDrive < 1) || (iDrive > 2)) return HelpLastCommand(); iDrive--; // second param is command int nFound = FindParam( g_aArgs[ 2 ].sArg, MATCH_EXACT, iParam, _PARAM_DISK_BEGIN, _PARAM_DISK_END ); if (! nFound) return HelpLastCommand(); if (iParam == PARAM_DISK_EJECT) { if (nArgs > 2) return HelpLastCommand(); diskCard.EjectDisk( iDrive ); GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS); } else if (iParam == PARAM_DISK_PROTECT) { if (nArgs > 3) return HelpLastCommand(); bool bProtect = true; if (nArgs == 3) bProtect = g_aArgs[ 3 ].nValue ? true : false; diskCard.SetProtect( iDrive, bProtect ); GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS); } else { if (nArgs != 3) return HelpLastCommand(); LPCTSTR pDiskName = g_aArgs[ 3 ].sArg; // DISK # "Diskname" diskCard.InsertDisk( iDrive, pDiskName, IMAGE_FORCE_WRITE_PROTECTED, IMAGE_DONT_CREATE ); GetFrame().FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS); } return UPDATE_CONSOLE_DISPLAY; } // Memory _________________________________________________________________________________________ // TO DO: // . Add support for dumping Disk][ device //=========================================================================== bool MemoryDumpCheck (int nArgs, WORD * pAddress_ ) { if (! nArgs) return false; Arg_t *pArg = &g_aArgs[1]; WORD nAddress = pArg->nValue; bool bUpdate = false; pArg->eDevice = DEV_MEMORY; // Default if (strncmp(g_aArgs[1].sArg, "MB", 2) == 0) // Mockingboard sub-unit (6522+AY8913): "MBs" or "MBsn" { UINT slot = (UINT)-1; UINT subUnit = 0; // Default to 6522-A if (strlen(g_aArgs[1].sArg) >= 3) // "MBs" where s = slot# slot = g_aArgs[1].sArg[2] - '0'; if (strlen(g_aArgs[1].sArg) == 4) // "MBsn" where s = slot#, n = SY6522 A or B eg. AY4A subUnit = g_aArgs[1].sArg[3] - 'A'; if (slot <= 7 && subUnit <= 1) { nAddress = (slot << 4) | subUnit; // slot=[0..7] | subUnit=[0..1] pArg->eDevice = DEV_MB_SUBUNIT; bUpdate = true; } } else if (strncmp(g_aArgs[1].sArg, "AY", 2) == 0) // AY8913: "AYs" or "AYsn" { UINT slot = (UINT)-1; UINT subUnit = 0; // Default to 6522-A if (strlen(g_aArgs[1].sArg) >= 3) // "AYs" where s = slot# slot = g_aArgs[1].sArg[2] - '0'; if (strlen(g_aArgs[1].sArg) == 4) // "AYsn" where s = slot#, n = SY6522 A or B eg. AY4A subUnit = g_aArgs[1].sArg[3] - 'A'; if (slot <= 7 && subUnit <= 1) { nAddress = (slot << 4) | subUnit; // slot=[0..7] | subUnit=[0..1] pArg->eDevice = DEV_AY8913_PAIR; // for Phasor bUpdate = true; } } #ifdef SUPPORT_Z80_EMU else if (strcmp(g_aArgs[1].sArg, "*AF") == 0) { nAddress = *(WORD*)(mem + REG_AF); bUpdate = true; } else if (strcmp(g_aArgs[1].sArg, "*BC") == 0) { nAddress = *(WORD*)(mem + REG_BC); bUpdate = true; } else if (strcmp(g_aArgs[1].sArg, "*DE") == 0) { nAddress = *(WORD*)(mem + REG_DE); bUpdate = true; } else if (strcmp(g_aArgs[1].sArg, "*HL") == 0) { nAddress = *(WORD*)(mem + REG_HL); bUpdate = true; } else if (strcmp(g_aArgs[1].sArg, "*IX") == 0) { nAddress = *(WORD*)(mem + REG_IX); bUpdate = true; } #endif if (bUpdate) { pArg->nValue = nAddress; strncpy_s( pArg->sArg, WordToHexStr(nAddress).c_str(), _TRUNCATE ); } if (pAddress_) { *pAddress_ = nAddress; } return true; } //=========================================================================== Update_t CmdMemoryCompare (int nArgs ) { if (nArgs < 3) return Help_Arg_1( CMD_MEMORY_COMPARE ); WORD nSrcAddr = g_aArgs[1].nValue; WORD nDstAddr = g_aArgs[3].nValue; WORD nSrcSymAddr; WORD nDstSymAddr; if (!nSrcAddr) { nSrcSymAddr = GetAddressFromSymbol( g_aArgs[1].sArg ); if (nSrcAddr != nSrcSymAddr) nSrcAddr = nSrcSymAddr; } if (!nDstAddr) { nDstSymAddr = GetAddressFromSymbol( g_aArgs[3].sArg ); if (nDstAddr != nDstSymAddr) nDstAddr = nDstSymAddr; } // if ((!nSrcAddr) || (!nDstAddr)) // return Help_Arg_1( CMD_MEMORY_COMPARE ); return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== static Update_t _CmdMemoryDump (int nArgs, int iWhich, int iView ) { WORD nAddress = 0; if ( ! MemoryDumpCheck(nArgs, & nAddress ) ) { return Help_Arg_1( g_iCommand ); } g_aMemDump[iWhich].nAddress = nAddress; g_aMemDump[iWhich].eDevice = g_aArgs[1].eDevice; g_aMemDump[iWhich].bActive = true; g_aMemDump[iWhich].eView = (MemoryView_e) iView; return UPDATE_MEM_DUMP; // TODO: This really needed? Don't think we do any actual ouput } //=========================================================================== bool _MemoryCheckMiniDump ( int iWhich ) { if ((iWhich < 0) || (iWhich > NUM_MEM_MINI_DUMPS)) { ConsoleDisplayErrorFormat( " Only %d memory mini dumps", NUM_MEM_MINI_DUMPS ); return true; } return false; } //=========================================================================== Update_t CmdMemoryMiniDumpHex (int nArgs) { int iWhich = g_iCommand - CMD_MEM_MINI_DUMP_HEX_1; if (_MemoryCheckMiniDump( iWhich )) return UPDATE_CONSOLE_DISPLAY; return _CmdMemoryDump(nArgs, iWhich, MEM_VIEW_HEX ); } //=========================================================================== Update_t CmdMemoryMiniDumpAscii (int nArgs) { int iWhich = g_iCommand - CMD_MEM_MINI_DUMP_ASCII_1; if (_MemoryCheckMiniDump( iWhich )) return UPDATE_CONSOLE_DISPLAY; return _CmdMemoryDump(nArgs, iWhich, MEM_VIEW_ASCII ); } //=========================================================================== Update_t CmdMemoryMiniDumpApple (int nArgs) { int iWhich = g_iCommand - CMD_MEM_MINI_DUMP_APPLE_1; if (_MemoryCheckMiniDump( iWhich )) return UPDATE_CONSOLE_DISPLAY; return _CmdMemoryDump(nArgs, iWhich, MEM_VIEW_APPLE ); // MEM_VIEW_TXT_LO ); } //=========================================================================== //Update_t CmdMemoryMiniDumpLow (int nArgs) //{ // int iWhich = g_iCommand - CMD_MEM_MINI_DUMP_TXT_LO_1; // if (_MemoryCheckMiniDump( iWhich )) // return UPDATE_CONSOLE_DISPLAY; // // return _CmdMemoryDump(nArgs, iWhich, MEM_VIEW_APPLE ); // MEM_VIEW_TXT_LO ); //} //=========================================================================== //Update_t CmdMemoryMiniDumpHigh (int nArgs) //{ // int iWhich = g_iCommand - CMD_MEM_MINI_DUMP_TXT_HI_1; // if (_MemoryCheckMiniDump( iWhich )) // return UPDATE_CONSOLE_DISPLAY; // // return _CmdMemoryDump(nArgs, iWhich, MEM_VIEW_APPLE ); // MEM_VIEW_TXT_HI ); //} //=========================================================================== Update_t CmdMemoryEdit (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } // MEB addr 8_bit_value //=========================================================================== Update_t CmdMemoryEnterByte (int nArgs) { if ((nArgs < 2) || ((g_aArgs[2].sArg[0] != TEXT('0')) && (!g_aArgs[2].nValue))) // arg2 not numeric or not specified { Help_Arg_1( CMD_MEMORY_ENTER_WORD ); } WORD nAddress = g_aArgs[1].nValue; while (nArgs >= 2) { WORD nData = g_aArgs[nArgs].nValue; if ( nData > 0xFF) { *(mem + nAddress + nArgs - 2) = (BYTE)(nData >> 0); *(mem + nAddress + nArgs - 1) = (BYTE)(nData >> 8); } else { *(mem + nAddress+nArgs-2) = (BYTE)nData; } *(memdirty+(nAddress >> 8)) = 1; nArgs--; } return UPDATE_ALL; } // MEW addr 16-bit_vaue //=========================================================================== Update_t CmdMemoryEnterWord (int nArgs) { if ((nArgs < 2) || ((g_aArgs[2].sArg[0] != TEXT('0')) && (!g_aArgs[2].nValue))) // arg2 not numeric or not specified { Help_Arg_1( CMD_MEMORY_ENTER_WORD ); } WORD nAddress = g_aArgs[1].nValue; while (nArgs >= 2) { WORD nData = g_aArgs[nArgs].nValue; // Little Endian *(mem + nAddress + nArgs - 2) = (BYTE)(nData >> 0); *(mem + nAddress + nArgs - 1) = (BYTE)(nData >> 8); *(memdirty+(nAddress >> 8)) |= 1; nArgs--; } return UPDATE_ALL; } //=========================================================================== void MemMarkDirty( WORD nAddressStart, WORD nAddressEnd ) { for ( int iPage = (nAddressStart >> 8); iPage <= (nAddressEnd >> 8); iPage++ ) { *(memdirty+iPage) = 1; } } //=========================================================================== Update_t CmdMemoryFill (int nArgs) { // F address end value // F address,len value // F address:end value if ((!nArgs) || (nArgs < 3) || (nArgs > 4)) return Help_Arg_1( CMD_MEMORY_FILL ); WORD nAddress2 = 0; WORD nAddressStart = 0; WORD nAddressEnd = 0; int nAddressLen = 0; BYTE nValue = 0; if ( nArgs == 3) { nAddressStart = g_aArgs[1].nValue; nAddressEnd = g_aArgs[2].nValue; nAddressLen = MIN(_6502_MEM_END , nAddressEnd - nAddressStart + 1 ); } else { RangeType_t eRange; eRange = Range_Get( nAddressStart, nAddress2, 1 ); if (! Range_CalcEndLen( eRange, nAddressStart, nAddress2, nAddressEnd, nAddressLen )) return Help_Arg_1( CMD_MEMORY_MOVE ); } #if DEBUG_VAL_2 nBytes = MAX(1,g_aArgs[1].nVal2); // TODO: This actually work?? #endif if ((nAddressLen > 0) && (nAddressEnd <= _6502_MEM_END)) { MemMarkDirty( nAddressStart, nAddressEnd ); nValue = g_aArgs[nArgs].nValue & 0xFF; while ( nAddressLen-- ) // v2.7.0.22 { // TODO: Optimize - split into pre_io, and post_io if ((nAddress2 < _6502_IO_BEGIN) || (nAddress2 > _6502_IO_END)) { *(mem + nAddressStart) = nValue; } nAddressStart++; } } return UPDATE_ALL; // UPDATE_CONSOLE_DISPLAY; } static std::string g_sMemoryLoadSaveFileName; // "PWD" //=========================================================================== Update_t CmdConfigGetDebugDir (int nArgs) { if ( nArgs != 0 ) return Help_Arg_1( CMD_CONFIG_GET_DEBUG_DIR ); // TODO: debugger dir has no ` CONSOLE_COLOR_ESCAPE_CHAR ?!?! ConsoleBufferPushFormat( "Path: %s", g_sCurrentDir.c_str() ); return ConsoleUpdate(); } // Usage: // CD "" // CD ".." // Note: Subdirectory MUST be quoted with double quotes. //=========================================================================== Update_t CmdConfigSetDebugDir (int nArgs) { if ( nArgs > 1 ) return Help_Arg_1( CMD_CONFIG_SET_DEBUG_DIR ); if ( nArgs == 0 ) return CmdConfigGetDebugDir(0); // http://msdn.microsoft.com/en-us/library/aa365530(VS.85).aspx // TODO: Support paths prefixed with "\\?\" (for long/unicode pathnames) if (strncmp("\\\\?\\", g_aArgs[1].sArg, 4) == 0) return Help_Arg_1( CMD_CONFIG_SET_DEBUG_DIR ); std::string sPath; if (g_aArgs[1].sArg[1] == ':') // Absolute { sPath = g_aArgs[1].sArg; } else if (g_aArgs[1].sArg[0] == PATH_SEPARATOR) // Absolute { if (g_sCurrentDir[1] == ':') { sPath = g_sCurrentDir.substr(0, 2) + g_aArgs[1].sArg; // Prefix with drive letter & colon } else { sPath = g_aArgs[1].sArg; } } else // Relative { std::string SAME_DIR( "." ); SAME_DIR += PATH_SEPARATOR; std::string UP_DIR ( ".."); UP_DIR += PATH_SEPARATOR; std::string sNewPath( g_aArgs[1].sArg ); // if new path doesn't have a trailing slash, append one if (*(sNewPath.rbegin()) != PATH_SEPARATOR) sNewPath += PATH_SEPARATOR; // Support ".." and various permutations // cd "..\" // cd "abc\..\def\" // // 1. find next slash in newpath // 2. subdir = newpath.substr() // 3. if subdir == "..\" // reverse find slash in g_sCurrentDir // g_sCurrentDir = g_sCurrentDir.substr() // else // g_sCurrentDir += subdir size_t iPrevSeparator = 0; size_t iPathSeparator = 0; while ((iPathSeparator = sNewPath.find( PATH_SEPARATOR, iPrevSeparator )) != std::string::npos) { #if _DEBUG LogOutput( "Prev: %" SIZE_T_FMT "\n", iPrevSeparator ); LogOutput( "Next: %" SIZE_T_FMT "\n", iPathSeparator ); LogOutput( "%s\n", sNewPath.c_str() ); LogOutput( "%*s%s\n", int(iPathSeparator), "", "^" ); #endif std::string sSubDir = sNewPath.substr( iPrevSeparator, iPathSeparator - iPrevSeparator + 1 ); const size_t nSubDirLen = sSubDir.size(); if ((nSubDirLen == 2) && (sSubDir == SAME_DIR)) // Same directory ".\" in the subpath? { // Intentional: Nothing to do } else if ((nSubDirLen == 3) && (sSubDir == UP_DIR)) // Up directory "..\" in the subpath? { size_t nCurrentLen = g_sCurrentDir.size(); size_t nLastSeparator = g_sCurrentDir.rfind( '\\', nCurrentLen - 2 ); if (nLastSeparator != std::string::npos) { #if _DEBUG LogOutput( "Last: %" SIZE_T_FMT "\n", nLastSeparator ); LogOutput( "%s\n", g_sCurrentDir.c_str() ); LogOutput( "%*s%s\n", int(nLastSeparator), "", "^" ); #endif std::string sCurrentDir = g_sCurrentDir.substr( 0, nLastSeparator + 1 ); // Path always has trailing slash so include it g_sCurrentDir = sCurrentDir; } } else g_sCurrentDir += sSubDir; iPathSeparator++; // start next search past path separator iPrevSeparator = iPathSeparator; } // TODO: debugger dir has no ` CONSOLE_COLOR_ESCAPE_CHAR ?!?! sPath = g_sCurrentDir; } if ( SetCurrentImageDir( sPath ) ) nArgs = 0; // intentional fall into return CmdConfigGetDebugDir(0); // Show the new PWD } //=========================================================================== #if 0 // Original Update_t CmdMemoryLoad (int nArgs) { // BLOAD ["Filename"] , addr[, len] // BLOAD ["Filename"] , addr[: end] // 1 2 3 4 5 if (nArgs > 5) return Help_Arg_1( CMD_MEMORY_LOAD ); bool bHaveFileName = false; int iArgAddress = 3; if (g_aArgs[1].bType & TYPE_QUOTED_2) bHaveFileName = true; // if (g_aArgs[2].bType & TOKEN_QUOTE_DOUBLE) // bHaveFileName = true; if (nArgs > 1) { if (g_aArgs[1].bType & TYPE_QUOTED_2) bHaveFileName = true; int iArgComma1 = 2; int iArgAddress = 3; int iArgComma2 = 4; int iArgLength = 5; if (! bHaveFileName) { iArgComma1 = 1; iArgAddress = 2; iArgComma2 = 3; iArgLength = 4; if (nArgs > 4) return Help_Arg_1( CMD_MEMORY_LOAD ); } if (g_aArgs[ iArgComma1 ].eToken != TOKEN_COMMA) return Help_Arg_1( CMD_MEMORY_SAVE ); std::string sLoadSaveFilePath = g_sCurrentDir; // TODO: g_sDebugDir WORD nAddressStart; WORD nAddress2 = 0; WORD nAddressEnd = 0; int nAddressLen = 0; RangeType_t eRange; eRange = Range_Get( nAddressStart, nAddress2, iArgAddress ); if (nArgs > 4) { if (eRange == RANGE_MISSING_ARG_2) { return Help_Arg_1( CMD_MEMORY_LOAD ); } // if (eRange == RANGE_MISSING_ARG_2) if (! Range_CalcEndLen( eRange, nAddressStart, nAddress2, nAddressEnd, nAddressLen )) { return Help_Arg_1( CMD_MEMORY_SAVE ); } } BYTE *pMemory = new BYTE [ _6502_MEM_END + 1 ]; // default 64K buffer BYTE *pDst = mem + nAddressStart; BYTE *pSrc = pMemory; if (bHaveFileName) { g_sMemoryLoadSaveFileName = g_aArgs[ 1 ].sArg; } sLoadSaveFilePath += g_sMemoryLoadSaveFileName; FILE *hFile = fopen( sLoadSaveFilePath.c_str(), "rb" ); if (hFile) { int nFileBytes = _GetFileSize( hFile ); if (nFileBytes > _6502_MEM_END) nFileBytes = _6502_MEM_END + 1; // Bank-switched RAMR/ROM is only 16-bit // Caller didnt' specify how many bytes to read, default to them all if (nAddressLen == 0) { nAddressLen = nFileBytes; } size_t nRead = fread( pMemory, nAddressLen, 1, hFile ); if (nRead == 1) // (size_t)nLen) { for ( int iByte = 0; iByte < nAddressLen; iByte++ ) { *pDst++ = *pSrc++; } ConsoleBufferPush( TEXT( "Loaded." ) ); } fclose( hFile ); } else { ConsoleBufferPush( TEXT( "ERROR: Bad filename" ) ); CmdConfigGetDebugDir( 0 ); ConsoleBufferPushFormat( "File: %s", g_sMemoryLoadSaveFileName.c_str() ); } delete [] pMemory; } else return Help_Arg_1( CMD_MEMORY_LOAD ); return ConsoleUpdate(); } #else // Extended cmd for loading physical memory Update_t CmdMemoryLoad (int nArgs) { // Active memory: // BLOAD ["Filename"] , addr[, len] // BLOAD ["Filename"] , addr[: end] // 1 2 3 4 5 // Physical 64K memory bank: // BLOAD ["Filename"] , bank : addr [, len] // BLOAD ["Filename"] , bank : addr [: end] // 1 2 3 4 5 6 7 if (nArgs > 7) return Help_Arg_1( CMD_MEMORY_LOAD ); if (nArgs < 1) return Help_Arg_1( CMD_MEMORY_LOAD ); bool bHaveFileName = false; if (g_aArgs[1].bType & TYPE_QUOTED_2) bHaveFileName = true; // if (g_aArgs[2].bType & TOKEN_QUOTE_DOUBLE) // bHaveFileName = true; int iArgComma1 = 2; int iArgAddress = 3; int iArgComma2 = 4; int iArgLength = 5; int iArgBank = 3; int iArgColon = 4; int nBank = 0; bool bBankSpecified = false; if (! bHaveFileName) { iArgComma1 = 1; iArgAddress = 2; iArgComma2 = 3; iArgLength = 4; iArgBank = 2; iArgColon = 3; if (nArgs > 6) return Help_Arg_1( CMD_MEMORY_LOAD ); } if (nArgs >= 5) { if (!(g_aArgs[iArgBank].bType & TYPE_ADDRESS && g_aArgs[iArgColon].eToken == TOKEN_COLON)) return Help_Arg_1( CMD_MEMORY_LOAD ); nBank = g_aArgs[iArgBank].nValue; bBankSpecified = true; iArgAddress += 2; iArgComma2 += 2; iArgLength += 2; } else { bBankSpecified = false; } struct KnownFileType_t { const char *pExtension; int nAddress; int nLength; }; const KnownFileType_t aFileTypes[] = { { "" , 0, 0 } // n/a ,{ ".hgr" , 0x2000, 0x2000 } ,{ ".hgr2", 0x4000, 0x2000 } // TODO: extension ".dhgr", ".dhgr2" }; const int nFileTypes = sizeof( aFileTypes ) / sizeof( KnownFileType_t ); const KnownFileType_t *pFileType = NULL; const char *pFileName = g_aArgs[ 1 ].sArg; int nLen = strlen( pFileName ); const char *pEnd = pFileName + nLen - 1; while ( pEnd > pFileName ) { if ( *pEnd == '.' ) { for ( int i = 1; i < nFileTypes; i++ ) { if ( strcmp( pEnd, aFileTypes[i].pExtension ) == 0 ) { pFileType = &aFileTypes[i]; break; } } } if ( pFileType ) break; pEnd--; } if ( !pFileType ) if (g_aArgs[ iArgComma1 ].eToken != TOKEN_COMMA) return Help_Arg_1( CMD_MEMORY_LOAD ); WORD nAddressStart = 0; WORD nAddress2 = 0; WORD nAddressEnd = 0; int nAddressLen = 0; if ( pFileType ) { nAddressStart = pFileType->nAddress; nAddressLen = pFileType->nLength; nAddressEnd = pFileType->nLength + nAddressLen; } RangeType_t eRange = RANGE_MISSING_ARG_2; if (g_aArgs[ iArgComma1 ].eToken == TOKEN_COMMA) eRange = Range_Get( nAddressStart, nAddress2, iArgAddress ); if ( nArgs > iArgComma2 ) { if (eRange == RANGE_MISSING_ARG_2) { return Help_Arg_1( CMD_MEMORY_LOAD ); } // if (eRange == RANGE_MISSING_ARG_2) if (! Range_CalcEndLen( eRange, nAddressStart, nAddress2, nAddressEnd, nAddressLen )) { return Help_Arg_1( CMD_MEMORY_LOAD ); } } if (bHaveFileName) { g_sMemoryLoadSaveFileName = pFileName; } const std::string sLoadSaveFilePath = g_sCurrentDir + g_sMemoryLoadSaveFileName; // TODO: g_sDebugDir BYTE * const pMemBankBase = bBankSpecified ? MemGetBankPtr(nBank) : mem; if (!pMemBankBase) { ConsoleBufferPush( TEXT( "Error: Bank out of range." ) ); return ConsoleUpdate(); } FILE *hFile = fopen( sLoadSaveFilePath.c_str(), "rb" ); if (hFile) { size_t nFileBytes = _GetFileSize( hFile ); if (nFileBytes > _6502_MEM_END) nFileBytes = _6502_MEM_END + 1; // Bank-switched RAM/ROM is only 16-bit // Caller didn't specify how many bytes to read, default to them all if (nAddressLen == 0) { nAddressLen = nFileBytes; } size_t nRead = fread( pMemBankBase+nAddressStart, nAddressLen, 1, hFile ); if (nRead == 1) { ConsoleBufferPushFormat( "Loaded @ A$%04X,L$%04X", nAddressStart, nAddressLen ); } else { ConsoleBufferPush( "Error loading data." ); } fclose( hFile ); if (bBankSpecified) { MemUpdatePaging(TRUE); } else { for (WORD i=(nAddressStart>>8); i!=((nAddressStart+(WORD)nAddressLen)>>8); i++) { memdirty[i] = 0xff; } } } else { ConsoleBufferPush( "ERROR: Bad filename" ); CmdConfigGetDebugDir( 0 ); ConsoleBufferPushFormat( "File: %s", g_sMemoryLoadSaveFileName.c_str() ); } return ConsoleUpdate(); } #endif // dst src : len //=========================================================================== Update_t CmdMemoryMove (int nArgs) { // M destaddr address end // M destaddr address,len // M destaddr address:end // 2000<4000.5FFFM if (nArgs < 3) return Help_Arg_1( CMD_MEMORY_MOVE ); WORD nDst = g_aArgs[1].nValue; // WORD nSrc = g_aArgs[2].nValue; // WORD nLen = g_aArgs[3].nValue - nSrc; WORD nAddress2 = 0; WORD nAddressStart = 0; WORD nAddressEnd = 0; int nAddressLen = 0; RangeType_t eRange; eRange = Range_Get( nAddressStart, nAddress2, 2 ); // if (eRange == RANGE_MISSING_ARG_2) if (! Range_CalcEndLen( eRange, nAddressStart, nAddress2, nAddressEnd, nAddressLen )) return Help_Arg_1( CMD_MEMORY_MOVE ); if ((nAddressLen > 0) && (nAddressEnd <= _6502_MEM_END)) { MemMarkDirty( nAddressStart, nAddressEnd ); // BYTE *pSrc = mem + nAddressStart; // BYTE *pDst = mem + nDst; // BYTE *pEnd = pSrc + nAddressLen; while ( nAddressLen-- ) // v2.7.0.23 { // TODO: Optimize - split into pre_io, and post_io if ((nDst < _6502_IO_BEGIN) || (nDst > _6502_IO_END)) { *(mem + nDst) = *(mem + nAddressStart); } nDst++; nAddressStart++; } return UPDATE_ALL; } return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== #if 0 // Original Update_t CmdMemorySave (int nArgs) { // BSAVE ["Filename"] , addr , len // BSAVE ["Filename"] , addr : end // 1 2 3 4 5 static WORD nAddressStart = 0; WORD nAddress2 = 0; static WORD nAddressEnd = 0; static int nAddressLen = 0; if (nArgs > 5) return Help_Arg_1( CMD_MEMORY_SAVE ); if (! nArgs) { if (nAddressLen) { ConsoleBufferPushFormat( "Last saved: $%04X:$%04X, %04X", nAddressStart, nAddressEnd, nAddressLen ); } else { ConsoleBufferPush( "Last saved: none" ); } } else { bool bHaveFileName = false; if (g_aArgs[1].bType & TYPE_QUOTED_2) bHaveFileName = true; // if (g_aArgs[1].bType & TOKEN_QUOTE_DOUBLE) // bHaveFileName = true; int iArgComma1 = 2; int iArgAddress = 3; int iArgComma2 = 4; int iArgLength = 5; if (! bHaveFileName) { iArgComma1 = 1; iArgAddress = 2; iArgComma2 = 3; iArgLength = 4; if (nArgs > 4) return Help_Arg_1( CMD_MEMORY_SAVE ); } // if ((g_aArgs[ iArgComma1 ].eToken != TOKEN_COMMA) || // (g_aArgs[ iArgComma2 ].eToken != TOKEN_COLON)) // return Help_Arg_1( CMD_MEMORY_SAVE ); TCHAR sLoadSaveFilePath[ MAX_PATH ]; _tcscpy( sLoadSaveFilePath, g_sCurrentDir ); // g_sProgramDir RangeType_t eRange; eRange = Range_Get( nAddressStart, nAddress2, iArgAddress ); // if (eRange == RANGE_MISSING_ARG_2) if (! Range_CalcEndLen( eRange, nAddressStart, nAddress2, nAddressEnd, nAddressLen )) return Help_Arg_1( CMD_MEMORY_SAVE ); if ((nAddressLen) && (nAddressEnd <= _6502_MEM_END)) { if (! bHaveFileName) { g_sMemoryLoadSaveFileName = StrFormat( "%04X.%04X.bin", nAddressStart, nAddressLen ); // nAddressEnd ); } else { g_sMemoryLoadSaveFileName = g_aArgs[ 1 ].sArg; } strcat( sLoadSaveFilePath, g_sMemoryLoadSaveFileName ); // if (nArgs == 2) { BYTE *pMemory = new BYTE [ nAddressLen ]; BYTE *pDst = pMemory; BYTE *pSrc = mem + nAddressStart; // memcpy -- copy out of active memory bank for ( int iByte = 0; iByte < nAddressLen; iByte++ ) { *pDst++ = *pSrc++; } FILE *hFile = fopen( sLoadSaveFilePath, "rb" ); if (hFile) { ConsoleBufferPush( TEXT( "Warning: File already exists. Overwriting." ) ); fclose( hFile ); } hFile = fopen( sLoadSaveFilePath, "wb" ); if (hFile) { size_t nWrote = fwrite( pMemory, nAddressLen, 1, hFile ); if (nWrote == 1) // (size_t)nAddressLen) { ConsoleBufferPush( TEXT( "Saved." ) ); } else { ConsoleBufferPush( TEXT( "Error saving." ) ); } fclose( hFile ); } delete [] pMemory; } } } return ConsoleUpdate(); } #else // Extended cmd for saving physical memory Update_t CmdMemorySave (int nArgs) { // Active memory: // BSAVE ["Filename"] , addr , len // BSAVE ["Filename"] , addr : end // 1 2 3 4 5 // Physical 64K memory bank: // BSAVE ["Filename"] , bank : addr , len // BSAVE ["Filename"] , bank : addr : end // 1 2 3 4 5 6 7 static WORD nAddressStart = 0; WORD nAddress2 = 0; static WORD nAddressEnd = 0; static int nAddressLen = 0; static int nBank = 0; static bool bBankSpecified = false; if (nArgs > 7) return Help_Arg_1( CMD_MEMORY_SAVE ); if (! nArgs) { if (nAddressLen) { if (!bBankSpecified) ConsoleBufferPushFormat( "Last saved: $%04X:$%04X, %04X", nAddressStart, nAddressEnd, nAddressLen ); else ConsoleBufferPushFormat( "Last saved: Bank=%02X $%04X:$%04X, %04X", nBank, nAddressStart, nAddressEnd, nAddressLen ); } else { ConsoleBufferPush( TEXT( "Last saved: none" ) ); } } else { bool bHaveFileName = false; if (g_aArgs[1].bType & TYPE_QUOTED_2) bHaveFileName = true; // if (g_aArgs[1].bType & TOKEN_QUOTE_DOUBLE) // bHaveFileName = true; // int iArgComma1 = 2; int iArgAddress = 3; int iArgComma2 = 4; int iArgLength = 5; int iArgBank = 3; int iArgColon = 4; if (! bHaveFileName) { // iArgComma1 = 1; iArgAddress = 2; iArgComma2 = 3; iArgLength = 4; iArgBank = 2; iArgColon = 3; if (nArgs > 6) return Help_Arg_1( CMD_MEMORY_SAVE ); } if (nArgs > 5) { if (!(g_aArgs[iArgBank].bType & TYPE_ADDRESS && g_aArgs[iArgColon].eToken == TOKEN_COLON)) return Help_Arg_1( CMD_MEMORY_SAVE ); nBank = g_aArgs[iArgBank].nValue; bBankSpecified = true; iArgAddress += 2; iArgComma2 += 2; iArgLength += 2; } else { bBankSpecified = false; } // if ((g_aArgs[ iArgComma1 ].eToken != TOKEN_COMMA) || // (g_aArgs[ iArgComma2 ].eToken != TOKEN_COLON)) // return Help_Arg_1( CMD_MEMORY_SAVE ); std::string sLoadSaveFilePath = g_sCurrentDir; // g_sProgramDir RangeType_t eRange; eRange = Range_Get( nAddressStart, nAddress2, iArgAddress ); // if (eRange == RANGE_MISSING_ARG_2) if (! Range_CalcEndLen( eRange, nAddressStart, nAddress2, nAddressEnd, nAddressLen )) return Help_Arg_1( CMD_MEMORY_SAVE ); if ((nAddressLen) && (nAddressEnd <= _6502_MEM_END)) { if (! bHaveFileName) { g_sMemoryLoadSaveFileName = (bBankSpecified) ? StrFormat( "%04X.%04X.bank%02X.bin", nAddressStart, nAddressLen, nBank ) : StrFormat( "%04X.%04X.bin", nAddressStart, nAddressLen ); } else { g_sMemoryLoadSaveFileName = g_aArgs[ 1 ].sArg; } sLoadSaveFilePath += g_sMemoryLoadSaveFileName; const BYTE * const pMemBankBase = bBankSpecified ? MemGetBankPtr(nBank) : mem; if (!pMemBankBase) { ConsoleBufferPush( TEXT( "Error: Bank out of range." ) ); return ConsoleUpdate(); } FILE *hFile = fopen( sLoadSaveFilePath.c_str(), "rb" ); if (hFile) { ConsoleBufferPush( TEXT( "Warning: File already exists. Overwriting." ) ); fclose( hFile ); // TODO: BUG: Is this a bug/feature that we can over-write files and the user has no control over that? } hFile = fopen( sLoadSaveFilePath.c_str(), "wb" ); if (hFile) { size_t nWrote = fwrite( pMemBankBase+nAddressStart, nAddressLen, 1, hFile ); if (nWrote == 1) { ConsoleBufferPush( TEXT( "Saved." ) ); } else { ConsoleBufferPush( TEXT( "Error saving." ) ); } fclose( hFile ); } else { ConsoleBufferPush( TEXT( "Error opening file." ) ); } } } return ConsoleUpdate(); } #endif char g_aTextScreen[ DEBUG_VIRTUAL_TEXT_HEIGHT * (DEBUG_VIRTUAL_TEXT_WIDTH + 4) ]; // (80 column + CR + LF) * 24 rows + NULL int g_nTextScreen = 0; /* $FBC1 BASCALC IN: A=row, OUT: $28=low, $29=hi BASCALC PHA ; 000abcde -> temp LSR ; 0000abcd CARRY=e y /= 2 AND #3 ; 000000cd y & 3 ORA #4 ; 000001cd y | 4 STA $29 PLA ; 000abcde <- temp AND #$18 ; 000ab000 BCC BASCLC2 ; e=0? ADC #7F ; 100ab000 yes CARRY=e=1 -> #$+80 BASCLC2 STA $28 ; e00ab000 no CARRY=e=0 ASL ; 00ab0000 ASL ; 0ab00000 ORA $28 ; 0abab000 STA $28 ; eabab000 300:A9 00 20 C1 FB A5 29 A6 28 4C 41 F9 y Hex 000a_bcde 01cd_eaba_b000 0 00 0000_0000 -> $400 0100 0000_0000 1 01 0000_0001 -> $480 0100 1000_0000 2 02 0000_0010 -> $500 0101 0000_0000 3 03 0000_0011 -> $580 0101 1000_0000 4 04 0000_0100 -> $600 0110 0000_0000 5 05 0000_0101 -> $680 0110 1000_0000 6 06 0000_0110 -> $700 0111 0000_0000 7 07 0000_0111 -> $780 0111 1000_0000 8 08 0000_1000 -> $428 0100 0010_1000 9 09 0000_1001 -> $4A8 0100 1010_1000 10 0A 0000_1010 -> $528 0101 0010_1000 11 0B 0000_1011 -> $5A8 0101 1010_1000 12 0C 0000_1100 -> $628 0110 0010_1000 13 0D 0000_1101 -> $6A8 0110 1010_1000 14 0E 0000_1110 -> $728 0111 0010_1000 15 0F 0000_1111 -> $7A8 0111 1010_1000 16 10 0001_0000 -> $450 0100 0101_0000 17 11 0001_0001 -> $4D0 0100 1101_0000 18 12 0001_0010 -> $550 0101 0101_0000 19 13 0001_0011 -> $5D0 0101 1101_0000 20 14 0001_0100 -> $650 0110 0101_0000 21 15 0001_0101 -> $6D0 0110_1101_0000 22 16 0001_0110 -> $750 0111_0101_0000 23 17 0001_0111 -> $7D0 0111 1101 0000 */ // Convert ctrl characters to displayable // Note: FormatCharTxtCtrl() and RemapChar() static char RemapChar(const char c) { if ( c < 0x20 ) return c + '@'; // Remap INVERSE control character to NORMAL else if ( c == 0x7F ) return ' '; // Remap checkboard (DEL) to space return c; } size_t Util_GetDebuggerText( char* &pText_ ) { char *pBeg = &g_aTextScreen[0]; char *pEnd = &g_aTextScreen[0]; g_nTextScreen = 0; memset( pBeg, 0, sizeof( g_aTextScreen ) ); memset( g_aDebuggerVirtualTextScreen, 0, sizeof( g_aDebuggerVirtualTextScreen ) ); DebugDisplay(); for ( int y = 0; y < DEBUG_VIRTUAL_TEXT_HEIGHT; y++ ) { for ( int x = 0; x < DEBUG_VIRTUAL_TEXT_WIDTH; x++ ) { char c = g_aDebuggerVirtualTextScreen[y][x]; if ( (c < 0x20) || (c >= 0x7F) ) c = ' '; // convert null to spaces to keep everything non-proptional *pEnd++ = c; } #ifdef _WIN32 *pEnd++ = 0x0D; // CR // Windows inserts extra char #endif *pEnd++ = 0x0A; // LF // OSX, Linux } *pEnd = 0; g_nTextScreen = pEnd - pBeg; pText_ = pBeg; return g_nTextScreen; } size_t Util_GetTextScreen ( char* &pText_ ) { WORD nAddressStart = 0; char *pBeg = &g_aTextScreen[0]; char *pEnd = &g_aTextScreen[0]; g_nTextScreen = 0; memset( pBeg, 0, sizeof( g_aTextScreen ) ); unsigned int uBank2 = GetVideo().VideoGetSWPAGE2() ? 1 : 0; LPBYTE g_pTextBank1 = MemGetAuxPtr (0x400 << uBank2); LPBYTE g_pTextBank0 = MemGetMainPtr(0x400 << uBank2); for ( int y = 0; y < 24; y++ ) { // nAddressStart = 0x400 + (y%8)*0x80 + (y/8)*0x28; nAddressStart = ((y&7)<<7) | ((y&0x18)<<2) | (y&0x18); // no 0x400| since using MemGet*Ptr() for ( int x = 0; x < 40; x++ ) // always 40 columns { char c; // TODO: FormatCharTxtCtrl() ? if ( GetVideo().VideoGetSW80COL() ) { // AUX c = g_pTextBank1[ nAddressStart ] & 0x7F; c = RemapChar(c); *pEnd++ = c; } // MAIN -- NOTE: intentional indent & outside if () ! c = g_pTextBank0[ nAddressStart ] & 0x7F; c = RemapChar(c); *pEnd++ = c; nAddressStart++; } // Newline // http://en.wikipedia.org/wiki/Newline #ifdef _WIN32 *pEnd++ = 0x0D; // CR // Windows inserts extra char #endif *pEnd++ = 0x0A; // LF // OSX, Linux } *pEnd = 0; g_nTextScreen = pEnd - pBeg; pText_ = pBeg; return g_nTextScreen; } //=========================================================================== Update_t CmdNTSC (int nArgs) { int iParam; int nFound = FindParam( g_aArgs[ 1 ].sArg, MATCH_EXACT, iParam, _PARAM_GENERAL_BEGIN, _PARAM_GENERAL_END ); struct KnownFileType_t { const char *pExtension; }; enum KnownFileType_e { TYPE_UNKNOWN ,TYPE_BMP ,TYPE_RAW ,NUM_FILE_TYPES }; const KnownFileType_t aFileTypes[ NUM_FILE_TYPES ] = { { "" } // n/a ,{ ".bmp" } ,{ ".data" } // ,{ ".raw" } // ,{ ".ntsc" } }; const int nFileType = sizeof( aFileTypes ) / sizeof( KnownFileType_t ); const KnownFileType_t *pFileType = NULL; /* */ KnownFileType_e iFileType = TYPE_UNKNOWN; #if _DEBUG assert( (nFileType == NUM_FILE_TYPES) ); #endif const char *pFileName = (nArgs > 1) ? g_aArgs[ 2 ].sArg : ""; int nLen = strlen( pFileName ); const char *pEnd = pFileName + nLen - 1; while ( pEnd > pFileName ) { if ( *pEnd == '.' ) { for ( int i = TYPE_BMP; i < NUM_FILE_TYPES; i++ ) { if ( strcmp( pEnd, aFileTypes[i].pExtension ) == 0 ) { pFileType = &aFileTypes[i]; iFileType = (KnownFileType_e) i; break; } } } if ( pFileType ) break; pEnd--; } if ( nLen == 0 ) pFileName = "AppleWinNTSC4096x4@32.data"; static std::string sPaletteFilePath; sPaletteFilePath = g_sCurrentDir + pFileName; class ConsoleFilename { public: static void update( const char *pPrefixText ) { size_t len1 = strlen( pPrefixText ); size_t len2 = sPaletteFilePath.size(); size_t len = len1 + len2; if (len >= CONSOLE_WIDTH) { ConsoleBufferPush( pPrefixText ); // TODO: Add a ": " separator #if _DEBUG LogOutput( "Filename.length.1: %d\n", len1 ); LogOutput( "Filename.length.2: %d\n", len2 ); OutputDebugString( sPaletteFilePath.c_str() ); #endif // File path is too long // TODO: Need to split very long path names char text[CONSOLE_WIDTH * 2] = ""; strncpy( text, sPaletteFilePath.c_str(), CONSOLE_WIDTH ); ConsoleBufferPush( text ); // TODO: Switch ConsoleBufferPush() to ConsoleBufferPushFormat() } else { ConsoleBufferPushFormat( "%s: %s", pPrefixText, sPaletteFilePath.c_str() ); } } }; class Swizzle32 { public: static void RGBAswapBGRA( size_t nSize, const uint8_t *pSrc, uint8_t *pDst ) // Note: pSrc and pDst _may_ alias; code handles this properly { const uint8_t* pEnd = pSrc + nSize; while ( pSrc < pEnd ) { const uint8_t r = pSrc[2]; const uint8_t g = pSrc[1]; const uint8_t b = pSrc[0]; const uint8_t a = 255; // Force A=1, 100% opacity; as pSrc[3] might not be 255 *pDst++ = r; *pDst++ = g; *pDst++ = b; *pDst++ = a; pSrc += 4; } } static void ABGRswizzleBGRA( size_t nSize, const uint8_t *pSrc, uint8_t *pDst ) // Note: pSrc and pDst _may_ alias; code handles this properly { const uint8_t* pEnd = pSrc + nSize; while ( pSrc < pEnd ) { const uint8_t r = pSrc[3]; const uint8_t g = pSrc[2]; const uint8_t b = pSrc[1]; const uint8_t a = 255; // Force A=1, 100% opacity; as pSrc[3] might not be 255 *pDst++ = b; *pDst++ = g; *pDst++ = r; *pDst++ = a; pSrc += 4; } } #if 0 static void ABGRswizzleRGBA( size_t nSize, const uint8_t *pSrc, uint8_t *pDst ) // Note: pSrc and pDst _may_ alias; code handles this properly { const uint8_t* pEnd = pSrc + nSize; while ( pSrc < pEnd ) { const uint8_t r = pSrc[3]; const uint8_t g = pSrc[2]; const uint8_t b = pSrc[1]; const uint8_t a = 255; // Force A=1, 100% opacity; as pSrc[3] might not be 255 *pDst++ = r; *pDst++ = g; *pDst++ = b; *pDst++ = a; pSrc += 4; } } #endif }; class Transpose64x1 { public: static void transposeTo64x256( const uint8_t *pSrc, uint8_t *pDst ) { const uint32_t nBPP = 4; // bytes per pixel // Expand y from 1 to 256 rows const size_t nBytesPerScanLine = 16 * 4 * nBPP; // 16 colors * 4 phases for ( int y = 0; y < 256; y++ ) memcpy( pDst + y*nBytesPerScanLine, pSrc, nBytesPerScanLine ); } }; // Transpose from 16x1 to 4096x16 class Transpose16x1 { public: /* .Column . Phases 0..3 . X P0 P1 P2 P3 . 0 0 0 0 0 . 1 1 8 4 2 . 2 2 1 8 4 . 3 3 9 C 6 . 4 4 2 1 8 . 5 5 A 5 A . 6 6 3 9 C . 7 7 B D E . 8 8 4 2 1 . 9 9 C 6 3 . A A 5 A 5 . B B D E 7 . C C 6 3 9 . D D E 7 B . E E 7 B D . F F F F F . . 1 2 4 8 Delta */ static void transposeTo64x1( const uint8_t *pSrc, uint8_t *pDst ) { const uint32_t *pPhase0 = (uint32_t*) pSrc; /* */ uint32_t *pTmp = (uint32_t*) pDst; #if 1 // Loop // Expand x from 16 colors (single phase) to 64 colors (16 * 4 phases) for ( int iPhase = 0; iPhase < 4; iPhase++ ) { int phase = iPhase; if (iPhase == 1) phase = 3; if (iPhase == 3) phase = 1; int mul = (1 << phase); // Mul: *1 *8 *4 *2 for ( int iDstX = 0; iDstX < 16; iDstX++ ) { int iSrcX = (iDstX * mul) % 15; // Delta: +1 +2 +4 +8 if (iDstX == 15) iSrcX = 15; #if 0 // _DEBUG LogOutput( "[ %X ] = [ %X ]\n", iDstX, iSrcX ); #endif pTmp[ iDstX + 16*iPhase ] = pPhase0[ iSrcX ]; } } #else // Manual Loop unrolled const uint32_t nBPP = 4; // bytes per pixel const size_t nBytesPerScanLine = 16 * 4 * nBPP; // 16 colors * 4 phases memcpy( pDst, pSrc, nBytesPerScanLine ); int iPhase = 1; int iDstX = iPhase * 16; pTmp[ iDstX + 0x0 ] = pPhase0[ 0x0 ]; pTmp[ iDstX + 0x1 ] = pPhase0[ 0x8 ]; pTmp[ iDstX + 0x2 ] = pPhase0[ 0x1 ]; pTmp[ iDstX + 0x3 ] = pPhase0[ 0x9 ]; pTmp[ iDstX + 0x4 ] = pPhase0[ 0x2 ]; pTmp[ iDstX + 0x5 ] = pPhase0[ 0xA ]; pTmp[ iDstX + 0x6 ] = pPhase0[ 0x3 ]; pTmp[ iDstX + 0x7 ] = pPhase0[ 0xB ]; pTmp[ iDstX + 0x8 ] = pPhase0[ 0x4 ]; pTmp[ iDstX + 0x9 ] = pPhase0[ 0xC ]; pTmp[ iDstX + 0xA ] = pPhase0[ 0x5 ]; pTmp[ iDstX + 0xB ] = pPhase0[ 0xD ]; pTmp[ iDstX + 0xC ] = pPhase0[ 0x6 ]; pTmp[ iDstX + 0xD ] = pPhase0[ 0xE ]; pTmp[ iDstX + 0xE ] = pPhase0[ 0x7 ]; pTmp[ iDstX + 0xF ] = pPhase0[ 0xF ]; iPhase = 2; iDstX = iPhase * 16; pTmp[ iDstX + 0x0 ] = pPhase0[ 0x0 ]; pTmp[ iDstX + 0x1 ] = pPhase0[ 0x4 ]; pTmp[ iDstX + 0x2 ] = pPhase0[ 0x8 ]; pTmp[ iDstX + 0x3 ] = pPhase0[ 0xC ]; pTmp[ iDstX + 0x4 ] = pPhase0[ 0x1 ]; pTmp[ iDstX + 0x5 ] = pPhase0[ 0x5 ]; pTmp[ iDstX + 0x6 ] = pPhase0[ 0x9 ]; pTmp[ iDstX + 0x7 ] = pPhase0[ 0xD ]; pTmp[ iDstX + 0x8 ] = pPhase0[ 0x2 ]; pTmp[ iDstX + 0x9 ] = pPhase0[ 0x6 ]; pTmp[ iDstX + 0xA ] = pPhase0[ 0xA ]; pTmp[ iDstX + 0xB ] = pPhase0[ 0xE ]; pTmp[ iDstX + 0xC ] = pPhase0[ 0x3 ]; pTmp[ iDstX + 0xD ] = pPhase0[ 0x7 ]; pTmp[ iDstX + 0xE ] = pPhase0[ 0xB ]; pTmp[ iDstX + 0xF ] = pPhase0[ 0xF ]; iPhase = 3; iDstX = iPhase * 16; pTmp[ iDstX + 0x0 ] = pPhase0[ 0x0 ]; pTmp[ iDstX + 0x1 ] = pPhase0[ 0x2 ]; pTmp[ iDstX + 0x2 ] = pPhase0[ 0x4 ]; pTmp[ iDstX + 0x3 ] = pPhase0[ 0x6 ]; pTmp[ iDstX + 0x4 ] = pPhase0[ 0x8 ]; pTmp[ iDstX + 0x5 ] = pPhase0[ 0xA ]; pTmp[ iDstX + 0x6 ] = pPhase0[ 0xC ]; pTmp[ iDstX + 0x7 ] = pPhase0[ 0xE ]; pTmp[ iDstX + 0x8 ] = pPhase0[ 0x1 ]; pTmp[ iDstX + 0x9 ] = pPhase0[ 0x3 ]; pTmp[ iDstX + 0xA ] = pPhase0[ 0x5 ]; pTmp[ iDstX + 0xB ] = pPhase0[ 0x7 ]; pTmp[ iDstX + 0xC ] = pPhase0[ 0x9 ]; pTmp[ iDstX + 0xD ] = pPhase0[ 0xB ]; pTmp[ iDstX + 0xE ] = pPhase0[ 0xD ]; pTmp[ iDstX + 0xF ] = pPhase0[ 0xF ]; #endif } /* . Source layout = 16x1 @ 32-bit . | phase 0 | . +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ . |BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA| row 0 . +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ . \ 0/ \ 1/ \ 2/ \ 3/ \ 4/ \ 5/ \ 6/ \ 7/ \ 8/ \ 9/ \ A/ \ B/ \ C/ \ D/ \ E/ \ F/ . . |<----------------------------------- 16 px ----------------------------------->| . 64 byte . . Destination layout = 4096x4 @ 32-bit . +----+----+----+----+----+ . |BGRA|BGRA|BGRA|... |BGRA| phase 0 . +----+----+----+----+----+ . |BGRA|BGRA|BGRA|... |BGRA| phase 1 . +----+----+----+----+----| . |BGRA|BGRA|BGRA|... |BGRA| phase 2 . +----+----+----+----+----+ . |BGRA|BGRA|BGRA|... |BGRA| phase 3 . +----+----+----+----+----+ . 0 1 2 4095 column */ /* static void transposeFrom16x1( const uint8_t *pSrc, uint8_t *pDst ) { const uint8_t *pTmp = pSrc; const uint32_t nBPP = 4; // bytes per pixel for ( int x = 0; x < 16; x++ ) { pTmp = pSrc + (x * nBPP); // dst is 16-px column for ( int y = 0; y < 256; y++ ) { *pDst++ = pTmp[0]; *pDst++ = pTmp[1]; *pDst++ = pTmp[2]; *pDst++ = pTmp[3]; } } // we duplicate phase 0 a total of 4 times const size_t nBytesPerScanLine = 4096 * nBPP; for ( int iPhase = 1; iPhase < 4; iPhase++ ) memcpy( pDst + iPhase*nBytesPerScanLine, pDst, nBytesPerScanLine ); } */ }; class Transpose4096x4 { /* . Source layout = 4096x4 @ 32-bit . +----+----+----+----+----+ . |BGRA|BGRA|BGRA|... |BGRA| phase 0 . +----+----+----+----+----+ . |BGRA|BGRA|BGRA|... |BGRA| phase 1 . +----+----+----+----+----| . |BGRA|BGRA|BGRA|... |BGRA| phase 2 . +----+----+----+----+----+ . |BGRA|BGRA|BGRA|... |BGRA| phase 3 . +----+----+----+----+----+ . 0 1 2 4095 column . . Destination layout = 64x256 @ 32-bit . | phase 0 | phase 1 | phase 2 | phase 3 | . +----+----+----+----+----+----+----+----+ . |BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA| row 0 . +----+----+----+----+----+----+----+----+ . |BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA| row 1 . +----+----+----+----+----+----+----+----+ . |... |... |... |... |... |... |... |... | . +----+----+----+----+----+----+----+----+ . |BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA|BGRA| row 255 . +----+----+----+----+----+----+----+----+ . \ 16 px / \ 16 px / \ 16 px / \ 16 px / = 64 pixels . 64 byte 64 byte 64 byte 64 byte . .Column . Phases 0..3 . X P0 P1 P2 P3 . 0 0 0 0 0 . 1 1 8 4 2 . 2 2 1 8 4 . 3 3 9 C 6 . 4 4 2 1 8 . 5 5 A 5 A . 6 6 3 9 C . 7 7 B D E . 8 8 4 2 1 . 9 9 C 6 3 . A A 5 A 5 . B B D E 7 . C C 6 3 9 . D D E 7 B . E E 7 B D . F F F F F . . 1 2 4 8 Delta */ public: static void transposeTo64x256( const uint8_t *pSrc, uint8_t *pDst ) { /* */ uint8_t *pTmp = pDst; const uint32_t nBPP = 4; // bytes per pixel for ( int iPhase = 0; iPhase < 4; iPhase++ ) { pDst = pTmp + (iPhase * 16 * nBPP); // dst is 16-px column for ( int x = 0; x < 4096/16; x++ ) // 4096px/16 px = 256 columns { for ( int i = 0; i < 16*nBPP; i++ ) // 16 px, 32-bit *pDst++ = *pSrc++; pDst -= (16*nBPP); pDst += (64*nBPP); // move to next scan line } } } static void transposeFrom64x256( const uint8_t *pSrc, uint8_t *pDst ) { const uint8_t *pTmp = pSrc; const uint32_t nBPP = 4; // bytes per pixel for ( int iPhase = 0; iPhase < 4; iPhase++ ) { pSrc = pTmp + (iPhase * 16 * nBPP); // src is 16-px column for ( int y = 0; y < 256; y++ ) { for ( int i = 0; i < 16*nBPP; i++ ) // 16 px, 32-bit *pDst++ = *pSrc++; pSrc -= (16*nBPP); pSrc += (64*nBPP); // move to next scan line } } } }; bool bColorTV = (GetVideo().GetVideoType() == VT_COLOR_TV); uint32_t* pChromaTable = NTSC_VideoGetChromaTable( false, bColorTV ); //uint8_t* pTmp = (uint8_t*) pChromaTable; //*pTmp++ = 0xFF; // b //*pTmp++ = 0x00; // g //*pTmp++ = 0x00; // r //*pTmp++ = 0xFF; // a if (nFound) { if (iParam == PARAM_RESET) { NTSC_VideoInitChroma(); ConsoleBufferPush( TEXT(" Resetting NTSC palette." ) ); } else if (iParam == PARAM_SAVE) { FILE *pFile = fopen( sPaletteFilePath.c_str(), "w+b" ); if ( pFile ) { size_t nWrote = 0; uint8_t *pSwizzled = new uint8_t[ g_nChromaSize ]; if ( iFileType == TYPE_BMP ) { // need to save 32-bit bpp as 24-bit bpp // VideoSaveScreenShot() Transpose4096x4::transposeTo64x256( (uint8_t*) pChromaTable, pSwizzled ); // Write BMP header WinBmpHeader_t bmp, *pBmp = &bmp; GetVideo().Video_SetBitmapHeader( pBmp, 64, 256, 32 ); fwrite( pBmp, sizeof( WinBmpHeader_t ), 1, pFile ); } else { // RAW has no header Swizzle32::RGBAswapBGRA( g_nChromaSize, (uint8_t*) pChromaTable, pSwizzled ); } nWrote = fwrite( pSwizzled, g_nChromaSize, 1, pFile ); fclose( pFile ); delete [] pSwizzled; if (nWrote == 1) { ConsoleFilename::update( "Saved" ); } else ConsoleBufferPush( TEXT( "Error saving." ) ); } else { ConsoleFilename::update( "File" ); ConsoleBufferPush( TEXT( "Error couldn't open file for writing." ) ); } } else if (iParam == PARAM_LOAD) { std::string sStatusText; FILE *pFile = fopen( sPaletteFilePath.c_str(), "rb" ); if ( pFile ) { sStatusText = "Loaded"; // Get File Size size_t nFileSize = _GetFileSize( pFile ); uint8_t *pSwizzled = new uint8_t[ g_nChromaSize ]; bool bSwizzle = true; WinBmpHeader4_t bmp = { 0 }; if ( iFileType == TYPE_BMP ) { fread( &bmp, sizeof( WinBmpHeader4_t ), 1, pFile ); fseek( pFile, bmp.nOffsetData, SEEK_SET ); if (bmp.nBitsPerPixel != 32) { sStatusText = "Bitmap not 32-bit RGBA"; goto _error; } if (bmp.nOffsetData > nFileSize) { sStatusText = "Bad BITMAP: Data > file size !?"; goto _error; } if ( ! ( ((bmp.nWidthPixels == 64 ) && (bmp.nHeightPixels == 256)) || ((bmp.nWidthPixels == 64 ) && (bmp.nHeightPixels == 1)) || ((bmp.nWidthPixels == 16 ) && (bmp.nHeightPixels == 1)) )) { sStatusText = "Bitmap not 64x256, 64x1, or 16x1"; goto _error; } if (bmp.nStructSize == 0x28) { if ( bmp.nCompression == 0) // BI_RGB mode bSwizzle = false; } else // 0x7C version4 bitmap { if ( bmp.nCompression == 3 ) // BI_BITFIELDS { if ((bmp.nRedMask == 0xFF000000 ) // Gimp writes in ABGR order && (bmp.nGreenMask == 0x00FF0000 ) && (bmp.nBlueMask == 0x0000FF00 )) bSwizzle = true; } } } else if ( nFileSize != g_nChromaSize ) { sStatusText = StrFormat( "Raw size != %d", 64 * 256 * 4 ); goto _error; } fread( pSwizzled, g_nChromaSize, 1, pFile ); if ( iFileType == TYPE_BMP ) { if (bmp.nHeightPixels == 1) { uint8_t *pTemp64x256 = new uint8_t[ 64 * 256 * 4 ]; memset( pTemp64x256, 0, g_nChromaSize ); //Transpose16x1::transposeFrom16x1( pSwizzled, (uint8_t*) pChromaTable ); if (bmp.nWidthPixels == 16) { Transpose16x1::transposeTo64x1( pSwizzled, pTemp64x256 ); Transpose64x1::transposeTo64x256( pTemp64x256, pTemp64x256 ); } else if (bmp.nWidthPixels == 64) Transpose64x1::transposeTo64x256( pSwizzled, pTemp64x256 ); Transpose4096x4::transposeFrom64x256( pTemp64x256, (uint8_t*) pChromaTable ); delete [] pTemp64x256; } else Transpose4096x4::transposeFrom64x256( pSwizzled, (uint8_t*) pChromaTable ); if ( bSwizzle ) Swizzle32::ABGRswizzleBGRA( g_nChromaSize, (uint8_t*) pChromaTable, (uint8_t*) pChromaTable ); } else Swizzle32::RGBAswapBGRA( g_nChromaSize, pSwizzled, (uint8_t*) pChromaTable ); _error: fclose( pFile ); delete [] pSwizzled; } else { sStatusText = "File: "; ConsoleBufferPush( "Error couldn't open file for reading." ); } ConsoleFilename::update( sStatusText.c_str() ); } else return HelpLastCommand(); } // else return ConsoleUpdate(); } //=========================================================================== int CmdTextSave (int nArgs) { // Save the TEXT1 40-colomn to text file (Default: AppleWin_Text40.txt" // TSAVE ["Filename"] // TSAVE ["Filename"] // 1 if (nArgs > 1) return Help_Arg_1( CMD_TEXT_SAVE ); bool bHaveFileName = false; if (g_aArgs[1].bType & TYPE_QUOTED_2) bHaveFileName = true; char *pText; size_t nSize = Util_GetTextScreen( pText ); std::string sLoadSaveFilePath = g_sCurrentDir; // g_sProgramDir if ( bHaveFileName ) g_sMemoryLoadSaveFileName = g_aArgs[ 1 ].sArg; else { if ( GetVideo().VideoGetSW80COL() ) g_sMemoryLoadSaveFileName = "AppleWin_Text80.txt"; else g_sMemoryLoadSaveFileName = "AppleWin_Text40.txt"; } sLoadSaveFilePath += g_sMemoryLoadSaveFileName; FILE *hFile = fopen( sLoadSaveFilePath.c_str(), "rb" ); if (hFile) { ConsoleBufferPush( "Warning: File already exists. Overwriting." ); fclose( hFile ); } hFile = fopen( sLoadSaveFilePath.c_str(), "wb" ); if (hFile) { size_t nWrote = fwrite( pText, nSize, 1, hFile ); if (nWrote == 1) { ConsoleBufferPushFormat( "Saved: %s", g_sMemoryLoadSaveFileName.c_str() ); } else { ConsoleBufferPush( "Error saving." ); } fclose( hFile ); } else { ConsoleBufferPush( "Error opening file." ); } return ConsoleUpdate(); } //=========================================================================== int _SearchMemoryFind( MemorySearchValues_t vMemorySearchValues, WORD nAddressStart, WORD nAddressEnd ) { int nFound = 0; g_vMemorySearchResults.clear(); g_vMemorySearchResults.push_back( NO_6502_TARGET ); uint32_t nAddress; // NB. can't be uint16_t, since need to count up to 0x10000 if nAddressEnd is 0xFFFF for ( nAddress = nAddressStart; nAddress <= nAddressEnd; nAddress++ ) { bool bMatchAll = true; uint32_t nAddress2 = nAddress; int nMemBlocks = vMemorySearchValues.size(); for ( int iBlock = 0; iBlock < nMemBlocks; iBlock++, nAddress2++ ) { MemorySearch_t ms = vMemorySearchValues.at( iBlock ); ms.m_bFound = false; if ((ms.m_iType == MEM_SEARCH_BYTE_EXACT ) || (ms.m_iType == MEM_SEARCH_NIB_HIGH_EXACT) || (ms.m_iType == MEM_SEARCH_NIB_LOW_EXACT )) { BYTE nTarget = *(mem + nAddress2); if (ms.m_iType == MEM_SEARCH_NIB_LOW_EXACT) nTarget &= 0x0F; if (ms.m_iType == MEM_SEARCH_NIB_HIGH_EXACT) nTarget &= 0xF0; if (ms.m_nValue == nTarget) { // ms.m_nAddress = nAddress2; ms.m_bFound = true; continue; } else { bMatchAll = false; break; } } else if (ms.m_iType == MEM_SEARCH_BYTE_1_WILD) { // match by definition } else { // start 2ndary search // if next block matches, then this block matches (since we are wild) if ((iBlock + 1) == nMemBlocks) // there is no next block, hence we match continue; for (uint32_t nAddress3 = nAddress2; nAddress3 < nAddressEnd; nAddress3++ ) { if ((ms.m_iType == MEM_SEARCH_BYTE_EXACT ) || (ms.m_iType == MEM_SEARCH_NIB_HIGH_EXACT) || (ms.m_iType == MEM_SEARCH_NIB_LOW_EXACT )) { BYTE nTarget = *(mem + nAddress3); if (ms.m_iType == MEM_SEARCH_NIB_LOW_EXACT) nTarget &= 0x0F; if (ms.m_iType == MEM_SEARCH_NIB_HIGH_EXACT) nTarget &= 0xF0; if (ms.m_nValue == nTarget) { nAddress2 = nAddress3; continue; } else { bMatchAll = false; break; } } } } } if (bMatchAll) { nFound++; // Save the search result g_vMemorySearchResults.push_back( nAddress ); } } return nFound; } //=========================================================================== Update_t _SearchMemoryDisplay (int nArgs) { int const nFound = g_vMemorySearchResults.size() - 1; if (nFound > 0) { std::string sMatches; int iFound = 1; while (iFound <= nFound) { WORD const nAddress = g_vMemorySearchResults.at( iFound ); // 2.6.2.17 Search Results: The n'th result now using correct color (was command, now number decimal) // BUGFIX: 2.6.2.32 n'th Search results were being displayed in dec, yet parser takes hex numbers. i.e. SH D000:FFFF A9 00 // Intentional default instead of CHC_ARG_SEP for better readability // 2.6.2.16 Fixed: Search Results: The hex specify for target address results now colorized properly // 2.6.2.15 Fixed: Search Results: Added space between results for better readability // FIXME: Color is DEC whereas the format is "%X". What's the real intention? std::string sResult = StrFormat( CHC_NUM_DEC "%02X" CHC_DEFAULT ":" CHC_ARG_SEP "$" CHC_ADDRESS "%04X ", iFound, nAddress ); // Fit on same line? if ((sMatches.length() + sResult.length()) > (size_t(g_nConsoleDisplayWidth) - 1)) // CONSOLE_WIDTH { //ConsoleDisplayPush( sMatches.c_str() ); ConsolePrint( sMatches.c_str() ); sMatches = sResult; } else { sMatches += sResult; } iFound++; } ConsolePrint( sMatches.c_str() ); } //ConsoleDisplayPushFormat( "Total: %d (#$%04X)", nFound, nFound ); ConsolePrintFormat( CHC_USAGE "Total" CHC_DEFAULT ": " CHC_NUM_DEC "%d " CHC_ARG_SEP "(" CHC_ARG_SEP "#$" CHC_NUM_HEX "%04X" CHC_ARG_SEP ")", nFound /*dec*/, nFound /*hex*/ ); // g_vMemorySearchResults is cleared in DebugEnd() // return UPDATE_CONSOLE_DISPLAY; return ConsoleUpdate(); } //=========================================================================== Update_t _CmdMemorySearch (int nArgs, bool bTextIsAscii = true ) { WORD nAddressStart = 0; WORD nAddress2 = 0; WORD nAddressEnd = 0; int nAddressLen = 0; RangeType_t eRange; eRange = Range_Get( nAddressStart, nAddress2 ); // if (eRange == RANGE_MISSING_ARG_2) if (! Range_CalcEndLen( eRange, nAddressStart, nAddress2, nAddressEnd, nAddressLen)) return ConsoleDisplayError( "Error: Missing address separator (comma or colon)" ); int iArgFirstByte = 4; int iArg; MemorySearchValues_t vMemorySearchValues; MemorySearch_e tLastType = MEM_SEARCH_BYTE_N_WILD; // Get search "string" Arg_t *pArg = & g_aArgs[ iArgFirstByte ]; for (iArg = iArgFirstByte; iArg <= nArgs; iArg++, pArg++ ) { WORD nTarget = pArg->nValue; MemorySearch_t ms; ms.m_nValue = nTarget & 0xFF; ms.m_iType = MEM_SEARCH_BYTE_EXACT; ms.m_bFound = false; if (nTarget > 0xFF) // searching for 16-bit address { vMemorySearchValues.push_back( ms ); ms.m_nValue = (nTarget >> 8); tLastType = ms.m_iType; } else { TCHAR *pByte = pArg->sArg; if (pArg->bType & TYPE_QUOTED_1) { // Convert string to hex byte(s) int iChar = 0; int nChars = pArg->nArgLen; if (nChars) { ms.m_iType = MEM_SEARCH_BYTE_EXACT; ms.m_bFound = false; while (iChar < nChars) { ms.m_nValue = pArg->sArg[ iChar ]; // Ascii (Low-Bit) // Apple (High-Bit) // if (! bTextIsAscii) // NOTE: Single quote chars is opposite hi-bit !!! // ms.m_nValue &= 0x7F; // else ms.m_nValue |= 0x80; // last char is handle in common case below iChar++; if (iChar < nChars) vMemorySearchValues.push_back( ms ); } } } else if (pArg->bType & TYPE_QUOTED_2) { // Convert string to hex byte(s) int iChar = 0; int nChars = pArg->nArgLen; if (nChars) { ms.m_iType = MEM_SEARCH_BYTE_EXACT; ms.m_bFound = false; while (iChar < nChars) { ms.m_nValue = pArg->sArg[ iChar ]; // Ascii (Low-Bit) // Apple (High-Bit) // if (bTextIsAscii) ms.m_nValue &= 0x7F; // else // ms.m_nValue |= 0x80; iChar++; // last char is handle in common case below if (iChar < nChars) vMemorySearchValues.push_back( ms ); } } } else { // must be numeric .. make sure not too big if (pArg->nArgLen > 2) { vMemorySearchValues.clear(); return HelpLastCommand(); } if (pArg->nArgLen == 1) { if (pByte[0] == g_aParameters[ PARAM_MEM_SEARCH_WILD ].m_sName[0]) // Hack: hard-coded one char token { ms.m_iType = MEM_SEARCH_BYTE_1_WILD; } } else { if (pByte[0] == g_aParameters[ PARAM_MEM_SEARCH_WILD ].m_sName[0]) // Hack: hard-coded one char token { ms.m_iType = MEM_SEARCH_NIB_LOW_EXACT; ms.m_nValue = pArg->nValue & 0x0F; } if (pByte[1] == g_aParameters[ PARAM_MEM_SEARCH_WILD ].m_sName[0]) // Hack: hard-coded one char token { if (ms.m_iType == MEM_SEARCH_NIB_LOW_EXACT) { ms.m_iType = MEM_SEARCH_BYTE_N_WILD; } else { ms.m_iType = MEM_SEARCH_NIB_HIGH_EXACT; ms.m_nValue = (pArg->nValue << 4) & 0xF0; } } } } } // skip over multiple byte_wild, since they are redundent // xx ?? ?? xx // ^ // redundant if ((tLastType == MEM_SEARCH_BYTE_N_WILD) && (ms.m_iType == MEM_SEARCH_BYTE_N_WILD)) continue; vMemorySearchValues.push_back( ms ); tLastType = ms.m_iType; } _SearchMemoryFind( vMemorySearchValues, nAddressStart, nAddressEnd ); vMemorySearchValues.clear(); return _SearchMemoryDisplay(); } //=========================================================================== Update_t CmdMemorySearch (int nArgs) { // S address,length # [,#] if (nArgs < 4) return HelpLastCommand(); return _CmdMemorySearch( nArgs, true ); return UPDATE_CONSOLE_DISPLAY; } // Search for ASCII text (no Hi-Bit set) //=========================================================================== Update_t CmdMemorySearchAscii (int nArgs) { if (nArgs < 4) return HelpLastCommand(); return _CmdMemorySearch( nArgs, true ); } // Search for Apple text (Hi-Bit set) //=========================================================================== Update_t CmdMemorySearchApple (int nArgs) { if (nArgs < 4) return HelpLastCommand(); return _CmdMemorySearch( nArgs, false ); } //=========================================================================== Update_t CmdMemorySearchHex (int nArgs) { if (nArgs < 4) return HelpLastCommand(); return _CmdMemorySearch( nArgs, true ); } // Registers ______________________________________________________________________________________ //=========================================================================== Update_t CmdRegisterSet (int nArgs) { if (nArgs < 2) // || ((g_aArgs[2].sArg[0] != TEXT('0')) && !g_aArgs[2].nValue)) { return Help_Arg_1( CMD_REGISTER_SET ); } else { TCHAR *pName = g_aArgs[1].sArg; int iParam; if (FindParam( pName, MATCH_EXACT, iParam, _PARAM_REGS_BEGIN, _PARAM_REGS_END )) { int iArg = 2; if (g_aArgs[ iArg ].eToken == TOKEN_EQUAL) iArg++; if (iArg > nArgs) return Help_Arg_1( CMD_REGISTER_SET ); BYTE b = (BYTE)(g_aArgs[ iArg ].nValue & 0xFF); WORD w = (WORD)(g_aArgs[ iArg ].nValue & 0xFFFF); switch (iParam) { case PARAM_REG_A : regs.a = b; break; case PARAM_REG_PC: regs.pc = w; g_nDisasmCurAddress = regs.pc; DisasmCalcTopBotAddress(); break; case PARAM_REG_SP: regs.sp = b | 0x100; break; case PARAM_REG_X : regs.x = b; break; case PARAM_REG_Y : regs.y = b; break; default: return Help_Arg_1( CMD_REGISTER_SET ); } } } // g_nDisasmCurAddress = regs.pc; // DisasmCalcTopBotAddress(); return UPDATE_ALL; // 1 } // Output _________________________________________________________________________________________ //=========================================================================== Update_t CmdOutputCalc (int nArgs) { const int nBits = 8; if (! nArgs) return Help_Arg_1( CMD_OUTPUT_CALC ); WORD nAddress = g_aArgs[1].nValue; bool bHi = false; bool bLo = false; char c = FormatChar4Font( (BYTE) nAddress, &bHi, &bLo ); bool bParen = bHi || bLo; int nBit = 0; int iBit = 0; for ( iBit = 0; iBit < nBits; iBit++ ) { bool bSet = (nAddress >> iBit) & 1; if (bSet) nBit |= (1 << (iBit * 4)); // 4 bits per hex digit } // TODO: Colorize output // CHC_NUM_HEX // CHC_NUM_BIN -- doesn't exist, use CHC_INFO // CHC_NUM_DEC // CHC_ARG_ // CHC_STRING std::string sText = StrFormat( "$%04X 0z%08X %5d '%c' ", nAddress, nBit, nAddress, c ); if (bParen) sText += '('; if (bHi && bLo) sText += "High Ctrl"; else if (bHi) sText += "High"; else if (bLo) sText += "Ctrl"; if (bParen) sText += ')'; ConsoleBufferPush( sText.c_str() ); // If we colorize then w must also guard against character ouput $60 // ConsolePrint( sText.c_str() ); return ConsoleUpdate(); } //=========================================================================== Update_t CmdOutputEcho (int nArgs) { if (g_aArgs[1].bType & TYPE_QUOTED_2) { ConsoleDisplayPush( g_aArgs[1].sArg ); } else { const char *pText = g_pConsoleFirstArg; // ConsoleInputPeek(); if (pText) { ConsoleDisplayPush( pText ); } } return ConsoleUpdate(); } enum PrintState_e { PS_LITERAL , PS_TYPE , PS_ESCAPE , PS_NEXT_ARG_BIN , PS_NEXT_ARG_HEX , PS_NEXT_ARG_DEC , PS_NEXT_ARG_CHR }; struct PrintFormat_t { int nValue; int eType; }; //=========================================================================== Update_t CmdOutputPrint (int nArgs) { // PRINT "A:",A," X:",X // Removed: PRINT "A:%d",A," X: %d",X std::string sText; if (nArgs <= 0) goto _Help; for ( int iArg = 1; iArg <= nArgs; iArg++ ) { sText += (!!(g_aArgs[ iArg ].bType & TYPE_QUOTED_2)) ? g_aArgs[ iArg ].sArg : WordToHexStr( g_aArgs[ iArg ].nValue ); iArg++; //if (iArg > nArgs) // goto _Help; if (iArg <= nArgs && g_aArgs[ iArg ].eToken != TOKEN_COMMA) goto _Help; } if (!sText.empty()) ConsoleBufferPush( sText.c_str() ); return ConsoleUpdate(); _Help: return Help_Arg_1( CMD_OUTPUT_PRINT ); } //=========================================================================== Update_t CmdOutputPrintf (int nArgs) { // PRINTF "A:%d X:%d",A,X // PRINTF "Hex:%x Dec:%d Bin:%z",A,A,A if (! nArgs) return Help_Arg_1( CMD_OUTPUT_PRINTF ); std::vector aValues; for ( int iArg = 1; iArg <= nArgs; iArg++ ) { if (g_aArgs[ iArg ].bType & TYPE_QUOTED_2) continue; else if (g_aArgs[ iArg ].eToken == TOKEN_COMMA) continue; else { Arg_t entry; entry.nValue = g_aArgs[ iArg ].nValue; aValues.push_back( entry ); } } std::string sText; sText.reserve(CONSOLE_WIDTH); PrintState_e eThis = PS_LITERAL; int nWidth = 0; const size_t nParamValues = aValues.size(); size_t iValue = 0; for ( int iArg = 1; iArg <= nArgs; iArg++ ) { if (g_aArgs[ iArg ].bType & TYPE_QUOTED_2) { for ( const char* cp = g_aArgs[iArg].sArg, *ep = cp + strlen(g_aArgs[iArg].sArg); cp < ep; ++cp ) { const char c = *cp; switch ( eThis ) { case PS_LITERAL: switch ( c ) { case '\\': eThis = PS_ESCAPE; break; case '%': eThis = PS_TYPE; break; default: sText += c; break; } break; case PS_ESCAPE: switch ( c ) { case 'n': case 'r': eThis = PS_LITERAL; sText += '\n'; break; } break; case PS_TYPE: if (iValue >= nParamValues) { ConsoleBufferPushFormat( "Error: Missing value arg: %" SIZE_T_FMT, iValue + 1 ); return ConsoleUpdate(); } switch ( c ) { case 'X': case 'x': // PS_NEXT_ARG_HEX sText += WordToHexStr( aValues[ iValue ].nValue ); iValue++; break; case 'D': case 'd': // PS_NEXT_ARG_DEC sText += StrFormat( "%d", aValues[ iValue ].nValue ); iValue++; break; break; case 'Z': case 'z': { const int nValue = aValues[ iValue ].nValue; if (!nWidth) nWidth = 8; int nBits = nWidth; while (nBits-- > 0) { sText += ((nValue >> nBits) & 1) ? '1' : '0'; } iValue++; break; } case 'c': // PS_NEXT_ARG_CHR; sText += char( aValues[ iValue ].nValue ); iValue++; break; case '%': default: sText += c; break; } eThis = PS_LITERAL; break; default: break; } } } else if (g_aArgs[ iArg ].eToken == TOKEN_COMMA) { iArg++; if (iArg > nArgs) goto _Help; } else goto _Help; } if (!sText.empty()) ConsoleBufferPush( sText.c_str() ); return ConsoleUpdate(); _Help: return Help_Arg_1( CMD_OUTPUT_PRINTF ); } //=========================================================================== Update_t CmdOutputRun (int nArgs) { g_bScriptReadOk = false; if (! nArgs) return Help_Arg_1( CMD_OUTPUT_RUN ); if (nArgs != 1) return Help_Arg_1( CMD_OUTPUT_RUN ); // Read in script // could be made global, to cache last run. // Opens up the possibility of: // CHEAT [ON | OFF] -> re-run script // with conditional logic // IF @ON .... MemoryTextFile_t script; const std::string pFileName = g_aArgs[ 1 ].sArg; std::string sFileName; std::string sMiniFileName; // [CONSOLE_WIDTH]; // if (g_aArgs[1].bType & TYPE_QUOTED_2) sMiniFileName = pFileName.substr(0, MIN(pFileName.size(), CONSOLE_WIDTH)); // strcat( sMiniFileName, ".aws" ); // HACK: MAGIC STRING if (pFileName[0] == PATH_SEPARATOR || pFileName[1] == ':') // NB. Any prefix quote has already been stripped { // Abs pathname sFileName = sMiniFileName; } else { // Rel pathname sFileName = g_sCurrentDir + sMiniFileName; } if (script.Read( sFileName )) { g_bScriptReadOk = true; int nLine = script.GetNumLines(); Update_t bUpdateDisplay = UPDATE_NOTHING; for ( int iLine = 0; iLine < nLine; iLine++ ) { script.GetLine( iLine, g_pConsoleInput, CONSOLE_WIDTH-2 ); g_nConsoleInputChars = _tcslen( g_pConsoleInput ); bUpdateDisplay |= DebuggerProcessCommand( false ); } } else { ConsolePrintFormat("%sCouldn't load filename:", CHC_ERROR); ConsolePrintFormat("%s%s", CHC_STRING, sFileName.c_str()); } return ConsoleUpdate(); } // Source Level Debugging _________________________________________________________________________ //=========================================================================== bool BufferAssemblyListing( const std::string & pFileName ) { bool bStatus = false; // true = loaded if (pFileName.empty()) return bStatus; g_AssemblerSourceBuffer.Reset(); g_AssemblerSourceBuffer.Read( pFileName ); if (g_AssemblerSourceBuffer.GetNumLines()) { g_bSourceLevelDebugging = true; bStatus = true; } return bStatus; } //=========================================================================== int FindSourceLine( WORD nAddress ) { int iAddress = 0; int iLine = 0; int iSourceLine = NO_SOURCE_LINE; // iterate of // probably should be sorted by address // then can do binary search // iSourceLine = g_aSourceDebug.find( nAddress ); #if 0 // _DEBUG { for (int i = 0; i < g_vSourceLines.size(); i++ ) { LogOutput( "%d: %s\n", i, g_vSourceLines[ i ] ); } } #endif SourceAssembly_t::iterator iSource = g_aSourceDebug.begin(); while (iSource != g_aSourceDebug.end() ) { iAddress = iSource->first; iLine = iSource->second; #if 0 // _DEBUG LogOutput( "%04X -> %d line\n", iAddress, iLine ); #endif if (iAddress == nAddress) { iSourceLine = iLine; break; } iSource++; } // not found return iSourceLine; } //=========================================================================== bool ParseAssemblyListing( bool bBytesToMemory, bool bAddSymbols ) { bool bStatus = false; // true = loaded // Assembler source listing file: // // xxxx:_b1_[b2]_[b3]__n_[label]_[opcode]_[param] // char sByte1[ 2 ]; // char sByte2[ 2 ]; // char sByte3[ 2 ]; // char sLineN[ W ]; char sName[ MAX_SYMBOLS_LEN ]; // char sAsm [ W ]; // char sParam[ W ]; const int MAX_LINE = 256; char sLine[ MAX_LINE ]; char sText[ MAX_LINE ]; // char sLabel[ MAX_LINE ]; g_nSourceAssembleBytes = 0; g_nSourceAssemblySymbols = 0; const DWORD INVALID_ADDRESS = _6502_MEM_END + 1; int nLines = g_AssemblerSourceBuffer.GetNumLines(); for ( int iLine = 0; iLine < nLines; iLine++ ) { g_AssemblerSourceBuffer.GetLine( iLine, sText, MAX_LINE - 1 ); DWORD nAddress = INVALID_ADDRESS; _tcscpy( sLine, sText ); char *p = sLine; p = strstr( sLine, ":" ); if (p) { *p = 0; // sscanf( sLine, "%s %s %s %s %s %s %s %s", sAddr1, sByte1, sByte2, sByte3, sLineN, sLabel, sAsm, sParam ); sscanf( sLine, "%X", &nAddress ); if (nAddress >= INVALID_ADDRESS) // || (sName[0] == 0) ) continue; if (bBytesToMemory) { char *pEnd = p + 1; char *pStart; int iByte; for (iByte = 0; iByte < 4; iByte++ ) // BUG: Some assemblers also put 4 bytes on a line { // xx xx xx // ^ ^ // | | // | end // start pStart = pEnd + 1; pEnd = const_cast( SkipUntilWhiteSpace( pStart )); int nLen = (pEnd - pStart); if (nLen != 2) { break; } *pEnd = 0; if (TextIsHexByte( pStart )) { BYTE nByte = TextConvert2CharsToByte( pStart ); *(mem + ((WORD)nAddress) + iByte ) = nByte; } } g_nSourceAssembleBytes += iByte; } g_aSourceDebug[ (WORD) nAddress ] = iLine; // g_nSourceAssemblyLines; } _tcscpy( sLine, sText ); if (bAddSymbols) { // Add user symbol: symbolname EQU $address // or user symbol: address: symbolname DFB #bytes char *pEQU = strstr( sLine, "EQU" ); // EQUal / EQUate char *pDFB = strstr( sLine, "DFB" ); // DeFine Byte char *pLabel = NULL; if (pEQU) pLabel = pEQU; if (pDFB) pLabel = pDFB; if (pLabel) { char *pLabelEnd = pLabel - 1; pLabelEnd = const_cast( SkipWhiteSpaceReverse( pLabelEnd, &sLine[ 0 ] )); char * pLabelStart = NULL; // SkipWhiteSpaceReverse( pLabelEnd, &sLine[ 0 ] ); if (pLabelEnd) { pLabelStart = const_cast( SkipUntilWhiteSpaceReverse( pLabelEnd, &sLine[ 0 ] )); pLabelEnd++; pLabelStart++; int nLen = pLabelEnd - pLabelStart; nLen = MIN( nLen, MAX_SYMBOLS_LEN ); strncpy( sName, pLabelStart, nLen ); sName[ nLen - 1 ] = 0; char *pAddressEQU = strstr( pLabel, "$" ); char *pAddressDFB = strstr( sLine, ":" ); // Get address from start of line char *pAddress = NULL; if (pAddressEQU) pAddress = pAddressEQU + 1; if (pAddressDFB) { *pAddressDFB = 0; pAddress = sLine; } if (pAddress) { char *pAddressEnd; nAddress = (DWORD) strtol( pAddress, &pAddressEnd, 16 ); g_aSymbols[ SYMBOLS_SRC_2 ][ (WORD) nAddress] = sName; g_nSourceAssemblySymbols++; } } } } } // for bStatus = true; return bStatus; } //=========================================================================== Update_t CmdSource (int nArgs) { if (! nArgs) { g_bSourceLevelDebugging = false; } else { g_bSourceAddMemory = false; g_bSourceAddSymbols = false; for ( int iArg = 1; iArg <= nArgs; iArg++ ) { const std::string pFileName = g_aArgs[ iArg ].sArg; int iParam; bool bFound = FindParam( pFileName.c_str(), MATCH_EXACT, iParam, _PARAM_SOURCE_BEGIN, _PARAM_SOURCE_END ) > 0 ? true : false; if (bFound && (iParam == PARAM_SRC_SYMBOLS)) { g_bSourceAddSymbols = true; } else if (bFound && (iParam == PARAM_SRC_MEMORY)) { g_bSourceAddMemory = true; } else { const std::string sFileName = g_sProgramDir + pFileName; const int MAX_MINI_FILENAME = 20; const std::string sMiniFileName = sFileName.substr(0, MIN(MAX_MINI_FILENAME, sFileName.size())); if (BufferAssemblyListing( sFileName )) { g_aSourceFileName = pFileName; if (! ParseAssemblyListing( g_bSourceAddMemory, g_bSourceAddSymbols )) { ConsoleBufferPushFormat( "Couldn't load filename: %s", sMiniFileName.c_str() ); } else { if (g_nSourceAssembleBytes) { ConsoleBufferPushFormat( " Read: %d lines, %d symbols, %d bytes" , g_AssemblerSourceBuffer.GetNumLines() // g_nSourceAssemblyLines , g_nSourceAssemblySymbols, g_nSourceAssembleBytes ); } else { ConsoleBufferPushFormat( " Read: %d lines, %d symbols" , g_AssemblerSourceBuffer.GetNumLines() // g_nSourceAssemblyLines , g_nSourceAssemblySymbols ); } } } else { ConsoleBufferPushFormat( "Error reading: %s", sMiniFileName.c_str() ); } } } return ConsoleUpdate(); } return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdSync (int nArgs) { // TODO return UPDATE_CONSOLE_DISPLAY; } // Stack __________________________________________________________________________________________ //=========================================================================== Update_t CmdStackPush (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdStackPop (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdStackPopPseudo (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } // Video __________________________________________________________________________________________ Update_t CmdVideoScannerInfo(int nArgs) { if (nArgs != 1) { return Help_Arg_1(CMD_VIDEO_SCANNER_INFO); } else { if (strcmp(g_aArgs[1].sArg, "dec") == 0) g_videoScannerDisplayInfo.isDecimal = true; else if (strcmp(g_aArgs[1].sArg, "hex") == 0) g_videoScannerDisplayInfo.isDecimal = false; else if (strcmp(g_aArgs[1].sArg, "real") == 0) g_videoScannerDisplayInfo.isHorzReal = true; else if (strcmp(g_aArgs[1].sArg, "apple") == 0) g_videoScannerDisplayInfo.isHorzReal = false; else return Help_Arg_1(CMD_VIDEO_SCANNER_INFO); } ConsoleBufferPushFormat("Video-scanner display updated: %s", g_aArgs[1].sArg); ConsoleBufferToDisplay(); return UPDATE_ALL; } // Cycles __________________________________________________________________________________________ Update_t CmdCyclesInfo(int nArgs) { if (nArgs != 1) { return Help_Arg_1(CMD_CYCLES_INFO); } else { if (strcmp(g_aArgs[1].sArg, "abs") == 0) g_videoScannerDisplayInfo.cycleMode = VideoScannerDisplayInfo::abs; else if (strcmp(g_aArgs[1].sArg, "rel") == 0) g_videoScannerDisplayInfo.cycleMode = VideoScannerDisplayInfo::rel; else if (strcmp(g_aArgs[1].sArg, "part") == 0) g_videoScannerDisplayInfo.cycleMode = VideoScannerDisplayInfo::part; else return Help_Arg_1(CMD_CYCLES_INFO); if (g_videoScannerDisplayInfo.cycleMode == VideoScannerDisplayInfo::part) CmdCyclesReset(0); } ConsoleBufferPushFormat("Cycles display updated: %s", g_aArgs[1].sArg); ConsoleBufferToDisplay(); return UPDATE_ALL; } Update_t CmdCyclesReset(int /*nArgs*/) { g_videoScannerDisplayInfo.savedCumulativeCycles = g_nCumulativeCycles; return UPDATE_ALL; } // View ___________________________________________________________________________________________ // See: CmdWindowViewOutput (int nArgs) enum ViewVideoPage_t { VIEW_PAGE_X, // current page VIEW_PAGE_0, // Pseudo VIEW_PAGE_1, VIEW_PAGE_2, VIEW_PAGE_3, // Pseudo VIEW_PAGE_4, // Pseudo VIEW_PAGE_5 // Pseudo }; Update_t _ViewOutput( ViewVideoPage_t iPage, int bVideoModeFlags ) { switch ( iPage ) { case VIEW_PAGE_X: bVideoModeFlags |= !GetVideo().VideoGetSWPAGE2() ? 0 : VF_PAGE2; bVideoModeFlags |= !GetVideo().VideoGetSWMIXED() ? 0 : VF_MIXED; break; // Page Current & current MIXED state case VIEW_PAGE_0: bVideoModeFlags |= VF_PAGE0; break; // Pseudo Page 0 ($0000) case VIEW_PAGE_1: bVideoModeFlags |= 0 ; break; // Hardware Page 1 ($2000), NOTE: VF_HIRES will be passed in case VIEW_PAGE_2: bVideoModeFlags |= VF_PAGE2; break; // Hardware Page 2 ($4000) case VIEW_PAGE_3: bVideoModeFlags |= VF_PAGE3; break; // Pseudo Page 3 ($6000) case VIEW_PAGE_4: bVideoModeFlags |= VF_PAGE4; break; // Pseudo Page 4 ($8000) case VIEW_PAGE_5: bVideoModeFlags |= VF_PAGE5; break; // Pseudo Page 5 ($A000) default: _ASSERT(0); break; } DebugVideoMode::Instance().Set(bVideoModeFlags); GetFrame().VideoRefreshScreen( bVideoModeFlags, true ); return UPDATE_NOTHING; // intentional } // Text 40 Update_t CmdViewOutput_Text4X (int nArgs) { return _ViewOutput( VIEW_PAGE_X, VF_TEXT ); } Update_t CmdViewOutput_Text41 (int nArgs) { return _ViewOutput( VIEW_PAGE_1, VF_TEXT ); } Update_t CmdViewOutput_Text42 (int nArgs) { return _ViewOutput( VIEW_PAGE_2, VF_TEXT ); } // Text 80 Update_t CmdViewOutput_Text8X (int nArgs) { return _ViewOutput( VIEW_PAGE_X, VF_TEXT | VF_80COL ); } Update_t CmdViewOutput_Text81 (int nArgs) { return _ViewOutput( VIEW_PAGE_1, VF_TEXT | VF_80COL ); } Update_t CmdViewOutput_Text82 (int nArgs) { return _ViewOutput( VIEW_PAGE_2, VF_TEXT | VF_80COL ); } // Lo-Res Update_t CmdViewOutput_GRX (int nArgs) { return _ViewOutput( VIEW_PAGE_X, 0 ); } Update_t CmdViewOutput_GR1 (int nArgs) { return _ViewOutput( VIEW_PAGE_1, 0 ); } Update_t CmdViewOutput_GR2 (int nArgs) { return _ViewOutput( VIEW_PAGE_2, 0 ); } // Double Lo-Res Update_t CmdViewOutput_DGRX (int nArgs) { return _ViewOutput( VIEW_PAGE_X, VF_DHIRES | VF_80COL ); } Update_t CmdViewOutput_DGR1 (int nArgs) { return _ViewOutput( VIEW_PAGE_1, VF_DHIRES | VF_80COL ); } Update_t CmdViewOutput_DGR2 (int nArgs) { return _ViewOutput( VIEW_PAGE_2, VF_DHIRES | VF_80COL ); } // Hi-Res Update_t CmdViewOutput_HGRX (int nArgs) { return _ViewOutput( VIEW_PAGE_X, VF_HIRES ); } Update_t CmdViewOutput_HGR0 (int nArgs) { return _ViewOutput( VIEW_PAGE_0, VF_HIRES ); // Pseudo page ($0000) } Update_t CmdViewOutput_HGR1 (int nArgs) { return _ViewOutput( VIEW_PAGE_1, VF_HIRES ); } Update_t CmdViewOutput_HGR2 (int nArgs) { return _ViewOutput( VIEW_PAGE_2, VF_HIRES ); } Update_t CmdViewOutput_HGR3 (int nArgs) { return _ViewOutput( VIEW_PAGE_3, VF_HIRES ); // Pseudo page ($6000) } Update_t CmdViewOutput_HGR4 (int nArgs) { return _ViewOutput( VIEW_PAGE_4, VF_HIRES ); // Pseudo page ($8000) } Update_t CmdViewOutput_HGR5 (int nArgs) { return _ViewOutput( VIEW_PAGE_5, VF_HIRES ); // Pseudo page ($A000) } // Double Hi-Res Update_t CmdViewOutput_DHGRX (int nArgs) { return _ViewOutput( VIEW_PAGE_X, VF_HIRES | VF_DHIRES | VF_80COL ); } Update_t CmdViewOutput_DHGR1 (int nArgs) { return _ViewOutput( VIEW_PAGE_1, VF_HIRES | VF_DHIRES | VF_80COL); } Update_t CmdViewOutput_DHGR2 (int nArgs) { return _ViewOutput( VIEW_PAGE_2, VF_HIRES | VF_DHIRES | VF_80COL ); } // Super Hi-Res Update_t CmdViewOutput_SHR(int nArgs) { return _ViewOutput( VIEW_PAGE_1, VF_SHR ); } // Watches ________________________________________________________________________________________ //=========================================================================== Update_t CmdWatchAdd (int nArgs) { // WA [address] // WA # address if (!nArgs) { return CmdWatchList(0); } int iArg = 1; int iWatch = NO_6502_TARGET; if (nArgs > 1) { iWatch = g_aArgs[1].nValue; if (iWatch >= MAX_WATCHES) { ConsoleDisplayPushFormat("Watch index too big. (Max: %d)", MAX_WATCHES - 1); return ConsoleUpdate(); } iArg++; } bool bAdded = false; for (; iArg <= nArgs; iArg++ ) { if (g_aArgs[iArg].eToken == TOKEN_ALPHANUMERIC && g_aArgs[iArg].sArg[0] == 'v') // 'video' ? { g_aWatches[iWatch].bSet = true; g_aWatches[iWatch].bEnabled = true; g_aWatches[iWatch].eSource = BP_SRC_VIDEO_SCANNER; g_aWatches[iWatch].nAddress = 0xDEAD; bAdded = true; g_nWatches++; iWatch++; continue; } WORD nAddress = g_aArgs[iArg].nValue; // Make sure address isn't an IO address if ((nAddress >= _6502_IO_BEGIN) && (nAddress <= _6502_IO_END)) return ConsoleDisplayError("You cannot watch an I/O location."); if (iWatch == NO_6502_TARGET) { iWatch = 0; while ((iWatch < MAX_WATCHES) && (g_aWatches[iWatch].bSet)) { iWatch++; } } if ((iWatch >= MAX_WATCHES) && !bAdded) { ConsoleDisplayPushFormat("All watches are currently in use. (Max: %d)", MAX_WATCHES); return ConsoleUpdate(); } if ((iWatch < MAX_WATCHES) && (g_nWatches < MAX_WATCHES)) { g_aWatches[iWatch].bSet = true; g_aWatches[iWatch].bEnabled = true; g_aWatches[iWatch].eSource = BP_SRC_MEM_RW; g_aWatches[iWatch].nAddress = (WORD) nAddress; bAdded = true; g_nWatches++; iWatch++; } } if (!bAdded) goto _Help; return UPDATE_WATCH; _Help: return Help_Arg_1( CMD_WATCH_ADD ); } //=========================================================================== Update_t CmdWatchClear (int nArgs) { if (!g_nWatches) return ConsoleDisplayError("There are no watches defined."); if (!nArgs) return Help_Arg_1( CMD_WATCH_CLEAR ); _BWZ_ClearViaArgs( nArgs, g_aWatches, MAX_WATCHES, g_nWatches ); // if (! g_nWatches) // { // UpdateDisplay(UPDATE_BACKGROUND); // 1 // return UPDATE_NOTHING; // 0 // } return UPDATE_CONSOLE_DISPLAY | UPDATE_WATCH; // 1 } //=========================================================================== Update_t CmdWatchDisable (int nArgs) { if (! g_nWatches) return ConsoleDisplayError("There are no watches defined."); if (!nArgs) return Help_Arg_1( CMD_WATCH_DISABLE ); _BWZ_EnableDisableViaArgs( nArgs, g_aWatches, MAX_WATCHES, false ); return UPDATE_WATCH; } //=========================================================================== Update_t CmdWatchEnable (int nArgs) { if (! g_nWatches) return ConsoleDisplayError("There are no watches defined."); if (!nArgs) return Help_Arg_1( CMD_WATCH_ENABLE ); _BWZ_EnableDisableViaArgs( nArgs, g_aWatches, MAX_WATCHES, true ); return UPDATE_WATCH; } //=========================================================================== Update_t CmdWatchList (int nArgs) { if (! g_nWatches) { ConsoleBufferPushFormat( " There are no current watches. (Max: %d)", MAX_WATCHES ); } else { // _BWZ_List( g_aWatches, MAX_WATCHES ); _BWZ_ListAll( g_aWatches, MAX_WATCHES ); } return ConsoleUpdate(); } /* //=========================================================================== Update_t CmdWatchLoad (int nArgs) { if (!nArgs) return Help_Arg_1( CMD_WATCH_LOAD ); return UPDATE_CONSOLE_DISPLAY; } */ //=========================================================================== Update_t CmdWatchSave (int nArgs) { if (!nArgs) return Help_Arg_1( CMD_WATCH_SAVE ); return UPDATE_CONSOLE_DISPLAY; } // Window _________________________________________________________________________________________ //=========================================================================== void _WindowJoin () { g_aWindowConfig[ g_iWindowThis ].bSplit = false; } //=========================================================================== void _WindowSplit ( Window_e eNewBottomWindow ) { g_aWindowConfig[ g_iWindowThis ].bSplit = true; g_aWindowConfig[ g_iWindowThis ].eBot = eNewBottomWindow; } //=========================================================================== void _WindowLast () { int eNew = g_iWindowLast; g_iWindowLast = g_iWindowThis; g_iWindowThis = eNew; } //=========================================================================== void _WindowSwitch( int eNewWindow ) { g_iWindowLast = g_iWindowThis; g_iWindowThis = eNewWindow; } //=========================================================================== Update_t _CmdWindowViewCommon ( int iNewWindow ) { // Switching to same window, remove split if (g_iWindowThis == iNewWindow) { g_aWindowConfig[ iNewWindow ].bSplit = false; } else { _WindowSwitch( iNewWindow ); } // WindowUpdateConsoleDisplayedSize(); WindowUpdateSizes(); return UPDATE_ALL; } //=========================================================================== Update_t _CmdWindowViewFull ( int iNewWindow ) { if (g_iWindowThis != iNewWindow) { g_aWindowConfig[ iNewWindow ].bSplit = false; _WindowSwitch( iNewWindow ); WindowUpdateConsoleDisplayedSize(); } return UPDATE_ALL; } //=========================================================================== void WindowUpdateConsoleDisplayedSize() { g_nConsoleDisplayLines = MIN_DISPLAY_CONSOLE_LINES; #if USE_APPLE_FONT g_bConsoleFullWidth = true; g_nConsoleDisplayWidth = CONSOLE_WIDTH - 1; if (g_iWindowThis == WINDOW_CONSOLE) { g_nConsoleDisplayLines = MAX_DISPLAY_LINES; g_nConsoleDisplayWidth = CONSOLE_WIDTH - 1; g_bConsoleFullWidth = true; } #else g_nConsoleDisplayWidth = (CONSOLE_WIDTH / 2) + 10; g_bConsoleFullWidth = false; // g_bConsoleFullWidth = false; // g_nConsoleDisplayWidth = CONSOLE_WIDTH - 10; if (g_iWindowThis == WINDOW_CONSOLE) { g_nConsoleDisplayLines = MAX_DISPLAY_LINES; g_nConsoleDisplayWidth = CONSOLE_WIDTH - 1; g_bConsoleFullWidth = true; } #endif } //=========================================================================== int WindowGetHeight( int iWindow ) { // if (iWindow == WINDOW_CODE) return g_nDisasmWinHeight; } //=========================================================================== void WindowUpdateDisasmSize() { if (g_aWindowConfig[ g_iWindowThis ].bSplit) { g_nDisasmWinHeight = (MAX_DISPLAY_LINES - g_nConsoleDisplayLines) / 2; } else { g_nDisasmWinHeight = MAX_DISPLAY_LINES - g_nConsoleDisplayLines; } g_nDisasmCurLine = MAX(0, (g_nDisasmWinHeight - 1) / 2); #if _DEBUG #endif } //=========================================================================== void WindowUpdateSizes() { WindowUpdateDisasmSize(); WindowUpdateConsoleDisplayedSize(); } //=========================================================================== Update_t CmdWindowCycleNext( int nArgs ) { g_iWindowThis++; if (g_iWindowThis >= NUM_WINDOWS) g_iWindowThis = 0; WindowUpdateSizes(); return UPDATE_ALL; } //=========================================================================== Update_t CmdWindowCyclePrev( int nArgs ) { g_iWindowThis--; if (g_iWindowThis < 0) g_iWindowThis = NUM_WINDOWS-1; WindowUpdateSizes(); return UPDATE_ALL; } //=========================================================================== Update_t CmdWindowShowCode (int nArgs) { // g_bWindowDisplayShowChild = false; // g_bWindowDisplayShowRoot = WINDOW_CODE; if (g_iWindowThis == WINDOW_CODE) { g_aWindowConfig[ g_iWindowThis ].bSplit = false; g_aWindowConfig[ g_iWindowThis ].eBot = WINDOW_CODE; // not really needed, but SAFE HEX ;-) } else if (g_iWindowThis == WINDOW_DATA) { g_aWindowConfig[ g_iWindowThis ].bSplit = true; g_aWindowConfig[ g_iWindowThis ].eBot = WINDOW_CODE; } WindowUpdateSizes(); return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowCode1 (int nArgs) { /* if ((g_iWindowThis == WINDOW_CODE) || (g_iWindowThis != WINDOW_DATA)) { g_aWindowConfig[ g_iWindowThis ].bSplit = true; g_aWindowConfig[ g_iWindowThis ].eTop = WINDOW_CODE; Window_e eWindow = WINDOW_CODE; if (g_iWindowThis == WINDOW_DATA) eWindow = WINDOW_DATA; g_aWindowConfig[ g_iWindowThis ].eBot = eWindow; return UPDATE_ALL; } */ return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowCode2 (int nArgs) { if ((g_iWindowThis == WINDOW_CODE) || (g_iWindowThis == WINDOW_DATA)) { if (g_iWindowThis == WINDOW_CODE) { _WindowJoin(); WindowUpdateDisasmSize(); } else if (g_iWindowThis == WINDOW_DATA) { _WindowSplit( WINDOW_CODE ); WindowUpdateDisasmSize(); } return UPDATE_DISASM; } return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowData (int nArgs) { if (g_iWindowThis == WINDOW_CODE) { g_aWindowConfig[ g_iWindowThis ].bSplit = true; g_aWindowConfig[ g_iWindowThis ].eBot = WINDOW_DATA; return UPDATE_ALL; } else if (g_iWindowThis == WINDOW_DATA) { g_aWindowConfig[ g_iWindowThis ].bSplit = false; g_aWindowConfig[ g_iWindowThis ].eBot = WINDOW_DATA; // not really needed, but SAFE HEX ;-) return UPDATE_ALL; } return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowData1 (int nArgs) { /* if (g_iWindowThis != PARAM_CODE_1) { g_iWindowLast = g_iWindowThis; g_iWindowThis = PARAM_DATA_1; return UPDATE_ALL; } */ return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowData2 (int nArgs) { if ((g_iWindowThis == WINDOW_CODE) || (g_iWindowThis == WINDOW_DATA)) { if (g_iWindowThis == WINDOW_CODE) { _WindowSplit( WINDOW_DATA ); } else if (g_iWindowThis == WINDOW_DATA) { _WindowJoin(); } return UPDATE_DISASM; } return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowSource (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowSource1 (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowShowSource2 (int nArgs) { _WindowSplit( WINDOW_SOURCE ); WindowUpdateSizes(); return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdWindowViewCode (int nArgs) { return _CmdWindowViewCommon( WINDOW_CODE ); } //=========================================================================== Update_t CmdWindowViewConsole (int nArgs) { return _CmdWindowViewFull( WINDOW_CONSOLE ); } //=========================================================================== Update_t CmdWindowViewData (int nArgs) { return _CmdWindowViewCommon( WINDOW_DATA ); } //=========================================================================== Update_t CmdWindowViewOutput (int nArgs) { GetFrame().VideoRedrawScreen(); DebugVideoMode::Instance().Set( GetVideo().GetVideoMode() ); return UPDATE_NOTHING; // intentional } //=========================================================================== Update_t CmdWindowViewSource (int nArgs) { return _CmdWindowViewFull( WINDOW_CONSOLE ); } //=========================================================================== Update_t CmdWindowViewSymbols (int nArgs) { return _CmdWindowViewFull( WINDOW_CONSOLE ); } //=========================================================================== Update_t CmdWindow (int nArgs) { if (!nArgs) return Help_Arg_1( CMD_WINDOW ); int iParam; TCHAR *pName = g_aArgs[1].sArg; int nFound = FindParam( pName, MATCH_EXACT, iParam, _PARAM_WINDOW_BEGIN, _PARAM_WINDOW_END ); if (nFound) { switch (iParam) { case PARAM_CODE : return CmdWindowViewCode(0) ; break; case PARAM_CONSOLE: return CmdWindowViewConsole(0); break; case PARAM_DATA : return CmdWindowViewData(0) ; break; // case PARAM_INFO : CmdWindowInfo(); break; case PARAM_SOURCE : return CmdWindowViewSource(0) ; break; case PARAM_SYMBOLS: return CmdWindowViewSymbols(0); break; default: return Help_Arg_1( CMD_WINDOW ); break; } } WindowUpdateConsoleDisplayedSize(); return UPDATE_ALL; } //=========================================================================== Update_t CmdWindowLast (int nArgs) { _WindowLast(); WindowUpdateConsoleDisplayedSize(); return UPDATE_ALL; } // ZeroPage _______________________________________________________________________________________ //=========================================================================== Update_t CmdZeroPageAdd (int nArgs) { // ZP [address] // ZP # address [address...] if (!nArgs) { return CmdZeroPageList(0); } int iArg = 1; int iZP = NO_6502_TARGET; if (nArgs > 1) { iZP = g_aArgs[1].nValue; if (iZP >= MAX_ZEROPAGE_POINTERS) { ConsoleDisplayPushFormat("Zero page pointer index too big. (Max: %d)", MAX_ZEROPAGE_POINTERS - 1); return ConsoleUpdate(); } iArg++; } bool bAdded = false; for (; iArg <= nArgs; iArg++ ) { WORD nAddress = g_aArgs[iArg].nValue; // Make sure address is a ZP address if (nAddress > _6502_ZEROPAGE_END) { ConsoleDisplayPushFormat("Zero page pointer must be in the range: [00..%02X].", _6502_ZEROPAGE_END); return ConsoleUpdate(); } if (iZP == NO_6502_TARGET) { iZP = 0; while ((iZP < MAX_ZEROPAGE_POINTERS) && (g_aZeroPagePointers[iZP].bSet)) { iZP++; } } if ((iZP >= MAX_ZEROPAGE_POINTERS) && !bAdded) { ConsoleDisplayPushFormat("All zero page pointers are currently in use. (Max: %d)", MAX_ZEROPAGE_POINTERS); return ConsoleUpdate(); } if ((iZP < MAX_ZEROPAGE_POINTERS) && (g_nZeroPagePointers < MAX_ZEROPAGE_POINTERS)) { g_aZeroPagePointers[iZP].bSet = true; g_aZeroPagePointers[iZP].bEnabled = true; g_aZeroPagePointers[iZP].nAddress = (BYTE) nAddress; bAdded = true; g_nZeroPagePointers++; iZP++; } } if (!bAdded) goto _Help; return UPDATE_ZERO_PAGE | ConsoleUpdate(); _Help: return Help_Arg_1( CMD_ZEROPAGE_POINTER_ADD ); } Update_t _ZeroPage_Error() { // return ConsoleDisplayError( "There are no (ZP) pointers defined." ); // ConsoleBufferPushFormat( " There are no current (ZP) pointers. (Max: %d)", MAX_ZEROPAGE_POINTERS ); return ConsoleDisplayErrorFormat( " There are no current (ZP) pointers. (Max: %d)", MAX_ZEROPAGE_POINTERS ); } //=========================================================================== Update_t CmdZeroPageClear (int nArgs) { if (!g_nZeroPagePointers) return _ZeroPage_Error(); // CHECK FOR ERRORS if (!nArgs) return Help_Arg_1( CMD_ZEROPAGE_POINTER_CLEAR ); _BWZ_ClearViaArgs( nArgs, g_aZeroPagePointers, MAX_ZEROPAGE_POINTERS, g_nZeroPagePointers ); if (!g_nZeroPagePointers) { UpdateDisplay( UPDATE_BACKGROUND ); return UPDATE_CONSOLE_DISPLAY; } return UPDATE_CONSOLE_DISPLAY | UPDATE_ZERO_PAGE; } //=========================================================================== Update_t CmdZeroPageDisable (int nArgs) { if (!nArgs) return Help_Arg_1( CMD_ZEROPAGE_POINTER_DISABLE ); if (! g_nZeroPagePointers) return _ZeroPage_Error(); _BWZ_EnableDisableViaArgs( nArgs, g_aZeroPagePointers, MAX_ZEROPAGE_POINTERS, false ); return UPDATE_ZERO_PAGE; } //=========================================================================== Update_t CmdZeroPageEnable (int nArgs) { if (! g_nZeroPagePointers) return _ZeroPage_Error(); if (!nArgs) return Help_Arg_1( CMD_ZEROPAGE_POINTER_ENABLE ); _BWZ_EnableDisableViaArgs( nArgs, g_aZeroPagePointers, MAX_ZEROPAGE_POINTERS, true ); return UPDATE_ZERO_PAGE; } //=========================================================================== Update_t CmdZeroPageList (int nArgs) { if (! g_nZeroPagePointers) { _ZeroPage_Error(); } else { _BWZ_ListAll( g_aZeroPagePointers, MAX_ZEROPAGE_POINTERS ); } return ConsoleUpdate(); } /* //=========================================================================== Update_t CmdZeroPageLoad (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } */ //=========================================================================== Update_t CmdZeroPageSave (int nArgs) { return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdZeroPagePointer (int nArgs) { // p[0..4] : disable // p[0..4] : enable if ( (nArgs != 0) && (nArgs != 1) ) return Help_Arg_1( g_iCommand ); // return DisplayHelp(CmdZeroPagePointer); // int nPtrNum = g_aArgs[0].sArg[1] - '0'; // HACK: hard-coded to command length int iZP = g_iCommand - CMD_ZEROPAGE_POINTER_0; if ( (iZP < 0) || (iZP >= MAX_ZEROPAGE_POINTERS) ) return Help_Arg_1( g_iCommand ); if (nArgs == 0) { g_aZeroPagePointers[iZP].bEnabled = false; } else { g_aZeroPagePointers[iZP].bSet = true; g_aZeroPagePointers[iZP].bEnabled = true; WORD nAddress = g_aArgs[1].nValue; g_aZeroPagePointers[iZP].nAddress = (BYTE) nAddress; } return UPDATE_ZERO_PAGE; } // Command Input __________________________________________________________________________________ // Note: Range is [iParamBegin,iParamEnd], not the usually (STL) expected [iParamBegin,iParamEnd) //=========================================================================== int FindParam(LPCTSTR pLookupName, Match_e eMatch, int & iParam_, int iParamBegin, int iParamEnd ) { int nFound = 0; int nLen = _tcslen( pLookupName ); int iParam = 0; if (! nLen) return nFound; #if ALLOW_INPUT_LOWERCASE eMatch = MATCH_FUZZY; #endif if (eMatch == MATCH_EXACT) { // while (iParam < NUM_PARAMS ) for (iParam = iParamBegin; iParam <= iParamEnd; iParam++ ) { TCHAR *pParamName = g_aParameters[iParam].m_sName; int eCompare = _tcsicmp(pLookupName, pParamName); if (! eCompare) // exact match? { nFound++; iParam_ = g_aParameters[iParam].iCommand; break; } } } else if (eMatch == MATCH_FUZZY) { #if ALLOW_INPUT_LOWERCASE TCHAR aLookup[ 256 ] = ""; for ( int i = 0; i < nLen; i++ ) { aLookup[ i ] = toupper( pLookupName[ i ] ); } #endif for (iParam = iParamBegin; iParam <= iParamEnd; iParam++ ) { TCHAR *pParamName = g_aParameters[ iParam ].m_sName; // _tcsnccmp #if ALLOW_INPUT_LOWERCASE if (! _tcsncmp(aLookup, pParamName ,nLen)) #else if (! _tcsncmp(pLookupName, pParamName ,nLen)) #endif { nFound++; iParam_ = g_aParameters[iParam].iCommand; if (!_tcsicmp(pLookupName, pParamName)) // exact match? { nFound = 1; // Exact match takes precidence over fuzzy matches break; } } } } return nFound; } //=========================================================================== int FindCommand( LPCTSTR pName, CmdFuncPtr_t & pFunction_, int * iCommand_ ) { g_vPotentialCommands.clear(); int nFound = 0; int nLen = _tcslen( pName ); int iCommand = 0; if (! nLen) return nFound; char sCommand[ CONSOLE_WIDTH ]; strcpy( sCommand, pName ); _strupr( sCommand ); while ((iCommand < NUM_COMMANDS_WITH_ALIASES)) // && (name[0] >= g_aCommands[iCommand].aName[0])) Command no longer in Alphabetical order { TCHAR *pCommandName = g_aCommands[iCommand].m_sName; // int iCmp = strcasecmp( sCommand, pCommandName, nLen ) if (! _tcsncmp(sCommand, pCommandName, nLen)) { pFunction_ = g_aCommands[iCommand].pFunction; if (pFunction_) { g_iCommand = g_aCommands[iCommand].iCommand; // Don't push the same comamnd/alias if already on the list if (std::find( g_vPotentialCommands.begin(), g_vPotentialCommands.end(), g_iCommand) == g_vPotentialCommands.end()) { nFound++; g_vPotentialCommands.push_back( g_iCommand ); if (iCommand_) *iCommand_ = iCommand; // !_tcscmp if (!_tcsicmp(sCommand, pCommandName)) // exact match? { // if (iCommand_) // *iCommand_ = iCommand; nFound = 1; // Exact match takes precidence over fuzzy matches g_vPotentialCommands.clear(); break; } } } } iCommand++; } // if (nFound == 1) // { // // } return nFound; } //=========================================================================== void DisplayAmbigiousCommands( int nFound ) { ConsolePrintFormat("Ambiguous " CHC_NUM_DEC "%" SIZE_T_FMT CHC_DEFAULT " Commands:" , g_vPotentialCommands.size() ); int iCommand = 0; while (iCommand < nFound) { std::string sPotentialCommands = CHC_COMMAND " "; while ((iCommand < nFound) && (sPotentialCommands.length() < size_t(g_nConsoleDisplayWidth))) { int const nCommand = g_vPotentialCommands[ iCommand ]; const char *const pName = g_aCommands[ nCommand ].m_sName; size_t const nLen = strlen( pName ); if ((sPotentialCommands.length() + nLen) >= (CONSOLE_WIDTH - 1)) break; sPotentialCommands.append(pName, nLen).append(1, ' '); iCommand++; } ConsolePrint( sPotentialCommands.c_str() ); } } bool IsHexDigit( char c ) { if ((c >= '0') && (c <= '9')) return true; else if ((c >= 'A') && (c <= 'F')) return true; else if ((c >= 'a') && (c <= 'f')) return true; return false; } //=========================================================================== Update_t ExecuteCommand (int nArgs) { Arg_t * pArg = & g_aArgs[ 0 ]; char * pCommand = & pArg->sArg[0]; CmdFuncPtr_t pFunction = NULL; int nFound = FindCommand( pCommand, pFunction ); // int nCookMask = (1 << NUM_TOKENS) - 1; // ArgToken_e used as bit mask! // BUGFIX: commands that are also valid hex addresses // ####:# [#] // #<#.#M int nLen = pArg->nArgLen; if ((! nFound) || (nLen < 6)) { // 2.7.0.36 Fixed: empty command was re-triggering previous command. Example: DW 6062, // test if ( nLen > 0 ) { // verify pCommand[ 0 .. (nLen-1) ] are hex digits bool bIsHex = false; char *pChar = pCommand; for (int iChar = 0; iChar < (nLen - 1); iChar++, pChar++ ) { bIsHex = IsHexDigit( *pChar ); if ( !bIsHex ) { break; } } if (bIsHex) { // Support Apple Monitor commands WORD nAddress = 0; // ####G -> JMP $address (exit debugger) // NB. AppleWin 'g','gg' commands handled via pFunction below if ((pCommand[nLen-1] == 'G') || (pCommand[nLen-1] == 'g')) { pCommand[nLen-1] = 0; ArgsGetValue( pArg, & nAddress ); regs.pc = nAddress; g_nAppMode = MODE_RUNNING; // exit the debugger nFound = 1; g_iCommand = CMD_OUTPUT_ECHO; // hack: don't cook args } else // ####L -> Unassemble $address if (((pCommand[nLen-1] == 'L') || (pCommand[nLen-1] == 'l'))&& (_stricmp("cl", pCommand) != 0)) // workaround for ambiguous "cl": must be handled by "clear flag" command { pCommand[nLen-1] = 0; ArgsGetValue( pArg, & nAddress ); g_iCommand = CMD_UNASSEMBLE; // replace: addrL // with: comamnd addr pArg[1] = pArg[0]; strcpy( pArg->sArg, g_aCommands[ g_iCommand ].m_sName ); pArg->nArgLen = strlen( pArg->sArg ); pArg++; pArg->nValue = nAddress; nArgs++; pFunction = g_aCommands[ g_iCommand ].pFunction; nFound = 1; } else // address: byte ... if ((pArg+1)->eToken == TOKEN_COLON) { g_iCommand = CMD_MEMORY_ENTER_BYTE; // replace: addr : // with: command addr pArg[1] = pArg[0]; strcpy( pArg->sArg, g_aCommands[ g_iCommand ].m_sName ); pArg->nArgLen = strlen( pArg->sArg ); // nCookMask &= ~ (1 << TOKEN_COLON); // nArgs++; pFunction = g_aCommands[ g_iCommand ].pFunction; nFound = 1; } else // #<#.#M if (pArg[1].eToken == TOKEN_LESS_THAN) { // Look for period nLen = pArg[2].nArgLen; char *pDst = pArg[0].sArg; char *pSrc = pArg[2].sArg; char *pEnd = 0; bool bFoundSrc = false; bool bFoundLen = false; pChar = pSrc; while ( *pChar ) { if ( *pChar == '.' ) { if ( pEnd ) // only allowed one period { pEnd = 0; break; } *pChar = 0; // ':'; pEnd = pChar + 1; bFoundSrc = true; } else if ( !IsHexDigit( *pChar ) ) { break; } pChar++; } if ( pEnd ) { if ( (*pChar == 'M') || (*pChar == 'm')) { *pChar++ = 0; if ( ! *pChar ) bFoundLen = true; } if ( bFoundSrc && bFoundLen ) { //ArgsGetValue( pArg, & nAddress ); //ConsolePrintFormat( "Dst:%s Src: %s End: %s", pDst, pSrc, pEnd ); g_iCommand = CMD_MEMORY_MOVE; pFunction = g_aCommands[ g_iCommand ].pFunction; strcpy( pArg[4].sArg, pEnd ); strcpy( pArg[3].sArg, g_aTokens[ TOKEN_COLON ].sToken ); strcpy( pArg[2].sArg, pSrc ); strcpy( pArg[1].sArg, pDst ); strcpy( pArg[0].sArg, g_aCommands[ g_iCommand ].m_sName ); // pDst moved from arg0 to arg1 ! pArg[1].bType = TYPE_VALUE; pArg[2].bType = TYPE_VALUE; pArg[3].bType = TYPE_OPERATOR; pArg[4].bType = TYPE_VALUE; ArgsGetValue( &pArg[1], &pArg[1].nValue ); ArgsGetValue( &pArg[2], &pArg[2].nValue ); pArg[3].eToken = TOKEN_COLON; ArgsGetValue( &pArg[4], &pArg[4].nValue ); nFound = 1; nArgs = 4; } } } // TODO: display memory at address // addr1 [addr2] -> display byte at address // MDB memory display byte (is deprecated, so can be re-used) } } else { return UPDATE_CONSOLE_DISPLAY; } } if (nFound > 1) { // ASSERT (nFound == g_vPotentialCommands.size() ); DisplayAmbigiousCommands( nFound ); return ConsoleUpdate(); // return ConsoleDisplayError( gaPotentialCommands ); } if (nFound) { bool bCook = true; if (g_iCommand == CMD_OUTPUT_ECHO) bCook = false; int nArgsCooked = nArgs; if (bCook) nArgsCooked = ArgsCook( nArgs ); // nCookMask if (nArgsCooked == ARG_SYNTAX_ERROR) return ConsoleDisplayError( "Syntax Error" ); if (pFunction) return pFunction( nArgsCooked ); // Eat them return UPDATE_CONSOLE_DISPLAY; } else return ConsoleDisplayError( "Illegal Command" ); } // ________________________________________________________________________________________________ //=========================================================================== void OutputTraceLine () { if (!g_hTraceFile) return; DisasmLine_t line; GetDisassemblyLine( regs.pc, line ); // DrawDisassemblyLine( 0,regs.pc, sDisassembly); // Get Disasm String std::string sDisassembly = FormatDisassemblyLine( line ); char sFlags[] = "........"; WORD nRegFlags = regs.ps; int nFlag = _6502_NUM_FLAGS; while (nFlag--) { int iFlag = (_6502_NUM_FLAGS - nFlag - 1); bool bSet = (nRegFlags & 1); if (bSet) sFlags[nFlag] = g_aBreakpointSource[BP_SRC_FLAG_C + iFlag][0]; nRegFlags >>= 1; } if (g_bTraceHeader) { g_bTraceHeader = false; if (g_bTraceFileWithVideoScanner) { fprintf( g_hTraceFile, // "0000 0000 0000 00 00 00 00 0000 -------- 0000:90 90 90 NOP" "Vert Horz Addr Data A: X: Y: SP: Flags Addr:Opcode Mnemonic\n"); } else { fprintf( g_hTraceFile, // "00000000 00 00 00 0000 -------- 0000:90 90 90 NOP" "Cycles A: X: Y: SP: Flags Addr:Opcode Mnemonic\n"); } } //std::string const sTarget = (line.bTargetValue) // ? StrFormat( "%s:%s", line.sTargetPointer , line.sTargetValue ) // : std::string(); if (g_bTraceFileWithVideoScanner) { uint16_t vert, horz; NTSC_GetVideoVertHorzForDebugger(vert, horz); // update video scanner's vert/horz position - needed for when in fullspeed (GH#1164) uint32_t data; int dataSize; uint16_t addr = NTSC_GetScannerAddressAndData(data, dataSize); fprintf( g_hTraceFile, "%04X %04X %04X %02X %02X %02X %02X %04X %s %s\n", vert, horz, addr, (uint8_t)data, // truncated (unsigned)regs.a, (unsigned)regs.x, (unsigned)regs.y, (unsigned)regs.sp, sFlags , sDisassembly.c_str() //, sTarget.c_str() // TODO: Show target? ); } else { const UINT cycles = (UINT)g_nCumulativeCycles; fprintf( g_hTraceFile, "%08X %02X %02X %02X %04X %s %s\n", cycles, (unsigned)regs.a, (unsigned)regs.x, (unsigned)regs.y, (unsigned)regs.sp, sFlags , sDisassembly.c_str() //, sTarget.c_str() // TODO: Show target? ); } } //=========================================================================== int ParseInput ( LPTSTR pConsoleInput, bool bCook ) { int nArg = 0; // TODO: need to check for non-quoted command separator ';', and buffer input RemoveWhiteSpaceReverse( pConsoleInput ); ArgsClear(); nArg = ArgsGet( pConsoleInput ); // Get the Raw Args int iArg; for ( iArg = 0; iArg <= nArg; iArg++ ) { g_aArgs[ iArg ] = g_aArgRaw[ iArg ]; } return nArg; } //=========================================================================== void ParseParameter( ) { } // Return address of next line to write to. //=========================================================================== ProfileLine_t ProfileLinePeek ( int iLine ) { if (iLine < 0) iLine = 0; return ( g_nProfileLine == 0 || iLine <= g_nProfileLine ) ? ProfileLine_t( g_aProfileLine[ iLine ], sizeof(g_aProfileLine[iLine]) ) : ProfileLine_t(); } //=========================================================================== ProfileLine_t ProfileLinePush () { if (g_nProfileLine < NUM_PROFILE_LINES) { g_nProfileLine++; } return ProfileLinePeek( g_nProfileLine ); } void ProfileLineReset() { g_nProfileLine = 0; } #define DELIM "%s" //=========================================================================== void ProfileFormat( bool bExport, ProfileFormat_e eFormatMode ) { std::string sSeparator7; std::string sSeparator2; std::string sSeparator1; if (eFormatMode == PROFILE_FORMAT_COMMA) { sSeparator7 = ','; sSeparator2 = ','; sSeparator1 = ','; } else if (eFormatMode == PROFILE_FORMAT_SPACE) { sSeparator7.assign(7, ' '); sSeparator2.assign(2, ' '); sSeparator1 = ' '; } else { sSeparator7 = '\t'; sSeparator2 = '\t'; sSeparator1 = '\t'; } ProfileLineReset(); bool bOpcodeGood = true; bool bOpmodeGood = true; std::vector< ProfileOpcode_t > vProfileOpcode( &g_aProfileOpcodes[0], &g_aProfileOpcodes[ NUM_OPCODES ] ); std::vector< ProfileOpmode_t > vProfileOpmode( &g_aProfileOpmodes[0], &g_aProfileOpmodes[ NUM_OPMODES ] ); // sort > std::sort( vProfileOpcode.begin(), vProfileOpcode.end(), ProfileOpcode_t() ); std::sort( vProfileOpmode.begin(), vProfileOpmode.end(), ProfileOpmode_t() ); Profile_t nOpcodeTotal = 0; Profile_t nOpmodeTotal = 0; for ( int iOpcode = 0; iOpcode < NUM_OPCODES; ++iOpcode ) { nOpcodeTotal += vProfileOpcode[ iOpcode ].m_nCount; } for ( int iOpmode = 0; iOpmode < NUM_OPMODES; ++iOpmode ) { nOpmodeTotal += vProfileOpmode[ iOpmode ].m_nCount; } if (nOpcodeTotal < 1.) { nOpcodeTotal = 1; bOpcodeGood = false; } const char *pColorOperator = ""; const char *pColorNumber = ""; const char *pColorOpcode = ""; const char *pColorMnemonic = ""; const char *pColorOpmode = ""; const char *pColorTotal = ""; if (! bExport) { pColorOperator = CHC_ARG_SEP; // grey pColorNumber = CHC_NUM_DEC; // cyan pColorOpcode = CHC_NUM_HEX; // yellow pColorMnemonic = CHC_COMMAND; // green pColorOpmode = CHC_USAGE ; // yellow pColorTotal = CHC_DEFAULT; // white } ProfileLine_t prfline = ProfileLinePeek(0); // Opcode if (bExport) // Export = SeperateColumns prfline.Format( "\"Percent\"" DELIM "\"Count\"" DELIM "\"Opcode\"" DELIM "\"Mnemonic\"" DELIM "\"Addressing Mode\"\n", sSeparator7.c_str(), sSeparator2.c_str(), sSeparator1.c_str(), sSeparator1.c_str() ); else prfline.Format( "Percent" DELIM "Count" DELIM "Mnemonic" DELIM "Addressing Mode\n", sSeparator7.c_str(), sSeparator2.c_str(), sSeparator1.c_str() ); prfline = ProfileLinePush(); for ( int iOpcode = 0; iOpcode < NUM_OPCODES; ++iOpcode ) { ProfileOpcode_t tProfileOpcode = vProfileOpcode.at( iOpcode ); Profile_t nCount = tProfileOpcode.m_nCount; // Don't spam with empty data if dumping to the console if ((! nCount) && (! bExport)) continue; int nOpcode = tProfileOpcode.m_iOpcode; int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode; double nPercent = (100. * nCount) / nOpcodeTotal; //std::string sOpmode = StrFormat( g_aOpmodes[ nOpmode ].m_sFormat, 0 ); // Excel Bug: Quoted numbers are NOT treated as strings in .csv! WTF? // @reference: http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q214233 // // Workaround: Prefix with (') apostrophe -- this doesn't break HEX2DEC() // This works properly in Openoffice. // In Excel, this ONLY works IF you TYPE it in! // // Solution: Quote the numbers, but you must select the "TEXT" Column data format for the "Opcode" column. // We don't use .csv, since you aren't given the Import Dialog in Excel! // StrFormat( "'%02X", nOpcode ); // SHOULD work with Excel, but only works with OpenOffice. const std::string sOpcode = (bExport) ? StrFormat("\"%02X\"", nOpcode) // Works with Excel, IF using Import dialog & choose Text. (also works with OpenOffice) : ByteToHexStr(nOpcode); const std::string sAddress = (bExport) ? ( '"' + std::string(g_aOpmodes[nOpmode].m_sName) + '"' ) : std::string(g_aOpmodes[ nOpmode ].m_sName); // BUG: Yeah 100% is off by 1 char. Profiling only one opcode isn't worth fixing this visual alignment bug. prfline.Format( "%s%7.4f%s%%" DELIM "%s%9u" DELIM "%s%s" DELIM "%s%s" DELIM "%s%s\n" , pColorNumber , nPercent , pColorOperator , sSeparator2.c_str() , pColorNumber , static_cast(nCount), sSeparator2.c_str() , pColorOpcode , sOpcode.c_str(), sSeparator2.c_str() , pColorMnemonic , g_aOpcodes[ nOpcode ].sMnemonic, sSeparator2.c_str() , pColorOpmode , sAddress.c_str() ); prfline = ProfileLinePush(); } if (! bOpcodeGood) nOpcodeTotal = 0; prfline.Format( "Total: " DELIM "%s%9u\n" , sSeparator2.c_str() , pColorTotal , static_cast(nOpcodeTotal) ); prfline = ProfileLinePush(); prfline.Assign( "\n" ); prfline = ProfileLinePush(); // Opmode // "Percent Count Adressing Mode\n" ); if (bExport) // Note: 2 extra dummy columns are inserted to keep Addressing Mode in same column prfline.Format( "\"Percent\"" DELIM "\"Count\"" DELIM DELIM DELIM "\"Addressing Mode\"\n", sSeparator7.c_str(), sSeparator2.c_str(), sSeparator2.c_str(), sSeparator2.c_str() ); else prfline.Format( "Percent" DELIM "Count" DELIM "Addressing Mode\n", sSeparator7.c_str(), sSeparator2.c_str() ); prfline = ProfileLinePush(); if (nOpmodeTotal < 1) { nOpmodeTotal = 1.; bOpmodeGood = false; } for ( int iOpmode = 0; iOpmode < NUM_OPMODES; ++iOpmode ) { ProfileOpmode_t tProfileOpmode = vProfileOpmode.at( iOpmode ); Profile_t nCount = tProfileOpmode.m_nCount; // Don't spam with empty data if dumping to the console if ((! nCount) && (! bExport)) continue; int nOpmode = tProfileOpmode.m_iOpmode; double nPercent = (100. * nCount) / nOpmodeTotal; const std::string sAddress = (bExport) // Note: 2 extra dummy columns are inserted to keep Addressing Mode in same column ? StrFormat( "%s%s\"%s\"", sSeparator1.c_str(), sSeparator1.c_str(), g_aOpmodes[ nOpmode ].m_sName ) // not qouted if dumping to console : std::string( g_aOpmodes[ nOpmode ].m_sName ); // BUG: Yeah 100% is off by 1 char. Profiling only one opcode isn't worth fixing this visual alignment bug. prfline.Format( "%s%7.4f%s%%" DELIM "%s%9u" DELIM "%s%s\n" , pColorNumber , nPercent , pColorOperator , sSeparator2.c_str() , pColorNumber , static_cast(nCount), sSeparator2.c_str() , pColorOpmode , sAddress.c_str() ); prfline = ProfileLinePush(); } if (! bOpmodeGood) nOpmodeTotal = 0; prfline.Format( "Total: " DELIM "%s%9u\n" , sSeparator2.c_str() , pColorTotal , static_cast(nOpmodeTotal) ); prfline = ProfileLinePush(); prfline.Assign( "===================\n" ); prfline = ProfileLinePush(); unsigned int cycles = static_cast(g_nCumulativeCycles - g_nProfileBeginCycles); prfline.Format( "Cycles: " DELIM "%s%9u\n" , sSeparator2.c_str() , pColorNumber , cycles ); prfline = ProfileLinePush(); } #undef DELIM //=========================================================================== void ProfileReset() { for ( int iOpcode = 0; iOpcode < NUM_OPCODES; iOpcode++ ) { g_aProfileOpcodes[ iOpcode ].m_iOpcode = iOpcode; g_aProfileOpcodes[ iOpcode ].m_nCount = 0; } for ( int iOpmode = 0; iOpmode < NUM_OPMODES; iOpmode++ ) { g_aProfileOpmodes[ iOpmode ].m_iOpmode = iOpmode; g_aProfileOpmodes[ iOpmode ].m_nCount = 0; } g_nProfileBeginCycles = g_nCumulativeCycles; } //=========================================================================== bool ProfileSave() { bool bStatus = false; const std::string sFilename = g_sProgramDir + g_FileNameProfile; // TODO: Allow user to decide? FILE *hFile = fopen( sFilename.c_str(), "wt" ); if (hFile) { const int nLine = g_nProfileLine; for ( int iLine = 0; iLine < nLine; iLine++ ) { ProfileLine_t prfline = ProfileLinePeek( iLine ); if ( prfline.buf ) { fputs( prfline.buf, hFile ); } } fclose( hFile ); bStatus = true; } return bStatus; } static void InitDisasm(void) { g_nDisasmCurAddress = regs.pc; DisasmCalcTopBotAddress(); } // _____________________________________________________________________________________ // | | // | Public Functions | // | | // |_____________________________________________________________________________________| //=========================================================================== void DebugBegin () { // This is called every time the debugger is entered. GetDebuggerMemDC(); g_nAppMode = MODE_DEBUG; GetFrame().FrameRefreshStatus(DRAW_TITLE | DRAW_DISK_STATUS); if (GetMainCpu() == CPU_6502) { g_aOpcodes = & g_aOpcodes6502[ 0 ]; // Apple ][, ][+, //e g_aOpmodes[ AM_2 ].m_nBytes = 1; g_aOpmodes[ AM_3 ].m_nBytes = 1; } else { g_aOpcodes = & g_aOpcodes65C02[ 0 ]; // Enhanced Apple //e g_aOpmodes[ AM_2 ].m_nBytes = 2; g_aOpmodes[ AM_3 ].m_nBytes = 3; } InitDisasm(); DebugVideoMode::Instance().Reset(); UpdateDisplay( UPDATE_ALL ); g_LBR = LBR_UNDEFINED; // reset LBR, so LBR isn't stale from a previous debugging session #if DEBUG_APPLE_FONT int iFG = 7; int iBG = 4; // DebuggerSetColorFG( aColors[ iFG ] ); // DebuggerSetColorBG( aColors[ iBG ] ); int iChar = 0; int x = 0; int y = 0; for (iChar = 0; iChar < 256; iChar++) { x = (iChar % 16); y = (iChar / 16); iFG = (x >> 1); // (iChar % 8); iBG = (y >> 1) & 7; // (iChar / 8) & 7; DebuggerSetColorFG( aConsoleColors[ iFG ] ); DebuggerSetColorBG( aConsoleColors[ iBG ] ); DebuggerPrintChar( x * (APPLE_FONT_WIDTH / 2), y * (APPLE_FONT_HEIGHT / 2), iChar ); } #endif } //=========================================================================== void DebugExitDebugger () { ClearTempBreakpoints(); // make sure we remove temp breakpoints before checking if (g_nBreakpoints == 0 && g_hTraceFile == NULL) { DebugEnd(); return; } // Still have some BPs set or tracing to file, so continue single-stepping if (!g_bLastGoCmdWasFullSpeed) CmdGoNormalSpeed(0); else CmdGoFullSpeed(0); } //=========================================================================== static void CheckBreakOpcode( int iOpcode ) { if (iOpcode == 0x00) // BRK g_bDebugBreakpointHit |= IsDebugBreakOnInvalid(AM_IMPLIED) ? BP_HIT_INVALID : 0; if (g_aOpcodes[iOpcode].sMnemonic[0] >= 'a') // All 6502/65C02 undocumented opcodes mnemonics are lowercase strings! { // Translate g_aOpcodes[iOpcode].nAddressMode into {AM_1, AM_2, AM_3} int iOpcodeType = AM_1; switch (g_aOpcodes[iOpcode].nAddressMode) { case AM_1: // Invalid 1 Byte case AM_IMPLIED: iOpcodeType = AM_1; break; case AM_2: // Invalid 2 Bytes case AM_M: // 4 #Immediate case AM_Z: // 6 Zeropage case AM_ZX: // 9 Zeropage, X case AM_ZY: // 10 Zeropage, Y case AM_R: // 11 Relative case AM_IZX: // 12 Indexed (Zeropage Indirect, X) case AM_NZY: // 14 Indirect (Zeropage) Indexed, Y case AM_NZ: // 15 Indirect (Zeropage) iOpcodeType = AM_2; break; case AM_3: // Invalid 3 Bytes case AM_A: // 5 $Absolute case AM_AX: // 7 Absolute, X case AM_AY: // 8 Absolute, Y case AM_IAX: // 13 Indexed (Absolute Indirect, X) case AM_NA: // 16 Indirect (Absolute) i.e. JMP iOpcodeType = AM_3; break; default: _ASSERT(0); } g_bDebugBreakpointHit |= IsDebugBreakOnInvalid(iOpcodeType) ? BP_HIT_INVALID : 0; } // User wants to enter debugger on specific opcode? (NB. Can't be BRK) if (g_iDebugBreakOnOpcode && g_iDebugBreakOnOpcode == iOpcode) g_bDebugBreakpointHit |= BP_HIT_OPCODE; } static void UpdateLBR(void) { const BYTE nOpcode = *(mem + regs.pc); bool isControlFlowOpcode = nOpcode == OPCODE_BRK || nOpcode == OPCODE_JSR || nOpcode == OPCODE_JMP_A || nOpcode == OPCODE_RTI || nOpcode == OPCODE_RTS || nOpcode == OPCODE_JMP_NA; if (GetMainCpu() == CPU_65C02 && nOpcode == OPCODE_JMP_IAX) isControlFlowOpcode = true; if (g_aOpcodes[nOpcode].nAddressMode == AM_R) { if ( (nOpcode == OPCODE_BRA) || (nOpcode == OPCODE_BPL && !(regs.ps & AF_SIGN)) || (nOpcode == OPCODE_BMI && (regs.ps & AF_SIGN)) || (nOpcode == OPCODE_BVC && !(regs.ps & AF_OVERFLOW)) || (nOpcode == OPCODE_BVS && (regs.ps & AF_OVERFLOW)) || (nOpcode == OPCODE_BCC && !(regs.ps & AF_CARRY)) || (nOpcode == OPCODE_BCS && (regs.ps & AF_CARRY)) || (nOpcode == OPCODE_BNE && !(regs.ps & AF_ZERO)) || (nOpcode == OPCODE_BEQ && (regs.ps & AF_ZERO)) ) isControlFlowOpcode = true; // Branch taken } if (isControlFlowOpcode) g_LBR = regs.pc; } void DebugContinueStepping(const bool bCallerWillUpdateDisplay/*=false*/) { static bool bForceSingleStepNext = false; // Allow at least one instruction to execute so we don't trigger on the same invalid opcode if (g_nDebugSkipLen > 0) { if ((regs.pc >= g_nDebugSkipStart) && (regs.pc < (g_nDebugSkipStart + g_nDebugSkipLen))) { // Enter turbo debugger mode -- UI not updated, etc. g_nDebugSteps = -1; g_nAppMode = MODE_STEPPING; } else { // Enter normal debugger mode -- UI updated every instruction, etc. g_nDebugSteps = 1; g_nAppMode = MODE_STEPPING; } } if (g_nDebugSteps) { bool bDoSingleStep = true; if (bForceSingleStepNext) { bForceSingleStepNext = false; g_bDebugBreakpointHit = BP_HIT_NONE; // Don't show 'Stop Reason' msg a 2nd time } else if (GetActiveCpu() != CPU_Z80) { if (g_hTraceFile) OutputTraceLine(); g_bDebugBreakpointHit = BP_HIT_NONE; if ( MemIsAddrCodeMemory(regs.pc) ) { BYTE nOpcode = *(mem+regs.pc); // Update profiling stats int nOpmode = g_aOpcodes[ nOpcode ].nAddressMode; g_aProfileOpcodes[ nOpcode ].m_nCount++; g_aProfileOpmodes[ nOpmode ].m_nCount++; CheckBreakOpcode( nOpcode ); // Can set g_bDebugBreakpointHit } else { g_bDebugBreakpointHit = BP_HIT_PC_READ_FLOATING_BUS_OR_IO_MEM; } if (g_bDebugBreakpointHit) { bDoSingleStep = false; bForceSingleStepNext = true; // Allow next single-step (after this) to execute } } if (bDoSingleStep) { UpdateLBR(); const WORD oldPC = regs.pc; SingleStep(g_bGoCmd_ReinitFlag); g_bGoCmd_ReinitFlag = false; if (IsInterruptInLastExecution()) { g_LBR = oldPC; if (g_bDebugBreakOnInterrupt) g_bDebugBreakpointHit |= BP_HIT_INTERRUPT; } g_bDebugBreakpointHit |= CheckBreakpointsIO() | CheckBreakpointsReg() | CheckBreakpointsVideo() | CheckBreakpointsDmaToOrFromIOMemory() | CheckBreakpointsDmaToOrFromMemory(-1); } if (regs.pc == g_nDebugStepUntil || g_bDebugBreakpointHit) { std::string stopReason = "Unknown!"; bool skipStopReason = false; if (regs.pc == g_nDebugStepUntil) stopReason = "PC matches 'Go until' address"; else if (g_bDebugBreakpointHit & BP_HIT_INVALID) stopReason = "Invalid opcode"; else if (g_bDebugBreakpointHit & BP_HIT_OPCODE) stopReason = "Opcode match"; else if (g_bDebugBreakpointHit & BP_HIT_REG) stopReason = "Register matches value"; else if (g_bDebugBreakpointHit & BP_HIT_MEM) stopReason = StrFormat("Memory access at $%04X", g_uBreakMemoryAddress); else if (g_bDebugBreakpointHit & BP_HIT_MEMW) stopReason = StrFormat("Write access at $%04X", g_uBreakMemoryAddress); else if (g_bDebugBreakpointHit & BP_HIT_MEMR) stopReason = StrFormat("Read access at $%04X", g_uBreakMemoryAddress); else if (g_bDebugBreakpointHit & BP_HIT_PC_READ_FLOATING_BUS_OR_IO_MEM) stopReason = "PC reads from floating bus or I/O memory"; else if (g_bDebugBreakpointHit & BP_HIT_INTERRUPT) stopReason = (g_LBR == LBR_UNDEFINED) ? StrFormat("Interrupt occurred (LBR unknown)") : StrFormat("Interrupt occurred at $%04X", g_LBR); else if (g_bDebugBreakpointHit & BP_HIT_VIDEO_POS) stopReason = StrFormat("Video scanner position matches at vpos=$%04X", NTSC_GetVideoVertForDebugger()); else if (g_bDebugBreakpointHit & BP_DMA_TO_IO_MEM) stopReason = StrFormat("HDD DMA to I/O memory or ROM at $%04X", g_DebugBreakOnDMAIO.memoryAddr); else if (g_bDebugBreakpointHit & BP_DMA_FROM_IO_MEM) stopReason = StrFormat("HDD DMA from I/O memory at $%04X ", g_DebugBreakOnDMAIO.memoryAddr); else if (g_bDebugBreakpointHit & (BP_DMA_FROM_MEM | BP_DMA_TO_MEM)) skipStopReason = true; if (!skipStopReason) ConsoleBufferPushFormat( "Stop reason: %s", stopReason.c_str() ); for (int i = 0; i < NUM_BREAK_ON_DMA; i++) { int nDebugBreakpointHit = CheckBreakpointsDmaToOrFromMemory(i); if (nDebugBreakpointHit) { if (nDebugBreakpointHit & BP_DMA_TO_MEM) stopReason = StrFormat("HDD DMA to memory $%04X-%04X (BP#%d)", g_DebugBreakOnDMA[i].memoryAddr, g_DebugBreakOnDMA[i].memoryAddrEnd, g_DebugBreakOnDMA[i].BPid); else if (nDebugBreakpointHit & BP_DMA_FROM_MEM) stopReason = StrFormat("HDD DMA from memory $%04X-%04X (BP#%d)", g_DebugBreakOnDMA[i].memoryAddr, g_DebugBreakOnDMA[i].memoryAddrEnd, g_DebugBreakOnDMA[i].BPid); ConsoleBufferPushFormat("Stop reason: %s", stopReason.c_str()); } } ConsoleUpdate(); g_nDebugSteps = 0; } if (g_nDebugSteps > 0) g_nDebugSteps--; } if (!g_nDebugSteps) { SoundCore_SetFade(FADE_OUT); // NB. Call when MODE_STEPPING (not MODE_DEBUG) - see function g_nAppMode = MODE_DEBUG; GetFrame().FrameRefreshStatus(DRAW_TITLE | DRAW_DISK_STATUS); // BUG: PageUp, Trace - doesn't center cursor g_nDisasmCurAddress = regs.pc; DisasmCalcTopBotAddress(); if (!bCallerWillUpdateDisplay) UpdateDisplay( UPDATE_ALL ); } } //=========================================================================== void DebugStopStepping(void) { _ASSERT(g_nAppMode == MODE_STEPPING); if (g_nAppMode != MODE_STEPPING) return; g_nDebugSteps = 0; // On next DebugContinueStepping(), stop single-stepping and transition to MODE_DEBUG } //=========================================================================== void DebugDestroy () { DebugEnd(); FontsDestroy(); // DeleteObject(g_hFontDisasm ); // DeleteObject(g_hFontDebugger); // DeleteObject(g_hFontWebDings); // TODO: Symbols_Clear() for ( int iTable = 0; iTable < NUM_SYMBOL_TABLES; iTable++ ) { _CmdSymbolsClear( (SymbolTable_Index_e) iTable ); } // TODO: DataDisassembly_Clear() ReleaseConsoleFontDC(); } //=========================================================================== static void DebugEnd () { // Stepping ... calls us when key hit?! FrameWndProc() ProcessButtonClick() DebugEnd() if (g_bProfiling) { // See: .csv / .txt note in CmdProfile() ProfileFormat( true, PROFILE_FORMAT_TAB ); // Export in Excel-ready text format. ProfileSave(); } if (g_hTraceFile) { fclose(g_hTraceFile); g_hTraceFile = NULL; } g_vMemorySearchResults.clear(); g_nAppMode = MODE_RUNNING; ReleaseDebuggerMemDC(); } //=========================================================================== void DebugInitialize () { AssemblerOff(); // update prompt #if _DEBUG DWORD nError = 0; #endif #if _DEBUG nError = GetLastError(); #endif // Must select a bitmap into the temp DC ! // HDC hTmpDC = CreateCompatibleDC( FrameGetDC() ); #if _DEBUG nError = GetLastError(); #endif GetConsoleFontDC(); // Load font memset( g_aConsoleDisplay, 0, sizeof( g_aConsoleDisplay ) ); // CONSOLE_WIDTH * CONSOLE_HEIGHT ); ConsoleInputReset(); for ( int iWindow = 0; iWindow < NUM_WINDOWS; iWindow++ ) { WindowSplit_t *pWindow = & g_aWindowConfig[ iWindow ]; pWindow->bSplit = false; pWindow->eTop = (Window_e) iWindow; pWindow->eBot = (Window_e) iWindow; } g_iWindowThis = WINDOW_CODE; g_iWindowLast = WINDOW_CODE; WindowUpdateDisasmSize(); ConfigColorsReset(); WindowUpdateConsoleDisplayedSize(); // CLEAR THE BREAKPOINT AND WATCH TABLES memset( g_aBreakpoints , 0, MAX_BREAKPOINTS * sizeof(Breakpoint_t)); g_nBreakpoints = 0; memset( g_aWatches , 0, MAX_WATCHES * sizeof(Watches_t) ); g_nWatches = 0; memset( g_aZeroPagePointers, 0, MAX_ZEROPAGE_POINTERS * sizeof(ZeroPagePointers_t)); g_nZeroPagePointers = 0; // Load Main, Applesoft, and User Symbols g_bSymbolsDisplayMissingFile = false; g_iCommand = CMD_SYMBOLS_ROM; CmdSymbolsLoad(0); g_iCommand = CMD_SYMBOLS_APPLESOFT; CmdSymbolsLoad(0); // ,0x7,0xFF // Treat zero-page as data // $00 GOWARM JSR ... // $01 LOC1 DW // $03 GOSTROUT JSR ... // $07..$B0 // $B1 CHRGET // $C8 // $C9 RNDSEED DW // $D0..$FF g_iCommand = CMD_SYMBOLS_USER_1; CmdSymbolsLoad(0); g_bSymbolsDisplayMissingFile = true; #if OLD_FONT // CREATE A FONT FOR THE DEBUGGING SCREEN int nArgs = _Arg_1( g_sFontNameDefault ); #endif FontsInitialize(); int iColor; iColor = FG_CONSOLE_OUTPUT; COLORREF nColor = g_aColorPalette[ g_aColorIndex[ iColor ] ]; g_anConsoleColor[ CONSOLE_COLOR_x ] = nColor; /* g_hFontDebugger = CreateFont( g_nFontHeight // Height , 0 // Width , 0 // Escapement , 0 // Orientatin , FW_MEDIUM // Weight , 0 // Italic , 0 // Underline , 0 // Strike Out , DEFAULT_CHARSET // "OEM_CHARSET" DEFAULT_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , ANTIALIASED_QUALITY // DEFAULT_QUALITY , FIXED_PITCH | FF_MODERN // HACK: MAGIC #: 4 // FIXED_PITCH , g_sFontNameDefault ); g_hFontWebDings = CreateFont( g_nFontHeight // Height , 0 // Width , 0 // Escapement , 0 // Orientatin , FW_MEDIUM // Weight , 0 // Italic , 0 // Underline , 0 // Strike Out , DEFAULT_CHARSET // ANSI_CHARSET // OEM_CHARSET DEFAULT_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , ANTIALIASED_QUALITY // DEFAULT_QUALITY , DEFAULT_PITCH | FF_DECORATIVE // FIXED_PITCH | 4 | FF_MODERN , g_sFontNameBranch ); */ // if (g_hFontWebDings) #if !USE_APPLE_FONT if (g_aFontConfig[ FONT_DISASM_BRANCH ]._hFont) { g_iConfigDisasmBranchType = DISASM_BRANCH_FANCY; } else { g_iConfigDisasmBranchType = DISASM_BRANCH_PLAIN; } #endif // ConsoleInputReset(); already called in DebugInitialize() VerifyDebuggerCommandTable(); // Check all summary help to see if it fits within the console for (int iCmd = 0; iCmd < NUM_COMMANDS; iCmd++ ) { const char *pHelp = g_aCommands[ iCmd ].pHelpSummary; if (pHelp) { int nLen = _tcslen( pHelp ) + 2; if (nLen > (CONSOLE_WIDTH-1)) { ConsoleBufferPushFormat( "Warning: %s help is %d chars", pHelp, nLen ); } } } #if _DEBUG //g_bConsoleBufferPaused = true; #endif _Bookmark_Reset(); static bool doneAutoRun = false; if (!doneAutoRun) // Don't re-run on a VM restart { doneAutoRun = true; const std::string debuggerAutoRunName = "DebuggerAutoRun.txt"; // Look in g_sCurrentDir, otherwise try g_sProgramDir std::string pathname = g_sCurrentDir + debuggerAutoRunName; strncpy_s(g_aArgs[1].sArg, MAX_ARG_LEN, pathname.c_str(), _TRUNCATE); CmdOutputRun(1); if (!g_bScriptReadOk) { pathname = g_sProgramDir + debuggerAutoRunName; strncpy_s(g_aArgs[1].sArg, MAX_ARG_LEN, pathname.c_str(), _TRUNCATE); CmdOutputRun(1); } } CmdMOTD(0); } //=========================================================================== void DebugReset(void) { g_videoScannerDisplayInfo.Reset(); g_LBR = LBR_UNDEFINED; } // Add character to the input line //=========================================================================== void DebuggerInputConsoleChar( TCHAR ch ) { _ASSERT(g_nAppMode == MODE_DEBUG); if (g_nAppMode != MODE_DEBUG) return; if (g_bConsoleBufferPaused) return; if (g_bIgnoreNextKey) { g_bIgnoreNextKey = false; return; } if (ch == CONSOLE_COLOR_ESCAPE_CHAR) return; if (g_nConsoleInputSkip == ch) return; if (ch == CHAR_SPACE) { // If don't have console input, don't pass space to the input line // exception: pass to assembler if ((! g_nConsoleInputChars) && (! g_bAssemblerInput)) return; } if (g_nConsoleInputChars > (g_nConsoleDisplayWidth-1)) return; if ((ch >= CHAR_SPACE) && (ch <= 126)) // HACK MAGIC # 32 -> ' ', # 126 { if ((ch == TCHAR_QUOTE_DOUBLE) || (ch == TCHAR_QUOTE_SINGLE)) g_bConsoleInputQuoted = ! g_bConsoleInputQuoted; if (!g_bConsoleInputQuoted) { // TODO: must fix param matching to ignore case #if ALLOW_INPUT_LOWERCASE #else ch = (TCHAR)CharUpper((LPTSTR)ch); #endif } ConsoleInputChar( ch ); DebuggerCursorNext(); DrawConsoleInput(); StretchBltMemToFrameDC(); } else if (ch == 0x16) // HACK: Ctrl-V. WTF!? { ProcessClipboardCommands(); } } // Triggered when ENTER is pressed, or via script //=========================================================================== Update_t DebuggerProcessCommand ( const bool bEchoConsoleInput ) { Update_t bUpdateDisplay = UPDATE_NOTHING; if (bEchoConsoleInput) ConsoleDisplayPush( ConsoleInputPeek() ); if (g_bAssemblerInput) { if (g_nConsoleInputChars) { ParseInput( g_pConsoleInput, false ); // Don't cook the args bUpdateDisplay |= _CmdAssemble( g_nAssemblerAddress, 0, g_nArgRaw ); } else { AssemblerOff(); int nDelayedTargets = AssemblerDelayedTargetsSize(); if (nDelayedTargets) { ConsoleDisplayPushFormat( " Asm: %d sym declared, not defined", nDelayedTargets ); bUpdateDisplay |= UPDATE_CONSOLE_DISPLAY; } } ConsoleInputReset(); bUpdateDisplay |= UPDATE_CONSOLE_DISPLAY | UPDATE_CONSOLE_INPUT; ConsoleUpdate(); // udpate console, don't pause } else if (g_nConsoleInputChars) { // BufferedInputPush( // Handle Buffered Input // while ( BufferedInputPeek() ) int nArgs = ParseInput( g_pConsoleInput ); if (nArgs == ARG_SYNTAX_ERROR) { bUpdateDisplay |= ConsoleDisplayErrorFormat( "Syntax error: %s", g_aArgs[0].sArg ); } else { bUpdateDisplay |= ExecuteCommand( nArgs ); // ParseInput()); } if (!g_bConsoleBufferPaused) { ConsoleInputReset(); } } return bUpdateDisplay; } void ToggleFullScreenConsole() { // Switch to Console Window if (g_iWindowThis != WINDOW_CONSOLE) { CmdWindowViewConsole( 0 ); } else // switch back to last window { CmdWindowLast( 0 ); } } //=========================================================================== void DebuggerProcessKey( int keycode ) { if (g_nAppMode != MODE_DEBUG) return; if (DebugVideoMode::Instance().IsSet()) { if ((VK_SHIFT == keycode) || (VK_CONTROL == keycode) || (VK_MENU == keycode)) { return; } // Normally any key press takes us out of "Viewing Apple Output" g_nAppMode // VK_F# are already processed, so we can't use them to cycle next video g_nAppMode // if ((g_nAppMode != MODE_LOGO) && (g_nAppMode != MODE_DEBUG)) GetVideo().ClearSHRResidue(); // Clear the framebuffer to remove any SHR residue in the borders DebugVideoMode::Instance().Reset(); UpdateDisplay( UPDATE_ALL ); // 1 return; } Update_t bUpdateDisplay = UPDATE_NOTHING; // For long output, allow user to read it if (g_nConsoleBuffer) { if ((VK_SPACE == keycode) || (VK_RETURN == keycode) || (VK_TAB == keycode) || (VK_ESCAPE == keycode)) { int nLines = MIN( g_nConsoleBuffer, g_nConsoleDisplayLines - 1 ); // was -2 if (VK_ESCAPE == keycode) // user doesn't want to read all this stu { nLines = g_nConsoleBuffer; } ConsoleBufferTryUnpause( nLines ); // don't really need since 'else if (keycode = VK_BACK)' but better safe then sorry keycode = 0; // don't single-step } bUpdateDisplay |= UPDATE_CONSOLE_DISPLAY | UPDATE_CONSOLE_INPUT; ConsoleDisplayPause(); } else // If have console input, don't invoke curmovement // TODO: Probably should disable all "movement" keys to map them to line editing g_nAppMode if ((keycode == VK_SPACE) && g_nConsoleInputChars) return; else if (keycode == VK_ESCAPE) { g_bConsoleInputQuoted = false; ConsoleInputReset(); bUpdateDisplay |= UPDATE_CONSOLE_INPUT; } else if (keycode == VK_BACK) { // Note: Checks prev char if QUTOE - SINGLE or DOUBLE // ConsoleUpdateCursor( CHAR_SPACE ); if (! ConsoleInputBackSpace()) { // CmdBeep(); } bUpdateDisplay |= UPDATE_CONSOLE_INPUT; } else if (keycode == VK_RETURN) { // ConsoleUpdateCursor( 0 ); if (! g_nConsoleInputChars) { // bugfix: 2.6.1.35 Fixed: Pressing enter on blank line while in assembler wouldn't exit it. if ( g_bAssemblerInput ) { bUpdateDisplay |= DebuggerProcessCommand( false ); } else { ToggleFullScreenConsole(); bUpdateDisplay |= UPDATE_ALL; } } else { ConsoleScrollEnd(); bUpdateDisplay |= DebuggerProcessCommand( true ); // copy console input to console output // BUGFIX: main disassembly listing doesn't get updated in full screen console //bUpdateDisplay |= UPDATE_CONSOLE_DISPLAY; bUpdateDisplay |= UPDATE_ALL; } } else if (( keycode == VK_OEM_3 ) || // US: Tilde ~ (key to the immediate left of numeral 1) ( keycode == VK_OEM_8 )) // UK: Logical NOT ¬ (key to the immediate left of numeral 1) { if (KeybGetCtrlStatus()) { ToggleFullScreenConsole(); bUpdateDisplay |= UPDATE_ALL; } else { g_nConsoleInputSkip = 0; // VK_OEM_3; DebuggerInputConsoleChar( '~' ); } g_nConsoleInputSkip = '~'; // VK_OEM_3; } else { switch (keycode) { case VK_TAB: { if (g_nConsoleInputChars) { // TODO: TabCompletionCommand() // TODO: TabCompletionSymbol() bUpdateDisplay |= ConsoleInputTabCompletion(); } else if (KeybGetCtrlStatus() && KeybGetShiftStatus()) bUpdateDisplay |= CmdWindowCyclePrev( 0 ); else if (KeybGetCtrlStatus()) bUpdateDisplay |= CmdWindowCycleNext( 0 ); else bUpdateDisplay |= CmdCursorJumpPC( CURSOR_ALIGN_CENTER ); break; } case VK_SPACE: if (g_bAssemblerInput) { // if (g_nConsoleInputChars) // { // ParseInput( g_pConsoleInput, false ); // Don't cook the args // bUpdateDisplay |= _CmdAssemble( g_nAssemblerAddress, 0, g_nArgRaw ); // } } else { if (KeybGetShiftStatus()) bUpdateDisplay |= CmdStepOut(0); else if (KeybGetCtrlStatus()) bUpdateDisplay |= CmdStepOver(0); else bUpdateDisplay |= CmdTrace(0); } break; case VK_HOME: if (g_iWindowThis == WINDOW_CONSOLE) { ConsoleScrollHome(); } else if (g_nConsoleInputChars > 0) { if (KeybGetShiftStatus()) { bUpdateDisplay |= ConsoleScrollHome(); } else { // Move cursor to start of console input } } else { // If you really want $000 at the top of the screen... // g_nDisasmTopAddress = _6502_MEM_BEGIN; // DisasmCalcCurFromTopAddress(); // DisasmCalcBotFromTopAddress(); g_nDisasmCurAddress = _6502_MEM_BEGIN; DisasmCalcTopBotAddress(); } bUpdateDisplay |= UPDATE_DISASM; break; case VK_END: if (g_iWindowThis == WINDOW_CONSOLE) { ConsoleScrollEnd(); } else if (g_nConsoleInputChars > 0) { if (KeybGetShiftStatus()) { bUpdateDisplay |= ConsoleScrollEnd(); } else { // Move cursor to end of console input } } else { // If you really want $8000 at the top of the screen... // g_nDisasmTopAddress = (_6502_MEM_END / 2) + 1; // DisasmCalcCurFromTopAddress(); // DisasmCalcTopBotAddress(); g_nDisasmCurAddress = (_6502_MEM_END / 2) + 1; DisasmCalcTopBotAddress(); } bUpdateDisplay |= UPDATE_DISASM; break; case VK_PRIOR: // VK_PAGE_UP if (g_iWindowThis == WINDOW_CONSOLE) { bUpdateDisplay |= ConsoleScrollPageUp(); } else if (g_nConsoleInputChars > 0) { if (KeybGetShiftStatus()) { bUpdateDisplay |= ConsoleScrollPageUp(); } else { // Scroll through console input history bUpdateDisplay |= ConsoleScrollUp( 3 ); } } else { if (KeybGetShiftStatus()) bUpdateDisplay |= CmdCursorPageUp256(0); else if (KeybGetCtrlStatus()) bUpdateDisplay |= CmdCursorPageUp4K(0); else bUpdateDisplay |= CmdCursorPageUp(0); } break; case VK_NEXT: // VK_PAGE_DN if (g_iWindowThis == WINDOW_CONSOLE) { bUpdateDisplay |= ConsoleScrollPageDn(); } else if (g_nConsoleInputChars > 0) { if (KeybGetShiftStatus()) { bUpdateDisplay |= ConsoleScrollPageDn(); } else { // Scroll through console input history bUpdateDisplay |= ConsoleScrollDn( 3 ); } } else { if (KeybGetShiftStatus()) bUpdateDisplay |= CmdCursorPageDown256(0); else if (KeybGetCtrlStatus()) bUpdateDisplay |= CmdCursorPageDown4K(0); else bUpdateDisplay |= CmdCursorPageDown(0); } break; case VK_UP: if (g_iWindowThis == WINDOW_CONSOLE) { bUpdateDisplay |= ConsoleScrollUp( 1 ); } else if (g_nConsoleInputChars > 0) { if (KeybGetShiftStatus()) { bUpdateDisplay |= ConsoleScrollUp( 1 ); } else { // TODO: FIXME: Scroll through console input history } } else { // Shift the Top offset up by 1 byte // i.e. no smart disassembly like LineUp() // Normally UP moves to the previous "line" which may be multiple bytes. if (KeybGetShiftStatus()) bUpdateDisplay |= CmdCursorLineUp(1); else bUpdateDisplay |= CmdCursorLineUp(0); // smart disassembly } break; case VK_DOWN: if (g_iWindowThis == WINDOW_CONSOLE) { bUpdateDisplay |= ConsoleScrollDn( 1 ); } else if (g_nConsoleInputChars > 0) { if (KeybGetShiftStatus()) { bUpdateDisplay |= ConsoleScrollDn( 1 ); } else { // TODO: FIXME: Scroll through console input history } } else { if (KeybGetCtrlStatus()) bUpdateDisplay |= CmdCursorRunUntil(0); else if (KeybGetShiftStatus()) // Shift the Offest down by 1 byte // i.e. no smart disassembly like LineDown() bUpdateDisplay |= CmdCursorLineDown(1); else bUpdateDisplay |= CmdCursorLineDown(0); } break; case VK_RIGHT: if (KeybGetCtrlStatus()) bUpdateDisplay |= CmdCursorSetPC(0/*unused*/); else if (KeybGetShiftStatus()) bUpdateDisplay |= CmdCursorJumpPC( CURSOR_ALIGN_TOP ); else if (KeybGetAltStatus()) bUpdateDisplay |= CmdCursorJumpPC( CURSOR_ALIGN_CENTER ); else bUpdateDisplay |= CmdCursorFollowTarget( CURSOR_ALIGN_TOP ); break; case VK_LEFT: if (KeybGetShiftStatus()) bUpdateDisplay |= CmdCursorJumpRetAddr( CURSOR_ALIGN_TOP ); // Jump to Caller else bUpdateDisplay |= CmdCursorJumpRetAddr( CURSOR_ALIGN_CENTER ); break; default: if ((keycode >= '0') && (keycode <= '9')) { int nArgs = 1; int iBookmark = keycode - '0'; if (KeybGetCtrlStatus() && KeybGetShiftStatus()) { nArgs = 2; g_aArgs[ 1 ].nValue = iBookmark; g_aArgs[ 2 ].nValue = g_nDisasmCurAddress; bUpdateDisplay |= CmdBookmarkAdd( nArgs ); g_bIgnoreNextKey = true; } else if (KeybGetCtrlStatus()) { nArgs = 1; g_aArgs[ 1 ].nValue = iBookmark; bUpdateDisplay |= CmdBookmarkGoto( nArgs ); g_bIgnoreNextKey = true; } } break; } // switch } if (bUpdateDisplay && !DebugVideoMode::Instance().IsSet()) // & UPDATE_BACKGROUND) UpdateDisplay( bUpdateDisplay ); } void DebugDisplay( BOOL bInitDisasm/*=FALSE*/ ) { if (bInitDisasm) InitDisasm(); if (DebugVideoMode::Instance().IsSet()) { uint32_t mode = 0; DebugVideoMode::Instance().Get(&mode); GetFrame().VideoRefreshScreen(mode, true); return; } UpdateDisplay( UPDATE_ALL ); } //=========================================================================== void DebuggerUpdate() { DebuggerCursorUpdate(); } //=========================================================================== void DebuggerCursorUpdate() { if (g_nAppMode != MODE_DEBUG) return; const int nUpdatesPerSecond = 4; const DWORD nUpdateInternal_ms = 1000 / nUpdatesPerSecond; static DWORD nBeg = GetTickCount(); // timeGetTime(); DWORD nNow = GetTickCount(); // timeGetTime(); if (((nNow - nBeg) >= nUpdateInternal_ms) && !DebugVideoMode::Instance().IsSet()) { nBeg = nNow; DebuggerCursorNext(); DrawConsoleCursor(); StretchBltMemToFrameDC(); } else { Sleep(1); // Stop process hogging CPU } } //=========================================================================== void DebuggerCursorNext() { g_bInputCursor ^= true; if (g_bInputCursor) ConsoleUpdateCursor( g_aInputCursor[ g_iInputCursor ] ); else ConsoleUpdateCursor( 0 ); // show char under cursor } //=========================================================================== //char DebuggerCursorGet() //{ // return g_aInputCursor[ g_iInputCursor ]; //} //=========================================================================== bool IsDebugSteppingAtFullSpeed(void) { return (g_nAppMode == MODE_STEPPING) && g_bDebugFullSpeed; }