////////////////////////////////////////////////////////////////////// // // // cpuMemory.pas: CPU memory subsystem // // This handles all memory access and cartridge control // // // // The contents of this file are subject to the Bottled Light // // Public License Version 1.0 (the "License"); you may not use this // // file except in compliance with the License. You may obtain a // // copy of the License at http://www.bottledlight.com/BLPL/ // // // // Software distributed under the License is distributed on an // // "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or // // implied. See the License for the specific language governing // // rights and limitations under the License. // // // // The Original Code is the Mappy VM Core, released April 1st, 2003 // // The Initial Developer of the Original Code is Bottled Light, // // Inc. Portions created by Bottled Light, Inc. are Copyright // // (C) 2001 - 2003 Bottled Light, Inc. All Rights Reserved. // // // // Author(s): // // Michael Noland (joat), michael@bottledlight.com // // // // Changelog: // // 1.0: First public release (April 1st, 2003) // // // // Greets: // // Forgotten provided C code to handle EEPROM access which I used // // to fix my version. This was before VBA was released under the // // GPL. // // // // Changelog: // // 1.0: First public release // // // // Notes: // // CPU writes currently trigger a log write, thanks to my attempt // // to support the e-Reader. This involves a ROM writes to a high // // address that needs to succeed (i.e. portions of the ROM space // // for the e-Reader are actually RAM or control registers, or at // // the very least, a single word). In addition, at least the // // american version of the e-Reader has strangely banked Flash // // ROM, using a typical Flash control sequence ($B0 $xx, which // // would normally be just $B0 to suspend a prior flash sector // // erase command, but instead switches to bank xx) to bankswitch // // between two 64 KB pages. I've yet to test if all 8 bits are // // used, or only the LSB, but I'd wager its only the LSB. // // Right now, the e-Reader support is disabled, see // // WriteROMBaby32 if you're interested in re-enabling the ROM // // as RAM emulation. I didn't keep it there because I didn't // // find out the extents of the special address space, only a // // single address seems to be currently used. // // // // The code in vmAddBreakpoint and vmRemoveBreakpoint could be // // simplified a bit by altering the code in the breakpoint class. // // The toggle primitive is a remenant from when the code was all // // a single project, no core seperation, and at the time it was // // all that was needed. // // // // I'm pretty sure the SRAM support is broken, or at least dodgy. // // // There is some preliminary signature support, which would allow // // the user interface to display which regions of ROM are data, // // and which regions contain either ARM or Thumb opcodes. It is // // currently IDFEF'ed out, and the sig writes were never added to // // data accesses. In any event, theres no mechanism for the user // // interface to access the sig array right now. // // // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// unit cpuMemory; ////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// interface //////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// uses Math, nexus, AddressSpace; ////////////////////////////////////////////////////////////////////// procedure SetWaitStates; function memReadByte(address: uint32): uint32; procedure memWriteByte(address: uint32; data: uint32); function memReadHalfWord(address: uint32): uint32; procedure memWriteHalfWord(address: uint32; data: uint32); function memReadWord(address: uint32): uint32; // Does the rotation shiz function memLoadWord(address: uint32): uint32; procedure memWriteWord(address: uint32; data: uint32); function memReadHalfWordUnc(address: uint32): uint32; function memReadWordUnc(address: uint32): uint32; procedure memWriteHalfWordUnc(address: uint32; data: uint32); procedure memWriteWordUnc(address: uint32; data: uint32); function memStopAtAddy(address: uint32): boolean; procedure InitMemory; procedure cpuMemFree; ////////////////////////////////////////////////////////////////////// // Exported functions //////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// function vmReadByte(address: uint32): uint8; function vmReadHalfword(address: uint32): uint16; function vmReadWord(address: uint32): uint32; procedure vmWriteByte(address: uint32; data: uint8); procedure vmWriteHalfword(address: uint32; data: uint16); procedure vmWriteWord(address: uint32; data: uint32); procedure vmAddBreakpoint(address: uint32; soft: boolean); procedure vmRemoveBreakpoint(address: uint32; mask: TBreakpointModes); procedure vmSoftBreakpoints(active: boolean); function vmIsBreakpoint(address: uint32): TBreakpointModes; procedure vmInsertCartridge(data: pointer; size: integer); procedure vmRemoveCartridge; procedure vmLockMemory(var banks: TvmMemoryLock1); procedure vmUnlockMemory(const banks: TvmMemoryLock1); function vmGetCartInfo(info: PvmOpaqueChunk): integer; procedure vmSetCartInfo(size: integer; info: PvmOpaqueChunk); ////////////////////////////////////////////////////////////////////// exports vmReadByte, vmReadHalfword, vmReadWord, vmWriteByte, vmWriteHalfword, vmWriteWord, vmAddBreakpoint, vmRemoveBreakpoint, vmSoftBreakpoints, vmIsBreakpoint, vmInsertCartridge, vmRemoveCartridge, vmLockMemory, vmUnlockMemory, vmGetCartInfo, vmSetCartInfo; ////////////////////////////////////////////////////////////////////// type TEepromMode = (emIdle, emReadAddress, emReadData, emReadData2, emWriteData); TFlashMode = (fmIdle, fmWriteData, fmProductID, fmExtended, fmErase, fmSectorErase, fmBankswitch); ////////////////////////////////////////////////////////////////////// // State Variables /////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// var // Memory spaces systemROM: array[0..SYSTEM_ROM_MASK] of byte; // 00000000h..00003FFFh exWRAM: array[0..EX_WRAM_MASK] of byte; // 02000000h..0203FFFFh WRAM: array[0..WRAM_MASK] of byte; // 03000000h..03007FFFh registers: array[0..REGISTERS_MASK] of byte; // 04000000h.. palette: array[0..PALETTE_MASK] of byte; // 05000000h..050003FFh VRAM: array[0..VRAM_MASK] of byte; // 06000000h..06017FFFh OAM: array[0..OAM_MASK] of byte; // 07000000h..070003FFh cartRAM: array[0..(SRAM_MASK+1)*2-1] of byte; // 0E000000h.. cartRAMdirty: boolean; lastAddress: uint32; // CPU status cpuGlobalTicks: uint32; cpuStopped, cpuHalted: boolean; irqPending: boolean; regs: array[0..36+2] of uint32; SPSR: uint32; // Events HBlankEvent: integer; enteringHBlank: boolean; eventCycleDelta: integer; eventCyclesLeft: integer; // Sound soundA, soundB: TDSoundRecord; sound1: TSoundChannel1; sound2: TSoundChannel2; sound3: TSoundChannel3; sound4: TSoundChannel4; ////////////////////////////////////////////////////////////////////// // Globals shared between the emulation functions //////////////////// ////////////////////////////////////////////////////////////////////// // CPSR components (packed into regs[CPSR] for savestates) carry: boolean; overflow: boolean; negative: boolean; zero: boolean; irqDisabled: boolean; fiqDisabled: boolean; thumbMode: boolean; cpuMode: integer; ////////////////////////////////////////////////////////////////////// // Not saved ///////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// var // Only used in the decoders cpuCurrentOpcode: uint32; hitBreakpoint: boolean; barrelCarry: boolean; haveFlippedThumb: boolean; // Must be zero outside of vmExecute code path quota: integer; soundCyclesUndone: integer; soundQuotaAtLastFlush: integer; timerQuotaAtLastFlush: integer; // Maybe should be saved, but isn't right now eepromIndex, eepromByte, eepromAddress: uint32; eepromMode: TeepromMode; flashMode: TFlashMode; eepromBuffer: array[0..15] of byte; flashIndex: uint32; cartLoaded: boolean; cartROM1, cartROM2: Puint8Array; cartSize1, cartSize2: uint32; cartMask1, cartMask2: uint32; downKeys: uint16; cpuSourceDebug: boolean; ////////////////////////////////////////////////////////////////////// // Signature support (will be used for smart disassembly) // Note: no support has been added to the memory read/write code yet ////////////////////////////////////////////////////////////////////// {$IFDEF SIGNATURES} const ARM_READ = 1; THUMB_READ = 2; DATA_READ = 4; DATA_WRITE = 8; var sigs: array[0..16777215] of byte; {$ENDIF} ////////////////////////////////////////////////////////////////////// implementation /////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// uses SysUtils, cpuMisc, cpuGraphics, cpuSound, cpuPeripherals; ////////////////////////////////////////////////////////////////////// type TBreakpointMemory = class private bpBanks: array[$0..$F] of Puint8Array; bpBankMasks: array[$0..$F] of uint32; FCartSize1, FCartSize2: uint32; bcartROM1, bcartROM2: Puint8Array; bsystemROM: array[0..(SYSTEM_ROM_MASK+1) shr 4-1] of byte; // 00000000h..00003FFFh bexWRAM: array[0..(EX_WRAM_MASK+1) shr 4-1] of byte; // 02000000h..0203FFFFh bWRAM: array[0..(WRAM_MASK+1) shr 4-1] of byte; // 03000000h..03007FFFh bpalette: array[0..(PALETTE_MASK+1) shr 4-1] of byte; // 05000000h..050003FFh bVRAM: array[0..(VRAM_MASK+1) shr 4-1] of byte; // 06000000h..06017FFFh bOAM: array[0..(OAM_MASK+1) shr 4-1] of byte; // 07000000h..070003FFh null: uint32; procedure SetCartSize1(const Value: uint32); procedure SetCartSize2(const Value: uint32); public constructor Create; destructor Destroy; override; property cartSize1: uint32 read FCartSize1 write SetCartSize1; property cartSize2: uint32 read FCartSize2 write SetCartSize2; function isBreakpoint(address: uint32): boolean; function toggleBreakpoint(address: uint32): boolean; end; ////////////////////////////////////////////////////////////////////// // Non-state Variables (all regenerated) ///////////////////////////// ////////////////////////////////////////////////////////////////////// var bankNTimes: array[$0..$F] of byte; bankSTimes: array[$0..$F] of byte; interestingAddresses: TBreakpointMemory; breakpoints, sourcePoints: TBreakpointMemory; ReadByteFuncs, ReadHalfFuncs, ReadWordFuncs: array[0..$F] of function (address: uint32): uint32; WriteByteFuncs, WriteHalfFuncs, WriteWordFuncs: array[0..$F] of procedure (address, data: uint32); memBanks: array[0..15] of pointer; memMasks: array[0..15] of uint32; curBank: byte; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// function ReadNOP(address: uint32): uint32; begin Result := 0; end; ////////////////////////////////////////////////////////////////////// procedure WriteNOP(address, data: uint32); begin // end; ////////////////////////////////////////////////////////////////////// // TODO: This is in-progress code for the e-Reader support // also: see the table set functions which reference these ////////////////////////////////////////////////////////////////////// procedure WriteROMBaby8(address, data: uint32); begin logWriteLn(Format('ROM[%8.8x]=%2.2x at %8.8x', [address, data, regs[R15]])); end; procedure WriteROMBaby16(address, data: uint32); begin logWriteLn(Format('ROM[%8.8x]=%4.4x at %8.8x', [address, data, regs[R15]])); end; procedure WriteROMBaby32(address, data: uint32); begin // if cartMask1 > 0 then Puint32(@(cartROM1^[address and cartMask1]))^ := data; logWriteLn(Format('ROM[%8.8x]=%8.8x at %8.8x', [address, data, regs[R15]])); end; ////////////////////////////////////////////////////////////////////// function ReadBIOS_08(address: uint32): uint32; begin Dec(quota); if regs[R15] < $02000000 then Result := systemROM[address and SYSTEM_ROM_MASK] else Result := 0; end; ////////////////////////////////////////////////////////////////////// function ReadBIOS_16(address: uint32): uint32; begin Dec(quota); if regs[R15] < $02000000 then Result := Puint16(@(systemROM[address and SYSTEM_ROM_MASK]))^ else Result := 0; end; ////////////////////////////////////////////////////////////////////// function ReadBIOS_32(address: uint32): uint32; begin Dec(quota); if regs[R15] < $02000000 then Result := Puint32(@(systemROM[address and SYSTEM_ROM_MASK]))^ else Result := 0; end; ////////////////////////////////////////////////////////////////////// function ReadExWRAM_08(address: uint32): uint32; begin Dec(quota, 3); Result := exWRAM[address and EX_WRAM_MASK]; end; ////////////////////////////////////////////////////////////////////// function ReadExWRAM_16(address: uint32): uint32; begin Dec(quota, 3); Result := Puint16(@(exWRAM[address and EX_WRAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// function ReadExWRAM_32(address: uint32): uint32; begin Dec(quota, 6); Result := Puint32(@(exWRAM[address and EX_WRAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// procedure WriteExWRAM_08(address, data: uint32); begin Dec(quota, 3); exWRAM[address and EX_WRAM_MASK] := data; end; ////////////////////////////////////////////////////////////////////// procedure WriteExWRAM_16(address, data: uint32); begin Dec(quota, 3); Puint16(@(exWRAM[address and EX_WRAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// procedure WriteExWRAM_32(address, data: uint32); begin Dec(quota, 6); Puint32(@(exWRAM[address and EX_WRAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// function ReadWRAM_08(address: uint32): uint32; begin Dec(quota); Result := WRAM[address and WRAM_MASK]; end; ////////////////////////////////////////////////////////////////////// function ReadWRAM_16(address: uint32): uint32; begin Dec(quota); Result := Puint16(@(WRAM[address and WRAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// function ReadWRAM_32(address: uint32): uint32; begin Dec(quota); Result := Puint32(@(WRAM[address and WRAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// procedure WriteWRAM_08(address, data: uint32); begin Dec(quota); WRAM[address and WRAM_MASK] := data; end; ////////////////////////////////////////////////////////////////////// procedure WriteWRAM_16(address, data: uint32); begin Dec(quota); Puint16(@(WRAM[address and WRAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// procedure WriteWRAM_32(address, data: uint32); begin Dec(quota); Puint32(@(WRAM[address and WRAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// function ReadIO_08(address: uint32): uint32; begin Dec(quota); address := address and REGISTERS_MASK; case address of // DMA source and dests always read $91009100 (test 3) $0B0, $0B2, $0B4, $0B6, $0BC, $0BE, $0C0, $0C2, $0C8, $0CA, $0CC, $0CE, $0D4, $0D6, $0D8, $0DA: Result := $00; $0B1, $0B3, $0B5, $0B7, $0BD, $0BF, $0C1, $0C3, $0C9, $0CB, $0CD, $0CF, $0D5, $0D7, $0D9, $0DB: Result := $91; // DMA count registers always read 0 $0B8, $0B9, $0C4, $0C5, $0D0, $0D1, $0DC, $0DD: Result := $00; // BLEND_CR bits 14-15 are fixed at 0 $051: Result := registers[$051] and $3F; // BLEND_A bits 5-7 are fixed at 0 $052: Result := registers[$052] and $1F; // BLEND_B bits 5-7 are fixed at 0 $053: Result := registers[$053] and $1F; // BLEND_Y appears to return $881B when I write $FFFF $054: Result := registers[$054] and $1B; $055: Result := registers[$055] and $88; // Timer CR high order regs are fixed at 0 $103, $107, $10B, $10F: Result := 0; // Timer CR bits 3-5 are fixed at 0 $102, $106, $10A, $10E: Result := registers[address] and $C7; // Write-only registers that are still untested $010..$047, $04C..$04D, $056..$05F, $0A0..$0AF: Result := 0; // Timer counter reads require flushing the timer values TIMER0: begin flushTimers; Result := Puint16(@(registers[TIMER0]))^ and $FF; end; TIMER0+1: begin flushTimers; Result := Puint16(@(registers[TIMER0]))^ shr 8; end; TIMER1: begin flushTimers; Result := Puint16(@(registers[TIMER1]))^ and $FF; end; TIMER1+1: begin flushTimers; Result := Puint16(@(registers[TIMER1]))^ shr 8; end; TIMER2: begin flushTimers; Result := Puint16(@(registers[TIMER2]))^ and $FF; end; TIMER2+1: begin flushTimers; Result := Puint16(@(registers[TIMER2]))^ shr 8; end; TIMER3: begin flushTimers; Result := Puint16(@(registers[TIMER3]))^ and $FF; end; TIMER3+1: begin flushTimers; Result := Puint16(@(registers[TIMER3]))^ shr 8; end; KEYS: Result := downkeys and $FF; KEYS+1: Result := (downkeys shr 8) and $3; SOUND_ENABLED: begin Result := registers[SOUND_ENABLED] and 128; if sound1.enabled then Result := Result or 1; if sound2.enabled then Result := Result or 2; if sound3.enabled then Result := Result or 4; if sound4.enabled then Result := Result or 8; end; else Result := registers[address]; end; end; ////////////////////////////////////////////////////////////////////// function ReadIO_16(address: uint32): uint32; begin Dec(quota); address := address and REGISTERS_MASK; case address of // DMA source and dests always read $91009100 (test 3) $0B0, $0B2, $0B4, $0B6, $0BC, $0BE, $0C0, $0C2, $0C8, $0CA, $0CC, $0CE, $0D4, $0D6, $0D8, $0DA: Result := $9100; // DMA count registers always read 0 $0B8, $0C4, $0D0, $0DC: Result := $00; // BLEND_CR bits 14-15 are fixed at 0 $050: Result := Puint16(@(registers[$050]))^ and $3FFF; // BLEND_A bits 5-7 are fixed at 0, BLEND_B bits 5-7 are fixed at 0 $052: Result := Puint16(@(registers[$052]))^ and $1F1F; // BLEND_Y appears to return $881B when I write $FFFF $054: Result := Puint16(@(registers[$054]))^ and $881B; // Timer CR bits 3-5,8-15 are fixed at 0 $102, $106, $10A, $10E: Result := registers[address] and $C7; // Write-only registers that are still untested $010..$046, $04C, $056..$05E, $0A0..$0AE: Result := 0; // Timer counter reads require flushing the timer values TIMER0: begin flushTimers; Result := Puint16(@(registers[TIMER0]))^; end; TIMER1: begin flushTimers; Result := Puint16(@(registers[TIMER1]))^; end; TIMER2: begin flushTimers; Result := Puint16(@(registers[TIMER2]))^; end; TIMER3: begin flushTimers; Result := Puint16(@(registers[TIMER3]))^; end; KEYS: Result := downkeys and $3FF; SOUND_ENABLED: begin Result := Puint16(@(registers[SOUND_ENABLED]))^ and 128; if sound1.enabled then Result := Result or 1; if sound2.enabled then Result := Result or 2; if sound3.enabled then Result := Result or 4; if sound4.enabled then Result := Result or 8; end; else Result := Puint16(@(registers[address]))^; end; end; ////////////////////////////////////////////////////////////////////// function ReadIO_32(address: uint32): uint32; begin Dec(quota); address := address and REGISTERS_MASK; case address of // DMA source and dests always read $91009100 (test 3) $0B0, $0B4, $0BC, $0C0, $0C8, $0CC, $0D4, $0D8: Result := $91009100; // BLEND_CR bits 14-15 are fixed at 0, BLEND_A bits 5-7 are fixed at 0, BLEND_B bits 5-7 are fixed at 0 $050: Result := Puint32(@(registers[$050]))^ and $1F1F3FFF; // BLEND_Y appears to return $881B when I write $FFFF, and I'll guess the rest of the bits are 0 $054: Result := Puint16(@(registers[$054]))^ and $881B; // Write-only registers that are still untested, but are adjacent to valid regs $010..$040, $0A0..$0AC: Result := 0; $044, $04C: Result := Puint32(@(registers[address]))^ and $FFFF0000; // Half of this is the CR and half the count, the cr is valid, but count reads 0 $0B8, $0C4, $0D0, $0DC: Result := Puint32(@(registers[address]))^ and $FFFF0000; // Timer counter reads require flushing the timer values, and timer CR bits 3-5,8-15 are fixed at 0 TIMER0: begin flushTimers; Result := Puint32(@(registers[address]))^ and $00C7FFFF; end; TIMER1: begin flushTimers; Result := Puint32(@(registers[address]))^ and $00C7FFFF; end; TIMER2: begin flushTimers; Result := Puint32(@(registers[address]))^ and $00C7FFFF; end; TIMER3: begin flushTimers; Result := Puint32(@(registers[address]))^ and $00C7FFFF; end; KEYS: Result := downkeys and $3FF; SOUND_ENABLED: begin Result := Puint32(@(registers[SOUND_ENABLED]))^ and ($FFFF0000+128); if sound1.enabled then Result := Result or 1; if sound2.enabled then Result := Result or 2; if sound3.enabled then Result := Result or 4; if sound4.enabled then Result := Result or 8; end; else Result := Puint32(@(registers[address]))^; end; end; ////////////////////////////////////////////////////////////////////// procedure WriteIO_08(address, data: uint32); begin Dec(quota); address := address and REGISTERS_MASK; // Log it if we want to if logIORegisters then LogWriteLn(Format(' u8[$%3.3x]=$%2.2x', [address, data])); case address of BG2_X..BG2_X+3: begin registers[address] := data; registers[BG2_X_DIRTY] := 1; if (Puint32(@(registers[BG2_X]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG2_X]))^ := Puint32(@(registers[BG2_X]))^ or $F0000000; Puint32(@(registers[BG2_X_LATCH]))^ := Puint32(@(registers[BG2_X]))^; end; BG2_Y..BG2_Y+3: begin registers[address] := data; registers[BG2_Y_DIRTY] := 1; if (Puint32(@(registers[BG2_Y]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG2_Y]))^ := Puint32(@(registers[BG2_Y]))^ or $F0000000; Puint32(@(registers[BG2_Y_LATCH]))^ := Puint32(@(registers[BG2_Y]))^; end; BG3_X..BG3_X+3: begin registers[address] := data; registers[BG3_X_DIRTY] := 1; if (Puint32(@(registers[BG3_X]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG3_X]))^ := Puint32(@(registers[BG3_X]))^ or $F0000000; Puint32(@(registers[BG3_X_LATCH]))^ := Puint32(@(registers[BG3_X]))^; end; BG3_Y..BG3_Y+3: begin registers[address] := data; registers[BG3_Y_DIRTY] := 1; if (Puint32(@(registers[BG3_Y]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG3_Y]))^ := Puint32(@(registers[BG3_Y]))^ or $F0000000; Puint32(@(registers[BG3_X_LATCH]))^ := Puint32(@(registers[BG3_Y]))^; end; TIMER0, TIMER0+1, TIMER1, TIMER1+1, TIMER2, TIMER2+1, TIMER3, TIMER3+1: registers[address+$400] := data; TIMER0_CR: begin registers[address] := data; WriteTimerCR(0); end; TIMER1_CR: begin registers[address] := data; WriteTimerCR(1); end; TIMER2_CR: begin registers[address] := data; WriteTimerCR(2); end; TIMER3_CR: begin registers[address] := data; WriteTimerCR(3); end; KEYS..KEYS+1: ; DMA0_CR+1: begin registers[address] := data; if Puint16(@(registers[DMA0_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(0); end; DMA1_CR: begin registers[address] := data; if Puint16(@(registers[DMA1_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(1); end; DMA2_CR: begin registers[address] := data; if Puint16(@(registers[DMA2_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(2); end; DMA3_CR: begin registers[address] := data; if Puint16(@(registers[DMA3_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(3); end; IRQ_FLAGS: Puint16(@(registers[IRQ_FLAGS]))^ := Puint16(@(registers[IRQ_FLAGS]))^ and not data; IRQ_FLAGS+1: Puint16(@(registers[IRQ_FLAGS]))^ := Puint16(@(registers[IRQ_FLAGS]))^ and not (data shl 8); WAIT_STATE_CR: begin registers[address] := data; SetWaitStates; end; WAIT_STATE_CR+1: begin registers[address] := data and $3F; SetWaitStates; end; SOUND1_SWEEP: begin soundChanging; registers[address] := data; soundSetSound1Sweep; end; SOUND1_LENGTH..SOUND1_LENGTH+1: begin soundChanging; registers[address] := data; soundSetSound1Length; end; SOUND1_FREQUENCY..SOUND1_FREQUENCY+1: begin soundChanging; registers[address] := data; soundSetSound1Freq; end; SOUND2_LENGTH..SOUND2_LENGTH+1: begin soundChanging; registers[address] := data; soundSetSound2Length; end; SOUND2_FREQUENCY..SOUND2_FREQUENCY+1: begin soundChanging; registers[address] := data; soundSetSound2Freq; end; SOUND3_CR: begin soundChanging; registers[address] := data; soundSetSound3CR; end; SOUND3_LENGTH..SOUND3_VOLUME: begin soundChanging; registers[address] := data; soundSetSound3Length; end; SOUND3_FREQUENCY: begin soundChanging; registers[address] := data; soundSetSound3Freq; end; SOUND4_LENGTH..SOUND4_LENGTH+1: begin soundChanging; registers[address] := data; soundSetSound4Length; end; SOUND4_CR..SOUND4_CR+1: begin soundChanging; registers[address] := data; soundSetSound4CR; end; SOUNDA_FIFO..SOUNDA_FIFO+3: begin registers[address] := data; soundIncrementFifoA; end; SOUNDB_FIFO..SOUNDB_FIFO+3: begin registers[address] := data; soundIncrementFifoB; end; SOUND_DSOUND_CR, SOUND_DSOUND_CR+1: begin soundChanging; registers[address] := data; soundWriteDSoundCR; end; $301: begin if data and $80 <> 0 then begin // Halt cpuStopped := true; cpuHalted := true; quota := 0; end else begin // Stop cpuStopped := true; quota := 0; end; end; else registers[address] := data; end; end; ////////////////////////////////////////////////////////////////////// procedure WriteIO_16(address, data: uint32); begin Dec(quota); address := address and REGISTERS_MASK; // Log it if we want to if logIORegisters then LogWriteLn(Format('u16[$%3.3x]=$%4.4x', [address, data])); case address of BG2_X, BG2_X+2: begin Puint16(@(registers[address]))^ := data; registers[BG2_X_DIRTY] := 1; if (Puint32(@(registers[BG2_X]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG2_X]))^ := Puint32(@(registers[BG2_X]))^ or $F0000000; Puint32(@(registers[BG2_X_LATCH]))^ := Puint32(@(registers[BG2_X]))^; end; BG2_Y, BG2_Y+2: begin Puint16(@(registers[address]))^ := data; registers[BG2_Y_DIRTY] := 1; if (Puint32(@(registers[BG2_Y]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG2_Y]))^ := Puint32(@(registers[BG2_Y]))^ or $F0000000; Puint32(@(registers[BG2_Y_LATCH]))^ := Puint32(@(registers[BG2_Y]))^; end; BG3_X, BG3_X+2: begin Puint16(@(registers[address]))^ := data; registers[BG3_X_DIRTY] := 1; if (Puint32(@(registers[BG3_X]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG3_X]))^ := Puint32(@(registers[BG3_X]))^ or $F0000000; Puint32(@(registers[BG3_X_LATCH]))^ := Puint32(@(registers[BG3_X]))^; end; BG3_Y, BG3_Y+2: begin Puint16(@(registers[address]))^ := data; registers[BG3_Y_DIRTY] := 1; if (Puint32(@(registers[BG3_Y]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG3_Y]))^ := Puint32(@(registers[BG3_Y]))^ or $F0000000; Puint32(@(registers[BG3_Y_LATCH]))^ := Puint32(@(registers[BG3_Y]))^; end; TIMER0: Puint16(@(registers[TIMER0_LATCH]))^ := data; TIMER1: Puint16(@(registers[TIMER1_LATCH]))^ := data; TIMER2: Puint16(@(registers[TIMER2_LATCH]))^ := data; TIMER3: Puint16(@(registers[TIMER3_LATCH]))^ := data; TIMER0_CR: begin Puint16(@(registers[address]))^ := data; WriteTimerCR(0); end; TIMER1_CR: begin Puint16(@(registers[address]))^ := data; WriteTimerCR(1); end; TIMER2_CR: begin Puint16(@(registers[address]))^ := data; WriteTimerCR(2); end; TIMER3_CR: begin Puint16(@(registers[address]))^ := data; WriteTimerCR(3); end; KEYS: ; DMA0_CR: begin Puint16(@(registers[address]))^ := data; if Puint16(@(registers[DMA0_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(0); end; DMA1_CR: begin Puint16(@(registers[address]))^ := data; if Puint16(@(registers[DMA1_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(1); end; DMA2_CR: begin Puint16(@(registers[address]))^ := data; if Puint16(@(registers[DMA2_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(2); end; DMA3_CR: begin Puint16(@(registers[address]))^ := data; if Puint16(@(registers[DMA3_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(3); end; IRQ_FLAGS: Puint16(@(registers[IRQ_FLAGS]))^ := Puint16(@(registers[IRQ_FLAGS]))^ and not data; WAIT_STATE_CR: begin Puint16(@(registers[address]))^ := data and $3FFF; SetWaitStates; end; SOUND1_SWEEP: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound1Sweep; end; SOUND1_LENGTH: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound1Length; end; SOUND1_FREQUENCY: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound1Freq; end; SOUND2_LENGTH: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound2Length; end; SOUND2_FREQUENCY: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound2Freq; end; SOUND3_CR: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound3CR; end; SOUND3_LENGTH: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound3Length; end; SOUND3_FREQUENCY: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound3Freq; end; SOUND4_LENGTH: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound4Length; end; SOUND4_CR: begin soundChanging; Puint16(@(registers[address]))^ := data; soundSetSound4CR; end; SOUNDA_FIFO, SOUNDA_FIFO+2: begin Puint16(@(registers[address]))^ := data; soundIncrementFifoA; end; SOUNDB_FIFO, SOUNDB_FIFO+2: begin Puint16(@(registers[address]))^ := data; soundIncrementFifoB; end; SOUND_DSOUND_CR: begin soundChanging; Puint16(@(registers[address]))^ := data; soundWriteDSoundCR; end; $300: begin if data and $8000 <> 0 then begin // Halt cpuStopped := true; cpuHalted := true; quota := 0; end else begin // Stop cpuStopped := true; quota := 0; end; end; else Puint16(@(registers[address]))^ := data; end; end; ////////////////////////////////////////////////////////////////////// procedure WriteIO_32(address, data: uint32); begin Dec(quota); address := address and REGISTERS_MASK; // Log it if we want to if logIORegisters then LogWriteLn(Format('u32[$%3.3x]=$%8.8x', [address, data])); case address of BG2_X: begin Puint32(@(registers[address]))^ := data; registers[BG2_X_DIRTY] := 1; if (Puint32(@(registers[BG2_X]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG2_X]))^ := Puint32(@(registers[BG2_X]))^ or $F0000000; Puint32(@(registers[BG2_X_LATCH]))^ := Puint32(@(registers[BG2_X]))^; end; BG2_Y: begin Puint32(@(registers[address]))^ := data; registers[BG2_Y_DIRTY] := 1; if (Puint32(@(registers[BG2_Y]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG2_Y]))^ := Puint32(@(registers[BG2_Y]))^ or $F0000000; Puint32(@(registers[BG2_Y_LATCH]))^ := Puint32(@(registers[BG2_Y]))^; end; BG3_X: begin Puint32(@(registers[address]))^ := data; registers[BG3_X_DIRTY] := 1; if (Puint32(@(registers[BG3_X]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG3_X]))^ := Puint32(@(registers[BG3_X]))^ or $F0000000; Puint32(@(registers[BG3_X_LATCH]))^ := Puint32(@(registers[BG3_X]))^; end; BG3_Y: begin Puint32(@(registers[address]))^ := data; registers[BG3_Y_DIRTY] := 1; if (Puint32(@(registers[BG3_Y]))^ shr 27) and 1 <> 0 then Puint32(@(registers[BG3_Y]))^ := Puint32(@(registers[BG3_Y]))^ or $F0000000; Puint32(@(registers[BG3_Y_LATCH]))^ := Puint32(@(registers[BG3_Y]))^; end; KEYS: ; TIMER0: begin Puint16(@(registers[TIMER0_LATCH]))^ := data and $FFFF; Puint16(@(registers[TIMER0_CR]))^ := data shr 16; WriteTimerCR(0); end; TIMER1: begin Puint16(@(registers[TIMER1_LATCH]))^ := data and $FFFF; Puint16(@(registers[TIMER1_CR]))^ := data shr 16; WriteTimerCR(1); end; TIMER2: begin Puint16(@(registers[TIMER2_LATCH]))^ := data and $FFFF; Puint16(@(registers[TIMER2_CR]))^ := data shr 16; WriteTimerCR(2); end; TIMER3: begin Puint16(@(registers[TIMER3_LATCH]))^ := data and $FFFF; Puint16(@(registers[TIMER3_CR]))^ := data shr 16; WriteTimerCR(3); end; DMA0_COUNT: begin Puint32(@(registers[address]))^ := data; if Puint16(@(registers[DMA0_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(0); end; DMA1_COUNT: begin Puint32(@(registers[address]))^ := data; if Puint16(@(registers[DMA1_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(1); end; DMA2_COUNT: begin Puint32(@(registers[address]))^ := data; if Puint16(@(registers[DMA2_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(2); end; DMA3_COUNT: begin Puint32(@(registers[address]))^ := data; if Puint16(@(registers[DMA3_CR]))^ and (1 shl 15) <> 0 then InitiateDMATransfer(3); end; IRQ_ENABLED: begin Puint16(@(registers[address]))^ := data and $FFFF; Puint16(@(registers[IRQ_FLAGS]))^ := Puint16(@(registers[IRQ_FLAGS]))^ and not (data shr 16); end; WAIT_STATE_CR: begin Puint32(@(registers[address]))^ := data; Puint16(@(registers[WAIT_STATE_CR]))^ := Puint16(@(registers[WAIT_STATE_CR]))^ and $3FFF; SetWaitStates; end; SOUND1_SWEEP: begin soundChanging; Puint32(@(registers[address]))^ := data; soundSetSound1Sweep; soundSetSound1Length; end; SOUND1_FREQUENCY: begin soundChanging; Puint32(@(registers[address]))^ := data; soundSetSound1Freq; end; SOUND2_LENGTH: begin soundChanging; Puint32(@(registers[address]))^ := data; soundSetSound2Length; soundSetSound2Freq; end; SOUND3_CR: begin soundChanging; Puint32(@(registers[address]))^ := data; soundSetSound3CR; soundSetSound3Length; end; SOUND3_FREQUENCY: begin soundChanging; Puint32(@(registers[address]))^ := data; soundSetSound3Freq; end; SOUND4_LENGTH: begin soundChanging; Puint32(@(registers[address]))^ := data; soundSetSound4Length; soundSetSound4CR; end; SOUNDA_FIFO: begin Puint32(@(registers[address]))^ := data; soundIncrementFifoA; end; SOUNDB_FIFO: begin Puint32(@(registers[address]))^ := data; soundIncrementFifoB; end; $080: begin soundChanging; Puint32(@(registers[address]))^ := data; soundWriteDSoundCR; end; $300: begin if data and $8000 <> 0 then begin // Halt cpuStopped := true; cpuHalted := true; quota := 0; end else begin // Stop cpuStopped := true; quota := 0; end; end; else Puint32(@(registers[address]))^ := data; end; end; ////////////////////////////////////////////////////////////////////// function ReadPal_08(address: uint32): uint32; begin Dec(quota); Result := Palette[address and PALETTE_MASK]; end; ////////////////////////////////////////////////////////////////////// function ReadPal_16(address: uint32): uint32; begin Dec(quota); Result := Puint16(@(Palette[address and PALETTE_MASK]))^; end; ////////////////////////////////////////////////////////////////////// function ReadPal_32(address: uint32): uint32; begin Dec(quota, 2); Result := Puint32(@(Palette[address and PALETTE_MASK]))^; end; ////////////////////////////////////////////////////////////////////// procedure WritePal_08(address, data: uint32); begin Dec(quota); address := address and (PALETTE_MASK and not 1); Puint16(@(Palette[address]))^ := data + data shl 8; end; ////////////////////////////////////////////////////////////////////// procedure WritePal_16(address, data: uint32); begin Dec(quota); Puint16(@(Palette[address and PALETTE_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// procedure WritePal_32(address, data: uint32); begin Dec(quota, 2); Puint32(@(Palette[address and PALETTE_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// function ReadVRAM_08(address: uint32): uint32; begin Dec(quota); Result := VRAM[address and VRAM_MASK]; end; ////////////////////////////////////////////////////////////////////// function ReadVRAM_16(address: uint32): uint32; begin Dec(quota); Result := Puint16(@(VRAM[address and VRAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// function ReadVRAM_32(address: uint32): uint32; begin Dec(quota, 2); Result := Puint32(@(VRAM[address and VRAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// procedure WriteVRAM_08(address, data: uint32); begin Dec(quota); address := address and (VRAM_MASK and not 1); Puint16(@(VRAM[address]))^ := data + data shl 8; end; ////////////////////////////////////////////////////////////////////// procedure WriteVRAM_16(address, data: uint32); begin Dec(quota); Puint16(@(VRAM[address and VRAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// procedure WriteVRAM_32(address, data: uint32); begin Dec(quota, 2); Puint32(@(VRAM[address and VRAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// function ReadOAM_08(address: uint32): uint32; begin Dec(quota); Result := OAM[address and OAM_MASK]; end; ////////////////////////////////////////////////////////////////////// function ReadOAM_16(address: uint32): uint32; begin Dec(quota); Result := Puint16(@(OAM[address and OAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// function ReadOAM_32(address: uint32): uint32; begin Dec(quota, 2); Result := Puint32(@(OAM[address and OAM_MASK]))^; end; ////////////////////////////////////////////////////////////////////// procedure WriteOAM_08(address, data: uint32); begin Dec(quota); address := address and (OAM_MASK and not 1); Puint16(@(OAM[address]))^ := data + data shl 8; end; ////////////////////////////////////////////////////////////////////// procedure WriteOAM_16(address, data: uint32); begin Dec(quota); Puint16(@(OAM[address and OAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// procedure WriteOAM_32(address, data: uint32); begin Dec(quota, 2); Puint32(@(OAM[address and OAM_MASK]))^ := data; end; ////////////////////////////////////////////////////////////////////// function ReadROM1_08(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); Result := cartROM1^[address and cartMask1]; { part of the e-Reader testing: if address >= $08700000 then logWriteLn(Format('%2.2x in ROM[%8.8x], at %8.8x', [Result, address, regs[R15]]));} end; ////////////////////////////////////////////////////////////////////// function ReadROM1_16(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); Result := Puint16(@(cartROM1^[address and cartMask1]))^; { part of the e-Reader testing: if address >= $08700000 then logWriteLn(Format('%4.4x in ROM[%8.8x], at %8.8x', [Result, address, regs[R15]]));} end; ////////////////////////////////////////////////////////////////////// function ReadROM1_32(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]*2 + 1) else Dec(quota, bankNTimes[curBank] + bankSTimes[curBank] + 1); Result := Puint32(@(cartROM1^[address and cartMask1]))^; { part of the e-Reader testing: if address >= $08700000 then logWriteLn(Format('%8.8x in ROM[%8.8x], at %8.8x', [Result, address, regs[R15]]));} end; ////////////////////////////////////////////////////////////////////// function ReadROM2_08(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); Result := cartROM2^[address and cartMask2]; end; ////////////////////////////////////////////////////////////////////// function ReadROM2_16(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); Result := Puint16(@(cartROM2^[address and cartMask2]))^; end; ////////////////////////////////////////////////////////////////////// function ReadROM2_32(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]*2 + 1) else Dec(quota, bankNTimes[curBank] + bankSTimes[curBank] + 1); Result := Puint32(@(cartROM2^[address and cartMask2]))^; end; ////////////////////////////////////////////////////////////////////// // Thanks to Forgotten for his help with EEPROM support ////////////// ////////////////////////////////////////////////////////////////////// function ReadEEPROM_08(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); Result := 0; case eepromMode of emIdle, emReadAddress, emWriteData: Result := 1; emReadData: begin Inc(eepromIndex); if eepromIndex = 4 then begin eepromMode := emReadData2; eepromIndex := 0; eepromByte := 0; end; end; emReadData2: begin Result := (cartRAM[eepromAddress shl 3 + eepromByte] shr (7 - eepromIndex and 7)) and 1; Inc(eepromIndex); if eepromIndex and 7 = 0 then Inc(eepromByte); if eepromIndex = 64 then eepromMode := emIdle; end; end; end; ////////////////////////////////////////////////////////////////////// function ReadEEPROM_16(address: uint32): uint32; begin Result := ReadEEPROM_08(address); end; ////////////////////////////////////////////////////////////////////// function ReadEEPROM_32(address: uint32): uint32; begin Result := ReadEEPROM_08(address); Dec(quota, bankSTimes[curBank] + 1); end; ////////////////////////////////////////////////////////////////////// procedure WriteEEPROM_08(address, data: uint32); var i: uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); cartRAMdirty := true; data := data and 1; case eepromMode of emIdle: begin eepromIndex := 1; eepromByte := 0; eepromBuffer[0] := data; eepromMode := emReadAddress; end; emReadAddress: begin eepromBuffer[eepromByte] := eepromBuffer[eepromByte] shl 1; eepromBuffer[eepromByte] := eepromBuffer[eepromByte] or data; Inc(eepromIndex); if eepromIndex and 7 = 0 then Inc(eepromByte); if eepromIndex = 9 then begin eepromAddress := eepromBuffer[0] and $3F; if eepromBuffer[0] and $40 = 0 then eepromMode := emWriteData else begin eepromMode := emReadData; eepromByte := 0; eepromIndex := 0; end; end; end; emReadData, emReadData2: begin eepromMode := emIdle; end; emWriteData: begin eepromBuffer[eepromByte] := eepromBuffer[eepromByte] shl 1; eepromBuffer[eepromByte] := eepromBuffer[eepromByte] or data; Inc(eepromIndex); if eepromIndex and 7 = 0 then Inc(eepromByte); if eepromIndex = $49 then begin // Write data for i := 0 to 7 do cartRAM[(eepromAddress shl 3 + i) and EEPROM_MASK] := eepromBuffer[1+i]; eepromMode := emIdle; end; end; end; end; ////////////////////////////////////////////////////////////////////// procedure WriteEEPROM_16(address, data: uint32); begin WriteEEPROM_08(address, data); end; ////////////////////////////////////////////////////////////////////// procedure WriteEEPROM_32(address, data: uint32); begin WriteEEPROM_08(address, data); Dec(quota, bankSTimes[curBank] + 1); end; ////////////////////////////////////////////////////////////////////// { SRAM support is currently disabled function ReadSRAM_08(address: uint32): uint32; begin logwriteln(format('SRAM read of %.4x',[address and $FFFF])); if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); Result := cartRAM[address and SRAM_MASK]; end; ////////////////////////////////////////////////////////////////////// procedure WriteSRAM_08(address, data: uint32); begin logwriteln(format('SRAM write: [%.4x] = %.2x',[address and $FFFF, data])); if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); cartRAM[address and SRAM_MASK] := data; cartRAMdirty := true; end; } ////////////////////////////////////////////////////////////////////// var bankId: longword = 0; function ReadFlash_08(address: uint32): uint32; begin if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); if flashMode = fmProductID then begin // if address and 1 <> 0 then Result := $BC else Result := $1F; if address and 1 <> 0 then Result := $09 else Result := $C2; end else Result := cartRAM[bankId + address and SRAM_MASK]; if address and $FFF < 5 then logwriteln(format('FLASH read of %.4x (%.2x), i/m/b=%d/%d/%d, from %.8x', [address and $FFFF, Result, flashIndex, Ord(flashMode), bankId shr 16, regs[R15]])); end; ////////////////////////////////////////////////////////////////////// const MX_FLASH_READ_ID = $90; MX_FLASH_PROGRAM = $A0; MX_FLASH_RESET = $F0; MX_FLASH_SECTOR_ERASE_SUSPEND = $B0; // Not needed, since sector erase is currently instentanious // MX_FLASH_SECTOR_ERASE_RESUME = $30; double strikeout, not needed as above, and same as erase command MX_FLASH_EXTENDED = $80; // Requires a MX_FLASH_EXTENDED first MX_FLASH_CHIP_ERASE = $10; MX_FLASH_SECTOR_ERASE = $30; // MX_FLASH_SECTOR_UNLOCK = $20; not needed, since there are no lockable sectors procedure WriteFlash_08(address, data: uint32); begin cartRAMdirty := true; logwriteln(format('FLASH write: [%.4x] = %.2x, at %.8x', [address and $FFFF, data, regs[R15]])); if address-lastAddress <= 4 then Dec(quota, bankSTimes[curBank]) else Dec(quota, bankNTimes[curBank]); data := data and $FF; if flashMode = fmWriteData then begin cartRAM[bankID + address and SRAM_MASK] := {cartRAM[bankID + address and SRAM_MASK] and }data; // cartRAM[address and SRAM_MASK] := data; flashMode := fmIdle; flashIndex := 0; end else if flashMode = fmBankswitch then begin flashMode := fmIdle; flashIndex := 0; bankID := (data and 1) shl 16; logwriteln(format('Bankswitch: [%.4x] = %.2x, at %.8x', [address and $FFFF, data, regs[R15]])); end else if data = MX_FLASH_RESET then begin flashMode := fmIdle; flashIndex := 0; end else case flashIndex of 0: if (address and $FFFF = $5555) and (data and $FF = $AA) then Inc(flashIndex); 1: if (address and $FFFF = $2AAA) and (data and $FF = $55) then Inc(flashIndex); 2: begin if (data = MX_FLASH_SECTOR_ERASE) and (flashMode = fmExtended) then begin // Do a sector erase FillChar(cartRAM[bankID + (address and (2*SRAM_MASK+1)) and not $3FFF], $4000, $FF); // FillChar(cartRAM[(address and SRAM_MASK) and not $1F], 32, $00); flashMode := fmIdle; end; if (address and $FFFF = $5555) then case data of MX_FLASH_EXTENDED: flashMode := fmExtended; MX_FLASH_READ_ID: flashMode := fmProductID; MX_FLASH_PROGRAM: flashMode := fmWriteData; MX_FLASH_SECTOR_ERASE: ; // done above MX_FLASH_SECTOR_ERASE_SUSPEND: flashMode := fmBankswitch; MX_FLASH_CHIP_ERASE: if flashMode = fmExtended then begin // Erase the whole chip FillChar(cartRAM, 2*SRAM_MASK+2, $FF); // FillChar(cartRAM, SRAM_MASK+1, $FF); flashMode := fmIdle; end; else logWriteLn(format('Invalid flash command: [%.4x] = %.2x',[address and $FFFF, data])); flashMode := fmIdle; end; flashIndex := 0; end; end; end; ////////////////////////////////////////////////////////////////////// function memReadByte(address: uint32): uint32; begin curBank := (address shr 24) and $F; Result := ReadByteFuncs[curBank](address); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// function memReadHalfword(address: uint32): uint32; begin address := address and not 1; curBank := (address shr 24) and $F; Result := ReadHalfFuncs[curBank](address); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// function memReadWord(address: uint32): uint32; var s: byte; begin curBank := (address shr 24) and $F; Result := ReadWordFuncs[curBank](address and not 3); lastAddress := address; s := (lastAddress and 3) shl 3; Result := (Result shr s) or (Result shl (32-s)); end; ////////////////////////////////////////////////////////////////////// function memLoadWord(address: uint32): uint32; var s: byte; begin curBank := (address shr 24) and $F; Result := ReadWordFuncs[curBank](address and not 3); lastAddress := address; s := (lastAddress and 3) shl 3; Result := (Result shr s) or (Result shl (32-s)); end; ////////////////////////////////////////////////////////////////////// function memReadHalfwordUnc(address: uint32): uint32; begin curBank := (address shr 24) and $F; Result := ReadHalfFuncs[curBank](address); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// function memReadWordUnc(address: uint32): uint32; begin curBank := (address shr 24) and $F; Result := ReadWordFuncs[curBank](address); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// procedure memWriteByte(address: uint32; data: uint32); begin curBank := (address shr 24) and $F; WriteByteFuncs[curBank](address, data); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// procedure memWriteHalfword(address: uint32; data: uint32); begin address := address and not 1; curBank := (address shr 24) and $F; WriteHalfFuncs[curBank](address, data); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// procedure memWriteWord(address: uint32; data: uint32); begin address := address and not 3; // fixme: this might need to go! curBank := (address shr 24) and $F; WriteWordFuncs[curBank](address, data); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// procedure memWriteHalfwordUnc(address: uint32; data: uint32); begin curBank := (address shr 24) and $F; WriteHalfFuncs[curBank](address, data); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// procedure memWriteWordUnc(address: uint32; data: uint32); begin curBank := (address shr 24) and $F; WriteWordFuncs[curBank](address, data); lastAddress := address; end; ////////////////////////////////////////////////////////////////////// function vmReadByte(address: uint32): byte; var la, lc, cpc: uint32; begin cpc := regs[R15]; regs[R15] := 0; lc := quota; la := lastAddress; Result := memReadByte(address); regs[R15] := cpc; lastAddress := la; quota := lc; end; ///////////////////////////////////////////////////////////////////// function vmReadHalfWord(address: uint32): uint16; var la, lc, cpc: uint32; begin cpc := regs[R15]; regs[R15] := 0; lc := quota; la := lastAddress; Result := memReadHalfword(address); regs[R15] := cpc; lastAddress := la; quota := lc; end; ////////////////////////////////////////////////////////////////////// function vmReadWord(address: uint32): uint32; var la, lc, cpc: uint32; begin cpc := regs[R15]; regs[R15] := 0; lc := quota; la := lastAddress; Result := memReadWord(address); regs[R15] := cpc; lastAddress := la; quota := lc; end; ////////////////////////////////////////////////////////////////////// procedure vmWriteByte(address: uint32; data: uint8); var la, lc, cpc: uint32; begin cpc := regs[R15]; regs[R15] := 0; lc := quota; la := lastAddress; memWriteByte(address, data); regs[R15] := cpc; lastAddress := la; quota := lc; end; ////////////////////////////////////////////////////////////////////// procedure vmWriteHalfword(address: uint32; data: uint16); var la, lc, cpc: uint32; begin cpc := regs[R15]; regs[R15] := 0; lc := quota; la := lastAddress; memWriteHalfword(address, data); regs[R15] := cpc; lastAddress := la; quota := lc; end; ////////////////////////////////////////////////////////////////////// procedure vmWriteWord(address: uint32; data: uint32); var la, lc, cpc: uint32; begin cpc := regs[R15]; regs[R15] := 0; lc := quota; la := lastAddress; memWriteWord(address, data); regs[R15] := cpc; lastAddress := la; quota := lc; end; ////////////////////////////////////////////////////////////////////// procedure SetWaitStates; var romMode: byte; sramMode: byte; wait: uint16; i: integer; begin wait := Puint16(@(registers[WAIT_STATE_CR]))^; // Set ROM bank times romMode := (wait shr 2) and $7; bankNTimes[$8] := romWait[romMode, 0]; bankSTimes[$8] := romWait[romMode, 1]; bankNTimes[$9] := romWait[romMode, 0]; bankSTimes[$9] := romWait[romMode, 1]; // Set mirror bank 2 times romMode := (wait shr 5) and $7; bankNTimes[$A] := romWait[romMode, 0]; bankSTimes[$A] := romWait[romMode, 2]; bankNTimes[$B] := romWait[romMode, 0]; bankSTimes[$B] := romWait[romMode, 2]; // Set mirror bank 3 times romMode := (wait shr 8) and $7; bankNTimes[$C] := romWait[romMode, 0]; bankSTimes[$C] := romWait[romMode, 3]; bankNTimes[$D] := romWait[romMode, 0]; bankSTimes[$D] := romWait[romMode, 3]; // Set SRAM times sramMode := wait and $3; bankNTimes[$E] := sramWait[sramMode]; bankSTimes[$E] := sramWait[sramMode]; bankNTimes[$F] := sramWait[sramMode]; bankSTimes[$F] := sramWait[sramMode]; for i := 0 to 15 do begin Inc(bankNTimes[i]); Inc(bankSTimes[i]); end; end; ////////////////////////////////////////////////////////////////////// procedure ResetBank1; begin // Set the sizes to 0 cartSize1 := 0; cartMask1 := 0; // Set the bank 1 memory functions to NOP ReadByteFuncs[$8] := ReadNOP; WriteByteFuncs[$8] := WriteNOP; ReadByteFuncs[$A] := ReadNOP; WriteByteFuncs[$A] := WriteNOP; ReadByteFuncs[$C] := ReadNOP; WriteByteFuncs[$C] := WriteNOP; ReadHalfFuncs[$8] := ReadNOP; WriteHalfFuncs[$8] := WriteNOP; ReadHalfFuncs[$A] := ReadNOP; WriteHalfFuncs[$A] := WriteNOP; ReadHalfFuncs[$C] := ReadNOP; WriteHalfFuncs[$C] := WriteNOP; ReadWordFuncs[$8] := ReadNOP; WriteWordFuncs[$8] := WriteNOP; ReadWordFuncs[$A] := ReadNOP; WriteWordFuncs[$A] := WriteNOP; ReadWordFuncs[$C] := ReadNOP; WriteWordFuncs[$C] := WriteNOP; memBanks[$8] := nil; memBanks[$A] := nil; memBanks[$C] := nil; // Set the bank 1 breakpoints to null breakpoints.cartSize1 := 0; interestingAddresses.cartSize1 := 0; sourcePoints.cartSize1 := 0; end; ////////////////////////////////////////////////////////////////////// procedure ResetBank2; begin // Set the sizes to 0 cartSize2 := 0; cartMask2 := 0; // Set the bank 2 memory functions to NOP eepromIndex := 0; eepromByte := 0; eepromMode := emIdle; flashIndex := 0; flashMode := fmIdle; // If there is no ROM loaded, treat this space as an EEPROM ReadByteFuncs[$9] := ReadEEPROM_08; WriteByteFuncs[$9] := WriteEEPROM_08; ReadByteFuncs[$B] := ReadEEPROM_08; WriteByteFuncs[$B] := WriteEEPROM_08; ReadByteFuncs[$D] := ReadEEPROM_08; WriteByteFuncs[$D] := WriteEEPROM_08; ReadHalfFuncs[$9] := ReadEEPROM_16; WriteHalfFuncs[$9] := WriteEEPROM_16; ReadHalfFuncs[$B] := ReadEEPROM_16; WriteHalfFuncs[$B] := WriteEEPROM_16; ReadHalfFuncs[$D] := ReadEEPROM_16; WriteHalfFuncs[$D] := WriteEEPROM_16; ReadWordFuncs[$9] := ReadEEPROM_32; WriteWordFuncs[$9] := WriteEEPROM_32; ReadWordFuncs[$B] := ReadEEPROM_32; WriteWordFuncs[$B] := WriteEEPROM_32; ReadWordFuncs[$D] := ReadEEPROM_32; WriteWordFuncs[$D] := WriteEEPROM_32; memBanks[$9] := nil; memBanks[$B] := nil; memBanks[$D] := nil; // Set the bank 2 breakpoints to null breakpoints.cartSize2 := 0; interestingAddresses.cartSize2 := 0; sourcePoints.cartSize2 := 0; end; ////////////////////////////////////////////////////////////////////// procedure vmRemoveCartridge; begin if not cartLoaded then Exit; if cartSize1 > 0 then begin FreeMem(cartROM1, cartSize1+cartSize2); ResetBank1; end; if cartSize2 > 0 then ResetBank2; //currentHeader := nil; cartLoaded := false; end; ////////////////////////////////////////////////////////////////////// function RoundUpToPow2(num: uint32): uint32; begin Result := 1; while Result < num do Result := Result shl 1; end; ////////////////////////////////////////////////////////////////////// procedure vmInsertCartridge(data: pointer; size: integer); begin // Remove the old cart vmRemoveCartridge; // Figure out how much to read, and how much to allocate for the ROM cartSize1 := Min(RoundUpToPow2(size), 1 shl 25); GetMem(cartROM1, cartSize1); Move(data^, cartROM1^, size); if cartSize1 > 1 shl 24 then begin cartSize2 := cartSize1 - 1 shl 24; cartSize1 := 1 shl 24; cartROM2 := pointer(integer(cartROM1) + 1 shl 24); cartMask2 := cartSize2 - 1; end; if cartSize1 > 0 then begin cartMask1 := cartSize1 - 1; ReadByteFuncs[$8] := ReadROM1_08; WriteByteFuncs[$8] := WriteROMBaby8; ReadByteFuncs[$A] := ReadROM1_08; WriteByteFuncs[$A] := WriteROMBaby8; ReadByteFuncs[$C] := ReadROM1_08; WriteByteFuncs[$C] := WriteROMBaby8; ReadHalfFuncs[$8] := ReadROM1_16; WriteHalfFuncs[$8] := WriteROMBaby16; ReadHalfFuncs[$A] := ReadROM1_16; WriteHalfFuncs[$A] := WriteROMBaby16; ReadHalfFuncs[$C] := ReadROM1_16; WriteHalfFuncs[$C] := WriteROMBaby16; ReadWordFuncs[$8] := ReadROM1_32; WriteWordFuncs[$8] := WriteROMBaby32; ReadWordFuncs[$A] := ReadROM1_32; WriteWordFuncs[$A] := WriteROMBaby32; ReadWordFuncs[$C] := ReadROM1_32; WriteWordFuncs[$C] := WriteROMBaby32; memBanks[$8] := cartROM1; memMasks[$8] := cartMask1; memBanks[$A] := cartROM1; memMasks[$A] := cartMask1; memBanks[$C] := cartROM1; memMasks[$C] := cartMask1; // Set up the bank 1 breakpoints breakpoints.cartSize1 := Max(cartSize1 shr 4, 1); interestingAddresses.cartSize1 := breakpoints.cartSize1; sourcePoints.cartSize1 := breakpoints.cartSize1; end else ResetBank1; if cartSize2 > 0 then begin cartMask2 := cartSize2 - 1; ReadByteFuncs[$9] := ReadROM2_08; WriteByteFuncs[$9] := WriteROMBaby8; ReadByteFuncs[$B] := ReadROM2_08; WriteByteFuncs[$B] := WriteROMBaby8; ReadByteFuncs[$D] := ReadROM2_08; WriteByteFuncs[$D] := WriteROMBaby8; ReadHalfFuncs[$9] := ReadROM2_16; WriteHalfFuncs[$9] := WriteROMBaby16; ReadHalfFuncs[$B] := ReadROM2_16; WriteHalfFuncs[$B] := WriteROMBaby16; ReadHalfFuncs[$D] := ReadROM2_16; WriteHalfFuncs[$D] := WriteROMBaby16; ReadWordFuncs[$9] := ReadROM2_32; WriteWordFuncs[$9] := WriteROMBaby32; ReadWordFuncs[$B] := ReadROM2_32; WriteWordFuncs[$B] := WriteROMBaby32; ReadWordFuncs[$D] := ReadROM2_32; WriteWordFuncs[$D] := WriteROMBaby32; { ReadByteFuncs[$9] := ReadROM2_08; WriteByteFuncs[$9] := WriteNOP; ReadByteFuncs[$B] := ReadROM2_08; WriteByteFuncs[$B] := WriteNOP; ReadByteFuncs[$D] := ReadROM2_08; WriteByteFuncs[$D] := WriteNOP; ReadHalfFuncs[$9] := ReadROM2_16; WriteHalfFuncs[$9] := WriteNOP; ReadHalfFuncs[$B] := ReadROM2_16; WriteHalfFuncs[$B] := WriteNOP; ReadHalfFuncs[$D] := ReadROM2_16; WriteHalfFuncs[$D] := WriteNOP; ReadWordFuncs[$9] := ReadROM2_32; WriteWordFuncs[$9] := WriteNOP; ReadWordFuncs[$B] := ReadROM2_32; WriteWordFuncs[$B] := WriteNOP; ReadWordFuncs[$D] := ReadROM2_32; WriteWordFuncs[$D] := WriteNOP;} memBanks[$9] := cartROM2; memMasks[$9] := cartMask2; memBanks[$B] := cartROM2; memMasks[$B] := cartMask2; memBanks[$D] := cartROM2; memMasks[$D] := cartMask2; // Set up the bank 2 breakpoints breakpoints.cartSize2 := Max(cartSize2 shr 4, 1); interestingAddresses.cartSize2 := breakpoints.cartSize2; sourcePoints.cartSize2 := breakpoints.cartSize2; end else ResetBank2; {$IFDEF SIGNATURES} FillChar(sigs, SizeOf(sigs), 0); {$ENDIF} // Righty ho, cart loaded cartLoaded := true; end; ////////////////////////////////////////////////////////////////////// procedure InitMemory; begin memBanks[$0] := nil; memMasks[$0] := 0; memBanks[$1] := nil; memMasks[$1] := 0; memBanks[$2] := @exWRAM; memMasks[$2] := EX_WRAM_MASK; memBanks[$3] := @WRAM; memMasks[$3] := WRAM_MASK; memBanks[$4] := nil; memMasks[$4] := 0; memBanks[$5] := @palette; memMasks[$5] := PALETTE_MASK; memBanks[$6] := @VRAM; memMasks[$6] := VRAM_MASK; memBanks[$7] := @OAM; memMasks[$7] := OAM_MASK; memBanks[$8] := nil; memMasks[$8] := 0; memBanks[$9] := nil; memMasks[$9] := 0; memBanks[$A] := nil; memMasks[$A] := 0; memBanks[$B] := nil; memMasks[$B] := 0; memBanks[$C] := nil; memMasks[$C] := 0; memBanks[$D] := nil; memMasks[$D] := 0; memBanks[$E] := nil; memMasks[$E] := 0; memBanks[$F] := nil; memMasks[$F] := 0; ReadByteFuncs[$0] := ReadBIOS_08; WriteByteFuncs[$0] := WriteNOP; ReadByteFuncs[$1] := ReadNOP; WriteByteFuncs[$1] := WriteNOP; ReadByteFuncs[$2] := ReadExWRAM_08; WriteByteFuncs[$2] := WriteExWRAM_08; ReadByteFuncs[$3] := ReadWRAM_08; WriteByteFuncs[$3] := WriteWRAM_08; ReadByteFuncs[$4] := ReadIO_08; WriteByteFuncs[$4] := WriteIO_08; ReadByteFuncs[$5] := ReadPal_08; WriteByteFuncs[$5] := WritePal_08; ReadByteFuncs[$6] := ReadVRAM_08; WriteByteFuncs[$6] := WriteVRAM_08; ReadByteFuncs[$7] := ReadOAM_08; WriteByteFuncs[$7] := WriteOAM_08; ReadByteFuncs[$E] := ReadFlash_08; WriteByteFuncs[$E] := WriteFlash_08; ReadByteFuncs[$F] := ReadFlash_08; WriteByteFuncs[$F] := WriteFlash_08; ReadHalfFuncs[$0] := ReadBIOS_16; WriteHalfFuncs[$0] := WriteNOP; ReadHalfFuncs[$1] := ReadNOP; WriteHalfFuncs[$1] := WriteNOP; ReadHalfFuncs[$2] := ReadExWRAM_16; WriteHalfFuncs[$2] := WriteExWRAM_16; ReadHalfFuncs[$3] := ReadWRAM_16; WriteHalfFuncs[$3] := WriteWRAM_16; ReadHalfFuncs[$4] := ReadIO_16; WriteHalfFuncs[$4] := WriteIO_16; ReadHalfFuncs[$5] := ReadPal_16; WriteHalfFuncs[$5] := WritePal_16; ReadHalfFuncs[$6] := ReadVRAM_16; WriteHalfFuncs[$6] := WriteVRAM_16; ReadHalfFuncs[$7] := ReadOAM_16; WriteHalfFuncs[$7] := WriteOAM_16; ReadHalfFuncs[$E] := ReadFlash_08; WriteHalfFuncs[$E] := WriteFlash_08; ReadHalfFuncs[$F] := ReadFlash_08; WriteHalfFuncs[$F] := WriteFlash_08; ReadWordFuncs[$0] := ReadBIOS_32; WriteWordFuncs[$0] := WriteNOP; ReadWordFuncs[$1] := ReadNOP; WriteWordFuncs[$1] := WriteNOP; ReadWordFuncs[$2] := ReadExWRAM_32; WriteWordFuncs[$2] := WriteExWRAM_32; ReadWordFuncs[$3] := ReadWRAM_32; WriteWordFuncs[$3] := WriteWRAM_32; ReadWordFuncs[$4] := ReadIO_32; WriteWordFuncs[$4] := WriteIO_32; ReadWordFuncs[$5] := ReadPal_32; WriteWordFuncs[$5] := WritePal_32; ReadWordFuncs[$6] := ReadVRAM_32; WriteWordFuncs[$6] := WriteVRAM_32; ReadWordFuncs[$7] := ReadOAM_32; WriteWordFuncs[$7] := WriteOAM_32; ReadWordFuncs[$E] := ReadFlash_08; WriteWordFuncs[$E] := WriteFlash_08; ReadWordFuncs[$F] := ReadFlash_08; WriteWordFuncs[$F] := WriteFlash_08; flashMode := fmIdle; cartLoaded := false; breakpoints := TBreakpointMemory.Create; interestingAddresses := TBreakpointMemory.Create; sourcePoints := TBreakpointMemory.Create; ResetBank1; ResetBank2; SetWaitStates; end; ////////////////////////////////////////////////////////////////////// procedure cpuMemFree; begin breakpoints.Free; sourcePoints.Free; interestingAddresses.Free; end; ////////////////////////////////////////////////////////////////////// procedure vmLockMemory(var banks: TvmMemoryLock1); begin // fill out the structure FillChar(banks, SizeOf(banks), 0); banks.version := 1; banks.bios := @systemROM; banks.exwram := @exWRAM; banks.wram := @WRAM; banks.iospace := @registers; banks.palette := @palette; banks.vram := @VRAM; banks.oam := @OAM; banks.rom := cartROM1; banks.romsize := cartSize1 + cartSize2; end; ////////////////////////////////////////////////////////////////////// procedure vmUnlockMemory(const banks: TvmMemoryLock1); begin // do nothing, since its not really a lock right now end; ////////////////////////////////////////////////////////////////////// // TBreakpointMemory ///////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// constructor TBreakpointMemory.Create; begin FCartSize1 := 0; FCartSize2 := 0; cartROM1 := nil; cartROM2 := nil; null := 0; bpBanks[$0] := @bsystemROM; bpBankMasks[$0] := (SYSTEM_ROM_MASK+1) shr 4 - 1; FillChar(bsystemROM, (SYSTEM_ROM_MASK+1) shr 4, 0); bpBanks[$1] := @null; bpBankMasks[$1] := 0; bpBanks[$2] := @bexWRAM; bpBankMasks[$2] := (EX_WRAM_MASK+1) shr 4 - 1; FillChar(bexWRAM, (EX_WRAM_MASK+1) shr 4, 0); bpBanks[$3] := @bWRAM; bpBankMasks[$3] := (WRAM_MASK+1) shr 4 - 1; FillChar(bWRAM, (WRAM_MASK+1) shr 4, 0); bpBanks[$4] := @null; bpBankMasks[$4] := 0; bpBanks[$5] := @palette; bpBankMasks[$5] := (PALETTE_MASK+1) shr 4 - 1; FillChar(bpalette, (PALETTE_MASK+1) shr 4, 0); bpBanks[$6] := @bVRAM; bpBankMasks[$6] := (VRAM_MASK+1) shr 4 - 1; FillChar(bVRAM, (VRAM_MASK+1) shr 4, 0); bpBanks[$7] := @bOAM; bpBankMasks[$7] := (OAM_MASK+1) shr 4 - 1; FillChar(bOAM, (OAM_MASK+1) shr 4, 0); bpBanks[$E] := @null; bpBankMasks[$E] := 0; bpBanks[$F] := @null; bpBankMasks[$F] := 0; end; ////////////////////////////////////////////////////////////////////// destructor TBreakpointMemory.Destroy; begin if Assigned(bcartROM1) and (FCartSize1 > 0) then FreeMem(bcartROM1, FCartSize1); if Assigned(bcartROM2) and (FCartSize2 > 0) then FreeMem(bcartROM2, FCartSize2); inherited; end; ////////////////////////////////////////////////////////////////////// procedure TBreakpointMemory.SetCartSize1(const value: uint32); var mask: uint32; mem: pointer; begin // Free any previously active breakpoints if Assigned(bcartROM1) and (FCartSize1 > 0) then FreeMem(bcartROM1, FCartSize1); // Get memory for new breakpoints and clear it FCartSize1 := Value; if FCartSize1 > 0 then begin GetMem(bcartRom1, FCartSize1); FillChar(bcartROM1^, FCartSize1, 0); mask := FCartSize1 - 1; mem := bcartROM1; end else begin mask := 0; mem := @null; end; // Set the bank masks bpBanks[$8] := mem; bpBankMasks[$8] := mask; bpBanks[$A] := mem; bpBankMasks[$A] := mask; bpBanks[$C] := mem; bpBankMasks[$C] := mask; end; ////////////////////////////////////////////////////////////////////// procedure TBreakpointMemory.SetCartSize2(const value: uint32); var mask: uint32; mem: pointer; begin // Free any previously active breakpoints if Assigned(bcartROM2) and (FCartSize2 > 0) then FreeMem(bcartROM2, FCartSize2); // Get memory for new breakpoints and clear it FCartSize2 := Value; if FCartSize2 > 0 then begin GetMem(bcartROM2, FCartSize2); FillChar(bcartROM2^, FCartSize2, 0); mask := FCartSize2 - 1; mem := bcartROM2; end else begin mask := 0; mem := @null; end; // Set the bank masks bpBanks[$9] := mem; bpBankMasks[$9] := mask; bpBanks[$B] := mem; bpBankMasks[$B] := mask; bpBanks[$D] := mem; bpBankMasks[$D] := mask; end; ////////////////////////////////////////////////////////////////////// function TBreakpointMemory.isBreakpoint(address: uint32): boolean; var t: uint32; data: byte; begin // Read a byte of flags from the breakpoint list and test for the address t := (address shr 24) and $F; data := bpBanks[t]^[(address shr 4) and bpBankMasks[t]]; Result := data and (1 shl ((address shr 1) and 7)) <> 0; end; ////////////////////////////////////////////////////////////////////// // Returns the new value of the toggle state function TBreakpointMemory.toggleBreakpoint(address: uint32): boolean; var t: uint32; data: ^byte; mask: uint32; begin // Read a byte of flags from the breakpoint list t := (address shr 24) and $F; data := @(bpBanks[t]^[(address shr 4) and bpBankMasks[t]]); if data = @null then begin Result := false; end else begin // Set or clear the breakpoint bit for this address mask := 1 shl ((address shr 1) and 7); data^ := (data^) xor mask; Result := (data^) and mask <> 0; end; end; ////////////////////////////////////////////////////////////////////// function memIsBreakpoint(address: uint32): boolean; begin Result := breakpoints.isBreakpoint(address); end; ////////////////////////////////////////////////////////////////////// function memStopAtAddy(address: uint32): boolean; begin Result := interestingAddresses.isBreakpoint(address); if Result then begin if not cpuSourceDebug then Result := breakpoints.isBreakpoint(address); end; end; ////////////////////////////////////////////////////////////////////// procedure vmAddBreakpoint(address: uint32; soft: boolean); begin // fixme findme, just ditch the toggle stuff from the classes if soft then begin if not sourcePoints.isBreakpoint(address) then sourcePoints.toggleBreakpoint(address); end else begin if not breakpoints.isBreakpoint(address) then breakpoints.toggleBreakpoint(address); end; if interestingAddresses.isBreakpoint(address) <> (sourcePoints.isBreakpoint(address) or breakpoints.isBreakpoint(address)) then interestingAddresses.toggleBreakpoint(address); end; ////////////////////////////////////////////////////////////////////// procedure vmRemoveBreakpoint(address: uint32; mask: TBreakpointModes); begin // fixme findme, just ditch the toggle stuff from the classes if bpmSoft in mask then begin if sourcePoints.isBreakpoint(address) then sourcePoints.toggleBreakpoint(address); end else if bpmHard in mask then begin if breakpoints.isBreakpoint(address) then breakpoints.toggleBreakpoint(address); end; if interestingAddresses.isBreakpoint(address) <> (sourcePoints.isBreakpoint(address) or breakpoints.isBreakpoint(address)) then interestingAddresses.toggleBreakpoint(address); end; ////////////////////////////////////////////////////////////////////// procedure vmSoftBreakpoints(active: boolean); begin cpuSourceDebug := active; end; ////////////////////////////////////////////////////////////////////// function vmIsBreakpoint(address: uint32): TBreakpointModes; begin Result := []; if sourcePoints.isBreakpoint(address) then Result := Result + [bpmSoft]; if breakpoints.isBreakpoint(address) then Result := Result + [bpmHard]; end; ////////////////////////////////////////////////////////////////////// function vmGetCartInfo(info: PvmOpaqueChunk): integer; begin if cartRAMdirty then begin Result := 65536*2; if info <> nil then begin Move(cartRAM, info^, Result); cartRAMdirty := false; end; end else Result := 0; end; ////////////////////////////////////////////////////////////////////// procedure vmSetCartInfo(size: integer; info: PvmOpaqueChunk); begin Move(info^, cartRAM, Min(size, 2*65536)); cartRAMdirty := false; end; ////////////////////////////////////////////////////////////////////// end. //////////////////////////////////////////////////////////////////////