1
0
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:
Josh Dersch
2020-03-26 09:02:21 +01:00
parent e037b0d36d
commit 828e4f5371
13 changed files with 1083 additions and 725 deletions

View File

@@ -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);
}
}

View File

@@ -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();
}

View 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();
}

View File

@@ -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 },

View 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;
};

View File

@@ -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;

View File

@@ -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();

View File

@@ -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:

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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;
}
}

View File

@@ -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 ;
}
//