1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-18 01:07:58 +00:00

Merge pull request #720 from TomHarte/Jasmin

Adds emulation of the Oric's Jasmin disk interface.
This commit is contained in:
Thomas Harte 2020-01-05 22:13:11 -05:00 committed by GitHub
commit 45afb13a54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 351 additions and 48 deletions

View File

@ -100,6 +100,34 @@ static bool IsMicrodisc(Storage::Encodings::MFM::Parser &parser) {
return !std::memcmp(signature, first_sample.data(), sizeof(signature));
}
static bool IsJasmin(Storage::Encodings::MFM::Parser &parser) {
/*
The Jasmin boot sector is sector 1 of track 0 and is loaded at $400;
disassemble it to test it for validity.
*/
Storage::Encodings::MFM::Sector *sector = parser.get_sector(0, 0, 1);
if(!sector) return false;
if(sector->samples.empty()) return false;
const std::vector<uint8_t> &first_sample = sector->samples[0];
if(first_sample.size() != 256) return false;
// Grab a disassembly.
const auto disassembly =
Analyser::Static::MOS6502::Disassemble(first_sample, Analyser::Static::Disassembler::OffsetMapper(0x400), {0x400});
// Check for references to the Jasmin registers.
int register_hits = 0;
for(auto list : {disassembly.external_stores, disassembly.external_loads, disassembly.external_modifies}) {
for(auto address : list) {
register_hits += (address >= 0x3f4 && address <= 0x3ff);
}
}
// Arbitrary, sure, but as long as at least two accesses to Jasmin registers are found, accept this.
return register_hits >= 2;
}
Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
auto target = std::make_unique<Target>();
target->machine = Machine::Oric;
@ -115,7 +143,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
for(const auto &file : tape_files) {
if(file.data_type == File::MachineCode) {
std::vector<uint16_t> entry_points = {file.starting_address};
Analyser::Static::MOS6502::Disassembly disassembly =
const Analyser::Static::MOS6502::Disassembly disassembly =
Analyser::Static::MOS6502::Disassemble(file.data, Analyser::Static::Disassembler::OffsetMapper(file.starting_address), entry_points);
int basic10_score = Basic10Score(disassembly);
@ -130,12 +158,17 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
}
if(!media.disks.empty()) {
// Only the Microdisc is emulated right now, so accept only disks that it can boot from.
// 8-DOS is recognised by a dedicated Disk II analyser, so check only for Microdisc and
// Jasmin formats here.
for(auto &disk: media.disks) {
Storage::Encodings::MFM::Parser parser(true, disk);
if(IsMicrodisc(parser)) {
target->disk_interface = Target::DiskInterface::Microdisc;
target->media.disks.push_back(disk);
} else if(IsJasmin(parser)) {
target->disk_interface = Target::DiskInterface::Jasmin;
target->should_start_jasmin = true;
target->media.disks.push_back(disk);
}
}
}

View File

@ -26,12 +26,14 @@ struct Target: public ::Analyser::Static::Target {
enum class DiskInterface {
Microdisc,
Pravetz,
Jasmin,
None
};
ROM rom = ROM::BASIC11;
DiskInterface disk_interface = DiskInterface::None;
std::string loading_command;
bool should_start_jasmin = false;
};
}

70
Machines/Oric/Jasmin.cpp Normal file
View File

