Start moving towards reset being just another style of interrupt.

Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
Adrian Conlon 2018-08-25 01:34:30 +01:00
parent bca7977a23
commit 6d4223c368
17 changed files with 189 additions and 136 deletions

View File

@ -25,6 +25,7 @@ namespace EightBit {
Intel8080(Bus& bus, InputOutput& ports);
Signal<Intel8080> ExecutingInstruction;
Signal<Intel8080> ExecutedInstruction;
virtual int execute(uint8_t opcode) final;
virtual int step() final;
@ -35,7 +36,8 @@ namespace EightBit {
virtual register16_t& HL() final;
protected:
virtual void reset() final;
virtual void handleRESET() final;
virtual void handleINT() final;
private:
bool m_interruptEnable = false;

View File

@ -23,9 +23,21 @@ EightBit::register16_t& EightBit::Intel8080::HL() {
return hl;
}
void EightBit::Intel8080::reset() {
IntelProcessor::reset();
void EightBit::Intel8080::handleRESET() {
Processor::handleRESET();
di();
addCycles(3);
}
void EightBit::Intel8080::handleINT() {
Processor::handleINT();
raise(HALT());
if (m_interruptEnable) {
di();
execute(BUS().DATA());
}
addCycles(3);
}
void EightBit::Intel8080::di() {
@ -258,18 +270,17 @@ int EightBit::Intel8080::step() {
ExecutingInstruction.fire(*this);
resetCycles();
if (LIKELY(powered())) {
if (UNLIKELY(lowered(INT()))) {
raise(HALT());
raise(INT());
if (m_interruptEnable) {
di();
return execute(BUS().DATA());
}
if (UNLIKELY(lowered(RESET()))) {
handleRESET();
} else if (UNLIKELY(lowered(INT()))) {
handleINT();
} else if (UNLIKELY(lowered(HALT()))) {
execute(0); // NOP
} else {
execute(fetchByte());
}
if (UNLIKELY(lowered(HALT())))
return execute(0); // NOP
return execute(fetchByte());
}
ExecutedInstruction.fire(*this);
return cycles();
}

View File

@ -18,7 +18,6 @@ void Board::initialise() {
m_ram.load(romDirectory + "/8080EX1.COM", 0x100); // Cringle/Bartholomew
//m_ram.load(romDirectory + "/CPUTEST.COM", 0x100); // SuperSoft diagnostics
poke(5, 0xc9); // ret
m_cpu.ExecutingInstruction.connect(std::bind(&Board::Cpu_ExecutingInstruction_Cpm, this, std::placeholders::_1));
if (m_configuration.isProfileMode()) {
@ -30,15 +29,23 @@ void Board::initialise() {
}
CPU().powerOn();
CPU().PC() = m_configuration.getStartAddress();
CPU().reset();
poke(0, 0xc3); // JMP
poke(1, m_configuration.getStartAddress().low);
poke(2, m_configuration.getStartAddress().high);
poke(5, 0xc9); // ret
}
void Board::Cpu_ExecutingInstruction_Cpm(EightBit::Intel8080& cpu) {
switch (cpu.PC().word) {
case 0x0: // CP/M warm start
CPU().powerOff();
if (m_configuration.isProfileMode()) {
m_profiler.dump();
if (++m_warmstartCount == 3) {
CPU().powerOff();
if (m_configuration.isProfileMode()) {
m_profiler.dump();
}
}
break;
case 0x5: // BDOS

View File

@ -31,6 +31,7 @@ private:
EightBit::Intel8080 m_cpu;
EightBit::Disassembler m_disassembler;
EightBit::Profiler m_profiler;
int m_warmstartCount = 0;
void Cpu_ExecutingInstruction_Cpm(EightBit::Intel8080& cpu);

View File

@ -39,8 +39,6 @@ namespace EightBit {
virtual register16_t& DE() final { return de; }
virtual register16_t& HL() final { return hl; }
int singleStep();
protected:
virtual void reset() final;
virtual int execute(uint8_t opcode) final;

View File

@ -277,38 +277,30 @@ void EightBit::GameBoy::LR35902::ccf() {
clearFlag(F(), CF, F() & CF);
}
int EightBit::GameBoy::LR35902::singleStep() {
const auto interruptEnable = BUS().peek(IoRegisters::BASE + IoRegisters::IE);
const auto interruptFlags = m_bus.IO().peek(IoRegisters::IF);
auto masked = interruptEnable & interruptFlags;
if (masked) {
if (IME()) {
m_bus.IO().poke(IoRegisters::IF, 0);
lower(INT());
const int index = EightBit::findFirstSet(masked);
BUS().DATA() = 0x38 + (index << 3);
} else {
if (halted())
proceed();
}
}
const auto current = step();
m_bus.IO().checkTimers(current);
m_bus.IO().transferDma();
return current;
}
int EightBit::GameBoy::LR35902::step() {
ExecutingInstruction.fire(*this);
m_prefixCB = false;
resetCycles();
int ran = 0;
if (LIKELY(powered())) {
const auto interruptEnable = BUS().peek(IoRegisters::BASE + IoRegisters::IE);
const auto interruptFlags = m_bus.IO().peek(IoRegisters::IF);
auto masked = interruptEnable & interruptFlags;
if (masked) {
if (IME()) {
m_bus.IO().poke(IoRegisters::IF, 0);
lower(INT());
const int index = EightBit::findFirstSet(masked);
BUS().DATA() = 0x38 + (index << 3);
} else {
if (halted())
proceed();
}
}
if (UNLIKELY(lowered(INT()))) {
raise(HALT());
raise(INT());
@ -320,6 +312,10 @@ int EightBit::GameBoy::LR35902::step() {
} else {
ran = execute(fetchByte());
}
m_bus.IO().checkTimers(ran);
m_bus.IO().transferDma();
}
ExecutedInstruction.fire(*this);
return ran;

View File

@ -6,6 +6,8 @@ EightBit::MOS6502::MOS6502(Bus& bus)
void EightBit::MOS6502::powerOn() {
Processor::powerOn();
X() = Bit7;
Y() = 0;
A() = 0;
@ -13,8 +15,6 @@ void EightBit::MOS6502::powerOn() {
S() = Mask8;
raise(SO());
Processor::powerOn();
}
int EightBit::MOS6502::step() {

View File

@ -74,11 +74,17 @@ namespace EightBit {
PinLevel& FIRQ() { return m_firq; }
protected:
virtual void reset() final;
// Default push/pop handlers
virtual void push(uint8_t value) final { pushS(value); }
virtual uint8_t pop() final { return popS(); }
// Interrupt (etc.) handlers
virtual void handleRESET() final;
virtual void handleNMI() final;
private:
const uint8_t RESETvector = 0xfe; // RESET vector
const uint8_t NMIvector = 0xfc; // NMI vector
@ -113,8 +119,13 @@ namespace EightBit {
return register16_t(low, high);
}
register16_t popWordS() { popWord(S()); }
register16_t popWordU() { popWord(U()); }
register16_t popWordS() { return popWord(S()); }
register16_t popWordU() { return popWord(U()); }
// Interrupt (etc.) handlers
void handleIRQ();
void handleFIRQ();
// Execution helpers
@ -122,12 +133,6 @@ namespace EightBit {
void execute10(uint8_t opcode);
void execute11(uint8_t opcode);
// Interrupt handlers
void handleNMI();
void handleIRQ();
void handleFIRQ();
// Register selection for "indexed"
register16_t& RR(int which);

View File

@ -12,7 +12,9 @@ int EightBit::mc6809::step() {
if (LIKELY(powered())) {
m_prefix10 = m_prefix11 = false;
ExecutingInstruction.fire(*this);
if (lowered(NMI()))
if (lowered(RESET()))
handleRESET();
else if (lowered(NMI()))
handleNMI();
else if (lowered(FIRQ()) && !(CC() & FF))
handleFIRQ();
@ -27,36 +29,37 @@ int EightBit::mc6809::step() {
// Interrupt (etc.) handlers
void EightBit::mc6809::reset() {
Processor::reset();
DP() = 0; // Reestablish zero page
setFlag(CC(), IF | FF); // Disable IRQ and FIRQ
void EightBit::mc6809::handleRESET() {
Processor::handleRESET();
raise(NMI());
DP() = 0;
setFlag(CC(), IF | FF);
jump(getWordPaged(0xff, RESETvector));
}
void EightBit::mc6809::handleNMI() {
raise(NMI());
addCycles(21);
Processor::handleNMI();
saveEntireRegisterState();
jump(getWordPaged(0xff, NMIvector));
addCycles(21);
}
void EightBit::mc6809::handleIRQ() {
raise(IRQ());
addCycles(21);
Processor::handleINT(); // Synonymous
saveEntireRegisterState();
setFlag(CC(), IF); // Disable IRQ
setFlag(CC(), IF);
jump(getWordPaged(0xff, IRQvector));
addCycles(21);
}
void EightBit::mc6809::handleFIRQ() {
addCycles(12);
raise(FIRQ());
clearFlag(CC(), EF); // Clear the EF flag. i.e. this only saves PC and CC
clearFlag(CC(), EF);
pushWordS(PC());
pushS(CC());
setFlag(CC(), IF | FF); // Disable IRQ and FIRQ
setFlag(CC(), IF | FF);
jump(getWordPaged(0xff, FIRQvector));
addCycles(12);
}
//

View File

@ -49,11 +49,13 @@ namespace EightBit {
Z80(Bus& bus, InputOutput& ports);
Signal<Z80> ExecutingInstruction;
Signal<Z80> ExecutedInstruction;
PinLevel& M1() { return m_m1Line; } // Out
virtual int execute(uint8_t opcode) final;
virtual int step() final;
virtual void powerOn() final;
virtual register16_t& AF() final;
virtual register16_t& BC() final;
@ -83,7 +85,9 @@ namespace EightBit {
}
protected:
virtual void reset() final;
virtual void handleRESET() final;
virtual void handleNMI() final;
virtual void handleINT() final;
private:
PinLevel m_m1Line = Low;

View File

@ -24,9 +24,9 @@ EightBit::register16_t& EightBit::Z80::HL() {
return m_registers[m_registerSet][HL_IDX];
}
void EightBit::Z80::reset() {
void EightBit::Z80::powerOn() {
IntelProcessor::reset();
IntelProcessor::powerOn();
raise(M1());
@ -34,23 +34,53 @@ void EightBit::Z80::reset() {
IM() = 0;
REFRESH() = 0;
IV() = 0xff;
IV() = Mask8;
exxAF();
exx();
AF() = 0xffff;
BC() = 0xffff;
DE() = 0xffff;
HL() = 0xffff;
IX() = 0xffff;
IY() = 0xffff;
IX() = IY() = AF() = BC() = DE() = HL() = Mask16;
m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false;
}
void EightBit::Z80::handleRESET() {
Processor::handleRESET();
di();
addCycles(3);
}
void EightBit::Z80::handleNMI() {
Processor::handleNMI();
raise(HALT());
IFF1() = false;
restart(0x66);
addCycles(13);
}
void EightBit::Z80::handleINT() {
Processor::handleINT();
raise(HALT());
if (IFF1()) {
di();
switch (IM()) {
case 0: // i8080 equivalent
execute(BUS().DATA());
break;
case 1:
restart(7 << 3);
addCycles(13);
break;
case 2:
call(MEMPTR() = register16_t(BUS().DATA(), IV()));
addCycles(19);
break;
default:
UNREACHABLE;
}
}
}
void EightBit::Z80::di() {
IFF1() = IFF2() = false;
}
@ -614,39 +644,19 @@ int EightBit::Z80::step() {
resetCycles();
if (LIKELY(powered())) {
lower(M1());
if (UNLIKELY(lowered(NMI()))) {
raise(HALT());
raise(NMI());
IFF1() = false;
restart(0x66);
addCycles(13);
return cycles();
if (UNLIKELY(lowered(RESET()))) {
handleRESET();
} else if (UNLIKELY(lowered(NMI()))) {
handleNMI();
} else if (UNLIKELY(lowered(INT()))) {
handleINT();
} else if (UNLIKELY(lowered(HALT()))) {
execute(0); // NOP
} else {
execute(fetchByte());
}
if (UNLIKELY(lowered(INT()))) {
raise(HALT());
raise(INT());
if (IFF1()) {
di();
switch (IM()) {
case 0: // i8080 equivalent
return execute(BUS().DATA());
case 1:
restart(7 << 3);
addCycles(13);
return cycles();
case 2:
call(MEMPTR() = register16_t(BUS().DATA(), IV()));
addCycles(19);
return cycles();
default:
UNREACHABLE;
}
}
}
if (UNLIKELY(lowered(HALT())))
return execute(0); // NOP
return execute(fetchByte());
}
ExecutedInstruction.fire(*this);
return cycles();
}

View File

@ -18,7 +18,6 @@ void Board::initialise() {
m_ram.load(romDirectory + "/zexall.com", 0x100); // Cringle/Bartholomew
//m_ram.load(romDirectory + "/CPUTEST.COM", 0x100); // SuperSoft diagnostics
poke(5, 0xc9); // ret
m_cpu.ExecutingInstruction.connect(std::bind(&Board::Cpu_ExecutingInstruction_Cpm, this, std::placeholders::_1));
if (m_configuration.isProfileMode()) {
@ -30,7 +29,13 @@ void Board::initialise() {
}
CPU().powerOn();
CPU().PC() = m_configuration.getStartAddress();
CPU().reset();
poke(0, 0xc3); // JMP
poke(1, m_configuration.getStartAddress().low);
poke(2, m_configuration.getStartAddress().high);
poke(5, 0xc9); // ret
}
void Board::Cpu_ExecutingInstruction_Cpm(EightBit::Z80& cpu) {
@ -38,9 +43,11 @@ void Board::Cpu_ExecutingInstruction_Cpm(EightBit::Z80& cpu) {
CPU().powerOff();
switch (cpu.PC().word) {
case 0x0: // CP/M warm start
CPU().powerOff();
if (m_configuration.isProfileMode()) {
m_profiler.dump();
if (++m_warmstartCount == 3) {
CPU().powerOff();
if (m_configuration.isProfileMode()) {
m_profiler.dump();
}
}
break;
case 0x5: // BDOS

View File

@ -32,6 +32,7 @@ private:
EightBit::Z80 m_cpu;
EightBit::Disassembler m_disassembler;
EightBit::Profiler m_profiler;
int m_warmstartCount = 0;
void Cpu_ExecutingInstruction_Cpm(EightBit::Z80& cpu);

View File

@ -56,12 +56,12 @@ namespace EightBit {
uint8_t& H() { return HL().high; }
uint8_t& L() { return HL().low; }
virtual void powerOn() override;
protected:
IntelProcessor(Bus& bus);
virtual ~IntelProcessor() = default;
virtual void reset() override;
template<class T> static void adjustSign(uint8_t& f, const uint8_t value) {
setFlag(f, T::SF, value & T::SF);
}

View File

@ -78,11 +78,11 @@ namespace EightBit {
PinLevel& POWER() { return m_powerLine; }
bool powered() { return raised(POWER()); }
virtual void powerOn() { raise(POWER()); raise(HALT()); reset(); }
virtual void powerOn();
void powerOff() { lower(POWER()); }
void reset() { lower(RESET()); }
int run(int limit);
virtual int singleStep();
virtual int step() = 0;
virtual int execute(uint8_t opcode) = 0;
@ -102,12 +102,14 @@ namespace EightBit {
Processor(Bus& memory);
virtual ~Processor() = default;
virtual void reset();
bool halted() { return lowered(HALT()); }
void halt() { --PC(); lower(HALT()); }
void proceed() { ++PC(); raise(HALT()); }
virtual void handleRESET();
virtual void handleNMI();
virtual void handleINT();
uint8_t getBytePaged(uint8_t page, uint8_t offset) {
return BUS().read(register16_t(offset, page));
}

View File

@ -7,8 +7,8 @@ EightBit::IntelProcessor::IntelProcessor(Bus& bus)
m_decodedOpcodes[i] = i;
}
void EightBit::IntelProcessor::reset() {
Processor::reset();
void EightBit::IntelProcessor::powerOn() {
Processor::powerOn();
SP() = AF() = BC() = DE() = HL() = Mask16;
}

View File

@ -5,24 +5,30 @@ EightBit::Processor::Processor(Bus& bus)
: m_bus(bus) {
}
void EightBit::Processor::reset() {
if (lowered(POWER()))
throw std::logic_error("POWER cannot be low");
void EightBit::Processor::powerOn() {
raise(RESET());
raise(HALT());
raise(INT());
raise(NMI());
raise(POWER());
}
void EightBit::Processor::handleRESET() {
raise(RESET());
PC() = 0;
}
void EightBit::Processor::handleNMI() {
raise(NMI());
}
void EightBit::Processor::handleINT() {
raise(INT());
}
int EightBit::Processor::run(const int limit) {
int current = 0;
while (LIKELY(powered()) && current < limit)
current += singleStep();
current += step();
return current;
}
int EightBit::Processor::singleStep() {
if (UNLIKELY(lowered(RESET())))
reset();
return step();
}