diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 4af76c948..184434791 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -36,9 +36,6 @@ int Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) { set_hsync(true); if(nmi_is_enabled_) { set_non_maskable_interrupt_line(true); - if(!get_halt_line()) { - set_wait_line(true); - } } video_->run_for_cycles(horizontal_counter_ - vsync_start_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; 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()) { return 0; } diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 20622b965..ce9c9bb46 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -76,14 +76,16 @@ struct PartialMachineCycle { Internal, BusAcknowledge, - ReadStart, ReadWait, - WriteStart, WriteWait, - InputStart, InputWait, + OutputWait, + InterruptWait, + + ReadStart, + WriteStart, + InputStart, OutputStart, - OutputWait } operation; int length; uint16_t *address; @@ -96,6 +98,9 @@ struct PartialMachineCycle { inline bool is_terminal() const { return operation <= Operation::BusAcknowledge; } + inline bool is_wait() const { + return operation >= Operation::ReadWait && operation <= Operation::InterruptWait; + } }; // Elemental bus operations @@ -115,11 +120,12 @@ struct PartialMachineCycle { #define InputWait(addr, val, f) {PartialMachineCycle::InputWait, 1, &addr.full, &val, f} #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 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 #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 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. #define Sequence(...) { __VA_ARGS__, {MicroOp::MoveToNextProgram} } @@ -186,7 +192,7 @@ template class Processor { }; uint8_t request_status_; uint8_t last_request_status_; - bool irq_line_; + bool irq_line_, nmi_line_; bool bus_request_line_; bool wait_line_; @@ -765,6 +771,7 @@ template class Processor { request_status_(Interrupt::PowerOn), last_request_status_(Interrupt::PowerOn), irq_line_(false), + nmi_line_(false), bus_request_line_(false), pc_increment_(1), scheduled_program_counter_(nullptr) { @@ -800,6 +807,7 @@ template class Processor { { MicroOp::BeginNMI }, BusOp(ReadOpcodeStart()), BusOp(ReadOpcodeWait(1, false)), + BusOp(ReadOpcodeWait(1, true)), BusOp(Refresh(2)), Push(pc_), { MicroOp::JumpTo66, nullptr, nullptr}, @@ -808,11 +816,13 @@ template class Processor { MicroOp irq_mode0_program[] = { { MicroOp::BeginIRQMode0 }, BusOp(IntAck(4, operation_)), + BusOp(IntWait(operation_)), { MicroOp::DecodeOperationNoRChange } }; MicroOp irq_mode1_program[] = { { MicroOp::BeginIRQ }, BusOp(IntAck(5, operation_)), + BusOp(IntWait(operation_)), BusOp(Refresh(2)), Push(pc_), { MicroOp::Move16, &temp16_.full, &pc_.full }, @@ -821,6 +831,7 @@ template class Processor { MicroOp irq_mode2_program[] = { { MicroOp::BeginIRQ }, BusOp(IntAck(5, temp16_.bytes.low)), + BusOp(IntWait(temp16_.bytes.low)), BusOp(Refresh(2)), Push(pc_), { MicroOp::Move8, &ir_.bytes.high, &temp16_.bytes.high }, @@ -872,7 +883,7 @@ template class Processor { while(1) { 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(this)->perform_machine_cycle(bus_acknowledge_cycle) + 1; if(!number_of_cycles_) { static_cast(this)->flush(); @@ -1878,6 +1889,10 @@ template class Processor { } } + bool get_interrupt_line() { + return irq_line_; + } + /*! Sets the logical value of the non-maskable interrupt line. @@ -1885,6 +1900,7 @@ template class Processor { */ void set_non_maskable_interrupt_line(bool value, int offset = 0) { // NMIs are edge triggered and cannot be masked. + nmi_line_ = value; if(value) { request_status_ |= Interrupt::NMI; if(offset < 0) { @@ -1893,6 +1909,10 @@ template class Processor { } } + bool get_non_maskable_interrupt_line() { + return nmi_line_; + } + /*! Sets the logical value of the bus request line. */ @@ -1900,6 +1920,10 @@ template class Processor { bus_request_line_ = value; } + bool get_bus_request_line() { + return bus_request_line_; + } + /*! Sets the logical value of the reset line. */ @@ -1925,6 +1949,10 @@ template class Processor { wait_line_ = value; } + bool get_wait_line() { + return wait_line_; + } + /*! For receivers of perform_machine_cycle only. Temporarily rejects the current machine cycle, causing time to be rewinded to its beginning.