mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 16:31:31 +00:00
Merge branch 'master' into ShifterSync
This commit is contained in:
commit
229b7b36ed
@ -20,7 +20,9 @@
|
||||
|
||||
using namespace Analyser::Static::Oric;
|
||||
|
||||
static int Score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations) {
|
||||
namespace {
|
||||
|
||||
int score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations) {
|
||||
int score = 0;
|
||||
|
||||
for(const auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
|
||||
@ -30,7 +32,7 @@ static int Score(const Analyser::Static::MOS6502::Disassembly &disassembly, cons
|
||||
return score;
|
||||
}
|
||||
|
||||
static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
int basic10_score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
const std::set<uint16_t> rom_functions = {
|
||||
0x0228, 0x022b,
|
||||
0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738,
|
||||
@ -51,10 +53,10 @@ static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembl
|
||||
0x0228, 0x0229, 0x022a, 0x022b, 0x022c, 0x022d, 0x0230
|
||||
};
|
||||
|
||||
return Score(disassembly, rom_functions, variable_locations);
|
||||
return score(disassembly, rom_functions, variable_locations);
|
||||
}
|
||||
|
||||
static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
int basic11_score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
const std::set<uint16_t> rom_functions = {
|
||||
0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247,
|
||||
0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d,
|
||||
@ -76,10 +78,10 @@ static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembl
|
||||
0x0244, 0x0245, 0x0246, 0x0247, 0x0248, 0x0249, 0x024a, 0x024b, 0x024c
|
||||
};
|
||||
|
||||
return Score(disassembly, rom_functions, variable_locations);
|
||||
return score(disassembly, rom_functions, variable_locations);
|
||||
}
|
||||
|
||||
static bool IsMicrodisc(Storage::Encodings::MFM::Parser &parser) {
|
||||
bool is_microdisc(Storage::Encodings::MFM::Parser &parser) {
|
||||
/*
|
||||
The Microdisc boot sector is sector 2 of track 0 and contains a 23-byte signature.
|
||||
*/
|
||||
@ -100,17 +102,22 @@ 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) {
|
||||
bool is_400_loader(Storage::Encodings::MFM::Parser &parser, uint16_t range_start, uint16_t range_end) {
|
||||
/*
|
||||
The Jasmin boot sector is sector 1 of track 0 and is loaded at $400;
|
||||
disassemble it to test it for validity.
|
||||
Both the Jasmin and BD-DOS boot sectors are sector 1 of track 0 and are loaded at $400;
|
||||
use disassembly to test for likely matches.
|
||||
*/
|
||||
|
||||
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;
|
||||
// Take a copy of the first sampling, and keep only the final 256 bytes (assuming at least that many were found).
|
||||
std::vector<uint8_t> first_sample = sector->samples[0];
|
||||
if(first_sample.size() < 256) return false;
|
||||
if(first_sample.size() > 256) {
|
||||
first_sample.erase(first_sample.end() - 256, first_sample.end());
|
||||
}
|
||||
|
||||
// Grab a disassembly.
|
||||
const auto disassembly =
|
||||
@ -120,14 +127,24 @@ static bool IsJasmin(Storage::Encodings::MFM::Parser &parser) {
|
||||
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);
|
||||
register_hits += (address >= range_start && address <= range_end);
|
||||
}
|
||||
}
|
||||
|
||||
// Arbitrary, sure, but as long as at least two accesses to Jasmin registers are found, accept this.
|
||||
// Arbitrary, sure, but as long as at least two accesses to the requested register range are found, accept this.
|
||||
return register_hits >= 2;
|
||||
}
|
||||
|
||||
bool is_jasmin(Storage::Encodings::MFM::Parser &parser) {
|
||||
return is_400_loader(parser, 0x3f4, 0x3ff);
|
||||
}
|
||||
|
||||
bool is_bd500(Storage::Encodings::MFM::Parser &parser) {
|
||||
return is_400_loader(parser, 0x310, 0x323);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
@ -146,9 +163,7 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
|
||||
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);
|
||||
int basic11_score = Basic11Score(disassembly);
|
||||
if(basic10_score > basic11_score) basic10_votes++; else basic11_votes++;
|
||||
if(basic10_score(disassembly) > basic11_score(disassembly)) ++basic10_votes; else ++basic11_votes;
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,17 +173,22 @@ Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &med
|
||||
}
|
||||
|
||||
if(!media.disks.empty()) {
|
||||
// 8-DOS is recognised by a dedicated Disk II analyser, so check only for Microdisc and
|
||||
// Jasmin formats here.
|
||||
// 8-DOS is recognised by a dedicated Disk II analyser, so check only for Microdisc,
|
||||
// Jasmin and BD-DOS formats here.
|
||||
for(auto &disk: media.disks) {
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
if(IsMicrodisc(parser)) {
|
||||
|
||||
if(is_microdisc(parser)) {
|
||||
target->disk_interface = Target::DiskInterface::Microdisc;
|
||||
target->media.disks.push_back(disk);
|
||||
} else if(IsJasmin(parser)) {
|
||||
} else if(is_jasmin(parser)) {
|
||||
target->disk_interface = Target::DiskInterface::Jasmin;
|
||||
target->should_start_jasmin = true;
|
||||
target->media.disks.push_back(disk);
|
||||
} else if(is_bd500(parser)) {
|
||||
target->disk_interface = Target::DiskInterface::BD500;
|
||||
target->media.disks.push_back(disk);
|
||||
target->rom = Target::ROM::BASIC10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ struct Target: public ::Analyser::Static::Target {
|
||||
Microdisc,
|
||||
Pravetz,
|
||||
Jasmin,
|
||||
BD500,
|
||||
None
|
||||
};
|
||||
|
||||
|
@ -824,6 +824,10 @@ void WD1770::set_head_loaded(bool head_loaded) {
|
||||
if(head_loaded) posit_event(int(Event1770::HeadLoad));
|
||||
}
|
||||
|
||||
bool WD1770::get_head_loaded() {
|
||||
return head_is_loaded_;
|
||||
}
|
||||
|
||||
ClockingHint::Preference WD1770::preferred_clocking() {
|
||||
if(status_.busy) return ClockingHint::Preference::RealTime;
|
||||
return Storage::Disk::MFMController::preferred_clocking();
|
||||
|
@ -80,6 +80,9 @@ class WD1770: public Storage::Disk::MFMController {
|
||||
virtual void set_motor_on(bool motor_on);
|
||||
void set_head_loaded(bool head_loaded);
|
||||
|
||||
/// @returns The last value posted to @c set_head_loaded.
|
||||
bool get_head_loaded();
|
||||
|
||||
private:
|
||||
Personality personality_;
|
||||
inline bool has_motor_on_line() { return (personality_ != P1793 ) && (personality_ != P1773); }
|
||||
|
146
Machines/Oric/BD500.cpp
Normal file
146
Machines/Oric/BD500.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
//
|
||||
// BD500.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/01/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "BD500.hpp"
|
||||
|
||||
using namespace Oric;
|
||||
|
||||
BD500::BD500() : DiskController(P1793, 9000000, Storage::Disk::Drive::ReadyType::ShugartModifiedRDY) {
|
||||
disable_basic_rom_ = true;
|
||||
select_paged_item();
|
||||
set_is_double_density(true);
|
||||
}
|
||||
|
||||
void BD500::write(int address, uint8_t value) {
|
||||
access(address);
|
||||
|
||||
if(address >= 0x0320 && address <= 0x0323) {
|
||||
// if(address == 0x320) printf("Command %02x\n", value);
|
||||
WD::WD1770::write(address, value);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t BD500::read(int address) {
|
||||
access(address);
|
||||
|
||||
switch(address) {
|
||||
default: return 0xff;
|
||||
|
||||
case 0x0320: case 0x0321: case 0x0322: case 0x0323:
|
||||
return WD::WD1770::read(address);
|
||||
|
||||
case 0x312: return (get_data_request_line() ? 0x80 : 0x00) | (get_interrupt_request_line() ? 0x40 : 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
void BD500::access(int address) {
|
||||
// Determine whether to perform a command.
|
||||
switch(address) {
|
||||
case 0x0320: case 0x0321: case 0x0322: case 0x0323: case 0x0312:
|
||||
return;
|
||||
|
||||
case 0x310: enable_overlay_ram_ = true; break;
|
||||
case 0x313: enable_overlay_ram_ = false; break;
|
||||
case 0x317: disable_basic_rom_ = false; break; // Could be 0x311.
|
||||
|
||||
default:
|
||||
// printf("Switch %04x???\n", address);
|
||||
break;
|
||||
}
|
||||
|
||||
select_paged_item();
|
||||
}
|
||||
|
||||
/*
|
||||
The following was used when trying to find appropriate soft switch locations. It is preserved
|
||||
as the values I have above are unlikely to be wholly correct and further research might be
|
||||
desirable.
|
||||
|
||||
void BD500::access(int address) {
|
||||
// 0,1,4,5,10,11 -> 64kb Atmos
|
||||
// 2,3,9 -> 56kb Atmos.
|
||||
// Broken: 6, 7, 8
|
||||
|
||||
int order = 5;
|
||||
int commands[4];
|
||||
std::vector<int> available_commands = {0, 1, 2, 3};
|
||||
const int modulos[] = {6, 2, 1, 1};
|
||||
|
||||
for(int c = 0; c < 4; ++c) {
|
||||
const int index = order / modulos[c];
|
||||
commands[c] = available_commands[size_t(index)];
|
||||
available_commands.erase(available_commands.begin() + index);
|
||||
order %= modulos[c];
|
||||
}
|
||||
|
||||
|
||||
// Determine whether to perform a command.
|
||||
int index = -1;
|
||||
switch(address) {
|
||||
case 0x0320: case 0x0321: case 0x0322: case 0x0323: case 0x0312:
|
||||
return;
|
||||
|
||||
case 0x310: index = 0; break;
|
||||
case 0x313: index = 1; break;
|
||||
case 0x314: index = 2; break;
|
||||
case 0x317: index = 3; break;
|
||||
|
||||
default:
|
||||
printf("Switch %04x???\n", address);
|
||||
break;
|
||||
}
|
||||
|
||||
select_paged_item();
|
||||
|
||||
if(index >= 0) {
|
||||
switch(commands[index]) {
|
||||
case 0: enable_overlay_ram_ = true; break; // +RAM
|
||||
case 1: disable_basic_rom_ = false; break; // -rom
|
||||
case 2: disable_basic_rom_ = true; break; // +rom
|
||||
case 3: enable_overlay_ram_ = false; break; // -RAM
|
||||
|
||||
}
|
||||
select_paged_item();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void BD500::set_head_load_request(bool head_load) {
|
||||
// Turn all motors on or off; if off then unload the head instantly.
|
||||
is_loading_head_ |= head_load;
|
||||
for(auto &drive : drives_) {
|
||||
if(drive) drive->set_motor_on(head_load);
|
||||
}
|
||||
if(!head_load) set_head_loaded(false);
|
||||
}
|
||||
|
||||
void BD500::run_for(const Cycles cycles) {
|
||||
// If a head load is in progress and the selected drive is now ready,
|
||||
// declare head loaded.
|
||||
if(is_loading_head_ && drives_[selected_drive_] && drives_[selected_drive_]->get_is_ready()) {
|
||||
set_head_loaded(true);
|
||||
is_loading_head_ = false;
|
||||
}
|
||||
|
||||
WD::WD1770::run_for(cycles);
|
||||
}
|
||||
|
||||
void BD500::set_activity_observer(Activity::Observer *observer) {
|
||||
observer_ = observer;
|
||||
if(observer) {
|
||||
observer->register_led("BD-500");
|
||||
observer_->set_led_status("BD-500", get_head_loaded());
|
||||
}
|
||||
}
|
||||
|
||||
void BD500::set_head_loaded(bool loaded) {
|
||||
WD::WD1770::set_head_loaded(loaded);
|
||||
if(observer_) {
|
||||
observer_->set_led_status("BD-500", loaded);
|
||||
}
|
||||
}
|
43
Machines/Oric/BD500.hpp
Normal file
43
Machines/Oric/BD500.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// BD500.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/01/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef BD500_hpp
|
||||
#define BD500_hpp
|
||||
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
#include "DiskController.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace Oric {
|
||||
|
||||
class BD500: public DiskController {
|
||||
public:
|
||||
BD500();
|
||||
|
||||
void write(int address, uint8_t value);
|
||||
uint8_t read(int address);
|
||||
|
||||
void run_for(const Cycles cycles);
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
void set_head_load_request(bool head_load) final;
|
||||
bool is_loading_head_ = false;
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
|
||||
void access(int address);
|
||||
void set_head_loaded(bool loaded);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif /* BD500_hpp */
|
85
Machines/Oric/DiskController.hpp
Normal file
85
Machines/Oric/DiskController.hpp
Normal file
@ -0,0 +1,85 @@
|
||||
//
|
||||
// DiskController.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 14/01/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef DiskController_h
|
||||
#define DiskController_h
|
||||
|
||||
namespace Oric {
|
||||
|
||||
class DiskController: public WD::WD1770 {
|
||||
public:
|
||||
DiskController(WD::WD1770::Personality personality, int clock_rate, Storage::Disk::Drive::ReadyType ready_type) :
|
||||
WD::WD1770(personality), clock_rate_(clock_rate), ready_type_(ready_type) {}
|
||||
|
||||
void 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>(clock_rate_, 300, 2, ready_type_);
|
||||
if(drive == selected_drive_) set_drive(drives_[drive]);
|
||||
}
|
||||
drives_[drive]->set_disk(disk);
|
||||
}
|
||||
|
||||
enum class PagedItem {
|
||||
DiskROM,
|
||||
BASIC,
|
||||
RAM
|
||||
};
|
||||
|
||||
struct Delegate: public WD1770::Delegate {
|
||||
virtual void disk_controller_did_change_paged_item(DiskController *controller) = 0;
|
||||
};
|
||||
inline void set_delegate(Delegate *delegate) {
|
||||
delegate_ = delegate;
|
||||
WD1770::set_delegate(delegate);
|
||||
if(delegate) delegate->disk_controller_did_change_paged_item(this);
|
||||
}
|
||||
inline PagedItem get_paged_item() {
|
||||
return paged_item_;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::array<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
|
||||
size_t selected_drive_ = 0;
|
||||
void select_drive(size_t drive) {
|
||||
if(drive != selected_drive_) {
|
||||
selected_drive_ = drive;
|
||||
set_drive(drives_[selected_drive_]);
|
||||
}
|
||||
}
|
||||
Delegate *delegate_ = nullptr;
|
||||
|
||||
bool enable_overlay_ram_ = false;
|
||||
bool disable_basic_rom_ = false;
|
||||
void select_paged_item() {
|
||||
PagedItem item = PagedItem::RAM;
|
||||
if(!enable_overlay_ram_) {
|
||||
item = disable_basic_rom_ ? PagedItem::DiskROM : PagedItem::BASIC;
|
||||
}
|
||||
set_paged_item(item);
|
||||
}
|
||||
|
||||
private:
|
||||
PagedItem paged_item_ = PagedItem::DiskROM;
|
||||
int clock_rate_;
|
||||
Storage::Disk::Drive::ReadyType ready_type_;
|
||||
|
||||
inline void set_paged_item(PagedItem item) {
|
||||
if(paged_item_ == item) return;
|
||||
paged_item_ = item;
|
||||
if(delegate_) {
|
||||
delegate_->disk_controller_did_change_paged_item(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* DiskController_h */
|
@ -12,17 +12,9 @@ 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) {
|
||||
Jasmin::Jasmin() : DiskController(P1770, 8000000, Storage::Disk::Drive::ReadyType::ShugartRDY) {
|
||||
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);
|
||||
select_paged_item();
|
||||
}
|
||||
|
||||
void Jasmin::write(int address, uint8_t value) {
|
||||
@ -40,23 +32,20 @@ void Jasmin::write(int address, uint8_t value) {
|
||||
|
||||
case 0x3fa: {
|
||||
// If b0, enable overlay RAM.
|
||||
posit_paging_flags((paging_flags_ & BASICDisable) | ((value & 1) ? OverlayRAMEnable : 0));
|
||||
enable_overlay_ram_ = value & 1;
|
||||
select_paged_item();
|
||||
} break;
|
||||
|
||||
case 0x3fb:
|
||||
// If b0, disable BASIC ROM.
|
||||
posit_paging_flags((paging_flags_ & OverlayRAMEnable) | ((value & 1) ? BASICDisable : 0));
|
||||
disable_basic_rom_ = value & 1;
|
||||
select_paged_item();
|
||||
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_);
|
||||
}
|
||||
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(false);
|
||||
select_drive(size_t(address - 0x3fc));
|
||||
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(motor_on_);
|
||||
} break;
|
||||
|
||||
default:
|
||||
@ -67,4 +56,15 @@ void Jasmin::write(int address, uint8_t value) {
|
||||
void Jasmin::set_motor_on(bool on) {
|
||||
motor_on_ = on;
|
||||
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(motor_on_);
|
||||
if(observer_) {
|
||||
observer_->set_led_status("Jasmin", on);
|
||||
}
|
||||
}
|
||||
|
||||
void Jasmin::set_activity_observer(Activity::Observer *observer) {
|
||||
observer_ = observer;
|
||||
if(observer) {
|
||||
observer->register_led("Jasmin");
|
||||
observer_->set_led_status("Jasmin", motor_on_);
|
||||
}
|
||||
}
|
||||
|
@ -11,48 +11,23 @@
|
||||
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include "DiskController.hpp"
|
||||
|
||||
namespace Oric {
|
||||
|
||||
class Jasmin: public WD::WD1770 {
|
||||
class Jasmin: public DiskController {
|
||||
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_; }
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
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;
|
||||
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -18,20 +18,10 @@ namespace {
|
||||
const Cycles::IntType head_load_request_counter_target = 7653333;
|
||||
}
|
||||
|
||||
Microdisc::Microdisc() : WD1770(P1793) {
|
||||
Microdisc::Microdisc() : DiskController(P1793, 8000000, Storage::Disk::Drive::ReadyType::ShugartRDY) {
|
||||
set_control_register(last_control_, 0xff);
|
||||
}
|
||||
|
||||
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]);
|
||||
drives_[drive]->set_activity_observer(observer_, drive_name(drive), false);
|
||||
}
|
||||
drives_[drive]->set_disk(disk);
|
||||
}
|
||||
|
||||
void Microdisc::set_control_register(uint8_t control) {
|
||||
const uint8_t changes = last_control_ ^ control;
|
||||
last_control_ = control;
|
||||
@ -73,8 +63,9 @@ void Microdisc::set_control_register(uint8_t control, uint8_t changes) {
|
||||
// b7: EPROM select (0 = select)
|
||||
// b1: ROM disable (0 = disable)
|
||||
if(changes & 0x82) {
|
||||
paging_flags_ = ((control & 0x02) ? 0 : BASICDisable) | ((control & 0x80) ? MicrodiscDisable : 0);
|
||||
if(delegate_) delegate_->microdisc_did_change_paging_flags(this);
|
||||
enable_overlay_ram_ = control & 0x80;
|
||||
disable_basic_rom_ = !(control & 0x02);
|
||||
select_paged_item();
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,23 +112,10 @@ void Microdisc::run_for(const Cycles cycles) {
|
||||
WD::WD1770::run_for(cycles);
|
||||
}
|
||||
|
||||
bool Microdisc::get_drive_is_ready() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Microdisc::set_activity_observer(Activity::Observer *observer) {
|
||||
observer_ = observer;
|
||||
if(observer) {
|
||||
observer->register_led("Microdisc");
|
||||
observer_->set_led_status("Microdisc", head_load_request_);
|
||||
}
|
||||
size_t c = 0;
|
||||
for(auto &drive : drives_) {
|
||||
if(drive) drive->set_activity_observer(observer, drive_name(c), false);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Microdisc::drive_name(size_t index) {
|
||||
return "Drive " + std::to_string(index);
|
||||
}
|
||||
|
@ -11,16 +11,14 @@
|
||||
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include "DiskController.hpp"
|
||||
|
||||
namespace Oric {
|
||||
|
||||
class Microdisc: public WD::WD1770 {
|
||||
class Microdisc: public DiskController {
|
||||
public:
|
||||
Microdisc();
|
||||
|
||||
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();
|
||||
@ -29,42 +27,19 @@ class Microdisc: public WD::WD1770 {
|
||||
|
||||
void run_for(const Cycles cycles);
|
||||
|
||||
enum PagingFlags {
|
||||
/// Indicates that the BASIC ROM should be disabled; if this is set then either
|
||||
/// the Microdisc ROM or overlay RAM will be visible. If it is not set, BASIC
|
||||
/// should be visible.
|
||||
BASICDisable = (1 << 0),
|
||||
|
||||
/// Indicates that the Microdisc ROM is disabled. If BASIC is disabled and the Microdisc
|
||||
/// is also disabled, overlay RAM should be visible.
|
||||
MicrodiscDisable = (1 << 1)
|
||||
};
|
||||
|
||||
class Delegate: public WD1770::Delegate {
|
||||
public:
|
||||
virtual void microdisc_did_change_paging_flags(Microdisc *microdisc) = 0;
|
||||
};
|
||||
inline void set_delegate(Delegate *delegate) { delegate_ = delegate; WD1770::set_delegate(delegate); }
|
||||
inline int get_paging_flags() { return paging_flags_; }
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
void set_control_register(uint8_t control, uint8_t changes);
|
||||
void set_head_load_request(bool head_load) override;
|
||||
bool get_drive_is_ready();
|
||||
void set_head_load_request(bool head_load) final;
|
||||
|
||||
std::array<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
|
||||
size_t selected_drive_;
|
||||
void set_control_register(uint8_t control, uint8_t changes);
|
||||
uint8_t last_control_ = 0;
|
||||
bool irq_enable_ = false;
|
||||
int paging_flags_ = BASICDisable;
|
||||
|
||||
Cycles::IntType head_load_request_counter_ = -1;
|
||||
bool head_load_request_ = false;
|
||||
Delegate *delegate_ = nullptr;
|
||||
uint8_t last_control_ = 0;
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
|
||||
std::string drive_name(size_t index);
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Oric.hpp"
|
||||
|
||||
#include "BD500.hpp"
|
||||
#include "Jasmin.hpp"
|
||||
#include "Keyboard.hpp"
|
||||
#include "Microdisc.hpp"
|
||||
@ -223,8 +224,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
||||
public Utility::TypeRecipient,
|
||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||
public Microdisc::Delegate,
|
||||
public Jasmin::Delegate,
|
||||
public DiskController::Delegate,
|
||||
public ClockingHint::Observer,
|
||||
public Activity::Source,
|
||||
public Machine,
|
||||
@ -244,7 +244,16 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
speaker_.set_input_rate(1000000.0f);
|
||||
via_port_handler_.set_interrupt_delegate(this);
|
||||
tape_player_.set_delegate(this);
|
||||
|
||||
// Slight hack here: I'm unclear what RAM should look like at startup.
|
||||
// Actually, I think completely random might be right since the Microdisc
|
||||
// sort of assumes it, but also the BD-500 never explicitly sets PAL mode
|
||||
// so I can't have any switch-to-NTSC bytes in the display area. Hence:
|
||||
// disallow all atributes.
|
||||
Memory::Fuzz(ram_, sizeof(ram_));
|
||||
for(size_t c = 0; c < sizeof(ram_); ++c) {
|
||||
ram_[c] |= 0x40;
|
||||
}
|
||||
|
||||
if constexpr (disk_interface == DiskInterface::Pravetz) {
|
||||
diskii_.set_clocking_hint_observer(this);
|
||||
@ -266,6 +275,9 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
size_t diskii_state_machine_index = 0;
|
||||
switch(disk_interface) {
|
||||
default: break;
|
||||
case DiskInterface::BD500:
|
||||
rom_names.emplace_back(machine_name, "the ORIC Byte Drive 500 ROM", "bd500.rom", 8*1024, 0x61952e34);
|
||||
break;
|
||||
case DiskInterface::Jasmin:
|
||||
rom_names.emplace_back(machine_name, "the ORIC Jasmin ROM", "jasmin.rom", 2*1024, 0x37220e89);
|
||||
break;
|
||||
@ -293,13 +305,17 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
|
||||
switch(disk_interface) {
|
||||
default: break;
|
||||
case DiskInterface::BD500:
|
||||
disk_rom_ = std::move(*roms[2]);
|
||||
disk_rom_.resize(8192);
|
||||
break;
|
||||
case DiskInterface::Jasmin:
|
||||
jasmin_rom_ = std::move(*roms[2]);
|
||||
jasmin_rom_.resize(2048);
|
||||
disk_rom_ = std::move(*roms[2]);
|
||||
disk_rom_.resize(2048);
|
||||
break;
|
||||
case DiskInterface::Microdisc:
|
||||
microdisc_rom_ = std::move(*roms[2]);
|
||||
microdisc_rom_.resize(8192);
|
||||
disk_rom_ = std::move(*roms[2]);
|
||||
disk_rom_.resize(8192);
|
||||
break;
|
||||
case DiskInterface::Pravetz: {
|
||||
pravetz_rom_ = std::move(*roms[2]);
|
||||
@ -314,14 +330,15 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
|
||||
switch(target.disk_interface) {
|
||||
default: break;
|
||||
case DiskInterface::Microdisc:
|
||||
microdisc_did_change_paging_flags(µdisc_);
|
||||
microdisc_.set_delegate(this);
|
||||
case DiskInterface::BD500:
|
||||
bd500_.set_delegate(this);
|
||||
break;
|
||||
case DiskInterface::Jasmin:
|
||||
jasmin_did_change_paging_flags(&jasmin_);
|
||||
jasmin_.set_delegate(this);
|
||||
break;
|
||||
case DiskInterface::Microdisc:
|
||||
microdisc_.set_delegate(this);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!target.loading_command.empty()) {
|
||||
@ -353,7 +370,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
audio_queue_.flush();
|
||||
}
|
||||
|
||||
void set_key_state(uint16_t key, bool is_pressed) override final {
|
||||
void set_key_state(uint16_t key, bool is_pressed) final {
|
||||
if(key == KeyNMI) {
|
||||
m6502_.set_nmi_line(is_pressed);
|
||||
} else {
|
||||
@ -361,7 +378,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
}
|
||||
}
|
||||
|
||||
void clear_all_keys() override final {
|
||||
void clear_all_keys() final {
|
||||
keyboard_.clear_all_keys();
|
||||
}
|
||||
|
||||
@ -380,7 +397,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
return true;
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
bool insert_media(const Analyser::Static::Media &media) final {
|
||||
bool inserted = false;
|
||||
|
||||
if(!media.tapes.empty()) {
|
||||
@ -390,16 +407,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
|
||||
if(!media.disks.empty()) {
|
||||
switch(disk_interface) {
|
||||
case DiskInterface::Jasmin:
|
||||
inserted |= insert_disks(media, jasmin_, 4);
|
||||
break;
|
||||
case DiskInterface::Microdisc: {
|
||||
inserted |= insert_disks(media, microdisc_, 4);
|
||||
} break;
|
||||
case DiskInterface::Pravetz: {
|
||||
inserted |= insert_disks(media, diskii_, 2);
|
||||
} break;
|
||||
|
||||
case DiskInterface::BD500: inserted |= insert_disks(media, bd500_, 4); break;
|
||||
case DiskInterface::Jasmin: inserted |= insert_disks(media, jasmin_, 4); break;
|
||||
case DiskInterface::Microdisc: inserted |= insert_disks(media, microdisc_, 4); break;
|
||||
case DiskInterface::Pravetz: inserted |= insert_disks(media, diskii_, 2); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@ -434,6 +445,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
} else {
|
||||
switch(disk_interface) {
|
||||
default: break;
|
||||
case DiskInterface::BD500:
|
||||
if(isReadOperation(operation)) *value = bd500_.read(address);
|
||||
else bd500_.write(address, *value);
|
||||
break;
|
||||
case DiskInterface::Jasmin:
|
||||
if(address >= 0x3f4) {
|
||||
if(isReadOperation(operation)) *value = jasmin_.read(address);
|
||||
@ -497,8 +512,11 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
tape_player_.run_for(Cycles(1));
|
||||
switch(disk_interface) {
|
||||
default: break;
|
||||
case DiskInterface::BD500:
|
||||
bd500_.run_for(Cycles(9)); // i.e. effective clock rate of 9Mhz.
|
||||
break;
|
||||
case DiskInterface::Jasmin:
|
||||
jasmin_.run_for(Cycles(8));
|
||||
jasmin_.run_for(Cycles(8));; // i.e. effective clock rate of 8Mhz.
|
||||
|
||||
// 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
|
||||
@ -511,12 +529,12 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
}
|
||||
break;
|
||||
case DiskInterface::Microdisc:
|
||||
microdisc_.run_for(Cycles(8));
|
||||
microdisc_.run_for(Cycles(8));; // i.e. effective clock rate of 8Mhz.
|
||||
break;
|
||||
case DiskInterface::Pravetz:
|
||||
if(diskii_clocking_preference_ == ClockingHint::Preference::RealTime) {
|
||||
diskii_.set_data_input(*value);
|
||||
diskii_.run_for(Cycles(2));
|
||||
diskii_.run_for(Cycles(2));; // i.e. effective clock rate of 2Mhz.
|
||||
} else {
|
||||
cycles_since_diskii_update_ += Cycles(2);
|
||||
}
|
||||
@ -534,74 +552,53 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
}
|
||||
|
||||
// to satisfy CRTMachine::Machine
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final {
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
|
||||
video_output_.set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) final {
|
||||
video_output_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() override final {
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
|
||||
void run_for(const Cycles cycles) override final {
|
||||
void run_for(const Cycles cycles) final {
|
||||
m6502_.run_for(cycles);
|
||||
}
|
||||
|
||||
// to satisfy MOS::MOS6522IRQDelegate::Delegate
|
||||
void mos6522_did_change_interrupt_status(void *mos6522) override final {
|
||||
void mos6522_did_change_interrupt_status(void *mos6522) final {
|
||||
set_interrupt_line();
|
||||
}
|
||||
|
||||
// to satisfy Storage::Tape::BinaryTapePlayer::Delegate
|
||||
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player) override final {
|
||||
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player) final {
|
||||
// set CB1
|
||||
via_.set_control_line_input(MOS::MOS6522::Port::B, MOS::MOS6522::Line::One, !tape_player->get_input());
|
||||
}
|
||||
|
||||
// for Utility::TypeRecipient::Delegate
|
||||
void type_string(const std::string &string) override final {
|
||||
void type_string(const std::string &string) final {
|
||||
string_serialiser_ = std::make_unique<Utility::StringSerialiser>(string, true);
|
||||
}
|
||||
|
||||
// for Microdisc::Delegate
|
||||
void microdisc_did_change_paging_flags(class Microdisc *microdisc) override final {
|
||||
const int flags = microdisc->get_paging_flags();
|
||||
if(!(flags&Microdisc::PagingFlags::BASICDisable)) {
|
||||
ram_top_ = basic_visible_ram_top_;
|
||||
paged_rom_ = rom_.data();
|
||||
} else {
|
||||
if(flags&Microdisc::PagingFlags::MicrodiscDisable) {
|
||||
ram_top_ = basic_invisible_ram_top_;
|
||||
} else {
|
||||
ram_top_ = 0xdfff;
|
||||
paged_rom_ = microdisc_rom_.data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// DiskController::Delegate
|
||||
void disk_controller_did_change_paged_item(DiskController *controller) final {
|
||||
switch(controller->get_paged_item()) {
|
||||
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:
|
||||
case DiskController::PagedItem::RAM:
|
||||
ram_top_ = basic_invisible_ram_top_;
|
||||
break;
|
||||
|
||||
// BASIC disabled, overlay disabled.
|
||||
case Jasmin::BASICDisable:
|
||||
ram_top_ = 0xf7ff;
|
||||
paged_rom_ = jasmin_rom_.data();
|
||||
case DiskController::PagedItem::DiskROM:
|
||||
ram_top_ = uint16_t(0xffff - disk_rom_.size());
|
||||
paged_rom_ = disk_rom_.data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -649,6 +646,12 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
switch(disk_interface) {
|
||||
default: break;
|
||||
case DiskInterface::BD500:
|
||||
bd500_.set_activity_observer(observer);
|
||||
break;
|
||||
case DiskInterface::Jasmin:
|
||||
jasmin_.set_activity_observer(observer);
|
||||
break;
|
||||
case DiskInterface::Microdisc:
|
||||
microdisc_.set_activity_observer(observer);
|
||||
break;
|
||||
@ -669,7 +672,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_, jasmin_rom_;
|
||||
std::vector<uint8_t> rom_, disk_rom_;
|
||||
uint8_t ram_[65536];
|
||||
Cycles cycles_since_video_update_;
|
||||
inline void update_video() {
|
||||
@ -705,6 +708,9 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
Jasmin jasmin_;
|
||||
int jasmin_reset_counter_ = 0;
|
||||
|
||||
// the BD-500, if in use.
|
||||
BD500 bd500_;
|
||||
|
||||
// the Pravetz/Disk II, if in use.
|
||||
Apple::DiskII diskii_;
|
||||
Cycles cycles_since_diskii_update_;
|
||||
@ -771,6 +777,7 @@ Machine *Machine::Oric(const Analyser::Static::Target *target_hint, const ROMMac
|
||||
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);
|
||||
case DiskInterface::BD500: return new ConcreteMachine<DiskInterface::BD500>(*oric_target, rom_fetcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,6 +369,7 @@
|
||||
4B7BA03023C2B19C00B98D9E /* Jasmin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */; };
|
||||
4B7BA03123C2B19C00B98D9E /* Jasmin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */; };
|
||||
4B7BA03423C58B1F00B98D9E /* STX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03323C58B1E00B98D9E /* STX.cpp */; };
|
||||
4B7BA03723CEB86000B98D9E /* BD500.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BA03523CEB86000B98D9E /* BD500.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 */; };
|
||||
@ -1181,6 +1182,9 @@
|
||||
4B7BA02F23C2B19B00B98D9E /* Jasmin.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Jasmin.hpp; path = Oric/Jasmin.hpp; sourceTree = "<group>"; };
|
||||
4B7BA03223C58B1E00B98D9E /* STX.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = STX.hpp; sourceTree = "<group>"; };
|
||||
4B7BA03323C58B1E00B98D9E /* STX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = STX.cpp; sourceTree = "<group>"; };
|
||||
4B7BA03523CEB86000B98D9E /* BD500.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BD500.cpp; path = Oric/BD500.cpp; sourceTree = "<group>"; };
|
||||
4B7BA03623CEB86000B98D9E /* BD500.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = BD500.hpp; path = Oric/BD500.hpp; sourceTree = "<group>"; };
|
||||
4B7BA03823CEB8D200B98D9E /* DiskController.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DiskController.hpp; path = Oric/DiskController.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>"; };
|
||||
@ -3533,16 +3537,19 @@
|
||||
4BCF1FA51DADC3E10039D2E7 /* Oric */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B7BA03523CEB86000B98D9E /* BD500.cpp */,
|
||||
4B7BA02E23C2B19B00B98D9E /* Jasmin.cpp */,
|
||||
4B54C0BD1F8D8F450050900F /* Keyboard.cpp */,
|
||||
4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */,
|
||||
4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */,
|
||||
4B2BFDB01DAEF5FF001A68B8 /* Video.cpp */,
|
||||
4B7BA03623CEB86000B98D9E /* BD500.hpp */,
|
||||
4B7BA02F23C2B19B00B98D9E /* Jasmin.hpp */,
|
||||
4B54C0BE1F8D8F450050900F /* Keyboard.hpp */,
|
||||
4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */,
|
||||
4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */,
|
||||
4B2BFDB11DAEF5FF001A68B8 /* Video.hpp */,
|
||||
4B7BA03823CEB8D200B98D9E /* DiskController.hpp */,
|
||||
);
|
||||
name = Oric;
|
||||
sourceTree = "<group>";
|
||||
@ -3838,6 +3845,7 @@
|
||||
};
|
||||
4BB73E9D1B587A5100552FC2 = {
|
||||
CreatedOnToolsVersion = 7.0;
|
||||
DevelopmentTeam = CP2SKEB3XT;
|
||||
LastSwiftMigration = 1020;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
@ -4455,6 +4463,7 @@
|
||||
4B89452E201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4BD5D2682199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */,
|
||||
4BC890D3230F86020025A55A /* DirectAccessDevice.cpp in Sources */,
|
||||
4B7BA03723CEB86000B98D9E /* BD500.cpp in Sources */,
|
||||
4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */,
|
||||
4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */,
|
||||
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */,
|
||||
@ -5047,6 +5056,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = CP2SKEB3XT;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
@ -5089,6 +5100,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = CP2SKEB3XT;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
|
@ -50,7 +50,8 @@ typedef NS_ENUM(NSInteger, CSMachineOricDiskInterface) {
|
||||
CSMachineOricDiskInterfaceNone,
|
||||
CSMachineOricDiskInterfaceMicrodisc,
|
||||
CSMachineOricDiskInterfacePravetz,
|
||||
CSMachineOricDiskInterfaceJasmin
|
||||
CSMachineOricDiskInterfaceJasmin,
|
||||
CSMachineOricDiskInterfaceBD500
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineVic20Region) {
|
||||
|
@ -103,6 +103,7 @@
|
||||
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;
|
||||
case CSMachineOricDiskInterfaceBD500: target->disk_interface = Target::DiskInterface::BD500; break;
|
||||
}
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
|
@ -331,7 +331,7 @@ Gw
|
||||
</popUpButtonCell>
|
||||
</popUpButton>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fYL-p6-wyn">
|
||||
<rect key="frame" x="111" y="36" width="97" height="25"/>
|
||||
<rect key="frame" x="111" y="36" width="129" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="None" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="XhK-Jh-oTW" id="aYb-m1-H9X">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -341,6 +341,7 @@ Gw
|
||||
<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"/>
|
||||
<menuItem title="Byte Drive 500" tag="4" id="lkS-Rr-m1D"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
|
@ -196,6 +196,7 @@ class MachinePicker: NSObject {
|
||||
case 1: diskInterface = .microdisc
|
||||
case 2: diskInterface = .pravetz
|
||||
case 3: diskInterface = .jasmin
|
||||
case 4: diskInterface = .BD500
|
||||
default: break;
|
||||
|
||||
}
|
||||
|
@ -5,6 +5,11 @@ Expected files:
|
||||
basic10.rom
|
||||
basic11.rom
|
||||
colour.rom
|
||||
microdisc.rom
|
||||
8dos.rom
|
||||
pravetz.rom
|
||||
pravetz.rom
|
||||
|
||||
Also potentially required:
|
||||
|
||||
* microdisc.rom, for loading Microdisc software;
|
||||
* 8dos.rom, for loading Pravetz 8-DOS software;
|
||||
* jasmin.rom, for loading Jasmin disk interface software; and
|
||||
* bd500.rom, for loading Byte Drive 500 software.
|
@ -77,7 +77,7 @@ std::shared_ptr<Track> OricMFMDSK::get_track_at_position(Track::Address address)
|
||||
for(int byte = 0; byte < 6; byte++) {
|
||||
last_header[byte] = file_.get8();
|
||||
encoder->add_byte(last_header[byte]);
|
||||
track_offset++;
|
||||
++track_offset;
|
||||
if(track_offset == 6250) break;
|
||||
}
|
||||
break;
|
||||
@ -85,8 +85,12 @@ std::shared_ptr<Track> OricMFMDSK::get_track_at_position(Track::Address address)
|
||||
case 0xfb:
|
||||
for(int byte = 0; byte < (128 << last_header[3]) + 2; byte++) {
|
||||
encoder->add_byte(file_.get8());
|
||||
track_offset++;
|
||||
if(track_offset == 6250) break;
|
||||
++track_offset;
|
||||
// Special exception: don't interrupt a sector body if it seems to
|
||||
// be about to run over the end of the track. It seems like BD-500
|
||||
// disks break the usual 6250-byte rule, pushing out to just less
|
||||
// than 6400 bytes total.
|
||||
if(track_offset == 6400) break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -18,9 +18,10 @@
|
||||
|
||||
using namespace Storage::Disk;
|
||||
|
||||
Drive::Drive(int input_clock_rate, int revolutions_per_minute, int number_of_heads):
|
||||
Drive::Drive(int input_clock_rate, int revolutions_per_minute, int number_of_heads, ReadyType rdy_type):
|
||||
Storage::TimedEventLoop(input_clock_rate),
|
||||
available_heads_(number_of_heads) {
|
||||
available_heads_(number_of_heads),
|
||||
ready_type_(rdy_type) {
|
||||
set_rotation_speed(revolutions_per_minute);
|
||||
|
||||
const auto seed = static_cast<std::default_random_engine::result_type>(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
@ -35,7 +36,7 @@ Drive::Drive(int input_clock_rate, int revolutions_per_minute, int number_of_hea
|
||||
}
|
||||
}
|
||||
|
||||
Drive::Drive(int input_clock_rate, int number_of_heads) : Drive(input_clock_rate, 300, number_of_heads) {}
|
||||
Drive::Drive(int input_clock_rate, int number_of_heads, ReadyType rdy_type) : Drive(input_clock_rate, 300, number_of_heads, rdy_type) {}
|
||||
|
||||
void Drive::set_rotation_speed(float revolutions_per_minute) {
|
||||
// Rationalise the supplied speed so that cycles_per_revolution_ is exact.
|
||||
@ -54,6 +55,9 @@ Drive::~Drive() {
|
||||
}
|
||||
|
||||
void Drive::set_disk(const std::shared_ptr<Disk> &disk) {
|
||||
if(ready_type_ == ReadyType::ShugartModifiedRDY || ready_type_ == ReadyType::IBMRDY) {
|
||||
is_ready_ = false;
|
||||
}
|
||||
if(disk_) disk_->flush_tracks();
|
||||
disk_ = disk;
|
||||
has_disk_ = !!disk_;
|
||||
@ -68,7 +72,7 @@ bool Drive::has_disk() const {
|
||||
}
|
||||
|
||||
ClockingHint::Preference Drive::preferred_clocking() {
|
||||
return (!motor_is_on_ || !has_disk_) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
|
||||
return (!has_disk_ || (time_until_motor_transition == Cycles(0) && !disk_is_rotating_)) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
|
||||
}
|
||||
|
||||
bool Drive::get_is_track_zero() const {
|
||||
@ -76,6 +80,10 @@ bool Drive::get_is_track_zero() const {
|
||||
}
|
||||
|
||||
void Drive::step(HeadPosition offset) {
|
||||
if(ready_type_ == ReadyType::IBMRDY) {
|
||||
is_ready_ = true;
|
||||
}
|
||||
|
||||
HeadPosition old_head_position = head_position_;
|
||||
head_position_ += offset;
|
||||
if(head_position_ < HeadPosition(0)) {
|
||||
@ -142,30 +150,37 @@ bool Drive::get_is_read_only() const {
|
||||
}
|
||||
|
||||
bool Drive::get_is_ready() const {
|
||||
return ready_index_count_ == 2;
|
||||
return is_ready_;
|
||||
}
|
||||
|
||||
void Drive::set_motor_on(bool motor_is_on) {
|
||||
if(motor_is_on_ != motor_is_on) {
|
||||
motor_is_on_ = motor_is_on;
|
||||
// Do nothing if the input hasn't changed.
|
||||
if(motor_input_is_on_ == motor_is_on) return;
|
||||
motor_input_is_on_ = motor_is_on;
|
||||
|
||||
if(observer_) {
|
||||
observer_->set_drive_motor_status(drive_name_, motor_is_on_);
|
||||
if(announce_motor_led_) {
|
||||
observer_->set_led_status(drive_name_, motor_is_on_);
|
||||
}
|
||||
}
|
||||
|
||||
if(!motor_is_on) {
|
||||
ready_index_count_ = 0;
|
||||
if(disk_) disk_->flush_tracks();
|
||||
}
|
||||
update_clocking_observer();
|
||||
// If this now means that the input and the actual state are in harmony,
|
||||
// cancel any planned change and stop.
|
||||
if(disk_is_rotating_ == motor_is_on) {
|
||||
time_until_motor_transition = Cycles(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a transition to on, start immediately.
|
||||
// TODO: spin-up?
|
||||
// TODO: momentum.
|
||||
if(motor_is_on) {
|
||||
set_disk_is_rotating(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a transition from on to off. Simulate momentum (ha!)
|
||||
// by delaying the time until complete standstill.
|
||||
if(time_until_motor_transition == Cycles(0))
|
||||
time_until_motor_transition = get_input_clock_rate();
|
||||
}
|
||||
|
||||
bool Drive::get_motor_on() const {
|
||||
return motor_is_on_;
|
||||
return motor_input_is_on_;
|
||||
}
|
||||
|
||||
bool Drive::get_index_pulse() const {
|
||||
@ -185,7 +200,16 @@ void Drive::run_for(const Cycles cycles) {
|
||||
// Assumed: the index pulse pulses even if the drive has stopped spinning.
|
||||
index_pulse_remaining_ = std::max(index_pulse_remaining_ - cycles, Cycles(0));
|
||||
|
||||
if(motor_is_on_) {
|
||||
if(time_until_motor_transition > Cycles(0)) {
|
||||
if(time_until_motor_transition > cycles) {
|
||||
time_until_motor_transition -= cycles;
|
||||
} else {
|
||||
time_until_motor_transition = Cycles(0);
|
||||
set_disk_is_rotating(!disk_is_rotating_);
|
||||
}
|
||||
}
|
||||
|
||||
if(disk_is_rotating_) {
|
||||
if(has_disk_) {
|
||||
Time zero(0);
|
||||
|
||||
@ -287,7 +311,10 @@ void Drive::get_next_event(float duration_already_passed) {
|
||||
|
||||
void Drive::process_next_event() {
|
||||
if(current_event_.type == Track::Event::IndexHole) {
|
||||
if(ready_index_count_ < 2) ready_index_count_++;
|
||||
++ready_index_count_;
|
||||
if(ready_index_count_ == 2 && (ready_type_ == ReadyType::ShugartRDY || ready_type_ == ReadyType::ShugartModifiedRDY)) {
|
||||
is_ready_ = true;
|
||||
}
|
||||
cycles_since_index_hole_ = 0;
|
||||
}
|
||||
if(
|
||||
@ -400,6 +427,26 @@ bool Drive::is_writing() const {
|
||||
return !is_reading_;
|
||||
}
|
||||
|
||||
void Drive::set_disk_is_rotating(bool is_rotating) {
|
||||
disk_is_rotating_ = is_rotating;
|
||||
|
||||
if(observer_) {
|
||||
observer_->set_drive_motor_status(drive_name_, motor_input_is_on_);
|
||||
if(announce_motor_led_) {
|
||||
observer_->set_led_status(drive_name_, motor_input_is_on_);
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_rotating) {
|
||||
if(ready_type_ == ReadyType::ShugartRDY) {
|
||||
is_ready_ = false;
|
||||
}
|
||||
ready_index_count_ = 0;
|
||||
if(disk_) disk_->flush_tracks();
|
||||
}
|
||||
update_clocking_observer();
|
||||
}
|
||||
|
||||
void Drive::set_activity_observer(Activity::Observer *observer, const std::string &name, bool add_motor_led) {
|
||||
observer_ = observer;
|
||||
announce_motor_led_ = add_motor_led;
|
||||
@ -407,11 +454,11 @@ void Drive::set_activity_observer(Activity::Observer *observer, const std::strin
|
||||
drive_name_ = name;
|
||||
|
||||
observer->register_drive(drive_name_);
|
||||
observer->set_drive_motor_status(drive_name_, motor_is_on_);
|
||||
observer->set_drive_motor_status(drive_name_, disk_is_rotating_);
|
||||
|
||||
if(add_motor_led) {
|
||||
observer->register_led(drive_name_);
|
||||
observer->set_led_status(drive_name_, motor_is_on_);
|
||||
observer->set_led_status(drive_name_, disk_is_rotating_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,21 @@ namespace Disk {
|
||||
|
||||
class Drive: public ClockingHint::Source, public TimedEventLoop {
|
||||
public:
|
||||
Drive(int input_clock_rate, int revolutions_per_minute, int number_of_heads);
|
||||
Drive(int input_clock_rate, int number_of_heads);
|
||||
enum class ReadyType {
|
||||
/// Indicates that RDY will go active when the motor is on and two index holes have passed; it will go inactive when the motor is off.
|
||||
ShugartRDY,
|
||||
/// Indicates that RDY will go active when the motor is on and two index holes have passed; it will go inactive when the disk is ejected.
|
||||
ShugartModifiedRDY,
|
||||
/// Indicates that RDY will go active when the head steps; it will go inactive when the disk is ejected.
|
||||
IBMRDY,
|
||||
};
|
||||
|
||||
Drive(int input_clock_rate, int revolutions_per_minute, int number_of_heads, ReadyType rdy_type = ReadyType::ShugartRDY);
|
||||
Drive(int input_clock_rate, int number_of_heads, ReadyType rdy_type = ReadyType::ShugartRDY);
|
||||
~Drive();
|
||||
|
||||
/*!
|
||||
Replaces whatever is in the drive with @c disk.
|
||||
Replaces whatever is in the drive with @c disk. Supply @c nullptr to eject any current disk and leave none inserted.
|
||||
*/
|
||||
void set_disk(const std::shared_ptr<Disk> &disk);
|
||||
|
||||
@ -75,7 +84,7 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
|
||||
void set_motor_on(bool);
|
||||
|
||||
/*!
|
||||
@returns @c true if the motor is on; @c false otherwise.
|
||||
@returns @c true if the motor on input is active; @c false otherwise. This does not necessarily indicate whether the drive is spinning, due to momentum.
|
||||
*/
|
||||
bool get_motor_on() const;
|
||||
|
||||
@ -213,7 +222,10 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
|
||||
int available_heads_ = 0;
|
||||
|
||||
// Motor control state.
|
||||
bool motor_is_on_ = false;
|
||||
bool motor_input_is_on_ = false;
|
||||
bool disk_is_rotating_ = false;
|
||||
Cycles time_until_motor_transition;
|
||||
void set_disk_is_rotating(bool);
|
||||
|
||||
// Current state of the index pulse output.
|
||||
Cycles index_pulse_remaining_;
|
||||
@ -229,8 +241,10 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
|
||||
PCMSegment write_segment_;
|
||||
Time write_start_time_;
|
||||
|
||||
// Indicates progress towards drive ready state.
|
||||
// Indicates progress towards Shugart-style drive ready states.
|
||||
int ready_index_count_ = 0;
|
||||
ReadyType ready_type_;
|
||||
bool is_ready_ = false;
|
||||
|
||||
// Maintains appropriate counting to know when to indicate that writing
|
||||
// is complete.
|
||||
|
Loading…
Reference in New Issue
Block a user