diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h index 1a2300604..720b25170 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.h @@ -58,6 +58,7 @@ typedef NS_ENUM(NSInteger, CSTestMachinePortLogic) { - (uint8_t)valueAtAddress:(uint16_t)address; - (void)runForNumberOfCycles:(int)cycles; +- (void)runForInstruction; - (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg; - (uint16_t)valueForRegister:(CSTestMachineZ80Register)reg; diff --git a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm index a95a215c1..65ec87bc7 100644 --- a/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm +++ b/OSBindings/Mac/Clock SignalTests/Bridges/TestMachineZ80.mm @@ -146,6 +146,10 @@ struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelega _processor->run_for(Cycles(cycles)); } +- (void)runForInstruction { + _processor->run_for_instruction(); +} + - (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg { _processor->set_value_of_register(registerForRegister(reg), value); } diff --git a/Processors/Z80/AllRAM/Z80AllRAM.cpp b/Processors/Z80/AllRAM/Z80AllRAM.cpp index 603a0d1d7..9c6c22958 100644 --- a/Processors/Z80/AllRAM/Z80AllRAM.cpp +++ b/Processors/Z80/AllRAM/Z80AllRAM.cpp @@ -60,40 +60,59 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler { return HalfCycles(0); } - void run_for(const Cycles cycles) { + void run_for(const Cycles cycles) final { z80_.run_for(cycles); } - uint16_t get_value_of_register(Register r) { + void run_for_instruction() final { + int toggles = 0; + int cycles = 0; + + // Run: + // (1) until is_starting_new_instruction is true; + // (2) until it is false again; and + // (3) until it is true again. + while(true) { + if(z80_.is_starting_new_instruction() != (toggles&1)) { + ++toggles; + if(toggles == 3) break; + } + z80_.run_for(Cycles(1)); + ++cycles; + } + } + + uint16_t get_value_of_register(Register r) final { return z80_.get_value_of_register(r); } - void set_value_of_register(Register r, uint16_t value) { + void set_value_of_register(Register r, uint16_t value) final { z80_.set_value_of_register(r, value); } - bool get_halt_line() { + bool get_halt_line() final { return z80_.get_halt_line(); } - void reset_power_on() { + void reset_power_on() final { return z80_.reset_power_on(); } - void set_interrupt_line(bool value) { + void set_interrupt_line(bool value) final { z80_.set_interrupt_line(value); } - void set_non_maskable_interrupt_line(bool value) { + void set_non_maskable_interrupt_line(bool value) final { z80_.set_non_maskable_interrupt_line(value); } - void set_wait_line(bool value) { + void set_wait_line(bool value) final { z80_.set_wait_line(value); } private: CPU::Z80::Processor z80_; + bool was_m1_ = false; }; } diff --git a/Processors/Z80/AllRAM/Z80AllRAM.hpp b/Processors/Z80/AllRAM/Z80AllRAM.hpp index 1d55f9dea..c8f160a20 100644 --- a/Processors/Z80/AllRAM/Z80AllRAM.hpp +++ b/Processors/Z80/AllRAM/Z80AllRAM.hpp @@ -36,6 +36,7 @@ class AllRAMProcessor: } virtual void run_for(const Cycles cycles) = 0; + virtual void run_for_instruction() = 0; virtual uint16_t get_value_of_register(Register r) = 0; virtual void set_value_of_register(Register r, uint16_t value) = 0; virtual bool get_halt_line() = 0; diff --git a/Processors/Z80/Implementation/Z80Storage.cpp b/Processors/Z80/Implementation/Z80Storage.cpp index ed45f6d9a..0dc59eec4 100644 --- a/Processors/Z80/Implementation/Z80Storage.cpp +++ b/Processors/Z80/Implementation/Z80Storage.cpp @@ -80,6 +80,7 @@ ProcessorStorage::ProcessorStorage() { /* The following are helper macros that define common parts of instructions */ #define Inc16(r) {(&r == &pc_) ? MicroOp::IncrementPC : MicroOp::Increment16, &r.full} +#define Inc8NoFlags(r) {MicroOp::Increment8NoFlags, &r} #define ReadInc(addr, val) Read3(addr, val), Inc16(addr) #define Read4Inc(addr, val) Read4(addr, val), Inc16(addr) @@ -104,10 +105,10 @@ ProcessorStorage::ProcessorStorage() { /* The following are actual instructions */ #define NOP Sequence(BusOp(Refresh(4))) -#define JP(cc) StdInstr(Read16Inc(pc_, temp16_), {MicroOp::cc, nullptr}, {MicroOp::Move16, &temp16_.full, &pc_.full}) -#define CALL(cc) StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, temp16_.halves.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full}) +#define JP(cc) StdInstr(Read16Inc(pc_, memptr_), {MicroOp::cc, nullptr}, {MicroOp::Move16, &memptr_.full, &pc_.full}) +#define CALL(cc) StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, memptr_.halves.high), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}) #define RET(cc) Instr(6, {MicroOp::cc, nullptr}, Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}) -#define JR(cc) StdInstr(ReadInc(pc_, temp8_), {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}) +#define JR(cc) StdInstr(ReadInc(pc_, temp8_), {MicroOp::Move16, &pc_.full, &memptr_.full}, {MicroOp::cc, nullptr}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}) #define RST() Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}) #define LD(a, b) StdInstr({MicroOp::Move8, &b, &a}) @@ -166,7 +167,7 @@ ProcessorStorage::ProcessorStorage() { #define SBC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full}) void ProcessorStorage::install_default_instruction_set() { - MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, temp16_.halves.high)); + MicroOp conditional_call_untaken_program[] = Sequence(ReadInc(pc_, memptr_.halves.high)); copy_program(conditional_call_untaken_program, conditional_call_untaken_program_); assemble_base_page(base_page_, hl_, false, cb_page_); @@ -244,7 +245,7 @@ void ProcessorStorage::install_default_instruction_set() { void ProcessorStorage::assemble_ed_page(InstructionPage &target) { #define IN_C(r) StdInstr(Input(bc_, r), {MicroOp::SetInFlags, &r}) -#define OUT_C(r) StdInstr(Output(bc_, r)) +#define OUT_C(r) StdInstr(Output(bc_, r), {MicroOp::SetOutFlags, &r}) #define IN_OUT(r) IN_C(r), OUT_C(r) #define NOP_ROW() NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP @@ -370,7 +371,7 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1 DEC_INC_DEC_LD(bc_, bc_.halves.low), /* 0x0f RRCA */ StdInstr({MicroOp::RRCA}), - /* 0x10 DJNZ */ Instr(6, ReadInc(pc_, temp8_), {MicroOp::DJNZ}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}), + /* 0x10 DJNZ */ Instr(6, ReadInc(pc_, temp8_), {MicroOp::Move16, &pc_.full, &memptr_.full}, {MicroOp::DJNZ}, InternalOperation(10), {MicroOp::CalculateIndexAddress, &pc_.full}, {MicroOp::Move16, &memptr_.full, &pc_.full}), /* 0x11 LD DE, nn */ StdInstr(Read16Inc(pc_, de_)), /* 0x12 LD (DE), A */ StdInstr({MicroOp::SetAddrAMemptr, &de_.full}, Write3(de_, a_)), @@ -476,13 +477,13 @@ void ProcessorStorage::assemble_base_page(InstructionPage &target, RegisterPair1 /* 0xc4 CALL NZ */ CALL(TestNZ), /* 0xc5 PUSH BC */ Instr(6, Push(bc_)), /* 0xc6 ADD A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}), /* 0xc7 RST 00h */ RST(), - /* 0xc8 RET Z */ RET(TestZ), /* 0xc9 RET */ StdInstr(Pop(pc_)), + /* 0xc8 RET Z */ RET(TestZ), /* 0xc9 RET */ StdInstr(Pop(memptr_), {MicroOp::Move16, &memptr_.full, &pc_.full}), /* 0xca JP Z */ JP(TestZ), /* 0xcb [CB page] */StdInstr(FINDEX(), {MicroOp::SetInstructionPage, &cb_page}), - /* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ StdInstr(ReadInc(pc_, temp16_.halves.low), Read4Inc(pc_, temp16_.halves.high), Push(pc_), {MicroOp::Move16, &temp16_.full, &pc_.full}), + /* 0xcc CALL Z */ CALL(TestZ), /* 0xcd CALL */ StdInstr(ReadInc(pc_, memptr_.halves.low), Read4Inc(pc_, memptr_.halves.high), Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full}), /* 0xce ADC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}), /* 0xcf RST 08h */ RST(), /* 0xd0 RET NC */ RET(TestNC), /* 0xd1 POP DE */ StdInstr(Pop(de_)), - /* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */StdInstr(ReadInc(pc_, temp16_.halves.low), {MicroOp::Move8, &a_, &temp16_.halves.high}, Output(temp16_, a_)), + /* 0xd2 JP NC */ JP(TestNC), /* 0xd3 OUT (n), A */StdInstr(ReadInc(pc_, memptr_.halves.low), {MicroOp::Move8, &a_, &memptr_.halves.high}, Output(memptr_, a_), Inc8NoFlags(memptr_.halves.low)), /* 0xd4 CALL NC */ CALL(TestNC), /* 0xd5 PUSH DE */ Instr(6, Push(de_)), /* 0xd6 SUB n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}), /* 0xd7 RST 10h */ RST(), @@ -543,3 +544,9 @@ void ProcessorStorage::assemble_fetch_decode_execute(InstructionPage &target, in copy_program((length == 4) ? normal_fetch_decode_execute : short_fetch_decode_execute, target.fetch_decode_execute); target.fetch_decode_execute_data = target.fetch_decode_execute.data(); } + +bool ProcessorBase::is_starting_new_instruction() { + return + current_instruction_page_ == &base_page_ && + scheduled_program_counter_ == &base_page_.fetch_decode_execute[0]; +} diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index 55cb4080d..d0ee4a1a9 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -224,6 +224,13 @@ class ProcessorBase: public ProcessorStorage { reset at the first opportunity. Use @c reset_power_on to disable that behaviour. */ void reset_power_on(); + + /*! + @returns @c true if the Z80 is currently beginning to fetch a new instruction; @c false otherwise. + + This is not a speedy operation. + */ + bool is_starting_new_instruction(); }; /*!