diff --git a/10.01_base/2_src/arm/device.cpp b/10.01_base/2_src/arm/device.cpp index 998d617..60eb782 100644 --- a/10.01_base/2_src/arm/device.cpp +++ b/10.01_base/2_src/arm/device.cpp @@ -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 @@ -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::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 diff --git a/10.01_base/2_src/arm/device.hpp b/10.01_base/2_src/arm/device.hpp index c37b3df..f07a273 100644 --- a/10.01_base/2_src/arm/device.hpp +++ b/10.01_base/2_src/arm/device.hpp @@ -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 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 }; diff --git a/10.01_base/2_src/arm/parameter.cpp b/10.01_base/2_src/arm/parameter.cpp index 36d92b2..430a88e 100644 --- a/10.01_base/2_src/arm/parameter.cpp +++ b/10.01_base/2_src/arm/parameter.cpp @@ -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() { diff --git a/10.01_base/2_src/arm/parameter.hpp b/10.01_base/2_src/arm/parameter.hpp index 825bf24..6f1b9b3 100644 --- a/10.01_base/2_src/arm/parameter.hpp +++ b/10.01_base/2_src/arm/parameter.hpp @@ -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) ; }; diff --git a/10.01_base/2_src/arm/storagecontroller.cpp b/10.01_base/2_src/arm/storagecontroller.cpp index d2cc43c..42255cc 100644 --- a/10.01_base/2_src/arm/storagecontroller.cpp +++ b/10.01_base/2_src/arm/storagecontroller.cpp @@ -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::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::iterator it; - for (it = storagedrives.begin(); it != storagedrives.end(); it++) { - (*it)->worker_stop(); - } -} - diff --git a/10.01_base/2_src/arm/storagecontroller.hpp b/10.01_base/2_src/arm/storagecontroller.hpp index 45c7c6b..6a3891b 100644 --- a/10.01_base/2_src/arm/storagecontroller.hpp +++ b/10.01_base/2_src/arm/storagecontroller.hpp @@ -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; }; diff --git a/10.01_base/2_src/arm/storagedrive.cpp b/10.01_base/2_src/arm/storagedrive.cpp index d006441..670b863 100644 --- a/10.01_base/2_src/arm/storagedrive.cpp +++ b/10.01_base/2_src/arm/storagedrive.cpp @@ -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); diff --git a/10.01_base/2_src/arm/unibusadapter.cpp b/10.01_base/2_src/arm/unibusadapter.cpp index 14669f1..1ac1206 100644 --- a/10.01_base/2_src/arm/unibusadapter.cpp +++ b/10.01_base/2_src/arm/unibusadapter.cpp @@ -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(context); - bus->dma_worker(); + bus->dma_worker(); return nullptr; } @@ -115,11 +97,8 @@ unibusadapter_c *unibusadapter; // another Singleton // is registered in device_c.list ... 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(this)); + int status = pthread_create(&_busWorker_pthread, &attribs, &bus_worker, + reinterpret_cast(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. diff --git a/10.01_base/2_src/arm/unibusadapter.hpp b/10.01_base/2_src/arm/unibusadapter.hpp index 043d40a..c569aff 100644 --- a/10.01_base/2_src/arm/unibusadapter.hpp +++ b/10.01_base/2_src/arm/unibusadapter.hpp @@ -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 _dmaRequests; - std::queue _irqRequests; - pthread_t _busWorker_pthread; - pthread_cond_t _busWakeup_cond; - pthread_cond_t _requestFinished_cond; - pthread_mutex_t _busWorker_mutex; + + std::queue _dmaRequests; + std::queue _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 diff --git a/10.01_base/2_src/arm/unibusdevice.cpp b/10.01_base/2_src/arm/unibusdevice.cpp index 8c1ab9f..123ff05 100644 --- a/10.01_base/2_src/arm/unibusdevice.cpp +++ b/10.01_base/2_src/arm/unibusdevice.cpp @@ -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 //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); } diff --git a/10.01_base/2_src/arm/unibusdevice.hpp b/10.01_base/2_src/arm/unibusdevice.hpp index 3348e30..1722047 100644 --- a/10.01_base/2_src/arm/unibusdevice.hpp +++ b/10.01_base/2_src/arm/unibusdevice.hpp @@ -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); }; diff --git a/10.02_devices/2_src/cpu.cpp b/10.02_devices/2_src/cpu.cpp index d004ed0..368e250 100644 --- a/10.02_devices/2_src/cpu.cpp +++ b/10.02_devices/2_src/cpu.cpp @@ -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 } diff --git a/10.02_devices/2_src/cpu.hpp b/10.02_devices/2_src/cpu.hpp index 05e34f5..4197557 100644 --- a/10.02_devices/2_src/cpu.hpp +++ b/10.02_devices/2_src/cpu.hpp @@ -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; }; diff --git a/10.02_devices/2_src/cpu20/ka11.c b/10.02_devices/2_src/cpu20/ka11.c index 4552f83..fa4fb48 100644 --- a/10.02_devices/2_src/cpu20/ka11.c +++ b/10.02_devices/2_src/cpu20/ka11.c @@ -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; diff --git a/10.02_devices/2_src/demo_io.cpp b/10.02_devices/2_src/demo_io.cpp index 0180b6a..42c93de 100644 --- a/10.02_devices/2_src/demo_io.cpp +++ b/10.02_devices/2_src/demo_io.cpp @@ -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 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 // 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/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 } diff --git a/10.02_devices/2_src/demo_io.hpp b/10.02_devices/2_src/demo_io.hpp index bf062c7..cefdec0 100644 --- a/10.02_devices/2_src/demo_io.hpp +++ b/10.02_devices/2_src/demo_io.hpp @@ -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; }; diff --git a/10.02_devices/2_src/demo_regs.cpp b/10.02_devices/2_src/demo_regs.cpp index 2b55ae0..4e59d25 100644 --- a/10.02_devices/2_src/demo_regs.cpp +++ b/10.02_devices/2_src/demo_regs.cpp @@ -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 } diff --git a/10.02_devices/2_src/demo_regs.hpp b/10.02_devices/2_src/demo_regs.hpp index b8b0b2d..ad33873 100644 --- a/10.02_devices/2_src/demo_regs.hpp +++ b/10.02_devices/2_src/demo_regs.hpp @@ -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; }; diff --git a/10.02_devices/2_src/mscp_drive.cpp b/10.02_devices/2_src/mscp_drive.cpp index 4d07a45..72772bb 100644 --- a/10.02_devices/2_src/mscp_drive.cpp +++ b/10.02_devices/2_src/mscp_drive.cpp @@ -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 #include @@ -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(_rctData.get() + rctBlockNumber * GetBlockSize()), - reinterpret_cast(buffer), - GetBlockSize()); + memcpy(reinterpret_cast(_rctData.get() + rctBlockNumber * GetBlockSize()), + reinterpret_cast(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(buffer), - reinterpret_cast(_rctData.get() + rctBlockNumber * GetBlockSize()), - GetBlockSize()); - - return buffer; -} + memcpy(reinterpret_cast(buffer), + reinterpret_cast(_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(_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(_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(); } - diff --git a/10.02_devices/2_src/mscp_drive.hpp b/10.02_devices/2_src/mscp_drive.hpp index bc221b3..04dd154 100644 --- a/10.02_devices/2_src/mscp_drive.hpp +++ b/10.02_devices/2_src/mscp_drive.hpp @@ -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 _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 _rctData; }; diff --git a/10.02_devices/2_src/mscp_server.cpp b/10.02_devices/2_src/mscp_server.cpp index 03c0f78..b5e3c40 100644 --- a/10.02_devices/2_src/mscp_server.cpp +++ b/10.02_devices/2_src/mscp_server.cpp @@ -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. diff --git a/10.02_devices/2_src/mscp_server.hpp b/10.02_devices/2_src/mscp_server.hpp index f27977e..8b72757 100644 --- a/10.02_devices/2_src/mscp_server.hpp +++ b/10.02_devices/2_src/mscp_server.hpp @@ -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); diff --git a/10.02_devices/2_src/panel.cpp b/10.02_devices/2_src/panel.cpp index a824219..c226c99 100644 --- a/10.02_devices/2_src/panel.cpp +++ b/10.02_devices/2_src/panel.cpp @@ -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 @@ -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."); } diff --git a/10.02_devices/2_src/panel.hpp b/10.02_devices/2_src/panel.hpp index e99128c..dd93eb8 100644 --- a/10.02_devices/2_src/panel.hpp +++ b/10.02_devices/2_src/panel.hpp @@ -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 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); diff --git a/10.02_devices/2_src/rk05.cpp b/10.02_devices/2_src/rk05.cpp index 18c970e..850a7c2 100755 --- a/10.02_devices/2_src/rk05.cpp +++ b/10.02_devices/2_src/rk05.cpp @@ -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 @@ -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(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(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(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(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); } diff --git a/10.02_devices/2_src/rk05.hpp b/10.02_devices/2_src/rk05.hpp index e4f2fc8..bf7085f 100755 --- a/10.02_devices/2_src/rk05.hpp +++ b/10.02_devices/2_src/rk05.hpp @@ -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; diff --git a/10.02_devices/2_src/rk11.cpp b/10.02_devices/2_src/rk11.cpp index df4a374..40afe2a 100755 --- a/10.02_devices/2_src/rk11.cpp +++ b/10.02_devices/2_src/rk11.cpp @@ -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; diff --git a/10.02_devices/2_src/rk11.hpp b/10.02_devices/2_src/rk11.hpp index 718fcb6..78c3eda 100755 --- a/10.02_devices/2_src/rk11.hpp +++ b/10.02_devices/2_src/rk11.hpp @@ -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; diff --git a/10.02_devices/2_src/rl0102.cpp b/10.02_devices/2_src/rl0102.cpp index b641733..3615085 100644 --- a/10.02_devices/2_src/rl0102.cpp +++ b/10.02_devices/2_src/rl0102.cpp @@ -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 @@ -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(); diff --git a/10.02_devices/2_src/rl0102.hpp b/10.02_devices/2_src/rl0102.hpp index 496602a..fa5a749 100644 --- a/10.02_devices/2_src/rl0102.hpp +++ b/10.02_devices/2_src/rl0102.hpp @@ -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; diff --git a/10.02_devices/2_src/rl11.cpp b/10.02_devices/2_src/rl11.cpp index 5d6b3bf..8ad41ce 100644 --- a/10.02_devices/2_src/rl11.cpp +++ b/10.02_devices/2_src/rl11.cpp @@ -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 diff --git a/10.02_devices/2_src/rl11.hpp b/10.02_devices/2_src/rl11.hpp index 26a1dbd..73ef9ef 100644 --- a/10.02_devices/2_src/rl11.hpp +++ b/10.02_devices/2_src/rl11.hpp @@ -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 diff --git a/10.02_devices/2_src/uda.cpp b/10.02_devices/2_src/uda.cpp index b6af444..8a133be 100644 --- a/10.02_devices/2_src/uda.cpp +++ b/10.02_devices/2_src/uda.cpp @@ -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. diff --git a/10.02_devices/2_src/uda.hpp b/10.02_devices/2_src/uda.hpp index aa9557f..367f41d 100644 --- a/10.02_devices/2_src/uda.hpp +++ b/10.02_devices/2_src/uda.hpp @@ -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( diff --git a/10.03_app_demo/2_src/menu_device_exercisers.cpp b/10.03_app_demo/2_src/menu_device_exercisers.cpp index b536f8a..577f734 100644 --- a/10.03_app_demo/2_src/menu_device_exercisers.cpp +++ b/10.03_app_demo/2_src/menu_device_exercisers.cpp @@ -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 diff --git a/10.03_app_demo/2_src/menu_devices.cpp b/10.03_app_demo/2_src/menu_devices.cpp index c855121..10806f2 100644 --- a/10.03_app_demo/2_src/menu_devices.cpp +++ b/10.03_app_demo/2_src/menu_devices.cpp @@ -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 Enable a device\n"); + printf("dis Disable device\n"); printf("sd Select \"current device\"\n"); + if (cur_device) { printf("p Set parameter value of current device\n"); printf("p 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::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::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(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 diff --git a/10.03_app_demo/2_src/menu_interrupts.cpp b/10.03_app_demo/2_src/menu_interrupts.cpp index acc078e..f3a352a 100644 --- a/10.03_app_demo/2_src/menu_interrupts.cpp +++ b/10.03_app_demo/2_src/menu_interrupts.cpp @@ -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; } diff --git a/10.03_app_demo/2_src/menu_masterslave.cpp b/10.03_app_demo/2_src/menu_masterslave.cpp index 2bb5595..1b2a841 100644 --- a/10.03_app_demo/2_src/menu_masterslave.cpp +++ b/10.03_app_demo/2_src/menu_masterslave.cpp @@ -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);