1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Adds single-stepping. Of a kind.

This commit is contained in:
Thomas Harte 2020-02-24 23:31:42 -05:00
parent 79dd402bc8
commit 7959d243f6
6 changed files with 56 additions and 17 deletions

View File

@ -58,6 +58,7 @@ typedef NS_ENUM(NSInteger, CSTestMachinePortLogic) {
- (uint8_t)valueAtAddress:(uint16_t)address; - (uint8_t)valueAtAddress:(uint16_t)address;
- (void)runForNumberOfCycles:(int)cycles; - (void)runForNumberOfCycles:(int)cycles;
- (void)runForInstruction;
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg; - (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg;
- (uint16_t)valueForRegister:(CSTestMachineZ80Register)reg; - (uint16_t)valueForRegister:(CSTestMachineZ80Register)reg;

View File

@ -146,6 +146,10 @@ struct PortAccessDelegate191: public CPU::Z80::AllRAMProcessor::PortAccessDelega
_processor->run_for(Cycles(cycles)); _processor->run_for(Cycles(cycles));
} }
- (void)runForInstruction {
_processor->run_for_instruction();
}
- (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg { - (void)setValue:(uint16_t)value forRegister:(CSTestMachineZ80Register)reg {
_processor->set_value_of_register(registerForRegister(reg), value); _processor->set_value_of_register(registerForRegister(reg), value);
} }

View File

@ -60,40 +60,59 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
return HalfCycles(0); return HalfCycles(0);
} }
void run_for(const Cycles cycles) { void run_for(const Cycles cycles) final {
z80_.run_for(cycles); 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); 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); z80_.set_value_of_register(r, value);
} }
bool get_halt_line() { bool get_halt_line() final {
return z80_.get_halt_line(); return z80_.get_halt_line();
} }
void reset_power_on() { void reset_power_on() final {
return z80_.reset_power_on(); return z80_.reset_power_on();
} }
void set_interrupt_line(bool value) { void set_interrupt_line(bool value) final {
z80_.set_interrupt_line(value); 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); 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); z80_.set_wait_line(value);
} }
private: private:
CPU::Z80::Processor<ConcreteAllRAMProcessor, false, true> z80_; CPU::Z80::Processor<ConcreteAllRAMProcessor, false, true> z80_;
bool was_m1_ = false;
}; };
} }

View File

@ -36,6 +36,7 @@ class AllRAMProcessor:
} }
virtual void run_for(const Cycles cycles) = 0; 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 uint16_t get_value_of_register(Register r) = 0;
virtual void set_value_of_register(Register r, uint16_t value) = 0; virtual void set_value_of_register(Register r, uint16_t value) = 0;
virtual bool get_halt_line() = 0; virtual bool get_halt_line() = 0;

View File

@ -80,6 +80,7 @@ ProcessorStorage::ProcessorStorage() {
/* The following are helper macros that define common parts of instructions */ /* The following are helper macros that define common parts of instructions */
#define Inc16(r) {(&r == &pc_) ? MicroOp::IncrementPC : MicroOp::Increment16, &r.full} #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 ReadInc(addr, val) Read3(addr, val), Inc16(addr)
#define Read4Inc(addr, val) Read4(addr, val), Inc16(addr) #define Read4Inc(addr, val) Read4(addr, val), Inc16(addr)
@ -104,10 +105,10 @@ ProcessorStorage::ProcessorStorage() {
/* The following are actual instructions */ /* The following are actual instructions */
#define NOP Sequence(BusOp(Refresh(4))) #define NOP Sequence(BusOp(Refresh(4)))
#define JP(cc) StdInstr(Read16Inc(pc_, temp16_), {MicroOp::cc, nullptr}, {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_, temp16_.halves.low), {MicroOp::cc, conditional_call_untaken_program_.data()}, Read4Inc(pc_, temp16_.halves.high), Push(pc_), {MicroOp::Move16, &temp16_.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 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 RST() Instr(6, {MicroOp::CalculateRSTDestination}, Push(pc_), {MicroOp::Move16, &memptr_.full, &pc_.full})
#define LD(a, b) StdInstr({MicroOp::Move8, &b, &a}) #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}) #define SBC16(d, s) StdInstr(InternalOperation(8), InternalOperation(6), {MicroOp::SBC16, &s.full, &d.full})
void ProcessorStorage::install_default_instruction_set() { 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_); copy_program(conditional_call_untaken_program, conditional_call_untaken_program_);
assemble_base_page(base_page_, hl_, false, cb_page_); 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) { void ProcessorStorage::assemble_ed_page(InstructionPage &target) {
#define IN_C(r) StdInstr(Input(bc_, r), {MicroOp::SetInFlags, &r}) #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 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 #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), DEC_INC_DEC_LD(bc_, bc_.halves.low),
/* 0x0f RRCA */ StdInstr({MicroOp::RRCA}), /* 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_)), /* 0x11 LD DE, nn */ StdInstr(Read16Inc(pc_, de_)),
/* 0x12 LD (DE), A */ StdInstr({MicroOp::SetAddrAMemptr, &de_.full}, Write3(de_, a_)), /* 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_)), /* 0xc4 CALL NZ */ CALL(TestNZ), /* 0xc5 PUSH BC */ Instr(6, Push(bc_)),
/* 0xc6 ADD A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}), /* 0xc6 ADD A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADD8, &temp8_}),
/* 0xc7 RST 00h */ RST(), /* 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}), /* 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_}), /* 0xce ADC A, n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::ADC8, &temp8_}),
/* 0xcf RST 08h */ RST(), /* 0xcf RST 08h */ RST(),
/* 0xd0 RET NC */ RET(TestNC), /* 0xd1 POP DE */ StdInstr(Pop(de_)), /* 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_)), /* 0xd4 CALL NC */ CALL(TestNC), /* 0xd5 PUSH DE */ Instr(6, Push(de_)),
/* 0xd6 SUB n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}), /* 0xd6 SUB n */ StdInstr(ReadInc(pc_, temp8_), {MicroOp::SUB8, &temp8_}),
/* 0xd7 RST 10h */ RST(), /* 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); 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(); 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];
}

View File

@ -224,6 +224,13 @@ class ProcessorBase: public ProcessorStorage {
reset at the first opportunity. Use @c reset_power_on to disable that behaviour. reset at the first opportunity. Use @c reset_power_on to disable that behaviour.
*/ */
void reset_power_on(); 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();
}; };
/*! /*!