1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-26 15:32:04 +00:00

Merge pull request #80 from TomHarte/Microdisc

Adds support for the Microdisc interface to the Oric emulation
This commit is contained in:
Thomas Harte 2016-12-03 23:25:44 +08:00 committed by GitHub
commit a0043ec336
30 changed files with 881 additions and 321 deletions

View File

@ -11,18 +11,30 @@
using namespace WD;
WD1770::WD1770() :
WD1770::Status::Status() :
type(Status::One),
write_protect(false),
record_type(false),
spin_up(false),
record_not_found(false),
crc_error(false),
seek_error(false),
lost_data(false),
data_request(false),
busy(false)
{}
WD1770::WD1770(Personality p) :
Storage::Disk::Controller(8000000, 16, 300),
status_(0),
interesting_event_mask_(Event::Command),
resume_point_(0),
delay_time_(0),
index_hole_count_target_(-1),
is_awaiting_marker_value_(false),
is_reading_data_(false),
interrupt_request_line_(false),
data_request_line_(false),
delegate_(nullptr)
delegate_(nullptr),
personality_(p),
head_is_loaded_(false)
{
set_is_double_density(false);
posit_event(Event::Command);
@ -44,9 +56,20 @@ void WD1770::set_register(int address, uint8_t value)
switch(address&3)
{
case 0:
command_ = value;
posit_event(Event::Command);
// TODO: is this force interrupt?
{
if((value&0xf0) == 0xd0)
{
printf("!!!TODO: force interrupt!!!\n");
update_status([] (Status &status) {
status.type = Status::One;
});
}
else
{
command_ = value;
posit_event(Event::Command);
}
}
break;
case 1: track_ = value; break;
case 2: sector_ = value; break;
@ -58,10 +81,52 @@ uint8_t WD1770::get_register(int address)
{
switch(address&3)
{
default: return status_ | (data_request_line_ ? Flag::DataRequest : 0);
default:
{
uint8_t status =
(status_.write_protect ? Flag::WriteProtect : 0) |
(status_.crc_error ? Flag::CRCError : 0) |
(status_.busy ? Flag::Busy : 0);
switch(status_.type)
{
case Status::One:
status |=
(get_is_track_zero() ? Flag::TrackZero : 0) |
(status_.seek_error ? Flag::SeekError : 0);
// TODO: index hole
break;
case Status::Two:
case Status::Three:
status |=
(status_.record_type ? Flag::RecordType : 0) |
(status_.lost_data ? Flag::LostData : 0) |
(status_.data_request ? Flag::DataRequest : 0) |
(status_.record_not_found ? Flag::RecordNotFound : 0);
break;
}
if(!has_motor_on_line())
{
status |= get_drive_is_ready() ? 0 : Flag::NotReady;
if(status_.type == Status::One)
status |= (head_is_loaded_ ? Flag::HeadLoaded : 0);
}
else
{
status |= (get_motor_on() ? Flag::MotorOn : 0);
if(status_.type == Status::One)
status |= (status_.spin_up ? Flag::SpinUp : 0);
}
return status;
}
case 1: return track_;
case 2: return sector_;
case 3: set_data_request(false); return data_;
case 3:
update_status([] (Status &status) {
status.data_request = false;
});
return data_;
}
}
@ -188,11 +253,16 @@ void WD1770::process_index_hole()
}
// motor power-down
if(index_hole_count_ == 9 && !(status_&Flag::Busy))
if(index_hole_count_ == 9 && !status_.busy && has_motor_on_line())
{
status_ &= ~Flag::MotorOn;
set_motor_on(false);
}
// head unload
if(index_hole_count_ == 15 && !status_.busy && has_head_load_line())
{
set_head_load_request(false);
}
}
// +------+----------+-------------------------+
@ -233,28 +303,39 @@ void WD1770::process_index_hole()
#define LINE_LABEL INDIRECT_CONCATENATE(label, __LINE__)
#define SPIN_UP() \
status_ |= Flag::MotorOn; \
set_motor_on(true); \
index_hole_count_ = 0; \
index_hole_count_target_ = 6; \
WAIT_FOR_EVENT(Event::IndexHoleTarget);
WAIT_FOR_EVENT(Event::IndexHoleTarget); \
status_.spin_up = true;
void WD1770::posit_event(Event new_event_type)
{
if(!(interesting_event_mask_ & (int)new_event_type)) return;
interesting_event_mask_ &= ~new_event_type;
Status new_status;
BEGIN_SECTION()
// Wait for a new command, branch to the appropriate handler.
wait_for_command:
printf("Idle...\n");
is_reading_data_ = false;
status_ &= ~Flag::Busy;
index_hole_count_ = 0;
update_status([] (Status &status) {
status.busy = false;
});
WAIT_FOR_EVENT(Event::Command);
update_status([] (Status &status) {
status.busy = true;
});
printf("Starting %02x\n", command_);
status_ |= Flag::Busy;
if(!(command_ & 0x80)) goto begin_type_1;
if(!(command_ & 0x40)) goto begin_type_2;
goto begin_type_3;
@ -265,14 +346,32 @@ void WD1770::posit_event(Event new_event_type)
*/
begin_type_1:
// Set initial flags, skip spin-up if possible.
status_ &= ~Flag::SeekError;
set_interrupt_request(false);
set_data_request(false);
if((command_&0x08) || (status_ & Flag::MotorOn)) goto test_type1_type;
update_status([] (Status &status) {
status.type = Status::One;
status.seek_error = false;
status.crc_error = false;
status.data_request = false;
});
// Perform spin up.
if(!has_motor_on_line() && !has_head_load_line()) goto test_type1_type;
if(has_motor_on_line()) goto begin_type1_spin_up;
goto begin_type1_load_head;
begin_type1_load_head:
if(!(command_&0x08))
{
set_head_load_request(false);
goto test_type1_type;
}
set_head_load_request(true);
if(head_is_loaded_) goto test_type1_type;
WAIT_FOR_EVENT(Event::HeadLoad);
goto test_type1_type;
begin_type1_spin_up:
if((command_&0x08) || get_motor_on()) goto test_type1_type;
SPIN_UP();
status_ |= Flag::SpinUp;
test_type1_type:
// Set step direction if this is a step in or out.
@ -305,10 +404,10 @@ void WD1770::posit_event(Event new_event_type)
switch(command_ & 3)
{
default:
case 0: time_to_wait = 6; break; // 2 on a 1772
case 1: time_to_wait = 12; break; // 3 on a 1772
case 2: time_to_wait = 20; break; // 5 on a 1772
case 3: time_to_wait = 30; break; // 6 on a 1772
case 0: time_to_wait = 6; break;
case 1: time_to_wait = 12; break;
case 2: time_to_wait = (personality_ == P1772) ? 2 : 20; break;
case 3: time_to_wait = (personality_ == P1772) ? 3 : 30; break;
}
WAIT_FOR_TIME(time_to_wait);
if(command_ >> 5) goto verify;
@ -321,7 +420,6 @@ void WD1770::posit_event(Event new_event_type)
verify:
if(!(command_ & 0x04))
{
set_interrupt_request(true);
goto wait_for_command;
}
@ -334,8 +432,9 @@ void WD1770::posit_event(Event new_event_type)
if(index_hole_count_ == 6)
{
set_interrupt_request(true);
status_ |= Flag::SeekError;
update_status([] (Status &status) {
status.seek_error = true;
});
goto wait_for_command;
}
if(distance_into_section_ == 7)
@ -345,8 +444,9 @@ void WD1770::posit_event(Event new_event_type)
if(header_[0] == track_)
{
printf("Reached track %d\n", track_);
status_ &= ~Flag::CRCError;
set_interrupt_request(true);
update_status([] (Status &status) {
status.crc_error = false;
});
goto wait_for_command;
}
@ -359,12 +459,30 @@ void WD1770::posit_event(Event new_event_type)
Type 2 entry point.
*/
begin_type_2:
status_ &= ~(Flag::LostData | Flag::RecordNotFound | Flag::WriteProtect | Flag::RecordType);
set_interrupt_request(false);
set_data_request(false);
update_status([] (Status &status) {
status.type = Status::Two;
status.lost_data = false;
status.record_not_found = false;
status.write_protect = false;
status.record_type = false;
status.data_request = false;
});
distance_into_section_ = 0;
if((command_&0x08) || (status_ & Flag::MotorOn)) goto test_type2_delay;
if((command_&0x08) && has_motor_on_line()) goto test_type2_delay;
if(!has_motor_on_line() && !has_head_load_line()) goto test_type2_delay;
if(has_motor_on_line()) goto begin_type2_spin_up;
goto begin_type2_load_head;
begin_type2_load_head:
set_head_load_request(true);
if(head_is_loaded_) goto test_type2_delay;
WAIT_FOR_EVENT(Event::HeadLoad);
goto test_type2_delay;
begin_type2_spin_up:
if(get_motor_on()) goto test_type2_delay;
// Perform spin up.
SPIN_UP();
@ -376,8 +494,9 @@ void WD1770::posit_event(Event new_event_type)
test_type2_write_protection:
if(command_&0x20) // TODO:: && is_write_protected
{
set_interrupt_request(true);
status_ |= Flag::WriteProtect;
update_status([] (Status &status) {
status.write_protect = true;
});
goto wait_for_command;
}
@ -387,14 +506,17 @@ void WD1770::posit_event(Event new_event_type)
if(index_hole_count_ == 5)
{
set_interrupt_request(true);
status_ |= Flag::RecordNotFound;
printf("Failed to find sector %d\n", sector_);
update_status([] (Status &status) {
status.record_not_found = true;
});
goto wait_for_command;
}
if(distance_into_section_ == 7)
{
is_reading_data_ = false;
if(header_[0] == track_ && header_[2] == sector_)
if(header_[0] == track_ && header_[2] == sector_ &&
(has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1]))
{
// TODO: test CRC
goto type2_read_or_write_data;
@ -413,7 +535,9 @@ void WD1770::posit_event(Event new_event_type)
// TODO: timeout
if(latest_token_.type == Token::Data || latest_token_.type == Token::DeletedData)
{
status_ |= (latest_token_.type == Token::DeletedData) ? Flag::RecordType : 0;
update_status([this] (Status &status) {
status.record_type = (latest_token_.type == Token::DeletedData);
});
distance_into_section_ = 0;
is_reading_data_ = true;
goto type2_read_byte;
@ -423,9 +547,11 @@ void WD1770::posit_event(Event new_event_type)
type2_read_byte:
WAIT_FOR_EVENT(Event::Token);
if(latest_token_.type != Token::Byte) goto type2_read_byte;
if(data_request_line_) status_ |= Flag::LostData;
data_ = latest_token_.byte_value;
set_data_request(true);
update_status([] (Status &status) {
status.lost_data |= status.data_request;
status.data_request = true;
});
distance_into_section_++;
if(distance_into_section_ == 128 << header_[3])
{
@ -447,7 +573,6 @@ void WD1770::posit_event(Event new_event_type)
sector_++;
goto test_type2_write_protection;
}
set_interrupt_request(true);
printf("Read sector %d\n", sector_);
goto wait_for_command;
}
@ -458,26 +583,33 @@ void WD1770::posit_event(Event new_event_type)
printf("!!!TODO: data portion of sector!!!\n");
begin_type_3:
update_status([] (Status &status) {
status.type = Status::Three;
});
printf("!!!TODO: type 3 commands!!!\n");
END_SECTION()
}
void WD1770::set_interrupt_request(bool interrupt_request)
void WD1770::update_status(std::function<void(Status &)> updater)
{
if(interrupt_request_line_ != interrupt_request)
if(delegate_)
{
interrupt_request_line_ = interrupt_request;
if(delegate_) delegate_->wd1770_did_change_interrupt_request_status(this);
Status old_status = status_;
updater(status_);
bool did_change =
(status_.busy != old_status.busy) ||
(status_.data_request != old_status.data_request);
if(did_change) delegate_->wd1770_did_change_output(this);
}
else updater(status_);
}
void WD1770::set_data_request(bool data_request)
void WD1770::set_head_load_request(bool head_load) {}
void WD1770::set_head_loaded(bool head_loaded)
{
if(data_request_line_ != data_request)
{
data_request_line_ = data_request;
if(delegate_) delegate_->wd1770_did_change_data_request_status(this);
}
head_is_loaded_ = head_loaded;
if(head_loaded) posit_event(Event::HeadLoad);
}

View File

@ -15,7 +15,13 @@ namespace WD {
class WD1770: public Storage::Disk::Controller {
public:
WD1770();
enum Personality {
P1770, // implies automatic motor-on management with Type 2 commands offering a spin-up disable
P1772, // as per the 1770, with different stepping rates
P1773, // implements the side number-testing logic of the 1793; omits spin-up/loading logic
P1793 // implies Type 2 commands use side number testing logic; spin-up/loading is by HLD and HLT
};
WD1770(Personality p);
void set_is_double_density(bool is_double_density);
void set_register(int address, uint8_t value);
@ -24,10 +30,12 @@ class WD1770: public Storage::Disk::Controller {
void run_for_cycles(unsigned int number_of_cycles);
enum Flag: uint8_t {
NotReady = 0x80,
MotorOn = 0x80,
WriteProtect = 0x40,
RecordType = 0x20,
SpinUp = 0x20,
HeadLoaded = 0x20,
RecordNotFound = 0x10,
SeekError = 0x10,
CRCError = 0x08,
@ -38,17 +46,38 @@ class WD1770: public Storage::Disk::Controller {
Busy = 0x01
};
inline bool get_interrupt_request_line() { return interrupt_request_line_; }
inline bool get_data_request_line() { return data_request_line_; }
inline bool get_interrupt_request_line() { return !status_.busy; }
inline bool get_data_request_line() { return status_.data_request; }
class Delegate {
public:
virtual void wd1770_did_change_interrupt_request_status(WD1770 *wd1770) = 0;
virtual void wd1770_did_change_data_request_status(WD1770 *wd1770) = 0;
virtual void wd1770_did_change_output(WD1770 *wd1770) = 0;
};
inline void set_delegate(Delegate *delegate) { delegate_ = delegate; }
protected:
virtual void set_head_load_request(bool head_load);
void set_head_loaded(bool head_loaded);
private:
uint8_t status_;
Personality personality_;
inline bool has_motor_on_line() { return (personality_ != P1793 ) && (personality_ != P1773); }
inline bool has_head_load_line() { return (personality_ == P1793 ); }
struct Status {
Status();
bool write_protect;
bool record_type;
bool spin_up;
bool record_not_found;
bool crc_error;
bool seek_error;
bool lost_data;
bool data_request;
bool busy;
enum {
One, Two, Three
} type;
} status_;
uint8_t track_;
uint8_t sector_;
uint8_t data_;
@ -61,8 +90,7 @@ class WD1770: public Storage::Disk::Controller {
bool is_awaiting_marker_value_;
int step_direction_;
void set_interrupt_request(bool interrupt_request);
void set_data_request(bool interrupt_request);
void update_status(std::function<void(Status &)> updater);
// Tokeniser
bool is_reading_data_;
@ -80,9 +108,10 @@ class WD1770: public Storage::Disk::Controller {
Command = (1 << 0), // Indicates receipt of a new command.
Token = (1 << 1), // Indicates recognition of a new token in the flux stream. Interrogate latest_token_ for details.
IndexHole = (1 << 2), // Indicates the passing of a physical index hole.
HeadLoad = (1 << 3), // Indicates the head has been loaded (1973 only).
Timer = (1 << 3), // Indicates that the delay_time_-powered timer has timed out.
IndexHoleTarget = (1 << 4) // Indicates that index_hole_count_ has reached index_hole_count_target_.
Timer = (1 << 4), // Indicates that the delay_time_-powered timer has timed out.
IndexHoleTarget = (1 << 5) // Indicates that index_hole_count_ has reached index_hole_count_target_.
};
void posit_event(Event type);
int interesting_event_mask_;
@ -92,9 +121,10 @@ class WD1770: public Storage::Disk::Controller {
// ID buffer
uint8_t header_[6];
// output line statuses
bool interrupt_request_line_;
bool data_request_line_;
// 1793 head-loading logic
bool head_is_loaded_;
// delegate
Delegate *delegate_;
// Storage::Disk::Controller

View File

@ -10,10 +10,16 @@
using namespace Electron;
Plus3::Plus3() : WD1770(P1770) {}
void Plus3::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive)
{
if(!_drives[drive]) _drives[drive].reset(new Storage::Disk::Drive);
_drives[drive]->set_disk(disk);
if(!drives_[drive])
{
drives_[drive].reset(new Storage::Disk::Drive);
if(drive == selected_drive_) set_drive(drives_[drive]);
}
drives_[drive]->set_disk(disk);
}
void Plus3::set_control_register(uint8_t control)
@ -25,11 +31,11 @@ void Plus3::set_control_register(uint8_t control)
// bit 3 => single density select
switch(control&3)
{
case 0: set_drive(nullptr); break;
default: set_drive(_drives[0]); break;
case 2: set_drive(_drives[1]); break;
case 0: selected_drive_ = -1; set_drive(nullptr); break;
default: selected_drive_ = 0; set_drive(drives_[0]); break;
case 2: selected_drive_ = 1; set_drive(drives_[1]); break;
}
if(_drives[0]) _drives[0]->set_head((control & 0x04) ? 1 : 0);
if(_drives[1]) _drives[1]->set_head((control & 0x04) ? 1 : 0);
if(drives_[0]) drives_[0]->set_head((control & 0x04) ? 1 : 0);
if(drives_[1]) drives_[1]->set_head((control & 0x04) ? 1 : 0);
set_is_double_density(!(control & 0x08));
}

View File

@ -15,11 +15,14 @@ namespace Electron {
class Plus3 : public WD::WD1770 {
public:
Plus3();
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
void set_control_register(uint8_t control);
private:
std::shared_ptr<Storage::Disk::Drive> _drives[2];
std::shared_ptr<Storage::Disk::Drive> drives_[2];
int selected_drive_;
};
}

128
Machines/Oric/Microdisc.cpp Normal file
View File

@ -0,0 +1,128 @@
//
// Microdisc.cpp
// Clock Signal
//
// Created by Thomas Harte on 22/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Microdisc.hpp"
using namespace Oric;
namespace {
// The number below, in cycles against an 8Mhz clock, was arrived at fairly unscientifically,
// by comparing the amount of time this emulator took to show a directory versus a video of
// a real Oric. It therefore assumes all other timing measurements were correct on the day
// of the test. More work to do, I think.
const int head_load_request_counter_target = 7653333;
}
Microdisc::Microdisc() :
irq_enable_(false),
delegate_(nullptr),
paging_flags_(BASICDisable),
head_load_request_counter_(-1),
WD1770(P1793)
{}
void Microdisc::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive)
{
if(!drives_[drive])
{
drives_[drive].reset(new Storage::Disk::Drive);
if(drive == selected_drive_) set_drive(drives_[drive]);
}
drives_[drive]->set_disk(disk);
}
void Microdisc::set_control_register(uint8_t control)
{
printf("control: %d%d%d%d%d%d%d%d\n",
(control >> 7)&1,
(control >> 6)&1,
(control >> 5)&1,
(control >> 4)&1,
(control >> 3)&1,
(control >> 2)&1,
(control >> 1)&1,
(control >> 0)&1);
// b2: data separator clock rate select (1 = double) [TODO]
// b65: drive select
selected_drive_ = (control >> 5)&3;
set_drive(drives_[selected_drive_]);
// b4: side select
unsigned int head = (control & 0x10) ? 1 : 0;
for(int c = 0; c < 4; c++)
{
if(drives_[c]) drives_[c]->set_head(head);
}
// b3: double density select (0 = double)
set_is_double_density(!(control & 0x08));
// b0: IRQ enable
bool had_irq = get_interrupt_request_line();
irq_enable_ = !!(control & 0x01);
bool has_irq = get_interrupt_request_line();
if(has_irq != had_irq && delegate_)
{
delegate_->wd1770_did_change_output(this);
}
// b7: EPROM select (0 = select)
// b1: ROM disable (0 = disable)
int new_paging_flags = ((control & 0x02) ? 0 : BASICDisable) | ((control & 0x80) ? MicrodscDisable : 0);
if(new_paging_flags != paging_flags_)
{
paging_flags_ = new_paging_flags;
if(delegate_) delegate_->microdisc_did_change_paging_flags(this);
}
}
bool Microdisc::get_interrupt_request_line()
{
return irq_enable_ && WD1770::get_interrupt_request_line();
}
uint8_t Microdisc::get_interrupt_request_register()
{
return 0x7f | (get_interrupt_request_line() ? 0x00 : 0x80);
}
uint8_t Microdisc::get_data_request_register()
{
return 0x7f | (get_data_request_line() ? 0x00 : 0x80);
}
void Microdisc::set_head_load_request(bool head_load)
{
set_motor_on(head_load);
if(head_load)
{
head_load_request_counter_ = 0;
}
else
{
head_load_request_counter_ = head_load_request_counter_target;
set_head_loaded(head_load);
}
}
void Microdisc::run_for_cycles(unsigned int number_of_cycles)
{
if(head_load_request_counter_ < head_load_request_counter_target)
{
head_load_request_counter_ += number_of_cycles;
if(head_load_request_counter_ >= head_load_request_counter_target) set_head_loaded(true);
}
WD::WD1770::run_for_cycles(number_of_cycles);
}
bool Microdisc::get_drive_is_ready()
{
return true;
}

View File

@ -0,0 +1,54 @@
//
// Microdisc.hpp
// Clock Signal
//
// Created by Thomas Harte on 22/11/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Microdisc_hpp
#define Microdisc_hpp
#include "../../Components/1770/1770.hpp"
namespace Oric {
class Microdisc: public WD::WD1770 {
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();
bool get_interrupt_request_line();
void run_for_cycles(unsigned int number_of_cycles);
enum PagingFlags {
BASICDisable = (1 << 0),
MicrodscDisable = (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_; }
private:
void set_head_load_request(bool head_load);
bool get_drive_is_ready();
std::shared_ptr<Storage::Disk::Drive> drives_[4];
int selected_drive_;
bool irq_enable_;
int paging_flags_;
int head_load_request_counter_;
Delegate *delegate_;
};
}
#endif /* Microdisc_hpp */

View File

@ -12,25 +12,28 @@
using namespace Oric;
Machine::Machine() :
_cycles_since_video_update(0),
_use_fast_tape_hack(false),
_typer_delay(2500000),
_keyboard_read_count(0)
cycles_since_video_update_(0),
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);
_keyboard.reset(new Keyboard);
_via.keyboard = _keyboard;
via_.set_interrupt_delegate(this);
via_.keyboard = keyboard_;
clear_all_keys();
_via.tape->set_delegate(this);
Memory::Fuzz(_ram, sizeof(_ram));
via_.tape->set_delegate(this);
Memory::Fuzz(ram_, sizeof(ram_));
}
void Machine::configure_as_target(const StaticAnalyser::Target &target)
{
if(target.tapes.size())
{
_via.tape->set_tape(target.tapes.front());
via_.tape->set_tape(target.tapes.front());
}
if(target.loadingCommand.length()) // TODO: and automatic loading option enabled
@ -38,42 +41,61 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target)
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.data(), std::min(_basic11.size(), sizeof(_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;
is_using_basic11_ = true;
tape_get_byte_address_ = 0xe6c9;
scan_keyboard_address_ = 0xf495;
tape_speed_address_ = 0x024d;
}
else
{
memcpy(_rom, _basic10.data(), std::min(_basic10.size(), sizeof(_rom)));
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;
is_using_basic11_ = false;
tape_get_byte_address_ = 0xe630;
scan_keyboard_address_ = 0xf43c;
tape_speed_address_ = 0x67;
}
}
void Machine::set_rom(ROM rom, const std::vector<uint8_t> &data)
{
if(rom == BASIC11) _basic11 = std::move(data); else _basic10 = std::move(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;
}
}
unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value)
{
if(address >= 0xc000)
if(address > ram_top_)
{
if(isReadOperation(operation)) *value = _rom[address&16383];
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 && _use_fast_tape_hack && operation == CPU6502::BusOperation::ReadOpcode && _via.tape->has_tape() && !_via.tape->get_tape()->is_at_end())
if(address == tape_get_byte_address_ && paged_rom_ == rom_ && use_fast_tape_hack_ && operation == CPU6502::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]);
uint8_t next_byte = via_.tape->get_next_byte(!ram_[tape_speed_address_]);
set_value_of_register(CPU6502::A, next_byte);
set_value_of_register(CPU6502::Flags, next_byte ? 0 : CPU6502::Flag::Zero);
*value = 0x60; // i.e. RTS
@ -83,26 +105,46 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
{
if((address & 0xff00) == 0x0300)
{
if(isReadOperation(operation)) *value = _via.get_register(address);
else _via.set_register(address, *value);
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];
*value = ram_[address];
else
{
if(address >= 0x9800) { update_video(); _typer_delay = 0; }
_ram[address] = *value;
if(address >= 0x9800 && address <= 0xc000) { update_video(); typer_delay_ = 0; }
ram_[address] = *value;
}
}
}
if(_typer && address == _scan_keyboard_address && operation == CPU6502::BusOperation::ReadOpcode)
if(_typer && address == scan_keyboard_address_ && operation == CPU6502::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++;
if(!keyboard_read_count_) keyboard_read_count_++;
else if(!_typer->type_next_character())
{
clear_all_keys();
@ -110,39 +152,40 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
}
}
_via.run_for_cycles(1);
_cycles_since_video_update++;
via_.run_for_cycles(1);
if(microdisc_is_enabled_) microdisc_.run_for_cycles(8);
cycles_since_video_update_++;
return 1;
}
void Machine::synchronise()
{
update_video();
_via.synchronise();
via_.synchronise();
}
void Machine::update_video()
{
_videoOutput->run_for_cycles(_cycles_since_video_update);
_cycles_since_video_update = 0;
video_output_->run_for_cycles(cycles_since_video_update_);
cycles_since_video_update_ = 0;
}
void Machine::setup_output(float aspect_ratio)
{
_videoOutput.reset(new VideoOutput(_ram));
_via.ay8910.reset(new GI::AY38910());
_via.ay8910->set_clock_rate(1000000);
video_output_.reset(new VideoOutput(ram_));
via_.ay8910.reset(new GI::AY38910());
via_.ay8910->set_clock_rate(1000000);
}
void Machine::close_output()
{
_videoOutput.reset();
_via.ay8910.reset();
video_output_.reset();
via_.ay8910.reset();
}
void Machine::mos6522_did_change_interrupt_status(void *mos6522)
{
set_irq_line(_via.get_interrupt_line());
set_interrupt_line();
}
void Machine::set_key_state(uint16_t key, bool isPressed)
@ -154,36 +197,36 @@ void Machine::set_key_state(uint16_t key, bool isPressed)
else
{
if(isPressed)
_keyboard->rows[key >> 8] |= (key & 0xff);
keyboard_->rows[key >> 8] |= (key & 0xff);
else
_keyboard->rows[key >> 8] &= ~(key & 0xff);
keyboard_->rows[key >> 8] &= ~(key & 0xff);
}
}
void Machine::clear_all_keys()
{
memset(_keyboard->rows, 0, sizeof(_keyboard->rows));
memset(keyboard_->rows, 0, sizeof(keyboard_->rows));
}
void Machine::set_use_fast_tape_hack(bool activate)
{
_use_fast_tape_hack = activate;
use_fast_tape_hack_ = activate;
}
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());
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 _videoOutput->get_crt();
return video_output_->get_crt();
}
std::shared_ptr<Outputs::Speaker> Machine::get_speaker()
{
return _via.ay8910;
return via_.ay8910;
}
void Machine::run_for_cycles(int number_of_cycles)
@ -195,14 +238,14 @@ void Machine::run_for_cycles(int number_of_cycles)
Machine::VIA::VIA() :
MOS::MOS6522<Machine::VIA>(),
_cycles_since_ay_update(0),
cycles_since_ay_update_(0),
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;
if(port) ay_bdir_ = value; else ay_bc1_ = value;
update_ay();
}
}
@ -235,23 +278,23 @@ uint8_t Machine::VIA::get_port_input(Port port)
void Machine::VIA::synchronise()
{
ay8910->run_for_cycles(_cycles_since_ay_update);
ay8910->run_for_cycles(cycles_since_ay_update_);
ay8910->flush();
_cycles_since_ay_update = 0;
cycles_since_ay_update_ = 0;
}
void Machine::VIA::run_for_cycles(unsigned int number_of_cycles)
{
_cycles_since_ay_update += number_of_cycles;
cycles_since_ay_update_ += number_of_cycles;
MOS::MOS6522<VIA>::run_for_cycles(number_of_cycles);
tape->run_for_cycles((int)number_of_cycles);
}
void Machine::VIA::update_ay()
{
ay8910->run_for_cycles(_cycles_since_ay_update);
_cycles_since_ay_update = 0;
ay8910->set_control_lines( (GI::AY38910::ControlLines)((_ay_bdir ? GI::AY38910::BCDIR : 0) | (_ay_bc1 ? GI::AY38910::BC1 : 0) | GI::AY38910::BC2));
ay8910->run_for_cycles(cycles_since_ay_update_);
cycles_since_ay_update_ = 0;
ay8910->set_control_lines( (GI::AY38910::ControlLines)((ay_bdir_ ? GI::AY38910::BCDIR : 0) | (ay_bc1_ ? GI::AY38910::BC1 : 0) | GI::AY38910::BC2));
}
#pragma mark - TapePlayer
@ -262,5 +305,41 @@ Machine::TapePlayer::TapePlayer() :
uint8_t Machine::TapePlayer::get_next_byte(bool fast)
{
return (uint8_t)_parser.get_next_byte(get_tape(), 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()
{
set_irq_line(
via_.get_interrupt_line() ||
(microdisc_is_enabled_ && microdisc_.get_interrupt_request_line()));
}

View File

@ -19,6 +19,7 @@
#include "../../Storage/Tape/Parsers/Oric.hpp"
#include "Video.hpp"
#include "Microdisc.hpp"
#include "../../Storage/Tape/Tape.hpp"
@ -52,7 +53,7 @@ enum Key: uint16_t {
};
enum ROM {
BASIC10, BASIC11
BASIC10, BASIC11, Microdisc
};
class Machine:
@ -61,7 +62,8 @@ class Machine:
public ConfigurationTarget::Machine,
public MOS::MOS6522IRQDelegate::Delegate,
public Utility::TypeRecipient,
public Storage::Tape::BinaryTapePlayer::Delegate {
public Storage::Tape::BinaryTapePlayer::Delegate,
public Microdisc::Delegate {
public:
Machine();
@ -95,20 +97,24 @@ class Machine:
// for Utility::TypeRecipient::Delegate
uint16_t *sequence_for_character(Utility::Typer *typer, char character);
// for Microdisc::Delegate
void microdisc_did_change_paging_flags(class Microdisc *microdisc);
void wd1770_did_change_output(WD::WD1770 *wd1770);
private:
// RAM and ROM
std::vector<uint8_t> _basic11, _basic10;
uint8_t _ram[65536], _rom[16384];
int _cycles_since_video_update;
std::vector<uint8_t> basic11_rom_, basic10_rom_, microdisc_rom_;
uint8_t ram_[65536], rom_[16384];
int 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;
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> _videoOutput;
std::unique_ptr<VideoOutput> video_output_;
// Keyboard
class Keyboard {
@ -116,7 +122,7 @@ class Machine:
uint8_t row;
uint8_t rows[8];
};
int _typer_delay;
int typer_delay_;
// The tape
class TapePlayer: public Storage::Tape::BinaryTapePlayer {
@ -125,9 +131,9 @@ class Machine:
uint8_t get_next_byte(bool fast);
private:
Storage::Tape::Oric::Parser _parser;
Storage::Tape::Oric::Parser parser_;
};
bool _use_fast_tape_hack;
bool use_fast_tape_hack_;
// VIA (which owns the tape and the AY)
class VIA: public MOS::MOS6522<VIA>, public MOS::MOS6522IRQDelegate {
@ -148,11 +154,19 @@ class Machine:
private:
void update_ay();
bool _ay_bdir, _ay_bc1;
unsigned int _cycles_since_ay_update;
bool ay_bdir_, ay_bc1_;
unsigned int cycles_since_ay_update_;
};
VIA _via;
std::shared_ptr<Keyboard> _keyboard;
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();
};
}

View File

@ -20,18 +20,17 @@ namespace {
}
VideoOutput::VideoOutput(uint8_t *memory) :
_ram(memory),
_frame_counter(0), _counter(0),
_is_graphics_mode(false),
_character_set_base_address(0xb400),
_phase(0),
_v_sync_start_position(PAL50VSyncStartPosition), _v_sync_end_position(PAL50VSyncEndPosition),
_counter_period(PAL50Period), _next_frame_is_sixty_hertz(false)
ram_(memory),
frame_counter_(0), counter_(0),
is_graphics_mode_(false),
character_set_base_address_(0xb400),
phase_(0),
v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition),
counter_period_(PAL50Period), next_frame_is_sixty_hertz_(false),
crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 1))
{
_crt.reset(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 1));
// TODO: this is a copy and paste from the Electron; factor out.
_crt->set_rgb_sampling_function(
crt_->set_rgb_sampling_function(
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
"{"
"uint texValue = texture(sampler, coordinate).r;"
@ -39,13 +38,13 @@ VideoOutput::VideoOutput(uint8_t *memory) :
"return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));"
"}");
_crt->set_output_device(Outputs::CRT::Television);
_crt->set_visible_area(_crt->get_rect_for_area(50, 224, 16 * 6, 40 * 6, 4.0f / 3.0f));
crt_->set_output_device(Outputs::CRT::Television);
crt_->set_visible_area(crt_->get_rect_for_area(50, 224, 16 * 6, 40 * 6, 4.0f / 3.0f));
}
std::shared_ptr<Outputs::CRT::CRT> VideoOutput::get_crt()
{
return _crt;
return crt_;
}
void VideoOutput::run_for_cycles(int number_of_cycles)
@ -58,58 +57,58 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
while(number_of_cycles)
{
int h_counter =_counter & 63;
int h_counter = counter_ & 63;
int cycles_run_for = 0;
if(_counter >= _v_sync_start_position && _counter < _v_sync_end_position)
if(counter_ >= v_sync_start_position_ && counter_ < v_sync_end_position_)
{
// this is a sync line
cycles_run_for = _v_sync_end_position - _counter;
clamp(_crt->output_sync((unsigned int)(_v_sync_end_position - _v_sync_start_position) * 6));
cycles_run_for = v_sync_end_position_ - counter_;
clamp(crt_->output_sync((unsigned int)(v_sync_end_position_ - v_sync_start_position_) * 6));
}
else if(_counter < 224*64 && h_counter < 40)
else if(counter_ < 224*64 && h_counter < 40)
{
// this is a pixel line
if(!h_counter)
{
_ink = 0xff;
_paper = 0x00;
_use_alternative_character_set = _use_double_height_characters = _blink_text = false;
ink_ = 0xff;
paper_ = 0x00;
use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false;
set_character_set_base_address();
_phase += 64;
_pixel_target = _crt->allocate_write_area(120);
phase_ += 64;
pixel_target_ = crt_->allocate_write_area(120);
if(!_counter)
if(!counter_)
{
_phase += 128; // TODO: incorporate all the lines that were missed
_frame_counter++;
phase_ += 128; // TODO: incorporate all the lines that were missed
frame_counter_++;
_v_sync_start_position = _next_frame_is_sixty_hertz ? PAL60VSyncStartPosition : PAL50VSyncStartPosition;
_v_sync_end_position = _next_frame_is_sixty_hertz ? PAL60VSyncEndPosition : PAL50VSyncEndPosition;
_counter_period = _next_frame_is_sixty_hertz ? PAL60Period : PAL50Period;
v_sync_start_position_ = next_frame_is_sixty_hertz_ ? PAL60VSyncStartPosition : PAL50VSyncStartPosition;
v_sync_end_position_ = next_frame_is_sixty_hertz_ ? PAL60VSyncEndPosition : PAL50VSyncEndPosition;
counter_period_ = next_frame_is_sixty_hertz_ ? PAL60Period : PAL50Period;
}
}
cycles_run_for = std::min(40 - h_counter, number_of_cycles);
int columns = cycles_run_for;
int pixel_base_address = 0xa000 + (_counter >> 6) * 40;
int character_base_address = 0xbb80 + (_counter >> 9) * 40;
uint8_t blink_mask = (_blink_text && (_frame_counter&32)) ? 0x00 : 0xff;
int pixel_base_address = 0xa000 + (counter_ >> 6) * 40;
int character_base_address = 0xbb80 + (counter_ >> 9) * 40;
uint8_t blink_mask = (blink_text_ && (frame_counter_&32)) ? 0x00 : 0xff;
while(columns--)
{
uint8_t pixels, control_byte;
if(_is_graphics_mode && _counter < 200*64)
if(is_graphics_mode_ && counter_ < 200*64)
{
control_byte = pixels = _ram[pixel_base_address + h_counter];
control_byte = pixels = ram_[pixel_base_address + h_counter];
}
else
{
int address = character_base_address + h_counter;
control_byte = _ram[address];
int line = _use_double_height_characters ? ((_counter >> 7) & 7) : ((_counter >> 6) & 7);
pixels = _ram[_character_set_base_address + (control_byte&127) * 8 + line];
control_byte = ram_[address];
int line = use_double_height_characters_ ? ((counter_ >> 7) & 7) : ((counter_ >> 6) & 7);
pixels = ram_[character_set_base_address_ + (control_byte&127) * 8 + line];
}
uint8_t inverse_mask = (control_byte & 0x80) ? 0x77 : 0x00;
@ -117,65 +116,65 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
if(control_byte & 0x60)
{
if(_pixel_target)
if(pixel_target_)
{
uint8_t colours[2] = {
(uint8_t)(_paper ^ inverse_mask),
(uint8_t)(_ink ^ inverse_mask),
(uint8_t)(paper_ ^ inverse_mask),
(uint8_t)(ink_ ^ inverse_mask),
};
_pixel_target[0] = (colours[(pixels >> 4)&1] & 0x0f) | (colours[(pixels >> 5)&1] & 0xf0);
_pixel_target[1] = (colours[(pixels >> 2)&1] & 0x0f) | (colours[(pixels >> 3)&1] & 0xf0);
_pixel_target[2] = (colours[(pixels >> 0)&1] & 0x0f) | (colours[(pixels >> 1)&1] & 0xf0);
pixel_target_[0] = (colours[(pixels >> 4)&1] & 0x0f) | (colours[(pixels >> 5)&1] & 0xf0);
pixel_target_[1] = (colours[(pixels >> 2)&1] & 0x0f) | (colours[(pixels >> 3)&1] & 0xf0);
pixel_target_[2] = (colours[(pixels >> 0)&1] & 0x0f) | (colours[(pixels >> 1)&1] & 0xf0);
}
}
else
{
switch(control_byte & 0x1f)
{
case 0x00: _ink = 0x00; break;
case 0x01: _ink = 0x44; break;
case 0x02: _ink = 0x22; break;
case 0x03: _ink = 0x66; break;
case 0x04: _ink = 0x11; break;
case 0x05: _ink = 0x55; break;
case 0x06: _ink = 0x33; break;
case 0x07: _ink = 0x77; break;
case 0x00: ink_ = 0x00; break;
case 0x01: ink_ = 0x44; break;
case 0x02: ink_ = 0x22; break;
case 0x03: ink_ = 0x66; break;
case 0x04: ink_ = 0x11; break;
case 0x05: ink_ = 0x55; break;
case 0x06: ink_ = 0x33; break;
case 0x07: ink_ = 0x77; break;
case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f:
_use_alternative_character_set = (control_byte&1);
_use_double_height_characters = (control_byte&2);
_blink_text = (control_byte&4);
use_alternative_character_set_ = (control_byte&1);
use_double_height_characters_ = (control_byte&2);
blink_text_ = (control_byte&4);
set_character_set_base_address();
break;
case 0x10: _paper = 0x00; break;
case 0x11: _paper = 0x44; break;
case 0x12: _paper = 0x22; break;
case 0x13: _paper = 0x66; break;
case 0x14: _paper = 0x11; break;
case 0x15: _paper = 0x55; break;
case 0x16: _paper = 0x33; break;
case 0x17: _paper = 0x77; break;
case 0x10: paper_ = 0x00; break;
case 0x11: paper_ = 0x44; break;
case 0x12: paper_ = 0x22; break;
case 0x13: paper_ = 0x66; break;
case 0x14: paper_ = 0x11; break;
case 0x15: paper_ = 0x55; break;
case 0x16: paper_ = 0x33; break;
case 0x17: paper_ = 0x77; break;
case 0x18: case 0x19: case 0x1a: case 0x1b:
case 0x1c: case 0x1d: case 0x1e: case 0x1f:
_is_graphics_mode = (control_byte & 4);
_next_frame_is_sixty_hertz = !(control_byte & 2);
is_graphics_mode_ = (control_byte & 4);
next_frame_is_sixty_hertz_ = !(control_byte & 2);
break;
default: break;
}
if(_pixel_target) _pixel_target[0] = _pixel_target[1] = _pixel_target[2] = (uint8_t)(_paper ^ inverse_mask);
if(pixel_target_) pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = (uint8_t)(paper_ ^ inverse_mask);
}
if(_pixel_target) _pixel_target += 3;
if(pixel_target_) pixel_target_ += 3;
h_counter++;
}
if(h_counter == 40)
{
_crt->output_data(40 * 6, 2);
crt_->output_data(40 * 6, 2);
}
}
else
@ -185,34 +184,34 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
{
cycles_run_for = 48 - h_counter;
clamp(
int period = (_counter < 224*64) ? 8 : 48;
_crt->output_blank((unsigned int)period * 6);
int period = (counter_ < 224*64) ? 8 : 48;
crt_->output_blank((unsigned int)period * 6);
);
}
else if(h_counter < 54)
{
cycles_run_for = 54 - h_counter;
clamp(_crt->output_sync(6 * 6));
clamp(crt_->output_sync(6 * 6));
}
else if(h_counter < 56)
{
cycles_run_for = 56 - h_counter;
clamp(_crt->output_colour_burst(2 * 6, _phase, 128));
clamp(crt_->output_colour_burst(2 * 6, phase_, 128));
}
else
{
cycles_run_for = 64 - h_counter;
clamp(_crt->output_blank(8 * 6));
clamp(crt_->output_blank(8 * 6));
}
}
_counter = (_counter + cycles_run_for)%_counter_period;
counter_ = (counter_ + cycles_run_for)%counter_period_;
number_of_cycles -= cycles_run_for;
}
}
void VideoOutput::set_character_set_base_address()
{
if(_is_graphics_mode) _character_set_base_address = _use_alternative_character_set ? 0x9c00 : 0x9800;
else _character_set_base_address = _use_alternative_character_set ? 0xb800 : 0xb400;
if(is_graphics_mode_) character_set_base_address_ = use_alternative_character_set_ ? 0x9c00 : 0x9800;
else character_set_base_address_ = use_alternative_character_set_ ? 0xb800 : 0xb400;
}

