mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-01-28 04:47:46 +00:00
PRU1 code split into multiple images 1. test functions 2. UNIBUS operation PRU1 bus latch interface Write byte/bits access not with MACROS (random optimizer influence), now with *_helper() procedures. Same timing, more determinism, much code saving. Nono more ASM code to write PRU0 XFER area. demo: menu to test UNIBUS signals directly rework "Arbitration" logic: now 3-fold Rework of UNIBUs arbtiration: NONE/CLIENT/MASTER - no Arbitrator (SACK penidng for 11/34 Konsole) (NONE) - phyiscal PDP_11 CPU is Arbitrator (CLIENT) - UniBone implements Arbitrator (MASTER) - Same PRU code loop handles all arbitration types PRU buslatch timing slower, for some problematic PCBs More aggressive bus latch selftest (mixed patterns, running on PRU now) Refinement of ready-to-run scripts - Adapted to changed "demo" menu - new name scheme <OS>_<boot- drive>_<PDP-11CPU> indicates - which OS is run - which disk emulation is used and what is the boot device - what is the (minimum) PDP-11 to run that Merged in Joshs DMA timing for 11/84 UNIBUS master cycles waits 350 us before MSYN, instead 150. Merged in Joshs DMA request queue multiple devices canrequest INTR and DMAs concurrently, will be put on the bus sequentially Merged in Joshs MSCP driver - Build RT-11v5.5 for MSCP - added boot loader "du.lst" MSCP run scrips 2.11BSD on MSCP on PDP-11/44 RT11 on MSCP Fix: image file sizing Disk image file exptend automatically if block beyond current file end is written
320 lines
6.6 KiB
C++
Executable File
320 lines
6.6 KiB
C++
Executable File
/*
|
|
rk05.cpp: implementation of RK05 disk drive, attached to RK11D controller
|
|
|
|
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
|
|
Contributed under the BSD 2-clause license.
|
|
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
using namespace std;
|
|
|
|
#include "logger.hpp"
|
|
#include "utils.hpp"
|
|
#include "rk11.hpp"
|
|
#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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
bool rk05_c::get_write_protect(void)
|
|
{
|
|
return _wps;
|
|
}
|
|
|
|
bool rk05_c::get_rws_ready(void)
|
|
{
|
|
return _rwsrdy;
|
|
}
|
|
|
|
bool rk05_c::get_drive_ready(void)
|
|
{
|
|
return _dry;
|
|
}
|
|
|
|
bool rk05_c::get_sector_counter_ok(void)
|
|
{
|
|
return _sok;
|
|
}
|
|
|
|
bool rk05_c::get_seek_incomplete(void)
|
|
{
|
|
return _sin;
|
|
}
|
|
|
|
bool rk05_c::get_drive_unsafe(void)
|
|
{
|
|
return _dru;
|
|
}
|
|
|
|
bool rk05_c::get_rk05_disk_online(void)
|
|
{
|
|
return _rk05;
|
|
}
|
|
|
|
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::on_param_changed(
|
|
parameter_c *param)
|
|
{
|
|
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 false;
|
|
}
|
|
|
|
//
|
|
// 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_init_changed(void)
|
|
{
|
|
// called at high priority.
|
|
|
|
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);
|
|
|
|
_current_cylinder = cylinder;
|
|
|
|
// 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);
|
|
|
|
timeout_c delay;
|
|
|
|
// 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);
|
|
|
|
// Set RWS ready now that we're done.
|
|
_rwsrdy = true;
|
|
|
|
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);
|
|
|
|
_current_cylinder = cylinder;
|
|
|
|
// 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);
|
|
|
|
timeout_c delay;
|
|
|
|
// 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);
|
|
|
|
// Set RWS ready now that we're done.
|
|
_rwsrdy = true;
|
|
|
|
controller->on_drive_status_changed(this);
|
|
}
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
// 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);
|
|
|
|
seek(0);
|
|
// SCP change will be posted when the seek instigated above is completed.
|
|
}
|
|
|
|
void rk05_c::worker(void)
|
|
{
|
|
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
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|