mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-08 03:54:27 +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:
commit
8afd83b91f
@ -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 */
|
||||
|
@ -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_;
|
||||
|
@ -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:
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -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];
|
||||
|
@ -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(µdisc_);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_);
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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)];
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ class ConcreteAllRAMProcessor: public AllRAMProcessor, public BusHandler {
|
||||
}
|
||||
|
||||
private:
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor> z80_;
|
||||
CPU::Z80::Processor<ConcreteAllRAMProcessor, false> z80_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user