mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Introduces real hard disk images to the nascent world of SCSI.
This commit is contained in:
parent
30cef1ee22
commit
ca08716c52
@ -12,8 +12,8 @@
|
||||
|
||||
using namespace NCR::NCR5380;
|
||||
|
||||
NCR5380::NCR5380(int clock_rate) :
|
||||
device_(bus_, 6),
|
||||
NCR5380::NCR5380(SCSI::Bus &bus, int clock_rate) :
|
||||
bus_(bus),
|
||||
clock_rate_(clock_rate) {
|
||||
device_id_ = bus_.add_device();
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../Storage/MassStorage/SCSI/SCSI.hpp"
|
||||
#include "../../Storage/MassStorage/SCSI/DirectAccessDevice.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../ClockReceiver/ClockingHintSource.hpp"
|
||||
|
||||
@ -25,7 +24,7 @@ namespace NCR5380 {
|
||||
*/
|
||||
class NCR5380 final: public ClockingHint::Source {
|
||||
public:
|
||||
NCR5380(int clock_rate);
|
||||
NCR5380(SCSI::Bus &bus, int clock_rate);
|
||||
|
||||
/*! Writes @c value to @c address. */
|
||||
void write(int address, uint8_t value);
|
||||
@ -53,10 +52,7 @@ class NCR5380 final: public ClockingHint::Source {
|
||||
ClockingHint::Preference preferred_clocking() final;
|
||||
|
||||
private:
|
||||
// TEMPORARY. For development expediency, the 5380 owns its own
|
||||
// SCSI bus and target. These will be moved out.
|
||||
SCSI::Bus bus_;
|
||||
SCSI::Target::Target<SCSI::DirectAccessDevice> device_;
|
||||
SCSI::Bus &bus_;
|
||||
|
||||
const int clock_rate_;
|
||||
size_t device_id_;
|
||||
|
@ -37,6 +37,9 @@
|
||||
#include "../../../Components/DiskII/MacintoshDoubleDensityDrive.hpp"
|
||||
#include "../../../Processors/68000/68000.hpp"
|
||||
|
||||
#include "../../../Storage/MassStorage/SCSI/SCSI.hpp"
|
||||
#include "../../../Storage/MassStorage/SCSI/DirectAccessDevice.hpp"
|
||||
|
||||
#include "../../../Analyser/Static/Macintosh/Target.hpp"
|
||||
|
||||
#include "../../Utility/MemoryPacker.hpp"
|
||||
@ -71,7 +74,8 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
video_(audio_, drive_speed_accumulator_),
|
||||
via_(via_port_handler_),
|
||||
via_port_handler_(*this, clock_, keyboard_, video_, audio_, iwm_, mouse_),
|
||||
scsi_(CLOCK_RATE * 2),
|
||||
scsi_(scsi_bus_, CLOCK_RATE * 2),
|
||||
hard_drive_(scsi_bus_, 6 /* SCSI ID */),
|
||||
drives_{
|
||||
{CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke},
|
||||
{CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke}
|
||||
@ -435,16 +439,23 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
}
|
||||
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
if(media.disks.empty())
|
||||
if(media.disks.empty() && media.mass_storage_devices.empty())
|
||||
return false;
|
||||
|
||||
// TODO: shouldn't allow disks to be replaced like this, as the Mac
|
||||
// uses software eject. Will need to expand messaging ability of
|
||||
// insert_media.
|
||||
if(drives_[0].has_disk())
|
||||
drives_[1].set_disk(media.disks[0]);
|
||||
else
|
||||
drives_[0].set_disk(media.disks[0]);
|
||||
if(!media.disks.empty()) {
|
||||
if(drives_[0].has_disk())
|
||||
drives_[1].set_disk(media.disks[0]);
|
||||
else
|
||||
drives_[0].set_disk(media.disks[0]);
|
||||
}
|
||||
|
||||
// TODO: allow this only at machine startup.
|
||||
if(!media.mass_storage_devices.empty()) {
|
||||
hard_drive_->set_storage(media.mass_storage_devices.front());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -720,7 +731,9 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
VIAPortHandler via_port_handler_;
|
||||
|
||||
Zilog::SCC::z8530 scc_;
|
||||
SCSI::Bus scsi_bus_;
|
||||
NCR::NCR5380::NCR5380 scsi_;
|
||||
SCSI::Target::Target<SCSI::DirectAccessDevice> hard_drive_;
|
||||
bool scsi_is_clocked_ = false;
|
||||
|
||||
HalfCycles via_clock_;
|
||||
|
@ -10,13 +10,15 @@
|
||||
|
||||
using namespace SCSI;
|
||||
|
||||
bool DirectAccessDevice::read(const Target::CommandState &state, Target::Responder &responder) {
|
||||
std::vector<uint8_t> data(512);
|
||||
for(size_t c = 0; c < 512; ++c) {
|
||||
data[c] = uint8_t(c);
|
||||
}
|
||||
|
||||
responder.send_data(std::move(data), [] (const Target::CommandState &state, Target::Responder &responder) {
|
||||
void DirectAccessDevice::set_storage(const std::shared_ptr<Storage::MassStorage::MassStorageDevice> &device) {
|
||||
device_ = device;
|
||||
}
|
||||
|
||||
bool DirectAccessDevice::read(const Target::CommandState &state, Target::Responder &responder) {
|
||||
if(!device_) return false;
|
||||
|
||||
responder.send_data(device_->get_block(state.address()), [] (const Target::CommandState &state, Target::Responder &responder) {
|
||||
responder.end_command();
|
||||
});
|
||||
|
||||
|
@ -10,12 +10,25 @@
|
||||
#define SCSI_DirectAccessDevice_hpp
|
||||
|
||||
#include "Target.hpp"
|
||||
#include "../MassStorageDevice.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace SCSI {
|
||||
|
||||
class DirectAccessDevice: public Target::Executor {
|
||||
public:
|
||||
|
||||
/*!
|
||||
Sets the backing storage exposed by this direct-access device.
|
||||
*/
|
||||
void set_storage(const std::shared_ptr<Storage::MassStorage::MassStorageDevice> &device);
|
||||
|
||||
/* SCSI commands. */
|
||||
bool read(const Target::CommandState &, Target::Responder &);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Storage::MassStorage::MassStorageDevice> device_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ using namespace SCSI::Target;
|
||||
|
||||
CommandState::CommandState(const std::vector<uint8_t> &data) : data_(data) {}
|
||||
|
||||
uint32_t CommandState::address() {
|
||||
uint32_t CommandState::address() const {
|
||||
switch(data_.size()) {
|
||||
default: return 0;
|
||||
case 6:
|
||||
@ -30,7 +30,7 @@ uint32_t CommandState::address() {
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t CommandState::number_of_blocks() {
|
||||
uint16_t CommandState::number_of_blocks() const {
|
||||
switch(data_.size()) {
|
||||
default: return 0;
|
||||
case 6:
|
||||
|
@ -24,8 +24,8 @@ class CommandState {
|
||||
public:
|
||||
CommandState(const std::vector<uint8_t> &data);
|
||||
|
||||
uint32_t address();
|
||||
uint16_t number_of_blocks();
|
||||
uint32_t address() const;
|
||||
uint16_t number_of_blocks() const;
|
||||
|
||||
private:
|
||||
const std::vector<uint8_t> &data_;
|
||||
@ -134,9 +134,13 @@ template <typename Executor> class Target: public Bus::Observer, public Responde
|
||||
*/
|
||||
Target(Bus &bus, int scsi_id);
|
||||
|
||||
Executor executor;
|
||||
inline Executor *operator->() {
|
||||
return &executor_;
|
||||
}
|
||||
|
||||
private:
|
||||
Executor executor_;
|
||||
|
||||
// Bus::Observer.
|
||||
void scsi_bus_did_change(Bus *, BusState new_state) final;
|
||||
|
||||
|
@ -147,29 +147,29 @@ template <typename Executor> bool Target<Executor>::dispatch_command() {
|
||||
switch(command_[0]) {
|
||||
default: return false;
|
||||
|
||||
case G0(0x00): return executor.test_unit_ready(arguments, *this);
|
||||
case G0(0x01): return executor.rezero_unit(arguments, *this);
|
||||
case G0(0x03): return executor.request_sense(arguments, *this);
|
||||
case G0(0x04): return executor.format_unit(arguments, *this);
|
||||
case G0(0x08): return executor.read(arguments, *this);
|
||||
case G0(0x0a): return executor.write(arguments, *this);
|
||||
case G0(0x0b): return executor.seek(arguments, *this);
|
||||
case G0(0x16): return executor.reserve_unit(arguments, *this);
|
||||
case G0(0x17): return executor.release_unit(arguments, *this);
|
||||
case G0(0x1c): return executor.read_diagnostic(arguments, *this);
|
||||
case G0(0x1d): return executor.write_diagnostic(arguments, *this);
|
||||
case G0(0x12): return executor.inquiry(arguments, *this);
|
||||
case G0(0x00): return executor_.test_unit_ready(arguments, *this);
|
||||
case G0(0x01): return executor_.rezero_unit(arguments, *this);
|
||||
case G0(0x03): return executor_.request_sense(arguments, *this);
|
||||
case G0(0x04): return executor_.format_unit(arguments, *this);
|
||||
case G0(0x08): return executor_.read(arguments, *this);
|
||||
case G0(0x0a): return executor_.write(arguments, *this);
|
||||
case G0(0x0b): return executor_.seek(arguments, *this);
|
||||
case G0(0x16): return executor_.reserve_unit(arguments, *this);
|
||||
case G0(0x17): return executor_.release_unit(arguments, *this);
|
||||
case G0(0x1c): return executor_.read_diagnostic(arguments, *this);
|
||||
case G0(0x1d): return executor_.write_diagnostic(arguments, *this);
|
||||
case G0(0x12): return executor_.inquiry(arguments, *this);
|
||||
|
||||
case G1(0x05): return executor.read_capacity(arguments, *this);
|
||||
case G1(0x08): return executor.read(arguments, *this);
|
||||
case G1(0x0a): return executor.write(arguments, *this);
|
||||
case G1(0x0e): return executor.write_and_verify(arguments, *this);
|
||||
case G1(0x0f): return executor.verify(arguments, *this);
|
||||
case G1(0x11): return executor.search_data_equal(arguments, *this);
|
||||
case G1(0x10): return executor.search_data_high(arguments, *this);
|
||||
case G1(0x12): return executor.search_data_low(arguments, *this);
|
||||
case G1(0x05): return executor_.read_capacity(arguments, *this);
|
||||
case G1(0x08): return executor_.read(arguments, *this);
|
||||
case G1(0x0a): return executor_.write(arguments, *this);
|
||||
case G1(0x0e): return executor_.write_and_verify(arguments, *this);
|
||||
case G1(0x0f): return executor_.verify(arguments, *this);
|
||||
case G1(0x11): return executor_.search_data_equal(arguments, *this);
|
||||
case G1(0x10): return executor_.search_data_high(arguments, *this);
|
||||
case G1(0x12): return executor_.search_data_low(arguments, *this);
|
||||
|
||||
case G5(0x09): return executor.set_block_limits(arguments, *this);
|
||||
case G5(0x09): return executor_.set_block_limits(arguments, *this);
|
||||
}
|
||||
|
||||
#undef G0
|
||||
|
Loading…
x
Reference in New Issue
Block a user