mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-19 23:29:05 +00:00
Merge pull request #135 from TomHarte/InterruptWaitStates
Ensures wait states are observed during interrupt response
This commit is contained in:
commit
342574761f
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user