1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-29 00:29:34 +00:00

As per my keenness for cleanliness improvements corresponding to my ever-increasing C++ ability: turned the Amstrad into something that a factory produces, allowing me completely to hide a bunch of implementation details.

This commit is contained in:
Thomas Harte 2017-07-31 22:32:04 -04:00
parent e3f677fa37
commit 3df13cddd4
3 changed files with 92 additions and 78 deletions

View File

@ -8,16 +8,88 @@
#include "AmstradCPC.hpp"
#include "../../Processors/Z80/Z80.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Components/6845/CRTC6845.hpp"
using namespace AmstradCPC;
Machine::Machine() :
struct CRTCBusHandler {
public:
CRTCBusHandler() : cycles_(0), was_enabled_(false), was_sync_(false) {}
inline void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
cycles_++;
bool is_sync = state.hsync || state.vsync;
if(state.display_enable != was_enabled_ || is_sync != was_sync_) {
if(was_sync_) {
crt_->output_sync((unsigned int)(cycles_ * 2) * 8);
} else {
uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1);
if(colour_pointer) *colour_pointer = was_enabled_ ? 0xff : 0x00;
crt_->output_level((unsigned int)(cycles_ * 2) * 8);
}
cycles_ = 0;
was_sync_ = is_sync;
was_enabled_ = state.display_enable;
}
}
std::shared_ptr<Outputs::CRT::CRT> crt_;
private:
int cycles_;
bool was_enabled_, was_sync_;
};
class ConcreteMachine:
public CPU::Z80::Processor<ConcreteMachine>,
public Machine {
public:
ConcreteMachine();
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle);
void flush();
void setup_output(float aspect_ratio);
void close_output();
std::shared_ptr<Outputs::CRT::CRT> get_crt();
std::shared_ptr<Outputs::Speaker> get_speaker();
void run_for(const Cycles cycles);
void configure_as_target(const StaticAnalyser::Target &target);
void set_rom(ROMType type, std::vector<uint8_t> data);
private:
CRTCBusHandler crtc_bus_handler_;
Motorola::CRTC::CRTC6845<CRTCBusHandler> crtc_;
HalfCycles clock_offset_;
HalfCycles crtc_counter_;
uint8_t ram_[65536];
std::vector<uint8_t> os_, basic_;
uint8_t *read_pointers_[4];
uint8_t *write_pointers_[4];
};
Machine *Machine::AmstradCPC() {
return new ConcreteMachine;
}
ConcreteMachine::ConcreteMachine() :
crtc_counter_(HalfCycles(4)), // This starts the CRTC exactly out of phase with the memory accesses
crtc_(crtc_bus_handler_) {
// primary clock is 4Mhz
set_clock_rate(4000000);
}
HalfCycles Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
HalfCycles ConcreteMachine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
// Amstrad CPC timing scheme: assert WAIT for three out of four cycles
clock_offset_ = (clock_offset_ + cycle.length) & HalfCycles(7);
set_wait_line(clock_offset_ >= HalfCycles(2));
@ -111,10 +183,10 @@ HalfCycles Machine::perform_machine_cycle(const CPU::Z80::PartialMachineCycle &c
return HalfCycles(0);
}
void Machine::flush() {
void ConcreteMachine::flush() {
}
void Machine::set_rom(ROMType type, std::vector<uint8_t> data) {
void ConcreteMachine::set_rom(ROMType type, std::vector<uint8_t> data) {
// Keep only the two ROMs that are currently of interest.
switch(type) {
case ROMType::OS464: os_ = data; break;
@ -123,7 +195,7 @@ void Machine::set_rom(ROMType type, std::vector<uint8_t> data) {
}
}
void Machine::setup_output(float aspect_ratio) {
void ConcreteMachine::setup_output(float aspect_ratio) {
crtc_bus_handler_.crt_.reset(new Outputs::CRT::CRT(1024, 8, Outputs::CRT::DisplayType::PAL50, 1));
crtc_bus_handler_.crt_->set_rgb_sampling_function(
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
@ -132,23 +204,23 @@ void Machine::setup_output(float aspect_ratio) {
"}");
}
void Machine::close_output() {
void ConcreteMachine::close_output() {
crtc_bus_handler_.crt_.reset();
}
std::shared_ptr<Outputs::CRT::CRT> Machine::get_crt() {
std::shared_ptr<Outputs::CRT::CRT> ConcreteMachine::get_crt() {
return crtc_bus_handler_.crt_;
}
std::shared_ptr<Outputs::Speaker> Machine::get_speaker() {
std::shared_ptr<Outputs::Speaker> ConcreteMachine::get_speaker() {
return nullptr;
}
void Machine::run_for(const Cycles cycles) {
CPU::Z80::Processor<Machine>::run_for(cycles);
void ConcreteMachine::run_for(const Cycles cycles) {
CPU::Z80::Processor<ConcreteMachine>::run_for(cycles);
}
void Machine::configure_as_target(const StaticAnalyser::Target &target) {
void ConcreteMachine::configure_as_target(const StaticAnalyser::Target &target) {
read_pointers_[0] = os_.data();
read_pointers_[1] = &ram_[16384];
read_pointers_[2] = &ram_[32768];

View File

@ -12,10 +12,6 @@
#include "../ConfigurationTarget.hpp"
#include "../CRTMachine.hpp"
#include "../../Processors/Z80/Z80.hpp"
#include "../../Components/AY38910/AY38910.hpp"
#include "../../Components/6845/CRTC6845.hpp"
namespace AmstradCPC {
enum ROMType: uint8_t {
@ -24,70 +20,12 @@ enum ROMType: uint8_t {
AMSDOS
};
struct CRTCBusHandler {
public:
CRTCBusHandler() : cycles_(0), was_enabled_(false), was_sync_(false) {
}
inline void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
cycles_++;
bool is_sync = state.hsync || state.vsync;
if(state.display_enable != was_enabled_ || is_sync != was_sync_) {
if(was_sync_) {
crt_->output_sync((unsigned int)(cycles_ * 2) * 8);
} else {
uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1);
if(colour_pointer) *colour_pointer = was_enabled_ ? 0xff : 0x00;
crt_->output_level((unsigned int)(cycles_ * 2) * 8);
}
cycles_ = 0;
was_sync_ = is_sync;
was_enabled_ = state.display_enable;
}
}
std::shared_ptr<Outputs::CRT::CRT> crt_;
private:
int cycles_;
bool was_enabled_, was_sync_;
};
class Machine:
public CPU::Z80::Processor<Machine>,
public CRTMachine::Machine,
public ConfigurationTarget::Machine {
public:
Machine();
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle);
void flush();
void setup_output(float aspect_ratio);
void close_output();
std::shared_ptr<Outputs::CRT::CRT> get_crt();
std::shared_ptr<Outputs::Speaker> get_speaker();
void run_for(const Cycles cycles);
void configure_as_target(const StaticAnalyser::Target &target);
void set_rom(ROMType type, std::vector<uint8_t> data);
private:
CRTCBusHandler crtc_bus_handler_;
Motorola::CRTC::CRTC6845<CRTCBusHandler> crtc_;
HalfCycles clock_offset_;
HalfCycles crtc_counter_;
uint8_t ram_[65536];
std::vector<uint8_t> os_, basic_;
uint8_t *read_pointers_[4];
uint8_t *write_pointers_[4];
static Machine *AmstradCPC();
virtual void set_rom(ROMType type, std::vector<uint8_t> data) = 0;
};
}

View File

@ -15,16 +15,20 @@
#import "NSBundle+DataResource.h"
@implementation CSAmstradCPC {
AmstradCPC::Machine _amstradCPC;
std::unique_ptr<AmstradCPC::Machine> _amstradCPC;
}
- (CRTMachine::Machine * const)machine {
return &_amstradCPC;
if(!_amstradCPC) {
_amstradCPC.reset(AmstradCPC::Machine::AmstradCPC());
}
return _amstradCPC.get();
}
- (instancetype)init {
self = [super init];
if(self) {
[self machine];
NSDictionary *roms = @{
@(AmstradCPC::ROMType::OS464) : @"os464",
@(AmstradCPC::ROMType::OS664) : @"os664",
@ -40,7 +44,7 @@
NSString *name = roms[key];
NSData *data = [self rom:name];
if(data) {
_amstradCPC.set_rom(type, data.stdVector8);
_amstradCPC->set_rom(type, data.stdVector8);
} else {
NSLog(@"Amstrad CPC ROM missing: %@", name);
}