1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-01-13 15:27:09 +00:00

Initial commit of work-in-progress RH11 (MASSBUS) emulation.

This commit is contained in:
Josh Dersch 2020-01-10 03:27:08 +01:00
parent a81785391e
commit a2c8d6f2bc
8 changed files with 821 additions and 2 deletions

View File

@ -0,0 +1,51 @@
/*
massbus_device.hpp: Generic interface for MASSBUS devices.
Copyright Vulcan Inc. 2020 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
*/
#pragma once
//
// Provides a virtual interface connecting a MASSBUS device implementation
// to the RH11 Unibus device implementation. Provides metadata about
// MASSBUS registers, defines methods for reading and writing them, and
// provides methods for transferring blocks of data.
//
class massbus_device_c
{
public:
//
// MASSBUS Register Metadata:
//
// Indicates whether this device implements the specified register.
virtual bool ImplementsRegister(uint32_t register) = 0;
// Provides the mnemonic name for this register
virtual std::string RegisterName(uint32_t register) = 0;
// Unibus DATI/DATO activity bits
virtual bool RegisterActiveOnDATI(uint32_t register) = 0;
virtual bool RegisterActiveOnDATO(uint32_t register) = 0;
// Reset value
virtual uint16_t RegisterResetValue(uint32_t register) = 0;
// Writable bits
virtual uint16_t RegisterWritableBits(uint32_t register) = 0;
//
// MASSBUS Register reads and writes
//
virtual void WriteRegister(uint32_t unit, uint32_t register, uint16_t value) = 0;
virtual uint16_t ReadRegister(uint32_t unit, uint32_t register) = 0;
//
// Block transfers
// TODO: define these
//
};

View File

@ -0,0 +1,168 @@
/*
massbus_rp_c.cpp: Implements MASSBUS device logic for moving-head RP04/05/06 drives.
Copyright Vulcan Inc. 2020 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
*/
#include <stdint.h>
#include <string.h>
using namespace std;
#include "storagedrive.hpp"
#include "rh11.hpp"
#include "massbus_device.hpp"
#include "massbus_rp.hpp"
massbus_rp_c::massbus_rp_c(
rh11_c* controller) :
device_c(),
_controller(controller)
{
name.value="MASSBUS_RP";
type_name.value = "massbus_rp_c";
log_label = "MASSBUS_RP";
}
massbus_rp_c::~massbus_rp_c()
{
}
bool
massbus_rp_c::ImplementsRegister(
uint32_t register)
{
return false;
}
std::string
massbus_rp_c::RegisterName(
uint32_t register)
{
std::string name("foo");
return name;
}
bool
massbus_rp_c::RegisterActiveOnDATI(
uint32_t reg)
{
return _registerMetadata[reg].ActiveOnDATI;
}
bool
massbus_rp_c::RegisterActiveOnDATO(
uint32_t reg)
{
return _registerMetadata[reg].ActiveOnDATO;
}
uint16_t
massbus_rp_c::RegisterResetValue(
uint32_t reg)
{
return _registerMetadata[reg].ResetValue;
}
uint16_t
massbus_rp_c::RegisterWritableBits(
uint32_t reg)
{
return _registerMetadata[reg].WritableBits;
}
void
massbus_rp_c::WriteRegister(
uint32_t unit,
uint32_t reg,
uint16_t value)
{
INFO("RP reg write: unit %d register 0%o value 0%o", unit, reg, value);
switch(reg)
{
case 0:
DoCommand(unit, value);
break;
default:
FATAL("Unimplemented RP register write.");
break;
}
}
void massbus_rp_c::DoCommand(
uint32_t unit,
uint16_t command)
{
// check for GO bit; if unset we have nothing to do here.
if ((command & RP_GO) == 0)
{
return;
}
uint32_t function = (command & RP_FUNC) >> 1;
INFO("RP function 0%o", function);
switch(function)
{
case FunctionCode::Nop:
// Nothing.
break;
case FunctionCode::ReadInPreset:
INFO("RP Read-In Preset");
//
// "This command sets the VV (volume valid) bit, clears the desired
// sector/track address register, and clears the FMT, HCI, and ECI
// bits in the offset register. It is used to bootstrap the device."
//
set_bit
break;
case FunctionCode::ReadData:
INFO("RP Read Data");
break;
default:
FATAL("Unimplemented RP function.");
break;
}
}
uint16_t
massbus_rp_c::ReadRegister(
uint32_t unit,
uint32_t reg)
{
INFO("RP reg read: unit %d register 0%o", unit, reg);
FATAL("Unimplemented RP register read.");
return 0;
}
// background worker function
void
massbus_rp_c::worker(
unsigned instance)
{
}
void
massbus_rp_c::on_power_changed(void)
{
}
void
massbus_rp_c::on_init_changed(void)
{
}

