mirror of https://github.com/stenzek/duckstation
				
				
				
			Add interrupt controller emulation
							parent
							
								
									c615e007c0
								
							
						
					
					
						commit
						2128a2984b
					
				@ -0,0 +1 @@
 | 
			
		||||
#include "interrupt_controller.h"
 | 
			
		||||
@ -0,0 +1,86 @@
 | 
			
		||||
#include "interrupt_controller.h"
 | 
			
		||||
#include "YBaseLib/Log.h"
 | 
			
		||||
#include "common/state_wrapper.h"
 | 
			
		||||
#include "cpu_core.h"
 | 
			
		||||
Log_SetChannel(InterruptController);
 | 
			
		||||
 | 
			
		||||
InterruptController::InterruptController() = default;
 | 
			
		||||
 | 
			
		||||
InterruptController::~InterruptController() = default;
 | 
			
		||||
 | 
			
		||||
bool InterruptController::Initialize(CPU::Core* cpu)
 | 
			
		||||
{
 | 
			
		||||
  m_cpu = cpu;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InterruptController::Reset()
 | 
			
		||||
{
 | 
			
		||||
  m_interrupt_status_register = 0;
 | 
			
		||||
  m_interrupt_mask_register = DEFAULT_INTERRUPT_MASK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool InterruptController::DoState(StateWrapper& sw)
 | 
			
		||||
{
 | 
			
		||||
  sw.Do(&m_interrupt_status_register);
 | 
			
		||||
  sw.Do(&m_interrupt_mask_register);
 | 
			
		||||
 | 
			
		||||
  return !sw.HasError();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InterruptController::InterruptRequest(IRQ irq)
 | 
			
		||||
{
 | 
			
		||||
  const u32 bit = (u32(1) << static_cast<u32>(irq));
 | 
			
		||||
  m_interrupt_status_register |= (bit & m_interrupt_mask_register);
 | 
			
		||||
  UpdateCPUInterruptRequest();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 InterruptController::ReadRegister(u32 offset)
 | 
			
		||||
{
 | 
			
		||||
  switch (offset)
 | 
			
		||||
  {
 | 
			
		||||
    case 0x00: // I_STATUS
 | 
			
		||||
      return m_interrupt_status_register;
 | 
			
		||||
 | 
			
		||||
    case 0x04: // I_MASK
 | 
			
		||||
      return m_interrupt_mask_register;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      Log_ErrorPrintf("Invalid read at offset 0x%08X", offset);
 | 
			
		||||
      return UINT32_C(0xFFFFFFFF);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InterruptController::WriteRegister(u32 offset, u32 value)
 | 
			
		||||
{
 | 
			
		||||
  switch (offset)
 | 
			
		||||
  {
 | 
			
		||||
    case 0x00: // I_STATUS
 | 
			
		||||
    {
 | 
			
		||||
      Log_DebugPrintf("Clearing bits 0x%08X", value);
 | 
			
		||||
      m_interrupt_status_register = m_interrupt_status_register & (~(value & REGISTER_WRITE_MASK));
 | 
			
		||||
      UpdateCPUInterruptRequest();
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
    case 0x04: // I_MASK
 | 
			
		||||
    {
 | 
			
		||||
      Log_DebugPrintf("Interrupt mask <- 0x%08X", value);
 | 
			
		||||
      m_interrupt_mask_register = value & REGISTER_WRITE_MASK;
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      Log_ErrorPrintf("Invalid write at offset 0x%08X", offset);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void InterruptController::UpdateCPUInterruptRequest()
 | 
			
		||||
{
 | 
			
		||||
  // external interrupts set bit 10 only?
 | 
			
		||||
  if (m_interrupt_status_register != 0)
 | 
			
		||||
    m_cpu->SetExternalInterrupt(3);
 | 
			
		||||
  else
 | 
			
		||||
    m_cpu->ClearExternalInterrupt(3);
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,60 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "types.h"
 | 
			
		||||
 | 
			
		||||
class StateWrapper;
 | 
			
		||||
 | 
			
		||||
namespace CPU
 | 
			
		||||
{
 | 
			
		||||
class Core;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class InterruptController
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
  static constexpr u32 NUM_IRQS = 11;
 | 
			
		||||
 | 
			
		||||
  enum class IRQ : u32
 | 
			
		||||
  {
 | 
			
		||||
    VBLANK = 0, // IRQ0 - VBLANK
 | 
			
		||||
    GPU = 1,    // IRQ1 - GPU via GP0(1Fh)
 | 
			
		||||
    CDROM = 2,  // IRQ2 - CDROM
 | 
			
		||||
    DMA = 3,    // IRQ3 - DMA
 | 
			
		||||
    TMR0 = 4,   // IRQ4 - TMR0 - Sysclk or Dotclk
 | 
			
		||||
    TMR1 = 5,   // IRQ5 - TMR1 - Sysclk Hblank
 | 
			
		||||
    TMR2 = 6,   // IRQ6 - TMR2 - Sysclk or Sysclk / 8
 | 
			
		||||
    IRQ7 = 7,   // IRQ7 - Controller and Memory Card Byte Received
 | 
			
		||||
    SIO = 8,    // IRQ8 - SIO
 | 
			
		||||
    SPU = 9,    // IRQ9 - SPU
 | 
			
		||||
    IRQ10 = 10  // IRQ10 - Lightpen interrupt, PIO
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  InterruptController();
 | 
			
		||||
  ~InterruptController();
 | 
			
		||||
 | 
			
		||||
  bool Initialize(CPU::Core* cpu);
 | 
			
		||||
  void Reset();
 | 
			
		||||
  bool DoState(StateWrapper& sw);
 | 
			
		||||
 | 
			
		||||
  // Should mirror CPU state.
 | 
			
		||||
  bool GetIRQLineState() const { return (m_interrupt_status_register != 0); }
 | 
			
		||||
 | 
			
		||||
  // Interupts are edge-triggered, so if it is masked when TriggerInterrupt() is called, it will be lost.
 | 
			
		||||
  void InterruptRequest(IRQ irq);
 | 
			
		||||
 | 
			
		||||
  // I/O
 | 
			
		||||
  u32 ReadRegister(u32 offset);
 | 
			
		||||
  void WriteRegister(u32 offset, u32 value);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  static constexpr u32 REGISTER_WRITE_MASK = (u32(1) << NUM_IRQS) - 1;
 | 
			
		||||
  static constexpr u32 DEFAULT_INTERRUPT_MASK = (u32(1) << NUM_IRQS) - 1;
 | 
			
		||||
 | 
			
		||||
  void UpdateCPUInterruptRequest();
 | 
			
		||||
 | 
			
		||||
  CPU::Core* m_cpu;
 | 
			
		||||
 | 
			
		||||
  u32 m_interrupt_status_register = 0;
 | 
			
		||||
  u32 m_interrupt_mask_register = DEFAULT_INTERRUPT_MASK;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue