1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 18:30:21 +00:00

Hid most of the Oric innards, and corrected a potential multi-thread access error emanating from the Mac side of the world.

This commit is contained in:
Thomas Harte 2017-08-16 14:35:53 -04:00
parent 2105597910
commit 148591b7f2
3 changed files with 455 additions and 469 deletions

View File

@ -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 <cstdint>
#include <memory>
#include <vector>
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<uint8_t> &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(&microdisc_);
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<Outputs::CRT::CRT> get_crt() {
return video_output_->get_crt();
}
std::shared_ptr<Outputs::Speaker> 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<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) {
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<ConcreteMachine> m6502_;
// RAM and ROM
std::vector<uint8_t> 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<VideoOutput> 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<VIA>, public MOS::MOS6522IRQDelegate {
public:
VIA() :
MOS::MOS6522<VIA>(),
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<VIA>::run_for(cycles);
tape->run_for(cycles);
}
void flush() {
ay8910->run_for(cycles_since_ay_update_.flush());
ay8910->flush();
}
std::shared_ptr<GI::AY38910::AY38910> ay8910;
std::unique_ptr<TapePlayer> tape;
std::shared_ptr<Keyboard> 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> 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(&microdisc_);
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<CharacterMapper> mapper(new CharacterMapper);
Utility::TypeRecipient::set_typer_for_string(string, std::move(mapper));
}
void Machine::set_rom(ROM rom, const std::vector<uint8_t> &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<Outputs::CRT::CRT> Machine::get_crt() {
return video_output_->get_crt();
}
std::shared_ptr<Outputs::Speaker> 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<Machine::VIA>(),
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<VIA>::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() {}

View File

@ -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 <cstdint>
#include <vector>
#include <memory>
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<uint8_t> &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<uint8_t> &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<Outputs::CRT::CRT> get_crt();
virtual std::shared_ptr<Outputs::Speaker> 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<Machine> m6502_;
// RAM and ROM
std::vector<uint8_t> 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<VideoOutput> 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<VIA>, 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<GI::AY38910::AY38910> ay8910;
std::unique_ptr<TapePlayer> tape;
std::shared_ptr<Keyboard> keyboard;
void flush();
private:
void update_ay();
bool ay_bdir_, ay_bc1_;
Cycles cycles_since_ay_update_;
};
VIA via_;
std::shared_ptr<Keyboard> 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;
};
}

View File

@ -16,7 +16,14 @@
#import "NSBundle+DataResource.h"
@implementation CSOric {
Oric::Machine _oric;
std::unique_ptr<Oric::Machine> _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);
}
}