1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-16 22:28:57 +00:00

Merge pull request #135 from TomHarte/InterruptWaitStates

Ensures wait states are observed during interrupt response
This commit is contained in:
Thomas Harte 2017-06-22 20:15:54 -04:00 committed by GitHub
commit 342574761f
2 changed files with 42 additions and 13 deletions

View File

@ -36,9 +36,6 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
set_hsync(true); set_hsync(true);
if(nmi_is_enabled_) { if(nmi_is_enabled_) {
set_non_maskable_interrupt_line(true); set_non_maskable_interrupt_line(true);
if(!get_halt_line()) {
set_wait_line(true);
}
} }
video_->run_for_cycles(horizontal_counter_ - vsync_start_cycle_); video_->run_for_cycles(horizontal_counter_ - vsync_start_cycle_);
} else if(previous_counter < vsync_end_cycle_ && horizontal_counter_ >= vsync_end_cycle_) { } else if(previous_counter < vsync_end_cycle_ && horizontal_counter_ >= vsync_end_cycle_) {
@ -56,6 +53,10 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
if(is_zx81_) horizontal_counter_ %= 207; if(is_zx81_) horizontal_counter_ %= 207;
tape_player_.run_for_cycles(cycle.length); tape_player_.run_for_cycles(cycle.length);
if(nmi_is_enabled_ && !get_halt_line() && get_non_maskable_interrupt_line()) {
set_wait_line(true);
}
if(!cycle.is_terminal()) { if(!cycle.is_terminal()) {
return 0; return 0;
} }

View File

@ -76,14 +76,16 @@ struct PartialMachineCycle {
Internal, Internal,
BusAcknowledge, BusAcknowledge,
ReadStart,
ReadWait, ReadWait,
WriteStart,
WriteWait, WriteWait,
InputStart,
InputWait, InputWait,
OutputWait,
InterruptWait,
ReadStart,
WriteStart,
InputStart,
OutputStart, OutputStart,
OutputWait
} operation; } operation;
int length; int length;
uint16_t *address; uint16_t *address;
@ -96,6 +98,9 @@ struct PartialMachineCycle {
inline bool is_terminal() const { inline bool is_terminal() const {
return operation <= Operation::BusAcknowledge; return operation <= Operation::BusAcknowledge;
} }
inline bool is_wait() const {
return operation >= Operation::ReadWait && operation <= Operation::InterruptWait;
}
}; };
// Elemental bus operations // Elemental bus operations
@ -115,11 +120,12 @@ struct PartialMachineCycle {
#define InputWait(addr, val, f) {PartialMachineCycle::InputWait, 1, &addr.full, &val, f} #define InputWait(addr, val, f) {PartialMachineCycle::InputWait, 1, &addr.full, &val, f}
#define InputEnd(addr, val) {PartialMachineCycle::Input, 1, &addr.full, &val, false} #define InputEnd(addr, val) {PartialMachineCycle::Input, 1, &addr.full, &val, false}
#define OutputStart(addr, val) {PartialMachineCycle::OutputStart, 2, &addr.full, &val} #define OutputStart(addr, val) {PartialMachineCycle::OutputStart, 2, &addr.full, &val, false}
#define OutputWait(addr, val, f) {PartialMachineCycle::OutputWait, 1, &addr.full, &val, f} #define OutputWait(addr, val, f) {PartialMachineCycle::OutputWait, 1, &addr.full, &val, f}
#define OutputEnd(addr, val) {PartialMachineCycle::Output, 1, &addr.full, &val} #define OutputEnd(addr, val) {PartialMachineCycle::Output, 1, &addr.full, &val, false}
#define IntAck(length, val) {PartialMachineCycle::Interrupt, length, nullptr, &val} #define IntAck(length, val) {PartialMachineCycle::Interrupt, length, nullptr, &val, false}
#define IntWait(val) {PartialMachineCycle::InterruptWait, 1, nullptr, &val, true}
// A wrapper to express a bus operation as a micro-op // A wrapper to express a bus operation as a micro-op
#define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op} #define BusOp(op) {MicroOp::BusOperation, nullptr, nullptr, op}
@ -134,7 +140,7 @@ struct PartialMachineCycle {
#define Input(addr, val) BusOp(InputStart(addr, val)), BusOp(InputWait(addr, val, false)), BusOp(InputWait(addr, val, true)), BusOp(InputEnd(addr, val)) #define Input(addr, val) BusOp(InputStart(addr, val)), BusOp(InputWait(addr, val, false)), BusOp(InputWait(addr, val, true)), BusOp(InputEnd(addr, val))
#define Output(addr, val) BusOp(OutputStart(addr, val)), BusOp(OutputWait(addr, val, false)), BusOp(OutputWait(addr, val, true)), BusOp(OutputEnd(addr, val)) #define Output(addr, val) BusOp(OutputStart(addr, val)), BusOp(OutputWait(addr, val, false)), BusOp(OutputWait(addr, val, true)), BusOp(OutputEnd(addr, val))
#define InternalOperation(len) {MicroOp::BusOperation, nullptr, nullptr, {PartialMachineCycle::Internal, len}} #define InternalOperation(len) {MicroOp::BusOperation, nullptr, nullptr, {PartialMachineCycle::Internal, len, nullptr, nullptr, false}}
/// A sequence is a series of micro-ops that ends in a move-to-next-program operation. /// A sequence is a series of micro-ops that ends in a move-to-next-program operation.
#define Sequence(...) { __VA_ARGS__, {MicroOp::MoveToNextProgram} } #define Sequence(...) { __VA_ARGS__, {MicroOp::MoveToNextProgram} }
@ -186,7 +192,7 @@ template <class T> class Processor {
}; };
uint8_t request_status_; uint8_t request_status_;
uint8_t last_request_status_; uint8_t last_request_status_;
bool irq_line_; bool irq_line_, nmi_line_;
bool bus_request_line_; bool bus_request_line_;
bool wait_line_; bool wait_line_;
@ -765,6 +771,7 @@ template <class T> class Processor {
request_status_(Interrupt::PowerOn), request_status_(Interrupt::PowerOn),
last_request_status_(Interrupt::PowerOn), last_request_status_(Interrupt::PowerOn),
irq_line_(false), irq_line_(false),
nmi_line_(false),
bus_request_line_(false), bus_request_line_(false),
pc_increment_(1), pc_increment_(1),
scheduled_program_counter_(nullptr) { scheduled_program_counter_(nullptr) {
@ -800,6 +807,7 @@ template <class T> class Processor {
{ MicroOp::BeginNMI }, { MicroOp::BeginNMI },
BusOp(ReadOpcodeStart()), BusOp(ReadOpcodeStart()),
BusOp(ReadOpcodeWait(1, false)), BusOp(ReadOpcodeWait(1, false)),
BusOp(ReadOpcodeWait(1, true)),
BusOp(Refresh(2)), BusOp(Refresh(2)),
Push(pc_), Push(pc_),
{ MicroOp::JumpTo66, nullptr, nullptr}, { MicroOp::JumpTo66, nullptr, nullptr},
@ -808,11 +816,13 @@ template <class T> class Processor {
MicroOp irq_mode0_program[] = { MicroOp irq_mode0_program[] = {
{ MicroOp::BeginIRQMode0 }, { MicroOp::BeginIRQMode0 },
BusOp(IntAck(4, operation_)), BusOp(IntAck(4, operation_)),
BusOp(IntWait(operation_)),
{ MicroOp::DecodeOperationNoRChange } { MicroOp::DecodeOperationNoRChange }
}; };
MicroOp irq_mode1_program[] = { MicroOp irq_mode1_program[] = {
{ MicroOp::BeginIRQ }, { MicroOp::BeginIRQ },
BusOp(IntAck(5, operation_)), BusOp(IntAck(5, operation_)),
BusOp(IntWait(operation_)),
BusOp(Refresh(2)), BusOp(Refresh(2)),
Push(pc_), Push(pc_),
{ MicroOp::Move16, &temp16_.full, &pc_.full }, { MicroOp::Move16, &temp16_.full, &pc_.full },
@ -821,6 +831,7 @@ template <class T> class Processor {
MicroOp irq_mode2_program[] = { MicroOp irq_mode2_program[] = {
{ MicroOp::BeginIRQ }, { MicroOp::BeginIRQ },
BusOp(IntAck(5, temp16_.bytes.low)), BusOp(IntAck(5, temp16_.bytes.low)),
BusOp(IntWait(temp16_.bytes.low)),
BusOp(Refresh(2)), BusOp(Refresh(2)),
Push(pc_), Push(pc_),
{ MicroOp::Move8, &ir_.bytes.high, &temp16_.bytes.high }, { MicroOp::Move8, &ir_.bytes.high, &temp16_.bytes.high },
@ -872,7 +883,7 @@ template <class T> class Processor {
while(1) { while(1) {
while(bus_request_line_) { while(bus_request_line_) {
static PartialMachineCycle bus_acknowledge_cycle = {PartialMachineCycle::BusAcknowledge, 1}; static PartialMachineCycle bus_acknowledge_cycle = {PartialMachineCycle::BusAcknowledge, 1, nullptr, nullptr, false};
number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(bus_acknowledge_cycle) + 1; number_of_cycles_ -= static_cast<T *>(this)->perform_machine_cycle(bus_acknowledge_cycle) + 1;
if(!number_of_cycles_) { if(!number_of_cycles_) {
static_cast<T *>(this)->flush(); static_cast<T *>(this)->flush();
@ -1878,6 +1889,10 @@ template <class T> class Processor {
} }
} }
bool get_interrupt_line() {
return irq_line_;
}
/*! /*!
Sets the logical value of the non-maskable interrupt line. Sets the logical value of the non-maskable interrupt line.
@ -1885,6 +1900,7 @@ template <class T> class Processor {
*/ */
void set_non_maskable_interrupt_line(bool value, int offset = 0) { void set_non_maskable_interrupt_line(bool value, int offset = 0) {
// NMIs are edge triggered and cannot be masked. // NMIs are edge triggered and cannot be masked.
nmi_line_ = value;
if(value) { if(value) {
request_status_ |= Interrupt::NMI; request_status_ |= Interrupt::NMI;
if(offset < 0) { if(offset < 0) {
@ -1893,6 +1909,10 @@ template <class T> class Processor {
} }
} }
bool get_non_maskable_interrupt_line() {
return nmi_line_;
}
/*! /*!
Sets the logical value of the bus request line. Sets the logical value of the bus request line.
*/ */
@ -1900,6 +1920,10 @@ template <class T> class Processor {
bus_request_line_ = value; bus_request_line_ = value;
} }
bool get_bus_request_line() {
return bus_request_line_;
}
/*! /*!
Sets the logical value of the reset line. Sets the logical value of the reset line.
*/ */
@ -1925,6 +1949,10 @@ template <class T> class Processor {
wait_line_ = value; wait_line_ = value;
} }
bool get_wait_line() {
return wait_line_;
}
/*! /*!
For receivers of perform_machine_cycle only. Temporarily rejects the current machine For receivers of perform_machine_cycle only. Temporarily rejects the current machine
cycle, causing time to be rewinded to its beginning. cycle, causing time to be rewinded to its beginning.