import * as fs from 'fs' ; import { RemoteBase , RemoteBreakpoint , BREAK_REASON_NUMBER , Remote } from '../remotebase' ; import { GenericWatchpoint , GenericBreakpoint } from '../../genericwatchpoint' ; import { Z80RegistersClass , Z80_REG , Z80Registers } from '../z80registers' ; import { MemBank16k } from './membank16k' ; import { SnaFile } from './snafile' ; import { NexFile } from './nexfile' ; import { Settings } from '../../settings' ; import { Utility } from '../../misc/utility' ; import * as path from 'path' ; import { Labels } from '../../labels/labels' ; import { gzip , ungzip } from 'node-gzip' ; import { TimeWait } from '../../misc/timewait' ; import { Log } from '../../log' ; import { Zx128MemoryModel , Zx48MemoryModel , ZxNextMemoryModel } from '../Paging/memorymodel' ; import { Z80RegistersStandardDecoder } from '../z80registersstandarddecoder' ; import { PromiseCallbacks } from '../../misc/promisecallbacks' ; export const DZRP_VERSION = [ 2 , 0 , 0 ] ; export const DZRP_PROGRAM_NAME = "DeZog v" + process . version ; /**
 * The DZP commands and responses.
 * The response contains the command with the bit 7 set.
 */ export enum DZRP { CMD_INIT = 1 , CMD_CLOSE = 2 , CMD_GET_REGISTERS = 3 , CMD_SET_REGISTER = 4 , CMD_WRITE_BANK = 5 , CMD_CONTINUE = 6 , CMD_PAUSE = 7 , CMD_READ_MEM = 8 , CMD_WRITE_MEM = 9 , CMD_SET_SLOT = 10 , CMD_GET_TBBLUE_REG = 11 , CMD_SET_BORDER = 12 , CMD_SET_BREAKPOINTS = 13 , CMD_RESTORE_MEM = 14 , CMD_LOOPBACK = 15 , CMD_GET_SPRITES_PALETTE = 16 , CMD_GET_SPRITES_CLIP_WINDOW_AND_CONTROL = 17 , CMD_GET_SPRITES = 18 , CMD_GET_SPRITE_PATTERNS = 19 , CMD_ADD_BREAKPOINT = 40 , CMD_REMOVE_BREAKPOINT = 41 , CMD_ADD_WATCHPOINT = 42 , CMD_REMOVE_WATCHPOINT = 43 , CMD_READ_STATE = 50 , CMD_WRITE_STATE = 51 } /**
 * DZRP notifications.
 */ export enum DZRP_NTF { NTF_PAUSE = 1 } /**
 * Used for the DZRP CMD_CONTINUE alternate command for performance
 * improvement.
 * Is not implemented yet in DeZog but the DZRP already defines it.
 */ export enum AlternateCommand { CONTINUE = 0 , STEP_OVER = 1 , STEP_OUT = 2 } /**
 * Defines the machine type that is returned in CMD_INIT.
 * It is required to determine the memory model.
 */ export enum DzrpMachineType { ZX16K = 1 , ZX48K = 2 , ZX128K = 3 , ZXNEXT = 4 } /**
 * A class that communicates with the remote via the DZRP protocol.
 * It is base class for all DZRP remote classes that implement
 * special transports like serial connection or socket.
 *
 * All sendDzrpCmd... methods are empty stubs which need to be filled
 * by the specialized implementation.
 *
 * The class also implements flow/state handling for complex tasks
 * like 'continue'.
 */ export class DzrpRemote extends RemoteBase { protected funcContinueResolve ? : ( { breakNumber , breakAddress , breakReasonString } ) => void ; protected continueResolve ? : PromiseCallbacks < string > ; protected pauseStep = false ; protected timeWait : TimeWait ; protected tmpBreakpoints = new Map < number , Array < GenericBreakpoint > > ( ) ; protected addedWatchpoints = new Set < GenericWatchpoint > ( ) ; constructor ( ) { super ( ) ; } /**
     * Checks if there still is an open promise and runs it.
     */ public dispose ( ) { if ( this . continueResolve ) { this . continueResolve . resolve ( '' ) ; this . continueResolve = undefined ; this . funcContinueResolve = undefined ; } super . dispose ( ) ; } public async doInitialization ( ) { } emits protected async onConnect ( ) { try { const resp = await this . sendDzrpCmdInit ( ) ; if ( resp . error ) throw Error ( resp . error ) ; await this . loadExecutable ( ) ; Z80Registers . decoder = new Z80RegistersStandardDecoder ( ) ; switch ( resp . machineType ) { case DzrpMachineType . ZX48K : this . memoryModel = new Zx48MemoryModel ( ) ; break ; case DzrpMachineType . ZX128K : this . memoryModel = new Zx128MemoryModel ( ) ; break ; case DzrpMachineType . ZXNEXT : this . memoryModel = new ZxNextMemoryModel ( ) ; break ; default : throw Error ( "Unknown machine type " + resp . machineType + " received." ) ; } this . memoryModel . init ( ) ; Labels . convertLabelsTo ( this . memoryModel ) ; await Remote . setLaunchExecAddress ( ) ; await this . getRegistersFromEmulator ( ) ; const text = "'" + resp . programName + "' initialized." ; this . emit ( 'initialized' , text ) ; } catch ( err ) { this . emit ( 'error' , err ) ; } } param handler public async disconnect ( ) { } /**
     * If cache is empty retrieves the registers from
     * the Remote.
     */ public async getRegistersFromEmulator ( ) { const regs = await this . sendDzrpCmdGetRegisters ( ) ; Z80Registers . setCache ( regs ) ; } param cmd returns public async dbgExec ( cmd ) { const cmdArray = cmd . split ( ' ' ) ; const cmd_name = cmdArray . shift ( ) ; if ( cmd_name == "help" ) { return "Use e.g. 'cmd_init' to send a DZRP command to the ZX Next." ; } let response = "" ; if ( cmd_name == "cmd_init" ) { const resp = await this . sendDzrpCmdInit ( ) ; response = "Program: '" + resp . programName + "', DZRP Version: " + resp . dzrpVersion + "', machineType: " + resp . machineType + ", Error: " + resp . error ; } else if ( cmd_name == "cmd_continue" ) { await this . sendDzrpCmdContinue ( ) ; } else if ( cmd_name == "cmd_pause" ) { await this . sendDzrpCmdPause ( ) ; } else if ( cmd_name == "cmd_get_registers" ) { const regs = await this . sendDzrpCmdGetRegisters ( ) ; const regNames = [ "PC" , "SP" , "AF" , "BC" , "DE" , "HL" , "IX" , "IY" , "AF'" , "BC'" , "DE'" , "HL'" , "IR" , "IM" ] ; let i = 0 ; for ( const name of regNames ) { const value = regs [ i ] ; response += "\n" + name + "(" + i + "): 0x" + Utility . getHexString ( value , 4 ) + "/" + value ; i ++ ; } i ++ ; const slotCount = regs [ i ++ ] ; response += '\nslots.length=' + slotCount ; for ( let k = 0 ; k < slotCount ; k ++ ) response += '\n slots[' + k + ']=' + regs [ k + i ] ; } else if ( cmd_name == "cmd_set_register" ) { if ( cmdArray . length < 2 ) { throw Error ( "Expecting 2 parameters: regIndex and value." ) ; } const regIndex = Utility . parseValue ( cmdArray [ 0 ] ) ; const value = Utility . parseValue ( cmdArray [ 1 ] ) ; await this . sendDzrpCmdSetRegister ( regIndex as Z80_REG , value ) ; } else if ( cmd_name == "cmd_write_bank" ) { if ( cmdArray . length < 1 ) { throw Error ( "Expecting 1 parameter: 8k bank number [0-223]." ) ; } const bank = Utility . parseValue ( cmdArray [ 0 ] ) ; const data = new Uint8Array ( 0x2000 ) ; for ( let i = 0 ; i < data . length ; i ++ ) data [ i ] = i & 0xFF ; await this . sendDzrpCmdWriteBank ( bank , data ) ; } else if ( cmd_name == "cmd_read_mem" ) { if ( cmdArray . length < 2 ) { throw Error ( "Expecting at least 2 parameters: address and count." ) ; } const addr = Utility . parseValue ( cmdArray [ 0 ] ) ; const count = Utility . parseValue ( cmdArray [ 1 ] ) ; const data = await this . sendDzrpCmdReadMem ( addr , count ) ; response = Utility . getHexString ( addr , 4 ) + "h: " ; for ( const dat of data ) response += Utility . getHexString ( dat , 2 ) + "h " ; } else if ( cmd_name == "cmd_write_mem" ) { if ( cmdArray . length < 2 ) { throw Error ( "Expecting at least 2 parameters: address and memory content list." ) ; } const addr = Utility . parseValue ( cmdArray . shift ( ) ! ) ; const length = cmdArray . length ; const data = new Uint8Array ( length ) ; for ( let i = 0 ; i < data . length ; i ++ ) data [ i ] = Utility . parseValue ( cmdArray [ i ] ) & 0xFF ; await this . sendDzrpCmdWriteMem ( addr , data ) ; } else if ( cmd_name == "cmd_set_slot" ) { if ( cmdArray . length != 2 ) { throw Error ( "Expecting 2 parameters: slot and bank." ) ; } const slot = Utility . parseValue ( cmdArray [ 0 ] ) ; const bank = Utility . parseValue ( cmdArray [ 1 ] ) ; await this . sendDzrpCmdSetSlot ( slot , bank ) ; } else if ( cmd_name == "cmd_get_tbblue_reg" ) { if ( cmdArray . length < 1 ) { throw Error ( "Expecting 1 parameter: register." ) ; } const reg = Utility . parseValue ( cmdArray [ 0 ] ) ; const value = await this . sendDzrpCmdGetTbblueReg ( reg ) ; response += "\nReg[" + Utility . getHexString ( reg , 2 ) + "h/" + reg + "]: " + Utility . getHexString ( value , 2 ) + "h/" + value ; } else if ( cmd_name == "cmd_get_sprites_palette" ) { if ( cmdArray . length < 1 ) { throw Error ( "Expecting 1 parameter: palette number (0 or 1)." ) ; } const paletteNumber = Utility . parseValue ( cmdArray [ 0 ] ) ; const palette = await this . sendDzrpCmdGetSpritesPalette ( paletteNumber ) ; for ( const p of palette ) response += Utility . getHexString ( p , 3 ) + " " ; } else if ( cmd_name == "cmd_get_sprites_clip_window_and_control" ) { const clip = await this . sendDzrpCmdGetSpritesClipWindow ( ) ; response += "xl=" + clip . xl + ", xr=" + clip . xr + ", yt=" + clip . yt + ", yb=" + clip . yb + ", control=" + Utility . getBitsString ( clip . control , 8 ) ; } else if ( cmd_name == "cmd_set_breakpoints" ) { if ( cmdArray . length != 2 ) { throw Error ( "Expecting 2 parameters: address and bank." ) ; } const address = Utility . parseValue ( cmdArray [ 0 ] ) ; const bank = Utility . parseValue ( cmdArray [ 1 ] ) ; const longAddress = address + ( ( bank + 1 ) << 16 ) ; const memValues = await this . sendDzrpCmdSetBreakpoints ( [ longAddress ] ) ; const value = memValues [ 0 ] ; response += '\n Response: 0x' + Utility . getHexString ( value , 2 ) + '/' + value ; } else if ( cmd_name == "cmd_restore_mem" ) { if ( cmdArray . length != 3 ) { throw Error ( "Expecting 3 parameters: address, bank and value." ) ; } const address = Utility . parseValue ( cmdArray [ 0 ] ) ; const bank = Utility . parseValue ( cmdArray [ 1 ] ) ; const value = Utility . parseValue ( cmdArray [ 2 ] ) ; const longAddress = address + ( ( bank + 1 ) << 16 ) ; await this . sendDzrpCmdRestoreMem ( [ { address : longAddress , value } ] ) ; } else if ( cmd_name == "cmd_add_breakpoint" ) { if ( cmdArray . length != 2 ) { throw Error ( "Expecting 2 parameters: address and bank." ) ; } const address = Utility . parseValue ( cmdArray [ 0 ] ) ; const bank = Utility . parseValue ( cmdArray [ 1 ] ) ; const longAddress = address + ( ( bank + 1 ) << 16 ) ; const bp = { address : longAddress } ; await this . sendDzrpCmdAddBreakpoint ( bp ) ; response += '\n Breakpoint ID: ' + bp . bpId ; } else if ( cmd_name == "cmd_remove_breakpoint" ) { if ( cmdArray . length != 1 ) { throw Error ( "Expecting 1 parameter: breakpoint ID." ) ; } const bp = { address : - 1 , bpId : Utility . parseValue ( cmdArray [ 0 ] ) } ; await this . sendDzrpCmdRemoveBreakpoint ( bp ) ; } else { throw Error ( "Error: not supported." ) ; } let result = "Sent " + cmd_name . toUpperCase ( ) + ".\nResponse received" ; if ( response ) result += ": " + response ; else result += "." ; return result ; } param register param value return public async setRegisterValue ( register , value ) { const index = Z80RegistersClass . getEnumFromName ( register ) as number ; Utility . assert ( index != undefined ) ; await this . sendDzrpCmdSetRegister ( index , value ) ; await this . getRegistersFromEmulator ( ) ; const realValue = Z80Registers . getRegValueByName ( register ) ; return realValue ; } param slot param bank public async setSlot ( slotIndex , bank ) { await this . sendDzrpCmdSetSlot ( slotIndex , bank ) ; } param The return protected getWatchpointsByAddress ( address ) { const address64 = address & 0xFFFF ; const arr = new Array < GenericWatchpoint > ( ) ; const slots = this . getSlots ( ) ! ; for ( const wp of this . addedWatchpoints ) { const addr = wp . address ; const addr64 = addr & 0xFFFF ; if ( address64 < addr64 || address64 >= addr64 + wp . size ) continue ; const bank = Z80Registers . getBankFromAddress ( addr ) ; if ( bank >= 0 ) { const slotNr = Z80Registers . getSlotFromAddress ( addr ) ; const slotBank = slots [ slotNr ] ; if ( bank != slotBank ) continue ; } arr . push ( wp ) ; } return arr ; } param bpAddress returns protected getBreakpointsByAddress ( bpAddress ) { let foundBps = this . tmpBreakpoints . get ( bpAddress ) ; if ( ! foundBps ) foundBps = this . tmpBreakpoints . get ( bpAddress & 0xFFFF ) || [ ] ; return foundBps ; } /**
     * Creates a temporary map from the breakpoints, logpoints and assertions.
     * If one entry is set the entry contains a pointer to the breakpoint.
     * Or better it contains an array of breakpoints that all share the
     * same address.
     * Note: normally this array contains only one entry.
     */ protected createTemporaryBreakpoints ( ) { const tmpBps = this . tmpBreakpoints ; tmpBps . clear ( ) ; const enabledLogPoints = this . getEnabledLogpoints ( ) ; const assertionBps = ( this . assertionBreakpointsEnabled ) ? this . assertionBreakpoints : [ ] ; const allBps = [ ... this . breakpoints , ... enabledLogPoints , ... assertionBps ] ; allBps . forEach ( bp => { this . addTmpBreakpoint ( bp ) ; } ) ; } /**
     * Adds a breakpoint to the temporary array.
     * Is called by createTemporaryBreakpoints or if a BP
     * is created during a running debugged program.
     */ protected addTmpBreakpoint ( bp ) { const tmpBps = this . tmpBreakpoints ; const bpAddress = bp . address ; let bpInner = tmpBps . get ( bpAddress ) ; if ( ! bpInner ) { bpInner = new Array < GenericBreakpoint > ( ) ; tmpBps . set ( bp . address , bpInner ) ; } bpInner . push ( bp ) ; } /**
     * Removes a breakpoint from the temporary array.
     * Is called by createTemporaryBreakpoints or if a BP
     * is removed during a running debugged program.
     */ protected removeTmpBreakpoint ( bp ) { const bpAddress = bp . address ; const bpArray = this . tmpBreakpoints . get ( bpAddress ) ! ; Utility . assert ( bpArray ) ; const len = bpArray . length ; for ( let i = 0 ; i < len ; i ++ ) { const bpa = bpArray [ i ] ; if ( bpa . bpId == bp . bpId ) { bpArray . splice ( i , 1 ) ; if ( bpArray . length == 0 ) this . tmpBreakpoints . delete ( bpAddress ) ; return ; } } } param bp returns protected checkConditionAndLog ( bp ) { if ( bp ) { if ( bp . condition ) { try { const evalCond = Utility . evalExpression ( bp . condition , true ) ; if ( evalCond != 0 ) return { condition : bp . condition , log : bp . log } ; } catch ( e ) { e . message = "Evaluation condition '" + bp . condition + "': " + ( e . message || "Unknown error" ) ; throw e ; } return { condition : undefined , log : bp . log } ; } else { return { condition : '' , log : bp . log } ; } } return { condition : '' , log : undefined } ; } param breakNumber param breakAddress param condition param breakReasonString returns protected async constructBreakReasonString ( breakNumber , breakAddress , condition , breakReasonString ) { Utility . assert ( condition != undefined ) ; if ( breakReasonString == undefined ) breakReasonString = '' ; let reasonString ; switch ( breakNumber ) { case BREAK_REASON_NUMBER . NO_REASON : reasonString = "" ; break ; case BREAK_REASON_NUMBER . MANUAL_BREAK : reasonString = "Manual break." ; break ; case BREAK_REASON_NUMBER . BREAKPOINT_HIT : const abps = this . assertionBreakpoints . filter ( abp => abp . address == breakAddress ) ; for ( const abp of abps ) { if ( condition == abp . condition ) { const assertionCond = Utility . getAssertionFromCondition ( condition ) ; const replaced = Utility . replaceVarsWithValues ( assertionCond ) ; reasonString = "Assertion failed: " + replaced ; return reasonString ; } } const addrString = Utility . getHexString ( breakAddress & 0xFFFF , 4 ) ; let bankString = "" ; const bank = breakAddress >>> 16 ; if ( bank != 0 ) bankString = " (bank=" + ( bank - 1 ) . toString ( ) + ")" ; reasonString = "Breakpoint hit @" + addrString + "h" + bankString + "." ; if ( condition ) reasonString += " Condition: " + condition ; return reasonString ; case BREAK_REASON_NUMBER . WATCHPOINT_READ : case BREAK_REASON_NUMBER . WATCHPOINT_WRITE : const address = breakAddress ; reasonString = "Watchpoint " + ( ( breakNumber == BREAK_REASON_NUMBER . WATCHPOINT_READ ) ? "read" : "write" ) + " access at address 0x" + Utility . getLongAddressString ( address ) ; const labels = Labels . getLabelsPlusIndexForNumber64k ( address ) ; if ( labels . length > 0 ) { const labelsString = labels . join ( ', ' ) ; reasonString += " (" + labelsString + ")" ; } reasonString += ". " + breakReasonString ; break ; default : reasonString = breakReasonString ; } return reasonString ; } /**
     * This method is called before a step (stepOver, stepInto, stepOut,
     * continue, stepBack, etc.) is called.
     */ public startProcessing ( ) { this . createTemporaryBreakpoints ( ) ; this . pauseStep = false ; this . timeWait = new TimeWait ( 1000 , 200 , 100 ) ; } /**
     * This method is called after a step (stepOver, stepInto, stepOut,
     * continue, stepBack, etc.) is called.
     */ param breakNumber param breakAddress returns protected async evalBpConditionAndLog ( breakNumber , breakAddress ) { let condition ; let correctedBreakNumber = breakNumber ; switch ( breakNumber ) { case BREAK_REASON_NUMBER . WATCHPOINT_READ : case BREAK_REASON_NUMBER . WATCHPOINT_WRITE : const wps = this . getWatchpointsByAddress ( breakAddress ) ; for ( const wp of wps ) { let found = false ; if ( breakNumber == BREAK_REASON_NUMBER . WATCHPOINT_READ ) { found = wp . access . includes ( 'r' ) ; } else { found = wp . access . includes ( 'w' ) ; } if ( found ) { condition = '' ; break ; } } break ; case BREAK_REASON_NUMBER . NO_REASON : case BREAK_REASON_NUMBER . BREAKPOINT_HIT : const bps = this . getBreakpointsByAddress ( breakAddress ) ; for ( const bp of bps ) { let { condition : cond , log } = this . checkConditionAndLog ( bp ) ; if ( cond != undefined && log ) { const evalLog = await Utility . evalLogString ( log ) ; this . emit ( 'debug_console' , "Log: " + evalLog ) ; cond = undefined ; } if ( cond != undefined ) { condition = cond ; correctedBreakNumber = BREAK_REASON_NUMBER . BREAKPOINT_HIT ; } } if ( breakNumber == BREAK_REASON_NUMBER . NO_REASON ) { if ( condition == undefined ) { condition = '' ; } } break ; case BREAK_REASON_NUMBER . STEPPING_NOT_ALLOWED : default : condition = '' ; } if ( correctedBreakNumber == BREAK_REASON_NUMBER . NO_REASON || condition == undefined ) { if ( this . pauseStep ) { condition = '' ; correctedBreakNumber = BREAK_REASON_NUMBER . MANUAL_BREAK ; } } return { condition , correctedBreakNumber } ; } returns public async continue ( ) { return new Promise < string > ( async ( resolve ) => { Utility . assert ( ! this . continueResolve ) ; this . continueResolve = new PromiseCallbacks < string > ( this , 'continueResolve' , resolve ) ; const funcContinueResolve = async ( { breakNumber , breakAddress , breakReasonString } ) => { try { await this . getRegistersFromEmulator ( ) ; const { condition , correctedBreakNumber } = await this . evalBpConditionAndLog ( breakNumber , breakAddress ) ; if ( condition == undefined ) { this . funcContinueResolve = funcContinueResolve ; await this . sendDzrpCmdContinue ( ) ; } else { breakReasonString = await this . constructBreakReasonString ( correctedBreakNumber , breakAddress , condition , breakReasonString ) ; await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; this . continueResolve ! . resolve ( breakReasonString ) ; } } catch ( e ) { try { await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; } catch ( e ) { } const reason = e . message ; this . continueResolve ! . resolve ( reason ) ; } } ; this . funcContinueResolve = funcContinueResolve ; await this . sendDzrpCmdContinue ( ) ; } ) ; } /**
     * 'pause' the debugger.
     */ public async pause ( ) { this . pauseStep = true ; await this . sendDzrpCmdPause ( ) ; } param stepOver returns public async stepOver ( stepOver = true ) { return new Promise < string | undefined > ( async ( resolve ) => { Utility . assert ( ! this . continueResolve ) ; this . continueResolve = new PromiseCallbacks < string > ( this , 'continueResolve' , resolve ) ; const funcContinueResolve = async ( { breakNumber , breakAddress , breakReasonString } ) => { await this . timeWait . waitAtInterval ( ) ; await this . getRegistersFromEmulator ( ) ; let { condition , correctedBreakNumber } = await this . evalBpConditionAndLog ( breakNumber , breakAddress ) ; if ( condition == undefined ) { this . funcContinueResolve = funcContinueResolve ; await this . sendDzrpCmdContinue ( bp1 , bp2 ) ; } else { breakReasonString = await this . constructBreakReasonString ( correctedBreakNumber , breakAddress , condition , breakReasonString ) ; await this . getCallStackFromEmulator ( ) ; this . continueResolve ! . resolve ( breakReasonString ) ; } } ; let [ , bp1 , bp2 ] = await this . calcStepBp ( stepOver ) ; this . funcContinueResolve = funcContinueResolve ; await this . sendDzrpCmdContinue ( bp1 , bp2 ) ; } ) ; } returns public async stepInto ( ) { return this . stepOver ( false ) ; } returns public async stepOut ( ) { return new Promise < string | undefined > ( async ( resolve ) => { Utility . assert ( ! this . continueResolve ) ; this . continueResolve = new PromiseCallbacks < string > ( this , 'continueResolve' , resolve ) ; const startSp = Z80Registers . getRegValue ( Z80_REG . SP ) ; let prevSp = startSp ; let prevPc = 0 ; const funcContinueResolve = async ( { breakNumber , breakAddress , breakReasonString } ) => { try { await this . timeWait . waitAtInterval ( ) ; await this . getRegistersFromEmulator ( ) ; let { condition , correctedBreakNumber } = await this . evalBpConditionAndLog ( breakNumber , breakAddress ) ; if ( correctedBreakNumber == BREAK_REASON_NUMBER . NO_REASON ) condition = undefined ; if ( condition == undefined ) { const currSp = Z80Registers . getRegValue ( Z80_REG . SP ) ; if ( currSp > startSp && currSp > prevSp ) { const bytes = await this . readMemoryDump ( prevPc , 2 ) ; const opcodes = bytes [ 0 ] + ( bytes [ 1 ] << 8 ) ; if ( this . isRet ( opcodes ) ) { condition = '' ; correctedBreakNumber = BREAK_REASON_NUMBER . NO_REASON ; } } } if ( condition == undefined ) { let [ , bp1 , bp2 ] = await this . calcStepBp ( true ) ; this . funcContinueResolve = funcContinueResolve ; prevPc = Z80Registers . getPC ( ) ; await this . sendDzrpCmdContinue ( bp1 , bp2 ) ; } else { breakReasonString = await this . constructBreakReasonString ( correctedBreakNumber , breakAddress , condition , breakReasonString ) ; await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; this . continueResolve ! . resolve ( breakReasonString ) ; } } catch ( e ) { await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; const reason = e ; this . continueResolve ! . resolve ( reason ) ; } } ; let [ , bp1 , bp2 ] = await this . calcStepBp ( true ) ; this . funcContinueResolve = funcContinueResolve ; prevPc = Z80Registers . getPC ( ) ; await this . sendDzrpCmdContinue ( bp1 , bp2 ) ; } ) ; } param opcodes returns public isRet ( opcodes ) { const opcode0 = opcodes & 0xFF ; if ( 0xC9 == opcode0 ) return true ; if ( 0xED == opcode0 ) { const opcode1 = ( opcodes >>> 8 ) & 0xFF ; if ( 0x4D == opcode1 || 0x45 == opcode1 ) return true ; } const mask = 0b11000111 ; if ( ( opcode0 & mask ) == 0b11000000 ) { return true ; } return false ; } param wp public async setWatchpoint ( wp ) { this . addedWatchpoints . add ( wp ) ; await this . sendDzrpCmdAddWatchpoint ( wp . address , wp . size , wp . access ) ; } param wp public async removeWatchpoint ( wp ) { this . addedWatchpoints . delete ( wp ) ; await this . sendDzrpCmdRemoveWatchpoint ( wp . address , wp . size , wp . access ) ; } param enable public async enableAssertionBreakpoints ( enable ) { for ( let abp of this . assertionBreakpoints ) { if ( enable ) { if ( ! abp . bpId ) { await this . sendDzrpCmdAddBreakpoint ( abp ) ; } } else { if ( abp . bpId ) { await this . sendDzrpCmdRemoveBreakpoint ( abp ) ; abp . bpId = undefined ; } } } this . assertionBreakpointsEnabled = enable ; } param logpoints param enable returns public async enableLogpoints ( logpoints , enable ) { for ( let lp of logpoints ) { if ( enable ) { if ( ! lp . bpId ) { await this . sendDzrpCmdAddBreakpoint ( lp ) ; } } else { if ( lp . bpId ) { await this . sendDzrpCmdRemoveBreakpoint ( lp ) ; lp . bpId = undefined ; } } } } public async setBreakpoint ( bp ) { if ( bp . address < 0 ) { this . emit ( 'warning' , 'DZRP does only support PC breakpoints.' ) ; bp . address = - 1 ; return 0 ; } await this . sendDzrpCmdAddBreakpoint ( bp ) ; if ( bp . bpId == 0 ) bp . address = - 1 ; this . breakpoints . push ( bp ) ; if ( this . funcContinueResolve ) { this . addTmpBreakpoint ( bp ) ; } return bp . bpId ; } /**
     * Clears one breakpoint.
     */ public async removeBreakpoint ( bp ) { let index = this . breakpoints . indexOf ( bp ) ; Utility . assert ( index !== - 1 , 'Breakpoint should be removed but does not exist.' ) ; this . breakpoints . splice ( index , 1 ) ; if ( this . funcContinueResolve ) { this . removeTmpBreakpoint ( bp ) ; } await this . sendDzrpCmdRemoveBreakpoint ( bp ) ; } param address param size returns public async readMemoryDump ( address , size ) { return this . sendDzrpCmdReadMem ( address , size ) ; } param address param dataArray public async writeMemoryDump ( address , dataArray ) { await this . sendDzrpCmdWriteMem ( address , dataArray ) ; } /**
     * Loads .nex or .sna files.
     */ protected async loadBin ( filePath ) { const ext = path . extname ( filePath ) ; if ( ext == '.sna' ) await this . loadBinSna ( filePath ) ; else if ( ext == '.nex' ) await this . loadBinNex ( filePath ) ; else { throw Error ( "File extension not supported in '" + filePath + "' with remoteType:'" + Settings . launch . remoteType + "'. Can only load .sna and .nex files." ) ; } } param filePath param startAddress protected async loadObj ( filePath , startAddress ) { const objBuffer = fs . readFileSync ( filePath ) ; await this . sendDzrpCmdWriteMem ( startAddress , objBuffer ) ; } /**
     * Loads a .sna file.
     * See https://faqwiki.zxnet.co.uk/wiki/SNA_format
     */ protected async loadBinSna ( filePath ) { const snaFile = new SnaFile ( ) ; snaFile . readFile ( filePath ) ; await this . sendDzrpCmdSetBorder ( snaFile . borderColor ) ; for ( const memBank of snaFile . memBanks ) { const bank8 = 2 * memBank . bank ; await this . sendDzrpCmdWriteBank ( bank8 , memBank . data . slice ( 0 , MemBank16k . BANK16K_SIZE / 2 ) ) ; await this . sendDzrpCmdWriteBank ( bank8 + 1 , memBank . data . slice ( MemBank16k . BANK16K_SIZE / 2 ) ) ; } const slotBanks = [ 254 , 255 , 10 , 11 , 4 , 5 , 0 , 1 ] ; for ( let slot = 0 ; slot < 8 ; slot ++ ) { const bank8 = slotBanks [ slot ] ; await this . sendDzrpCmdSetSlot ( slot , bank8 ) ; } await this . sendDzrpCmdSetRegister ( Z80_REG . PC , snaFile . pc ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . SP , snaFile . sp ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . AF , snaFile . af ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . BC , snaFile . bc ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . DE , snaFile . de ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . HL , snaFile . hl ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . IX , snaFile . ix ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . IY , snaFile . iy ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . AF2 , snaFile . af2 ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . BC2 , snaFile . bc2 ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . DE2 , snaFile . de2 ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . HL2 , snaFile . hl2 ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . R , snaFile . r ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . I , snaFile . i ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . IM , snaFile . im ) ; } /**
     * Loads a .nex file.
     * See https://wiki.specnext.dev/NEX_file_format
     */ protected async loadBinNex ( filePath ) { const nexFile = new NexFile ( ) ; nexFile . readFile ( filePath ) ; await this . sendDzrpCmdSetBorder ( nexFile . borderColor ) ; for ( const memBank of nexFile . memBanks ) { Log . log ( "loadBinNex: Writing 16k bank " + memBank . bank ) ; const bank8 = 2 * memBank . bank ; await this . sendDzrpCmdWriteBank ( bank8 , memBank . data . slice ( 0 , MemBank16k . BANK16K_SIZE / 2 ) ) ; await this . sendDzrpCmdWriteBank ( bank8 + 1 , memBank . data . slice ( MemBank16k . BANK16K_SIZE / 2 ) ) ; } const slotBanks = [ 254 , 255 , 10 , 11 , 4 , 5 , 0 , 1 ] ; for ( let slot = 0 ; slot < 8 ; slot ++ ) { const bank8 = slotBanks [ slot ] ; await this . sendDzrpCmdSetSlot ( slot , bank8 ) ; } await this . sendDzrpCmdSetRegister ( Z80_REG . SP , nexFile . sp ) ; await this . sendDzrpCmdSetRegister ( Z80_REG . PC , nexFile . pc ) ; } param filePath public async stateSave ( filePath ) { const stateData = await this . sendDzrpCmdReadState ( ) ; const zippedData = await gzip ( stateData ) ; fs . writeFileSync ( filePath , zippedData ) ; } param filePath public async stateRestore ( filePath ) { const zippedData = fs . readFileSync ( filePath ) ; const stateData = await ungzip ( zippedData ) ; await this . sendDzrpCmdWriteState ( stateData ) ; await this . getRegistersFromEmulator ( ) ; await this . getCallStackFromEmulator ( ) ; } param registerNr returns public async getTbblueRegister ( registerNr ) { const value = await this . sendDzrpCmdGetTbblueReg ( registerNr ) ; return value ; } param paletteNr returns public async getTbblueSpritesPalette ( paletteNr ) { const palette = await this . sendDzrpCmdGetSpritesPalette ( paletteNr ) ; return palette ; } returns public async getTbblueSpritesClippingWindow ( ) { const clip = await this . sendDzrpCmdGetSpritesClipWindow ( ) ; return clip ; } param slot param count returns public async getTbblueSprites ( slot , count ) { const sprites = await this . sendDzrpCmdGetSprites ( slot , count ) ; return sprites ; } param index param count preturns public async getTbblueSpritePatterns ( index , count ) { const patterns = await this . sendDzrpCmdGetSpritePatterns ( index , count ) ; return patterns ; } returns protected async sendDzrpCmdInit ( ) { Utility . assert ( false ) ; return { error : undefined , dzrpVersion : "" , programName : "" , machineType : 0 } ; } /**
     * Override.
     * The last command sent. Closes the debug session.
     */ protected async sendDzrpCmdClose ( ) { Utility . assert ( false ) ; } returns protected async sendDzrpCmdGetRegisters ( ) { Utility . assert ( false ) ; return new Uint16Array ( 0 ) ; } param regIndex param value protected async sendDzrpCmdSetRegister ( regIndex , value ) { Utility . assert ( false ) ; } param bp1Address param bp2Address protected async sendDzrpCmdContinue ( bp1Address ? , bp2Address ? ) { Utility . assert ( false ) ; } /**
     * Override.
     * Sends the command to pause a running program.
     */ protected async sendDzrpCmdPause ( ) { Utility . assert ( false ) ; } param bp protected async sendDzrpCmdAddBreakpoint ( bp ) { Utility . assert ( false ) ; } param bp protected async sendDzrpCmdRemoveBreakpoint ( bp ) { Utility . assert ( false ) ; } param address param size param access protected async sendDzrpCmdAddWatchpoint ( address , size , access ) { throw Error ( "Watchpoints not supported!" ) ; } param address param size param access protected async sendDzrpCmdRemoveWatchpoint ( address , size , access ) { throw Error ( "Watchpoints not supported!" ) ; } param address param size returns protected async sendDzrpCmdReadMem ( address , size ) { Utility . assert ( false ) ; return new Uint8Array ( 0 ) ; } param address param dataArray public async sendDzrpCmdWriteMem ( address , dataArray ) { Utility . assert ( false ) ; } param bank param dataArray throws public async sendDzrpCmdWriteBank ( bank , dataArray ) { Utility . assert ( false ) ; } param slot param bank returns public async sendDzrpCmdSetSlot ( slot , bank ) { Utility . assert ( false ) ; return 0 ; } returns public async sendDzrpCmdReadState ( ) { throw Error ( "Read state not supported!" ) ; } param The public async sendDzrpCmdWriteState ( stateData ) { throw Error ( "Write state not supported!" ) ; } param register returns public async sendDzrpCmdGetTbblueReg ( register ) { throw Error ( "Reading Tbblue registers is not supported." ) ; } param index returns public async sendDzrpCmdGetSpritesPalette ( index ) { throw Error ( "Get sprite palette not supported!" ) ; } param index param count returns public async sendDzrpCmdGetSprites ( index , count ) { throw Error ( "Get sprites not supported!" ) ; } param index param count returns protected async sendDzrpCmdGetSpritePatterns ( index , count ) { throw Error ( "Get sprite patterns not supported!" ) ; } returns public async sendDzrpCmdGetSpritesClipWindow ( ) { throw Error ( "Get sprites clip window not supported!" ) ; } /**
     * Sends the command to set the border.
      */ public async sendDzrpCmdSetBorder ( borderColor ) { Utility . assert ( false ) ; } param bpAddresses returns protected async sendDzrpCmdSetBreakpoints ( bpAddresses ) { Utility . assert ( false ) ; return [ ] ; } param elems protected async sendDzrpCmdRestoreMem ( elems ) { Utility . assert ( false ) ; } }