View File

@ -0,0 +1,132 @@
/*
massbus_rp_c.hpp: Implements MASSBUS device logic for moving-head RP04/05/06 drives.
Copyright Vulcan Inc. 2020 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
*/
#ifndef _MASSBUS_RP_HPP_
#define _MASSBUS_RP_HPP_
#include <stdint.h>
#include <string.h>
using namespace std;
#include "storagedrive.hpp"
#include "rh11.hpp"
#include "massbus_device.hpp"
// Registers
enum class Registers
{
Control = 00,
Status = 01,
Error1 = 02,
Maintenance = 03,
AttentionSummary = 04,
DesiredAddress = 05,
LookAhead = 07,
DriveType = 06,
SerialNo = 014,
Offset = 011,
DesiredCylinderAddress = 012,
CurrentCylinderAddress = 013,
Error2 = 010,
Error3 = 015,
ECCPosition = 016,
ECCPattern = 017,
};
// Control Function codes
#define RP_GO 01
#define RP_FUNC 076
enum class FunctionCode
{
Nop = 00,
Unload = 01,
Recalibrate = 03,
DriveClear = 04,
Release = 05,
Search = 014,
WriteCheckData = 024,
WriteCheckHeaderAndData = 025,
WriteData = 030,
WriteHeaderAndData = 031,
ReadData = 034,
ReadHeaderAndData = 035,
Seek = 02,
Offset = 06,
ReturnToCenterline = 07,
PackAcknowledge = 011,
ReadInPreset = 010,
};
//
// Register metadata
//
struct rp_register_data
{
bool ActiveOnDATI;
bool ActiveOnDATO;
uint16_t ResetValue;
uint16_t WritableBits;
};
//
// Encapsulates MASSBUS control logic for RP04/05/06 moving-head disks.
// Talks to one or more rp_drive units.
//
class massbus_rp_c: public device_c, public massbus_device_c
{
public:
massbus_rp_c(rh11_c* controller);
virtual ~massbus_rp_c();
public:
bool ImplementsRegister(uint32_t register) override;
std::string RegisterName(uint32_t register) override;
bool RegisterActiveOnDATI(uint32_t register) override;
bool RegisterActiveOnDATO(uint32_t register) override;
uint16_t RegisterResetValue(uint32_t register) override;
uint16_t RegisterWritableBits(uint32_t register) override;
void WriteRegister(uint32_t unit, uint32_t register, uint16_t value) override;
uint16_t ReadRegister(uint32_t unit, uint32_t register) override;
private:
void DoCommand(uint32_t unit, uint16_t command);
// background worker function
void worker(unsigned instance) override;
void on_power_changed(void) override;
void on_init_changed(void) override;
private:
rh11_c* _controller;
rp_register_data _registerMetadata[16] =
{
{ false, false, 0, 0 }, // 0, not used
{ false, false, 0, 0177700 }, // Status
{ false, true , 0, 0177777 }, // Error #1 - writable by diagnostics
{ false, true , 0, 0177777 }, // Maintenance
{ false, false, 0, 0 }, // Attention summary
{ false, false, 0, 0017437 }, // Desired Sector/Track
{ false, false, 0, 0 }, // Look Ahead
{ false, false, 0, 0 }, // Drive Type
{ false, false, 0, 0 }, // Serial Number
{ false, false, 0, 0177777 }, // Offset
{ false, false, 0, 0001777 }, // Desired Cylinder
{ false, false, 0, 0 }, // Current Cylinder
{ false, false, 0, 0 }, // Error #2
{ false, false, 0, 0 }, // Error #3
{ false, false, 0, 0 }, // ECC Position
{ false, false, 0, 0 }, // ECC Pattern
};
};
#endif

