import { Labels } from '../labels/labels' ; import { Utility } from '../misc/utility' ; import { Settings } from '../settings' ; import { DecodeRegisterData , RegisterData } from './decoderegisterdata' ; export let Z80RegisterVarFormat ; export let Z80RegisterHoverFormat ; export enum Z80_REG { PC , SP , AF , BC , DE , HL , IX , IY , AF2 , BC2 , DE2 , HL2 , IR , IM , F , A , C , B , E , D , L , H , IXL , IXH , IYL , IYH , F2 , A2 , C2 , B2 , E2 , D2 , L2 , H2 , R , I } ; /**
 * Class to deal with the Z80 registers.
 * Note: the Z80Registers class and derivations are supposed
 * not to communicate via sockets directly.
 * I.e. there is no asynchronicity in these methods.
 *
 * For each Remote (Emulator) a derivation of this class is required
 * to parse the data received from the remote for the registers.
 * The derived class normally needs to implement the methods:
 * - parsePC/SP/AF/BC/...AF2/BC2/HL2/DE2, i.e. the 2 byte (word) registers
 * - parseI, parseR
 * I.e. the other 1 byte register parse methods might be implemented as
 * well but it is not necessary as the default implementation will normally
 * work fine.
 *
 * After upgrade to 'long addresses' the registers also contain the state of the slots.
 * Embedding it in the registers significantly simplifies the design.
 * As these values need to be updated the same time. The values are
 * also required at the same time to correctly calculate the long addresses.
 * I.e. the cache data can also be stored into the history easily.
 */ export class Z80RegistersClass { public static FLAG_S = 1 << 7 ; public static FLAG_Z = 1 << 6 ; public static FLAG_H = 1 << 4 ; public static FLAG_PV = 1 << 2 ; public static FLAG_N = 1 << 1 ; public static FLAG_C = 1 << 0 ; protected static registerNames : Array < string > ; protected RegisterCache : RegisterData ; protected funcCreateLongAddress : ( address : number , slots : number [ ] ) => number ; protected funcGetSlotFromAddress : ( addr : number ) => number ; /**
     * Called during the launchRequest to create the singleton.
     */ public static createRegisters ( ) { Z80Registers = new Z80RegistersClass ( ) ; Z80RegistersClass . Init ( ) ; } /**
     * Sets/gets the decoder with the knowledge to decode
     * the RegisterData.
     */ private _decoder : DecodeRegisterData ; public get decoder ( ) : DecodeRegisterData { return this . _decoder ; } ; public set decoder ( value ) { this . _decoder = value ; } ; param slotCount constructor ( ) { } /**
     * Called during the launchRequest.
     */ public static Init ( ) { const names = Object . values ( Z80_REG ) ; this . registerNames = new Array < string > ( ) ; for ( let name of names ) { if ( typeof name != 'string' ) break ; name = name . replace ( '2' , "'" ) ; this . registerNames . push ( name ) ; } Z80RegisterVarFormat = Z80RegistersClass . createFormattingMap ( Settings . launch . formatting . registerVar ) ; Z80RegisterHoverFormat = Z80RegistersClass . createFormattingMap ( Settings . launch . formatting . registerHover ) ; } /**
     * Creates a RegisterData object from the given registers.
     */ public static getRegisterData ( PC , SP , AF , BC , DE , HL , IX , IY , AF2 , BC2 , DE2 , HL2 , I , R , IM , slots ) { const regData = new Uint16Array ( Z80_REG . IM + 1 + slots . length + 1 ) ; regData [ Z80_REG . PC ] = PC ; regData [ Z80_REG . SP ] = SP ; regData [ Z80_REG . AF ] = AF ; regData [ Z80_REG . BC ] = BC ; regData [ Z80_REG . DE ] = DE ; regData [ Z80_REG . HL ] = HL ; regData [ Z80_REG . IX ] = IX ; regData [ Z80_REG . IY ] = IY ; regData [ Z80_REG . AF2 ] = AF2 ; regData [ Z80_REG . BC2 ] = BC2 ; regData [ Z80_REG . DE2 ] = DE2 ; regData [ Z80_REG . HL2 ] = HL2 ; regData [ Z80_REG . IR ] = ( I << 8 ) | R ; regData [ Z80_REG . IM ] = IM ; let i = Z80_REG . IM + 1 ; regData [ i ++ ] = slots . length ; for ( const slot of slots ) { regData [ i ++ ] = slot ; } return regData ; } param settingsMap returns private static createFormattingMap ( settingsMap ) { const formattingMap = new Map ( ) ; for ( let i = 0 ; i < settingsMap . length ; i += 2 ) { let regRegex = new RegExp ( '^' + settingsMap [ i ] + '$' ) ; let regFormat = settingsMap [ i + 1 ] ; for ( let regName of Z80RegistersClass . registerNames ) { const format = formattingMap . get ( regName ) ; if ( format != undefined ) continue ; let keyWo = regName ; let rLen = keyWo . length ; if ( keyWo [ rLen - 1 ] == '\'' ) keyWo = keyWo . substr ( 0 , rLen - 1 ) ; const match = regRegex . exec ( keyWo ) ; if ( match == undefined ) continue ; formattingMap . set ( regName , regFormat ) ; } } for ( let regName of Z80RegistersClass . registerNames ) { const format = formattingMap . get ( regName ) ; if ( format != undefined ) continue ; let rLen ; if ( regName == "IXH" || regName == "IXL" || regName == "IYH" || regName == "IYL" || regName == "IM" ) { rLen = 1 ; } else { rLen = regName . length ; if ( regName [ rLen - 1 ] == '\'' ) -- rLen ; } if ( rLen == 1 ) formattingMap . set ( regName , '${hex}h, ${unsigned}u' ) ; else formattingMap . set ( regName , '${hex}h, ${unsigned}u${, :labelsplus|, }' ) ; } return formattingMap ; } param reg returns public static getEnumFromName ( reg ) { const regUpper = reg . toUpperCase ( ) ; const index = Z80RegistersClass . registerNames . indexOf ( regUpper ) ; if ( index < 0 ) return undefined ; return index ; } param reg public static isRegister ( reg ) { return ( Z80RegistersClass . getEnumFromName ( reg ) != undefined ) ; } param reg public static isSingleRegister ( reg ) { const regEnum = Z80RegistersClass . getEnumFromName ( reg ) ; if ( regEnum == undefined ) return false ; if ( regEnum >= Z80_REG . F ) return true ; return false ; } param cc param flags returns public static isCcMetByFlag ( cc , flags ) { const testSet = ( ( cc & 0x01 ) != 0 ) ; let condTest ; cc = ( cc >>> 1 ) & 0x03 ; switch ( cc ) { case 0 : condTest = ( ( flags & Z80RegistersClass . FLAG_Z ) != 0 ) ; break ; case 1 : condTest = ( ( flags & Z80RegistersClass . FLAG_C ) != 0 ) ; break ; case 2 : condTest = ( ( flags & Z80RegistersClass . FLAG_PV ) != 0 ) ; break ; case 3 : condTest = ( ( flags & Z80RegistersClass . FLAG_S ) != 0 ) ; break ; default : Utility . assert ( false ) ; } const ccIsTrue = ( condTest == testSet ) ; return ccIsTrue ; } /**
     * Clears the register cache.
     */ /**
     * Sets the register cache.
     * Used by ZesaruxEmulator.getRegistersFromEmulator and the cpu history.
     */ public setCache ( data ) { this . RegisterCache = data ; } /**
     * Returns the register cache.
     * Used by the cpu history.
     */ public getCache ( ) { return this . RegisterCache ; } /**
     * Returns true if the register is available.
     */ public valid ( ) { return this . RegisterCache != undefined ; } param regIn param formatMap returns protected getFormattedReg ( regIn , formatMap ) { const reg = regIn . toUpperCase ( ) ; const format = formatMap . get ( reg ) ; Utility . assert ( format != undefined , 'Register ' + reg + ' does not exist.' ) ; const value = this . decoder . getRegValueByName ( reg , this . RegisterCache ) ; if ( reg == "IM" ) { if ( value == 0xFF ) return "?" ; } let rLen = reg . length ; if ( reg [ rLen - 1 ] == '\'' ) -- rLen ; if ( rLen == 3 ) rLen = 1 ; Utility . assert ( this . valid ( ) ) ; const res = Utility . numberFormattedSync ( value , rLen , format , false , reg ) ; return res ; } param reg returns public getVarFormattedReg ( reg ) { return this . getFormattedReg ( reg , Z80RegisterVarFormat ) ; } param reg returns public getHoverFormattedReg ( reg ) { return this . getFormattedReg ( reg , Z80RegisterVarFormat ) ; } param regName returns public getRegValueByName ( regName ) { return this . decoder . getRegValueByName ( regName , this . RegisterCache ) ; } param reg returns public getRegValue ( reg ) { const name = Z80RegistersClass . registerNames [ reg ] ; return this . decoder . getRegValueByName ( name , this . RegisterCache ) ; } returns public getPC ( ) { return this . getRegValue ( Z80_REG . PC ) ; } returns public getSP ( ) { return this . getRegValue ( Z80_REG . SP ) ; } returns public getSlots ( ) { if ( this . funcCreateLongAddress != undefined ) { const slots = this . decoder . parseSlots ( this . RegisterCache ) ; return slots ; } return undefined ; } returns public getPCLong ( ) { const pc = this . getPC ( ) ; const slots = this . getSlots ( ) ; const pcLong = this . createLongAddress ( pc , slots ) ; return pcLong ; } param address param slots returns public createLongAddress ( address , slots ) { if ( ! slots ) return address ; if ( ! Labels . AreLongAddressesUsed ( ) ) return address ; if ( this . funcCreateLongAddress == undefined ) throw Error ( "Internal error: Maybe the memory model is wrong." ) ; const result = this . funcCreateLongAddress ( address , slots ) ; return result ; } param addr return public getSlotFromAddress ( addr ) { if ( this . funcGetSlotFromAddress == undefined ) throw Error ( "Internal error: Maybe the memory model is wrong." ) ; const slotIndex = this . funcGetSlotFromAddress ( addr & 0xFFFF ) ; return slotIndex ; } param addr return public getBankFromAddress ( addr ) { const bankPart = addr >>> 16 ; return bankPart - 1 ; } param addr param bank return public getLongAddressWithBank ( addr , bank ) { const longAddr = addr + ( ( bank + 1 ) << 16 ) ; return longAddr ; } param funcCreateLongAddress param funcGetSlotFromAddress public setSlotsAndBanks ( funcCreateLongAddress , funcGetSlotFromAddress ) { this . funcCreateLongAddress = funcCreateLongAddress ! ; this . funcGetSlotFromAddress = funcGetSlotFromAddress ! ; } } export let Z80Registers ;