mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-04-14 23:58:15 +00:00
RH11 now runs 2.11bsd reliably; interrupt priority was mistakenly set to 6 instead of 5. Fixed.
Cleaned up bizarre formatting screwups in rk11/rk05 and mscp code, along with other general cleanups.
This commit is contained in:
@@ -621,7 +621,7 @@ massbus_rp_c::Spin(void)
|
||||
|
||||
// We update only the sector count portion of the register.
|
||||
lookAhead = (lookAhead + 1) % 22;
|
||||
_controller->WriteRegister(static_cast<uint32_t>(Registers::LookAhead), lookAhead << 6);
|
||||
// _controller->WriteRegister(static_cast<uint32_t>(Registers::LookAhead), lookAhead << 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,44 +25,58 @@ using namespace std;
|
||||
#include "mscp_drive.hpp"
|
||||
#include "mscp_server.hpp"
|
||||
|
||||
mscp_drive_c::mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber) :
|
||||
storagedrive_c(controller), _useImageSize(false) {
|
||||
set_workers_count(0) ; // needs no worker()
|
||||
log_label = "MSCPD";
|
||||
SetDriveType("RA81");
|
||||
SetOffline();
|
||||
mscp_drive_c::mscp_drive_c(
|
||||
storagecontroller_c *controller,
|
||||
uint32_t driveNumber) :
|
||||
storagedrive_c(controller),
|
||||
_useImageSize(false)
|
||||
{
|
||||
set_workers_count(0) ; // needs no worker()
|
||||
log_label = "MSCPD";
|
||||
SetDriveType("RA81");
|
||||
SetOffline();
|
||||
|
||||
// Calculate the unit's ID:
|
||||
_unitDeviceNumber = driveNumber + 1;
|
||||
// Calculate the unit's ID:
|
||||
_unitDeviceNumber = driveNumber + 1;
|
||||
}
|
||||
|
||||
mscp_drive_c::~mscp_drive_c() {
|
||||
if (file_is_open()) {
|
||||
file_close();
|
||||
}
|
||||
mscp_drive_c::~mscp_drive_c()
|
||||
{
|
||||
if (file_is_open())
|
||||
{
|
||||
file_close();
|
||||
}
|
||||
}
|
||||
|
||||
// on_param_changed():
|
||||
// Handles configuration parameter changes.
|
||||
bool mscp_drive_c::on_param_changed(parameter_c *param) {
|
||||
// no own "enable" logic
|
||||
if (&type_name == param) {
|
||||
return SetDriveType(type_name.new_value.c_str());
|
||||
} else if (&image_filepath == param) {
|
||||
// Try to open the image file.
|
||||
if (file_open(image_filepath.new_value, true)) {
|
||||
image_filepath.value = image_filepath.new_value;
|
||||
UpdateCapacity();
|
||||
return true;
|
||||
}
|
||||
// TODO: if file is a nonstandard size?
|
||||
} else if (&use_image_size == param) {
|
||||
_useImageSize = use_image_size.new_value;
|
||||
use_image_size.value = use_image_size.new_value;
|
||||
UpdateCapacity();
|
||||
return true;
|
||||
}
|
||||
return device_c::on_param_changed(param); // more actions (for enable)false;
|
||||
bool mscp_drive_c::on_param_changed(parameter_c *param)
|
||||
{
|
||||
// no own "enable" logic
|
||||
if (&type_name == param)
|
||||
{
|
||||
return SetDriveType(type_name.new_value.c_str());
|
||||
}
|
||||
else if (&image_filepath == param)
|
||||
{
|
||||
// Try to open the image file.
|
||||
if (file_open(image_filepath.new_value, true))
|
||||
{
|
||||
image_filepath.value = image_filepath.new_value;
|
||||
UpdateCapacity();
|
||||
return true;
|
||||
}
|
||||
// TODO: if file is a nonstandard size?
|
||||
}
|
||||
else if (&use_image_size == param)
|
||||
{
|
||||
_useImageSize = use_image_size.new_value;
|
||||
use_image_size.value = use_image_size.new_value;
|
||||
UpdateCapacity();
|
||||
return true;
|
||||
}
|
||||
|
||||
return device_c::on_param_changed(param); // more actions (for enable);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -70,11 +84,12 @@ bool mscp_drive_c::on_param_changed(parameter_c *param) {
|
||||
// Returns the size, in bytes, of a single block on this drive.
|
||||
// This is either 512 or 576 bytes.
|
||||
//
|
||||
uint32_t mscp_drive_c::GetBlockSize() {
|
||||
//
|
||||
// For the time being this is always 512 bytes.
|
||||
//
|
||||
return 512;
|
||||
uint32_t mscp_drive_c::GetBlockSize()
|
||||
{
|
||||
//
|
||||
// For the time being this is always 512 bytes.
|
||||
//
|
||||
return 512;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -82,56 +97,65 @@ uint32_t mscp_drive_c::GetBlockSize() {
|
||||
// Get the size of the data space (not including RCT area) of this
|
||||
// drive, in blocks.
|
||||
//
|
||||
uint32_t mscp_drive_c::GetBlockCount() {
|
||||
if (_useImageSize) {
|
||||
// Return the image size / Block size (rounding down).
|
||||
return file_size() / GetBlockSize();
|
||||
} else {
|
||||
//
|
||||
// Use the size defined by the drive type.
|
||||
//
|
||||
return _driveInfo.BlockCount;
|
||||
}
|
||||
uint32_t mscp_drive_c::GetBlockCount()
|
||||
{
|
||||
if (_useImageSize)
|
||||
{
|
||||
// Return the image size / Block size (rounding down).
|
||||
return file_size() / GetBlockSize();
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Use the size defined by the drive type.
|
||||
//
|
||||
return _driveInfo.BlockCount;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GetRCTBlockCount():
|
||||
// Returns the total size of the RCT area in blocks.
|
||||
//
|
||||
uint32_t mscp_drive_c::GetRCTBlockCount() {
|
||||
return _driveInfo.RCTSize * GetRCTCopies();
|
||||
uint32_t mscp_drive_c::GetRCTBlockCount()
|
||||
{
|
||||
return _driveInfo.RCTSize * GetRCTCopies();
|
||||
}
|
||||
|
||||
//
|
||||
// GetMediaID():
|
||||
// Returns the media ID specific to this drive's type.
|
||||
//
|
||||
uint32_t mscp_drive_c::GetMediaID() {
|
||||
return _driveInfo.MediaID;
|
||||
uint32_t mscp_drive_c::GetMediaID()
|
||||
{
|
||||
return _driveInfo.MediaID;
|
||||
}
|
||||
|
||||
//
|
||||
// GetDeviceNumber():
|
||||
// Returns the unique device number for this drive.
|
||||
//
|
||||
uint32_t mscp_drive_c::GetDeviceNumber() {
|
||||
return _unitDeviceNumber;
|
||||
uint32_t mscp_drive_c::GetDeviceNumber()
|
||||
{
|
||||
return _unitDeviceNumber;
|
||||
}
|
||||
|
||||
//
|
||||
// GetClassModel():
|
||||
// Returns the class and model information for this drive.
|
||||
//
|
||||
uint16_t mscp_drive_c::GetClassModel() {
|
||||
return _unitClassModel;
|
||||
uint16_t mscp_drive_c::GetClassModel()
|
||||
{
|
||||
return _unitClassModel;
|
||||
}
|
||||
|
||||
//
|
||||
// GetRCTSize():
|
||||
// Returns the size of one copy of the RCT.
|
||||
//
|
||||
uint16_t mscp_drive_c::GetRCTSize() {
|
||||
return _driveInfo.RCTSize;
|
||||
uint16_t mscp_drive_c::GetRCTSize()
|
||||
{
|
||||
return _driveInfo.RCTSize;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -139,8 +163,9 @@ uint16_t mscp_drive_c::GetRCTSize() {
|
||||
// Returns the number of replacement blocks per track for
|
||||
// this drive.
|
||||
//
|
||||
uint8_t mscp_drive_c::GetRBNs() {
|
||||
return 0;
|
||||
uint8_t mscp_drive_c::GetRBNs()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -148,8 +173,9 @@ uint8_t mscp_drive_c::GetRBNs() {
|
||||
// Returns the number of copies of the RCT present in the RCT
|
||||
// area.
|
||||
//
|
||||
uint8_t mscp_drive_c::GetRCTCopies() {
|
||||
return 1;
|
||||
uint8_t mscp_drive_c::GetRCTCopies()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -157,8 +183,9 @@ uint8_t mscp_drive_c::GetRCTCopies() {
|
||||
// Indicates whether this drive is available (i.e. has an image
|
||||
// assigned to it and can thus be used by the controller.)
|
||||
//
|
||||
bool mscp_drive_c::IsAvailable() {
|
||||
return file_is_open();
|
||||
bool mscp_drive_c::IsAvailable()
|
||||
{
|
||||
return file_is_open();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -166,41 +193,45 @@ bool mscp_drive_c::IsAvailable() {
|
||||
// Indicates whether this drive has been placed into an Online
|
||||
// state (for example by the ONLINE command).
|
||||
//
|
||||
bool mscp_drive_c::IsOnline() {
|
||||
return _online;
|
||||
bool mscp_drive_c::IsOnline()
|
||||
{
|
||||
return _online;
|
||||
}
|
||||
|
||||
//
|
||||
// SetOnline():
|
||||
// Brings the drive online.
|
||||
//
|
||||
void mscp_drive_c::SetOnline() {
|
||||
_online = true;
|
||||
void mscp_drive_c::SetOnline()
|
||||
{
|
||||
_online = true;
|
||||
|
||||
//
|
||||
// Once online, the drive's type and image cannot be changed until
|
||||
// the drive is offline.
|
||||
//
|
||||
// type_name.readonly = true;
|
||||
// image_filepath.readonly = true;
|
||||
//
|
||||
// Once online, the drive's type and image cannot be changed until
|
||||
// the drive is offline.
|
||||
//
|
||||
// type_name.readonly = true;
|
||||
// image_filepath.readonly = true;
|
||||
}
|
||||
|
||||
//
|
||||
// SetOffline():
|
||||
// Takes the drive offline.
|
||||
//
|
||||
void mscp_drive_c::SetOffline() {
|
||||
_online = false;
|
||||
type_name.readonly = false;
|
||||
image_filepath.readonly = false;
|
||||
void mscp_drive_c::SetOffline()
|
||||
{
|
||||
_online = false;
|
||||
type_name.readonly = false;
|
||||
image_filepath.readonly = false;
|
||||
}
|
||||
|
||||
//
|
||||
// Writes the specified number of bytes from the provided buffer,
|
||||
// starting at the specified logical block.
|
||||
//
|
||||
void mscp_drive_c::Write(uint32_t blockNumber, size_t lengthInBytes, uint8_t* buffer) {
|
||||
file_write(buffer, blockNumber * GetBlockSize(), lengthInBytes);
|
||||
void mscp_drive_c::Write(uint32_t blockNumber, size_t lengthInBytes, uint8_t* buffer)
|
||||
{
|
||||
file_write(buffer, blockNumber * GetBlockSize(), lengthInBytes);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -208,14 +239,15 @@ void mscp_drive_c::Write(uint32_t blockNumber, size_t lengthInBytes, uint8_t* bu
|
||||
// block. Returns a pointer to a buffer containing the data read.
|
||||
// Caller is responsible for freeing this buffer.
|
||||
//
|
||||
uint8_t* mscp_drive_c::Read(uint32_t blockNumber, size_t lengthInBytes) {
|
||||
uint8_t* buffer = new uint8_t[lengthInBytes];
|
||||
uint8_t* mscp_drive_c::Read(uint32_t blockNumber, size_t lengthInBytes)
|
||||
{
|
||||
uint8_t* buffer = new uint8_t[lengthInBytes];
|
||||
|
||||
assert(nullptr != buffer);
|
||||
assert(nullptr != buffer);
|
||||
|
||||
file_read(buffer, blockNumber * GetBlockSize(), lengthInBytes);
|
||||
file_read(buffer, blockNumber * GetBlockSize(), lengthInBytes);
|
||||
|
||||
return buffer;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -223,11 +255,14 @@ uint8_t* mscp_drive_c::Read(uint32_t blockNumber, size_t lengthInBytes) {
|
||||
// RCT area at the specified RCT block. Buffer must be at least as large
|
||||
// as the disk's block size.
|
||||
//
|
||||
void mscp_drive_c::WriteRCTBlock(uint32_t rctBlockNumber, uint8_t* buffer) {
|
||||
assert(rctBlockNumber < GetRCTBlockCount());
|
||||
void mscp_drive_c::WriteRCTBlock(uint32_t rctBlockNumber, uint8_t* buffer)
|
||||
{
|
||||
assert(rctBlockNumber < GetRCTBlockCount());
|
||||
|
||||
memcpy(reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
|
||||
reinterpret_cast<void *>(buffer), GetBlockSize());
|
||||
memcpy(reinterpret_cast<void *>(
|
||||
_rctData.get() + rctBlockNumber * GetBlockSize()),
|
||||
reinterpret_cast<void *>(buffer),
|
||||
GetBlockSize());
|
||||
}
|
||||
|
||||
//
|
||||
@@ -235,39 +270,42 @@ void mscp_drive_c::WriteRCTBlock(uint32_t rctBlockNumber, uint8_t* buffer) {
|
||||
// block offset). Returns a pointer to a buffer containing the data read.
|
||||
// Caller is responsible for freeing this buffer.
|
||||
//
|
||||
uint8_t* mscp_drive_c::ReadRCTBlock(uint32_t rctBlockNumber) {
|
||||
assert(rctBlockNumber < GetRCTBlockCount());
|
||||
uint8_t* mscp_drive_c::ReadRCTBlock(uint32_t rctBlockNumber)
|
||||
{
|
||||
assert(rctBlockNumber < GetRCTBlockCount());
|
||||
|
||||
uint8_t* buffer = new uint8_t[GetBlockSize()];
|
||||
assert(nullptr != buffer);
|
||||
uint8_t* buffer = new uint8_t[GetBlockSize()];
|
||||
assert(nullptr != buffer);
|
||||
|
||||
memcpy(reinterpret_cast<void *>(buffer),
|
||||
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
|
||||
GetBlockSize());
|
||||
memcpy(reinterpret_cast<void *>(buffer),
|
||||
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
|
||||
GetBlockSize());
|
||||
|
||||
return buffer;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//
|
||||
// UpdateCapacity():
|
||||
// Updates the capacity parameter of the drive based on the block count and block size.
|
||||
//
|
||||
void mscp_drive_c::UpdateCapacity() {
|
||||
capacity.value = GetBlockCount() * GetBlockSize();
|
||||
void mscp_drive_c::UpdateCapacity()
|
||||
{
|
||||
capacity.value = GetBlockCount() * GetBlockSize();
|
||||
}
|
||||
|
||||
//
|
||||
// UpdateMetadata():
|
||||
// Updates the Unit Class / Model info and RCT area based on the selected drive type.
|
||||
//
|
||||
void mscp_drive_c::UpdateMetadata() {
|
||||
_unitClassModel = 0x0200 | _driveInfo.Model;
|
||||
void mscp_drive_c::UpdateMetadata()
|
||||
{
|
||||
_unitClassModel = 0x0200 | _driveInfo.Model;
|
||||
|
||||
// Initialize the RCT area
|
||||
size_t rctSize = _driveInfo.RCTSize * GetBlockSize();
|
||||
_rctData.reset(new uint8_t[rctSize]);
|
||||
assert(_rctData != nullptr);
|
||||
memset(reinterpret_cast<void *>(_rctData.get()), 0, rctSize);
|
||||
// Initialize the RCT area
|
||||
size_t rctSize = _driveInfo.RCTSize * GetBlockSize();
|
||||
_rctData.reset(new uint8_t[rctSize]);
|
||||
assert(_rctData != nullptr);
|
||||
memset(reinterpret_cast<void *>(_rctData.get()), 0, rctSize);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -279,46 +317,47 @@ void mscp_drive_c::UpdateMetadata() {
|
||||
// drive types, the drive's type is not changed and false
|
||||
// is returned.
|
||||
//
|
||||
bool mscp_drive_c::SetDriveType(const char* typeName) {
|
||||
//
|
||||
// Search through drive data table for name,
|
||||
// and if valid, set the type appropriately.
|
||||
//
|
||||
int index = 0;
|
||||
while (g_driveTable[index].BlockCount != 0) {
|
||||
if (!strcasecmp(typeName, g_driveTable[index].TypeName)) {
|
||||
_driveInfo = g_driveTable[index];
|
||||
type_name.value = _driveInfo.TypeName;
|
||||
UpdateCapacity();
|
||||
UpdateMetadata();
|
||||
return true;
|
||||
}
|
||||
bool mscp_drive_c::SetDriveType(const char* typeName)
|
||||
{
|
||||
//
|
||||
// Search through drive data table for name,
|
||||
// and if valid, set the type appropriately.
|
||||
//
|
||||
int index = 0;
|
||||
while (g_driveTable[index].BlockCount != 0)
|
||||
{
|
||||
if (!strcasecmp(typeName, g_driveTable[index].TypeName))
|
||||
{
|
||||
_driveInfo = g_driveTable[index];
|
||||
type_name.value = _driveInfo.TypeName;
|
||||
UpdateCapacity();
|
||||
UpdateMetadata();
|
||||
return true;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// Not found
|
||||
return false;
|
||||
// Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// worker():
|
||||
// worker method for this drive. No work is necessary.
|
||||
//
|
||||
//
|
||||
// on_power_changed():
|
||||
// Handle power change notifications.
|
||||
//
|
||||
void mscp_drive_c::on_power_changed(void) {
|
||||
// Take the drive offline due to power change
|
||||
SetOffline();
|
||||
void mscp_drive_c::on_power_changed(void)
|
||||
{
|
||||
// Take the drive offline due to power change
|
||||
SetOffline();
|
||||
}
|
||||
|
||||
//
|
||||
// on_init_changed():
|
||||
// Handle INIT signal.
|
||||
void mscp_drive_c::on_init_changed(void) {
|
||||
// Take the drive offline due to reset
|
||||
SetOffline();
|
||||
void mscp_drive_c::on_init_changed(void)
|
||||
{
|
||||
// Take the drive offline due to reset
|
||||
SetOffline();
|
||||
}
|
||||
|
||||
|
||||
192
10.02_devices/2_src/mscp_drive.cpp.old
Normal file
192
10.02_devices/2_src/mscp_drive.cpp.old
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
mscp_drive.cpp: Implementation of MSCP disks.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include "logger.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "mscp_drive.hpp"
|
||||
#include "mscp_server.hpp"
|
||||
|
||||
mscp_drive_c::mscp_drive_c(
|
||||
storagecontroller_c *controller,
|
||||
uint32_t driveNumber) :
|
||||
storagedrive_c(controller)
|
||||
{
|
||||
log_label = "MSCPD";
|
||||
SetDriveType("RA81");
|
||||
SetOffline();
|
||||
|
||||
// Calculate the unit's ID:
|
||||
// drive number in upper 32 bits, class/model in lower.
|
||||
_unitID = (static_cast<uint64_t>(0xffffffff) << 32) | 0x02020000;
|
||||
}
|
||||
|
||||
mscp_drive_c::~mscp_drive_c()
|
||||
{
|
||||
if (file_is_open())
|
||||
{
|
||||
file_close();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mscp_drive_c::GetBlockSize()
|
||||
{
|
||||
//
|
||||
// For the time being this is always 512 bytes.
|
||||
//
|
||||
return 512;
|
||||
}
|
||||
|
||||
uint32_t mscp_drive_c::GetBlockCount()
|
||||
{
|
||||
// TODO: need to be able to handle drives of arbitrary size, not just
|
||||
// DEC-branded units.
|
||||
return _driveInfo.BlockCount;
|
||||
}
|
||||
|
||||
uint32_t mscp_drive_c::GetMediaID()
|
||||
{
|
||||
return _driveInfo.MediaID;
|
||||
}
|
||||
|
||||
uint64_t mscp_drive_c::GetUnitID()
|
||||
{
|
||||
return _unitID;
|
||||
}
|
||||
|
||||
bool mscp_drive_c::IsAvailable()
|
||||
{
|
||||
return file_is_open();
|
||||
}
|
||||
|
||||
bool mscp_drive_c::IsOnline()
|
||||
{
|
||||
return _online;
|
||||
}
|
||||
|
||||
void mscp_drive_c::SetOnline()
|
||||
{
|
||||
_online = true;
|
||||
|
||||
//
|
||||
// Once online, the drive's type and image cannot be changed until
|
||||
// the drive is offline.
|
||||
//
|
||||
type_name.readonly = true;
|
||||
image_filepath.readonly = true;
|
||||
}
|
||||
|
||||
void mscp_drive_c::SetOffline()
|
||||
{
|
||||
_online = false;
|
||||
type_name.readonly = false;
|
||||
image_filepath.readonly = false;
|
||||
}
|
||||
|
||||
//
|
||||
// Writes the specified number of bytes from the provided buffer,
|
||||
// starting at the specified logical block.
|
||||
//
|
||||
void mscp_drive_c::Write(
|
||||
uint32_t blockNumber,
|
||||
size_t lengthInBytes,
|
||||
uint8_t* buffer)
|
||||
{
|
||||
file_write(
|
||||
buffer,
|
||||
blockNumber * GetBlockSize(),
|
||||
lengthInBytes);
|
||||
}
|
||||
|
||||
//
|
||||
// Reads the specifed number of bytes starting at the specified logical
|
||||
// block. Returns a pointer to a buffer containing the data read.
|
||||
// Caller is responsible for freeing this buffer.
|
||||
//
|
||||
uint8_t* mscp_drive_c::Read(
|
||||
uint32_t blockNumber,
|
||||
size_t lengthInBytes)
|
||||
{
|
||||
uint8_t* buffer = new uint8_t[lengthInBytes];
|
||||
|
||||
assert(nullptr != buffer);
|
||||
|
||||
file_read(
|
||||
buffer,
|
||||
blockNumber * GetBlockSize(),
|
||||
lengthInBytes);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool mscp_drive_c::on_param_changed(
|
||||
parameter_c *param)
|
||||
{
|
||||
if (&type_name == param)
|
||||
{
|
||||
return SetDriveType(type_name.new_value.c_str());
|
||||
}
|
||||
else if (&image_filepath == param)
|
||||
{
|
||||
//
|
||||
// Try to open the image file.
|
||||
//
|
||||
if (file_open(image_filepath.new_value, true))
|
||||
{
|
||||
image_filepath.value = image_filepath.new_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: if file is a nonstandard size?
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mscp_drive_c::SetDriveType(const char* typeName)
|
||||
{
|
||||
//
|
||||
// Search through drive data table for name,
|
||||
// and if valid, set the type appropriately.
|
||||
//
|
||||
int index = 0;
|
||||
while (g_driveTable[index].BlockCount != 0)
|
||||
{
|
||||
if (!strcasecmp(typeName, g_driveTable[index].TypeName))
|
||||
{
|
||||
_driveInfo = g_driveTable[index];
|
||||
type_name.value = _driveInfo.TypeName;
|
||||
capacity.value = GetBlockCount() * GetBlockSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
void mscp_drive_c::worker(void)
|
||||
{
|
||||
// Nothing to do here at the moment.
|
||||
}
|
||||
|
||||
void mscp_drive_c::on_power_changed(void)
|
||||
{
|
||||
// Take the drive offline due to power change
|
||||
SetOffline();
|
||||
}
|
||||
|
||||
void mscp_drive_c::on_init_changed(void)
|
||||
{
|
||||
// Take the drive offline due to reset
|
||||
SetOffline();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ private:
|
||||
uint32_t MediaID;
|
||||
uint8_t Model;
|
||||
uint16_t RCTSize;bool Removable;bool ReadOnly;
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// TODO: add a lot more drive types.
|
||||
@@ -80,7 +80,7 @@ private:
|
||||
{ "RC25F", 50902, 0x20643319, 3, 0, true, false },
|
||||
{ "RD52", 60480, 0x25644034, 8, 4, false, false },
|
||||
{ "RD32", 83236, 0x25641047, 15, 4, false, false },
|
||||
{ "RD53", 138672, 0x25644035, 9, 5, false, false },
|
||||
{ "RD53", 138672, 0x25644035, 9, 5, false, false },
|
||||
{ "RA80", 237212, 0x20643019, 1, 0, false, false },
|
||||
{ "RD54", 311200, 0x25644036, 13, 7, false, false },
|
||||
{ "RA60", 400176, 0x22a4103c, 4, 1008, true, false },
|
||||
|
||||
89
10.02_devices/2_src/mscp_drive.hpp.old
Normal file
89
10.02_devices/2_src/mscp_drive.hpp.old
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
mscp_drive.hpp: Implementation of MSCP drive, used with MSCP controller.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "parameter.hpp"
|
||||
#include "storagedrive.hpp"
|
||||
|
||||
//
|
||||
// Implements the backing store for MSCP disk images
|
||||
//
|
||||
class mscp_drive_c : public storagedrive_c
|
||||
{
|
||||
public:
|
||||
mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber);
|
||||
~mscp_drive_c(void);
|
||||
|
||||
uint32_t GetBlockSize(void);
|
||||
uint32_t GetBlockCount(void);
|
||||
uint32_t GetMediaID(void);
|
||||
uint64_t GetUnitID(void);
|
||||
|
||||
void SetOnline(void);
|
||||
void SetOffline(void);
|
||||
bool IsOnline(void);
|
||||
bool IsAvailable(void);
|
||||
|
||||
void Write(
|
||||
uint32_t blockNumber,
|
||||
size_t lengthInBytes,
|
||||
uint8_t* buffer);
|
||||
|
||||
uint8_t* Read(
|
||||
uint32_t blockNumber,
|
||||
size_t lengthInBytes);
|
||||
|
||||
public:
|
||||
bool on_param_changed(parameter_c *param) override;
|
||||
void on_power_changed(void) override;
|
||||
void on_init_changed(void) override;
|
||||
|
||||
void worker(void) override;
|
||||
|
||||
private:
|
||||
|
||||
struct DriveInfo
|
||||
{
|
||||
char TypeName[16];
|
||||
size_t BlockCount;
|
||||
uint32_t MediaID;
|
||||
bool Removable;
|
||||
bool ReadOnly;
|
||||
};
|
||||
|
||||
DriveInfo g_driveTable[23]
|
||||
{
|
||||
{ "RX50", 800, 0x25658032, true, false },
|
||||
{ "RX33", 2400, 0x25658021, true, false },
|
||||
{ "RD51", 21600, 0x25644033, false, false },
|
||||
{ "RD31", 41560, 0x2564401f, false, false },
|
||||
{ "RC25", 50902, 0x20643019, true, false },
|
||||
{ "RC25F", 50902, 0x20643319, true, false },
|
||||
{ "RD52", 60480, 0x25644034, false, false },
|
||||
{ "RD32", 83236, 0x25641047, false, false },
|
||||
{ "RD53", 138672, 0x25644035, false, false },
|
||||
{ "RA80", 237212, 0x20643019, false, false },
|
||||
{ "RD54", 311200, 0x25644036, false, false },
|
||||
{ "RA60", 400176, 0x22a4103c, true, false },
|
||||
{ "RA70", 547041, 0x20643019, false, false },
|
||||
{ "RA81", 891072, 0x25641051, false, false },
|
||||
{ "RA82", 1216665, 0x25641052, false, false },
|
||||
{ "RA71", 1367310, 0x25641047, false, false },
|
||||
{ "RRD40", 1331200, 0x25652228, true, true },
|
||||
{ "RA72", 1953300, 0x25641048, false, false },
|
||||
{ "RA90", 2376153, 0x2564105a, false, false },
|
||||
{ "RA92", 2940951, 0x2564105c, false, false },
|
||||
{ "RA73", 3920490, 0x25641049, false, false },
|
||||
{ "JD90", 2376153, 0x2564105d, false, false },
|
||||
{ "", 0, 0, false, false }
|
||||
};
|
||||
|
||||
bool SetDriveType(const char* typeName);
|
||||
DriveInfo _driveInfo;
|
||||
bool _online;
|
||||
uint64_t _unitID;
|
||||
};
|
||||
@@ -58,26 +58,25 @@ void* polling_worker(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mscp_server::mscp_server(
|
||||
uda_c *port) :
|
||||
device_c(),
|
||||
_hostTimeout(0),
|
||||
_controllerFlags(0),
|
||||
_abort_polling(false),
|
||||
_pollState(PollingState::Wait),
|
||||
polling_cond(PTHREAD_COND_INITIALIZER),
|
||||
polling_mutex(PTHREAD_MUTEX_INITIALIZER),
|
||||
_credits(INIT_CREDITS)
|
||||
mscp_server::mscp_server(uda_c *port) :
|
||||
device_c(),
|
||||
_hostTimeout(0),
|
||||
_controllerFlags(0),
|
||||
_abort_polling(false),
|
||||
_pollState(PollingState::Wait),
|
||||
polling_cond(PTHREAD_COND_INITIALIZER),
|
||||
polling_mutex(PTHREAD_MUTEX_INITIALIZER),
|
||||
_credits(INIT_CREDITS)
|
||||
{
|
||||
set_workers_count(0) ; // no std worker()
|
||||
name.value = "mscp_server" ;
|
||||
type_name.value = "mscp_server_c" ;
|
||||
log_label = "MSSVR" ;
|
||||
set_workers_count(0); // no std worker()
|
||||
name.value = "mscp_server";
|
||||
type_name.value = "mscp_server_c";
|
||||
log_label = "MSSVR";
|
||||
// Alias the port pointer. We do not own the port, we merely reference it.
|
||||
_port = port;
|
||||
|
||||
enabled.set(true) ;
|
||||
enabled.readonly = true ; // always active
|
||||
enabled.set(true);
|
||||
enabled.readonly = true; // always active
|
||||
|
||||
StartPollingThread();
|
||||
}
|
||||
@@ -89,15 +88,17 @@ mscp_server::~mscp_server()
|
||||
}
|
||||
|
||||
|
||||
bool mscp_server::on_param_changed(parameter_c *param) {
|
||||
// no own parameter or "enable" logic
|
||||
if (param == &enabled) {
|
||||
// accpet, but do not react on enable/disable, always active
|
||||
return true ;
|
||||
}
|
||||
return device_c::on_param_changed(param) ; // more actions (for enable)
|
||||
}
|
||||
bool mscp_server::on_param_changed(parameter_c *param)
|
||||
{
|
||||
// no own parameter or "enable" logic
|
||||
if (param == &enabled)
|
||||
{
|
||||
// accept, but do not react on enable/disable, always active
|
||||
return true;
|
||||
}
|
||||
|
||||
return device_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
@@ -1070,7 +1071,7 @@ mscp_server::GetParameterPointer(
|
||||
shared_ptr<Message> message)
|
||||
{
|
||||
// We silence a strict aliasing warning here; this is safe (if perhaps not recommended
|
||||
// the general case.)
|
||||
// in the general case.)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
return reinterpret_cast<ControlMessageHeader*>(message->Message)->Parameters;
|
||||
|
||||
@@ -123,7 +123,7 @@ rh11_c::rh11_c() :
|
||||
// base addr, intr-vector, intr level
|
||||
// TODO: make configurable based on type (fixed, tape, moving-head disk)
|
||||
// right now it is hardcoded for moving-head disks.
|
||||
set_default_bus_params(0776700, 5, 0254, 6);
|
||||
set_default_bus_params(0776700, 5, 0254, 5);
|
||||
|
||||
_massbus.reset(new massbus_rp_c(this));
|
||||
|
||||
@@ -251,7 +251,7 @@ rh11_c::BusStatus(
|
||||
_go = false;
|
||||
}
|
||||
|
||||
UpdateCS1();
|
||||
UpdateCS1(false /* no interrupt, yet */);
|
||||
|
||||
if (!_controllerClear)
|
||||
{
|
||||
@@ -259,14 +259,15 @@ rh11_c::BusStatus(
|
||||
UpdateCS2();
|
||||
}
|
||||
|
||||
if ((attention || ready) && completion)
|
||||
if ((attention || ready) && completion && _interruptEnable)
|
||||
{
|
||||
Interrupt();
|
||||
_interruptEnable = false;
|
||||
UpdateCS1(true /* interrupt & clear IE bit */);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rh11_c::UpdateCS1()
|
||||
rh11_c::UpdateCS1(bool interrupt)
|
||||
{
|
||||
uint16_t newStatus =
|
||||
(_attention ? 0100000 : 0) |
|
||||
@@ -280,11 +281,18 @@ rh11_c::UpdateCS1()
|
||||
(_go ? 0000001 : 0);
|
||||
|
||||
DEBUG("RHCS1 is now o%o", newStatus);
|
||||
|
||||
set_register_dati_value(
|
||||
RH_reg[RHCS1],
|
||||
newStatus,
|
||||
"UpdateCS1");
|
||||
|
||||
if (interrupt)
|
||||
{
|
||||
unibusadapter->INTR(intr_request, RH_reg[RHCS1], newStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_register_dati_value(
|
||||
RH_reg[RHCS1],
|
||||
newStatus,
|
||||
"UpdateCS1");
|
||||
}
|
||||
}
|
||||
|
||||
void rh11_c::UpdateCS2()
|
||||
@@ -402,7 +410,7 @@ rh11_c::IncrementBusAddress(uint32_t delta)
|
||||
{
|
||||
_busAddress += delta;
|
||||
|
||||
UpdateCS1();
|
||||
UpdateCS1(false /* no interrupt */);
|
||||
|
||||
set_register_dati_value(
|
||||
RH_reg[RHBA],
|
||||
@@ -602,7 +610,7 @@ void rh11_c::on_after_register_access(
|
||||
}
|
||||
|
||||
DEBUG("RHCS1: IE %d BA o%o func o%o go %d", _interruptEnable, _busAddress, _function, _go);
|
||||
UpdateCS1();
|
||||
UpdateCS1(false /* no interrupt */);
|
||||
|
||||
if ((dato_mask & 0x00ff) == 0x00ff)
|
||||
{
|
||||
@@ -704,20 +712,6 @@ void rh11_c::on_after_register_access(
|
||||
|
||||
}
|
||||
|
||||
void rh11_c::Interrupt(void)
|
||||
{
|
||||
if(_interruptEnable)
|
||||
{
|
||||
DEBUG("Interrupt!");
|
||||
|
||||
// IE is cleared after the interrupt is raised (probably should be at the same time.)
|
||||
_interruptEnable = false;
|
||||
UpdateCS1();
|
||||
|
||||
unibusadapter->INTR(intr_request, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void rh11_c::reset_controller(void)
|
||||
{
|
||||
reset_unibus_registers();
|
||||
|
||||
@@ -42,7 +42,6 @@ private:
|
||||
dma_request_c dma_request = dma_request_c(this); // operated by unibusadapter
|
||||
intr_request_c intr_request = intr_request_c(this);
|
||||
|
||||
void Interrupt(void);
|
||||
void reset_controller(void);
|
||||
|
||||
void IncrementBusAddress(uint32_t delta);
|
||||
@@ -51,7 +50,7 @@ private:
|
||||
bool DMAWrite(uint32_t address, size_t lengthInWords, uint16_t* buffer);
|
||||
uint16_t* DMARead(uint32_t address, size_t lengthInWords);
|
||||
|
||||
void UpdateCS1();
|
||||
void UpdateCS1(bool interrupt);
|
||||
void UpdateCS2();
|
||||
|
||||
private:
|
||||
|
||||
@@ -16,250 +16,307 @@ using namespace std;
|
||||
#include "rk05.hpp"
|
||||
|
||||
rk05_c::rk05_c(storagecontroller_c *controller) :
|
||||
storagedrive_c(controller), _current_cylinder(0), _seek_count(0), _sectorCount(0), _wps(
|
||||
false), _rwsrdy(true), _dry(false), _sok(false), _sin(false), _dru(false), _rk05(
|
||||
true), _dpl(false), _scp(false) {
|
||||
name.value = "RK05";
|
||||
type_name.value = "RK05";
|
||||
log_label = "RK05";
|
||||
_geometry.Cylinders = 203; // Standard RK05
|
||||
_geometry.Heads = 2;
|
||||
_geometry.Sectors = 12;
|
||||
_geometry.Sector_Size_Bytes = 512;
|
||||
storagedrive_c(controller),
|
||||
_current_cylinder(0),
|
||||
_seek_count(0),
|
||||
_sectorCount(0),
|
||||
_wps(false),
|
||||
_rwsrdy(true),
|
||||
_dry(false),
|
||||
_sok(false),
|
||||
_sin(false),
|
||||
_dru(false),
|
||||
_rk05(true),
|
||||
_dpl(false),
|
||||
_scp(false)
|
||||
{
|
||||
name.value = "RK05";
|
||||
type_name.value = "RK05";
|
||||
log_label = "RK05";
|
||||
_geometry.Cylinders = 203; // Standard RK05
|
||||
_geometry.Heads = 2;
|
||||
_geometry.Sectors = 12;
|
||||
_geometry.Sector_Size_Bytes = 512;
|
||||
}
|
||||
|
||||
//
|
||||
// Status registers
|
||||
//
|
||||
|
||||
uint32_t rk05_c::get_sector_counter(void) {
|
||||
return _sectorCount;
|
||||
uint32_t rk05_c::get_sector_counter(void)
|
||||
{
|
||||
return _sectorCount;
|
||||
}
|
||||
|
||||
bool rk05_c::get_write_protect(void) {
|
||||
return _wps;
|
||||
bool rk05_c::get_write_protect(void)
|
||||
{
|
||||
return _wps;
|
||||
}
|
||||
|
||||
bool rk05_c::get_rws_ready(void) {
|
||||
return _rwsrdy;
|
||||
bool rk05_c::get_rws_ready(void)
|
||||
{
|
||||
return _rwsrdy;
|
||||
}
|
||||
|
||||
bool rk05_c::get_drive_ready(void) {
|
||||
return _dry;
|
||||
bool rk05_c::get_drive_ready(void)
|
||||
{
|
||||
return _dry;
|
||||
}
|
||||
|
||||
bool rk05_c::get_sector_counter_ok(void) {
|
||||
return _sok;
|
||||
bool rk05_c::get_sector_counter_ok(void)
|
||||
{
|
||||
return _sok;
|
||||
}
|
||||
|
||||
bool rk05_c::get_seek_incomplete(void) {
|
||||
return _sin;
|
||||
bool rk05_c::get_seek_incomplete(void)
|
||||
{
|
||||
return _sin;
|
||||
}
|
||||
|
||||
bool rk05_c::get_drive_unsafe(void) {
|
||||
return _dru;
|
||||
bool rk05_c::get_drive_unsafe(void)
|
||||
{
|
||||
return _dru;
|
||||
}
|
||||
|
||||
bool rk05_c::get_rk05_disk_online(void) {
|
||||
return _rk05;
|
||||
bool rk05_c::get_rk05_disk_online(void)
|
||||
{
|
||||
return _rk05;
|
||||
}
|
||||
|
||||
bool rk05_c::get_drive_power_low(void) {
|
||||
return _dpl;
|
||||
bool rk05_c::get_drive_power_low(void)
|
||||
{
|
||||
return _dpl;
|
||||
}
|
||||
|
||||
bool rk05_c::get_search_complete(void) {
|
||||
bool scp = _scp;
|
||||
_scp = false;
|
||||
return scp;
|
||||
bool rk05_c::get_search_complete(void)
|
||||
{
|
||||
bool scp = _scp;
|
||||
_scp = false;
|
||||
return scp;
|
||||
}
|
||||
|
||||
bool rk05_c::on_param_changed(parameter_c *param) {
|
||||
if (param == &enabled) {
|
||||
if (!enabled.new_value) {
|
||||
// disable switches power OFF.
|
||||
drive_reset();
|
||||
}
|
||||
} else if (&image_filepath == param) {
|
||||
if (file_open(image_filepath.new_value, true)) {
|
||||
_dry = true;
|
||||
controller->on_drive_status_changed(this);
|
||||
image_filepath.value = image_filepath.new_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return storagedrive_c::on_param_changed(param); // more actions (for enable)
|
||||
bool rk05_c::on_param_changed(parameter_c *param)
|
||||
{
|
||||
if (param == &enabled)
|
||||
{
|
||||
if (!enabled.new_value)
|
||||
{
|
||||
// disable switches power OFF.
|
||||
drive_reset();
|
||||
}
|
||||
}
|
||||
else if (&image_filepath == param)
|
||||
{
|
||||
if (file_open(image_filepath.new_value, true))
|
||||
{
|
||||
_dry = true;
|
||||
controller->on_drive_status_changed(this);
|
||||
image_filepath.value = image_filepath.new_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return storagedrive_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
//
|
||||
// Reset / Power handlers
|
||||
//
|
||||
|
||||
void rk05_c::on_power_changed(void) {
|
||||
// called at high priority.
|
||||
if (power_down) {
|
||||
// power-on defaults
|
||||
drive_reset();
|
||||
}
|
||||
void rk05_c::on_power_changed(void)
|
||||
{
|
||||
if (power_down)
|
||||
{
|
||||
// power-on defaults
|
||||
drive_reset();
|
||||
}
|
||||
}
|
||||
|
||||
void rk05_c::on_init_changed(void) {
|
||||
// called at high priority.
|
||||
|
||||
if (init_asserted) {
|
||||
drive_reset();
|
||||
}
|
||||
void rk05_c::on_init_changed(void)
|
||||
{
|
||||
if (init_asserted)
|
||||
{
|
||||
drive_reset();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Disk actions (read/write/seek/reset)
|
||||
//
|
||||
|
||||
void rk05_c::read_sector(uint32_t cylinder, uint32_t surface, uint32_t sector,
|
||||
uint16_t* out_buffer) {
|
||||
assert(cylinder < _geometry.Cylinders);
|
||||
assert(surface < _geometry.Heads);
|
||||
assert(sector < _geometry.Sectors);
|
||||
void rk05_c::read_sector(
|
||||
uint32_t cylinder,
|
||||
uint32_t surface,
|
||||
uint32_t sector,
|
||||
uint16_t* out_buffer)
|
||||
{
|
||||
assert(cylinder < _geometry.Cylinders);
|
||||
assert(surface < _geometry.Heads);
|
||||
assert(sector < _geometry.Sectors);
|
||||
|
||||
_current_cylinder = cylinder;
|
||||
_current_cylinder = cylinder;
|
||||
|
||||
// SCP is cleared at the start of any function.
|
||||
_scp = false;
|
||||
// SCP is cleared at the start of any function.
|
||||
_scp = false;
|
||||
|
||||
//
|
||||
// reset Read/Write/Seek Ready flag while we do this operation
|
||||
//
|
||||
_rwsrdy = false;
|
||||
controller->on_drive_status_changed(this);
|
||||
//
|
||||
// reset Read/Write/Seek Ready flag while we do this operation
|
||||
//
|
||||
_rwsrdy = false;
|
||||
controller->on_drive_status_changed(this);
|
||||
|
||||
timeout_c delay;
|
||||
timeout_c delay;
|
||||
|
||||
// Delay for seek / read.
|
||||
// TODO: maybe base this on real drive specs.
|
||||
delay.wait_ms(10);
|
||||
// Delay for seek / read.
|
||||
// TODO: maybe base this on real drive specs.
|
||||
delay.wait_ms(10);
|
||||
|
||||
// Read the sector into the buffer passed to us.
|
||||
file_read(reinterpret_cast<uint8_t*>(out_buffer),
|
||||
get_disk_byte_offset(cylinder, surface, sector), _geometry.Sector_Size_Bytes);
|
||||
// Read the sector into the buffer passed to us.
|
||||
file_read(
|
||||
reinterpret_cast<uint8_t*>(out_buffer),
|
||||
get_disk_byte_offset(cylinder, surface, sector),
|
||||
_geometry.Sector_Size_Bytes);
|
||||
|
||||
// Set RWS ready now that we're done.
|
||||
_rwsrdy = true;
|
||||
// Set RWS ready now that we're done.
|
||||
_rwsrdy = true;
|
||||
|
||||
controller->on_drive_status_changed(this);
|
||||
controller->on_drive_status_changed(this);
|
||||
}
|
||||
|
||||
void rk05_c::write_sector(uint32_t cylinder, uint32_t surface, uint32_t sector,
|
||||
uint16_t* in_buffer) {
|
||||
assert(cylinder < _geometry.Cylinders);
|
||||
assert(surface < _geometry.Heads);
|
||||
assert(sector < _geometry.Sectors);
|
||||
void rk05_c::write_sector(
|
||||
uint32_t cylinder,
|
||||
uint32_t surface,
|
||||
uint32_t sector,
|
||||
uint16_t* in_buffer)
|
||||
{
|
||||
assert(cylinder < _geometry.Cylinders);
|
||||
assert(surface < _geometry.Heads);
|
||||
assert(sector < _geometry.Sectors);
|
||||
|
||||
_current_cylinder = cylinder;
|
||||
_current_cylinder = cylinder;
|
||||
|
||||
// SCP is cleared at the start of any function.
|
||||
_scp = false;
|
||||
// SCP is cleared at the start of any function.
|
||||
_scp = false;
|
||||
|
||||
//
|
||||
// reset Read/Write/Seek Ready flag while we do this operation
|
||||
//
|
||||
_rwsrdy = false;
|
||||
controller->on_drive_status_changed(this);
|
||||
//
|
||||
// reset Read/Write/Seek Ready flag while we do this operation
|
||||
//
|
||||
_rwsrdy = false;
|
||||
controller->on_drive_status_changed(this);
|
||||
|
||||
timeout_c delay;
|
||||
timeout_c delay;
|
||||
|
||||
// Delay for seek / read.
|
||||
// TODO: maybe base this on real drive specs.
|
||||
delay.wait_ms(10);
|
||||
// Delay for seek / read.
|
||||
// TODO: maybe base this on real drive specs.
|
||||
delay.wait_ms(10);
|
||||
|
||||
// Read the sector into the buffer passed to us.
|
||||
file_write(reinterpret_cast<uint8_t*>(in_buffer),
|
||||
get_disk_byte_offset(cylinder, surface, sector), _geometry.Sector_Size_Bytes);
|
||||
// Read the sector into the buffer passed to us.
|
||||
file_write(
|
||||
reinterpret_cast<uint8_t*>(in_buffer),
|
||||
get_disk_byte_offset(cylinder, surface, sector),
|
||||
_geometry.Sector_Size_Bytes);
|
||||
|
||||
// Set RWS ready now that we're done.
|
||||
_rwsrdy = true;
|
||||
// Set RWS ready now that we're done.
|
||||
_rwsrdy = true;
|
||||
|
||||
controller->on_drive_status_changed(this);
|
||||
controller->on_drive_status_changed(this);
|
||||
}
|
||||
|
||||
void rk05_c::seek(uint32_t cylinder) {
|
||||
assert(cylinder < _geometry.Cylinders);
|
||||
void rk05_c::seek(uint32_t cylinder)
|
||||
{
|
||||
assert(cylinder < _geometry.Cylinders);
|
||||
|
||||
_seek_count = abs((int32_t) _current_cylinder - (int32_t) cylinder) + 1;
|
||||
_current_cylinder = cylinder;
|
||||
_seek_count = abs((int32_t) _current_cylinder - (int32_t) cylinder) + 1;
|
||||
_current_cylinder = cylinder;
|
||||
|
||||
if (_seek_count > 0) {
|
||||
// We'll be busy for awhile:
|
||||
_rwsrdy = false;
|
||||
_scp = false;
|
||||
} else {
|
||||
_rwsrdy = true;
|
||||
_scp = true;
|
||||
}
|
||||
controller->on_drive_status_changed(this);
|
||||
if (_seek_count > 0)
|
||||
{
|
||||
// We'll be busy for awhile:
|
||||
_rwsrdy = false;
|
||||
_scp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rwsrdy = true;
|
||||
_scp = true;
|
||||
}
|
||||
controller->on_drive_status_changed(this);
|
||||
}
|
||||
|
||||
void rk05_c::set_write_protect(bool protect) {
|
||||
UNUSED(protect);
|
||||
void rk05_c::set_write_protect(bool protect)
|
||||
{
|
||||
UNUSED(protect);
|
||||
|
||||
// Not implemented at the moment.
|
||||
_scp = false;
|
||||
// Not implemented at the moment.
|
||||
_scp = false;
|
||||
}
|
||||
|
||||
void rk05_c::drive_reset(void) {
|
||||
//
|
||||
// "The controller directs the selected disk drive to move its
|
||||
// head mechanism to cylinder address 000 and reset all active
|
||||
// error status lines."
|
||||
//
|
||||
// This is basically the same as a seek to cylinder 0 plus
|
||||
// a reset of error status.
|
||||
//
|
||||
_sin = false;
|
||||
_dru = false;
|
||||
_dpl = false;
|
||||
controller->on_drive_status_changed(this);
|
||||
void rk05_c::drive_reset(void)
|
||||
{
|
||||
//
|
||||
// "The controller directs the selected disk drive to move its
|
||||
// head mechanism to cylinder address 000 and reset all active
|
||||
// error status lines."
|
||||
//
|
||||
// This is basically the same as a seek to cylinder 0 plus
|
||||
// a reset of error status.
|
||||
//
|
||||
_sin = false;
|
||||
_dru = false;
|
||||
_dpl = false;
|
||||
controller->on_drive_status_changed(this);
|
||||
|
||||
seek(0);
|
||||
// SCP change will be posted when the seek instigated above is completed.
|
||||
seek(0);
|
||||
// SCP change will be posted when the seek initiated above is completed.
|
||||
}
|
||||
|
||||
void rk05_c::worker(unsigned instance) {
|
||||
UNUSED(instance) ; // only one
|
||||
timeout_c timeout;
|
||||
void rk05_c::worker(unsigned instance)
|
||||
{
|
||||
UNUSED(instance) ; // only one
|
||||
timeout_c timeout;
|
||||
|
||||
while (true) {
|
||||
if (_seek_count > 0) {
|
||||
// A seek is active. Wait at least 10ms and decrement
|
||||
// The seek count by a certain amount. This is completely fudged.
|
||||
timeout.wait_ms(3);
|
||||
_seek_count -= 25;
|
||||
// since simultaneous interrupts
|
||||
// confuse me right now
|
||||
while (true)
|
||||
{
|
||||
if (_seek_count > 0)
|
||||
{
|
||||
// A seek is active. Wait at least 10ms and decrement
|
||||
// The seek count by a certain amount. This is completely fudged.
|
||||
timeout.wait_ms(3);
|
||||
_seek_count -= 25;
|
||||
// since simultaneous interrupts
|
||||
// confuse me right now
|
||||
|
||||
if (_seek_count < 0) {
|
||||
// Out of seeks to do, let the controller know we're done.
|
||||
_scp = true;
|
||||
controller->on_drive_status_changed(this);
|
||||
if (_seek_count < 0)
|
||||
{
|
||||
// Out of seeks to do, let the controller know we're done.
|
||||
_scp = true;
|
||||
controller->on_drive_status_changed(this);
|
||||
|
||||
// Set RWSRDY only after posting status change / interrupt...
|
||||
_rwsrdy = true;
|
||||
}
|
||||
} else {
|
||||
// Move SectorCounter to next sector
|
||||
// every 1/300th of a second (or so).
|
||||
// (1600 revs/min = 25 revs / sec = 300 sectors / sec)
|
||||
timeout.wait_ms(3);
|
||||
if (file_is_open()) {
|
||||
_sectorCount = (_sectorCount + 1) % 12;
|
||||
_sok = true;
|
||||
controller->on_drive_status_changed(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set RWSRDY only after posting status change / interrupt...
|
||||
_rwsrdy = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move SectorCounter to next sector
|
||||
// every 1/300th of a second (or so).
|
||||
// (1600 revs/min = 25 revs / sec = 300 sectors / sec)
|
||||
timeout.wait_ms(3);
|
||||
if (file_is_open())
|
||||
{
|
||||
_sectorCount = (_sectorCount + 1) % 12;
|
||||
_sok = true;
|
||||
controller->on_drive_status_changed(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t rk05_c::get_disk_byte_offset(uint32_t cylinder, uint32_t surface, uint32_t sector) {
|
||||
return _geometry.Sector_Size_Bytes
|
||||
* ((cylinder * _geometry.Heads * _geometry.Sectors) + (surface * _geometry.Sectors)
|
||||
+ sector);
|
||||
uint64_t rk05_c::get_disk_byte_offset(
|
||||
uint32_t cylinder,
|
||||
uint32_t surface,
|
||||
uint32_t sector)
|
||||
{
|
||||
return _geometry.Sector_Size_Bytes *
|
||||
((cylinder * _geometry.Heads * _geometry.Sectors) +
|
||||
(surface * _geometry.Sectors) + sector);
|
||||
}
|
||||
|
||||
@@ -33,74 +33,71 @@ struct Geometry
|
||||
class rk05_c: public storagedrive_c
|
||||
{
|
||||
private:
|
||||
// Drive geometry details
|
||||
Geometry _geometry;
|
||||
// Drive geometry details
|
||||
Geometry _geometry;
|
||||
|
||||
// Current position of the heads
|
||||
volatile uint32_t _current_cylinder;
|
||||
volatile int32_t _seek_count;
|
||||
// Current position of the heads
|
||||
volatile uint32_t _current_cylinder;
|
||||
volatile int32_t _seek_count;
|
||||
|
||||
// Current sector under the heads (used to satisfy RKDS register,
|
||||
// incremented by worker thread, unrelated to sector reads/writes)
|
||||
volatile uint32_t _sectorCount;
|
||||
// Current sector under the heads (used to satisfy RKDS register,
|
||||
// incremented by worker thread, unrelated to sector reads/writes)
|
||||
volatile uint32_t _sectorCount;
|
||||
|
||||
// Status bits
|
||||
volatile bool _wps; // Write Protect status
|
||||
volatile bool _rwsrdy; // Indicates that the drive is ready to accept a new function.
|
||||
volatile bool _dry; // Indicates that the drive is powered, loaded, running, rotating, and not unsafe.
|
||||
volatile bool _sok; // Indicates that the _sectorCount value is not in a state of flux.
|
||||
volatile bool _sin; // Indicates that the seek could not be completed.
|
||||
volatile bool _dru; // Indicates that an unusual condition has occurred in the disk drive and is unsafe.
|
||||
volatile bool _rk05; // Always set, identifies the drive as an RK05
|
||||
volatile bool _dpl; // Set when an attempt to initiate a new function (or a function is in progress) when power is low.
|
||||
|
||||
volatile bool _scp; // Indicates the completion of a seek
|
||||
|
||||
|
||||
uint64_t get_disk_byte_offset(
|
||||
uint32_t cylinder,
|
||||
uint32_t surface,
|
||||
uint32_t sector);
|
||||
// Status bits
|
||||
volatile bool _wps; // Write Protect status
|
||||
volatile bool _rwsrdy; // Indicates that the drive is ready to accept a new function.
|
||||
volatile bool _dry; // Indicates that the drive is powered, loaded, running, rotating, and not unsafe.
|
||||
volatile bool _sok; // Indicates that the _sectorCount value is not in a state of flux.
|
||||
volatile bool _sin; // Indicates that the seek could not be completed.
|
||||
volatile bool _dru; // Indicates that an unusual condition has occurred in the disk drive and is unsafe.
|
||||
volatile bool _rk05; // Always set, identifies the drive as an RK05
|
||||
volatile bool _dpl; // Set when an attempt to initiate a new function (or a function is in progress) when power is low.
|
||||
volatile bool _scp; // Indicates the completion of a seek
|
||||
|
||||
uint64_t get_disk_byte_offset(
|
||||
uint32_t cylinder,
|
||||
uint32_t surface,
|
||||
uint32_t sector);
|
||||
|
||||
public:
|
||||
Geometry get_geometry(void);
|
||||
uint32_t get_cylinder(void);
|
||||
Geometry get_geometry(void);
|
||||
uint32_t get_cylinder(void);
|
||||
|
||||
// Status bits
|
||||
uint32_t get_sector_counter(void);
|
||||
bool get_write_protect(void);
|
||||
bool get_rws_ready(void);
|
||||
bool get_drive_ready(void);
|
||||
bool get_sector_counter_ok(void);
|
||||
bool get_seek_incomplete(void);
|
||||
bool get_drive_unsafe(void);
|
||||
bool get_rk05_disk_online(void);
|
||||
bool get_drive_power_low(void);
|
||||
// Status bits
|
||||
uint32_t get_sector_counter(void);
|
||||
bool get_write_protect(void);
|
||||
bool get_rws_ready(void);
|
||||
bool get_drive_ready(void);
|
||||
bool get_sector_counter_ok(void);
|
||||
bool get_seek_incomplete(void);
|
||||
bool get_drive_unsafe(void);
|
||||
bool get_rk05_disk_online(void);
|
||||
bool get_drive_power_low(void);
|
||||
|
||||
// Not a status bit per-se, indicates whether a seek has completed since the last status change.
|
||||
bool get_search_complete(void);
|
||||
// Not a status bit per-se, indicates whether a seek has completed since the last status change.
|
||||
bool get_search_complete(void);
|
||||
|
||||
// Commands
|
||||
void read_sector(uint32_t cylinder, uint32_t surface, uint32_t sector, uint16_t* out_buffer);
|
||||
void write_sector(uint32_t cylinder, uint32_t surface, uint32_t sector, uint16_t* in_buffer);
|
||||
void seek(uint32_t cylinder);
|
||||
void set_write_protect(bool protect);
|
||||
void drive_reset(void);
|
||||
// Commands
|
||||
void read_sector(uint32_t cylinder, uint32_t surface, uint32_t sector, uint16_t* out_buffer);
|
||||
void write_sector(uint32_t cylinder, uint32_t surface, uint32_t sector, uint16_t* in_buffer);
|
||||
void seek(uint32_t cylinder);
|
||||
void set_write_protect(bool protect);
|
||||
void drive_reset(void);
|
||||
|
||||
public:
|
||||
DriveType _drivetype;
|
||||
DriveType _drivetype;
|
||||
|
||||
rk05_c(storagecontroller_c *controller);
|
||||
rk05_c(storagecontroller_c *controller);
|
||||
|
||||
bool on_param_changed(parameter_c* param) override;
|
||||
|
||||
void on_power_changed(void) override;
|
||||
void on_power_changed(void) override;
|
||||
|
||||
void on_init_changed(void) override;
|
||||
void on_init_changed(void) override;
|
||||
|
||||
// background worker function
|
||||
void worker(unsigned instance) override;
|
||||
// background worker function
|
||||
void worker(unsigned instance) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,8 +26,8 @@ rk11_c::rk11_c() :
|
||||
type_name.value = "RK11";
|
||||
log_label = "rk";
|
||||
|
||||
// base addr, intr-vector, intr level
|
||||
set_default_bus_params(0777400, 10, 0220, 5) ;
|
||||
// base addr, intr-vector, intr level
|
||||
set_default_bus_params(0777400, 10, 0220, 5) ;
|
||||
|
||||
// The RK11 controller has seven registers,
|
||||
// We allocate 8 because one address in the address space is unused.
|
||||
@@ -118,17 +118,24 @@ rk11_c::~rk11_c()
|
||||
|
||||
// return false, if illegal parameter value.
|
||||
// verify "new_value", must output error messages
|
||||
bool rk11_c::on_param_changed(parameter_c *param) {
|
||||
// no own parameter or "enable" logic
|
||||
if (param == &priority_slot) {
|
||||
dma_request.set_priority_slot(priority_slot.new_value);
|
||||
intr_request.set_priority_slot(priority_slot.new_value);
|
||||
} else if (param == &intr_level) {
|
||||
intr_request.set_level(intr_level.new_value);
|
||||
} else if (param == &intr_vector) {
|
||||
intr_request.set_vector(intr_vector.new_value);
|
||||
}
|
||||
return storagecontroller_c::on_param_changed(param) ; // more actions (for enable)
|
||||
bool rk11_c::on_param_changed(parameter_c *param)
|
||||
{
|
||||
// no own parameter or "enable" logic
|
||||
if (param == &priority_slot)
|
||||
{
|
||||
dma_request.set_priority_slot(priority_slot.new_value);
|
||||
intr_request.set_priority_slot(priority_slot.new_value);
|
||||
}
|
||||
else if (param == &intr_level)
|
||||
{
|
||||
intr_request.set_level(intr_level.new_value);
|
||||
}
|
||||
else if (param == &intr_vector)
|
||||
{
|
||||
intr_request.set_vector(intr_vector.new_value);
|
||||
}
|
||||
|
||||
return storagecontroller_c::on_param_changed(param) ; // more actions (for enable)
|
||||
}
|
||||
|
||||
|
||||
@@ -148,12 +155,12 @@ void rk11_c::dma_transfer(DMARequest &request)
|
||||
{
|
||||
// Write FROM buffer TO unibus memory, IBA on:
|
||||
// We only need to write the last word in the buffer to memory.
|
||||
unibusadapter->DMA(dma_request, true,
|
||||
unibusadapter->DMA(dma_request, true,
|
||||
UNIBUS_CONTROL_DATO,
|
||||
request.address,
|
||||
request.buffer + request.count - 1,
|
||||
1);
|
||||
request.timeout = !dma_request.success ;
|
||||
request.timeout = !dma_request.success;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -209,7 +216,7 @@ void rk11_c::dma_transfer(DMARequest &request)
|
||||
// Handle device operations.
|
||||
void rk11_c::worker(unsigned instance)
|
||||
{
|
||||
UNUSED(instance) ; // only one
|
||||
UNUSED(instance) ; // only one
|
||||
|
||||
worker_init_realtime_priority(rt_device);
|
||||
|
||||
@@ -223,41 +230,41 @@ void rk11_c::worker(unsigned instance)
|
||||
switch (_worker_state)
|
||||
{
|
||||
case Worker_Idle:
|
||||
{
|
||||
pthread_mutex_lock(&on_after_register_access_mutex);
|
||||
|
||||
while (!_new_command_ready)
|
||||
{
|
||||
pthread_mutex_lock(&on_after_register_access_mutex);
|
||||
|
||||
while (!_new_command_ready)
|
||||
{
|
||||
pthread_cond_wait(
|
||||
&on_after_register_access_cond,
|
||||
&on_after_register_access_mutex);
|
||||
}
|
||||
|
||||
//
|
||||
// Make a local copy of the new command to execute.
|
||||
//
|
||||
command = _new_command;
|
||||
_new_command_ready = false;
|
||||
pthread_mutex_unlock(&on_after_register_access_mutex);
|
||||
|
||||
//
|
||||
// Clear GO now that we've accepted the command.
|
||||
//
|
||||
_go = false;
|
||||
update_RKCS();
|
||||
|
||||
//
|
||||
// We will interrupt after completion of a command except
|
||||
// in certain error cases.
|
||||
//
|
||||
do_interrupt = true;
|
||||
|
||||
//
|
||||
// Move to the execution state to start executing the command.
|
||||
//
|
||||
_worker_state = Worker_Execute;
|
||||
pthread_cond_wait(
|
||||
&on_after_register_access_cond,
|
||||
&on_after_register_access_mutex);
|
||||
}
|
||||
break;
|
||||
|
||||
//
|
||||
// Make a local copy of the new command to execute.
|
||||
//
|
||||
command = _new_command;
|
||||
_new_command_ready = false;
|
||||
pthread_mutex_unlock(&on_after_register_access_mutex);
|
||||
|
||||
//
|
||||
// Clear GO now that we've accepted the command.
|
||||
//
|
||||
_go = false;
|
||||
update_RKCS();
|
||||
|
||||
//
|
||||
// We will interrupt after completion of a command except
|
||||
// in certain error cases.
|
||||
//
|
||||
do_interrupt = true;
|
||||
|
||||
//
|
||||
// Move to the execution state to start executing the command.
|
||||
//
|
||||
_worker_state = Worker_Execute;
|
||||
}
|
||||
break;
|
||||
|
||||
case Worker_Execute:
|
||||
switch(command.function)
|
||||
@@ -265,197 +272,190 @@ void rk11_c::worker(unsigned instance)
|
||||
case Write:
|
||||
case Read:
|
||||
case Write_Check:
|
||||
{
|
||||
bool write = command.function == Write;
|
||||
bool read_format = command.function == Read && command.format;
|
||||
bool read = command.function == Read && !command.format;
|
||||
bool write_check = command.function == Write_Check;
|
||||
{
|
||||
bool write = command.function == Write;
|
||||
bool read_format = command.function == Read && command.format;
|
||||
bool read = command.function == Read && !command.format;
|
||||
bool write_check = command.function == Write_Check;
|
||||
|
||||
// We loop over the requested address range
|
||||
// and submit DMA requests one at a time, waiting for
|
||||
// their completion.
|
||||
// We loop over the requested address range
|
||||
// and submit DMA requests one at a time, waiting for
|
||||
// their completion.
|
||||
|
||||
uint16_t sectorBuffer[256];
|
||||
uint16_t checkBuffer[256];
|
||||
uint16_t sectorBuffer[256];
|
||||
uint16_t checkBuffer[256];
|
||||
|
||||
uint32_t current_address = command.address;
|
||||
int16_t current_count = -(int16_t)(get_register_dato_value(RKWC_reg));
|
||||
bool abort = false;
|
||||
while(current_count > 0 && !abort)
|
||||
uint32_t current_address = command.address;
|
||||
int16_t current_count = -(int16_t)(get_register_dato_value(RKWC_reg));
|
||||
bool abort = false;
|
||||
while(current_count > 0 && !abort)
|
||||
{
|
||||
// If a new command has been written in the CS register, abandon
|
||||
// this one ASAP.
|
||||
if (_new_command_ready)
|
||||
{
|
||||
// If a new command has been written in the CS register, abandon
|
||||
// this one ASAP.
|
||||
if (_new_command_ready)
|
||||
{
|
||||
DEBUG("Command canceled.");
|
||||
abort = true;
|
||||
continue;
|
||||
}
|
||||
DEBUG("Command canceled.");
|
||||
abort = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate that the current disk address is valid.
|
||||
if (!validate_seek())
|
||||
// Validate that the current disk address is valid.
|
||||
if (!validate_seek())
|
||||
{
|
||||
// The above check set error bits accordingly.
|
||||
// Just abort the transfer now.
|
||||
// Set OVR if we've gone off the end of the disk.
|
||||
if (_rkda_cyl > 202)
|
||||
{
|
||||
// The above check set error bits accordingly.
|
||||
// Just abort the transfer now.
|
||||
// Set OVR if we've gone off the end of the disk.
|
||||
if (_rkda_cyl > 202)
|
||||
{
|
||||
_ovr = true;
|
||||
// update_RKER();
|
||||
}
|
||||
abort = true;
|
||||
continue;
|
||||
}
|
||||
_ovr = true;
|
||||
}
|
||||
abort = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// Clear the buffer. This is only necessary because short writes
|
||||
// and reads expect the rest of the sector to be filled with zeroes.
|
||||
//
|
||||
memset(sectorBuffer, 0, sizeof(sectorBuffer));
|
||||
//
|
||||
// Clear the buffer. This is only necessary because short writes
|
||||
// and reads expect the rest of the sector to be filled with zeroes.
|
||||
//
|
||||
memset(sectorBuffer, 0, sizeof(sectorBuffer));
|
||||
|
||||
if (read)
|
||||
if (read)
|
||||
{
|
||||
// Doing a normal read from disk: Grab the sector data and then
|
||||
// DMA it into memory.
|
||||
selected_drive()->read_sector(
|
||||
_rkda_cyl,
|
||||
_rkda_surface,
|
||||
_rkda_sector,
|
||||
sectorBuffer);
|
||||
}
|
||||
else if (read_format)
|
||||
{
|
||||
// Doing a Read Format: Read only the header word from the disk
|
||||
// and DMA that single word into memory.
|
||||
// We don't actually read the header word from disk since we don't
|
||||
// store header data in the disk image; we just fake it up --
|
||||
// since we always seek correctly this is all that is required.
|
||||
//
|
||||
// The header is just the cylinder address, as in RKDA 05-12 (p. 3-9)
|
||||
sectorBuffer[0] = (_rkda_cyl << 5);
|
||||
}
|
||||
else if (write_check)
|
||||
{
|
||||
// Doing a Write Check: Grab the sector data from the disk into
|
||||
// the check buffer.
|
||||
selected_drive()->read_sector(
|
||||
_rkda_cyl,
|
||||
_rkda_surface,
|
||||
_rkda_sector,
|
||||
checkBuffer);
|
||||
}
|
||||
|
||||
//
|
||||
// Normal Read, or Write/Write Format: DMA the data to/from memory,
|
||||
// etc.
|
||||
//
|
||||
// build the DMA transfer request:
|
||||
DMARequest request = { 0 };
|
||||
request.address = current_address;
|
||||
request.count = (!read_format) ?
|
||||
min(static_cast<int16_t>(256) , current_count) : 1;
|
||||
|
||||
request.write = !(write || write_check); // Inverted sense from disk action
|
||||
request.timeout = false;
|
||||
request.buffer = sectorBuffer;
|
||||
request.iba = command.iba;
|
||||
|
||||
// And actually do the transfer.
|
||||
dma_transfer(request);
|
||||
|
||||
// Check completion status -- if there was an error,
|
||||
// we'll abort and set the appropriate flags.
|
||||
if (request.timeout)
|
||||
{
|
||||
_nxm = true;
|
||||
_he = true;
|
||||
_err = true;
|
||||
abort = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
// Doing a normal read from disk: Grab the sector data and then
|
||||
// DMA it into memory.
|
||||
selected_drive()->read_sector(
|
||||
// Doing a write to disk: Write the buffer DMA'd from
|
||||
// memory to the disk.
|
||||
selected_drive()->write_sector(
|
||||
_rkda_cyl,
|
||||
_rkda_surface,
|
||||
_rkda_sector,
|
||||
sectorBuffer);
|
||||
}
|
||||
else if (read_format)
|
||||
{
|
||||
// Doing a Read Format: Read only the header word from the disk
|
||||
// and DMA that single word into memory.
|
||||
// We don't actually read the header word from disk since we don't
|
||||
// store header data in the disk image; we just fake it up --
|
||||
// since we always seek correctly this is all that is required.
|
||||
//
|
||||
// The header is just the cylinder address, as in RKDA 05-12 (p. 3-9)
|
||||
sectorBuffer[0] = (_rkda_cyl << 5);
|
||||
}
|
||||
else if (write_check)
|
||||
{
|
||||
// Doing a Write Check: Grab the sector data from the disk into
|
||||
// the check buffer.
|
||||
selected_drive()->read_sector(
|
||||
_rkda_cyl,
|
||||
_rkda_surface,
|
||||
_rkda_sector,
|
||||
checkBuffer);
|
||||
}
|
||||
|
||||
//
|
||||
// Normal Read, or Write/Write Format: DMA the data to/from memory,
|
||||
// etc.
|
||||
//
|
||||
// build the DMA transfer request:
|
||||
DMARequest request = { 0 };
|
||||
request.address = current_address;
|
||||
request.count = (!read_format) ?
|
||||
min(static_cast<int16_t>(256) , current_count) :
|
||||
1;
|
||||
request.write = !(write || write_check); // Inverted sense from disk action
|
||||
request.timeout = false;
|
||||
request.buffer = sectorBuffer;
|
||||
request.iba = command.iba;
|
||||
|
||||
// And actually do the transfer.
|
||||
dma_transfer(request);
|
||||
|
||||
// Check completion status -- if there was an error,
|
||||
// we'll abort and set the appropriate flags.
|
||||
if (request.timeout)
|
||||
{
|
||||
_nxm = true;
|
||||
_he = true;
|
||||
_err = true;
|
||||
// update_RKCS();
|
||||
// update_RKER();
|
||||
abort = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (write)
|
||||
// Compare check buffer with sector buffer, if there are
|
||||
// any discrepancies, set WCE and interrupt as necessary.
|
||||
for (int i = 0; i < request.count; i++)
|
||||
{
|
||||
// Doing a write to disk: Write the buffer DMA'd from
|
||||
// memory to the disk.
|
||||
selected_drive()->write_sector(
|
||||
_rkda_cyl,
|
||||
_rkda_surface,
|
||||
_rkda_sector,
|
||||
sectorBuffer);
|
||||
}
|
||||
else if (write_check)
|
||||
{
|
||||
// Compare check buffer with sector buffer, if there are
|
||||
// any discrepancies, set WCE and interrupt as necessary.
|
||||
for (int i = 0; i < request.count; i++)
|
||||
if (sectorBuffer[i] != checkBuffer[i])
|
||||
{
|
||||
if (sectorBuffer[i] != checkBuffer[i])
|
||||
_wce = true;
|
||||
_err = true;
|
||||
|
||||
if (_sse)
|
||||
{
|
||||
_wce = true;
|
||||
_err = true;
|
||||
// update_RKER();
|
||||
// update_RKCS();
|
||||
|
||||
if (_sse)
|
||||
{
|
||||
// Finish this transfer and abort.
|
||||
abort = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_wce && _sse)
|
||||
{
|
||||
// Control action stops on error after this transfer
|
||||
// if SSE (Stop on Soft Error) is set.
|
||||
abort = true;
|
||||
// Finish this transfer and abort.
|
||||
abort = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Read
|
||||
|
||||
if (_wce && _sse)
|
||||
{
|
||||
// After read complete, set RKDB to the last word
|
||||
// read (this satisfies ZRKK):
|
||||
set_register_dati_value(
|
||||
RKDB_reg,
|
||||
sectorBuffer[request.count - 1],
|
||||
"RK11 READ");
|
||||
}
|
||||
// Control action stops on error after this transfer
|
||||
// if SSE (Stop on Soft Error) is set.
|
||||
abort = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer completed. Move to next and update registers.
|
||||
current_count -= request.count;
|
||||
|
||||
set_register_dati_value(
|
||||
RKWC_reg,
|
||||
(uint16_t)(-current_count),
|
||||
__func__);
|
||||
|
||||
if (!command.iba)
|
||||
else // Read
|
||||
{
|
||||
current_address += (request.count * 2); // Byte address
|
||||
// After read complete, set RKDB to the last word
|
||||
// read (this satisfies ZRKK):
|
||||
set_register_dati_value(
|
||||
RKBA_reg,
|
||||
(uint16_t)current_address,
|
||||
__func__);
|
||||
_mex = ((current_address) >> 16) & 0x3;
|
||||
// update_RKCS();
|
||||
RKDB_reg,
|
||||
sectorBuffer[request.count - 1],
|
||||
"RK11 READ");
|
||||
}
|
||||
|
||||
// Move to next disk address
|
||||
increment_RKDA();
|
||||
|
||||
// And go around, do it again.
|
||||
}
|
||||
|
||||
// timeout.wait_us(100);
|
||||
DEBUG("R/W: Complete.");
|
||||
_worker_state = Worker_Finish;
|
||||
// Transfer completed. Move to next and update registers.
|
||||
current_count -= request.count;
|
||||
|
||||
set_register_dati_value(
|
||||
RKWC_reg,
|
||||
(uint16_t)(-current_count),
|
||||
__func__);
|
||||
|
||||
if (!command.iba)
|
||||
{
|
||||
current_address += (request.count * 2); // Byte address
|
||||
set_register_dati_value(
|
||||
RKBA_reg,
|
||||
(uint16_t)current_address,
|
||||
__func__);
|
||||
_mex = ((current_address) >> 16) & 0x3;
|
||||
}
|
||||
|
||||
// Move to next disk address
|
||||
increment_RKDA();
|
||||
|
||||
// And go around, do it again.
|
||||
}
|
||||
break;
|
||||
|
||||
DEBUG("R/W: Complete.");
|
||||
_worker_state = Worker_Finish;
|
||||
}
|
||||
break;
|
||||
|
||||
case Read_Check:
|
||||
//
|
||||
@@ -488,7 +488,6 @@ void rk11_c::worker(unsigned instance)
|
||||
(uint16_t)(-current_count),
|
||||
__func__);
|
||||
_mex = ((current_address) >> 16) & 0x3;
|
||||
// update_RKCS();
|
||||
}
|
||||
|
||||
// Move to next disk address.
|
||||
@@ -542,7 +541,7 @@ void rk11_c::worker(unsigned instance)
|
||||
_worker_state = Worker_Finish;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case Worker_Finish:
|
||||
// Set RDY, interrupt (if enabled) and go back to the Idle state.
|
||||
@@ -563,7 +562,7 @@ void rk11_c::worker(unsigned instance)
|
||||
}
|
||||
pthread_mutex_unlock(&on_after_register_access_mutex);
|
||||
_worker_state = Worker_Idle;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -598,11 +597,9 @@ bool rk11_c::validate_seek(void)
|
||||
// Set the Control/Status register HE and ERR registers.
|
||||
_he = true;
|
||||
_err = true;
|
||||
// update_RKCS();
|
||||
|
||||
// Do not interrupt here, caller will do so based on
|
||||
// our return value.
|
||||
// invoke_interrupt();
|
||||
}
|
||||
|
||||
return !error;
|
||||
@@ -679,100 +676,84 @@ void rk11_c::on_after_register_access(
|
||||
|
||||
// GO gets cleared
|
||||
_go = false;
|
||||
// update_RKER();
|
||||
// update_RKCS();
|
||||
// error = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(_function)
|
||||
{
|
||||
case Control_Reset:
|
||||
//
|
||||
// "The Control Reset function initializes all internal registers and flip-flops
|
||||
// and clears all the bits of the seven programmable registers except RKCS 07 (RDY)
|
||||
// which it sets, and RKDS 01 through 11, which are not affected.
|
||||
//
|
||||
reset_controller();
|
||||
break;
|
||||
|
||||
case Write_Lock:
|
||||
//
|
||||
// "Write-protects a selected disk drive until the condition is overridden by the
|
||||
// operation of the corresponding WT PROT switch on the disk drive..."
|
||||
//
|
||||
selected_drive()->set_write_protect(true);
|
||||
_scp = false;
|
||||
// update_RKCS();
|
||||
break;
|
||||
|
||||
default:
|
||||
// All other commands take significant time and happen on the worker thread.
|
||||
// First check:
|
||||
// If this is not a Drive Reset command and the drive is not ready or has taken
|
||||
// a fault, we will abort, set the appropriate error bits and interrupt.
|
||||
//
|
||||
if (_function != Drive_Reset && !check_drive_ready())
|
||||
{
|
||||
_dre = true;
|
||||
// update_RKER();
|
||||
_he = true;
|
||||
_err = true;
|
||||
_scp = false;
|
||||
_go = false;
|
||||
// update_RKCS();
|
||||
// INFO("setting DRE, fn %o dr rdy %d rws rdy %d", _function,
|
||||
// selected_drive()->get_drive_ready(),
|
||||
// selected_drive()->get_rws_ready());
|
||||
|
||||
// invoke_interrupt();
|
||||
error = true;
|
||||
switch(_function)
|
||||
{
|
||||
case Control_Reset:
|
||||
//
|
||||
// "The Control Reset function initializes all internal registers and flip-flops
|
||||
// and clears all the bits of the seven programmable registers except RKCS 07 (RDY)
|
||||
// which it sets, and RKDS 01 through 11, which are not affected.
|
||||
//
|
||||
reset_controller();
|
||||
break;
|
||||
}
|
||||
|
||||
// If this is an attempt to address a nonexistent (not present or not
|
||||
// loaded) drive, this triggers an NXD error.
|
||||
if (!check_drive_present())
|
||||
{
|
||||
_nxd = true;
|
||||
_he = true;
|
||||
_err = true;
|
||||
|
||||
case Write_Lock:
|
||||
//
|
||||
// "Write-protects a selected disk drive until the condition is overridden by the
|
||||
// operation of the corresponding WT PROT switch on the disk drive..."
|
||||
//
|
||||
selected_drive()->set_write_protect(true);
|
||||
_scp = false;
|
||||
_go = false;
|
||||
// update_RKCS();
|
||||
// update_RKER();
|
||||
|
||||
// invoke_interrupt();
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// All other commands take significant time and happen on the worker thread.
|
||||
// First check:
|
||||
// If this is not a Drive Reset command and the drive is not ready or has taken
|
||||
// a fault, we will abort, set the appropriate error bits and interrupt.
|
||||
//
|
||||
if (_function != Drive_Reset && !check_drive_ready())
|
||||
{
|
||||
_dre = true;
|
||||
_he = true;
|
||||
_err = true;
|
||||
_scp = false;
|
||||
_go = false;
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If this is an attempt to address a nonexistent (not present or not
|
||||
// loaded) drive, this triggers an NXD error.
|
||||
if (!check_drive_present())
|
||||
{
|
||||
_nxd = true;
|
||||
_he = true;
|
||||
_err = true;
|
||||
_scp = false;
|
||||
_go = false;
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear RDY, SCP bits:
|
||||
pthread_mutex_lock(&on_after_register_access_mutex);
|
||||
_rdy = false;
|
||||
_scp = false;
|
||||
// update_RKCS();
|
||||
// Clear RDY, SCP bits:
|
||||
pthread_mutex_lock(&on_after_register_access_mutex);
|
||||
_rdy = false;
|
||||
_scp = false;
|
||||
|
||||
//
|
||||
// Stow command data for this operation so that if RKCS gets changed
|
||||
// mid-op weird things won't happen.
|
||||
//
|
||||
_new_command.address = get_register_dato_value(RKBA_reg) | (_mex << 16);
|
||||
_new_command.drive = selected_drive();
|
||||
_new_command.function = _function;
|
||||
_new_command.interrupt = _ide;
|
||||
_new_command.stop_on_soft_error = _sse;
|
||||
_new_command.format = _fmt;
|
||||
_new_command.iba = _iba;
|
||||
_new_command_ready = true;
|
||||
//
|
||||
// Stow command data for this operation so that if RKCS gets changed
|
||||
// mid-op weird things won't happen.
|
||||
//
|
||||
_new_command.address = get_register_dato_value(RKBA_reg) | (_mex << 16);
|
||||
_new_command.drive = selected_drive();
|
||||
_new_command.function = _function;
|
||||
_new_command.interrupt = _ide;
|
||||
_new_command.stop_on_soft_error = _sse;
|
||||
_new_command.format = _fmt;
|
||||
_new_command.iba = _iba;
|
||||
_new_command_ready = true;
|
||||
|
||||
//
|
||||
// Wake the worker.
|
||||
//
|
||||
pthread_cond_signal(&on_after_register_access_cond);
|
||||
pthread_mutex_unlock(&on_after_register_access_mutex);
|
||||
break;
|
||||
}
|
||||
//
|
||||
// Wake the worker.
|
||||
//
|
||||
pthread_cond_signal(&on_after_register_access_cond);
|
||||
pthread_mutex_unlock(&on_after_register_access_mutex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
update_RKER();
|
||||
@@ -860,8 +841,7 @@ void rk11_c::on_drive_status_changed(storagedrive_c *drive)
|
||||
// interrupt now. Note that the call to get_search_complete() has
|
||||
// the side effect (eww) of resetting the drive's SCP bit, so we do it
|
||||
// first (so it always gets cleared even if we're not interrupting.)
|
||||
if (dynamic_cast<rk05_c*>(drive)->get_search_complete() &&
|
||||
_ide)
|
||||
if (dynamic_cast<rk05_c*>(drive)->get_search_complete() && _ide)
|
||||
{
|
||||
// Set SCP to indicate that this interrupt was due to a previous
|
||||
// Seek or Drive Reset function.
|
||||
|
||||
@@ -101,7 +101,7 @@ rp_drive_c::SeekTo(
|
||||
|
||||
if (IsConnected() && IsPackLoaded() && !_iae)
|
||||
{
|
||||
timeout.wait_ms(50);
|
||||
timeout.wait_ms(20);
|
||||
|
||||
_currentCylinder = destinationCylinder;
|
||||
return true;
|
||||
@@ -150,6 +150,9 @@ rp_drive_c::Write(
|
||||
_currentCylinder = cylinder;
|
||||
uint32_t offset = GetSectorForCHS(cylinder, track, sector);
|
||||
file_write(reinterpret_cast<uint8_t*>(buffer), offset * GetSectorSize(), countInWords * 2);
|
||||
//timeout_c timeout;
|
||||
//timeout.wait_ms(20);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -188,6 +191,9 @@ rp_drive_c::Read(
|
||||
uint32_t offset = GetSectorForCHS(cylinder, track, sector);
|
||||
DEBUG("Read from sector offset o%o", offset);
|
||||
file_read(reinterpret_cast<uint8_t*>(*buffer), offset * GetSectorSize(), countInWords * 2);
|
||||
//timeout_c timeout;
|
||||
//timeout.wait_ms(20);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,14 +96,20 @@ uda_c::~uda_c()
|
||||
|
||||
bool uda_c::on_param_changed(parameter_c *param) {
|
||||
// no own parameter or "enable" logic
|
||||
if (param == &priority_slot) {
|
||||
dma_request.set_priority_slot(priority_slot.new_value);
|
||||
intr_request.set_priority_slot(priority_slot.new_value);
|
||||
} else if (param == &intr_level) {
|
||||
intr_request.set_level(intr_level.new_value);
|
||||
} else if (param == &intr_vector) {
|
||||
intr_request.set_vector(intr_vector.new_value);
|
||||
}
|
||||
if (param == &priority_slot)
|
||||
{
|
||||
dma_request.set_priority_slot(priority_slot.new_value);
|
||||
intr_request.set_priority_slot(priority_slot.new_value);
|
||||
}
|
||||
else if (param == &intr_level)
|
||||
{
|
||||
intr_request.set_level(intr_level.new_value);
|
||||
}
|
||||
else if (param == &intr_vector)
|
||||
{
|
||||
intr_request.set_vector(intr_vector.new_value);
|
||||
}
|
||||
|
||||
return storagecontroller_c::on_param_changed(param) ; // more actions (for enable)
|
||||
}
|
||||
|
||||
@@ -968,8 +974,6 @@ uda_c::DMAWrite(
|
||||
uint8_t* buffer)
|
||||
{
|
||||
assert ((lengthInBytes % 2) == 0);
|
||||
// if (address >= 0x40000)
|
||||
// logger->dump(logger->default_filepath) ;
|
||||
assert (address < 0x40000);
|
||||
|
||||
unibusadapter->DMA(dma_request, true,
|
||||
@@ -977,7 +981,7 @@ uda_c::DMAWrite(
|
||||
address,
|
||||
reinterpret_cast<uint16_t*>(buffer),
|
||||
lengthInBytes >> 1);
|
||||
return dma_request.success ;
|
||||
return dma_request.success ;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user