1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-29 16:29:08 +00:00

Merge pull request #217 from TomHarte/CompiletimeOptions

Introduces compile-time selection of minor CPU core features and applies forceinline when appropriate
This commit is contained in:
Thomas Harte 2017-08-21 22:29:24 -04:00 committed by GitHub
commit 8afd83b91f
16 changed files with 169 additions and 199 deletions

View File

@ -9,10 +9,18 @@
#ifndef ForceInline_hpp
#define ForceInline_hpp
#ifdef DEBUG
#define forceinline
#else
#ifdef __GNUC__
#define forceinline __attribute__((always_inline)) inline
#elif _MSC_VER
#define forceinline __forceinline
#endif
#endif
#endif /* ForceInline_h */

View File

@ -22,6 +22,8 @@
#include "../../Storage/Tape/Tape.hpp"
#include "../../ClockReceiver/ForceInline.hpp"
namespace AmstradCPC {
/*!
@ -173,7 +175,7 @@ class CRTCBusHandler {
The CRTC entry function; takes the current bus state and determines what output
to produce based on the current palette and mode.
*/
inline void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
forceinline void perform_bus_cycle(const Motorola::CRTC::BusState &state) {
// The gate array waits 2µs to react to the CRTC's vsync signal, and then
// caps output at 4µs. Since the clock rate is 1Mhz, that's 2 and 4 cycles,
// respectively.
@ -691,7 +693,7 @@ class ConcreteMachine:
}
/// The entry point for performing a partial Z80 machine cycle.
inline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
forceinline HalfCycles 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);
z80_.set_wait_line(clock_offset_ >= HalfCycles(2));
@ -825,35 +827,35 @@ class ConcreteMachine:
}
/// A CRTMachine function; indicates that outputs should be created now.
void setup_output(float aspect_ratio) {
void setup_output(float aspect_ratio) override final {
crtc_bus_handler_.setup_output(aspect_ratio);
ay_.setup_output();
ay_.ay()->set_port_handler(&key_state_);
}
/// A CRTMachine function; indicates that outputs should be destroyed now.
void close_output() {
void close_output() override final {
crtc_bus_handler_.close_output();
ay_.close_output();
}
/// @returns the CRT in use.
std::shared_ptr<Outputs::CRT::CRT> get_crt() {
std::shared_ptr<Outputs::CRT::CRT> get_crt() override final {
return crtc_bus_handler_.get_crt();
}
/// @returns the speaker in use.
std::shared_ptr<Outputs::Speaker> get_speaker() {
std::shared_ptr<Outputs::Speaker> get_speaker() override final {
return ay_.get_speaker();
}
/// Wires virtual-dispatched CRTMachine run_for requests to the static Z80 method.
void run_for(const Cycles cycles) {
void run_for(const Cycles cycles) override final {
z80_.run_for(cycles);
}
/// The ConfigurationTarget entry point; should configure this meachine as described by @c target.
void configure_as_target(const StaticAnalyser::Target &target) {
void configure_as_target(const StaticAnalyser::Target &target) override final {
switch(target.amstradcpc.model) {
case StaticAnalyser::AmstradCPCModel::CPC464:
rom_model_ = ROMType::OS464;
@ -894,7 +896,7 @@ class ConcreteMachine:
insert_media(target.media);
}
bool insert_media(const StaticAnalyser::Media &media) {
bool insert_media(const StaticAnalyser::Media &media) override final {
// If there are any tapes supplied, use the first of them.
if(!media.tapes.empty()) {
tape_player_.set_tape(media.tapes.front());
@ -912,37 +914,37 @@ class ConcreteMachine:
}
// See header; provides the system ROMs.
void set_rom(ROMType type, std::vector<uint8_t> data) {
void set_rom(ROMType type, std::vector<uint8_t> data) override final {
roms_[(int)type] = data;
}
void set_component_is_sleeping(void *component, bool is_sleeping) {
void set_component_is_sleeping(void *component, bool is_sleeping) override final {
fdc_is_sleeping_ = fdc_.is_sleeping();
tape_player_is_sleeping_ = tape_player_.is_sleeping();
}
#pragma mark - Keyboard
void set_typer_for_string(const char *string) {
void set_typer_for_string(const char *string) override final {
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper());
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
}
HalfCycles get_typer_delay() {
HalfCycles get_typer_delay() override final {
return Cycles(4000000); // Wait 1 second before typing.
}
HalfCycles get_typer_frequency() {
HalfCycles get_typer_frequency() override final {
return Cycles(160000); // Type one character per frame.
}
// See header; sets a key as either pressed or released.
void set_key_state(uint16_t key, bool isPressed) {
void set_key_state(uint16_t key, bool isPressed) override final {
key_state_.set_is_pressed(isPressed, key >> 4, key & 7);
}
// See header; sets all keys to released.
void clear_all_keys() {
void clear_all_keys() override final {
key_state_.clear_all_keys();
}
@ -992,7 +994,7 @@ class ConcreteMachine:
}
}
CPU::Z80::Processor<ConcreteMachine> z80_;
CPU::Z80::Processor<ConcreteMachine, false> z80_;
CRTCBusHandler crtc_bus_handler_;
Motorola::CRTC::CRTC6845<CRTCBusHandler> crtc_;

View File

@ -184,7 +184,7 @@ template<class T> class Cartridge:
}
protected:
CPU::MOS6502::Processor<Cartridge<T>> m6502_;
CPU::MOS6502::Processor<Cartridge<T>, true> m6502_;
std::vector<uint8_t> rom_;
private:

View File

@ -152,7 +152,7 @@ class Machine:
void drive_via_did_set_data_density(void *driveVIA, int density);
private:
CPU::MOS6502::Processor<Machine> m6502_;
CPU::MOS6502::Processor<Machine, false> m6502_;
uint8_t ram_[0x800];
uint8_t rom_[0x4000];

View File

@ -14,6 +14,8 @@
#include "../../../Components/6560/6560.hpp"
#include "../../../Components/6522/6522.hpp"
#include "../../../ClockReceiver/ForceInline.hpp"
#include "../../../Storage/Tape/Parsers/Commodore.hpp"
#include "../SerialBus.hpp"
@ -253,7 +255,7 @@ class ConcreteMachine:
delete[] rom_;
}
void set_rom(ROMSlot slot, size_t length, const uint8_t *data) {
void set_rom(ROMSlot slot, size_t length, const uint8_t *data) override final {
uint8_t *target = nullptr;
size_t max_length = 0x2000;
switch(slot) {
@ -273,7 +275,7 @@ class ConcreteMachine:
}
}
void configure_as_target(const StaticAnalyser::Target &target) {
void configure_as_target(const StaticAnalyser::Target &target) override final {
if(target.loadingCommand.length()) {
set_typer_for_string(target.loadingCommand.c_str());
}
@ -304,7 +306,7 @@ class ConcreteMachine:
insert_media(target.media);
}
bool insert_media(const StaticAnalyser::Media &media) {
bool insert_media(const StaticAnalyser::Media &media) override final {
if(!media.tapes.empty()) {
tape_->set_tape(media.tapes.front());
}
@ -326,20 +328,20 @@ class ConcreteMachine:
return !media.tapes.empty() || (!media.disks.empty() && c1540_ != nullptr) || !media.cartridges.empty();
}
void set_key_state(uint16_t key, bool isPressed) {
void set_key_state(uint16_t key, bool isPressed) override final {
keyboard_via_->set_key_state(key, isPressed);
}
void clear_all_keys() {
void clear_all_keys() override final {
keyboard_via_->clear_all_keys();
}
void set_joystick_state(JoystickInput input, bool isPressed) {
void set_joystick_state(JoystickInput input, bool isPressed) override final {
user_port_via_->set_joystick_state(input, isPressed);
keyboard_via_->set_joystick_state(input, isPressed);
}
void set_memory_size(MemorySize size) {
void set_memory_size(MemorySize size) override final {
memset(processor_read_memory_map_, 0, sizeof(processor_read_memory_map_));
memset(processor_write_memory_map_, 0, sizeof(processor_write_memory_map_));
@ -373,7 +375,7 @@ class ConcreteMachine:
}
}
void set_region(Region region) {
void set_region(Region region) override final {
region_ = region;
switch(region) {
case PAL:
@ -393,12 +395,12 @@ class ConcreteMachine:
}
}
void set_use_fast_tape_hack(bool activate) {
void set_use_fast_tape_hack(bool activate) override final {
use_fast_tape_hack_ = activate;
}
// to satisfy CPU::MOS6502::Processor
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
forceinline Cycles 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));
@ -494,15 +496,15 @@ class ConcreteMachine:
return Cycles(1);
}
void flush() {
forceinline void flush() {
mos6560_->flush();
}
void run_for(const Cycles cycles) {
void run_for(const Cycles cycles) override final {
m6502_.run_for(cycles);
}
void setup_output(float aspect_ratio) {
void setup_output(float aspect_ratio) override final {
mos6560_.reset(new Vic6560());
mos6560_->get_speaker()->set_high_frequency_cut_off(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
set_region(region_);
@ -514,34 +516,34 @@ class ConcreteMachine:
mos6560_->colour_memory = colour_memory_;
}
void close_output() {
void close_output() override final {
mos6560_ = nullptr;
}
std::shared_ptr<Outputs::CRT::CRT> get_crt() {
std::shared_ptr<Outputs::CRT::CRT> get_crt() override final {
return mos6560_->get_crt();
}
std::shared_ptr<Outputs::Speaker> get_speaker() {
std::shared_ptr<Outputs::Speaker> get_speaker() override final {
return mos6560_->get_speaker();
}
void mos6522_did_change_interrupt_status(void *mos6522) {
void mos6522_did_change_interrupt_status(void *mos6522) override final {
m6502_.set_nmi_line(user_port_via_->get_interrupt_line());
m6502_.set_irq_line(keyboard_via_->get_interrupt_line());
}
void set_typer_for_string(const char *string) {
void set_typer_for_string(const char *string) override final {
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper());
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
}
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) {
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) override final {
keyboard_via_->set_control_line_input(KeyboardVIA::Port::A, KeyboardVIA::Line::One, !tape->get_input());
}
private:
CPU::MOS6502::Processor<ConcreteMachine> m6502_;
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
uint8_t character_rom_[0x1000];
uint8_t basic_rom_[0x2000];

View File

@ -11,6 +11,7 @@
#include "../../Processors/6502/6502.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../ClockReceiver/ForceInline.hpp"
#include "../Typer.hpp"
@ -44,7 +45,7 @@ class ConcreteMachine:
set_clock_rate(2000000);
}
void set_rom(ROMSlot slot, std::vector<uint8_t> data, bool is_writeable) {
void set_rom(ROMSlot slot, std::vector<uint8_t> data, bool is_writeable) override final {
uint8_t *target = nullptr;
switch(slot) {
case ROMSlotDFS: dfs_ = data; return;
@ -60,7 +61,7 @@ class ConcreteMachine:
memcpy(target, &data[0], std::min((size_t)16384, data.size()));
}
void set_key_state(uint16_t key, bool isPressed) {
void set_key_state(uint16_t key, bool isPressed) override final {
if(key == KeyBreak) {
m6502_.set_reset_line(isPressed);
} else {
@ -71,16 +72,16 @@ class ConcreteMachine:
}
}
void clear_all_keys() {
void clear_all_keys() override final {
memset(key_states_, 0, sizeof(key_states_));
if(is_holding_shift_) set_key_state(KeyShift, true);
}
void set_use_fast_tape_hack(bool activate) {
void set_use_fast_tape_hack(bool activate) override final {
use_fast_tape_hack_ = activate;
}
void configure_as_target(const StaticAnalyser::Target &target) {
void configure_as_target(const StaticAnalyser::Target &target) override final {
if(target.loadingCommand.length()) {
set_typer_for_string(target.loadingCommand.c_str());
}
@ -104,7 +105,7 @@ class ConcreteMachine:
insert_media(target.media);
}
bool insert_media(const StaticAnalyser::Media &media) {
bool insert_media(const StaticAnalyser::Media &media) override final {
if(!media.tapes.empty()) {
tape_.set_tape(media.tapes.front());
}
@ -122,7 +123,7 @@ class ConcreteMachine:
return !media.tapes.empty() || !media.disks.empty() || !media.cartridges.empty();
}
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
forceinline Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
unsigned int cycles = 1;
if(address < 0x8000) {
@ -342,13 +343,13 @@ class ConcreteMachine:
return Cycles((int)cycles);
}
void flush() {
forceinline void flush() {
update_display();
update_audio();
speaker_->flush();
}
void setup_output(float aspect_ratio) {
void setup_output(float aspect_ratio) override final {
video_output_.reset(new VideoOutput(ram_));
// The maximum output frequency is 62500Hz and all other permitted output frequencies are integral divisions of that;
@ -358,36 +359,36 @@ class ConcreteMachine:
speaker_->set_input_rate(2000000 / Speaker::clock_rate_divider);
}
void close_output() {
void close_output() override final {
video_output_.reset();
}
std::shared_ptr<Outputs::CRT::CRT> get_crt() {
std::shared_ptr<Outputs::CRT::CRT> get_crt() override final {
return video_output_->get_crt();
}
std::shared_ptr<Outputs::Speaker> get_speaker() {
std::shared_ptr<Outputs::Speaker> get_speaker() override final {
return speaker_;
}
virtual void run_for(const Cycles cycles) {
void run_for(const Cycles cycles) override final {
m6502_.run_for(cycles);
}
void tape_did_change_interrupt_status(Tape *tape) {
void tape_did_change_interrupt_status(Tape *tape) override final {
interrupt_status_ = (interrupt_status_ & ~(Interrupt::TransmitDataEmpty | Interrupt::ReceiveDataFull | Interrupt::HighToneDetect)) | tape_.get_interrupt_status();
evaluate_interrupts();
}
HalfCycles get_typer_delay() {
HalfCycles get_typer_delay() override final {
return m6502_.get_is_resetting() ? Cycles(625*25*128) : Cycles(0); // wait one second if resetting
}
HalfCycles get_typer_frequency() {
HalfCycles get_typer_frequency() override final {
return Cycles(625*128*2); // accept a new character every two frames
}
void set_typer_for_string(const char *string) {
void set_typer_for_string(const char *string) override final {
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper());
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
}
@ -430,7 +431,7 @@ class ConcreteMachine:
m6502_.set_irq_line(interrupt_status_ & 1);
}
CPU::MOS6502::Processor<ConcreteMachine> m6502_;
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
// Things that directly constitute the memory map.
uint8_t roms_[16][16384];

View File

@ -22,6 +22,8 @@
#include "../../Storage/Tape/Tape.hpp"
#include "../../Storage/Tape/Parsers/Oric.hpp"
#include "../../ClockReceiver/ForceInline.hpp"
#include <cstdint>
#include <memory>
#include <vector>
@ -54,7 +56,7 @@ class ConcreteMachine:
Memory::Fuzz(ram_, sizeof(ram_));
}
void set_rom(ROM rom, const std::vector<uint8_t> &data) {
void set_rom(ROM rom, const std::vector<uint8_t> &data) override final {
switch(rom) {
case BASIC11: basic11_rom_ = std::move(data); break;
case BASIC10: basic10_rom_ = std::move(data); break;
@ -66,7 +68,7 @@ class ConcreteMachine:
}
}
void set_key_state(uint16_t key, bool isPressed) {
void set_key_state(uint16_t key, bool isPressed) override final {
if(key == KeyNMI) {
m6502_.set_nmi_line(isPressed);
} else {
@ -77,20 +79,20 @@ class ConcreteMachine:
}
}
void clear_all_keys() {
void clear_all_keys() override final {
memset(keyboard_->rows, 0, sizeof(keyboard_->rows));
}
void set_use_fast_tape_hack(bool activate) {
void set_use_fast_tape_hack(bool activate) override final {
use_fast_tape_hack_ = activate;
}
void set_output_device(Outputs::CRT::OutputDevice output_device) {
void set_output_device(Outputs::CRT::OutputDevice output_device) override final {
video_output_->set_output_device(output_device);
}
// to satisfy ConfigurationTarget::Machine
void configure_as_target(const StaticAnalyser::Target &target) {
void configure_as_target(const StaticAnalyser::Target &target) override final {
if(target.oric.has_microdisc) {
microdisc_is_enabled_ = true;
microdisc_did_change_paging_flags(&microdisc_);
@ -120,7 +122,7 @@ class ConcreteMachine:
insert_media(target.media);
}
bool insert_media(const StaticAnalyser::Media &media) {
bool insert_media(const StaticAnalyser::Media &media) override final {
if(media.tapes.size()) {
via_.tape->set_tape(media.tapes.front());
}
@ -135,7 +137,7 @@ class ConcreteMachine:
}
// to satisfy CPU::MOS6502::BusHandler
Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
forceinline Cycles 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];
@ -193,55 +195,55 @@ class ConcreteMachine:
return Cycles(1);
}
void flush() {
forceinline void flush() {
update_video();
via_.flush();
}
// to satisfy CRTMachine::Machine
void setup_output(float aspect_ratio) {
void setup_output(float aspect_ratio) override final {
via_.ay8910.reset(new GI::AY38910::AY38910());
via_.ay8910->set_clock_rate(1000000);
video_output_.reset(new VideoOutput(ram_));
if(!colour_rom_.empty()) video_output_->set_colour_rom(colour_rom_);
}
void close_output() {
void close_output() override final {
video_output_.reset();
via_.ay8910.reset();
}
std::shared_ptr<Outputs::CRT::CRT> get_crt() {
std::shared_ptr<Outputs::CRT::CRT> get_crt() override final {
return video_output_->get_crt();
}
std::shared_ptr<Outputs::Speaker> get_speaker() {
std::shared_ptr<Outputs::Speaker> get_speaker() override final {
return via_.ay8910;
}
void run_for(const Cycles cycles) {
void run_for(const Cycles cycles) override final {
m6502_.run_for(cycles);
}
// to satisfy MOS::MOS6522IRQDelegate::Delegate
void mos6522_did_change_interrupt_status(void *mos6522) {
void mos6522_did_change_interrupt_status(void *mos6522) override final {
set_interrupt_line();
}
// to satisfy Storage::Tape::BinaryTapePlayer::Delegate
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player) {
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player) override final {
// set CB1
via_.set_control_line_input(VIA::Port::B, VIA::Line::One, !tape_player->get_input());
}
// for Utility::TypeRecipient::Delegate
void set_typer_for_string(const char *string) {
void set_typer_for_string(const char *string) override final {
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper);
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
}
// for Microdisc::Delegate
void microdisc_did_change_paging_flags(class Microdisc *microdisc) {
void microdisc_did_change_paging_flags(class Microdisc *microdisc) override final {
int flags = microdisc->get_paging_flags();
if(!(flags&Microdisc::PagingFlags::BASICDisable)) {
ram_top_ = 0xbfff;
@ -256,12 +258,12 @@ class ConcreteMachine:
}
}
void wd1770_did_change_output(WD::WD1770 *wd1770) {
void wd1770_did_change_output(WD::WD1770 *wd1770) override final {
set_interrupt_line();
}
private:
CPU::MOS6502::Processor<ConcreteMachine> m6502_;
CPU::MOS6502::Processor<ConcreteMachine, false> m6502_;
// RAM and ROM
std::vector<uint8_t> basic11_rom_, basic10_rom_, microdisc_rom_, colour_rom_;
@ -292,7 +294,7 @@ class ConcreteMachine:
public:
TapePlayer() : Storage::Tape::BinaryTapePlayer(1000000) {}
uint8_t get_next_byte(bool fast) {
inline uint8_t get_next_byte(bool fast) {
return (uint8_t)parser_.get_next_byte(get_tape(), fast);
}

View File

@ -12,6 +12,8 @@
#include "../../Storage/Tape/Tape.hpp"
#include "../../Storage/Tape/Parsers/ZX8081.hpp"
#include "../../ClockReceiver/ForceInline.hpp"
#include "../MemoryFuzzer.hpp"
#include "../Typer.hpp"
@ -45,7 +47,7 @@ class ConcreteMachine:
clear_all_keys();
}
HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
HalfCycles previous_counter = horizontal_counter_;
horizontal_counter_ += cycle.length;
@ -204,31 +206,31 @@ class ConcreteMachine:
return HalfCycles(0);
}
void flush() {
forceinline void flush() {
video_->flush();
}
void setup_output(float aspect_ratio) {
void setup_output(float aspect_ratio) override final {
video_.reset(new Video);
}
void close_output() {
void close_output() override final {
video_.reset();
}
std::shared_ptr<Outputs::CRT::CRT> get_crt() {
std::shared_ptr<Outputs::CRT::CRT> get_crt() override final {
return video_->get_crt();
}
std::shared_ptr<Outputs::Speaker> get_speaker() {
std::shared_ptr<Outputs::Speaker> get_speaker() override final {
return nullptr;
}
void run_for(const Cycles cycles) {
void run_for(const Cycles cycles) override final {
z80_.run_for(cycles);
}
void configure_as_target(const StaticAnalyser::Target &target) {
void configure_as_target(const StaticAnalyser::Target &target) override final {
is_zx81_ = target.zx8081.isZX81;
if(is_zx81_) {
rom_ = zx81_rom_;
@ -275,7 +277,7 @@ class ConcreteMachine:
insert_media(target.media);
}
bool insert_media(const StaticAnalyser::Media &media) {
bool insert_media(const StaticAnalyser::Media &media) override final {
if(!media.tapes.empty()) {
tape_player_.set_tape(media.tapes.front());
}
@ -283,12 +285,12 @@ class ConcreteMachine:
return !media.tapes.empty();
}
void set_typer_for_string(const char *string) {
void set_typer_for_string(const char *string) override final {
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper(is_zx81_));
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
}
void set_rom(ROMType type, std::vector<uint8_t> data) {
void set_rom(ROMType type, std::vector<uint8_t> data) override final {
switch(type) {
case ZX80: zx80_rom_ = data; break;
case ZX81: zx81_rom_ = data; break;
@ -297,40 +299,40 @@ class ConcreteMachine:
#pragma mark - Keyboard
void set_key_state(uint16_t key, bool isPressed) {
void set_key_state(uint16_t key, bool isPressed) override final {
if(isPressed)
key_states_[key >> 8] &= (uint8_t)(~key);
else
key_states_[key >> 8] |= (uint8_t)key;
}
void clear_all_keys() {
void clear_all_keys() override final {
memset(key_states_, 0xff, 8);
}
#pragma mark - Tape control
void set_use_fast_tape_hack(bool activate) {
void set_use_fast_tape_hack(bool activate) override final {
use_fast_tape_hack_ = activate;
}
void set_use_automatic_tape_motor_control(bool enabled) {
void set_use_automatic_tape_motor_control(bool enabled) override final {
use_automatic_tape_motor_control_ = enabled;
if(!enabled) {
tape_player_.set_motor_control(false);
}
}
void set_tape_is_playing(bool is_playing) {
void set_tape_is_playing(bool is_playing) override final {
tape_player_.set_motor_control(is_playing);
}
#pragma mark - Typer timing
HalfCycles get_typer_delay() { return Cycles(7000000); }
HalfCycles get_typer_frequency() { return Cycles(390000); }
HalfCycles get_typer_delay() override final { return Cycles(7000000); }
HalfCycles get_typer_frequency() override final { return Cycles(390000); }
private:
CPU::Z80::Processor<ConcreteMachine> z80_;
CPU::Z80::Processor<ConcreteMachine, false> z80_;
std::shared_ptr<Video> video_;
std::vector<uint8_t> zx81_rom_, zx80_rom_;
@ -367,17 +369,17 @@ class ConcreteMachine:
#pragma mark - Video
void set_vsync(bool sync) {
inline void set_vsync(bool sync) {
vsync_ = sync;
update_sync();
}
void set_hsync(bool sync) {
inline void set_hsync(bool sync) {
hsync_ = sync;
update_sync();
}
void update_sync() {
inline void update_sync() {
video_->set_sync(vsync_ || hsync_);
}
};

View File

@ -31,8 +31,6 @@ extern const uint8_t CSTestMachine6502JamOpcode;
- (void)setValue:(uint16_t)value forRegister:(CSTestMachine6502Register)reg;
- (uint16_t)valueForRegister:(CSTestMachine6502Register)reg;
- (void)returnFromSubroutine;
@property (nonatomic, readonly) BOOL isJammed;
@property (nonatomic, readonly) uint32_t timestamp;
@property (nonatomic, assign) BOOL irqLine;

View File

@ -97,10 +97,6 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
#pragma mark - Actions
- (void)returnFromSubroutine {
_processor->return_from_subroutine();
}
- (void)runForNumberOfCycles:(int)cycles {
_processor->run_for(Cycles(cycles));
}

View File

@ -18,33 +18,29 @@
#pragma mark - Prebuilt tracks
- (std::shared_ptr<Storage::Disk::Track>)togglingTrack
{
- (std::shared_ptr<Storage::Disk::Track>)togglingTrack {
Storage::Disk::PCMSegment segment;
segment.data = { 0xff, 0xff, 0xff, 0xff };
segment.number_of_bits = 32;
return std::shared_ptr<Storage::Disk::Track>(new Storage::Disk::PCMTrack(segment));
}
- (std::shared_ptr<Storage::Disk::Track>)patchableTogglingTrack
{
- (std::shared_ptr<Storage::Disk::Track>)patchableTogglingTrack {
std::shared_ptr<Storage::Disk::Track> track = self.togglingTrack;
return std::shared_ptr<Storage::Disk::Track>(new Storage::Disk::PCMPatchedTrack(track));
}
- (std::shared_ptr<Storage::Disk::Track>)fourSegmentPatchedTrack
{
- (std::shared_ptr<Storage::Disk::Track>)fourSegmentPatchedTrack {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.patchableTogglingTrack;
Storage::Disk::PCMPatchedTrack *patchable = static_cast<Storage::Disk::PCMPatchedTrack *>(patchableTrack.get());
for(int c = 0; c < 4; c++)
{
for(int c = 0; c < 4; c++) {
Storage::Disk::PCMSegment segment;
segment.data = {0xff};
segment.number_of_bits = 8;
segment.length_of_a_bit.length = 1;
segment.length_of_a_bit.clock_rate = 32;
patchable->add_segment(Storage::Time(c, 4), segment);
patchable->add_segment(Storage::Time(c, 4), segment, false);
}
return patchableTrack;
@ -52,41 +48,34 @@
#pragma mark -
- (std::vector<Storage::Disk::Track::Event>)eventsFromTrack:(std::shared_ptr<Storage::Disk::Track>)track
{
- (std::vector<Storage::Disk::Track::Event>)eventsFromTrack:(std::shared_ptr<Storage::Disk::Track>)track {
std::vector<Storage::Disk::Track::Event> events;
while(1)
{
while(1) {
events.push_back(track->get_next_event());
if(events.back().type == Storage::Disk::Track::Event::IndexHole) break;
}
return events;
}
- (Storage::Time)timeForEvents:(const std::vector<Storage::Disk::Track::Event> &)events
{
- (Storage::Time)timeForEvents:(const std::vector<Storage::Disk::Track::Event> &)events {
Storage::Time result(0);
for(auto event : events)
{
for(auto event : events) {
result += event.length;
}
return result;
}
- (void)patchTrack:(std::shared_ptr<Storage::Disk::Track>)track withSegment:(Storage::Disk::PCMSegment)segment atTime:(Storage::Time)time
{
- (void)patchTrack:(std::shared_ptr<Storage::Disk::Track>)track withSegment:(Storage::Disk::PCMSegment)segment atTime:(Storage::Time)time {
Storage::Disk::PCMPatchedTrack *patchable = static_cast<Storage::Disk::PCMPatchedTrack *>(track.get());
patchable->add_segment(time, segment);
patchable->add_segment(time, segment, false);
}
#pragma mark - Repeating Asserts
- (void)assertOneThirtyTwosForTrack:(std::shared_ptr<Storage::Disk::Track>)track
{
- (void)assertOneThirtyTwosForTrack:(std::shared_ptr<Storage::Disk::Track>)track {
// Confirm that there are now flux transitions (just the first five will do)
// located 1/32nd of a rotation apart.
for(int c = 0; c < 5; c++)
{
for(int c = 0; c < 5; c++) {
Storage::Disk::Track::Event event = track->get_next_event();
XCTAssert(
event.length == (c ? Storage::Time(1, 32) : Storage::Time(1, 64)),
@ -94,8 +83,7 @@
}
}
- (void)assertEvents:(const std::vector<Storage::Disk::Track::Event> &)events hasEntries:(size_t)numberOfEntries withEntry:(size_t)entry ofLength:(Storage::Time)time
{
- (void)assertEvents:(const std::vector<Storage::Disk::Track::Event> &)events hasEntries:(size_t)numberOfEntries withEntry:(size_t)entry ofLength:(Storage::Time)time {
XCTAssert(events.size() == numberOfEntries, @"Should be %zu total events", numberOfEntries);
XCTAssert(events[entry].length == time, @"Event %zu should have been %d/%d long, was %d/%d", entry, time.length, time.clock_rate, events[entry].length.length, events[entry].length.clock_rate);
@ -106,20 +94,17 @@
#pragma mark - Unpatched tracks
- (void)testUnpatchedRawTrack
{
- (void)testUnpatchedRawTrack {
[self assertOneThirtyTwosForTrack:self.togglingTrack];
}
- (void)testUnpatchedTrack
{
- (void)testUnpatchedTrack {
[self assertOneThirtyTwosForTrack:self.patchableTogglingTrack];
}
#pragma mark - Insertions affecting one existing segment
- (void)testSingleSplice
{
- (void)testSingleSplice {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.patchableTogglingTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(1, 32), 1, {0xff}) atTime:Storage::Time(3, 128)];
@ -133,16 +118,14 @@
XCTAssert(total_length == Storage::Time(1), @"Total track length should still be 1");
}
- (void)testLeftReplace
{
- (void)testLeftReplace {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.patchableTogglingTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(1, 16), 8, {0x00}) atTime:Storage::Time(0)];
[self assertEvents:[self eventsFromTrack:patchableTrack] hasEntries:17 withEntry:0 ofLength:Storage::Time(33, 64)];
}
- (void)testRightReplace
{
- (void)testRightReplace {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.patchableTogglingTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(1, 16), 8, {0x00}) atTime:Storage::Time(1, 2)];
@ -151,30 +134,26 @@
#pragma mark - Insertions affecting three existing segments
- (void)testMultiSegmentTrack
{
- (void)testMultiSegmentTrack {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.fourSegmentPatchedTrack;
[self assertEvents:[self eventsFromTrack:patchableTrack] hasEntries:33 withEntry:4 ofLength:Storage::Time(1, 32)];
}
- (void)testMultiTrimBothSideReplace
{
- (void)testMultiTrimBothSideReplace {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.fourSegmentPatchedTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(1, 16), 8, {0x00}) atTime:Storage::Time(1, 8)];
[self assertEvents:[self eventsFromTrack:patchableTrack] hasEntries:17 withEntry:4 ofLength:Storage::Time(17, 32)];
}
- (void)testMultiTrimRightReplace
{
- (void)testMultiTrimRightReplace {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.fourSegmentPatchedTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(3, 8), 1, {0x00}) atTime:Storage::Time(1, 8)];
[self assertEvents:[self eventsFromTrack:patchableTrack] hasEntries:21 withEntry:4 ofLength:Storage::Time(13, 32)];
}
- (void)testMultiTrimLeftReplace
{
- (void)testMultiTrimLeftReplace {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.fourSegmentPatchedTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(3, 8), 1, {0x00}) atTime:Storage::Time(1, 4)];
@ -183,24 +162,21 @@
#pragma mark - Insertions affecting two existing segments
- (void)testTwoSegmentOverlap
{
- (void)testTwoSegmentOverlap {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.fourSegmentPatchedTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(1, 32), 8, {0x00}) atTime:Storage::Time(1, 8)];
[self assertEvents:[self eventsFromTrack:patchableTrack] hasEntries:25 withEntry:4 ofLength:Storage::Time(9, 32)];
}
- (void)testTwoSegmentRightReplace
{
- (void)testTwoSegmentRightReplace {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.fourSegmentPatchedTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(3, 8), 1, {0x00}) atTime:Storage::Time(1, 8)];
[self assertEvents:[self eventsFromTrack:patchableTrack] hasEntries:21 withEntry:4 ofLength:Storage::Time(13, 32)];
}
- (void)testTwoSegmentLeftReplace
{
- (void)testTwoSegmentLeftReplace {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.fourSegmentPatchedTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(3, 8), 1, {0x00}) atTime:Storage::Time(0)];
@ -209,8 +185,7 @@
#pragma mark - Wrapping segment
- (void)testWrappingSegment
{
- (void)testWrappingSegment {
std::shared_ptr<Storage::Disk::Track> patchableTrack = self.patchableTogglingTrack;
[self patchTrack:patchableTrack withSegment:Storage::Disk::PCMSegment(Storage::Time(5, 2), 1, {0x00}) atTime:Storage::Time(0)];

View File

@ -9,6 +9,7 @@
#ifndef MOS6502_cpp
#define MOS6502_cpp
#include <cassert>
#include <cstdio>
#include <cstdint>
@ -131,12 +132,8 @@ class BusHandler {
@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 flush(), 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: public ProcessorBase {
template <class T, bool uses_ready_line> class Processor: public ProcessorBase {
private:
T &bus_handler_;
const MicroOp *scheduled_program_counter_;
@ -355,11 +352,11 @@ template <class T> class Processor: public ProcessorBase {
while(number_of_cycles > Cycles(0)) {
while (ready_is_active_ && number_of_cycles > Cycles(0)) {
while(uses_ready_line && ready_is_active_ && number_of_cycles > Cycles(0)) {
number_of_cycles -= bus_handler_.perform_bus_operation(BusOperation::Ready, busAddress, busValue);
}
if(!ready_is_active_) {
if(!uses_ready_line || !ready_is_active_) {
if(nextBusOperation != BusOperation::None) {
bus_access();
}
@ -822,7 +819,7 @@ template <class T> class Processor: public ProcessorBase {
continue;
}
if(ready_line_is_enabled_ && isReadOperation(nextBusOperation)) {
if(uses_ready_line && ready_line_is_enabled_ && isReadOperation(nextBusOperation)) {
ready_is_active_ = true;
break;
}
@ -883,27 +880,13 @@ template <class T> class Processor: public ProcessorBase {
}
}
/*!
Interrupts current execution flow to perform an RTS and, if the 6502 is currently jammed,
to unjam it.
*/
void return_from_subroutine() {
s_++;
bus_handler_.perform_bus_operation(MOS6502::BusOperation::Read, 0x100 | s_, &pc_.bytes.low); s_++;
bus_handler_.perform_bus_operation(MOS6502::BusOperation::Read, 0x100 | s_, &pc_.bytes.high);
pc_.full++;
if(is_jammed_) {
scheduled_program_counter_ = nullptr;
}
}
/*!
Sets the current level of the RDY line.
@param active @c true if the line is logically active; @c false otherwise.
*/
inline void set_ready_line(bool active) {
assert(uses_ready_line);
if(active) {
ready_line_is_enabled_ = true;
} else {

View File

@ -14,10 +14,11 @@ using namespace CPU::MOS6502;
namespace {
class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<ConcreteAllRAMProcessor> {
class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
public:
ConcreteAllRAMProcessor() {
set_power_on(false);
ConcreteAllRAMProcessor() :
mos6502_(*this) {
mos6502_.set_power_on(false);
}
inline Cycles perform_bus_operation(BusOperation operation, uint16_t address, uint8_t *value) {
@ -37,32 +38,31 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public Processor<Concrete
}
void run_for(const Cycles cycles) {
Processor<ConcreteAllRAMProcessor>::run_for(cycles);
mos6502_.run_for(cycles);
}
bool is_jammed() {
return Processor<ConcreteAllRAMProcessor>::is_jammed();
return mos6502_.is_jammed();
}
void set_irq_line(bool value) {
Processor<ConcreteAllRAMProcessor>::set_irq_line(value);
mos6502_.set_irq_line(value);
}
void set_nmi_line(bool value) {
Processor<ConcreteAllRAMProcessor>::set_nmi_line(value);
}
void return_from_subroutine() {
Processor<ConcreteAllRAMProcessor>::return_from_subroutine();
mos6502_.set_nmi_line(value);
}
uint16_t get_value_of_register(Register r) {
return Processor<ConcreteAllRAMProcessor>::get_value_of_register(r);
return mos6502_.get_value_of_register(r);
}
void set_value_of_register(Register r, uint16_t value) {
Processor<ConcreteAllRAMProcessor>::set_value_of_register(r, value);
mos6502_.set_value_of_register(r, value);
}
private:
CPU::MOS6502::Processor<ConcreteAllRAMProcessor, false> mos6502_;
};
}

View File

@ -26,7 +26,6 @@ class AllRAMProcessor:
virtual bool is_jammed() = 0;
virtual void set_irq_line(bool value) = 0;
virtual void set_nmi_line(bool value) = 0;
virtual void return_from_subroutine() = 0;
virtual uint16_t get_value_of_register(Register r) = 0;
virtual void set_value_of_register(Register r, uint16_t value) = 0;

View File

@ -9,6 +9,7 @@
#ifndef Z80_hpp
#define Z80_hpp
#include <cassert>
#include <cstdint>
#include <cstring>
#include <cstdio>
@ -180,7 +181,7 @@ class BusHandler {
order to provide the bus on which the Z80 operates and @c flush(), which is called upon completion of a continuous run
of cycles to allow a subclass to bring any on-demand activities up to date.
*/
template <class T> class Processor {
template <class T, bool uses_bus_request> class Processor {
private:
T &bus_handler_;
@ -910,7 +911,7 @@ template <class T> class Processor {
while(1) {
do_bus_acknowledge:
while(bus_request_line_) {
while(uses_bus_request && bus_request_line_) {
static PartialMachineCycle bus_acknowledge_cycle = {PartialMachineCycle::BusAcknowledge, HalfCycles(2), nullptr, nullptr, false};
number_of_cycles_ -= bus_handler_.perform_machine_cycle(bus_acknowledge_cycle) + HalfCycles(1);
if(!number_of_cycles_) {
@ -946,7 +947,7 @@ template <class T> class Processor {
number_of_cycles_ -= operation->machine_cycle.length;
last_request_status_ = request_status_;
number_of_cycles_ -= bus_handler_.perform_machine_cycle(operation->machine_cycle);
if(bus_request_line_) goto do_bus_acknowledge;
if(uses_bus_request && bus_request_line_) goto do_bus_acknowledge;
break;
case MicroOp::MoveToNextProgram:
advance_operation();
@ -1953,6 +1954,7 @@ template <class T> class Processor {
Sets the logical value of the bus request line.
*/
void set_bus_request_line(bool value) {
assert(uses_bus_request);
bus_request_line_ = value;
}

View File

@ -96,7 +96,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
}
private:
CPU::Z80::Processor<ConcreteAllRAMProcessor> z80_;
CPU::Z80::Processor<ConcreteAllRAMProcessor, false> z80_;
};
}