2019-10-07 03:14:05 +00:00
|
|
|
|
//
|
|
|
|
|
// MFP68901.cpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 06/10/2019.
|
|
|
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "MFP68901.hpp"
|
|
|
|
|
|
2019-12-21 04:33:14 +00:00
|
|
|
|
#include <algorithm>
|
2019-10-26 19:55:19 +00:00
|
|
|
|
#include <cstring>
|
|
|
|
|
|
2019-10-08 02:44:35 +00:00
|
|
|
|
#include "../../Outputs/Log.hpp"
|
|
|
|
|
|
2024-01-19 19:22:23 +00:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
Log::Logger<Log::Source::MFP68901> logger;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-07 03:14:05 +00:00
|
|
|
|
using namespace Motorola::MFP68901;
|
|
|
|
|
|
2020-05-10 01:22:51 +00:00
|
|
|
|
ClockingHint::Preference MFP68901::preferred_clocking() const {
|
2019-10-31 02:42:06 +00:00
|
|
|
|
// Rule applied: if any timer is actively running and permitted to produce an
|
|
|
|
|
// interrupt, request real-time running.
|
|
|
|
|
return
|
|
|
|
|
(timers_[0].mode >= TimerMode::Delay && interrupt_enable_&Interrupt::TimerA) ||
|
|
|
|
|
(timers_[1].mode >= TimerMode::Delay && interrupt_enable_&Interrupt::TimerB) ||
|
|
|
|
|
(timers_[2].mode >= TimerMode::Delay && interrupt_enable_&Interrupt::TimerC) ||
|
|
|
|
|
(timers_[3].mode >= TimerMode::Delay && interrupt_enable_&Interrupt::TimerD)
|
|
|
|
|
? ClockingHint::Preference::RealTime : ClockingHint::Preference::JustInTime;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-07 03:14:05 +00:00
|
|
|
|
uint8_t MFP68901::read(int address) {
|
2019-10-08 02:44:35 +00:00
|
|
|
|
address &= 0x1f;
|
2019-10-30 03:07:03 +00:00
|
|
|
|
|
|
|
|
|
// Interrupt block: various bits of state can be read, all passively.
|
|
|
|
|
if(address >= 0x03 && address <= 0x0b) {
|
|
|
|
|
const int shift = (address&1) << 3;
|
|
|
|
|
switch(address) {
|
|
|
|
|
case 0x03: case 0x04: return uint8_t(interrupt_enable_ >> shift);
|
|
|
|
|
case 0x05: case 0x06: return uint8_t(interrupt_pending_ >> shift);
|
|
|
|
|
case 0x07: case 0x08: return uint8_t(interrupt_in_service_ >> shift);
|
|
|
|
|
case 0x09: case 0x0a: return uint8_t(interrupt_mask_ >> shift);
|
|
|
|
|
case 0x0b: return interrupt_vector_;
|
|
|
|
|
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-08 02:44:35 +00:00
|
|
|
|
switch(address) {
|
2019-10-30 03:07:03 +00:00
|
|
|
|
// GPIP block: input, and configured active edge and direction values.
|
|
|
|
|
case 0x00: return (gpip_input_ & ~gpip_direction_) | (gpip_output_ & gpip_direction_);
|
|
|
|
|
case 0x01: return gpip_active_edge_;
|
|
|
|
|
case 0x02: return gpip_direction_;
|
|
|
|
|
|
|
|
|
|
/* Interrupt block dealt with above. */
|
|
|
|
|
default: break;
|
|
|
|
|
|
|
|
|
|
// Timer block: read back A, B and C/D control, and read current timer values.
|
|
|
|
|
case 0x0c: case 0x0d: return timer_ab_control_[address - 0xc];
|
|
|
|
|
case 0x0e: return timer_cd_control_;
|
|
|
|
|
case 0x0f: case 0x10:
|
|
|
|
|
case 0x11: case 0x12: return get_timer_data(address - 0xf);
|
|
|
|
|
|
|
|
|
|
// USART block: TODO.
|
2024-01-19 19:22:23 +00:00
|
|
|
|
case 0x13: logger.error().append("Read: sync character generator"); break;
|
|
|
|
|
case 0x14: logger.error().append("Read: USART control"); break;
|
|
|
|
|
case 0x15: logger.error().append("Read: receiver status"); break;
|
|
|
|
|
case 0x16: logger.error().append("Read: transmitter status"); break;
|
|
|
|
|
case 0x17: logger.error().append("Read: USART data"); break;
|
2019-10-08 02:44:35 +00:00
|
|
|
|
}
|
2019-10-11 02:46:58 +00:00
|
|
|
|
return 0x00;
|
2019-10-07 03:14:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MFP68901::write(int address, uint8_t value) {
|
2019-10-08 02:44:35 +00:00
|
|
|
|
address &= 0x1f;
|
2019-10-30 03:07:03 +00:00
|
|
|
|
|
|
|
|
|
// Interrupt block: enabled and masked interrupts can be set; pending and in-service interrupts can be masked.
|
|
|
|
|
if(address >= 0x03 && address <= 0x0b) {
|
|
|
|
|
const int shift = (address&1) << 3;
|
|
|
|
|
const int preserve = 0xff00 >> shift;
|
|
|
|
|
const int word_value = value << shift;
|
|
|
|
|
|
|
|
|
|
switch(address) {
|
|
|
|
|
default: break;
|
|
|
|
|
case 0x03: case 0x04: // Adjust enabled interrupts; disabled ones also cease to be pending.
|
|
|
|
|
interrupt_enable_ = (interrupt_enable_ & preserve) | word_value;
|
|
|
|
|
interrupt_pending_ &= interrupt_enable_;
|
|
|
|
|
break;
|
|
|
|
|
case 0x05: case 0x06: // Resolve pending interrupts.
|
|
|
|
|
interrupt_pending_ &= (preserve | word_value);
|
|
|
|
|
break;
|
|
|
|
|
case 0x07: case 0x08: // Resolve in-service interrupts.
|
|
|
|
|
interrupt_in_service_ &= (preserve | word_value);
|
|
|
|
|
break;
|
|
|
|
|
case 0x09: case 0x0a: // Adjust interrupt mask.
|
|
|
|
|
interrupt_mask_ = (interrupt_mask_ & preserve) | word_value;
|
|
|
|
|
break;
|
|
|
|
|
case 0x0b: // Set the interrupt vector, possibly changing end-of-interrupt mode.
|
|
|
|
|
interrupt_vector_ = value;
|
|
|
|
|
|
|
|
|
|
// If automatic end-of-interrupt mode has now been enabled, clear
|
|
|
|
|
// the in-process mask and re-evaluate.
|
|
|
|
|
if(interrupt_vector_ & 0x08) return;
|
|
|
|
|
interrupt_in_service_ = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Whatever just happened may have affected the state of the interrupt line.
|
|
|
|
|
update_interrupts();
|
2019-10-31 02:42:06 +00:00
|
|
|
|
update_clocking_observer();
|
2019-10-30 03:07:03 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-22 05:22:17 +00:00
|
|
|
|
constexpr int timer_prescales[] = {
|
2019-10-31 02:42:06 +00:00
|
|
|
|
1, 4, 10, 16, 50, 64, 100, 200
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-08 02:44:35 +00:00
|
|
|
|
switch(address) {
|
2019-10-30 03:07:03 +00:00
|
|
|
|
// GPIP block: output and configuration of active edge and direction values.
|
2019-10-22 03:02:30 +00:00
|
|
|
|
case 0x00:
|
|
|
|
|
gpip_output_ = value;
|
|
|
|
|
break;
|
|
|
|
|
case 0x01:
|
|
|
|
|
gpip_active_edge_ = value;
|
|
|
|
|
reevaluate_gpip_interrupts();
|
|
|
|
|
break;
|
|
|
|
|
case 0x02:
|
|
|
|
|
gpip_direction_ = value;
|
|
|
|
|
reevaluate_gpip_interrupts();
|
|
|
|
|
break;
|
2019-10-26 19:55:19 +00:00
|
|
|
|
|
2019-10-30 03:07:03 +00:00
|
|
|
|
/* Interrupt block dealt with above. */
|
|
|
|
|
default: break;
|
|
|
|
|
|
|
|
|
|
// Timer block.
|
2019-10-08 02:44:35 +00:00
|
|
|
|
case 0x0c:
|
|
|
|
|
case 0x0d: {
|
|
|
|
|
const auto timer = address - 0xc;
|
|
|
|
|
const bool reset = value & 0x10;
|
2019-10-28 02:38:49 +00:00
|
|
|
|
timer_ab_control_[timer] = value;
|
2019-10-08 02:44:35 +00:00
|
|
|
|
switch(value & 0xf) {
|
2019-10-27 03:20:13 +00:00
|
|
|
|
case 0x0: set_timer_mode(timer, TimerMode::Stopped, 1, reset); break;
|
2019-10-08 02:44:35 +00:00
|
|
|
|
case 0x1: set_timer_mode(timer, TimerMode::Delay, 4, reset); break;
|
|
|
|
|
case 0x2: set_timer_mode(timer, TimerMode::Delay, 10, reset); break;
|
|
|
|
|
case 0x3: set_timer_mode(timer, TimerMode::Delay, 16, reset); break;
|
|
|
|
|
case 0x4: set_timer_mode(timer, TimerMode::Delay, 50, reset); break;
|
|
|
|
|
case 0x5: set_timer_mode(timer, TimerMode::Delay, 64, reset); break;
|
|
|
|
|
case 0x6: set_timer_mode(timer, TimerMode::Delay, 100, reset); break;
|
|
|
|
|
case 0x7: set_timer_mode(timer, TimerMode::Delay, 200, reset); break;
|
2019-10-27 03:20:13 +00:00
|
|
|
|
case 0x8: set_timer_mode(timer, TimerMode::EventCount, 1, reset); break;
|
2019-10-08 02:44:35 +00:00
|
|
|
|
case 0x9: set_timer_mode(timer, TimerMode::PulseWidth, 4, reset); break;
|
|
|
|
|
case 0xa: set_timer_mode(timer, TimerMode::PulseWidth, 10, reset); break;
|
|
|
|
|
case 0xb: set_timer_mode(timer, TimerMode::PulseWidth, 16, reset); break;
|
|
|
|
|
case 0xc: set_timer_mode(timer, TimerMode::PulseWidth, 50, reset); break;
|
|
|
|
|
case 0xd: set_timer_mode(timer, TimerMode::PulseWidth, 64, reset); break;
|
|
|
|
|
case 0xe: set_timer_mode(timer, TimerMode::PulseWidth, 100, reset); break;
|
|
|
|
|
case 0xf: set_timer_mode(timer, TimerMode::PulseWidth, 200, reset); break;
|
|
|
|
|
}
|
|
|
|
|
} break;
|
|
|
|
|
case 0x0e:
|
2019-10-28 02:38:49 +00:00
|
|
|
|
timer_cd_control_ = value;
|
2019-10-31 02:42:06 +00:00
|
|
|
|
set_timer_mode(3, (value & 7) ? TimerMode::Delay : TimerMode::Stopped, timer_prescales[value & 7], false);
|
|
|
|
|
set_timer_mode(2, ((value >> 4) & 7) ? TimerMode::Delay : TimerMode::Stopped, timer_prescales[(value >> 4) & 7], false);
|
2019-10-08 02:44:35 +00:00
|
|
|
|
break;
|
|
|
|
|
case 0x0f: case 0x10: case 0x11: case 0x12:
|
|
|
|
|
set_timer_data(address - 0xf, value);
|
|
|
|
|
break;
|
2019-10-30 03:07:03 +00:00
|
|
|
|
|
|
|
|
|
// USART block: TODO.
|
2024-01-19 19:22:23 +00:00
|
|
|
|
case 0x13: logger.error().append("Write: sync character generator"); break;
|
|
|
|
|
case 0x14: logger.error().append("Write: USART control"); break;
|
|
|
|
|
case 0x15: logger.error().append("Write: receiver status"); break;
|
|
|
|
|
case 0x16: logger.error().append("Write: transmitter status"); break;
|
|
|
|
|
case 0x17: logger.error().append("Write: USART data"); break;
|
2019-10-08 02:44:35 +00:00
|
|
|
|
}
|
2019-10-31 02:42:06 +00:00
|
|
|
|
|
|
|
|
|
update_clocking_observer();
|
2019-10-07 03:14:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 19:57:41 +00:00
|
|
|
|
template <int timer>
|
|
|
|
|
void MFP68901::run_timer_for(int cycles) {
|
|
|
|
|
if(timers_[timer].mode >= TimerMode::Delay) {
|
|
|
|
|
// This code applies the timer prescaling only. prescale_count is used to count
|
|
|
|
|
// upwards rather than downwards for simplicity, but on the real hardware it's
|
|
|
|
|
// pretty safe to assume it actually counted downwards. So the clamp to 0 is
|
|
|
|
|
// because gymnastics may need to occur when the prescale value is altered, e.g.
|
|
|
|
|
// if a prescale of 256 is set and the prescale_count is currently 2 then the
|
|
|
|
|
// counter should roll over in 254 cycles. If the user at that point changes the
|
|
|
|
|
// prescale_count to 1 then the counter will need to be altered to -253 and
|
|
|
|
|
// allowed to keep counting up until it crosses both 0 and 1.
|
|
|
|
|
const int dividend = timers_[timer].prescale_count + cycles;
|
|
|
|
|
const int decrements = std::max(dividend / timers_[timer].prescale, 0);
|
|
|
|
|
if(decrements) {
|
|
|
|
|
decrement_timer<timer>(decrements);
|
|
|
|
|
timers_[timer].prescale_count = dividend % timers_[timer].prescale;
|
|
|
|
|
} else {
|
|
|
|
|
timers_[timer].prescale_count += cycles;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-08 02:44:35 +00:00
|
|
|
|
void MFP68901::run_for(HalfCycles time) {
|
|
|
|
|
cycles_left_ += time;
|
|
|
|
|
|
2019-12-10 00:23:08 +00:00
|
|
|
|
const int cycles = int(cycles_left_.flush<Cycles>().as_integral());
|
2019-12-21 04:33:14 +00:00
|
|
|
|
if(!cycles) return;
|
|
|
|
|
|
2023-11-27 19:57:41 +00:00
|
|
|
|
run_timer_for<0>(cycles);
|
|
|
|
|
run_timer_for<1>(cycles);
|
|
|
|
|
run_timer_for<2>(cycles);
|
|
|
|
|
run_timer_for<3>(cycles);
|
2019-10-07 03:14:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-10 22:00:49 +00:00
|
|
|
|
HalfCycles MFP68901::next_sequence_point() {
|
2021-04-21 01:56:58 +00:00
|
|
|
|
return HalfCycles::max();
|
2019-10-07 03:14:05 +00:00
|
|
|
|
}
|
2019-10-08 02:44:35 +00:00
|
|
|
|
|
|
|
|
|
// MARK: - Timers
|
|
|
|
|
|
|
|
|
|
void MFP68901::set_timer_mode(int timer, TimerMode mode, int prescale, bool reset_timer) {
|
2024-01-19 19:22:23 +00:00
|
|
|
|
logger.error().append("Timer %d mode set: %d; prescale: %d", timer, mode, prescale);
|
2019-10-08 02:44:35 +00:00
|
|
|
|
timers_[timer].mode = mode;
|
|
|
|
|
if(reset_timer) {
|
2019-12-21 04:33:14 +00:00
|
|
|
|
timers_[timer].prescale_count = 0;
|
2019-10-08 02:44:35 +00:00
|
|
|
|
timers_[timer].value = timers_[timer].reload_value;
|
2019-12-21 04:33:14 +00:00
|
|
|
|
} else {
|
|
|
|
|
// This hoop is because the prescale_count here goes upward but I'm assuming it goes downward in
|
|
|
|
|
// real hardware. Therefore this deals with the "switched to a lower prescaling" case whereby the
|
|
|
|
|
// old cycle should be allowed naturally to expire.
|
|
|
|
|
timers_[timer].prescale_count = prescale - (timers_[timer].prescale - timers_[timer].prescale_count);
|
2019-10-08 02:44:35 +00:00
|
|
|
|
}
|
2019-12-21 04:33:14 +00:00
|
|
|
|
|
|
|
|
|
timers_[timer].prescale = prescale;
|
2019-10-08 02:44:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MFP68901::set_timer_data(int timer, uint8_t value) {
|
|
|
|
|
if(timers_[timer].mode == TimerMode::Stopped) {
|
|
|
|
|
timers_[timer].value = value;
|
|
|
|
|
}
|
|
|
|
|
timers_[timer].reload_value = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t MFP68901::get_timer_data(int timer) {
|
|
|
|
|
return timers_[timer].value;
|
|
|
|
|
}
|
2019-10-10 03:01:11 +00:00
|
|
|
|
|
2023-11-27 19:57:41 +00:00
|
|
|
|
template <int channel>
|
|
|
|
|
void MFP68901::set_timer_event_input(bool value) {
|
2019-10-10 03:01:11 +00:00
|
|
|
|
if(timers_[channel].event_input == value) return;
|
|
|
|
|
|
|
|
|
|
timers_[channel].event_input = value;
|
2019-11-19 03:37:20 +00:00
|
|
|
|
if(timers_[channel].mode == TimerMode::EventCount && (value == !!(gpip_active_edge_ & (0x10 >> channel)))) {
|
|
|
|
|
// "The active state of the signal on TAI or TBI is dependent upon the associated
|
|
|
|
|
// Interrupt Channel’s edge bit (GPIP 4 for TAI and GPIP 3 for TBI [...] ).
|
|
|
|
|
// If the edge bit associated with the TAI or TBI input is a one, it will be active high.
|
2023-11-27 19:57:41 +00:00
|
|
|
|
decrement_timer<channel>(1);
|
2019-10-10 03:01:11 +00:00
|
|
|
|
}
|
2019-11-20 03:24:32 +00:00
|
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
|
//
|
|
|
|
|
// Altering the edge bit while the timer is in the event count mode can produce a count pulse.
|
|
|
|
|
// The interrupt channel associated with the input (I3 for I4 for TAI) is allowed to function normally.
|
|
|
|
|
// To count transitions reliably, the input must remain in each state (1/O) for a length of time equal
|
|
|
|
|
// to four periods of the timer clock.
|
|
|
|
|
//
|
|
|
|
|
// (the final bit probably explains 13 cycles of the DE to interrupt latency; not sure about the other ~15)
|
2019-10-10 03:01:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 19:57:41 +00:00
|
|
|
|
template void MFP68901::set_timer_event_input<0>(bool);
|
|
|
|
|
template void MFP68901::set_timer_event_input<1>(bool);
|
|
|
|
|
template void MFP68901::set_timer_event_input<2>(bool);
|
|
|
|
|
template void MFP68901::set_timer_event_input<3>(bool);
|
|
|
|
|
|
|
|
|
|
template <int timer>
|
|
|
|
|
void MFP68901::decrement_timer(int amount) {
|
2023-11-27 20:16:22 +00:00
|
|
|
|
while(amount) {
|
|
|
|
|
if(timers_[timer].value > amount) {
|
|
|
|
|
timers_[timer].value -= amount;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-20 03:24:32 +00:00
|
|
|
|
|
2023-11-27 20:16:22 +00:00
|
|
|
|
// Keep this check here to avoid the case where a decrement to zero occurs during one call to
|
|
|
|
|
// decrement_timer, triggering an interrupt, then the timer is already 0 at the next instance,
|
|
|
|
|
// causing a second interrupt.
|
|
|
|
|
//
|
|
|
|
|
// ... even though it would be nice to move it down below, after value has overtly been set to 0.
|
|
|
|
|
if(!timers_[timer].value) {
|
|
|
|
|
--timers_[timer].value;
|
|
|
|
|
--amount;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If here then amount is sufficient to, at least once, decrement the timer
|
|
|
|
|
// from 1 to 0. So there's an interrupt.
|
|
|
|
|
//
|
|
|
|
|
// (and, this switch is why this function is templated on timer ID)
|
|
|
|
|
switch(timer) {
|
|
|
|
|
case 0: begin_interrupts(Interrupt::TimerA); break;
|
|
|
|
|
case 1: begin_interrupts(Interrupt::TimerB); break;
|
|
|
|
|
case 2: begin_interrupts(Interrupt::TimerC); break;
|
|
|
|
|
case 3: begin_interrupts(Interrupt::TimerD); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Re: reloading when in event counting mode; I found the data sheet thoroughly unclear on
|
|
|
|
|
// this, but it appears empirically to be correct. See e.g. Pompey Pirates menu 27.
|
|
|
|
|
amount -= timers_[timer].value;
|
|
|
|
|
if(timers_[timer].mode == TimerMode::Delay || timers_[timer].mode == TimerMode::EventCount) {
|
|
|
|
|
timers_[timer].value = timers_[timer].reload_value; // TODO: properly.
|
|
|
|
|
} else {
|
|
|
|
|
timers_[timer].value = 0;
|
2019-10-27 03:20:13 +00:00
|
|
|
|
}
|
2019-10-10 03:01:11 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-22 03:02:30 +00:00
|
|
|
|
|
|
|
|
|
// MARK: - GPIP
|
|
|
|
|
void MFP68901::set_port_input(uint8_t input) {
|
|
|
|
|
gpip_input_ = input;
|
|
|
|
|
reevaluate_gpip_interrupts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t MFP68901::get_port_output() {
|
2019-11-20 03:32:07 +00:00
|
|
|
|
return 0xff; // TODO.
|
2019-10-22 03:02:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MFP68901::reevaluate_gpip_interrupts() {
|
2019-10-30 03:07:03 +00:00
|
|
|
|
const uint8_t gpip_state = (gpip_input_ & ~gpip_direction_) ^ gpip_active_edge_;
|
2019-10-25 02:33:42 +00:00
|
|
|
|
|
2019-10-22 03:02:30 +00:00
|
|
|
|
// An interrupt is detected on any falling edge.
|
2019-10-25 02:33:42 +00:00
|
|
|
|
const uint8_t new_interrupt_mask = (gpip_state ^ gpip_interrupt_state_) & gpip_interrupt_state_;
|
|
|
|
|
if(new_interrupt_mask) {
|
|
|
|
|
begin_interrupts(
|
|
|
|
|
(new_interrupt_mask & 0x0f) |
|
2019-10-25 02:37:32 +00:00
|
|
|
|
((new_interrupt_mask & 0x30) << 2) |
|
2019-10-25 02:33:42 +00:00
|
|
|
|
((new_interrupt_mask & 0xc0) << 8)
|
|
|
|
|
);
|
2019-10-22 03:02:30 +00:00
|
|
|
|
}
|
|
|
|
|
gpip_interrupt_state_ = gpip_state;
|
|
|
|
|
}
|
2019-10-24 03:09:49 +00:00
|
|
|
|
|
|
|
|
|
// MARK: - Interrupts
|
|
|
|
|
|
2019-10-25 02:33:42 +00:00
|
|
|
|
void MFP68901::begin_interrupts(int interrupt) {
|
2019-10-30 03:07:03 +00:00
|
|
|
|
interrupt_pending_ |= interrupt & interrupt_enable_;
|
2019-10-25 02:33:42 +00:00
|
|
|
|
update_interrupts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MFP68901::end_interrupts(int interrupt) {
|
2019-10-26 19:55:19 +00:00
|
|
|
|
interrupt_pending_ &= ~interrupt;
|
2019-10-25 02:33:42 +00:00
|
|
|
|
update_interrupts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MFP68901::update_interrupts() {
|
2019-10-26 19:55:19 +00:00
|
|
|
|
const auto old_interrupt_line = interrupt_line_;
|
2019-10-27 04:02:55 +00:00
|
|
|
|
const auto firing_interrupts = interrupt_pending_ & interrupt_mask_;
|
2019-10-26 19:55:19 +00:00
|
|
|
|
|
2019-10-27 04:02:55 +00:00
|
|
|
|
if(!firing_interrupts) {
|
2019-10-26 19:55:19 +00:00
|
|
|
|
interrupt_line_ = false;
|
|
|
|
|
} else {
|
|
|
|
|
if(interrupt_vector_ & 0x8) {
|
2019-10-29 02:51:00 +00:00
|
|
|
|
// Software interrupt mode: permit only if neither this interrupt
|
|
|
|
|
// nor a higher interrupt is currently in service.
|
2019-11-09 21:03:00 +00:00
|
|
|
|
const int highest_bit = msb16(firing_interrupts);
|
2019-11-13 03:18:13 +00:00
|
|
|
|
interrupt_line_ = !(interrupt_in_service_ & ~(highest_bit + highest_bit - 1));
|
2019-10-26 19:55:19 +00:00
|
|
|
|
} else {
|
|
|
|
|
// Auto-interrupt mode; just signal.
|
|
|
|
|
interrupt_line_ = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-25 02:33:42 +00:00
|
|
|
|
|
2019-10-26 19:55:19 +00:00
|
|
|
|
// Update the delegate if necessary.
|
2019-10-26 02:36:01 +00:00
|
|
|
|
if(interrupt_delegate_ && interrupt_line_ != old_interrupt_line) {
|
|
|
|
|
interrupt_delegate_->mfp68901_did_change_interrupt_status(this);
|
2019-10-25 02:33:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-24 03:09:49 +00:00
|
|
|
|
|
2019-10-25 02:33:42 +00:00
|
|
|
|
bool MFP68901::get_interrupt_line() {
|
|
|
|
|
return interrupt_line_;
|
2019-10-24 03:09:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 23:57:36 +00:00
|
|
|
|
int MFP68901::acknowledge_interrupt() {
|
|
|
|
|
if(!(interrupt_pending_ & interrupt_mask_)) {
|
|
|
|
|
return NoAcknowledgement;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 21:03:00 +00:00
|
|
|
|
const int mask = msb16(interrupt_pending_ & interrupt_mask_);
|
2019-10-26 19:55:19 +00:00
|
|
|
|
|
|
|
|
|
// Clear the pending bit regardless.
|
|
|
|
|
interrupt_pending_ &= ~mask;
|
|
|
|
|
|
|
|
|
|
// If this is software interrupt mode, set the in-service bit.
|
|
|
|
|
if(interrupt_vector_ & 0x8) {
|
|
|
|
|
interrupt_in_service_ |= mask;
|
2019-10-26 02:36:01 +00:00
|
|
|
|
}
|
2019-10-26 19:55:19 +00:00
|
|
|
|
|
|
|
|
|
update_interrupts();
|
|
|
|
|
|
2019-11-09 21:03:00 +00:00
|
|
|
|
int selected = 0;
|
|
|
|
|
while((1 << selected) != mask) ++selected;
|
2024-01-19 19:22:23 +00:00
|
|
|
|
// logger.error().append("Interrupt acknowledged: %d", selected);
|
2019-10-26 19:55:19 +00:00
|
|
|
|
return (interrupt_vector_ & 0xf0) | uint8_t(selected);
|
2019-10-26 02:36:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MFP68901::set_interrupt_delegate(InterruptDelegate *delegate) {
|
|
|
|
|
interrupt_delegate_ = delegate;
|
2019-10-24 03:09:49 +00:00
|
|
|
|
}
|