diff --git a/Machines/Amiga/Amiga.cpp b/Machines/Amiga/Amiga.cpp index ef9ba2702..5a5bf2208 100644 --- a/Machines/Amiga/Amiga.cpp +++ b/Machines/Amiga/Amiga.cpp @@ -82,6 +82,7 @@ class ConcreteMachine: const auto changes = chipset_.run_for(cycle.length); cia_a_.advance_tod(changes.vsyncs); cia_b_.advance_tod(changes.hsyncs); + mc68000_.set_interrupt_level(changes.interrupt_level); // Check for assertion of reset. if(cycle.operation & Microcycle::Reset) { @@ -89,6 +90,13 @@ class ConcreteMachine: LOG("Reset; PC is around " << PADHEX(8) << mc68000_.get_state().program_counter); } + // Autovector interrupts. + if(cycle.operation & Microcycle::InterruptAcknowledge) { + mc68000_.set_is_peripheral_address(true); + } else { + mc68000_.set_is_peripheral_address(false); + } + // Do nothing if no address is exposed. if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0); diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp index f8c8d46d8..87fd1610b 100644 --- a/Machines/Amiga/Chipset.cpp +++ b/Machines/Amiga/Chipset.cpp @@ -16,6 +16,27 @@ using namespace Amiga; +namespace { + +enum InterruptFlag: uint16_t { + SerialPortTransmit = 1 << 0, + DiskBlock = 1 << 1, + Software = 1 << 2, + IOPortsAndTimers = 1 << 3, + Copper = 1 << 4, + VerticalBlank = 1 << 5, + Blitter = 1 << 6, + AudioChannel0 = 1 << 7, + AudioChannel1 = 1 << 8, + AudioChannel2 = 1 << 9, + AudioChannel3 = 1 << 10, + SerialPortReceive = 1 << 11, + DiskSyncMatch = 1 << 12, + External = 1 << 13, +}; + +} + Chipset::Chipset(uint16_t *ram, size_t size) : blitter_(ram, size) { } @@ -33,9 +54,37 @@ Chipset::Changes Chipset::run_for(HalfCycles length) { changes.vsyncs = y_ / 312; y_ %= 312; + // y = 0 => start of vertical blank. + if(changes.vsyncs) { + interrupt_requests_ |= InterruptFlag::VerticalBlank; + update_interrupts(); + } + + changes.interrupt_level = interrupt_level_; return changes; } +void Chipset::update_interrupts() { + interrupt_level_ = 0; + + const uint16_t enabled_requests = interrupt_enable_ & interrupt_requests_ & 0x3fff; + if(enabled_requests && (interrupt_enable_ & 0x4000)) { + if(enabled_requests & (InterruptFlag::External)) { + interrupt_level_ = 6; + } else if(enabled_requests & (InterruptFlag::SerialPortReceive | InterruptFlag::DiskSyncMatch)) { + interrupt_level_ = 5; + } else if(enabled_requests & (InterruptFlag::AudioChannel0 | InterruptFlag::AudioChannel1 | InterruptFlag::AudioChannel2 | InterruptFlag::AudioChannel3)) { + interrupt_level_ = 4; + } else if(enabled_requests & (InterruptFlag::Copper | InterruptFlag::VerticalBlank | InterruptFlag::Blitter)) { + interrupt_level_ = 3; + } else if(enabled_requests & (InterruptFlag::External)) { + interrupt_level_ = 2; + } else if(enabled_requests & (InterruptFlag::SerialPortTransmit | InterruptFlag::DiskBlock | InterruptFlag::Software)) { + interrupt_level_ = 1; + } + } +} + void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { using Microcycle = CPU::MC68000::Microcycle; @@ -119,11 +168,18 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) { update_interrupts(); LOG("Interrupt enable mask modified by " << PADHEX(4) << cycle.value16() << "; is now " << std::bitset<16>{interrupt_enable_}); break; + case Read(0x01c): + cycle.set_value16(interrupt_enable_); + break; + case Write(0x09c): ApplySetClear(interrupt_requests_); update_interrupts(); LOG("Interrupt request modified by " << PADHEX(4) << cycle.value16() << "; is now " << std::bitset<16>{interrupt_requests_}); break; + case Read(0x01e): + cycle.set_value16(interrupt_requests_); + break; // Display management. case Write(0x08e): { diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp index 520e809e7..2d6fc1a98 100644 --- a/Machines/Amiga/Chipset.hpp +++ b/Machines/Amiga/Chipset.hpp @@ -29,7 +29,7 @@ class Chipset { struct Changes { int hsyncs = 0; int vsyncs = 0; - // TODO: interrupt change? + int interrupt_level = 0; }; /// Advances the stated amount of time. @@ -38,15 +38,19 @@ class Chipset { /// Performs the provided microcycle, which the caller guarantees to be a memory access. void perform(const CPU::MC68000::Microcycle &); + /// Provides the chipset's current interrupt level. + int get_interrupt_level() { + return interrupt_level_; + } + private: // MARK: - Interrupts. uint16_t interrupt_enable_ = 0; uint16_t interrupt_requests_ = 0; + int interrupt_level_ = 0; - void update_interrupts() { - // TODO. - } + void update_interrupts(); // MARK: - DMA Control and Blitter.