/* 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 "../Applewin.h" #include "../CPU.h" #include "../Disk.h" #include "../Frame.h" #include "../Keyboard.h" #include "../Memory.h" #include "../NTSC.h" #include "../SoundCore.h" // SoundCore_SetFade() #include "../Video.h" // #define DEBUG_COMMAND_HELP 1 // #define DEBUG_ASM_HASH 1 #define ALLOW_INPUT_LOWERCASE 1 // See /docs/Debugger_Changelog.txt for full details const int DEBUGGER_VERSION = MAKE_VERSION(2,9,0,15); // 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; static int g_bDebugBreakpointHit = 0; // See: BreakpointHit_t int g_nBreakpoints = 0; Breakpoint_t g_aBreakpoints[ MAX_BREAKPOINTS ]; // NOTE: Breakpoint_Source_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 // Memory "M" // Main // 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 TCHAR 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 ); char * ProfileLinePeek ( int iLine ); char * ProfileLinePush (); void ProfileLineReset (); // Soft-switches __________________________________________________________________________________ // Source Level Debugging _________________________________________________________________________ bool g_bSourceLevelDebugging = false; bool g_bSourceAddSymbols = false; bool g_bSourceAddMemory = false; char g_aSourceFileName[ MAX_PATH ] = ""; 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. __________________________________________________________________________________________ char g_sFileNameConfig [] = #ifdef MSDOS "AWDEBUGR.CFG"; #else "AppleWinDebugger.cfg"; #endif static char g_sFileNameTrace [] = "Trace.txt"; static bool g_bBenchmarking = false; static BOOL fulldisp = 0; 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; // 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_Clear( Breakpoint_t * aBreakWatchZero, int iSlot ); // Config - Colors static void _ConfigColorsReset ( BYTE *pPalDst = 0 ); // Config - Save bool ConfigSave_BufferToDisk ( 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 ); // Font static void _UpdateWindowFontHeights (int nFontHeight); // Source Level Debugging static bool BufferAssemblyListing ( char * 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 char FormatCharTxtCtrl ( const BYTE b, bool *pWasCtrl_ ); char FormatCharTxtAsci ( const BYTE b, bool *pWasAsci_ ); char FormatCharTxtHigh ( const BYTE b, bool *pWasHi_ ); char FormatChar4Font ( const BYTE b, bool *pWasHi_, bool *pWasLo_ ); void _CursorMoveDownAligned( int nDelta ); void _CursorMoveUpAligned( int nDelta ); void DisasmCalcTopFromCurAddress( bool bUpdateTop = true ); void DisasmCalcCurFromTopAddress(); void DisasmCalcBotFromTopAddress(); void DisasmCalcTopBotAddress (); WORD DisasmCalcAddressFromLines( WORD iAddress, int nLines ); // 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 _______________________________________________________________________ int _GetFileSize( FILE *hFile ) { fseek( hFile, 0, SEEK_END ); int 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; bDeleted = true; } } return bDeleted; } bool 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 true; } } return false; } //=========================================================================== 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; } } //=========================================================================== 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 if (! nArgs) { return CmdZeroPageList( 0 ); } int iArg = 1; int iBookmark = NO_6502_TARGET; if (nArgs > 1) { iBookmark = g_aArgs[ 1 ].nValue; iArg++; } bool bAdded = false; for (; iArg <= nArgs; iArg++ ) { WORD nAddress = g_aArgs[ iArg ].nValue; if (iBookmark == NO_6502_TARGET) { iBookmark = 0; while ((iBookmark < MAX_BOOKMARKS) && (g_aBookmarks[iBookmark].bSet)) { iBookmark++; } } if ((iBookmark >= MAX_BOOKMARKS) && !bAdded) { char sText[ CONSOLE_WIDTH ]; sprintf( sText, "All bookmarks are currently in use. (Max: %d)", MAX_BOOKMARKS ); ConsoleDisplayPush( sText ); return ConsoleUpdate(); } if ((iBookmark < MAX_BOOKMARKS) && (g_nBookmarks < MAX_BOOKMARKS)) { g_aBookmarks[iBookmark].bSet = true; g_aBookmarks[iBookmark].nAddress = nAddress; bAdded = true; g_nBookmarks++; iBookmark++; } } if (!bAdded) goto _Help; return UPDATE_DISASM | ConsoleUpdate(); _Help: return Help_Arg_1( CMD_BOOKMARK_ADD ); } //=========================================================================== 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)) { for (iBookmark = 0; iBookmark < MAX_BOOKMARKS; iBookmark++ ) { if (g_aBookmarks[ iBookmark ].bSet) g_aBookmarks[ iBookmark ].bSet = false; } break; } iBookmark = g_aArgs[ iArg ].nValue; if (g_aBookmarks[ iBookmark ].bSet) g_aBookmarks[ iBookmark ].bSet = false; } 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) { TCHAR sText[ CONSOLE_WIDTH ]; ConsoleBufferPushFormat( sText, TEXT(" There are no current bookmarks. (Max: %d)"), MAX_BOOKMARKS ); } else { _BWZ_ListAll( g_aBookmarks, MAX_BOOKMARKS ); } return ConsoleUpdate(); } //=========================================================================== Update_t CmdBookmarkLoad (int nArgs) { char sFilePath[ MAX_PATH ] = ""; if (nArgs == 1) { // strcpy( sMiniFileName, pFileName ); // strcat( sMiniFileName, ".aws" ); // HACK: MAGIC STRING // _tcscpy(sFileName, g_sCurrentDir); // // _tcscat(sFileName, sMiniFileName); } return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdBookmarkSave (int nArgs) { TCHAR sText[ CONSOLE_WIDTH ]; g_ConfigState.Reset(); ConfigSave_PrepareHeader( PARAM_CAT_BOOKMARKS, CMD_BOOKMARK_CLEAR ); int iBookmark = 0; while (iBookmark < MAX_BOOKMARKS) { if (g_aBookmarks[ iBookmark ].bSet) { sprintf( sText, "%s %x %04X\n" , g_aCommands[ CMD_BOOKMARK_ADD ].m_sName , iBookmark , g_aBookmarks[ iBookmark ].nAddress ); g_ConfigState.PushLine( sText ); } 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(); FrameRefreshStatus(DRAW_TITLE); VideoRedrawScreen(); DWORD currtime = GetTickCount(); while ((extbench = GetTickCount()) != currtime) ; // intentional busy-waiting KeybQueueKeypress(TEXT(' '),1); return UPDATE_ALL; // 0; } //=========================================================================== Update_t CmdProfile (int nArgs) { if (! nArgs) { sprintf( g_aArgs[ 1 ].sArg, "%s", g_aParameters[ PARAM_RESET ].m_sName ); 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) { char *pText; char sText[ CONSOLE_WIDTH ]; int nLine = g_nProfileLine; int iLine; for( iLine = 0; iLine < nLine; iLine++ ) { pText = ProfileLinePeek( iLine ); if (pText) { TextConvertTabsToSpaces( sText, pText, CONSOLE_WIDTH, 4 ); // ConsoleBufferPush( sText ); ConsolePrint( sText ); } } } if (iParam == PARAM_SAVE) { if (ProfileSave()) { TCHAR sText[ CONSOLE_WIDTH ]; ConsoleBufferPushFormat ( sText, " Saved: %s", g_FileNameProfile ); } else ConsoleBufferPush( TEXT(" 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 int IsDebugBreakOnInvalid( int iOpcodeType ) { g_bDebugBreakpointHit |= ((g_nDebugBreakOnInvalid >> iOpcodeType) & 1) ? BP_HIT_INVALID : 0; return g_bDebugBreakpointHit; } // 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)) goto _Help; 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 # ? // error TCHAR sText[ CONSOLE_WIDTH ]; bool bValidParam = true; int iParamArg = nArgs; 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; } else bValidParam = false; if (nArgs == 1) { if (! nFound) // bValidParam) // case 1a or 1c { 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( sText, TEXT("Enter debugger on BRK opcode: %s"), g_aParameters[ iParam ].m_sName ); else ConsoleBufferPushFormat( sText, TEXT("Enter debugger on INVALID %1X opcode: %s"), iType, g_aParameters[ iParam ].m_sName ); return ConsoleUpdate(); } else if (nArgs == 2) { if (! bValidParam) // case 2b { goto _Help; } else // case 2a (or not 2b ;-) { if ((iType < 0) || (iType > AM_3)) goto _Help; SetDebugBreakOnInvalid( iType, nActive ); if (iType == 0) ConsoleBufferPushFormat( sText, TEXT("Enter debugger on BRK opcode: %s"), g_aParameters[ iParam ].m_sName ); else ConsoleBufferPushFormat( sText, TEXT("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! { TCHAR sText[ CONSOLE_WIDTH ]; 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( sText, TEXT("Warning: clamping opcode: %02X"), g_iDebugBreakOnOpcode ); return ConsoleUpdate(); } } if (g_iDebugBreakOnOpcode == 0) // Show what the current break opcode is ConsoleBufferPushFormat( sText, TEXT("%s Break on Opcode: None") , sAction ); else // Show what the current break opcode is ConsoleBufferPushFormat( sText, TEXT("%s Break on Opcode: %02X %s") , sAction , g_iDebugBreakOnOpcode , g_aOpcodes65C02[ g_iDebugBreakOnOpcode ].sMnemonic ); 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 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; } //=========================================================================== 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) && (nVal < (pBP->nAddress + pBP->nLength))) bStatus = true; break; case BP_OP_NOT_EQUAL : // Rnage is: (,] (not-inclusive, inclusive) if ((nVal < pBP->nAddress) || (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; } //=========================================================================== int CheckBreakpointsIO () { const int NUM_TARGETS = 3; int aTarget[ NUM_TARGETS ] = { NO_6502_TARGET, NO_6502_TARGET, NO_6502_TARGET }; int nBytes; bool bBreakpointHit = 0; int iTarget; int nAddress; _6502_GetTargets( regs.pc, &aTarget[0], &aTarget[1], &aTarget[2], &nBytes, 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_1) { if (_CheckBreakpointValue( pBP, nAddress )) { g_uBreakMemoryAddress = (WORD) nAddress; return BP_HIT_MEM; } } } } } } } return bBreakpointHit; } // Returns true if a register breakpoint is triggered //=========================================================================== int CheckBreakpointsReg () { int bBreakpointHit = 0; for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t *pBP = &g_aBreakpoints[iBreakpoint]; if (! _BreakpointValid( pBP )) continue; 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) { bBreakpointHit = BP_HIT_REG; if (pBP->bTemp) _BWZ_Clear(pBP, iBreakpoint); break; } } return bBreakpointHit; } void ClearTempBreakpoints () { for (int iBreakpoint = 0; iBreakpoint < MAX_BREAKPOINTS; iBreakpoint++) { Breakpoint_t *pBP = &g_aBreakpoints[iBreakpoint]; if (! _BreakpointValid( pBP )) continue; if (pBP->bTemp) _BWZ_Clear(pBP, iBreakpoint); } } //=========================================================================== Update_t CmdBreakpoint (int nArgs) { return CmdBreakpointAddPC( nArgs ); } // smart breakpoint //=========================================================================== Update_t CmdBreakpointAddSmart (int nArgs) { 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) { pBP->eSource = iSrc; pBP->eOperator = iCmp; pBP->nAddress = nAddress; pBP->nLength = nLen; pBP->bSet = true; pBP->bEnabled = true; pBP->bTemp = bIsTempBreakpoint; 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(TEXT("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; } bool bHaveCmp = false; // 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 ; 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_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; } } } 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 CmdBreakpointAddMem (int nArgs) { BreakpointSource_t iSrc = BP_SRC_MEM_1; 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; } //=========================================================================== void _BWZ_Clear( Breakpoint_t * aBreakWatchZero, int iSlot ) { aBreakWatchZero[ iSlot ].bSet = false; aBreakWatchZero[ iSlot ].bEnabled = false; aBreakWatchZero[ iSlot ].nLength = 0; } void _BWZ_RemoveOne( Breakpoint_t *aBreakWatchZero, const int iSlot, int & nTotal ) { if (aBreakWatchZero[iSlot].bSet) { _BWZ_Clear( aBreakWatchZero, iSlot ); 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(TEXT("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(TEXT("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(TEXT("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; } void _BWZ_List( const Breakpoint_t * aBreakWatchZero, const int iBWZ ) //, bool bZeroBased ) { static char sText[ CONSOLE_WIDTH ]; static const char sFlags[] = "-*"; static char sName[ MAX_SYMBOLS_LEN+1 ]; WORD nAddress = aBreakWatchZero[ iBWZ ].nAddress; const char* pSymbol = GetSymbol( nAddress, 2 ); if (! pSymbol) { sName[0] = 0; pSymbol = sName; } ConsoleBufferPushFormat( sText, " #%d %c %04X %s", // (bZeroBased ? iBWZ + 1 : iBWZ), iBWZ, sFlags[ (int) aBreakWatchZero[ iBWZ ].bEnabled ], aBreakWatchZero[ iBWZ ].nAddress, pSymbol ); } 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) { TCHAR sText[ CONSOLE_WIDTH ]; ConsoleBufferPushFormat( sText, TEXT(" 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) { TCHAR sText[ CONSOLE_WIDTH ]; g_ConfigState.Reset(); ConfigSave_PrepareHeader( PARAM_CAT_BREAKPOINTS, CMD_BREAKPOINT_CLEAR ); int iBreakpoint = 0; while (iBreakpoint < MAX_BREAKPOINTS) { if (g_aBreakpoints[ iBreakpoint ].bSet) { sprintf( sText, "%s %x %04X,%04X\n" , g_aCommands[ CMD_BREAKPOINT_ADD_REG ].m_sName , iBreakpoint , g_aBreakpoints[ iBreakpoint ].nAddress , g_aBreakpoints[ iBreakpoint ].nLength ); g_ConfigState.PushLine( sText ); } if (! g_aBreakpoints[ iBreakpoint ].bEnabled) { sprintf( sText, "%s %x\n" , g_aCommands[ CMD_BREAKPOINT_DISABLE ].m_sName , iBreakpoint ); g_ConfigState.PushLine( sText ); } 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 ) { bool bHaveLabel = false; // 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) { bHaveLabel = true; // 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) 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) { // return Help_Arg_1( CMD_ASSEMBLE ); // Start assembler, continue with last assembled address AssemblerOn(); return UPDATE_CONSOLE_DISPLAY; } g_nAssemblerAddress = g_aArgs[1].nValue; 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 > _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 TCHAR sText[ CONSOLE_WIDTH ]; ConsoleBufferPushFormat( sText, TEXT("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; g_nAppMode = MODE_STEPPING; FrameRefreshStatus(DRAW_TITLE); 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(); } } 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; g_nAppMode = MODE_STEPPING; FrameRefreshStatus(DRAW_TITLE); DebugContinueStepping(); return UPDATE_ALL; // TODO: Verify // 0 } //=========================================================================== Update_t CmdTraceFile (int nArgs) { char sText[ CONSOLE_WIDTH ] = ""; if (g_hTraceFile) { fclose( g_hTraceFile ); g_hTraceFile = NULL; ConsoleBufferPush( "Trace stopped." ); } else { char sFileName[MAX_PATH]; if (nArgs) strcpy( sFileName, g_aArgs[1].sArg ); else strcpy( sFileName, g_sFileNameTrace ); g_bTraceFileWithVideoScanner = (nArgs >= 2); char sFilePath[ MAX_PATH ]; strcpy(sFilePath, g_sCurrentDir); // TODO: g_sDebugDir strcat(sFilePath, sFileName ); g_hTraceFile = fopen( sFilePath, "wt" ); if (g_hTraceFile) { const char* pTextHdr = g_bTraceFileWithVideoScanner ? "Trace (with video info) started: %s" : "Trace started: %s"; ConsoleBufferPushFormat( sText, pTextHdr, sFilePath ); g_bTraceHeader = true; } else { ConsoleBufferPushFormat( sText, "Trace ERROR: %s", sFilePath ); } } 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; g_nAppMode = MODE_STEPPING; FrameRefreshStatus(DRAW_TITLE); DebugContinueStepping(); 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(' '), 1); // 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 } // Color __________________________________________________________________________________________ void _ColorPrint( int iColor, COLORREF nColor ) { int R = (nColor >> 0) & 0xFF; int G = (nColor >> 8) & 0xFF; int B = (nColor >> 16) & 0xFF; TCHAR sText[ CONSOLE_WIDTH ]; ConsoleBufferPushFormat( sText, " 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 { TCHAR sText[ CONSOLE_WIDTH ]; wsprintf( sText, "Color: %d\nOut of range!", iColor ); MessageBox( g_hFrameWindow, sText, TEXT("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 ( 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; char sFileName[ MAX_PATH ]; _tcscpy(sFileName, g_sCurrentDir); // TODO: g_sDebugDir _tcscat(sFileName, pFileName ); FILE *hFile = fopen( pFileName, pMode ); if (hFile) { char *pText; int nLine = g_ConfigState.GetNumLines(); int iLine; for( iLine = 0; iLine < nLine; iLine++ ) { pText = g_ConfigState.GetLine( iLine ); if ( pText ) { fputs( pText, hFile ); } } fclose( hFile ); bStatus = true; } else { } return bStatus; } //=========================================================================== void ConfigSave_PrepareHeader ( const Parameters_e eCategory, const Commands_e eCommandClear ) { char sText[ CONSOLE_WIDTH ]; sprintf( sText, "%s %s = %s\n" , g_aTokens[ TOKEN_COMMENT_EOL ].sToken , g_aParameters[ PARAM_CATEGORY ].m_sName , g_aParameters[ eCategory ].m_sName ); g_ConfigState.PushLine( sText ); sprintf( sText, "%s %s\n" , g_aCommands[ eCommandClear ].m_sName , g_aParameters[ PARAM_WILDSTAR ].m_sName ); g_ConfigState.PushLine( sText ); } // Save Debugger Settings //=========================================================================== Update_t CmdConfigSave (int nArgs) { TCHAR sFilename[ MAX_PATH ]; _tcscpy( sFilename, g_sProgramDir ); // TODO: g_sDebugDir _tcscat( sFilename, 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; TCHAR sText[ CONSOLE_WIDTH ]; 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( sText, TEXT( "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( sText, TEXT( "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( sText, TEXT( "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( sText, TEXT( "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( sText, TEXT( "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( sText, TEXT( "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( sText, TEXT( "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; } // Config - Font __________________________________________________________________________________ //=========================================================================== Update_t CmdConfigFontLoad( int nArgs ) { return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdConfigFontSave( int nArgs ) { return UPDATE_CONSOLE_DISPLAY; } //=========================================================================== Update_t CmdConfigFontMode( int nArgs ) { if (nArgs != 2) return Help_Arg_1( CMD_CONFIG_FONT ); int nMode = g_aArgs[ 2 ].nValue; if ((nMode < 0) || (nMode >= NUM_FONT_SPACING)) return Help_Arg_1( CMD_CONFIG_FONT ); g_iFontSpacing = nMode; _UpdateWindowFontHeights( g_aFontConfig[ FONT_DISASM_DEFAULT ]._nFontHeight ); return UPDATE_CONSOLE_DISPLAY | UPDATE_DISASM; } //=========================================================================== Update_t CmdConfigFont (int nArgs) { int iArg; if (! nArgs) return CmdConfigGetFont( nArgs ); else if (nArgs <= 2) // nArgs { iArg = 1; // FONT * is undocumented, like VERSION * 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 )) ) { TCHAR sText[ CONSOLE_WIDTH ]; ConsoleBufferPushFormat( sText, "Lines: %d Font Px: %d Line Px: %d" , g_nDisasmDisplayLines , g_aFontConfig[ FONT_DISASM_DEFAULT ]._nFontHeight , g_aFontConfig[ FONT_DISASM_DEFAULT ]._nLineHeight ); ConsoleBufferToDisplay(); return UPDATE_CONSOLE_DISPLAY; } int iFound; int nFound; nFound = FindParam( g_aArgs[iArg].sArg, MATCH_EXACT, iFound, _PARAM_GENERAL_BEGIN, _PARAM_GENERAL_END ); if (nFound) { switch( iFound ) { case PARAM_LOAD: return CmdConfigFontLoad( nArgs ); break; case PARAM_SAVE: return CmdConfigFontSave( nArgs ); break; // TODO: FONT SIZE # // TODO: AA {ON|OFF} default: break; } } nFound = FindParam( g_aArgs[iArg].sArg, MATCH_EXACT, iFound, _PARAM_FONT_BEGIN, _PARAM_FONT_END ); if (nFound) { if (iFound == PARAM_FONT_MODE) return CmdConfigFontMode( nArgs ); } return CmdConfigSetFont( nArgs ); } return Help_Arg_1( CMD_CONFIG_FONT ); } // Only for FONT_DISASM_DEFAULT ! //=========================================================================== void _UpdateWindowFontHeights( int nFontHeight ) { if (nFontHeight) { int nConsoleTopY = GetConsoleTopPixels( g_nConsoleDisplayLines ); int nHeight = 0; if (g_iFontSpacing == FONT_SPACING_CLASSIC) { nHeight = nFontHeight + 1; g_nDisasmDisplayLines = nConsoleTopY / nHeight; } else if (g_iFontSpacing == FONT_SPACING_CLEAN) { nHeight = nFontHeight; g_nDisasmDisplayLines = nConsoleTopY / nHeight; } else if (g_iFontSpacing == FONT_SPACING_COMPRESSED) { nHeight = nFontHeight - 1; g_nDisasmDisplayLines = (nConsoleTopY + nHeight) / nHeight; // Ceil() } g_aFontConfig[ FONT_DISASM_DEFAULT ]._nLineHeight = nHeight; // int nHeightOptimal = (nHeight0 + nHeight1) / 2; // int nLinesOptimal = nConsoleTopY / nHeightOptimal; // g_nDisasmDisplayLines = nLinesOptimal; WindowUpdateSizes(); } } //=========================================================================== bool _CmdConfigFont ( int iFont, LPCSTR pFontName, int iPitchFamily, int nFontHeight ) { bool bStatus = false; HFONT hFont = (HFONT) 0; FontConfig_t *pFont = NULL; if (iFont < NUM_FONTS) pFont = & g_aFontConfig[ iFont ]; else return bStatus; if (pFontName) { // int nFontHeight = g_nFontHeight - 1; int bAntiAlias = (nFontHeight < 14) ? DEFAULT_QUALITY : ANTIALIASED_QUALITY; // Try allow new font hFont = CreateFont( nFontHeight , 0 // Width , 0 // Escapement , 0 // Orientatin , FW_MEDIUM // Weight , 0 // Italic , 0 // Underline , 0 // Strike Out , DEFAULT_CHARSET // OEM_CHARSET , OUT_DEFAULT_PRECIS , CLIP_DEFAULT_PRECIS , bAntiAlias // ANTIALIASED_QUALITY // DEFAULT_QUALITY , iPitchFamily // HACK: MAGIC #: 4 , pFontName ); if (hFont) { if (iFont == FONT_DISASM_DEFAULT) _UpdateWindowFontHeights( nFontHeight ); _tcsncpy( pFont->_sFontName, pFontName, MAX_FONT_NAME-1 ); pFont->_sFontName[ MAX_FONT_NAME-1 ] = 0; HDC hDC = FrameGetDC(); TEXTMETRIC tm; GetTextMetrics(hDC, &tm); SIZE size; TCHAR sText[] = "W"; int nLen = 1; int nFontWidthAvg; int nFontWidthMax; // if (! (tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) // Windows has this bitflag reversed! // { // Proportional font? // bool bStop = true; // } // GetCharWidth32() doesn't work with TrueType Fonts if (GetTextExtentPoint32( hDC, sText, nLen, &size )) { nFontWidthAvg = tm.tmAveCharWidth; nFontWidthMax = size.cx; } else { // Font Name Avg Max "W" // Arial 7 8 11 // Courier 5 32 11 // Courier New 7 14 nFontWidthAvg = tm.tmAveCharWidth; nFontWidthMax = tm.tmMaxCharWidth; } if (! nFontWidthAvg) { nFontWidthAvg = 7; nFontWidthMax = 7; } FrameReleaseDC(); // DeleteObject( g_hFontDisasm ); // g_hFontDisasm = hFont; pFont->_hFont = hFont; pFont->_nFontWidthAvg = nFontWidthAvg; pFont->_nFontWidthMax = nFontWidthMax; pFont->_nFontHeight = nFontHeight; bStatus = true; } } return bStatus; } //=========================================================================== Update_t CmdConfigSetFont (int nArgs) { #if OLD_FONT HFONT hFont = (HFONT) 0; TCHAR *pFontName = NULL; int nHeight = g_nFontHeight; int iFontTarget = FONT_DISASM_DEFAULT; int iFontPitch = FIXED_PITCH | FF_MODERN; // int iFontMode = bool bHaveTarget = false; bool bHaveFont = false; if (! nArgs) { // reset to defaut font pFontName = g_sFontNameDefault; } else if (nArgs <= 3) { int iArg = 1; pFontName = g_aArgs[1].sArg; // [DISASM|INFO|CONSOLE] "FontName" [#] // "FontName" can be either arg 1 or 2 int iFound; int nFound = FindParam( g_aArgs[iArg].sArg, MATCH_EXACT, iFound, _PARAM_WINDOW_BEGIN, _PARAM_WINDOW_END ); if (nFound) { switch( iFound ) { case PARAM_DISASM : iFontTarget = FONT_DISASM_DEFAULT; iFontPitch = FIXED_PITCH | FF_MODERN ; bHaveTarget = true; break; case PARAM_INFO : iFontTarget = FONT_INFO ; iFontPitch = FIXED_PITCH | FF_MODERN ; bHaveTarget = true; break; case PARAM_CONSOLE: iFontTarget = FONT_CONSOLE ; iFontPitch = DEFAULT_PITCH | FF_DECORATIVE; bHaveTarget = true; break; default: if (g_aArgs[2].bType != TOKEN_QUOTE_DOUBLE) return Help_Arg_1( CMD_CONFIG_FONT ); break; } if (bHaveTarget) { pFontName = g_aArgs[2].sArg; } } else if (nArgs == 2) { nHeight = atoi(g_aArgs[2].sArg ); if ((nHeight < 6) || (nHeight > 36)) nHeight = g_nFontHeight; } } else { return Help_Arg_1( CMD_CONFIG_FONT ); } if (! _CmdConfigFont( iFontTarget, pFontName, iFontPitch, nHeight )) { } #endif return UPDATE_ALL; } //=========================================================================== Update_t CmdConfigGetFont (int nArgs) { if (! nArgs) { for (int iFont = 0; iFont < NUM_FONTS; iFont++ ) { TCHAR sText[ CONSOLE_WIDTH ] = TEXT(""); ConsoleBufferPushFormat( sText, " Font: %-20s A:%2d M:%2d", // g_sFontNameCustom, g_nFontWidthAvg, g_nFontWidthMax ); g_aFontConfig[ iFont ]._sFontName, g_aFontConfig[ iFont ]._nFontWidthAvg, g_aFontConfig[ iFont ]._nFontWidthMax ); } return ConsoleUpdate(); } return UPDATE_CONSOLE_DISPLAY; } // Cursor _________________________________________________________________________________________ // Given an Address, and Line to display it on // Calculate the address of the top and bottom lines // @param bUpdateCur // true = Update Cur based on Top // false = Update Top & Bot based on Cur //=========================================================================== void DisasmCalcTopFromCurAddress( bool bUpdateTop ) { int nLen = ((g_nDisasmWinHeight - g_nDisasmCurLine) * 3); // max 3 opcodes/instruction, is our search window // Look for a start address that when disassembled, // will have the cursor on the specified line and address int iTop = g_nDisasmCurAddress - nLen; int iCur = g_nDisasmCurAddress; g_bDisasmCurBad = false; bool bFound = false; while (iTop <= iCur) { WORD iAddress = iTop; // int iOpcode; int iOpmode; int nOpbytes; for( int iLine = 0; iLine <= nLen; iLine++ ) // min 1 opcode/instruction { // a. _6502_GetOpmodeOpbyte( iAddress, iOpmode, nOpbytes ); // b. // _6502_GetOpcodeOpmodeOpbyte( iOpcode, iOpmode, nOpbytes ); if (iLine == g_nDisasmCurLine) // && (iAddress == g_nDisasmCurAddress)) { if (iAddress == g_nDisasmCurAddress) // b. // && (iOpmode != AM_1) && // && (iOpmode != AM_2) && // && (iOpmode != AM_3) && // && _6502_IsOpcodeValid( iOpcode)) { g_nDisasmTopAddress = iTop; bFound = true; break; } } // .20 Fixed: DisasmCalcTopFromCurAddress() //if ((eMode >= AM_1) && (eMode <= AM_3)) #if 0 // _DEBUG TCHAR sText[ CONSOLE_WIDTH ]; wsprintf( sText, "%04X : %d bytes\n", iAddress, nOpbytes ); OutputDebugString( sText ); #endif iAddress += nOpbytes; } if (bFound) { break; } iTop++; } if (! bFound) { // Well, we're up the creek. // There is no (valid) solution! // Basically, there is no address, that when disassembled, // will put our Address on the cursor Line! // So, like typical game programming, when we don't like the solution, change the problem! // if (bUpdateTop) g_nDisasmTopAddress = g_nDisasmCurAddress; g_bDisasmCurBad = true; // Bad Disassembler, no opcode for you! // We reall should move the cursor line to the top for one instruction. // Moving the cursor line around is not really a good idea, since we're breaking consistency paradigm for the user. // g_nDisasmCurLine = 0; #if 0 // _DEBUG TCHAR sText[ CONSOLE_WIDTH * 2 ]; sprintf( sText, TEXT("DisasmCalcTopFromCurAddress()\n" "\tTop: %04X\n" "\tLen: %04X\n" "\tMissed: %04X"), g_nDisasmCurAddress - nLen, nLen, g_nDisasmCurAddress ); MessageBox( g_hFrameWindow, sText, "ERROR", MB_OK ); #endif } } //=========================================================================== WORD DisasmCalcAddressFromLines( WORD iAddress, int nLines ) { while (nLines-- > 0) { int iOpmode; int nOpbytes; _6502_GetOpmodeOpbyte( iAddress, iOpmode, nOpbytes ); iAddress += nOpbytes; } return iAddress; } //=========================================================================== void DisasmCalcCurFromTopAddress() { g_nDisasmCurAddress = DisasmCalcAddressFromLines( g_nDisasmTopAddress, g_nDisasmCurLine ); } //=========================================================================== void DisasmCalcBotFromTopAddress( ) { g_nDisasmBotAddress = DisasmCalcAddressFromLines( g_nDisasmTopAddress, g_nDisasmWinHeight ); } //=========================================================================== void DisasmCalcTopBotAddress () { DisasmCalcTopFromCurAddress(); DisasmCalcBotFromTopAddress(); } //=========================================================================== 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; // if (! aBestTop.capacity() ) aTopCandidates.reserve( MAX_LOOK_AHEAD ); aTopCandidates.erase( aTopCandidates.begin(), aTopCandidates.end() ); 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 ); } //=========================================================================== WORD _ClampAddress( int nAddress ) { if (nAddress < 0) nAddress = 0; if (nAddress > _6502_MEM_END) nAddress = _6502_MEM_END; return (WORD) nAddress; } // nDelta must be a power of 2 //=========================================================================== void _CursorMoveDownAligned( int nDelta ) { if (g_iWindowThis == WINDOW_DATA) { if (g_aMemDump[0].bActive) { 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].bActive) { 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 nArgs) // TODO rename { regs.pc = nArgs; // HACK: return UPDATE_DISASM; } // Flags __________________________________________________________________________________________ //=========================================================================== Update_t CmdFlagClear (int nArgs) { int iFlag = (g_iCommand - CMD_FLAG_CLR_C); if (g_iCommand == CMD_FLAG_CLEAR) { 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] == g_aArgs[iArg].sArg[0]) { regs.ps &= ~(1 << iFlag); } 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) { 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] == g_aArgs[iArg].sArg[0]) { regs.ps |= (1 << iFlag); } 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 ___________________________________________________________________________________________ Update_t CmdDisk ( int nArgs) { if (! nArgs) goto _Help; // check for info command int iParam = 0; FindParam( g_aArgs[ 1 ].sArg, MATCH_EXACT, iParam, _PARAM_DISK_BEGIN, _PARAM_DISK_END ); if (iParam == PARAM_DISK_INFO) { if (nArgs > 2) goto _Help; int drive = DiskGetCurrentDrive() + 1; char buffer[200] = ""; ConsoleBufferPushFormat(buffer, "D%d at T$%X (%d), phase $%X, offset $%X, %s", drive, DiskGetCurrentTrack(), DiskGetCurrentTrack(), DiskGetCurrentPhase(), DiskGetCurrentOffset(), DiskGetCurrentState()); return ConsoleUpdate(); } if (nArgs < 2) goto _Help; // 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) goto _Help; if (iParam == PARAM_DISK_EJECT) { if (nArgs > 2) goto _Help; DiskEject( iDrive ); FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES); } else if (iParam == PARAM_DISK_PROTECT) { if (nArgs > 3) goto _Help; bool bProtect = true; if (nArgs == 3) bProtect = g_aArgs[ 3 ].nValue ? true : false; DiskSetProtect( iDrive, bProtect ); FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES); } else { if (nArgs != 3) goto _Help; LPCTSTR pDiskName = g_aArgs[ 3 ].sArg; // DISK # "Diskname" DiskInsert( iDrive, pDiskName, IMAGE_FORCE_WRITE_PROTECTED, IMAGE_DONT_CREATE ); FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES); } return UPDATE_CONSOLE_DISPLAY; _Help: return HelpLastCommand(); } // 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, "SY", 2) == 0) // SY6522 { nAddress = (g_aArgs[1].sArg[2] - '0') & 3; pArg->eDevice = DEV_SY6522; bUpdate = true; } else if(strncmp(g_aArgs[1].sArg, "AY", 2) == 0) // AY8910 { nAddress = (g_aArgs[1].sArg[2] - '0') & 3; pArg->eDevice = DEV_AY8910; 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; sprintf( pArg->sArg, "%04X", nAddress ); } 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)) { TCHAR sText[ CONSOLE_WIDTH ]; wsprintf( sText, TEXT(" Only %d memory mini dumps"), NUM_MEM_MINI_DUMPS ); ConsoleDisplayError( sText ); 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 TCHAR g_sMemoryLoadSaveFileName[ MAX_PATH ] = TEXT(""); // "PWD" //=========================================================================== Update_t CmdConfigGetDebugDir (int nArgs) { if( nArgs != 0 ) return Help_Arg_1( CMD_CONFIG_GET_DEBUG_DIR ); TCHAR sPath[ MAX_PATH + 8 ]; // TODO: debugger dir has no ` CONSOLE_COLOR_ESCAPE_CHAR ?!?! ConsoleBufferPushFormat( sPath, "Path: %s", g_sCurrentDir ); return ConsoleUpdate(); } // "CD" //=========================================================================== Update_t CmdConfigSetDebugDir (int nArgs) { if ( nArgs > 1 ) return Help_Arg_1( CMD_CONFIG_SET_DEBUG_DIR ); if ( nArgs == 0 ) return CmdConfigGetDebugDir(0); #if _WIN32 // 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 ); TCHAR sPath[ MAX_PATH + 1 ]; if (g_aArgs[1].sArg[1] == ':') // Absolute { _tcscpy( sPath, g_aArgs[1].sArg ); } else if (g_aArgs[1].sArg[0] == '\\') // Absolute { if (g_sCurrentDir[1] == ':') { _tcsncpy( sPath, g_sCurrentDir, 2 ); // Prefix with drive letter & colon sPath[2] = 0; } _tcscat( sPath, g_aArgs[1].sArg ); } else // Relative { // TODO: Support ".." - currently just appends (which still works) _tcscpy( sPath, g_sCurrentDir ); // TODO: debugger dir has no ` CONSOLE_COLOR_ESCAPE_CHAR ?!?! _tcscat( sPath, g_aArgs[1].sArg ); } if ( SetCurrentImageDir( sPath ) ) nArgs = 0; // intentional fall into #else #error "Need chdir() implemented" #endif 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 ); TCHAR sLoadSaveFilePath[ MAX_PATH ]; _tcscpy( 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) { _tcscpy( g_sMemoryLoadSaveFileName, g_aArgs[ 1 ].sArg ); } _tcscat( sLoadSaveFilePath, g_sMemoryLoadSaveFileName ); FILE *hFile = fopen( sLoadSaveFilePath, "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) { int iByte; for( iByte = 0; iByte < nAddressLen; iByte++ ) { *pDst++ = *pSrc++; } ConsoleBufferPush( TEXT( "Loaded." ) ); } fclose( hFile ); } else { ConsoleBufferPush( TEXT( "ERROR: Bad filename" ) ); CmdConfigGetDebugDir( 0 ); TCHAR sFile[ MAX_PATH + 8 ]; ConsoleBufferPushFormat( sFile, "File: %s", g_sMemoryLoadSaveFileName ); } 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 { 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; char *pFileName = g_aArgs[ 1 ].sArg; int nLen = strlen( pFileName ); 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 ); TCHAR sLoadSaveFilePath[ MAX_PATH ]; _tcscpy( sLoadSaveFilePath, g_sCurrentDir ); // TODO: g_sDebugDir 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) { _tcscpy( g_sMemoryLoadSaveFileName, pFileName ); } _tcscat( sLoadSaveFilePath, g_sMemoryLoadSaveFileName ); BYTE * const pMemBankBase = bBankSpecified ? MemGetBankPtr(nBank) : mem; if (!pMemBankBase) { ConsoleBufferPush( TEXT( "Error: Bank out of range." ) ); return ConsoleUpdate(); } FILE *hFile = fopen( sLoadSaveFilePath, "rb" ); if (hFile) { int 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) { char text[ 128 ]; ConsoleBufferPushFormat( text, "Loaded @ A$%04X,L$%04X", nAddressStart, nAddressLen ); } else { ConsoleBufferPush( TEXT( "Error loading data." ) ); } fclose( hFile ); if (bBankSpecified) { MemUpdatePaging(TRUE); } else { for (UINT i=(nAddressStart>>8); i!=((nAddressStart+nAddressLen)>>8); i++) { memdirty[i] = 0xff; } } } else { ConsoleBufferPush( TEXT( "ERROR: Bad filename" ) ); CmdConfigGetDebugDir( 0 ); TCHAR sFile[ MAX_PATH + 8 ]; ConsoleBufferPushFormat( sFile, "File: ", g_sMemoryLoadSaveFileName ); } 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) { TCHAR sLast[ CONSOLE_WIDTH ] = TEXT(""); if (nAddressLen) { ConsoleBufferPushFormat( sLast, TEXT("Last saved: $%04X:$%04X, %04X"), nAddressStart, nAddressEnd, nAddressLen ); } else { ConsoleBufferPush( sLast, 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; 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) { sprintf( g_sMemoryLoadSaveFileName, "%04X.%04X.bin", nAddressStart, nAddressLen ); // nAddressEnd ); } else { _tcscpy( g_sMemoryLoadSaveFileName, g_aArgs[ 1 ].sArg ); } _tcscat( 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 int iByte; for( 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) { TCHAR sLast[ CONSOLE_WIDTH ] = TEXT(""); if (nAddressLen) { if (!bBankSpecified) ConsoleBufferPushFormat( sLast, TEXT("Last saved: $%04X:$%04X, %04X"), nAddressStart, nAddressEnd, nAddressLen ); else ConsoleBufferPushFormat( sLast, TEXT("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 ); 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) { if (! bBankSpecified) sprintf( g_sMemoryLoadSaveFileName, "%04X.%04X.bin", nAddressStart, nAddressLen ); else sprintf( g_sMemoryLoadSaveFileName, "%04X.%04X.bank%02X.bin", nAddressStart, nAddressLen, nBank ); } else { _tcscpy( g_sMemoryLoadSaveFileName, g_aArgs[ 1 ].sArg ); } _tcscat( 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, "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, "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 = 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 ( 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; } void Util_CopyTextToClipboard ( const size_t nSize, const char *pText ) { HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, nSize + 1 ); memcpy( GlobalLock(hMem), pText, nSize + 1 ); GlobalUnlock( hMem ); OpenClipboard(0); EmptyClipboard(); SetClipboardData( CF_TEXT, hMem ); CloseClipboard(); // GlobalFree() ?? } //=========================================================================== 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 { 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 char *pFileName = (nArgs > 1) ? g_aArgs[ 2 ].sArg : ""; int nLen = strlen( pFileName ); 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 TCHAR sPaletteFilePath[ MAX_PATH ]; _tcscpy( sPaletteFilePath, g_sCurrentDir ); _tcscat( sPaletteFilePath, pFileName ); class ConsoleFilename { public: static void update( const char *pPrefixText ) { TCHAR text[ CONSOLE_WIDTH*2 ] = TEXT(""); size_t len1 = strlen( pPrefixText ); size_t len2 = strlen( sPaletteFilePath ); size_t len = len1 + len2; if (len >= CONSOLE_WIDTH) { ConsoleBufferPush( pPrefixText ); // TODO: Add a ": " separator #if _DEBUG sprintf( text, "Filename.length.1: %d\n", len1 ); OutputDebugString( text ); sprintf( text, "Filename.length.2: %d\n", len2 ); OutputDebugString( text ); OutputDebugString( sPaletteFilePath ); #endif // File path is too long // TODO: Need to split very long path names strncpy( text, sPaletteFilePath, CONSOLE_WIDTH ); ConsoleBufferPush( text ); // TODO: Switch ConsoleBufferPush() to ConsoleBufferPushFormat() } else { ConsoleBufferPushFormat( text, "%s: %s", pPrefixText, sPaletteFilePath ); } } }; 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 char text[ 128 ]; sprintf( text, "[ %X ] = [ %X ]\n", iDstX, iSrcX ); OutputDebugStringA( text ); #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 = (g_eVideoType == VT_COLOR_TV); uint32_t* pChromaTable = NTSC_VideoGetChromaTable( false, bColorTV ); char aStatusText[ CONSOLE_WIDTH*2 ] = "Loaded"; //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, "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; 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) { FILE *pFile = fopen( sPaletteFilePath, "rb" ); if( pFile ) { strcpy( aStatusText, "Loaded" ); // Get File Size size_t nFileSize = _GetFileSize( pFile ); uint8_t *pSwizzled = new uint8_t[ g_nChromaSize ]; bool bSwizzle = true; WinBmpHeader4_t bmp, *pBmp = &bmp; if( iFileType == TYPE_BMP ) { fread( pBmp, sizeof( WinBmpHeader4_t ), 1, pFile ); fseek( pFile, pBmp->nOffsetData, SEEK_SET ); if (pBmp->nBitsPerPixel != 32) { strcpy( aStatusText, "Bitmap not 32-bit RGBA" ); goto _error; } if (pBmp->nOffsetData > nFileSize) { strcpy( aStatusText, "Bad BITMAP: Data > file size !?" ); goto _error; } if( ! ( ((pBmp->nWidthPixels == 64 ) && (pBmp->nHeightPixels == 256)) || ((pBmp->nWidthPixels == 64 ) && (pBmp->nHeightPixels == 1)) || ((pBmp->nWidthPixels == 16 ) && (pBmp->nHeightPixels == 1)) )) { strcpy( aStatusText, "Bitmap not 64x256, 64x1, or 16x1" ); goto _error; } if(pBmp->nStructSize == 0x28) { if( pBmp->nCompression == 0) // BI_RGB mode bSwizzle = false; } else // 0x7C version4 bitmap { if( pBmp->nCompression == 3 ) // BI_BITFIELDS { if((pBmp->nRedMask == 0xFF000000 ) // Gimp writes in ABGR order && (pBmp->nGreenMask == 0x00FF0000 ) && (pBmp->nBlueMask == 0x0000FF00 )) bSwizzle = true; } } } else if( nFileSize != g_nChromaSize ) { sprintf( aStatusText, "Raw size != %d", 64*256*4 ); goto _error; } size_t nRead = fread( pSwizzled, g_nChromaSize, 1, pFile ); if( iFileType == TYPE_BMP ) { if (pBmp->nHeightPixels == 1) { uint8_t *pTemp64x256 = new uint8_t[ 64 * 256 * 4 ]; memset( pTemp64x256, 0, g_nChromaSize ); //Transpose16x1::transposeFrom16x1( pSwizzled, (uint8_t*) pChromaTable ); if (pBmp->nWidthPixels == 16) { Transpose16x1::transposeTo64x1( pSwizzled, pTemp64x256 ); Transpose64x1::transposeTo64x256( pTemp64x256, pTemp64x256 ); } else if (pBmp->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 { strcpy( aStatusText, "File: " ); ConsoleBufferPush( TEXT( "Error couldn't open file for reading." ) ); } ConsoleFilename::update( aStatusText ); } 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 ); TCHAR sLoadSaveFilePath[ MAX_PATH ]; _tcscpy( sLoadSaveFilePath, g_sCurrentDir ); // g_sProgramDir if( bHaveFileName ) _tcscpy( g_sMemoryLoadSaveFileName, g_aArgs[ 1 ].sArg ); else { if( VideoGetSW80COL() ) sprintf( g_sMemoryLoadSaveFileName, "AppleWin_Text80.txt" ); else sprintf( g_sMemoryLoadSaveFileName, "AppleWin_Text40.txt" ); } _tcscat( sLoadSaveFilePath, g_sMemoryLoadSaveFileName ); 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( pText, nSize, 1, hFile ); if (nWrote == 1) { TCHAR text[ CONSOLE_WIDTH ] = TEXT(""); ConsoleBufferPushFormat( text, "Saved: %s", g_sMemoryLoadSaveFileName ); } else { ConsoleBufferPush( TEXT( "Error saving." ) ); } fclose( hFile ); } else { ConsoleBufferPush( TEXT( "Error opening file." ) ); } return ConsoleUpdate(); } //=========================================================================== int _SearchMemoryFind( MemorySearchValues_t vMemorySearchValues, WORD nAddressStart, WORD nAddressEnd ) { int nFound = 0; g_vMemorySearchResults.erase( g_vMemorySearchResults.begin(), g_vMemorySearchResults.end() ); g_vMemorySearchResults.push_back( NO_6502_TARGET ); WORD nAddress; for( nAddress = nAddressStart; nAddress < nAddressEnd; nAddress++ ) { bool bMatchAll = true; WORD 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; MemorySearch_t ms2 = vMemorySearchValues.at( iBlock + 1 ); WORD nAddress3 = nAddress2; for (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) { const UINT nBuf = CONSOLE_WIDTH * 2; int nFound = g_vMemorySearchResults.size() - 1; int nLen = 0; // temp int nLineLen = 0; // string length of matches for this line, for word-wrap TCHAR sMatches[ nBuf ] = TEXT(""); TCHAR sResult[ nBuf ]; TCHAR sText[ nBuf ] = TEXT(""); if (nFound > 0) { int iFound = 1; while (iFound <= nFound) { WORD nAddress = g_vMemorySearchResults.at( iFound ); // sprintf( sText, "%2d:$%04X ", iFound, nAddress ); // int nLen = _tcslen( sText ); sResult[0] = 0; nLen = 0; StringCat( sResult, CHC_NUM_DEC, nBuf ); // 2.6.2.17 Search Results: The n'th result now using correct color (was command, now number decimal) sprintf( sText, "%02X", iFound ); // 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 nLen += StringCat( sResult, sText , nBuf ); StringCat( sResult, CHC_DEFAULT, nBuf ); // intentional default instead of CHC_ARG_SEP for better readability nLen += StringCat( sResult, ":" , nBuf ); StringCat( sResult, CHC_ARG_SEP, nBuf ); nLen += StringCat( sResult, "$" , nBuf ); // 2.6.2.16 Fixed: Search Results: The hex specify for target address results now colorized properly StringCat( sResult, CHC_ADDRESS, nBuf ); sprintf( sText, "%04X ", nAddress ); // 2.6.2.15 Fixed: Search Results: Added space between results for better readability nLen += StringCat( sResult, sText, nBuf ); // Fit on same line? if ((nLineLen + nLen) > (g_nConsoleDisplayWidth - 1)) // CONSOLE_WIDTH { //ConsoleDisplayPush( sMatches ); ConsolePrint( sMatches ); _tcscpy( sMatches, sResult ); nLineLen = nLen; } else { StringCat( sMatches, sResult, nBuf ); nLineLen += nLen; } iFound++; } ConsolePrint( sMatches ); } // wsprintf( sMatches, "Total: %d (#$%04X)", nFound, nFound ); // ConsoleDisplayPush( sMatches ); sResult[0] = 0; StringCat( sResult, CHC_USAGE , nBuf ); nLen += StringCat( sResult, "Total", nBuf ); StringCat( sResult, CHC_DEFAULT, nBuf ); nLen += StringCat( sResult, ": " , nBuf ); StringCat( sResult, CHC_NUM_DEC, nBuf ); // intentional CHC_DEFAULT instead of sprintf( sText, "%d ", nFound ); nLen += StringCat( sResult, sText, nBuf ); StringCat( sResult, CHC_ARG_SEP, nBuf ); // CHC_ARC_OPT -> CHC_ARG_SEP nLen += StringCat( sResult, "(" , nBuf ); StringCat( sResult, CHC_ARG_SEP, nBuf ); // CHC_DEFAULT nLen += StringCat( sResult, "#$", nBuf ); StringCat( sResult, CHC_NUM_HEX, nBuf ); sprintf( sText, "%04X", nFound ); nLen += StringCat( sResult, sText, nBuf ); StringCat( sResult, CHC_ARG_SEP, nBuf ); nLen += StringCat( sResult, ")" , nBuf ); ConsolePrint( sResult ); // 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( TEXT("Error: Missing address seperator (comma or colon)" ) ); int iArgFirstByte = 4; // S start,len # int nMinLen = nArgs - (iArgFirstByte - 1); bool bHaveWildCards = false; int iArg; MemorySearchValues_t vMemorySearchValues; MemorySearch_e tLastType = MEM_SEARCH_BYTE_N_WILD; // Get search "string" Arg_t *pArg = & g_aArgs[ iArgFirstByte ]; WORD nTarget; for (iArg = iArgFirstByte; iArg <= nArgs; iArg++, pArg++ ) { MemorySearch_t ms; nTarget = pArg->nValue; ms.m_nValue = nTarget & 0xFF; ms.m_iType = MEM_SEARCH_BYTE_EXACT; 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.erase( vMemorySearchValues.begin(), vMemorySearchValues.end() ); 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.erase( vMemorySearchValues.begin(), vMemorySearchValues.end() ); 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; TCHAR sText [ CONSOLE_WIDTH ]; 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 } wsprintf( sText, TEXT("$%04X 0z%08X %5d '%c' "), nAddress, nBit, nAddress, c ); if (bParen) _tcscat( sText, TEXT("(") ); if (bHi & bLo) _tcscat( sText, TEXT("High Ctrl") ); else if (bHi) _tcscat( sText, TEXT("High") ); else if (bLo) _tcscat( sText, TEXT("Ctrl") ); if (bParen) _tcscat( sText, TEXT(")") ); ConsoleBufferPush( sText ); return ConsoleUpdate(); } //=========================================================================== Update_t CmdOutputEcho (int nArgs) { TCHAR sText[ CONSOLE_WIDTH ] = TEXT(""); 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 TCHAR sText[ CONSOLE_WIDTH ] = TEXT(""); int nLen = 0; int nValue; if (! nArgs) goto _Help; int iArg; for (iArg = 1; iArg <= nArgs; iArg++ ) { if (g_aArgs[ iArg ].bType & TYPE_QUOTED_2) { int iChar; int nChar = _tcslen( g_aArgs[ iArg ].sArg ); for( iChar = 0; iChar < nChar; iChar++ ) { TCHAR c = g_aArgs[ iArg ].sArg[ iChar ]; sText[ nLen++ ] = c; } iArg++; // if (iArg > nArgs) // goto _Help; if (iArg <= nArgs) if (g_aArgs[ iArg ].eToken != TOKEN_COMMA) goto _Help; } else { nValue = g_aArgs[ iArg ].nValue; sprintf( &sText[ nLen ], "%04X", nValue ); while (sText[ nLen ]) nLen++; iArg++; if (iArg <= nArgs) if (g_aArgs[ iArg ].eToken != TOKEN_COMMA) goto _Help; } #if 0 sprintf( &sText[ nLen ], "%04X", nValue ); sprintf( &sText[ nLen ], "%d", nValue ); sprintf( &sText[ nLen ], "%c", nValue ); #endif } if (nLen) ConsoleBufferPush( sText ); 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 TCHAR sText[ CONSOLE_WIDTH ] = TEXT(""); std::vector aValues; Arg_t entry; int iValue = 0; int nValue = 0; if (! nArgs) goto _Help; int nLen = 0; PrintState_e eThis = PS_LITERAL; // PrintState_e eNext = PS_NEXT_ARG_HEX; // PS_LITERAL; int nWidth = 0; int iArg; for (iArg = 1; iArg <= nArgs; iArg++ ) { if (g_aArgs[ iArg ].bType & TYPE_QUOTED_2) continue; else if (g_aArgs[ iArg ].eToken == TOKEN_COMMA) continue; else { // entry.eType = PS_LITERAL; entry.nValue = g_aArgs[ iArg ].nValue; aValues.push_back( entry ); // nValue = g_aArgs[ iArg ].nValue; // aValues.push_back( nValue ); } } const int nParamValues = (int) aValues.size(); for (iArg = 1; iArg <= nArgs; iArg++ ) { if (g_aArgs[ iArg ].bType & TYPE_QUOTED_2) { int iChar; int nChar = _tcslen( g_aArgs[ iArg ].sArg ); for( iChar = 0; iChar < nChar; iChar++ ) { TCHAR c = g_aArgs[ iArg ].sArg[ iChar ]; switch ( eThis ) { case PS_LITERAL: switch( c ) { case '\\': eThis = PS_ESCAPE; break; case '%': eThis = PS_TYPE; break; default: sText[ nLen++ ] = c; break; } break; case PS_ESCAPE: switch( c ) { case 'n': case 'r': eThis = PS_LITERAL; sText[ nLen++ ] = '\n'; break; } break; case PS_TYPE: if (iValue >= nParamValues) { ConsoleBufferPushFormat( sText, TEXT("Error: Missing value arg: %d"), iValue + 1 ); return ConsoleUpdate(); } switch( c ) { case 'X': case 'x': // PS_NEXT_ARG_HEX nValue = aValues[ iValue ].nValue; sprintf( &sText[ nLen ], "%04X", nValue ); iValue++; break; case 'D': case 'd': // PS_NEXT_ARG_DEC nValue = aValues[ iValue ].nValue; sprintf( &sText[ nLen ], "%d", nValue ); iValue++; break; break; case 'Z': case 'z': { nValue = aValues[ iValue ].nValue; if (!nWidth) nWidth = 8; int nBits = nWidth; while (nBits-- > 0) { if ((nValue >> nBits) & 1) sText[ nLen++ ] = '1'; else sText[ nLen++ ] = '0'; } iValue++; break; } case 'c': // PS_NEXT_ARG_CHR; nValue = aValues[ iValue ].nValue; sprintf( &sText[ nLen ], "%c", nValue ); iValue++; break; case '%': default: sText[ nLen++ ] = c; break; } while (sText[ nLen ]) nLen++; eThis = PS_LITERAL; break; default: break; } } } else if (g_aArgs[ iArg ].eToken == TOKEN_COMMA) { iArg++; if (iArg > nArgs) goto _Help; } else goto _Help; } if (nLen) ConsoleBufferPush( sText ); return ConsoleUpdate(); _Help: return Help_Arg_1( CMD_OUTPUT_PRINTF ); } //=========================================================================== Update_t CmdOutputRun (int nArgs) { 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; TCHAR * pFileName = g_aArgs[ 1 ].sArg; TCHAR sFileName[ MAX_PATH ]; TCHAR sMiniFileName[ CONSOLE_WIDTH ]; // if (g_aArgs[1].bType & TYPE_QUOTED_2) _tcscpy( sMiniFileName, pFileName ); // _tcscat( sMiniFileName, ".aws" ); // HACK: MAGIC STRING if (pFileName[0] == '\\' || pFileName[1] == ':') // NB. Any prefix quote has already been stripped { // Abs pathname _tcscpy(sFileName, sMiniFileName); } else { // Rel pathname _tcscpy(sFileName, g_sCurrentDir); _tcscat(sFileName, sMiniFileName); } if (script.Read( sFileName )) { int iLine = 0; 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 { char sText[ CONSOLE_WIDTH ]; ConsolePrintFormat( sText, "%sCouldn't load filename: %s%s" , CHC_ERROR , CHC_STRING , sFileName ); } return ConsoleUpdate(); } // Source Level Debugging _________________________________________________________________________ //=========================================================================== bool BufferAssemblyListing( char *pFileName ) { bool bStatus = false; // true = loaded if (! pFileName) 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 { TCHAR sText[ CONSOLE_WIDTH ]; for (int i = 0; i < g_vSourceLines.size(); i++ ) { wsprintf( sText, "%d: %s\n", i, g_vSourceLines[ i ] ); OutputDebugString( sText ); } } #endif SourceAssembly_t::iterator iSource = g_aSourceDebug.begin(); while (iSource != g_aSourceDebug.end() ) { iAddress = iSource->first; iLine = iSource->second; #if 0 // _DEBUG TCHAR sText[ CONSOLE_WIDTH ]; wsprintf( sText, "%04X -> %d line\n", iAddress, iLine ); OutputDebugString( sText ); #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; bool bPrevSymbol = false; bool bFourBytes = false; BYTE nByte4 = 0; 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++ ) { TCHAR *pFileName = g_aArgs[ iArg ].sArg; int iParam; bool bFound = FindParam( pFileName, 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 { TCHAR sFileName[MAX_PATH]; _tcscpy(sFileName,g_sProgramDir); _tcscat(sFileName, pFileName); const int MAX_MINI_FILENAME = 20; TCHAR sMiniFileName[ MAX_MINI_FILENAME + 1 ]; _tcsncpy( sMiniFileName, pFileName, MAX_MINI_FILENAME - 1 ); sMiniFileName[ MAX_MINI_FILENAME ] = 0; if (BufferAssemblyListing( sFileName )) { _tcscpy( g_aSourceFileName, pFileName ); if (! ParseAssemblyListing( g_bSourceAddMemory, g_bSourceAddSymbols )) { ConsoleBufferPushFormat( sFileName, "Couldn't load filename: %s", sMiniFileName ); } else { if (g_nSourceAssembleBytes) { ConsoleBufferPushFormat( sFileName, " Read: %d lines, %d symbols, %d bytes" , g_AssemblerSourceBuffer.GetNumLines() // g_nSourceAssemblyLines , g_nSourceAssemblySymbols, g_nSourceAssembleBytes ); } else { ConsoleBufferPushFormat( sFileName, " Read: %d lines, %d symbols" , g_AssemblerSourceBuffer.GetNumLines() // g_nSourceAssemblyLines , g_nSourceAssemblySymbols ); } } } else { ConsoleBufferPushFormat( sFileName, "Error reading: %s", sMiniFileName ); } } } 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; } // View ___________________________________________________________________________________________ // See: CmdWindowViewOutput (int nArgs) enum ViewVideoPage_t { VIEW_PAGE_X, // current page VIEW_PAGE_1, VIEW_PAGE_2 }; Update_t _ViewOutput( ViewVideoPage_t iPage, int bVideoModeFlags ) { switch( iPage ) { case VIEW_PAGE_X: bVideoModeFlags |= !VideoGetSWPAGE2() ? 0 : VF_PAGE2; break; // Page Current case VIEW_PAGE_1: bVideoModeFlags |= 0; break; // Page 1 case VIEW_PAGE_2: bVideoModeFlags |= VF_PAGE2; break; // Page 2 default: break; } DebugVideoMode::Instance().Set(bVideoModeFlags); 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_HGR1 (int nArgs) { return _ViewOutput( VIEW_PAGE_1, VF_HIRES ); } Update_t CmdViewOutput_HGR2 (int nArgs) { return _ViewOutput( VIEW_PAGE_2, VF_HIRES ); } // 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 ); } // Watches ________________________________________________________________________________________ //=========================================================================== Update_t CmdWatch (int nArgs) { return CmdWatchAdd( nArgs ); } //=========================================================================== Update_t CmdWatchAdd (int nArgs) { // WA [adddress] // WA # address if (! nArgs) { return CmdWatchList( 0 ); } int iArg = 1; int iWatch = NO_6502_TARGET; if (nArgs > 1) { iWatch = g_aArgs[ 1 ].nValue; iArg++; } bool bAdded = false; for (; iArg <= nArgs; iArg++ ) { 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(TEXT("You may not watch an I/O location.")); if (iWatch == NO_6502_TARGET) { iWatch = 0; while ((iWatch < MAX_ZEROPAGE_POINTERS) && (g_aWatches[iWatch].bSet)) { iWatch++; } } if ((iWatch >= MAX_WATCHES) && !bAdded) { char sText[ CONSOLE_WIDTH ]; sprintf( sText, "All watches are currently in use. (Max: %d)", MAX_WATCHES ); ConsoleDisplayPush( sText ); return ConsoleUpdate(); } if ((iWatch < MAX_WATCHES) && (g_nWatches < MAX_WATCHES)) { g_aWatches[iWatch].bSet = true; g_aWatches[iWatch].bEnabled = true; 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(TEXT("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(TEXT("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(TEXT("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) { TCHAR sText[ CONSOLE_WIDTH ]; ConsoleBufferPushFormat( sText, TEXT(" 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) { VideoRedrawScreen(); DebugVideoMode::Instance().Set(g_uVideoMode); 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 CmdZeroPage (int nArgs) { // ZP [address] // ZP # address return CmdZeroPageAdd( nArgs ); } //=========================================================================== 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; iArg++; } bool bAdded = false; for (; iArg <= nArgs; iArg++ ) { WORD nAddress = g_aArgs[iArg].nValue; if (iZP == NO_6502_TARGET) { iZP = 0; while ((iZP < MAX_ZEROPAGE_POINTERS) && (g_aZeroPagePointers[iZP].bSet)) { iZP++; } } if ((iZP >= MAX_ZEROPAGE_POINTERS) && !bAdded) { char sText[ CONSOLE_WIDTH ]; sprintf( sText, "All zero page pointers are currently in use. (Max: %d)", MAX_ZEROPAGE_POINTERS ); ConsoleDisplayPush( sText ); 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." ); char sText[ CONSOLE_WIDTH ]; sprintf( sText, " There are no current (ZP) pointers. (Max: %d)", MAX_ZEROPAGE_POINTERS ); // ConsoleBufferPush( sText ); return ConsoleDisplayError( sText ); } //=========================================================================== Update_t CmdZeroPageClear (int nArgs) { if (!g_nBreakpoints) 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( LPTSTR 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( LPTSTR pName, CmdFuncPtr_t & pFunction_, int * iCommand_ ) { g_vPotentialCommands.erase( g_vPotentialCommands.begin(), g_vPotentialCommands.end() ); 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(pName, pCommandName)) // exact match? { // if (iCommand_) // *iCommand_ = iCommand; nFound = 1; // Exact match takes precidence over fuzzy matches g_vPotentialCommands.erase( g_vPotentialCommands.begin(), g_vPotentialCommands.end() ); break; } } } } iCommand++; } // if (nFound == 1) // { // // } return nFound; } //=========================================================================== void DisplayAmbigiousCommands( int nFound ) { char sText[ CONSOLE_WIDTH * 2 ]; ConsolePrintFormat( sText, "Ambiguous %s%d%s Commands:" , CHC_NUM_DEC , g_vPotentialCommands.size() , CHC_DEFAULT ); int iCommand = 0; while (iCommand < nFound) { char sPotentialCommands[ CONSOLE_WIDTH ]; sprintf( sPotentialCommands, "%s ", CHC_COMMAND ); int iWidth = strlen( sPotentialCommands ); while ((iCommand < nFound) && (iWidth < g_nConsoleDisplayWidth)) { int nCommand = g_vPotentialCommands[ iCommand ]; char *pName = g_aCommands[ nCommand ].m_sName; int nLen = strlen( pName ); if ((iWidth + nLen) >= (CONSOLE_WIDTH - 1)) break; sprintf( sText, "%s ", pName ); strcat( sPotentialCommands, sText ); iWidth += nLen + 1; iCommand++; } ConsolePrint( sPotentialCommands ); } } 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')) { 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 ); //char sText[ CONSOLE_WIDTH ]; //ConsolePrintFormat( sText, "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 () { DisasmLine_t line; GetDisassemblyLine( regs.pc, line ); char sDisassembly[ CONSOLE_WIDTH ]; // DrawDisassemblyLine( 0,regs.pc, sDisassembly); // Get Disasm String FormatDisassemblyLine( line, sDisassembly, CONSOLE_WIDTH ); char sFlags[ _6502_NUM_FLAGS + 1 ]; DrawFlags( 0, regs.ps, sFlags ); // Get Flags String if (!g_hTraceFile) return; 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, // "00 00 00 0000 -------- 0000:90 90 90 NOP" "A: X: Y: SP: Flags Addr:Opcode Mnemonic\n"); } } char sTarget[ 16 ]; if (line.bTargetValue) { sprintf( sTarget, "%s:%s" , line.sTargetPointer , line.sTargetValue ); } if (g_bTraceFileWithVideoScanner) { uint16_t addr = NTSC_VideoGetScannerAddress(0); // NB. uExecutedCycles==0 as SingleStep() called afterwards BYTE data = mem[addr]; fprintf( g_hTraceFile, "%04X %04X %04X %02X %02X %02X %02X %04X %s %s\n", g_nVideoClockVert, g_nVideoClockHorz, addr, data, (unsigned)regs.a, (unsigned)regs.x, (unsigned)regs.y, (unsigned)regs.sp, (char*) sFlags , sDisassembly //, sTarget // TODO: Show target? ); } else { fprintf( g_hTraceFile, "%02X %02X %02X %04X %s %s\n", (unsigned)regs.a, (unsigned)regs.x, (unsigned)regs.y, (unsigned)regs.sp, (char*) sFlags , sDisassembly //, sTarget // TODO: Show target? ); } } //=========================================================================== int ParseInput ( LPTSTR pConsoleInput, bool bCook ) { int nArg = 0; // TODO: need to check for non-quoted command seperator ';', 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. //=========================================================================== char * ProfileLinePeek ( int iLine ) { char *pText = NULL; if (iLine < 0) iLine = 0; if (! g_nProfileLine) pText = & g_aProfileLine[ iLine ][ 0 ]; if (iLine <= g_nProfileLine) pText = & g_aProfileLine[ iLine ][ 0 ]; return pText; } //=========================================================================== char * 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 ) { char sSeperator7[ 32 ] = "\t"; char sSeperator2[ 32 ] = "\t"; char sSeperator1[ 32 ] = "\t"; char sOpcode [ 8 ]; // 2 chars for opcode in hex, plus quotes on either side char sAddress[MAX_OPMODE_NAME]; if (eFormatMode == PROFILE_FORMAT_COMMA) { sSeperator7[0] = ','; sSeperator2[0] = ','; sSeperator1[0] = ','; } else if (eFormatMode == PROFILE_FORMAT_SPACE) { sprintf( sSeperator7, " " ); // 7 sprintf( sSeperator2, " " ); // 2 sprintf( sSeperator1, " " ); // 1 } ProfileLineReset(); char *pText = ProfileLinePeek( 0 ); int iOpcode; int iOpmode; 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 (iOpcode = 0; iOpcode < NUM_OPCODES; ++iOpcode ) { nOpcodeTotal += vProfileOpcode[ iOpcode ].m_nCount; } for (iOpmode = 0; iOpmode < NUM_OPMODES; ++iOpmode ) { nOpmodeTotal += vProfileOpmode[ iOpmode ].m_nCount; } if (nOpcodeTotal < 1.) { nOpcodeTotal = 1; bOpcodeGood = false; } char *pColorOperator = ""; char *pColorNumber = ""; char *pColorOpcode = ""; char *pColorMnemonic = ""; char *pColorOpmode = ""; 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 } // Opcode if (bExport) // Export = SeperateColumns sprintf( pText , "\"Percent\"" DELIM "\"Count\"" DELIM "\"Opcode\"" DELIM "\"Mnemonic\"" DELIM "\"Addressing Mode\"\n" , sSeperator7, sSeperator2, sSeperator1, sSeperator1 ); else sprintf( pText , "Percent" DELIM "Count" DELIM "Mnemonic" DELIM "Addressing Mode\n" , sSeperator7, sSeperator2, sSeperator1 ); pText = ProfileLinePush(); for (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; char sOpmode[ MAX_OPMODE_FORMAT ]; sprintf( sOpmode, g_aOpmodes[ nOpmode ].m_sFormat, 0 ); if (bExport) { // 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! sprintf( sOpcode, "\"%02X\"", nOpcode ); // Works with Excel, IF using Import dialog & choose Text. (also works with OpenOffice) // sprintf( sOpcode, "'%02X", nOpcode ); // SHOULD work with Excel, but only works with OpenOffice. sprintf( sAddress, "\"%s\"", g_aOpmodes[ nOpmode ].m_sName ); } else // not qouted if dumping to console { sprintf( sOpcode, "%02X", nOpcode ); strcpy( sAddress, 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. sprintf( pText, "%s%7.4f%s%%" DELIM "%s%9u" DELIM "%s%s" DELIM "%s%s" DELIM "%s%s\n" , pColorNumber , nPercent , pColorOperator , sSeperator2 , pColorNumber , static_cast(nCount), sSeperator2 , pColorOpcode , sOpcode, sSeperator2 , pColorMnemonic , g_aOpcodes[ nOpcode ].sMnemonic, sSeperator2 , pColorOpmode , sAddress ); pText = ProfileLinePush(); } if (! bOpcodeGood) nOpcodeTotal = 0; sprintf( pText , "Total: " DELIM "%s%9u\n" , sSeperator2 , pColorTotal , static_cast(nOpcodeTotal) ); pText = ProfileLinePush(); sprintf( pText, "\n" ); pText = ProfileLinePush(); // Opmode // "Percent Count Adressing Mode\n" ); if (bExport) // Note: 2 extra dummy columns are inserted to keep Addressing Mode in same column sprintf( pText , "\"Percent\"" DELIM "\"Count\"" DELIM DELIM DELIM "\"Addressing Mode\"\n" , sSeperator7, sSeperator2, sSeperator2, sSeperator2 ); else { sprintf( pText , "Percent" DELIM "Count" DELIM "Addressing Mode\n" , sSeperator7, sSeperator2 ); } pText = ProfileLinePush(); if (nOpmodeTotal < 1) { nOpmodeTotal = 1.; bOpmodeGood = false; } for (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; if (bExport) { // Note: 2 extra dummy columns are inserted to keep Addressing Mode in same column sprintf( sAddress, "%s%s\"%s\"", sSeperator1, sSeperator1, g_aOpmodes[ nOpmode ].m_sName ); } else // not qouted if dumping to console { strcpy( sAddress, 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. sprintf( pText , "%s%7.4f%s%%" DELIM "%s%9u" DELIM "%s%s\n" , pColorNumber , nPercent , pColorOperator , sSeperator2 , pColorNumber , static_cast(nCount), sSeperator2 , pColorOpmode , sAddress ); pText = ProfileLinePush(); } if (! bOpmodeGood) nOpmodeTotal = 0; sprintf( pText , "Total: " DELIM "%s%9u\n" , sSeperator2 , pColorTotal , static_cast(nOpmodeTotal) ); pText = ProfileLinePush(); sprintf( pText, "===================\n" ); pText = ProfileLinePush(); unsigned int cycles = static_cast(g_nCumulativeCycles - g_nProfileBeginCycles); sprintf( pText , "Cycles: " DELIM "%s%9u\n" , sSeperator2 , pColorNumber , cycles ); pText = ProfileLinePush(); } #undef DELIM //=========================================================================== void ProfileReset() { int iOpcode; int iOpmode; for (iOpcode = 0; iOpcode < NUM_OPCODES; iOpcode++ ) { g_aProfileOpcodes[ iOpcode ].m_iOpcode = iOpcode; g_aProfileOpcodes[ iOpcode ].m_nCount = 0; } for (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; char sFilename[MAX_PATH]; strcpy( sFilename, g_sProgramDir ); // TODO: Allow user to decide? strcat( sFilename, g_FileNameProfile ); FILE *hFile = fopen( sFilename, "wt" ); if (hFile) { char *pText; int nLine = g_nProfileLine; int iLine; for( iLine = 0; iLine < nLine; iLine++ ) { pText = ProfileLinePeek( iLine ); if ( pText ) { fputs( pText, 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; FrameRefreshStatus(DRAW_TITLE); 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 ); #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 () { 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 IsDebugBreakOnInvalid( AM_IMPLIED ); if (g_aOpcodes[iOpcode].sMnemonic[0] >= 'a') // All 6502/65C02 undocumented opcodes mnemonics are lowercase strings! { // TODO: Translate g_aOpcodes[iOpcode].nAddressMode into {AM_1, AM_2, AM_3} IsDebugBreakOnInvalid( AM_1 ); } // User wants to enter debugger on specific opcode? (NB. Can't be BRK) if (g_iDebugBreakOnOpcode && g_iDebugBreakOnOpcode == iOpcode) g_bDebugBreakpointHit |= BP_HIT_OPCODE; } void DebugContinueStepping () { 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) { SingleStep(g_bGoCmd_ReinitFlag); g_bGoCmd_ReinitFlag = false; g_bDebugBreakpointHit |= CheckBreakpointsIO() | CheckBreakpointsReg(); } if (regs.pc == g_nDebugStepUntil || g_bDebugBreakpointHit) { TCHAR sText[ CONSOLE_WIDTH ]; char szStopMessage[CONSOLE_WIDTH]; char* pszStopReason = szStopMessage; if (regs.pc == g_nDebugStepUntil) pszStopReason = TEXT("PC matches 'Go until' address"); else if (g_bDebugBreakpointHit & BP_HIT_INVALID) pszStopReason = TEXT("Invalid opcode"); else if (g_bDebugBreakpointHit & BP_HIT_OPCODE) pszStopReason = TEXT("Opcode match"); else if (g_bDebugBreakpointHit & BP_HIT_REG) pszStopReason = TEXT("Register matches value"); else if (g_bDebugBreakpointHit & BP_HIT_MEM) sprintf_s(szStopMessage, sizeof(szStopMessage), "Memory accessed at $%04X", g_uBreakMemoryAddress); else if (g_bDebugBreakpointHit & BP_HIT_PC_READ_FLOATING_BUS_OR_IO_MEM) pszStopReason = TEXT("PC reads from floating bus or I/O memory"); else pszStopReason = TEXT("Unknown!"); ConsoleBufferPushFormat( sText, TEXT("Stop reason: %s"), pszStopReason ); 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; FrameRefreshStatus(DRAW_TITLE); // BUG: PageUp, Trace - doesn't center cursor g_nDisasmCurAddress = regs.pc; DisasmCalcTopBotAddress(); Update_t bUpdate = UPDATE_ALL; UpdateDisplay( bUpdate ); } } //=========================================================================== 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 ClearTempBreakpoints(); } //=========================================================================== void DebugDestroy () { DebugEnd(); for (int iFont = 0; iFont < NUM_FONTS; iFont++ ) { DeleteObject( g_aFontConfig[ iFont ]._hFont ); g_aFontConfig[ iFont ]._hFont = NULL; } // 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() DeleteObject( g_hConsoleBrushFG ); DeleteObject( g_hConsoleBrushBG ); DeleteDC( g_hConsoleFontDC ); DeleteObject( g_hConsoleFontBitmap ); } //=========================================================================== 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.erase( g_vMemorySearchResults.begin(), g_vMemorySearchResults.end() ); g_nAppMode = MODE_RUNNING; ReleaseDebuggerMemDC(); } #if _DEBUG #define DEBUG_COLOR_RAMP 0 //=========================================================================== void _SetupColorRamp( const int iPrimary, int & iColor_ ) { TCHAR sRamp[ CONSOLE_WIDTH*2 ] = TEXT(""); #if DEBUG_COLOR_RAMP TCHAR sText[ CONSOLE_WIDTH ]; #endif bool bR = (iPrimary & 1) ? true : false; bool bG = (iPrimary & 2) ? true : false; bool bB = (iPrimary & 4) ? true : false; int dStep = 32; int nLevels = 256 / dStep; for (int iLevel = nLevels; iLevel > 0; iLevel-- ) { int nC = ((iLevel * dStep) - 1); int nR = bR ? nC : 0; int nG = bG ? nC : 0; int nB = bB ? nC : 0; DWORD nColor = RGB(nR,nG,nB); g_aColorPalette[ iColor_ ] = nColor; #if DEBUG_COLOR_RAMP wsprintf( sText, TEXT("RGB(%3d,%3d,%3d), "), nR, nG, nB ); _tcscat( sRamp, sText ); #endif iColor_++; } #if DEBUG_COLOR_RAMP wsprintf( sText, TEXT(" // %d%d%d\n"), bB, bG, bR ); _tcscat( sRamp, sText ); OutputDebugString( sRamp ); sRamp[0] = 0; #endif } #endif // _DEBUG // Full Screen uses the palette from g_pFramebufferinfo // BUT DebutInitialize() is called before VideoInitialize() // THUS this is called post-initialize to set up the global palette // // pPalDst is the first color in the palette that we can stick our custom debug colors in //=========================================================================== void Debug_UpdatePalette( BYTE *pPalDst ) { _ConfigColorsReset( pPalDst ); } //=========================================================================== void _ConfigColorsReset( BYTE *pPalDst ) { // int iColor = 1; // black only has one level, skip it, since black levels same as white levels // for (int iPrimary = 1; iPrimary < 8; iPrimary++ ) // { // _SetupColorRamp( iPrimary, iColor ); // } BYTE *pDst = pPalDst; // Setup default colors int iColor; for (iColor = 0; iColor < NUM_DEBUG_COLORS; iColor++ ) { COLORREF nColor = g_aColorPalette[ g_aColorIndex[ iColor ] ]; int R = (nColor >> 0) & 0xFF; int G = (nColor >> 8) & 0xFF; int B = (nColor >> 16) & 0xFF; if( pDst ) { *(pDst + 0) = B; *(pDst + 1) = G; *(pDst + 2) = R; *(pDst + 3) = 0; pDst += 4; } // There are many, many ways of shifting the color domain to the monochrome domain // NTSC uses 3x3 matrix, could map RGB -> wavelength, etc. int M = (R + G + B) / 3; // Monochrome component int nThreshold = 64; int BW; if (M < nThreshold) BW = 0; else BW = 255; COLORREF nMono = RGB(M,M,M); COLORREF nBW = RGB(BW,BW,BW); DebuggerSetColor( SCHEME_COLOR, iColor, nColor ); DebuggerSetColor( SCHEME_MONO , iColor, nMono ); DebuggerSetColor( SCHEME_BW , iColor, nBW ); } } //=========================================================================== 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 g_hConsoleFontDC = CreateCompatibleDC( FrameGetDC() ); #if _DEBUG nError = GetLastError(); #endif #if APPLE_FONT_NEW // Pre-scaled bitmap g_hConsoleFontBitmap = LoadBitmap(g_hInstance,TEXT("IDB_DEBUG_FONT_7x8")); SelectObject( g_hConsoleFontDC, g_hConsoleFontBitmap ); #else // Scale at run-time // Black = Transparent // White = Opaque HBITMAP hTmpBitamp = LoadBitmap(g_hInstance,TEXT("CHARSET40")); #if _DEBUG nError = GetLastError(); #endif SelectObject( hTmpDC ,hTmpBitamp); #if _DEBUG nError = GetLastError(); #endif g_hConsoleFontBrush = GetStockBrush( WHITE_BRUSH ); SelectObject(g_hConsoleFontDC, g_hConsoleFontBrush ); // SelectObject(hTmpDC, g_hDebugFontBrush ); #if _DEBUG nError = GetLastError(); #endif g_hConsoleFontBitmap = CreateCompatibleBitmap( hTmpDC, APPLE_FONT_X_REGIONSIZE/2, APPLE_FONT_Y_REGIONSIZE/2 ); #if _DEBUG nError = GetLastError(); #endif SelectObject( g_hConsoleFontDC, g_hConsoleFontBitmap ); StretchBlt( g_hConsoleFontDC, // HDC hdcDest, // handle to destination DC 0, 0, // int nXOriginDest, int nYOriginDest, // y-coord of destination upper-left corner APPLE_FONT_X_REGIONSIZE/2, APPLE_FONT_Y_REGIONSIZE/2, // int nWidthDest, int nHeightDest, hTmpDC, // HDC hdcSrc, // handle to source DC 0, APPLE_FONT_Y_APPLE_80COL, // int nXOriginSrc, int nYOriginSrc, APPLE_FONT_X_REGIONSIZE, APPLE_FONT_Y_REGIONSIZE, // int nWidthSrc, int nHeightSrc, SRCCOPY // DWORD dwRop // raster operation code ); DeleteObject( hTmpBitamp ); DeleteObject( hTmpDC ); #endif ZeroMemory( g_aConsoleDisplay, 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 ZeroMemory( g_aBreakpoints , MAX_BREAKPOINTS * sizeof(Breakpoint_t)); ZeroMemory( g_aWatches , MAX_WATCHES * sizeof(Watches_t) ); ZeroMemory( g_aZeroPagePointers, MAX_ZEROPAGE_POINTERS * sizeof(ZeroPagePointers_t)); // Load Main, Applesoft, and User Symbols extern bool g_bSymbolsDisplayMissingFile; 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 for (int iFont = 0; iFont < NUM_FONTS; iFont++ ) { g_aFontConfig[ iFont ]._hFont = NULL; #if USE_APPLE_FONT g_aFontConfig[ iFont ]._nFontHeight = CONSOLE_FONT_HEIGHT; g_aFontConfig[ iFont ]._nFontWidthAvg = CONSOLE_FONT_WIDTH; g_aFontConfig[ iFont ]._nFontWidthMax = CONSOLE_FONT_WIDTH; g_aFontConfig[ iFont ]._nLineHeight = CONSOLE_FONT_HEIGHT; #endif } #if OLD_FONT _CmdConfigFont( FONT_INFO , g_sFontNameInfo , FIXED_PITCH | FF_MODERN , g_nFontHeight ); // DEFAULT_CHARSET _CmdConfigFont( FONT_CONSOLE , g_sFontNameConsole, FIXED_PITCH | FF_MODERN , g_nFontHeight ); // DEFAULT_CHARSET _CmdConfigFont( FONT_DISASM_DEFAULT, g_sFontNameDisasm , FIXED_PITCH | FF_MODERN , g_nFontHeight ); // OEM_CHARSET _CmdConfigFont( FONT_DISASM_BRANCH , g_sFontNameBranch , DEFAULT_PITCH | FF_DECORATIVE, g_nFontHeight+3); // DEFAULT_CHARSET #endif _UpdateWindowFontHeights( g_aFontConfig[ FONT_DISASM_DEFAULT ]._nFontHeight ); 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() TCHAR sText[ CONSOLE_WIDTH ]; VerifyDebuggerCommandTable(); // Check all summary help to see if it fits within the console for (int iCmd = 0; iCmd < NUM_COMMANDS; iCmd++ ) { char *pHelp = g_aCommands[ iCmd ].pHelpSummary; if (pHelp) { int nLen = _tcslen( pHelp ) + 2; if (nLen > (CONSOLE_WIDTH-1)) { ConsoleBufferPushFormat( sText, TEXT("Warning: %s help is %d chars"), pHelp, nLen ); } } } #if _DEBUG //g_bConsoleBufferPaused = true; #endif _Bookmark_Reset(); CmdMOTD(0); } // 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!? { // Support Clipboard (paste) if (!IsClipboardFormatAvailable(CF_TEXT)) return; if (!OpenClipboard( g_hFrameWindow )) return; HGLOBAL hClipboard; LPTSTR pData; hClipboard = GetClipboardData(CF_TEXT); if (hClipboard != NULL) { pData = (char*) GlobalLock(hClipboard); if (pData != NULL) { LPTSTR pSrc = pData; char c; while (true) { c = *pSrc++; if (! c) break; if (c == CHAR_CR) { #if WIN32 // Eat char #endif #if MACOSX #pragma error( "TODO: Mac port - handle CR/LF") #endif } else if (c == CHAR_LF) { #if WIN32 DebuggerProcessCommand( true ); #endif #if MACOSX #pragma error( "TODO: Mac port - handle CR/LF") #endif } else { // If we didn't want verbatim, we could do: // DebuggerInputConsoleChar( c ); if ((c >= CHAR_SPACE) && (c <= 126)) // HACK MAGIC # 32 -> ' ', # 126 ConsoleInputChar( c ); } } GlobalUnlock(hClipboard); } } CloseClipboard(); UpdateDisplay( UPDATE_CONSOLE_DISPLAY ); } } // Triggered when ENTER is pressed, or via script //=========================================================================== Update_t DebuggerProcessCommand ( const bool bEchoConsoleInput ) { Update_t bUpdateDisplay = UPDATE_NOTHING; char sText[ CONSOLE_WIDTH ]; 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) { sprintf( sText, " Asm: %d sym declared, not defined", nDelayedTargets ); ConsoleDisplayPush( sText ); 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) { sprintf( sText, "Syntax error: %s", g_aArgs[0].sArg ); bUpdateDisplay |= ConsoleDisplayError( sText ); } 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)) 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( g_nDisasmCurAddress ); 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(); 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(10); // 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 ]; //} //=========================================================================== void DebuggerMouseClick( int x, int y ) { if (g_nAppMode != MODE_DEBUG) return; // NOTE: KeybUpdateCtrlShiftStatus() should be called before int iAltCtrlShift = 0; iAltCtrlShift |= (g_bAltKey & 1) << 0; iAltCtrlShift |= (g_bCtrlKey & 1) << 1; iAltCtrlShift |= (g_bShiftKey & 1) << 2; // GH#462 disasm click # if (iAltCtrlShift != g_bConfigDisasmClick) return; int nFontWidth = g_aFontConfig[ FONT_DISASM_DEFAULT ]._nFontWidthAvg * GetViewportScale(); int nFontHeight = g_aFontConfig[ FONT_DISASM_DEFAULT ]._nLineHeight * GetViewportScale(); // do picking const int nOffsetX = IsFullScreen() ? GetFullScreenOffsetX() : Get3DBorderWidth(); const int nOffsetY = IsFullScreen() ? GetFullScreenOffsetY() : Get3DBorderHeight(); const int nOffsetInScreenX = x - nOffsetX; const int nOffsetInScreenY = y - nOffsetY; if (nOffsetInScreenX < 0 || nOffsetInScreenY < 0) return; int cx = nOffsetInScreenX / nFontWidth; int cy = nOffsetInScreenY / nFontHeight; #if _DEBUG char sText[ CONSOLE_WIDTH ]; sprintf( sText, "x:%d y:%d cx:%d cy:%d", x, y, cx, cy ); ConsoleDisplayPush( sText ); DebugDisplay(); #endif if (g_iWindowThis == WINDOW_CODE) { // Display_AssemblyLine -- need Tabs if( g_bConfigDisasmAddressView ) { // HACK: hard-coded from DrawDisassemblyLine::aTabs[] !!! if( cx < 4) // #### { g_bConfigDisasmAddressView ^= true; DebugDisplay(); } else if (cx == 4) // : { g_bConfigDisasmAddressColon ^= true; DebugDisplay(); } else // AD 00 00 if ((cx > 4) && (cx <= 13)) { g_bConfigDisasmOpcodesView ^= true; DebugDisplay(); } } else { if( cx == 0 ) // : { // Three-way state // "addr:" // ":" // " " g_bConfigDisasmAddressColon ^= true; if( g_bConfigDisasmAddressColon ) { g_bConfigDisasmAddressView ^= true; } DebugDisplay(); } else if ((cx > 0) & (cx <= 13)) { g_bConfigDisasmOpcodesView ^= true; DebugDisplay(); } } // Click on PC inside reg window? if ((cx >= 51) && (cx <= 60)) { if (cy == 3) { CmdCursorJumpPC( CURSOR_ALIGN_CENTER ); DebugDisplay(); } else if (cy == 4 || cy == 5) { int iFlag = -1; int nFlag = _6502_NUM_FLAGS; while( nFlag --> 0 ) { // TODO: magic number instead of DrawFlags() DISPLAY_FLAG_COLUMN, rect.left += ((2 + _6502_NUM_FLAGS) * nSpacerWidth); // BP_SRC_FLAG_C is 6, cx 60 --> 0 // ... // BP_SRC_FLAG_N is 13, cx 53 --> 7 if (cx == (53 + nFlag)) { iFlag = 7 - nFlag; break; } } if (iFlag >= 0) { regs.ps ^= (1 << iFlag); DebugDisplay(); } } else // Click on stack if( cy > 3) { } } } } //=========================================================================== bool IsDebugSteppingAtFullSpeed(void) { return (g_nAppMode == MODE_STEPPING) && g_bDebugFullSpeed; }