diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index bf90d3fd1..db2fd9df9 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -8,287 +8,372 @@ #include "Oric.hpp" +#include "Video.hpp" +#include "Microdisc.hpp" #include "CharacterMapper.hpp" -#include "../MemoryFuzzer.hpp" +#include "../MemoryFuzzer.hpp" +#include "../Typer.hpp" + +#include "../../Processors/6502/6502.hpp" +#include "../../Components/6522/6522.hpp" +#include "../../Components/AY38910/AY38910.hpp" + +#include "../../Storage/Tape/Tape.hpp" +#include "../../Storage/Tape/Parsers/Oric.hpp" + +#include #include +#include + +namespace Oric { + +class ConcreteMachine: + public CPU::MOS6502::BusHandler, + public MOS::MOS6522IRQDelegate::Delegate, + public Utility::TypeRecipient, + public Storage::Tape::BinaryTapePlayer::Delegate, + public Microdisc::Delegate, + public Machine { + + public: + ConcreteMachine() : + m6502_(*this), + use_fast_tape_hack_(false), + typer_delay_(2500000), + keyboard_read_count_(0), + keyboard_(new Keyboard), + ram_top_(0xbfff), + paged_rom_(rom_), + microdisc_is_enabled_(false) { + set_clock_rate(1000000); + via_.set_interrupt_delegate(this); + via_.keyboard = keyboard_; + clear_all_keys(); + via_.tape->set_delegate(this); + Memory::Fuzz(ram_, sizeof(ram_)); + } + + void set_rom(ROM rom, const std::vector &data) { + switch(rom) { + case BASIC11: basic11_rom_ = std::move(data); break; + case BASIC10: basic10_rom_ = std::move(data); break; + case Microdisc: microdisc_rom_ = std::move(data); break; + case Colour: + colour_rom_ = std::move(data); + if(video_output_) video_output_->set_colour_rom(colour_rom_); + break; + } + } + + void set_key_state(uint16_t key, bool isPressed) { + if(key == KeyNMI) { + m6502_.set_nmi_line(isPressed); + } else { + if(isPressed) + keyboard_->rows[key >> 8] |= (key & 0xff); + else + keyboard_->rows[key >> 8] &= ~(key & 0xff); + } + } + + void clear_all_keys() { + memset(keyboard_->rows, 0, sizeof(keyboard_->rows)); + } + + void set_use_fast_tape_hack(bool activate) { + use_fast_tape_hack_ = activate; + } + + void set_output_device(Outputs::CRT::OutputDevice output_device) { + video_output_->set_output_device(output_device); + } + + // to satisfy ConfigurationTarget::Machine + void configure_as_target(const StaticAnalyser::Target &target) { + if(target.tapes.size()) { + via_.tape->set_tape(target.tapes.front()); + } + + if(target.loadingCommand.length()) { + set_typer_for_string(target.loadingCommand.c_str()); + } + + if(target.oric.has_microdisc) { + microdisc_is_enabled_ = true; + microdisc_did_change_paging_flags(µdisc_); + microdisc_.set_delegate(this); + } + + int drive_index = 0; + for(auto disk : target.disks) { + if(drive_index < 4) microdisc_.set_disk(disk, drive_index); + drive_index++; + } + + if(target.oric.use_atmos_rom) { + memcpy(rom_, basic11_rom_.data(), std::min(basic11_rom_.size(), sizeof(rom_))); + + is_using_basic11_ = true; + tape_get_byte_address_ = 0xe6c9; + scan_keyboard_address_ = 0xf495; + tape_speed_address_ = 0x024d; + } else { + memcpy(rom_, basic10_rom_.data(), std::min(basic10_rom_.size(), sizeof(rom_))); + + is_using_basic11_ = false; + tape_get_byte_address_ = 0xe630; + scan_keyboard_address_ = 0xf43c; + tape_speed_address_ = 0x67; + } + } + + // to satisfy CPU::MOS6502::BusHandler + 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]; + + // 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 == 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_]); + m6502_.set_value_of_register(CPU::MOS6502::A, next_byte); + m6502_.set_value_of_register(CPU::MOS6502::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero); + *value = 0x60; // i.e. RTS + } + } else { + if((address & 0xff00) == 0x0300) { + if(microdisc_is_enabled_ && address >= 0x0310) { + switch(address) { + case 0x0310: case 0x0311: case 0x0312: case 0x0313: + if(isReadOperation(operation)) *value = microdisc_.get_register(address); + else microdisc_.set_register(address, *value); + break; + case 0x314: case 0x315: case 0x316: case 0x317: + if(isReadOperation(operation)) *value = microdisc_.get_interrupt_request_register(); + else microdisc_.set_control_register(*value); + break; + case 0x318: case 0x319: case 0x31a: case 0x31b: + if(isReadOperation(operation)) *value = microdisc_.get_data_request_register(); + break; + } + } else { + if(isReadOperation(operation)) *value = via_.get_register(address); + else via_.set_register(address, *value); + } + } else { + if(isReadOperation(operation)) + *value = ram_[address]; + else { + if(address >= 0x9800 && address <= 0xc000) { update_video(); typer_delay_ = 0; } + ram_[address] = *value; + } + } + } + + 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_++; + else if(!typer_->type_next_character()) { + clear_all_keys(); + typer_.reset(); + } + } + + via_.run_for(Cycles(1)); + if(microdisc_is_enabled_) microdisc_.run_for(Cycles(8)); + cycles_since_video_update_++; + return Cycles(1); + } + + void flush() { + update_video(); + via_.flush(); + } + + // to satisfy CRTMachine::Machine + void setup_output(float aspect_ratio) { + 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() { + video_output_.reset(); + via_.ay8910.reset(); + } + + std::shared_ptr get_crt() { + return video_output_->get_crt(); + } + + std::shared_ptr get_speaker() { + return via_.ay8910; + } + + void run_for(const Cycles cycles) { + m6502_.run_for(cycles); + } + + // to satisfy MOS::MOS6522IRQDelegate::Delegate + void mos6522_did_change_interrupt_status(void *mos6522) { + set_interrupt_line(); + } + + // to satisfy Storage::Tape::BinaryTapePlayer::Delegate + void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player) { + // 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) { + std::unique_ptr 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) { + int flags = microdisc->get_paging_flags(); + if(!(flags&Microdisc::PagingFlags::BASICDisable)) { + ram_top_ = 0xbfff; + paged_rom_ = rom_; + } else { + if(flags&Microdisc::PagingFlags::MicrodscDisable) { + ram_top_ = 0xffff; + } else { + ram_top_ = 0xdfff; + paged_rom_ = microdisc_rom_.data(); + } + } + } + + void wd1770_did_change_output(WD::WD1770 *wd1770) { + set_interrupt_line(); + } + + private: + CPU::MOS6502::Processor m6502_; + + // RAM and ROM + std::vector basic11_rom_, basic10_rom_, microdisc_rom_, colour_rom_; + uint8_t ram_[65536], rom_[16384]; + Cycles cycles_since_video_update_; + inline void update_video() { + video_output_->run_for(cycles_since_video_update_.flush()); + } + + // ROM bookkeeping + bool is_using_basic11_; + uint16_t tape_get_byte_address_, scan_keyboard_address_, tape_speed_address_; + int keyboard_read_count_; + + // Outputs + std::unique_ptr video_output_; + + // Keyboard + class Keyboard { + public: + uint8_t row; + uint8_t rows[8]; + }; + int typer_delay_; + + // The tape + class TapePlayer: public Storage::Tape::BinaryTapePlayer { + public: + TapePlayer() : Storage::Tape::BinaryTapePlayer(1000000) {} + + uint8_t get_next_byte(bool fast) { + return (uint8_t)parser_.get_next_byte(get_tape(), fast); + } + + private: + Storage::Tape::Oric::Parser parser_; + }; + bool use_fast_tape_hack_; + + // VIA (which owns the tape and the AY) + class VIA: public MOS::MOS6522, public MOS::MOS6522IRQDelegate { + public: + VIA() : + MOS::MOS6522(), + tape(new TapePlayer) {} + + using MOS6522IRQDelegate::set_interrupt_status; + + void set_control_line_output(Port port, Line line, bool value) { + if(line) { + if(port) ay_bdir_ = value; else ay_bc1_ = value; + update_ay(); + } + } + + void set_port_output(Port port, uint8_t value, uint8_t direction_mask) { + if(port) { + keyboard->row = value; + tape->set_motor_control(value & 0x40); + } else { + ay8910->set_data_input(value); + } + } + + uint8_t get_port_input(Port port) { + if(port) { + uint8_t column = ay8910->get_port_output(false) ^ 0xff; + return (keyboard->rows[keyboard->row & 7] & column) ? 0x08 : 0x00; + } else { + return ay8910->get_data_output(); + } + } + + inline void run_for(const Cycles cycles) { + cycles_since_ay_update_ += cycles; + MOS::MOS6522::run_for(cycles); + tape->run_for(cycles); + } + + void flush() { + ay8910->run_for(cycles_since_ay_update_.flush()); + ay8910->flush(); + } + + std::shared_ptr ay8910; + std::unique_ptr tape; + std::shared_ptr keyboard; + + private: + void update_ay() { + ay8910->run_for(cycles_since_ay_update_.flush()); + ay8910->set_control_lines( (GI::AY38910::ControlLines)((ay_bdir_ ? GI::AY38910::BDIR : 0) | (ay_bc1_ ? GI::AY38910::BC1 : 0) | GI::AY38910::BC2)); + } + bool ay_bdir_, ay_bc1_; + Cycles cycles_since_ay_update_; + }; + VIA via_; + std::shared_ptr keyboard_; + + // the Microdisc, if in use + class Microdisc microdisc_; + bool microdisc_is_enabled_; + uint16_t ram_top_; + uint8_t *paged_rom_; + + inline void set_interrupt_line() { + m6502_.set_irq_line( + via_.get_interrupt_line() || + (microdisc_is_enabled_ && microdisc_.get_interrupt_request_line())); + } +}; + +} using namespace Oric; -Machine::Machine() : - m6502_(*this), - use_fast_tape_hack_(false), - typer_delay_(2500000), - keyboard_read_count_(0), - keyboard_(new Keyboard), - ram_top_(0xbfff), - paged_rom_(rom_), - microdisc_is_enabled_(false) { - set_clock_rate(1000000); - via_.set_interrupt_delegate(this); - via_.keyboard = keyboard_; - clear_all_keys(); - via_.tape->set_delegate(this); - Memory::Fuzz(ram_, sizeof(ram_)); +Machine *Machine::Oric() { + return new ConcreteMachine; } -void Machine::configure_as_target(const StaticAnalyser::Target &target) { - if(target.tapes.size()) { - via_.tape->set_tape(target.tapes.front()); - } - - if(target.loadingCommand.length()) { - set_typer_for_string(target.loadingCommand.c_str()); - } - - if(target.oric.has_microdisc) { - microdisc_is_enabled_ = true; - microdisc_did_change_paging_flags(µdisc_); - microdisc_.set_delegate(this); - } - - int drive_index = 0; - for(auto disk : target.disks) { - if(drive_index < 4) microdisc_.set_disk(disk, drive_index); - drive_index++; - } - - if(target.oric.use_atmos_rom) { - memcpy(rom_, basic11_rom_.data(), std::min(basic11_rom_.size(), sizeof(rom_))); - - is_using_basic11_ = true; - tape_get_byte_address_ = 0xe6c9; - scan_keyboard_address_ = 0xf495; - tape_speed_address_ = 0x024d; - } else { - memcpy(rom_, basic10_rom_.data(), std::min(basic10_rom_.size(), sizeof(rom_))); - - is_using_basic11_ = false; - tape_get_byte_address_ = 0xe630; - scan_keyboard_address_ = 0xf43c; - tape_speed_address_ = 0x67; - } -} - -void Machine::set_typer_for_string(const char *string) { - std::unique_ptr mapper(new CharacterMapper); - Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper)); -} - -void Machine::set_rom(ROM rom, const std::vector &data) { - switch(rom) { - case BASIC11: basic11_rom_ = std::move(data); break; - case BASIC10: basic10_rom_ = std::move(data); break; - case Microdisc: microdisc_rom_ = std::move(data); break; - case Colour: - colour_rom_ = std::move(data); - if(video_output_) video_output_->set_colour_rom(colour_rom_); - break; - } -} - -Cycles 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 == 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_]); - m6502_.set_value_of_register(CPU::MOS6502::A, next_byte); - m6502_.set_value_of_register(CPU::MOS6502::Flags, next_byte ? 0 : CPU::MOS6502::Flag::Zero); - *value = 0x60; // i.e. RTS - } - } else { - if((address & 0xff00) == 0x0300) { - if(microdisc_is_enabled_ && address >= 0x0310) { - switch(address) { - case 0x0310: case 0x0311: case 0x0312: case 0x0313: - if(isReadOperation(operation)) *value = microdisc_.get_register(address); - else microdisc_.set_register(address, *value); - break; - case 0x314: case 0x315: case 0x316: case 0x317: - if(isReadOperation(operation)) *value = microdisc_.get_interrupt_request_register(); - else microdisc_.set_control_register(*value); - break; - case 0x318: case 0x319: case 0x31a: case 0x31b: - if(isReadOperation(operation)) *value = microdisc_.get_data_request_register(); - break; - } - } else { - if(isReadOperation(operation)) *value = via_.get_register(address); - else via_.set_register(address, *value); - } - } else { - if(isReadOperation(operation)) - *value = ram_[address]; - else { - if(address >= 0x9800 && address <= 0xc000) { update_video(); typer_delay_ = 0; } - ram_[address] = *value; - } - } - } - - 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_++; - else if(!typer_->type_next_character()) { - clear_all_keys(); - typer_.reset(); - } - } - - via_.run_for(Cycles(1)); - if(microdisc_is_enabled_) microdisc_.run_for(Cycles(8)); - cycles_since_video_update_++; - return Cycles(1); -} - -void Machine::flush() { - update_video(); - via_.flush(); -} - -void Machine::update_video() { - video_output_->run_for(cycles_since_video_update_.flush()); -} - -void Machine::setup_output(float aspect_ratio) { - 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 Machine::close_output() { - video_output_.reset(); - via_.ay8910.reset(); -} - -void Machine::mos6522_did_change_interrupt_status(void *mos6522) { - set_interrupt_line(); -} - -void Machine::set_key_state(uint16_t key, bool isPressed) { - if(key == KeyNMI) { - m6502_.set_nmi_line(isPressed); - } else { - if(isPressed) - keyboard_->rows[key >> 8] |= (key & 0xff); - else - keyboard_->rows[key >> 8] &= ~(key & 0xff); - } -} - -void Machine::clear_all_keys() { - memset(keyboard_->rows, 0, sizeof(keyboard_->rows)); -} - -void Machine::set_use_fast_tape_hack(bool activate) { - use_fast_tape_hack_ = activate; -} - -void Machine::set_output_device(Outputs::CRT::OutputDevice output_device) { - video_output_->set_output_device(output_device); -} - -void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player) { - // set CB1 - via_.set_control_line_input(VIA::Port::B, VIA::Line::One, !tape_player->get_input()); -} - -std::shared_ptr Machine::get_crt() { - return video_output_->get_crt(); -} - -std::shared_ptr Machine::get_speaker() { - return via_.ay8910; -} - -void Machine::run_for(const Cycles cycles) { - m6502_.run_for(cycles); -} - -#pragma mark - The 6522 - -Machine::VIA::VIA() : - MOS::MOS6522(), - tape(new TapePlayer) {} - -void Machine::VIA::set_control_line_output(Port port, Line line, bool value) { - if(line) { - if(port) ay_bdir_ = value; else ay_bc1_ = value; - update_ay(); - } -} - -void Machine::VIA::set_port_output(Port port, uint8_t value, uint8_t direction_mask) { - if(port) { - keyboard->row = value; - tape->set_motor_control(value & 0x40); - } else { - ay8910->set_data_input(value); - } -} - -uint8_t Machine::VIA::get_port_input(Port port) { - if(port) { - uint8_t column = ay8910->get_port_output(false) ^ 0xff; - return (keyboard->rows[keyboard->row & 7] & column) ? 0x08 : 0x00; - } else { - return ay8910->get_data_output(); - } -} - -void Machine::VIA::flush() { - ay8910->run_for(cycles_since_ay_update_.flush()); - ay8910->flush(); -} - -void Machine::VIA::run_for(const Cycles cycles) { - cycles_since_ay_update_ += cycles; - MOS::MOS6522::run_for(cycles); - tape->run_for(cycles); -} - -void Machine::VIA::update_ay() { - ay8910->run_for(cycles_since_ay_update_.flush()); - ay8910->set_control_lines( (GI::AY38910::ControlLines)((ay_bdir_ ? GI::AY38910::BDIR : 0) | (ay_bc1_ ? GI::AY38910::BC1 : 0) | GI::AY38910::BC2)); -} - -#pragma mark - TapePlayer - -Machine::TapePlayer::TapePlayer() : - Storage::Tape::BinaryTapePlayer(1000000) {} - -uint8_t Machine::TapePlayer::get_next_byte(bool fast) { - return (uint8_t)parser_.get_next_byte(get_tape(), fast); -} - -#pragma mark - Microdisc - -void Machine::microdisc_did_change_paging_flags(class Microdisc *microdisc) { - int flags = microdisc->get_paging_flags(); - if(!(flags&Microdisc::PagingFlags::BASICDisable)) { - ram_top_ = 0xbfff; - paged_rom_ = rom_; - } else { - if(flags&Microdisc::PagingFlags::MicrodscDisable) { - ram_top_ = 0xffff; - } else { - ram_top_ = 0xdfff; - paged_rom_ = microdisc_rom_.data(); - } - } -} - -void Machine::wd1770_did_change_output(WD::WD1770 *wd1770) { - set_interrupt_line(); -} - -void Machine::set_interrupt_line() { - m6502_.set_irq_line( - via_.get_interrupt_line() || - (microdisc_is_enabled_ && microdisc_.get_interrupt_request_line())); -} +Machine::~Machine() {} diff --git a/Machines/Oric/Oric.hpp b/Machines/Oric/Oric.hpp index 7aefbb8a9..e8d8cc089 100644 --- a/Machines/Oric/Oric.hpp +++ b/Machines/Oric/Oric.hpp @@ -11,24 +11,17 @@ #include "../ConfigurationTarget.hpp" #include "../CRTMachine.hpp" -#include "../Typer.hpp" - -#include "../../Processors/6502/6502.hpp" -#include "../../Components/6522/6522.hpp" -#include "../../Components/AY38910/AY38910.hpp" -#include "../../Storage/Tape/Parsers/Oric.hpp" - -#include "Video.hpp" -#include "Microdisc.hpp" - -#include "../../Storage/Tape/Tape.hpp" +#include "../KeyboardMachine.hpp" #include #include -#include namespace Oric { +enum ROM { + BASIC10, BASIC11, Microdisc, Colour +}; + enum Key: uint16_t { Key3 = 0x0000 | 0x80, KeyX = 0x0000 | 0x40, Key1 = 0x0000 | 0x20, KeyV = 0x0000 | 0x08, Key5 = 0x0000 | 0x04, KeyN = 0x0000 | 0x02, Key7 = 0x0000 | 0x01, @@ -50,124 +43,27 @@ enum Key: uint16_t { KeyNMI = 0xfffd, }; -enum ROM { - BASIC10, BASIC11, Microdisc, Colour -}; - +/*! + Models an Oric 1/Atmos with or without a Microdisc. +*/ class Machine: - public CPU::MOS6502::BusHandler, public CRTMachine::Machine, public ConfigurationTarget::Machine, - public MOS::MOS6522IRQDelegate::Delegate, - public Utility::TypeRecipient, - public Storage::Tape::BinaryTapePlayer::Delegate, - public Microdisc::Delegate { - + public KeyboardMachine::Machine { public: - Machine(); + virtual ~Machine(); - void set_rom(ROM rom, const std::vector &data); - void set_key_state(uint16_t key, bool isPressed); - void clear_all_keys(); + /// Creates an returns an Oric on the heap. + static Machine *Oric(); - void set_use_fast_tape_hack(bool activate); - void set_output_device(Outputs::CRT::OutputDevice output_device); + /// Sets the contents of @c rom to @c data. Assumed to be a setup step; has no effect once a machine is running. + virtual void set_rom(ROM rom, const std::vector &data) = 0; - // to satisfy ConfigurationTarget::Machine - void configure_as_target(const StaticAnalyser::Target &target); + /// Enables or disables turbo-speed tape loading. + virtual void set_use_fast_tape_hack(bool activate) = 0; - // to satisfy CPU::MOS6502::Processor - Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value); - void flush(); - - // to satisfy CRTMachine::Machine - virtual void setup_output(float aspect_ratio); - virtual void close_output(); - virtual std::shared_ptr get_crt(); - virtual std::shared_ptr get_speaker(); - virtual void run_for(const Cycles cycles); - - // to satisfy MOS::MOS6522IRQDelegate::Delegate - void mos6522_did_change_interrupt_status(void *mos6522); - - // to satisfy Storage::Tape::BinaryTapePlayer::Delegate - void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player); - - // for Utility::TypeRecipient::Delegate - void set_typer_for_string(const char *string); - - // for Microdisc::Delegate - void microdisc_did_change_paging_flags(class Microdisc *microdisc); - void wd1770_did_change_output(WD::WD1770 *wd1770); - - private: - CPU::MOS6502::Processor m6502_; - - // RAM and ROM - std::vector basic11_rom_, basic10_rom_, microdisc_rom_, colour_rom_; - uint8_t ram_[65536], rom_[16384]; - Cycles cycles_since_video_update_; - inline void update_video(); - - // ROM bookkeeping - bool is_using_basic11_; - uint16_t tape_get_byte_address_, scan_keyboard_address_, tape_speed_address_; - int keyboard_read_count_; - - // Outputs - std::unique_ptr video_output_; - - // Keyboard - class Keyboard { - public: - uint8_t row; - uint8_t rows[8]; - }; - int typer_delay_; - - // The tape - class TapePlayer: public Storage::Tape::BinaryTapePlayer { - public: - TapePlayer(); - uint8_t get_next_byte(bool fast); - - private: - Storage::Tape::Oric::Parser parser_; - }; - bool use_fast_tape_hack_; - - // VIA (which owns the tape and the AY) - class VIA: public MOS::MOS6522, public MOS::MOS6522IRQDelegate { - public: - VIA(); - using MOS6522IRQDelegate::set_interrupt_status; - - void set_control_line_output(Port port, Line line, bool value); - void set_port_output(Port port, uint8_t value, uint8_t direction_mask); - uint8_t get_port_input(Port port); - inline void run_for(const Cycles cycles); - - std::shared_ptr ay8910; - std::unique_ptr tape; - std::shared_ptr keyboard; - - void flush(); - - private: - void update_ay(); - bool ay_bdir_, ay_bc1_; - Cycles cycles_since_ay_update_; - }; - VIA via_; - std::shared_ptr keyboard_; - - // the Microdisc, if in use - class Microdisc microdisc_; - bool microdisc_is_enabled_; - uint16_t ram_top_; - uint8_t *paged_rom_; - - inline void set_interrupt_line(); + /// Sets the type of display the Oric is connected to. + virtual void set_output_device(Outputs::CRT::OutputDevice output_device) = 0; }; } diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm index de5bcb3ef..b231c2ba1 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm @@ -16,7 +16,14 @@ #import "NSBundle+DataResource.h" @implementation CSOric { - Oric::Machine _oric; + std::unique_ptr _oric; +} + +- (CRTMachine::Machine * const)machine { + if(!_oric) { + _oric.reset(Oric::Machine::Oric()); + } + return _oric.get(); } - (instancetype)init { @@ -27,10 +34,10 @@ NSData *colour = [self rom:@"colour"]; NSData *microdisc = [self rom:@"microdisc"]; - if(basic10) _oric.set_rom(Oric::BASIC10, basic10.stdVector8); - if(basic11) _oric.set_rom(Oric::BASIC11, basic11.stdVector8); - if(colour) _oric.set_rom(Oric::Colour, colour.stdVector8); - if(microdisc) _oric.set_rom(Oric::Microdisc, microdisc.stdVector8); + if(basic10) _oric->set_rom(Oric::BASIC10, basic10.stdVector8); + if(basic11) _oric->set_rom(Oric::BASIC11, basic11.stdVector8); + if(colour) _oric->set_rom(Oric::Colour, colour.stdVector8); + if(microdisc) _oric->set_rom(Oric::Microdisc, microdisc.stdVector8); } return self; } @@ -39,89 +46,85 @@ return [[NSBundle mainBundle] dataForResource:name withExtension:@"rom" subdirectory:@"ROMImages/Oric"]; } -- (CRTMachine::Machine * const)machine { - return &_oric; -} - #pragma mark - CSKeyboardMachine - (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed { @synchronized(self) { switch(key) { - case VK_ANSI_0: _oric.set_key_state(Oric::Key::Key0, isPressed); break; - case VK_ANSI_1: _oric.set_key_state(Oric::Key::Key1, isPressed); break; - case VK_ANSI_2: _oric.set_key_state(Oric::Key::Key2, isPressed); break; - case VK_ANSI_3: _oric.set_key_state(Oric::Key::Key3, isPressed); break; - case VK_ANSI_4: _oric.set_key_state(Oric::Key::Key4, isPressed); break; - case VK_ANSI_5: _oric.set_key_state(Oric::Key::Key5, isPressed); break; - case VK_ANSI_6: _oric.set_key_state(Oric::Key::Key6, isPressed); break; - case VK_ANSI_7: _oric.set_key_state(Oric::Key::Key7, isPressed); break; - case VK_ANSI_8: _oric.set_key_state(Oric::Key::Key8, isPressed); break; - case VK_ANSI_9: _oric.set_key_state(Oric::Key::Key9, isPressed); break; + case VK_ANSI_0: _oric->set_key_state(Oric::Key::Key0, isPressed); break; + case VK_ANSI_1: _oric->set_key_state(Oric::Key::Key1, isPressed); break; + case VK_ANSI_2: _oric->set_key_state(Oric::Key::Key2, isPressed); break; + case VK_ANSI_3: _oric->set_key_state(Oric::Key::Key3, isPressed); break; + case VK_ANSI_4: _oric->set_key_state(Oric::Key::Key4, isPressed); break; + case VK_ANSI_5: _oric->set_key_state(Oric::Key::Key5, isPressed); break; + case VK_ANSI_6: _oric->set_key_state(Oric::Key::Key6, isPressed); break; + case VK_ANSI_7: _oric->set_key_state(Oric::Key::Key7, isPressed); break; + case VK_ANSI_8: _oric->set_key_state(Oric::Key::Key8, isPressed); break; + case VK_ANSI_9: _oric->set_key_state(Oric::Key::Key9, isPressed); break; - case VK_ANSI_Q: _oric.set_key_state(Oric::Key::KeyQ, isPressed); break; - case VK_ANSI_W: _oric.set_key_state(Oric::Key::KeyW, isPressed); break; - case VK_ANSI_E: _oric.set_key_state(Oric::Key::KeyE, isPressed); break; - case VK_ANSI_R: _oric.set_key_state(Oric::Key::KeyR, isPressed); break; - case VK_ANSI_T: _oric.set_key_state(Oric::Key::KeyT, isPressed); break; - case VK_ANSI_Y: _oric.set_key_state(Oric::Key::KeyY, isPressed); break; - case VK_ANSI_U: _oric.set_key_state(Oric::Key::KeyU, isPressed); break; - case VK_ANSI_I: _oric.set_key_state(Oric::Key::KeyI, isPressed); break; - case VK_ANSI_O: _oric.set_key_state(Oric::Key::KeyO, isPressed); break; - case VK_ANSI_P: _oric.set_key_state(Oric::Key::KeyP, isPressed); break; - case VK_ANSI_A: _oric.set_key_state(Oric::Key::KeyA, isPressed); break; - case VK_ANSI_S: _oric.set_key_state(Oric::Key::KeyS, isPressed); break; - case VK_ANSI_D: _oric.set_key_state(Oric::Key::KeyD, isPressed); break; - case VK_ANSI_F: _oric.set_key_state(Oric::Key::KeyF, isPressed); break; - case VK_ANSI_G: _oric.set_key_state(Oric::Key::KeyG, isPressed); break; - case VK_ANSI_H: _oric.set_key_state(Oric::Key::KeyH, isPressed); break; - case VK_ANSI_J: _oric.set_key_state(Oric::Key::KeyJ, isPressed); break; - case VK_ANSI_K: _oric.set_key_state(Oric::Key::KeyK, isPressed); break; - case VK_ANSI_L: _oric.set_key_state(Oric::Key::KeyL, isPressed); break; - case VK_ANSI_Z: _oric.set_key_state(Oric::Key::KeyZ, isPressed); break; - case VK_ANSI_X: _oric.set_key_state(Oric::Key::KeyX, isPressed); break; - case VK_ANSI_C: _oric.set_key_state(Oric::Key::KeyC, isPressed); break; - case VK_ANSI_V: _oric.set_key_state(Oric::Key::KeyV, isPressed); break; - case VK_ANSI_B: _oric.set_key_state(Oric::Key::KeyB, isPressed); break; - case VK_ANSI_N: _oric.set_key_state(Oric::Key::KeyN, isPressed); break; - case VK_ANSI_M: _oric.set_key_state(Oric::Key::KeyM, isPressed); break; + case VK_ANSI_Q: _oric->set_key_state(Oric::Key::KeyQ, isPressed); break; + case VK_ANSI_W: _oric->set_key_state(Oric::Key::KeyW, isPressed); break; + case VK_ANSI_E: _oric->set_key_state(Oric::Key::KeyE, isPressed); break; + case VK_ANSI_R: _oric->set_key_state(Oric::Key::KeyR, isPressed); break; + case VK_ANSI_T: _oric->set_key_state(Oric::Key::KeyT, isPressed); break; + case VK_ANSI_Y: _oric->set_key_state(Oric::Key::KeyY, isPressed); break; + case VK_ANSI_U: _oric->set_key_state(Oric::Key::KeyU, isPressed); break; + case VK_ANSI_I: _oric->set_key_state(Oric::Key::KeyI, isPressed); break; + case VK_ANSI_O: _oric->set_key_state(Oric::Key::KeyO, isPressed); break; + case VK_ANSI_P: _oric->set_key_state(Oric::Key::KeyP, isPressed); break; + case VK_ANSI_A: _oric->set_key_state(Oric::Key::KeyA, isPressed); break; + case VK_ANSI_S: _oric->set_key_state(Oric::Key::KeyS, isPressed); break; + case VK_ANSI_D: _oric->set_key_state(Oric::Key::KeyD, isPressed); break; + case VK_ANSI_F: _oric->set_key_state(Oric::Key::KeyF, isPressed); break; + case VK_ANSI_G: _oric->set_key_state(Oric::Key::KeyG, isPressed); break; + case VK_ANSI_H: _oric->set_key_state(Oric::Key::KeyH, isPressed); break; + case VK_ANSI_J: _oric->set_key_state(Oric::Key::KeyJ, isPressed); break; + case VK_ANSI_K: _oric->set_key_state(Oric::Key::KeyK, isPressed); break; + case VK_ANSI_L: _oric->set_key_state(Oric::Key::KeyL, isPressed); break; + case VK_ANSI_Z: _oric->set_key_state(Oric::Key::KeyZ, isPressed); break; + case VK_ANSI_X: _oric->set_key_state(Oric::Key::KeyX, isPressed); break; + case VK_ANSI_C: _oric->set_key_state(Oric::Key::KeyC, isPressed); break; + case VK_ANSI_V: _oric->set_key_state(Oric::Key::KeyV, isPressed); break; + case VK_ANSI_B: _oric->set_key_state(Oric::Key::KeyB, isPressed); break; + case VK_ANSI_N: _oric->set_key_state(Oric::Key::KeyN, isPressed); break; + case VK_ANSI_M: _oric->set_key_state(Oric::Key::KeyM, isPressed); break; - case VK_Space: _oric.set_key_state(Oric::Key::KeySpace, isPressed); break; - case VK_Return: _oric.set_key_state(Oric::Key::KeyReturn, isPressed); break; - case VK_ANSI_Minus: _oric.set_key_state(Oric::Key::KeyMinus, isPressed); break; - case VK_ANSI_Equal: _oric.set_key_state(Oric::Key::KeyEquals, isPressed); break; + case VK_Space: _oric->set_key_state(Oric::Key::KeySpace, isPressed); break; + case VK_Return: _oric->set_key_state(Oric::Key::KeyReturn, isPressed); break; + case VK_ANSI_Minus: _oric->set_key_state(Oric::Key::KeyMinus, isPressed); break; + case VK_ANSI_Equal: _oric->set_key_state(Oric::Key::KeyEquals, isPressed); break; case VK_ANSI_Backslash: - _oric.set_key_state(Oric::Key::KeyBackSlash, isPressed); break; - case VK_ANSI_Slash: _oric.set_key_state(Oric::Key::KeyForwardSlash, isPressed); break; + _oric->set_key_state(Oric::Key::KeyBackSlash, isPressed); break; + case VK_ANSI_Slash: _oric->set_key_state(Oric::Key::KeyForwardSlash, isPressed); break; case VK_ANSI_LeftBracket: - _oric.set_key_state(Oric::Key::KeyOpenSquare, isPressed); break; + _oric->set_key_state(Oric::Key::KeyOpenSquare, isPressed); break; case VK_ANSI_RightBracket: - _oric.set_key_state(Oric::Key::KeyCloseSquare, isPressed); break; - case VK_ANSI_Quote: _oric.set_key_state(Oric::Key::KeyQuote, isPressed); break; + _oric->set_key_state(Oric::Key::KeyCloseSquare, isPressed); break; + case VK_ANSI_Quote: _oric->set_key_state(Oric::Key::KeyQuote, isPressed); break; - case VK_RightArrow: _oric.set_key_state(Oric::Key::KeyRight, isPressed); break; - case VK_LeftArrow: _oric.set_key_state(Oric::Key::KeyLeft, isPressed); break; - case VK_DownArrow: _oric.set_key_state(Oric::Key::KeyDown, isPressed); break; - case VK_UpArrow: _oric.set_key_state(Oric::Key::KeyUp, isPressed); break; + case VK_RightArrow: _oric->set_key_state(Oric::Key::KeyRight, isPressed); break; + case VK_LeftArrow: _oric->set_key_state(Oric::Key::KeyLeft, isPressed); break; + case VK_DownArrow: _oric->set_key_state(Oric::Key::KeyDown, isPressed); break; + case VK_UpArrow: _oric->set_key_state(Oric::Key::KeyUp, isPressed); break; - case VK_Delete: _oric.set_key_state(Oric::Key::KeyDelete, isPressed); break; - case VK_Escape: _oric.set_key_state(Oric::Key::KeyEscape, isPressed); break; + case VK_Delete: _oric->set_key_state(Oric::Key::KeyDelete, isPressed); break; + case VK_Escape: _oric->set_key_state(Oric::Key::KeyEscape, isPressed); break; - case VK_ANSI_Comma: _oric.set_key_state(Oric::Key::KeyComma, isPressed); break; - case VK_ANSI_Period: _oric.set_key_state(Oric::Key::KeyFullStop, isPressed); break; + case VK_ANSI_Comma: _oric->set_key_state(Oric::Key::KeyComma, isPressed); break; + case VK_ANSI_Period: _oric->set_key_state(Oric::Key::KeyFullStop, isPressed); break; - case VK_ANSI_Semicolon: _oric.set_key_state(Oric::Key::KeySemiColon, isPressed); break; + case VK_ANSI_Semicolon: _oric->set_key_state(Oric::Key::KeySemiColon, isPressed); break; case VK_Shift: - _oric.set_key_state(Oric::Key::KeyLeftShift, isPressed); - _oric.set_key_state(Oric::Key::KeyRightShift, isPressed); + _oric->set_key_state(Oric::Key::KeyLeftShift, isPressed); + _oric->set_key_state(Oric::Key::KeyRightShift, isPressed); break; - case VK_RightShift: _oric.set_key_state(Oric::Key::KeyRightShift, isPressed); break; - case VK_Control: _oric.set_key_state(Oric::Key::KeyControl, isPressed); break; + case VK_RightShift: _oric->set_key_state(Oric::Key::KeyRightShift, isPressed); break; + case VK_Control: _oric->set_key_state(Oric::Key::KeyControl, isPressed); break; case VK_ANSI_Grave: - case VK_F12: _oric.set_key_state(Oric::Key::KeyNMI, isPressed); break; + case VK_F12: _oric->set_key_state(Oric::Key::KeyNMI, isPressed); break; default: printf("%02x\n", key); @@ -131,7 +134,9 @@ } - (void)clearAllKeys { - _oric.clear_all_keys(); + @synchronized(self) { + _oric->clear_all_keys(); + } } #pragma mark - Options @@ -139,14 +144,14 @@ - (void)setUseFastLoadingHack:(BOOL)useFastLoadingHack { @synchronized(self) { _useFastLoadingHack = useFastLoadingHack; - _oric.set_use_fast_tape_hack(useFastLoadingHack ? true : false); + _oric->set_use_fast_tape_hack(useFastLoadingHack ? true : false); } } - (void)setUseCompositeOutput:(BOOL)useCompositeOutput { @synchronized(self) { _useCompositeOutput = useCompositeOutput; - _oric.set_output_device(useCompositeOutput ? Outputs::CRT::Television : Outputs::CRT::Monitor); + _oric->set_output_device(useCompositeOutput ? Outputs::CRT::Television : Outputs::CRT::Monitor); } }