View File

@ -20,29 +20,29 @@ class VideoOutput {
void run_for_cycles(int number_of_cycles);
private:
uint8_t *_ram;
std::shared_ptr<Outputs::CRT::CRT> _crt;
uint8_t *ram_;
std::shared_ptr<Outputs::CRT::CRT> crt_;
// Counters and limits
int _counter, _frame_counter;
int _v_sync_start_position, _v_sync_end_position, _counter_period;
int counter_, frame_counter_;
int v_sync_start_position_, v_sync_end_position_, counter_period_;
// Output target
uint8_t *_pixel_target;
uint8_t *pixel_target_;
// Registers
uint8_t _ink, _paper;
uint8_t ink_, paper_;
int _character_set_base_address;
int character_set_base_address_;
inline void set_character_set_base_address();
bool _is_graphics_mode;
bool _next_frame_is_sixty_hertz;
bool _use_alternative_character_set;
bool _use_double_height_characters;
bool _blink_text;
bool is_graphics_mode_;
bool next_frame_is_sixty_hertz_;
bool use_alternative_character_set_;
bool use_double_height_characters_;
bool blink_text_;
uint8_t _phase;
uint8_t phase_;
};
}

View File

@ -55,6 +55,7 @@
4B5A12571DD55862007A2231 /* Disassembler6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5A12551DD55862007A2231 /* Disassembler6502.cpp */; };
4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; };
4B5FADBD1DE31D1500AEC565 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADBB1DE31D1500AEC565 /* OricMFMDSK.cpp */; };
4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */; };
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; };
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
@ -505,6 +506,8 @@
4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = "<group>"; };
4B5FADBB1DE31D1500AEC565 /* OricMFMDSK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OricMFMDSK.cpp; sourceTree = "<group>"; };
4B5FADBC1DE31D1500AEC565 /* OricMFMDSK.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricMFMDSK.hpp; sourceTree = "<group>"; };
4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Microdisc.cpp; path = Oric/Microdisc.cpp; sourceTree = "<group>"; };
4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Microdisc.hpp; path = Oric/Microdisc.hpp; sourceTree = "<group>"; };
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
@ -1791,6 +1794,8 @@
4BCF1FA51DADC3E10039D2E7 /* Oric */ = {
isa = PBXGroup;
children = (
4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */,
4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */,
4BCF1FA21DADC3DD0039D2E7 /* Oric.cpp */,
4BCF1FA31DADC3DD0039D2E7 /* Oric.hpp */,
4BC8A6291DCE4F2700DAC693 /* Typer.cpp */,
@ -2379,6 +2384,7 @@
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */,
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */,
4B8805FB1DCFF807003085B1 /* Oric.cpp in Sources */,
4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */,
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */,
4B8FE2271DA1DE2D0090D3CE /* NSBundle+DataResource.m in Sources */,
4B2A53A01D117D36003C6002 /* CSMachine.mm in Sources */,

View File

@ -26,10 +26,12 @@
if(self)
{
NSData *basic10 = [self rom:@"basic10"];
NSData *basic11 = [self rom:@"basic11"]; // test108j
NSData *basic11 = [self rom:@"basic11"];
NSData *microdisc = [self rom:@"microdisc"];
if(basic10) _oric.set_rom(Oric::BASIC10, basic10.stdVector8);
if(basic11) _oric.set_rom(Oric::BASIC11, basic11.stdVector8);
if(basic10) _oric.set_rom(Oric::BASIC10, basic10.stdVector8);
if(basic11) _oric.set_rom(Oric::BASIC11, basic11.stdVector8);
if(microdisc) _oric.set_rom(Oric::Microdisc, microdisc.stdVector8);
}
return self;
}

View File

@ -112,8 +112,16 @@ void StaticAnalyser::Oric::AddTargets(
}
}
// trust that any disk supplied can be handled by the Microdisc. TODO: check.
if(!disks.empty())
{
target.oric.has_microdisc = true;
target.disks = disks;
}
// TODO: really this should add two targets if not all votes agree
target.oric.use_atmos_rom = basic11_votes >= basic10_votes;
if(target.oric.has_microdisc) target.oric.use_atmos_rom = true;
if(target.tapes.size() || target.disks.size() || target.cartridges.size())
destination.push_back(target);

