mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-02-26 16:54:04 +00:00
Enable devices individually over param "enabled"
UNIBUS addr, intr vector, level setable
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
/* device.cpp - abstract base class for devices
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
12-nov-2018 JH entered beta phase
|
||||
|
||||
Abstract device, with or without UNIBUS registers.
|
||||
maybe mass storage controller, storage drive or other UNIBUS device
|
||||
@@ -32,7 +32,7 @@
|
||||
- has a worker()
|
||||
- has a logger
|
||||
- has parameters
|
||||
*/
|
||||
*/
|
||||
#define _DEVICE_CPP_
|
||||
|
||||
#include <string.h>
|
||||
@@ -68,17 +68,17 @@ static void device_worker_pthread_cleanup_handler(void *context) {
|
||||
|
||||
static void *device_worker_pthread_wrapper(void *context) {
|
||||
device_c *device = (device_c *) context;
|
||||
int oldstate ; // not used
|
||||
int oldstate; // not used
|
||||
#define this device // make INFO work
|
||||
// call real worker
|
||||
INFO("%s::worker() started", device->name.value.c_str());
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate) ;
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldstate) ; //ASYNCH not allowed!
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldstate); //ASYNCH not allowed!
|
||||
device->worker_terminate = false;
|
||||
device->worker_terminated = false;
|
||||
pthread_cleanup_push(device_worker_pthread_cleanup_handler, device) ;
|
||||
device->worker();
|
||||
pthread_cleanup_pop(1) ; // call cleanup_handler on regular exit
|
||||
pthread_cleanup_push(device_worker_pthread_cleanup_handler, device);
|
||||
device->worker();
|
||||
pthread_cleanup_pop(1); // call cleanup_handler on regular exit
|
||||
// not reached on pthread_cancel()
|
||||
#undef this
|
||||
return NULL;
|
||||
@@ -99,10 +99,13 @@ device_c::device_c() {
|
||||
// creation order of vector vs params?
|
||||
name.parameterized = this;
|
||||
type_name.parameterized = this;
|
||||
enabled.parameterized = this;
|
||||
verbosity.parameterized = this;
|
||||
verbosity.value = *log_level_ptr; // global default value from logger->logsource
|
||||
enabled.value = false; // must be activated by emulation logic/user interaction
|
||||
param_add(&name);
|
||||
param_add(&type_name);
|
||||
param_add(&enabled);
|
||||
param_add(&emulation_speed);
|
||||
param_add(&verbosity);
|
||||
emulation_speed.value = 1;
|
||||
@@ -125,7 +128,27 @@ device_c::~device_c() {
|
||||
mydevices.erase(p);
|
||||
}
|
||||
|
||||
bool device_c::on_param_changed(parameter_c *param) {
|
||||
if (param == &enabled) {
|
||||
if (enabled.new_value)
|
||||
worker_start();
|
||||
else
|
||||
worker_stop();
|
||||
}
|
||||
// all devices forward their "on_param_changed" to parent classes,
|
||||
// until a class rejects a value.
|
||||
// device_c is the grand pratnes and produces "OK" for unknown or passive parameters
|
||||
return true;
|
||||
}
|
||||
|
||||
// search device in global list mydevices[]
|
||||
device_c *device_c::find_by_name(char *name) {
|
||||
list<device_c *>::iterator it;
|
||||
for (it = device_c::mydevices.begin(); it != device_c::mydevices.end(); ++it)
|
||||
if (!strcasecmp((*it)->name.value.c_str(), name))
|
||||
return *it;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// set priority to max, keep policy, return current priority
|
||||
// do not change worker_sched_priority
|
||||
@@ -262,12 +285,13 @@ void device_c::worker_start(void) {
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
// pthread_attr_setstacksize(&attr, 1024*1024);
|
||||
assert(worker_terminated); // do not srtat device worker twiche in parallel!
|
||||
int status = pthread_create(&worker_pthread, &attr, &device_worker_pthread_wrapper,
|
||||
(void *) this);
|
||||
if (status != 0) {
|
||||
FATAL("Failed to create pthread with status = %d", status);
|
||||
}
|
||||
pthread_attr_destroy(&attr) ; // why?
|
||||
pthread_attr_destroy(&attr); // why?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +311,8 @@ void device_c::worker_stop(void) {
|
||||
// if thread is hanging in pthread_cond_wait(): send a cancellation request
|
||||
status = pthread_cancel(worker_pthread);
|
||||
if (status != 0)
|
||||
FATAL("Failed to send cancellation request to worker_pthread with status = %d", status);
|
||||
FATAL("Failed to send cancellation request to worker_pthread with status = %d",
|
||||
status);
|
||||
}
|
||||
|
||||
// !! If crosscompling: this causes a crash in the worker thread
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
/* device.hpp - abstract base class for devices
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
#ifndef _DEVICE_HPP_
|
||||
#define _DEVICE_HPP_
|
||||
|
||||
@@ -40,70 +40,74 @@ using namespace std;
|
||||
// sets device register values depending on internal status,
|
||||
// reacts on register read/write over UNIBUS by evaluation of PRU events.
|
||||
class device_c: public logsource_c, public parameterized_c {
|
||||
private:
|
||||
void worker_start(void);
|
||||
void worker_stop(void);
|
||||
|
||||
public:
|
||||
// the class holds a list of pointers to instantiated devices
|
||||
// also needed to have a list of threads
|
||||
static list<device_c *> mydevices;
|
||||
|
||||
device_c *parent ; // example: storagedrive_c.parent is storage-controller
|
||||
device_c *parent; // example: storagedrive_c.parent is storage-controller
|
||||
|
||||
// name of instance: "RL3"
|
||||
parameter_string_c name = parameter_string_c(NULL, "name", "name", /*readonly*/
|
||||
true, "Unique identifier of device");
|
||||
true, "Unique identifier of device");
|
||||
// name of type: "RL02". normally readonly.
|
||||
parameter_string_c type_name = parameter_string_c(NULL, "type", "type", /*readonly*/
|
||||
true, "Type");
|
||||
// NULL: do not link params to this device automatically over param constructor
|
||||
|
||||
parameter_unsigned_c emulation_speed = parameter_unsigned_c(NULL,
|
||||
"emulation_speed", "es", false, "", "%d",
|
||||
"1 = original speed, > 1: mechanics is this factor faster", 8, 10);
|
||||
// "enabled": controls device installation to PRU and worker() state.
|
||||
parameter_bool_c enabled = parameter_bool_c(NULL, "enabled", "en", true,
|
||||
"device installed and ready to use?");
|
||||
|
||||
parameter_unsigned_c emulation_speed = parameter_unsigned_c(NULL, "emulation_speed", "es",
|
||||
false, "", "%d", "1 = original speed, > 1: mechanics is this factor faster", 8, 10);
|
||||
// 1 = original speed, > 1: mechanics is this factor faster
|
||||
|
||||
parameter_unsigned_c verbosity = parameter_unsigned_c(NULL,
|
||||
"verbosity", "v", false, "", "%d",
|
||||
"1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug", 8, 10);
|
||||
|
||||
parameter_unsigned_c verbosity = parameter_unsigned_c(NULL, "verbosity", "v", false, "",
|
||||
"%d", "1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug", 8, 10);
|
||||
|
||||
// make data exchange with worker atomic
|
||||
std::mutex worker_mutex ;
|
||||
std::mutex worker_mutex;
|
||||
|
||||
// scheduler settings for worker thread
|
||||
int worker_sched_policy;
|
||||
int worker_sched_priority;
|
||||
|
||||
|
||||
|
||||
enum worker_priority_e {
|
||||
none_rt, // under all RT priorities
|
||||
rt_device, // all controeller and storage worker
|
||||
rt_max // 100% CPU, uninterruptable
|
||||
} ;
|
||||
void worker_init_realtime_priority(enum worker_priority_e priority) ;
|
||||
void worker_boost_realtime_priority(void) ;
|
||||
void worker_restore_realtime_priority(void) ;
|
||||
};
|
||||
void worker_init_realtime_priority(enum worker_priority_e priority);
|
||||
void worker_boost_realtime_priority(void);
|
||||
void worker_restore_realtime_priority(void);
|
||||
|
||||
device_c();
|
||||
virtual ~device_c(); // class with virtual functions should have virtual destructors
|
||||
|
||||
virtual bool on_param_changed(parameter_c *param);
|
||||
|
||||
// search in mydevices
|
||||
static device_c *find_by_name(char *name);
|
||||
|
||||
// a device can be powered down. use this to define power-up values
|
||||
volatile bool power_down ;
|
||||
volatile bool power_down;
|
||||
virtual void on_power_changed(void) = 0; // reset device, UNIBUS DC_LO
|
||||
|
||||
// every device has a INIT signal, which can be active (asserted) or inactive
|
||||
// set/release device from INIT state
|
||||
volatile bool init_asserted ;
|
||||
volatile bool init_asserted;
|
||||
virtual void on_init_changed(void) = 0; // reset device, like UNIBUS INIT
|
||||
|
||||
// worker thread
|
||||
volatile bool worker_terminate; // cmd flag to worker()
|
||||
volatile bool worker_terminated; // ACK flag from worker()
|
||||
|
||||
|
||||
pthread_t worker_pthread;
|
||||
void worker_start(void);
|
||||
void worker_stop(void);
|
||||
virtual void worker(void) = 0; // background worker function
|
||||
};
|
||||
|
||||
|
||||
@@ -73,16 +73,22 @@ parameter_string_c::parameter_string_c(parameterized_c *parameterized, string na
|
||||
parameter_string_c::~parameter_string_c() {
|
||||
}
|
||||
|
||||
void parameter_string_c::set(string new_value) {
|
||||
|
||||
if (value == new_value)
|
||||
return ; // call "on_change" only on change
|
||||
this->new_value = new_value ;
|
||||
// reject parsed value, if device parameter check complains
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
|
||||
// string parsing is just copying
|
||||
void parameter_string_c::parse(string text) {
|
||||
if (readonly)
|
||||
throw bad_parameter_readonly("Parameter \"" + name + "\" is read-only");
|
||||
|
||||
new_value = text;
|
||||
|
||||
// reject parsed value, if device parameter check complains
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
set(text) ;
|
||||
}
|
||||
|
||||
string *parameter_string_c::render() {
|
||||
@@ -96,7 +102,19 @@ parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name,
|
||||
value = false;
|
||||
}
|
||||
|
||||
// bool accepty 0/1, y*/n*, t*/f*
|
||||
|
||||
|
||||
void parameter_bool_c::set(bool new_value) {
|
||||
if (value == new_value)
|
||||
return ; // call "on_change" only on change
|
||||
|
||||
// reject parsed value, if device parameter check complains
|
||||
this->new_value = new_value ;
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
// bool accepts 0/1, y*/n*, t*/f*
|
||||
void parameter_bool_c::parse(string text) {
|
||||
char c;
|
||||
if (readonly)
|
||||
@@ -112,10 +130,7 @@ void parameter_bool_c::parse(string text) {
|
||||
new_value = false;
|
||||
else
|
||||
throw bad_parameter_parse("Illegal boolean expression \"" + text + "\"");
|
||||
|
||||
// reject parsed value, if device parameter check complains
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
set(new_value) ;
|
||||
}
|
||||
|
||||
string *parameter_bool_c::render() {
|
||||
@@ -134,6 +149,18 @@ parameter_unsigned_c::parameter_unsigned_c(parameterized_c *parameterized, strin
|
||||
this->base = base;
|
||||
value = 0;
|
||||
}
|
||||
|
||||
|
||||
void parameter_unsigned_c::set(unsigned new_value) {
|
||||
if (value == new_value)
|
||||
return ; // call "on_change" only on change
|
||||
|
||||
this->new_value = new_value ;
|
||||
// reject parsed value, if device parameter check complains
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
void parameter_unsigned_c::parse(string text) {
|
||||
char *endptr;
|
||||
if (readonly)
|
||||
@@ -146,10 +173,7 @@ void parameter_unsigned_c::parse(string text) {
|
||||
if (new_value & ~BitmaskFromLen32[bitwidth]) //
|
||||
throw bad_parameter_parse(
|
||||
"Number " + to_string(new_value) + " exceeds bitwidth " + to_string(bitwidth));
|
||||
|
||||
// reject parsed value, if device parameter check complains
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
set(new_value) ;
|
||||
}
|
||||
|
||||
string *parameter_unsigned_c::render() {
|
||||
@@ -168,6 +192,16 @@ parameter_unsigned64_c::parameter_unsigned64_c(parameterized_c *parameterized, s
|
||||
value = 0;
|
||||
}
|
||||
|
||||
void parameter_unsigned64_c::set(uint64_t new_value) {
|
||||
if (value == new_value)
|
||||
return ; // call "on_change" only on change
|
||||
|
||||
this->new_value = new_value ;
|
||||
// reject parsed value, if device parameter check complains
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
void parameter_unsigned64_c::parse(string text) {
|
||||
char *endptr;
|
||||
if (readonly)
|
||||
@@ -180,10 +214,7 @@ void parameter_unsigned64_c::parse(string text) {
|
||||
if (new_value & ~BitmaskFromLen64[bitwidth]) //
|
||||
throw bad_parameter_parse(
|
||||
"Number " + to_string(new_value) + " exceeds bitwidth " + to_string(bitwidth));
|
||||
|
||||
// reject parsed value, if device parameter check complains
|
||||
if (parameterized == NULL || parameterized->on_param_changed(this))
|
||||
value = new_value;
|
||||
set(new_value) ;
|
||||
}
|
||||
|
||||
string *parameter_unsigned64_c::render() {
|
||||
|
||||
@@ -93,25 +93,26 @@ class parameter_string_c: public parameter_c {
|
||||
public:
|
||||
// dynamic state
|
||||
string value;
|
||||
string new_value ; // after parse, checked by device.on_param_change_check()
|
||||
|
||||
string new_value;
|
||||
|
||||
parameter_string_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string info);
|
||||
~parameter_string_c();
|
||||
string *render(void) override;
|
||||
void parse(string text) override;
|
||||
void set(string new_value) ;
|
||||
};
|
||||
|
||||
class parameter_bool_c: public parameter_c {
|
||||
public:
|
||||
// dynamic state
|
||||
bool value;
|
||||
bool new_value ; // after parse, checked by device.on_param_change_check()
|
||||
bool new_value;
|
||||
|
||||
parameter_bool_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string info);
|
||||
parameter_bool_c();
|
||||
string *render(void) override;
|
||||
void parse(string text) override;
|
||||
void set(bool new_value) ;
|
||||
};
|
||||
|
||||
class parameter_unsigned_c: public parameter_c {
|
||||
@@ -123,12 +124,13 @@ public:
|
||||
|
||||
// dynamic state
|
||||
unsigned value;
|
||||
unsigned new_value ; // after parse, checked by device.on_param_change_check()
|
||||
unsigned new_value;
|
||||
|
||||
parameter_unsigned_c(parameterized_c *parameterized, string name,string shortname, bool readonly, string unit, string format,
|
||||
string info, unsigned bitwidth, unsigned base);
|
||||
string *render(void) override;
|
||||
void parse(string text) override;
|
||||
void set(unsigned new_value) ;
|
||||
};
|
||||
|
||||
class parameter_unsigned64_c: public parameter_c {
|
||||
@@ -140,12 +142,13 @@ public:
|
||||
|
||||
// dynamic state
|
||||
uint64_t value;
|
||||
uint64_t new_value ; // after parse, checked by device.on_param_change_check()
|
||||
uint64_t new_value ;
|
||||
|
||||
parameter_unsigned64_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string unit, string format,
|
||||
string info, unsigned bitwidth, unsigned base);
|
||||
string *render(void) override;
|
||||
void parse(string text) override;
|
||||
void set(uint64_t new_value) ;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
/* storagecontroller.cpp: a unibus device with several "storagedrives" attached
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
12-nov-2018 JH entered beta phase
|
||||
|
||||
A unibus device with several "storagedrives"
|
||||
supports the "attach" command
|
||||
A unibus device with several "storagedrives"
|
||||
supports the "attach" command
|
||||
*/
|
||||
#include "utils.hpp"
|
||||
|
||||
@@ -41,8 +41,15 @@ storagecontroller_c::~storagecontroller_c() {
|
||||
|
||||
// implements params, so must handle "change"
|
||||
bool storagecontroller_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param) ;
|
||||
return true ;
|
||||
if (param == &enabled) {
|
||||
if (!enabled.new_value)
|
||||
// power/up/down attached drives, then plug to UNIBUS
|
||||
// if disable, disable also the drives ("contreolelr plugged from UNIBUS)")
|
||||
// on enable, leave them disabled (user may decide which to use)
|
||||
for (unsigned i = 0; i < drivecount; i++)
|
||||
storagedrives[i]->enabled.set(false);
|
||||
}
|
||||
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
// forward BUS events to connected storage drives
|
||||
@@ -65,21 +72,3 @@ void storagecontroller_c::on_init_changed() {
|
||||
}
|
||||
}
|
||||
|
||||
// start/stop threads of this and all drives
|
||||
|
||||
void storagecontroller_c::worker_start() {
|
||||
vector<storagedrive_c*>::iterator it;
|
||||
for (it = storagedrives.begin(); it != storagedrives.end(); it++) {
|
||||
(*it)->worker_start();
|
||||
}
|
||||
device_c::worker_start();
|
||||
}
|
||||
|
||||
void storagecontroller_c::worker_stop() {
|
||||
device_c::worker_stop();
|
||||
vector<storagedrive_c*>::iterator it;
|
||||
for (it = storagedrives.begin(); it != storagedrives.end(); it++) {
|
||||
(*it)->worker_stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
/* storagecontroller.hpp: a unibus device with several "storagedrives" attached
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
12-nov-2018 JH entered beta phase
|
||||
|
||||
*/
|
||||
|
||||
@@ -41,15 +41,12 @@ public:
|
||||
|
||||
// does not instantiate the drives
|
||||
storagecontroller_c(void);
|
||||
virtual ~storagecontroller_c() ; // classes with virtual functions shoudl have virtual destructors
|
||||
virtual ~storagecontroller_c(); // classes with virtual functions shoudl have virtual destructors
|
||||
|
||||
virtual bool on_param_changed(parameter_c *param) override;
|
||||
virtual void on_power_changed() override ;
|
||||
virtual void on_init_changed() override ;
|
||||
virtual void on_drive_status_changed(storagedrive_c *drive) = 0 ;
|
||||
|
||||
void worker_start() ; // start/stop threads of this and all drives
|
||||
void worker_stop() ;
|
||||
virtual void on_power_changed() override;
|
||||
virtual void on_init_changed() override;
|
||||
virtual void on_drive_status_changed(storagedrive_c *drive) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ storagedrive_c::storagedrive_c(storagecontroller_c *controller) :
|
||||
|
||||
// implements params, so must handle "change"
|
||||
bool storagedrive_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param);
|
||||
return true;
|
||||
// no own "enable" logic
|
||||
return device_c::on_param_changed(param);
|
||||
}
|
||||
|
||||
// http://www.cplusplus.com/doc/tutorial/files/
|
||||
@@ -121,11 +121,11 @@ void storagedrive_c::file_write(uint8_t *buffer, uint64_t position, unsigned len
|
||||
assert(!file_readonly); // caller must take care
|
||||
|
||||
// enlarge file in chunks until filled up to "position"
|
||||
f.clear() ; // clear fail bit
|
||||
f.clear(); // clear fail bit
|
||||
f.seekp(0, ios::end); // move to current EOF
|
||||
file_size = f.tellp(); // current file len
|
||||
if (file_size < 0)
|
||||
file_size = 0 ; // -1 on emtpy files
|
||||
file_size = 0; // -1 on emtpy files
|
||||
while (file_size < write_pos) {
|
||||
// fill in '00' 'chunks up to desired end, but limit to max_chunk_size
|
||||
int chunk_size = std::min(max_chunk_size, (int) (write_pos - file_size));
|
||||
@@ -135,7 +135,7 @@ void storagedrive_c::file_write(uint8_t *buffer, uint64_t position, unsigned len
|
||||
assert(fillbuff);
|
||||
memset(fillbuff, 0, max_chunk_size);
|
||||
}
|
||||
f.clear() ; // clear fail bit
|
||||
f.clear(); // clear fail bit
|
||||
f.seekp(file_size, ios::beg); // move to end
|
||||
f.write((const char *) fillbuff, chunk_size);
|
||||
file_size += chunk_size;
|
||||
@@ -148,7 +148,7 @@ void storagedrive_c::file_write(uint8_t *buffer, uint64_t position, unsigned len
|
||||
assert(write_pos == 0);
|
||||
else {
|
||||
// move write pointer to target position
|
||||
f.clear() ; // clear fail bit
|
||||
f.clear(); // clear fail bit
|
||||
f.seekp(write_pos, ios::beg);
|
||||
p = f.tellp(); // position now < target?
|
||||
assert(p == write_pos);
|
||||
|
||||
@@ -67,47 +67,29 @@ using namespace std;
|
||||
#include "iopageregister.h"
|
||||
#include "unibusadapter.hpp"
|
||||
|
||||
dma_request_c::dma_request_c(
|
||||
uint8_t unibus_control,
|
||||
uint32_t unibus_addr,
|
||||
uint16_t* buffer,
|
||||
uint32_t wordcount) :
|
||||
_unibus_control(unibus_control),
|
||||
_unibus_start_addr(unibus_addr),
|
||||
_unibus_end_addr(0),
|
||||
_buffer(buffer),
|
||||
_wordcount(wordcount),
|
||||
_isComplete(false),
|
||||
_success(false)
|
||||
{
|
||||
dma_request_c::dma_request_c(uint8_t unibus_control, uint32_t unibus_addr, uint16_t* buffer,
|
||||
uint32_t wordcount) :
|
||||
_unibus_control(unibus_control), _unibus_start_addr(unibus_addr), _unibus_end_addr(0), _buffer(
|
||||
buffer), _wordcount(wordcount), _isComplete(false), _success(false) {
|
||||
|
||||
}
|
||||
|
||||
dma_request_c::~dma_request_c()
|
||||
{
|
||||
dma_request_c::~dma_request_c() {
|
||||
|
||||
}
|
||||
|
||||
irq_request_c::irq_request_c(
|
||||
unsigned level,
|
||||
unsigned vector) :
|
||||
_level(level),
|
||||
_vector(vector),
|
||||
_isComplete(false)
|
||||
{
|
||||
irq_request_c::irq_request_c(unsigned level, unsigned vector) :
|
||||
_level(level), _vector(vector), _isComplete(false) {
|
||||
|
||||
}
|
||||
|
||||
irq_request_c::~irq_request_c()
|
||||
{
|
||||
irq_request_c::~irq_request_c() {
|
||||
|
||||
}
|
||||
|
||||
void* bus_worker(
|
||||
void *context)
|
||||
{
|
||||
void* bus_worker(void *context) {
|
||||
unibusadapter_c* bus = reinterpret_cast<unibusadapter_c*>(context);
|
||||
bus->dma_worker();
|
||||
bus->dma_worker();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -115,11 +97,8 @@ unibusadapter_c *unibusadapter; // another Singleton
|
||||
// is registered in device_c.list<devices> ... order of static constructor calls ???
|
||||
|
||||
unibusadapter_c::unibusadapter_c() :
|
||||
device_c(),
|
||||
_busWakeup_cond(PTHREAD_COND_INITIALIZER),
|
||||
_requestFinished_cond(PTHREAD_COND_INITIALIZER),
|
||||
_busWorker_mutex(PTHREAD_MUTEX_INITIALIZER)
|
||||
{
|
||||
device_c(), _busWakeup_cond(PTHREAD_COND_INITIALIZER), _requestFinished_cond(
|
||||
PTHREAD_COND_INITIALIZER), _busWorker_mutex(PTHREAD_MUTEX_INITIALIZER) {
|
||||
unsigned i;
|
||||
log_label = "UNAPT";
|
||||
|
||||
@@ -137,26 +116,20 @@ unibusadapter_c::unibusadapter_c() :
|
||||
pthread_attr_t attribs;
|
||||
pthread_attr_init(&attribs);
|
||||
|
||||
int status = pthread_create(
|
||||
&_busWorker_pthread,
|
||||
&attribs,
|
||||
&bus_worker,
|
||||
reinterpret_cast<void*>(this));
|
||||
int status = pthread_create(&_busWorker_pthread, &attribs, &bus_worker,
|
||||
reinterpret_cast<void*>(this));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
if (status != 0) {
|
||||
FATAL("Failed to start unibus worker thread. Status 0x%x", status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool unibusadapter_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param);
|
||||
return true ;
|
||||
// no own parameter or "enable" logic
|
||||
return device_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
void unibusadapter_c::on_power_changed(void)
|
||||
{
|
||||
void unibusadapter_c::on_power_changed(void) {
|
||||
|
||||
}
|
||||
|
||||
@@ -175,9 +148,8 @@ void unibusadapter_c::worker_init_event() {
|
||||
device->on_init_changed();
|
||||
}
|
||||
|
||||
|
||||
// Clear bus request queues
|
||||
rundown_bus_requests();
|
||||
// Clear bus request queues
|
||||
rundown_bus_requests();
|
||||
}
|
||||
|
||||
void unibusadapter_c::worker_power_event() {
|
||||
@@ -410,7 +382,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) {
|
||||
for (i = 0; i < device.register_count; i++) {
|
||||
unibusdevice_register_t *device_reg = &(device.registers[i]);
|
||||
device_reg->addr = device.base_addr.value + 2 * i;
|
||||
if ( IOPAGE_REGISTER_ENTRY(*deviceregisters,device_reg->addr) != 0 )
|
||||
if ( IOPAGE_REGISTER_ENTRY(*deviceregisters,device_reg->addr)!= 0 )
|
||||
FATAL("IO page address conflict: %s implements register at %06o, belongs already to other device.",
|
||||
device.name.value.c_str(), device_reg->addr);
|
||||
}
|
||||
@@ -544,27 +516,19 @@ bool unibusadapter_c::request_INTR_active(const char *error_info) {
|
||||
// unibus_control = UNIBUS_CONTROL_DATI or _DATO
|
||||
// unibus_end_addr = last accessed address (success or timeout) and timeout condition
|
||||
// result: false on UNIBUS timeout
|
||||
bool unibusadapter_c::request_client_DMA(
|
||||
uint8_t unibus_control,
|
||||
uint32_t unibus_addr,
|
||||
uint16_t *buffer,
|
||||
uint32_t wordcount,
|
||||
uint32_t *unibus_end_addr) {
|
||||
bool unibusadapter_c::request_client_DMA(uint8_t unibus_control, uint32_t unibus_addr,
|
||||
uint16_t *buffer, uint32_t wordcount, uint32_t *unibus_end_addr) {
|
||||
|
||||
//
|
||||
// Acquire bus mutex; append new request to queue.
|
||||
// bus worker will wake and service the request in due time.
|
||||
//
|
||||
dma_request_c request(
|
||||
unibus_control,
|
||||
unibus_addr,
|
||||
buffer,
|
||||
wordcount);
|
||||
dma_request_c request(unibus_control, unibus_addr, buffer, wordcount);
|
||||
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_dmaRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_dmaRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
DEBUG("DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr,
|
||||
wordcount);
|
||||
@@ -573,59 +537,48 @@ bool unibusadapter_c::request_client_DMA(
|
||||
// Wait for request to finish.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!request.IsComplete())
|
||||
{
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
while (!request.IsComplete()) {
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
if (unibus_end_addr)
|
||||
*unibus_end_addr = request.GetUnibusEndAddr() ;
|
||||
*unibus_end_addr = request.GetUnibusEndAddr();
|
||||
|
||||
return request.GetSuccess() ;
|
||||
return request.GetSuccess();
|
||||
}
|
||||
|
||||
void unibusadapter_c::dma_worker()
|
||||
{
|
||||
void unibusadapter_c::dma_worker() {
|
||||
//worker_init_realtime_priority(rt_device);
|
||||
while(true)
|
||||
{
|
||||
while (true) {
|
||||
dma_request_c* dmaReq = nullptr;
|
||||
irq_request_c* irqReq = nullptr;
|
||||
|
||||
irq_request_c* irqReq = nullptr;
|
||||
|
||||
//
|
||||
// Wait for the next request.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while(_dmaRequests.empty() && _irqRequests.empty())
|
||||
{
|
||||
pthread_cond_wait(
|
||||
&_busWakeup_cond,
|
||||
&_busWorker_mutex);
|
||||
while (_dmaRequests.empty() && _irqRequests.empty()) {
|
||||
pthread_cond_wait(&_busWakeup_cond, &_busWorker_mutex);
|
||||
}
|
||||
|
||||
//
|
||||
// We have a request: prioritize IRQ over DMA, dequeue from the requisite
|
||||
// queue and get to work.
|
||||
//
|
||||
if (!_irqRequests.empty())
|
||||
{
|
||||
if (!_irqRequests.empty()) {
|
||||
irqReq = _irqRequests.front();
|
||||
_irqRequests.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
dmaReq = _dmaRequests.front();
|
||||
} else {
|
||||
dmaReq = _dmaRequests.front();
|
||||
_dmaRequests.pop();
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
|
||||
// Sanity check: Should be no active DMA or interrupt requests on the PRU.
|
||||
assert (!request_DMA_active(nullptr) && !request_INTR_active(nullptr));
|
||||
assert(!request_DMA_active(nullptr) && !request_INTR_active(nullptr));
|
||||
|
||||
if (dmaReq)
|
||||
{
|
||||
if (dmaReq) {
|
||||
// We do the DMA transfer in chunks so we can handle arbitrary buffer sizes.
|
||||
// (the PRU mailbox has limited space available.)
|
||||
// Configure the DMA transfer.
|
||||
@@ -636,22 +589,17 @@ void unibusadapter_c::dma_worker()
|
||||
uint32_t unibusAddr = dmaReq->GetUnibusStartAddr();
|
||||
uint32_t bufferOffset = 0;
|
||||
|
||||
|
||||
while (wordCount > 0)
|
||||
{
|
||||
while (wordCount > 0) {
|
||||
uint32_t chunkSize = std::min(maxTransferSize, wordCount);
|
||||
|
||||
mailbox->dma.startaddr = unibusAddr + bufferOffset * 2;
|
||||
mailbox->dma.control = dmaReq->GetUnibusControl();
|
||||
mailbox->dma.wordcount = chunkSize;
|
||||
mailbox->dma.wordcount = chunkSize;
|
||||
|
||||
// Copy outgoing data into maibox DMA buffer
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATO)
|
||||
{
|
||||
memcpy(
|
||||
(void*)mailbox->dma.words,
|
||||
dmaReq->GetBuffer() + bufferOffset,
|
||||
2 * chunkSize);
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATO) {
|
||||
memcpy((void*) mailbox->dma.words, dmaReq->GetBuffer() + bufferOffset,
|
||||
2 * chunkSize);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -662,11 +610,10 @@ void unibusadapter_c::dma_worker()
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to do this?
|
||||
timeout_c timeout;
|
||||
int retries = 0;
|
||||
while (request_DMA_active(nullptr) && retries < 10000)
|
||||
{
|
||||
int retries = 0;
|
||||
while (request_DMA_active(nullptr) && retries < 10000) {
|
||||
timeout.wait_us(50);
|
||||
retries++;
|
||||
retries++;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -677,18 +624,14 @@ void unibusadapter_c::dma_worker()
|
||||
// Nothing to do in that case but give up.
|
||||
// And log the issue. Should get to the root of this..
|
||||
//
|
||||
if (retries == 10000)
|
||||
{
|
||||
if (retries == 10000) {
|
||||
ERROR("dma timeout");
|
||||
}
|
||||
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATI)
|
||||
{
|
||||
|
||||
if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATI) {
|
||||
// Copy data read from mailbox to user's buffer.
|
||||
memcpy(
|
||||
dmaReq->GetBuffer() + bufferOffset,
|
||||
(void *)mailbox->dma.words,
|
||||
2 * chunkSize);
|
||||
memcpy(dmaReq->GetBuffer() + bufferOffset, (void *) mailbox->dma.words,
|
||||
2 * chunkSize);
|
||||
}
|
||||
wordCount -= chunkSize;
|
||||
bufferOffset += chunkSize;
|
||||
@@ -698,8 +641,9 @@ void unibusadapter_c::dma_worker()
|
||||
dmaReq->SetSuccess(mailbox->dma.cur_status == DMA_STATE_READY);
|
||||
// no success: UnibusEndAddr is first failed address
|
||||
|
||||
assert(dmaReq->GetUnibusStartAddr() + dmaReq->GetWordCount() * 2 ==
|
||||
mailbox->dma.cur_addr + 2);
|
||||
assert(
|
||||
dmaReq->GetUnibusStartAddr() + dmaReq->GetWordCount() * 2
|
||||
== mailbox->dma.cur_addr + 2);
|
||||
|
||||
//
|
||||
// Signal that the request is complete.
|
||||
@@ -708,30 +652,28 @@ void unibusadapter_c::dma_worker()
|
||||
dmaReq->SetComplete();
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Handle interrupt request
|
||||
switch(irqReq->GetInterruptLevel())
|
||||
{
|
||||
case 4:
|
||||
switch (irqReq->GetInterruptLevel()) {
|
||||
case 4:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case 5:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 6:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
|
||||
case 7:
|
||||
mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7;
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR("Request_INTR(): Illegal priority %u, aborting", irqReq->GetInterruptLevel());
|
||||
|
||||
default:
|
||||
ERROR("Request_INTR(): Illegal priority %u, aborting",
|
||||
irqReq->GetInterruptLevel());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -747,68 +689,60 @@ void unibusadapter_c::dma_worker()
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to
|
||||
// Wait for the transfer to complete.
|
||||
// TODO: we're polling the mailbox; is there a more efficient way to
|
||||
// do this? (as w/dma)
|
||||
timeout_c timeout;
|
||||
while(request_INTR_active(nullptr))
|
||||
{
|
||||
timeout.wait_us(50);
|
||||
}
|
||||
}
|
||||
timeout_c timeout;
|
||||
while (request_INTR_active(nullptr)) {
|
||||
timeout.wait_us(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unibusadapter_c::rundown_bus_requests()
|
||||
{
|
||||
void unibusadapter_c::rundown_bus_requests() {
|
||||
//
|
||||
// Cancel all pending DMA and IRQ requests, freeing threads waiting
|
||||
// on completion.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!_dmaRequests.empty())
|
||||
{
|
||||
while (!_dmaRequests.empty()) {
|
||||
dma_request_c* dmaReq = _dmaRequests.front();
|
||||
dmaReq->SetSuccess(false);
|
||||
dmaReq->SetComplete();
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
_dmaRequests.pop();
|
||||
}
|
||||
while (!_irqRequests.empty())
|
||||
{
|
||||
while (!_irqRequests.empty()) {
|
||||
irq_request_c* irqReq = _irqRequests.front();
|
||||
irqReq->SetComplete();
|
||||
pthread_cond_signal(&_requestFinished_cond);
|
||||
_irqRequests.pop();
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void unibusadapter_c::request_INTR(uint32_t level, uint32_t vector) {
|
||||
//
|
||||
// Acquire bus mutex; append new request to queue.
|
||||
// bus worker will wake and service the request in due time.
|
||||
//
|
||||
irq_request_c request(
|
||||
level,
|
||||
vector);
|
||||
//
|
||||
// Acquire bus mutex; append new request to queue.
|
||||
// bus worker will wake and service the request in due time.
|
||||
//
|
||||
irq_request_c request(level, vector);
|
||||
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_irqRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
_irqRequests.push(&request);
|
||||
pthread_cond_signal(&_busWakeup_cond);
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
//
|
||||
// Wait for request to finish.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!request.IsComplete())
|
||||
{
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
//
|
||||
// Wait for request to finish.
|
||||
//
|
||||
pthread_mutex_lock(&_busWorker_mutex);
|
||||
while (!request.IsComplete()) {
|
||||
pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&_busWorker_mutex);
|
||||
|
||||
//
|
||||
// And we're done.
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
/* unibusadapter.hpp: connects multiple "unibusdevices" to the PRU UNIBUS interface
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
12-nov-2018 JH entered beta phase
|
||||
|
||||
*/
|
||||
#ifndef _UNIBUSADAPTER_HPP_
|
||||
@@ -33,63 +33,83 @@
|
||||
#include "iopageregister.h"
|
||||
#include "unibusdevice.hpp"
|
||||
|
||||
class dma_request_c
|
||||
{
|
||||
class dma_request_c {
|
||||
public:
|
||||
dma_request_c(
|
||||
uint8_t unibus_control,
|
||||
uint32_t unibus_addr,
|
||||
uint16_t *buffer,
|
||||
uint32_t wordcount);
|
||||
dma_request_c(uint8_t unibus_control, uint32_t unibus_addr, uint16_t *buffer,
|
||||
uint32_t wordcount);
|
||||
|
||||
~dma_request_c();
|
||||
~dma_request_c();
|
||||
|
||||
uint8_t GetUnibusControl() { return _unibus_control; }
|
||||
uint32_t GetUnibusStartAddr() { return _unibus_start_addr; }
|
||||
uint16_t* GetBuffer() { return _buffer; }
|
||||
uint32_t GetWordCount() { return _wordcount; }
|
||||
uint32_t GetUnibusEndAddr() { return _unibus_end_addr; }
|
||||
void SetUnibusEndAddr(uint32_t end) { _unibus_end_addr = end; }
|
||||
uint8_t GetUnibusControl() {
|
||||
return _unibus_control;
|
||||
}
|
||||
uint32_t GetUnibusStartAddr() {
|
||||
return _unibus_start_addr;
|
||||
}
|
||||
uint16_t* GetBuffer() {
|
||||
return _buffer;
|
||||
}
|
||||
uint32_t GetWordCount() {
|
||||
return _wordcount;
|
||||
}
|
||||
uint32_t GetUnibusEndAddr() {
|
||||
return _unibus_end_addr;
|
||||
}
|
||||
void SetUnibusEndAddr(uint32_t end) {
|
||||
_unibus_end_addr = end;
|
||||
}
|
||||
|
||||
bool IsComplete() { return _isComplete; }
|
||||
bool GetSuccess() { return _success; }
|
||||
bool IsComplete() {
|
||||
return _isComplete;
|
||||
}
|
||||
bool GetSuccess() {
|
||||
return _success;
|
||||
}
|
||||
|
||||
void SetComplete() { _isComplete = true; }
|
||||
void SetSuccess(bool success) { _success = success; }
|
||||
void SetComplete() {
|
||||
_isComplete = true;
|
||||
}
|
||||
void SetSuccess(bool success) {
|
||||
_success = success;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint8_t _unibus_control;
|
||||
uint32_t _unibus_start_addr;
|
||||
uint32_t _unibus_end_addr;
|
||||
uint16_t* _buffer;
|
||||
uint32_t _wordcount;
|
||||
uint8_t _unibus_control;
|
||||
uint32_t _unibus_start_addr;
|
||||
uint32_t _unibus_end_addr;
|
||||
uint16_t* _buffer;
|
||||
uint32_t _wordcount;
|
||||
|
||||
bool _isComplete;
|
||||
bool _success;
|
||||
bool _isComplete;
|
||||
bool _success;
|
||||
};
|
||||
|
||||
class irq_request_c
|
||||
{
|
||||
class irq_request_c {
|
||||
public:
|
||||
irq_request_c(
|
||||
uint32_t level,
|
||||
uint32_t vector);
|
||||
irq_request_c(uint32_t level, uint32_t vector);
|
||||
|
||||
~irq_request_c();
|
||||
~irq_request_c();
|
||||
|
||||
uint32_t GetInterruptLevel() { return _level; }
|
||||
uint32_t GetVector() { return _vector; }
|
||||
bool IsComplete() { return _isComplete; }
|
||||
uint32_t GetInterruptLevel() {
|
||||
return _level;
|
||||
}
|
||||
uint32_t GetVector() {
|
||||
return _vector;
|
||||
}
|
||||
bool IsComplete() {
|
||||
return _isComplete;
|
||||
}
|
||||
|
||||
void SetComplete() { _isComplete = true; }
|
||||
void SetComplete() {
|
||||
_isComplete = true;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t _level;
|
||||
uint32_t _vector;
|
||||
bool _isComplete;
|
||||
uint32_t _level;
|
||||
uint32_t _vector;
|
||||
bool _isComplete;
|
||||
};
|
||||
|
||||
|
||||
// is a device_c. need a thread (but no params)
|
||||
class unibusadapter_c: public device_c {
|
||||
@@ -97,45 +117,46 @@ class unibusadapter_c: public device_c {
|
||||
public:
|
||||
unibusadapter_c();
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
|
||||
// list of registered devices.
|
||||
// Defines GRANT priority:
|
||||
// Lower index = "nearer to CPU" = higher priority
|
||||
unibusdevice_c *devices[MAX_DEVICE_HANDLE + 1];
|
||||
|
||||
volatile bool line_INIT ; // current state of these UNIBUS signals
|
||||
volatile bool line_DCLO ;
|
||||
volatile bool line_INIT; // current state of these UNIBUS signals
|
||||
volatile bool line_DCLO;
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
void on_power_changed(void) override; // must implement
|
||||
void on_init_changed(void) override; // must implement
|
||||
|
||||
void worker_init_event(void) ;
|
||||
void worker_power_event(void) ;
|
||||
void worker_deviceregister_event(void) ;
|
||||
void worker_init_event(void);
|
||||
void worker_power_event(void);
|
||||
void worker_deviceregister_event(void);
|
||||
void worker(void) override; // background worker function
|
||||
void dma_worker(void); // background DMA worker
|
||||
|
||||
void dma_worker(void); // background DMA worker
|
||||
|
||||
bool register_device(unibusdevice_c& device);
|
||||
void unregister_device(unibusdevice_c& device);
|
||||
|
||||
bool request_DMA_active(const char *error_info) ;
|
||||
bool request_INTR_active(const char *error_info) ;
|
||||
bool request_DMA_active(const char *error_info);
|
||||
bool request_INTR_active(const char *error_info);
|
||||
|
||||
bool request_client_DMA(uint8_t unibus_control, uint32_t unibus_addr,
|
||||
uint16_t *buffer, uint32_t wordcount, uint32_t *unibus_end_addr);
|
||||
bool request_client_DMA(uint8_t unibus_control, uint32_t unibus_addr, uint16_t *buffer,
|
||||
uint32_t wordcount, uint32_t *unibus_end_addr);
|
||||
void request_INTR(uint32_t level, uint32_t vector);
|
||||
void rundown_bus_requests(void);
|
||||
void rundown_bus_requests(void);
|
||||
|
||||
void print_shared_register_map(void);
|
||||
|
||||
private:
|
||||
|
||||
std::queue<dma_request_c*> _dmaRequests;
|
||||
std::queue<irq_request_c*> _irqRequests;
|
||||
pthread_t _busWorker_pthread;
|
||||
pthread_cond_t _busWakeup_cond;
|
||||
pthread_cond_t _requestFinished_cond;
|
||||
pthread_mutex_t _busWorker_mutex;
|
||||
|
||||
std::queue<dma_request_c*> _dmaRequests;
|
||||
std::queue<irq_request_c*> _irqRequests;
|
||||
pthread_t _busWorker_pthread;
|
||||
pthread_cond_t _busWakeup_cond;
|
||||
pthread_cond_t _requestFinished_cond;
|
||||
pthread_mutex_t _busWorker_mutex;
|
||||
};
|
||||
|
||||
extern unibusadapter_c *unibusadapter; // another Singleton
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
/* unibusdevice.cpp: abstract device with interface to unibusadapter
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
12-nov-2018 JH entered beta phase
|
||||
|
||||
abstract unibus device
|
||||
maybe mass storage controller or other device implementing
|
||||
UNIBUS IOpage registers.
|
||||
sets device register values depending on internal status,
|
||||
reacts on register read/write over UNIBUS by evaluation of PRU events.
|
||||
*/
|
||||
abstract unibus device
|
||||
maybe mass storage controller or other device implementing
|
||||
UNIBUS IOpage registers.
|
||||
sets device register values depending on internal status,
|
||||
reacts on register read/write over UNIBUS by evaluation of PRU events.
|
||||
*/
|
||||
//#include <string>
|
||||
//using namespace std;
|
||||
#include "logger.hpp"
|
||||
@@ -39,19 +39,51 @@ unibusdevice_c::unibusdevice_c() :
|
||||
device_c() {
|
||||
handle = 0;
|
||||
register_count = 0;
|
||||
// device is not yet enabled, UNIBUS properties can be set
|
||||
base_addr.readonly = false;
|
||||
intr_vector.readonly = false;
|
||||
intr_level.readonly = false;
|
||||
default_base_addr = 0;
|
||||
default_intr_vector = 0;
|
||||
default_intr_level = 0;
|
||||
|
||||
log_channelmask = 0; // no logging until set
|
||||
}
|
||||
|
||||
unibusdevice_c::~unibusdevice_c() {
|
||||
}
|
||||
|
||||
void unibusdevice_c::install(uint32_t base_addr, unsigned intr_vector, uint8_t intr_level) {
|
||||
this->base_addr.value = base_addr;
|
||||
this->intr_vector.value = intr_vector;
|
||||
this->intr_level.value = intr_level;
|
||||
// implements params, so must handle "change"
|
||||
bool unibusdevice_c::on_param_changed(parameter_c *param) {
|
||||
if (param == &enabled) {
|
||||
// plug/unplug device into UNIBUS:
|
||||
if (enabled.new_value) {
|
||||
// enable: lock UNIBUS config
|
||||
base_addr.readonly = true;
|
||||
intr_vector.readonly = true;
|
||||
intr_level.readonly = true;
|
||||
install(); // visible on UNIBUS
|
||||
} else {
|
||||
// disable
|
||||
uninstall();
|
||||
base_addr.readonly = false;
|
||||
intr_vector.readonly = false;
|
||||
intr_level.readonly = false;
|
||||
}
|
||||
}
|
||||
return device_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
// define default values for device BASE address and INTR
|
||||
void unibusdevice_c::set_default_bus_params(uint32_t default_base_addr, unsigned default_intr_vector, unsigned default_intr_level) {
|
||||
this->default_base_addr = this->base_addr.value = default_base_addr;
|
||||
this->default_intr_vector = this->intr_vector.value = default_intr_vector ;
|
||||
this->default_intr_level = this->intr_level.value = default_intr_level ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void unibusdevice_c::install(void) {
|
||||
unibusadapter->register_device(*this); // -> device_c ?
|
||||
// now has handle
|
||||
|
||||
@@ -62,10 +94,6 @@ void unibusdevice_c::install(uint32_t base_addr, unsigned intr_vector, uint8_t i
|
||||
on_power_changed();
|
||||
}
|
||||
|
||||
void unibusdevice_c::install(void) {
|
||||
install(default_base_addr, default_intr_vector, default_intr_level);
|
||||
}
|
||||
|
||||
void unibusdevice_c::uninstall(void) {
|
||||
unibusadapter->unregister_device(*this);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
/* unibusdevice.hpp: abstract device with interface to unibusadapter
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
|
||||
#ifndef _UNIBUSDEVICE_HPP_
|
||||
#define _UNIBUSDEVICE_HPP_
|
||||
@@ -38,7 +38,7 @@ class unibusdevice_c;
|
||||
typedef struct {
|
||||
// backlink
|
||||
unibusdevice_c *device;
|
||||
char name[40] ; // for display
|
||||
char name[40]; // for display
|
||||
unsigned index; // # of register in device register list
|
||||
uint32_t addr; // unibus address
|
||||
// so addr = device_base_addr + 2 * index
|
||||
@@ -77,26 +77,33 @@ typedef struct {
|
||||
} unibusdevice_register_t;
|
||||
|
||||
class unibusdevice_c: public device_c {
|
||||
private:
|
||||
// setup address tables, also in shared memory
|
||||
// start both threads
|
||||
void install(void);
|
||||
|
||||
void uninstall(void);
|
||||
bool is_installed() {
|
||||
return (handle > 0);
|
||||
}
|
||||
|
||||
public:
|
||||
uint8_t handle; // assigned by "unibus.adapter.register
|
||||
|
||||
// 0 = not "Plugged" in to UNIBUS
|
||||
parameter_unsigned_c base_addr = parameter_unsigned_c(this,
|
||||
"base_addr", "addr", true, "", "%06o",
|
||||
"controller base address in IO page", 18, 8);
|
||||
parameter_unsigned_c intr_vector = parameter_unsigned_c(this,
|
||||
"intr_vector", "iv", true, "", "%03o",
|
||||
"interrupt vector address", 9, 8);
|
||||
parameter_unsigned_c intr_level = parameter_unsigned_c(this,
|
||||
"intr_level", "il", true, "", "%o",
|
||||
"interrupt bus request level", 3, 8);
|
||||
|
||||
parameter_unsigned_c base_addr = parameter_unsigned_c(this, "base_addr", "addr", true, "",
|
||||
"%06o", "controller base address in IO page", 18, 8);
|
||||
parameter_unsigned_c intr_vector = parameter_unsigned_c(this, "intr_vector", "iv", true, "",
|
||||
"%03o", "interrupt vector address", 9, 8);
|
||||
parameter_unsigned_c intr_level = parameter_unsigned_c(this, "intr_level", "il", true, "",
|
||||
"%o", "interrupt bus request level", 3, 8);
|
||||
|
||||
// DEC defaults as defined by device type
|
||||
uint32_t default_base_addr;
|
||||
unsigned default_intr_vector;
|
||||
unsigned default_intr_level;
|
||||
void set_default_bus_params(uint32_t default_base_addr, unsigned default_intr_vector, unsigned default_intr_level) ;
|
||||
|
||||
|
||||
// controller register data as pointer to registers in shared PRU RAM
|
||||
// UNIBUS addr of register[i] = base_addr + 2*i
|
||||
@@ -104,35 +111,28 @@ public:
|
||||
unsigned register_count;
|
||||
unibusdevice_register_t registers[MAX_REGISTERS_PER_DEVICE];
|
||||
|
||||
unsigned log_channelmask ; // channelmask for DEBUG logging
|
||||
unsigned log_channelmask; // channelmask for DEBUG logging
|
||||
// device is the log channel. one of logger::LC_*
|
||||
|
||||
unibusdevice_c();
|
||||
virtual ~unibusdevice_c(); // class with virtual functions should have virtual destructors
|
||||
|
||||
// setup address tables, also in shared memory
|
||||
// start both threads
|
||||
void install(uint32_t base_addr, unsigned intr_vector, uint8_t intr_level);
|
||||
void install(void); // defaults
|
||||
|
||||
void uninstall(void);
|
||||
bool is_installed() {
|
||||
return (handle > 0);
|
||||
}
|
||||
bool on_param_changed(parameter_c *param) override;
|
||||
|
||||
// reset device
|
||||
// virtual void init() override ;
|
||||
|
||||
// access the value of a register in shared UNIBUS PRU space
|
||||
void set_register_dati_value(unibusdevice_register_t *device_reg, uint16_t value, const char *debug_info);
|
||||
void set_register_dati_value(unibusdevice_register_t *device_reg, uint16_t value,
|
||||
const char *debug_info);
|
||||
uint16_t get_register_dato_value(unibusdevice_register_t *device_reg);
|
||||
void reset_unibus_registers();
|
||||
|
||||
unibusdevice_register_t *register_by_name(string name) ;
|
||||
unibusdevice_register_t *register_by_unibus_address(uint32_t addr) ;
|
||||
unibusdevice_register_t *register_by_name(string name);
|
||||
unibusdevice_register_t *register_by_unibus_address(uint32_t addr);
|
||||
|
||||
// set an UNIBUS interrupt condition with intr_vector and intr_level
|
||||
void interrupt(void) ;
|
||||
void interrupt(void);
|
||||
|
||||
// callback to be called on controller register DATI/DATO events.
|
||||
// must ACK mailbox.event.signal. Asynchron!
|
||||
@@ -146,8 +146,7 @@ public:
|
||||
pthread_cond_t on_after_register_access_cond = PTHREAD_COND_INITIALIZER;
|
||||
pthread_mutex_t on_after_register_access_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
void log_register_event(const char *change_info, unibusdevice_register_t *changed_reg) ;
|
||||
void log_register_event(const char *change_info, unibusdevice_register_t *changed_reg);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -52,24 +52,36 @@ cpu_c::cpu_c() :
|
||||
init.value = false;
|
||||
|
||||
// current CPU does not publish registers to the bus
|
||||
// must be unibusdevice_c then!
|
||||
register_count = 0;
|
||||
|
||||
memset(&bus, 0, sizeof(bus)) ;
|
||||
memset(&ka11, 0, sizeof(ka11)) ;
|
||||
ka11.bus = &bus ;
|
||||
memset(&bus, 0, sizeof(bus));
|
||||
memset(&ka11, 0, sizeof(ka11));
|
||||
ka11.bus = &bus;
|
||||
}
|
||||
|
||||
cpu_c::~cpu_c() {
|
||||
|
||||
}
|
||||
|
||||
bool cpu_c::on_param_changed(parameter_c *param) {
|
||||
if (param == &enabled) {
|
||||
if (!enabled.new_value) {
|
||||
// HALT disabled CPU
|
||||
runmode.value = false;
|
||||
init.value = false;
|
||||
}
|
||||
}
|
||||
return device_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// functions to be used by Angelos CPU emulator
|
||||
// Result: 1 = OK, 0 = bus timeout
|
||||
int cpu_dato(unsigned addr, unsigned data) {
|
||||
bool timeout;
|
||||
mailbox->dma.words[0] = data;
|
||||
timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER , UNIBUS_CONTROL_DATO, addr, 1);
|
||||
timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATO, addr, 1);
|
||||
return !timeout;
|
||||
|
||||
}
|
||||
@@ -80,7 +92,6 @@ int cpu_datob(unsigned addr, unsigned data) {
|
||||
mailbox->dma.words[0] = data;
|
||||
timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATOB, addr, 1);
|
||||
return !timeout;
|
||||
|
||||
}
|
||||
|
||||
int cpu_dati(unsigned addr, unsigned *data) {
|
||||
@@ -88,12 +99,12 @@ int cpu_dati(unsigned addr, unsigned *data) {
|
||||
|
||||
timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATI, addr, 1);
|
||||
*data = mailbox->dma.words[0];
|
||||
// printf("DATI; ba=%o, data=%o\n", addr, *data) ;
|
||||
return !timeout;
|
||||
}
|
||||
}
|
||||
|
||||
// background worker.
|
||||
// udpate LEDS, poll switches direct to register flipflops
|
||||
void cpu_c::worker(void) {
|
||||
timeout_c timeout;
|
||||
bool nxm;
|
||||
@@ -115,11 +126,10 @@ void cpu_c::worker(void) {
|
||||
|
||||
if (init.value) {
|
||||
// user wants CPU reset
|
||||
reset(&ka11) ;
|
||||
init.value = 0 ;
|
||||
reset(&ka11);
|
||||
init.value = 0;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
if (runmode.value) {
|
||||
// simulate a fetch
|
||||
@@ -155,11 +165,6 @@ void cpu_c::on_after_register_access(unibusdevice_register_t *device_reg,
|
||||
UNUSED(unibus_control);
|
||||
}
|
||||
|
||||
bool cpu_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
void cpu_c::on_power_changed(void) {
|
||||
if (power_down) { // power-on defaults
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
23-nov-2018 JH created
|
||||
23-nov-2018 JH created
|
||||
*/
|
||||
#ifndef _CPU_HPP_
|
||||
#define _CPU_HPP_
|
||||
@@ -44,17 +44,17 @@ private:
|
||||
public:
|
||||
|
||||
cpu_c();
|
||||
~cpu_c() ;
|
||||
~cpu_c();
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
|
||||
parameter_bool_c runmode = parameter_bool_c(this, "run", "r",/*readonly*/
|
||||
false, "1 = CPU running, 0 = halt");
|
||||
parameter_bool_c init = parameter_bool_c(this, "init", "i",/*readonly*/
|
||||
false, "1 = CPU initalizing");
|
||||
|
||||
|
||||
struct Bus bus ; // UNIBU Sinterface of CPU
|
||||
struct KA11 ka11 ; // Angelos CPU state
|
||||
|
||||
struct Bus bus; // UNIBU Sinterface of CPU
|
||||
struct KA11 ka11; // Angelos CPU state
|
||||
|
||||
// background worker function
|
||||
void worker(void) override;
|
||||
@@ -63,7 +63,6 @@ public:
|
||||
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
|
||||
override;
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
void on_power_changed(void) override;
|
||||
void on_init_changed(void) override;
|
||||
};
|
||||
|
||||
@@ -355,7 +355,6 @@ step(KA11 *cpu)
|
||||
|
||||
INA(PC, cpu->ir);
|
||||
PC += 2; /* don't increment on bus error! */
|
||||
|
||||
by = !!(cpu->ir&B15);
|
||||
br = sxt(cpu->ir)<<1;
|
||||
src = cpu->ir>>6 & 077;
|
||||
|
||||
@@ -45,15 +45,14 @@ demo_io_c::demo_io_c() :
|
||||
name.value = "DEMO_IO";
|
||||
type_name.value = "demo_io_c";
|
||||
log_label = "di";
|
||||
default_base_addr = 0760100; // overwritten in install()?
|
||||
default_intr_vector = 0;
|
||||
default_intr_level = 0;
|
||||
|
||||
set_default_bus_params(0760100, 0, 0) ; // base addr, intr-vector, intr level
|
||||
|
||||
// init parameters
|
||||
switch_feedback.value = false ;
|
||||
switch_feedback.value = false;
|
||||
|
||||
// controller has only 2 register
|
||||
register_count = 2 ;
|
||||
register_count = 2;
|
||||
|
||||
switch_reg = &(this->registers[0]); // @ base addr
|
||||
strcpy(switch_reg->name, "SR"); // "Switches and Display"
|
||||
@@ -95,13 +94,17 @@ demo_io_c::demo_io_c() :
|
||||
|
||||
demo_io_c::~demo_io_c() {
|
||||
// close all gpio value files
|
||||
unsigned i ;
|
||||
for (i=0 ; i < 5 ; i++)
|
||||
gpio_inputs[i].close() ;
|
||||
for (i=0 ; i < 4 ; i++)
|
||||
gpio_outputs[i].close() ;
|
||||
unsigned i;
|
||||
for (i = 0; i < 5; i++)
|
||||
gpio_inputs[i].close();
|
||||
for (i = 0; i < 4; i++)
|
||||
gpio_outputs[i].close();
|
||||
}
|
||||
|
||||
bool demo_io_c::on_param_changed(parameter_c *param) {
|
||||
// no own parameter or "enable" logic
|
||||
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
/* helper: opens the control file for a gpio
|
||||
* exports, programs directions, assigns stream
|
||||
@@ -113,7 +116,7 @@ void demo_io_c::gpio_open(fstream& value_stream, bool is_input, unsigned gpio_nu
|
||||
|
||||
// 1. export pin, so it appears as .../gpio<nr>
|
||||
char export_filename[80];
|
||||
ofstream export_file ;
|
||||
ofstream export_file;
|
||||
sprintf(export_filename, "%s/export", gpio_class_path);
|
||||
export_file.open(export_filename);
|
||||
|
||||
@@ -127,7 +130,7 @@ void demo_io_c::gpio_open(fstream& value_stream, bool is_input, unsigned gpio_nu
|
||||
// 2. Now we have directory /sys/class/gpio<number>
|
||||
// Set to input or output
|
||||
char direction_filename[80];
|
||||
ofstream direction_file ;
|
||||
ofstream direction_file;
|
||||
sprintf(direction_filename, "%s/gpio%d/direction", gpio_class_path, gpio_number);
|
||||
direction_file.open(direction_filename);
|
||||
if (!direction_file.is_open()) {
|
||||
@@ -201,7 +204,7 @@ void demo_io_c::worker(void) {
|
||||
// into /sys/class/gpio<n>/value pseudo files
|
||||
|
||||
// LED control from switches or UNIBUS "DR" register?
|
||||
if (! switch_feedback.value)
|
||||
if (!switch_feedback.value)
|
||||
register_value = get_register_dato_value(display_reg);
|
||||
for (i = 0; i < 4; i++) {
|
||||
register_bitmask = (1 << i);
|
||||
@@ -224,11 +227,6 @@ void demo_io_c::on_after_register_access(unibusdevice_register_t *device_reg,
|
||||
UNUSED(unibus_control);
|
||||
}
|
||||
|
||||
bool demo_io_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
void demo_io_c::on_power_changed(void) {
|
||||
if (power_down) { // power-on defaults
|
||||
}
|
||||
|
||||
@@ -50,12 +50,13 @@ private:
|
||||
public:
|
||||
|
||||
demo_io_c();
|
||||
~demo_io_c() ;
|
||||
~demo_io_c();
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
|
||||
parameter_bool_c switch_feedback = parameter_bool_c(this, "switch_feedback", "sf",/*readonly*/
|
||||
false, "1 = hard wire Switches to LEDs, PDP-11 can not set LEDs");
|
||||
|
||||
|
||||
// background worker function
|
||||
void worker(void) override;
|
||||
|
||||
@@ -63,7 +64,6 @@ public:
|
||||
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
|
||||
override;
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
void on_power_changed(void) override;
|
||||
void on_init_changed(void) override;
|
||||
};
|
||||
|
||||
@@ -54,9 +54,9 @@ demo_regs_c::demo_regs_c() :
|
||||
name.value = "DEMO_REGS";
|
||||
type_name.value = "demo_regs_c";
|
||||
log_label = "dr";
|
||||
default_base_addr = 0760000; // overwritten in install()
|
||||
default_intr_vector = 0;
|
||||
default_intr_level = 0;
|
||||
|
||||
|
||||
set_default_bus_params(0760000, 0, 0) ; // base addr, intr-vector, intr level
|
||||
|
||||
register_count = 32; // up to 760076
|
||||
// registers are "active": receive "on_after_register_access"
|
||||
@@ -73,6 +73,11 @@ demo_regs_c::demo_regs_c() :
|
||||
|
||||
}
|
||||
|
||||
bool demo_regs_c::on_param_changed(parameter_c *param) {
|
||||
// no own parameter or "enable" logic
|
||||
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
// background worker.
|
||||
// Just print a heart beat
|
||||
void demo_regs_c::worker(void) {
|
||||
@@ -107,11 +112,6 @@ void demo_regs_c::on_after_register_access(unibusdevice_register_t *device_reg,
|
||||
// device_reg->addr, unibus_c::control2text(unibus_control));
|
||||
}
|
||||
|
||||
bool demo_regs_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
void demo_regs_c::on_power_changed(void) {
|
||||
if (power_down) { // power-on defaults
|
||||
}
|
||||
|
||||
@@ -35,12 +35,13 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
parameter_unsigned_c access_count = parameter_unsigned_c(this, "access_count",
|
||||
"ac",/*readonly*/
|
||||
true, "", "%u", "Total # of register accesses", 32, 10);
|
||||
parameter_unsigned_c access_count = parameter_unsigned_c(this, "access_count", "ac",/*readonly*/
|
||||
true, "", "%u", "Total # of register accesses", 32, 10);
|
||||
|
||||
demo_regs_c();
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
|
||||
// background worker function
|
||||
void worker(void) override;
|
||||
|
||||
@@ -48,7 +49,6 @@ public:
|
||||
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
|
||||
override;
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
void on_power_changed(void) override;
|
||||
void on_init_changed(void) override;
|
||||
};
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
/*
|
||||
mscp_drive.cpp: Implementation of MSCP disks.
|
||||
mscp_drive.cpp: Implementation of MSCP disks.
|
||||
|
||||
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
|
||||
Contributed under the BSD 2-clause license.
|
||||
|
||||
This provides the logic for reads and writes to the data and RCT space
|
||||
for a given drive, as well as configuration for different standard DEC
|
||||
drive types.
|
||||
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
|
||||
Contributed under the BSD 2-clause license.
|
||||
|
||||
Disk data is backed by an image file on disk. RCT data exists only in
|
||||
memory and is not saved -- it is provided to satisfy software that
|
||||
expects the RCT area to exist. Since no bad sectors will ever actually
|
||||
exist, the RCT area has no real purpose, so it is ephemeral in this
|
||||
implementation.
|
||||
*/
|
||||
This provides the logic for reads and writes to the data and RCT space
|
||||
for a given drive, as well as configuration for different standard DEC
|
||||
drive types.
|
||||
|
||||
Disk data is backed by an image file on disk. RCT data exists only in
|
||||
memory and is not saved -- it is provided to satisfy software that
|
||||
expects the RCT area to exist. Since no bad sectors will ever actually
|
||||
exist, the RCT area has no real purpose, so it is ephemeral in this
|
||||
implementation.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <memory>
|
||||
@@ -25,26 +25,43 @@ 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)
|
||||
{
|
||||
log_label = "MSCPD";
|
||||
SetDriveType("RA81");
|
||||
SetOffline();
|
||||
mscp_drive_c::mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber) :
|
||||
storagedrive_c(controller), _useImageSize(false) {
|
||||
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;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -52,12 +69,11 @@ mscp_drive_c::~mscp_drive_c()
|
||||
// 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;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -65,65 +81,56 @@ 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;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -131,9 +138,8 @@ 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;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -141,9 +147,8 @@ 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;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -151,9 +156,8 @@ 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();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -161,51 +165,41 @@ 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);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -213,20 +207,14 @@ void mscp_drive_c::Write(
|
||||
// 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;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -234,16 +222,11 @@ uint8_t* mscp_drive_c::Read(
|
||||
// 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());
|
||||
}
|
||||
|
||||
//
|
||||
@@ -251,84 +234,42 @@ void mscp_drive_c::WriteRCTBlock(
|
||||
// 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());
|
||||
|
||||
return buffer;
|
||||
}
|
||||
memcpy(reinterpret_cast<void *>(buffer),
|
||||
reinterpret_cast<void *>(_rctData.get() + rctBlockNumber * GetBlockSize()),
|
||||
GetBlockSize());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//
|
||||
// on_param_changed():
|
||||
// Handles configuration parameter changes.
|
||||
//
|
||||
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;
|
||||
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 false;
|
||||
}
|
||||
|
||||
//
|
||||
// SetDriveType():
|
||||
// Updates this drive's type to the specified type (i.e.
|
||||
@@ -337,57 +278,50 @@ bool mscp_drive_c::on_param_changed(
|
||||
// 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.
|
||||
//
|
||||
void mscp_drive_c::worker(void)
|
||||
{
|
||||
// Nothing to do here at the moment.
|
||||
void mscp_drive_c::worker(void) {
|
||||
// Nothing to do here at the moment. Thread will terminate immediately.
|
||||
}
|
||||
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
mscp_drive.hpp: Implementation of MSCP drive, used with MSCP controller.
|
||||
mscp_drive.hpp: Implementation of MSCP drive, used with MSCP controller.
|
||||
|
||||
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
|
||||
Contributed under the BSD 2-clause license.
|
||||
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
|
||||
Contributed under the BSD 2-clause license.
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -16,114 +16,92 @@
|
||||
//
|
||||
// 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);
|
||||
mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber);
|
||||
~mscp_drive_c(void);
|
||||
|
||||
uint32_t GetBlockSize(void);
|
||||
uint32_t GetBlockCount(void);
|
||||
uint32_t GetRCTBlockCount(void);
|
||||
uint32_t GetMediaID(void);
|
||||
uint32_t GetDeviceNumber(void);
|
||||
uint16_t GetClassModel(void);
|
||||
uint16_t GetRCTSize(void);
|
||||
uint8_t GetRBNs(void);
|
||||
uint8_t GetRCTCopies(void);
|
||||
bool on_param_changed(parameter_c *param) override;
|
||||
|
||||
void SetOnline(void);
|
||||
void SetOffline(void);
|
||||
bool IsOnline(void);
|
||||
bool IsAvailable(void);
|
||||
uint32_t GetBlockSize(void);
|
||||
uint32_t GetBlockCount(void);
|
||||
uint32_t GetRCTBlockCount(void);
|
||||
uint32_t GetMediaID(void);
|
||||
uint32_t GetDeviceNumber(void);
|
||||
uint16_t GetClassModel(void);
|
||||
uint16_t GetRCTSize(void);
|
||||
uint8_t GetRBNs(void);
|
||||
uint8_t GetRCTCopies(void);
|
||||
|
||||
void Write(
|
||||
uint32_t blockNumber,
|
||||
size_t lengthInBytes,
|
||||
uint8_t* buffer);
|
||||
void SetOnline(void);
|
||||
void SetOffline(void);bool IsOnline(void);bool IsAvailable(void);
|
||||
|
||||
uint8_t* Read(
|
||||
uint32_t blockNumber,
|
||||
size_t lengthInBytes);
|
||||
void Write(uint32_t blockNumber, size_t lengthInBytes, uint8_t* buffer);
|
||||
|
||||
void WriteRCTBlock(
|
||||
uint32_t rctBlockNumber,
|
||||
uint8_t* buffer);
|
||||
uint8_t* Read(uint32_t blockNumber, size_t lengthInBytes);
|
||||
|
||||
uint8_t* ReadRCTBlock(
|
||||
uint32_t rctBlockNumber);
|
||||
void WriteRCTBlock(uint32_t rctBlockNumber, uint8_t* buffer);
|
||||
|
||||
uint8_t* ReadRCTBlock(uint32_t rctBlockNumber);
|
||||
|
||||
public:
|
||||
bool on_param_changed(parameter_c *param) override;
|
||||
void on_power_changed(void) override;
|
||||
void on_init_changed(void) override;
|
||||
void on_power_changed(void) override;
|
||||
void on_init_changed(void) override;
|
||||
|
||||
void worker(void) override;
|
||||
void worker(void) override;
|
||||
|
||||
public:
|
||||
parameter_bool_c use_image_size = parameter_bool_c(
|
||||
this, "useimagesize", "uis", false, "Determine unit size from image file instead of drive type");
|
||||
parameter_bool_c use_image_size = parameter_bool_c(this, "useimagesize", "uis", false,
|
||||
"Determine unit size from image file instead of drive type");
|
||||
|
||||
private:
|
||||
|
||||
struct DriveInfo
|
||||
{
|
||||
char TypeName[16];
|
||||
size_t BlockCount;
|
||||
uint32_t MediaID;
|
||||
uint8_t Model;
|
||||
uint16_t RCTSize;
|
||||
bool Removable;
|
||||
bool ReadOnly;
|
||||
};
|
||||
|
||||
//
|
||||
// TODO: add a lot more drive types.
|
||||
// Does it make sense to support drive types not native to Unibus machines (SCSI types, for example?)
|
||||
// Need to add a ClassID table entry in that eventuality...
|
||||
// Also TODO: RCTSize parameters taken from SIMH rq source; how valid are these?
|
||||
DriveInfo g_driveTable[21]
|
||||
{
|
||||
struct DriveInfo {
|
||||
char TypeName[16];
|
||||
size_t BlockCount;
|
||||
uint32_t MediaID;
|
||||
uint8_t Model;
|
||||
uint16_t RCTSize;bool Removable;bool ReadOnly;
|
||||
};
|
||||
|
||||
//
|
||||
// TODO: add a lot more drive types.
|
||||
// Does it make sense to support drive types not native to Unibus machines (SCSI types, for example?)
|
||||
// Need to add a ClassID table entry in that eventuality...
|
||||
// Also TODO: RCTSize parameters taken from SIMH rq source; how valid are these?
|
||||
DriveInfo g_driveTable[21] {
|
||||
// Name Blocks MediaID Model RCTSize Removable ReadOnly
|
||||
{ "RX50", 800, 0x25658032, 7, 0, true, false },
|
||||
{ "RX33", 2400, 0x25658021, 10, 0, true, false },
|
||||
{ "RD51", 21600, 0x25644033, 6, 36, false, false },
|
||||
{ "RD31", 41560, 0x2564401f, 12, 3, false, false },
|
||||
{ "RC25", 50902, 0x20643019, 2, 0, true, false },
|
||||
{ "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 },
|
||||
{ "RA80", 237212, 0x20643019, 1, 0, false, false },
|
||||
{ "RD54", 311200, 0x25644036, 13, 7, false, false },
|
||||
{ "RA60", 400176, 0x22a4103c, 4, 1008, true, false },
|
||||
{ "RA70", 547041, 0x20643019, 18, 198, false, false },
|
||||
{ "RA81", 891072, 0x25641051, 5, 2856, false, false },
|
||||
{ "RA82", 1216665, 0x25641052, 11, 3420, false, false },
|
||||
{ "RA71", 1367310, 0x25641047, 40, 1428, false, false },
|
||||
{ "RA72", 1953300, 0x25641048, 37, 2040, false, false },
|
||||
{ "RA90", 2376153, 0x2564105a, 19, 1794, false, false },
|
||||
{ "RA92", 2940951, 0x2564105c, 29, 949, false, false },
|
||||
{ "RA73", 3920490, 0x25641049, 47, 198, false, false },
|
||||
{ "", 0, 0, 0, 0, false, false }
|
||||
};
|
||||
{ "RX50", 800, 0x25658032, 7, 0, true, false }, { "RX33", 2400, 0x25658021, 10, 0,
|
||||
true, false }, { "RD51", 21600, 0x25644033, 6, 36, false, false }, { "RD31",
|
||||
41560, 0x2564401f, 12, 3, false, false }, { "RC25", 50902, 0x20643019, 2, 0,
|
||||
true, false }, { "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 }, {
|
||||
"RA80", 237212, 0x20643019, 1, 0, false, false }, { "RD54", 311200,
|
||||
0x25644036, 13, 7, false, false }, { "RA60", 400176, 0x22a4103c, 4, 1008,
|
||||
true, false }, { "RA70", 547041, 0x20643019, 18, 198, false, false }, {
|
||||
"RA81", 891072, 0x25641051, 5, 2856, false, false }, { "RA82", 1216665,
|
||||
0x25641052, 11, 3420, false, false }, { "RA71", 1367310, 0x25641047, 40,
|
||||
1428, false, false },
|
||||
{ "RA72", 1953300, 0x25641048, 37, 2040, false, false }, { "RA90", 2376153,
|
||||
0x2564105a, 19, 1794, false, false }, { "RA92", 2940951, 0x2564105c, 29,
|
||||
949, false, false }, { "RA73", 3920490, 0x25641049, 47, 198, false, false },
|
||||
{ "", 0, 0, 0, 0, false, false } };
|
||||
|
||||
bool SetDriveType(const char* typeName);
|
||||
void UpdateCapacity(void);
|
||||
void UpdateMetadata(void);
|
||||
DriveInfo _driveInfo;
|
||||
bool _online;
|
||||
uint32_t _unitDeviceNumber;
|
||||
uint16_t _unitClassModel;
|
||||
bool _useImageSize;
|
||||
bool SetDriveType(const char* typeName);
|
||||
void UpdateCapacity(void);
|
||||
void UpdateMetadata(void);
|
||||
DriveInfo _driveInfo;bool _online;
|
||||
uint32_t _unitDeviceNumber;
|
||||
uint16_t _unitClassModel;bool _useImageSize;
|
||||
|
||||
//
|
||||
// RCT ("Replacement and Caching Table") data:
|
||||
// The size of this area varies depending on the drive. This is
|
||||
// provided only to appease software that expects the RCT to exist --
|
||||
// since there will never be any bad sectors in our disk images
|
||||
// there is no other purpose.
|
||||
// This data is not persisted to disk as it is unnecessary.
|
||||
//
|
||||
unique_ptr<uint8_t> _rctData;
|
||||
//
|
||||
// RCT ("Replacement and Caching Table") data:
|
||||
// The size of this area varies depending on the drive. This is
|
||||
// provided only to appease software that expects the RCT to exist --
|
||||
// since there will never be any bad sectors in our disk images
|
||||
// there is no other purpose.
|
||||
// This data is not persisted to disk as it is unnecessary.
|
||||
//
|
||||
unique_ptr<uint8_t> _rctData;
|
||||
};
|
||||
|
||||
@@ -71,9 +71,13 @@ mscp_server::mscp_server(
|
||||
{
|
||||
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
|
||||
|
||||
StartPollingThread();
|
||||
}
|
||||
|
||||
@@ -83,6 +87,18 @@ mscp_server::~mscp_server()
|
||||
AbortPollingThread();
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// StartPollingThread():
|
||||
// Initializes the MSCP polling thread and starts it running.
|
||||
|
||||
@@ -143,6 +143,7 @@ class mscp_server : public device_c
|
||||
public:
|
||||
mscp_server(uda_c *port);
|
||||
~mscp_server();
|
||||
bool on_param_changed(parameter_c *param) override ;
|
||||
|
||||
public:
|
||||
void Reset(void);
|
||||
@@ -153,7 +154,6 @@ public:
|
||||
void on_power_changed(void) override {}
|
||||
void on_init_changed(void) override {}
|
||||
void worker(void) override {}
|
||||
bool on_param_changed(parameter_c *param) override { UNUSED(param); return true; }
|
||||
|
||||
private:
|
||||
uint32_t Abort(void);
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
/* panels.cpp: a device to access lamps & buttons connected over I2C bus
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
12-nov-2018 JH entered beta phase
|
||||
|
||||
a device to access lamps & buttons connected over I2C bus
|
||||
Up to 8 MC23017 GPIO extenders each with 16 I/Os can be connected.
|
||||
a device to access lamps & buttons connected over I2C bus
|
||||
Up to 8 MC23017 GPIO extenders each with 16 I/Os can be connected.
|
||||
|
||||
Other devices register some of their bit-Parameters with IOs.
|
||||
Other devices register some of their bit-Parameters with IOs.
|
||||
|
||||
1 i2c driver - n panels
|
||||
1 paneldriver - controls for many devices of same type (4 buttons for each of 4 RL02s)
|
||||
1 control - identifed by unique combination of device name and control name
|
||||
("rl1", "load_button")
|
||||
device name ideally same as device-> name
|
||||
control name ideally same as deviceparameter->name
|
||||
1 i2c driver - n panels
|
||||
1 paneldriver - controls for many devices of same type (4 buttons for each of 4 RL02s)
|
||||
1 control - identifed by unique combination of device name and control name
|
||||
("rl1", "load_button")
|
||||
device name ideally same as device-> name
|
||||
control name ideally same as deviceparameter->name
|
||||
|
||||
! Static list of panel controls is consntat,
|
||||
but device parameters connected to controls is dynamic
|
||||
(run time device creation/deletion)
|
||||
! Static list of panel controls is consntat,
|
||||
but device parameters connected to controls is dynamic
|
||||
(run time device creation/deletion)
|
||||
|
||||
*/
|
||||
#include <stdint.h>
|
||||
@@ -159,6 +159,11 @@ paneldriver_c::~paneldriver_c() {
|
||||
unregister_controls();
|
||||
}
|
||||
|
||||
bool paneldriver_c::on_param_changed(parameter_c *param) {
|
||||
// no own parameter logic
|
||||
return device_c::on_param_changed(param);
|
||||
}
|
||||
|
||||
/* low level access to I2C bus slaves */
|
||||
// https://elinux.org/Interfacing_with_I2C_Devices#Opening_the_Bus
|
||||
// result: true = success
|
||||
@@ -208,7 +213,7 @@ bool paneldriver_c::i2c_write_byte(uint8_t slave_addr, uint8_t reg_addr, uint8_t
|
||||
// reprogram the I2C chips and restart the worker
|
||||
void paneldriver_c::reset(void) {
|
||||
timeout_c timeout;
|
||||
worker_stop();
|
||||
enabled.set(false); // worker_stop();
|
||||
|
||||
// pulse "panel_reset_l"
|
||||
// MC32017: at least 1 us
|
||||
@@ -245,12 +250,7 @@ void paneldriver_c::reset(void) {
|
||||
i2c_write_byte(slave_addr, MC23017_GPPUB, 0xff);
|
||||
}
|
||||
|
||||
worker_start();
|
||||
}
|
||||
|
||||
bool paneldriver_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param);
|
||||
return true ;
|
||||
enabled.set(true); // worker_start();
|
||||
}
|
||||
|
||||
void paneldriver_c::on_power_changed(void) {
|
||||
@@ -468,7 +468,7 @@ void paneldriver_c::test_moving_ones(void) {
|
||||
clear_all_outputs();
|
||||
timeout.wait_ms(delay_ms);
|
||||
// all "OFF" on exit
|
||||
worker_stop() ;
|
||||
enabled.set(false); // worker_stop();
|
||||
INFO("Worker stopped.");
|
||||
|
||||
}
|
||||
@@ -490,7 +490,7 @@ void paneldriver_c::test_manual_loopback(void) {
|
||||
(*it)->associate->value = (*it)->value;
|
||||
timeout.wait_ms(10);
|
||||
}
|
||||
worker_stop() ;
|
||||
enabled.set(false); // worker_stop();
|
||||
INFO("Worker stopped.");
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
/* panels.hpp: a device to access lamps & buttons connected over I2C bus
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "device.hpp"
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
unsigned value; // input or output
|
||||
|
||||
// output lamps can be linekd to their including buttons
|
||||
panelcontrol_c *associate ;
|
||||
panelcontrol_c *associate;
|
||||
|
||||
panelcontrol_c(string device_name, string control_name,
|
||||
bool is_input, uint8_t chip_addr, uint8_t reg_addr, uint8_t reg_bitmask);
|
||||
@@ -94,6 +94,8 @@ public:
|
||||
paneldriver_c();
|
||||
~paneldriver_c();
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
|
||||
vector<panelcontrol_c *> controls;
|
||||
|
||||
void unregister_controls(void); // clear static list of all connected controls
|
||||
@@ -112,16 +114,13 @@ public:
|
||||
// background worker function
|
||||
void worker(void) override;
|
||||
|
||||
bool on_param_changed(parameter_c *param) override; // must implement
|
||||
void on_power_changed(void) override; // must implement
|
||||
void on_init_changed(void) override; // must implement
|
||||
|
||||
|
||||
void clear_all_outputs(void) ;
|
||||
void clear_all_outputs(void);
|
||||
|
||||
// link_control_to_parameter(new panelcontrol_c()) ;
|
||||
void link_control_to_parameter(parameter_c *deviceparameter,
|
||||
panelcontrol_c *panelcontrol);
|
||||
void link_control_to_parameter(parameter_c *deviceparameter, panelcontrol_c *panelcontrol);
|
||||
void unlink_controls_from_device(device_c *device);
|
||||
|
||||
void refresh_params(device_c *device);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/*
|
||||
rk05.cpp: implementation of RK05 disk drive, attached to RK11D controller
|
||||
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.
|
||||
Copyright Vulcan Inc. 2019 via Living Computers: Museum + Labs, Seattle, WA.
|
||||
Contributed under the BSD 2-clause license.
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@@ -16,304 +16,249 @@ 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)
|
||||
{
|
||||
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 (&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;
|
||||
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) {
|
||||
// called at high priority.
|
||||
if (power_down) {
|
||||
// power-on defaults
|
||||
drive_reset();
|
||||
}
|
||||
}
|
||||
|
||||
void rk05_c::on_init_changed(void)
|
||||
{
|
||||
// called at high priority.
|
||||
void rk05_c::on_init_changed(void) {
|
||||
// called at high priority.
|
||||
|
||||
if (init_asserted)
|
||||
{
|
||||
drive_reset();
|
||||
}
|
||||
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;
|
||||
|
||||
// Delay for seek / read.
|
||||
// TODO: maybe base this on real drive specs.
|
||||
delay.wait_ms(10);
|
||||
timeout_c delay;
|
||||
|
||||
// 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);
|
||||
// Delay for seek / read.
|
||||
// TODO: maybe base this on real drive specs.
|
||||
delay.wait_ms(10);
|
||||
|
||||
// Set RWS ready now that we're done.
|
||||
_rwsrdy = true;
|
||||
// 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);
|
||||
|
||||
controller->on_drive_status_changed(this);
|
||||
// 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);
|
||||
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 instigated above is completed.
|
||||
}
|
||||
|
||||
void rk05_c::worker(void)
|
||||
{
|
||||
timeout_c timeout;
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
|
||||
rk05_c(storagecontroller_c *controller);
|
||||
|
||||
bool on_param_changed(parameter_c* param) override;
|
||||
bool on_param_changed(parameter_c* param) override;
|
||||
|
||||
void on_power_changed(void) override;
|
||||
|
||||
|
||||
@@ -25,10 +25,8 @@ rk11_c::rk11_c() :
|
||||
name.value = "rk";
|
||||
type_name.value = "RK11";
|
||||
log_label = "rk";
|
||||
default_base_addr = 0777400; // overwritten in install()?
|
||||
default_intr_vector = 0220; // TODO: make configurable
|
||||
default_intr_level = 5; // TODO: make configurable
|
||||
|
||||
|
||||
set_default_bus_params(0777400, 0220, 5) ; // base addr, intr-vector, intr level
|
||||
|
||||
// The RK11 controller has seven registers,
|
||||
// We allocate 8 because one address in the address space is unused.
|
||||
@@ -117,6 +115,14 @@ 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
|
||||
return storagecontroller_c::on_param_changed(param) ; // more actions (for enable)
|
||||
}
|
||||
|
||||
|
||||
void rk11_c::dma_transfer(DMARequest &request)
|
||||
{
|
||||
timeout_c timeout;
|
||||
|
||||
@@ -162,6 +162,8 @@ public:
|
||||
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;
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
/* rl02.cpp: implementation of RL01/RL02 disk drive, attached to RL11 controller
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@@ -44,22 +44,28 @@ RL0102_c::RL0102_c(storagecontroller_c *controller) :
|
||||
|
||||
}
|
||||
|
||||
|
||||
// return false, if illegal parameter value.
|
||||
// verify "new_value", must output error messages
|
||||
bool RL0102_c::on_param_changed(parameter_c *param) {
|
||||
if (param == &type_name) {
|
||||
if (param == &enabled) {
|
||||
if (!enabled.new_value) {
|
||||
// disable switches power OFF.
|
||||
// must be power on by caller or user after enable
|
||||
power_switch.value = false;
|
||||
change_state(RL0102_STATE_power_off);
|
||||
}
|
||||
} else if (param == &type_name) {
|
||||
if (!strcasecmp(type_name.new_value.c_str(), "RL01"))
|
||||
set_type(1) ;
|
||||
set_type(1);
|
||||
else if (!strcasecmp(type_name.new_value.c_str(), "RL02"))
|
||||
set_type(2) ;
|
||||
set_type(2);
|
||||
else {
|
||||
// throw bad_parameter_check("drive type must be RL01 or RL02") ;
|
||||
ERROR("drive type must be RL01 or RL02") ;
|
||||
return false ;
|
||||
ERROR("drive type must be RL01 or RL02");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true ;
|
||||
return storagedrive_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
void RL0102_c::set_type(uint8_t drivetype) {
|
||||
@@ -168,15 +174,15 @@ void RL0102_c::change_state(unsigned new_state) {
|
||||
state.value = new_state;
|
||||
update_status_word(); // contains state
|
||||
if (old_state != new_state)
|
||||
DEBUG("Change drive %s state from %d to %d. Status word %06o -> %06o.", name.value.c_str(),
|
||||
old_state, state.value, old_status_word, status_word);
|
||||
DEBUG("Change drive %s state from %d to %d. Status word %06o -> %06o.",
|
||||
name.value.c_str(), old_state, state.value, old_status_word, status_word);
|
||||
}
|
||||
|
||||
/*** state functions, called repeatedly ***/
|
||||
void RL0102_c::state_power_off() {
|
||||
// drive_ready_line = false; // verified
|
||||
// drive_error_line = true; // real RL02: RL11 show a DRIVE ERROR after power on / DC_LO
|
||||
type_name.readonly = false ; // may be changed between RL01/RL02
|
||||
type_name.readonly = false; // may be changed between RL01/RL02
|
||||
volume_check = true; // starts with volume check?
|
||||
cover_open.readonly = true;
|
||||
update_status_word(/*drive_ready_line*/false, /*drive_error_line*/true);
|
||||
@@ -195,7 +201,7 @@ void RL0102_c::state_power_off() {
|
||||
// drive stop, door unlocked, cartridge can be loaded
|
||||
void RL0102_c::state_load_cartridge() {
|
||||
// drive_ready_line = false; // verified
|
||||
type_name.readonly = true ; // must be powered of to changed between RL01/RL02
|
||||
type_name.readonly = true; // must be powered of to changed between RL01/RL02
|
||||
update_status_word(/*drive_ready_line*/false, drive_error_line);
|
||||
load_lamp.value = 1;
|
||||
ready_lamp.value = 0;
|
||||
@@ -256,7 +262,7 @@ void RL0102_c::state_spin_up() {
|
||||
|
||||
load_lamp.value = 0;
|
||||
ready_lamp.value = 0;
|
||||
writeprotect_lamp.value = writeprotect_button.value || file_readonly ;
|
||||
writeprotect_lamp.value = writeprotect_button.value || file_readonly;
|
||||
image_filepath.readonly = true; // "door locked", disk can not be changed
|
||||
|
||||
state_timeout.wait_ms(calcperiod_ms);
|
||||
@@ -377,7 +383,7 @@ void RL0102_c::state_lock_on() {
|
||||
|
||||
load_lamp.value = 0;
|
||||
ready_lamp.value = 1;
|
||||
writeprotect_lamp.value = writeprotect_button.value|| file_readonly;
|
||||
writeprotect_lamp.value = writeprotect_button.value || file_readonly;
|
||||
|
||||
// fast polling, if ZRLI tests time of 0 cly seek with head switch
|
||||
state_timeout.wait_ms(1);
|
||||
@@ -501,7 +507,7 @@ bool RL0102_c::header_on_track(uint16_t header) {
|
||||
} while(0)
|
||||
|
||||
#ifdef OLD
|
||||
#define NEXT_SECTOR_SEGMENT_ADVANCE do { \
|
||||
#define NEXT_SECTOR_SEGMENT_ADVANCE do { \
|
||||
next_sector_segment_under_heads = (next_sector_segment_under_heads + 1) % 80 ; \
|
||||
if (next_sector_segment_under_heads & 1) \
|
||||
/* time to pass one sector 600 data, 25 header? */ \
|
||||
@@ -526,7 +532,8 @@ bool RL0102_c::cmd_read_next_sector_header(uint16_t *buffer, unsigned buffer_siz
|
||||
|
||||
if (next_sector_segment_under_heads & 1) {
|
||||
// odd: next is data, let it pass the head
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE;
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE
|
||||
;
|
||||
// nanosleep() for rotational delay?
|
||||
}
|
||||
|
||||
@@ -542,7 +549,8 @@ bool RL0102_c::cmd_read_next_sector_header(uint16_t *buffer, unsigned buffer_siz
|
||||
buffer[2] = calc_crc(2, &buffer[0]); // header CRC
|
||||
|
||||
// circular advance to next header: 40x headers, 40x data
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE;
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE
|
||||
;
|
||||
// nanosleep() for rotational delay?
|
||||
return true;
|
||||
}
|
||||
@@ -558,7 +566,8 @@ bool RL0102_c::cmd_read_next_sector_data(uint16_t *buffer, unsigned buffer_size_
|
||||
|
||||
if (!(next_sector_segment_under_heads & 1)) {
|
||||
// even: next segment is header, let it pass the head
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE;
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE
|
||||
;
|
||||
// nanosleep() for rotational delay?
|
||||
}
|
||||
unsigned sectorno = next_sector_segment_under_heads >> 1; // LSB is header/data phase
|
||||
@@ -570,13 +579,12 @@ bool RL0102_c::cmd_read_next_sector_data(uint16_t *buffer, unsigned buffer_size_
|
||||
// LSB saved before MSB -> word/byte conversion on ARM (little endian) is easy
|
||||
file_read((uint8_t *) buffer, offset, sector_size_bytes);
|
||||
DEBUG("File Read 0x%x words from c/h/s=%d/%d/%d, file pos=0x%llx, words = %06o, %06o, ...",
|
||||
sector_size_bytes/2,
|
||||
cylinder,head, sectorno,
|
||||
offset, (unsigned)(buffer[0]), (unsigned)(buffer[1])) ;
|
||||
|
||||
sector_size_bytes / 2, cylinder, head, sectorno, offset, (unsigned )(buffer[0]),
|
||||
(unsigned )(buffer[1]));
|
||||
|
||||
// circular advance to next header: 40x headers, 40x data
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE;
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE
|
||||
;
|
||||
// nanosleep() for rotational delay?
|
||||
return true;
|
||||
}
|
||||
@@ -600,7 +608,8 @@ bool RL0102_c::cmd_write_next_sector_data(uint16_t *buffer, unsigned buffer_size
|
||||
|
||||
if (!(next_sector_segment_under_heads & 1)) {
|
||||
// even: next segment is header, let it pass the head
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE;
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE
|
||||
;
|
||||
// nanosleep() for rotational delay?
|
||||
}
|
||||
unsigned sectorno = next_sector_segment_under_heads >> 1; // LSB is header/data phase
|
||||
@@ -612,13 +621,12 @@ bool RL0102_c::cmd_write_next_sector_data(uint16_t *buffer, unsigned buffer_size
|
||||
// LSB saved before MSB -> word/byte conversion on ARM (little endian) is easy
|
||||
file_write((uint8_t *) buffer, offset, sector_size_bytes);
|
||||
DEBUG("File Write 0x%x words from c/h/s=%d/%d/%d, file pos=0x%llx, words = %06o, %06o, ...",
|
||||
sector_size_bytes/2,
|
||||
cylinder,head, sectorno,
|
||||
offset, (unsigned)(buffer[0]), (unsigned)(buffer[1])) ;
|
||||
|
||||
sector_size_bytes / 2, cylinder, head, sectorno, offset, (unsigned )(buffer[0]),
|
||||
(unsigned )(buffer[1]));
|
||||
|
||||
// circular advance to next header: 40x headers, 40x data
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE;
|
||||
NEXT_SECTOR_SEGMENT_ADVANCE
|
||||
;
|
||||
// nanosleep() for rotational delay?
|
||||
|
||||
return true;
|
||||
@@ -637,8 +645,13 @@ void RL0102_c::worker(void) {
|
||||
update_status_word(drive_ready_line, drive_error_line);
|
||||
|
||||
// global stuff for all states
|
||||
if (enabled.value && (!controller || !controller->enabled.value))
|
||||
// RL drive powered, but no controller: no clock -> FAULT
|
||||
fault_lamp.value = true;
|
||||
|
||||
if (power_switch.value == false)
|
||||
change_state(RL0102_STATE_power_off);
|
||||
|
||||
switch (state.value) {
|
||||
case RL0102_STATE_power_off:
|
||||
state_power_off();
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
/* rl02.cpp: implementation of RL01/RL02 disk drive, attached to RL11 controller
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
#ifndef _RL0102_HPP_
|
||||
#define _RL0102_HPP_
|
||||
|
||||
@@ -120,8 +120,8 @@ public:
|
||||
|
||||
volatile uint16_t status_word; // visible to controller
|
||||
|
||||
parameter_unsigned_c rotation_umin = parameter_unsigned_c(this, "rotation",
|
||||
"rot",/*readonly*/true, "rpm", "%d", "Current speed of disk", 32, 10);
|
||||
parameter_unsigned_c rotation_umin = parameter_unsigned_c(this, "rotation", "rot",/*readonly*/
|
||||
true, "rpm", "%d", "Current speed of disk", 32, 10);
|
||||
// RL0102_STATE_*. no enum, is param
|
||||
parameter_unsigned_c state = parameter_unsigned_c(this, "state", "st", /*readonly*/
|
||||
true, "", "%d", "Internal state", 32, 10);
|
||||
@@ -137,17 +137,16 @@ public:
|
||||
true, "State of READY lamp");
|
||||
parameter_bool_c fault_lamp = parameter_bool_c(this, "faultlamp", "fl", /*readonly*/
|
||||
true, "State of FAULT lamp");
|
||||
parameter_bool_c writeprotect_lamp = parameter_bool_c(this, "writeprotectlamp",
|
||||
"wpl", /*readonly*/true, "State of WRITE PROTECT lamp");
|
||||
parameter_bool_c writeprotect_button = parameter_bool_c(this,
|
||||
"writeprotectbutton", "wpb", /*readonly*/false, "Writeprotect button pressed");
|
||||
parameter_bool_c writeprotect_lamp = parameter_bool_c(this, "writeprotectlamp", "wpl", /*readonly*/
|
||||
true, "State of WRITE PROTECT lamp");
|
||||
parameter_bool_c writeprotect_button = parameter_bool_c(this, "writeprotectbutton", "wpb", /*readonly*/
|
||||
false, "Writeprotect button pressed");
|
||||
|
||||
// cover normally always "closed", need to get opened for ZRLI
|
||||
parameter_bool_c cover_open = parameter_bool_c(this, "coveropen", "co", /*readonly*/
|
||||
false, "1, if RL cover is open");
|
||||
// not readonly only in "load" state
|
||||
|
||||
|
||||
RL0102_c(storagecontroller_c *controller);
|
||||
|
||||
bool on_param_changed(parameter_c *param) override;
|
||||
|
||||
@@ -1,86 +1,86 @@
|
||||
/* rl11.cpp: Implementation of the RL11 controller
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
12-nov-2018 JH entered beta phase
|
||||
|
||||
|
||||
- implements a 4 UNIBUS register interface, which are shared with PRU.
|
||||
- gets notified of UNIBUS register access on_after_register_access()
|
||||
- starts 4 RL01/02 drives
|
||||
on_after_register_access() is a high priority RT thread.
|
||||
It may ONLY update the settings of UNIBUS interface registers by swapping in
|
||||
several internal registers (status for each drive, MP multipuprpose for different
|
||||
Commands) to UNIBUS registers.
|
||||
- execution of commands, access to drives etc is made in worker()
|
||||
worker() is waked by a signal from on_after_register_access()
|
||||
- implements a 4 UNIBUS register interface, which are shared with PRU.
|
||||
- gets notified of UNIBUS register access on_after_register_access()
|
||||
- starts 4 RL01/02 drives
|
||||
on_after_register_access() is a high priority RT thread.
|
||||
It may ONLY update the settings of UNIBUS interface registers by swapping in
|
||||
several internal registers (status for each drive, MP multipuprpose for different
|
||||
Commands) to UNIBUS registers.
|
||||
- execution of commands, access to drives etc is made in worker()
|
||||
worker() is waked by a signal from on_after_register_access()
|
||||
|
||||
Todo:
|
||||
- operation, when drive power OFF? error DSE drive select?
|
||||
1) RL0 powered off: CS =200, nach NOP
|
||||
Get status; DA=013, write 00004 Read: CS 102204 (ERR,OPI, DRVready=0
|
||||
MP = 006050 (3x identisch)
|
||||
Seek: Write 0006, read: 102206 (Spnerror, coveropen,brush home)
|
||||
MP = 20210
|
||||
2) RL0 powered on, LOAD
|
||||
NOOP: CS write 0, read 200
|
||||
Get Status: DA=013,write 0004, read 204. MP = 20217 (spinn down), dann 20210 (LOAD)
|
||||
Seek: Write CS=0006 , read 102206. MP = unchanged
|
||||
3) RL02 on track
|
||||
NOOP: CS write 0, read 140201 (Driverer, do Getstatus
|
||||
Get Status: DA=013, write 0004, read 205. MP = 020235
|
||||
Seek: DA=0377 (255)100, read = 207
|
||||
Todo:
|
||||
- operation, when drive power OFF? error DSE drive select?
|
||||
1) RL0 powered off: CS =200, nach NOP
|
||||
Get status; DA=013, write 00004 Read: CS 102204 (ERR,OPI, DRVready=0
|
||||
MP = 006050 (3x identisch)
|
||||
Seek: Write 0006, read: 102206 (Spnerror, coveropen,brush home)
|
||||
MP = 20210
|
||||
2) RL0 powered on, LOAD
|
||||
NOOP: CS write 0, read 200
|
||||
Get Status: DA=013,write 0004, read 204. MP = 20217 (spinn down), dann 20210 (LOAD)
|
||||
Seek: Write CS=0006 , read 102206. MP = unchanged
|
||||
3) RL02 on track
|
||||
NOOP: CS write 0, read 140201 (Driverer, do Getstatus
|
||||
Get Status: DA=013, write 0004, read 205. MP = 020235
|
||||
Seek: DA=0377 (255)100, read = 207
|
||||
|
||||
|
||||
|
||||
|
||||
- Which errors raise "OPI" (operation incomplete)
|
||||
NXM?
|
||||
- Mismatch DMA wordcount and sector buffer
|
||||
word len != sector border ? => no problem?
|
||||
end of track before worldcount == 0 ?
|
||||
"DA register is not incrmeneted in multisector transfer"
|
||||
=> OPI?
|
||||
- Which errors raise "OPI" (operation incomplete)
|
||||
NXM?
|
||||
- Mismatch DMA wordcount and sector buffer
|
||||
word len != sector border ? => no problem?
|
||||
end of track before worldcount == 0 ?
|
||||
"DA register is not incrmeneted in multisector transfer"
|
||||
=> OPI?
|
||||
|
||||
- "Read header: ": select which sector to read?
|
||||
Simulate disk rotation???
|
||||
How to generate CRC? -> simh!
|
||||
- "Read header: ": select which sector to read?
|
||||
Simulate disk rotation???
|
||||
How to generate CRC? -> simh!
|
||||
|
||||
- "read data without header"
|
||||
-> wait for sector pulse? disk rotation?
|
||||
- "read data without header"
|
||||
-> wait for sector pulse? disk rotation?
|
||||
|
||||
|
||||
Communication between on_after_register_access and worker():
|
||||
- use pthread condition variable pthrad_cond_*
|
||||
- normally a mutex show protect worker() against variable change
|
||||
by interrupting on_after_register_access()
|
||||
- the signal "controller_ready" is that mutex already:
|
||||
set by cmd-start in on_after_register_access(),
|
||||
released by worker() on completion
|
||||
- still a mutex needed, only for the thread condition variable as shown in
|
||||
- mutex in on_after_register_access() and worker()
|
||||
- all refgsier access are atomic 32bit anyhow
|
||||
http://openbook.rheinwerk-verlag.de/linux_unix_programmierung/Kap10-006.htm#RxxKap10006040003201F02818E
|
||||
https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032r/index.html#sync-59145
|
||||
Communication between on_after_register_access and worker():
|
||||
- use pthread condition variable pthrad_cond_*
|
||||
- normally a mutex show protect worker() against variable change
|
||||
by interrupting on_after_register_access()
|
||||
- the signal "controller_ready" is that mutex already:
|
||||
set by cmd-start in on_after_register_access(),
|
||||
released by worker() on completion
|
||||
- still a mutex needed, only for the thread condition variable as shown in
|
||||
- mutex in on_after_register_access() and worker()
|
||||
- all refgsier access are atomic 32bit anyhow
|
||||
http://openbook.rheinwerk-verlag.de/linux_unix_programmierung/Kap10-006.htm#RxxKap10006040003201F02818E
|
||||
https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032r/index.html#sync-59145
|
||||
|
||||
|
||||
*/
|
||||
@@ -136,9 +136,7 @@ RL11_c::RL11_c(void) :
|
||||
type_name.value = "RL11";
|
||||
log_label = "rl";
|
||||
|
||||
default_base_addr = 0774400;
|
||||
default_intr_vector = 0160;
|
||||
default_intr_level = 5;
|
||||
set_default_bus_params(0774400, 0160, 5) ; // base addr, intr-vector, intr level
|
||||
|
||||
// add 4 RL disk drives
|
||||
drivecount = 4;
|
||||
@@ -194,6 +192,19 @@ RL11_c::~RL11_c() {
|
||||
delete storagedrives[i];
|
||||
}
|
||||
|
||||
bool RL11_c::on_param_changed(parameter_c *param) {
|
||||
if (param == &enabled) {
|
||||
if (enabled.new_value) {
|
||||
// enabled
|
||||
connect_to_panel();
|
||||
} else {
|
||||
// disabled
|
||||
disconnect_from_panel();
|
||||
}
|
||||
}
|
||||
return storagecontroller_c::on_param_changed(param); // more actions (for enable)
|
||||
}
|
||||
|
||||
/* connect parameters of drives to i2c paneldriver
|
||||
* Changes to parameter values after user panel operation
|
||||
* or refresh_params_from_panel()
|
||||
@@ -451,12 +462,6 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg,
|
||||
// now SSYN goes inactive !
|
||||
}
|
||||
|
||||
|
||||
bool RL11_c::on_param_changed(parameter_c *param) {
|
||||
UNUSED(param) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
void RL11_c::on_power_changed(void) {
|
||||
// storagecontroller_c forwards to drives
|
||||
storagecontroller_c::on_power_changed();
|
||||
@@ -718,25 +723,25 @@ void RL11_c::state_readwrite() {
|
||||
//logger.debug_hexdump(LC_RL, "Read data between disk access and DMA",
|
||||
// (uint8_t *) silo, sizeof(silo), NULL);
|
||||
// start DMA transmission of SILO into memory
|
||||
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATO, unibus_address, silo,
|
||||
dma_wordcount, &unibus_address);
|
||||
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATO,
|
||||
unibus_address, silo, dma_wordcount, &unibus_address);
|
||||
} else if (function_code == CMD_WRITE_CHECK) {
|
||||
// read sector data to compare with sector data
|
||||
drive->cmd_read_next_sector_data(silo, 128);
|
||||
// logger.debug_hexdump(LC_RL, "Read data between disk access and DMA",
|
||||
// (uint8_t *) silo, sizeof(silo), NULL);
|
||||
// start DMA transmission of memory to compare with SILO
|
||||
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI, unibus_address, silo_compare,
|
||||
dma_wordcount, &unibus_address);
|
||||
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI,
|
||||
unibus_address, silo_compare, dma_wordcount, &unibus_address);
|
||||
} else if (function_code == CMD_WRITE_DATA) {
|
||||
// start DMA transmission of memory into SILO
|
||||
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI, unibus_address, silo,
|
||||
dma_wordcount, &unibus_address);
|
||||
error_dma_timeout = !unibusadapter->request_client_DMA(UNIBUS_CONTROL_DATI,
|
||||
unibus_address, silo, dma_wordcount, &unibus_address);
|
||||
}
|
||||
|
||||
// request_client_DMA() was blocking, DMA processed now.
|
||||
// unibus_address updated to last accesses address
|
||||
unibus_address += 2; // was last address, is now next to fill
|
||||
|
||||
// request_client_DMA() was blocking, DMA processed now.
|
||||
// unibus_address updated to last accesses address
|
||||
unibus_address += 2; // was last address, is now next to fill
|
||||
// if timeout: addr AFTER illegal address (verified)
|
||||
update_unibus_address(unibus_address); // set addr msb to cs
|
||||
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
/* rl11.hpp: Implementation of the RL11 controller
|
||||
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
Copyright (c) 2018, Joerg Hoppe
|
||||
j_hoppe@t-online.de, www.retrocmp.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
12-nov-2018 JH entered beta phase
|
||||
*/
|
||||
#ifndef _RL11_HPP_
|
||||
#define _RL11_HPP_
|
||||
|
||||
#include "storagecontroller.hpp"
|
||||
|
||||
class RL0102_c ;
|
||||
class RL0102_c;
|
||||
class RL11_c: public storagecontroller_c {
|
||||
private:
|
||||
/** everything sharead between different threads must be "volatile" */
|
||||
volatile unsigned state; // one of RL11_STATE_*
|
||||
|
||||
timeout_c timeout ;
|
||||
timeout_c timeout;
|
||||
|
||||
// which drive will communicate with the controeller via the drive bus.
|
||||
volatile uint8_t selected_drive_unitno;
|
||||
@@ -47,8 +47,8 @@ private:
|
||||
|
||||
volatile bool error_operation_incomplete; // OPI. operation aborted because of error
|
||||
volatile bool error_dma_timeout; // DMA operation addresses non existing memory
|
||||
volatile bool error_writecheck ; // mismatch between memory and disk data
|
||||
volatile bool error_header_not_found ; // sector address notfound on track
|
||||
volatile bool error_writecheck; // mismatch between memory and disk data
|
||||
volatile bool error_header_not_found; // sector address notfound on track
|
||||
|
||||
// after "read header": MP register show different values on successive reads
|
||||
volatile uint16_t mpr_silo[3]; // max 3 word deep buffer
|
||||
@@ -66,12 +66,10 @@ private:
|
||||
void update_unibus_address(uint32_t addr);
|
||||
uint16_t get_MP_wordcount(void);
|
||||
void set_MP_wordcount(uint16_t wordcount);
|
||||
void set_MP_dati_value(uint16_t w, const char *debug_info) ;
|
||||
void set_MP_dati_silo(const char *debug_info) ;
|
||||
void set_MP_dati_value(uint16_t w, const char *debug_info);
|
||||
void set_MP_dati_silo(const char *debug_info);
|
||||
|
||||
|
||||
|
||||
void clear_errors(void) ;
|
||||
void clear_errors(void);
|
||||
|
||||
// controller can accept commands again
|
||||
void do_command_done(void);
|
||||
@@ -80,10 +78,14 @@ private:
|
||||
void do_operation_incomplete(const char *info);
|
||||
|
||||
// state machines
|
||||
void change_state(unsigned new_state) ;
|
||||
void change_state(unsigned new_state);
|
||||
void state_seek(void);
|
||||
void state_readwrite(void);
|
||||
|
||||
void connect_to_panel(void);
|
||||
void disconnect_from_panel(void);
|
||||
void refresh_params_from_panel(void);
|
||||
|
||||
public:
|
||||
|
||||
// register interface to RL11 controller
|
||||
@@ -95,27 +97,23 @@ public:
|
||||
RL11_c(void);
|
||||
~RL11_c(void);
|
||||
|
||||
bool on_param_changed(parameter_c *param) override;
|
||||
|
||||
void reset(void);
|
||||
|
||||
void connect_to_panel(void) ;
|
||||
void disconnect_from_panel(void) ;
|
||||
void refresh_params_from_panel(void) ;
|
||||
|
||||
|
||||
// background worker function
|
||||
void worker(void) override;
|
||||
|
||||
RL0102_c *selected_drive(void) ;
|
||||
RL0102_c *selected_drive(void);
|
||||
|
||||
// called by unibusadapter after DATI/DATO access to active emulated register
|
||||
// Runs at 100% RT priority, UNIBUS is stopped by SSYN while this is running.
|
||||
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) ;
|
||||
void on_drive_status_changed(storagedrive_c *drive);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -46,9 +46,7 @@ uda_c::uda_c() :
|
||||
type_name.value = "UDA50";
|
||||
log_label = "uda";
|
||||
|
||||
default_base_addr = 0772150;
|
||||
default_intr_vector = 0154;
|
||||
default_intr_level = 5;
|
||||
set_default_bus_params(0772150, 0154, 5) ; // base addr, intr-vector, intr level
|
||||
|
||||
// The UDA50 controller has two registers.
|
||||
register_count = 2;
|
||||
@@ -94,6 +92,12 @@ uda_c::~uda_c()
|
||||
storagedrives.clear();
|
||||
}
|
||||
|
||||
bool uda_c::on_param_changed(parameter_c *param) {
|
||||
// no own parameter or "enable" logic
|
||||
return storagecontroller_c::on_param_changed(param) ; // more actions (for enable)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Reset():
|
||||
// Resets the UDA controller state.
|
||||
|
||||
@@ -61,6 +61,8 @@ public:
|
||||
uda_c();
|
||||
virtual ~uda_c();
|
||||
|
||||
bool on_param_changed(parameter_c *param) override;
|
||||
|
||||
void worker(void) override;
|
||||
|
||||
void on_after_register_access(
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
#include "unibus.h"
|
||||
#include "memoryimage.hpp"
|
||||
|
||||
#include "unibusadapter.hpp"
|
||||
#include "unibusdevice.hpp"
|
||||
//#include "unibusadapter.hpp"
|
||||
//#include "unibusdevice.hpp"
|
||||
|
||||
#include "devexer_rl.hpp"
|
||||
|
||||
@@ -60,12 +60,8 @@ void application_c::menu_device_exercisers(void) {
|
||||
// UNIBUS activity
|
||||
hardware_startup(pru_c::PRUCODE_UNIBUS);
|
||||
buslatches_output_enable(true);
|
||||
// no device emulation, no CPU arbitration
|
||||
|
||||
unibusadapter->worker_stop();
|
||||
//unibus->arbitrator_client = false;
|
||||
|
||||
// instantiate differebt device exercisers
|
||||
// instantiate different device exercisers
|
||||
|
||||
devexer::rl_c rl;
|
||||
USE(rl); // not accessed diretcly, but is registered to exerciser list
|
||||
|
||||
@@ -85,10 +85,6 @@ static void load_memory(enum unibus_c::arbitration_mode_enum arbitration_mode, c
|
||||
// CPU is enabled, act as ARBITRATION_MASTER
|
||||
void application_c::menu_devices(bool with_CPU) {
|
||||
/** list of usable devices ***/
|
||||
bool with_DEMOIO = true;
|
||||
bool with_RL = true;
|
||||
bool with_RK = true; // SIGINT on exit?
|
||||
bool with_MSCP = true;
|
||||
bool with_storage_file_test = false;
|
||||
|
||||
enum unibus_c::arbitration_mode_enum arbitration_mode;
|
||||
@@ -117,54 +113,27 @@ void application_c::menu_devices(bool with_CPU) {
|
||||
// now PRU executing UNIBUS master/slave code, physical PDP-11 CPU as arbitrator required.
|
||||
buslatches_output_enable(true);
|
||||
|
||||
unibusadapter->worker_start();
|
||||
unibusadapter->enabled.set(true);
|
||||
|
||||
// 2 demo controller
|
||||
cur_device = NULL;
|
||||
demo_io_c *demo_io = NULL;
|
||||
demo_io_c *demo_io = new demo_io_c();
|
||||
//demo_regs_c demo_regs; // mem at 160000: RT11 crashes?
|
||||
cpu_c *cpu = NULL;
|
||||
RL11_c *RL11 = NULL;
|
||||
// create RL11 + also 4 RL01/02 drives
|
||||
RL11_c *RL11 = new RL11_c();
|
||||
paneldriver->reset(); // reset I2C, restart worker()
|
||||
rk11_c *RK11 = NULL;
|
||||
uda_c *UDA50 = NULL;
|
||||
|
||||
if (with_DEMOIO) {
|
||||
demo_io = new demo_io_c();
|
||||
demo_io->install();
|
||||
demo_io->worker_start();
|
||||
}
|
||||
// create RK11 + drives
|
||||
rk11_c *RK11 = new rk11_c();
|
||||
// Create UDA50
|
||||
uda_c *UDA50 = new uda_c();
|
||||
|
||||
// //demo_regs.install();
|
||||
// //demo_regs.worker_start();
|
||||
|
||||
if (with_RL) {
|
||||
// create RL11 + drives
|
||||
// instantiates also 4 RL01/02 drives
|
||||
RL11 = new RL11_c();
|
||||
RL11->install();
|
||||
RL11->connect_to_panel();
|
||||
RL11->worker_start();
|
||||
}
|
||||
|
||||
if (with_RK) {
|
||||
// create RK11 + drives
|
||||
RK11 = new rk11_c();
|
||||
RK11->install();
|
||||
RK11->worker_start();
|
||||
}
|
||||
|
||||
if (with_CPU) {
|
||||
cpu = new cpu_c();
|
||||
cpu->install();
|
||||
cpu->worker_start();
|
||||
}
|
||||
|
||||
if (with_MSCP) {
|
||||
// Create UDA50
|
||||
UDA50 = new uda_c();
|
||||
UDA50->install();
|
||||
UDA50->worker_start();
|
||||
cpu->enabled.set(true);
|
||||
}
|
||||
|
||||
if (with_storage_file_test) {
|
||||
@@ -206,7 +175,10 @@ void application_c::menu_devices(bool with_CPU) {
|
||||
printf("m ll Reload last memory content from file \"%s\"\n",
|
||||
memory_filename);
|
||||
printf("ld List all defined devices\n");
|
||||
printf("en <dev> Enable a device\n");
|
||||
printf("dis <dev> Disable device\n");
|
||||
printf("sd <dev> Select \"current device\"\n");
|
||||
|
||||
if (cur_device) {
|
||||
printf("p <param> <val> Set parameter value of current device\n");
|
||||
printf("p <param> Get parameter value of current device\n");
|
||||
@@ -300,25 +272,54 @@ void application_c::menu_devices(bool with_CPU) {
|
||||
// m ll
|
||||
load_memory(arbitration_mode, memory_filename, "start");
|
||||
} else if (!strcasecmp(s_opcode, "ld") && n_fields == 1) {
|
||||
unsigned n;
|
||||
list<device_c *>::iterator it;
|
||||
cout << "Registered devices:\n";
|
||||
for (it = device_c::mydevices.begin(); it != device_c::mydevices.end(); ++it) {
|
||||
cout << "- " << (*it)->name.value << " (type is " << (*it)->type_name.value
|
||||
<< ")\n";
|
||||
if ((*it)->name.value.empty())
|
||||
printf("EMPTY\n");
|
||||
}
|
||||
} else if (!strcasecmp(s_opcode, "sd") && n_fields == 2) {
|
||||
list<device_c *>::iterator it;
|
||||
bool found = false;
|
||||
for (it = device_c::mydevices.begin(); it != device_c::mydevices.end(); ++it)
|
||||
if (!strcasecmp((*it)->name.value.c_str(), s_param[0])) {
|
||||
found = true;
|
||||
cur_device = *it;
|
||||
for (n = 0, it = device_c::mydevices.begin(); it != device_c::mydevices.end();
|
||||
++it)
|
||||
if ((*it)->enabled.value) {
|
||||
if (n == 0)
|
||||
cout << "Enabled devices:\n";
|
||||
n++;
|
||||
cout << "- " << (*it)->name.value << " (type is "
|
||||
<< (*it)->type_name.value << ")\n";
|
||||
// if ((*it)->name.value.empty())
|
||||
// printf("EMPTY\n");
|
||||
}
|
||||
if (!found)
|
||||
if (n == 0)
|
||||
cout << "No enabled devices.\n";
|
||||
|
||||
for (n = 0, it = device_c::mydevices.begin(); it != device_c::mydevices.end();
|
||||
++it)
|
||||
if (!(*it)->enabled.value) {
|
||||
if (n == 0)
|
||||
cout << "Disabled devices:\n";
|
||||
n++;
|
||||
cout << "- " << (*it)->name.value << " (type is "
|
||||
<< (*it)->type_name.value << ")\n";
|
||||
}
|
||||
if (n == 0)
|
||||
cout << "No disabled devices.\n";
|
||||
} else if (!strcasecmp(s_opcode, "en") && n_fields == 2) {
|
||||
device_c *dev = device_c::find_by_name(s_param[0]);
|
||||
if (!dev) {
|
||||
cout << "Device \"" << s_param[0] << "\" not found.\n";
|
||||
else {
|
||||
show_help = true;
|
||||
} else
|
||||
dev->enabled.set(true);
|
||||
} else if (!strcasecmp(s_opcode, "dis") && n_fields == 2) {
|
||||
device_c *dev = device_c::find_by_name(s_param[0]);
|
||||
if (!dev) {
|
||||
cout << "Device \"" << s_param[0] << "\" not found.\n";
|
||||
show_help = true;
|
||||
} else
|
||||
dev->enabled.set(false);
|
||||
} else if (!strcasecmp(s_opcode, "sd") && n_fields == 2) {
|
||||
cur_device = device_c::find_by_name(s_param[0]);
|
||||
|
||||
if (!cur_device) {
|
||||
cout << "Device \"" << s_param[0] << "\" not found.\n";
|
||||
show_help = true;
|
||||
} else {
|
||||
printf("Current device is \"%s\"\n", cur_device->name.value.c_str());
|
||||
// find base address of assoiated UNIBUS unibuscontroller
|
||||
if (cur_device != NULL && dynamic_cast<unibusdevice_c *>(cur_device))
|
||||
@@ -383,7 +384,7 @@ void application_c::menu_devices(bool with_CPU) {
|
||||
printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr);
|
||||
} else if (unibuscontroller && !strcasecmp(s_opcode, "e") && n_fields <= 2) {
|
||||
unsigned blocksize = 0; // default: no EXAM
|
||||
bool timeout ;
|
||||
bool timeout;
|
||||
uint32_t addr;
|
||||
unibusdevice_register_t *reg;
|
||||
if (n_fields == 2) { // single reg number given
|
||||
@@ -410,7 +411,7 @@ void application_c::menu_devices(bool with_CPU) {
|
||||
reg->addr, mailbox->dma.words[i]);
|
||||
}
|
||||
} else {
|
||||
timeout = false ;
|
||||
timeout = false;
|
||||
printf("Device has no UNIBUS registers.\n");
|
||||
}
|
||||
}
|
||||
@@ -428,40 +429,26 @@ void application_c::menu_devices(bool with_CPU) {
|
||||
} // ready
|
||||
|
||||
if (with_CPU) {
|
||||
cpu->worker_stop();
|
||||
cpu->uninstall();
|
||||
cpu->enabled.set(false);
|
||||
delete cpu;
|
||||
}
|
||||
|
||||
if (with_RL) {
|
||||
RL11->worker_stop();
|
||||
RL11->disconnect_from_panel();
|
||||
RL11->uninstall();
|
||||
delete RL11;
|
||||
}
|
||||
RL11->enabled.set(false);
|
||||
delete RL11;
|
||||
|
||||
if (with_RK) {
|
||||
RK11->worker_stop();
|
||||
RK11->uninstall();
|
||||
delete RK11;
|
||||
}
|
||||
RK11->enabled.set(false);
|
||||
delete RK11;
|
||||
|
||||
if (with_MSCP) {
|
||||
UDA50->worker_stop();
|
||||
UDA50->uninstall();
|
||||
delete UDA50;
|
||||
}
|
||||
UDA50->enabled.set(false);
|
||||
delete UDA50;
|
||||
|
||||
// //demo_regs.worker_stop();
|
||||
// //demo_regs.uninstall();
|
||||
|
||||
if (with_DEMOIO) {
|
||||
demo_io->worker_stop();
|
||||
demo_io->uninstall();
|
||||
delete demo_io;
|
||||
}
|
||||
demo_io->enabled.set(false);
|
||||
delete demo_io;
|
||||
|
||||
unibusadapter->worker_stop();
|
||||
unibusadapter->enabled.set(false);
|
||||
|
||||
buslatches_output_enable(false);
|
||||
hardware_shutdown(); // stop PRU
|
||||
|
||||
@@ -74,7 +74,7 @@ void application_c::menu_interrupts(void) {
|
||||
printf("***\n");
|
||||
printf("*** Starting full UNIBUS master/slave logic on PRU\n");
|
||||
printf("***\n");
|
||||
unibusadapter->worker_start();
|
||||
unibusadapter->enabled.set(true) ;
|
||||
active = true;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ void application_c::menu_interrupts(void) {
|
||||
printf("***\n");
|
||||
printf("*** Stopping UNIBUS logic on PRU\n");
|
||||
printf("***\n");
|
||||
unibusadapter->worker_stop();
|
||||
unibusadapter->enabled.set(false) ;
|
||||
active = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr
|
||||
|
||||
// PRUCODE_UNIBUS can raise events (INIT,ACLO,DCLO)
|
||||
// handle & clear these
|
||||
unibusadapter->worker_start() ;
|
||||
unibusadapter->enabled.set(true) ;
|
||||
|
||||
ready = false;
|
||||
|
||||
@@ -95,8 +95,7 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr
|
||||
printf("*** Starting full UNIBUS master/slave logic on PRU\n");
|
||||
printf("***\n");
|
||||
if (testcontroller_enabled) {
|
||||
demo_regs.install();
|
||||
demo_regs.worker_start();
|
||||
demo_regs.enabled.set(true);
|
||||
}
|
||||
unibusadapter->print_shared_register_map();
|
||||
active = true;
|
||||
@@ -371,12 +370,11 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr
|
||||
printf("***\n");
|
||||
|
||||
if (testcontroller_enabled) {
|
||||
demo_regs.worker_stop();
|
||||
demo_regs.uninstall();
|
||||
demo_regs.enabled.set(false);
|
||||
}
|
||||
active = false;
|
||||
}
|
||||
unibusadapter->worker_stop();
|
||||
unibusadapter->enabled.set(false) ;
|
||||
|
||||
// Switch off bus drivers
|
||||
buslatches_output_enable(false);
|
||||
|
||||
Reference in New Issue
Block a user