mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
This appears to be sufficient for the Electron to _read_ SCSI.
So that's step one.
This commit is contained in:
parent
f1ba040dd8
commit
906b6ccdb7
@ -146,6 +146,9 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
|
||||
if(!media.mass_storage_devices.empty()) {
|
||||
target->has_pres_adfs = false;
|
||||
target->has_acorn_adfs = true;
|
||||
|
||||
// TODO: validate an ADFS catalogue, at least.
|
||||
target->media.mass_storage_devices = media.mass_storage_devices;
|
||||
}
|
||||
|
||||
TargetList targets;
|
||||
|
@ -17,6 +17,9 @@
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||
#include "../../Processors/6502/6502.hpp"
|
||||
|
||||
#include "../../Storage/MassStorage/SCSI/SCSI.hpp"
|
||||
#include "../../Storage/MassStorage/SCSI/DirectAccessDevice.hpp"
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
|
||||
#include "../Utility/Typer.hpp"
|
||||
@ -31,7 +34,7 @@
|
||||
|
||||
namespace Electron {
|
||||
|
||||
class ConcreteMachine:
|
||||
template <bool has_scsi_bus> class ConcreteMachine:
|
||||
public Machine,
|
||||
public MachineTypes::TimedMachine,
|
||||
public MachineTypes::ScanProducer,
|
||||
@ -46,6 +49,9 @@ class ConcreteMachine:
|
||||
public:
|
||||
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||
m6502_(*this),
|
||||
scsi_bus_(4'000'000),
|
||||
hard_drive_(scsi_bus_, 0),
|
||||
scsi_device_(scsi_bus_.add_device()),
|
||||
video_output_(ram_),
|
||||
sound_generator_(audio_queue_),
|
||||
speaker_(sound_generator_) {
|
||||
@ -202,7 +208,12 @@ class ConcreteMachine:
|
||||
set_rom(slot, cartridge->get_segments().front().data, false);
|
||||
}
|
||||
|
||||
return !media.tapes.empty() || !media.disks.empty() || !media.cartridges.empty();
|
||||
// TODO: allow this only at machine startup?
|
||||
if(!media.mass_storage_devices.empty()) {
|
||||
hard_drive_->set_storage(media.mass_storage_devices.front());
|
||||
}
|
||||
|
||||
return !media.empty();
|
||||
}
|
||||
|
||||
forceinline Cycles perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) {
|
||||
@ -307,7 +318,6 @@ class ConcreteMachine:
|
||||
break;
|
||||
|
||||
case 0xfc04: case 0xfc05: case 0xfc06: case 0xfc07:
|
||||
printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value);
|
||||
if(plus3_ && (address&0x00f0) == 0x00c0) {
|
||||
if(is_holding_shift_ && address == 0xfcc4) {
|
||||
is_holding_shift_ = false;
|
||||
@ -320,15 +330,62 @@ class ConcreteMachine:
|
||||
}
|
||||
break;
|
||||
case 0xfc00:
|
||||
printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value);
|
||||
if(plus3_ && (address&0x00f0) == 0x00c0) {
|
||||
if(!isReadOperation(operation)) {
|
||||
plus3_->set_control_register(*value);
|
||||
} else *value = 1;
|
||||
}
|
||||
|
||||
if(has_scsi_bus && (address&0x00f0) == 0x0040) {
|
||||
scsi_acknowledge_ = true;
|
||||
if(!isReadOperation(operation)) {
|
||||
scsi_data_ = *value;
|
||||
push_scsi_output();
|
||||
} else {
|
||||
*value = SCSI::data_lines(scsi_bus_.get_state());
|
||||
push_scsi_output();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xfc03:
|
||||
printf("%04x %s %02x\n", address, isReadOperation(operation) ? "->" : "<-", *value);
|
||||
if(has_scsi_bus && (address&0x00f0) == 0x0040) {
|
||||
printf("SCSI IRQ: %s %02x\n", isReadOperation(operation) ? "->" : "<-", *value);
|
||||
}
|
||||
break;
|
||||
case 0xfc01:
|
||||
if(has_scsi_bus && (address&0x00f0) == 0x0040 && isReadOperation(operation)) {
|
||||
// Status byte is:
|
||||
//
|
||||
// b7: SCSI C/D
|
||||
// b6: SCSI I/O
|
||||
// b5: SCSI REQ
|
||||
// b4: interrupt flag
|
||||
// b3: 0
|
||||
// b2: 0
|
||||
// b1: SCSI BSY
|
||||
// b0: SCSI MSG
|
||||
const auto state = scsi_bus_.get_state();
|
||||
*value =
|
||||
(state & SCSI::Line::Control ? 0x80 : 0x00) |
|
||||
(state & SCSI::Line::Input ? 0x40 : 0x00) |
|
||||
(state & SCSI::Line::Request ? 0x20 : 0x00) |
|
||||
(state & SCSI::Line::Busy ? 0x02 : 0x00) |
|
||||
(state & SCSI::Line::Message ? 0x01 : 0x00);
|
||||
// TODO: interrupt flag.
|
||||
|
||||
// Empirical guess: this is also the trigger to affect busy/request/acknowledge
|
||||
// signalling. Maybe?
|
||||
if(scsi_select_ && scsi_bus_.get_state() & SCSI::Line::Busy) {
|
||||
scsi_select_ = false;
|
||||
push_scsi_output();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xfc02:
|
||||
if(has_scsi_bus && (address&0x00f0) == 0x0040) {
|
||||
scsi_select_ = true;
|
||||
push_scsi_output();
|
||||
}
|
||||
break;
|
||||
|
||||
// SCSI locations:
|
||||
@ -338,16 +395,6 @@ class ConcreteMachine:
|
||||
// fc42: select write
|
||||
// fc43: interrupt latch
|
||||
//
|
||||
// Status byte is:
|
||||
//
|
||||
// b7: SCSI C/D
|
||||
// b6: SCSI I/O
|
||||
// b5: SCSI REQ
|
||||
// b4: interrupt flag
|
||||
// b3: 0
|
||||
// b2: 0
|
||||
// b1: SCSI BSY
|
||||
// b0: SCSI MSG
|
||||
//
|
||||
// Interrupt latch is:
|
||||
//
|
||||
@ -451,6 +498,16 @@ class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: clock/change observe.
|
||||
if(has_scsi_bus) {
|
||||
scsi_bus_.run_for(Cycles(int(cycles)));
|
||||
|
||||
if(scsi_acknowledge_ && !(scsi_bus_.get_state() & SCSI::Line::Request)) {
|
||||
scsi_acknowledge_ = false;
|
||||
push_scsi_output();
|
||||
}
|
||||
}
|
||||
|
||||
return Cycles(int(cycles));
|
||||
}
|
||||
|
||||
@ -681,6 +738,21 @@ class ConcreteMachine:
|
||||
bool is_holding_shift_ = false;
|
||||
int shift_restart_counter_ = 0;
|
||||
|
||||
// Hard drive.
|
||||
SCSI::Bus scsi_bus_;
|
||||
SCSI::Target::Target<SCSI::DirectAccessDevice> hard_drive_;
|
||||
const size_t scsi_device_ = 0;
|
||||
uint8_t scsi_data_ = 0;
|
||||
bool scsi_select_ = false;
|
||||
bool scsi_acknowledge_ = false;
|
||||
void push_scsi_output() {
|
||||
scsi_bus_.set_device_output(scsi_device_,
|
||||
scsi_data_ |
|
||||
(scsi_select_ ? SCSI::Line::SelectTarget : 0) |
|
||||
(scsi_acknowledge_ ? SCSI::Line::Acknowledge : 0)
|
||||
);
|
||||
}
|
||||
|
||||
// Outputs
|
||||
VideoOutput video_output_;
|
||||
|
||||
@ -703,7 +775,12 @@ using namespace Electron;
|
||||
Machine *Machine::Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
|
||||
using Target = Analyser::Static::Acorn::Target;
|
||||
const Target *const acorn_target = dynamic_cast<const Target *>(target);
|
||||
return new Electron::ConcreteMachine(*acorn_target, rom_fetcher);
|
||||
|
||||
if(acorn_target->media.mass_storage_devices.empty()) {
|
||||
return new Electron::ConcreteMachine<false>(*acorn_target, rom_fetcher);
|
||||
} else {
|
||||
return new Electron::ConcreteMachine<true>(*acorn_target, rom_fetcher);
|
||||
}
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
@ -6,8 +6,8 @@
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef AcornADF_hpp
|
||||
#define AcornADF_hpp
|
||||
#ifndef MassStorage_AcornADF_hpp
|
||||
#define MassStorage_AcornADF_hpp
|
||||
|
||||
#include "../MassStorageDevice.hpp"
|
||||
#include "../../FileHolder.hpp"
|
||||
@ -36,4 +36,4 @@ class AcornADF: public MassStorageDevice {
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* AcornADF_hpp */
|
||||
#endif /* MassStorage_AcornADF_hpp */
|
||||
|
@ -90,6 +90,13 @@ constexpr double DeskewDelay = ns(45.0);
|
||||
/// any two devices.
|
||||
constexpr double CableSkew = ns(10.0);
|
||||
|
||||
/*!
|
||||
@returns The value of the data lines per @c state.
|
||||
*/
|
||||
constexpr uint8_t data_lines(BusState state) {
|
||||
return uint8_t(state & 0xff);
|
||||
}
|
||||
|
||||
#undef ns
|
||||
#undef us
|
||||
|
||||
|
@ -17,8 +17,8 @@ template <typename Executor> void Target<Executor>::scsi_bus_did_change(Bus *, B
|
||||
/*
|
||||
"The target determines that it is selected when the SEL# signal
|
||||
and its SCSI ID bit are active and the BSY# and I#/O signals
|
||||
are false. It then asserts the signal within a selection abort
|
||||
time."
|
||||
are false. It then asserts the signal within a selection
|
||||
abort time."
|
||||
*/
|
||||
|
||||
// Wait for deskew, at the very least.
|
||||
|
Loading…
Reference in New Issue
Block a user