mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2024-12-22 09:30:32 +00:00
Some more rationalisation of processor execution/stepping strategies.
Signed-off-by: Adrian.Conlon <adrian.conlon@gmail.com>
This commit is contained in:
parent
5fe3d458df
commit
e70686c5de
@ -21,9 +21,9 @@ namespace EightBit {
|
||||
Signal<Intel8080> ExecutingInstruction;
|
||||
|
||||
bool isInterruptable() const;
|
||||
|
||||
int interrupt(uint8_t value);
|
||||
|
||||
virtual int execute(uint8_t opcode);
|
||||
int step();
|
||||
|
||||
virtual register16_t& AF() override {
|
||||
@ -32,17 +32,9 @@ namespace EightBit {
|
||||
return af;
|
||||
}
|
||||
|
||||
virtual register16_t& BC() override {
|
||||
return bc;
|
||||
}
|
||||
|
||||
virtual register16_t& DE() override {
|
||||
return de;
|
||||
}
|
||||
|
||||
virtual register16_t& HL() override {
|
||||
return hl;
|
||||
}
|
||||
virtual register16_t& BC() override { return bc; }
|
||||
virtual register16_t& DE() override { return de; }
|
||||
virtual register16_t& HL() override { return hl; }
|
||||
|
||||
virtual void initialise();
|
||||
|
||||
@ -159,11 +151,9 @@ namespace EightBit {
|
||||
|
||||
static void subtract(uint8_t& f, uint8_t& operand, uint8_t value, int carry = 0);
|
||||
|
||||
virtual int execute(uint8_t opcode);
|
||||
void execute(int x, int y, int z, int p, int q);
|
||||
|
||||
static void increment(uint8_t& f, uint8_t& operand);
|
||||
|
||||
static void decrement(uint8_t& f, uint8_t& operand);
|
||||
|
||||
bool returnConditionalFlag(uint8_t& f, int flag);
|
||||
|
@ -1,8 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "Intel8080.h"
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Disassembler.h"
|
||||
#pragma region Reset and initialisation
|
||||
|
||||
EightBit::Intel8080::Intel8080(Memory& memory, InputOutput& ports)
|
||||
: IntelProcessor(memory),
|
||||
@ -16,6 +15,8 @@ void EightBit::Intel8080::initialise() {
|
||||
AF().word = BC().word = DE().word = HL().word = 0;
|
||||
}
|
||||
|
||||
#pragma endregion Reset and initialisation
|
||||
|
||||
#pragma region Interrupt routines
|
||||
|
||||
void EightBit::Intel8080::di() {
|
||||
@ -279,12 +280,18 @@ void EightBit::Intel8080::in() {
|
||||
|
||||
#pragma endregion I/O instructions
|
||||
|
||||
#pragma region Controlled instruction execution
|
||||
|
||||
int EightBit::Intel8080::step() {
|
||||
ExecutingInstruction.fire(*this);
|
||||
cycles = 0;
|
||||
return fetchExecute();
|
||||
}
|
||||
|
||||
#pragma endregion Controlled instruction execution
|
||||
|
||||
#pragma region Instruction decode and execution
|
||||
|
||||
int EightBit::Intel8080::execute(uint8_t opcode) {
|
||||
|
||||
const auto& decoded = getDecodedOpcode(opcode);
|
||||
@ -639,3 +646,5 @@ void EightBit::Intel8080::execute(int x, int y, int z, int p, int q) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion Instruction decode and execution
|
||||
|
@ -21,31 +21,14 @@ namespace EightBit {
|
||||
|
||||
Signal<LR35902> ExecutingInstruction;
|
||||
|
||||
void stop() { m_stopped = true; }
|
||||
void start() { m_stopped = false; }
|
||||
bool stopped() const { return m_stopped; }
|
||||
|
||||
bool& IME() { return m_ime; }
|
||||
|
||||
void di();
|
||||
void ei();
|
||||
|
||||
virtual register16_t& AF() override {
|
||||
af.low &= 0xf0;
|
||||
return af;
|
||||
}
|
||||
|
||||
virtual register16_t& BC() override {
|
||||
return bc;
|
||||
}
|
||||
|
||||
virtual register16_t& DE() override {
|
||||
return de;
|
||||
}
|
||||
|
||||
virtual register16_t& HL() override {
|
||||
return hl;
|
||||
}
|
||||
virtual register16_t& BC() override { return bc; }
|
||||
virtual register16_t& DE() override { return de; }
|
||||
virtual register16_t& HL() override { return hl; }
|
||||
|
||||
virtual void reset();
|
||||
virtual void initialise();
|
||||
@ -53,23 +36,11 @@ namespace EightBit {
|
||||
static int framesPerSecond() { return 60; }
|
||||
static int cyclesPerFrame() { return cyclesPerSecond() / framesPerSecond(); }
|
||||
|
||||
int runRasterLines() {
|
||||
m_bus.resetLY();
|
||||
int cycles = 0;
|
||||
for (int line = 0; line < Display::RasterHeight; ++line)
|
||||
cycles += runRasterLine();
|
||||
return cycles;
|
||||
}
|
||||
int runRasterLines();
|
||||
int runRasterLine();
|
||||
int runVerticalBlankLines();
|
||||
|
||||
int runVerticalBlankLines() {
|
||||
m_bus.triggerInterrupt(Bus::Interrupts::VerticalBlank);
|
||||
int cycles = 0;
|
||||
for (int line = 0; line < (Bus::TotalLineCount - Display::RasterHeight); ++line)
|
||||
cycles += runRasterLine();
|
||||
return cycles;
|
||||
}
|
||||
|
||||
int run(int limit);
|
||||
int singleStep();
|
||||
|
||||
protected:
|
||||
virtual int execute(uint8_t opcode);
|
||||
@ -84,21 +55,14 @@ namespace EightBit {
|
||||
register16_t hl;
|
||||
|
||||
bool m_ime;
|
||||
bool m_stopped;
|
||||
|
||||
bool m_prefixCB;
|
||||
|
||||
bool m_stopped;
|
||||
|
||||
static int cyclesPerSecond() { return 4 * 1024 * 1024; }
|
||||
static int cyclesPerLine() { return cyclesPerFrame() / Bus::TotalLineCount; }
|
||||
|
||||
int runRasterLine() {
|
||||
auto cycles = run(cyclesPerLine());
|
||||
m_bus.incrementLY();
|
||||
if ((m_bus.peekRegister(Bus::STAT) & Processor::Bit6) && (m_bus.peekRegister(Bus::LYC) == m_bus.peekRegister(Bus::LY)))
|
||||
m_bus.triggerInterrupt(Bus::Interrupts::DisplayControlStatus);
|
||||
return cycles;
|
||||
}
|
||||
bool& IME() { return m_ime; }
|
||||
|
||||
uint8_t R(int r, uint8_t& a) {
|
||||
switch (r) {
|
||||
@ -203,6 +167,13 @@ namespace EightBit {
|
||||
static void increment(uint8_t& f, uint8_t& operand);
|
||||
static void decrement(uint8_t& f, uint8_t& operand);
|
||||
|
||||
void stop() { m_stopped = true; }
|
||||
void start() { m_stopped = false; }
|
||||
bool stopped() const { return m_stopped; }
|
||||
|
||||
void di();
|
||||
void ei();
|
||||
|
||||
void reti();
|
||||
|
||||
bool jrConditionalFlag(uint8_t& f, int flag);
|
||||
|
@ -2,13 +2,14 @@
|
||||
#include "LR35902.h"
|
||||
|
||||
// based on http://www.z80.info/decoding.htm
|
||||
// Half carry flag help from https://github.com/oubiwann/z80
|
||||
|
||||
#pragma region Reset and initialisation
|
||||
|
||||
EightBit::LR35902::LR35902(Bus& memory)
|
||||
: IntelProcessor(memory),
|
||||
m_bus(memory),
|
||||
m_ime(false),
|
||||
m_prefixCB(false) {
|
||||
: IntelProcessor(memory),
|
||||
m_bus(memory),
|
||||
m_ime(false),
|
||||
m_prefixCB(false) {
|
||||
}
|
||||
|
||||
void EightBit::LR35902::reset() {
|
||||
@ -29,6 +30,8 @@ void EightBit::LR35902::initialise() {
|
||||
m_prefixCB = false;
|
||||
}
|
||||
|
||||
#pragma endregion Reset and initialisation
|
||||
|
||||
#pragma region Interrupt routines
|
||||
|
||||
void EightBit::LR35902::di() {
|
||||
@ -328,40 +331,66 @@ void EightBit::LR35902::ccf(uint8_t& a, uint8_t& f) {
|
||||
|
||||
#pragma endregion Miscellaneous instructions
|
||||
|
||||
int EightBit::LR35902::run(int limit) {
|
||||
#pragma region Controlled instruction execution
|
||||
|
||||
int EightBit::LR35902::runRasterLines() {
|
||||
m_bus.resetLY();
|
||||
int cycles = 0;
|
||||
for (int line = 0; line < Display::RasterHeight; ++line)
|
||||
cycles += runRasterLine();
|
||||
return cycles;
|
||||
}
|
||||
|
||||
int EightBit::LR35902::runRasterLine() {
|
||||
auto cycles = run(cyclesPerLine());
|
||||
m_bus.incrementLY();
|
||||
if ((m_bus.peekRegister(Bus::STAT) & Processor::Bit6) && (m_bus.peekRegister(Bus::LYC) == m_bus.peekRegister(Bus::LY)))
|
||||
m_bus.triggerInterrupt(Bus::Interrupts::DisplayControlStatus);
|
||||
return cycles;
|
||||
}
|
||||
|
||||
int EightBit::LR35902::runVerticalBlankLines() {
|
||||
m_bus.triggerInterrupt(Bus::Interrupts::VerticalBlank);
|
||||
int cycles = 0;
|
||||
for (int line = 0; line < (Bus::TotalLineCount - Display::RasterHeight); ++line)
|
||||
cycles += runRasterLine();
|
||||
return cycles;
|
||||
}
|
||||
|
||||
int EightBit::LR35902::singleStep() {
|
||||
|
||||
int current = 0;
|
||||
do {
|
||||
auto interruptEnable = m_bus.peekRegister(Bus::IE);
|
||||
auto interruptFlags = m_bus.peekRegister(Bus::IF);
|
||||
auto ime = IME();
|
||||
|
||||
auto masked = interruptEnable & interruptFlags;
|
||||
if (masked) {
|
||||
if (ime) {
|
||||
m_bus.pokeRegister(Bus::IF, 0);
|
||||
} else {
|
||||
if (isHalted())
|
||||
proceed();
|
||||
}
|
||||
}
|
||||
auto interruptEnable = m_bus.peekRegister(Bus::IE);
|
||||
auto interruptFlags = m_bus.peekRegister(Bus::IF);
|
||||
auto ime = IME();
|
||||
|
||||
if (ime && (masked & Bus::Interrupts::VerticalBlank)) {
|
||||
current += interrupt(0x40);
|
||||
} else if (ime && (masked & Bus::Interrupts::DisplayControlStatus)) {
|
||||
current += interrupt(0x48);
|
||||
} else if (ime && (masked & Bus::Interrupts::TimerOverflow)) {
|
||||
current += interrupt(0x50);
|
||||
} else if (ime && (masked & Bus::Interrupts::SerialTransfer)) {
|
||||
current += interrupt(0x58);
|
||||
} else if (ime && (masked & Bus::Interrupts::Keypad)) {
|
||||
current += interrupt(0x60);
|
||||
auto masked = interruptEnable & interruptFlags;
|
||||
if (masked) {
|
||||
if (ime) {
|
||||
m_bus.pokeRegister(Bus::IF, 0);
|
||||
} else {
|
||||
current += isHalted() ? 1 : step();
|
||||
if (isHalted())
|
||||
proceed();
|
||||
}
|
||||
}
|
||||
|
||||
m_bus.checkTimers(current);
|
||||
if (ime && (masked & Bus::Interrupts::VerticalBlank)) {
|
||||
current += interrupt(0x40);
|
||||
} else if (ime && (masked & Bus::Interrupts::DisplayControlStatus)) {
|
||||
current += interrupt(0x48);
|
||||
} else if (ime && (masked & Bus::Interrupts::TimerOverflow)) {
|
||||
current += interrupt(0x50);
|
||||
} else if (ime && (masked & Bus::Interrupts::SerialTransfer)) {
|
||||
current += interrupt(0x58);
|
||||
} else if (ime && (masked & Bus::Interrupts::Keypad)) {
|
||||
current += interrupt(0x60);
|
||||
} else {
|
||||
current += isHalted() ? 1 : step();
|
||||
}
|
||||
|
||||
m_bus.checkTimers(current);
|
||||
|
||||
} while (current < limit);
|
||||
return current;
|
||||
}
|
||||
|
||||
@ -372,6 +401,10 @@ int EightBit::LR35902::step() {
|
||||
return fetchExecute();
|
||||
}
|
||||
|
||||
#pragma endregion Controlled instruction execution
|
||||
|
||||
#pragma region Instruction decode and execution
|
||||
|
||||
int EightBit::LR35902::execute(uint8_t opcode) {
|
||||
|
||||
const auto& decoded = getDecodedOpcode(opcode);
|
||||
@ -864,4 +897,6 @@ void EightBit::LR35902::executeOther(int x, int y, int z, int p, int q) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion Instruction decode and execution
|
||||
|
@ -42,11 +42,16 @@ namespace EightBit {
|
||||
};
|
||||
|
||||
MOS6502(Memory& memory);
|
||||
virtual ~MOS6502();
|
||||
|
||||
Signal<MOS6502> ExecutingInstruction;
|
||||
Signal<MOS6502> ExecutedInstruction;
|
||||
|
||||
virtual void triggerIRQ();
|
||||
virtual void triggerNMI();
|
||||
|
||||
virtual int execute(uint8_t opcode);
|
||||
int step();
|
||||
|
||||
uint8_t& X() { return x; }
|
||||
uint8_t& Y() { return y; }
|
||||
uint8_t& A() { return a; }
|
||||
@ -54,23 +59,8 @@ namespace EightBit {
|
||||
uint8_t& P() { return p; }
|
||||
|
||||
virtual void initialise();
|
||||
|
||||
virtual int step();
|
||||
|
||||
virtual void reset();
|
||||
|
||||
virtual void triggerIRQ();
|
||||
virtual void triggerNMI();
|
||||
|
||||
void getWord(register16_t& output);
|
||||
void getWord(uint16_t offset, register16_t& output);
|
||||
void getWord(const register16_t& offset, register16_t& output);
|
||||
|
||||
protected:
|
||||
virtual void interrupt(uint16_t vector);
|
||||
|
||||
virtual int execute(uint8_t opcode);
|
||||
|
||||
private:
|
||||
void adjustZero(uint8_t datum) { clearFlag(P(), ZF, datum); }
|
||||
void adjustNegative(uint8_t datum) { setFlag(P(), NF, datum & NF); }
|
||||
@ -80,9 +70,15 @@ namespace EightBit {
|
||||
adjustNegative(datum);
|
||||
}
|
||||
|
||||
void getWord(register16_t& output);
|
||||
void getWord(uint16_t offset, register16_t& output);
|
||||
void getWord(const register16_t& offset, register16_t& output);
|
||||
|
||||
virtual void push(uint8_t value) override;
|
||||
virtual uint8_t pop() override;
|
||||
|
||||
void interrupt(uint16_t vector);
|
||||
|
||||
#pragma region 6502 addressing modes
|
||||
|
||||
#pragma region Addresses
|
||||
|
@ -24,9 +24,6 @@ EightBit::MOS6502::MOS6502(Memory& memory)
|
||||
};
|
||||
}
|
||||
|
||||
EightBit::MOS6502::~MOS6502() {
|
||||
}
|
||||
|
||||
void EightBit::MOS6502::initialise() {
|
||||
|
||||
Processor::initialise();
|
||||
|
@ -326,10 +326,7 @@ void Fuse::TestRunner::run() {
|
||||
initialise();
|
||||
auto allowedCycles = m_test.registerState.tstates;
|
||||
try {
|
||||
auto cycles = 0;
|
||||
do {
|
||||
cycles += m_cpu.step();
|
||||
} while (allowedCycles > cycles);
|
||||
m_cpu.run(allowedCycles);
|
||||
check();
|
||||
} catch (std::logic_error& error) {
|
||||
m_unimplemented = true;
|
||||
|
@ -46,9 +46,6 @@ namespace EightBit {
|
||||
|
||||
Signal<Z80> ExecutingInstruction;
|
||||
|
||||
void di();
|
||||
void ei();
|
||||
|
||||
int interruptMaskable(uint8_t value) { return interrupt(true, value); }
|
||||
int interruptMaskable() { return interruptMaskable(0); }
|
||||
int interruptNonMaskable() { return interrupt(false, 0); }
|
||||
@ -102,6 +99,12 @@ namespace EightBit {
|
||||
virtual void initialise();
|
||||
|
||||
protected:
|
||||
virtual int fetchExecute() override {
|
||||
M1() = true;
|
||||
return IntelProcessor::fetchExecute();
|
||||
}
|
||||
|
||||
private:
|
||||
InputOutput& m_ports;
|
||||
|
||||
enum { BC_IDX, DE_IDX, HL_IDX };
|
||||
@ -132,11 +135,6 @@ namespace EightBit {
|
||||
int8_t m_displacement;
|
||||
bool m_displaced;
|
||||
|
||||
virtual int fetchExecute() override{
|
||||
M1() = true;
|
||||
return IntelProcessor::fetchExecute();
|
||||
}
|
||||
|
||||
uint16_t displacedAddress() {
|
||||
assert(m_displaced);
|
||||
return MEMPTR().word = (m_prefixDD ? IX() : IY()).word + m_displacement;
|
||||
@ -326,6 +324,9 @@ namespace EightBit {
|
||||
static void increment(uint8_t& f, uint8_t& operand);
|
||||
static void decrement(uint8_t& f, uint8_t& operand);
|
||||
|
||||
void di();
|
||||
void ei();
|
||||
|
||||
void retn();
|
||||
void reti();
|
||||
|
||||
|
@ -2,7 +2,8 @@
|
||||
#include "Z80.h"
|
||||
|
||||
// based on http://www.z80.info/decoding.htm
|
||||
// Half carry flag help from https://github.com/oubiwann/z80
|
||||
|
||||
#pragma region Reset and initialisation
|
||||
|
||||
EightBit::Z80::Z80(Memory& memory, InputOutput& ports)
|
||||
: IntelProcessor(memory),
|
||||
@ -63,6 +64,8 @@ void EightBit::Z80::initialise() {
|
||||
m_prefixFD = false;
|
||||
}
|
||||
|
||||
#pragma endregion Reset and initialisation
|
||||
|
||||
#pragma region Interrupt routines
|
||||
|
||||
void EightBit::Z80::di() {
|
||||
@ -748,6 +751,8 @@ void EightBit::Z80::readPort() {
|
||||
|
||||
#pragma endregion I/O instructions
|
||||
|
||||
#pragma region Controlled instruction execution
|
||||
|
||||
int EightBit::Z80::step() {
|
||||
ExecutingInstruction.fire(*this);
|
||||
m_displaced = m_prefixCB = m_prefixDD = m_prefixED = m_prefixFD = false;
|
||||
@ -755,6 +760,10 @@ int EightBit::Z80::step() {
|
||||
return fetchExecute();
|
||||
}
|
||||
|
||||
#pragma endregion Controlled instruction execution
|
||||
|
||||
#pragma region Instruction decode and execution
|
||||
|
||||
int EightBit::Z80::execute(uint8_t opcode) {
|
||||
|
||||
if (!M1())
|
||||
@ -1547,4 +1556,6 @@ void EightBit::Z80::executeOther(int x, int y, int z, int p, int q) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion Instruction decode and execution
|
||||
|
@ -65,6 +65,10 @@ namespace EightBit {
|
||||
|
||||
void reset();
|
||||
|
||||
virtual int run(int limit);
|
||||
virtual int singleStep();
|
||||
virtual int step() = 0;
|
||||
|
||||
virtual int execute(uint8_t opcode) = 0;
|
||||
|
||||
protected:
|
||||
|
@ -15,3 +15,15 @@ void EightBit::Processor::reset() {
|
||||
void EightBit::Processor::initialise() {
|
||||
reset();
|
||||
}
|
||||
|
||||
int EightBit::Processor::run(int limit) {
|
||||
int current = 0;
|
||||
do {
|
||||
current += singleStep();
|
||||
} while (current < limit);
|
||||
return current;
|
||||
}
|
||||
|
||||
int EightBit::Processor::singleStep() {
|
||||
return step();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user