diff --git a/Components/68901/MFP68901.cpp b/Components/68901/MFP68901.cpp index 3667ccd15..7cbfea9a0 100644 --- a/Components/68901/MFP68901.cpp +++ b/Components/68901/MFP68901.cpp @@ -50,7 +50,9 @@ uint8_t MFP68901::read(int address) { case 0x0a: LOG("Read: interrupt mask B"); return uint8_t(interrupt_mask_); - case 0x0b: LOG("Read: vector"); break; + case 0x0b: + LOG("Read: vector"); + return interrupt_vector_; case 0x0c: LOG("Read: timer A control"); break; case 0x0d: LOG("Read: timer B control"); break; case 0x0e: LOG("Read: timers C/D control"); break; @@ -106,7 +108,10 @@ void MFP68901::write(int address, uint8_t value) { interrupt_mask_ = (interrupt_mask_ & 0xff00) | value; update_interrupts(); break; - case 0x0b: LOG("Write: vector " << PADHEX(2) << int(value)); break; + case 0x0b: + LOG("Write: vector " << PADHEX(2) << int(value)); + interrupt_vector_ = value; + break; case 0x0c: case 0x0d: { const auto timer = address - 0xc; @@ -261,11 +266,12 @@ void MFP68901::end_interrupts(int interrupt) { } void MFP68901::update_interrupts() { + const bool old_interrupt_line = interrupt_line_; interrupt_pending_ = interrupt_in_service_ & interrupt_enable_; interrupt_line_ = interrupt_pending_ & interrupt_mask_; - if(interrupt_line_) { - LOG("Should produce interrupt..."); + if(interrupt_delegate_ && interrupt_line_ != old_interrupt_line) { + interrupt_delegate_->mfp68901_did_change_interrupt_status(this); } } @@ -273,7 +279,17 @@ bool MFP68901::get_interrupt_line() { return interrupt_line_; } -uint16_t MFP68901::acknowledge_interrupt() { - // TODO. - return 0; +uint8_t MFP68901::acknowledge_interrupt() { + uint8_t selected_interrupt = 15; + uint16_t interrupt_mask = 0x8000; + while(!(interrupt_pending_ & interrupt_mask) && interrupt_mask) { + interrupt_mask >>= 1; + --selected_interrupt; + } + end_interrupts(interrupt_mask); + return (interrupt_vector_ & 0xf0) | selected_interrupt; +} + +void MFP68901::set_interrupt_delegate(InterruptDelegate *delegate) { + interrupt_delegate_ = delegate; } diff --git a/Components/68901/MFP68901.hpp b/Components/68901/MFP68901.hpp index b56756a08..71d9464d9 100644 --- a/Components/68901/MFP68901.hpp +++ b/Components/68901/MFP68901.hpp @@ -35,7 +35,12 @@ class MFP68901 { uint8_t get_port_output(); bool get_interrupt_line(); - uint16_t acknowledge_interrupt(); + uint8_t acknowledge_interrupt(); + + struct InterruptDelegate { + virtual void mfp68901_did_change_interrupt_status(MFP68901 *) = 0; + }; + void set_interrupt_delegate(InterruptDelegate *delegate); private: // MARK: - Timers @@ -69,6 +74,8 @@ class MFP68901 { // MARK: - Interrupts + InterruptDelegate *interrupt_delegate_ = nullptr; + // Ad hoc documentation: there seems to be a four-stage process here. // This is my current understanding: // @@ -88,6 +95,7 @@ class MFP68901 { int interrupt_pending_ = 0; int interrupt_mask_ = 0; bool interrupt_line_ = false; + uint8_t interrupt_vector_ = 0; enum Interrupt { GPIP0 = (1 << 0), diff --git a/Machines/AtariST/AtariST.cpp b/Machines/AtariST/AtariST.cpp index 516f4c71e..77c2232dc 100644 --- a/Machines/AtariST/AtariST.cpp +++ b/Machines/AtariST/AtariST.cpp @@ -215,7 +215,8 @@ class ConcreteMachine: public CPU::MC68000::BusHandler, public CRTMachine::Machine, public ClockingHint::Observer, - public Motorola::ACIA::ACIA::InterruptDelegate { + public Motorola::ACIA::ACIA::InterruptDelegate, + public Motorola::MFP68901::MFP68901::InterruptDelegate { public: ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : mc68000_(*this), @@ -259,6 +260,8 @@ class ConcreteMachine: keyboard_acia_->set_clocking_hint_observer(this); ikbd_.set_clocking_hint_observer(this); + mfp_->set_interrupt_delegate(this); + set_gpip_input(); } @@ -290,6 +293,23 @@ class ConcreteMachine: /* TODO: DTack, bus error, VPA. */ + // An interrupt acknowledge, perhaps? + if(cycle.operation & Microcycle::InterruptAcknowledge) { + // Current implementation: everything other than 6 (i.e. the MFP is autovectored. + if((cycle.word_address()&7) != 6) { + mc68000_.set_is_peripheral_address(true); + return HalfCycles(0); + } else { + if(cycle.operation & Microcycle::SelectByte) { + cycle.value->halves.low = mfp_->acknowledge_interrupt(); + } + return HalfCycles(0); + } + } + + // Just in case the last cycle was an interrupt acknowledge. TODO: find a better solution? + mc68000_.set_is_peripheral_address(false); + auto address = cycle.word_address(); // if(cycle.data_select_active()) printf("%c %06x\n", (cycle.operation & Microcycle::Read) ? 'r' : 'w', *cycle.address & 0xffffff); uint16_t *memory; @@ -612,6 +632,19 @@ class ConcreteMachine: ((keyboard_acia_->get_interrupt_line() || midi_acia_->get_interrupt_line()) ? 0x0 : 0x10) // Interrupts are active low. ); } + + // MARK - MFP input. + void mfp68901_did_change_interrupt_status(Motorola::MFP68901::MFP68901 *mfp) final { + update_interrupt_input(); + } + + void update_interrupt_input() { + if(mfp_->get_interrupt_line()) { + mc68000_.set_interrupt_level(6); + } else { + mc68000_.set_interrupt_level(0); + } + } }; }