import { zSocket , ZesaruxSocket } from './zesaruxsocket' ; import { Utility } from '../../misc/utility' ; import { Labels } from '../../labels/labels' ; import { Settings } from '../../settings' ; import { GenericWatchpoint , GenericBreakpoint } from '../../genericwatchpoint' ; import { RemoteBase , RemoteBreakpoint } from '../remotebase' ; import { ZesaruxCpuHistory , DecodeZesaruxHistoryInfo } from './zesaruxcpuhistory' ; import { Z80RegistersClass , Z80Registers } from '../z80registers' ; import { DecodeZesaruxRegisters } from './decodezesaruxdata' ; import { CpuHistory , CpuHistoryClass } from '../cpuhistory' ; import { MemoryModel , Zx128MemoryModel , ZxNextMemoryModel } from '../Paging/memorymodel' ; import { PromiseCallbacks } from '../../misc/promisecallbacks' ; const MIN_ZESARUX_VERSION = 8.1 ; class Zesarux { static MAX_ZESARUX_BREAKPOINTS = 100 ; static MAX_BREAKPOINT_CONDITION_LENGTH = 256 ; static MAX_MESSAGE_CATCH_BREAKPOINT = 4 * 32 - 1 ; } /**
 * The representation of the ZEsarUX emulator.
 * It receives the requests from the DebugAdapter and communicates with
 * the ZesaruxSocket.
 */ export class ZesaruxRemote extends RemoteBase { static MAX_USED_BREAKPOINTS = Zesarux . MAX_ZESARUX_BREAKPOINTS - 1 ; static STEP_BREAKPOINT_ID = 100 ; protected continueResolve ? : PromiseCallbacks < string | undefined > ; private freeBreakpointIds = new Array < number > ( ) ; public zesaruxVersion = 0.0 ; protected terminating = false ; constructor ( ) { super ( ) ; CpuHistoryClass . setCpuHistory ( new ZesaruxCpuHistory ( ) ) ; CpuHistory . decoder = new DecodeZesaruxHistoryInfo ( ) ; } /**
     * Checks if there still is an open promise and runs it.
     */ public dispose ( ) { if ( this . continueResolve ) { this . continueResolve . resolve ( '' ) ; this . continueResolve = undefined ; } super . dispose ( ) ; } public async doInitialization ( ) { this . setupSocket ( ) ; zSocket . connectDebugger ( ) ; } param handler public async disconnect ( ) { if ( ! zSocket ) return ; return new Promise < void > ( resolve => { zSocket . quit ( ( ) => { resolve ( ) ; } ) ; } ) ; } param event public removeAllListeners ( event ? ) { super . removeAllListeners ( ) ; zSocket ?. removeAllListeners ( ) ; return this ; } /**
     * Initializes the socket to zesarux but does not connect yet.
     * Installs handlers to react on connect and error.
     */ protected setupSocket ( ) { ZesaruxSocket . Init ( ) ; zSocket . on ( 'log' , msg => { this . emit ( 'debug_console' , "Log: " + msg ) ; } ) ; zSocket . on ( 'warning' , msg => { if ( this . terminating ) return ; msg = "ZEsarUX: " + msg ; this . emit ( 'warning' , msg ) ; } ) ; zSocket . on ( 'error' , err => { if ( this . terminating ) return ; err . message += " (Error in connection to ZEsarUX!)" ; this . emit ( 'error' , err ) ; } ) ; zSocket . on ( 'close' , ( ) => { if ( this . terminating ) return ; this . listFrames . clear ( ) ; this . breakpoints . length = 0 ; const err = new Error ( 'ZEsarUX terminated the connection!' ) ; this . emit ( 'error' , err ) ; } ) ; zSocket . on ( 'end' , ( ) => { if ( this . terminating ) return ; const err = new Error ( 'ZEsarUX terminated the connection!' ) ; this . emit ( 'error' , err ) ; } ) ; zSocket . on ( 'connected' , async ( ) => { if ( this . terminating ) return ; try { zSocket . send ( 'about' ) ; zSocket . send ( 'get-version' , data => { this . zesaruxVersion = parseFloat ( data ) ; if ( this . zesaruxVersion < MIN_ZESARUX_VERSION ) { zSocket . quit ( ) ; const err = new Error ( 'Please update ZEsarUX. Need at least version ' + MIN_ZESARUX_VERSION + '.' ) ; this . emit ( 'error' , err ) ; } } ) ; this . zesaruxConnected ( ) ; await zSocket . executeWhenQueueIsEmpty ( ) ; const debug_settings = ( Settings . launch . zrcp . skipInterrupt ) ? 32 : 0 ; await zSocket . sendAwait ( 'set-debug-settings ' + debug_settings ) ; if ( Settings . launch . zrcp . resetOnLaunch ) await zSocket . sendAwait ( 'hard-reset-cpu' ) ; await zSocket . sendAwait ( 'enter-cpu-step' ) ; const waitBeforeMs = Settings . launch . zrcp . loadDelay ; await Utility . timeout ( waitBeforeMs ) ; await this . loadExecutable ( ) ; const mtResp = await zSocket . sendAwait ( 'get-current-machine' ) as string ; const machineType = mtResp . toLowerCase ( ) ; if ( machineType . indexOf ( "tbblue" ) >= 0 || machineType . indexOf ( "zx spectrum next" ) >= 0 ) { Z80Registers . decoder = new DecodeZesaruxRegisters ( 8 ) ; this . memoryModel = new ZxNextMemoryModel ( ) ; } else if ( machineType . indexOf ( "128k" ) >= 0 ) { Z80Registers . decoder = new DecodeZesaruxRegisters ( 4 ) ; this . memoryModel = new Zx128MemoryModel ( ) ; } else { Z80Registers . decoder = new DecodeZesaruxRegisters ( 0 ) ; this . memoryModel = new MemoryModel ( ) ; } this . memoryModel . init ( ) ; Labels . convertLabelsTo ( this . memoryModel ) ; this . setLaunchExecAddress ( ) ; await this . initAfterLoad ( ) ; this . emit ( 'initialized' ) ; } catch ( e ) { this . emit ( 'error' , e ) ; } } ) ; } /**
     * Does the initialization necessary after a load or state restore.
     */ protected async initAfterLoad ( ) { this . initBreakpoints ( ) ; if ( Settings . launch . history . codeCoverageEnabled ) { await zSocket . sendAwait ( 'cpu-code-coverage enabled yes' , true ) ; await zSocket . sendAwait ( 'cpu-code-coverage clear' ) ; } else await zSocket . sendAwait ( 'cpu-code-coverage enabled no' , true ) ; CpuHistory . init ( ) ; await zSocket . sendAwait ( 'extended-stack enabled no' , true ) ; await zSocket . sendAwait ( 'extended-stack enabled yes' ) ; } /**
     * Is called right after Zesarux has been connected and the version info was read.
     * Can be overridden to check for extensions.
     */ protected zesaruxConnected ( ) { } /**
     * Initializes the zesarux breakpoints.
     * Override this if fast-breakpoints should be used.
     */ protected initBreakpoints ( ) { zSocket . send ( 'clear-membreakpoints' ) ; zSocket . send ( 'enable-breakpoints' , ( ) => { } , true ) ; this . clearAllZesaruxBreakpoints ( ) ; this . freeBreakpointIds . length = 0 ; for ( let i = ZesaruxRemote . MAX_USED_BREAKPOINTS ; i > 0 ; i -- ) this . freeBreakpointIds . push ( i ) ; } /**
     * Retrieves the slots from zesarux directly.
     */ protected async getSlotsFromEmulator ( ) { Utility . assert ( CpuHistory ) ; Utility . assert ( ! CpuHistory . isInStepBackMode ( ) ) ; const slotsString = await zSocket . sendAwait ( 'get-memory-pages' ) ; const slotsStringArray = slotsString . split ( ' ' ) ; let slots ; let count = slotsStringArray . length - 1 ; switch ( count ) { case 4 : for ( let i = 0 ; i < count ; i ++ ) slotsStringArray [ i ] = slotsStringArray [ i ] . substring ( 1 ) ; case 8 : slots = new Array < number > ( count ) ; for ( let i = 0 ; i < count ; i ++ ) { const bankString = slotsStringArray [ i ] ; const type = bankString . substring ( 0 , 1 ) ; const rest = bankString . substring ( 1 ) ; let bankNumber = parseInt ( rest ) ; if ( type == 'O' ) { bankNumber += 0xFE ; } slots [ i ] = bankNumber ; } break ; default : slots = new Array < number > ( count ) ; break ; } return slots ; } /**
     * If cache is empty retrieves the registers from
     * the Remote.
     */ public async getRegistersFromEmulator ( ) { Utility . assert ( CpuHistory ) ; Utility . assert ( ! CpuHistory . isInStepBackMode ( ) ) ; const data = await zSocket . sendAwait ( 'get-registers' ) ; Z80Registers . setCache ( data ) ; } param register param value return public async setRegisterValue ( register , value ) { return new Promise < number > ( resolve => { zSocket . send ( 'set-register ' + register + '=' + value , data => { this . getRegistersFromEmulator ( ) . then ( ( ) => { const realValue = this . getRegisterValue ( register ) ; resolve ( realValue ) ; } ) ; } ) ; } ) ; } param slot param bank public async setSlot ( slotIndex , bank ) { await zSocket . sendAwait ( 'tbblue-set-register ' + ( 0x50 + slotIndex ) + ' ' + bank ) ; } param stackEntryValue returns { name protected getStackEntryType ( stackEntryValue ) { const type = stackEntryValue . substring ( 5 ) ; if ( type == 'call' || type == 'rst' ) { return super . getStackEntryType ( stackEntryValue ) ; } return new Promise < { name : string ; callerAddr : number ; } | undefined > ( resolve => { if ( type . includes ( 'interrupt' ) ) { const retAddr = parseInt ( stackEntryValue , 16 ) ; resolve ( { name : this . getInterruptName ( ) , callerAddr : retAddr } ) ; } else { resolve ( undefined ) ; } } ) ; } returns public async getStackFromEmulator ( ) { return new Promise < Array < string > > ( async ( resolve ) => { const stack = await super . getStackFromEmulator ( ) ; const depth = stack . length ; if ( depth == 0 ) { resolve ( stack ) ; return ; } zSocket . send ( 'extended-stack get ' + depth , ( data ) => { data = data . replace ( /\r/gm , "" ) ; const zStack = data . split ( '\n' ) ; let len = zStack . length - 1 ; zStack . splice ( len ) ; if ( depth < len ) len = depth ; for ( let i = 0 ; i < len ; i ++ ) { const type = zStack [ i ] . substring ( 5 ) ; stack [ depth - 1 - i ] += type ; } resolve ( stack ) ; } ) ; } ) ; } returns public async continue ( ) { return new Promise < string > ( resolve => { Utility . assert ( ! this . continueResolve ) ; this . continueResolve = new PromiseCallbacks < string > ( this , 'continueResolve' , resolve ) ; zSocket . sendInterruptableRunCmd ( async ( text ) => { await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; this . handleCodeCoverage ( ) ; let breakReasonString = this . getBreakReason ( text ) ; const pcLong = this . getPCLong ( ) ; const abps = this . assertionBreakpoints . filter ( abp => abp . address == pcLong ) ; for ( const abp of abps ) { let conditionTrue = true ; if ( abp . condition != undefined ) { try { const evalCond = Utility . evalExpression ( abp . condition , true ) ; conditionTrue = ( evalCond != 0 ) ; } catch ( e ) { } } if ( conditionTrue ) { const assertionCond = Utility . getAssertionFromCondition ( abp . condition ) ; const replaced = Utility . replaceVarsWithValues ( assertionCond ) ; breakReasonString = "Assertion failed: " + replaced ; } } await CpuHistory . getHistorySpotFromRemote ( ) ; this . continueResolve ! . resolve ( breakReasonString ) ; } ) ; } ) ; } param text returns protected getBreakReason ( text ) { let result ; const textArray = text . split ( '\n' ) ; for ( const reason of textArray ) if ( ( reason . indexOf ( 'point hit' ) >= 0 ) || ( reason . indexOf ( 'point fired' ) >= 0 ) ) { result = reason ; break ; } return result ; } /**
      * 'pause' the debugger.
      */ public async pause ( ) { zSocket . sendBlank ( ) ; } returns public async stepOver ( ) { return new Promise < string | undefined > ( resolve => { Utility . assert ( ! this . continueResolve ) ; this . continueResolve = new PromiseCallbacks < string > ( this , 'continueResolve' , resolve ) ; const pc = Z80Registers . getPC ( ) ; zSocket . send ( 'disassemble ' + pc , ( disasm ) => { const opcode = disasm . substr ( 7 , 4 ) ; if ( opcode == "RST " || opcode == "CALL" ) { const sp = Z80Registers . getSP ( ) ; const condition = 'SP>=' + sp ; const bpId = ZesaruxRemote . STEP_BREAKPOINT_ID ; zSocket . send ( 'set-breakpointaction ' + bpId + ' prints step-over' , ( ) => { zSocket . send ( 'set-breakpoint ' + bpId + ' ' + condition , ( ) => { zSocket . send ( 'enable-breakpoint ' + bpId , ( ) => { zSocket . sendInterruptableRunCmd ( async ( text ) => { await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; this . handleCodeCoverage ( ) ; const breakReasonString = this . getBreakReason ( text ) ; zSocket . send ( 'disable-breakpoint ' + bpId , async ( ) => { await CpuHistory . getHistorySpotFromRemote ( ) ; this . continueResolve ! . resolve ( breakReasonString ) ; } ) ; } ) ; } ) ; } ) ; } ) ; } else { const cmd = ( opcode == "LDIR" || opcode == "LDDR" || opcode == "CPIR" || opcode == "CPDR" ) ? 'cpu-step-over' : 'cpu-step' ; zSocket . send ( cmd , async ( result ) => { await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; this . handleCodeCoverage ( ) ; const breakReasonString = this . getBreakReason ( result ) ; await CpuHistory . getHistorySpotFromRemote ( ) ; this . continueResolve ! . resolve ( breakReasonString ) ; } ) ; } } ) ; } ) ; } returns public async stepInto ( ) { return new Promise < string | undefined > ( resolve => { Utility . assert ( ! this . continueResolve ) ; this . continueResolve = new PromiseCallbacks < string > ( this , 'continueResolve' , resolve ) ; zSocket . send ( 'cpu-step' , async ( result ) => { await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; this . handleCodeCoverage ( ) ; await CpuHistory . getHistorySpotFromRemote ( ) ; this . continueResolve ! . resolve ( undefined ) ; } ) ; } ) ; } /**
     * Resets the T-States counter. Used before stepping to measure the
     * time.
     */ public async resetTstates ( ) { return new Promise < void > ( resolve => { zSocket . send ( 'reset-tstates-partial' , data => { resolve ( ) ; } ) ; } ) ; } returns public async getTstates ( ) { return new Promise < number > ( resolve => { zSocket . send ( 'get-tstates-partial' , data => { const tStates = parseInt ( data ) ; resolve ( tStates ) ; } ) ; } ) ; } returns public async getCpuFrequency ( ) { return new Promise < number > ( resolve => { zSocket . send ( 'get-cpu-frequency' , data => { const cpuFreq = parseInt ( data ) ; resolve ( cpuFreq ) ; } ) ; } ) ; } /**
     * Reads the coverage addresses and clears them in ZEsarUX.
     */ protected handleCodeCoverage ( ) { if ( ! Settings . launch . history . codeCoverageEnabled ) return ; zSocket . send ( 'cpu-code-coverage get' , data => { if ( data . startsWith ( 'Error' ) ) return ; const slots = Z80Registers . getSlots ( ) ; const addresses = new Set < number > ( ) ; const length = data . length ; for ( let k = 0 ; k < length ; k += 5 ) { const addressString = data . substr ( k , 4 ) ; const address = parseInt ( addressString , 16 ) ; const longAddress = Z80Registers . createLongAddress ( address , slots ) ; addresses . add ( longAddress ) ; } zSocket . send ( 'cpu-code-coverage clear' ) ; this . emit ( 'coverage' , addresses ) ; } ) ; } returns public async stepOut ( ) { return new Promise < string | undefined > ( resolve => { Utility . assert ( ! this . continueResolve ) ; this . continueResolve = new PromiseCallbacks < string > ( this , 'continueResolve' , resolve ) ; const sp = Z80Registers . getSP ( ) ; let depth = this . topOfStack - sp ; if ( depth > ZesaruxRemote . MAX_STACK_ITEMS ) depth = ZesaruxRemote . MAX_STACK_ITEMS ; if ( depth == 0 ) { this . continueResolve . resolve ( "Call stack empty" ) ; return ; } else if ( depth < 0 ) { this . continueResolve . resolve ( "SP above topOfStack. Stack corrupted?" ) ; return ; } zSocket . send ( 'extended-stack get ' + depth , ( data ) => { data = data . replace ( /\r/gm , "" ) ; const zStack = data . split ( '\n' ) ; zStack . splice ( zStack . length - 1 ) ; let bpSp = sp ; for ( const addrTypeString of zStack ) { bpSp += 2 ; const type = addrTypeString . substring ( 6 ) ; if ( type == "call" || type == "rst" || type . includes ( "interrupt" ) ) { const bpId = ZesaruxRemote . STEP_BREAKPOINT_ID ; zSocket . send ( 'set-breakpointaction ' + bpId + ' prints step-out' , ( ) => { const condition = 'PC=PEEKW(SP-2) AND SP>=' + bpSp ; zSocket . send ( 'set-breakpoint ' + bpId + ' ' + condition , ( ) => { zSocket . send ( 'enable-breakpoint ' + bpId , ( ) => { zSocket . sendInterruptableRunCmd ( async ( text ) => { await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; this . handleCodeCoverage ( ) ; const breakReasonString = this . getBreakReason ( text ) ; zSocket . send ( 'disable-breakpoint ' + bpId , async ( ) => { await CpuHistory . getHistorySpotFromRemote ( ) ; this . continueResolve ! . resolve ( breakReasonString ) ; } ) ; } ) ; } ) ; } ) ; } ) ; return ; } } this . continueResolve ! . resolve ( undefined ) ; } ) ; } ) ; } param wp public async setWatchpoint ( wp ) { return new Promise < void > ( resolve => { if ( wp . condition && wp . condition . length > 0 ) { } else { let type = 0 ; if ( wp . access . indexOf ( 'r' ) >= 0 ) type |= 0x01 ; if ( wp . access . indexOf ( 'w' ) >= 0 ) type |= 0x02 ; const size = wp . size ; let addr = wp . address & 0xFFFF ; zSocket . send ( 'set-membreakpoint ' + addr . toString ( 16 ) + 'h ' + type + ' ' + size ) ; } zSocket . executeWhenQueueIsEmpty ( ) . then ( resolve ) ; } ) ; } param wp public async removeWatchpoint ( wp ) { return new Promise < void > ( resolve => { const size = wp . size ; let addr = wp . address & 0xFFFF ; zSocket . send ( 'set-membreakpoint ' + addr . toString ( 16 ) + 'h 0 ' + size ) ; zSocket . executeWhenQueueIsEmpty ( ) . then ( resolve ) ; } ) ; } param enable public async enableAssertionBreakpoints ( enable ) { if ( enable ) { for ( let abp of this . assertionBreakpoints ) { if ( ! abp . bpId ) { abp . bpId = await this . setBreakpointZesarux ( abp . address , abp . condition ) ; } } } else { for ( let i = this . assertionBreakpoints . length - 1 ; i >= 0 ; i -- ) { const abp = this . assertionBreakpoints [ i ] ; if ( abp . bpId ) { await this . removeBreakpointZesarux ( abp . bpId ) ; abp . bpId = undefined ; } } } this . assertionBreakpointsEnabled = enable ; } param logpoints param enable public async enableLogpoints ( logpoints , enable ) { Utility . assert ( false ) ; } param group param enable public async enableLogpointGroup ( group , enable ) { if ( this . logpoints . size > 0 ) this . emit ( 'warning' , 'ZEsarUX does not support logpoints.' ) ; } param condition returns protected convertCondition ( condition ? ) { if ( ! condition || condition . length == 0 ) return '' ; if ( condition == '!(false)' ) return '' ; let regex = /\b[_a-z][\.0-9a-z_]*\b/gi ; let conds = condition . replace ( regex , label => { if ( Z80RegistersClass . isRegister ( label ) ) return label ; let addr = Labels . getNumberForLabel ( label ) ; if ( addr == undefined ) return label ; addr &= 0xFFFF ; return addr . toString ( ) ; } ) ; conds = conds . replace ( /==/g , '=' ) ; conds = conds . replace ( /!=/g , '<>' ) ; conds = conds . replace ( /&&/g , ' AND ' ) ; conds = conds . replace ( /\|\|/g , ' OR ' ) ; conds = conds . replace ( /==/g , '=' ) ; conds = conds . replace ( /!/g , 'NOT' ) ; conds = conds . replace ( /0x[0-9a-f]+/gi , value => { const valh = value . substring ( 2 ) + 'H' ; return valh ; } ) ; return conds ; } param address param bpCondition returns public async setBreakpointZesarux ( address , bpCondition ? ) { let zesaruxCondition = this . convertCondition ( bpCondition ) ; if ( zesaruxCondition == undefined ) { this . emit ( 'warning' , "Breakpoint: Can't set condition: " + ( bpCondition || '' ) ) ; return 0 ; } const bpId = this . freeBreakpointIds . pop ( ) ; if ( bpId == undefined ) return 0 ; let condition = '' ; if ( address >= 0 ) { condition = 'PC=0' + Utility . getHexString ( address & 0xFFFF , 4 ) + 'h' ; let bank = Z80Registers . getBankFromAddress ( address ) ; if ( bank != - 1 ) { const className = this . memoryModel . constructor . name ; if ( className == "Zx128MemoryModel" ) { const addr = address & 0xFFFF ; if ( addr <= 0x3FFF ) { bank = ( bank & 0x01 ) ; condition += ' and ROM=' + bank ; } else if ( addr >= 0xC000 ) { condition += ' and RAM=' + bank ; } } else { const slot = Z80Registers . getSlotFromAddress ( address ) ; if ( bank == 0xFE ) bank = 0x8000 ; else if ( bank == 0xFF ) bank = 0x8001 ; condition += ' and SEG' + slot + '=' + bank ; } } if ( zesaruxCondition . length > 0 ) { condition += ' and ' ; zesaruxCondition = '(' + zesaruxCondition + ')' ; } } if ( zesaruxCondition . length > 0 ) condition += zesaruxCondition ; const shortCond = ( condition . length < 50 ) ? condition : condition . substring ( 0 , 50 ) + '...' ; await zSocket . sendAwait ( 'set-breakpointaction ' + bpId + ' prints breakpoint ' + bpId + ' hit (' + shortCond + ')' ) ; await zSocket . sendAwait ( 'set-breakpoint ' + bpId + ' ' + condition ) ; await zSocket . sendAwait ( 'enable-breakpoint ' + bpId ) ; return bpId ; } param bpId protected async removeBreakpointZesarux ( bpId ) { await zSocket . sendAwait ( 'disable-breakpoint ' + bpId ) ; this . freeBreakpointIds . push ( bpId ) ; } public async setBreakpoint ( bp ) { if ( bp . log ) { this . emit ( 'warning' , 'ZEsarUX does not support logpoints ("' + bp . log + '").' ) ; bp . address = - 1 ; return 0 ; } const bpId = await this . setBreakpointZesarux ( bp . address , bp . condition ) ; if ( bpId <= 0 ) { bp . address = - 1 ; return 0 ; } bp . bpId = bpId ; this . breakpoints . push ( bp ) ; return bp . bpId ; } /**
     * Clears one breakpoint.
     */ public async removeBreakpoint ( bp ) { const bpId = bp . bpId ; if ( ! bpId ) return ; await this . removeBreakpointZesarux ( bpId ) ; let index = this . breakpoints . indexOf ( bp ) ; Utility . assert ( index !== - 1 , 'Breakpoint should be removed but does not exist.' ) ; this . breakpoints . splice ( index , 1 ) ; } /**
     * Disables all breakpoints set in zesarux on startup.
     */ protected clearAllZesaruxBreakpoints ( ) { for ( let i = 1 ; i <= Zesarux . MAX_ZESARUX_BREAKPOINTS ; i ++ ) { zSocket . send ( 'disable-breakpoint ' + i ) ; } } param path param givenBps param tmpDisasmFileHandler returns public async setBreakpoints ( path , givenBps ) { const bps = await super . setBreakpoints ( path , givenBps ) ; await zSocket . executeWhenQueueIsEmpty ( ) ; return bps ; } param cmd returns public async dbgExec ( cmd ) { cmd = cmd . trim ( ) ; if ( cmd . length == 0 ) { throw new Error ( 'No command given.' ) ; } return new Promise < string > ( resolve => { zSocket . send ( cmd , data => { resolve ( data ) ; } ) ; } ) ; } param address param size param handler public async readMemoryDump ( address , size ) { return new Promise < Uint8Array > ( resolve => { const chunkSize = 0x10000 ; const values = new Uint8Array ( size ) ; let k = 0 ; while ( size > 0 ) { const retrieveSize = ( size > chunkSize ) ? chunkSize : size ; zSocket . send ( 'read-memory ' + address + ' ' + retrieveSize , data => { const len = data . length ; Utility . assert ( len / 2 == retrieveSize ) ; for ( let i = 0 ; i < len ; i += 2 ) { const valueString = data . substr ( i , 2 ) ; const value = parseInt ( valueString , 16 ) ; values [ k ++ ] = value ; } } ) ; size -= chunkSize ; } zSocket . executeWhenQueueIsEmpty ( ) . then ( ( ) => { resolve ( values ) ; } ) ; } ) ; } param address param dataArray public async writeMemoryDump ( address , dataArray ) { return new Promise < void > ( resolve => { const chunkSize = 0x10000 ; let k = 0 ; let size = dataArray . length ; let chunkCount = 0 ; while ( size > 0 ) { const sendSize = ( size > chunkSize ) ? chunkSize : size ; let bytes = '' ; for ( let i = 0 ; i < sendSize ; i ++ ) { bytes += Utility . getHexString ( dataArray [ k ++ ] , 2 ) ; } chunkCount ++ ; zSocket . send ( 'write-memory-raw ' + address + ' ' + bytes , ( ) => { chunkCount -- ; if ( chunkCount == 0 ) resolve ( ) ; } ) ; size -= chunkSize ; } } ) ; } param address param value returns public async writeMemory ( address , value ) { return new Promise < number > ( resolve => { zSocket . send ( 'write-memory ' + address + ' ' + value , data => { zSocket . send ( 'read-memory ' + address + ' 1' , data => { const readValue = parseInt ( data , 16 ) ; resolve ( readValue ) ; } ) ; } ) ; } ) ; } param filePath returns public async stateSave ( filePath ) { return new Promise < void > ( resolve => { filePath += ".zsf" ; zSocket . send ( 'snapshot-save ' + filePath , data => { resolve ( ) ; } ) ; } ) ; } param filePath public async stateRestore ( filePath ) { return new Promise < void > ( resolve => { filePath += ".zsf" ; zSocket . send ( 'snapshot-load ' + filePath , async ( data ) => { await this . initAfterLoad ( ) ; await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; resolve ( ) ; } ) ; } ) ; } param registerNr returns public async getTbblueRegister ( registerNr ) { return new Promise < number > ( resolve => { zSocket . send ( 'tbblue-get-register ' + registerNr , ( data ) => { if ( data . startsWith ( "ERROR" ) ) { resolve ( 0 ) ; return ; } const valueString = data . substring ( 0 , 2 ) ; const value = parseInt ( valueString , 16 ) ; resolve ( value ) ; } ) ; } ) ; } param paletteNr returns public async getTbblueSpritesPalette ( paletteNr ) { return new Promise < Array < number > > ( resolve => { const paletteNrString = ( paletteNr == 0 ) ? 'first' : 'second' ; zSocket . send ( 'tbblue-get-palette sprite ' + paletteNrString + ' 0 256' , data => { const palette = new Array < number > ( 256 ) ; if ( ! data . startsWith ( "ERROR" ) ) { for ( let i = 0 ; i < 256 ; i ++ ) { const colorString = data . substr ( i * 4 , 3 ) ; const color = parseInt ( colorString , 16 ) ; palette [ i ] = ( color >>> 1 ) ; if ( color & 0x01 ) palette [ i ] += 0x100 ; } } resolve ( palette ) ; } ) ; } ) ; } returns public async getTbblueSpritesClippingWindow ( ) { return new Promise < { xl : number ; xr : number ; yt : number ; yb : number ; control : number ; } > ( resolve => { zSocket . send ( 'tbblue-get-clipwindow sprite' , data => { if ( data . startsWith ( "ERROR" ) ) { resolve ( { xl : 0 , xr : 0 , yt : 0 , yb : 0 , control : 0 } ) ; return ; } const clip = data . split ( ' ' ) ; const xl = parseInt ( clip [ 0 ] ) ; const xr = parseInt ( clip [ 1 ] ) ; const yt = parseInt ( clip [ 2 ] ) ; const yb = parseInt ( clip [ 3 ] ) ; this . getTbblueRegister ( 0x15 ) . then ( control => { resolve ( { xl , xr , yt , yb , control } ) ; } ) ; } ) ; } ) ; } param slot param count returns public async getTbblueSprites ( slot , count ) { return new Promise < Array < Uint8Array > > ( resolve => { zSocket . send ( 'tbblue-get-sprite ' + slot + ' ' + count , data => { const sprites = new Array < Uint8Array > ( ) ; if ( ! data . startsWith ( "ERROR" ) ) { const spriteLines = data . split ( '\n' ) ; for ( const line of spriteLines ) { if ( line . length == 0 ) continue ; const sprite = new Uint8Array ( 5 ) ; for ( let i = 0 ; i < 5 ; i ++ ) { const attrString = line . substr ( i * 3 , 2 ) ; if ( attrString . length > 0 ) { const attribute = parseInt ( attrString , 16 ) ; sprite [ i ] = attribute ; } } sprites . push ( sprite ) ; } } resolve ( sprites ) ; } ) ; } ) ; } param index param count preturns public async getTbblueSpritePatterns ( index , count ) { return new Promise < Array < Array < number > > > ( resolve => { zSocket . send ( 'tbblue-get-pattern ' + index + ' ' + count , data => { const patterns = new Array < Array < number > > ( ) ; if ( ! data . startsWith ( "ERROR" ) ) { const patternLines = data . split ( '\n' ) ; patternLines . pop ( ) ; for ( const line of patternLines ) { const pattern = new Array < number > ( 256 ) ; for ( let i = 0 ; i < 256 ; i ++ ) { const attrString = line . substr ( i * 3 , 2 ) ; const attribute = parseInt ( attrString , 16 ) ; pattern [ i ] = attribute ; } patterns . push ( pattern ) ; } } resolve ( patterns ) ; } ) ; } ) ; } param timeout returns public async waitForBeingQuietFor ( timeout ) { return new Promise < void > ( resolve => { let timerId ; const timer = ( ) => { clearTimeout ( timerId ) ; timerId = setTimeout ( ( ) => { zSocket . removeListener ( 'queueChanged' , timer ) ; resolve ( ) ; } , timeout ) ; } ; zSocket . on ( 'queueChanged' , timer ) ; zSocket . executeWhenQueueIsEmpty ( ) . then ( timer ) ; } ) ; } param path public async loadBin ( path ) { await zSocket . sendAwait ( 'smartload "' + Settings . launch . load + '"' ) ; } param path param address public async loadObj ( path , address ) { await zSocket . sendAwait ( 'load-binary ' + path + ' ' + address + ' 0' ) ; } }