import { Z80RegistersClass , Z80_REG , Z80Registers } from './z80registers' ; import { RefList } from '../misc/reflist' ; import { CallStackFrame } from '../callstackframe' ; import { EventEmitter } from 'events' ; import { GenericWatchpoint , GenericBreakpoint } from '../genericwatchpoint' ; import { Labels , SourceFileEntry } from '../labels/labels' ; import { Settings } from '../settings' ; import { Utility } from '../misc/utility' ; import { BaseMemory } from '../disassembler/basememory' ; import { Opcode , OpcodeFlag } from '../disassembler/opcode' ; import { Disassembly , DisassemblyClass } from '../misc/disassembly' ; import { MemoryBank , MemoryModel } from './Paging/memorymodel' ; import { Log } from '../log' ; /**
 * Breakpoint reason numbers.
 * Are used in DZRP as well, so be cautious when changing values.
 */ export enum BREAK_REASON_NUMBER { NO_REASON = 0 , MANUAL_BREAK = 1 , BREAKPOINT_HIT = 2 , WATCHPOINT_READ = 3 , WATCHPOINT_WRITE = 4 , CPU_ERROR = 5 , STEPPING_NOT_ALLOWED = 100 , UNKNOWN = 255 } /**
 * The breakpoint representation.
 */ export interface RemoteBreakpoint extends GenericBreakpoint { bpId : number ; filePath ? : string ; lineNr : number ; } /**
 * The Remote's base class.
 * It implements, provides stubs or interfaces to deal with:
 * - step, run (continue), etc.
 * - reverse debugging
 * - breakpoints
 *
 * Breakpoints:
 * The 'breakpoints' array contains all breakpoints set by the
 * vscode UI. I.e. the breakpoints you set manually/the red dot
 * at the start of the line.
 * vscode sends a setBreakPointsRequest with the breakpoints, they
 * are converted into the RemoteBreakpoint type and set with
 * setBreakPoints. The Remote will now compare the breakpoints with the internal
 * 'breakpoints' array and set/remove all changed breakpoints.
 *
 * The additional 'watchpoints', 'assertionBreakpoints' and 'logpoints'
 * arrays can be enabled/disabled as a group via a debug command.
 * - 'watchPoints': These are associated with the WPMEM keyword and create
 * a memory watchpoint (a breakpoint that is hit if a memory address is
 * accessed).
 * - 'assertionBreakpoints': These are very much like conditional breakpoints but associated with the ASSERTION keyword.
 * - 'logpoints': These are just like breakpoints with a log message but associated with the LOGPOINT keyword.
 * Note: The attached emulator may use the same mechanism for all these
 * kinds of breakpoints but in DeZog they are differentiated.
 *
 */ export class RemoteBase extends EventEmitter { static MAX_STACK_ITEMS = 100 ; public topOfStack : number ; /**
     * Sets the global Remote variable.
     */ public static setGlobalRemote ( remote ) { Remote = remote ; } protected listFrames : RefList < CallStackFrame > ; protected breakpoints = new Array < RemoteBreakpoint > ( ) ; public wpmemEnabled = false ; protected reverseDbgStack : RefList < CallStackFrame > ; protected wpmemWatchpoints = new Array < GenericWatchpoint > ( ) ; protected assertionBreakpoints = new Array < GenericBreakpoint > ( ) ; public assertionBreakpointsEnabled = false ; protected logpoints = new Map < string , Array < GenericBreakpoint > > ( ) ; public logpointsEnabled = new Map < string , boolean > ( ) ; public memoryModel : MemoryModel ; constructor ( ) { super ( ) ; } /**
     * Call this to dispose any resources.
     */ public dispose ( ) { Remote = undefined as any ; } public async init ( ) { await this . doInitialization ( ) ; const watchPointLines = Labels . getWatchPointLines ( ) ; const watchpoints = this . createWatchPoints ( watchPointLines ) ; this . setWPMEMArray ( watchpoints ) ; const assertionLines = Labels . getAssertionLines ( ) ; const assertionsArray = this . createAssertions ( assertionLines ) ; this . setASSERTIONArray ( assertionsArray ) ; const logPointLines = Labels . getLogPointLines ( ) ; const logPointsMap = this . createLogPoints ( logPointLines ) ; this . setLOGPOINTArray ( logPointsMap ) ; } /**
     * Do initialization.
     * E.g. create a socket or allocate memory.
     * This is called when the Remote is started by the debugger. I.e. at the start
     * of a debugging session.
     * When ready do a this.emit('initialized') or this.emit('error', exception);
     * Take care to implement the emits otherwise the system will hang on a start.
     * Please override.
     */ public async doInitialization ( ) { } /**
     * Loads the sna/nex file and the obj files.
     * Do not override.
     * Override
     */ public async loadExecutable ( ) { const loadPath = Settings . launch . load ; if ( loadPath ) await this . loadBin ( loadPath ) ; for ( const loadObj of Settings . launch . loadObjs ) { if ( loadObj . path ) { const start = Labels . getNumberFromString64k ( loadObj . start ) ; if ( isNaN ( start ) ) throw Error ( "Cannot evaluate 'loadObjs[].start' (" + loadObj . start + ")." ) ; await this . loadObj ( loadObj . path , start ) ; } } await this . getRegistersFromEmulator ( ) ; } param path protected async loadBin ( path ) { throw Error ( 'Loading files is not supported.' ) ; } param path param address protected async loadObj ( path , address ) { throw Error ( 'Loading object files is not supported.' ) ; } /**
     * Retrieves the exec address from the Settings (if available) and sets PC to it.
     * Do not override.
     */ public async setLaunchExecAddress ( ) { if ( Settings . launch . execAddress ) { const execAddress = Labels . getNumberFromString64k ( Settings . launch . execAddress ) ; if ( isNaN ( execAddress ) ) throw Error ( "Cannot evaluate 'execAddress' (" + Settings . launch . execAddress + ")." ) ; await this . setRegisterValue ( "PC" , execAddress ) ; } } param watchPointLines return protected createWatchPoints ( watchPointLines ) { const watchpoints = new Array < GenericWatchpoint > ( ) ; let i = - 1 ; for ( let entry of watchPointLines ) { i = i + 1 ; try { const line = entry . line . substr ( 5 ) ; const subParts = line . split ( ',' ) . map ( s => s . trim ( ) ) ; let addressString = subParts [ 0 ] ; let lengthString = subParts [ 1 ] ; let access = subParts [ 2 ] ; let cond = subParts [ 3 ] ; let entryAddress = entry . address ; if ( addressString && addressString . length > 0 ) entryAddress = Utility . evalExpression ( addressString , false ) ; if ( isNaN ( entryAddress ) ) continue ; let length = 1 ; if ( lengthString && lengthString . length > 0 ) { length = Utility . evalExpression ( lengthString , false ) ; } if ( access && access . length > 0 ) { access = access . trim ( ) ; access = access . toLowerCase ( ) ; if ( access != 'r' && access != 'w' && access != 'rw' ) { const errText = "Wrong access mode in watch point. Allowed are only 'r', 'w' or 'rw' but found '" + access + "' in line: '" + entry . line + "'" ; console . log ( errText ) ; continue ; } } else access = 'rw' ; watchpoints . push ( { address : entryAddress , size : length , access : access , condition : cond || '' } ) ; } catch ( e ) { throw Error ( "Problem with WPMEM. Could not evaluate: '" + entry . line + "': " + e . message + "" ) ; } } return watchpoints ; } param assertionLines return protected createAssertions ( assertionLines ) { const assertionMap = new Map < number , GenericBreakpoint > ( ) ; for ( let entry of assertionLines ) { try { const matchAssertion = /^ASSERTION(.*)/ . exec ( entry . line ) ; if ( ! matchAssertion ) continue ; const part = matchAssertion [ 1 ] . trim ( ) ; let conds = '' ; if ( part . length > 0 ) { const regex = /\s*([^;]*)/i ; let match = regex . exec ( part ) ; if ( ! match ) throw Error ( "Expecting 'ASSERTION expr'." ) ; conds = match [ 1 ] ; } conds = Utility . getConditionFromAssertion ( conds ) ; if ( conds . length > 0 ) { let bp = assertionMap . get ( entry . address ) ; if ( bp ) { bp . condition = '(' + bp . condition + ') || (' + conds + ')' ; } else { const assertionBp = { address : entry . address , condition : conds , log : undefined } ; assertionMap . set ( entry . address , assertionBp ) ; } } } catch ( e ) { console . log ( "Problem with ASSERTION. Could not evaluate: '" + entry . line + "': " + e . message + "" ) ; } } const assertionsArray = Array . from ( assertionMap . values ( ) ) ; return assertionsArray ; } param logPointLines return protected createLogPoints ( logPointLines ) { const logpoints = new Map < string , Array < GenericBreakpoint > > ( ) ; for ( let entry of logPointLines ) { const match = /^LOGPOINT\b(\s*\[\s*(\w*)\s*\])?\s*(.*)/gm . exec ( entry . line ) ; if ( match ) { const group = match [ 2 ] || "DEFAULT" ; const logMsg = '[' + group + '] ' + match [ 3 ] ; let array = logpoints . get ( group ) ; if ( ! array ) { array = new Array < GenericBreakpoint > ( ) ; logpoints . set ( group , array ) ; } try { const log = this . evalLogMessage ( logMsg ) ; array . push ( { address : entry . address , condition : '' , log : log } ) ; } catch ( e ) { console . log ( "Problem with LOGPOINT. Could not evaluate: '" + entry . line + "': " + e . message + "" ) ; } } } return logpoints ; } param logMsg returns public evalLogMessage ( logMsg ) { if ( ! logMsg ) return undefined ; const result = logMsg . replace ( /\${\s*(.*?)\s*}/g , ( match , inner ) => { const matchInner = /(([bw]@)?\s*\(\s*(.*?)\s*\)|(\w*)\s*)\s*(:\s*(unsigned|signed|hex|bits|flags))?\s*/i . exec ( inner ) ; if ( ! matchInner ) throw Error ( "Log message format error: '" + match + "' in '" + logMsg + "'" ) ; const end = ( matchInner [ 6 ] ) ? ':' + matchInner [ 6 ] : '' ; let addr = matchInner [ 3 ] || '' ; if ( addr . length ) { const access = matchInner [ 2 ] || '' ; if ( Z80RegistersClass . isRegister ( addr ) ) { return "${" + access + "(" + addr + ")" + end + "}" ; } else { try { const converted = Utility . evalExpression ( addr , false ) ; return "${" + access + "(" + converted . toString ( ) + ")" + end + "}" ; } catch ( e ) { throw Error ( "Log message format error: " + e . message + " in '" + logMsg + "'" ) ; } } } else { const reg = matchInner [ 4 ] ; if ( ! Z80RegistersClass . isRegister ( reg ) ) throw Error ( "Log message format error: Unsupported register '" + reg + "' in '" + logMsg + "'" ) ; return "${" + reg + end + "}" ; } } ) ; Log . log ( 'evalLog(point)Message: ' + result ) ; return result ; } param configuration public readListFiles ( configuration ) { Labels . readListFiles ( configuration ) ; const warnings = Labels . getWarnings ( ) ; if ( warnings ) this . emit ( 'warning' , warnings ) ; this . topOfStack = Labels . getNumberFromString64k ( Settings . launch . topOfStack ) ; if ( isNaN ( this . topOfStack ) ) throw Error ( "Cannot evaluate 'topOfStack' (" + Settings . launch . topOfStack + ")." ) ; if ( this . topOfStack == 0 ) this . topOfStack = 0x10000 ; } /**
     * Stops a remote.
     * This will e.g. disconnect the socket and un-use all data.
     * Called e.g. when vscode sends a disconnectRequest
     * Very much like 'terminate' but does not send the 'terminated' event.
     */ public async disconnect ( ) { } param message public async terminate ( message ? ) { await this . disconnect ( ) ; this . emit ( 'terminated' , message ) ; } /**
     * If cache is empty retrieves the registers from
     * the Remote.
     */ public async getRegistersFromEmulator ( ) { Utility . assert ( false ) ; } /**
     * Returns the PC value.
     */ public getPC ( ) { return Z80Registers . getRegValueByName ( "PC" ) ; } returns public getPCLong ( ) { const pcLong = Z80Registers . getPCLong ( ) ; return pcLong ; } param register public getRegisterValue ( register ) { const value = Z80Registers . getRegValueByName ( register ) ; return value ; } param value returns public getRegistersEqualTo ( value ) { let resRegs = [ ] ; if ( Z80Registers . valid ( ) ) { const regs = [ "HL" , "DE" , "IX" , "IY" , "SP" , "BC" , "HL'" , "DE'" , "BC'" ] ; resRegs = regs . filter ( reg => value == Z80Registers . getRegValueByName ( reg ) ) ; } return resRegs ; } param reg returns public getVarFormattedReg ( reg ) { return Z80Registers . getVarFormattedReg ( reg ) ; } param reg returns public getHoverFormattedReg ( reg ) { return Z80Registers . getHoverFormattedReg ( reg ) ; } param register param value return public async setRegisterValue ( register , value ) { Utility . assert ( false ) ; return 0 ; } param slot param bank public async setSlot ( slotIndex , bank ) { Utility . assert ( false ) ; } param stackEntryValue returns { name protected async getStackEntryType ( stackEntryValue ) { const addr = parseInt ( stackEntryValue , 16 ) ; const data = await this . readMemoryDump ( addr - 3 , 3 ) ; let calledAddr ; let callerAddr ; const opc3 = data [ 0 ] ; if ( opc3 == 0xCD || ( opc3 & 0b11000111 ) == 0b11000100 ) { calledAddr = ( data [ 2 ] << 8 ) + data [ 1 ] ; callerAddr = addr - 3 ; } else { } if ( calledAddr == undefined ) { return undefined ; } if ( Labels . AreLongAddressesUsed ( ) ) { const slots = this . getSlots ( ) ; callerAddr = Z80Registers . createLongAddress ( callerAddr , slots ) ; calledAddr = Z80Registers . createLongAddress ( calledAddr , slots ) ; } const labelCalledAddrArr = Labels . getLabelsForLongAddress ( calledAddr ) ; const labelCalledAddr = ( labelCalledAddrArr . length > 0 ) ? labelCalledAddrArr [ 0 ] : Utility . getHexString ( calledAddr & 0xFFFF , 4 ) + 'h' ; return { name : labelCalledAddr , callerAddr } ; } returns public async getStackFromEmulator ( ) { const sp = Z80Registers . getSP ( ) ; const tos = this . topOfStack ; let depth = tos - sp ; if ( depth > 2 * RemoteBase . MAX_STACK_ITEMS ) depth = 2 * RemoteBase . MAX_STACK_ITEMS ; const zStack = [ ] ; if ( depth > 0 ) { const data = await this . readMemoryDump ( sp , depth ) ; for ( let i = depth - 2 ; i >= 0 ; i -= 2 ) { const value = ( data [ i + 1 ] << 8 ) + data [ i ] ; zStack . push ( Utility . getHexString ( value , 4 ) ) ; } } return zStack ; } /**
     * Retrieves the stack from the emulator and filters all CALL addresses.
     */ public async getCallStackFromEmulator ( ) { const callStack = new RefList < CallStackFrame > ( ) ; const stack = await this . getStackFromEmulator ( ) ; const sp = Z80Registers . getRegValue ( Z80_REG . SP ) ; const len = stack . length ; const top = sp + 2 * len ; let lastCallStackFrame = new CallStackFrame ( 0 , top - 2 , this . getMainName ( top ) ) ; callStack . addObject ( lastCallStackFrame ) ; for ( let i = 0 ; i < len ; i ++ ) { const valueString = stack [ i ] ; const type = await this . getStackEntryType ( valueString ) ; if ( type ) { lastCallStackFrame . addr = type . callerAddr ; const frameSP = top - 2 - 2 * ( i + 1 ) ; lastCallStackFrame = new CallStackFrame ( 0 , frameSP , type . name ) ; callStack . addObject ( lastCallStackFrame ) ; } else { lastCallStackFrame . stack . push ( parseInt ( valueString , 16 ) ) ; } } const pc = this . getPCLong ( ) ; lastCallStackFrame . addr = pc ; this . listFrames = callStack ; } returns public async getCallStackCache ( ) { return this . listFrames ; } /**
     * Returns the name of the interrupt.
     */ public getInterruptName ( ) { return "__INTERRUPT__" ; } param sp returns public getMainName ( sp ) { let part = "" ; if ( this . topOfStack ) { const diff = this . topOfStack - sp ; if ( diff != 0 ) { if ( diff > 0 ) part = "+" ; part += diff . toString ( ) ; } } return "__MAIN" + part + "__" ; } param ref returns public getFrame ( ref ) { const frame = this . listFrames . getObject ( ref ) ; return frame ; } returns public async continue ( ) { Utility . assert ( false ) ; return '' ; } /**
     * 'pause' the debugger.
     */ public async pause ( ) { Utility . assert ( false ) ; } returns public async reverseContinue ( ) { Utility . assert ( false ) ; return "" ; } returns public async stepOver ( ) { Utility . assert ( false ) ; return undefined ; } returns public async stepInto ( ) { Utility . assert ( false ) ; return undefined ; } returns public async stepOut ( ) { Utility . assert ( false ) ; return undefined ; } param watchPoints public setWPMEMArray ( watchPoints ) { this . wpmemWatchpoints = [ ... watchPoints ] ; } param enable public async enableWPMEM ( enable ) { for ( let wp of this . wpmemWatchpoints ) { if ( enable ) await this . setWatchpoint ( wp ) ; else await this . removeWatchpoint ( wp ) ; } this . wpmemEnabled = enable ; } param wp public async setWatchpoint ( wp ) { Utility . assert ( false ) ; } param wp public async removeWatchpoint ( wp ) { Utility . assert ( false ) ; } /**
     * Returns the WPMEM watchpoints.
     */ public getAllWpmemWatchpoints ( ) { return this . wpmemWatchpoints ; } param assertionBreakpoints public setASSERTIONArray ( assertionBreakpoints ) { this . assertionBreakpoints = [ ... assertionBreakpoints ] ; } param enable public async enableAssertionBreakpoints ( enable ) { Utility . assert ( false ) ; } /**
     * Returns the ASSERTION breakpoints.
     */ public getAllAssertionBreakpoints ( ) { return this . assertionBreakpoints ; } param logpoints public setLOGPOINTArray ( logpoints ) { this . logpoints = logpoints ; this . logpointsEnabled = new Map < string , boolean > ( ) ; for ( const [ group ] of this . logpoints ) { this . logpointsEnabled . set ( group , false ) ; } } param logpoints param enable returns public async enableLogpoints ( logpoints , enable ) { Utility . assert ( false ) ; } param group param enable public async enableLogpointGroup ( group , enable ) { let lPoints ; if ( group ) { const array = this . logpoints . get ( group ) ; if ( ! array ) throw Error ( "Group '" + group + "' unknown." ) ; lPoints = new Map < string , GenericBreakpoint [ ] > ( [ [ group , array ] ] ) ; } else { lPoints = this . logpoints ; } for ( const [ grp , arr ] of lPoints ) { await this . enableLogpoints ( arr , enable ) ; this . logpointsEnabled . set ( grp , enable ) ; } } returns protected getEnabledLogpoints ( ) { const result = new Array < GenericBreakpoint > ( ) ; for ( const [ grp , arr ] of this . logpoints ) { const enabled = this . logpointsEnabled . get ( grp ) ; if ( enabled ) result . push ( ... arr ) ; } return result ; } param bp returns public async setBreakpoint ( bp ) { Utility . assert ( false ) ; return 0 ; } /**
     * Clears one breakpoint.
     * Breakpoint is removed at the Remote and removed from the 'breakpoints' array.
     * Also used by unit tests.
     */ public async removeBreakpoint ( bp ) { Utility . assert ( false ) ; } param path param givenBps param tmpDisasmFileHandler returns public async setBreakpoints ( path , givenBps ) { try { const oldBps = this . breakpoints . filter ( bp => bp . filePath == path ) ; const currentBps = new Array < RemoteBreakpoint > ( ) ; givenBps . forEach ( bp => { let ebp ; let error ; let addr = this . getAddrForFileAndLine ( path , bp . lineNr ) ; if ( addr >= 0 ) { const file = this . getFileAndLineForAddress ( addr ) ; if ( path . valueOf ( ) == file . fileName . valueOf ( ) ) { ebp = { bpId : 0 , filePath : file . fileName , lineNr : file . lineNr , address : addr , condition : bp . condition , log : bp . log } ; } else error = "File " + file . fileName + " found at address " + Utility . getHexString ( addr , 4 ) + "h" ; } else { error = "Address not found for " + path ; } if ( ! ebp ) { ebp = { bpId : 0 , filePath : path , lineNr : bp . lineNr , address : - 1 , condition : '' , log : undefined , error } ; } currentBps . push ( ebp ) ; } ) ; const newBps = currentBps . filter ( bp => bp . address >= 0 && oldBps . filter ( obp => ( obp . condition == bp . condition ) && ( obp . log == bp . log ) && ( obp . address == bp . address ) ) . length == 0 ) ; const removedBps = oldBps . filter ( bp => bp . address >= 0 && currentBps . filter ( obp => ( obp . condition == bp . condition ) && ( obp . log == bp . log ) && ( obp . address == bp . address ) ) . length == 0 ) ; try { for ( const bp of removedBps ) { await this . removeBreakpoint ( bp ) ; } for ( const bp of newBps ) { await this . setBreakpoint ( bp ) ; } } catch { return oldBps ; } return currentBps ; } catch ( e ) { throw e ; } } /**
     * Returns the remote breakpoints (PC breakpoint) array.
     */ public getBreakpointsArray ( ) { return this . breakpoints ; } param address returns public getFileAndLineForAddress ( address ) { let file = Labels . getFileAndLineForAddress ( address ) ; if ( ! file . fileName ) { const lineNr = Disassembly . getLineForAddress ( address ) ; if ( lineNr != undefined ) { file . fileName = DisassemblyClass . getAbsFilePath ( ) ; file . lineNr = lineNr ; } } return file ; } param fileName param lineNr returns public getAddrForFileAndLine ( fileName , lineNr ) { let addr = Labels . getAddrForFileAndLine ( fileName , lineNr ) ; if ( addr < 0 ) { const absFilePath = DisassemblyClass . getAbsFilePath ( ) ; if ( fileName == absFilePath ) { addr = Disassembly . getAddressForLine ( lineNr ) ; } } return addr ; } param cmd returns public async dbgExec ( cmd ) { return "Error: not supported." ; } param address param size param handler public async readMemoryDump ( address , size ) { Utility . assert ( false ) ; return new Uint8Array ( ) ; } param address param dataArray public async writeMemoryDump ( address , dataArray ) { Utility . assert ( false ) ; } param address returns public async readMemory ( address ) { const realValue = await this . readMemoryDump ( address , 1 ) ; return realValue [ 0 ] ; } param address param value returns public async writeMemory ( address , value ) { const data = new Uint8Array ( [ value ] ) ; await this . writeMemoryDump ( address , data ) ; const realValue = await this . readMemory ( address ) ; return realValue ; } returns public async getMemoryBanks ( ) { const slots = this . getSlots ( ) ; const pages = this . memoryModel . getMemoryBanks ( slots ) ; return pages ; } returns public getSlots ( ) { return Z80Registers . getSlots ( ) ; } /**
     * Resets the T-States counter. Used before stepping to measure the
     * time.
     */ public async resetTstates ( ) { } returns public async getTstates ( ) { return 0 ; } returns public async getCpuFrequency ( ) { return 0 ; } /**
     * This method is called by the DebugSessionClass before a step (stepOver, stepInto, stepOut,
     * continue, stepBack, etc.) is called.
     * It can be overridden e.g. to clear/initialize some stuff
     * e.g. coverage.
     */ public startProcessing ( ) { } /**
     * This method is called by the DebugSessionClass after a step (stepOver, stepInto, stepOut,
     * continue, stepBack, etc.) is called.
     * It can be overridden e.g. to do something at the end of a step.
     * E.g. emit coverage.
     */ public stopProcessing ( ) { } param registerNr returns public async getTbblueRegister ( registerNr ) { return 0 ; } param paletteNr returns public async getTbblueSpritesPalette ( paletteNr ) { return [ ] ; } returns public async getTbblueSpritesClippingWindow ( ) { return { xl : 0 , xr : 0 , yt : 0 , yb : 0 , control : 0 } ; } param slot param count returns public async getTbblueSprites ( slot , count ) { return [ ] ; } param index param count preturns public async getTbblueSpritePatterns ( index , count ) { return [ ] ; } param timeout returns public async waitForBeingQuietFor ( timeout ) { } param filePath returns public async stateSave ( filePath ) { } param filePath public async stateRestore ( filePath ) { } param stepOver returns protected async calcStepBp ( stepOver ) { const pc = this . getPC ( ) ; const opcodes = await this . readMemoryDump ( pc , 4 ) ; const buffer = new BaseMemory ( pc , opcodes ) ; const opcode = Opcode . getOpcodeAt ( buffer , pc ) ; let bpAddr1 = pc + opcode . length ; let bpAddr2 ; const ocFlags = opcode . flags ; if ( ocFlags & OpcodeFlag . BRANCH_ADDRESS && ( ocFlags & OpcodeFlag . CONDITIONAL ) == 0 && opcode . code == 0xCF ) { if ( stepOver ) { bpAddr1 = pc + 1 ; bpAddr2 = bpAddr1 + 1 ; } else { bpAddr2 = 0x0008 ; bpAddr1 = pc + 2 ; } } else if ( ocFlags & OpcodeFlag . RET ) { const sp = this . getRegisterValue ( "SP" ) ; const retArr = await this . readMemoryDump ( sp , 2 ) ; const retAddr = retArr [ 0 ] + ( retArr [ 1 ] << 8 ) ; if ( ocFlags & OpcodeFlag . CONDITIONAL ) bpAddr2 = retAddr ; else bpAddr1 = retAddr ; } else if ( stepOver && ( ocFlags & OpcodeFlag . CALL ) ) { } else if ( ocFlags & OpcodeFlag . BRANCH_ADDRESS ) { if ( ocFlags & OpcodeFlag . CONDITIONAL ) { bpAddr2 = opcode . value ; } else { bpAddr1 = opcode . value ; } } else if ( ocFlags & OpcodeFlag . STOP ) { if ( opcodes [ 0 ] == 0xE9 ) { bpAddr1 = this . getRegisterValue ( "HL" ) ; } else if ( opcodes [ 0 ] == 0xDD && opcodes [ 1 ] == 0xE9 ) { bpAddr1 = this . getRegisterValue ( "IX" ) ; } else if ( opcodes [ 0 ] == 0xFD && opcodes [ 1 ] == 0xE9 ) { bpAddr1 = this . getRegisterValue ( "IY" ) ; } } else { if ( opcodes [ 0 ] == 0xED ) { if ( opcodes [ 1 ] == 0xB0 || opcodes [ 1 ] == 0xB8 || opcodes [ 1 ] == 0xB1 || opcodes [ 1 ] == 0xB9 || opcodes [ 1 ] == 0xB2 || opcodes [ 1 ] == 0xBA || opcodes [ 1 ] == 0xB3 || opcodes [ 1 ] == 0xBB ) { if ( ! stepOver ) bpAddr2 = pc ; } } else if ( opcodes [ 0 ] == 0x76 ) { if ( ! stepOver ) bpAddr2 = pc ; } } bpAddr1 &= 0xFFFF ; if ( bpAddr2 ) bpAddr2 &= 0xFFFF ; return [ opcode , bpAddr1 , bpAddr2 ] ; } } export let Remote ;