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:
parent
79dd402bc8
commit
7959d243f6
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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<ConcreteAllRAMProcessor, false, true> z80_;
|
||||
bool was_m1_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
/*!
|
||||
|
Loading…
x
Reference in New Issue
Block a user