@ -0,0 +1,70 @@
//
// Jasmin.cpp
// Clock Signal
//
// Created by Thomas Harte on 05/01/2020.
// Copyright © 2020 Thomas Harte. All rights reserved.
//
#include "Jasmin.hpp"
using namespace Oric;
// NB: there's some controversy here on WD1770 versus WD1772, but between those two I think
// the only difference is stepping rates, and it says 1770 on the schematic I'm looking at.
Jasmin::Jasmin() : WD1770(P1770) {
set_is_double_density(true);
}
void Jasmin::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int d) {
const size_t drive = size_t(d);
if(!drives_[drive]) {
drives_[drive] = std::make_unique<Storage::Disk::Drive>(8000000, 300, 2);
if(drive == selected_drive_) set_drive(drives_[drive]);
}
drives_[drive]->set_disk(disk);
}
void Jasmin::write(int address, uint8_t value) {
switch(address) {
// Set side.
case 0x3f8:
for(auto &drive : drives_) {
if(drive) drive->set_head(value & 1);
}
break;
case 0x3f9:
/* TODO: reset. */
break;
case 0x3fa: {
// If b0, enable overlay RAM.
posit_paging_flags((paging_flags_ & BASICDisable) | ((value & 1) ? OverlayRAMEnable : 0));
} break;
case 0x3fb:
// If b0, disable BASIC ROM.
posit_paging_flags((paging_flags_ & OverlayRAMEnable) | ((value & 1) ? BASICDisable : 0));
break;
case 0x3fc: case 0x3fd: case 0x3fe: case 0x3ff: {
const size_t new_selected_drive = size_t(address - 0x3fc);
if(new_selected_drive != selected_drive_) {
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(false);
selected_drive_ = new_selected_drive;
set_drive(drives_[selected_drive_]);
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(motor_on_);
}
} break;
default:
return WD::WD1770::write(address, value);
}
}
void Jasmin::set_motor_on(bool on) {
motor_on_ = on;
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(motor_on_);
}

60
Machines/Oric/Jasmin.hpp Normal file
View File

@ -0,0 +1,60 @@
//
// Jasmin.hpp
// Clock Signal
//
// Created by Thomas Harte on 05/01/2020.
// Copyright © 2020 Thomas Harte. All rights reserved.
//
#ifndef Jasmin_hpp
#define Jasmin_hpp
#include "../../Components/1770/1770.hpp"
#include "../../Activity/Observer.hpp"
#include <array>
#include <memory>
namespace Oric {
class Jasmin: public WD::WD1770 {
public:
Jasmin();
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
void write(int address, uint8_t value);
enum PagingFlags {
/// Indicates that overlay RAM is enabled, implying no ROM is visible.
OverlayRAMEnable = (1 << 0),
/// Indicates that the BASIC ROM is disabled, implying that the JASMIN ROM
/// fills its space.
BASICDisable = (1 << 1)
};
struct Delegate: public WD1770::Delegate {
virtual void jasmin_did_change_paging_flags(Jasmin *jasmin) = 0;
};
inline void set_delegate(Delegate *delegate) { delegate_ = delegate; WD1770::set_delegate(delegate); }
inline int get_paging_flags() { return paging_flags_; }
private:
std::array<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
size_t selected_drive_;
int paging_flags_ = 0;
Delegate *delegate_ = nullptr;
void posit_paging_flags(int new_flags) {
if(new_flags != paging_flags_) {
paging_flags_ = new_flags;
if(delegate_) delegate_->jasmin_did_change_paging_flags(this);
}
}
void set_motor_on(bool on) final;
bool motor_on_ = false;
};
};
#endif /* Jasmin_hpp */

View File

@ -45,6 +45,9 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
BIND(Space, KeySpace);
BIND(Enter, KeyReturn);
BIND(F12, KeyNMI);
BIND(F1, KeyJasminReset);
}
#undef BIND

View File

