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:
commit
a0043ec336
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
128
Machines/Oric/Microdisc.cpp
Normal 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;
|
||||
}
|
54
Machines/Oric/Microdisc.hpp
Normal file
54
Machines/Oric/Microdisc.hpp
Normal 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 */
|
@ -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(µdisc_);
|
||||
microdisc_.set_delegate(this);
|
||||
}
|
||||
|
||||
int drive_index = 0;
|
||||
for(auto disk : target.disks)
|
||||
{
|
||||
if(drive_index < 4) microdisc_.set_disk(disk, drive_index);
|
||||
drive_index++;
|
||||
}
|
||||
|
||||
if(target.oric.use_atmos_rom)
|
||||
{
|
||||
memcpy(_rom, _basic11.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()));
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 */,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -52,6 +52,7 @@ struct Target {
|
||||
|
||||
struct {
|
||||
bool use_atmos_rom;
|
||||
bool has_microdisc;
|
||||
} oric;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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> §ors)
|
||||
{
|
||||
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> §ors)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
@ -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> §ors);
|
||||
std::shared_ptr<Storage::Disk::Track> GetFMTrackWithSectors(const std::vector<Sector> §ors);
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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_;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user