interrupt_controller.cpp (3423B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "interrupt_controller.h" 5 #include "cpu_core.h" 6 7 #include "util/state_wrapper.h" 8 9 #include "common/log.h" 10 11 Log_SetChannel(InterruptController); 12 13 namespace InterruptController { 14 15 static constexpr u32 REGISTER_WRITE_MASK = (u32(1) << NUM_IRQS) - 1; 16 static constexpr u32 DEFAULT_INTERRUPT_MASK = 0; 17 18 static void UpdateCPUInterruptRequest(); 19 20 static u32 s_interrupt_status_register = 0; 21 static u32 s_interrupt_mask_register = DEFAULT_INTERRUPT_MASK; 22 static u32 s_interrupt_line_state = 0; 23 24 [[maybe_unused]] static constexpr std::array<const char*, static_cast<size_t>(IRQ::MaxCount)> s_irq_names = { 25 {"VBLANK", "GPU", "CDROM", "DMA", "TMR0", "TMR1", "TMR2", "PAD", "SIO", "SPU", "IRQ10"}}; 26 27 } // namespace InterruptController 28 29 void InterruptController::Reset() 30 { 31 s_interrupt_status_register = 0; 32 s_interrupt_mask_register = DEFAULT_INTERRUPT_MASK; 33 s_interrupt_line_state = 0; 34 } 35 36 bool InterruptController::DoState(StateWrapper& sw) 37 { 38 sw.Do(&s_interrupt_status_register); 39 sw.Do(&s_interrupt_mask_register); 40 sw.DoEx(&s_interrupt_line_state, 63, s_interrupt_status_register); 41 42 return !sw.HasError(); 43 } 44 45 void InterruptController::SetLineState(IRQ irq, bool state) 46 { 47 // Interupts are edge-triggered, so only set the flag in the status register on a 0-1 transition. 48 const u32 bit = (1u << static_cast<u32>(irq)); 49 const u32 prev_state = s_interrupt_line_state; 50 s_interrupt_line_state = (s_interrupt_line_state & ~bit) | (state ? bit : 0u); 51 if (s_interrupt_line_state == prev_state) 52 return; 53 54 #ifdef _DEBUG 55 if (!(prev_state & bit) && state) 56 DEBUG_LOG("{} IRQ triggered", s_irq_names[static_cast<size_t>(irq)]); 57 else if ((prev_state & bit) && !state) 58 DEBUG_LOG("{} IRQ line inactive", s_irq_names[static_cast<size_t>(irq)]); 59 #endif 60 61 s_interrupt_status_register |= (state ? (prev_state ^ s_interrupt_line_state) : 0u) & s_interrupt_line_state; 62 UpdateCPUInterruptRequest(); 63 } 64 65 u32 InterruptController::ReadRegister(u32 offset) 66 { 67 switch (offset) 68 { 69 case 0x00: // I_STATUS 70 return s_interrupt_status_register; 71 72 case 0x04: // I_MASK 73 return s_interrupt_mask_register; 74 75 [[unlikely]] default: 76 ERROR_LOG("Invalid read at offset 0x{:08X}", offset); 77 return UINT32_C(0xFFFFFFFF); 78 } 79 } 80 81 void InterruptController::WriteRegister(u32 offset, u32 value) 82 { 83 switch (offset) 84 { 85 case 0x00: // I_STATUS 86 { 87 #ifdef _DEBUG 88 const u32 cleared_bits = (s_interrupt_status_register & ~value); 89 for (u32 i = 0; i < static_cast<u32>(IRQ::MaxCount); i++) 90 { 91 if (cleared_bits & (1u << i)) 92 DEBUG_LOG("{} IRQ cleared", s_irq_names[i]); 93 } 94 #endif 95 96 s_interrupt_status_register = s_interrupt_status_register & (value & REGISTER_WRITE_MASK); 97 UpdateCPUInterruptRequest(); 98 } 99 break; 100 101 case 0x04: // I_MASK 102 { 103 DEBUG_LOG("Interrupt mask <- 0x{:08X}", value); 104 s_interrupt_mask_register = value & REGISTER_WRITE_MASK; 105 UpdateCPUInterruptRequest(); 106 } 107 break; 108 109 default: 110 [[unlikely]] ERROR_LOG("Invalid write at offset 0x{:08X}", offset); 111 break; 112 } 113 } 114 115 ALWAYS_INLINE_RELEASE void InterruptController::UpdateCPUInterruptRequest() 116 { 117 const bool state = (s_interrupt_status_register & s_interrupt_mask_register) != 0; 118 CPU::SetIRQRequest(state); 119 }