import * as fs from 'fs' ; import { UnifiedPath } from './misc/unifiedpath' ; import * as vscode from 'vscode' ; import { Breakpoint , DebugSession , InitializedEvent , Scope , Source , StackFrame , StoppedEvent , TerminatedEvent , Thread , ContinuedEvent , CapabilitiesEvent , InvalidatedEvent } from 'vscode-debugadapter/lib/main' ; import { DebugProtocol } from 'vscode-debugprotocol/lib/debugProtocol' ; import { Labels } from './labels/labels' ; import { Log } from './log' ; import { Remote , RemoteBreakpoint } from './remotes/remotebase' ; import { MemoryDumpView } from './views/memorydumpview' ; import { MemoryRegisterView } from './views/memoryregisterview' ; import { Settings , SettingsParameters } from './settings' ; import { DisassemblyVar , ShallowVar , MemorySlotsVar , RegistersMainVar , RegistersSecondaryVar , StackVar , StructVar , MemDumpVar , ImmediateMemoryValue } from './variables/shallowvar' ; import { Utility } from './misc/utility' ; import { Z80RegisterHoverFormat , Z80RegistersClass , Z80Registers , } from './remotes/z80registers' ; import { RemoteFactory } from './remotes/remotefactory' ; import { ZxNextSpritesView } from './views/zxnextspritesview' ; import { TextView } from './views/textview' ; import { BaseView } from './views/baseview' ; import { ZxNextSpritePatternsView } from './views/zxnextspritepatternsview' ; import { MemAttribute } from './disassembler/memory' ; import { Decoration } from './decoration' ; import { ZSimulationView } from './remotes/zsimulator/zsimulationview' ; import { ZSimRemote } from './remotes/zsimulator/zsimremote' ; import { CpuHistoryClass , CpuHistory , StepHistory } from './remotes/cpuhistory' ; import { StepHistoryClass } from './remotes/stephistory' ; import { DisassemblyClass , Disassembly } from './misc/disassembly' ; import { TimeWait } from './misc/timewait' ; import { MemoryArray } from './misc/memoryarray' ; import { MemoryDumpViewWord } from './views/memorydumpviewword' ; import { ExpressionVariable } from './misc/expressionvariable' ; import { RefList } from './misc/reflist' ; import { PromiseCallbacks } from './misc/promisecallbacks' ; import { Z80UnitTestRunner } from './z80unittests/z80unittestrunner' ; import { DiagnosticsHandler } from './diagnosticshandler' ; enum DbgAdapterState { NORMAL , UNITTEST } /**
 * The Emulator Debug Adapter.
 * It receives the requests from vscode and sends events to it.
 */ export class DebugSessionClass extends DebugSession { protected static state = DbgAdapterState . NORMAL ; protected static unitTestsStartCallbacks : PromiseCallbacks < DebugSessionClass > | undefined ; protected dasmAddressQueue = new Array < number > ( ) ; protected disasmTextDoc : vscode . TextDocument ; protected listVariables = new RefList < ShallowVar > ( ) ; protected constExpressionsList = new Map < string , ExpressionVariable > ( ) ; protected disassemblyVar : DisassemblyVar ; protected localStackVar : StackVar ; public static THREAD_ID = 1 ; protected stackTraceResponses = new Array < DebugProtocol . StackTraceResponse > ( ) ; protected delayedDecorations = new Array < ( ) => void > ( ) ; protected pauseRequested = false ; protected processingSteppingRequest = false ; protected debugConsoleSavedText : string ; protected debugConsoleIndentation = "  " ; /**
     * Creates a new debug adapter that is used for one debug session.
     * We configure the default implementation of a debug adapter here.
     */ public constructor ( ) { super ( ) ; this . setDebuggerLinesStartAt1 ( false ) ; this . setDebuggerColumnsStartAt1 ( false ) ; } param configName param handler returns public static unitTestsStart ( configName ) { if ( vscode . debug . activeDebugSession ) throw Error ( "There is already an active debug session." ) ; if ( this . state != DbgAdapterState . NORMAL ) throw Error ( "Debugger state is wrong." ) ; const rootFolder = Utility . getRootPath ( ) ; const rootFolderUri = vscode . Uri . file ( rootFolder ) ; const workspaceFolder = vscode . workspace . getWorkspaceFolder ( rootFolderUri ) ; Utility . assert ( workspaceFolder ) ; this . state = DbgAdapterState . UNITTEST ; vscode . debug . startDebugging ( workspaceFolder , configName ) ; return new Promise < DebugSessionClass > ( ( resolve , reject ) => { new PromiseCallbacks < DebugSessionClass > ( this , 'unitTestsStartCallbacks' , resolve , reject ) ; } ) ; } param message private showWarning ( message ) { Log . log ( message ) ; vscode . window . showWarningMessage ( message ) ; } param message private showError ( message ) { Log . log ( message ) ; vscode . window . showErrorMessage ( message ) ; } param message protected terminate ( message ? ) { this . processDelayedDecorations ( ) ; if ( message ) this . showError ( message ) ; Log . log ( "Exit debugger!" ) ; this . removeAllListeners ( ) ; this . sendEvent ( new TerminatedEvent ( ) ) ; } /**
     * Overload sendEvent to logger.
     */ public sendEvent ( event ) { Log . log ( `<-: ${ event . event }(${ JSON . stringify ( event . body ) })` ) ; super . sendEvent ( event ) ; } /**
     * Overload sendRequest to logger.
     */ public sendRequest ( command , args , timeout , cb ) { Log . log ( `<-: ${ command }(${ JSON . stringify ( args ) })` ) ; super . sendRequest ( command , args , timeout , ( resp ) => { Log . log ( `->: ${ resp . command }(${ JSON . stringify ( resp . body ) })` ) ; cb ( resp ) ; } ) ; } /**
     * Overload sendResponse to logger.
     */ public sendResponse ( response ) { Log . log ( `<-: ${ response . command }(${ JSON . stringify ( response . body ) })` ) ; super . sendResponse ( response ) ; } param request protected dispatchRequest ( request ) { Log . log ( `->: ${ request . command }(${ JSON . stringify ( request . arguments ) })` ) ; super . dispatchRequest ( request ) ; } /**
     * DebugAdapter disconnects.
     * End forcefully.
     * Is called
     * - when user presses red square
     * - when the ZEsarUX socket connection is terminated
     * Not called:
     * - If user presses circled arrow/restart.
     */ protected async disconnectRequest ( response , args ) { await this . disconnectAll ( ) ; this . sendResponse ( response ) ; } /**
     * Disconnects Remote, listeners, views.
     * Is called
     * - when user presses red square
     * - when the user presses relaunch (circled arrow/restart)
     * - when the ZEsarUX socket connection is terminated
     */ protected async disconnectAll ( ) { BaseView . staticCloseAll ( ) ; this . removeListener ( 'update' , BaseView . staticCallUpdateFunctions ) ; this . removeAllListeners ( ) ; await Remote ?. disconnect ( ) ; CpuHistoryClass . removeCpuHistory ( ) ; RemoteFactory . removeRemote ( ) ; if ( this . disasmTextDoc ) { vscode . window . showTextDocument ( this . disasmTextDoc . uri , { preview : true , preserveFocus : false } ) . then ( ( ) => { return vscode . commands . executeCommand ( 'workbench.action.closeActiveEditor' ) ; } ) ; } if ( DebugSessionClass . state == DbgAdapterState . UNITTEST ) { await Z80UnitTestRunner . cancelUnitTests ( ) ; Decoration ?. clearAllButCodeCoverageDecorations ( ) ; } else { Decoration ?. clearAllDecorations ( ) ; } DebugSessionClass . state = DbgAdapterState . NORMAL ; } /**
     * 'initialize' request.
     * Respond with supported features.
     */ protected async initializeRequest ( response , args ) { response . body = response . body || { } ; response . body . supportsConfigurationDoneRequest = false ; response . body . supportTerminateDebuggee = true ; response . body . supportsGotoTargetsRequest = false ; response . body . supportsEvaluateForHovers = true ; response . body . supportsSetVariable = true ; response . body . supportsConditionalBreakpoints = true ; response . body . supportsRestartRequest = false ; response . body . supportsSetExpression = true ; this . sendResponse ( response ) ; } /**
     * Prints text to the debug console.
     */ protected debugConsoleAppend ( text ) { if ( vscode . debug . activeDebugSession ) vscode . debug . activeDebugConsole . append ( text ) ; else { this . debugConsoleSavedText += text ; } } protected debugConsoleAppendLine ( text ) { this . debugConsoleAppend ( text + '\n' ) ; } param response param args protected scopes : Array < Scope > ; protected async launchRequest ( response , args ) { try { DiagnosticsHandler . clear ( ) ; BaseView . staticInit ( ) ; ZxNextSpritePatternsView . staticInit ( ) ; BaseView . onChange ( ( ) => { this . sendEvent ( new InvalidatedEvent ( [ 'variables' ] ) ) ; } ) ; Settings . launch = Settings . Init ( args ) ; Settings . CheckSettings ( ) ; Utility . setRootPath ( Settings . launch . rootFolder ) ; this . listVariables . clear ( ) ; this . constExpressionsList . clear ( ) ; this . disassemblyVar = new DisassemblyVar ( ) ; this . disassemblyVar . count = Settings . launch . disassemblerArgs . numberOfLines ; this . localStackVar = new StackVar ( ) ; this . scopes = [ new Scope ( "Registers" , this . listVariables . addObject ( new RegistersMainVar ( ) ) ) , new Scope ( "Registers 2" , this . listVariables . addObject ( new RegistersSecondaryVar ( ) ) ) , new Scope ( "Disassembly" , this . listVariables . addObject ( this . disassemblyVar ) ) , new Scope ( "Memory Banks" , this . listVariables . addObject ( new MemorySlotsVar ( ) ) ) , new Scope ( "Local Stack" , this . listVariables . addObject ( this . localStackVar ) ) ] ; } catch ( e ) { response . success = false ; response . message = e . message ; this . sendResponse ( response ) ; return ; } this . debugConsoleSavedText = '' ; vscode . debug . onDidChangeActiveDebugSession ( dbgSession => { if ( dbgSession ) { vscode . debug . activeDebugConsole . append ( this . debugConsoleSavedText ) ; this . debugConsoleSavedText = '' ; } } ) ; await this . launch ( response ) ; } param response protected async launch ( response ) { DisassemblyClass . createDisassemblyInstance ( ) ; this . processingSteppingRequest = false ; this . removeListener ( 'update' , BaseView . staticCallUpdateFunctions ) ; this . on ( 'update' , BaseView . staticCallUpdateFunctions ) ; const msg = await this . startEmulator ( ) ; if ( msg ) { response . message = msg ; response . success = ( msg == undefined ) ; } else { if ( Settings . launch . history . reverseDebugInstructionCount > 0 ) { this . sendEvent ( new CapabilitiesEvent ( { supportsStepBack : true } ) ) ; } } this . sendResponse ( response ) ; } returns protected async startEmulator ( ) { try { Labels . init ( Settings . launch . smallValuesMaximum ) ; } catch ( e ) { Remote . terminate ( 'Labels: ' + e . message ) ; return "Error while initializing labels." ; } Decoration . clearAllDecorations ( ) ; Z80RegistersClass . createRegisters ( ) ; CpuHistoryClass . setCpuHistory ( undefined ) ; RemoteFactory . createRemote ( Settings . launch . remoteType ) ; Remote . on ( 'warning' , message => { this . showWarning ( message ) ; } ) ; Remote . on ( 'debug_console' , message => { this . debugConsoleIndentedText ( message ) ; } ) ; Remote . once ( 'error' , err => { Remote . disconnect ( ) ; this . terminate ( err . message ) ; } ) ; Remote . once ( 'terminated' , message => { this . terminate ( message ) ; } ) ; if ( ! ( CpuHistory as any ) ) { CpuHistoryClass . setCpuHistory ( new StepHistoryClass ( ) ) ; } try { Remote . readListFiles ( Settings . launch ) ; } catch ( err ) { DebugSessionClass . unitTestsStartCallbacks ?. reject ( err ) ; return err . message ; } Remote . on ( 'coverage' , coveredAddresses => { this . delayedDecorations . push ( ( ) => { Decoration . showCodeCoverage ( coveredAddresses ) ; } ) ; } ) ; StepHistory . on ( 'revDbgHistory' , addresses => { this . delayedDecorations . push ( ( ) => { Decoration . showRevDbgHistory ( addresses ) ; } ) ; } ) ; StepHistory . on ( 'historySpot' , ( startIndex , addresses , registers ) => { this . delayedDecorations . push ( ( ) => { Decoration . showHistorySpot ( startIndex , addresses , registers ) ; } ) ; } ) ; return new Promise < undefined > ( async ( resolve , reject ) => { Remote . once ( 'initialized' , async ( text ) => { if ( text ) { this . debugConsoleAppendLine ( text ) ; } await Remote . getRegistersFromEmulator ( ) ; await Remote . getCallStackFromEmulator ( ) ; if ( ! StepHistory . decoder ) StepHistory . decoder = Z80Registers . decoder ; StepHistory . init ( ) ; for ( const cmd of Settings . launch . commandsAfterLaunch ) { this . debugConsoleAppendLine ( cmd ) ; try { const outText = await this . evaluateCommand ( cmd ) ; this . debugConsoleAppendLine ( outText ) ; } catch ( err ) { const output = "Error while executing '" + cmd + "' in 'commandsAfterLaunch': " + err . message ; this . showWarning ( output ) ; } } if ( Remote instanceof ZSimRemote ) { const zsim = Remote ; if ( DebugSessionClass . state == DbgAdapterState . NORMAL ) { zsim . customCode ?. execute ( ) ; } new ZSimulationView ( zsim ) ; } this . sendEvent ( new InitializedEvent ( ) ) ; resolve ( undefined ) ; StepHistory . clear ( ) ; if ( DebugSessionClass . unitTestsStartCallbacks ) { DebugSessionClass . unitTestsStartCallbacks . resolve ( this ) ; } else { if ( Settings . launch . startAutomatically ) { setTimeout ( ( ) => { this . handleRequest ( undefined , async ( ) => { return this . remoteContinue ( ) ; } ) ; } , 500 ) ; } else { this . sendEvent ( new StoppedEvent ( 'stop on start' , DebugSessionClass . THREAD_ID ) ) ; } } } ) ; try { await Remote . init ( ) ; } catch ( e ) { const error = e . message || "Error" ; Remote . terminate ( 'Init remote: ' + error ) ; reject ( e ) ; DebugSessionClass . unitTestsStartCallbacks ?. reject ( e ) ; } } ) ; } param response param args protected async setBreakPointsRequest ( response , args ) { const path = UnifiedPath . getUnifiedPath ( < string > args . source . path ) ; const givenBps = args . breakpoints || [ ] ; const bps = new Array < RemoteBreakpoint > ( ) ; for ( const bp of givenBps ) { try { const log = Remote . evalLogMessage ( bp . logMessage ) ; const mbp = { bpId : 0 , filePath : path , lineNr : this . convertClientLineToDebugger ( bp . line ) , address : - 1 , condition : ( bp . condition ) ? bp . condition : '' , log : log } ; bps . push ( mbp ) ; } catch ( e ) { this . showWarning ( e . message ) ; } } const currentBreakpoints = await Remote . setBreakpoints ( path , bps ) ; const source = this . createSource ( path ) ; const vscodeBreakpoints = givenBps . map ( gbp => { let foundCbp ; const lineNr = gbp . line ; for ( const cbp of currentBreakpoints ) { const cLineNr = this . convertDebuggerLineToClient ( cbp . lineNr ) ; if ( cLineNr == lineNr ) { foundCbp = cbp ; break ; } } const verified = ( foundCbp != undefined ) && ( foundCbp . address >= 0 ) ; const bp = new Breakpoint ( verified , lineNr , 0 , source ) ; if ( foundCbp && foundCbp . address >= 0 ) { const addrString = Utility . getLongAddressString ( foundCbp . address ) ; let txt = addrString ; const labels = Labels . getLabelsForNumber64k ( foundCbp . address ) ; labels . forEach ( lbl => txt += '\n' + lbl ) ; ( bp as any ) . message = txt ; } if ( ! verified ) { const text = JSON . stringify ( bp ) ; this . debugConsoleAppendLine ( 'Unverified breakpoint: ' + text ) ; if ( foundCbp && foundCbp . error ) { this . debugConsoleAppendLine ( '  Additional info: ' + foundCbp . error ) ; } } return bp ; } ) ; response . body = { breakpoints : vscodeBreakpoints } ; this . sendResponse ( response ) ; } /**
     * Returns the one and only "thread".
     */ protected async threadsRequest ( response ) { response . body = { threads : [ new Thread ( DebugSessionClass . THREAD_ID , "thread_default" ) ] } ; this . sendResponse ( response ) ; } param filePath returns private createSource ( filePath ) { if ( filePath . length == 0 ) return undefined ; const uFilePath = UnifiedPath . getUnifiedPath ( filePath ) ; const fname = UnifiedPath . basename ( uFilePath ) ; const debPath = this . convertDebuggerPathToClient ( uFilePath ) ; const uDebPath = UnifiedPath . getUnifiedPath ( debPath ) ; return new Source ( fname , uDebPath , undefined , undefined , undefined ) ; } /**
     * Returns the stack frames.
     */ protected async stackTraceRequest ( response , args ) { this . stackTraceResponses . push ( response ) ; if ( this . stackTraceResponses . length > 1 ) return ; const sfrs = new Array < StackFrame > ( ) ; let doDisassembly = false ; const fetchAddresses = new Array < number > ( ) ; let frameCount = 0 ; let callStack ; if ( StepHistory . isInStepBackMode ( ) ) { callStack = StepHistory . getCallStack ( ) ; } else { callStack = await Remote . getCallStackCache ( ) ; } frameCount = callStack . length ; for ( let index = frameCount - 1 ; index >= 0 ; index -- ) { const frame = callStack [ index ] ; const addr = frame . addr ; const file = Labels . getFileAndLineForAddress ( addr ) ; const src = this . createSource ( file . fileName ) ; const lineNr = ( src ) ? this . convertDebuggerLineToClient ( file . lineNr ) : 0 ; const sf = new StackFrame ( index + 1 , frame . name , src , lineNr ) ; sfrs . push ( sf ) ; if ( ! sf . source ) { const csFrame = callStack [ index ] ; fetchAddresses . push ( csFrame . addr ) ; } } const fetchSize = 100 ; const memArray = new MemoryArray ( ) ; for ( const fetchAddress of fetchAddresses ) { memArray . addRange ( fetchAddress , fetchSize ) ; } const fetchHistorySize = 20 ; const historyAddresses = new Array < number > ( ) ; for ( let i = 1 ; i <= 10 ; i ++ ) { const addr = StepHistory . getPreviousAddress ( i ) ; if ( addr == undefined ) break ; memArray . addRange ( addr , fetchHistorySize ) ; historyAddresses . unshift ( addr ) ; } for ( const range of memArray . ranges ) { const data = await Remote . readMemoryDump ( range . address , range . size ) ; range . data = data ; } const fetchAddressesCount = fetchAddresses . length ; if ( ! doDisassembly ) { const checkSize = 40 ; if ( fetchAddressesCount > 0 ) { for ( let index = 0 ; index < fetchAddressesCount ; index ++ ) { const fetchAddress = fetchAddresses [ index ] ; for ( let k = 0 ; k < checkSize ; k ++ ) { const val = Disassembly . memory . getValueAt ( ( fetchAddress + k ) & 0xFFFF ) ; const memAttr = Disassembly . memory . getAttributeAt ( fetchAddress + k ) ; const newVal = memArray . getValueAtAddress ( ( fetchAddress + k ) & 0xFFFF ) ; if ( ( val != newVal ) || ( memAttr == MemAttribute . UNUSED ) ) { doDisassembly = true ; break ; } } } } } for ( let i = 0 ; i < fetchAddressesCount ; i ++ ) { const addr = fetchAddresses [ i ] ; if ( this . dasmAddressQueue . indexOf ( addr ) < 0 ) this . dasmAddressQueue . unshift ( addr ) ; if ( ! doDisassembly ) { const memAttr = Disassembly . memory . getAttributeAt ( addr & 0xFFFF ) ; if ( ! ( memAttr & MemAttribute . CODE_FIRST ) ) doDisassembly = true ; } } if ( doDisassembly ) { const absFilePath = DisassemblyClass . getAbsFilePath ( ) ; const uri = vscode . Uri . file ( absFilePath ) ; const editCreate = new vscode . WorkspaceEdit ( ) ; editCreate . createFile ( uri , { overwrite : true } ) ; await vscode . workspace . applyEdit ( editCreate ) ; const textDoc = await vscode . workspace . openTextDocument ( absFilePath ) ; this . disasmTextDoc = textDoc ; Disassembly . initWithCodeAdresses ( [ ... historyAddresses , ... fetchAddresses ] , memArray . ranges ) ; Disassembly . disassemble ( ) ; const text = Disassembly . getDisassemblyText ( ) ; const bps = vscode . debug . breakpoints ; const disSrc = this . disasmTextDoc . uri . toString ( ) ; const sbps = bps . filter ( bp => { if ( bp . hasOwnProperty ( 'location' ) ) { const sbp = bp as vscode . SourceBreakpoint ; const sbpSrc = sbp . location . uri . toString ( ) ; if ( sbpSrc == disSrc ) return true ; } return false ; } ) as vscode . SourceBreakpoint [ ] ; const changedBps = new Array < vscode . SourceBreakpoint > ( ) ; if ( sbps . length > 0 ) { const prevTextLines = this . disasmTextDoc . getText ( ) . split ( '\n' ) ; for ( const sbp of sbps ) { const lineNr = sbp . location . range . start . line ; const line = prevTextLines [ lineNr ] ; const addr = parseInt ( line , 16 ) ; if ( ! isNaN ( addr ) ) { const nLineNr = Disassembly . getLineForAddress ( addr ) || - 1 ; const nLoc = new vscode . Location ( this . disasmTextDoc . uri , new vscode . Position ( nLineNr , 0 ) ) ; const cbp = new vscode . SourceBreakpoint ( nLoc , sbp . enabled , sbp . condition , sbp . hitCondition , sbp . logMessage ) ; changedBps . push ( cbp ) ; } } } vscode . debug . removeBreakpoints ( sbps ) ; const editReplace = new vscode . WorkspaceEdit ( ) ; editReplace . replace ( this . disasmTextDoc . uri , new vscode . Range ( 0 , 0 , this . disasmTextDoc . lineCount , 0 ) , text ) ; await vscode . workspace . applyEdit ( editReplace ) ; await this . disasmTextDoc . save ( ) ; vscode . debug . addBreakpoints ( changedBps ) ; const editors = vscode . window . visibleTextEditors ; for ( const editor of editors ) { if ( editor . document == this . disasmTextDoc ) { Decoration . setDisasmCoverageDecoration ( editor ) ; } } } if ( frameCount > 0 ) { const absFilePath = DisassemblyClass . getAbsFilePath ( ) ; const src = this . createSource ( absFilePath ) as Source ; let indexDump = 0 ; for ( let i = 0 ; i < frameCount ; i ++ ) { const sf = sfrs [ i ] ; if ( sf . source ) continue ; const addr = fetchAddresses [ indexDump ] ; const foundLine = Disassembly . getLineForAddress ( addr ) || - 1 ; const lineNr = this . convertDebuggerLineToClient ( foundLine ) ; sf . source = src ; sf . line = lineNr ; indexDump ++ ; } } while ( this . stackTraceResponses . length > 0 ) { const resp = this . stackTraceResponses [ 0 ] ; this . stackTraceResponses . shift ( ) ; resp . body = { stackFrames : sfrs , totalFrames : 1 } ; this . sendResponse ( resp ) ; } this . processDelayedDecorations ( ) ; } /**
     * This is called at the end of a stack trace request btu also when a unit test case was finished debugging.
     * Writes everything in 'delayedDecorations' into the decorations.
     */ protected processDelayedDecorations ( ) { for ( const func of this . delayedDecorations ) func ( ) ; this . delayedDecorations . length = 0 ; } param response param args protected async scopesRequest ( response , args ) { const frameId = args . frameId ; let frame ; if ( StepHistory . isInStepBackMode ( ) ) frame = StepHistory . getCallStack ( ) . getObject ( frameId ) ; else { await Remote . getCallStackCache ( ) ; frame = Remote . getFrame ( frameId ) ; } if ( ! frame ) { response . body = { scopes : [ ] } ; this . sendResponse ( response ) ; return ; } this . disassemblyVar . address = frame . addr & 0xFFFF ; this . localStackVar . setFrameAddress ( frame . stack , frame . stackStartAddress ) ; response . body = { scopes : this . scopes } ; this . sendResponse ( response ) ; } param response param args protected async variablesRequest ( response , args ) { const ref = args . variablesReference ; const varObj = this . listVariables . getObject ( ref ) ; if ( varObj ) { const varList = await varObj . getContent ( args . start , args . count ) ; response . body = { variables : varList } ; } else { response . body = { variables : new Array < DebugProtocol . Variable > ( ) } ; } this . sendResponse ( response ) ; } param public decorateBreak ( breakReason ) { if ( ! breakReason ) return ; const pc = Remote . getPCLong ( ) ; Decoration . showBreak ( pc , breakReason ) ; } /**
     * This method is called before a step (stepOver, stepInto, stepOut,
     * continue, stepBack, etc.) is called.
     */ protected startProcessing ( ) { this . processingSteppingRequest = true ; this . pauseRequested = false ; Decoration . clearBreak ( ) ; Remote . startProcessing ( ) ; } /**
     * This method is called after a step (stepOver, stepInto, stepOut,
     * continue, stepBack, etc.) is called.
     */ protected stopProcessing ( ) { this . processingSteppingRequest = false ; Remote . stopProcessing ( ) ; } param response param args public async continueRequest ( response , args ) { this . handleRequest ( response , async ( ) => { let event ; if ( StepHistory . isInStepBackMode ( ) ) { await this . startStepInfo ( 'Continue' ) ; const breakReason = await StepHistory . continue ( ) ; if ( breakReason ) { this . debugConsoleIndentedText ( breakReason ) ; this . decorateBreak ( breakReason ) ; } event = new StoppedEvent ( 'step' , DebugSessionClass . THREAD_ID ) ; } else { event = await this . remoteContinue ( ) ; } return event ; } ) ; } /**
     * Calls 'continue' (run) on the remote (emulator).
     * Called at the beginning (startAutomatically) and from the
     * vscode UI (continueRequest).
     */ public async remoteContinue ( ) { await this . startStepInfo ( 'Continue' ) ; Decoration . clearBreak ( ) ; StepHistory . clear ( ) ; const breakReasonString = await Remote . continue ( ) ; if ( Remote == undefined ) return new StoppedEvent ( 'exception' , DebugSessionClass . THREAD_ID ) ; if ( breakReasonString ) { this . debugConsoleIndentedText ( breakReasonString ) ; this . decorateBreak ( breakReasonString ) ; } await this . endStepInfo ( ) ; if ( DebugSessionClass . state == DbgAdapterState . UNITTEST ) { const finished = await Z80UnitTestRunner . dbgCheckUnitTest ( breakReasonString ) ; if ( ! finished ) { this . sendEventBreakAndUpdate ( ) ; } return undefined as any ; } return new StoppedEvent ( 'break' , DebugSessionClass . THREAD_ID ) ; } /**
     * Is called by unit tests to simulate a 'break'.
     */ public async sendEventBreakAndUpdate ( ) { this . update ( ) ; this . sendEvent ( new StoppedEvent ( 'break' , DebugSessionClass . THREAD_ID ) ) ; } /**
     * Sends a continued event to update the UI.
     */ public sendEventContinued ( ) { this . sendEvent ( new ContinuedEvent ( DebugSessionClass . THREAD_ID ) ) ; } param response param args protected async pauseRequest ( response , args ) { try { this . pauseRequested = true ; if ( StepHistory . isInStepBackMode ( ) ) StepHistory . pause ( ) ; else await Remote . pause ( ) ; } catch ( e ) { this . showError ( e . message ) ; } this . sendResponse ( response ) ; } param response param args protected async reverseContinueRequest ( response , args ) { this . handleRequest ( response , async ( ) => { await this . startStepInfo ( 'Reverse-Continue' , true ) ; const breakReason = await StepHistory . reverseContinue ( ) ; if ( breakReason ) { this . debugConsoleIndentedText ( breakReason ) ; this . decorateBreak ( breakReason ) ; } return new StoppedEvent ( 'break' , DebugSessionClass . THREAD_ID ) ; } , 100 ) ; } /**
     * Is called by all request (step, stepInto, continue, etc.).
     * This handles the display in the vscode UI.
     * If the command can be handled in a short amount of time (e.g. in 1 sec)
     * then the response is sent after the command.
     * When the response is received by vscode it changes the current highlighted line
     * into an un-highlighted state and shows the 'pause' button.
     * I.e. for short commands this could lead to flickering, but if the
     * UI is changed after command no flickering appears.
     * On the other hand, if a command takes too long it is necessary to show
     * the 'pause' button. So a timer assures that the response is sent after a timeout.
     * The function takes care that the response is sent only once.
     */ protected handleRequest ( response , command , responseTime = 750 ) { if ( this . processingSteppingRequest ) { this . sendResponse ( response ) ; return ; } this . startProcessing ( ) ; let respTimer ; if ( response ) { respTimer = setTimeout ( ( ) => { respTimer = undefined ; this . sendResponse ( response ) ; } , responseTime ) ; } ( async ( ) => { const event = await command ( ) ; if ( ! Remote ) return ; this . stopProcessing ( ) ; this . update ( { step : true } ) ; StepHistory . emitHistory ( ) ; if ( respTimer ) { clearTimeout ( respTimer ) ; this . sendResponse ( response ) ; } if ( event ) this . sendEvent ( event ) ; } ) ( ) ; } param response param args protected async nextRequest ( response , args ) { this . handleRequest ( response , async ( ) => { await this . startStepInfo ( 'Step-Over' ) ; const stepBackMode = StepHistory . isInStepBackMode ( ) ; const prevPc = Remote . getPCLong ( ) ; const prevFileLoc = Labels . getFileAndLineForAddress ( prevPc ) ; let i = 0 ; let breakReason ; const timeWait = new TimeWait ( 500 , 200 , 100 ) ; while ( true ) { i ++ ; await timeWait . waitAtInterval ( ) ; const instr = await this . getCurrentInstruction ( i ) ; if ( instr ) this . debugConsoleIndentedText ( instr ) ; if ( stepBackMode ) { breakReason = await StepHistory . stepOver ( ) ; } else { breakReason = await Remote . stepOver ( ) ; } if ( this . pauseRequested ) { breakReason = "Manual break" ; } if ( breakReason ) { break ; } const pc = Remote . getPCLong ( ) ; const nextFileLoc = Labels . getFileAndLineForAddress ( pc ) ; if ( prevFileLoc . fileName == '' ) break ; if ( nextFileLoc . lineNr != prevFileLoc . lineNr ) break ; if ( nextFileLoc . fileName != prevFileLoc . fileName ) break ; } if ( breakReason ) { this . debugConsoleIndentedText ( breakReason ) ; this . decorateBreak ( breakReason ) ; } if ( ! stepBackMode ) { await this . endStepInfo ( ) ; } if ( DebugSessionClass . state == DbgAdapterState . UNITTEST ) { await Z80UnitTestRunner . dbgCheckUnitTest ( breakReason ) ; } return new StoppedEvent ( 'step' , DebugSessionClass . THREAD_ID ) ; } , 100 ) ; } param text param alwaysHistorical protected async startStepInfo ( text ? , alwaysHistorical = false ) { const stepBackMode = StepHistory . isInStepBackMode ( ) || alwaysHistorical ; if ( text ) { if ( stepBackMode ) { text = 'Time-travel ' + text ; if ( ! ( CpuHistory as any ) ) text += ' (Lite)' ; } this . debugConsoleAppendLine ( text ) ; } if ( ! stepBackMode ) { if ( ! ( CpuHistory as any ) ) { const regsCache = Z80Registers . getCache ( ) ; StepHistory . pushHistoryInfo ( regsCache ) ; const callStack = await Remote . getCallStackCache ( ) ; StepHistory . pushCallStack ( callStack ) ; } await Remote . resetTstates ( ) ; } } param disasm protected async endStepInfo ( ) { const tStates = await Remote . getTstates ( ) ; let tStatesText ; if ( tStates ) { tStatesText = 'T-States: ' + tStates ; const cpuFreq = await Remote . getCpuFrequency ( ) ; if ( cpuFreq ) { let time = tStates / cpuFreq ; let unit = 's' ; if ( time < 1e-3 ) { time *= 1e+6 ; unit = 'us' ; } else if ( time < 1 ) { time *= 1e+3 ; unit = 'ms' ; } let clockStr = ( cpuFreq * 1E-6 ) . toPrecision ( 2 ) ; if ( clockStr . endsWith ( '.0' ) ) clockStr = clockStr . substr ( 0 , clockStr . length - 2 ) ; tStatesText += ', time: ' + time . toPrecision ( 3 ) + unit + '@' + clockStr + 'MHz' ; } } if ( tStatesText ) this . debugConsoleIndentedText ( tStatesText ) ; } param count returns protected async getCurrentInstruction ( count = 0 ) { const maxInstructionCount = 10 ; const pc = Remote . getPC ( ) ; const pcStr = Utility . getHexString ( pc , 4 ) ; if ( count == maxInstructionCount ) return pcStr + ' ...' ; if ( count > maxInstructionCount ) return undefined ; let disInstr ; const stepBackMode = StepHistory . isInStepBackMode ( ) ; if ( stepBackMode ) { disInstr = StepHistory . getCurrentInstruction ( ) ; } else { const data = await Remote . readMemoryDump ( pc , 4 ) ; disInstr = DisassemblyClass . getInstruction ( pc , data ) ; } let result ; if ( disInstr ) result = pcStr + " " + disInstr ; return result ; } param response param args protected async stepInRequest ( response , args ) { this . handleRequest ( response , async ( ) => { await this . startStepInfo ( 'Step-Into' ) ; const instr = await this . getCurrentInstruction ( ) ; if ( instr ) this . debugConsoleIndentedText ( instr ) ; let breakReason ; const stepBackMode = StepHistory . isInStepBackMode ( ) ; if ( stepBackMode ) { breakReason = await StepHistory . stepInto ( ) ; } else { StepHistory . clear ( ) ; breakReason = await Remote . stepInto ( ) ; } if ( breakReason ) { this . debugConsoleIndentedText ( breakReason ) ; this . decorateBreak ( breakReason ) ; } if ( ! stepBackMode ) { await this . endStepInfo ( ) ; } if ( DebugSessionClass . state == DbgAdapterState . UNITTEST ) { await Z80UnitTestRunner . dbgCheckUnitTest ( breakReason ) ; } return new StoppedEvent ( 'step' , DebugSessionClass . THREAD_ID ) ; } ) ; } param response param args protected async stepOutRequest ( response , args ) { this . handleRequest ( response , async ( ) => { await this . startStepInfo ( 'Step-Out' ) ; let breakReasonString ; const stepBackMode = StepHistory . isInStepBackMode ( ) ; if ( stepBackMode ) { breakReasonString = await StepHistory . stepOut ( ) ; } else { StepHistory . clear ( ) ; breakReasonString = await Remote . stepOut ( ) ; } if ( breakReasonString ) { this . debugConsoleIndentedText ( breakReasonString ) ; this . decorateBreak ( breakReasonString ) ; } if ( ! stepBackMode ) { await this . endStepInfo ( ) ; } if ( DebugSessionClass . state == DbgAdapterState . UNITTEST ) { await Z80UnitTestRunner . dbgCheckUnitTest ( breakReasonString ) ; } return new StoppedEvent ( 'step' , DebugSessionClass . THREAD_ID ) ; } ) ; } param response param args protected async stepBackRequest ( response , args ) { this . handleRequest ( response , async ( ) => { await this . startStepInfo ( 'Step-Back' , true ) ; const breakReason = await StepHistory . stepBack ( ) ; if ( breakReason ) { this . debugConsoleIndentedText ( breakReason ) ; this . decorateBreak ( breakReason ) ; } else { const instr = await this . getCurrentInstruction ( ) ; if ( instr ) this . debugConsoleIndentedText ( instr ) ; } return new StoppedEvent ( 'step' , DebugSessionClass . THREAD_ID ) ; } ) ; } /**
     * Sets the value of an expression from the WATCH pane.
     * Does so only for the top level. I.e. if top level is an L-value.
     * E.g. a byte to change.
     * For structures or memory array is is already supported by the
     * StructVar and MemDumpVar.
     */ protected async setExpressionRequest ( response , args , request ? ) { response . success = false ; const item = this . constExpressionsList . get ( args . expression ) ; if ( item && item . immediateValue ) { const value = Utility . parseValue ( args . value ) ; const formattedString = await item . immediateValue . setValue ( value ) ; if ( formattedString ) { response . body = { value : formattedString } ; response . success = true ; if ( ShallowVar . memoryChanged ) { await this . memoryHasBeenChanged ( ) ; this . sendEvent ( new InvalidatedEvent ( [ 'variables' ] ) ) ; } ShallowVar . clearChanged ( ) ; } } this . sendResponse ( response ) ; } param command returns protected async evaluateCommand ( command ) { const expression = command . trim ( ) . replace ( /\s+/g , ' ' ) ; const tokens = expression . split ( ' ' ) ; const cmd = tokens . shift ( ) ; if ( ! cmd ) throw Error ( "No command." ) ; let viewTitle ; if ( tokens [ 0 ] == '-view' ) { tokens . shift ( ) ; viewTitle = cmd . substr ( 1 ) + ' ' + tokens . join ( ' ' ) ; } let output ; if ( cmd == '-help' || cmd == '-h' ) { output = await this . evalHelp ( tokens ) ; } else if ( cmd == '-ASSERTION' || cmd == '-assertion' ) { output = await this . evalASSERTION ( tokens ) ; } else if ( cmd == '-eval' ) { output = await this . evalEval ( tokens ) ; } else if ( cmd == '-exec' || cmd == '-e' ) { output = await this . evalExec ( tokens ) ; } else if ( cmd == '-label' || cmd == '-l' ) { output = await this . evalLabel ( tokens ) ; } else if ( cmd == '-LOGPOINT' || cmd == '-logpoint' ) { output = await this . evalLOGPOINT ( tokens ) ; } else if ( cmd == '-md' ) { output = await this . evalMemDump ( tokens ) ; } else if ( cmd == '-msetb' ) { output = await this . evalMemSetByte ( tokens ) ; } else if ( cmd == '-msetw' ) { output = await this . evalMemSetWord ( tokens ) ; } else if ( cmd == '-ms' ) { output = await this . evalMemSave ( tokens ) ; } else if ( cmd == '-mv' ) { output = await this . evalMemViewByte ( tokens ) ; } else if ( cmd == '-mvw' ) { output = await this . evalMemViewWord ( tokens ) ; } else if ( cmd == '-rmv' ) { output = await this . evalRegisterMemView ( tokens ) ; } else if ( cmd == '-dasm' ) { output = await this . evalDasm ( tokens ) ; } else if ( cmd == '-patterns' ) { output = await this . evalSpritePatterns ( tokens ) ; } else if ( cmd == '-WPMEM' || cmd == '-wpmem' ) { output = await this . evalWPMEM ( tokens ) ; } else if ( cmd == '-sprites' ) { output = await this . evalSprites ( tokens ) ; } else if ( cmd == '-state' ) { output = await this . evalStateSaveRestore ( tokens ) ; } else if ( cmd == '-dbg' ) { output = await this . evalDebug ( tokens ) ; } else { throw Error ( "Unknown command: '" + expression + "'" ) ; } if ( viewTitle ) { const panel = new TextView ( viewTitle , output ) ; await panel . update ( ) ; return 'OK' ; } else { return output ; } } /**
     * Is called when hovering or when an expression is added to the watches.
     * Or if commands are input in the debug console.
     * All have different formats:
     * - hovering: "word", e.g. "data_b60" or ".loop" or "HL"
     * - debug console: starts with "-", e.g. "-wpmem enable"
     * - watch: anything else.
     * args.context contains info that the request comes from the console, watch panel or hovering.
     * 'watch': evaluate is run in a watch.
     * 'repl': evaluate is run from REPL console.
     * 'hover': evaluate is run from a data hover.
     */ protected async evaluateRequest ( response , args ) { Log . log ( 'evaluate.expression: ' + args . expression ) ; Log . log ( 'evaluate.context: ' + args . context ) ; const expression = args . expression . trim ( ) ; const tokens = expression . split ( ' ' ) ; const cmd = tokens . shift ( ) ; if ( cmd == undefined ) { this . sendResponse ( response ) ; return ; } switch ( args . context ) { case 'repl' : let text ; try { text = await this . evaluateCommand ( expression ) ; response . body = { result : text + "\n\n" , type : undefined , presentationHint : undefined , variablesReference : 0 , namedVariables : undefined , indexedVariables : undefined } ; } catch ( err ) { text = "Error" ; if ( err . message ) text += ': ' + err . message ; } response . body = { result : text + "\n\n" , type : undefined , presentationHint : undefined , variablesReference : 0 , namedVariables : undefined , indexedVariables : undefined } ; break ; case 'hover' : let formattedValue = '' ; try { if ( Z80RegistersClass . isRegister ( expression ) ) { formattedValue = await Utility . getFormattedRegister ( expression , Z80RegisterHoverFormat ) ; } else { const pcLongAddr = Remote . getPCLong ( ) ; const entry = Labels . getFileAndLineForAddress ( pcLongAddr ) ; const lastLabel = entry . lastLabel ; const modulePrefix = entry . modulePrefix ; const labelValue = Utility . evalExpression ( expression , true , modulePrefix , lastLabel ) ; if ( labelValue != undefined ) { const memDump = await Remote . readMemoryDump ( labelValue , 2 ) ; const memByte = memDump [ 0 ] ; const formattedByte = Utility . numberFormattedSync ( memByte , 1 , Settings . launch . formatting . watchByte , true ) ; const memWord = memByte + 256 * memDump [ 1 ] ; const formattedWord = Utility . numberFormattedSync ( memWord , 2 , Settings . launch . formatting . watchWord , true ) ; const addrString = Utility . getHexString ( labelValue , 4 ) + 'h' ; if ( ! formattedValue ) formattedValue = expression + ': ' + addrString ; formattedValue += '\n(' + addrString + ')b=' + formattedByte + '\n(' + addrString + ')w=' + formattedWord ; } } } catch { } response . body = { result : formattedValue , variablesReference : 0 } ; break ; case 'watch' : try { const item = await this . evaluateLabelExpression ( expression ) ; let result = '' ; if ( item . immediateValue ) { result = await item . immediateValue . getValue ( ) ; } response . body = { result , variablesReference : item . varRef , type : item . description , indexedVariables : item . count } ; } catch ( e ) { response . body = { result : e . message , variablesReference : 0 } ; } break ; } this . sendResponse ( response ) ; } param expression returns protected async evaluateLabelExpression ( expression ) { const response = await this . constExpressionsList . get ( expression ) ; if ( response ) return response ; let text = expression ; const k = text . indexOf ( ';' ) ; if ( k >= 0 ) text = text . slice ( 0 , k ) ; const tokens = text . split ( ',' ) ; if ( tokens . length > 4 ) throw Error ( "Too many components in expression: " + expression ) ; let labelString = ( tokens [ 0 ] || '' ) . trim ( ) ; if ( ! labelString ) throw Error ( "No expression found." ) ; const matchIndex = /(.*)[^\[]*\[([^\]]+)\]/ . exec ( labelString ) ; let lblIndexString = '' ; if ( matchIndex ) { labelString = matchIndex [ 1 ] . trim ( ) ; lblIndexString = matchIndex [ 2 ] . trim ( ) ; } let lblType = ( tokens [ 1 ] || '' ) . trim ( ) ; let elemCountString = ( tokens [ 2 ] || '' ) . trim ( ) ; const endianess = ( tokens [ 3 ] || 'little' ) . trim ( ) . toLowerCase ( ) ; let littleEndian = true ; if ( endianess == 'big' ) littleEndian = false ; else if ( endianess != 'little' ) { throw Error ( "Unknown endianes: " + endianess ) ; } let labelValue ; let lastLabel ; let modulePrefix ; let lblIndex = 0 ; let elemCount = 1 ; let elemSize = 1 ; const pcLongAddr = Remote . getPCLong ( ) ; const entry = Labels . getFileAndLineForAddress ( pcLongAddr ) ; lastLabel = entry . lastLabel ; modulePrefix = entry . modulePrefix ; labelValue = Utility . evalExpression ( labelString , true , modulePrefix , lastLabel ) ; if ( isNaN ( labelValue ) ) throw Error ( "Could not parse label: " + labelString ) ; if ( lblType ) { elemSize = Utility . evalExpression ( lblType , true , modulePrefix , lastLabel ) ; if ( isNaN ( elemSize ) ) throw Error ( "Could not parse element size." ) ; if ( elemSize <= 0 ) throw Error ( "Element size must be > 0, is " + elemSize + "." ) ; } if ( lblIndexString ) { lblIndex = Utility . evalExpression ( lblIndexString , false , modulePrefix , lastLabel ) ; if ( isNaN ( lblIndex ) ) throw Error ( "Could not parse index." ) ; if ( lblIndex < 0 ) throw Error ( "Index must be > 0, is " + lblIndex + "." ) ; } if ( elemCountString ) { elemCount = Utility . evalExpression ( elemCountString , true , modulePrefix , lastLabel ) ; if ( isNaN ( elemCount ) ) throw Error ( "Could not parse element count." ) ; if ( elemCount <= 0 ) throw Error ( "Element count must be > 0, is " + elemCount + "." ) ; } else { let distAddr = Labels . getNumberForLabel ( labelString ) ; if ( distAddr == undefined ) distAddr = labelValue ; elemCount = Labels . getDistanceToNextLabel ( distAddr ! ) || 1 ; if ( ! lblType && elemCount == 2 ) { elemSize = 2 ; elemCount = 1 ; } else { elemCount = Math . floor ( ( elemCount + elemSize - 1 ) / elemSize ) ; if ( elemCount < 1 ) elemCount = 1 ; if ( elemCount > 1000 ) elemCount = 1000 ; } } const indexOffset = lblIndex * elemSize ; const labelValue64k = ( labelValue + indexOffset ) & 0xFFFF ; let labelVar ; let immediateValue ; let props ; let propsLength = 0 ; if ( lblType != undefined ) { props = Labels . getSubLabels ( lblType ) ; propsLength = props . length ; } if ( propsLength == 0 ) { if ( elemSize > 6 ) throw Error ( 'The size of an element must be smaller than 7.' ) ; if ( elemCount <= 1 ) { immediateValue = new ImmediateMemoryValue ( labelValue64k , elemSize , littleEndian ) ; } else { labelVar = new MemDumpVar ( labelValue64k , elemCount , elemSize , littleEndian ) ; } } else { if ( propsLength > 0 ) { labelVar = new StructVar ( labelValue64k , elemCount , elemSize , lblType , props , this . listVariables , littleEndian ) ; } if ( ! labelVar ) { labelVar = new MemDumpVar ( labelValue64k , elemCount , elemSize , littleEndian ) ; } } const description = Utility . getLongAddressString ( labelValue64k ) ; const varRef = this . listVariables . addObject ( labelVar ) ; const exprVar = { description , immediateValue , varRef , count : elemCount } ; const exprContainsRegs = Utility . exprContainsMainRegisters ( labelString ) ; if ( ! exprContainsRegs ) { this . constExpressionsList . set ( expression , exprVar ) ; } return exprVar ; } param tokens param A protected async evalHelp ( tokens ) { const output = `Allowed commands are:
"-ASSERTION enable|disable|status":
	- enable|disable: Enables/disables all breakpoints caused by ASSERTIONs set in the sources. All ASSERTIONs are by default enabled after startup of the debugger.
	- status: Shows enable status of ASSERTION breakpoints.
"-dasm address count": Disassembles a memory area. count=number of lines.
"-eval expr": Evaluates an expression. The expression might contain mathematical expressions and also labels. It will also return the label if
the value correspondends to a label.
"-exec|e cmd args": cmd and args are directly passed to ZEsarUX. E.g. "-exec get-registers".
"-help|h": This command. Do "-e help" to get all possible ZEsarUX commands.
"-label|-l XXX": Returns the matching labels (XXX) with their values. Allows wildcard "*".
"-LOGPOINT enable|disable|status [group]":
	- enable|disable: Enables/disables all logpoints caused by LOGPOINTs of a certain group set in the sources. If no group is given all logpoints are affected. All logpoints are by default disabled after startup of the debugger.
	- status: Shows enable status of LOGPOINTs per group.
"-md address size [dec|hex] [word] [little|big]": Memory dump at 'address' with 'size' bytes. Output is in 'hex' (default) or 'dec'imal. Per default data will be grouped in bytes. But if chosen, words are output. Last argument is the endianness which is little endian by default.
"-msetb address value [repeat]:"
	- address: The address to fill. Can also be a label or expression.
	- value: The byte value to set.
	- repeat: (Optional) How often the value is repeated.
	Examples:
	"-msetb 8000h 0Fh" : Puts a 15 into memory location 0x8000.
	"-msetb 8000h 0 100h" : fills memory locations 0x8000 to 0x80FF with zeroes.
	"-msetb fill_colors_ptr+4 FEh": If fill_colors_ptr is e.g. 0xCF02 the value FEh is put into location 0xCF06.
"-msetw address value [repeat [endianness]]:"
	- address: The address to fill. Can also be a label or expression.
	- value: The word value to set.
	- repeat: (Optional) How often the value is repeated.
	- endianness: (Optional) 'little' (default) or 'big'.
	Examples:
	"-msetw 8000h AF34h" : Puts 34h into location 0x8000 and AFh into location 0x8001.
	"-msetw 8000h AF34h 1 big" : Puts AFh into location 0x8000 and 34h into location 0x8001.
	"-msetw 8000h 1234h 100h" : fills memory locations 0x8000 to 0x81FF with the word value 1234h.
"-ms address size filename": Saves a memory dump to a file. The file is saved to the temp directory.
"-mv address size [address_n size_n]*": Memory view at 'address' with 'size' bytes. Will open a new view to display the memory contents.
"-patterns [index[+count|-endindex] [...]": Shows the tbblue sprite patterns beginning at 'index' until 'endindex' or a number of 'count' indices.
	The values can be omitted. 'index' defaults to 0 and 'count' to 1.
	Without any parameter it will show all sprite patterns.
	You can concat several ranges.
	Example: "-patterns 10-15 20+3 33" will show sprite patterns at index 10, 11, 12, 13, 14, 15, 20, 21, 22, 33.
"-rmv": Shows the memory register view. I.e. a dynamic view with the memory contents the registers point to.
"-sprites [slot[+count|-endslot] [...]": Shows the tbblue sprite registers beginning at 'slot' until 'endslot' or a number of 'count' slots. The values can be omitted. 'slot' defaults to 0 and 'count' to 1. You can concat several ranges.
	Example: "-sprite 10-15 20+3 33" will show sprite slots 10, 11, 12, 13, 14, 15, 20, 21, 22, 33.
	Without any parameter it will show all visible sprites automatically.
"-state save|restore|list|clear|clearall [statename]": Saves/restores the current state. I.e. the complete RAM + the registers.
"-WPMEM enable|disable|status":
	- enable|disable: Enables/disables all WPMEM set in the sources. All WPMEM are by default enabled after startup of the debugger.
	- status: Shows enable status of WPMEM watchpoints.

Some examples:
"-exec h 0 100": Does a hexdump of 100 bytes at address 0.
"-e write-memory 8000h 9fh": Writes 9fh to memory address 8000h.
"-e gr": Shows all registers.
"-eval 2+3*5": Results to "17".
"-msetb mylabel 3": Sets the data at memory location 'mylabel' to 3.
"-mv 0 10": Shows the memory at address 0 to address 9.
"-sprites": Shows all visible sprites.
"-state save 1": Stores the current state as 'into' 1.
"-state restore 1": Restores the state 'from' 1.

Notes:
For all commands (if it makes sense or not) you can add "-view" as first parameter. This will redirect the output to a new view instead of the console. E.g. use "-help -view" to put the help text in an own view.
` ; this . sendEvent ( new StoppedEvent ( 'Value updated' ) ) ; return output ; } param tokens returns protected async evalEval ( tokens ) { const expr = tokens . join ( ' ' ) . trim ( ) ; if ( expr . length == 0 ) { throw new Error ( "Expression expected." ) ; } let result ; const value = Utility . evalExpression ( expr ) ; result = value . toString ( ) ; result += ', ' + value . toString ( 16 ) . toUpperCase ( ) + 'h' ; result += ', ' + value . toString ( 2 ) + 'b' ; const labels = Labels . getLabelsPlusIndexForNumber64k ( value ) ; if ( labels . length > 0 ) { result += ', ' + labels . join ( ', ' ) ; } return result ; } param tokens returns protected async evalExec ( tokens ) { const machineCmd = tokens . join ( ' ' ) ; const textData = await Remote . dbgExec ( machineCmd ) ; return textData ; } param tokens returns protected async evalLabel ( tokens ) { const expr = tokens . join ( ' ' ) . trim ( ) ; if ( expr . length == 0 ) { return "Label expected." ; } const rString = '^' + expr . replace ( /\*/g , '.*?' ) + '$' ; const labels = Labels . getLabelsForRegEx ( rString ) ; let result = '' ; if ( labels . length > 0 ) { labels . forEach ( label => { let value = Labels . getNumberForLabel ( label ) ; if ( value != undefined ) value &= 0xFFFF ; result += label + ': ' + Utility . getHexString ( value , 4 ) + 'h\n' ; } ) ; } else { result = 'No label matches.' ; } return result ; } param tokens returns protected async evalMemDump ( tokens ) { if ( tokens . length < 2 ) { throw Error ( "Address and size expected." ) ; } const addressString = tokens [ 0 ] ; const address = Utility . evalExpression ( addressString ) ; if ( address < 0 || address > 0xFFFF ) throw Error ( "Address (" + address + ") out of range." ) ; const sizeString = tokens [ 1 ] ; const size = Utility . evalExpression ( sizeString ) ; if ( size < 0 || size > 0xFFFF ) throw Error ( "Size (" + size + ") out of range." ) ; let unitSize = 1 ; let bigEndian = false ; let hex = true ; const typeString = tokens [ 2 ] ; if ( typeString ) { const typeStringLower = typeString . toLowerCase ( ) ; if ( typeStringLower != "hex" && typeStringLower != "dec" && typeStringLower != "word" ) throw Error ( "'hex', 'dec' or 'word' expected but got '" + typeString + "'." ) ; let k = 2 ; if ( typeString == 'hex' ) k ++ ; else if ( typeString == 'dec' ) { hex = false ; k ++ ; } const unitSizeString = tokens [ k ] ; if ( unitSizeString ) { const unitSizeStringLower = unitSizeString . toLowerCase ( ) ; if ( unitSizeStringLower != "word" ) throw Error ( "'word' expected but got '" + unitSizeString + "'." ) ; unitSize = 2 ; const endianness = tokens [ k + 1 ] ; if ( endianness ) { const endiannessLower = endianness . toLowerCase ( ) ; if ( endiannessLower == "big" ) { bigEndian = true ; } else if ( endiannessLower != "little" ) { throw Error ( "'little' or 'big' expected but got '" + endianness + "'." ) ; } } } } const data = await Remote . readMemoryDump ( address , size ) ; let output = '' ; for ( let i = 0 ; i < size ; i += unitSize ) { let value = data [ i ] ; if ( unitSize == 2 ) { if ( bigEndian ) value = ( value << 8 ) + data [ i + 1 ] ; else value += data [ i + 1 ] << 8 ; } if ( hex ) output += Utility . getHexString ( value , 2 * unitSize ) + ' ' ; else output += value + ' ' ; } return output ; } param endiannessString returns protected isLittleEndianString ( endiannessString ) { let littleEndian = true ; if ( endiannessString != undefined ) { const s = endiannessString . toLowerCase ( ) ; if ( s != 'little' && s != 'big' ) throw Error ( "Endianness (" + endiannessString + ") unknown." ) ; littleEndian = ( s == 'little' ) ; } return littleEndian ; } param valSize param addressString param valueString param repeatString param endiannessString returns protected async memSet ( valSize , addressString , valueString , repeatString ? , endiannessString ? ) { const address = Utility . evalExpression ( addressString ) ; if ( address < 0 || address > 0xFFFF ) throw Error ( "Address (" + address + ") out of range." ) ; const value = Utility . evalExpression ( valueString ) ; const maxValue = 2 ** ( valSize * 8 ) ; if ( value >= maxValue || value < ( - maxValue / 2 ) ) throw Error ( "Value (" + value + ") too big (or too small)." ) ; const repeat = ( repeatString != undefined ) ? Utility . evalExpression ( repeatString ) : 1 ; const totalSize = valSize * repeat ; if ( totalSize <= 0 || totalSize > 0xFFFF ) throw Error ( "Repetition (" + repeat + ") out of range." ) ; const littleEndian = this . isLittleEndianString ( endiannessString ) ; const data = new Uint8Array ( totalSize ) ; let index = 0 ; for ( let r = 0 ; r < repeat ; r ++ ) { let val = value ; for ( let k = 0 ; k < valSize ; k ++ ) { if ( littleEndian ) { data [ index + k ] = val & 0xFF ; } else { data [ index + valSize - k - 1 ] = val & 0xFF ; } val = val >> 8 ; } index += valSize ; } await Remote . writeMemoryDump ( address , data ) ; this . update ( ) ; return 'OK' ; } param tokens returns protected async evalMemSetByte ( tokens ) { if ( tokens . length < 2 ) { throw Error ( "At least address and value expected." ) ; } if ( tokens . length > 3 ) { throw Error ( "Too many arguments." ) ; } return this . memSet ( 1 , tokens [ 0 ] , tokens [ 1 ] , tokens [ 2 ] ) ; } param tokens returns protected async evalMemSetWord ( tokens ) { if ( tokens . length < 2 ) { throw Error ( "At least address and value expected." ) ; } if ( tokens . length > 4 ) { throw Error ( "Too many arguments." ) ; } return this . memSet ( 2 , tokens [ 0 ] , tokens [ 1 ] , tokens [ 2 ] , tokens [ 3 ] ) ; } param tokens returns protected async evalMemSave ( tokens ) { if ( tokens . length < 2 ) { throw Error ( "Address and size expected." ) ; } const addressString = tokens [ 0 ] ; const address = Utility . evalExpression ( addressString ) ; if ( address < 0 || address > 0xFFFF ) throw Error ( "Address (" + address + ") out of range." ) ; const sizeString = tokens [ 1 ] ; const size = Utility . evalExpression ( sizeString ) ; if ( size < 0 || size > 0xFFFF ) throw Error ( "Size (" + size + ") out of range." ) ; const filename = tokens [ 2 ] ; if ( ! filename ) throw Error ( "No filename given." ) ; const data = await Remote . readMemoryDump ( address , size ) ; const relPath = Utility . getRelTmpFilePath ( filename ) ; const absPath = Utility . getAbsFilePath ( relPath ) ; fs . writeFileSync ( absPath , data ) ; return 'OK' ; } param tokens returns protected async evalMemViewByte ( tokens ) { if ( tokens . length == 0 ) { throw new Error ( "Address and size expected." ) ; } if ( tokens . length % 2 != 0 ) { throw new Error ( "No size given for address '" + tokens [ tokens . length - 1 ] + "'." ) ; } const addrSizes = new Array < number > ( ) ; for ( let k = 0 ; k < tokens . length ; k += 2 ) { const addressString = tokens [ k ] ; const address = Utility . evalExpression ( addressString ) ; addrSizes . push ( address ) ; const sizeString = tokens [ k + 1 ] ; const size = Utility . evalExpression ( sizeString ) ; addrSizes . push ( size ) ; } const panel = new MemoryDumpView ( ) ; for ( let k = 0 ; k < tokens . length ; k += 2 ) { const start = addrSizes [ k ] ; const size = addrSizes [ k + 1 ] ; panel . addBlock ( start , size , Utility . getHexString ( start & 0xFFFF , 4 ) + 'h-' + Utility . getHexString ( ( start + size - 1 ) & 0xFFFF , 4 ) + 'h' ) ; } panel . mergeBlocks ( ) ; await panel . update ( ) ; return 'OK' ; } param tokens returns protected async evalMemViewWord ( tokens ) { let littleEndian = true ; if ( tokens . length % 2 != 0 ) { const endiannessString = tokens . pop ( ) ; littleEndian = this . isLittleEndianString ( endiannessString ) ; } if ( tokens . length == 0 ) { throw new Error ( "Address and size expected." ) ; } const addrSizes = new Array < number > ( ) ; for ( let k = 0 ; k < tokens . length ; k += 2 ) { const addressString = tokens [ k ] ; const address = Utility . evalExpression ( addressString ) ; addrSizes . push ( address ) ; const sizeString = tokens [ k + 1 ] ; const size = Utility . evalExpression ( sizeString ) ; addrSizes . push ( size ) ; } const panel = new MemoryDumpViewWord ( littleEndian ) ; for ( let k = 0 ; k < tokens . length ; k += 2 ) { const start = addrSizes [ k ] ; const size = addrSizes [ k + 1 ] ; panel . addBlock ( start , size , Utility . getHexString ( start & 0xFFFF , 4 ) + 'h-' + Utility . getHexString ( ( start + 2 * size - 1 ) & 0xFFFF , 4 ) + 'h' ) ; } panel . mergeBlocks ( ) ; await panel . update ( ) ; return 'OK' ; } returns protected async evalRegisterMemView ( tokens ) { if ( tokens . length != 0 ) { throw new Error ( "No parameters expected." ) ; } const registerMemoryView = new MemoryRegisterView ( ) ; const regs = Settings . launch . memoryViewer . registersMemoryView ; registerMemoryView . addRegisters ( regs ) ; await registerMemoryView . update ( ) ; return 'OK' ; } param tokens returns protected async evalDasm ( tokens ) { if ( tokens . length == 0 ) { throw new Error ( "Address and number of lines expected." ) ; } if ( tokens . length > 2 ) { throw new Error ( "Too many arguments." ) ; } const addressString = tokens [ 0 ] ; const address = Utility . evalExpression ( addressString ) ; const countString = tokens [ 1 ] ; let count = 10 ; if ( tokens . length > 1 ) { count = Utility . evalExpression ( countString ) ; } const data = await Remote . readMemoryDump ( address , 4 * count ) ; const dasmArray = DisassemblyClass . getLines ( address , data , count ) ; let txt = '' ; for ( const line of dasmArray ) { txt += Utility . getHexString ( line . address , 4 ) + '\t' + line . instruction + '\n' ; } return txt ; } param tokens returns protected async evalLOGPOINT ( tokens ) { const param = tokens [ 0 ] || '' ; const group = tokens [ 1 ] ; if ( param == 'enable' || param == 'disable' ) { const enable = ( param == 'enable' ) ; await Remote . enableLogpointGroup ( group , enable ) ; } else if ( param == 'status' ) { } else { throw new Error ( "Unknown argument: '" + param + "'" ) ; } let result = 'LOGPOINT groups:' ; const enableMap = Remote . logpointsEnabled ; if ( enableMap . size == 0 ) result += ' none' ; else { for ( const [ grp , enable ] of enableMap ) { result += '\n  ' + grp + ': ' + ( ( enable ) ? 'enabled' : 'disabled' ) ; } } return result ; } param tokens returns protected async evalASSERTION ( tokens ) { const param = tokens [ 0 ] || '' ; if ( param == 'enable' || param == 'disable' ) { const paramEnable = ( param == 'enable' ) ; await Remote . enableAssertionBreakpoints ( paramEnable ) ; } else if ( param == 'status' ) { } else { throw new Error ( "Unknown argument: '" + param + "'" ) ; } const enable = Remote . assertionBreakpointsEnabled ; const enableString = ( enable ) ? 'enabled' : 'disabled' ; let result = 'ASSERTION breakpoints are ' + enableString + '.\n' ; if ( enable ) { const abps = Remote . getAllAssertionBreakpoints ( ) ; for ( const abp of abps ) { result += Utility . getLongAddressString ( abp . address ) ; const labels = Labels . getLabelsForLongAddress ( abp . address ) ; if ( labels . length > 0 ) { const labelsString = labels . join ( ', ' ) ; result += ' (' + labelsString + ')' ; } result += ', Condition: ' + Utility . getAssertionFromCondition ( abp . condition ) + '\n' ; } if ( abps . length == 0 ) result += 'No ASSERTION breakpoints.\n' ; } return result ; } param tokens returns protected async evalWPMEM ( tokens ) { const param = tokens [ 0 ] || '' ; if ( param == 'enable' || param == 'disable' ) { const paramEnable = ( param == 'enable' ) ; await Remote . enableWPMEM ( paramEnable ) ; } else if ( param == 'status' ) { } else { throw Error ( "Unknown argument: '" + param + "'" ) ; } const enable = Remote . wpmemEnabled ; const enableString = ( enable ) ? 'enabled' : 'disabled' ; let result = 'WPMEM watchpoints are ' + enableString + '.\n' ; if ( enable ) { const wps = Remote . getAllWpmemWatchpoints ( ) ; for ( const wp of wps ) { result += Utility . getLongAddressString ( wp . address ) ; const labels = Labels . getLabelsForLongAddress ( wp . address ) ; if ( labels . length > 0 ) { const labelsString = labels . join ( ', ' ) ; result += ' (' + labelsString + ')' ; } result += ', size=' + wp . size + '\n' ; } if ( wps . length == 0 ) result += 'No WPMEM watchpoints.\n' ; } return result ; } param tokens returns protected async evalSpritePatterns ( tokens ) { let title ; let params = [ ] ; if ( tokens . length == 0 ) { title = 'Sprite Patterns: 0-63' ; params . push ( 0 ) ; params . push ( 64 ) ; } else { title = 'Sprite Patterns: ' + tokens . join ( ' ' ) ; while ( true ) { const param = tokens . shift ( ) ; if ( ! param ) break ; const match = /([^+-]*)(([-+])(.*))?/ . exec ( param ) ; if ( ! match ) throw new Error ( "Can't parse: '" + param + "'" ) ; const start = Utility . parseValue ( match [ 1 ] ) ; if ( isNaN ( start ) ) throw new Error ( "Expected slot but got: '" + match [ 1 ] + "'" ) ; let countValue = 1 ; if ( match [ 3 ] ) { countValue = Utility . parseValue ( match [ 4 ] ) ; if ( isNaN ( countValue ) ) throw new Error ( "Can't parse: '" + match [ 4 ] + "'" ) ; if ( match [ 3 ] == "-" ) countValue += 1 - start ; } if ( countValue <= 0 ) throw new Error ( "Not allowed count: '" + match [ 0 ] + "'" ) ; params . push ( start ) ; params . push ( countValue ) ; } const slotString = tokens [ 0 ] || '0' ; const slot = Utility . parseValue ( slotString ) ; if ( isNaN ( slot ) ) { throw new Error ( "Expected slot but got: '" + slotString + "'" ) ; } const countString = tokens [ 1 ] || '1' ; const count = Utility . parseValue ( countString ) ; if ( isNaN ( count ) ) { throw new Error ( "Expected count but got: '" + countString + "'" ) ; } } const panel = new ZxNextSpritePatternsView ( title , params ) ; await panel . update ( ) ; return 'OK' ; } param tokens returns protected async evalSprites ( tokens ) { let title ; let params ; if ( tokens . length == 0 ) { title = 'Visible Sprites' ; } else { title = 'Sprites: ' + tokens . join ( ' ' ) ; params = [ ] ; while ( true ) { const param = tokens . shift ( ) ; if ( ! param ) break ; const match = /([^+-]*)(([-+])(.*))?/ . exec ( param ) ; if ( ! match ) throw new Error ( "Can't parse: '" + param + "'" ) ; const start = Utility . parseValue ( match [ 1 ] ) ; if ( isNaN ( start ) ) throw new Error ( "Expected slot but got: '" + match [ 1 ] + "'" ) ; let countValue = 1 ; if ( match [ 3 ] ) { countValue = Utility . parseValue ( match [ 4 ] ) ; if ( isNaN ( countValue ) ) throw new Error ( "Can't parse: '" + match [ 4 ] + "'" ) ; if ( match [ 3 ] == "-" ) countValue += 1 - start ; } if ( countValue <= 0 ) throw new Error ( "Not allowed count: '" + match [ 0 ] + "'" ) ; params . push ( start ) ; params . push ( countValue ) ; } const slotString = tokens [ 0 ] || '0' ; const slot = Utility . parseValue ( slotString ) ; if ( isNaN ( slot ) ) { throw new Error ( "Expected slot but got: '" + slotString + "'" ) ; } const countString = tokens [ 1 ] || '1' ; const count = Utility . parseValue ( countString ) ; if ( isNaN ( count ) ) { throw new Error ( "Expected count but got: '" + countString + "'" ) ; } } const panel = new ZxNextSpritesView ( title , params ) ; await panel . update ( ) ; return 'OK' ; } param tokens returns protected async evalStateSaveRestore ( tokens ) { const param = tokens [ 0 ] || '' ; const stateName = tokens [ 1 ] ; if ( ! stateName && ( param == 'save' || param == 'restore' || param == 'clear' ) ) throw new Error ( "Parameter missing: You need to add a name for the state, e.g. '0', '1' or more descriptive 'start'" ) ; if ( param == 'save' ) { await this . stateSave ( stateName ) ; return "Saved state '" + stateName + "'." ; } else if ( param == 'restore' ) { await this . stateRestore ( stateName ) ; return "Restored state '" + stateName + "'." ; } else if ( param == 'list' ) { let files ; try { const dir = Utility . getAbsStateFileName ( '' ) ; files = fs . readdirSync ( dir ) ; } catch { } let text ; if ( files == undefined || files . length == 0 ) text = "No states saved yet." ; else text = "All states:\n" + files . join ( '\n' ) ; return text ; } else if ( param == 'clearall' ) { try { const dir = Utility . getAbsStateFileName ( '' ) ; const files = fs . readdirSync ( dir ) ; for ( const file of files ) { const path = Utility . getAbsStateFileName ( file ) ; fs . unlinkSync ( path ) ; } } catch ( e ) { return e . message ; } return "All states deleted." ; } else if ( param == 'clear' ) { try { const path = Utility . getAbsStateFileName ( stateName ) ; fs . unlinkSync ( path ) ; } catch ( e ) { return e . message ; } return "State '" + stateName + "' deleted." ; } else { throw new Error ( "Unknown argument: '" + param + "'" ) ; } } param tokens returns protected async evalDebug ( tokens ) { const param1 = tokens [ 0 ] || '' ; let unknownArg = param1 ; throw new Error ( "Unknown argument: '" + unknownArg + "'" ) ; } /**
    * Called eg. if user changes a register value.
    */ protected async setVariableRequest ( response , args ) { const ref = args . variablesReference ; const name = args . name ; const value = Utility . parseValue ( args . value ) ; ShallowVar . clearChanged ( ) ; const varObj = this . listVariables . getObject ( ref ) ; response . success = false ; if ( varObj ) { const msg = varObj . changeable ( name ) ; if ( msg ) { response . message = msg ; } else { const formattedString = await varObj . setValue ( name , value ) ; if ( formattedString ) { response . body = { value : formattedString } ; response . success = true ; } } } this . sendResponse ( response ) ; if ( ShallowVar . pcChanged ) await this . pcHasBeenChanged ( ) ; if ( ShallowVar . spChanged ) await this . spHasBeenChanged ( ) ; if ( ShallowVar . otherRegisterChanged ) await this . otherRegisterHasBeenChanged ( ) ; if ( ShallowVar . memoryChanged ) { await this . memoryHasBeenChanged ( ) ; this . sendEvent ( new InvalidatedEvent ( [ 'variables' ] ) ) ; } ShallowVar . clearChanged ( ) ; } /**
     * Should be called if PC is manually changed.
     */ protected async pcHasBeenChanged ( ) { await Remote . getCallStackFromEmulator ( ) ; this . sendEvent ( new StoppedEvent ( "PC changed" , DebugSessionClass . THREAD_ID ) ) ; await BaseView . staticCallUpdateRegisterChanged ( ) ; } /**
     * Should be called if SP is manually changed.
     */ protected async spHasBeenChanged ( ) { await Remote . getCallStackFromEmulator ( ) ; this . sendEvent ( new InvalidatedEvent ( [ 'variables' ] ) ) ; await BaseView . staticCallUpdateRegisterChanged ( ) ; } /**
     * Should be called if any  other register is manually changed.
     * Also for PC or SP. In that case both functions are called.
     */ protected async otherRegisterHasBeenChanged ( ) { this . sendEvent ( new InvalidatedEvent ( [ 'variables' ] ) ) ; await BaseView . staticCallUpdateRegisterChanged ( ) ; } /**
     * Should be called if memory content has been manually changed.
     */ protected async memoryHasBeenChanged ( ) { await BaseView . staticCallUpdateFunctions ( ) ; } param filename param lineNr protected async setPcToLine ( filename , lineNr ) { const realLineNr = lineNr ; let addr = Remote . getAddrForFileAndLine ( filename , realLineNr ) ; if ( addr < 0 ) return ; const slots = Remote . getSlots ( ) ; if ( slots ) { const bank = Z80Registers . getBankFromAddress ( addr ) ; if ( bank >= 0 ) { const slotIndex = Z80Registers . getSlotFromAddress ( addr ) ; if ( bank != slots [ slotIndex ] ) { this . showError ( "Cannot set PC to a location (address=" + Utility . getHexString ( addr & 0xFFFF , 4 ) + "h) of a bank (bank " + bank + ") that is currently not paged in." ) ; return ; } } } await Remote . setRegisterValue ( 'PC' , addr & 0xFFFF ) ; await Remote . getRegistersFromEmulator ( ) ; StepHistory . clear ( ) ; await this . pcHasBeenChanged ( ) ; } param filename param fromLineNr param toLineNr protected async disassemblyAtCursor ( filename , fromLineNr , toLineNr ) { let fromAddr ; while ( fromLineNr <= toLineNr ) { fromAddr = Remote . getAddrForFileAndLine ( filename , fromLineNr ) ; if ( fromAddr >= 0 ) break ; fromLineNr ++ ; } let toAddr ; while ( fromLineNr <= toLineNr ) { toAddr = Remote . getAddrForFileAndLine ( filename , toLineNr ) ; if ( toAddr >= 0 ) break ; toLineNr -- ; } if ( fromAddr < 0 ) return ; const slots = Remote . getSlots ( ) ; if ( slots ) { const fromBank = Z80Registers . getBankFromAddress ( fromAddr ) ; if ( fromBank >= 0 ) { const slotIndex = Z80Registers . getSlotFromAddress ( fromAddr ) ; if ( fromBank != slots [ slotIndex ] ) { this . debugConsoleAppendLine ( "Memory currently not paged in.  (address=" + Utility . getHexString ( fromBank & 0xFFFF , 4 ) + "h, bank=" + fromBank + ")" ) ; return ; } } const toBank = Z80Registers . getBankFromAddress ( toAddr ) ; if ( toBank >= 0 ) { const slotIndex = Z80Registers . getSlotFromAddress ( toAddr ) ; if ( toBank != slots [ slotIndex ] ) { this . debugConsoleAppendLine ( "Memory currently not paged in.  (address=" + Utility . getHexString ( toBank & 0xFFFF , 4 ) + "h, bank=" + toBank + ")" ) ; return ; } } } this . debugConsoleAppendLine ( '' ) ; let size = ( toAddr - fromAddr + 1 ) & 0xFFFF ; if ( size > 0x800 ) { size = 0x800 ; this . debugConsoleAppendLine ( 'Note: Disassembly limited to ' + size + ' bytes.' ) ; } fromAddr &= 0xFFFF ; const data = await Remote . readMemoryDump ( fromAddr , size + 3 ) ; const dasmArray = DisassemblyClass . getDasmMemory ( fromAddr , data ) ; for ( const addrInstr of dasmArray ) { this . debugConsoleAppendLine ( Utility . getHexString ( addrInstr . address , 4 ) + " " + addrInstr . instruction ) ; } } param command param response param args protected async customRequest ( command , response , args ) { switch ( command ) { case 'setPcToLine' : { const filename = args [ 0 ] ; const lineNr = args [ 1 ] ; await this . setPcToLine ( filename , lineNr ) ; this . sendResponse ( response ) ; } break ; case 'disassemblyAtCursor' : { const filename = args [ 0 ] ; const fromLineNr = args [ 1 ] ; const toLineNr = args [ 2 ] ; await this . disassemblyAtCursor ( filename , fromLineNr , toLineNr ) ; this . sendResponse ( response ) ; } break ; default : super . customRequest ( command , response , args ) ; return ; } } param reason protected update ( reason ? ) { this . emit ( 'update' , reason ) ; } param stateName protected async stateSave ( stateName ) { const filePath = Utility . getAbsStateFileName ( stateName ) ; try { try { const dir = Utility . getAbsStateFileName ( '' ) ; fs . mkdirSync ( dir ) ; } catch { } await Remote . stateSave ( filePath ) ; } catch ( e ) { const errTxt = "Can't save '" + filePath + "': " + e . message ; throw new Error ( errTxt ) ; } } param stateName protected async stateRestore ( stateName ) { if ( this . processingSteppingRequest ) { throw new Error ( "Can't restore state while running. Please stop first." ) ; } let filePath ; try { filePath = Utility . getAbsStateFileName ( stateName ) ; await Remote . stateRestore ( filePath ) ; } catch ( e ) { const errTxt = "Can't load '" + filePath + "': " + e . message ; throw new Error ( errTxt ) ; } StepHistory . init ( ) ; Decoration ?. clearAllDecorations ( ) ; await Remote . getRegistersFromEmulator ( ) ; await Remote . getCallStackFromEmulator ( ) ; this . update ( ) ; this . sendEvent ( new StoppedEvent ( 'restore' , DebugSessionClass . THREAD_ID ) ) ; } param text protected debugConsoleIndentedText ( text ) { this . debugConsoleAppendLine ( this . debugConsoleIndentation + text ) ; } } DebugSessionClass . run ( DebugSessionClass ) ;