1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-26 15:32:04 +00:00

Pulled the 6502 into a CPU namespace, making it an instance of something that has micro-opcodes and schedules them, and factoring out the formulation of a register pair.

This commit is contained in:
Thomas Harte 2017-05-14 22:08:15 -04:00
parent b81a2cc273
commit 0808e9b6fb
32 changed files with 302 additions and 199 deletions

View File

@ -11,7 +11,7 @@
#include <stdint.h>
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Processors/6502/6502.hpp"
#include "../CRTMachine.hpp"
#include "Bus.hpp"
#include "PIA.hpp"

View File

@ -9,25 +9,25 @@
#ifndef Atari2600_Cartridge_hpp
#define Atari2600_Cartridge_hpp
#include "../../../Processors/6502/CPU6502.hpp"
#include "../../../Processors/6502/6502.hpp"
#include "../Bus.hpp"
namespace Atari2600 {
template<class T> class Cartridge:
public CPU6502::Processor<Cartridge<T>>,
public CPU::MOS6502::Processor<Cartridge<T>>,
public Bus {
public:
Cartridge(const std::vector<uint8_t> &rom) :
rom_(rom) {}
void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Cartridge<T>>::run_for_cycles(number_of_cycles); }
void set_reset_line(bool state) { CPU6502::Processor<Cartridge<T>>::set_reset_line(state); }
void run_for_cycles(int number_of_cycles) { CPU::MOS6502::Processor<Cartridge<T>>::run_for_cycles(number_of_cycles); }
void set_reset_line(bool state) { CPU::MOS6502::Processor<Cartridge<T>>::set_reset_line(state); }
void advance_cycles(unsigned int cycles) {}
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
// to satisfy CPU::MOS6502::Processor
unsigned int perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
uint8_t returnValue = 0xff;
unsigned int cycles_run_for = 3;
@ -35,7 +35,7 @@ template<class T> class Cartridge:
// leap to the end of ready only once ready is signalled — because on a 6502 ready doesn't take
// effect until the next read; therefore it isn't safe to assume that signalling ready immediately
// skips to the end of the line.
if(operation == CPU6502::BusOperation::Ready)
if(operation == CPU::MOS6502::BusOperation::Ready)
cycles_run_for = (unsigned int)tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_);
cycles_since_speaker_update_ += cycles_run_for;
@ -43,7 +43,7 @@ template<class T> class Cartridge:
cycles_since_6532_update_ += (cycles_run_for / 3);
static_cast<T *>(this)->advance_cycles(cycles_run_for / 3);
if(operation != CPU6502::BusOperation::Ready) {
if(operation != CPU::MOS6502::BusOperation::Ready) {
// give the cartridge a chance to respond to the bus access
static_cast<T *>(this)->perform_bus_operation(operation, address, value);
@ -91,7 +91,7 @@ template<class T> class Cartridge:
case 0x00: update_video(); tia_->set_sync(*value & 0x02); break;
case 0x01: update_video(); tia_->set_blank(*value & 0x02); break;
case 0x02: CPU6502::Processor<Cartridge<T>>::set_ready_line(true); break;
case 0x02: CPU::MOS6502::Processor<Cartridge<T>>::set_ready_line(true); break;
case 0x03: update_video(); tia_->reset_horizontal_counter(); break;
// TODO: audio will now be out of synchronisation — fix
@ -156,7 +156,7 @@ template<class T> class Cartridge:
}
}
if(!tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_)) CPU6502::Processor<Cartridge<T>>::set_ready_line(false);
if(!tia_->get_cycles_until_horizontal_blank(cycles_since_video_update_)) CPU::MOS6502::Processor<Cartridge<T>>::set_ready_line(false);
return cycles_run_for / 3;
}

View File

