From 3efe6a587d95db7427ccfcd1d479f56fd9296497 Mon Sep 17 00:00:00 2001 From: Neil Berkman Date: Fri, 6 Mar 2026 00:14:50 -0800 Subject: [PATCH] [#877] Bus: Set IO_MEM_EXECUTABLE_IO for all non-IMapped peripherals Previously, only ArrayMemory peripherals had the IO_MEM_EXECUTABLE_IO flag set, allowing instruction fetch via I/O callbacks. Any other non-IMapped peripheral (e.g. dynamically compiled C# peripherals) would trigger cpu_abort when the CPU tried to execute code from their address range. Generalize the check from `peripheral is ArrayMemory` to `!(peripheral is IMapped)` in three places: - SystemBus.RegisterInner: set flag when peripheral is first registered - SystemBus.MoveRegistrationWithinContext: preserve flag on re-registration - Machine.PostCreationActions: set flag for peripherals registered before CPU --- src/Emulator/Main/Core/Machine.cs | 5 +++-- src/Emulator/Main/Peripherals/Bus/SystemBus.cs | 12 +++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Emulator/Main/Core/Machine.cs b/src/Emulator/Main/Core/Machine.cs index 0978f68e4..601974479 100644 --- a/src/Emulator/Main/Core/Machine.cs +++ b/src/Emulator/Main/Core/Machine.cs @@ -1027,10 +1027,11 @@ public void PostCreationActions() } } - // Register io_executable flags for all ArrayMemory peripherals + // Register io_executable flags for all non-IMapped peripherals so that + // instruction fetches from them go through I/O callbacks instead of aborting. foreach(var context in SystemBus.GetAllContextKeys()) { - foreach(var registration in SystemBus.GetRegistrationsForPeripheralType(context)) + foreach(var registration in SystemBus.GetRegistrationsForPeripheralType(context).Where(r => !(r.Peripheral is IMapped))) { var range = registration.RegistrationPoint.Range; var perCore = registration.RegistrationPoint.Initiator; diff --git a/src/Emulator/Main/Peripherals/Bus/SystemBus.cs b/src/Emulator/Main/Peripherals/Bus/SystemBus.cs index 4f6cb174d..ad6a1eace 100644 --- a/src/Emulator/Main/Peripherals/Bus/SystemBus.cs +++ b/src/Emulator/Main/Peripherals/Bus/SystemBus.cs @@ -1260,7 +1260,7 @@ public void MoveRegistrationWithinContext(IBusPeripheral peripheral, BusRangeReg AddMappingsForPeripheral(peripheral, newRegistration, context); } - if(peripheral is ArrayMemory) + if(!(peripheral is IMapped)) { foreach(var cpu in GetCPUsForContext(context)) { @@ -2054,6 +2054,16 @@ private void RegisterInner(IBusPeripheral peripheral, PeripheralAccessMethods me }); // let's add new mappings AddMappingsForPeripheral(peripheral, registrationPoint, context); + // For non-IMapped peripherals, mark the range as executable I/O so that + // instruction fetches go through I/O callbacks instead of causing cpu_abort. + if(!(peripheral is IMapped)) + { + foreach(var cpu in GetCPUsForContext(context)) + { + var range = registrationPoint.Range; + cpu.RegisterAccessFlags(range.StartAddress, range.Size, isIoMemory: true); + } + } // After adding new mappings, if the address range is locked, the mappings possibly have to be modified/unmapped on the CPU's side // RelockRange to make this happen if(IsAddressRangeLocked(registrationPoint.Range, context))