import { MemBuffer , Serializeable } from '../../misc/membuffer' ; import { Utility } from '../../misc/utility' ; /**
 * Watchpoint class used by 'watchPointMemory'.
 */ interface SimWatchpoint { read : number ; write : number ; } /**
 * The memory in the banks can be ROM, RAM or completely unused.
 */ export enum BankType { ROM = 0 , RAM = 1 , UNUSED = 2 } /**
 * Represents the simulated memory.
 * It is a base class to allow memory paging etc.
 * The simulated memory always works with slots although they might not be visible
 * to the outside.
 * I.e. the ZX48K is built of 4 slots per 16K. 1rst is ROM the other 3 are RAM.
 * To the outside is does not show any of these slots.
 * But for configuration (what is ROM/RAM) it is required.
 */ export class SimulatedMemory implements Serializeable { protected memoryData : Uint8Array ; protected slots : number [ ] ; protected bankTypes : BankType [ ] ; protected bankSize : number ; protected shiftCount : number ; protected visualMemory : Array < number > ; protected VISUAL_MEM_SIZE_SHIFT = 8 ; protected VISUAL_MEM_COL_READ = 1 ; protected VISUAL_MEM_COL_WRITE = 2 ; protected VISUAL_MEM_COL_PROG = 3 ; public watchpointHit : boolean ; public hitAddress : number ; public hitAccess : string ; protected watchPointMemory : Array < SimWatchpoint > ; param slotCount param bankCount constructor ( slotCount , bankCount ) { Utility . assert ( bankCount >= slotCount ) ; this . visualMemory = new Array < number > ( 1 << ( 16 - this . VISUAL_MEM_SIZE_SHIFT ) ) ; this . clearVisualMemory ( ) ; this . bankSize = 0x10000 / slotCount ; this . memoryData = new Uint8Array ( bankCount * this . bankSize ) ; this . bankTypes = new Array < BankType > ( bankCount ) ; this . bankTypes . fill ( BankType . RAM ) ; let sc = slotCount ; let bits = 0 ; while ( sc > 1 ) { bits ++ ; sc /= 2 ; } this . shiftCount = 16 - bits ; this . slots = new Array < number > ( slotCount ) ; for ( let i = 0 ; i < slotCount ; i ++ ) this . slots [ i ] = i ; this . clearHit ( ) ; this . watchPointMemory = Array . from ( { length : 0x10000 } , ( ) => ( { read : 0 , write : 0 } ) ) ; } /**
     * Clears the whole memory (all banks) with 0s.
     * So far only used by unit tests.
     */ public clear ( ) { this . memoryData . fill ( 0 ) ; } returns public getMemoryData ( ) { return this . memoryData ; } param bank param enableRom /**
     * Returns the size the serialized object would consume.
     */ public getSerializedSize ( ) { const memBuffer = new MemBuffer ( ) ; this . serialize ( memBuffer ) ; const size = memBuffer . getSize ( ) ; return size ; } /**
     * Serializes the object.
     */ public serialize ( memBuffer ) { memBuffer . write8 ( this . slots . length ) ; for ( const bank of this . slots ) memBuffer . write8 ( bank ) ; memBuffer . writeArrayBuffer ( this . memoryData ) ; } /**
     * Deserializes the object.
     */ public deserialize ( memBuffer ) { const slotLength = memBuffer . read8 ( ) ; this . slots = [ ] ; for ( let i = 0 ; i < slotLength ; i ++ ) this . slots . push ( memBuffer . read8 ( ) ) ; const buffer = memBuffer . readArrayBuffer ( ) ; Utility . assert ( buffer . length == this . memoryData . byteLength ) ; this . memoryData . set ( buffer ) ; this . clearVisualMemory ( ) ; } param address param size param access public setWatchpoint ( address , size , access ) { const readAdd = access . includes ( 'r' ) ? 1 : 0 ; const writeAdd = access . includes ( 'w' ) ? 1 : 0 ; for ( let i = 0 ; i < size ; i ++ ) { const wp = this . watchPointMemory [ address & 0xFFFF ] ; wp . read += readAdd ; wp . write += writeAdd ; address ++ ; } } param address param size param access public removeWatchpoint ( address , size , access ) { const readAdd = access . includes ( 'r' ) ? 1 : 0 ; const writeAdd = access . includes ( 'w' ) ? 1 : 0 ; for ( let i = 0 ; i < size ; i ++ ) { const wp = this . watchPointMemory [ address & 0xFFFF ] ; if ( wp . read > 0 ) wp . read -= readAdd ; if ( wp . write > 0 ) wp . write -= writeAdd ; address ++ ; } } /**
     * Clears the hit flag and the arrays.
     */ public clearHit ( ) { this . hitAddress = - 1 ; this . hitAccess = '' ; } public read8 ( addr ) { const wp = this . watchPointMemory [ addr ] ; if ( wp ) { if ( ( this . hitAddress < 0 ) && wp . read > 0 ) { this . hitAddress = addr ; this . hitAccess = 'r' ; } } this . visualMemory [ addr >>> this . VISUAL_MEM_SIZE_SHIFT ] = this . VISUAL_MEM_COL_READ ; const slotIndex = addr >>> this . shiftCount ; const bankNr = this . slots [ slotIndex ] ; const ramAddr = bankNr * this . bankSize + ( addr & ( this . bankSize - 1 ) ) ; const value = this . memoryData [ ramAddr ] ; return value ; } public write8 ( addr , val ) { const wp = this . watchPointMemory [ addr ] ; if ( wp ) { if ( ( this . hitAddress < 0 ) && wp . write > 0 ) { this . hitAddress = addr ; this . hitAccess = 'w' ; } } this . visualMemory [ addr >>> this . VISUAL_MEM_SIZE_SHIFT ] = this . VISUAL_MEM_COL_WRITE ; const slotIndex = addr >>> this . shiftCount ; const bankNr = this . slots [ slotIndex ] ; if ( this . bankTypes [ bankNr ] == BankType . RAM ) { const ramAddr = bankNr * this . bankSize + ( addr & ( this . bankSize - 1 ) ) ; this . memoryData [ ramAddr ] = val ; } } public getMemory16 ( addr ) { let address = addr & ( this . bankSize - 1 ) ; let slotIndex = addr >>> this . shiftCount ; let bankNr = this . slots [ slotIndex ] ; let ramAddr = bankNr * this . bankSize + address ; const mem = this . memoryData ; let value = mem [ ramAddr ] ; address ++ ; if ( address < this . bankSize ) { ramAddr ++ ; } else { slotIndex = ( ( addr + 1 ) & 0xFFFF ) >>> this . shiftCount ; bankNr = this . slots [ slotIndex ] ; ramAddr = bankNr * this . bankSize ; } value += mem [ ramAddr ] << 8 ; return value ; } public getMemory32 ( addr ) { let address = addr & ( this . bankSize - 1 ) ; let slotIndex = addr >>> this . shiftCount ; let bankNr = this . slots [ slotIndex ] ; let ramAddr = bankNr * this . bankSize + address ; const mem = this . memoryData ; let value = mem [ ramAddr ] ; if ( address < this . bankSize - 3 ) { value += mem [ ++ ramAddr ] << 8 ; value += mem [ ++ ramAddr ] << 16 ; value += mem [ ++ ramAddr ] * 256 * 65536 ; } else { let mult = 256 ; for ( let i = 3 ; i > 0 ; i -- ) { addr ++ ; address = addr & ( this . bankSize - 1 ) ; slotIndex = ( addr & 0xFFFF ) >>> this . shiftCount ; bankNr = this . slots [ slotIndex ] ; ramAddr = bankNr * this . bankSize + address ; value += mem [ ramAddr ] * mult ; mult *= 256 ; } } return value ; } param offset param data public writeMemoryData ( offset , data ) { let size = data . length ; if ( offset + size > this . memoryData . length ) size = this . memoryData . length - offset ; let start = 0 ; const bankSize = this . bankSize ; while ( size > 0 ) { const bank = Math . floor ( offset / bankSize ) ; const offsRemainder = offset % bankSize ; let minSize = size ; if ( offsRemainder + minSize > bankSize ) minSize = bankSize - offsRemainder ; const bankType = this . bankTypes [ bank ] ; if ( bankType == BankType . RAM ) { const data2 = data . slice ( start , minSize ) ; this . memoryData . set ( data2 , offset ) ; } start += minSize ; offset += minSize ; size -= minSize ; } } public setVisualProg ( addr ) { this . visualMemory [ addr >>> this . VISUAL_MEM_SIZE_SHIFT ] = this . VISUAL_MEM_COL_PROG ; } param addr returns param bank param fillValue public fillBank ( bank , fillValue ) { const ramAddr = bank * this . bankSize ; this . memoryData . fill ( fillValue , ramAddr , ramAddr + this . bankSize ) ; } /**
     * Associates a slot with a bank number.
     */ public setSlot ( slot , bank ) { this . slots [ slot ] = bank ; } /**
     * Returns the slots array, or undefined if not paged.
     */ public getSlots ( ) { return undefined ; } param startAddress param size public readBlock ( startAddress , size ) { const totalBlock = new Uint8Array ( size ) ; let offset = 0 ; let addr = startAddress ; const mem = this . memoryData ; while ( size > 0 ) { const slot = ( addr & 0xFFFF ) >>> this . shiftCount ; const bankAddr = addr & ( this . bankSize - 1 ) ; const bank = this . slots [ slot ] ; let ramAddr = bank * this . bankSize + bankAddr ; let blockEnd = bankAddr + size ; if ( blockEnd > this . bankSize ) blockEnd = this . bankSize ; const partBlockSize = blockEnd - bankAddr ; const partBlock = mem . subarray ( ramAddr , ramAddr + partBlockSize ) ; totalBlock . set ( partBlock , offset ) ; offset += partBlockSize ; size -= partBlockSize ; addr += partBlockSize ; } return totalBlock ; } param startAddress param totalBlock public writeBlock ( startAddress , totalBlock ) { if ( ! ( totalBlock instanceof Uint8Array ) ) totalBlock = new Uint8Array ( totalBlock ) ; let offset = 0 ; let addr = startAddress ; let size = totalBlock . length ; const mem = this . memoryData ; while ( size > 0 ) { const slot = ( addr & 0xFFFF ) >>> this . shiftCount ; const bankAddr = addr & ( this . bankSize - 1 ) ; const bank = this . slots [ slot ] ; let ramAddr = bank * this . bankSize + bankAddr ; let blockEnd = bankAddr + size ; if ( blockEnd > this . bankSize ) blockEnd = this . bankSize ; const partBlockSize = blockEnd - bankAddr ; if ( this . bankTypes [ bank ] != BankType . UNUSED ) { const partBlock = totalBlock . subarray ( offset , offset + partBlockSize ) ; mem . set ( partBlock , ramAddr ) ; } offset += partBlockSize ; size -= partBlockSize ; addr += partBlockSize ; } return totalBlock ; } param bank param block public writeBank ( bank , block ) { if ( block . length != this . bankSize ) throw Error ( "writeBank: Block length " + block . length + " not allowed. Expected " + this . bankSize + "." ) ; const ramAddr = bank * this . bankSize ; this . memoryData . set ( block , ramAddr ) ; } /**
     * Clears the visual buffer.
     */ public clearVisualMemory ( ) { this . visualMemory . fill ( 0 ) ; } returns public getVisualMemory ( ) { return this . visualMemory ; } }