View File

@ -52,6 +52,7 @@ struct Target {
struct {
bool use_atmos_rom;
bool has_microdisc;
} oric;
};

View File

@ -7,3 +7,16 @@
//
#include "Disk.hpp"
using namespace Storage::Disk;
std::shared_ptr<Track> Disk::get_track_at_position(unsigned int head, unsigned int position)
{
int address = (int)(position * get_head_count() + head);
std::map<int, std::shared_ptr<Track>>::iterator cached_track = cached_tracks_.find(address);
if(cached_track != cached_tracks_.end()) return cached_track->second;
std::shared_ptr<Track> track = get_uncached_track_at_position(head, position);
cached_tracks_[address] = track;
return track;
}

View File

@ -10,6 +10,7 @@
#define Disk_hpp
#include <memory>
#include <map>
#include "../Storage.hpp"
namespace Storage {
@ -66,22 +67,34 @@ class Disk {
public:
/*!
Returns the number of discrete positions that this disk uses to model its complete surface area.
@returns the number of discrete positions that this disk uses to model its complete surface area.
This is not necessarily a track count. There is no implicit guarantee that every position will
return a distinct track, or if the media is holeless will return any track at all.
return a distinct track, or e.g. if the media is holeless will return any track at all.
*/
virtual unsigned int get_head_position_count() = 0;
/*!
Returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
@returns the number of heads (and, therefore, impliedly surfaces) available on this disk.
*/
virtual unsigned int get_head_count() { return 1; }
/*!
Returns the @c Track at @c position if there are any detectable events there; returns @c nullptr otherwise.
@returns the @c Track at @c position underneath @c head if there are any detectable events there;
returns @c nullptr otherwise.
*/
virtual std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position) = 0;
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
protected:
/*!
Subclasses should implement this to return the @c Track at @c position underneath @c head. Returned tracks
are cached internally so subclasses shouldn't attempt to build their own caches or worry about preparing
for track accesses at file load time. Appropriate behaviour is to create them lazily, on demand.
*/
virtual std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position) = 0;
private:
std::map<int, std::shared_ptr<Track>> cached_tracks_;
};
}