@ -33,6 +33,7 @@ enum Key: uint16_t {
KeyForwardSlash = 0x0700 | 0x08, Key0 = 0x0700 | 0x04, KeyL = 0x0700 | 0x02, Key8 = 0x0700 | 0x01,
KeyNMI = 0xfffd,
KeyJasminReset = 0xfffc,
};
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {

View File

@ -22,7 +22,8 @@ Microdisc::Microdisc() : WD1770(P1793) {
set_control_register(last_control_, 0xff);
}
void Microdisc::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
void Microdisc::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int d) {
const size_t drive = size_t(d);
if(!drives_[drive]) {
drives_[drive] = std::make_unique<Storage::Disk::Drive>(8000000, 300, 2);
if(drive == selected_drive_) set_drive(drives_[drive]);

View File

@ -20,7 +20,7 @@ class Microdisc: public WD::WD1770 {
public:
Microdisc();
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
void set_control_register(uint8_t control);
uint8_t get_interrupt_request_register();
uint8_t get_data_request_register();

View File

@ -8,6 +8,7 @@
#include "Oric.hpp"
#include "Jasmin.hpp"
#include "Keyboard.hpp"
#include "Microdisc.hpp"
#include "Video.hpp"
@ -40,6 +41,8 @@
namespace Oric {
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
enum ROM {
BASIC10 = 0, BASIC11, Microdisc, Colour
};
@ -63,17 +66,32 @@ std::vector<std::unique_ptr<Configurable::Option>> get_options() {
*/
class Keyboard {
public:
Keyboard() {
struct SpecialKeyHandler {
virtual void perform_special_key(Oric::Key key) = 0;
};
Keyboard(SpecialKeyHandler *handler) : special_key_handler_(handler) {
clear_all_keys();
}
/// Sets whether @c key is or is not pressed, per @c is_pressed.
void set_key_state(uint16_t key, bool is_pressed) {
uint8_t mask = key & 0xff;
int line = key >> 8;
switch(key) {
default: {
const uint8_t mask = key & 0xff;
const int line = key >> 8;
if(is_pressed) rows_[line] |= mask;
else rows_[line] &= ~mask;
if(is_pressed) rows_[line] |= mask;
else rows_[line] &= ~mask;
} break;
case KeyNMI:
case KeyJasminReset:
if(is_pressed) {
special_key_handler_->perform_special_key(Oric::Key(key));
}
break;
}
}
/// Sets all keys as unpressed.
@ -94,6 +112,7 @@ class Keyboard {
private:
uint8_t row_ = 0;
uint8_t rows_[8];
SpecialKeyHandler *const special_key_handler_;
};
/*!
@ -205,9 +224,11 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
public Utility::TypeRecipient,
public Storage::Tape::BinaryTapePlayer::Delegate,
public Microdisc::Delegate,
public Jasmin::Delegate,
public ClockingHint::Observer,
public Activity::Source,
public Machine {
public Machine,
public Keyboard::SpecialKeyHandler {
public:
ConcreteMachine(const Analyser::Static::Oric::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
@ -217,6 +238,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
speaker_(ay8910_),
via_port_handler_(audio_queue_, ay8910_, speaker_, tape_player_, keyboard_),
via_(via_port_handler_),
keyboard_(this),
diskii_(2000000) {
set_clock_rate(1000000);
speaker_.set_input_rate(1000000.0f);
@ -224,7 +246,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
tape_player_.set_delegate(this);
Memory::Fuzz(ram_, sizeof(ram_));
if constexpr (disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) {
if constexpr (disk_interface == DiskInterface::Pravetz) {
diskii_.set_clocking_hint_observer(this);
}
@ -244,10 +266,13 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
size_t diskii_state_machine_index = 0;
switch(disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
case DiskInterface::Jasmin:
rom_names.emplace_back(machine_name, "the ORIC Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89);
break;
case DiskInterface::Microdisc:
rom_names.emplace_back(machine_name, "the ORIC Microdisc ROM", "microdisc.rom", 8*1024, 0xa9664a9c);
break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
case DiskInterface::Pravetz:
rom_names.emplace_back(machine_name, "the 8DOS boot ROM", "8dos.rom", 512, 0x49a74c06);
// These ROM details are coupled with those in the DiskIICard.
diskii_state_machine_index = rom_names.size();
@ -268,11 +293,15 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
switch(disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
case DiskInterface::Jasmin:
jasmin_rom_ = std::move(*roms[2]);
jasmin_rom_.resize(2048);
break;
case DiskInterface::Microdisc:
microdisc_rom_ = std::move(*roms[2]);
microdisc_rom_.resize(8192);
break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz: {
case DiskInterface::Pravetz: {
pravetz_rom_ = std::move(*roms[2]);
pravetz_rom_.resize(512);
@ -285,16 +314,26 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
switch(target.disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
case DiskInterface::Microdisc:
microdisc_did_change_paging_flags(&microdisc_);
microdisc_.set_delegate(this);
break;
case DiskInterface::Jasmin:
jasmin_did_change_paging_flags(&jasmin_);
jasmin_.set_delegate(this);
break;
}
if(!target.loading_command.empty()) {
type_string(target.loading_command);
}
if(target.should_start_jasmin) {
// If Jasmin autostart is requested then plan to do so in 3 seconds; empirically long enough
// for the Oric to boot normally, before the Jasmin intercedes.
jasmin_reset_counter_ = 3000000;
}
switch(target.rom) {
case Analyser::Static::Oric::Target::ROM::BASIC10:
tape_get_byte_address_ = 0xe630;
@ -330,6 +369,17 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
use_fast_tape_hack_ = activate;
}
template <typename DiskInterface> bool insert_disks(const Analyser::Static::Media &media, DiskInterface &interface, int num_drives) {
int drive_index = 0;
for(auto &disk : media.disks) {
interface.set_disk(disk, drive_index);
++drive_index;
if(drive_index == num_drives) break;
}
return true;
}
bool insert_media(const Analyser::Static::Media &media) override final {
bool inserted = false;
@ -340,21 +390,14 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
if(!media.disks.empty()) {
switch(disk_interface) {
case Analyser::Static::Oric::Target::DiskInterface::Microdisc: {
inserted = true;
size_t drive_index = 0;
for(auto &disk : media.disks) {
if(drive_index < 4) microdisc_.set_disk(disk, drive_index);
++drive_index;
}
case DiskInterface::Jasmin:
inserted |= insert_disks(media, jasmin_, 4);
break;
case DiskInterface::Microdisc: {
inserted |= insert_disks(media, microdisc_, 4);
} break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz: {
inserted = true;
int drive_index = 0;
for(auto &disk : media.disks) {
if(drive_index < 2) diskii_.set_disk(disk, drive_index);
++drive_index;
}
case DiskInterface::Pravetz: {
inserted |= insert_disks(media, diskii_, 2);
} break;
default: break;
@ -385,13 +428,19 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
}
} else {
if((address & 0xff00) == 0x0300) {
if(address < 0x0310 || (disk_interface == Analyser::Static::Oric::Target::DiskInterface::None)) {
if(address < 0x0310 || (disk_interface == DiskInterface::None)) {
if(isReadOperation(operation)) *value = via_.read(address);
else via_.write(address, *value);
} else {
switch(disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
case DiskInterface::Jasmin:
if(address >= 0x3f4) {
if(isReadOperation(operation)) *value = jasmin_.read(address);
else jasmin_.write(address, *value);
}
break;
case DiskInterface::Microdisc:
switch(address) {
case 0x0310: case 0x0311: case 0x0312: case 0x0313:
if(isReadOperation(operation)) *value = microdisc_.read(address);
@ -406,7 +455,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
break;
}
break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
case DiskInterface::Pravetz:
if(address >= 0x0320) {
if(isReadOperation(operation)) *value = pravetz_rom_[pravetz_rom_base_pointer_ + (address & 0xff)];
else {
@ -448,10 +497,23 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
tape_player_.run_for(Cycles(1));
switch(disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
case DiskInterface::Jasmin:
jasmin_.run_for(Cycles(8));
// Jasmin autostart hack: wait for a period, then trigger a reset, having forced
// the Jasmin to page its ROM in first. I assume the latter being what the Jasmin's
// hardware boot button did.
if(jasmin_reset_counter_) {
--jasmin_reset_counter_;
if(!jasmin_reset_counter_) {
perform_special_key(KeyJasminReset);
}
}
break;
case DiskInterface::Microdisc:
microdisc_.run_for(Cycles(8));
break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
case DiskInterface::Pravetz:
if(diskii_clocking_preference_ == ClockingHint::Preference::RealTime) {
diskii_.set_data_input(*value);
diskii_.run_for(Cycles(2));
@ -461,6 +523,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
break;
}
cycles_since_video_update_++;
return Cycles(1);
}
@ -505,7 +568,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
// for Microdisc::Delegate
void microdisc_did_change_paging_flags(class Microdisc *microdisc) override final {
int flags = microdisc->get_paging_flags();
const int flags = microdisc->get_paging_flags();
if(!(flags&Microdisc::PagingFlags::BASICDisable)) {
ram_top_ = basic_visible_ram_top_;
paged_rom_ = rom_.data();
@ -519,6 +582,31 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
}
}
// Jasmin::Delegate
void jasmin_did_change_paging_flags(Jasmin *jasmin) override final {
const int flags = jasmin->get_paging_flags();
switch(flags) {
// BASIC enabled, overlay disabled.
default:
ram_top_ = basic_visible_ram_top_;
paged_rom_ = rom_.data();
break;
// Overlay RAM enabled, with or without BASIC.
case Jasmin::OverlayRAMEnable:
case Jasmin::OverlayRAMEnable | Jasmin::BASICDisable:
ram_top_ = basic_invisible_ram_top_;
break;
// BASIC disabled, overlay disabled.
case Jasmin::BASICDisable:
ram_top_ = 0xf7ff;
paged_rom_ = jasmin_rom_.data();
break;
}
}
// WD::WD1770::Delegate
void wd1770_did_change_output(WD::WD1770 *wd1770) override final {
set_interrupt_line();
}
@ -561,10 +649,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
void set_activity_observer(Activity::Observer *observer) override {
switch(disk_interface) {
default: break;
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
case DiskInterface::Microdisc:
microdisc_.set_activity_observer(observer);
break;
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
case DiskInterface::Pravetz:
diskii_.set_activity_observer(observer);
break;
}
@ -581,7 +669,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, ConcreteMachine, false> m6502_;
// RAM and ROM
std::vector<uint8_t> rom_, microdisc_rom_;
std::vector<uint8_t> rom_, microdisc_rom_, jasmin_rom_;
uint8_t ram_[65536];
Cycles cycles_since_video_update_;
inline void update_video() {
@ -610,10 +698,14 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
MOS::MOS6522::MOS6522<VIAPortHandler> via_;
Keyboard keyboard_;
// the Microdisc, if in use
// the Microdisc, if in use.
class Microdisc microdisc_;
// the Pravetz/Disk II, if in use
// the Jasmin, if in use.
Jasmin jasmin_;
int jasmin_reset_counter_ = 0;
// the Pravetz/Disk II, if in use.
Apple::DiskII diskii_;
Cycles cycles_since_diskii_update_;
void flush_diskii() {
@ -630,11 +722,40 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
// Helper to discern current IRQ state
inline void set_interrupt_line() {
bool irq_line = via_.get_interrupt_line();
if constexpr (disk_interface == Analyser::Static::Oric::Target::DiskInterface::Microdisc)
// The Microdisc directly provides an interrupt line.
if constexpr (disk_interface == DiskInterface::Microdisc) {
irq_line |= microdisc_.get_interrupt_request_line();
}
// The Jasmin reroutes its data request line to the processor's interrupt line.
if constexpr (disk_interface == DiskInterface::Jasmin) {
irq_line |= jasmin_.get_data_request_line();
}
m6502_.set_irq_line(irq_line);
}
// Keys that aren't read by polling.
void perform_special_key(Oric::Key key) override {
switch(key) {
default: break;
case KeyJasminReset:
jasmin_.write(0x3fa, 0);
jasmin_.write(0x3fb, 1);
m6502_.set_power_on(true);
break;
case KeyNMI:
// As luck would have it, the 6502's NMI line is edge triggered.
// So just forcing through an edge will work here.
m6502_.set_nmi_line(true);
m6502_.set_nmi_line(false);
break;
}
}
// MARK - typing
std::unique_ptr<Utility::StringSerialiser> string_serialiser_;
};
@ -645,11 +766,11 @@ using namespace Oric;
Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMachine::ROMFetcher &rom_fetcher) {
auto *const oric_target = dynamic_cast<const Analyser::Static::Oric::Target *>(target_hint);
using DiskInterface = Analyser::Static::Oric::Target::DiskInterface;
switch(oric_target->disk_interface) {
default: return new ConcreteMachine<DiskInterface::None>(*oric_target, rom_fetcher);
case DiskInterface::Microdisc: return new ConcreteMachine<DiskInterface::Microdisc>(*oric_target, rom_fetcher);
case DiskInterface::Pravetz: return new ConcreteMachine<DiskInterface::Pravetz>(*oric_target, rom_fetcher);
case DiskInterface::Jasmin: return new ConcreteMachine<DiskInterface::Jasmin>(*oric_target, rom_fetcher);
}
}

View File

@ -369,6 +369,8 @@
4B79E4461E3AF38600141F11 /* floppy525.png in Resources */ = {isa = PBXBuildFile; fileRef = 4B79E4431E3AF38600141F11 /* floppy525.png */; };
4B7A90E52041097C008514A2 /* ColecoVision.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7A90E42041097C008514A2 /* ColecoVision.cpp */; };
4B7A90ED20410A85008514A2 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7A90EC20410A85008514A2 /* StaticAnalyser.cpp */; };
4B7BA03023C2B19C00B98D9E /* Jasmin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */; };
4B7BA03123C2B19C00B98D9E /* Jasmin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */; };
4B7BC7F51F58F27800D1B1B4 /* 6502AllRAM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B6A4C911F58F09E00E3F787 /* 6502AllRAM.cpp */; };
4B7F188E2154825E00388727 /* MasterSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7F188C2154825D00388727 /* MasterSystem.cpp */; };
4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7F188C2154825D00388727 /* MasterSystem.cpp */; };
@ -1178,6 +1180,8 @@
4B7A90E42041097C008514A2 /* ColecoVision.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ColecoVision.cpp; sourceTree = "<group>"; };
4B7A90EB20410A85008514A2 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
4B7A90EC20410A85008514A2 /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Jasmin.cpp; path = Oric/Jasmin.cpp; sourceTree = "<group>"; };
4B7BA02F23C2B19B00B98D9E /* Jasmin.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Jasmin.hpp; path = Oric/Jasmin.hpp; sourceTree = "<group>"; };
4B7F188C2154825D00388727 /* MasterSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MasterSystem.cpp; sourceTree = "<group>"; };
4B7F188D2154825D00388727 /* MasterSystem.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MasterSystem.hpp; sourceTree = "<group>"; };
4B7F1895215486A100388727 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = "<group>"; };
@ -3529,10 +3533,12 @@
4BCF1FA51DADC3E10039D2E7 /* Oric */ = {
isa = PBXGroup;
children = (
4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */,
4B54C0BD1F8D8F450050900F /* Keyboard.cpp */,
4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */,
4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */,
4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */,
4B7BA02F23C2B19B00B98D9E /* Jasmin.hpp */,
4B54C0BE1F8D8F450050900F /* Keyboard.hpp */,
4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */,
4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */,
@ -4215,6 +4221,7 @@
4B055AD51FAE9B0B0060FFFF /* Video.cpp in Sources */,
4B894521201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
4B8318B522D3E548006DB630 /* Macintosh.cpp in Sources */,
4B7BA03123C2B19C00B98D9E /* Jasmin.cpp in Sources */,
4B7F188F2154825E00388727 /* MasterSystem.cpp in Sources */,
4B055AA51FAE85EF0060FFFF /* Encoder.cpp in Sources */,
4BD5D2692199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */,
@ -4472,6 +4479,7 @@
4B4518831F75E91A00926311 /* PCMTrack.cpp in Sources */,
4B0ACC3223775819008902D0 /* Atari2600.cpp in Sources */,
4B45189F1F75FD1C00926311 /* AcornADF.cpp in Sources */,
4B7BA03023C2B19C00B98D9E /* Jasmin.cpp in Sources */,
4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */,
4B4518A21F75FD1C00926311 /* G64.cpp in Sources */,
4B89452C201967B4007DE474 /* Tape.cpp in Sources */,

View File

@ -49,7 +49,8 @@ typedef NS_ENUM(NSInteger, CSMachineOricModel) {
typedef NS_ENUM(NSInteger, CSMachineOricDiskInterface) {
CSMachineOricDiskInterfaceNone,
CSMachineOricDiskInterfaceMicrodisc,
CSMachineOricDiskInterfacePravetz
CSMachineOricDiskInterfacePravetz,
CSMachineOricDiskInterfaceJasmin
};
typedef NS_ENUM(NSInteger, CSMachineVic20Region) {

View File

@ -102,6 +102,7 @@
case CSMachineOricDiskInterfaceNone: target->disk_interface = Target::DiskInterface::None; break;
case CSMachineOricDiskInterfaceMicrodisc: target->disk_interface = Target::DiskInterface::Microdisc; break;
case CSMachineOricDiskInterfacePravetz: target->disk_interface = Target::DiskInterface::Pravetz; break;
case CSMachineOricDiskInterfaceJasmin: target->disk_interface = Target::DiskInterface::Jasmin; break;
}
_targets.push_back(std::move(target));
}

View File

@ -18,7 +18,7 @@
<windowStyleMask key="styleMask" titled="YES" documentModal="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="720" height="205"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
<rect key="screenRect" x="0.0" y="0.0" width="3840" height="2137"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="720" height="205"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -64,7 +64,7 @@ Gw
<tabViewItems>
<tabViewItem label="Apple II" identifier="appleii" id="P59-QG-LOa">
<view key="view" id="dHz-Yv-GNq">
<rect key="frame" x="10" y="33" width="604" height="94"/>
<rect key="frame" x="10" y="33" width="674" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V5Z-dX-Ns4">
@ -305,7 +305,7 @@ Gw
</tabViewItem>
<tabViewItem label="Oric" identifier="oric" id="NSx-DC-p4M">
<view key="view" id="sOR-e0-8iZ">
<rect key="frame" x="10" y="33" width="604" height="94"/>
<rect key="frame" x="10" y="33" width="674" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0ct-tf-uRH">
@ -339,6 +339,7 @@ Gw
<items>
<menuItem title="None" state="on" id="XhK-Jh-oTW"/>
<menuItem title="Microdisc" tag="1" id="1jS-Lz-FRj"/>
<menuItem title="Jasmin" tag="3" id="CGU-gd-xov"/>
<menuItem title="8DOS" tag="2" id="edb-fl-C8Y"/>
</items>
</menu>
@ -370,7 +371,7 @@ Gw
</tabViewItem>
<tabViewItem label="Vic-20" identifier="vic20" id="cyO-PU-hSU">
<view key="view" id="fLI-XB-QCr">
<rect key="frame" x="10" y="33" width="604" height="94"/>
<rect key="frame" x="10" y="33" width="674" height="94"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ueK-gq-gaF">

View File

@ -195,6 +195,7 @@ class MachinePicker: NSObject {
switch oricDiskInterfaceButton!.selectedTag() {
case 1: diskInterface = .microdisc
case 2: diskInterface = .pravetz
case 3: diskInterface = .jasmin
default: break;
}