1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-01-28 20:51:08 +00:00
Files
livingcomputermuseum.UniBone/10.02_devices/2_src/uda.hpp
Josh Dersch 2189e264c3 Initial stab at MSCP implementation. Strives to be MSCP compliant but is not an emulation
of the UDA50 controller.

Currently works acceptably with RT-11, does not currently boot.  Many holes in implementation.
2019-04-16 02:30:40 +02:00

163 lines
3.7 KiB
C++

/*
uda.hpp: MSCP controller port (UDA50)
*/
#pragma once
#include <memory>
#include "utils.hpp"
#include "unibusdevice.hpp"
#include "storagecontroller.hpp"
#include "mscp_server.hpp"
// TODO: this currently assumes a little-endian machine!
struct Message
{
uint16_t MessageLength alignas(2);
union
{
uint16_t Word1;
struct
{
uint16_t Credits : 4;
uint16_t MessageType : 4;
uint16_t ConnectionID : 8;
} Info;
} Word1 alignas(2);
// 384 bytes is the minimum needed to support
// datagram messages. The underlying buffer will
// be allocated to cover whatever size needed.
uint8_t Message[384] alignas(2);
};
/*
This implements the Transport layer for a Unibus MSCP controller.
Logic for initialization, reset, and communcation with the MSCP Server
is implemented here.
*/
class uda_c : public storagecontroller_c
{
public:
uda_c();
virtual ~uda_c();
void worker(void) override;
void on_after_register_access(
unibusdevice_register_t *device_reg,
uint8_t unibus_control) override;
void on_power_changed(void) override;
void on_init_changed(void) override;
void on_drive_status_changed(storagedrive_c *drive) override;
public:
//
// Returns the next command message from the command ring, if any.
// Returns NULL if the ring is empty.
//
Message* GetNextCommand(void);
//
// Posts a response message to the response ring and memory
// if there is space.
// Returns FALSE if the ring is full.
bool PostResponse(std::shared_ptr<Message> response);
uint64_t GetControllerIdentifier(void);
private:
// TODO: consolidate these private/public groups here
void Reset(void);
void Interrupt(void);
uint32_t GetCommandDescriptorAddress(size_t index);
uint32_t GetResponseDescriptorAddress(size_t index);
public:
bool DMAWriteWord(uint32_t address, uint16_t word);
uint16_t DMAReadWord(uint32_t address, bool& success);
bool DMAWrite(uint32_t address, size_t lengthInBytes, uint8_t* buffer);
uint8_t* DMARead(uint32_t address, size_t lengthInBytes);
private:
void update_SA(void);
// UDA50 registers:
unibusdevice_register_t *IP_reg;
unibusdevice_register_t *SA_reg;
uint16_t _sa;
std::unique_ptr<mscp_server> _server;
uint32_t _ringBase;
// Lengths are in terms of slots (32 bits each) in the
// corresponding rings.
size_t _commandRingLength;
size_t _responseRingLength;
// The current slot in the ring being accessed.
uint32_t _commandRingPointer;
uint32_t _responseRingPointer;
// Interrupt vector -- if zero, no interrupts
// will be generated.
uint32_t _interruptVector;
// Interrupt enable flag
bool _interruptEnable;
// Purge interrupt enable flag
bool _purgeInterruptEnable;
// Value written during step1, saved
// to make manipulation easier.
uint16_t _step1Value;
enum InitializationStep
{
Uninitialized = 0,
Step1 = 1,
Step2 = 2,
Step3 = 4,
Step4 = 8,
Complete,
};
InitializationStep _initStep;
bool _next_step;
void StateTransition(InitializationStep nextStep);
// TODO: this currently assumes a little-endian machine!
struct Descriptor
{
union alignas(2)
{
uint16_t Word0;
uint16_t EnvelopeLow;
} Word0;
union alignas(2)
{
uint16_t Word1;
struct
{
uint16_t EnvelopeHigh : 2;
uint16_t Reserved : 12;
uint16_t Flag : 1;
uint16_t Ownership : 1;
} Fields;
} Word1;
};
};