1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-01-28 04:47:46 +00:00
Files
livingcomputermuseum.UniBone/10.02_devices/2_src/rk05.cpp
Joerg Hoppe db0167afe1 Version 2019-06: many changes
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
2019-06-14 16:31:01 +02:00

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