View File

@ -25,13 +25,11 @@ Controller::Controller(unsigned int clock_rate, unsigned int clock_rate_multipli
set_expected_bit_length(one);
}
void Controller::setup_track() // Time initial_offset
void Controller::setup_track()
{
_track = _drive->get_track();
// _track = _disk->get_track_at_position(0, (unsigned int)_head_position);
// TODO: probably a better implementation of the empty track?
/* Time offset;
Time offset;
if(_track && _time_into_track.length > 0)
{
Time time_found = _track->seek_to(_time_into_track).simplify();
@ -42,11 +40,10 @@ void Controller::setup_track() // Time initial_offset
{
offset = _time_into_track;
_time_into_track.set_zero();
}*/
}
reset_timer();
reset_timer_to_offset(offset * _rotational_multiplier);
get_next_event();
// reset_timer_to_offset(offset * _rotational_multiplier);
}
void Controller::run_for_cycles(int number_of_cycles)
@ -128,6 +125,12 @@ bool Controller::get_is_track_zero()
return _drive->get_is_track_zero();
}
bool Controller::get_drive_is_ready()
{
if(!_drive) return false;
return _drive->has_disk();
}
void Controller::step(int direction)
{
if(_drive) _drive->step(direction);
@ -139,6 +142,11 @@ void Controller::set_motor_on(bool motor_on)
_motor_is_on = motor_on;
}
bool Controller::get_motor_on()
{
return _motor_is_on;
}
void Controller::set_drive(std::shared_ptr<Drive> drive)
{
_drive = drive;

View File

@ -54,6 +54,11 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
*/
void set_motor_on(bool motor_on);
/*!
@returns @c true if the motor is on; @c false otherwise.
*/
bool get_motor_on();
/*!
Should be implemented by subclasses; communicates each bit that the PLL recognises, also specifying
the amount of time since the index hole was last seen.
@ -73,6 +78,7 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
bool get_is_track_zero();
void step(int direction);
virtual bool get_drive_is_ready();
private:
Time _bit_length;

View File

@ -13,24 +13,10 @@
using namespace Storage::Encodings::MFM;
template <class T> class Shifter {
class MFMEncoder: public Encoder {
public:
virtual void add_byte(uint8_t input) = 0;
virtual void add_index_address_mark() = 0;
virtual void add_ID_address_mark() = 0;
virtual void add_data_address_mark() = 0;
virtual void add_deleted_data_address_mark() = 0;
MFMEncoder(std::vector<uint8_t> &target) : Encoder(target) {}
protected:
/*!
Intended to be overridden by subclasses; should write value out as PCM data,
MSB first.
*/
void output_short(uint16_t value);
};
template <class T> class MFMShifter: public Shifter<T> {
public:
void add_byte(uint8_t input) {
uint16_t spread_value =
(uint16_t)(
@ -45,26 +31,26 @@ template <class T> class MFMShifter: public Shifter<T> {
);
uint16_t or_bits = (uint16_t)((spread_value << 1) | (spread_value >> 1) | (output_ << 15));
output_ = spread_value | ((~or_bits) & 0xaaaa);
static_cast<T *>(this)->output_short(output_);
output_short(output_);
}
void add_index_address_mark() {
static_cast<T *>(this)->output_short(output_ = MFMIndexAddressMark);
output_short(output_ = MFMIndexAddressMark);
add_byte(MFMIndexAddressByte);
}
void add_ID_address_mark() {
static_cast<T *>(this)->output_short(output_ = MFMAddressMark);
output_short(output_ = MFMAddressMark);
add_byte(MFMIDAddressByte);
}
void add_data_address_mark() {
static_cast<T *>(this)->output_short(output_ = MFMAddressMark);
output_short(output_ = MFMAddressMark);
add_byte(MFMDataAddressByte);
}
void add_deleted_data_address_mark() {
static_cast<T *>(this)->output_short(output_ = MFMAddressMark);
output_short(output_ = MFMAddressMark);
add_byte(MFMDeletedDataAddressByte);
}
@ -72,11 +58,13 @@ template <class T> class MFMShifter: public Shifter<T> {
uint16_t output_;
};
template <class T> class FMShifter: public Shifter<T> {
class FMEncoder: public Encoder {
// encodes each 16-bit part as clock, data, clock, data [...]
public:
FMEncoder(std::vector<uint8_t> &target) : Encoder(target) {}
void add_byte(uint8_t input) {
static_cast<T *>(this)->output_short(
output_short(
(uint16_t)(
((input & 0x01) << 0) |
((input & 0x02) << 1) |
@ -90,10 +78,10 @@ template <class T> class FMShifter: public Shifter<T> {
));
}
void add_index_address_mark() { static_cast<T *>(this)->output_short(FMIndexAddressMark); }
void add_ID_address_mark() { static_cast<T *>(this)->output_short(FMIDAddressMark); }
void add_data_address_mark() { static_cast<T *>(this)->output_short(FMDataAddressMark); }
void add_deleted_data_address_mark() { static_cast<T *>(this)->output_short(FMDeletedDataAddressMark); }
void add_index_address_mark() { output_short(FMIndexAddressMark); }
void add_ID_address_mark() { output_short(FMIDAddressMark); }
void add_data_address_mark() { output_short(FMDataAddressMark); }
void add_deleted_data_address_mark() { output_short(FMDeletedDataAddressMark); }
};
static uint8_t logarithmic_size_for_size(size_t size)
@ -118,7 +106,9 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
size_t inter_sector_gap,
size_t expected_track_bytes)
{
T shifter;
Storage::Disk::PCMSegment segment;
segment.data.reserve(expected_track_bytes);
T shifter(segment.data);
NumberTheory::CRC16 crc_generator(0x1021, 0xffff);
// output the index mark
@ -174,26 +164,25 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
for(int c = 0; c < inter_sector_gap; c++) shifter.add_byte(0x4e);
}
while(shifter.segment.data.size() < expected_track_bytes) shifter.add_byte(0x00);
while(segment.data.size() < expected_track_bytes) shifter.add_byte(0x00);
shifter.segment.number_of_bits = (unsigned int)(shifter.segment.data.size() * 8);
return std::shared_ptr<Storage::Disk::Track>(new Storage::Disk::PCMTrack(std::move(shifter.segment)));
segment.number_of_bits = (unsigned int)(segment.data.size() * 8);
return std::shared_ptr<Storage::Disk::Track>(new Storage::Disk::PCMTrack(std::move(segment)));
}
struct VectorReceiver {
void output_short(uint16_t value) {
segment.data.push_back(value >> 8);
segment.data.push_back(value & 0xff);
}
Storage::Disk::PCMSegment segment;
};
Encoder::Encoder(std::vector<uint8_t> &target) :
target_(target)
{}
void Encoder::output_short(uint16_t value)
{
target_.push_back(value >> 8);
target_.push_back(value & 0xff);
}
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSectors(const std::vector<Sector> &sectors)
{
struct VectorShifter: public FMShifter<VectorShifter>, VectorReceiver {
using VectorReceiver::output_short;
};
return GetTrackWithSectors<VectorShifter>(
return GetTrackWithSectors<FMEncoder>(
sectors,
16, 0x00,
6, 0,
@ -204,10 +193,7 @@ std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetFMTrackWithSec
std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSectors(const std::vector<Sector> &sectors)
{
struct VectorShifter: public MFMShifter<VectorShifter>, VectorReceiver {
using VectorReceiver::output_short;
};
return GetTrackWithSectors<VectorShifter>(
return GetTrackWithSectors<MFMEncoder>(
sectors,
50, 0x4e,
12, 22,
@ -215,3 +201,13 @@ std::shared_ptr<Storage::Disk::Track> Storage::Encodings::MFM::GetMFMTrackWithSe
32,
12500); // unintelligently: double the single-density bytes/rotation (or: 500kps @ 300 rpm)
}
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetMFMEncoder(std::vector<uint8_t> &target)
{
return std::unique_ptr<Encoder>(new MFMEncoder(target));
}
std::unique_ptr<Encoder> Storage::Encodings::MFM::GetFMEncoder(std::vector<uint8_t> &target)
{
return std::unique_ptr<Encoder>(new FMEncoder(target));
}

View File

@ -29,7 +29,6 @@ const uint8_t MFMIDAddressByte = 0xfe;
const uint8_t MFMDataAddressByte = 0xfb;
const uint8_t MFMDeletedDataAddressByte = 0xf8;
struct Sector {
uint8_t track, side, sector;
std::vector<uint8_t> data;
@ -38,6 +37,25 @@ struct Sector {
std::shared_ptr<Storage::Disk::Track> GetMFMTrackWithSectors(const std::vector<Sector> &sectors);
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<Sector> &sectors);
class Encoder {
public:
Encoder(std::vector<uint8_t> &target);
virtual void add_byte(uint8_t input) = 0;
virtual void add_index_address_mark() = 0;
virtual void add_ID_address_mark() = 0;
virtual void add_data_address_mark() = 0;
virtual void add_deleted_data_address_mark() = 0;
protected:
void output_short(uint16_t value);
private:
std::vector<uint8_t> &target_;
};
std::unique_ptr<Encoder> GetMFMEncoder(std::vector<uint8_t> &target);
std::unique_ptr<Encoder> GetFMEncoder(std::vector<uint8_t> &target);
}
}
}

View File

@ -47,7 +47,7 @@ unsigned int AcornADF::get_head_count()
return 1;
}
std::shared_ptr<Track> AcornADF::get_track_at_position(unsigned int head, unsigned int position)
std::shared_ptr<Track> AcornADF::get_uncached_track_at_position(unsigned int head, unsigned int position)
{
std::shared_ptr<Track> track;

View File

@ -35,7 +35,8 @@ class AcornADF: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
unsigned int get_head_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
};
}

View File

@ -41,7 +41,7 @@ unsigned int D64::get_head_position_count()
return number_of_tracks_*2;
}
std::shared_ptr<Track> D64::get_track_at_position(unsigned int head, unsigned int position)
std::shared_ptr<Track> D64::get_uncached_track_at_position(unsigned int head, unsigned int position)
{
// every other track is missing, as is any head above 0
if(position&1 || head)

View File

@ -34,9 +34,9 @@ class D64: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
unsigned int number_of_tracks_;
uint16_t disk_id_;
};

View File

@ -40,7 +40,7 @@ unsigned int G64::get_head_position_count()
return number_of_tracks_ > 84 ? number_of_tracks_ : 84;
}
std::shared_ptr<Track> G64::get_track_at_position(unsigned int head, unsigned int position)
std::shared_ptr<Track> G64::get_uncached_track_at_position(unsigned int head, unsigned int position)
{
std::shared_ptr<Track> resulting_track;

View File

@ -37,9 +37,9 @@ class G64: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
uint8_t number_of_tracks_;
uint16_t maximum_track_size_;
};

View File

@ -8,6 +8,7 @@
#include "OricMFMDSK.hpp"
#include "../PCMTrack.hpp"
#include "../Encodings/MFM.hpp"
using namespace Storage::Disk;
@ -21,7 +22,7 @@ OricMFMDSK::OricMFMDSK(const char *file_name) :
track_count_ = fgetc32le();
geometry_type_ = fgetc32le();
if(geometry_type_ > 1)
if(geometry_type_ < 1 || geometry_type_ > 2)
throw ErrorNotOricMFMDSK;
}
@ -35,24 +36,66 @@ unsigned int OricMFMDSK::get_head_count()
return head_count_;
}
std::shared_ptr<Track> OricMFMDSK::get_track_at_position(unsigned int head, unsigned int position)
std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int head, unsigned int position)
{
long offset = 0;
long seek_offset = 0;
switch(geometry_type_)
{
case 0:
offset = (head * track_count_) + position;
break;
case 1:
offset = (position * track_count_ * head_count_) + head;
seek_offset = (head * track_count_) + position;
break;
case 2:
seek_offset = (position * track_count_ * head_count_) + head;
break;
}
fseek(file_, (offset * 6400) + 256, SEEK_SET);
fseek(file_, (seek_offset * 6400) + 256, SEEK_SET);
PCMSegment segment;
segment.number_of_bits = 6250*8;
segment.data.resize(6250);
fread(segment.data.data(), 1, 6250, file_);
// The file format omits clock bits. So it's not a genuine MFM capture.
// A consumer must contextually guess when an FB, FC, etc is meant to be a control mark.
size_t track_offset = 0;
uint8_t last_header[6];
std::unique_ptr<Encodings::MFM::Encoder> encoder = Encodings::MFM::GetMFMEncoder(segment.data);
while(track_offset < 6250)
{
uint8_t next_byte = (uint8_t)fgetc(file_);
track_offset++;
switch(next_byte)
{
default:
encoder->add_byte(next_byte);
break;
case 0xfe: // an ID synchronisation
{
encoder->add_ID_address_mark();
for(int byte = 0; byte < 6; byte++)
{
last_header[byte] = (uint8_t)fgetc(file_);
encoder->add_byte(last_header[byte]);
track_offset++;
if(track_offset == 6250) break;
}
}
break;
case 0xfb: // a data synchronisation
encoder->add_data_address_mark();
for(int byte = 0; byte < (128 << last_header[3]) + 2; byte++)
{
encoder->add_byte((uint8_t)fgetc(file_));
track_offset++;
if(track_offset == 6250) break;
}
break;
}
}
segment.number_of_bits = (unsigned int)(segment.data.size() * 8);
std::shared_ptr<PCMTrack> track(new PCMTrack(segment));
return track;

View File

@ -16,7 +16,7 @@ namespace Storage {
namespace Disk {
/*!
Provies a @c Disk containing an Oric MFM-stype disk image an MFM bit stream.
Provies a @c Disk containing an Oric MFM-stype disk image a stream of the MFM data bits with clocks omitted.
*/
class OricMFMDSK: public Disk, public Storage::FileHolder {
public:
@ -35,9 +35,9 @@ class OricMFMDSK: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
unsigned int get_head_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
uint32_t head_count_;
uint32_t track_count_;
uint32_t geometry_type_;

View File

@ -40,7 +40,7 @@ unsigned int SSD::get_head_count()
return head_count_;
}
std::shared_ptr<Track> SSD::get_track_at_position(unsigned int head, unsigned int position)
std::shared_ptr<Track> SSD::get_uncached_track_at_position(unsigned int head, unsigned int position)
{
std::shared_ptr<Track> track;

View File

@ -35,9 +35,9 @@ class SSD: public Disk, public Storage::FileHolder {
// implemented to satisfy @c Disk
unsigned int get_head_position_count();
unsigned int get_head_count();
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
private:
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
unsigned int head_count_;
unsigned int track_count_;
};