View File

@ -17,7 +17,8 @@
//
// Implements the backing store for MSCP disk images
//
class mscp_drive_c: public storagedrive_c {
class mscp_drive_c: public storagedrive_c
{
public:
mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber);
~mscp_drive_c(void);
@ -55,7 +56,8 @@ public:
private:
struct DriveInfo {
struct DriveInfo
{
char TypeName[16];
size_t BlockCount;
uint32_t MediaID;

291
10.02_devices/2_src/rh11.cpp Executable file
View File

@ -0,0 +1,291 @@
/*
rh11_cpp: RH11 UNIBUS-MASSBUS controller
Copyright Vulcan Inc. 2020 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
*/
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include "unibus.h"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp"
#include "storagecontroller.hpp"
#include "rh11.hpp"
#include "massbus_rp.hpp"
rh11_c::rh11_c() :
storagecontroller_c(),
_massbus(nullptr),
_interruptEnable(false),
_busAddress(0),
_unit(0)
{
// static config
name.value = "rh";
type_name.value = "RH11";
log_label = "rh";
// 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, 11, 0254, 5);
// TODO: allow configuration for different device types
_massbus.reset(new massbus_rp_c(this));
// The RH11 controller exposes up to 32 registers, not all are used
// and use depends on the devices attached to the MASSBUS.
// TODO: What does an RH11 do when an unimplemented register is accessed?
register_count = 32;
for (int i=0; i<register_count; i++)
{
RH_reg[i] = &(this->registers[i]);
switch (i)
{
case RHCS1:
// Control & Status Reg 1
strcpy(RH_reg[i]->name, "RHCS1");
RH_reg[i]->active_on_dati = false;
RH_reg[i]->active_on_dato = true;
RH_reg[i]->reset_value = 0x0008;
RH_reg[i]->writable_bits = 0x037f;
break;
case RHWC:
// Word count
strcpy(RH_reg[i]->name, "RHWC");
RH_reg[i]->active_on_dati = false;
RH_reg[i]->active_on_dato = false;
RH_reg[i]->reset_value = 0;
RH_reg[i]->writable_bits = 0xffff;
break;
case RHBA:
// Bus address
strcpy(RH_reg[i]->name, "RHBA");
RH_reg[i]->active_on_dati = false;
RH_reg[i]->active_on_dato = false;
RH_reg[i]->reset_value = 0;
RH_reg[i]->writable_bits = 0xffff;
break;
case RHCS2:
// Control & Status Reg 2
strcpy(RH_reg[i]->name, "RHCS2");
RH_reg[i]->active_on_dati = false;
RH_reg[i]->active_on_dato = true;
RH_reg[i]->reset_value = 0;
RH_reg[i]->writable_bits = 0x003f;
break;
case RHDB:
// Data Buffer (maintenance only)
strcpy(RH_reg[i]->name, "RHDB");
RH_reg[i]->active_on_dati = true;
RH_reg[i]->active_on_dato = true;
RH_reg[i]->reset_value = 0;
RH_reg[i]->writable_bits = 0xffff;
break;
default:
//
// This is a "REMOTE" register implemented by the device(s)
// attached to the massbus.
//
if (_massbus->ImplementsRegister(i))
{
strcpy(RH_reg[i]->name, _massbus->RegisterName(i).c_str());
RH_reg[i]->active_on_dati = _massbus->RegisterActiveOnDATI(i);
RH_reg[i]->active_on_dato = _massbus->RegisterActiveOnDATO(i);
RH_reg[i]->reset_value = _massbus->RegisterResetValue(i);
RH_reg[i]->writable_bits = _massbus->RegisterWritableBits(i);
}
else
{
// Not implemented by this device type
RH_reg[i] = nullptr;
}
break;
}
}
}
rh11_c::~rh11_c()
{
}
void
rh11_c::write_register(
uint32_t reg,
uint16_t value)
{
set_register_dati_value(
RH_reg[_massbusToUnibusRegisterMap[reg]],
value,
"write_register");
}
uint16_t
rh11_c::read_register(
uint32_t reg)
{
return get_register_dato_value(
RH_reg[_massbusToUnibusRegisterMap[reg]]);
}
// return false, if illegal parameter value.
// verify "new_value", must output error messages
bool rh11_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)
}
// Background worker.
// Handle device operations.
void rh11_c::worker(unsigned instance)
{
UNUSED(instance); // only one
worker_init_realtime_priority(rt_device);
bool do_interrupt = true;
timeout_c timeout;
/*
while (!workers_terminate)
{
}
*/
}
//
// process DATI/DATO access to the RK11's "active" registers.
// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS.
// The time between PRU event and program flow into this callback
// is determined by ARM Linux context switch
//
// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc:
// do not read back dati_flipflops.
void rh11_c::on_after_register_access(
unibusdevice_register_t *device_reg,
uint8_t unibus_control)
{
UNUSED(unibus_control);
uint16_t value = device_reg->active_dato_flipflops;
switch(device_reg->index)
{
case RHCS1: // Control & Status 1
{
if (UNIBUS_CONTROL_DATO == unibus_control)
{
// IE bit
_interruptEnable = !!(value & 0x40);
// Extended bus address bits
_busAddress = (_busAddress & 0xffff) | ((value & 0x300) << 8);
// Let the massbus device take a crack at the shared bits
_massbus->WriteRegister(_unit, RHCS1, value & 0x3f);
INFO("RHCS1: IE %x BA 0%o", _interruptEnable, _busAddress);
}
}
break;
case RHCS2: // Control & Status 2
{
if (UNIBUS_CONTROL_DATO == unibus_control)
{
_unit = (value & 0x7);
_busAddressIncrementProhibit = !!(value & 0x8);
_parityTest = !!(value & 0x10);
// TODO: handle System Register Clear (bit 5)
INFO("RHCS2: unit %d", _unit);
}
}
break;
default:
// See if a massbus device wishes to answer
if (RH_reg[device_reg->index] != nullptr)
{
if (UNIBUS_CONTROL_DATO == unibus_control)
{
_massbus->WriteRegister(_unit, _unibusToMassbusRegisterMap[device_reg->index], value);
}
else
{
set_register_dati_value(
device_reg,
_massbus->ReadRegister(_unit, _unibusToMassbusRegisterMap[device_reg->index]),
"on_after_register_access");
}
}
break;
}
}
void rh11_c::invoke_interrupt(void)
{
}
void rh11_c::reset_controller(void)
{
reset_unibus_registers();
}
void rh11_c::on_power_changed(void)
{
storagecontroller_c::on_power_changed();
if (power_down)
{
// power-on defaults
reset_controller();
}
}
// UNIBUS INIT: clear all registers
void rh11_c::on_init_changed(void)
{
// write all registers to "reset-values"
if (init_asserted)
{
reset_controller();
}
storagecontroller_c::on_init_changed();
}
void
rh11_c::on_drive_status_changed(storagedrive_c *drive)
{
}

158
10.02_devices/2_src/rh11.hpp Executable file
View File

@ -0,0 +1,158 @@
/*
rh11.hpp: RH11 UNIBUS-MASSBUS controller
Copyright Vulcan Inc. 2020 via Living Computers: Museum + Labs, Seattle, WA.
Contributed under the BSD 2-clause license.
*/
#ifndef _RH11_HPP_
#define _RH11_HPP_
#include <fstream>
#include <cstdint>
#include <memory>
using namespace std;
#include "utils.hpp"
#include "unibusadapter.hpp"
#include "unibusdevice.hpp"
#include "storagecontroller.hpp"
#include "massbus_device.hpp"
// Maps the unibus register index to the MASSBUS register number.
// -1 entries are local to the rh11.
int32_t _unibusToMassbusRegisterMap[] =
{
00, // 776700
-1, // 776702
-1, // 776704
05, // 776706
-1, // 776710
01, // 776712
02, // 776714
04, // 776716
07, // 776720
-1, // 776722
03, // 776724
06, // 776726
010, // 776730
011, // 776732
012, // 776734
013, // 776736
014, // 776740
015, // 776742
016, // etc.
017,
020,
021,
022,
023,
024,
025,
026,
027,
030,
031,
032,
033
};
int32_t _massbusToUnibusRegisterMap[] =
{
00, // 0
05,
06,
012,
07,
03,
013,
010,
014, // 10
015,
016,
017,
020,
021,
022,
023,
024, // 20
025,
026,
027,
030,
031,
032,
033,
034, // 30
035,
036,
037,
-1,
-1,
-1,
-1,
};
class rh11_c: public storagecontroller_c
{
private:
// 32 RH11 Registers:
unibusdevice_register_t* RH_reg[32];
// "Local" registers (or mostly local anyway)
#define RHCS1 0
#define RHWC 1
#define RHBA 2
#define RHCS2 4
#define RHDB 011
// Unibusadapter: RH11 has one INTR and DMA
// should be merged with RH11::DMARequest
dma_request_c dma_request = dma_request_c(this); // operated by unibusadapter
intr_request_c intr_request = intr_request_c(this);
void invoke_interrupt(void);
void reset_controller(void);
private:
std::unique_ptr<massbus_device_c> _massbus;
// Control & Status reg 1 bits
bool _interruptEnable;
uint32_t _busAddress;
// Control & Status reg 2 bits
uint16_t _unit;
bool _busAddressIncrementProhibit;
bool _parityTest;
public:
rh11_c();
virtual ~rh11_c();
// Unibus register access (for devices on massbus)
void write_register(uint32_t reg, uint16_t value);
uint16_t read_register(uint32_t reg);
public:
// background worker function
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(
unibusdevice_register_t *device_reg,
uint8_t unibus_control) override;
bool on_param_changed(parameter_c *param) override;
void on_power_changed(void) override;
void on_init_changed(void) override;
void on_drive_status_changed(storagedrive_c *drive) override;
};
#endif

View File

@ -104,6 +104,8 @@ OBJECTS = $(OBJDIR)/application.o \
$(OBJDIR)/uda.o \
$(OBJDIR)/mscp_server.o \
$(OBJDIR)/mscp_drive.o \
$(OBJDIR)/rh11.o \
$(OBJDIR)/massbus_rp.o \
$(OBJDIR)/rs232.o \
$(OBJDIR)/rs232adapter.o \
$(OBJDIR)/dl11w.o \
@ -239,6 +241,15 @@ $(OBJDIR)/mscp_server.o : $(DEVICE_SRC_DIR)/mscp_server.cpp $(DEVICE_SRC_DIR)/
$(OBJDIR)/mscp_drive.o : $(DEVICE_SRC_DIR)/mscp_drive.cpp $(DEVICE_SRC_DIR)/mscp_drive.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/rh11.o : $(DEVICE_SRC_DIR)/rh11.cpp $(DEVICE_SRC_DIR)/rh11.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/massbus_device.o : $(DEVICE_SRC_DIR)/massbus_device.cpp $(DEVICE_SRC_DIR)/massbus_device.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/massbus_rp.o : $(DEVICE_SRC_DIR)/massbus_rp.cpp $(DEVICE_SRC_DIR)/massbus_rp.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/rs232.o : $(DEVICE_SRC_DIR)/rs232.cpp $(DEVICE_SRC_DIR)/rs232.hpp
$(CC) $(CCFLAGS) $< -o $@

View File

@ -52,6 +52,7 @@
#include "rl11.hpp"
#include "rk11.hpp"
#include "uda.hpp"
#include "rh11.hpp"
#include "dl11w.hpp"
#include "cpu.hpp"
@ -156,6 +157,8 @@ void application_c::menu_devices(const char *menu_code, bool with_emulated_CPU)
rk11_c *RK11 = new rk11_c();
// Create UDA50
uda_c *UDA50 = new uda_c();
// Create RH11
rh11_c *RH11 = new rh11_c();
// Create SLU+ LTC
slu_c *DL11 = new slu_c();
// to inject characters into DL11 receiver
@ -567,6 +570,9 @@ void application_c::menu_devices(const char *menu_code, bool with_emulated_CPU)
UDA50->enabled.set(false);
delete UDA50;
RH11->enabled.set(false);
delete RH11;
//test_controller->enabled.set(false);
//delete test_controller;