import { Z80Ports } from './z80ports' ; import { Z80RegistersClass } from '../z80registers' ; import { MemBuffer , Serializeable } from '../../misc/membuffer' ; import { Settings } from '../../settings' ; import * as Z80 from '../../3rdparty/z80.js/Z80.js' ; import { SimulatedMemory } from './simmemory' ; export class Z80Cpu implements Serializeable { protected z80 : any ; protected remainingInterruptTstates : number ; protected INTERRUPT_TIME_AS_T_STATES : number ; protected cpuLoadTstates : number ; public cpuWithHaltTstates : number ; public cpuLoad : number ; protected cpuLoadRange : number ; protected cpuLoadRangeCounter : number ; public cpuTstatesCounter : number ; protected vsyncInterrupt : boolean ; public cpuFreq : number ; public memory : SimulatedMemory ; public ports : Z80Ports ; public error : string | undefined ; protected vertInterruptFunc : ( ) => void ; param memory param ports param vertInterruptFunc constructor ( memory , ports , vertInterruptFunc = ( ) => { } ) { this . vertInterruptFunc = vertInterruptFunc ; this . error = undefined ; this . memory = memory ; this . ports = ports ; this . cpuFreq = Settings . launch . zsim . cpuFrequency ; this . INTERRUPT_TIME_AS_T_STATES = 0.02 * this . cpuFreq ; this . remainingInterruptTstates = this . INTERRUPT_TIME_AS_T_STATES ; this . cpuTstatesCounter = 0 ; this . cpuLoadTstates = 0 ; this . cpuWithHaltTstates = 0 ; this . cpuLoad = 1.0 ; this . cpuLoadRangeCounter = 0 ; this . cpuLoadRange = Settings . launch . zsim . cpuLoadInterruptRange ; this . vsyncInterrupt = Settings . launch . zsim . vsyncInterrupt ; const z80n_enabled = Settings . launch . zsim . Z80N ; this . z80 = new ( Z80 . Z80 as any ) ( { mem_read : ( address ) => { return memory . read8 ( address ) ; } , mem_write : ( address , val ) => { memory . write8 ( address , val ) ; } , io_read : ( address ) => { try { return ports . read ( address ) ; } catch ( e ) { this . error = "io_read: " + e . message ; return 0 ; } } , io_write : ( address , val ) => { try { ports . write ( address , val ) ; } catch ( e ) { this . error = "io_write: " + e . message ; } } , z80n_enabled : z80n_enabled } ) ; } returns public execute ( ) { const z80 = this . z80 ; const tStates = z80 . run_instruction ( ) ; let accumulatedTstates = tStates ; if ( z80 . halted ) { if ( z80 . interruptsEnabled && this . vsyncInterrupt ) { accumulatedTstates = this . remainingInterruptTstates ; this . remainingInterruptTstates = 0 ; } else { this . cpuLoadTstates += tStates ; } } else { this . cpuLoadTstates += tStates ; } this . cpuTstatesCounter += accumulatedTstates ; this . cpuWithHaltTstates += accumulatedTstates ; this . remainingInterruptTstates -= tStates ; if ( this . remainingInterruptTstates <= 0 ) { this . remainingInterruptTstates = this . INTERRUPT_TIME_AS_T_STATES ; if ( this . vsyncInterrupt ) { this . vertInterruptFunc ( ) ; this . generateInterrupt ( false , 0 ) ; } } return accumulatedTstates ; } /**
     * Properties to set flags.
     */ set pc ( value ) { this . z80 . pc = value ; } get pc ( ) { return this . z80 . pc ; } set sp ( value ) { this . z80 . sp = value ; } get sp ( ) { return this . z80 . sp ; } set af ( value ) { const r = this . z80 . getState ( ) ; r . a = value >>> 8 ; r . flags = this . revConvertFlags ( value & 0xFF ) ; this . z80 . setState ( r ) ; } set bc ( value ) { const r = this . z80 . getState ( ) ; r . b = value >>> 8 ; r . c = value & 0xFF ; this . z80 . setState ( r ) ; } set de ( value ) { const r = this . z80 . getState ( ) ; r . d = value >>> 8 ; r . e = value & 0xFF ; this . z80 . setState ( r ) ; } set hl ( value ) { const r = this . z80 . getState ( ) ; r . h = value >>> 8 ; r . l = value & 0xFF ; this . z80 . setState ( r ) ; } set ix ( value ) { const r = this . z80 . getState ( ) ; r . ix = value ; this . z80 . setState ( r ) ; } set iy ( value ) { const r = this . z80 . getState ( ) ; r . iy = value ; this . z80 . setState ( r ) ; } set af2 ( value ) { const r = this . z80 . getState ( ) ; r . a_prime = value >>> 8 ; r . flags_prime = this . revConvertFlags ( value & 0xFF ) ; this . z80 . setState ( r ) ; } set bc2 ( value ) { const r = this . z80 . getState ( ) ; r . b_prime = value >>> 8 ; r . c_prime = value & 0xFF ; this . z80 . setState ( r ) ; } set de2 ( value ) { const r = this . z80 . getState ( ) ; r . d_prime = value >>> 8 ; r . e_prime = value & 0xFF ; this . z80 . setState ( r ) ; } set hl2 ( value ) { const r = this . z80 . getState ( ) ; r . h_prime = value >>> 8 ; r . l_prime = value & 0xFF ; this . z80 . setState ( r ) ; } set im ( value ) { const r = this . z80 . getState ( ) ; r . imode = value ; this . z80 . setState ( r ) ; } set iff1 ( value ) { const r = this . z80 . getState ( ) ; r . iff1 = value ; this . z80 . setState ( r ) ; } set iff2 ( value ) { const r = this . z80 . getState ( ) ; r . iff2 = value ; this . z80 . setState ( r ) ; } set r ( value ) { const r = this . z80 . getState ( ) ; r . r = value ; this . z80 . setState ( r ) ; } set i ( value ) { const r = this . z80 . getState ( ) ; r . i = value ; this . z80 . setState ( r ) ; } set a ( value ) { const r = this . z80 . getState ( ) ; r . a = value ; this . z80 . setState ( r ) ; } set f ( value ) { const r = this . z80 . getState ( ) ; r . f = this . revConvertFlags ( value ) ; this . z80 . setState ( r ) ; } set b ( value ) { const r = this . z80 . getState ( ) ; r . b = value ; this . z80 . setState ( r ) ; } set c ( value ) { const r = this . z80 . getState ( ) ; r . c = value ; this . z80 . setState ( r ) ; } set d ( value ) { const r = this . z80 . getState ( ) ; r . d = value ; this . z80 . setState ( r ) ; } set e ( value ) { const r = this . z80 . getState ( ) ; r . e = value ; this . z80 . setState ( r ) ; } set h ( value ) { const r = this . z80 . getState ( ) ; r . h = value ; this . z80 . setState ( r ) ; } set l ( value ) { const r = this . z80 . getState ( ) ; r . l = value ; this . z80 . setState ( r ) ; } set ixl ( value ) { const r = this . z80 . getState ( ) ; r . ix = ( r . ix & 0xFF00 ) + value ; this . z80 . setState ( r ) ; } set ixh ( value ) { const r = this . z80 . getState ( ) ; r . ix = ( r . ix & 0xFF ) + 256 * value ; this . z80 . setState ( r ) ; } set iyl ( value ) { const r = this . z80 . getState ( ) ; r . iy = ( r . iy & 0xFF00 ) + value ; this . z80 . setState ( r ) ; } set iyh ( value ) { const r = this . z80 . getState ( ) ; r . iy = ( r . iy & 0xFF ) + 256 * value ; this . z80 . setState ( r ) ; } param non_maskable param data public generateInterrupt ( non_maskable , data ) { this . z80 . interrupt ( non_maskable , data ) ; this . cpuLoadRangeCounter ++ ; if ( this . cpuLoadRangeCounter >= this . cpuLoadRange ) { if ( this . cpuWithHaltTstates > 0 ) { this . cpuLoad = this . cpuLoadTstates / this . cpuWithHaltTstates ; this . cpuLoadTstates = 0 ; this . cpuWithHaltTstates = 0 ; this . cpuLoadRangeCounter = 0 ; } } } /**
     * Converts the Z80 flags object into a number.
     */ protected convertFlags ( flags ) { const f = 128 * flags . S + 64 * flags . Z + 32 * flags . Y + 16 * flags . H + 8 * flags . X + 4 * flags . P + 2 * flags . N + flags . C ; return f ; } /**
     * Returns all registers.
     */ protected getAllRegisters ( ) { const r = this . z80 . getState ( ) ; const flags = this . convertFlags ( r . flags ) ; const flags2 = this . convertFlags ( r . flags_prime ) ; const regs = { pc : r . pc , sp : r . sp , af : r . a * 256 + flags , bc : r . b * 256 + r . c , de : r . d * 256 + r . e , hl : r . h * 256 + r . l , ix : r . ix , iy : r . iy , af2 : r . a_prime * 256 + flags2 , bc2 : r . b_prime * 256 + r . c_prime , de2 : r . d_prime * 256 + r . e_prime , hl2 : r . h_prime * 256 + r . l_prime , i : r . i , r : r . r , im : r . imode , iff1 : r . iff1 , iff2 : r . iff2 } ; return regs ; } /**
     * Returns the register data in the Z80Registers format.
     */ public getRegisterData ( ) { const r = this . getAllRegisters ( ) ; const slots = this . memory . getSlots ( ) || [ ] ; const regData = Z80RegistersClass . getRegisterData ( r . pc , r . sp , r . af , r . bc , r . de , r . hl , r . ix , r . iy , r . af2 , r . bc2 , r . de2 , r . hl2 , r . i , r . r , r . im , slots ) ; return regData ; } /**
     * Returns the register, opcode and sp contents data,
     */ public getHistoryData ( ) { const regData = this . getRegisterData ( ) ; const startHist = regData . length ; const histData = new Uint16Array ( startHist + 3 ) ; histData . set ( regData ) ; const z80 = this . z80 ; const pc = z80 . pc ; const opcodes = this . memory . getMemory32 ( pc ) ; histData [ startHist ] = opcodes & 0xFFFF ; histData [ startHist + 1 ] = opcodes >>> 16 ; const sp = z80 . sp ; const spContents = this . memory . getMemory16 ( sp ) ; histData [ startHist + 2 ] = spContents ; return histData ; } /**
     * Returns the size the serialized object would consume.
     */ public getSerializedSize ( ) { const memBuffer = new MemBuffer ( ) ; this . serialize ( memBuffer ) ; const size = memBuffer . getSize ( ) ; return size ; } /**
     * Converts the Z80 flags object into a number.
     */ protected revConvertFlags ( flags ) { const f = { S : ( flags >>> 7 ) & 0x01 , Z : ( flags >>> 6 ) & 0x01 , Y : ( flags >>> 5 ) & 0x01 , H : ( flags >>> 4 ) & 0x01 , X : ( flags >>> 3 ) & 0x01 , P : ( flags >>> 2 ) & 0x01 , N : ( flags >>> 1 ) & 0x01 , C : flags & 0x01 , } ; return f ; } /**
     * Serializes the object.
     */ public serialize ( memBuffer ) { const r = this . getAllRegisters ( ) ; memBuffer . write16 ( r . pc ) ; memBuffer . write16 ( r . sp ) ; memBuffer . write16 ( r . af ) ; memBuffer . write16 ( r . bc ) ; memBuffer . write16 ( r . de ) ; memBuffer . write16 ( r . hl ) ; memBuffer . write16 ( r . ix ) ; memBuffer . write16 ( r . iy ) ; memBuffer . write16 ( r . af2 ) ; memBuffer . write16 ( r . bc2 ) ; memBuffer . write16 ( r . de2 ) ; memBuffer . write16 ( r . hl2 ) ; memBuffer . write8 ( r . i ) ; memBuffer . write8 ( r . r ) ; memBuffer . write8 ( r . im ) ; memBuffer . write8 ( r . iff1 ) ; memBuffer . write8 ( r . iff2 ) ; const s = this . z80 . getState ( ) ; memBuffer . write8 ( Number ( s . halted ) ) ; memBuffer . write8 ( Number ( s . do_delayed_di ) ) ; memBuffer . write8 ( Number ( s . do_delayed_ei ) ) ; memBuffer . writeNumber ( this . remainingInterruptTstates ) ; memBuffer . writeNumber ( this . cpuTstatesCounter ) ; } /**
     * Deserializes the object.
     */ public deserialize ( memBuffer ) { let r = new Object ( ) as any ; r . pc = memBuffer . read16 ( ) ; r . sp = memBuffer . read16 ( ) ; const af = memBuffer . read16 ( ) ; r . a = af >>> 8 ; r . flags = this . revConvertFlags ( af & 0xFF ) ; const bc = memBuffer . read16 ( ) ; r . b = bc >>> 8 ; r . c = bc & 0xFF ; const de = memBuffer . read16 ( ) ; r . d = de >>> 8 ; r . e = de & 0xFF ; const hl = memBuffer . read16 ( ) ; r . h = hl >>> 8 ; r . l = hl & 0xFF ; r . ix = memBuffer . read16 ( ) ; r . iy = memBuffer . read16 ( ) ; const af2 = memBuffer . read16 ( ) ; r . a_prime = af2 >>> 8 ; r . flags_prime = this . revConvertFlags ( af2 & 0xFF ) ; const bc2 = memBuffer . read16 ( ) ; r . b_prime = bc2 >>> 8 ; r . c_prime = bc2 & 0xFF ; const de2 = memBuffer . read16 ( ) ; r . d_prime = de2 >>> 8 ; r . e_prime = de2 & 0xFF ; const hl2 = memBuffer . read16 ( ) ; r . h_prime = hl2 >>> 8 ; r . l_prime = hl2 & 0xFF ; r . i = memBuffer . read8 ( ) ; r . r = memBuffer . read8 ( ) ; r . imode = memBuffer . read8 ( ) ; r . iff1 = memBuffer . read8 ( ) ; r . iff2 = memBuffer . read8 ( ) ; r . halted = ( memBuffer . read8 ( ) != 0 ) ; r . do_delayed_di = ( memBuffer . read8 ( ) != 0 ) ; r . do_delayed_ei = ( memBuffer . read8 ( ) != 0 ) ; r . cycle_counter = 0 ; const z80 = this . z80 ; z80 . setState ( r ) ; this . remainingInterruptTstates = memBuffer . readNumber ( ) ; this . cpuTstatesCounter = memBuffer . readNumber ( ) ; this . cpuLoadTstates = 0 ; this . cpuWithHaltTstates = 0 ; this . cpuLoad = 1.0 ; } }