1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

Takes another stab at MFP interrupt management.

This commit is contained in:
Thomas Harte 2019-10-26 15:55:19 -04:00
parent a8d481a764
commit e96386f572
2 changed files with 73 additions and 30 deletions

View File

@ -8,6 +8,8 @@
#include "MFP68901.hpp"
#include <cstring>
#define LOG_PREFIX "[MFP] "
//#define NDEBUG
#include "../../Outputs/Log.hpp"
@ -87,27 +89,31 @@ void MFP68901::write(int address, uint8_t value) {
case 0x03:
LOG("Write: interrupt enable A " << PADHEX(2) << int(value));
interrupt_enable_ = (interrupt_enable_ & 0x00ff) | (value << 8);
update_interrupts();
break;
case 0x04:
LOG("Write: interrupt enable B " << PADHEX(2) << int(value));
interrupt_enable_ = (interrupt_enable_ & 0xff00) | value;
update_interrupts();
break;
case 0x05:
LOG("Write: interrupt pending A " << PADHEX(2) << int(value));
interrupt_pending_ = (interrupt_pending_ & 0x00ff) | (value << 8);
interrupt_in_service_ &= 0x00ff | (value << 8);
interrupt_pending_ &= 0x00ff | (value << 8);
update_interrupts();
break;
case 0x06:
LOG("Write: interrupt pending B " << PADHEX(2) << int(value));
interrupt_pending_ = (interrupt_pending_ & 0xff00) | value;
interrupt_pending_ &= 0xff00 | value;
update_interrupts();
break;
case 0x07:
LOG("Write: interrupt in-service A (no-op?) " << PADHEX(2) << int(value));
interrupt_in_service_ &= 0x00ff | (value << 8);
update_interrupts();
break;
case 0x08:
LOG("Write: interrupt in-service B (no-op?) " << PADHEX(2) << int(value));
interrupt_in_service_ &= 0xff00 | value;
update_interrupts();
break;
case 0x07: LOG("Write: interrupt in-service A (no-op?) " << PADHEX(2) << int(value)); break;
case 0x08: LOG("Write: interrupt in-service B (no-op?) " << PADHEX(2) << int(value)); break;
case 0x09:
LOG("Write: interrupt mask A " << PADHEX(2) << int(value));
interrupt_mask_ = (interrupt_mask_ & 0x00ff) | (value << 8);
@ -121,6 +127,13 @@ void MFP68901::write(int address, uint8_t value) {
case 0x0b:
LOG("Write: vector " << PADHEX(2) << int(value));
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)) {
interrupt_in_service_ = 0;
update_interrupts();
}
break;
case 0x0c:
case 0x0d: {
@ -266,20 +279,34 @@ void MFP68901::reevaluate_gpip_interrupts() {
// MARK: - Interrupts
void MFP68901::begin_interrupts(int interrupt) {
interrupt_in_service_ |= interrupt;
interrupt_pending_ |= interrupt;
update_interrupts();
}
void MFP68901::end_interrupts(int interrupt) {
interrupt_in_service_ &= ~interrupt;
interrupt_pending_ &= ~interrupt;
update_interrupts();
}
void MFP68901::update_interrupts() {
const bool old_interrupt_line = interrupt_line_;
interrupt_pending_ = interrupt_in_service_ & interrupt_enable_;
interrupt_line_ = interrupt_pending_ & interrupt_mask_;
const auto old_interrupt_line = interrupt_line_;
const auto permitted_interrupts = interrupt_pending_ & interrupt_mask_;
if(!permitted_interrupts) {
interrupt_line_ = false;
} else {
if(interrupt_vector_ & 0x8) {
// Software interrupt mode: permit only if no higher interrupts
// are currently in service.
const int highest_bit = 1 << (fls(permitted_interrupts) - 1);
interrupt_line_ = !(interrupt_in_service_ & ~(highest_bit + highest_bit - 1));
} else {
// Auto-interrupt mode; just signal.
interrupt_line_ = true;
}
}
// Update the delegate if necessary.
if(interrupt_delegate_ && interrupt_line_ != old_interrupt_line) {
interrupt_delegate_->mfp68901_did_change_interrupt_status(this);
}
@ -290,14 +317,20 @@ bool MFP68901::get_interrupt_line() {
}
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;
const int selected = fls(interrupt_pending_ & interrupt_mask_) - 1;
const int mask = 1 << selected;
// 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;
}
end_interrupts(interrupt_mask);
return (interrupt_vector_ & 0xf0) | selected_interrupt;
update_interrupts();
return (interrupt_vector_ & 0xf0) | uint8_t(selected);
}
void MFP68901::set_interrupt_delegate(InterruptDelegate *delegate) {

View File

@ -76,24 +76,34 @@ class MFP68901 {
InterruptDelegate *interrupt_delegate_ = nullptr;
// Ad hoc documentation: there seems to be a four-stage process here.
// This is my current understanding:
// Ad hoc documentation:
//
// Interrupt in-service refers to whether the signal that would cause an
// interrupt is active.
// An interrupt becomes pending if it is enabled at the time it occurs.
//
// If the interrupt is in-service and enabled, it will be listed as pending.
// If a pending interrupt is enabled in the interrupt mask, a processor
// interrupt is generated. Otherwise no processor interrupt is generated.
//
// If a pending interrupt is enabled in the interrupt mask, it will generate
// a processor interrupt.
// (Disabling a bit in the enabled mask also instantaneously clears anything
// in the pending mask.)
//
// The user can write to the pending interrupt register; a write
// masks whatever is there — so you can disable bits but you cannot set them.
//
// If the vector register's 'S' bit is set then software end-of-interrupt mode applies:
// Acknowledgement of an interrupt clears that interrupt's pending bit, but also sets
// its in-service bit. That bit will remain set until the user writes a zero to its position.
// If any bits are set in the in-service register, then they will prevent lower-priority
// interrupts from being signalled to the CPU. Further interrupts of the same or a higher
// priority may occur.
//
// If the vector register's 'S' bit is clear then automatic end-of-interrupt mode applies:
// Acknowledgement of an interrupt will automatically clear the corresponding
// pending bit.
//
// So, the designers seem to have wanted to allow for polling and interrupts,
// and then also decided to have some interrupts be able to be completely
// disabled, so that don't even show up for polling.
int interrupt_in_service_ = 0;
int interrupt_enable_ = 0;
int interrupt_pending_ = 0;
int interrupt_mask_ = 0;
int interrupt_in_service_ = 0;
bool interrupt_line_ = false;
uint8_t interrupt_vector_ = 0;