@ -19,13 +19,13 @@ class CartridgeActivisionStack: public Cartridge<CartridgeActivisionStack> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
if(!(address & 0x1000)) return;
// This is a bit of a hack; a real cartridge can't see either the sync or read lines, and can't see
// address line 13. Instead it looks for a pattern in recent address accesses that would imply an
// RST or JSR.
if(operation == CPU6502::BusOperation::ReadOpcode && (last_opcode_ == 0x20 || last_opcode_ == 0x60)) {
if(operation == CPU::MOS6502::BusOperation::ReadOpcode && (last_opcode_ == 0x20 || last_opcode_ == 0x60)) {
if(address & 0x2000) {
rom_ptr_ = rom_.data();
} else {
@ -37,7 +37,7 @@ class CartridgeActivisionStack: public Cartridge<CartridgeActivisionStack> {
*value = rom_ptr_[address & 4095];
}
if(operation == CPU6502::BusOperation::ReadOpcode) last_opcode_ = *value;
if(operation == CPU::MOS6502::BusOperation::ReadOpcode) last_opcode_ = *value;
}
private:

View File

@ -20,7 +20,7 @@ class CartridgeAtari16k: public Cartridge<CartridgeAtari16k> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
@ -42,7 +42,7 @@ class CartridgeAtari16kSuperChip: public Cartridge<CartridgeAtari16kSuperChip> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -20,7 +20,7 @@ class CartridgeAtari32k: public Cartridge<CartridgeAtari32k> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
@ -42,7 +42,7 @@ class CartridgeAtari32kSuperChip: public Cartridge<CartridgeAtari32kSuperChip> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -20,7 +20,7 @@ class CartridgeAtari8k: public Cartridge<CartridgeAtari8k> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;
@ -43,7 +43,7 @@ class CartridgeAtari8kSuperChip: public Cartridge<CartridgeAtari8kSuperChip> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -20,7 +20,7 @@ class CartridgeCBSRAMPlus: public Cartridge<CartridgeCBSRAMPlus> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -16,7 +16,7 @@ class CartridgeCommaVid: public Cartridge<CartridgeCommaVid> {
CartridgeCommaVid(const std::vector<uint8_t> &rom) :
Cartridge(rom) {}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
if(!(address & 0x1000)) return;
address &= 0x1fff;

View File

@ -22,7 +22,7 @@ class CartridgeMNetwork: public Cartridge<CartridgeMNetwork> {
high_ram_ptr_ = high_ram_;
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -21,7 +21,7 @@ class CartridgeMegaBoy: public Cartridge<CartridgeMegaBoy> {
rom_ptr_ = rom_.data();
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -23,7 +23,7 @@ class CartridgeParkerBros: public Cartridge<CartridgeParkerBros> {
rom_ptr_[3] = rom_ptr_[2] + 1024;
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -26,7 +26,7 @@ class CartridgePitfall2: public Cartridge<CartridgePitfall2> {
cycles_since_audio_update_ += cycles;
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
address &= 0x1fff;
if(!(address & 0x1000)) return;

View File

@ -21,7 +21,7 @@ class CartridgeTigervision: public Cartridge<CartridgeTigervision> {
rom_ptr_[1] = rom_ptr_[0] + 2048;
}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
if((address&0x1fff) == 0x3f) {
int offset = ((*value) * 2048) & (rom_.size() - 1);
rom_ptr_[0] = rom_.data() + offset;

View File

@ -18,7 +18,7 @@ class CartridgeUnpaged: public Cartridge<CartridgeUnpaged> {
CartridgeUnpaged(const std::vector<uint8_t> &rom) :
Cartridge(rom) {}
void perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
if(isReadOperation(operation) && (address & 0x1000)) {
*value = rom_[address & (rom_.size() - 1)];
}

View File

@ -34,7 +34,7 @@ void Machine::set_serial_bus(std::shared_ptr<::Commodore::Serial::Bus> serial_bu
Commodore::Serial::AttachPortAndBus(serial_port_, serial_bus);
}
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
unsigned int Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
/*
Memory map (given that I'm unsure yet on any potential mirroring):
@ -80,7 +80,7 @@ void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk) {
}
void Machine::run_for_cycles(int number_of_cycles) {
CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles);
CPU::MOS6502::Processor<Machine>::run_for_cycles(number_of_cycles);
set_motor_on(drive_VIA_.get_motor_enabled());
if(drive_VIA_.get_motor_enabled()) // TODO: motor speed up/down
Storage::Disk::Controller::run_for_cycles(number_of_cycles);

View File

@ -9,7 +9,7 @@
#ifndef Commodore1540_hpp
#define Commodore1540_hpp
#include "../../../Processors/6502/CPU6502.hpp"
#include "../../../Processors/6502/6502.hpp"
#include "../../../Components/6522/6522.hpp"
#include "../SerialBus.hpp"
@ -120,7 +120,7 @@ class SerialPort : public ::Commodore::Serial::Port {
Provides an emulation of the C1540.
*/
class Machine:
public CPU6502::Processor<Machine>,
public CPU::MOS6502::Processor<Machine>,
public MOS::MOS6522IRQDelegate::Delegate,
public DriveVIA::Delegate,
public Storage::Disk::Controller {
@ -141,8 +141,8 @@ class Machine:
void run_for_cycles(int number_of_cycles);
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk);
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy CPU::MOS6502::Processor
unsigned int perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy MOS::MOS6522::Delegate
virtual void mos6522_did_change_interrupt_status(void *mos6522);

View File

@ -97,7 +97,7 @@ Machine::~Machine() {
delete[] rom_;
}
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
unsigned int Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
// run the phase-1 part of this cycle, in which the VIC accesses memory
if(!is_running_at_zero_cost_) mos6560_->run_for_cycles(1);
@ -115,7 +115,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// PC hits the start of the loop that just waits for an interesting tape interrupt to have
// occurred then skip both 6522s and the tape ahead to the next interrupt without any further
// CPU or 6560 costs.
if(use_fast_tape_hack_ && tape_->has_tape() && operation == CPU6502::BusOperation::ReadOpcode) {
if(use_fast_tape_hack_ && tape_->has_tape() && operation == CPU::MOS6502::BusOperation::ReadOpcode) {
if(address == 0xf7b2) {
// Address 0xf7b2 contains a JSR to 0xf8c0 that will fill the tape buffer with the next header.
// So cancel that via a double NOP and fill in the next header programmatically.
@ -137,7 +137,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
*value = 0x0c; // i.e. NOP abs
} else if(address == 0xf90b) {
uint8_t x = (uint8_t)get_value_of_register(CPU6502::Register::X);
uint8_t x = (uint8_t)get_value_of_register(CPU::MOS6502::Register::X);
if(x == 0xe) {
Storage::Tape::Commodore::Parser parser;
std::unique_ptr<Storage::Tape::Commodore::Data> data = parser.get_next_data(tape_->get_tape());
@ -158,13 +158,13 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// set tape status, carry and flag
user_basic_memory_[0x90] |= 0x40;
uint8_t flags = (uint8_t)get_value_of_register(CPU6502::Register::Flags);
flags &= ~(uint8_t)(CPU6502::Flag::Carry | CPU6502::Flag::Interrupt);
set_value_of_register(CPU6502::Register::Flags, flags);
uint8_t flags = (uint8_t)get_value_of_register(CPU::MOS6502::Register::Flags);
flags &= ~(uint8_t)(CPU::MOS6502::Flag::Carry | CPU::MOS6502::Flag::Interrupt);
set_value_of_register(CPU::MOS6502::Register::Flags, flags);
// to ensure that execution proceeds to 0xfccf, pretend a NOP was here and
// ensure that the PC leaps to 0xfccf
set_value_of_register(CPU6502::Register::ProgramCounter, 0xfccf);
set_value_of_register(CPU::MOS6502::Register::ProgramCounter, 0xfccf);
*value = 0xea; // i.e. NOP implied
}
}
@ -181,7 +181,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
user_port_via_->run_for_cycles(1);
keyboard_via_->run_for_cycles(1);
if(typer_ && operation == CPU6502::BusOperation::ReadOpcode && address == 0xEB1E) {
if(typer_ && operation == CPU::MOS6502::BusOperation::ReadOpcode && address == 0xEB1E) {
if(!typer_->type_next_character()) {
clear_all_keys();
typer_.reset();

View File

@ -13,7 +13,7 @@
#include "../../CRTMachine.hpp"
#include "../../Typer.hpp"
#include "../../../Processors/6502/CPU6502.hpp"
#include "../../../Processors/6502/6502.hpp"
#include "../../../Components/6560/6560.hpp"
#include "../../../Components/6522/6522.hpp"
@ -141,7 +141,7 @@ class Vic6560: public MOS::MOS6560<Vic6560> {
};
class Machine:
public CPU6502::Processor<Machine>,
public CPU::MOS6502::Processor<Machine>,
public CRTMachine::Machine,
public MOS::MOS6522IRQDelegate::Delegate,
public Utility::TypeRecipient,
@ -167,8 +167,8 @@ class Machine:
inline void set_use_fast_tape_hack(bool activate) { use_fast_tape_hack_ = activate; }
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy CPU::MOS6502::Processor
unsigned int perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
void synchronise() { mos6560_->synchronise(); }
// to satisfy CRTMachine::Machine
@ -176,7 +176,7 @@ class Machine:
virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return mos6560_->get_crt(); }
virtual std::shared_ptr<Outputs::Speaker> get_speaker() { return mos6560_->get_speaker(); }
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
virtual void run_for_cycles(int number_of_cycles) { CPU::MOS6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
// to satisfy MOS::MOS6522::Delegate
virtual void mos6522_did_change_interrupt_status(void *mos6522);

View File

@ -123,7 +123,7 @@ void Machine::set_rom(ROMSlot slot, std::vector<uint8_t> data, bool is_writeable
#pragma mark - The bus
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
unsigned int Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
unsigned int cycles = 1;
if(address < 0x8000) {
@ -246,7 +246,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
if(
use_fast_tape_hack_ &&
tape_.has_tape() &&
(operation == CPU6502::BusOperation::ReadOpcode) &&
(operation == CPU::MOS6502::BusOperation::ReadOpcode) &&
(
(address == 0xf4e5) || (address == 0xf4e6) || // double NOPs at 0xf4e5, 0xf6de, 0xf6fa and 0xfa51
(address == 0xf6de) || (address == 0xf6df) || // act to disable the normal branch into tape-handling
@ -263,7 +263,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
// allow the PC read to return an RTS.
)
) {
uint8_t service_call = (uint8_t)get_value_of_register(CPU6502::Register::X);
uint8_t service_call = (uint8_t)get_value_of_register(CPU::MOS6502::Register::X);
if(address == 0xf0a8) {
if(!ram_[0x247] && service_call == 14) {
tape_.set_delegate(nullptr);
@ -285,8 +285,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
interrupt_status_ |= tape_.get_interrupt_status();
fast_load_is_in_data_ = true;
set_value_of_register(CPU6502::Register::A, 0);
set_value_of_register(CPU6502::Register::Y, tape_.get_data_register());
set_value_of_register(CPU::MOS6502::Register::A, 0);
set_value_of_register(CPU::MOS6502::Register::Y, tape_.get_data_register());
*value = 0x60; // 0x60 is RTS
}
else *value = os_[address & 16383];

View File

@ -9,7 +9,7 @@
#ifndef Electron_hpp
#define Electron_hpp
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Processors/6502/6502.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include "../ConfigurationTarget.hpp"
@ -68,7 +68,7 @@ enum Key: uint16_t {
Acorn Electron.
*/
class Machine:
public CPU6502::Processor<Machine>,
public CPU::MOS6502::Processor<Machine>,
public CRTMachine::Machine,
public Tape::Delegate,
public Utility::TypeRecipient,
@ -87,8 +87,8 @@ class Machine:
// to satisfy ConfigurationTarget::Machine
void configure_as_target(const StaticAnalyser::Target &target);
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy CPU::MOS6502::Processor
unsigned int perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
void synchronise();
// to satisfy CRTMachine::Machine
@ -96,7 +96,7 @@ class Machine:
virtual void close_output();
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt();
virtual std::shared_ptr<Outputs::Speaker> get_speaker();
virtual void run_for_cycles(int number_of_cycles) { CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
virtual void run_for_cycles(int number_of_cycles) { CPU::MOS6502::Processor<Machine>::run_for_cycles(number_of_cycles); }
// to satisfy Tape::Delegate
virtual void tape_did_change_interrupt_status(Tape *tape);

View File

@ -78,16 +78,16 @@ void Machine::set_rom(ROM rom, const std::vector<uint8_t> &data) {
}
}
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
unsigned int Machine::perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
if(address > ram_top_) {
if(isReadOperation(operation)) *value = paged_rom_[address - ram_top_ - 1];
// 024D = 0 => fast; otherwise slow
// E6C9 = read byte: return byte in A
if(address == tape_get_byte_address_ && paged_rom_ == rom_ && use_fast_tape_hack_ && operation == CPU6502::BusOperation::ReadOpcode && via_.tape->has_tape() && !via_.tape->get_tape()->is_at_end()) {
if(address == tape_get_byte_address_ && paged_rom_ == rom_ && use_fast_tape_hack_ && operation == CPU::MOS6502::BusOperation::ReadOpcode && via_.tape->has_tape() && !via_.tape->get_tape()->is_at_end()) {
uint8_t next_byte = via_.tape->get_next_byte(!ram_[tape_speed_address_]);
set_value_of_register(CPU6502::A, next_byte);
set_value_of_register(CPU6502::Flags, next_byte ? 0 : CPU6502::Flag::Zero);
set_value_of_register(CPU::MOS6502::A, next_byte);
set_value_of_register(CPU::MOS6502::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero);
*value = 0x60; // i.e. RTS
}
} else {
@ -120,7 +120,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
}
}
if(typer_ && address == scan_keyboard_address_ && operation == CPU6502::BusOperation::ReadOpcode) {
if(typer_ && address == scan_keyboard_address_ && operation == CPU::MOS6502::BusOperation::ReadOpcode) {
// the Oric 1 misses any key pressed on the very first entry into the read keyboard routine, so don't
// do anything until at least the second, regardless of machine
if(!keyboard_read_count_) keyboard_read_count_++;
@ -199,7 +199,7 @@ std::shared_ptr<Outputs::Speaker> Machine::get_speaker() {
}
void Machine::run_for_cycles(int number_of_cycles) {
CPU6502::Processor<Machine>::run_for_cycles(number_of_cycles);
CPU::MOS6502::Processor<Machine>::run_for_cycles(number_of_cycles);
}
#pragma mark - The 6522

View File

@ -13,7 +13,7 @@
#include "../CRTMachine.hpp"
#include "../Typer.hpp"
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Processors/6502/6502.hpp"
#include "../../Components/6522/6522.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Storage/Tape/Parsers/Oric.hpp"
@ -57,7 +57,7 @@ enum ROM {
};
class Machine:
public CPU6502::Processor<Machine>,
public CPU::MOS6502::Processor<Machine>,
public CRTMachine::Machine,
public ConfigurationTarget::Machine,
public MOS::MOS6522IRQDelegate::Delegate,
@ -78,8 +78,8 @@ class Machine:
// to satisfy ConfigurationTarget::Machine
void configure_as_target(const StaticAnalyser::Target &target);
// to satisfy CPU6502::Processor
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
// to satisfy CPU::MOS6502::Processor
unsigned int perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
void synchronise();
// to satisfy CRTMachine::Machine

View File

@ -12,9 +12,9 @@
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
4B121F951E05E66800BFDA12 /* PCMPatchedTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F941E05E66800BFDA12 /* PCMPatchedTrackTests.mm */; };
4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */; };
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414591B58879D00E04248 /* CPU6502AllRAM.cpp */; };
4B14145B1B58879D00E04248 /* 6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* 6502.cpp */; };
4B14145D1B5887A600E04248 /* 6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* 6502.cpp */; };
4B14145E1B5887AA00E04248 /* 6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414591B58879D00E04248 /* 6502AllRAM.cpp */; };
4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */; };
4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414611B58888700E04248 /* KlausDormannTests.swift */; };
4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B1D08051E0F7A1100763741 /* TimeTests.mm */; };
@ -33,6 +33,7 @@
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */; };
4B2BFDB21DAEF5FF001A68B8 /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */; };
4B2C45421E3C3896002A2389 /* cartridge.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B2C45411E3C3896002A2389 /* cartridge.png */; };
4B2C455D1EC9442600FC74DD /* RegisterSizes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2C455B1EC9442600FC74DD /* RegisterSizes.cpp */; };
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; };
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; };
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B30512B1D989E2200B4FED8 /* Drive.cpp */; };
@ -443,10 +444,10 @@
4B121F971E060CF000BFDA12 /* PCMSegment.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMSegment.hpp; sourceTree = "<group>"; };
4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMSegmentEventSourceTests.mm; sourceTree = "<group>"; };
4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ClockSignal-Bridging-Header.h"; sourceTree = "<group>"; };
4B1414571B58879D00E04248 /* CPU6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPU6502.cpp; sourceTree = "<group>"; };
4B1414581B58879D00E04248 /* CPU6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPU6502.hpp; sourceTree = "<group>"; };
4B1414591B58879D00E04248 /* CPU6502AllRAM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPU6502AllRAM.cpp; sourceTree = "<group>"; };
4B14145A1B58879D00E04248 /* CPU6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPU6502AllRAM.hpp; sourceTree = "<group>"; };
4B1414571B58879D00E04248 /* 6502.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502.cpp; sourceTree = "<group>"; };
4B1414581B58879D00E04248 /* 6502.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502.hpp; sourceTree = "<group>"; };
4B1414591B58879D00E04248 /* 6502AllRAM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = 6502AllRAM.cpp; sourceTree = "<group>"; };
4B14145A1B58879D00E04248 /* 6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 6502AllRAM.hpp; sourceTree = "<group>"; };
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = "<group>"; };
4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = "<group>"; };
4B1D08051E0F7A1100763741 /* TimeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TimeTests.mm; sourceTree = "<group>"; };
@ -480,6 +481,8 @@
4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Oric/Video.cpp; sourceTree = "<group>"; };
4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = Oric/Video.hpp; sourceTree = "<group>"; };
4B2C45411E3C3896002A2389 /* cartridge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cartridge.png; sourceTree = "<group>"; };
4B2C455B1EC9442600FC74DD /* RegisterSizes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegisterSizes.cpp; sourceTree = "<group>"; };
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RegisterSizes.hpp; sourceTree = "<group>"; };
4B2E2D971C3A06EC00138695 /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = "<group>"; };
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Atari2600.hpp; sourceTree = "<group>"; };
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atari2600Inputs.h; sourceTree = "<group>"; };
@ -547,6 +550,7 @@
4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DiskController.hpp; sourceTree = "<group>"; };
4B77069B1EC904570053B588 /* Z80.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80.cpp; path = Z80/Z80.cpp; sourceTree = "<group>"; };
4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Z80.hpp; path = Z80/Z80.hpp; sourceTree = "<group>"; };
4B7706A01EC9398D0053B588 /* MicroOpScheduler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MicroOpScheduler.hpp; sourceTree = "<group>"; };
4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Electron/Video.cpp; sourceTree = "<group>"; };
4B7913CB1DFCD80E00175A82 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = Electron/Video.hpp; sourceTree = "<group>"; };
4B79E4411E3AF38600141F11 /* cassette.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = cassette.png; sourceTree = "<group>"; };
@ -1014,10 +1018,10 @@
4B1414561B58879D00E04248 /* 6502 */ = {
isa = PBXGroup;
children = (
4B1414571B58879D00E04248 /* CPU6502.cpp */,
4B1414581B58879D00E04248 /* CPU6502.hpp */,
4B1414591B58879D00E04248 /* CPU6502AllRAM.cpp */,
4B14145A1B58879D00E04248 /* CPU6502AllRAM.hpp */,
4B1414571B58879D00E04248 /* 6502.cpp */,
4B1414581B58879D00E04248 /* 6502.hpp */,
4B1414591B58879D00E04248 /* 6502AllRAM.cpp */,
4B14145A1B58879D00E04248 /* 6502AllRAM.hpp */,
);
path = 6502;
sourceTree = "<group>";
@ -1804,6 +1808,9 @@
children = (
4B1414561B58879D00E04248 /* 6502 */,
4B77069E1EC9045B0053B588 /* Z80 */,
4B7706A01EC9398D0053B588 /* MicroOpScheduler.hpp */,
4B2C455B1EC9442600FC74DD /* RegisterSizes.cpp */,
4B2C455C1EC9442600FC74DD /* RegisterSizes.hpp */,
);
name = Processors;
path = ../../Processors;
@ -2509,6 +2516,7 @@
4B9CCDA11DA279CA0098B625 /* Vic20OptionsPanel.swift in Sources */,
4B8805F01DCFC99C003085B1 /* Acorn.cpp in Sources */,
4B3051301D98ACC600B4FED8 /* Plus3.cpp in Sources */,
4B2C455D1EC9442600FC74DD /* RegisterSizes.cpp in Sources */,
4B30512D1D989E2200B4FED8 /* Drive.cpp in Sources */,
4BCA6CC81D9DD9F000C2D7B2 /* CommodoreROM.cpp in Sources */,
4BA22B071D8817CE0008C640 /* Disk.cpp in Sources */,
@ -2516,7 +2524,7 @@
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */,
4B4C83701D4F623200CD541F /* D64.cpp in Sources */,
4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */,
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */,
4B14145B1B58879D00E04248 /* 6502.cpp in Sources */,
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */,
4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */,
4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */,
@ -2536,8 +2544,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */,
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */,
4B14145E1B5887AA00E04248 /* 6502AllRAM.cpp in Sources */,
4B14145D1B5887A600E04248 /* 6502.cpp in Sources */,
4B1E85811D176468001EF87D /* 6532Tests.swift in Sources */,
4BC9E1EE1D23449A003FCEE4 /* 6502InterruptTests.swift in Sources */,
4BEF6AAA1D35CE9E00E73575 /* DigitalPhaseLockedLoopBridge.mm in Sources */,

View File

@ -8,15 +8,15 @@
#import "TestMachine.h"
#include <stdint.h>
#include "CPU6502AllRAM.hpp"
#include "6502AllRAM.hpp"
const uint8_t CSTestMachineJamOpcode = CPU6502::JamOpcode;
const uint8_t CSTestMachineJamOpcode = CPU::MOS6502::JamOpcode;
class MachineJamHandler: public CPU6502::AllRAMProcessor::JamHandler {
class MachineJamHandler: public CPU::MOS6502::AllRAMProcessor::JamHandler {
public:
MachineJamHandler(CSTestMachine *targetMachine) : _targetMachine(targetMachine) {}
void processor_did_jam(CPU6502::AllRAMProcessor::Processor *processor, uint16_t address) override {
void processor_did_jam(CPU::MOS6502::AllRAMProcessor::Processor *processor, uint16_t address) override {
[_targetMachine.jamHandler testMachine:_targetMachine didJamAtAddress:address];
}
@ -25,33 +25,33 @@ class MachineJamHandler: public CPU6502::AllRAMProcessor::JamHandler {
};
@implementation CSTestMachine {
CPU6502::AllRAMProcessor _processor;
CPU::MOS6502::AllRAMProcessor _processor;
MachineJamHandler *_cppJamHandler;
}
- (uint8_t)valueForAddress:(uint16_t)address {
uint8_t value;
_processor.perform_bus_operation(CPU6502::BusOperation::Read, address, &value);
_processor.perform_bus_operation(CPU::MOS6502::BusOperation::Read, address, &value);
return value;
}
- (void)setValue:(uint8_t)value forAddress:(uint16_t)address {
_processor.perform_bus_operation(CPU6502::BusOperation::Write, address, &value);
_processor.perform_bus_operation(CPU::MOS6502::BusOperation::Write, address, &value);
}
- (void)returnFromSubroutine {
_processor.return_from_subroutine();
}
- (CPU6502::Register)registerForRegister:(CSTestMachineRegister)reg {
- (CPU::MOS6502::Register)registerForRegister:(CSTestMachineRegister)reg {
switch (reg) {
case CSTestMachineRegisterProgramCounter: return CPU6502::Register::ProgramCounter;
case CSTestMachineRegisterLastOperationAddress: return CPU6502::Register::LastOperationAddress;
case CSTestMachineRegisterFlags: return CPU6502::Register::Flags;
case CSTestMachineRegisterA: return CPU6502::Register::A;
case CSTestMachineRegisterX: return CPU6502::Register::X;
case CSTestMachineRegisterY: return CPU6502::Register::Y;
case CSTestMachineRegisterStackPointer: return CPU6502::Register::S;
case CSTestMachineRegisterProgramCounter: return CPU::MOS6502::Register::ProgramCounter;
case CSTestMachineRegisterLastOperationAddress: return CPU::MOS6502::Register::LastOperationAddress;
case CSTestMachineRegisterFlags: return CPU::MOS6502::Register::Flags;
case CSTestMachineRegisterA: return CPU::MOS6502::Register::A;
case CSTestMachineRegisterX: return CPU::MOS6502::Register::X;
case CSTestMachineRegisterY: return CPU::MOS6502::Register::Y;
case CSTestMachineRegisterStackPointer: return CPU::MOS6502::Register::S;
default: break;
}
}

View File

@ -1,11 +1,11 @@
//
// CPU6502.cpp
// 6502.cpp
// CLK
//
// Created by Thomas Harte on 09/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#include "CPU6502.hpp"
#include "6502.hpp"
const uint8_t CPU6502::JamOpcode = 0xf2;
const uint8_t CPU::MOS6502::JamOpcode = 0xf2;

View File

@ -1,18 +1,22 @@
//
// CPU6502.hpp
// 6502.hpp
// CLK
//
// Created by Thomas Harte on 09/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#ifndef CPU6502_cpp
#define CPU6502_cpp
#ifndef MOS6502_cpp
#define MOS6502_cpp
#include <cstdio>
#include <cstdint>
namespace CPU6502 {
#include "../MicroOpScheduler.hpp"
#include "../RegisterSizes.hpp"
namespace CPU {
namespace MOS6502 {
/*
The list of registers that can be accessed via @c set_value_of_register and @c set_value_of_register.
@ -56,13 +60,58 @@ enum BusOperation {
/*!
Evaluates to `true` if the operation is a read; `false` if it is a write.
*/
#define isReadOperation(v) (v == CPU6502::BusOperation::Read || v == CPU6502::BusOperation::ReadOpcode)
#define isReadOperation(v) (v == CPU::MOS6502::BusOperation::Read || v == CPU::MOS6502::BusOperation::ReadOpcode)
/*!
An opcode that is guaranteed to cause the CPU to jam.
*/
extern const uint8_t JamOpcode;
/*
This emulation functions by decomposing instructions into micro programs, consisting of the micro operations
as per the enum below. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle
to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle).
*/
enum MicroOp {
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
OperationSetI,
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
CycleReadVectorLow, CycleReadVectorHigh,
CycleReadFromS, CycleReadFromPC,
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
CycleNoWritePush,
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute,
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead,
OperationMoveToNextProgram, OperationIncrementPC,
CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA,
CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand,
OperationIncrementOperand, OperationORA, OperationAND, OperationEOR,
OperationINS, OperationADC, OperationSBC, OperationLDA,
OperationLDX, OperationLDY, OperationLAX, OperationSTA,
OperationSTX, OperationSTY, OperationSAX, OperationSHA,
OperationSHX, OperationSHY, OperationSHS, OperationCMP,
OperationCPX, OperationCPY, OperationBIT, OperationASL,
OperationASO, OperationROL, OperationRLA, OperationLSR,
OperationLSE, OperationASR, OperationROR, OperationRRA,
OperationCLC, OperationCLI, OperationCLV, OperationCLD,
OperationSEC, OperationSEI, OperationSED, OperationINC,
OperationDEC, OperationINX, OperationDEX, OperationINY,
OperationDEY, OperationBPL, OperationBMI, OperationBVC,
OperationBVS, OperationBCC, OperationBCS, OperationBNE,
OperationBEQ, OperationTXA, OperationTYA, OperationTXS,
OperationTAY, OperationTAX, OperationTSX, OperationARR,
OperationSBX, OperationLXA, OperationANE, OperationANC,
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
OperationSetOperandFromFlags,
OperationSetFlagsFromA,
CycleScheduleJam
};
/*!
@abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism.
@ -74,7 +123,7 @@ extern const uint8_t JamOpcode;
that will cause call outs when the program counter reaches those addresses. @c return_from_subroutine can be used to exit from a
jammed state.
*/
template <class T> class Processor {
template <class T> class Processor: public MicroOpScheduler<MicroOp> {
public:
class JamHandler {
@ -84,60 +133,8 @@ template <class T> class Processor {
private:
/*
This emulation funcitons by decomposing instructions into micro programs, consisting of the micro operations
as per the enum below. Each micro op takes at most one cycle. By convention, those called CycleX take a cycle
to perform whereas those called OperationX occur for free (so, in effect, their cost is loaded onto the next cycle).
*/
enum MicroOp {
CycleFetchOperation, CycleFetchOperand, OperationDecodeOperation, CycleIncPCPushPCH,
CyclePushPCH, CyclePushPCL, CyclePushA, CyclePushOperand,
OperationSetI,
OperationBRKPickVector, OperationNMIPickVector, OperationRSTPickVector,
CycleReadVectorLow, CycleReadVectorHigh,
CycleReadFromS, CycleReadFromPC,
CyclePullOperand, CyclePullPCL, CyclePullPCH, CyclePullA,
CycleNoWritePush,
CycleReadAndIncrementPC, CycleIncrementPCAndReadStack, CycleIncrementPCReadPCHLoadPCL, CycleReadPCHLoadPCL,
CycleReadAddressHLoadAddressL, CycleReadPCLFromAddress, CycleReadPCHFromAddress, CycleLoadAddressAbsolute,
OperationLoadAddressZeroPage, CycleLoadAddessZeroX, CycleLoadAddessZeroY, CycleAddXToAddressLow,
CycleAddYToAddressLow, CycleAddXToAddressLowRead, OperationCorrectAddressHigh, CycleAddYToAddressLowRead,
OperationMoveToNextProgram, OperationIncrementPC,
CycleFetchOperandFromAddress, CycleWriteOperandToAddress, OperationCopyOperandFromA, OperationCopyOperandToA,
CycleIncrementPCFetchAddressLowFromOperand, CycleAddXToOperandFetchAddressLow, CycleIncrementOperandFetchAddressHigh, OperationDecrementOperand,
OperationIncrementOperand, OperationORA, OperationAND, OperationEOR,
OperationINS, OperationADC, OperationSBC, OperationLDA,
OperationLDX, OperationLDY, OperationLAX, OperationSTA,
OperationSTX, OperationSTY, OperationSAX, OperationSHA,
OperationSHX, OperationSHY, OperationSHS, OperationCMP,
OperationCPX, OperationCPY, OperationBIT, OperationASL,
OperationASO, OperationROL, OperationRLA, OperationLSR,
OperationLSE, OperationASR, OperationROR, OperationRRA,
OperationCLC, OperationCLI, OperationCLV, OperationCLD,
OperationSEC, OperationSEI, OperationSED, OperationINC,
OperationDEC, OperationINX, OperationDEX, OperationINY,
OperationDEY, OperationBPL, OperationBMI, OperationBVC,
OperationBVS, OperationBCC, OperationBCS, OperationBNE,
OperationBEQ, OperationTXA, OperationTYA, OperationTXS,
OperationTAY, OperationTAX, OperationTSX, OperationARR,
OperationSBX, OperationLXA, OperationANE, OperationANC,
OperationLAS, CycleAddSignedOperandToPC, OperationSetFlagsFromOperand, OperationSetOperandFromFlagsWithBRKSet,
OperationSetOperandFromFlags,
OperationSetFlagsFromA,
CycleScheduleJam
};
#define JAM {CycleFetchOperand, CycleScheduleJam, OperationMoveToNextProgram}
union RegisterPair {
uint16_t full;
struct {
uint8_t low, high;
} bytes;
};
/*
Storage for the 6502 registers; F is stored as individual flags.
*/
@ -151,16 +148,6 @@ template <class T> class Processor {
uint8_t operation_, operand_;
RegisterPair address_, next_address_;
/*
Up to four programs can be scheduled; each will be carried out in turn. This
storage maintains pointers to the scheduled list of programs.
Programs should be terminated by an OperationMoveToNextProgram, causing this
queue to take that step.
*/
const MicroOp *scheduled_programs_[4];
unsigned int schedule_programs_write_pointer_, schedule_programs_read_pointer_, schedule_program_program_counter_;
/*
Temporary storage allowing a common dispatch point for calling perform_bus_operation;
possibly deferring is no longer of value.
@ -169,18 +156,6 @@ template <class T> class Processor {
uint16_t bus_address_;
uint8_t *bus_value_;
/*!
Schedules a new program, adding it to the end of the queue. Programs should be
terminated with a OperationMoveToNextProgram. No attempt to copy the program
is made; a non-owning reference is kept.
@param program The program to schedule.
*/
inline void schedule_program(const MicroOp *program) {
scheduled_programs_[schedule_programs_write_pointer_] = program;
schedule_programs_write_pointer_ = (schedule_programs_write_pointer_+1)&3;
}
/*!
Gets the flags register.
@ -535,14 +510,11 @@ template <class T> class Processor {
protected:
Processor() :
schedule_programs_read_pointer_(0),
schedule_programs_write_pointer_(0),
is_jammed_(false),
jam_handler_(nullptr),
cycles_left_to_run_(0),
ready_line_is_enabled_(false),
ready_is_active_(false),
scheduled_programs_{nullptr, nullptr, nullptr, nullptr},
inverse_interrupt_flag_(0),
irq_request_history_(0),
s_(0),
@ -1176,8 +1148,8 @@ template <class T> class Processor {
*/
void return_from_subroutine() {
s_++;
static_cast<T *>(this)->perform_bus_operation(CPU6502::BusOperation::Read, 0x100 | s_, &pc_.bytes.low); s_++;
static_cast<T *>(this)->perform_bus_operation(CPU6502::BusOperation::Read, 0x100 | s_, &pc_.bytes.high);
static_cast<T *>(this)->perform_bus_operation(MOS6502::BusOperation::Read, 0x100 | s_, &pc_.bytes.low); s_++;
static_cast<T *>(this)->perform_bus_operation(MOS6502::BusOperation::Read, 0x100 | s_, &pc_.bytes.high);
pc_.full++;
if(is_jammed_) {
@ -1278,6 +1250,7 @@ template <class T> class Processor {
}
};
}
}
#endif /* CPU6502_cpp */
#endif /* MOS6502_cpp */

View File

@ -1,22 +1,22 @@
//
// CPU6502AllRAM.cpp
// 6502AllRAM.cpp
// CLK
//
// Created by Thomas Harte on 13/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#include "CPU6502AllRAM.hpp"
#include "6502AllRAM.hpp"
#include <algorithm>
#include <string.h>
using namespace CPU6502;
using namespace CPU::MOS6502;
AllRAMProcessor::AllRAMProcessor() : _timestamp(0) {
set_power_on(false);
}
int AllRAMProcessor::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value) {
int AllRAMProcessor::perform_bus_operation(MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
_timestamp++;
if(isReadOperation(operation)) {

View File

@ -1,17 +1,18 @@
//
// CPU6502AllRAM.hpp
// 6502AllRAM.hpp
// CLK
//
// Created by Thomas Harte on 13/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
#ifndef CPU6502AllRAM_cpp
#define CPU6502AllRAM_cpp
#ifndef MOS6502AllRAM_cpp
#define MOS6502AllRAM_cpp
#include "CPU6502.hpp"
#include "6502.hpp"
namespace CPU6502 {
namespace CPU {
namespace MOS6502 {
class AllRAMProcessor: public Processor<AllRAMProcessor> {
@ -19,7 +20,7 @@ class AllRAMProcessor: public Processor<AllRAMProcessor> {
AllRAMProcessor();
int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
int perform_bus_operation(MOS6502::BusOperation operation, uint16_t address, uint8_t *value);
void set_data_at_address(uint16_t startAddress, size_t length, const uint8_t *data);
uint32_t get_timestamp();
@ -29,6 +30,7 @@ class AllRAMProcessor: public Processor<AllRAMProcessor> {
uint32_t _timestamp;
};
}
}
#endif /* CPU6502AllRAM_cpp */
#endif /* MOS6502AllRAM_cpp */

View File

@ -0,0 +1,48 @@
//
// MicroOpScheduler.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef MicroOpScheduler_hpp
#define MicroOpScheduler_hpp
namespace CPU {
template <class T> class MicroOpScheduler {
public:
MicroOpScheduler() :
scheduled_programs_{nullptr, nullptr, nullptr, nullptr},
schedule_programs_write_pointer_(0),
schedule_programs_read_pointer_(0),
schedule_program_program_counter_(0) {}
protected:
/*
Up to four programs can be scheduled; each will be carried out in turn. This
storage maintains pointers to the scheduled list of programs.
Programs should be terminated by an OperationMoveToNextProgram, causing this
queue to take that step.
*/
const T *scheduled_programs_[4];
unsigned int schedule_programs_write_pointer_, schedule_programs_read_pointer_, schedule_program_program_counter_;
/*!
Schedules a new program, adding it to the end of the queue. Programs should be
terminated with a OperationMoveToNextProgram. No attempt to copy the program
is made; a non-owning reference is kept.
@param program The program to schedule.
*/
inline void schedule_program(const T *program) {
scheduled_programs_[schedule_programs_write_pointer_] = program;
schedule_programs_write_pointer_ = (schedule_programs_write_pointer_+1)&3;
}
};
}
#endif /* MicroOpScheduler_hpp */

View File

@ -0,0 +1,9 @@
//
// RegisterSizes.cpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#include "RegisterSizes.hpp"

View File

@ -0,0 +1,25 @@
//
// RegisterSizes.hpp
// Clock Signal
//
// Created by Thomas Harte on 14/05/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef RegisterSizes_hpp
#define RegisterSizes_hpp
#include <cstdint>
namespace CPU {
union RegisterPair {
uint16_t full;
struct {
uint8_t low, high;
} bytes;
};
}
#endif /* RegisterSizes_hpp */

View File

@ -54,11 +54,49 @@ enum Flag: uint8_t {
Subclasses will be given the task of performing bus operations, allowing them to provide whatever interface they like
between a Z80 and the rest of the system. @c BusOperation lists the types of bus operation that may be requested.
@c None is reserved for internal use. It will never be requested from a subclass. It is safe always to use the
isReadOperation macro to make a binary choice between reading and writing.
@c None is reserved for internal use. It will never be requested from a subclass.
*/
enum BusOperation {
Read, ReadOpcode, Write, Input, Output, Interrupt, BusRequest, None
ReadOpcode,
Read, Write,
Input, Output,
Interrupt,
BusRequest, BusAcknowledge,
None
};
/*!
@abstact An abstract base class for emulation of a 6502 processor via the curiously recurring template pattern/f-bounded polymorphism.
@discussion Subclasses should implement @c perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) in
order to provide the bus on which the 6502 operates and @c synchronise(), which is called upon completion of a continuous run
of cycles to allow a subclass to bring any on-demand activities up to date.
Additional functionality can be provided by the host machine by providing a jam handler and inserting jam opcodes where appropriate;
that will cause call outs when the program counter reaches those addresses. @c return_from_subroutine can be used to exit from a
jammed state.
*/
template <class T> class Processor {
private:
struct MicroOp {
enum {
} type;
void *source;
void *destination;
};
union RegisterPair {
uint16_t full;
struct {
uint8_t low, high;
} bytes;
};
RegisterPair bc_, de_, hl_, afDash_, bcDash_, hlDash_, ix_, iy_;
uint8_t a, i, r;
const MicroOp *scheduled_programs_[4];
unsigned int schedule_programs_write_pointer_, schedule_programs_read_pointer_, schedule_program_program_counter_;
};
}