diff --git a/10.01_base/2_src/arm/ddrmem.cpp b/10.01_base/2_src/arm/ddrmem.cpp index 80eaafc..d77b44a 100644 --- a/10.01_base/2_src/arm/ddrmem.cpp +++ b/10.01_base/2_src/arm/ddrmem.cpp @@ -1,28 +1,28 @@ /* ddrmem.cpp - Control the shared DDR RAM - used for UNIBUS memory - 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 #include @@ -42,10 +42,9 @@ ddrmem_c *ddrmem; ddrmem_c::ddrmem_c() { - log_label = "DDRMEM" ; + log_label = "DDRMEM"; } - // check allocated memory and print info void ddrmem_c::info() { INFO("Shared DDR memory: %u bytes available, %u bytes needed.", len, sizeof(ddrmem_t)); @@ -130,7 +129,7 @@ void ddrmem_c::fill_pattern(void) { void ddrmem_c::fill_pattern_pru(void) { // ddrmem_base_physical and _len already set assert((uint32_t )mailbox->ddrmem_base_physical == base_physical); - mailbox_execute(ARM2PRU_DDR_FILL_PATTERN, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_DDR_FILL_PATTERN); } // set corrected values for emulated memory range @@ -183,6 +182,6 @@ void ddrmem_c::unibus_slave(uint32_t startaddr, uint32_t endaddr) { s = inputline(buf, sizeof(buf), NULL); } while (strlen(s) == 0); // clearing arm2pru_req stops the emulation - mailbox_execute(ARM2PRU_NONE, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_NONE); } diff --git a/10.01_base/2_src/arm/device.cpp b/10.01_base/2_src/arm/device.cpp index 998d617..e7bcb1f 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 @@ -55,30 +55,28 @@ list device_c::mydevices; // called on cancel and exit() static void device_worker_pthread_cleanup_handler(void *context) { - device_c *device = (device_c *) context; + device_worker_c *worker_instance = (device_worker_c *) context; + device_c *device = worker_instance->device; #define this device // make INFO work - device->worker_terminate = false; - device->worker_terminated = true; // ended on its own or on worker_terminate - INFO("Worker terminated for device %s.", device->name.value.c_str()); - device->worker_terminate = false; - device->worker_terminated = true; // ended on its own or on worker_terminate + worker_instance->running = false; + INFO("%s::worker(%d) terminated.", device->name.value.c_str(), worker_instance->instance); // printf("cleanup for device %s\n", device->name.value.c_str()) ; #undef this } static void *device_worker_pthread_wrapper(void *context) { - device_c *device = (device_c *) context; - int oldstate ; // not used + device_worker_c *worker_instance = (device_worker_c *) context; + device_c *device = worker_instance->device; + 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! - 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 + INFO("%s::worker(%u) started", device->name.value.c_str(), worker_instance->instance); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldstate); //ASYNCH not allowed! + worker_instance->running = true; + pthread_cleanup_push(device_worker_pthread_cleanup_handler, worker_instance); + device->worker(worker_instance->instance); + pthread_cleanup_pop(1); // call cleanup_handler on regular exit // not reached on pthread_cancel() #undef this return NULL; @@ -92,17 +90,21 @@ device_c::device_c() { parent = NULL; - worker_terminate = false; - worker_terminated = true; + // init workers + workers_terminate = false; + set_workers_count(1); // default: 1 worker // do not link params to this device over param constructor // 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 +127,40 @@ device_c::~device_c() { mydevices.erase(p); } +// default is 1 worker. Default: null function, terminates if not overwritten by child class +// can be set > 1 if device needs multiple worker instances +// only to be called in constructors +void device_c::set_workers_count(unsigned workers_count) { + workers.resize(workers_count); + for (unsigned instance = 0; instance < workers_count; instance++) { + device_worker_c *worker_instance = &workers[instance]; + worker_instance->device = this; + worker_instance->instance = instance; + worker_instance->running = false; + } +} +bool device_c::on_param_changed(parameter_c *param) { + if (param == &enabled) { + if (enabled.new_value) + workers_start(); + else + workers_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 @@ -251,51 +286,59 @@ void device_c::worker_init_realtime_priority(enum worker_priority_e priority) { /* worker_start - executes threads * * use of C++11 std::thread failed: - * thead.join() crashes with random system_errors + * thread.join() crashes with random system_errors * So use classic "pthread" wrapper + * TODO: crash still there, was caused by cross compile -> back to C++11 threads! */ -void device_c::worker_start(void) { - worker_terminate = false; - { +void device_c::workers_start(void) { + workers_terminate = false; + for (unsigned instance = 0; instance < workers.size(); instance++) { + device_worker_c *worker_instance = &workers[instance]; + worker_instance->running = true; // start pthread pthread_attr_t attr; pthread_attr_init(&attr); // pthread_attr_setstacksize(&attr, 1024*1024); - int status = pthread_create(&worker_pthread, &attr, &device_worker_pthread_wrapper, - (void *) this); + int status = pthread_create(&worker_instance->pthread, &attr, + &device_worker_pthread_wrapper, (void *) worker_instance); if (status != 0) { FATAL("Failed to create pthread with status = %d", status); } - pthread_attr_destroy(&attr) ; // why? + pthread_attr_destroy(&attr); // why? } } -void device_c::worker_stop(void) { +void device_c::workers_stop(void) { timeout_c timeout; int status; - if (worker_terminated) { - DEBUG("%s.worker_stop(): already terminated.", name.name.c_str()); - return; - } - INFO("Waiting for %s.worker() to stop ...", name.value.c_str()); - worker_terminate = true; - // 100ms - timeout.wait_ms(100); - // worker_wrapper must do "worker_terminated = true;" on exit - if (!worker_terminated) { - // 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); - } - // !! If crosscompling: this causes a crash in the worker thread - // !! at pthread_cond_wait() or other cancellation points. - // !! No problem for compiles build on BBB itself. - status = pthread_join(worker_pthread, NULL); - if (status != 0) { - FATAL("Failed to join worker_pthread with status = %d", status); + workers_terminate = true; // global signal to all instances + timeout.wait_ms(100); + + for (unsigned instance = 0; instance < workers.size(); instance++) { + device_worker_c *worker_instance = &workers[instance]; + +// if (!worker_instance->running) { +// DEBUG("%s.worker_stop(%u): already terminated.", name.name.c_str(), instance); +// return; +// } + if (worker_instance->running) { + INFO("%s.worker(%u) not cooperative: cancel it ...", name.value.c_str(), instance); + // if thread is hanging in pthread_cond_wait(): send a cancellation request + status = pthread_cancel(worker_instance->pthread); + if (status != 0) + FATAL("Failed to send cancellation request to worker_pthread with status = %d", + status); + } + + // !! If crosscompling: this causes a crash in the worker thread + // !! at pthread_cond_wait() or other cancellation points. + // !! No problem for compiles build on BBB itself. + status = pthread_join(worker_instance->pthread, NULL); + if (status != 0) { + FATAL("Failed to join worker_pthread with status = %d", status); + } } } diff --git a/10.01_base/2_src/arm/device.hpp b/10.01_base/2_src/arm/device.hpp index c37b3df..3f850fe 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_ @@ -32,79 +32,102 @@ #include using namespace std; +#include "utils.hpp" #include "parameter.hpp" #include "logsource.hpp" +// instance of a worker thread for a device +class device_c; +class device_worker_c { +public: + // thread for this worker instance + device_c *device; // link to parent + unsigned instance; // id of this running instance + pthread_t pthread;bool running; // run state +}; + // abstract unibus device // maybe mass storage controller, storage drive or other device // 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 workers_start(void); + void workers_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 + none_rt, // lower than all RT priorities + rt_device, // all controller 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(); + device_c(void); virtual ~device_c(); // class with virtual functions should have virtual destructors + void set_workers_count(unsigned workers_count); + + 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() + // worker threads: multiple instances of single worker() are running in parallel + // device must implement a worker(instance) { + // switch((instance) { ... } } + // 'instance' from 0 .. worker_count-1 + volatile bool workers_terminate; // cmd flag to all worker() instances + vector workers; - pthread_t worker_pthread; - void worker_start(void); - void worker_stop(void); - virtual void worker(void) = 0; // background worker function + // default background worker function for devices without need. + virtual void worker(unsigned instance) { + UNUSED(instance); + printf("Warning: default device_c::worker() called, better use set_worker_count(0) "); + } }; #endif diff --git a/10.01_base/2_src/arm/gpios.cpp b/10.01_base/2_src/arm/gpios.cpp index ae67409..5611b8b 100644 --- a/10.01_base/2_src/arm/gpios.cpp +++ b/10.01_base/2_src/arm/gpios.cpp @@ -616,7 +616,7 @@ void buslatches_register() { // PRU1 does it void buslatches_pru_reset() { assert(pru->prucode_id == pru_c::PRUCODE_TEST); - mailbox_execute(ARM2PRU_BUSLATCH_INIT, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_INIT); } // read the REG_DATIN[0..7] pins @@ -627,7 +627,7 @@ unsigned buslatches_getval(unsigned reg_sel) { while (mailbox->buslatch.addr != reg_sel) ; // cache ! - mailbox_execute(ARM2PRU_BUSLATCH_GET, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_GET); return mailbox->buslatch.val; // PRU1 has put the result here } @@ -639,7 +639,7 @@ void buslatches_setval(unsigned reg_sel, unsigned bitmask, unsigned val) { mailbox->buslatch.bitmask = bitmask & 0xff; mailbox->buslatch.val = val; - mailbox_execute(ARM2PRU_BUSLATCH_SET, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_SET); } // some pattern tests on a register latch @@ -849,7 +849,7 @@ void buslatches_test_simple_pattern_multi(unsigned pattern) { mailbox->buslatch_exerciser.pattern = (pass_no % MAILBOX_BUSLATCH_EXERCISER_PATTERN_COUNT); - mailbox_execute(ARM2PRU_BUSLATCH_EXERCISER, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_BUSLATCH_EXERCISER); // check: mailbox readvalues == write values ? for (unsigned i = 0; i < BUSLATCHES_COUNT; i++) { diff --git a/10.01_base/2_src/arm/gpios.hpp b/10.01_base/2_src/arm/gpios.hpp index 72eb6e9..f142a77 100644 --- a/10.01_base/2_src/arm/gpios.hpp +++ b/10.01_base/2_src/arm/gpios.hpp @@ -83,11 +83,11 @@ typedef struct { #define MAX_GPIOCOUNT 100 // test pins -// set 1 -> pin auf H -#define SET_DEBUG_PIN0(n) GPIO_SETVAL(gpios->led[0], !!(n)) -#define SET_DEBUG_PIN1(n) GPIO_SETVAL(gpios->led[1], !!(n)) -#define SET_DEBUG_PIN2(n) GPIO_SETVAL(gpios->led[2], !!(n)) -#define SET_DEBUG_PIN3(n) GPIO_SETVAL(gpios->led[3], !!(n)) +// SET(1) -> pin auf H, LED OFF +#define ARM_DEBUG_PIN0(n) GPIO_SETVAL(gpios->led[0], !!(n)) +#define ARM_DEBUG_PIN1(n) GPIO_SETVAL(gpios->led[1], !!(n)) +#define ARM_DEBUG_PIN2(n) GPIO_SETVAL(gpios->led[2], !!(n)) +#define ARM_DEBUG_PIN3(n) GPIO_SETVAL(gpios->led[3], !!(n)) class gpios_c: public logsource_c { private: @@ -117,7 +117,6 @@ public: void test_loopback(void); }; - #define BUSLATCHES_COUNT 8 // save current state uf gpios and registers, @@ -235,7 +234,7 @@ void buslatches_setval(unsigned reg_sel, unsigned bitmask, unsigned val); unsigned buslatches_getval(unsigned reg_sel); void buslatches_test_simple_pattern(unsigned pattern, unsigned reg_sel); -void buslatches_test_simple_pattern_multi( unsigned pattern); +void buslatches_test_simple_pattern_multi(unsigned pattern); void buslatches_test_timing(uint8_t addr_0_7, uint8_t addr_8_15, uint8_t data_0_7, uint8_t data_8_15); diff --git a/10.01_base/2_src/arm/iopageregister.cpp b/10.01_base/2_src/arm/iopageregister.cpp index 7162217..66aa80a 100644 --- a/10.01_base/2_src/arm/iopageregister.cpp +++ b/10.01_base/2_src/arm/iopageregister.cpp @@ -1,28 +1,28 @@ /* iopageregister.cpp: handle ARM-PRU shared struct with device register descriptors - 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 + */ #define _IOPAGEREGISTER_CPP_ diff --git a/10.01_base/2_src/arm/mailbox.cpp b/10.01_base/2_src/arm/mailbox.cpp index 40c93f6..4e0d5fb 100644 --- a/10.01_base/2_src/arm/mailbox.cpp +++ b/10.01_base/2_src/arm/mailbox.cpp @@ -1,29 +1,28 @@ /* mailbox.cpp: datastructs common to ARM and PRU - 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 + */ #define _MAILBOX_CPP_ @@ -39,8 +38,7 @@ // is located in PRU 12kb shared memory. // address symbol "" fetched from linker map -volatile mailbox_t *mailbox; - +volatile mailbox_t *mailbox = NULL; // Init all fields, most to 0's int mailbox_connect(void) { @@ -57,7 +55,7 @@ int mailbox_connect(void) { // now ARM and PRU can access the mailbox - memset((void*)mailbox, 0, sizeof(mailbox_t)) ; + memset((void*) mailbox, 0, sizeof(mailbox_t)); // tell PRU location of shared DDR RAM mailbox->ddrmem_base_physical = (ddrmem_t *) ddrmem->base_physical; @@ -65,11 +63,9 @@ int mailbox_connect(void) { return 0; } - - void mailbox_print(void) { printf("INFO: Content of mailbox to PRU:\n" - "arm2pru: req=0x%x, resp=0x%x\n", mailbox->arm2pru_req, mailbox->arm2pru_resp); + "arm2pru: req=0x%x, resp=0x%x\n", mailbox->arm2pru_req, mailbox->arm2pru_resp); } /* simulate simple register accesses: @@ -103,21 +99,19 @@ void mailbox_test1() { } /* start cmd to PRU via mailbox. Wait until ready - * mailbox union members must have been filled + * mailbox union members must have been filled. */ -uint32_t xxx ; -void mailbox_execute(uint8_t request, uint8_t stopcode) { +uint32_t xxx; +void mailbox_execute(uint8_t request) { // write to arm2pru_req must be last memory operation __sync_synchronize(); + while (mailbox->arm2pru_req != ARM2PRU_NONE) + ; // wait to complete + mailbox->arm2pru_req = request; // go! do { - xxx = mailbox-> arm2pru_req ; -if (mailbox->events.eventmask) { - // event not processed? will hang DMA. -// printf("WARNING: Unprocessed mailbox.events.eventmask = 0x%x\n", (unsigned) mailbox->events.eventmask) ; -// mailbox->events.eventmask = 0 ; -} - } while (xxx != stopcode) ; - while (mailbox->arm2pru_req != stopcode) + xxx = mailbox->arm2pru_req; + } while (xxx != ARM2PRU_NONE); + while (mailbox->arm2pru_req != ARM2PRU_NONE) ; // wait until processed } diff --git a/10.01_base/2_src/arm/memoryimage.cpp b/10.01_base/2_src/arm/memoryimage.cpp index 0082f88..63ed502 100644 --- a/10.01_base/2_src/arm/memoryimage.cpp +++ b/10.01_base/2_src/arm/memoryimage.cpp @@ -2,37 +2,37 @@ Copyright (c) 2017, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com - All rights reserved. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. - - Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - 12-nov-2018 JH entered beta phase - 18-Jun-2017 JH created + 12-nov-2018 JH entered beta phase + 18-Jun-2017 JH created */ #define _MEMORYIMAGE_CPP_ @@ -132,7 +132,7 @@ bool memoryimage_c::load_binary(const char *fname) { unsigned wordidx, n; fin = fopen(fname, "rb"); if (!fin) { - printf("%s\n",fileErrorText("Error opening file %s for read", fname)); + printf("%s\n", fileErrorText("Error opening file %s for read", fname)); return false; } // try to read max address range, shorter files are OK @@ -149,7 +149,7 @@ void memoryimage_c::save_binary(const char *fname, unsigned bytecount) { unsigned n; fout = fopen(fname, "wb"); if (!fout) { - printf("%s\n",fileErrorText("Error opening file %s for write", fname)); + printf("%s\n", fileErrorText("Error opening file %s for write", fname)); return; } // try to read max address range, shorter files are OK @@ -204,7 +204,7 @@ bool memoryimage_c::load_addr_value_text(const char *fname) { fin = fopen(fname, "r"); if (!fin) { - printf("%s\n", fileErrorText("Error opening file %s for write", fname)) ; + printf("%s\n", fileErrorText("Error opening file %s for write", fname)); return false; } entry_address = MEMORY_ADDRESS_INVALID; // not known @@ -467,7 +467,7 @@ bool memoryimage_c::load_macro11_listing(const char *fname, const char *entrylab tokenidx = 0; // # of number processed ready = false; addr = 0; - line_addr = 0 ; + line_addr = 0; while (!ready) { while (*tp && isspace(*tp)) tp++; // skip white space @@ -538,7 +538,7 @@ bool memoryimage_c::load_papertape(const char *fname) { entry_address = MEMORY_ADDRESS_INVALID; // not yet known stream_byte_index = 0; - block_byte_size = addr = 0 ; // -Wmaybe-uninitialized + block_byte_size = addr = 0; // -Wmaybe-uninitialized while (!feof(fin)) { b = fgetc(fin); // ERROR("[0x%04x] state=%d b=0x%02x sum=0x%02x block_byte_idx=%d", diff --git a/10.01_base/2_src/arm/memoryimage.hpp b/10.01_base/2_src/arm/memoryimage.hpp index 3fd8502..2868aaf 100644 --- a/10.01_base/2_src/arm/memoryimage.hpp +++ b/10.01_base/2_src/arm/memoryimage.hpp @@ -2,37 +2,37 @@ Copyright (c) 2017, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com - All rights reserved. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. - - Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - 12-nov-2018 JH entered beta phase - 18-Jun-2017 JH created + 12-nov-2018 JH entered beta phase + 18-Jun-2017 JH created */ @@ -83,7 +83,7 @@ public: int entry_address; // start address, if found. MEMORY_ADDRESS_INVALID = invalid memoryimage_c() { - log_label = "MEMIMG" ; + log_label = "MEMIMG"; } void init(void); @@ -94,12 +94,11 @@ public: assert_address(addr); return valid[wordidx]; } - void fill(uint16_t fillword) ; + void fill(uint16_t fillword); void get_addr_range(unsigned *first, unsigned* last); unsigned get_word_count(void); - void set_addr_range(unsigned first, unsigned last) ; - + void set_addr_range(unsigned first, unsigned last); bool load_addr_value_text(const char *fname);bool load_macro11_listing(const char *fname, const char *entrylabel);bool load_papertape(const char *fname);bool load_binary( diff --git a/10.01_base/2_src/arm/parameter.cpp b/10.01_base/2_src/arm/parameter.cpp index 36d92b2..8e862d0 100644 --- a/10.01_base/2_src/arm/parameter.cpp +++ b/10.01_base/2_src/arm/parameter.cpp @@ -32,9 +32,8 @@ #include "device.hpp" #define _PARAMETER_CPP_ - parameter_c::parameter_c(parameterized_c *parameterized, string name, string shortname, - bool readonly, string unit, string format, string info) { +bool readonly, string unit, string format, string info) { this->parameterized = parameterized; this->name = name; this->shortname = shortname; @@ -53,8 +52,7 @@ parameter_c::~parameter_c() { // to be implemented in subclass void parameter_c::parse(string text) { - throw bad_parameter_parse( - "parameter_c::parse(" + text + ") to be implemented in subclass"); + throw bad_parameter_parse("parameter_c::parse(" + text + ") to be implemented in subclass"); } // convert to text @@ -73,16 +71,21 @@ 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() { @@ -90,13 +93,24 @@ string *parameter_string_c::render() { return &printbuffer; } -parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name, string shortname, +parameter_bool_c::parameter_bool_c(parameterized_c *parameterized, string name, + string shortname, bool readonly, string info) : parameter_c(parameterized, name, shortname, readonly, "", "", info) { 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 +126,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 +145,17 @@ 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) @@ -141,15 +163,11 @@ void parameter_unsigned_c::parse(string text) { TRIM_STRING(text); new_value = strtol(text.c_str(), &endptr, base); if (*endptr) - throw bad_parameter_parse( - "Format error in \"" + text + "\" at \"" + *endptr + "\""); + throw bad_parameter_parse("Format error in \"" + text + "\" at \"" + *endptr + "\""); 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 +186,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) @@ -175,15 +203,11 @@ void parameter_unsigned64_c::parse(string text) { TRIM_STRING(text); new_value = strtoll(text.c_str(), &endptr, base); if (*endptr) - throw bad_parameter_parse( - "Format error in \"" + text + "\" at \"" + *endptr + "\""); + throw bad_parameter_parse("Format error in \"" + text + "\" at \"" + *endptr + "\""); 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() { @@ -193,7 +217,6 @@ string *parameter_unsigned64_c::render() { return &printbuffer; } - // add reference to parameter. It will be automatically deleted parameter_c *parameterized_c::param_add(parameter_c *param) { parameter.push_back(param); @@ -216,4 +239,3 @@ parameter_c *parameterized_c::param_by_name(string name) { return NULL; } - diff --git a/10.01_base/2_src/arm/parameter.hpp b/10.01_base/2_src/arm/parameter.hpp index 825bf24..8324bca 100644 --- a/10.01_base/2_src/arm/parameter.hpp +++ b/10.01_base/2_src/arm/parameter.hpp @@ -1,28 +1,28 @@ /* parameter.hpp: collection of typed name/value pairs, used by devices and other objects - Copyright (c) 2018-2019, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2018-2019, 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. - 16-mar-2019 JH unlinked from devices - 12-nov-2018 JH entered beta phase -*/ + 16-mar-2019 JH unlinked from devices + 12-nov-2018 JH entered beta phase + */ #ifndef _PARAMETER_HPP_ #define _PARAMETER_HPP_ @@ -33,43 +33,45 @@ #include using namespace std; - class bad_parameter: public exception { - private: - string message; - public: - bad_parameter(string message) { - (this->message = message); - } - const char* what() const noexcept { - return message.c_str(); - } -} ; +private: + string message; +public: + bad_parameter(string message) { + (this->message = message); + } + const char* what() const noexcept { + return message.c_str(); + } +}; class bad_parameter_parse: public bad_parameter { public: - bad_parameter_parse(string message): bad_parameter(message) { } + bad_parameter_parse(string message) : + bad_parameter(message) { + } }; class bad_parameter_check: public bad_parameter { public: - bad_parameter_check(string message): bad_parameter(message) { } + bad_parameter_check(string message) : + bad_parameter(message) { + } }; - class bad_parameter_readonly: public bad_parameter { public: - bad_parameter_readonly(string message): bad_parameter(message) { } + bad_parameter_readonly(string message) : + bad_parameter(message) { + } }; - - -class parameterized_c ; +class parameterized_c; class parameter_c { private: public: - parameterized_c *parameterized ; // link to parent object + parameterized_c *parameterized; // link to parent object string name; string shortname; bool readonly; @@ -78,7 +80,8 @@ public: string format; // printf, scanf // parameter_c(); - parameter_c(parameterized_c *parameterized, string name, string shortname, bool readonly, string unit, string format, string info); + parameter_c(parameterized_c *parameterized, string name, string shortname, bool readonly, + string unit, string format, string info); virtual ~parameter_c(); // class with virtual functions should have virtual destructors // convert text to value. result: ok? @@ -88,30 +91,32 @@ public: virtual string *render(void); }; - 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(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(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 +128,14 @@ 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); + 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,20 +147,21 @@ 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); + 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); }; - // objects with parameters are "parameterized" and inherit this. class parameterized_c { - public: - vector parameter; - +public: + vector parameter; + // register parameter parameter_c *param_add(parameter_c *param); @@ -161,11 +169,10 @@ class parameterized_c { parameter_c *param_by_name(string name); // sort? - + // called after param value changed. // result: false = "new_value" not excepted, error printed. - virtual bool on_param_changed(parameter_c *param) = 0 ; -} ; - + virtual bool on_param_changed(parameter_c *param) = 0; +}; #endif diff --git a/10.01_base/2_src/arm/priorityrequest.cpp b/10.01_base/2_src/arm/priorityrequest.cpp new file mode 100644 index 0000000..309645f --- /dev/null +++ b/10.01_base/2_src/arm/priorityrequest.cpp @@ -0,0 +1,107 @@ +/* priorityrequest.cpp: DMA or iNTR request of an device + + Copyright (c) 2019 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: + + 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. + + + jul-2019 JH start: multiple parallel arbitration levels + */ + +#include +#include + +#include "unibusdevice.hpp" +#include "priorityrequest.hpp" + +priority_request_c::priority_request_c(unibusdevice_c *device) { + this->log_label = "REQ"; + this->device = device; + this->complete = false; + this->executing_on_PRU = false; + this->slot = 0xff; // uninitialized, asserts() if used + complete_mutex = PTHREAD_MUTEX_INITIALIZER; + //complete_cond = PTHREAD_COND_INITIALIZER; // PRU signal notifies request on completeness +} + +priority_request_c::~priority_request_c() { + // not used, but need some virtual func for dynamic_cast() +} + +void priority_request_c::set_priority_slot(uint8_t priority_slot) { + assert(priority_slot > 0); // 0 reserved + assert(priority_slot < PRIORITY_SLOT_COUNT); + unibusdevice_c *ubdevice = unibusdevice_c::find_by_request_slot(priority_slot); + if (ubdevice && ubdevice != this->device) { + WARNING("Slot %u already used by device %s", (unsigned) priority_slot, ubdevice->name.value.c_str()); + } + this->slot = priority_slot; + // todo: check for collision with all other devices, all other requests +} + +// create invalid requests, is setup by unibusadapter +dma_request_c::dma_request_c(unibusdevice_c *device) : + priority_request_c(device) { + this->level_index = PRIORITY_LEVEL_INDEX_NPR; + this->success = false; + // register request for device + if (device) { + device->dma_requests.push_back(this); + } +} + +dma_request_c::~dma_request_c() { + if (device) { + // find and erase from device's request list + std::vector::iterator it = std::find(device->dma_requests.begin(), + device->dma_requests.end(), this); + device->dma_requests.erase(it); + } +} + +// create invalid requests, is setup by unibusadapter +intr_request_c::intr_request_c(unibusdevice_c *device) : + priority_request_c(device) { + // convert UNIBUS level 4,5,6,7 to internal priority, see REQUEST_INDEX_* + this->level_index = 0xff; // uninitialized, asserts() if used + this->vector = 0xffff; // uninitialized, asserts() if used + this->signal_level = 0; + if (device) + device->intr_requests.push_back(this); +} + +intr_request_c::~intr_request_c() { + if (device) { + // find and erase from device's request list + std::vector::iterator it = std::find(device->intr_requests.begin(), + device->intr_requests.end(), this); + device->intr_requests.erase(it); + } +} + +void intr_request_c::set_level(uint8_t level) { + assert(level >= 4 && level <= 7); + this->level_index = level - 4; // one of PRIORITY_LEVEL_INDEX_* +} + +void intr_request_c::set_vector(uint16_t vector) { + assert((vector & 3) == 0); // multiple of 2 words + this->vector = vector; +} + diff --git a/10.01_base/2_src/arm/priorityrequest.hpp b/10.01_base/2_src/arm/priorityrequest.hpp new file mode 100644 index 0000000..41c86e5 --- /dev/null +++ b/10.01_base/2_src/arm/priorityrequest.hpp @@ -0,0 +1,183 @@ +/* priorityrequest.hpp: DMA or iNTR request of an device + + Copyright (c) 2019 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: + + 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. + + + jul-2019 JH start: multiple parallel arbitration levels + */ + +/* + Handling priorities of Arbitration Requests: + 1. Priority of arbitration levels + Ascending priority: INTR BR4,5,6,7, DMA NPR + => 5 Priority Arbitration Levels encoded with index 0..4 + 2. Priority within one request level + Priority for Requests of same level given by backplane slot. + Backplane closests to CPU is granted first => highest priority + + So priority of a request is given by two coordinates: level and slot. + + Implementation: + all 5 levels are handled in parallel: priority_request_level_c [5]) + In each level, a refernece array[slot] holds open requests. + For fast determination of lowest active slot, a bitarray + marks active slots. + */ +#ifndef _PRIORITYREQUEST_HPP_ +#define _PRIORITYREQUEST_HPP_ + +#include +#include + +#include "logsource.hpp" + +// linear indexes for different UNIBUS arbitration levels +#define PRIORITY_LEVEL_INDEX_BR4 0 +#define PRIORITY_LEVEL_INDEX_BR5 1 +#define PRIORITY_LEVEL_INDEX_BR6 2 +#define PRIORITY_LEVEL_INDEX_BR7 3 +#define PRIORITY_LEVEL_INDEX_NPR 4 +#define PRIORITY_LEVEL_COUNT 5 + +#define PRIORITY_SLOT_COUNT 32 // backplane slot numbers 0..31 may be used + +class unibusdevice_c; + +// (almost) abstract base class for dma and intr requests +class priority_request_c: public logsource_c { + friend class intr_request_c; + friend class dma_request_c; + friend class unibusadapter_c; +private: + unibusdevice_c *device; // this device owns the request + // maybe NULL, in request is not used for device meualtion + // (test console EXAM, DEPOSIT for example). + + // internal priority index of a request level, see REQUEST_INDEX_* + uint8_t level_index; // is BR4567,NPR level - 4 + + uint8_t slot; // backplane slot which triggered request +public: + // better make state variables volatile, accessed by unibusadapter::worker + volatile bool executing_on_PRU; // true between schedule to PRU and compelte signal + volatile bool complete; + + // PRU -> signal -> worker() -> request -> device. INTR/DMA + pthread_mutex_t complete_mutex; + //pthread_cond_t complete_cond; // PRU signal notifies request on completeness + + priority_request_c(unibusdevice_c *device); + virtual ~priority_request_c(); // not used, but need dynamic_cast + + void set_priority_slot(uint8_t slot); + uint8_t get_priority_slot(void) { + return slot; + } +}; + +class dma_request_c: public priority_request_c { + friend class unibusadapter_c; +public: + dma_request_c(unibusdevice_c *device); + + ~dma_request_c(); + // const for all chunks + uint8_t unibus_control; // DATI,DATO + uint32_t unibus_start_addr; + uint32_t unibus_end_addr; + uint16_t* buffer; + uint32_t wordcount; + + // DMA transaction are divided in to smaller DAT transfer "chunks" + uint32_t chunk_max_words; // max is PRU capacity PRU_MAX_DMA_WORDCOUNT (512) + uint32_t chunk_unibus_start_addr; // current chunk + uint32_t chunk_words; // size of current chunks + + volatile bool success; // DMA can fail with bus timeout + + // return ptr to chunk pos in buffer + uint16_t *chunk_buffer_start(void) { + return buffer + (chunk_unibus_start_addr - unibus_start_addr) / 2; + } + + // words already transfered in previous chunks + uint32_t wordcount_completed_chunks(void) { + return (chunk_unibus_start_addr - unibus_start_addr) / 2; + } + +}; + +struct unibusdevice_register_struct; +// forward + +class intr_request_c: public priority_request_c { + friend class unibusadapter_c; +public: + enum interrupt_edge_enum { + INTERRUPT_EDGE_NONE, INTERRUPT_EDGE_RAISING, INTERRUPT_EDGE_FALLING + }; +private: + uint16_t vector; // PDP-11 interrupt vector + + // optionally register, with which a device signals presence of interrupt condition + struct unibusdevice_register_struct *interrupt_register; + uint16_t interrupt_register_value; + + // static level of some device INTR signal. + // raising edge calculated with edge_detect() + bool signal_level; + +public: + intr_request_c(unibusdevice_c *device); + + ~intr_request_c(); + + void set_level(uint8_t level); + void set_vector(uint16_t vector); + uint8_t get_level(void) { + return level_index + 4; + } + uint8_t get_vector(void) { + return vector; + } + + // service for device logic: calculate change of static INTR condition + void edge_detect_reset() { + signal_level = 0; + } + + // detect raising edge of interrupt level + enum interrupt_edge_enum edge_detect(bool new_signal_level) { + if (signal_level == new_signal_level) + return INTERRUPT_EDGE_NONE; + else { + // change: which edge? + signal_level = new_signal_level; + if (signal_level) + return INTERRUPT_EDGE_RAISING; + else + return INTERRUPT_EDGE_FALLING; + } + } + +}; + +#endif diff --git a/10.01_base/2_src/arm/pru.cpp b/10.01_base/2_src/arm/pru.cpp index e0b92db..6a6896b 100644 --- a/10.01_base/2_src/arm/pru.cpp +++ b/10.01_base/2_src/arm/pru.cpp @@ -134,7 +134,6 @@ int pru_c::start(enum prucode_enum prucode_id) { // use stop() before restart() assert(this->prucode_id == PRUCODE_NONE); - /* initialize PRU */ if ((rtn = prussdrv_init()) != 0) { ERROR("prussdrv_init() failed"); @@ -153,7 +152,6 @@ int pru_c::start(enum prucode_enum prucode_id) { goto error; } - /* http://credentiality2.blogspot.com/2015/09/beaglebone-pru-ddr-memory-access.html * get pointer to shared DDR @@ -208,7 +206,7 @@ int pru_c::start(enum prucode_enum prucode_id) { // verify PRU1 is executing its command loop mailbox->arm2pru_req = ARM2PRU_NOP; timeout.wait_ms(1); - if (mailbox->arm2pru_req != ARM2PRU_NONE) { + if (mailbox->arm2pru_req != ARM2PRU_NONE) { FATAL("PRU1 is not executing its command loop"); goto error; } diff --git a/10.01_base/2_src/arm/pru.hpp b/10.01_base/2_src/arm/pru.hpp index 70dc9bf..4ac5a94 100644 --- a/10.01_base/2_src/arm/pru.hpp +++ b/10.01_base/2_src/arm/pru.hpp @@ -2,37 +2,37 @@ Copyright (c) 2018, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com - All rights reserved. + All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. - - Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - 18-apr-2019 JH added PRU code dictionary - 12-nov-2018 JH entered beta phase + 18-apr-2019 JH added PRU code dictionary + 12-nov-2018 JH entered beta phase */ #ifndef _PRU_HPP_ @@ -43,8 +43,6 @@ #include "logsource.hpp" - - /*** PRU Shared addresses ***/ // Mailbox page & offset in PRU internal shared 12 KB RAM // Accessible by both PRUs, must be located in shared RAM @@ -57,35 +55,31 @@ #define PRU0_ENTRY_ADDR 0x00000000 #define PRU1_ENTRY_ADDR 0x00000000 - #ifndef PRU_MAILBOX_RAM_ID - #define PRU_MAILBOX_RAM_ID PRUSS0_SHARED_DATARAM - #define PRU_MAILBOX_RAM_OFFSET 0 +#define PRU_MAILBOX_RAM_ID PRUSS0_SHARED_DATARAM +#define PRU_MAILBOX_RAM_OFFSET 0 #endif // Device register page & offset in PRU0 8KB RAM mapped into PRU1 space // offset 0 == addr 0x2000 in linker cmd files for PRU1 projects. // For use with prussdrv_map_prumem() #ifndef PRU_DEVICEREGISTER_RAM_ID - #define PRU_DEVICEREGISTER_RAM_ID PRUSS0_PRU0_DATARAM - #define PRU_DEVICEREGISTER_RAM_OFFSET 0 +#define PRU_DEVICEREGISTER_RAM_ID PRUSS0_PRU0_DATARAM +#define PRU_DEVICEREGISTER_RAM_OFFSET 0 #endif - - - class pru_c: public logsource_c { public: // IDs for code variants, so callers can select one enum prucode_enum { - PRUCODE_EOD = 0, // special marker: end of dictionary - PRUCODE_NONE = 0, // no code running, RPU reset - PRUCODE_TEST = 1, // only selftest functions - PRUCODE_UNIBUS = 2 // regular UNIBUS operation + PRUCODE_EOD = 0, // special marker: end of dictionary + PRUCODE_NONE = 0, // no code running, RPU reset + PRUCODE_TEST = 1, // only selftest functions + PRUCODE_UNIBUS = 2 // regular UNIBUS operation // with or without physical CPU for arbitration - } ; + }; public: - enum prucode_enum prucode_id ; // currently running code + enum prucode_enum prucode_id; // currently running code pru_c(); int start(enum prucode_enum prucode_id); diff --git a/10.01_base/2_src/arm/storagecontroller.cpp b/10.01_base/2_src/arm/storagecontroller.cpp index d2cc43c..8783952 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/storagedrive.hpp b/10.01_base/2_src/arm/storagedrive.hpp index e15df05..93613fc 100644 --- a/10.01_base/2_src/arm/storagedrive.hpp +++ b/10.01_base/2_src/arm/storagedrive.hpp @@ -102,8 +102,6 @@ public: } virtual void on_init_changed(void) { } - virtual void worker(void) { - } void test(void); }; diff --git a/10.01_base/2_src/arm/unibus.cpp b/10.01_base/2_src/arm/unibus.cpp index ea7c5e1..4ee85d1 100644 --- a/10.01_base/2_src/arm/unibus.cpp +++ b/10.01_base/2_src/arm/unibus.cpp @@ -20,7 +20,7 @@ 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. - + jul-2019 JH rewrite: multiple parallel arbitration levels 12-nov-2018 JH entered beta phase */ @@ -40,6 +40,8 @@ #include "memoryimage.hpp" #include "mailbox.h" // for test of PRU code #include "utils.hpp" // for test of PRU code +#include "unibusadapter.hpp" // DMA, INTR + #include "unibus.h" /* Singleton */ @@ -47,8 +49,14 @@ unibus_c *unibus; unibus_c::unibus_c() { log_label = "UNIBUS"; - dma_bandwidth_percent = 50; - dma_wordcount = MAX_DMA_WORDCOUNT; + dma_request = new dma_request_c(NULL); + // priority backplane slot # for helper DMA not important, as typically used stand-alone + // (no other devioces on the backplane active, except perhaps "testcontroller") + dma_request->set_priority_slot(16); +} + +unibus_c::~unibus_c() { + delete dma_request; } /* return a 16 bit result, or TIMEOUT @@ -96,40 +104,31 @@ void unibus_c::init(void) { timeout.wait_ms(duration_ms); buslatches_setval(7, BIT(3), 0); */ - mailbox_execute(ARM2PRU_INITPULSE, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_INITPULSE); } /* Simulate a power cycle */ void unibus_c::powercycle(void) { - mailbox_execute(ARM2PRU_POWERCYCLE, ARM2PRU_NONE); + mailbox_execute(ARM2PRU_POWERCYCLE); } -// do an UNIBUS INTR transaction with Arbitration by PDP-11 CPU -// todo: ARBITRATOR_MASTER? -void unibus_c::interrupt(uint8_t priority, uint16_t vector) { - assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); - switch (priority) { - case 4: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4; +void unibus_c::set_arbitration_mode(arbitration_mode_enum arbitration_mode) { + // switch pru to desired mode + switch (arbitration_mode) { + case unibus_c::ARBITRATION_MODE_NONE: + mailbox_execute(ARM2PRU_ARB_MODE_NONE); break; - case 5: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5; + case unibus_c::ARBITRATION_MODE_CLIENT: + mailbox_execute(ARM2PRU_ARB_MODE_CLIENT); break; - case 6: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6; - break; - case 7: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7; + case unibus_c::ARBITRATION_MODE_MASTER: + mailbox_execute(ARM2PRU_ARB_MODE_MASTER); break; default: - ERROR("unibus_interrupt(): Illegal priority %u, aborting", priority); - return; + printf("Illegal arbitration_mode %d\n", (int) arbitration_mode); + exit(1); } - mailbox->intr.vector = vector; - // mail last infinite, if processor priority above "priority" - // timeout ?? - mailbox_execute(ARM2PRU_INTR, ARM2PRU_NONE); } // do a DMA transaction with or without abritration (arbitration_client) @@ -137,36 +136,19 @@ void unibus_c::interrupt(uint8_t priority, uint16_t vector) { // if result = timeout: = // 0 = bus time, error address = mailbox->dma.cur_addr // 1 = all transfered -bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_t control, - uint32_t startaddr, unsigned blocksize) { +// A limit for time used by DMA can be compiled-in +bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, bool blocking, + uint8_t control, uint32_t startaddr, uint16_t *buffer, unsigned wordcount) { + int dma_bandwidth_percent = 50; // use 50% of time for DMA, rest for running PDP-11 CPU uint64_t dmatime_ns, totaltime_ns; - uint8_t dma_opcode = ARBITRATION_MODE_NONE; // inihibit compiler warnings // can access bus with DMA when there's a Bus Arbitrator assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); -// TODO: assert pru->prucode_features & (PRUCODE_FEATURE_DMA) ??? - // TODO: Arbitration Master waits for SACK, 11/34 blocks? - mailbox->dma.startaddr = startaddr; - mailbox->dma.control = control; - mailbox->dma.wordcount = blocksize; - timeout.start(0); // no timeout, just running timer + set_arbitration_mode(arbitration_mode); // changes PRU behaviour - switch (arbitration_mode) { - case unibus_c::ARBITRATION_MODE_NONE: - dma_opcode = ARM2PRU_DMA_ARB_NONE; - break; - case unibus_c::ARBITRATION_MODE_CLIENT: - dma_opcode = ARM2PRU_DMA_ARB_CLIENT; - break; - case unibus_c::ARBITRATION_MODE_MASTER: - dma_opcode = ARM2PRU_DMA_ARB_MASTER; - break; - default: - FATAL("Illegal arbitration_mode"); - } - // wait until PRU ready - mailbox_execute(dma_opcode, ARM2PRU_NONE); + timeout.start_ns(0); // no timeout, just running timer + unibusadapter->DMA(*dma_request, blocking, control, startaddr, buffer, wordcount); dmatime_ns = timeout.elapsed_ns(); // wait before next transaction, to reduce Unibus bandwidth @@ -176,31 +158,26 @@ bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_ // 25% -> total = 4* dma totaltime_ns = (dmatime_ns * 100) / dma_bandwidth_percent; // whole transaction requires totaltime, dma already done -//INFO("DMA time= %lluus, waiting %lluus", dmatime_ns/1000, (totaltime_ns - dmatime_ns)/1000) ; timeout.wait_ns(totaltime_ns - dmatime_ns); - return (mailbox->dma.cur_status == DMA_STATE_READY); - // all other end states are errors, only error is bus timeout + return dma_request->success; // only useful if blocking } /* scan unibus addresses ascending from 0. * Stop on error, return first invalid address * return 0: no memory found at all * arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses + * words[]: buffer for whole UNIBUS address range, is filled with data */ uint32_t unibus_c::test_sizer(enum unibus_c::arbitration_mode_enum arbitration_mode) { // tests chunks of 128 word - bool timeout; unsigned addr = 0; - //SET_DEBUG_PIN0(0) ; - do { - // printf("unibus_test_sizer(): %06o..%06o\n", addr, addr+2*unibus_dma_wordcount-2) ; - timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, dma_wordcount); - addr += 2 * dma_wordcount; // prep for next round - } while (!timeout); - //SET_DEBUG_PIN0(1) ; // signal end - //SET_DEBUG_PIN0(0) ; - return mailbox->dma.cur_addr; // first non implemented address + + set_arbitration_mode(arbitration_mode); // changes PRU behaviour + // one big transaction, automatically split in chunks + unibusadapter->DMA(*dma_request, true, UNIBUS_CONTROL_DATI, addr, testwords, + UNIBUS_WORDCOUNT); + return dma_request->unibus_end_addr; // first non implemented address } /* @@ -213,32 +190,15 @@ uint32_t unibus_c::test_sizer(enum unibus_c::arbitration_mode_enum arbitration_m // // DMA blocksize can be choosen arbitrarily void unibus_c::mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words, - unsigned start_addr, unsigned end_addr, unsigned block_wordcount, - bool *timeout) { - unsigned block_start_addr = 0; - unsigned n; + unsigned unibus_start_addr, unsigned unibus_end_addr, bool *timeout) { + unsigned wordcount = (unibus_end_addr - unibus_start_addr) / 2 + 1; + uint16_t *buffer_start_addr = words + unibus_start_addr / 2; assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); - assert(block_wordcount <= MAX_DMA_WORDCOUNT); - *timeout = 0; - for (block_start_addr = start_addr; !*timeout && block_start_addr <= end_addr; - block_start_addr += 2 * block_wordcount) { - // trunc last block - n = (end_addr - block_start_addr) / 2 + 1; // words left until memend - if (n < block_wordcount) - block_wordcount = n; //trunc last buffer - // fill data into dma buffer - memcpy((void*) (mailbox->dma.words), (void*) (words + block_start_addr / 2), - 2 * block_wordcount); - /* for (n = 0; n < block_wordcount; n++) { - cur_addr = block_start_addr + 2 * n; - mailbox->dma.words[n] = words[cur_addr / 2]; - } - */*timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATO, block_start_addr, - block_wordcount); - if (*timeout) { - printf("\nWrite timeout @ 0%6o\n", mailbox->dma.cur_addr); - return; - } + *timeout = !dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, unibus_start_addr, + buffer_start_addr, wordcount); + if (*timeout) { + printf("\nWrite timeout @ 0%6o\n", mailbox->dma.cur_addr); + return; } } @@ -247,34 +207,54 @@ void unibus_c::mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, // DMA blocksize can be choosen arbitrarily // arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses void unibus_c::mem_read(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words, - uint32_t start_addr, uint32_t end_addr, unsigned block_wordcount, - bool *timeout) { - unsigned block_start_addr = 0; - unsigned n; + uint32_t unibus_start_addr, uint32_t unibus_end_addr, bool *timeout) { + unsigned wordcount = (unibus_end_addr - unibus_start_addr) / 2 + 1; + uint16_t *buffer_start_addr = words + unibus_start_addr / 2; assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); - assert(block_wordcount <= MAX_DMA_WORDCOUNT); - *timeout = 0; - for (block_start_addr = start_addr; !*timeout && block_start_addr <= end_addr; - block_start_addr += 2 * block_wordcount) { - // trunc last block - n = (end_addr - block_start_addr) / 2 + 1; // words left until memend - if (n < block_wordcount) - block_wordcount = n; //trunc last buffer - *timeout = !dma(arbitration_mode, UNIBUS_CONTROL_DATI, block_start_addr, - block_wordcount); + *timeout = !dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, unibus_start_addr, + buffer_start_addr, wordcount); + if (*timeout) { + printf("\nRead timeout @ 0%6o\n", mailbox->dma.cur_addr); + return; + } +} + +// read or write +void unibus_c::mem_access_random(enum unibus_c::arbitration_mode_enum arbitration_mode, + uint8_t unibus_control, uint16_t *words, uint32_t unibus_start_addr, + uint32_t unibus_end_addr, bool *timeout, uint32_t *block_counter) { + uint32_t block_unibus_start_addr, block_unibus_end_addr; + // in average, make 16 sub transactions + assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); + assert(unibus_control == UNIBUS_CONTROL_DATI || unibus_control == UNIBUS_CONTROL_DATO); + block_unibus_start_addr = unibus_start_addr; + // split transaction in random sized blocks + uint32_t max_block_wordcount = (unibus_end_addr - unibus_start_addr + 2) / 2; + + do { + uint16_t *block_buffer_start = words + block_unibus_start_addr / 2; + uint32_t block_wordcount; + do { + block_wordcount = random32_log(max_block_wordcount); + } while (block_wordcount < 1); + assert(block_wordcount < max_block_wordcount); + // wordcount limited by "words left to transfer" + block_wordcount = std::min(block_wordcount, + (unibus_end_addr - block_unibus_start_addr) / 2 + 1); + block_unibus_end_addr = block_unibus_start_addr + 2 * block_wordcount - 2; + assert(block_unibus_end_addr <= unibus_end_addr); + (*block_counter) += 1; + // printf("%06d: %5u words %06o-0%06o\n", *block_counter, block_wordcount, block_unibus_start_addr, block_unibus_end_addr) ; + *timeout = !dma(arbitration_mode, true, unibus_control, block_unibus_start_addr, + block_buffer_start, block_wordcount); if (*timeout) { - printf("\nRead timeout @ 0%6o\n", mailbox->dma.cur_addr); + printf("\n%s timeout @ 0%6o\n", control2text(unibus_control), + mailbox->dma.cur_addr); return; } - // move from DMA buffer into words[] - memcpy((void*) (words + block_start_addr / 2), (void*) (mailbox->dma.words), - 2 * block_wordcount); - /* for (n = 0; n < block_wordcount; n++) { - cur_addr = block_start_addr + 2 * n; - words[cur_addr / 2] = mailbox->dma.words[n]; - } - */} + block_unibus_start_addr = block_unibus_end_addr + 2; + } while (block_unibus_start_addr <= unibus_end_addr); } // arbitration_active: if 1, perform NPR/NPG/SACK arbitration before mem accesses @@ -285,7 +265,7 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, bool timeout = 0, mismatch = 0; unsigned mismatch_count = 0; uint32_t cur_test_addr; - unsigned block_wordcount; + unsigned pass_count = 0, total_read_block_count = 0, total_write_block_count = 0; assert(pru->prucode_id == pru_c::PRUCODE_UNIBUS); @@ -298,25 +278,28 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, testwords[cur_test_addr / 2] = (cur_test_addr >> 1) & 0xffff; /**** 2. Write memory ****/ progress.put("W"); //info : full memory write - block_wordcount = 113; // something queer - mem_write(arbitration_mode, testwords, start_addr, end_addr, block_wordcount, &timeout); + mem_write(arbitration_mode, testwords, start_addr, end_addr, &timeout); /**** 3. read until ^C ****/ while (!SIGINTreceived && !timeout && !mismatch_count) { + pass_count++; + if (pass_count % 10 == 0) + progress.putf(" %d ", pass_count); + total_write_block_count++; // not randomized + total_read_block_count++; progress.put("R"); - block_wordcount = 67; // something queer // read back into unibus_membuffer[] - mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr, - block_wordcount, &timeout); + mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr, &timeout); // compare - SET_DEBUG_PIN0(0) ; for (mismatch_count = 0, cur_test_addr = start_addr; cur_test_addr <= end_addr; cur_test_addr += 2) { uint16_t cur_mem_val = membuffer->data.words[cur_test_addr / 2]; mismatch = (testwords[cur_test_addr / 2] != cur_mem_val); if (mismatch) { - SET_DEBUG_PIN0(1) ; // trigger - SET_DEBUG_PIN0(0) ; + ARM_DEBUG_PIN0(1) + ; // trigger + ARM_DEBUG_PIN0(0) + ; } if (mismatch && ++mismatch_count <= MAX_ERROR_COUNT) // print only first errors printf( @@ -329,32 +312,39 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, case 2: // full write, full read /**** 1. Full write generate test values */ +// start_addr = 0; +// end_addr = 076; while (!SIGINTreceived && !timeout && !mismatch_count) { + pass_count++; + if (pass_count % 10 == 0) + progress.putf(" %d ", pass_count); + for (cur_test_addr = start_addr; cur_test_addr <= end_addr; cur_test_addr += 2) - testwords[cur_test_addr / 2] = random24() & 0xffff; + testwords[cur_test_addr / 2] = random24() & 0xffff; // random +// testwords[cur_test_addr / 2] = (cur_test_addr >> 1) & 0xffff; // linear + progress.put("W"); //info : full memory write - block_wordcount = 97; // something queer - mem_write(arbitration_mode, testwords, start_addr, end_addr, block_wordcount, - &timeout); + mem_access_random(arbitration_mode, UNIBUS_CONTROL_DATO, testwords, start_addr, + end_addr, &timeout, &total_write_block_count); if (SIGINTreceived || timeout) break; // leave loop // first full read progress.put("R"); //info : full memory write - block_wordcount = 111; // something queer // read back into unibus_membuffer[] - mem_read(arbitration_mode, membuffer->data.words, start_addr, end_addr, - block_wordcount, &timeout); + mem_access_random(arbitration_mode, UNIBUS_CONTROL_DATI, membuffer->data.words, + start_addr, end_addr, &timeout, &total_read_block_count); // compare - SET_DEBUG_PIN0(0) ; for (mismatch_count = 0, cur_test_addr = start_addr; cur_test_addr <= end_addr; cur_test_addr += 2) { uint16_t cur_mem_val = membuffer->data.words[cur_test_addr / 2]; mismatch = (testwords[cur_test_addr / 2] != cur_mem_val); if (mismatch) { - SET_DEBUG_PIN0(1) ; // trigger - SET_DEBUG_PIN0(0) ; + ARM_DEBUG_PIN0(1) + ; // trigger + ARM_DEBUG_PIN0(0) + ; } if (mismatch && ++mismatch_count <= MAX_ERROR_COUNT) // print only first errors printf( @@ -370,44 +360,7 @@ void unibus_c::test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, printf("Stopped by error: %stimeout, %d mismatches\n", (timeout ? "" : "no "), mismatch_count); else - printf("All OK!\n"); + printf("All OK! Total %d passes, split into %d block writes and %d block reads\n", + pass_count, total_write_block_count, total_read_block_count); } -#ifdef USED -/* load a word buffer from file - * words assumed to be little endian (LSB first) - */ -void unibus_c::loadfromfile(char *fname, uint16_t *words, unsigned *bytecount) { - FILE *f; - unsigned n; - f = fopen(fname, "rb"); - if (!f) { - ERROR("unibus_loadfromfile(): could not open file \"%s\" for read.", fname); - return; - } - // try to read max address range, shorter files are OK - n = fread((void *) words, 1, 2 * UNIBUS_WORDCOUNT, f); - fclose(f); - *bytecount = n; - INFO("Read %d bytes from \"%s\".", *bytecount, fname); -} - -/* save a word buffer to file */ -void unibus_c::savetofile(char *fname, uint16_t *words, unsigned bytecount) { - FILE *f; - unsigned n; - f = fopen(fname, "w"); - if (!f) { - ERROR("unibus_savetofile(): could not open file \"%s\" for write.", fname); - return; - } - n = fwrite((void *) words, 1, bytecount, f); - if (n != bytecount) { - ERROR("unibus_savetofile(): tried to write %u bytes, only %d successful.", bytecount, - n); - } - fclose(f); - INFO("Wrote %d bytes to \"%s\".", n, fname); -} -#endif - diff --git a/10.01_base/2_src/arm/unibusadapter.cpp b/10.01_base/2_src/arm/unibusadapter.cpp index 14669f1..d83333b 100644 --- a/10.01_base/2_src/arm/unibusadapter.cpp +++ b/10.01_base/2_src/arm/unibusadapter.cpp @@ -1,6 +1,6 @@ /* unibusadapter.cpp: connects multiple "unibusdevices" to the PRU UNIBUS interface - Copyright (c) 2018, Joerg Hoppe + Copyright (c) 2018-2019, Joerg Hoppe j_hoppe@t-online.de, www.retrocmp.com Permission is hereby granted, free of charge, to any person obtaining a @@ -21,6 +21,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + jul-2019 JH rewrite: multiple parallel arbitration levels 12-nov-2018 JH entered beta phase unibusadapter @@ -65,61 +66,15 @@ using namespace std; #include "prussdrv.h" #include "pruss_intc_mapping.h" #include "iopageregister.h" +#include "priorityrequest.hpp" #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() -{ - -} - -irq_request_c::irq_request_c( - unsigned level, - unsigned vector) : - _level(level), - _vector(vector), - _isComplete(false) -{ - -} - -irq_request_c::~irq_request_c() -{ - -} - -void* bus_worker( - void *context) -{ - unibusadapter_c* bus = reinterpret_cast(context); - bus->dma_worker(); - return nullptr; -} - unibusadapter_c *unibusadapter; // another Singleton // is registered in device_c.list ... order of static constructor calls ??? +bool unibusadapter_debug_flag = 0; + unibusadapter_c::unibusadapter_c() : - device_c(), - _busWakeup_cond(PTHREAD_COND_INITIALIZER), - _requestFinished_cond(PTHREAD_COND_INITIALIZER), - _busWorker_mutex(PTHREAD_MUTEX_INITIALIZER) - { + device_c() { unsigned i; log_label = "UNAPT"; @@ -131,246 +86,23 @@ unibusadapter_c::unibusadapter_c() : line_INIT = false; line_DCLO = false; - // - // Start bus worker thread - // - pthread_attr_t attribs; - pthread_attr_init(&attribs); + requests_mutex = PTHREAD_MUTEX_INITIALIZER; - int status = pthread_create( - &_busWorker_pthread, - &attribs, - &bus_worker, - reinterpret_cast(this)); - - if (status != 0) - { - FATAL("Failed to start unibus worker thread. Status 0x%x", status); - } + requests_init(); } - 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) { } void unibusadapter_c::on_init_changed(void) { } -// set state of INIT -void unibusadapter_c::worker_init_event() { - unsigned device_handle; - unibusdevice_c *device; - // notify device on changed of INIT line - DEBUG("worker_init_event(): INIT %s", line_INIT ? "asserted" : "deasserted"); - for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) - if ((device = devices[device_handle])) { - device->init_asserted = line_INIT; - device->on_init_changed(); - } - - - // Clear bus request queues - rundown_bus_requests(); -} - -void unibusadapter_c::worker_power_event() { - unsigned device_handle; - unibusdevice_c *device; - // notify device on changed of INIT line - DEBUG("worker_power_event(): DCLO %s", line_DCLO ? "asserted" : "deasserted"); - for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) - if ((device = devices[device_handle])) { - device->power_down = line_DCLO; - device->on_power_changed(); - } - - // Clear bus request queues - rundown_bus_requests(); -} - -// process DATI/DATO access to active device registers - -void unibusadapter_c::worker_deviceregister_event() { - unsigned device_handle; - unibusdevice_c *device; - device_handle = mailbox->events.device_handle; - assert(device_handle); - device = devices[device_handle]; - unsigned evt_idx = mailbox->events.device_register_idx; - uint32_t evt_addr = mailbox->events.addr; - // normally evt_data == device_reg->shared_register->value - // but shared value gets desorted if INIT in same event clears the registers before DATO - uint16_t evt_data = mailbox->events.data; - unibusdevice_register_t *device_reg = &(device->registers[evt_idx]); - uint8_t unibus_control = mailbox->events.unibus_control; - - /* call device event callback - - The PRU has only one "value" for each register, but "active" registers have separate - read(DATI) and write(DATO) flipflops. So if register is "active: - BEFORE processing of logic state changes: - DATO: save written value into .active_write_val - restore changed shared PRU register with .active_read_val for next read - */ - if (device_reg->active_on_dati && !UNIBUS_CONTROL_ISDATO(unibus_control)) { - // register is read with DATI, this changes the logic state - evt_addr &= ~1; // make even - assert(evt_addr == device->base_addr.value + 2 * evt_idx); - unibus_control = UNIBUS_CONTROL_DATI; - // read access: dati-flipflops do not change - - // signal: changed by UNIBUS - device->log_register_event("DATI", device_reg); - - device->on_after_register_access(device_reg, unibus_control); - } else if (device_reg->active_on_dato && UNIBUS_CONTROL_ISDATO(unibus_control)) { -// uint16_t reg_value_written = device_reg->shared_register->value; - // restore value accessible by DATI - device_reg->shared_register->value = device_reg->active_dati_flipflops; - // Restauration of shared_register->value IS NOT ATOMIC against device logic threads. - // Devices must use only reg->active_dati_flipflops ! - switch (unibus_control) { - case UNIBUS_CONTROL_DATO: - // write into a register with separate read/write flipflops - assert(evt_addr == device->base_addr.value + 2 * evt_idx); - // clear unused bits, save written value - device_reg->active_dato_flipflops = evt_data & device_reg->writable_bits; - // signal: changed by UNIBUS - device->log_register_event("DATO", device_reg); - break; - case UNIBUS_CONTROL_DATOB: - // UNIBUS may access only 8bit half of register with DATOB. - // convert all active registers accesses to 16 bit - evt_data &= device_reg->writable_bits; // clear unused bits - // save written value - if (evt_addr & 1) // odd address: bits 15:8 written - device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0x00ff) - | (evt_data & 0xff00); - else - // even address : bits 7:0 written - device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0xff00) - | (evt_data & 0x00ff); - unibus_control = UNIBUS_CONTROL_DATO; // simulate 16 bit access - // signal: changed by UNIBUS - device->log_register_event("DATOB", device_reg); - break; - } - device->on_after_register_access(device_reg, unibus_control); - /* - DEBUG(LL_DEBUG, LC_UNIBUS, "dev.reg=%d.%d, %s, addr %06o, data %06o->%06o", - device_handle, evt_idx, - unibus_c::control2text(mailbox->event.unibus_control), evt_addr, - oldval, device_reg->shared_register->value); - */ - } -// only in worker()!! Starts PRU mailbox->events.eventmask &= ~EVENT_DEVICEREGISTER; // clear bit -} - -// runs in background, catches and distributes PRU events -void unibusadapter_c::worker() { - int res; - - // set thread priority to MAX. - // - fastest response to slect() call in prussdrv_pru_wait_event_timeout() - // (minimal I/O latency) - // - not interrupted by other tasks while running - // check with tool "top" or "htop". - worker_init_realtime_priority(rt_max); // set to max prio - - while (!worker_terminate) { - // Timing: - // This is THE ONE mechanism where "realtime meets Linux" - // To respond to the PRU signal, Linux must wake up schedule this thread - // Test: - // - set RT prio of this worker thread to true 100% (SCHED_FIFO. max prio, - // /proc/sys/kernel/sched_rt_runtime_us = -1 - // - let the PRU pulse a GPIO on signal_set - // - let this worker raise a GPIO while its active - // - verify with scope that PRU - signal to worker start is about 100-300us - // MUST NOT GET LARGER on any Linux activity, - - // main loop - // wait 0.1 sec, just tell - /* The prussdrv_pru_wait_event() function returns the number of times - the event has taken place, as an unsigned int. There is no out-of- - band value to indicate error (and it can wrap around to 0 if you - run the program just a whole lot of times). */ - res = prussdrv_pru_wait_event_timeout(PRU_EVTOUT_0, 100000/*us*/); -//res = prussdrv_pru_wait_event(PRU_EVTOUT_0); - // PRU may have raised more than one event before signal is accepted. - // single combination of only INIT+DATI/O possible - prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); - // uses select() internally: 0 = timeout, -1 = error, else event count received - while (res > 0 && mailbox->events.eventmask) { // res is const -// SET_DEBUG_PIN0(1) ; // debug: PRU event accepted - // Process multiple events sent by PRU. - // - // while ARM accepts the signal, the PRU may set more events - // critical a mix of INIT and DATI/DATO: RESET and register access follow directly - // But no DATI/DATO occurs while INIT active. So reconstruct event order by - // processing order: INIT_DEASSERT, DATI/O, INIT_ASSERT, DCLO/ACLO - bool init_raising_edge = false; - bool init_falling_edge = false; - bool dclo_raising_edge = false; - bool dclo_falling_edge = false; - // DEBUG("mailbox->events: mask=0x%x", mailbox->events.eventmask); - if (mailbox->events.eventmask & EVENT_INITIALIZATIONSIGNALS) { - // robust: any change in ACLO/DCL=INIT updates state of all 3. - // Initial DCLO-cycle to PDP_11 intialize these states - if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_INIT) { - if (!line_INIT) - init_raising_edge = true; - line_INIT = true; - } else { - if (line_INIT) - init_falling_edge = true; - line_INIT = false; - } - if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_DCLO) { - if (!line_DCLO) - dclo_raising_edge = true; - line_DCLO = true; - } else { - if (line_DCLO) - dclo_falling_edge = true; - line_DCLO = false; - } - DEBUG( - "EVENT_INITIALIZATIONSIGNALS: (sigprev=0x%x,) cur=0x%x, init_rais=%d, init_fall=%d, dclo_rais=%d, dclo_fall=%d", - mailbox->events.initialization_signals_prev, - mailbox->events.initialization_signals_cur, init_raising_edge, - init_falling_edge, dclo_raising_edge, dclo_falling_edge); - - mailbox->events.eventmask &= ~EVENT_INITIALIZATIONSIGNALS; // ACK, now PRU continues - - if (dclo_raising_edge || dclo_falling_edge) - worker_power_event(); // power signal power change - } - if (init_falling_edge) // INIT asserted -> deasserted. DATI/DATO cycle only possible after that. - worker_init_event(); - if (mailbox->events.eventmask & EVENT_DEVICEREGISTER) { - // DATI/DATO - // DEBUG("EVENT_DEVICEREGISTER: control=%d, addr=%06o", (int)mailbox->events.unibus_control, mailbox->events.addr); - worker_deviceregister_event(); - mailbox->events.eventmask &= ~EVENT_DEVICEREGISTER; // ACK, now PRU continues - } - if (init_raising_edge) // INIT deasserted -> asserted DATI/DATO cycle only possible before that. - worker_init_event(); - - } -//SET_DEBUG_PIN0(0) ; // debug: PRU event processed - - // Signal to PRU: continue UNIBUS cycles now with SSYN deassert - } -} - // register_device ... "plug" the device into UNIBUs backplane // - assign handle // - setup register map for a device @@ -396,7 +128,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) { while (device_handle <= MAX_DEVICE_HANDLE && devices[device_handle] != NULL) device_handle++; if (device_handle > MAX_DEVICE_HANDLE) { - ERROR("Tried to register more than %u devices!", MAX_DEVICE_HANDLE); + ERROR("register_device() Tried to register more than %u devices!", MAX_DEVICE_HANDLE); return false; } devices[device_handle] = &device; @@ -410,8 +142,8 @@ 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 ) - FATAL("IO page address conflict: %s implements register at %06o, belongs already to other device.", + if ( IOPAGE_REGISTER_ENTRY(*deviceregisters,device_reg->addr)!= 0 ) + FATAL("register_device() IO page address conflict: %s implements register at %06o, belongs already to other device.", device.name.value.c_str(), device_reg->addr); } @@ -432,7 +164,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) { register_handle = i; unsigned free_handles = MAX_REGISTER_COUNT - register_handle - 1; if (free_handles < device.register_count) { - ERROR("Can not register device %s, needs %d register, only %d left.", + ERROR("register_device() can not register device %s, needs %d register, only %d left.", device.name.value.c_str(), device.register_count, free_handles); return false; } @@ -457,7 +189,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) { if (device_reg->active_on_dati || device_reg->active_on_dato) { if (device_reg->active_on_dati && !device_reg->active_on_dato) { FATAL( - "Register configuration error for device %s, register idx %u:\n" + "register_device() Register configuration error for device %s, register idx %u:\n" "A device register may not be passive on DATO and active on DATI.\n" "Passive DATO -> value written only saved in shared UNIBUS reg value\n" "Active DATI: shared UNIBUS reg value updated from flipflops -> DATO value overwritten\n" @@ -505,316 +237,780 @@ void unibusadapter_c::unregister_device(unibusdevice_c& device) { } } -/* interface for devices to issue DMA and INTR +/*** Access requests in [level,slot] table ***/ - **** !!! TODO: SORTING and SERIALIZING of requests required !!!! *** +// initialize slot tables in empty state +void unibusadapter_c::requests_init(void) { + for (unsigned level_index = 0; level_index < PRIORITY_LEVEL_COUNT; level_index++) { + priority_request_level_c *prl = &request_levels[level_index]; + for (unsigned slot = 0; slot < PRIORITY_SLOT_COUNT; slot++) + prl->slot_request[slot] = NULL; + prl->slot_request_mask = 0; + prl->active = NULL; + } +} - device has "slot" nr after registration +// put a request into the level/slot table +// do not yet active! +void unibusadapter_c::request_schedule(priority_request_c& request) { + // Must run under pthread_mutex_lock(&requests_mutex); + priority_request_level_c *prl = &request_levels[request.level_index]; + // DEBUG("request_schedule") ; + + // a device may reraise on of its own interrupts, but not an DMA on same slot + if (dynamic_cast(&request)) { + if (prl->slot_request[request.slot] != NULL) + FATAL("Concurrent DMA requested for slot %d.", (unsigned )request.slot); + } else if (dynamic_cast(&request)) { + if (prl->slot_request[request.slot] != NULL) { + unibusdevice_c *slotdevice = prl->slot_request[request.slot]->device; + if (slotdevice != request.device) + FATAL( + "Devices %s and %s share both slot %u for INTR request with priority index %u", + slotdevice ? slotdevice->name.value.c_str() : "NULL", + request.device->name.value.c_str(), (unsigned )request.slot, + (unsigned )request.level_index); + // DEBUG("request_schedule(): update request %p into level %u, slot %u",&request, request.level_index, request.slot); + } else { + // DEBUG("request_schedule(): insert request %p into level %u, slot %u",&request, request.level_index, request.slot); + } + } + + prl->slot_request[request.slot] = &request; // mark slot with request + prl->slot_request_mask |= (1 << request.slot); // set slot bit +} + +// Cancel all pending device_DMA and IRQ requests of every level. +// requests which are active on the PRU (->active) are left running, +// and the PRU terminates DMA sequences on INIT. +void unibusadapter_c::requests_cancel_scheduled(void) { + priority_request_c *req; + + // Must run under pthread_mutex_lock(&requests_mutex); + for (unsigned level_index = 0; level_index < PRIORITY_LEVEL_COUNT; level_index++) { + priority_request_level_c *prl = &request_levels[level_index]; + prl->slot_request_mask = 0; // clear alls slot from request + prl->active = NULL; + + for (unsigned slot = 0; slot < PRIORITY_SLOT_COUNT; slot++) + if ((req = prl->slot_request[slot])) { + dma_request_c *dmareq; + req->executing_on_PRU = false; + req->complete = true; + if ((dmareq = dynamic_cast(req))) + dmareq->success = false; // device gets an DMA error, but will not understand + prl->slot_request[slot] = NULL; + // signal to blockin DMA() or INTR() + pthread_mutex_unlock(&req->complete_mutex); + //pthread_cond_signal(&req->complete_cond); + } + } +} + +/* + // is a request of given level active on the PRU? + bool unibusadapter_c::request_is_active(unsigned level_index) { + // Must run under pthread_mutex_lock(&requests_mutex); + priority_request_level_c *prl = &request_levels[level_index]; + return (prl->active != NULL); + } */ -// internal only: is any DMA or INTR issued by UNIBONE active? -// false: UNIBUS DMA or INTR pending or in progress -// true: new DMA or INTR may be started -bool unibusadapter_c::request_DMA_active(const char *error_info) { - if (mailbox->arm2pru_req == ARM2PRU_DMA_ARB_NONE - || mailbox->arm2pru_req == ARM2PRU_DMA_ARB_CLIENT - || mailbox->arm2pru_req == ARM2PRU_DMA_ARB_MASTER) { - if (error_info) - ERROR("%s: DMA requests active!", error_info); - return true; +// find highest prioritized request for a given level, via slots +priority_request_c *unibusadapter_c::request_activate_lowest_slot(unsigned level_index) { + /* To find the lowest slot with an active request of priority 'level' + gcc __builtin_ffs "Returns one plus the index of the least significant 1-bit of x, or if x is zero, returns zero. " + (= slow with highest priority within 'level') + Is implemented on ARM as just 2 opcodes: rbit (bit reverse), clz (count number of leading zeros) + VERY FAST (without sorting list) + */ + // Must run under pthread_mutex_lock(&requests_mutex); + priority_request_level_c *prl = &request_levels[level_index]; + priority_request_c *rq; + + assert(prl->active == NULL); + // DEBUG("request_activate_lowest_slot") ; + + unsigned slot = __builtin_ffs(prl->slot_request_mask); + if (slot == 0) + rq = NULL; // no slot requesting on this level + else { + rq = prl->slot_request[slot - 1]; // slot 0 -> bit 0 -> slot=1 + assert(rq != NULL); + } + prl->active = rq; + // if (prl->active) + // DEBUG("request_activate_lowest_slot(): ->active = dma_request %p, level %u, slot %u",prl->active, prl->active->level_index, prl->active->slot); + // else + // DEBUG("request_activate_lowest_slot(): ->active = NULL"); + assert((prl->slot_request_mask == 0) == (prl->active == NULL)); + + return rq; +} + +// is any request of higher or same level executed? Is the next request executed delayed? +bool unibusadapter_c::request_is_blocking_active(uint8_t level_index) { + while (level_index < PRIORITY_LEVEL_COUNT) { + priority_request_level_c *prl = &request_levels[level_index]; + if (prl->active) + return true; + if (prl->slot_request_mask) + return true; + level_index++; } -// TODO: check queue for another device request -// rely on RL11 to check for completion and sorting DMA/INTR requests. return false; } -// if error_info: create error if INTR running -bool unibusadapter_c::request_INTR_active(const char *error_info) { - if (mailbox->arm2pru_req == ARM2PRU_INTR) { - if (error_info) - ERROR("%s: INTR requests active!", error_info); - return true; +// helper: push the active request to the PRU for execution +// VB: the next request to schedule already calculated and saved in priority_request_level_c.active +void unibusadapter_c::request_execute_active_on_PRU(unsigned level_index) { + priority_request_level_c *prl = &request_levels[level_index]; + assert(prl->active); + // Must run under pthread_mutex_lock(&requests_mutex); + // DEBUG("request_execute_active_on_PRU(level_idx=%u)", level_index); + if (level_index == PRIORITY_LEVEL_INDEX_NPR) { + dma_request_c *dmareq = dynamic_cast(prl->active); + assert(dmareq); + + // We do the device_DMA transfer in chunks so we can handle arbitrary buffer sizes. + // (the PRU mailbox has limited space available.) + + // Push the chunk to the PRU. + unsigned wordcount_remaining = dmareq->wordcount - dmareq->wordcount_completed_chunks(); + //dmareq->chunk_max_words = 2; // TEST + dmareq->chunk_words = std::min(dmareq->chunk_max_words, wordcount_remaining); + + assert(dmareq->chunk_words); // if complete, the dmareq should not be active anymore + + mailbox->dma.startaddr = dmareq->chunk_unibus_start_addr; + mailbox->dma.control = dmareq->unibus_control; + mailbox->dma.wordcount = dmareq->chunk_words; + + // Copy outgoing data into mailbox device_DMA buffer + if (dmareq->unibus_control == UNIBUS_CONTROL_DATO) { + memcpy((void*) mailbox->dma.words, dmareq->chunk_buffer_start(), + 2 * dmareq->chunk_words); + } + + // + // Start the PRU: + // signal still not cleared in worker() while processing this + // assert(mailbox->events.event_dma == 0); // previous signal must have been processed + // DEBUG("request_execute_active_on_PRU() DMA: ->active = dma_request %p, start = 0%06o, control=%u, wordcount=%u, data=0%06o ...", dmareq, + // mailbox->dma.startaddr, (unsigned)mailbox->dma.control, (unsigned)mailbox->dma.wordcount, (unsigned)mailbox->dma.words[0]); + + mailbox_execute(ARM2PRU_DMA); + // scheduling is fast, on complete there's a signal. + dmareq->executing_on_PRU = true; + + /* if DMA is done in multiple chunks, + then after PRU is complete, we don not call "active_complete() to remove the request. + Instead we leave it active, with transferrred data clipped from buffer start. + the "complete" signal will relaunch the remaining dma request automatically + we need a new address "chunk_start_addr" in dma_request_c + + As side effect, a higher priorized device may be serviced before the next chunk is transmitted, + This is intended and prevents data loss. + */ + + } else { + // Not DMA? must be INTR + intr_request_c *intrreq = dynamic_cast(prl->active); + assert(intrreq); + // Handle interrupt request to PRU. Setup mailbox: + mailbox->intr.level_index = intrreq->level_index; + mailbox->intr.vector[intrreq->level_index] = intrreq->vector; + if (intrreq->interrupt_register) + mailbox->intr.iopage_register_handle = + intrreq->interrupt_register->shared_register_handle; + else + mailbox->intr.iopage_register_handle = 0; // none + mailbox->intr.iopage_register_value = intrreq->interrupt_register_value; + // decode index 0..3 = BR4..BR7 => PRU signal register bit + switch (intrreq->level_index) { + case PRIORITY_LEVEL_INDEX_BR4: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B4; + break; + case PRIORITY_LEVEL_INDEX_BR5: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B5; + break; + case PRIORITY_LEVEL_INDEX_BR6: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B6; + break; + case PRIORITY_LEVEL_INDEX_BR7: + mailbox->intr.priority_arbitration_bit = PRIORITY_ARBITRATION_BIT_B7; + break; + + default: + ERROR("Request_INTR(): Illegal priority %u, aborting", intrreq->level_index); + return; + } + + // start on PRU + // PRU have got arbitration for an INTR of different level in the mean time: + // assert(mailbox->events.event_intr == 0) would trigger + mailbox_execute(ARM2PRU_INTR); + intrreq->executing_on_PRU = true; // waiting for GRANT + // PRU now changes state } -// TODO: check queue for another device request -// rely on RL11 to check for completion and sorting DMA/INTR requests. - return false; + + /* when PRU is finished, the worker() gets a signal, + then worker_dma_chunk_complete_event() or worker_intr_complete_event() is called. + On INTR or last DMA chunk, the request is completed. + It is removed from the slot schedule table and request->compelte_conmd is signaled + to DMA() or INTR() + */ +} + +// remove request pointer currently handled by PRU from tables +// also called on INTR_CANCEL +void unibusadapter_c::request_active_complete(unsigned level_index) { + // Must run under pthread_mutex_lock(&requests_mutex); + + priority_request_level_c *prl = &request_levels[level_index]; + if (!prl->active) // PRU compelted after INIT cleared the tables + return; + // DEBUG("request_active_complete") ; + + unsigned slot = prl->active->slot; + //if (prl->slot_request[slot] != prl->active) + // mailbox_execute(ARM2PRU_HALT) ; // LA: trigger on timeout REG_WRITE + // active not in table, if table cleared by INIT requests_cancel_scheduled() + assert(prl->slot_request[slot] == prl->active); // must still be in table + + // mark as complete + prl->active->executing_on_PRU = false; +// prl->active->complete = true; + // remove table entries + prl->slot_request[slot] = NULL; // clear slot from request + prl->slot_request_mask &= ~(1 << slot); // mask out slot bit + + priority_request_c *tmprq = prl->active; + prl->active = NULL; + + // signal to DMA() or INTR() + tmprq->complete = true; // close to signal + pthread_mutex_unlock(&tmprq->complete_mutex); + +// pthread_cond_signal(&tmprq->complete_cond); + +// pthread_cond_broadcast(&tmprq->complete_cond); } // Request a DMA cycle from Arbitrator. // 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) { +// Blocking == true: DMA() wait for request to complete +// Blocking == false: return immediately, the device logic should +// evaluate the request.complete flag or wait for the mutex - // - // 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); +void unibusadapter_c::DMA(dma_request_c& dma_request, bool blocking, uint8_t unibus_control, + uint32_t unibus_addr, uint16_t *buffer, uint32_t wordcount) { + assert(dma_request.slot < PRIORITY_SLOT_COUNT); + assert(dma_request.level_index == PRIORITY_LEVEL_INDEX_NPR); - pthread_mutex_lock(&_busWorker_mutex); - _dmaRequests.push(&request); - pthread_cond_signal(&_busWakeup_cond); - pthread_mutex_unlock(&_busWorker_mutex); + // setup device request + assert(wordcount > 0); + assert((unibus_addr + 2*wordcount) <= 2*UNIBUS_WORDCOUNT); - DEBUG("DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr, - wordcount); + pthread_mutex_lock(&requests_mutex); // lock schedule table operations - // - // Wait for request to finish. - // - pthread_mutex_lock(&_busWorker_mutex); - while (!request.IsComplete()) - { - pthread_cond_wait(&_requestFinished_cond, &_busWorker_mutex); + // In contrast to re-raised INTR, overlapping DMA requests from same board + // must not be ignored (different DATA situation) and are an device implementation error. + // If a device indeed has multiple DMA channels, it must use different pseudo-slots. + priority_request_level_c *prl = &request_levels[PRIORITY_LEVEL_INDEX_NPR]; + assert(prl->slot_request[dma_request.slot] == NULL); // not scheduled or prev completed + + pthread_mutex_lock(&dma_request.complete_mutex); // lock early, else PRU can signal cond before we lock + // dma_request.level-index, priority_slot in constructor + dma_request.complete = false; + dma_request.executing_on_PRU = false; + dma_request.unibus_control = unibus_control; + dma_request.unibus_start_addr = unibus_addr; + dma_request.chunk_unibus_start_addr = unibus_addr; + dma_request.unibus_end_addr = 0; // last transfered addr, or error position + dma_request.buffer = buffer; + dma_request.wordcount = wordcount; + dma_request.chunk_max_words = PRU_MAX_DMA_WORDCOUNT; // PRU limit, maybe less + // DEBUG("DMA(): initialized dma_request %p",&dma_request); + + // put into schedule tables + + request_schedule(dma_request); // assertion, if twice for same slot + if (!prl->active) { +// if (!request_is_active(dma_request.level_index)) { + // no device_DMA current performed: start immediately + // else triggered by PRU signals + request_activate_lowest_slot(dma_request.level_index); + request_execute_active_on_PRU(dma_request.level_index); } - pthread_mutex_unlock(&_busWorker_mutex); + pthread_mutex_unlock(&requests_mutex); - if (unibus_end_addr) - *unibus_end_addr = request.GetUnibusEndAddr() ; + // DEBUG("device DMA start: %s @ %06o, len=%d", unibus->control2text(unibus_control), unibus_addr, wordcount); + if (blocking) { + // acquire locked mutex => wait for worker to release + pthread_mutex_lock(&dma_request.complete_mutex); + pthread_mutex_unlock(&dma_request.complete_mutex); - return request.GetSuccess() ; + /* + // DMA() is blocking: Wait for request to finish. + // pthread_mutex_lock(&dma_request.mutex); + while (!dma_request.complete) { + // busy waiting OK + int res = pthread_cond_wait(&dma_request.complete_cond, &dma_request.complete_mutex); + assert(!res) ; + dma_request.dbg_complete_sig_received++ ; + } + */ + } } -void unibusadapter_c::dma_worker() -{ - //worker_init_realtime_priority(rt_device); - while(true) - { - dma_request_c* dmaReq = 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); +// A device raises an interrupt and simultaneously changes a value in +// one of its registers, the "interrupt register". +// ''interrupt_register' may be NULL if none. +// INTR() is NOT BLOCKING: it returns immediately. +// the actual interrupt vector is transfered when CPU interrupt level is lowered enough and +// other arbitration rules apply, which may never be the case. +// While pending, device may call INTR() again, causing waiting PRU requests to be modified. + +void unibusadapter_c::INTR(intr_request_c& intr_request, + unibusdevice_register_t *interrupt_register, uint16_t interrupt_register_value) { + assert(intr_request.slot < PRIORITY_SLOT_COUNT); + assert(intr_request.level_index <= 3); + assert((intr_request.vector & 3) == 0); // multiple of 2 words + + priority_request_level_c *prl = &request_levels[intr_request.level_index]; + pthread_mutex_lock(&requests_mutex); // lock schedule table operations + + // Is an INTR with same slot and level already executed on PRU + // or waiting in the schedule table? + // If yes: do not re-raise, will be completed at some time later. + if (prl->slot_request[intr_request.slot] != NULL) { + intr_request_c *scheduled_intr_req = + dynamic_cast(prl->slot_request[intr_request.slot]); + assert(scheduled_intr_req); + // A device may re-raised a pending INTR again + // (quite normal situation when other ISRs block, CPU overload) + // A re-raise will be ignored. + // ! Another device MAY NOT reraise an INTR with same slot/level + // ! (else complete signals may be routed to wrong device) + assert(scheduled_intr_req->device == intr_request.device); + assert(scheduled_intr_req->vector == intr_request.vector); + // if different vector, it may not be ignored -> change in program flow + + // If device uses multiple INTRs with different vectors (DL11 rcv+xmt), + // it must use different pseudo-slots. + + // scheduled and request_active_complete() not called + pthread_mutex_unlock(&requests_mutex); + if (interrupt_register) { + // if device re-raises a blocked INTR, CSR must complete immediately + intr_request.device->set_register_dati_value(interrupt_register, + interrupt_register_value, __func__); } - // - // We have a request: prioritize IRQ over DMA, dequeue from the requisite - // queue and get to work. - // - if (!_irqRequests.empty()) - { - irqReq = _irqRequests.front(); - _irqRequests.pop(); + return; // do not schedule a 2nd time + } + + intr_request.complete = false; + intr_request.executing_on_PRU = false; + + if (interrupt_register) + assert(intr_request.device == interrupt_register->device); + + // The associated device interrupt register (if any) should be updated + // atomically with raising the INTR signal line by PRU. + if (interrupt_register && request_is_blocking_active(intr_request.level_index)) { + // one or more another requests are handled by PRU: INTR signal delayed by Arbitrator, + // write intr register asynchronically here. + intr_request.device->set_register_dati_value(interrupt_register, + interrupt_register_value, __func__); + intr_request.interrupt_register = NULL; // don't do a 2nd time + } else { // forward to PRU + // intr_request.level_index, priority_slot, vector in constructor + intr_request.interrupt_register = interrupt_register; + intr_request.interrupt_register_value = interrupt_register_value; + } + + // put into schedule tables + request_schedule(intr_request); // assertion, if twice for same slot + + if (!prl->active) { + // INTR of this level can be raised immediately + // If other level active, let PRU atomically set the interrupt register value. + request_activate_lowest_slot(intr_request.level_index); + request_execute_active_on_PRU(intr_request.level_index); + // else activation triggered by PRU signal in worker() + } + + pthread_mutex_unlock(&requests_mutex); // work on schedule table finished + + /* + // If INTR() is blocking: Wait for request to finish. + pthread_mutex_lock(&intr_request.mutex); + while (!intr_request.complete) { + pthread_cond_wait(&intr_request.complete_cond, &intr_request.mutex); + } + pthread_mutex_unlock(&intr_request.mutex); + */ +} + +/* A device may cancel an INTR request, if not yet GRANTed by Arbitrator. + Maybe useful if device sees INTR is not handeled by Arbitrator due to + blocked interrupt of this level. + Relevance of this usage pattern unclear, but used in KW11 diag, ZDLDI0 Test 17. + After cancelation, ARM receives NO completion event from PRU. + */ +void unibusadapter_c::cancel_INTR(intr_request_c& intr_request) { + uint8_t level_index = intr_request.level_index; // alias + priority_request_level_c *prl = &request_levels[level_index]; + if (prl->slot_request[intr_request.slot] == NULL) + return; // not scheduled or active + + pthread_mutex_lock(&requests_mutex); // lock schedule table operations + if (&intr_request == prl->active) { + // already on PRU + mailbox_execute(ARM2PRU_INTR_CANCEL); + request_active_complete(level_index); + + // restart next request + request_activate_lowest_slot(level_index); + if (prl->active) + request_execute_active_on_PRU(level_index); + } else { + // not active on PRU: just remove from schedule table + prl->slot_request[intr_request.slot] = NULL; // clear slot from request + prl->slot_request_mask &= ~(1 << intr_request.slot); // mask out slot bit + } + // both empty, or both filled + assert((prl->slot_request_mask == 0) == (prl->active == NULL)); + pthread_mutex_unlock(&intr_request.complete_mutex); + + pthread_mutex_unlock(&requests_mutex); // lock schedule table operations + +} + +// set state of INIT +void unibusadapter_c::worker_init_event() { + unsigned device_handle; + unibusdevice_c *device; + // notify device on change of INIT line + DEBUG("worker_init_event(): INIT %s", line_INIT ? "asserted" : "deasserted"); + for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) + if ((device = devices[device_handle])) { + device->init_asserted = line_INIT; + device->on_init_changed(); } - else - { - dmaReq = _dmaRequests.front(); - _dmaRequests.pop(); + + // Clear bus request queues + pthread_mutex_lock(&requests_mutex); + requests_cancel_scheduled(); + pthread_mutex_unlock(&requests_mutex); +} + +void unibusadapter_c::worker_power_event() { + unsigned device_handle; + unibusdevice_c *device; + // notify device on change of DC_LO line + DEBUG("worker_power_event(): DCLO %s", line_DCLO ? "asserted" : "deasserted"); + for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) + if ((device = devices[device_handle])) { + device->power_down = line_DCLO; + device->on_power_changed(); } - 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)); + // Clear bus request queues + pthread_mutex_lock(&requests_mutex); + requests_cancel_scheduled(); + pthread_mutex_unlock(&requests_mutex); +} - 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. +// process DATI/DATO access to active device registers - uint32_t maxTransferSize = 512; +void unibusadapter_c::worker_deviceregister_event() { + unsigned device_handle; + unibusdevice_c *device; + device_handle = mailbox->events.device_handle; + assert(device_handle); + device = devices[device_handle]; + unsigned evt_idx = mailbox->events.device_register_idx; + uint32_t evt_addr = mailbox->events.addr; + // normally evt_data == device_reg->shared_register->value + // but shared value gets desorted if INIT in same event clears the registers before DATO + uint16_t evt_data = mailbox->events.data; + unibusdevice_register_t *device_reg = &(device->registers[evt_idx]); + uint8_t unibus_control = mailbox->events.unibus_control; - uint32_t wordCount = dmaReq->GetWordCount(); - uint32_t unibusAddr = dmaReq->GetUnibusStartAddr(); - uint32_t bufferOffset = 0; + /* call device event callback + The PRU has only one "value" for each register, but "active" registers have separate + read(DATI) and write(DATO) flipflops. So if register is "active: + BEFORE processing of logic state changes: + DATO: save written value into .active_write_val + restore changed shared PRU register with .active_read_val for next read + */ + if (device_reg->active_on_dati && !UNIBUS_CONTROL_ISDATO(unibus_control)) { + // register is read with DATI, this changes the logic state + evt_addr &= ~1; // make even + assert(evt_addr == device->base_addr.value + 2 * evt_idx); + unibus_control = UNIBUS_CONTROL_DATI; + // read access: dati-flipflops do not change - while (wordCount > 0) - { - uint32_t chunkSize = std::min(maxTransferSize, wordCount); + // signal: changed by UNIBUS + device->log_register_event("DATI", device_reg); - mailbox->dma.startaddr = unibusAddr + bufferOffset * 2; - mailbox->dma.control = dmaReq->GetUnibusControl(); - mailbox->dma.wordcount = chunkSize; + device->on_after_register_access(device_reg, unibus_control); + } else if (device_reg->active_on_dato && UNIBUS_CONTROL_ISDATO(unibus_control)) { + // uint16_t reg_value_written = device_reg->shared_register->value; + // restore value accessible by DATI + device_reg->shared_register->value = device_reg->active_dati_flipflops; + // Restauration of shared_register->value IS NOT ATOMIC against device logic threads. + // Devices must use only reg->active_dati_flipflops ! + switch (unibus_control) { + case UNIBUS_CONTROL_DATO: + // write into a register with separate read/write flipflops + assert(evt_addr == device->base_addr.value + 2 * evt_idx); + // clear unused bits, save written value + device_reg->active_dato_flipflops = evt_data & device_reg->writable_bits; + // signal: changed by UNIBUS + device->log_register_event("DATO", device_reg); + break; + case UNIBUS_CONTROL_DATOB: + // UNIBUS may access only 8bit half of register with DATOB. + // convert all active registers accesses to 16 bit + evt_data &= device_reg->writable_bits; // clear unused bits + // save written value + if (evt_addr & 1) // odd address: bits 15:8 written + device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0x00ff) + | (evt_data & 0xff00); + else + // even address : bits 7:0 written + device_reg->active_dato_flipflops = (device_reg->active_dato_flipflops & 0xff00) + | (evt_data & 0x00ff); + unibus_control = UNIBUS_CONTROL_DATO; // simulate 16 bit access + // signal: changed by UNIBUS + device->log_register_event("DATOB", device_reg); + break; + } + device->on_after_register_access(device_reg, unibus_control); + /* + DEBUG(LL_DEBUG, LC_UNIBUS, "dev.reg=%d.%d, %s, addr %06o, data %06o->%06o", + device_handle, evt_idx, + unibus_c::control2text(mailbox->event.unibus_control), evt_addr, + oldval, device_reg->shared_register->value); + */ + } +} - // Copy outgoing data into maibox DMA buffer - if (dmaReq->GetUnibusControl() == UNIBUS_CONTROL_DATO) - { - memcpy( - (void*)mailbox->dma.words, - dmaReq->GetBuffer() + bufferOffset, - 2 * chunkSize); - } +// called by PRU signal when DMA transmission complete +void unibusadapter_c::worker_dma_chunk_complete_event() { + priority_request_level_c *prl = &request_levels[PRIORITY_LEVEL_INDEX_NPR]; + bool more_chunks; + // Must run under pthread_mutex_lock(&requests_mutex) ; - // - // Start the PRU: - mailbox->arm2pru_req = ARM2PRU_DMA_ARB_CLIENT; + dma_request_c *dmareq = dynamic_cast(prl->active); - // - // 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) - { - timeout.wait_us(50); - retries++; - } + assert(dmareq != NULL); + dmareq->unibus_end_addr = mailbox->dma.cur_addr; // track emnd of trasnmission, eror position + unsigned wordcount_transferred = dmareq->wordcount_completed_chunks() + + mailbox->dma.wordcount; + assert(wordcount_transferred <= dmareq->wordcount); + if (mailbox->dma.control == UNIBUS_CONTROL_DATI) { + // guard against buffer overrun + // PRU read chunk data from UNIBUS into mailbox + // copy result cur_DMA_wordcount from mailbox->DMA buffer to cur_DMA_buffer + memcpy(dmareq->chunk_buffer_start(), (void *) mailbox->dma.words, + 2 * mailbox->dma.wordcount); + } + if (mailbox->dma.cur_status != DMA_STATE_READY) { + // failure: abort remaining chunks + dmareq->success = false; + more_chunks = false; + } else if (wordcount_transferred == dmareq->wordcount) { + // last chunk completed + dmareq->success = true; + more_chunks = false; + } else { + // more data to transfer: next chunk. + dmareq->chunk_unibus_start_addr = mailbox->dma.cur_addr + 2; + // dmarequest remains prl->active and ->busy - // - // TODO: this should not be necessary. There are rare occasions - // where it appears that the PRU dma transfer is interrupted - // but never clears the DMA active flag, so we hang in the loop above - // forever. - // Nothing to do in that case but give up. - // And log the issue. Should get to the root of this.. - // - if (retries == 10000) - { - ERROR("dma timeout"); - } - - 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); - } - wordCount -= chunkSize; - bufferOffset += chunkSize; - } + // re-activate this request, or choose another with higher slot priority, + // inserted in parallel (interrupt this DMA) + prl->active = NULL; + request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR); - dmaReq->SetUnibusEndAddr(mailbox->dma.cur_addr); - dmaReq->SetSuccess(mailbox->dma.cur_status == DMA_STATE_READY); - // no success: UnibusEndAddr is first failed address + request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); + more_chunks = true; + // DEBUG("DMA chunk: %s @ %06o..%06o, wordcount %d, data=%06o, %06o, ... %s", + // unibus->control2text(mailbox->dma.control), mailbox->dma.startaddr, + // mailbox->dma.cur_addr, mailbox->dma.wordcount, mailbox->dma.words[0], + // mailbox->dma.words[1], dmareq->success ? "OK" : "TIMEOUT"); - assert(dmaReq->GetUnibusStartAddr() + dmaReq->GetWordCount() * 2 == - mailbox->dma.cur_addr + 2); + } + if (!more_chunks) { + DEBUG("DMA ready: %s @ %06o..%06o, wordcount %d, data=%06o, %06o, ... %s", + unibus->control2text(dmareq->unibus_control), dmareq->unibus_start_addr, + dmareq->unibus_end_addr, dmareq->wordcount, dmareq->buffer[0], + dmareq->buffer[1], dmareq->success ? "OK" : "TIMEOUT"); + // clear from schedule table of this level + request_active_complete(PRIORITY_LEVEL_INDEX_NPR); + + // check and execute DMA on other priority_slot + if (request_activate_lowest_slot(PRIORITY_LEVEL_INDEX_NPR)) + request_execute_active_on_PRU(PRIORITY_LEVEL_INDEX_NPR); + } +} + +// called by PRU signal when INTR vector transmission complete +// OR the request has been canceled +// priority_level_index: 0..3 = BR4..BR7 +void unibusadapter_c::worker_intr_complete_event(uint8_t level_index) { + // Must run under pthread_mutex_lock(&requests_mutex) ; + priority_request_level_c *prl = &request_levels[level_index]; + + // if 1.st opcode of an ISR is a clear of INTR condition, + // cancle_INTR() may be called in worker_deviceregsiter_event() + // then the request is aloready remoced ferom schedule table. + //assert(prl->active); + + // clear from schedule table of this level + request_active_complete(level_index); + + // activate next request of this level on PRU for priority arbitration + request_activate_lowest_slot(level_index); + if (prl->active) + request_execute_active_on_PRU(level_index); + // else INTRs for all slots of this level completed +} + +// runs in background, catches and distributes PRU events +void unibusadapter_c::worker(unsigned instance) { + UNUSED(instance); // only one + int res; + bool any_event; + + // set thread priority to MAX. + // - fastest response to select() call in prussdrv_pru_wait_event_timeout() + // (minimal I/O latency) + // - not interrupted by other tasks while running + // check with tool "top" or "htop". + worker_init_realtime_priority(rt_device); // do not block while debugging + // worker_init_realtime_priority(rt_max); // set to max prio + + // mailbox may be un-initialized + + while (!workers_terminate) { + // Timing: + // This is THE ONE mechanism where "realtime meets Linux" + // To respond to the PRU signal, Linux must wake up schedule this thread + // Test: + // - set RT prio of this worker thread to true 100% (SCHED_FIFO. max prio, + // /proc/sys/kernel/sched_rt_runtime_us = -1 + // - let the PRU pulse a GPIO on signal_set + // - let this worker raise a GPIO while its active + // - verify with scope that PRU - signal to worker start is about 100-300us + // MUST NOT GET LARGER on any Linux activity, + + // main loop + // wait 0.1 sec, just tell + /* The prussdrv_pru_wait_event() function returns the number of times + the event has taken place, as an unsigned int. There is no out-of- + band value to indicate error (and it can wrap around to 0 if you + run the program just a whole lot of times). */ + res = prussdrv_pru_wait_event_timeout(PRU_EVTOUT_0, 100000/*us*/); +//res = prussdrv_pru_wait_event(PRU_EVTOUT_0); + // PRU may have raised more than one event before signal is accepted. + // single combination of only INIT+DATI/O possible + prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); + // uses select() internally: 0 = timeout, -1 = error, else event count received + any_event = true; + // at startup sequence, mailbox may be not yet valid + while (mailbox && res > 0 && any_event) { // res is const + any_event = false; + // Process multiple events sent by PRU. // - // Signal that the request is complete. - // - pthread_mutex_lock(&_busWorker_mutex); - dmaReq->SetComplete(); - pthread_cond_signal(&_requestFinished_cond); - pthread_mutex_unlock(&_busWorker_mutex); - } - else - { - // Handle interrupt request - switch(irqReq->GetInterruptLevel()) - { - case 4: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B4; - break; - - case 5: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B5; - break; - - case 6: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B6; - break; - - case 7: - mailbox->intr.priority_bit = ARBITRATION_PRIORITY_BIT_B7; - break; - - default: - ERROR("Request_INTR(): Illegal priority %u, aborting", irqReq->GetInterruptLevel()); - return; + // while ARM accepts the signal, the PRU may set more events + // critical a mix of INIT and DATI/DATO: RESET and register access follow directly + // But no DATI/DATO occurs while INIT active. So reconstruct event order by + // processing order: INIT_DEASSERT, DATI/O, INIT_ASSERT, DCLO/ACLO + bool init_raising_edge = false; + bool init_falling_edge = false; + bool dclo_raising_edge = false; + bool dclo_falling_edge = false; + // DEBUG("mailbox->events: mask=0x%x", mailbox->events.eventmask); + if (mailbox->events.event_init) { + any_event = true; + // robust: any change in ACLO/DCL=INIT updates state of all 3. + // Initial DCLO-cycle to PDP_11 intialize these states + if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_INIT) { + if (!line_INIT) + init_raising_edge = true; + line_INIT = true; + } else { + if (line_INIT) + init_falling_edge = true; + line_INIT = false; + } + mailbox->events.event_init = 0; // PRU may re-raise and change mailbox now } + if (mailbox->events.event_power) { + any_event = true; + if (mailbox->events.initialization_signals_cur & INITIALIZATIONSIGNAL_DCLO) { + if (!line_DCLO) + dclo_raising_edge = true; + line_DCLO = true; + } else { + if (line_DCLO) + dclo_falling_edge = true; + line_DCLO = false; + } + mailbox->events.event_power = 0; // PRU may re-raise and change mailbox now + DEBUG( + "EVENT_INITIALIZATIONSIGNALS: (sigprev=0x%x,) cur=0x%x, init_raise=%d, init_fall=%d, dclo_raise=%d, dclo_fall=%d", + mailbox->events.initialization_signals_prev, + mailbox->events.initialization_signals_cur, init_raising_edge, + init_falling_edge, dclo_raising_edge, dclo_falling_edge); - mailbox->intr.vector = irqReq->GetVector(); + } + if (dclo_raising_edge || dclo_falling_edge) + worker_power_event(); // power signal power change + if (init_falling_edge) // INIT asserted -> deasserted. DATI/DATO cycle only possible after that. + worker_init_event(); + if (mailbox->events.event_deviceregister) { + any_event = true; - // start! - mailbox->arm2pru_req = ARM2PRU_INTR; - // PRU now changes state - - // Signal that the request has been raised. - pthread_mutex_lock(&_busWorker_mutex); - irqReq->SetComplete(); - 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 - // do this? (as w/dma) - timeout_c timeout; - while(request_INTR_active(nullptr)) - { - timeout.wait_us(50); - } - } + // DATI/DATO + // DEBUG("EVENT_DEVICEREGISTER: control=%d, addr=%06o", (int)mailbox->events.unibus_control, mailbox->events.addr); + worker_deviceregister_event(); + // ARM2PRU opcodes raised by device logic are processed in midst of bus cycle + mailbox->events.event_deviceregister = 0; // PRU continous bus cycle with SSYN now + } + if (mailbox->events.event_dma) { + any_event = true; + pthread_mutex_lock(&requests_mutex); + worker_dma_chunk_complete_event(); + pthread_mutex_unlock(&requests_mutex); + mailbox->events.event_dma = 0; // PRU may re-raise and change mailbox now + } + if (mailbox->events.event_intr) { + // INTRs are granted unpredictable by Arbitrator + any_event = true; + // INTR of which level? the .active rquest of the" + pthread_mutex_lock(&requests_mutex); + worker_intr_complete_event(mailbox->events.event_intr_level_index); + pthread_mutex_unlock(&requests_mutex); + mailbox->events.event_intr = 0; // PRU may re-raise and change mailbox now + } + if (init_raising_edge) // INIT deasserted -> asserted. DATI/DATO cycle only possible before that. + worker_init_event(); + } + // Signal to PRU: continue UNIBUS cycles now with SSYN deassert } } -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()) - { - dma_request_c* dmaReq = _dmaRequests.front(); - dmaReq->SetSuccess(false); - dmaReq->SetComplete(); - pthread_cond_signal(&_requestFinished_cond); - _dmaRequests.pop(); - } - while (!_irqRequests.empty()) - { - irq_request_c* irqReq = _irqRequests.front(); - irqReq->SetComplete(); - pthread_cond_signal(&_requestFinished_cond); - _irqRequests.pop(); - } - 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); - - 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); - - // - // And we're done. - // -} - // debugging: print PRU sharead regsster map void unibusadapter_c::print_shared_register_map() { unsigned i; @@ -846,7 +1042,7 @@ void unibusadapter_c::print_shared_register_map() { } } -// dump known devices + // dump known devices printf("Registered devices by handle:\n"); for (device_handle = 0; device_handle <= MAX_DEVICE_HANDLE; device_handle++) if (devices[device_handle] != NULL) { diff --git a/10.01_base/2_src/arm/unibusadapter.hpp b/10.01_base/2_src/arm/unibusadapter.hpp index e88003d..c4d8bbc 100644 --- a/10.01_base/2_src/arm/unibusadapter.hpp +++ b/10.01_base/2_src/arm/unibusadapter.hpp @@ -1,140 +1,109 @@ /* 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-2019 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 + jul-2019 JH rewrite: multiple parallel arbitration levels + 12-nov-2018 JH entered beta phase */ #ifndef _UNIBUSADAPTER_HPP_ #define _UNIBUSADAPTER_HPP_ -#include -#include - #include "iopageregister.h" +#include "priorityrequest.hpp" +#include "unibusadapter.hpp" #include "unibusdevice.hpp" -class dma_request_c -{ +// for each priority arbitration level, theres a table with backplane slots. +// Each device sits in a slot, the slot determinss the request priority within one level (BR4567,NP). +class priority_request_level_c { public: - dma_request_c( - uint8_t unibus_control, - uint32_t unibus_addr, - uint16_t *buffer, - uint32_t wordcount); + // remember for each backplane slot wether the device has requested + // INTR or DMA at this level + priority_request_c* slot_request[PRIORITY_SLOT_COUNT + 1]; + // Optimization to find the high priorized slot in use very fast. + // bit array: bit set -> slot has open request. + uint32_t slot_request_mask; - ~dma_request_c(); + priority_request_c* active; // request currently handled by PRU, not in table anymore - 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; } - - 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; - - bool _isComplete; - bool _success; + void clear(); }; -class irq_request_c -{ -public: - irq_request_c( - uint32_t level, - uint32_t vector); - - ~irq_request_c(); - - uint32_t GetInterruptLevel() { return _level; } - uint32_t GetVector() { return _vector; } - bool IsComplete() { return _isComplete; } - - void SetComplete() { _isComplete = true; } - -private: - uint32_t _level; - uint32_t _vector; - bool _isComplete; -}; - - // is a device_c. need a thread (but no params) class unibusadapter_c: public device_c { +private: + + // handle arbitration for each of the 5 request levels in parallel + priority_request_level_c request_levels[PRIORITY_LEVEL_COUNT]; + + pthread_mutex_t requests_mutex; + + void worker_init_event(void); + void worker_power_event(void); + void worker_deviceregister_event(void); + void worker_dma_chunk_complete_event(void); + void worker_intr_complete_event(uint8_t level_index); + void worker(unsigned instance) override; // background worker function 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(void) override; // background worker function - 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_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); + // Helper for request processing + void requests_init(void); + + void request_schedule(priority_request_c& request); + void requests_cancel_scheduled(void); + priority_request_c *request_activate_lowest_slot(unsigned level_index); +// bool request_is_active( unsigned level_index); + bool request_is_blocking_active(uint8_t level_index); + void request_active_complete(unsigned level_index); + void request_execute_active_on_PRU(unsigned level_index); + + void DMA(dma_request_c& dma_request, bool blocking, uint8_t unibus_control, + uint32_t unibus_addr, uint16_t *buffer, uint32_t wordcount); + void INTR(intr_request_c& intr_request, unibusdevice_register_t *interrupt_register, + uint16_t interrupt_register_value); + void cancel_INTR(intr_request_c& intr_request) ; + 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; }; 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..9be9cf4 100644 --- a/10.01_base/2_src/arm/unibusdevice.cpp +++ b/10.01_base/2_src/arm/unibusdevice.cpp @@ -1,36 +1,38 @@ /* 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 +#include #include "logger.hpp" #include "unibusadapter.hpp" #include "unibusdevice.hpp" @@ -39,19 +41,55 @@ 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_priority_slot = 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; + priority_slot.readonly = true; + intr_vector.readonly = true; + intr_level.readonly = true; + install(); // visible on UNIBUS + } else { + // disable + uninstall(); + base_addr.readonly = false; + priority_slot.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 priority_slot, + unsigned default_intr_vector, unsigned default_intr_level) { + assert(priority_slot <= PRIORITY_SLOT_COUNT); // bitmask! + this->default_base_addr = this->base_addr.value = default_base_addr; + this->default_priority_slot = this->intr_vector.value = priority_slot; + 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 +100,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); } @@ -132,13 +166,6 @@ void unibusdevice_c::reset_unibus_registers() { } } -// set an UNIBUS interrupt condition with intr_vector and intr_level -void unibusdevice_c::interrupt(void) { - // delegate to unibusadapter_c - unibusadapter->request_INTR(intr_level.value, intr_vector.value); - // WARNING("unibusdevice_c::interrupt() TODO: generated interrupt!"); -} - // log register state changes: // print event info // - print full register content to logger @@ -184,3 +211,89 @@ void unibusdevice_c::log_register_event(const char *change_info, } DEBUG(buffer); } + +// search device in global list mydevices[] +unibusdevice_c *unibusdevice_c::find_by_request_slot(uint8_t priority_slot) { + list::iterator devit; + for (devit = device_c::mydevices.begin(); devit != device_c::mydevices.end(); ++devit) { + unibusdevice_c *ubdevice = dynamic_cast(*devit); + if (ubdevice) { + // all dma and intr requests + for (vector::iterator reqit = ubdevice->dma_requests.begin(); + reqit < ubdevice->dma_requests.end(); reqit++) + if ((*reqit)->get_priority_slot() == priority_slot) + return ubdevice; + for (vector::iterator reqit = ubdevice->intr_requests.begin(); + reqit < ubdevice->intr_requests.end(); reqit++) + if ((*reqit)->get_priority_slot() == priority_slot) + return ubdevice; + } + } + return NULL; +} + +// returns a string of form +// reg_first-reg_last, slots from-to, DMA, INTR level1/vec1,level2/vec2,... +char *unibusdevice_c::get_unibus_resource_info(void) { + static char buffer[1024]; + char tmpbuff[256]; + buffer[0] = 0; + + // get register address range + // use parameter "base_addr", register struct only valid after unibusadapter.install() + if (register_count == 0) // cpu is a device without register interface + strcpy(tmpbuff, ""); + else if (register_count == 1) + sprintf(tmpbuff, "addr %06o", base_addr.value); + else + sprintf(tmpbuff, "addr %06o-%06o (%d regs)", base_addr.value, base_addr.value+2*(register_count-1), register_count) ; + strcat(buffer, tmpbuff); + + // get priority slot range from DMA request and intr_requests + uint8_t slot_from = 0xff, slot_to = 0; + for (vector::iterator it = dma_requests.begin(); it < dma_requests.end(); + it++) { + slot_from = std::min(slot_from, (*it)->get_priority_slot()); + slot_to = std::max(slot_to, (*it)->get_priority_slot()); + } + for (vector::iterator it = intr_requests.begin(); + it < intr_requests.end(); it++) { + slot_from = std::min(slot_from, (*it)->get_priority_slot()); + slot_to = std::max(slot_to, (*it)->get_priority_slot()); + } + + if (slot_from > slot_to) // no requests: use devcie parameter + slot_from = slot_to = priority_slot.value; + if (slot_from == slot_to) + sprintf(tmpbuff, ", slot %u", (unsigned) slot_from); + else + sprintf(tmpbuff, ", slots %u-%u", (unsigned) slot_from, (unsigned) slot_to); + strcat(buffer, tmpbuff); + + // DMA channels + if (dma_requests.size() > 0) { + if (dma_requests.size() == 1) + sprintf(tmpbuff, ", DMA"); + else + sprintf(tmpbuff, ", %uxDMA", dma_requests.size()); + strcat(buffer, tmpbuff); + } + // Interrupts + if (intr_requests.size() > 4) { + // that crazy testcontroller has 31*4 ! + sprintf(tmpbuff, "%d INTRs", intr_requests.size()); + strcat(buffer, tmpbuff); + } else if (intr_requests.size() > 0) { + const char *sep = ":"; + strcat(buffer, ", INTRs"); + for (vector::iterator it = intr_requests.begin(); + it < intr_requests.end(); it++) { + sprintf(tmpbuff, "%s%d/%03o", sep, (*it)->get_level(), (*it)->get_vector()); + strcat(buffer, tmpbuff); + sep = ","; + } + } + + return buffer; +} + diff --git a/10.01_base/2_src/arm/unibusdevice.hpp b/10.01_base/2_src/arm/unibusdevice.hpp index 3348e30..33dc5c8 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_ @@ -33,12 +33,15 @@ #include "device.hpp" #include "iopageregister.h" +#include "priorityrequest.hpp" +// forwards class unibusdevice_c; -typedef struct { + +typedef struct unibusdevice_register_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 +80,49 @@ typedef struct { } unibusdevice_register_t; class unibusdevice_c: public device_c { +public: + static unibusdevice_c *find_by_request_slot(uint8_t priority_slot) ; + +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); + // !!! slot, vector, level READONLY. If user shoudl change, + // !! add logic to update dma_request_c and intr_request_c + // 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 priority_slot = parameter_unsigned_c(this, "slot", "sl", true, "", + "%d", "backplane slot #, interrupt priority within one level, 0 = next to CPU", 16, + 10); + // dump devices without buffers toward CPU, smart buffering devices other end + + 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: 4,5,6,7", 3, 8); // DEC defaults as defined by device type uint32_t default_base_addr; - unsigned default_intr_vector; - unsigned default_intr_level; + uint8_t default_priority_slot; + uint8_t default_intr_level; + uint16_t default_intr_vector; + + // requests in use + std::vector dma_requests; + std::vector intr_requests; + + void set_default_bus_params(uint32_t default_base_addr, unsigned default_priority_slot, + 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 +130,25 @@ 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) ; - - // set an UNIBUS interrupt condition with intr_vector and intr_level - void interrupt(void) ; + unibusdevice_register_t *register_by_name(string name); + unibusdevice_register_t *register_by_unibus_address(uint32_t addr); // callback to be called on controller register DATI/DATO events. // must ACK mailbox.event.signal. Asynchron! @@ -146,8 +162,9 @@ 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) ; + char *get_unibus_resource_info(void) ; }; diff --git a/10.01_base/2_src/arm/utils.cpp b/10.01_base/2_src/arm/utils.cpp index 122f269..a3a0c10 100644 --- a/10.01_base/2_src/arm/utils.cpp +++ b/10.01_base/2_src/arm/utils.cpp @@ -1,29 +1,29 @@ /* utils.cpp: misc. utilities - 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 - 20-may-2018 JH created -*/ + 12-nov-2018 JH entered beta phase + 20-may-2018 JH created + */ #define _UTILS_CPP_ @@ -31,8 +31,11 @@ #include #include #include -#include +#include #include +#include +#include +#include #include #include #include @@ -46,13 +49,12 @@ using namespace std; - /********************************* * strcpy without buffer overlfow */ void strcpy_s(char *dest, int len, const char *src) { - strncpy(dest, src, len-1) ; - dest[len-1] = 0 ; // termiante if truncated + strncpy(dest, src, len - 1); + dest[len - 1] = 0; // termiante if truncated } /********************************* @@ -72,25 +74,48 @@ void SIGINTcatchnext() { SIGINTreceived = 0; } -void break_here(void) {} - +void break_here(void) { +} /*** time measuring ***/ - timeout_c::timeout_c() { - log_label = "TO" ; + log_label = "TO"; } -void timeout_c::start(uint64_t duration_ns) { +uint64_t timeout_c::get_resolution_ns() { + struct timespec res; + clock_getres(CLOCK_MONOTONIC, &res); + return BILLION * res.tv_sec + res.tv_nsec; +} + +void timeout_c::start_ns(uint64_t duration_ns) { this->duration_ns = duration_ns; clock_gettime(CLOCK_MONOTONIC, &starttime); } +void timeout_c::start_us(uint64_t duration_us) { + start_ns(duration_us * 1000); +} + +void timeout_c::start_ms(uint64_t duration_ms) { + start_ns(duration_ms * MILLION); +} + uint64_t timeout_c::elapsed_ns(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - return BILLION * (now.tv_sec - starttime.tv_sec) + now.tv_nsec - starttime.tv_nsec; + uint64_t result = (uint64_t) BILLION * (now.tv_sec - starttime.tv_sec) + + (uint64_t) now.tv_nsec - starttime.tv_nsec; + return result; +} + +uint64_t timeout_c::elapsed_us(void) { + return elapsed_ns() / 1000; +} + +uint64_t timeout_c::elapsed_ms(void) { + return elapsed_ns() / MILLION; } bool timeout_c::reached() { @@ -102,7 +127,7 @@ void timeout_c::wait_ns(uint64_t duration_ns) { struct timespec ts = { (long) (duration_ns / BILLION), (long) (duration_ns % BILLION) }; int res = nanosleep(&ts, NULL); if (res) - DEBUG("nanosleep() return a %d", res) ; + DEBUG("nanosleep() return a %d", res); } // wait a number of milliseconds @@ -126,24 +151,68 @@ void progress_c::init(unsigned linewidth) { } void progress_c::put(const char *info) { - printf("%s", info); cur_col += strlen(info); if (cur_col >= linewidth) { printf("\n"); - cur_col = 0; + cur_col = strlen(info); } + printf("%s", info); fflush(stdout); } +void progress_c::putf(const char *fmt, ...) { + static char buffer[256]; + va_list arg_ptr; + + va_start(arg_ptr, fmt); + vsprintf(buffer, fmt, arg_ptr); + va_end(arg_ptr); + + put(buffer); +} /* random number with 24 valid bits * RAND_MAX is only guaranteed 15 bits */ unsigned random24() { unsigned val; + assert(RAND_MAX >= 0x3fff); val = rand() ^ (rand() << 9); return val & 0xffffff; } +/* random numbers, distributed logarithmically + * returns 0..limit-1 + */ +uint32_t random32_log(uint32_t limit) { + uint32_t result, mantissa; + int rand_exponent, limit_exp; + assert(limit > 0); + assert(RAND_MAX >= 0x3fff); // 15 bits + // generate normalized mantissa, bit 31 set + mantissa = rand(); + mantissa ^= (rand() << 9); + mantissa ^= (rand() << 18); + while ((mantissa & (1 << 31)) == 0) + mantissa <<= 1; + // rand_exponent of limit: 2^limit_exp <= limit + // ctz = "Count Leading Zeros" + // limit = 1 -> exp=0, limit = 0xffffffff -> exp = 31 + limit_exp = 31 - __builtin_clz(limit); + limit_exp++; // 2^limit_exp >= limit + + // random rand_exponent 0..limit-1 + rand_exponent = rand() % limit_exp; + // 2^rand_exponent <= limit + result = mantissa >> (31 - rand_exponent); + // mantissa has bit 31 set, is never shifted more then 31 + assert(result); + + // final masking + if (limit > 1) + result %= limit; + return result; +} + char *cur_time_text() { static char result[80], millibuff[10]; timeval cur_time; @@ -166,17 +235,16 @@ bool fileExists(const std::string& filename) { // Generates "perror()" printout, // msgfmt must have one "%s" field for absolute filename char *fileErrorText(const char *msgfmt, const char *fname) { - static char linebuff[PATH_MAX +100]; - char abspath[PATH_MAX] ; + static char linebuff[PATH_MAX + 100]; + char abspath[PATH_MAX]; realpath(fname, abspath); sprintf(linebuff, msgfmt, abspath); - strcat(linebuff, ": ") ; - strcat (linebuff, strerror(errno)) ; + strcat(linebuff, ": "); + strcat(linebuff, strerror(errno)); // perror(linebuff); - return linebuff ; + return linebuff; } - // add a number of microseconds to a time struct timespec timespec_add_us(struct timespec ts, unsigned us) { ts.tv_nsec += us * 1000; @@ -204,3 +272,102 @@ struct timespec timespec_future_us(unsigned offset_us) { } */ +// decodes C escape sequences \char, \nnn octal, \xnn hex +// result string is smaller or same as "encoded", must have at least "ncoded" size +// return: true of OK, else false +static int digitval(char c) { + c = toupper(c); + if (c < '0') + return 0; // illegal + else if (c <= '9') + return c - '0'; + else if (c < 'A') + return 0; // illegal + else if (c <= 'F') + return c - 'A' + 10; + else + return 0; // illegal +} + +bool str_decode_escapes(char *result, unsigned result_size, char *encoded) { + int c ; + char *wp = result; // write pointer + char *rp = encoded; // read pointer + assert(result_size >= strlen(encoded)); + while (*rp) { + if (*rp != '\\') { + *wp++ = *rp++; // not escaped + continue; + } + // decode escapes + rp++; // eat backslash + int n = strspn(rp, "01234567"); // + if (n >= 1) { // \nnn given + // use max 3 digits for octal literal + c = digitval(*rp++) ; + if (n >= 2) + c = c * 8 + digitval(*rp++) ; + if (n >= 3) + c = c * 8 + digitval(*rp++) ; + *wp++ = (char) c; + continue; + } + switch (*rp) { + // literals allowed behind backslash + case '\'': + case '"': + case '?': + case '\\': + *wp++ = *rp++; + continue; + case 'a': + *wp++ = 0x07; // audible bell + rp++; + continue; + case 'b': + *wp++ = 0x08; // backspace + rp++; + continue; + case 'f': + *wp++ = 0x0c; // form feed - new page + rp++; + continue; + case 'n': + *wp++ = 0x0a; // line feed - new line + rp++; + continue; + case 'r': + *wp++ = 0x0d; // carriage return + rp++; + continue; + case 't': + *wp++ = 0x09; // horizontal tab + rp++; + continue; + case 'v': + *wp++ = 0x0b; // vertical tab + rp++; + continue; + case 'x': // hex: \xnn + rp++; // eat "x" + // in contrast to the standard, max 2 hex digits are evaualted, not arbitrary amount. + // this makes it easy to write "L 200" as "L\x20200". + // Else \xnnnn may eat following chars not meant as part of the hex sequence + // convert and skip arbitrary count of hex characters + n = strspn(rp, "0123456789aAbBcCdDeEfF"); + if (n < 1) + return false ; // no hexdigit after "x" + // use max 2 digits for hex literal + c = digitval(toupper(*rp++)) ; + if (n >= 2) + c = c * 16 + digitval(toupper(*rp++)) ; + // c = strtol(rp, &rp, 16) ; if unlimited hex chars + *wp++ = (char) c; + continue; + default: + return false; // unknown char behind backslash + } + } + *wp = 0; + return true; +} diff --git a/10.01_base/2_src/arm/utils.hpp b/10.01_base/2_src/arm/utils.hpp index f11bc58..fc08390 100644 --- a/10.01_base/2_src/arm/utils.hpp +++ b/10.01_base/2_src/arm/utils.hpp @@ -1,29 +1,29 @@ /* utils.hpp: misc. utilities - 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 - 20-may-2018 JH created -*/ + 12-nov-2018 JH entered beta phase + 20-may-2018 JH created + */ #ifndef _UTILS_HPP_ #define _UTILS_HPP_ @@ -40,13 +40,14 @@ #define MILLION 1000000L #define BILLION (1000L * MILLION) +#define BIT(n) (1 << (n)) + #ifndef _UTILS_CPP_ extern volatile int SIGINTreceived; #endif - // my version -void strcpy_s(char *dest, int len, const char *src) ; +void strcpy_s(char *dest, int len, const char *src); // mark unused parameters #define UNUSED(x) (void)(x) @@ -56,23 +57,28 @@ void strcpy_s(char *dest, int len, const char *src) ; void SIGINTcatchnext(); // dummy to have an executable line for break points -void break_here(void) ; +void break_here(void); class timeout_c: public logsource_c { private: struct timespec starttime; uint64_t duration_ns; public: - timeout_c() ; - void start(uint64_t duration_ns); - uint64_t elapsed_ns(void);bool reached(void); + timeout_c(); + uint64_t get_resolution_ns(void) ; + void start_ns(uint64_t duration_ns); + void start_us(uint64_t duration_us) ; + void start_ms(uint64_t duration_ms) ; + uint64_t elapsed_ns(void); + uint64_t elapsed_us(void); + uint64_t elapsed_ms(void); + bool reached(void); void wait_ns(uint64_t duration_ns); void wait_us(unsigned duration_us); void wait_ms(unsigned duration_ms); }; - class progress_c { private: unsigned linewidth; @@ -81,27 +87,32 @@ public: progress_c(unsigned linewidth); void init(unsigned linewidth); void put(const char *info); + void putf(const char *fmt, ...); + }; -unsigned random24(); +unsigned random24(void); +uint32_t random32_log(uint32_t limit); -char *cur_time_text(void) ; +char *cur_time_text(void); // remove leading/trailing spaces // https://stackoverflow.com/questions/83439/remove-spaces-from-stdstring-in-c #define TRIM_STRING(str) str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end()) -bool fileExists(const std::string& filename) ; +bool fileExists(const std::string& filename); -char * fileErrorText(const char *msgfmt, const char *fname) ; +char * fileErrorText(const char *msgfmt, const char *fname); //ool caseInsCompare(const std::string& s1, const std::string& s2) ; - // add a number of microseconds to a time -struct timespec timespec_add_us(struct timespec ts, unsigned us) ; +struct timespec timespec_add_us(struct timespec ts, unsigned us); // add microseconds to current time -struct timespec timespec_future_us(unsigned offset_us) ; +struct timespec timespec_future_us(unsigned offset_us); + +// decodes C escape sequences \char, \nnn octal, \xnn hex +bool str_decode_escapes(char *result, unsigned result_size, char *encoded) ; #endif /* _UTILS_H_ */ diff --git a/10.01_base/2_src/pru1/Makefile b/10.01_base/2_src/pru1/Makefile index 5963304..c4490f4 100644 --- a/10.01_base/2_src/pru1/Makefile +++ b/10.01_base/2_src/pru1/Makefile @@ -46,7 +46,8 @@ CFLAGS_OPTIMIZER=--opt_level=3 --opt_for_speed=5 --auto_inline --c_src_interli #CFLAGS_OPTIMIZER=--opt_level=3 --auto_inline --c_src_interlist --optimizer_interlist --gen_opt_info=2 #Common compiler and linker flags (Defined in 'PRU Optimizing C/C++ Compiler User's Guide) CFLAGS=-v3 $(CFLAGS_OPTIMIZER) \ - --display_error_number --endian=little --hardware_mac=on --obj_directory=$(OBJ_DIR) --pp_directory=$(OBJ_DIR) -ppd -ppa + --display_error_number --emit_warnings_as_errors --verbose_diagnostics \ + --endian=little --hardware_mac=on --obj_directory=$(OBJ_DIR) --pp_directory=$(OBJ_DIR) -ppd -ppa #Linker flags (Defined in 'PRU Optimizing C/C++ Compiler User's Guide) LFLAGS=--reread_libs --warn_sections --stack_size=$(STACK_SIZE) --heap_size=$(HEAP_SIZE) @@ -74,6 +75,7 @@ OBJECTS_COMMON= \ $(OBJ_DIR)/pru1_statemachine_intr.object \ $(OBJ_DIR)/pru1_statemachine_powercycle.object \ $(OBJ_DIR)/pru1_statemachine_slave.object \ + $(OBJ_DIR)/pru1_timeouts.object \ $(OBJ_DIR)/pru1_utils.object diff --git a/10.01_base/2_src/pru1/pru1_arm_mailbox.c b/10.01_base/2_src/pru1/pru1_arm_mailbox.c index 922386a..1224dec 100644 --- a/10.01_base/2_src/pru1/pru1_arm_mailbox.c +++ b/10.01_base/2_src/pru1/pru1_arm_mailbox.c @@ -1,38 +1,37 @@ /* pru1_arm_mailbox.c: datastructures common to ARM and PRU - 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 #define _MAILBOX_C_ #include "mailbox.h" - // Place PRU-ARM mailbox struct at begin of PRU_SHAREDMEM // (shared 12KB memory) #pragma DATA_SECTION(mailbox,".mailbox_arm_sec") -volatile far mailbox_t mailbox ; +volatile far mailbox_t mailbox; diff --git a/10.01_base/2_src/pru1/pru1_buslatches.c b/10.01_base/2_src/pru1/pru1_buslatches.c index 7250c74..d1f5eef 100644 --- a/10.01_base/2_src/pru1/pru1_buslatches.c +++ b/10.01_base/2_src/pru1/pru1_buslatches.c @@ -352,8 +352,6 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { // spruh73n, chapter 4.4.1.2.3.2, CT_CFG.GPCFG1_bit.PRU1_GPI_MODE = 0; - DEBUG_PIN_SET(0); // clear, no error - #ifdef TEST_66MHZ while (1) { __R30 |= (1 << 12); // set PRU1.12 @@ -396,16 +394,16 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { uint8_t resvar; // echo DATA0 read only buslatches_test_get(2,resvar); - DEBUG_PIN_SET(buslatches_getbyte(2) != a); + PRU_DEBUG_PIN(buslatches_getbyte(2) != a); // buslatches_debug_set(resvar & 1); buslatches_test_get(3,resvar); - DEBUG_PIN_SET(buslatches_getbyte(3) != b); + PRU_DEBUG_PIN(buslatches_getbyte(3) != b); //buslatches_debug_set(resvar & 1); buslatches_test_get(5,resvar); - DEBUG_PIN_SET(buslatches_getbyte(5) != c); + PRU_DEBUG_PIN(buslatches_getbyte(5) != c); //buslatches_debug_set(resvar & 1); buslatches_test_get(6,resvar); - DEBUG_PIN_SET(buslatches_getbyte(6) != d); + PRU_DEBUG_PIN(buslatches_getbyte(6) != d); //buslatches_debug_set(resvar & 1); } #endif @@ -423,16 +421,16 @@ void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d) { buslatches_setbyte(5, c) ; if (buslatches_getbyte(2) != a) - DEBUG_PIN_PULSE_100NS + PRU_DEBUG_PIN_PULSE_100NS ;// show error flag. cleared by next reg_sel buslatches_setbyte(6, d) ; if (buslatches_getbyte(3) != b) - DEBUG_PIN_PULSE_100NS; + PRU_DEBUG_PIN_PULSE_100NS; if (buslatches_getbyte(5) != c) - DEBUG_PIN_PULSE_100NS; + PRU_DEBUG_PIN_PULSE_100NS; if (buslatches_getbyte(6) != d) - DEBUG_PIN_PULSE_100NS; + PRU_DEBUG_PIN_PULSE_100NS; a++; b++; c++; diff --git a/10.01_base/2_src/pru1/pru1_buslatches.h b/10.01_base/2_src/pru1/pru1_buslatches.h index 793bc14..2a2d8b7 100644 --- a/10.01_base/2_src/pru1/pru1_buslatches.h +++ b/10.01_base/2_src/pru1/pru1_buslatches.h @@ -69,7 +69,6 @@ extern buslatches_t buslatches; (__R31 & 0xff) \ ) - // identify register which must be set byte-wise #define BUSLATCHES_REG_IS_BYTE(reg_sel) ( \ ((reg_sel) == 2) || ((reg_sel) == 3) || ((reg_sel) == 5) || ((reg_sel) == 6) \ @@ -133,7 +132,7 @@ void buslatches_reset(void); void buslatches_powercycle(void); -void buslatches_exerciser(void) ; +void buslatches_exerciser(void); void buslatches_test(uint8_t a, uint8_t b, uint8_t c, uint8_t d); diff --git a/10.01_base/2_src/pru1/pru1_buslatches_overlapping.txt b/10.01_base/2_src/pru1/pru1_buslatches_overlapping.txt deleted file mode 100644 index 2005a2c..0000000 --- a/10.01_base/2_src/pru1/pru1_buslatches_overlapping.txt +++ /dev/null @@ -1,52 +0,0 @@ -Speed up busaltch access with overlapping access -to 3:8 mux -On read: -macro buslatches_get(cycles_since_regmux, reg_sel, reg_sel_next, result_var, reg_sel_current_var) - -R1 - check if 74138 regmux is already set as expected (vvariable "reg_sel_current") -if yes: - R21 wait until required delay to last mux switch is reached - Cycle time since last access to regmux_sel in "cycles_since_regmux") - (read_ 10 cycles, 6 alone for BBB signal processing) -if no: - R31 - set mux - R32 wait full delay (10 cycles) -R22- read in muxed data from lvt541 - result_var := R31 -R4- set next mux expected value "reg_sel_next" -R5 update reg_sel_current - - -On write: macro buslatches_set(cycles_since_regmux, reg_sel, reg_sel_next, val, reg_sel_current_variable) -W1 - setup data do PRU0 outputs -W2 - set regmux, update reg_sel_current -Dont check if 74138 regmux is already set as exepected, -Setting of output data on PRU0 uses moretime than set up uf regmux and -progation of selct singals to 74371 latches (only 10ns!) - - - -Code path: -- check needed, if reg_mux is as set by previous busaltch_get/set() -- checks static, may be elimianted by compiler. -- if reg_sel_next is a local var, it may be eliminated totally -- if reg_sel_next is a global var, expected "next" value in different state machnies - may be read from logic analyzer trace of "typical" operation. - - a local variable "cycles_since_last_regmux" may be used to - count processing time. It is only set with const vlaues, - so may be elimated by compiler and may be used in delay_cycles() - - -uint8_t reg_sel_current ; // local var - reg_sel_current = 0xff - -a0_7 = buslatches_get(0, 4, 5, reg_sel_current) ; -// => if (4 != 0xff) -> code R31,R32, wait 10 cycles - - -a8_15 = buslatches_get(4, 5, 2, reg_sel_current) ; -if (5 != 5) => mux already correct, - R21: delaycycles(10-4), // estimated 4 cycles processing time - - - diff --git a/10.01_base/2_src/pru1/pru1_ddrmem.c b/10.01_base/2_src/pru1/pru1_ddrmem.c index 9ffcad3..922ec3c 100644 --- a/10.01_base/2_src/pru1/pru1_ddrmem.c +++ b/10.01_base/2_src/pru1/pru1_ddrmem.c @@ -1,28 +1,28 @@ /* pru1_ddrmem.c: Control the shared DDR RAM - used for UNIBUS memory - 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 #include @@ -33,8 +33,8 @@ // fill whole memory with pattern // called by mailbox command ARM2PRU_REQUEST_DDR_FILL_PATTERN void ddrmem_fill_pattern(void) { - unsigned n ; + unsigned n; volatile uint16_t *wordaddr = mailbox.ddrmem_base_physical->memory.words; - for (n = 0; n < UNIBUS_WORDCOUNT ; n++) - *wordaddr++ = n ; + for (n = 0; n < UNIBUS_WORDCOUNT; n++) + *wordaddr++ = n; } diff --git a/10.01_base/2_src/pru1/pru1_iopageregisters.c b/10.01_base/2_src/pru1/pru1_iopageregisters.c index e53e3d2..9df2970 100644 --- a/10.01_base/2_src/pru1/pru1_iopageregisters.c +++ b/10.01_base/2_src/pru1/pru1_iopageregisters.c @@ -1,28 +1,28 @@ /* pru1_iopageregisters.c: handle UNIBUS behaviour of emulated 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 + */ #define _IOPAGEREGISTERS_C_ @@ -30,7 +30,7 @@ #include #include "pru1_utils.h" -#include "pru1_buslatches.h" // DEBUG_PIN_SET +#include "pru1_buslatches.h" // PRU_DEBUG_PIN #include "mailbox.h" #include "iopageregister.h" #include "ddrmem.h" @@ -42,7 +42,6 @@ // not volatile: data seldom changed by ARM, speed matters! iopageregisters_t deviceregisters; - /* request value from a device register * page_table_entry already calculated for addr * may have side effects onto other registers! @@ -52,26 +51,27 @@ iopageregisters_t deviceregisters; * for post processing. SSYN must remain asserted until ARM is complete */ uint8_t iopageregisters_read(uint32_t addr, uint16_t *val) { - uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr); + uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr); if (page_table_entry == PAGE_MEMORY) { // addr in allowed 18bit memory range, not in I/O page *val = DDRMEM_MEMGET_W(addr); return 1; } else if (page_table_entry == PAGE_IO) { // uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)]; - uint8_t reghandle ; - reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ; + uint8_t reghandle; + reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr); if (!reghandle) { return 0; // register not implemented as "active" - } + } // return register value. remove "volatile" attribute -// DEBUG_PIN_SET(1) ; - // indexing this records takes 4,6 us, if record size != 8 + // indexing this records takes 4,6 us, if record size != 8 iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias -// DEBUG_PIN_SET(0) ; *val = reg->value; if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATI) DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATI, addr, *val); + // ARM is clearing this, while SSYN asserted, so no concurrent next bus cycle. + // no concurrent ARP+PRU access + return 1; } else return 0; @@ -83,7 +83,7 @@ uint8_t iopageregisters_read(uint32_t addr, uint16_t *val) { * may set mailbox event to ARM, then SSYN must remain asserted until ARM is complete */ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) { - uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr); + uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr); if (page_table_entry == PAGE_MEMORY) { // addr in allowed 18bit memory range, not in I/O page // no check wether addr is even (A00=0) @@ -92,13 +92,13 @@ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) { return 1; } else if (page_table_entry == PAGE_IO) { // uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)]; - uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ; + uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr); if (!reghandle) return 0; // register not implemented // change register value iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias uint16_t reg_val = (reg->value & ~reg->writable_bits) | (w & reg->writable_bits); - reg->value = reg_val ; + reg->value = reg_val; if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATO) DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATO, addr, reg_val); return 1; @@ -107,29 +107,29 @@ uint8_t iopageregisters_write_w(uint32_t addr, uint16_t w) { } uint8_t iopageregisters_write_b(uint32_t addr, uint8_t b) { - uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters,addr); + uint8_t page_table_entry = PAGE_TABLE_ENTRY(deviceregisters, addr); if (page_table_entry == PAGE_MEMORY) { // addr in allowed 18bit memory range, not in I/O page DDRMEM_MEMSET_B(addr, b); return 1; } else if (page_table_entry == PAGE_IO) { // uint8_t reghandle = deviceregisters.iopage_register_handles[ADDR2IOPAGEWORD(addr)]; - uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters,addr) ; + uint8_t reghandle = IOPAGE_REGISTER_ENTRY(deviceregisters, addr); if (!reghandle) return 0; // register not implemented // change register value iopageregister_t *reg = (iopageregister_t *) &(deviceregisters.registers[reghandle]); // alias - uint16_t reg_val ; + uint16_t reg_val; if (addr & 1) // odd address = write upper byte reg_val = (reg->value & 0x00ff) // don't touch lower byte | (reg->value & ~reg->writable_bits & 0xff00) // protected upper byte bits - | (((uint16_t)b << 8) & reg->writable_bits); // changed upper byte bits + | (((uint16_t) b << 8) & reg->writable_bits); // changed upper byte bits else // even address: write lower byte reg_val = (reg->value & 0xff00) // don' touch upper byte | (reg->value & ~reg->writable_bits & 0x00ff) // protected upper byte bits | (b & reg->writable_bits); // changed lower byte bits - reg->value = reg_val ; + reg->value = reg_val; if (reg->event_flags & IOPAGEREGISTER_EVENT_FLAG_DATO) DO_EVENT_DEVICEREGISTER(reg, UNIBUS_CONTROL_DATOB, addr, reg_val); return 1; diff --git a/10.01_base/2_src/pru1/pru1_main_test.c b/10.01_base/2_src/pru1/pru1_main_test.c index d3ae8cd..a698e93 100644 --- a/10.01_base/2_src/pru1/pru1_main_test.c +++ b/10.01_base/2_src/pru1/pru1_main_test.c @@ -38,10 +38,12 @@ #include #include +#include #include #include "resource_table_empty.h" #include "pru1_utils.h" +#include "pru1_timeouts.h" #include "pru_pru_mailbox.h" #include "mailbox.h" @@ -56,22 +58,26 @@ #include "pru1_statemachine_init.h" #include "pru1_statemachine_powercycle.h" +// Supress warnings about using void * as function pointers +// sm_slave_state = (statemachine_state_func)&sm_slave_start; +// while (sm_slave_state = sm_slave_state()) << usage +#pragma diag_push +#pragma diag_remark=515 void main(void) { /* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */ CT_CFG.SYSCFG_bit.STANDBY_INIT = 0; + timeout_init(); + // clear all tables, as backup if ARM fails todo iopageregisters_init(); buslatches_reset(); // all deasserted // init mailbox - mailbox.arm2pru_req = ARM2PRU_NONE; - mailbox.events.eventmask = 0; - mailbox.events.initialization_signals_prev = 0; - mailbox.events.initialization_signals_cur = 0; + memset((void *) &mailbox, 0, sizeof(mailbox)); while (1) { // display opcode (active for one cycle @@ -99,7 +105,7 @@ void main(void) { __halt(); // that's it break; #ifdef USED - case ARM2PRU_MAILBOXTEST1: + case ARM2PRU_MAILBOXTEST1: // simulate a register read access. #ifdef TEST_TIMEOUT while (1) { @@ -120,8 +126,8 @@ void main(void) { // pru_pru_mailbox.pru0_r30 = mailbox.mailbox_test.addr & 0xff; // __R30 = (mailbox.mailbox_test.addr & 0xf) << 8; mailbox.mailbox_test.val = mailbox.mailbox_test.addr; - __R30 = (mailbox.arm2pru_req & 0xf) << 8; // optical ACK - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + __R30 = (mailbox.arm2pru_req & 0xf) << 8;// optical ACK + mailbox.arm2pru_req = ARM2PRU_NONE;// ACK: done break; #endif case ARM2PRU_BUSLATCH_INIT: // set all mux registers to "neutral" @@ -151,10 +157,10 @@ void main(void) { mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; } - case ARM2PRU_BUSLATCH_EXERCISER: // exercise 8 byte accesses to mux registers - buslatches_exerciser() ; - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break ; + case ARM2PRU_BUSLATCH_EXERCISER: // exercise 8 byte accesses to mux registers + buslatches_exerciser(); + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; case ARM2PRU_BUSLATCH_TEST: { buslatches_test(mailbox.buslatch_test.addr_0_7, mailbox.buslatch_test.addr_8_15, @@ -163,24 +169,26 @@ void main(void) { break; } case ARM2PRU_INITPULSE: // generate a pulse on UNIBUS INIT - // INIT: latch[7], bit 3 + // INIT: latch[7], bit 3 buslatches_setbits(7, BIT(3), BIT(3)); // assert INIT __delay_cycles(MILLISECS(250)); // INIT is 250ms buslatches_setbits(7, BIT(3), 0); // deassert INIT - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; case ARM2PRU_POWERCYCLE: // do ACLO/DCLO power cycle buslatches_powercycle(); mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; - case ARM2PRU_DMA_ARB_NONE: - sm_dma_start(); // without NPR/NPG arbitration + case ARM2PRU_DMA: { + // without NPR/NPG arbitration + statemachine_state_func sm_dma_state = (statemachine_state_func) &sm_dma_start; // simply call current state function, until stopped // parallel the BUS-slave statemachine is triggered // by master logic. - while (!sm_dma.state()) + while (sm_dma_state = sm_dma_state()) ; + } mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done break; case ARM2PRU_DDR_FILL_PATTERN: @@ -197,9 +205,10 @@ void main(void) { // do UNIBUS slave cycles, until ARM abort this by // writing into mailbox.arm2pru_req while (mailbox.arm2pru_req == ARM2PRU_DDR_SLAVE_MEMORY) { - sm_slave_start(); + statemachine_state_func sm_slave_state = + (statemachine_state_func) &sm_slave_start; // do all states of an access, start when MSYN found. - while (!sm_slave.state()) + while (sm_slave_state = sm_slave_state()) ; } mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done @@ -207,4 +216,5 @@ void main(void) { } // switch } // while } +#pragma diag_pop diff --git a/10.01_base/2_src/pru1/pru1_main_unibus.c b/10.01_base/2_src/pru1/pru1_main_unibus.c index 1e486f8..5ec1305 100644 --- a/10.01_base/2_src/pru1/pru1_main_unibus.c +++ b/10.01_base/2_src/pru1/pru1_main_unibus.c @@ -48,6 +48,7 @@ #include "resource_table_empty.h" #include "pru1_utils.h" +#include "pru1_timeouts.h" #include "pru_pru_mailbox.h" #include "mailbox.h" @@ -62,148 +63,198 @@ #include "pru1_statemachine_init.h" #include "pru1_statemachine_powercycle.h" +// supress warnigns about using void * as function pointers +// sm_slave_state = (statemachine_state_func)&sm_slave_start; +// while (sm_slave_state = sm_slave_state()) << usage +#pragma diag_push +#pragma diag_remark=515 + +/*** + 3 major states executed in circular 1- 2- 3 order. + + 1. "SLAVE": + UniBone monitoring BUS traffic as slave for DATI/DATO cycles + Watch UNIBUS for CPU access to emulated memory/devices + High speed not necessary: Bus master will wait with MSYN if UniBone not responding. + wathcin BG/BPG signals, catching requested GRANts and forwardinf + other GRANTS + - monitorinf INIT and AC_LO/DC_LO + - watching fpr AMR2PRU commands + 2. "BBSYWAIT": UNibone got PRIORITY GRAMT, has set SACK and released BR/NPR + waits for current BUS master to relaeasy BBSY (ony DATI/DATO cycle max) + - SACK active: no GRANT forward necessary, no arbitration necessary + - INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary + 3. "MASTER": UniBone is Bus master: transfering INTR vector or doing DMA + - Own timing: transfer DMA data block or INTR vector with master cycles + - SACK active: no GRANT forward necessary, no arbitration necessary + - INIT is monitored by DMA statemachine: no DC_LO/INIT monitoring necessary + */ + void main(void) { + // state function pointer for different state machines + statemachine_arb_worker_func sm_arb_worker = &sm_arb_worker_client; + statemachine_state_func sm_data_slave_state = NULL; + statemachine_state_func sm_data_master_state = NULL; + statemachine_state_func sm_init_state = NULL; + statemachine_state_func sm_powercycle_state = NULL; + // these are function pointers: could be 16bit on PRU? /* Clear SYSCFG[STANDBY_INIT] to enable OCP master port */ CT_CFG.SYSCFG_bit.STANDBY_INIT = 0; + timeout_init(); + // clear all tables, as backup if ARM fails todo iopageregisters_init(); buslatches_reset(); // all deasserted, caches cleared /* ARM must init mailbox, especially: - mailbox.arm2pru_req = ARM2PRU_NONE; - mailbox.events.eventmask = 0; - mailbox.events.initialization_signals_prev = 0; - mailbox.events.initialization_signals_cur = 0; - */ - - /* start parallel emulation of all devices, - * Process __DMA and _INTR bus master operations - * - * ! Several state machines (DMA, Powercycle, INIT,) use the same global timeout. - * ! Never execute these in parallel ! + mailbox.arm2pru_req = ARM2PRU_NONE; + mailbox.events.eventmask = 0; + mailbox.events.initialization_signals_prev = 0; + mailbox.events.initialization_signals_cur = 0; */ - // Reset PDP-11 with power-cycle simulation. - // Necessary, as until now NPR/NPG/BG/BR/SACK lines were "unconnected" -// buslatches_powercycle(); -// __delay_cycles(MILLISECS(100)); - // execute 2x, because M9312 boot ROMs need this - // __delay_cycles(MILLISECS(250)); - // buslatches_powercycle(); - // buslatches_pulse_debug ; - - // base operation: accept and execute slave cycles - sm_slave_start(); + sm_arb_reset(); while (true) { - uint32_t arm2pru_req_cached; - // do all states of an access, start when MSYN found. + uint8_t arm2pru_req_cached; - // slave cycles may trigger events to ARM, which changes "active" registers - // and issues interrupts - while (!sm_slave.state()) - ; // execute complete slave cycle, then check NPR/INTR + if (sm_data_master_state == NULL) { + // State 1 "SLAVE" + // fast: a complete slave data cycle + if (!sm_data_slave_state) + sm_data_slave_state = (statemachine_state_func) &sm_slave_start; + while ((sm_data_slave_state = sm_data_slave_state()) && !mailbox.events.event_deviceregister) + // throws signals to ARM, + // Acess to interna lregsitres may may issue AMR2PRU opcode, so exit loop then + ;// execute complete slave cycle, then check NPR/INTR - // update state of init lines - // INIT never asserted in the midst of a transaction, bit 3,4,5 - do_event_initializationsignals(); + // one phase of INIT or power cycle + if (sm_powercycle_state) + sm_powercycle_state = sm_powercycle_state(); + else if (sm_init_state) + // init only if no power cycle, power cycle overrides INIT + sm_init_state = sm_init_state(); - // standard operation may be interrupt by other requests - arm2pru_req_cached = mailbox.arm2pru_req; - switch (arm2pru_req_cached) { - case ARM2PRU_NONE: - // pass BG[4-7] to next device, state machine "idle" - // pass all Arbitration GRANT IN to GRANT OUT for next device. - // This is not necessary while INTR or DMA is actiove: + // signal INT or PWR FAIL to ARM + do_event_initializationsignals(); + + // Priority Arbitration + // execute one of the arbitration workers + uint8_t grant_mask = sm_arb_worker(); + // sm_arb_worker()s include State 2 "BBSYWAIT". + // So now SACK maybe set, even if grant_mask is still 0 + + if (grant_mask & PRIORITY_ARBITRATION_BIT_NP) { + sm_data_master_state = (statemachine_state_func) &sm_dma_start; + // can data_master_state be overwritten in the midst of a running data_master_state ? + // no: when running, SACK is set, no new GRANTs + } else if (grant_mask & PRIORITY_ARBITRATION_INTR_MASK) { + // convert bit in grant_mask to INTR index + uint8_t idx = PRIORITY_ARBITRATION_INTR_BIT2IDX(grant_mask); + // now transfer INTR vector for interupt of GRANted level. + // vector and ARM context have been setup by ARM before ARM2PRU_INTR already + sm_intr.vector = mailbox.intr.vector[idx]; + sm_intr.level_index = idx; // to be returned to ARM on complete + + sm_data_master_state = (statemachine_state_func) &sm_intr_start; + } + } else { + // State 3 "MASTER" + // we have been GRANTed bus mastership and are doing DMA or INTR + // SACK held here -> no further arbitration // INTR is only 1 cycle, DMA has SACK set all the time, arbitration - // prohibited then. - sm_arb_state_idle(); - // do only forward GRANT lines if not INTR is pending, - // else our GRANT would be passed too. - break; // fast case: only slave operation - case ARM2PRU_NOP: // needed to probe PRU run state - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break; - case ARM2PRU_DMA_ARB_NONE: // ignore SACK condition - case ARM2PRU_DMA_ARB_MASTER: // also without arbitration, TODO! - sm_dma_start(); - while (!sm_dma.state()) - ; - // a dma cycle into a device register may trigger an interrupt - // do not delete that condition - if (mailbox.arm2pru_req == arm2pru_req_cached) - mailbox.arm2pru_req = ARM2PRU_NONE; // clear request - break; - case ARM2PRU_DMA_ARB_CLIENT: - // start DMA cycle - // can not run parallel with INTR levels - sm_arb_start(ARBITRATION_PRIORITY_BIT_NP); - while (!sm_arb.state()) { - // sm_slave is most time critical, as it must keep track with MSYN/SSYN bus traffic. - // so give it more cpu cycles - while (!sm_slave.state()) - ; + // prohibited then. + + sm_data_master_state = sm_data_master_state(); // execute only ONE state , + // else DMA blocks will block prcoessing of other state machines + // throws signals to ARM, causes may issue mailbox.arm2pru_req + } + + // process ARM commands in master and slave mode + // standard operation may be interrupt by other requests + if (arm2pru_req_cached = mailbox.arm2pru_req) { + // not ARM2PRU_NONE + switch (arm2pru_req_cached) { + case ARM2PRU_NOP: // needed to probe PRU run state + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_ARB_MODE_NONE: // ignore SACK condition + // from now on, ignore INTR requests and allow DMA request immediately + sm_arb_worker = &sm_arb_worker_none; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_ARB_MODE_CLIENT: + // request DMA from external Arbitrator + sm_arb_worker = &sm_arb_worker_client; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_ARB_MODE_MASTER: + sm_arb_worker = &sm_arb_worker_master; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_DMA: + // request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_* + sm_arb.request_mask |= PRIORITY_ARBITRATION_BIT_NP; + // sm_arb_worker() evaluates this,extern Arbitrator raises Grant, excution starts in future loop + // end of DMA is signaled to ARM with signal + + /* TODO: speed up DMA + While DMA is active: + - SACK active: no GRANT forward necessary + no arbitration necessary + - INIT is monitored: no DC_LO/INIT monitoring necessary + - no scan for new ARM2PRU commands: ARM2PRU_DMA is blocking + - smaller chunks ? + */ + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_INTR: + // request INTR, arbitrator must've been selected with ARM2PRU_ARB_MODE_* + // start one INTR cycle. May be raised in midst of slave cycle + // by ARM, if access to "active" register triggers INTR. + sm_arb.request_mask |= mailbox.intr.priority_arbitration_bit; + // sm_arb_worker() evaluates this, extern Arbitrator raises Grant, + // vector of GRANted level is transfered with statemachine sm_intr + + // Atomically change state in a device's associates interrupt register. + // The Interupt Register is set immediately. No wait for INTR GRANT, + // INTR level may be blocked. + if (mailbox.intr.iopage_register_handle) + deviceregisters.registers[mailbox.intr.iopage_register_handle].value = + mailbox.intr.iopage_register_value; + mailbox.arm2pru_req = ARM2PRU_NONE; // done + // end of INTR is signaled to ARM with signal + break; + case ARM2PRU_INTR_CANCEL: + // cancels an INTR request. If already Granted, the GRANT is forwarded, + // and canceled by reaching a "SACK turnaround terminator" or "No SACK TIMEOUT" in the arbitrator. + sm_arb.request_mask &= ~ mailbox.intr.priority_arbitration_bit ; + // no completion event, could interfer with othe INTRs? + mailbox.arm2pru_req = ARM2PRU_NONE; // done + break ; + case ARM2PRU_INITPULSE: + if (!sm_init_state) + sm_init_state = (statemachine_state_func) &sm_init_start; + // INIT aborts DMA + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_POWERCYCLE: + sm_powercycle_state = (statemachine_state_func) &sm_powercycle_start; + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + break; + case ARM2PRU_HALT: + mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done + __halt() ; // LA: trigger on timeout of REG_WRITE + break ; } - // now SACK held and BBSY set, slave state machine ended, since BBSY found inactive - - // debug pin reset by bus access - //DEBUG_PIN_SET(1) ; - sm_dma_start(); - //DEBUG_PIN_SET(1) ; - while (!sm_dma.state()) - //DEBUG_PIN_SET(1) ; - ;// execute dma master cycles - // a dma cycle into a device register may trigger an interrupt - // do not delete that condition - if (mailbox.arm2pru_req == arm2pru_req_cached) - mailbox.arm2pru_req = ARM2PRU_NONE; // clear request - break; - case ARM2PRU_INTR: - // start one INTR cycle. May be raised in midst of slave cycle - // by ARM, if access to "active" register triggers INTR. - // no multiple levels simultaneously allowed, not parallel with DMA ! - sm_arb_start(mailbox.intr.priority_bit); - // wait while INTR is accepted. This may take long time, - // if system is at high processor priority (PSW register) - while (!sm_arb.state()) { - // sm_slave is most time critical, as it must keep track with MSYN/SSYN bus traffic. - // so give it more cpu cycles - while (!sm_slave.state()) - ; - } - // now SACK held and BBSY set, slave state machine ended, since BBSY found inactive - sm_intr_start(); - while (!sm_intr.state()) - ; // execute intr cycle as bus master - mailbox.arm2pru_req = ARM2PRU_NONE; // clear request - break; - case ARM2PRU_INITPULSE: // generate a pulse on UNIBUS INIT - // only busmaster may assert INIT. violated here! - sm_slave_start(); - sm_init_start(); - while (!sm_slave.state() || !sm_init.state()) - ; - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break; - case ARM2PRU_POWERCYCLE: // do ACLO/DCLO power cycle - // Runs for 4* POWERCYCLE_DELAY_MS millsecs, approx 1 sec. - // perform slave states in parallel, so emulated memory - // is existent for power fail trap and reboot - sm_slave_start(); - sm_powercycle_start(); - while (!sm_slave.state() || !sm_powercycle.state()) - ; - mailbox.arm2pru_req = ARM2PRU_NONE; // ACK: done - break; - - default: // ignore all other requestes while executing emulation - ; - } // switch - } // while (true) + } + } // never reached } +#pragma diag_pop + diff --git a/10.01_base/2_src/pru1/pru1_pru_mailbox.c b/10.01_base/2_src/pru1/pru1_pru_mailbox.c index ac6bae6..fd5d8e6 100644 --- a/10.01_base/2_src/pru1/pru1_pru_mailbox.c +++ b/10.01_base/2_src/pru1/pru1_pru_mailbox.c @@ -1,43 +1,42 @@ /* pru1_pru_mailbox.c: datastructures common to PRU0 and PRU1 - 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 - datastructures common to PRU0 and PRU1 - Compiled in both PR0 and PRU1 code - To be included into both PRU0 and PRU1 projects. - Section ".pru_pru_mailbox_sec" to be placed at the end of PRU0 internal RAM. - pos in PRU0 space: 0x1f00 - pos in PRU1 space: 0x3f00 - See linker control file! + datastructures common to PRU0 and PRU1 + Compiled in both PR0 and PRU1 code + To be included into both PRU0 and PRU1 projects. + Section ".pru_pru_mailbox_sec" to be placed at the end of PRU0 internal RAM. + pos in PRU0 space: 0x1f00 + pos in PRU1 space: 0x3f00 + See linker control file! */ #include #include "pru_pru_mailbox.h" #define _PRU_PRU_MAILBOX_C_ - // everything here is to be placed in the same section #pragma DATA_SECTION(pru_pru_mailbox,".pru_pru_mailbox_sec") -volatile pru_pru_mailbox_t pru_pru_mailbox ; +volatile pru_pru_mailbox_t pru_pru_mailbox; diff --git a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c index 2c73439..deba29a 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.c @@ -1,51 +1,52 @@ /* pru1_statemachine_arbitration.c: state machine for INTR/DMA arbitration - 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 - Statemachine for execution of the Priority Arbitration protocol - NPR arbitration and BR interrupt arbitration + Statemachine for execution of the Priority Arbitration protocol + NPR arbitration and BR interrupt arbitration - UniBone is neither interrupt fielding processor nor abitrator. - It is a plain bus slave here. + PRU handles all 5 requests in parallel: + 4x INTR BR4-BR7 + 1x DMA NPR. + Several ARM device may raise the same BR|NPR level, ARM must serialize this to PRU. - This state machine can handle ONLY ONE request at a time. - ARM code threads must wait until a request is completed by PRU. - Problem: Multiple devices may trigger DMA and Interrupts independently! - Solution: these events must be serialized by an "priority arbitration scheduler" , - May be implemented by a event queue for each priority level, - containing refereneces to all devices requesting arbitration. + Flow: + 1. ARM sets a REQUEST by + filling the RQUEST struct and perhaps DMA data + doing AMR2PRO_PRIORITY_ARBITRATION_REQUEST, + 2. PRU sets BR4567|NPR lines according to open requests + 3. PRU monitors IN GRANT lines BG4567,NPG. + IN state of idle requests is forwarded to BG|NPG OUT liens, + to be processed by other UNIBUS cards. + BG*|NPG IN state line of active request cleares BR*|NPR line, + sets SACK, and starts INTR or DMA state machine. + 4. INTR or DMA sent a signal on compelte to PRU. + PRU may then start next request on same (completed) BR*|NPR level. - Or Todo: handle all 5 request/grants in parallel. - - 1. Start a request: set bit in _start() - 2. PRU executes statemachine - 3. on complete BBSY is acquires, UniBone is bus master - - - All references "PDP11BUS handbook 1979" - - At any time, CPU receives NPR it asserts NPG - - between CPU instructions: + All references "PDP11BUS handbook 1979" + - At any time, CPU receives NPR it asserts NPG + - between CPU instructions: if PRI < n and BRn is received, assert BGn else if PRI < 7 and BR7 is reived, assert BG7 else if PRI < 6 and BR6 is reived, assert BG6 @@ -53,7 +54,7 @@ else if PRI < 4 and BR4 is reived, assert BG4 - if PRU detectes a BGINn which it not requested, it passes it to BGOUTn + If PRU detectes a BGINn which it not requested, it passes it to BGOUTn "passing the grant" if PRU detects BGIN which was requests, it "blocks the GRANT" )sets SACK and transmit the INT (BG*) or becomes @@ -67,6 +68,14 @@ BBSY is set before SACK is released. SACK is relased imemdiatley after BBSY, enabling next arbitration in parallel to curretn data transfer "Only the device with helds SACk asserted can assert BBSY + + + Several arbitration "workers" which set request, monitor or generate GRANT signals + and allocate SACK. + Which worker to use depends on wether a physical PDP-11 CPU is Arbitrator, + the Arbitrator is implmented here (CPU emulation), + or DMA should be possible always + (even if some other CPU monitr is holding SACK (11/34). */ #define _PRU1_STATEMACHINE_ARBITRATION_C_ @@ -78,150 +87,116 @@ #include "mailbox.h" #include "pru1_buslatches.h" -#include "pru1_statemachine_arbitration.h" +#include "pru1_statemachine_arbitration.h" statemachine_arbitration_t sm_arb; -// forwards ; -static uint8_t sm_arb_state_1(void); -static uint8_t sm_arb_state_2(void); -static uint8_t sm_arb_state_3(void); -static uint8_t sm_arb_state_4(void); - /********** NPR/NPG/SACK arbitrations **************/ -void sm_arb_start(uint8_t priority_bit) { - sm_arb.priority_bit = priority_bit; // single priority bit for this arbitration process - sm_arb.state = &sm_arb_state_1; + +// to be called on INIT signal: abort the abritration rpcoess +void sm_arb_reset() { + // cleanup: clear all REQUESTS and SACK + buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK | BIT(5), 0); + sm_arb.request_mask = 0; + sm_arb.bbsy_wait_grant_mask = 0; } -// idle. call _start() -// execute in parallel with slave! -// pass BGIN[4-7],NPGIN to next device , if DMA engine idle -uint8_t sm_arb_state_idle() { - uint8_t tmpval; - tmpval = buslatches_getbyte(0); - // forward all 5 GRANT IN inverted to GRANT OUT - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval) - ; - return 1; -} +/* sm_arb_workers_*() + If return !=0: we have SACK on the GRANt lines return in a bit mask + see PRIORITY_ARBITRATION_BIT_* + */ -// wait for GRANT idle -// Assert REQUEST, wait for GRANT, assert SACK, wait for NPG==0, set SACK=0 , -// execute in parallel with slave! -static uint8_t sm_arb_state_1() { - uint8_t tmpval; - tmpval = buslatches_getbyte(0); - // forward all lines, until idle - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval) ; - // wait for GRANT idle, other cycle in progress? - if (tmpval & sm_arb.priority_bit) - return 0; - // no need to wait for SACK: arbitrator responds only with a GRANT IN - buslatches_setbits(1, sm_arb.priority_bit, sm_arb.priority_bit); // REQUEST = latch1 - sm_arb.state = &sm_arb_state_2; // wait for GRANT IN active - return 0; -} +/* worker_none(): + * No arbitration protocol. Make DMA possible in every bus configuration: + * Ignores active SACK and/or BBSY from other bus masters. + * For diagnostics on hung CPU, active device or console processor holding SACK. + */ +uint8_t sm_arb_worker_none() { + // Unconditionally forward GRANT IN to GRANT OUT + uint8_t grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN + buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK, ~grant_mask) ; - - -// wait for BG*,NPG or INIT -// execute in parallel with slave! -static uint8_t sm_arb_state_2() { - uint8_t tmpval; - - if (buslatches_getbyte(7) & BIT(3)) { // INIT stops transaction: latch[7], bit 3 - // cleanup: clear all REQUESTS and SACK - buslatches_setbits(1, ARBITRATION_PRIORITY_MASK| BIT(5), 0); - // Todo: signal INIT to ARM! - sm_arb.state = &sm_arb_state_idle; - return 0 ; - } - tmpval = buslatches_getbyte(0); - // forward all other BG lines - // preceding arbitration must see BG removed by master on SACK - - if (tmpval & sm_arb.priority_bit) { - // got GRANT IN. Clear GRANT OUT, don't pass to next device - tmpval &= ~sm_arb.priority_bit ; - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval); // forward all without our GRANT - // set SACK - buslatches_setbits(1, BIT(5), BIT(5)); - sm_arb.state = &sm_arb_state_3; + // ignore BR* INTR requests, only ack DMA. + if (sm_arb.request_mask & PRIORITY_ARBITRATION_BIT_NP) { + sm_arb.request_mask &= ~PRIORITY_ARBITRATION_BIT_NP; + return PRIORITY_ARBITRATION_BIT_NP; } else - buslatches_setbits(0, ARBITRATION_PRIORITY_MASK, ~tmpval); // forward all + return 0; +} + +/* worker_client: + Issue request to extern Arbitrator (PDP-11 CPU). + Watch for GRANTs on the bus signal lines, then raise SACK. + Wait for current bus master to release bus => Wait for BBSY clear. + Then return GRANTed request. + "Wait for BBSY clear" may not be part of the arbitration protocol. + But it guarantees caller may now issue an DMA or INTR. + */ +uint8_t sm_arb_worker_client() { + uint8_t grant_mask; + + // Always update UNIBUS BR/NPR lines, are ORed with requests from other devices. + buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK, sm_arb.request_mask) + ; + // read GRANT IN lines from CPU (Arbitrator). Only one at a time may be active + // Arbitrator asserts SACK is inactive + // latch[0]: BG signals are inverted, "get" is non-inverting: bit = active signal. + // "set" is inverting! + grant_mask = buslatches_getbyte(0) & PRIORITY_ARBITRATION_BIT_MASK; // read GRANT IN + // forward un-requested GRANT IN to GRANT OUT for other cards on neighbor slots + buslatches_setbits(0, PRIORITY_ARBITRATION_BIT_MASK & ~sm_arb.request_mask, ~grant_mask) + ; + + if (sm_arb.bbsy_wait_grant_mask == 0) { + // State 1: Wait For GRANT: + // process the requested grant for an open requests. + grant_mask &= sm_arb.request_mask; + if (grant_mask) { + // one of our requests was granted: + // set SACK + // AND simultaneously Clear granted requests BR*/NPR + // BIT(5): SACK mask and level + buslatches_setbits(1, (PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask) | BIT(5), + ~grant_mask | BIT(5)) + ; + /* + buslatches_setbits(1, BIT(5), BIT(5)); + // Arbitrator now clears GRANT IN + + // clear granted requests BR* / NPR on UNIBUS + // "~": disable BR for set BG + buslatches_setbits(1, PRIORITY_ARBITRATION_BIT_MASK & sm_arb.request_mask, ~grant_mask) + ; + */ + + + // clear granted requests internally + sm_arb.request_mask &= ~grant_mask; + // Arbitrator should remove GRANT now. Data section on Bus still BBSY + sm_arb.bbsy_wait_grant_mask = grant_mask; // next is State 2: wait for BBSY clear + } + return 0; // no GRANT for us, or wait for BBSY + } else { + // wait for BBSY to clear + if (buslatches_getbyte(1) & BIT(6)) + return 0; // BBSY still set + grant_mask = sm_arb.bbsy_wait_grant_mask; + sm_arb.bbsy_wait_grant_mask = 0; // Next State is 1 + + return grant_mask; // signal what request we got granted. + } + // UNIBUS DATA section is indepedent: MSYN, SSYN, BBSY may still be active. + // -> DMA and INTR statemachine must wait for BBSY. +} + +/* "worker_master" + Act as Arbitrator. + Grant highest of requests, if SACK deasserted. + Execute UNIBUS priority algorithm: + - Grant DMA request, if present + - GRANT BR* in descending priority, when CPU execution level allows . + */ +uint8_t sm_arb_worker_master() { return 0; } -// GRANT received. wait for previous bus master to complete transaction, -// then become bus master -// Forwarding of other GRANTs not necessary ... arbitrator granted us. -static uint8_t sm_arb_state_3() { - if (buslatches_getbyte(7) & BIT(3)) { // INIT stops transaction: latch[7], bit 3 - // cleanup: clear all REQUESTS and SACk - buslatches_setbits(1, ARBITRATION_PRIORITY_MASK| BIT(5), 0); - // Todo: signal INIT to ARM! - sm_arb.state = &sm_arb_state_idle; - return 1; - } - if (buslatches_getbyte(0) & sm_arb.priority_bit) // wait for GRANT IN to be deasserted - return 0; - // wait until old bus master cleared BBSY - if (buslatches_getbyte(1) & BIT(6)) - return 0; - // wait until SSYN deasserted by old slave - if (buslatches_getbyte(4) & BIT(5)) - return 0; - // now become new bus master: Set BBSY, Clear REQUEST - // BBSY= bit 6 - buslatches_setbits(1, sm_arb.priority_bit | BIT(6), BIT(6)); - // SACK is cleared later in "data transfer" statemachines (DMA or INTR) - sm_arb.state = &sm_arb_state_4; // bus mastership acquired - return 1; -} - -// bus mastership acquired. DMA data transfer must terminate this state -static uint8_t sm_arb_state_4() { - return 1; -} - -#ifdef USED - -S1: -if SACK deasserted: - -set all Reqest lines in latch 1, which have bits set in mailbox.arb_request -(requests all in parallel - - wait for any GRANT - - // always: pass un-requestedgrants - read GRANT lines BG*IN, NPGIN - mask with my requests - write pattern to BG*OUT, NPGOUT - - S2: - get all 5 GRANT signals - get mask with highest asserted GRANT line ("active Grant") , which was requested in S1 - // always: pass un-requestedgrants to BG*OUT, NPGOUT - if grants for us: - set SACK - wait for active GRANT line going LOW - wait until BBSY==0 && SSYN==0 && active GRANT==0 free (long time!) - set BBSY - set SACK low - NO: SHOULD BE "BEFORE LAST DATA TRAMSFER BY CURRENT MASTER" - -> interaction with statemachine_master - setting SACK low early start arbitration early. - High priority interrupts while bus occpied by DMA (after ealry priority cycle) - are ignoreed then. - - set bit in mailbox in .arb_grant - - INIT: clear all requests, set INIT flag in mailbox! - - freeing BBSY: - on end of next DMA trasnfer, - OR after INTR transmission - -#endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h index 680c947..84f8cd5 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_arbitration.h @@ -1,47 +1,68 @@ /* pru1_statemachine_arbitration.h: state machine for INTR/DMA arbitration - 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 -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_ARBITRATION_H_ #define _PRU1_STATEMACHINE_ARBITRATION_H_ +#include -// execution of a state. return : 1, if statemachine stopped -typedef uint8_t (*sm_arb_state_func_ptr)(void); +// a priority-arbitration-worker returns a bit mask with the GRANT signal he recognized + +typedef uint8_t (*statemachine_arb_worker_func)(); typedef struct { - sm_arb_state_func_ptr state; // current state as ptr to "state function" - uint8_t priority_bit ; // single priority bit for this arbitration process - // one of ARBITRATION_PRIORITY_* + // There are 5 request/grant signals (BR4,5,6,7 and NPR). + // These are encoded as bitmask fitting the buslatch[0] or[1] + // BR/NPR lines = set of _PRIORITY_ARBITRATION_BIT_* + uint8_t request_mask; + + // sm_arb has 2 states: State 1 "Wait for GRANT" and State 2 "wait for BBSY" + // When arbitrator GRANts a request, we set SACK, GRAMT is cleared and we wait + // for BBSY clear. + // 0: not waitong for BBSY. + // != saves GRANTed reqiest and idnicates BBSY wait state + uint8_t bbsy_wait_grant_mask ; + + } statemachine_arbitration_t; +/* receives a grant_mask with 1 bit set and returns the index of that bit + when interpreted as intr odma request + BR4->0, BR5->1,BR6->2, BR7->3,NPR->4 + grant_mask as value from buslatch 0: BR4 at bit 0 + Undefined result if grant_mask empty or > 0x10 + */ +#define PRIORITY_ARBITRATION_INTR_BIT2IDX(grant_mask) \ + __lmbd((grant_mask), 1) +// "LMBD" = "Left Most Bit Detect" -#ifndef _PRU1_STATEMACHINE_ARBITRATION_C_ extern statemachine_arbitration_t sm_arb; -#endif -void sm_arb_start(uint8_t priority_bit); -uint8_t sm_arb_state_idle(void) ; +void sm_arb_reset(void); +uint8_t sm_arb_worker_none(void); +uint8_t sm_arb_worker_client(void); +uint8_t sm_arb_worker_master(void); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_dma.c b/10.01_base/2_src/pru1/pru1_statemachine_dma.c index 2be8a52..c9af48d 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_dma.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_dma.c @@ -1,56 +1,58 @@ /* pru1_statemachine_dma.c: state machine for bus master DMA - 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 + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - Statemachines to execute multiple masterr DATO or DATI cycles. - All references "PDP11BUS handbook 1979" - Precondition: BBSY already asserted (arbitration got) + Statemachines to execute multiple masterr DATO or DATI cycles. + All references "PDP11BUS handbook 1979" + Precondition: BBSY already asserted (arbitration got) - Master reponds to INIT by stopping transactions. - new state + Master reponds to INIT by stopping transactions. + new state - Start: setup dma mailbox setup with - startaddr, wordcount, cycle, words[] - Then sm_dma_init() ; - sm_dma_state = DMA_STATE_RUNNING ; - while(sm_dma_state != DMA_STATE_READY) - sm_dma_service() ; - state is 0 for OK, or 2 for timeout error. - mailbox.dma.cur_addr is error location + Start: setup dma mailbox setup with + startaddr, wordcount, cycle, words[] + Then sm_dma_init() ; + sm_dma_state = DMA_STATE_RUNNING ; + while(sm_dma_state != DMA_STATE_READY) + sm_dma_service() ; + state is 0 for OK, or 2 for timeout error. + mailbox.dma.cur_addr is error location - Speed: (clpru 2.2, -O3: - Example: DATI, time SSYN- active -> (processing) -> MSYN inactive - a) 2 states, buslatch_set/get function calls, TIMEOUT_SET/REACHED(75) -> 700ns - b) 2 states, buslatch_set/get macro, TIMEOUT_SET/REACHED(75) -> 605ns - c) 2 states, no TIMEOUT (75 already met) -> 430ns - d) 1 marged state, no TIMEOUT ca. 350ns + Speed: (clpru 2.2, -O3: + Example: DATI, time SSYN- active -> (processing) -> MSYN inactive + a) 2 states, buslatch_set/get function calls, TIMEOUT_SET/REACHED(75) -> 700ns + b) 2 states, buslatch_set/get macro, TIMEOUT_SET/REACHED(75) -> 605ns + c) 2 states, no TIMEOUT (75 already met) -> 430ns + d) 1 marged state, no TIMEOUT ca. 350ns - ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! + ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! */ #define _PRU1_STATEMACHINE_DMA_C_ +#include #include #include @@ -58,6 +60,7 @@ #include "mailbox.h" #include "pru1_buslatches.h" #include "pru1_utils.h" +#include "pru1_timeouts.h" #include "pru1_statemachine_arbitration.h" #include "pru1_statemachine_dma.h" @@ -65,38 +68,52 @@ /* sometimes short timeout of 75 and 150ns are required * 75ns between state changes is not necessary, code runs longer * 150ns between state changes is necessary - * Overhead for extra state and TIEOUTSET/REACHED is 100ns + * Overhead for extra state and TIMEOUTSET/REACHED is 100ns */ statemachine_dma_t sm_dma; /********** Master DATA cycles **************/ // forwards ; -static uint8_t sm_dma_state_1(void); -static uint8_t sm_dma_state_11(void); -static uint8_t sm_dma_state_21(void); -static uint8_t sm_dma_state_99(void); +static statemachine_state_func sm_dma_state_1(void); +static statemachine_state_func sm_dma_state_11(void); +static statemachine_state_func sm_dma_state_21(void); +static statemachine_state_func sm_dma_state_99(void); // dma mailbox setup with // startaddr, wordcount, cycle, words[] ? // "cycle" must be UNIBUS_CONTROL_DATI or UNIBUS_CONTROL_DATO -// BBSY already set, SACK held asserted -void sm_dma_start() { +// Wait for BBSY, SACK already held asserted +statemachine_state_func sm_dma_start() { // assert BBSY: latch[1], bit 6 // buslatches_setbits(1, BIT(6), BIT(6)); mailbox.dma.cur_addr = mailbox.dma.startaddr; sm_dma.dataptr = (uint16_t *) mailbox.dma.words; // point to start of data buffer - sm_dma.state = &sm_dma_state_1; sm_dma.cur_wordsleft = mailbox.dma.wordcount; mailbox.dma.cur_status = DMA_STATE_RUNNING; + + // do not wait for BBSY here. This is part of Arbitration. + buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY // next call to sm_dma.state() starts state machine + return (statemachine_state_func) &sm_dma_state_1; } +/* +// wait for BBSY deasserted, then assert +static statemachine_state_func sm_dma_state_1() { + if (buslatches_getbyte(1) & BIT(6)) + return (statemachine_state_func) &sm_dma_state_1; // wait + buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY + return (statemachine_state_func) &sm_dma_state_1; +} +*/ + + // place address and control bits onto bus, also data for DATO // If slave address is internal (= implemented by UniBone), // fast UNIBUS slave protocol is generated on the bus. -static uint8_t sm_dma_state_1() { +static statemachine_state_func sm_dma_state_1() { uint32_t tmpval; uint32_t addr = mailbox.dma.cur_addr; // non-volatile snapshot uint16_t data; @@ -104,11 +121,10 @@ static uint8_t sm_dma_state_1() { // uint8_t page_table_entry; uint8_t b; bool internal; - - // should test SACK and BBSY ! - if (mailbox.dma.cur_status != DMA_STATE_RUNNING || mailbox.dma.wordcount == 0) - return 1; // still stopped + // BBSY released + if (mailbox.dma.cur_status != DMA_STATE_RUNNING || mailbox.dma.wordcount == 0) + return NULL; // still stopped if (sm_dma.cur_wordsleft == 1) { // deassert SACK, enable next arbitration cycle @@ -173,17 +189,18 @@ static uint8_t sm_dma_state_1() { buslatches_setbits(4, BIT(4), 0); // master deassert MSYN buslatches_setbyte(5, 0); // master removes data buslatches_setbyte(6, 0); - // perhaps PRU2ARM_INTERRUPT now active, - // assert SSYN after ARM completes "active" register logic and INIT - while (mailbox.events.eventmask) ; + // perhaps ARM issued ARM2PRU_INTR, request set in parallel state machine. + // Arbitrator will GRANT it after DMA ready (SACK deasserted). + // assert SSYN after ARM completes "active" register logic + // while (mailbox.events.event_deviceregister) ; buslatches_setbits(4, BIT(5), 0); // slave deassert SSYN - sm_dma.state = &sm_dma_state_99; // next word + return (statemachine_state_func) &sm_dma_state_99; // next word } else { // DATO to external slave // wait for a slave SSYN - TIMEOUT_SET(NANOSECS(1000*UNIBUS_TIMEOUT_PERIOD_US)); - sm_dma.state = &sm_dma_state_21; // wait SSYN DATAO + timeout_set(TIMEOUT_DMA, MICROSECS(UNIBUS_TIMEOUT_PERIOD_US)); + return (statemachine_state_func) &sm_dma_state_21; // wait SSYN DATAO } } else { // DATI or DATIP @@ -219,30 +236,29 @@ static uint8_t sm_dma_state_1() { buslatches_setbits(4, BIT(4), 0); // master deassert MSYN buslatches_setbyte(5, 0); // slave removes data buslatches_setbyte(6, 0); - // perhaps PRU2ARM_INTERRUPT now active, - // assert SSYN after ARM completes "active" register logic and INIT - while (mailbox.events.eventmask) ; + // perhaps ARM issued ARM2PRU_INTR, request set in parallel state machine. + // Arbitrator will GRANT it after DMA ready (SACK deasserted). + // assert SSYN after ARM completes "active" register logic + // while (mailbox.events.event_deviceregister) ; buslatches_setbits(4, BIT(5), 0); // slave deassert SSYN - sm_dma.state = &sm_dma_state_99; // next word + return (statemachine_state_func) &sm_dma_state_99; // next word } else { // DATI to external slave // wait for a slave SSYN - TIMEOUT_SET(NANOSECS(1000*UNIBUS_TIMEOUT_PERIOD_US)); - sm_dma.state = &sm_dma_state_11; // wait SSYN DATI + timeout_set(TIMEOUT_DMA, MICROSECS(UNIBUS_TIMEOUT_PERIOD_US)); + return (statemachine_state_func) &sm_dma_state_11; // wait SSYN DATI } } - - return 0; // still running } // DATI to external slave: MSYN set, wait for SSYN or timeout -static uint8_t sm_dma_state_11() { +static statemachine_state_func sm_dma_state_11() { uint16_t tmpval; - sm_dma.state_timeout = TIMEOUT_REACHED; + sm_dma.state_timeout = timeout_reached(TIMEOUT_DMA); // SSYN = latch[4], bit 5 if (!sm_dma.state_timeout && !(buslatches_getbyte(4) & BIT(5))) - return 0; // no SSYN yet: wait + return (statemachine_state_func) &sm_dma_state_11; // no SSYN yet: wait // SSYN set by slave (or timeout). read data __delay_cycles(NANOSECS(75) - 6); // assume 2*3 cycles for buslatches_getbyte @@ -257,16 +273,15 @@ static uint8_t sm_dma_state_11() { buslatches_setbits(4, BIT(4), 0); // DATI: remove address,control, MSYN,SSYN from bus, 75ns after MSYN inactive __delay_cycles(NANOSECS(75) - 8); // assume 8 cycles for state change - sm_dma.state = &sm_dma_state_99; - return 0; // still running + return (statemachine_state_func) &sm_dma_state_99; } // DATO to external slave: wait for SSYN or timeout -static uint8_t sm_dma_state_21() { - sm_dma.state_timeout = TIMEOUT_REACHED; // SSYN timeout? +static statemachine_state_func sm_dma_state_21() { + sm_dma.state_timeout = timeout_reached(TIMEOUT_DMA); // SSYN timeout? // SSYN = latch[4], bit 5 if (!sm_dma.state_timeout && !(buslatches_getbyte(4) & BIT(5))) - return 0; // no SSYN yet: wait + return (statemachine_state_func) &sm_dma_state_21; // no SSYN yet: wait // SSYN set by slave (or timeout): negate MSYN, remove DATA from bus buslatches_setbits(4, BIT(4), 0); // deassert MSYN @@ -274,12 +289,11 @@ static uint8_t sm_dma_state_21() { buslatches_setbyte(6, 0); // DATO: remove address,control, MSYN,SSYN from bus, 75ns after MSYN inactive __delay_cycles(NANOSECS(75) - 8); // assume 8 cycles for state change - sm_dma.state = &sm_dma_state_99; - return 0; // still running + return (statemachine_state_func) &sm_dma_state_99; } // word is transfered, or timeout. -static uint8_t sm_dma_state_99() { +static statemachine_state_func sm_dma_state_99() { uint8_t final_dma_state; // from state_12, state_21 @@ -302,14 +316,12 @@ static uint8_t sm_dma_state_99() { buslatches_setbits(1, BIT(5), 0); // deassert SACK = latch[1], bit 5 } else final_dma_state = DMA_STATE_RUNNING; // more words: continue - } - sm_dma.state = &sm_dma_state_1; // in any case, reloop if (final_dma_state == DMA_STATE_RUNNING) { - // dataptr and wordsleft already incremented + // dataptr and words_left already incremented mailbox.dma.cur_addr += 2; // signal progress to ARM - return 0; + return (statemachine_state_func) &sm_dma_state_1; // reloop } else { // remove addr and control from bus buslatches_setbyte(2, 0); @@ -317,10 +329,20 @@ static uint8_t sm_dma_state_99() { buslatches_setbits(4, 0x3f, 0); // remove BBSY: latch[1], bit 6 buslatches_setbits(1, BIT(6), 0); - // terminate arbitration state - sm_arb.state = &sm_arb_state_idle; + // SACK already de-asserted at wordcount==1 + timeout_cleanup(TIMEOUT_DMA); mailbox.dma.cur_status = final_dma_state; // signal to ARM - return 1; // now stopped + + timeout_cleanup(TIMEOUT_DMA); + + // signal to ARM + mailbox.events.event_dma = 1; + // ARM is clearing this, before requesting new DMA. + // no concurrent ARP+PRU access + PRU2ARM_INTERRUPT + ; + + return NULL; // now stopped } } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_dma.h b/10.01_base/2_src/pru1/pru1_statemachine_dma.h index 84f01b8..4a1a432 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_dma.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_dma.h @@ -1,53 +1,46 @@ /* pru1_statemachine_dma.h: state machine for bus master DMA - 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 -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_DMA_H_ #define _PRU1_STATEMACHINE_DMA_H_ - - -// execution of a state. return : 1, if statemachine stopped -typedef uint8_t (*sm_dma_state_func_ptr)(void); - +#include "pru1_utils.h" // statemachine_state_func // Transfers a block of worst as data cycles typedef struct { - sm_dma_state_func_ptr state; // current state as ptr to "state fucntion" uint8_t state_timeout; // timeout occured? - uint16_t *dataptr ; // points to current word in mailbox.words[] ; + uint16_t *dataptr; // points to current word in mailbox.words[] ; uint16_t cur_wordsleft; // # of words left to transfer } statemachine_dma_t; - - #ifndef _PRU1_STATEMACHINE_DMA_C_ extern statemachine_dma_t sm_dma; #endif - -void sm_dma_start(void) ; +statemachine_state_func sm_dma_start(void); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_init.c b/10.01_base/2_src/pru1/pru1_statemachine_init.c index 1a3b5f7..76ab6ff 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_init.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_init.c @@ -1,42 +1,45 @@ /* pru1_statemachine_init.c: state machine for pulse on INIT - 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 + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - Statemachine for a pulse on UNIBUS INIT - ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! + Statemachine for a pulse on UNIBUS INIT + ! Uses single global timeout, don't run in parallel with other statemachines using timeout ! */ #define _PRU1_STATEMACHINE_INIT_C_ +#include #include #include "mailbox.h" #include "pru1_utils.h" +#include "pru1_timeouts.h" #include "pru1_buslatches.h" #include "pru1_statemachine_init.h" - +#include "pru1_statemachine_arbitration.h" /*** detection of changes in INIT,DCLO,ACLO ***/ @@ -44,48 +47,54 @@ // history initialized (among others) by powercycle // Assume this events come so slow, no one gets raised until // prev event processed. -void do_event_initializationsignals() { - uint8_t tmp = buslatches_getbyte(7) & 0x38 ; - if (tmp != mailbox.events.initialization_signals_cur) { - // save old state, so ARM can detect what changed - mailbox.events.initialization_signals_prev = mailbox.events.initialization_signals_cur ; - mailbox.events.initialization_signals_cur = tmp ; - mailbox.events.eventmask |= EVENT_INITIALIZATIONSIGNALS ; - PRU2ARM_INTERRUPT ; - } +void do_event_initializationsignals() { + uint8_t mb_cur = mailbox.events.initialization_signals_cur; // as saved + uint8_t bus_cur = buslatches_getbyte(7) & 0x38; // now sampled + + if (bus_cur & INITIALIZATIONSIGNAL_INIT) + sm_arb.request_mask = 0 ; // INIT clears all PRIORITY request signals + // SACK cleared later on end of INTR/DMA transaction + + if (bus_cur != mb_cur) { + // save old state, so ARM can detect what changed + mailbox.events.initialization_signals_prev = mb_cur; + mailbox.events.initialization_signals_cur = bus_cur; + // trigger the correct event: power and/or INIT + if ((mb_cur ^ bus_cur) & (INITIALIZATIONSIGNAL_DCLO | INITIALIZATIONSIGNAL_ACLO)) + // AC_LO or DC_LO changed + mailbox.events.event_power = 1; + if ((mb_cur ^ bus_cur) & INITIALIZATIONSIGNAL_INIT) + // INIT changed + mailbox.events.event_init = 1; + PRU2ARM_INTERRUPT + ; } -// No event queue: INIT event may overide register access ... thats OK: -// INIT reset all register states - - - - -statemachine_init_t sm_init; +} // forwards -uint8_t sm_init_state_idle(void); -static uint8_t sm_init_state_1(void); +static statemachine_state_func sm_init_state_1(void); // setup -void sm_init_start() { - TIMEOUT_SET(MILLISECS(INITPULSE_DELAY_MS)) - ; +statemachine_state_func sm_init_start() { + timeout_set(TIMEOUT_INIT, MILLISECS(INITPULSE_DELAY_MS)); // INIT: latch[7], bit 3 buslatches_setbits(7, BIT(3), BIT(3)); // assert INIT - mailbox.events.initialization_signals_prev &= ~INITIALIZATIONSIGNAL_INIT ; // force INIT event - do_event_initializationsignals() ; - sm_init.state = &sm_init_state_1; + mailbox.events.initialization_signals_prev &= ~INITIALIZATIONSIGNAL_INIT; // force INIT event + return (statemachine_state_func) &sm_init_state_1; } -uint8_t sm_init_state_idle() { - return 1; // ready -} - -static uint8_t sm_init_state_1() { - if (!TIMEOUT_REACHED) - return 0; +static statemachine_state_func sm_init_state_1() { + if (!timeout_reached(TIMEOUT_INIT)) + return (statemachine_state_func) &sm_init_state_1; // wait buslatches_setbits(7, BIT(3), 0); // deassert INIT - do_event_initializationsignals() ; - sm_init.state = &sm_init_state_idle; - return 1; + timeout_cleanup(TIMEOUT_INIT); + return NULL; // ready +} + +// terminate INIT +// To be called from Powercycle? +void sm_init_reset() { + buslatches_setbits(7, BIT(3), 0); // deassert INIT + do_event_initializationsignals(); + timeout_cleanup(TIMEOUT_INIT); } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_init.h b/10.01_base/2_src/pru1/pru1_statemachine_init.h index a29859e..bbfd850 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_init.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_init.h @@ -1,51 +1,45 @@ /* pru1_statemachine_init.h: state machine for pulse on INIT - 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 -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_INIT_H_ #define _PRU1_STATEMACHINE_INIT_H_ #include - +#include "pru1_utils.h" // statemachine_state_func #define INITPULSE_DELAY_MS 250 // length of INIT pulse -// execution of a state. return : 1, if statemachine stopped -typedef uint8_t (*sm_powercycle_init_func_ptr)(void); - -typedef struct { - sm_powercycle_init_func_ptr state; // current state as ptr to "state function" -} statemachine_init_t; - #ifndef _PRU1_STATEMACHINE_INIT_C_ -extern uint8_t prev_initialization_signals ; -extern statemachine_init_t sm_init; +extern uint8_t prev_initialization_signals; #endif -void do_event_initializationsignals(void) ; +void do_event_initializationsignals(void); -void sm_init_start(); +void sm_init_reset(void); +statemachine_state_func sm_init_start(); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_intr.c b/10.01_base/2_src/pru1/pru1_statemachine_intr.c index a9719ab..ba169cd 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_intr.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_intr.c @@ -1,35 +1,37 @@ /* pru1_statemachine_intr.c: state machine to transfer an interrupt vector - 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 + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - State machines to transfer an interrupt vector. - All references "PDP11BUS handbook 1979" - Precondition: BBSY already asserted (arbitration got) + State machines to transfer an interrupt vector. + All references "PDP11BUS handbook 1979" + Precondition: BBSY already asserted (arbitration got) */ #define _PRU1_STATEMACHINE_INTR_C_ +#include #include //#include "devices.h" @@ -37,34 +39,30 @@ #include "pru1_buslatches.h" #include "pru1_utils.h" -#include "pru1_statemachine_arbitration.h" +//#include "pru1_statemachine_arbitration.h" #include "pru1_statemachine_intr.h" +// states statemachine_intr_t sm_intr; // forwards -static uint8_t sm_intr_state_idle(void); -static uint8_t sm_intr_state_1(void); -static uint8_t sm_intr_state_2(void); +static statemachine_state_func sm_intr_state_1(void); +static statemachine_state_func sm_intr_state_2(void); -// BBSY already set, SACK held asserted -void sm_intr_start() { - // BBSY already asserted. : latch[1], bit 6 - // buslatches_setbits(1, BIT(6), BIT(6)); - sm_intr.state = &sm_intr_state_1; - // next call to sm_intr.state() starts state machine -} - -static uint8_t sm_intr_state_idle() { - return 1; +// Wait for BBSY deasserted, then assert, SACK already held asserted +statemachine_state_func sm_intr_start() { + // Do not wait for BBSY here, this is part of Arbitration + // if (buslatches_getbyte(1) & BIT(6)) + // return (statemachine_state_func) &sm_intr_start; // wait + buslatches_setbits(1, BIT(6), BIT(6)); // assert BBSY + return (statemachine_state_func) &sm_intr_state_1; } // place vector onto data, then set INTR -static uint8_t sm_intr_state_1() { - uint16_t vector = mailbox.intr.vector; +static statemachine_state_func sm_intr_state_1() { - buslatches_setbyte(5, vector & 0xff); // DATA[0..7] = latch[5] - buslatches_setbyte(6, vector >> 8); // DATA[8..15] = latch[6] + buslatches_setbyte(5, sm_intr.vector & 0xff); // DATA[0..7] = latch[5] + buslatches_setbyte(6, sm_intr.vector >> 8); // DATA[8..15] = latch[6] // set INTR buslatches_setbits(7, BIT(0), BIT(0)); // INTR = latch 7, bit 0 @@ -75,25 +73,42 @@ static uint8_t sm_intr_state_1() { buslatches_setbits(1, BIT(5), 0); // SACK = latch[1], bit 5 // wait for processor to accept vector (no timeout?) - sm_intr.state = &sm_intr_state_2; - return 0; + return (statemachine_state_func) &sm_intr_state_2; } // wait for SSYN -static uint8_t sm_intr_state_2() { +static statemachine_state_func sm_intr_state_2() { if (!(buslatches_getbyte(4) & BIT(5))) - return 0; + return (statemachine_state_func) &sm_intr_state_2; // wait // received SSYN - //remove vector, then remove INTR + // Complete and signal this INTR transaction only after ARM has processed the previous event. + // INTR may come faster than ARM Linux can process, + // especially if Arbitrator grants INTRs of multiple levels almost simultaneaously in parallel. + if (mailbox.events.event_intr) + return (statemachine_state_func) &sm_intr_state_2; // wait + + // remove vector buslatches_setbyte(5, 0); // DATA[0..7] = latch[5] buslatches_setbyte(6, 0); // DATA[8..15] = latch[6] + + // deassert INTR. Interrupt fielding processor then removes SSYN buslatches_setbits(7, BIT(0), 0); // INTR = latch 7, bit 0 + // deassert BBSY buslatches_setbits(1, BIT(6), 0); + // SACK already removed - sm_intr.state = &sm_intr_state_idle; - return 1; + // signal to ARM which INTR was completed + // change mailbox only after ARM has ack'ed mailbox.events.event_intr + mailbox.events.event_intr_level_index = sm_intr.level_index; + mailbox.events.event_intr = 1; + // ARM is clearing this, before requesting new interrupt of same level + // so no concurrent ARP+PRU access + PRU2ARM_INTERRUPT + ; + + return NULL; // ready // master still drives SSYN } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_intr.h b/10.01_base/2_src/pru1/pru1_statemachine_intr.h index 65b4473..3361621 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_intr.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_intr.h @@ -1,43 +1,41 @@ /* pru1_statemachine_intr.h: state machine to transfer an interrupt vector - 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 -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_INTR_H_ #define _PRU1_STATEMACHINE_INTR_H_ -// execution of a state. return : 1, if statemachine stopped -typedef uint8_t (*sm_intr_state_func_ptr)(void); +#include "pru1_utils.h" // statemachine_state_func -// Transfers a block of worst as data cycles typedef struct { - sm_intr_state_func_ptr state; // current state as ptr to "state fucntion" + uint16_t vector; // interrupt vector to transfer + uint8_t level_index; // 0..3 = BR..BR7. to be returned to ARM on complete } statemachine_intr_t; -#ifndef _PRU1_STATEMACHINE_INTR_C_ -extern statemachine_dma_t sm_intr; -#endif +extern statemachine_intr_t sm_intr; -void sm_intr_start(void); +statemachine_state_func sm_intr_start(void); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c index a14d9e0..948eee4 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.c @@ -1,27 +1,28 @@ /* pru1_statemachine_powercycle.c: state machine to generate an ACLO/DCLO pseudo power cycle - 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 + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase Statemachine for execution of an ACLO/DCLO pseudo power cycle. @@ -41,81 +42,68 @@ #define _PRU1_STATEMACHINE_POWERCYCLE_C_ +#include #include - #include "mailbox.h" #include "pru1_utils.h" +#include "pru1_timeouts.h" #include "pru1_buslatches.h" #include "pru1_statemachine_init.h" #include "pru1_statemachine_powercycle.h" -statemachine_powercycle_t sm_powercycle; - // forwards ; / -uint8_t sm_powercycle_state_idle(void); -static uint8_t sm_powercycle_state_1(void); -static uint8_t sm_powercycle_state_2(void); -static uint8_t sm_powercycle_state_3(void); -static uint8_t sm_powercycle_state_4(void); +static statemachine_state_func sm_powercycle_state_1(void); +static statemachine_state_func sm_powercycle_state_2(void); +static statemachine_state_func sm_powercycle_state_3(void); +static statemachine_state_func sm_powercycle_state_4(void); // setup with -void sm_powercycle_start() { - sm_powercycle.state = &sm_powercycle_state_1; +statemachine_state_func sm_powercycle_start() { + return (statemachine_state_func) &sm_powercycle_state_1; // next call to sm_slave.state() starts state machine } -uint8_t sm_powercycle_state_idle() { - return 1; // ready -} - // "Line power shutdown": assert ACLO, then wait -static uint8_t sm_powercycle_state_1() { +static statemachine_state_func sm_powercycle_state_1() { buslatches_setbits(7, BIT(4), BIT(4)); // ACLO asserted - TIMEOUT_SET(MILLISECS(POWERCYCLE_DELAY_MS)) - ; // wait for DC power shutdown - sm_powercycle.state = &sm_powercycle_state_2; + timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // wait for DC power shutdown // DEBUG_OUT(0x01) ; - do_event_initializationsignals() ; // DEBUG_OUT(0x02) ; - return 0; + return (statemachine_state_func) &sm_powercycle_state_2; } // "Power supply switched off": assert DCLO, then wait -static uint8_t sm_powercycle_state_2() { - if (!TIMEOUT_REACHED) - return 0; +static statemachine_state_func sm_powercycle_state_2() { + if (!timeout_reached(TIMEOUT_POWERCYCLE)) + return (statemachine_state_func) &sm_powercycle_state_2; // wait buslatches_setbits(7, BIT(5), BIT(5)); // DCLO asserted - TIMEOUT_SET(MILLISECS(POWERCYCLE_DELAY_MS)) - ; // system powered off - sm_powercycle.state = &sm_powercycle_state_3; - // DEBUG_OUT(0x03) ; - do_event_initializationsignals() ; - // DEBUG_OUT(0x04) ; - return 0; + timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // system powered off + // DEBUG_OUT(0x03) ; + // DEBUG_OUT(0x04) ; + // make sure we don't "come up from power" with INIT already set. + sm_init_reset(); + + return (statemachine_state_func) &sm_powercycle_state_3; } // "Line power back again": deassert ACLO, then wait -static uint8_t sm_powercycle_state_3() { - if (!TIMEOUT_REACHED) - return 0; +static statemachine_state_func sm_powercycle_state_3() { + if (!timeout_reached(TIMEOUT_POWERCYCLE)) + return (statemachine_state_func) &sm_powercycle_state_3; // wait buslatches_setbits(7, BIT(4), 0); // ACLO deasserted - TIMEOUT_SET(MILLISECS(POWERCYCLE_DELAY_MS)) - ; // "power supply stabilizing" - sm_powercycle.state = &sm_powercycle_state_4; - do_event_initializationsignals() ; - return 0; + timeout_set(TIMEOUT_POWERCYCLE, MILLISECS(POWERCYCLE_DELAY_MS)); // "power supply stabilizing" + return (statemachine_state_func) &sm_powercycle_state_4; } // "Logic power stabilized": deassert DCLO, ready -static uint8_t sm_powercycle_state_4() { - if (!TIMEOUT_REACHED) - return 0; - buslatches_setbits(7, BIT(5), 0); // DCLO deasserted - sm_powercycle.state = &sm_powercycle_state_idle; - do_event_initializationsignals() ; - return 1; +static statemachine_state_func sm_powercycle_state_4() { + if (!timeout_reached(TIMEOUT_POWERCYCLE)) + return (statemachine_state_func) &sm_powercycle_state_4; + buslatches_setbits(7, BIT(5), 0); // DCLO deasserted + timeout_cleanup(TIMEOUT_POWERCYCLE); + return NULL; } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h index 220e47d..bc96363 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_powercycle.h @@ -1,46 +1,37 @@ /* pru1_statemachine_powercycle.h: state machine to generate an ACLO/DCLO pseudo power cycle - 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 -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_POWERCYCLE_H_ #define _PRU1_STATEMACHINE_POWERCYCLE_H_ #include +#include "pru1_utils.h" // statemachine_state_func #define POWERCYCLE_DELAY_MS 100 // wait period in millsecs between ACLO/DCLO transitions -// execution of a state. return : 1, if statemachine stopped -typedef uint8_t (*sm_powercycle_state_func_ptr)(void); - -typedef struct { - sm_powercycle_state_func_ptr state; // current state as ptr to "state function" -} statemachine_powercycle_t; - -#ifndef _PRU1_STATEMACHINE_POWERCYCLE_C_ -extern statemachine_powercycle_t sm_powercycle; -#endif - -void sm_powercycle_start(); +statemachine_state_func sm_powercycle_start(); #endif diff --git a/10.01_base/2_src/pru1/pru1_statemachine_slave.c b/10.01_base/2_src/pru1/pru1_statemachine_slave.c index cf9fc98..d822a13 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_slave.c +++ b/10.01_base/2_src/pru1/pru1_statemachine_slave.c @@ -1,40 +1,42 @@ /* pru1_statemachine_slave.c: state machine for execution of slave DATO* or DATI* cycles - 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 + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase - Statemachine for execution of slave DATO* or DATI* cycles. - All references "PDP11BUS handbook 1979" + Statemachine for execution of slave DATO* or DATI* cycles. + All references "PDP11BUS handbook 1979" - Slave reponds not to INIT on this level, master must stop bus transactions. + Slave reponds not to INIT on this level, master must stop bus transactions. - - Slave waits for MSYN L->H - - slaves fetches address and control lines - - address is evaluated, ggf mem access + - Slave waits for MSYN L->H + - slaves fetches address and control lines + - address is evaluated, ggf mem access */ #define _PRU1_STATEMACHINE_SLAVE_C_ +#include #include #include "pru1_utils.h" @@ -46,22 +48,14 @@ #include "pru1_buslatches.h" #include "pru1_statemachine_slave.h" -statemachine_slave_t sm_slave; - // forwards ; -static uint8_t sm_slave_state_1(void); -static uint8_t sm_slave_state_10(void); -static uint8_t sm_slave_state_20(void); -static uint8_t sm_slave_state_99(void); - -// setup with -void sm_slave_start() { - sm_slave.state = &sm_slave_state_1; - // next call to sm_slave.state() starts state machine -} +//statemachine_state_func sm_slave_start(void); +static statemachine_state_func sm_slave_state_10(void); +static statemachine_state_func sm_slave_state_20(void); +static statemachine_state_func sm_slave_state_99(void); // check for MSYN active -static uint8_t sm_slave_state_1() { +statemachine_state_func sm_slave_start() { uint8_t latch2val, latch3val, latch4val; // uint8_t iopage; uint32_t addr; @@ -76,10 +70,10 @@ static uint8_t sm_slave_state_1() { // MSYN active ? if (!(latch4val & BIT(4))) - return 1; // still idle + return NULL; // still idle if (latch4val & BIT(5)) // SSYN active: cycle answered by other bus slave - return 1; // still idle + return NULL; // still idle // checking against SSYN guarantees address if valid if fetched now. // However, another Bus slave can SSYN immediately @@ -115,15 +109,13 @@ static uint8_t sm_slave_state_1() { buslatches_setbyte(5, data & 0xff); // DATA[8..15] = latch[6] buslatches_setbyte(6, data >> 8); - //DEBUG_PIN_PULSE ; // trigger scope/LA. auto cleared on next reg_sel // set SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), BIT(5)); - sm_slave.state = &sm_slave_state_20; + return (statemachine_state_func) &sm_slave_state_20; // perhaps PRU2ARM_INTERRUPT now active } else // no address match: wait for MSYN to go inactive - sm_slave.state = &sm_slave_state_99; - break; + return (statemachine_state_func) &sm_slave_state_99; case UNIBUS_CONTROL_DATO: // fetch data in any case // DATA[0..7] = latch[5] @@ -131,17 +123,15 @@ static uint8_t sm_slave_state_1() { // DATA[8..15] = latch[6] w |= (uint16_t) buslatches_getbyte(6) << 8; if (iopageregisters_write_w(addr, w)) { - //DEBUG_PIN_PULSE ; // trigger scope/LA. auto cleared on next reg_sel // SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), BIT(5)); // wait for MSYN to go inactive, then SSYN inactive - sm_slave.state = &sm_slave_state_10; + return (statemachine_state_func) &sm_slave_state_10; // perhaps PRU2ARM_INTERRUPT now active } else // no address match: wait for MSYN to go inactive - sm_slave.state = &sm_slave_state_99; - break; + return (statemachine_state_func) &sm_slave_state_99; case UNIBUS_CONTROL_DATOB: // A00 = 1, odd address: get upper byte // A00 = 0: even address, get lower byte @@ -157,44 +147,46 @@ static uint8_t sm_slave_state_1() { // SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), BIT(5)); // wait for MSYN to go inactive, then SSYN inactive - sm_slave.state = &sm_slave_state_10; + return (statemachine_state_func) &sm_slave_state_10; // perhaps PRU2ARM_INTERRUPT now active } else // no address match: wait for MSYN to go inactive - sm_slave.state = &sm_slave_state_99; - break; + return (statemachine_state_func) &sm_slave_state_99; } - return 0; // busy + return NULL; // not reached } // End DATO: wait for MSYN to go inactive, then SSYN inactive // also wait for EVENT ACK -static uint8_t sm_slave_state_10() { +static statemachine_state_func sm_slave_state_10() { // MSYN = latch[4], bit 4 if (buslatches_getbyte(4) & BIT(4)) - return 0; // MSYN still active - if (mailbox.events.eventmask) - return 0; // long SSYN delay until ARM acknowledges all events + return (statemachine_state_func) &sm_slave_state_10; // wait, MSYN still active + if (mailbox.events.event_deviceregister) + // unibusadapter.worker() did not yet run on_after_register_access() + // => wait, long SSYN delay until ARM acknowledges event + return (statemachine_state_func) &sm_slave_state_10; // if ARM was triggered by event and changed the device state, - // now an Interrupt arbitration may be pending! + // now an Interrupt arbitration may be pending. // clear SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), 0); - sm_slave.state = &sm_slave_state_1; - return 1; // ready } + return NULL; // ready } // End DATI: wait for MSYN to go inactive, then SSYN and DATA inactive // also wait for EVENT ACK -static uint8_t sm_slave_state_20() { +static statemachine_state_func sm_slave_state_20() { // MSYN = latch[4], bit 4 if (buslatches_getbyte(4) & BIT(4)) - return 0; // MSYN still active - if (mailbox.events.eventmask) - return 0; // long SSYN delay until ARM acknowledges event + return (statemachine_state_func) &sm_slave_state_20; // wait, MSYN still active + if (mailbox.events.event_deviceregister) + // unibusadapter.worker() did not yet run on_after_register_access() + // => wait, long SSYN delay until ARM acknowledges event + return (statemachine_state_func) &sm_slave_state_20; // if ARM was triggered by event and changed the device state, - // now an Interrupt arbitration may be pending! + // now an Interrupt arbitration may be pending. // clear first data, then SSYN // DATA[0..7] = latch[5] @@ -203,16 +195,14 @@ static uint8_t sm_slave_state_20() { buslatches_setbyte(6, 0); // clear SSYN = latch[4], bit 5 buslatches_setbits(4, BIT(5), 0); - sm_slave.state = &sm_slave_state_1; - return 1; // ready } + return NULL; // ready } // end of inactive cycle: wait for MSYN to go inactive -static uint8_t sm_slave_state_99() { +static statemachine_state_func sm_slave_state_99() { // MSYN = latch[4], bit 4 if (buslatches_getbyte(4) & BIT(4)) - return 0; // MSYN still active + return (statemachine_state_func) &sm_slave_state_99; // wait, MSYN still active - sm_slave.state = &sm_slave_state_1; - return 1; // ready } + return NULL; // ready } diff --git a/10.01_base/2_src/pru1/pru1_statemachine_slave.h b/10.01_base/2_src/pru1/pru1_statemachine_slave.h index c8595d5..9e9bfd6 100644 --- a/10.01_base/2_src/pru1/pru1_statemachine_slave.h +++ b/10.01_base/2_src/pru1/pru1_statemachine_slave.h @@ -1,47 +1,35 @@ /* pru1_statemachine_slave.h: state machine for execution of slave DATO* or DATI* cycles - 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 -*/ + 29-jun-2019 JH rework: state returns ptr to next state func + 12-nov-2018 JH entered beta phase + */ #ifndef _PRU1_STATEMACHINE_SLAVE_H_ #define _PRU1_STATEMACHINE_SLAVE_H_ #include +#include "pru1_utils.h" // statemachine_state_func -// execution of a state. return : 1, if statemachine stopped -typedef uint8_t (*sm_slave_state_func_ptr)(void); - -typedef struct { - sm_slave_state_func_ptr state; // current state as ptr to "state function" -// uint32_t addr; // adress fetched from bus on MSYN -// uint8_t control; // C1,C0 fetched from bus on MSYN -// uint16_t data; // data fetched from bus on MSYN/ to be written to BUS on SSYN -} statemachine_slave_t; - -#ifndef _PRU1_STATEMACHINE_SLAVE_C_ -extern statemachine_slave_t sm_slave; -#endif - -void sm_slave_start(); +statemachine_state_func sm_slave_start(void); #endif diff --git a/10.01_base/2_src/pru1/pru1_timeouts.c b/10.01_base/2_src/pru1/pru1_timeouts.c new file mode 100644 index 0000000..b89371a --- /dev/null +++ b/10.01_base/2_src/pru1/pru1_timeouts.c @@ -0,0 +1,111 @@ +/* pru1_timeouts.c: timeout conditions + + Copyright (c) 2019, 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: + + 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. + + 3-jul-2019 JH begin edit + + Several timers are needed, but PRU hs only one + global cycle counter CYCLEOUNT. It is 32 bit and runs at 200 MHz, + so rollaround every 21 seconds. + + Usage: + - if no timer is running, the first "timeout" reuqest clears CYCLEOUNT + - for each timer, the "timeout" cycle count is set + - tiemr msut be polled for timeout by user. + - a timer is considered "timed-out", if its timeout is 0. + - a global variable regsiteres the active running timeouts. + - a running timeout MUST be canceled, or polled until "timeout_rechaed" !! + + + The PRU CYCLECOUNT may not be reset if one timeout is active. + So the total run time of all parallel running timeous must not exceed 21 seconds + At least every 21 seconds all timers must be expired. + + */ +#include +#include + +#include "pru1_utils.h" +#include "pru1_timeouts.h" // own + +// count running timers. +static uint8_t timeouts_active = 0; + +// cycle end count for each active timeoput. +uint32_t timeout_target_cycles[TIMEOUT_COUNT]; + +// all functions receive a pointer to one of these array members + +#define TIMEOUT_INTERNAL_CYCLES 24 + +void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles) { + // stop timeout, if already running + if (*target_cycles_var > 0) { + *target_cycles_var = 0; + timeouts_active--; // was inactive + } + + if (timeouts_active == 0) { + // first timeout: clear and restart counter + PRU1_CTRL.CTRL_bit.CTR_EN = 0; + PRU1_CTRL.CYCLE = 0; + } + + /* 4 cycle used in TIMEOUT_REACHED */ + if (delta_cycles < TIMEOUT_INTERNAL_CYCLES) + delta_cycles = 0; + else + delta_cycles -= TIMEOUT_INTERNAL_CYCLES; + *target_cycles_var = PRU1_CTRL.CYCLE + delta_cycles; + PRU1_CTRL.CTRL_bit.CTR_EN = 1; + timeouts_active++; // now one more active +} + +// msut be called, if timeout polled anymore for "timeout-reached() +void timeout_cleanup(uint32_t *target_cycles_var) { + if (*target_cycles_var > 0) { + *target_cycles_var = 0; + timeouts_active--; // was inactive + } +} + +// + +// test a timeout, wehter it reached its arg count nor or earlier +bool timeout_reached(uint32_t *target_cycles_var) { + // fast path: assume timeout_reached() is called + // because timeout is active + if (PRU1_CTRL.CYCLE < *target_cycles_var) + return false; + else if (*target_cycles_var == 0) + return true; // already inactive + else { + // switched from "running" to "timeout reached" + *target_cycles_var = 0; + timeouts_active--; + return true; + } +} + +void timeout_init(void) { + timeouts_active = 0; + memset(timeout_target_cycles, 0, sizeof(uint32_t) * TIMEOUT_COUNT); +} diff --git a/10.01_base/2_src/pru1/pru1_timeouts.h b/10.01_base/2_src/pru1/pru1_timeouts.h new file mode 100644 index 0000000..717a533 --- /dev/null +++ b/10.01_base/2_src/pru1/pru1_timeouts.h @@ -0,0 +1,50 @@ +/* pru1_timeouts.h: timeout conditions + + Copyright (c) 2019, 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: + + 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. + + 3-jul-2019 JH begin edit + */ +#ifndef _PRU1_TIMEOUTS_H_ +#define _PRU1_TIMEOUTS_H_ + +#include +#include + +// predefined timeouts +#define TIMEOUT_COUNT 3 + +// fixed pointers +#define TIMEOUT_DMA (&timeout_target_cycles[0]) +#define TIMEOUT_INIT (&timeout_target_cycles[1]) +#define TIMEOUT_POWERCYCLE (&timeout_target_cycles[2]) + +// cycle end count for each active timeoput. +extern uint32_t timeout_target_cycles[TIMEOUT_COUNT]; + +// call all functions mit timeout_func(TIMEOUT_*,..) +// This allows the compiler to optimize the timeout_target_cycles[idx] expr + +void timeout_init(void); +void timeout_set(uint32_t *target_cycles_var, uint32_t delta_cycles); +bool timeout_reached(uint32_t *target_cycles_var); +void timeout_cleanup(uint32_t *target_cycles_var); + +#endif diff --git a/10.01_base/2_src/pru1/pru1_utils.c b/10.01_base/2_src/pru1/pru1_utils.c index 27249e1..edd9f70 100644 --- a/10.01_base/2_src/pru1/pru1_utils.c +++ b/10.01_base/2_src/pru1/pru1_utils.c @@ -1,38 +1,32 @@ /* pru1_utils.c: misc. utilities - 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 + */ #define _PRU1_UTILS_C_ - #include - #include "pru1_utils.h" - -uint32_t timeout_target ; - - diff --git a/10.01_base/2_src/pru1/pru1_utils.h b/10.01_base/2_src/pru1/pru1_utils.h index f7b1d40..a60d0f7 100644 --- a/10.01_base/2_src/pru1/pru1_utils.h +++ b/10.01_base/2_src/pru1/pru1_utils.h @@ -1,32 +1,31 @@ /* pru1_utils.h: misc. utilities - 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 _PRU1_UTILS_H_ #define _PRU1_UTILS_H_ - #include #include @@ -36,13 +35,15 @@ volatile register uint32_t __R30; volatile register uint32_t __R31; - - +// execution of a state. return : next state; or NULL if statemachine stopped +// return type is void *, but should be statemachine_state_func_ptr recursively +// typedef statemachine_state_func * (*statemachine_state_func)(void); +// Not possible?! So return void * and cast to void *(func(void) on use +typedef void * (*statemachine_state_func)(void); #define MILLION 1000000L #define BILLION (1000L * MILLION) - // SSYN timeout after 20 milliseconds #define UNIBUS_TIMEOUT_PERIOD_US 20 @@ -62,18 +63,18 @@ volatile register uint32_t __R31; * Translated with "clpru 2.2, -O3" * * Test setup: -while (1) { - __R30 |= (1 << 8); - TIMEOUT_SET(NANOSECS(1000)) ; // 1 usec / level - while (!TIMEOUT_REACHED) ; - __R30 &= ~(1 << 8); - TIMEOUT_SET(NANOSECS(1000)) ; - while (!TIMEOUT_REACHED) ; -} -* -* Enhancement: save address of CYLCE in cosntant btabel C28, see -http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-prus-part-1/449 -* ALTERNATIVE 1: + while (1) { + __R30 |= (1 << 8); + TIMEOUT_SET(NANOSECS(1000)) ; // 1 usec / level + while (!TIMEOUT_REACHED) ; + __R30 &= ~(1 << 8); + TIMEOUT_SET(NANOSECS(1000)) ; + while (!TIMEOUT_REACHED) ; + } + * + * Enhancement: save address of CYLCE in cosntant btabel C28, see + http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-prus-part-1/449 + * ALTERNATIVE 1: * The Industrial Ethernet module has a general purpose time and compare registers: * 32 bit Counter is permanetly running cyclic through overlfow * TIMEOUT_SET: compare0 = counter + delay, clear compare0 flag @@ -85,35 +86,17 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru * dann nur compare0 nutzbar * counter overflow auf 2^32 5ns = 21 sek -* ALTERNATIVE 2: -* PRU0 decrements a mailbox value until it is 0. -* introduces delay in PRU0 outputs! + * ALTERNATIVE 2: + * PRU0 decrements a mailbox value until it is 0. + * introduces delay in PRU0 outputs! */ -#define TIMEOUT_INTERNAL_CYCLES 24 -#define TIMEOUT_SET(cycles) do { \ - PRU1_CTRL.CTRL_bit.CTR_EN = 0; \ - PRU1_CTRL.CYCLE = 0 ; \ - timeout_target = ((cycles) > TIMEOUT_INTERNAL_CYCLES) ? ((cycles) - TIMEOUT_INTERNAL_CYCLES) : 0 ; /* 4 cycle used in TIMEOUT_REACHED */ \ - PRU1_CTRL.CTRL_bit.CTR_EN = 1; \ -} while(0) - -#define TIMEOUT_REACHED \ - ( (PRU1_CTRL.CYCLE >= timeout_target)) - // set PRU1_12 to 0 or 1 -#define DEBUG_PIN_SET(val) ( (val) ? (__R30 |= (1 << 12) ) : (__R30 &= ~(1<< 12)) ) +#define PRU_DEBUG_PIN(val) ( (val) ? (__R30 |= (1 << 12) ) : (__R30 &= ~(1<< 12)) ) -// 20ns pulse an PRU1_12 -#define DEBUG_PIN_PULSE do { \ - __R30 |= (1 << 12) ; \ - __delay_cycles(4-1) ; \ - __R30 &= ~(1 << 12) ; \ - } while(0) - - -#define DEBUG_PIN_PULSE_100NS do { \ +// 100ns pulse an PRU1_12 +#define PRU_DEBUG_PIN_PULSE_100NS do { \ __R30 |= (1 << 12) ; \ __delay_cycles(18) ; \ __R30 &= ~(1 << 12) ; \ @@ -129,8 +112,6 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru } while(0) #endif - - // To signal the host that we're done, we set bit 5 in our R31 // simultaneously with putting the number of the signal we want // into R31 bits 0-3. See 5.2.2.2 in AM335x PRU-ICSS Reference Guide. @@ -142,13 +123,6 @@ http://theembeddedkitchen.net/beaglelogic-building-a-logic-analyzer-with-the-pru #define PRU2ARM_INTERRUPT do { \ /* is Interrupt "level" or "edge"??? */ \ __R31 = PRU2ARM_INTERRUPT_PRU0_R31_VEC_VALID |PRU2ARM_INTERRUPT_SIGNUM ; /* 35 */ \ -DEBUG_PIN_PULSE ; \ } while(0) - -#ifndef _PRU1_UTILS_C_ -extern uint32_t timeout_target ; -#endif - - #endif diff --git a/10.01_base/2_src/shared/ddrmem.h b/10.01_base/2_src/shared/ddrmem.h index e4a79e8..ff9edb3 100644 --- a/10.01_base/2_src/shared/ddrmem.h +++ b/10.01_base/2_src/shared/ddrmem.h @@ -38,26 +38,25 @@ public: /* these values are generated by prussdrv functions */ // base address of shared DDR memory, in ARM Linux memory space volatile ddrmem_t *base_virtual; - uint32_t len;// size of allocated range as given by UIO driver + uint32_t len; // size of allocated range as given by UIO driver // physical ddrmem_base address, for access by PRU uint32_t base_physical; // emulated address range - bool enabled = false ; // true if startaddr <= endaddr + bool enabled = false; // true if startaddr <= endaddr uint32_t unibus_startaddr; uint32_t unibus_endaddr; - ddrmem_c() ; + ddrmem_c(); void info(void); void save(char *fname); void load(char *fname); void clear(void); void fill_pattern(void); void fill_pattern_pru(void); - void unibus_slave(uint32_t startaddr, uint32_t endaddr); - bool set_range(uint32_t startaddr, uint32_t endaddr); - bool deposit(uint32_t addr, uint16_t w); - bool exam(uint32_t addr, uint16_t *w); + void unibus_slave(uint32_t startaddr, uint32_t endaddr);bool set_range(uint32_t startaddr, + uint32_t endaddr);bool deposit(uint32_t addr, uint16_t w);bool exam(uint32_t addr, + uint16_t *w); }; #ifndef _DDRMEM_C_ @@ -78,7 +77,6 @@ extern ddrmem_c *ddrmem; #define DDRMEM_MEMGET_W(addr) \ ( mailbox.ddrmem_base_physical->memory.words[(addr)/2] ) - void ddrmem_fill_pattern(void); #endif diff --git a/10.01_base/2_src/shared/iopageregister.h b/10.01_base/2_src/shared/iopageregister.h index 3677aec..b504267 100644 --- a/10.01_base/2_src/shared/iopageregister.h +++ b/10.01_base/2_src/shared/iopageregister.h @@ -1,28 +1,28 @@ - /* iopageregister.h: Tables of implemented devices and their registers. - Datastructures common to ARM and PRU. +/* iopageregister.h: Tables of implemented devices and their registers. + Datastructures common to ARM and PRU. - 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 Implementation of UNIBUS devices: @@ -116,7 +116,6 @@ // #define MAX_REGISTERS_PER_DEVICE 32 // RK611 has the most? #define MAX_DEVICE_HANDLE 255 // 0 not used, must fit in 8bits - // Bitmask: Create event fpr iopagergister DATI/DATO access ? #define IOPAGEREGISTER_EVENT_FLAG_DATI 0x01 #define IOPAGEREGISTER_EVENT_FLAG_DATO 0x02 diff --git a/10.01_base/2_src/shared/mailbox.h b/10.01_base/2_src/shared/mailbox.h index 893f031..aa0fa9d 100644 --- a/10.01_base/2_src/shared/mailbox.h +++ b/10.01_base/2_src/shared/mailbox.h @@ -1,29 +1,28 @@ /* mailbox.h: Command and status data structures common to ARM and PRU - 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 _MAILBOX_H_ #define _MAILBOX_H_ @@ -32,7 +31,7 @@ #include "unibus.h" // arm to pru -#define ARM2PRU_NONE 0 // Operation complete: don't change +#define ARM2PRU_NONE 0 // Operation complete: must be 0! #define ARM2PRU_NOP 1 // to check wether PRU is running #define ARM2PRU_HALT 2 // run PRU1 into halt #define ARM2PRU_MAILBOXTEST1 3 @@ -43,12 +42,14 @@ #define ARM2PRU_BUSLATCH_TEST 8 // read a mux register #define ARM2PRU_INITPULSE 9 // pulse UNIBUS INIT #define ARM2PRU_POWERCYCLE 10 // ACLO/DCLO power cycle simulation -#define ARM2PRU_DMA_ARB_NONE 11 // DMA without NPR/NPG/SACK arbitration -#define ARM2PRU_DMA_ARB_CLIENT 12 // DMA with arbitration by external Arbitrator -#define ARM2PRU_DMA_ARB_MASTER 13 // DMA as Arbitrator -#define ARM2PRU_DDR_FILL_PATTERN 14 // fill DDR with test pattern -#define ARM2PRU_DDR_SLAVE_MEMORY 15 // use DDR as UNIBUS slave memory -#define ARM2PRU_INTR 16 // INTR, only with arbitration +#define ARM2PRU_ARB_MODE_NONE 11 // DMA without NPR/NPG/SACK arbitration +#define ARM2PRU_ARB_MODE_CLIENT 12 // DMA with arbitration by external Arbitrator +#define ARM2PRU_ARB_MODE_MASTER 13 // DMA as Arbitrator +#define ARM2PRU_DMA 14 // DMA with selcted arbitration +#define ARM2PRU_INTR 15 // INTR with arbitration by external Arbitrator +#define ARM2PRU_INTR_CANCEL 16 // clear INTR which has been requested +#define ARM2PRU_DDR_FILL_PATTERN 17 // fill DDR with test pattern +#define ARM2PRU_DDR_SLAVE_MEMORY 18 // use DDR as UNIBUS slave memory // possible states of DMA machine #define DMA_STATE_READY 0 // idle @@ -58,12 +59,17 @@ #define DMA_STATE_INITSTOP 4 // stop because INIT signal sensed // bits BR*/NPR interrupts in buslatch 0 and 1 -#define ARBITRATION_PRIORITY_BIT_B4 0x01 -#define ARBITRATION_PRIORITY_BIT_B5 0x02 -#define ARBITRATION_PRIORITY_BIT_B6 0x04 -#define ARBITRATION_PRIORITY_BIT_B7 0x08 -#define ARBITRATION_PRIORITY_BIT_NP 0x10 -#define ARBITRATION_PRIORITY_MASK 0x1f +// bit # is index into arbitration_request[] array. +#define PRIORITY_ARBITRATION_BIT_B4 0x01 +#define PRIORITY_ARBITRATION_BIT_B5 0x02 +#define PRIORITY_ARBITRATION_BIT_B6 0x04 +#define PRIORITY_ARBITRATION_BIT_B7 0x08 +#define PRIORITY_ARBITRATION_BIT_NP 0x10 +#define PRIORITY_ARBITRATION_INTR_MASK 0x0f // BR4|BR5|BR6|BR7 +#define PRIORITY_ARBITRATION_BIT_MASK 0x1f + +// data for a requested DMA operation +#define PRU_MAX_DMA_WORDCOUNT 512 #include "ddrmem.h" @@ -87,13 +93,12 @@ typedef struct { #define MAILBOX_BUSLATCH_EXERCISER_PATTERN_COUNT 4 typedef struct { - uint8_t pattern ; // input: which access pattern? - uint8_t addr[8] ; // access sequence of register addresses - uint8_t writeval[8] ; // data value for each - uint8_t readval[8] ; // read back results + uint8_t pattern; // input: which access pattern? + uint8_t addr[8]; // access sequence of register addresses + uint8_t writeval[8]; // data value for each + uint8_t readval[8]; // read back results } mailbox_buslatch_exerciser_t; - typedef struct { uint8_t addr_0_7; // start values for test sequence uint8_t addr_8_15; @@ -102,27 +107,35 @@ typedef struct { } mailbox_buslatch_test_t; // data for a requested DMA operation -#define MAX_DMA_WORDCOUNT 512 typedef struct { // take care of 32 bit word borders for struct members uint8_t cur_status; // 0 = idle, 1 = running, 2 = timeout error uint8_t control; // cycle to perform: only DATO, DATI allowed uint16_t wordcount; // # of remaining words transmit/receive, static - uint32_t cur_addr; // current address in tranwfer, if timeout: offending address. + // ---dword--- + uint32_t cur_addr; // current address in transfer, if timeout: offending address. + // if complete: last address accessed. uint32_t startaddr; // address of 1st word to transfer - uint16_t words[MAX_DMA_WORDCOUNT]; // buffer for rcv/xmt data + uint16_t words[PRU_MAX_DMA_WORDCOUNT]; // buffer for rcv/xmt data } mailbox_dma_t; +// data for all 4 pending INTR requests // vector for an INTR transaction typedef struct { - uint16_t vector; // interrupt vector to be transferred - uint8_t priority_bit; // one of ARBITRATION_PRIORITY_B[4-7] -} mailbox_intr_t; + /* all requested INTRs */ + uint16_t vector[4]; // interrupt vectors for BR4..7 to be transferred + // ---dword--- -// event triggered on UNIBUS access to "active" device registers -// set by PRU, read by ARM on event. Bitmask. -#define EVENT_DEVICEREGISTER 0x01 -#define EVENT_INITIALIZATIONSIGNALS 0x02 + /* data for currently requested with ARM2PRU_INTR */ + uint8_t priority_arbitration_bit; // PRIORITY_ARBITRATION_BIT_* + uint8_t level_index; // newly requested BR*. 0 = BR4, ... 3 = BR7 + // interrupt register state to be set atomically with BR line + uint16_t iopage_register_value; + // ---dword--- + uint8_t iopage_register_handle; + uint8_t _dummy1, _dummy2, _dummy3; + // multiple of 32 bit now +} mailbox_intr_t; // states of initialization section lines. Bitmask = latch[7] #define INITIALIZATIONSIGNAL_INIT (1 << 3) @@ -130,20 +143,47 @@ typedef struct { #define INITIALIZATIONSIGNAL_DCLO (1 << 5) typedef struct { - uint8_t eventmask; // bitwise. triggered, 0 = invalid/ACKEed by ARM - /*** EVENT_DEVICEREGISTER ***/ + // trigger flags raised by PRU, reset by ARM + // differemt events can be raised asynchronically and concurrent, + // but a single event type is sequentially raised by PRU and cleared by ARM. + + /* Access to device register ***/ + uint8_t event_deviceregister; // trigger flag // info about register access uint8_t unibus_control; // DATI,DATO,DATOB // handle of controller uint8_t device_handle; // # of register in device space uint8_t device_register_idx; + // ---dword--- // UNIBUS address accessed uint32_t addr; // accessed address: odd/even important for DATOB - uint16_t data ; // data vale for DATO event + // ---dword--- + uint16_t data; // data value for DATO event - /*** EVENT_INITIALIZATIONSIGNALS ***/ + /*** DMA transfer complete + After ARM2PRU_DMA_*, NPR/NPG/SACK protocll was executed and + Data trasnfered accoring to mailbox_dma_t. + After that, mailbox_dma_t is updated and signal raised. + */ + uint8_t event_dma; // trigger flag + + /*** Event priority arbitration data transfer complete + After ARM2PRU_INTR, one of BR4/5/6/7 NP was requested, + granted, and the data transfer was handled as bus master. + */ + uint8_t _dummy1; + // ---dword--- + uint8_t event_intr; // trigger flag: 1 = one of BR4,5,6,7 vector on UNIBUS + uint8_t event_intr_level_index; // 0..3 -> BR4..BR7 + uint8_t _dummy2, _dummy3; + // ---dword--- + + /*** INIT or Power cycle seen on UNIBUS ***/ + uint8_t event_init; // trigger flag + uint8_t event_power; // trigger flag uint8_t initialization_signals_prev; // on event: a signal changed from this ... + // ---dword--- uint8_t initialization_signals_cur; // ... to this // uint8_t dummy[2]; // make record multiple of dword !!! @@ -161,14 +201,16 @@ typedef struct { // set by PRU, read by ARM on event mailbox_events_t events; + mailbox_intr_t intr; + + mailbox_dma_t dma; + // data structs for misc. opcodes union { mailbox_test_t mailbox_test; mailbox_buslatch_t buslatch; mailbox_buslatch_test_t buslatch_test; - mailbox_buslatch_exerciser_t buslatch_exerciser; - mailbox_dma_t dma; - mailbox_intr_t intr; + mailbox_buslatch_exerciser_t buslatch_exerciser; }; } mailbox_t; @@ -188,7 +230,7 @@ extern volatile mailbox_t *mailbox; void mailbox_print(void); int mailbox_connect(void); void mailbox_test1(void); -void mailbox_execute(uint8_t request, uint8_t stopcode); +void mailbox_execute(uint8_t request); #else // included by PRU code @@ -204,8 +246,9 @@ extern volatile far mailbox_t mailbox; mailbox.events.device_handle = _reg->event_device_handle ;\ mailbox.events.device_register_idx = _reg->event_device_register_idx ; \ mailbox.events.addr = _addr ; \ - mailbox.events.data = _data ; \ - mailbox.events.eventmask |= EVENT_DEVICEREGISTER ; /* data for ARM valid now*/ \ + mailbox.events.data = _data ; \ + mailbox.events.event_deviceregister = 1 ; \ + /* data for ARM valid now*/ \ PRU2ARM_INTERRUPT ; \ /* leave SSYN asserted until mailbox.event.signal ACKEd to 0 */ \ } while(0) diff --git a/10.01_base/2_src/shared/pru_pru_mailbox.h b/10.01_base/2_src/shared/pru_pru_mailbox.h index 5073dff..3940a66 100644 --- a/10.01_base/2_src/shared/pru_pru_mailbox.h +++ b/10.01_base/2_src/shared/pru_pru_mailbox.h @@ -1,28 +1,28 @@ /* pru_pru_mailbox.h: structure for data exchange between PRU0 and PRU1 - 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 _PRU_PRU_MAILBOX_H_ #define _PRU_PRU_MAILBOX_H_ @@ -30,16 +30,15 @@ #include typedef struct { - // just the state of PRU0 R30 output. + // just the state of PRU0 R30 output. // PRU1 writes the value // PRU0 monitors and copies to its local R30 = REG_DATA_OUT pins - uint32_t xxx_pru0_r30 ; - -} pru_pru_mailbox_t ; + uint32_t xxx_pru0_r30; +} pru_pru_mailbox_t; #ifndef _PRU_PRU_MAILBOX_C_ -extern volatile pru_pru_mailbox_t pru_pru_mailbox ; +extern volatile pru_pru_mailbox_t pru_pru_mailbox; #endif #endif diff --git a/10.01_base/2_src/shared/tuning.h b/10.01_base/2_src/shared/tuning.h index a0dca7e..d677d7f 100644 --- a/10.01_base/2_src/shared/tuning.h +++ b/10.01_base/2_src/shared/tuning.h @@ -1,30 +1,28 @@ /* tuning.h: Constants to adapt UNIBUS functions - Copyright (c) 2019, Joerg Hoppe - j_hoppe@t-online.de, www.retrocmp.com + Copyright (c) 2019, 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. - - - 7-jun-2019 JH entered beta phase -*/ + 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. + 7-jun-2019 JH entered beta phase + */ #define TUNING_PCB_LEGACY_SECURE //#define TUNING_PCB_2018_12_OPTIMIZED @@ -34,50 +32,47 @@ // A BBB with optimized terminators can reach 8 // BBG can reach *ALMOST* 9 // #define BUSLATCHES_GETBYTE_DELAY 10 // Standard - #if defined(TUNING_PCB_TEST) - // experimental to test error rates - #define BUSLATCHES_GETBYTE_DELAY 10 - #define BUSLATCHES_SETBITS_DELAY 4 - #define BUSLATCHES_SETBYTE_DELAY 6 +// experimental to test error rates +#define BUSLATCHES_GETBYTE_DELAY 10 +#define BUSLATCHES_SETBITS_DELAY 4 +#define BUSLATCHES_SETBYTE_DELAY 6 #elif defined(TUNING_PCB_LEGACY_SECURE) - /* Secure setting for PCBs <= 2018-12, delivered before June 2019. - Necessary for longtime ZKMA on critical PCBs. - BeagleBone: BBB (no BBG) - U2 (REGSEL): 74AC138 - RN8,9 (DATIN) : 47 - RN10 <1:6>(REGADR): 33 - RN10 <7:8>(REGWRITE): 33 - R6,R7 (REGWRITE TERM): none - RN6,RN7 (DATOUT inline): 22 - RN4,RN5 [[/DATOUT]] end) -> 1K/- - */ - #define BUSLATCHES_GETBYTE_DELAY 11 - #define BUSLATCHES_SETBITS_DELAY 5 - #define BUSLATCHES_SETBYTE_DELAY 7 +/* Secure setting for PCBs <= 2018-12, delivered before June 2019. + Necessary for longtime ZKMA on critical PCBs. + BeagleBone: BBB (no BBG) + U2 (REGSEL): 74AC138 + RN8,9 (DATIN) : 47 + RN10 <1:6>(REGADR): 33 + RN10 <7:8>(REGWRITE): 33 + R6,R7 (REGWRITE TERM): none + RN6,RN7 (DATOUT inline): 22 + RN4,RN5 [[/DATOUT]] end) -> 1K/- + */ +#define BUSLATCHES_GETBYTE_DELAY 11 +#define BUSLATCHES_SETBITS_DELAY 5 +#define BUSLATCHES_SETBYTE_DELAY 7 #elif defined(TUNING_PCB_2018_12_OPTIMIZED) - /* Setting for PCB v2018_12 with optimized timing (ticket 21, June 2019) - BeagleBone: BBB (no BBG) - U2 (REGSEL): 74AC138 -> 74AHC138 - RN8,9 (DATIN) : 47 -> 68 Ohm - RN10 <1:6>(REGADR): 33->0 Ohm - RN10 <7:8>(REGWRITE): 33->0 Ohm - R6,R7 (REGWRITE TERM): none - RN6,RN7 (DATOUT inline): 22 -> 27 - RN4,RN5 [[/DATOUT]] end) -> 180/- - */ - #define BUSLATCHES_GETBYTE_DELAY 9 - #define BUSLATCHES_SETBITS_DELAY 4 - #define BUSLATCHES_SETBYTE_DELAY 6 - //#define BUSLATCHES_GETBYTE_DELAY 8 - //#define BUSLATCHES_SETBITS_DELAY 3 - //#define BUSLATCHES_SETBYTE_DELAY 5 +/* Setting for PCB v2018_12 with optimized timing (ticket 21, June 2019) + BeagleBone: BBB (no BBG) + U2 (REGSEL): 74AC138 -> 74AHC138 + RN8,9 (DATIN) : 47 -> 68 Ohm + RN10 <1:6>(REGADR): 33->0 Ohm + RN10 <7:8>(REGWRITE): 33->0 Ohm + R6,R7 (REGWRITE TERM): none + RN6,RN7 (DATOUT inline): 22 -> 27 + RN4,RN5 [[/DATOUT]] end) -> 180/- + */ +#define BUSLATCHES_GETBYTE_DELAY 9 +#define BUSLATCHES_SETBITS_DELAY 4 +#define BUSLATCHES_SETBYTE_DELAY 6 +//#define BUSLATCHES_GETBYTE_DELAY 8 +//#define BUSLATCHES_SETBITS_DELAY 3 +//#define BUSLATCHES_SETBYTE_DELAY 5 #endif - - // UNIBUS timing: Wait to stabilize DATA before MSYN asserted // per DEC spec // #define UNIBUS_DMA_MASTER_PRE_MSYN_NS 150 diff --git a/10.01_base/2_src/shared/unibus.h b/10.01_base/2_src/shared/unibus.h index b3546d1..bfdcece 100644 --- a/10.01_base/2_src/shared/unibus.h +++ b/10.01_base/2_src/shared/unibus.h @@ -1,31 +1,30 @@ /* unibus.h: shared structs used in PRU and ARM - 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 - 19-may-2018 JH created - -*/ + 12-nov-2018 JH entered beta phase + 19-may-2018 JH created + */ #ifndef _UNIBUS_H_ #define _UNIBUS_H_ @@ -34,13 +33,12 @@ #define UNIBUS_WORDCOUNT 0x20000 // 128KiW = 256 KiB - // bus transaction. can be directly assigned to lines C1,C0 #define UNIBUS_CONTROL_DATI 0x00 // 16 bit word from slave to master #define UNIBUS_CONTROL_DATIP 0x01 // DATI, inhibts core restore. DATO must follow. #define UNIBUS_CONTROL_DATO 0x02 // 16 bit word from master to slave #define UNIBUS_CONTROL_DATOB 0x03 // 8 bit byte from master to slave - // data<15:8> for a00 = 1, data<7:0> for a00 = 0 +// data<15:8> for a00 = 1, data<7:0> for a00 = 0 #define UNIBUS_CONTROL_ISDATO(c) (!!((c) & 0x02)) // check for DATO/B #define UNIBUS_TIMEOUTVAL 0xffffffff // EXAM result for bus timeout @@ -51,27 +49,29 @@ // pru c-compile is NOT called with --endian=big typedef struct { union { - uint16_t words[UNIBUS_WORDCOUNT] ; - uint8_t bytes[2*UNIBUS_WORDCOUNT] ; - } ; + uint16_t words[UNIBUS_WORDCOUNT]; + uint8_t bytes[2 * UNIBUS_WORDCOUNT]; + }; } unibus_memory_t; - - #ifdef ARM // included only by ARM code #include "logsource.hpp" #include "utils.hpp" // parameter and functions for low level UNIBUS control + +class dma_request_c; +class intr_request_c; + class unibus_c: public logsource_c { public: -enum arbitration_mode_enum { -ARBITRATION_MODE_NONE = 0, // no BR*/BG*, NR/NPG SACK protocoll -ARBITRATION_MODE_CLIENT = 1, // external Arbitrator (running PDP-11 CPU) required -ARBITRATION_MODE_MASTER = 2 // implmenet Arbitrator -// with or without physical CPU for arbitration -} ; + enum arbitration_mode_enum { + ARBITRATION_MODE_NONE = 0, // no BR*/BG*, NR/NPG SACK protocoll + ARBITRATION_MODE_CLIENT = 1, // external Arbitrator (running PDP-11 CPU) required + ARBITRATION_MODE_MASTER = 2 // implmenet Arbitrator + // with or without physical CPU for arbitration + }; private: @@ -79,40 +79,43 @@ private: public: - // percent of time to be used for DMA master cycles - unsigned dma_bandwidth_percent ; - // default size of dma block transfers - unsigned dma_wordcount ; + unibus_c(); + ~unibus_c(); - unibus_c() ; - - static char *control2text(uint8_t control) ; + static char *control2text(uint8_t control); static char *data2text(unsigned val); void init(void); - void powercycle(void) ; + static void set_arbitration_mode(enum arbitration_mode_enum arbitration_mode); - void interrupt(uint8_t priority, uint16_t vector) ; - bool dma(enum unibus_c::arbitration_mode_enum arbitration_mode, uint8_t control, uint32_t startaddr, - unsigned blocksize); + void powercycle(void); - void mem_read(enum unibus_c::arbitration_mode_enum arbitration_mode, - uint16_t *words, uint32_t start_addr, - uint32_t end_addr, unsigned blocksize, bool *timeout) ; - void mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, - uint16_t *words, unsigned start_addr, - unsigned end_addr, unsigned blocksize, bool *timeout) ; + // functions of unibusadapter to do simple DMA + dma_request_c *dma_request; + //intr_request_c *intr_request; - uint32_t test_sizer(enum unibus_c::arbitration_mode_enum arbitration_mode) ; + bool dma(enum unibus_c::arbitration_mode_enum arbitration_mode, bool blocking, uint8_t control, + uint32_t startaddr, uint16_t *buffer, unsigned wordcount); + + void mem_read(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words, + uint32_t unibus_start_addr, uint32_t unibus_end_addr, bool *timeout); + void mem_write(enum unibus_c::arbitration_mode_enum arbitration_mode, uint16_t *words, + unsigned unibus_start_addr, unsigned unibus_end_addr, bool *timeout); + + void mem_access_random(enum unibus_c::arbitration_mode_enum arbitration_mode, + uint8_t unibus_control, uint16_t *words, uint32_t unibus_start_addr, + uint32_t unibus_end_addr, bool *timeout, uint32_t *block_counter); + + uint32_t test_sizer(enum unibus_c::arbitration_mode_enum arbitration_mode); uint16_t testwords[UNIBUS_WORDCOUNT]; - void test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, uint32_t start_addr, uint32_t end_addr, unsigned mode) ; + void test_mem(enum unibus_c::arbitration_mode_enum arbitration_mode, uint32_t start_addr, + uint32_t end_addr, unsigned mode); }; - -extern unibus_c *unibus ; +extern unibus_c *unibus; #endif /* ARM */ diff --git a/10.01_base/3_test/aaatestplan.txt b/10.01_base/3_test/aaatestplan.txt deleted file mode 100644 index 8cb93fb..0000000 --- a/10.01_base/3_test/aaatestplan.txt +++ /dev/null @@ -1,96 +0,0 @@ -Unibone testplan - -Platform: PDP11/34. - -Plug UniBone in Slot with NPR/NPG unconnected (CA1 =!= CA2) - -1. Slave memory -UniBone implements a memory board. - -a) Slave cycles -implement 256Kb 000000-777776 memory. -Boot XXDP2.5, run ZKMA?? with SW= 010000 --> 256KB tested - -b) memory layout -Assert memors layout udner ARM = memory layout for PDP-11 -load "Papertape Basic" image into DDR RAM -start at 16104 -> BASCI prompt? - -c) Address logic -Add 32KB MS11-J board M7847 (00000-077776) -UniBone implements 100000-777776 memory. -Boot XXDP2.5, run ZKMA?? with SW= 010000 --> 256KB tested - - - -2. Bus Master only without arbitration -UniBone performs bus master DMA cycles. -No slave memory emulation, no NPR/G/SACK arbitration. -- add PDP-11 memory board -- Halt CPU (will crash if Unibone generates Bus cycles) - -a) single cycle: Set Switchreg to 123456 -Unibone: EXAM 777570 ->12345 -EXAM next -> Bustimeout - -b) perform memory test on PDP-11 memory - -3. Bus Master with feed back to internal salve, without arbitration -If master cycles hit addresses for emualted memory, slave logic anwsers master cycles, -This could be an internal operation, but UniBus signals MSYN/SSYN/Control/DATA/ADDtr are generated - -- add 32KB board. Emulates slave memory 100000-777776. - -a) UniBone runs memory test -Watch UNIBUS signals: access to real mmeoryy at 00000-077776 should look like itnernal -access to 100000-777776. - - -4.) Bus master DMA with arbitration -Like 3., but NPR/NPG/SACK arbitration is performed -- plug UniBone in 11/34 between KX11 console and RL11 controller. -Card order: CPU - KY11 - UniBone - RL11 -- Confirm that GRANT Chain (CA1/CB1) is open in UniBone backplane slot -- remove jumper between NPG_IN-NPG_OUT -- start Unibone emulation, - -a) Boot from RL11. RL11 performs DMA, this works only if UniBone -"connects" NPG_IN to NPG_OUT actively - - -b) PDP11GUI memory test on lower 56kb 000000-157776 -Perform Busmaster memory test per DMA parallel in upper memory- 200000..757776 -"tr 200000 777777" --> lots of NPG transaction in parallel with regualr operation - -c) Same as b), but memory test with XXDP ZKMA on lower 56kb 000000-177776 (SR=000000) -- load zkmaf0_200 into memory -- on M9312 console: -@L 200 -@G - - - -d) NPR priority: 11/34 KY Programmer console: -HALT -> KY11 is busmaster and holds SACK -UniBone Exam -> seems to hang, as KY11 holds SACK -KY11: CONT -> start CPU, release SACK -> UniBone EXAM gets completed - - -5) Interrupts -Use "ti" test option and "intrtst" PDP-11 Programm. -Issue interrupts at differnt levels, -combine with different processor pririoty levels. - - - - - - - - - - - diff --git a/10.01_base/3_test/intrtst.cmd b/10.01_base/3_test/intrtst.cmd new file mode 100644 index 0000000..4e3bc79 --- /dev/null +++ b/10.01_base/3_test/intrtst.cmd @@ -0,0 +1,185 @@ +# "demo" test sequences for basic interrupt test +# +# Testprogram "intrtst" evaluates chars from DL11, +# but input can be given by deposit char to 7776. + +ti # enter menu +pwr # restart PDP-11 +.wait 1000 +m # emulate missing memory +ll intrtst.lst # load PDP-11 test program + +.print Now start Program on PDP-11. +.print On M9312: "L " , "S" +.input + +.print First all tests with looong blocking ISRs in "Slow" mode +d 7776 'S + +.print ************************************************************************* +.print *** Test 1: single INTR +.print 4 Interrupts raised in order +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +d 7776 '0 +.wait 1000 +i 4 40 +.wait 250 +i 5 50 +.wait 250 +i 6 60 +.wait 250 +i 7 70 +.wait 250 +.print PDP-11 should respond "" "" "" "" +.input + +.print ************************************************************************* +.print *** Test 2: INTR Priority +.print CPU INTR disabled, 4 Interrupts raised and not processed +.print +.print Set CPU level to 7 (all levels BR4..BR7 disabled) +d 7776 '7 +.wait 1000 +i 4 40 +i 5 50 +i 6 60 +i 7 70 +.print PDP-11 should raise no ISRs. +.print Set CPU level to 6, PDP-11 should respond "ISR 70" +d 7776 '6 +.wait 1000 + +.print Set CPU level to 0 +d 7776 '0 +.print PDP-11 should respond "ISR 60" "ISR 50" "ISR 40" +.input + + +.print ************************************************************************* +.print *** Test 3: raise INTR while others are processed +.print The printout "ISR " needs some time and is done on CPU priority level 7 +.print (with INTR disabled). In that time other ISR are requested +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +d 7776 '0 +.wait 1000 +i 4 40 +i 6 60 +i 5 50 +i 7 70 +.print PDP-11 should respond "ISR 40" "ISR 70" "ISR 60" "ISR 50" +.input + + +.print ************************************************************************* +.print *** Test 4: raise INTR multiple +.print The printout "ISR " needs some time and is done on CPU priority level 7 +.print (with INTR disabled). In that time INTR are re-raised again. +.print This is a "CPU interrupt overload" scenario, ISRs are not called as often +.print as required. +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +d 7776 '0 +.wait 1000 +i 4 40 # accepted, ISR called blocking +i 7 70 # ISR 70 scheduled +i 7 70 # re-raise ignored +i 7 70 # re-raise ignored +i 4 40 # ISR 40 scheduled +i 4 40 # re-raise ignored +i 7 70 # re-raise ignored +i 4 40 # re-raise ignored +i 4 40 # re-raise ignored +i 4 40 # re-raise ignored +# end ISR 40: call ISR 70 (highest prio), then ISR 40- +.print PDP-11 should call only two ISRs:"ISR 40", "ISR 70" "ISR 40" +.input + + +.print ************************************************************************* +.print *** Test 5: INTR Slot priority +.print Test: raise INTR while others are processed +.print The printout "ISR " needs some time and is done on CPU priority level 7 +.print (with INTR) disabled. In that time other ISR are requested and waiting. +.print Set CPU level to 7 (all levels BR4..BR7 disabled) +d 7776 '7 +.wait 1000 +# i +i 4 1 110 +i 4 2 120 +i 5 10 70 +i 5 11 100 +i 6 20 50 +i 6 21 60 +i 7 14 40 # is first (waiting on PRU, highest prio), not sorted as not blocked +i 7 12 20 +i 7 11 10 +i 7 13 30 +.print Set CPU level to 0 +d 7776 '0 +.print PDP-11 should process ISRs sorted first by level, then by slot:"ISR 40, 10, 20, ... 120" +.input + +.print *** Test 6: repeat INTR Slot priority, fast ISR +d 7776 'F +d 7776 '7 +.wait 1000 +# i +i 4 1 110 # is #4 +i 4 2 120 +i 5 10 70 # is #3 +i 5 11 100 +i 6 20 50 # is #2 (waiting on PRU, prio after 7) +i 6 21 60 +i 7 14 40 # is #1 (waiting on PRU, highest prio), not sorted as not blocked +i 7 12 20 +i 7 11 10 +i 7 13 30 +.print Set CPU level to 0 +d 7776 '0 +.print PDP-11 should process ISRs sorted first by level, then by slot:"ISR 40, 10, 20, ... 120" +d 7776 'F # display ISR log +.input + + +.print ************************************************************************* +.print *** Test 7: DMA priority +.print Start multi-chunk DMA "1" on less priorized slot, +.print then one "2" in priorized slot into same memory. +.print DMA "2" is interrupting DMA "1" on firstchunk of "1" + +# dma is non blocking and DEPOSITing +# dma +# channel #0 has higher slot prority than #1 + +dma 1 100000 757776 1 +dma 0 100000 757776 2 + +.wait 1000 # wait for DMAs to complete +.print Check: So first data words are "2" (DMA channel 1 runs 2nd). +e 100000 +.print Last data words are "1" (DMA channel 1 finished later and overwrote channel 0's data) +e 757776 +.input + + +d 7776 '0 # all INTR enabled +.wait 1000 +d 7776 'S # slow ISR is printing text +.wait 1000 + +.print ************************************************************************* +.print Test 8: DMA and INTR mixed +.print Set CPU level to 0 (all levels BR4..BR7 enabled) +.print Verify in logic analyzer, that DMA interupts +.print while ISR 70 is printing its text. +.print Trigger LA on 2nd SACK H->L +.input +i 7 70 +dma 0 100000 757776 123 # trigger on 2nd SACK +i 4 40 +.print Is Interrupt BR4 40 services after first DMA chunk or at the end (both OK!) + + + + + + diff --git a/10.01_base/3_test/intrtst.lst b/10.01_base/3_test/intrtst.lst new file mode 100644 index 0000000..7a75f89 --- /dev/null +++ b/10.01_base/3_test/intrtst.lst @@ -0,0 +1,1322 @@ + 1 + 2 .title INTR and DMA test + 3 + 4 ; This program tests the DEC DL11 console interface + 5 ; and the INTR and DMA systems. + 6 ; The foreground thread runs in 4 phases: + 7 ; 1.1. print a start message, + 8 ; 1.2. echoes chars typed to the output until ^C is hit + 9 ; Chars 0..7 set the new processor priority level. + 10 ; 1.3 prints an end message and HALTs. + 11 ; 1.4. on CONT it repeats. + 12 ; + 13 ; 2. For INTR test, the 256 vectors 0,4,10,14,..374 each print + 14 ; a string on interrupt. + 15 ; In "Slow" mode, the ISR prints the vector directly + 16 ; (ISR rountine > 100ms) + 17 ; In "Fast" mode, the ISR logs the vector in a list + 18 ; (It can be printed later with "F" + 19 ; + 20 ; As alternative to input over DL11, + 21 ; "keyboard input chars" can be deposited into 7776 + 22 ; + 23 ; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com + 24 + 25 177560 dladr = 177560 ; DL11 console base address + 26 177776 psw = 177776 ; processor status + 27 + 28 + 29 ; count of automatically generated interrupt vectors + 30 000100 veccnt = 100 + 31 + 32 ; ------- macro to define interrupt vector # ------ + 33 .macro vector vecidx + 34 .=4*vecidx ; vector #vecidx + 35 .word isr'vecidx ; new PC of ISR + 36 .word 340 ; new PSW: priority is max = 7 + 37 .endm + 38 + 39 ; ----- macro to define ISR for vector #n ------- + 40 .macro isr vecidx + 41 isr'vecidx: + 42 mov r0,-(sp) + 43 mov #vecidx*4,r0 ; vector in r0 + 44 call @#doisr ; print message for vector in r0 + 45 mov (sp)+,r0 + 46 rti + 47 .endm + 48 + 49 + 50 + 51 + 52 .asect + 53 + 54 000000 . = 0 + 55 ; ---- "veccnt" Interrupt Vectors --------- + 56 000000 n=0 + 57 .rept veccnt + 58 vector \n + 59 n=n+1 + 60 .endr + 1 vector \n + 1 000000 .=4*0 ; vector #0 + 2 000000 000400 .word isr0 ; new PC of ISR + 3 000002 000340 .word 340 ; new PSW: priority is max = 7 + 2 000001 n=n+1 + 1 vector \n + 1 000004 .=4*1 ; vector #1 + 2 000004 000416 .word isr1 ; new PC of ISR + 3 000006 000340 .word 340 ; new PSW: priority is max = 7 + 2 000002 n=n+1 + 1 vector \n + 1 000010 .=4*2 ; vector #2 + 2 000010 000434 .word isr2 ; new PC of ISR + 3 000012 000340 .word 340 ; new PSW: priority is max = 7 + 2 000003 n=n+1 + 1 vector \n + 1 000014 .=4*3 ; vector #3 + 2 000014 000452 .word isr3 ; new PC of ISR + 3 000016 000340 .word 340 ; new PSW: priority is max = 7 + 2 000004 n=n+1 + 1 vector \n + 1 000020 .=4*4 ; vector #4 + 2 000020 000470 .word isr4 ; new PC of ISR + 3 000022 000340 .word 340 ; new PSW: priority is max = 7 + 2 000005 n=n+1 + 1 vector \n + 1 000024 .=4*5 ; vector #5 + 2 000024 000506 .word isr5 ; new PC of ISR + 3 000026 000340 .word 340 ; new PSW: priority is max = 7 + 2 000006 n=n+1 + 1 vector \n + 1 000030 .=4*6 ; vector #6 + 2 000030 000524 .word isr6 ; new PC of ISR + 3 000032 000340 .word 340 ; new PSW: priority is max = 7 + 2 000007 n=n+1 + 1 vector \n + 1 000034 .=4*7 ; vector #7 + 2 000034 000542 .word isr7 ; new PC of ISR + 3 000036 000340 .word 340 ; new PSW: priority is max = 7 + 2 000010 n=n+1 + 1 vector \n + 1 000040 .=4*10 ; vector #10 + 2 000040 000560 .word isr10 ; new PC of ISR + 3 000042 000340 .word 340 ; new PSW: priority is max = 7 + 2 000011 n=n+1 + 1 vector \n + 1 000044 .=4*11 ; vector #11 + 2 000044 000576 .word isr11 ; new PC of ISR + 3 000046 000340 .word 340 ; new PSW: priority is max = 7 + 2 000012 n=n+1 + 1 vector \n + 1 000050 .=4*12 ; vector #12 + 2 000050 000614 .word isr12 ; new PC of ISR + 3 000052 000340 .word 340 ; new PSW: priority is max = 7 + 2 000013 n=n+1 + 1 vector \n + 1 000054 .=4*13 ; vector #13 + 2 000054 000632 .word isr13 ; new PC of ISR + 3 000056 000340 .word 340 ; new PSW: priority is max = 7 + 2 000014 n=n+1 + 1 vector \n + 1 000060 .=4*14 ; vector #14 + 2 000060 000650 .word isr14 ; new PC of ISR + 3 000062 000340 .word 340 ; new PSW: priority is max = 7 + 2 000015 n=n+1 + 1 vector \n + 1 000064 .=4*15 ; vector #15 + 2 000064 000666 .word isr15 ; new PC of ISR + 3 000066 000340 .word 340 ; new PSW: priority is max = 7 + 2 000016 n=n+1 + 1 vector \n + 1 000070 .=4*16 ; vector #16 + 2 000070 000704 .word isr16 ; new PC of ISR + 3 000072 000340 .word 340 ; new PSW: priority is max = 7 + 2 000017 n=n+1 + 1 vector \n + 1 000074 .=4*17 ; vector #17 + 2 000074 000722 .word isr17 ; new PC of ISR + 3 000076 000340 .word 340 ; new PSW: priority is max = 7 + 2 000020 n=n+1 + 1 vector \n + 1 000100 .=4*20 ; vector #20 + 2 000100 000740 .word isr20 ; new PC of ISR + 3 000102 000340 .word 340 ; new PSW: priority is max = 7 + 2 000021 n=n+1 + 1 vector \n + 1 000104 .=4*21 ; vector #21 + 2 000104 000756 .word isr21 ; new PC of ISR + 3 000106 000340 .word 340 ; new PSW: priority is max = 7 + 2 000022 n=n+1 + 1 vector \n + 1 000110 .=4*22 ; vector #22 + 2 000110 000774 .word isr22 ; new PC of ISR + 3 000112 000340 .word 340 ; new PSW: priority is max = 7 + 2 000023 n=n+1 + 1 vector \n + 1 000114 .=4*23 ; vector #23 + 2 000114 001012 .word isr23 ; new PC of ISR + 3 000116 000340 .word 340 ; new PSW: priority is max = 7 + 2 000024 n=n+1 + 1 vector \n + 1 000120 .=4*24 ; vector #24 + 2 000120 001030 .word isr24 ; new PC of ISR + 3 000122 000340 .word 340 ; new PSW: priority is max = 7 + 2 000025 n=n+1 + 1 vector \n + 1 000124 .=4*25 ; vector #25 + 2 000124 001046 .word isr25 ; new PC of ISR + 3 000126 000340 .word 340 ; new PSW: priority is max = 7 + 2 000026 n=n+1 + 1 vector \n + 1 000130 .=4*26 ; vector #26 + 2 000130 001064 .word isr26 ; new PC of ISR + 3 000132 000340 .word 340 ; new PSW: priority is max = 7 + 2 000027 n=n+1 + 1 vector \n + 1 000134 .=4*27 ; vector #27 + 2 000134 001102 .word isr27 ; new PC of ISR + 3 000136 000340 .word 340 ; new PSW: priority is max = 7 + 2 000030 n=n+1 + 1 vector \n + 1 000140 .=4*30 ; vector #30 + 2 000140 001120 .word isr30 ; new PC of ISR + 3 000142 000340 .word 340 ; new PSW: priority is max = 7 + 2 000031 n=n+1 + 1 vector \n + 1 000144 .=4*31 ; vector #31 + 2 000144 001136 .word isr31 ; new PC of ISR + 3 000146 000340 .word 340 ; new PSW: priority is max = 7 + 2 000032 n=n+1 + 1 vector \n + 1 000150 .=4*32 ; vector #32 + 2 000150 001154 .word isr32 ; new PC of ISR + 3 000152 000340 .word 340 ; new PSW: priority is max = 7 + 2 000033 n=n+1 + 1 vector \n + 1 000154 .=4*33 ; vector #33 + 2 000154 001172 .word isr33 ; new PC of ISR + 3 000156 000340 .word 340 ; new PSW: priority is max = 7 + 2 000034 n=n+1 + 1 vector \n + 1 000160 .=4*34 ; vector #34 + 2 000160 001210 .word isr34 ; new PC of ISR + 3 000162 000340 .word 340 ; new PSW: priority is max = 7 + 2 000035 n=n+1 + 1 vector \n + 1 000164 .=4*35 ; vector #35 + 2 000164 001226 .word isr35 ; new PC of ISR + 3 000166 000340 .word 340 ; new PSW: priority is max = 7 + 2 000036 n=n+1 + 1 vector \n + 1 000170 .=4*36 ; vector #36 + 2 000170 001244 .word isr36 ; new PC of ISR + 3 000172 000340 .word 340 ; new PSW: priority is max = 7 + 2 000037 n=n+1 + 1 vector \n + 1 000174 .=4*37 ; vector #37 + 2 000174 001262 .word isr37 ; new PC of ISR + 3 000176 000340 .word 340 ; new PSW: priority is max = 7 + 2 000040 n=n+1 + 1 vector \n + 1 000200 .=4*40 ; vector #40 + 2 000200 001300 .word isr40 ; new PC of ISR + 3 000202 000340 .word 340 ; new PSW: priority is max = 7 + 2 000041 n=n+1 + 1 vector \n + 1 000204 .=4*41 ; vector #41 + 2 000204 001316 .word isr41 ; new PC of ISR + 3 000206 000340 .word 340 ; new PSW: priority is max = 7 + 2 000042 n=n+1 + 1 vector \n + 1 000210 .=4*42 ; vector #42 + 2 000210 001334 .word isr42 ; new PC of ISR + 3 000212 000340 .word 340 ; new PSW: priority is max = 7 + 2 000043 n=n+1 + 1 vector \n + 1 000214 .=4*43 ; vector #43 + 2 000214 001352 .word isr43 ; new PC of ISR + 3 000216 000340 .word 340 ; new PSW: priority is max = 7 + 2 000044 n=n+1 + 1 vector \n + 1 000220 .=4*44 ; vector #44 + 2 000220 001370 .word isr44 ; new PC of ISR + 3 000222 000340 .word 340 ; new PSW: priority is max = 7 + 2 000045 n=n+1 + 1 vector \n + 1 000224 .=4*45 ; vector #45 + 2 000224 001406 .word isr45 ; new PC of ISR + 3 000226 000340 .word 340 ; new PSW: priority is max = 7 + 2 000046 n=n+1 + 1 vector \n + 1 000230 .=4*46 ; vector #46 + 2 000230 001424 .word isr46 ; new PC of ISR + 3 000232 000340 .word 340 ; new PSW: priority is max = 7 + 2 000047 n=n+1 + 1 vector \n + 1 000234 .=4*47 ; vector #47 + 2 000234 001442 .word isr47 ; new PC of ISR + 3 000236 000340 .word 340 ; new PSW: priority is max = 7 + 2 000050 n=n+1 + 1 vector \n + 1 000240 .=4*50 ; vector #50 + 2 000240 001460 .word isr50 ; new PC of ISR + 3 000242 000340 .word 340 ; new PSW: priority is max = 7 + 2 000051 n=n+1 + 1 vector \n + 1 000244 .=4*51 ; vector #51 + 2 000244 001476 .word isr51 ; new PC of ISR + 3 000246 000340 .word 340 ; new PSW: priority is max = 7 + 2 000052 n=n+1 + 1 vector \n + 1 000250 .=4*52 ; vector #52 + 2 000250 001514 .word isr52 ; new PC of ISR + 3 000252 000340 .word 340 ; new PSW: priority is max = 7 + 2 000053 n=n+1 + 1 vector \n + 1 000254 .=4*53 ; vector #53 + 2 000254 001532 .word isr53 ; new PC of ISR + 3 000256 000340 .word 340 ; new PSW: priority is max = 7 + 2 000054 n=n+1 + 1 vector \n + 1 000260 .=4*54 ; vector #54 + 2 000260 001550 .word isr54 ; new PC of ISR + 3 000262 000340 .word 340 ; new PSW: priority is max = 7 + 2 000055 n=n+1 + 1 vector \n + 1 000264 .=4*55 ; vector #55 + 2 000264 001566 .word isr55 ; new PC of ISR + 3 000266 000340 .word 340 ; new PSW: priority is max = 7 + 2 000056 n=n+1 + 1 vector \n + 1 000270 .=4*56 ; vector #56 + 2 000270 001604 .word isr56 ; new PC of ISR + 3 000272 000340 .word 340 ; new PSW: priority is max = 7 + 2 000057 n=n+1 + 1 vector \n + 1 000274 .=4*57 ; vector #57 + 2 000274 001622 .word isr57 ; new PC of ISR + 3 000276 000340 .word 340 ; new PSW: priority is max = 7 + 2 000060 n=n+1 + 1 vector \n + 1 000300 .=4*60 ; vector #60 + 2 000300 001640 .word isr60 ; new PC of ISR + 3 000302 000340 .word 340 ; new PSW: priority is max = 7 + 2 000061 n=n+1 + 1 vector \n + 1 000304 .=4*61 ; vector #61 + 2 000304 001656 .word isr61 ; new PC of ISR + 3 000306 000340 .word 340 ; new PSW: priority is max = 7 + 2 000062 n=n+1 + 1 vector \n + 1 000310 .=4*62 ; vector #62 + 2 000310 001674 .word isr62 ; new PC of ISR + 3 000312 000340 .word 340 ; new PSW: priority is max = 7 + 2 000063 n=n+1 + 1 vector \n + 1 000314 .=4*63 ; vector #63 + 2 000314 001712 .word isr63 ; new PC of ISR + 3 000316 000340 .word 340 ; new PSW: priority is max = 7 + 2 000064 n=n+1 + 1 vector \n + 1 000320 .=4*64 ; vector #64 + 2 000320 001730 .word isr64 ; new PC of ISR + 3 000322 000340 .word 340 ; new PSW: priority is max = 7 + 2 000065 n=n+1 + 1 vector \n + 1 000324 .=4*65 ; vector #65 + 2 000324 001746 .word isr65 ; new PC of ISR + 3 000326 000340 .word 340 ; new PSW: priority is max = 7 + 2 000066 n=n+1 + 1 vector \n + 1 000330 .=4*66 ; vector #66 + 2 000330 001764 .word isr66 ; new PC of ISR + 3 000332 000340 .word 340 ; new PSW: priority is max = 7 + 2 000067 n=n+1 + 1 vector \n + 1 000334 .=4*67 ; vector #67 + 2 000334 002002 .word isr67 ; new PC of ISR + 3 000336 000340 .word 340 ; new PSW: priority is max = 7 + 2 000070 n=n+1 + 1 vector \n + 1 000340 .=4*70 ; vector #70 + 2 000340 002020 .word isr70 ; new PC of ISR + 3 000342 000340 .word 340 ; new PSW: priority is max = 7 + 2 000071 n=n+1 + 1 vector \n + 1 000344 .=4*71 ; vector #71 + 2 000344 002036 .word isr71 ; new PC of ISR + 3 000346 000340 .word 340 ; new PSW: priority is max = 7 + 2 000072 n=n+1 + 1 vector \n + 1 000350 .=4*72 ; vector #72 + 2 000350 002054 .word isr72 ; new PC of ISR + 3 000352 000340 .word 340 ; new PSW: priority is max = 7 + 2 000073 n=n+1 + 1 vector \n + 1 000354 .=4*73 ; vector #73 + 2 000354 002072 .word isr73 ; new PC of ISR + 3 000356 000340 .word 340 ; new PSW: priority is max = 7 + 2 000074 n=n+1 + 1 vector \n + 1 000360 .=4*74 ; vector #74 + 2 000360 002110 .word isr74 ; new PC of ISR + 3 000362 000340 .word 340 ; new PSW: priority is max = 7 + 2 000075 n=n+1 + 1 vector \n + 1 000364 .=4*75 ; vector #75 + 2 000364 002126 .word isr75 ; new PC of ISR + 3 000366 000340 .word 340 ; new PSW: priority is max = 7 + 2 000076 n=n+1 + 1 vector \n + 1 000370 .=4*76 ; vector #76 + 2 000370 002144 .word isr76 ; new PC of ISR + 3 000372 000340 .word 340 ; new PSW: priority is max = 7 + 2 000077 n=n+1 + 1 vector \n + 1 000374 .=4*77 ; vector #77 + 2 000374 002162 .word isr77 ; new PC of ISR + 3 000376 000340 .word 340 ; new PSW: priority is max = 7 + 2 000100 n=n+1 + 61 + 62 ; ---- veccnt ISRs --------- + 63 000000 n=0 + 64 .rept veccnt + 65 isr \n + 66 n=n+1 + 67 .endr + 1 isr \n + 1 isr0: + 2 000400 010046 mov r0,-(sp) + 3 000402 012700 000000 mov #0*4,r0 ; vector in r0 + 4 000406 004737 010236 call @#doisr ; print message for vector in r0 + 5 000412 012600 mov (sp)+,r0 + 6 000414 000002 rti + 2 000001 n=n+1 + 1 isr \n + 1 isr1: + 2 000416 010046 mov r0,-(sp) + 3 000420 012700 000004 mov #1*4,r0 ; vector in r0 + 4 000424 004737 010236 call @#doisr ; print message for vector in r0 + 5 000430 012600 mov (sp)+,r0 + 6 000432 000002 rti + 2 000002 n=n+1 + 1 isr \n + 1 isr2: + 2 000434 010046 mov r0,-(sp) + 3 000436 012700 000010 mov #2*4,r0 ; vector in r0 + 4 000442 004737 010236 call @#doisr ; print message for vector in r0 + 5 000446 012600 mov (sp)+,r0 + 6 000450 000002 rti + 2 000003 n=n+1 + 1 isr \n + 1 isr3: + 2 000452 010046 mov r0,-(sp) + 3 000454 012700 000014 mov #3*4,r0 ; vector in r0 + 4 000460 004737 010236 call @#doisr ; print message for vector in r0 + 5 000464 012600 mov (sp)+,r0 + 6 000466 000002 rti + 2 000004 n=n+1 + 1 isr \n + 1 isr4: + 2 000470 010046 mov r0,-(sp) + 3 000472 012700 000020 mov #4*4,r0 ; vector in r0 + 4 000476 004737 010236 call @#doisr ; print message for vector in r0 + 5 000502 012600 mov (sp)+,r0 + 6 000504 000002 rti + 2 000005 n=n+1 + 1 isr \n + 1 isr5: + 2 000506 010046 mov r0,-(sp) + 3 000510 012700 000024 mov #5*4,r0 ; vector in r0 + 4 000514 004737 010236 call @#doisr ; print message for vector in r0 + 5 000520 012600 mov (sp)+,r0 + 6 000522 000002 rti + 2 000006 n=n+1 + 1 isr \n + 1 isr6: + 2 000524 010046 mov r0,-(sp) + 3 000526 012700 000030 mov #6*4,r0 ; vector in r0 + 4 000532 004737 010236 call @#doisr ; print message for vector in r0 + 5 000536 012600 mov (sp)+,r0 + 6 000540 000002 rti + 2 000007 n=n+1 + 1 isr \n + 1 isr7: + 2 000542 010046 mov r0,-(sp) + 3 000544 012700 000034 mov #7*4,r0 ; vector in r0 + 4 000550 004737 010236 call @#doisr ; print message for vector in r0 + 5 000554 012600 mov (sp)+,r0 + 6 000556 000002 rti + 2 000010 n=n+1 + 1 isr \n + 1 isr10: + 2 000560 010046 mov r0,-(sp) + 3 000562 012700 000040 mov #10*4,r0 ; vector in r0 + 4 000566 004737 010236 call @#doisr ; print message for vector in r0 + 5 000572 012600 mov (sp)+,r0 + 6 000574 000002 rti + 2 000011 n=n+1 + 1 isr \n + 1 isr11: + 2 000576 010046 mov r0,-(sp) + 3 000600 012700 000044 mov #11*4,r0 ; vector in r0 + 4 000604 004737 010236 call @#doisr ; print message for vector in r0 + 5 000610 012600 mov (sp)+,r0 + 6 000612 000002 rti + 2 000012 n=n+1 + 1 isr \n + 1 isr12: + 2 000614 010046 mov r0,-(sp) + 3 000616 012700 000050 mov #12*4,r0 ; vector in r0 + 4 000622 004737 010236 call @#doisr ; print message for vector in r0 + 5 000626 012600 mov (sp)+,r0 + 6 000630 000002 rti + 2 000013 n=n+1 + 1 isr \n + 1 isr13: + 2 000632 010046 mov r0,-(sp) + 3 000634 012700 000054 mov #13*4,r0 ; vector in r0 + 4 000640 004737 010236 call @#doisr ; print message for vector in r0 + 5 000644 012600 mov (sp)+,r0 + 6 000646 000002 rti + 2 000014 n=n+1 + 1 isr \n + 1 isr14: + 2 000650 010046 mov r0,-(sp) + 3 000652 012700 000060 mov #14*4,r0 ; vector in r0 + 4 000656 004737 010236 call @#doisr ; print message for vector in r0 + 5 000662 012600 mov (sp)+,r0 + 6 000664 000002 rti + 2 000015 n=n+1 + 1 isr \n + 1 isr15: + 2 000666 010046 mov r0,-(sp) + 3 000670 012700 000064 mov #15*4,r0 ; vector in r0 + 4 000674 004737 010236 call @#doisr ; print message for vector in r0 + 5 000700 012600 mov (sp)+,r0 + 6 000702 000002 rti + 2 000016 n=n+1 + 1 isr \n + 1 isr16: + 2 000704 010046 mov r0,-(sp) + 3 000706 012700 000070 mov #16*4,r0 ; vector in r0 + 4 000712 004737 010236 call @#doisr ; print message for vector in r0 + 5 000716 012600 mov (sp)+,r0 + 6 000720 000002 rti + 2 000017 n=n+1 + 1 isr \n + 1 isr17: + 2 000722 010046 mov r0,-(sp) + 3 000724 012700 000074 mov #17*4,r0 ; vector in r0 + 4 000730 004737 010236 call @#doisr ; print message for vector in r0 + 5 000734 012600 mov (sp)+,r0 + 6 000736 000002 rti + 2 000020 n=n+1 + 1 isr \n + 1 isr20: + 2 000740 010046 mov r0,-(sp) + 3 000742 012700 000100 mov #20*4,r0 ; vector in r0 + 4 000746 004737 010236 call @#doisr ; print message for vector in r0 + 5 000752 012600 mov (sp)+,r0 + 6 000754 000002 rti + 2 000021 n=n+1 + 1 isr \n + 1 isr21: + 2 000756 010046 mov r0,-(sp) + 3 000760 012700 000104 mov #21*4,r0 ; vector in r0 + 4 000764 004737 010236 call @#doisr ; print message for vector in r0 + 5 000770 012600 mov (sp)+,r0 + 6 000772 000002 rti + 2 000022 n=n+1 + 1 isr \n + 1 isr22: + 2 000774 010046 mov r0,-(sp) + 3 000776 012700 000110 mov #22*4,r0 ; vector in r0 + 4 001002 004737 010236 call @#doisr ; print message for vector in r0 + 5 001006 012600 mov (sp)+,r0 + 6 001010 000002 rti + 2 000023 n=n+1 + 1 isr \n + 1 isr23: + 2 001012 010046 mov r0,-(sp) + 3 001014 012700 000114 mov #23*4,r0 ; vector in r0 + 4 001020 004737 010236 call @#doisr ; print message for vector in r0 + 5 001024 012600 mov (sp)+,r0 + 6 001026 000002 rti + 2 000024 n=n+1 + 1 isr \n + 1 isr24: + 2 001030 010046 mov r0,-(sp) + 3 001032 012700 000120 mov #24*4,r0 ; vector in r0 + 4 001036 004737 010236 call @#doisr ; print message for vector in r0 + 5 001042 012600 mov (sp)+,r0 + 6 001044 000002 rti + 2 000025 n=n+1 + 1 isr \n + 1 isr25: + 2 001046 010046 mov r0,-(sp) + 3 001050 012700 000124 mov #25*4,r0 ; vector in r0 + 4 001054 004737 010236 call @#doisr ; print message for vector in r0 + 5 001060 012600 mov (sp)+,r0 + 6 001062 000002 rti + 2 000026 n=n+1 + 1 isr \n + 1 isr26: + 2 001064 010046 mov r0,-(sp) + 3 001066 012700 000130 mov #26*4,r0 ; vector in r0 + 4 001072 004737 010236 call @#doisr ; print message for vector in r0 + 5 001076 012600 mov (sp)+,r0 + 6 001100 000002 rti + 2 000027 n=n+1 + 1 isr \n + 1 isr27: + 2 001102 010046 mov r0,-(sp) + 3 001104 012700 000134 mov #27*4,r0 ; vector in r0 + 4 001110 004737 010236 call @#doisr ; print message for vector in r0 + 5 001114 012600 mov (sp)+,r0 + 6 001116 000002 rti + 2 000030 n=n+1 + 1 isr \n + 1 isr30: + 2 001120 010046 mov r0,-(sp) + 3 001122 012700 000140 mov #30*4,r0 ; vector in r0 + 4 001126 004737 010236 call @#doisr ; print message for vector in r0 + 5 001132 012600 mov (sp)+,r0 + 6 001134 000002 rti + 2 000031 n=n+1 + 1 isr \n + 1 isr31: + 2 001136 010046 mov r0,-(sp) + 3 001140 012700 000144 mov #31*4,r0 ; vector in r0 + 4 001144 004737 010236 call @#doisr ; print message for vector in r0 + 5 001150 012600 mov (sp)+,r0 + 6 001152 000002 rti + 2 000032 n=n+1 + 1 isr \n + 1 isr32: + 2 001154 010046 mov r0,-(sp) + 3 001156 012700 000150 mov #32*4,r0 ; vector in r0 + 4 001162 004737 010236 call @#doisr ; print message for vector in r0 + 5 001166 012600 mov (sp)+,r0 + 6 001170 000002 rti + 2 000033 n=n+1 + 1 isr \n + 1 isr33: + 2 001172 010046 mov r0,-(sp) + 3 001174 012700 000154 mov #33*4,r0 ; vector in r0 + 4 001200 004737 010236 call @#doisr ; print message for vector in r0 + 5 001204 012600 mov (sp)+,r0 + 6 001206 000002 rti + 2 000034 n=n+1 + 1 isr \n + 1 isr34: + 2 001210 010046 mov r0,-(sp) + 3 001212 012700 000160 mov #34*4,r0 ; vector in r0 + 4 001216 004737 010236 call @#doisr ; print message for vector in r0 + 5 001222 012600 mov (sp)+,r0 + 6 001224 000002 rti + 2 000035 n=n+1 + 1 isr \n + 1 isr35: + 2 001226 010046 mov r0,-(sp) + 3 001230 012700 000164 mov #35*4,r0 ; vector in r0 + 4 001234 004737 010236 call @#doisr ; print message for vector in r0 + 5 001240 012600 mov (sp)+,r0 + 6 001242 000002 rti + 2 000036 n=n+1 + 1 isr \n + 1 isr36: + 2 001244 010046 mov r0,-(sp) + 3 001246 012700 000170 mov #36*4,r0 ; vector in r0 + 4 001252 004737 010236 call @#doisr ; print message for vector in r0 + 5 001256 012600 mov (sp)+,r0 + 6 001260 000002 rti + 2 000037 n=n+1 + 1 isr \n + 1 isr37: + 2 001262 010046 mov r0,-(sp) + 3 001264 012700 000174 mov #37*4,r0 ; vector in r0 + 4 001270 004737 010236 call @#doisr ; print message for vector in r0 + 5 001274 012600 mov (sp)+,r0 + 6 001276 000002 rti + 2 000040 n=n+1 + 1 isr \n + 1 isr40: + 2 001300 010046 mov r0,-(sp) + 3 001302 012700 000200 mov #40*4,r0 ; vector in r0 + 4 001306 004737 010236 call @#doisr ; print message for vector in r0 + 5 001312 012600 mov (sp)+,r0 + 6 001314 000002 rti + 2 000041 n=n+1 + 1 isr \n + 1 isr41: + 2 001316 010046 mov r0,-(sp) + 3 001320 012700 000204 mov #41*4,r0 ; vector in r0 + 4 001324 004737 010236 call @#doisr ; print message for vector in r0 + 5 001330 012600 mov (sp)+,r0 + 6 001332 000002 rti + 2 000042 n=n+1 + 1 isr \n + 1 isr42: + 2 001334 010046 mov r0,-(sp) + 3 001336 012700 000210 mov #42*4,r0 ; vector in r0 + 4 001342 004737 010236 call @#doisr ; print message for vector in r0 + 5 001346 012600 mov (sp)+,r0 + 6 001350 000002 rti + 2 000043 n=n+1 + 1 isr \n + 1 isr43: + 2 001352 010046 mov r0,-(sp) + 3 001354 012700 000214 mov #43*4,r0 ; vector in r0 + 4 001360 004737 010236 call @#doisr ; print message for vector in r0 + 5 001364 012600 mov (sp)+,r0 + 6 001366 000002 rti + 2 000044 n=n+1 + 1 isr \n + 1 isr44: + 2 001370 010046 mov r0,-(sp) + 3 001372 012700 000220 mov #44*4,r0 ; vector in r0 + 4 001376 004737 010236 call @#doisr ; print message for vector in r0 + 5 001402 012600 mov (sp)+,r0 + 6 001404 000002 rti + 2 000045 n=n+1 + 1 isr \n + 1 isr45: + 2 001406 010046 mov r0,-(sp) + 3 001410 012700 000224 mov #45*4,r0 ; vector in r0 + 4 001414 004737 010236 call @#doisr ; print message for vector in r0 + 5 001420 012600 mov (sp)+,r0 + 6 001422 000002 rti + 2 000046 n=n+1 + 1 isr \n + 1 isr46: + 2 001424 010046 mov r0,-(sp) + 3 001426 012700 000230 mov #46*4,r0 ; vector in r0 + 4 001432 004737 010236 call @#doisr ; print message for vector in r0 + 5 001436 012600 mov (sp)+,r0 + 6 001440 000002 rti + 2 000047 n=n+1 + 1 isr \n + 1 isr47: + 2 001442 010046 mov r0,-(sp) + 3 001444 012700 000234 mov #47*4,r0 ; vector in r0 + 4 001450 004737 010236 call @#doisr ; print message for vector in r0 + 5 001454 012600 mov (sp)+,r0 + 6 001456 000002 rti + 2 000050 n=n+1 + 1 isr \n + 1 isr50: + 2 001460 010046 mov r0,-(sp) + 3 001462 012700 000240 mov #50*4,r0 ; vector in r0 + 4 001466 004737 010236 call @#doisr ; print message for vector in r0 + 5 001472 012600 mov (sp)+,r0 + 6 001474 000002 rti + 2 000051 n=n+1 + 1 isr \n + 1 isr51: + 2 001476 010046 mov r0,-(sp) + 3 001500 012700 000244 mov #51*4,r0 ; vector in r0 + 4 001504 004737 010236 call @#doisr ; print message for vector in r0 + 5 001510 012600 mov (sp)+,r0 + 6 001512 000002 rti + 2 000052 n=n+1 + 1 isr \n + 1 isr52: + 2 001514 010046 mov r0,-(sp) + 3 001516 012700 000250 mov #52*4,r0 ; vector in r0 + 4 001522 004737 010236 call @#doisr ; print message for vector in r0 + 5 001526 012600 mov (sp)+,r0 + 6 001530 000002 rti + 2 000053 n=n+1 + 1 isr \n + 1 isr53: + 2 001532 010046 mov r0,-(sp) + 3 001534 012700 000254 mov #53*4,r0 ; vector in r0 + 4 001540 004737 010236 call @#doisr ; print message for vector in r0 + 5 001544 012600 mov (sp)+,r0 + 6 001546 000002 rti + 2 000054 n=n+1 + 1 isr \n + 1 isr54: + 2 001550 010046 mov r0,-(sp) + 3 001552 012700 000260 mov #54*4,r0 ; vector in r0 + 4 001556 004737 010236 call @#doisr ; print message for vector in r0 + 5 001562 012600 mov (sp)+,r0 + 6 001564 000002 rti + 2 000055 n=n+1 + 1 isr \n + 1 isr55: + 2 001566 010046 mov r0,-(sp) + 3 001570 012700 000264 mov #55*4,r0 ; vector in r0 + 4 001574 004737 010236 call @#doisr ; print message for vector in r0 + 5 001600 012600 mov (sp)+,r0 + 6 001602 000002 rti + 2 000056 n=n+1 + 1 isr \n + 1 isr56: + 2 001604 010046 mov r0,-(sp) + 3 001606 012700 000270 mov #56*4,r0 ; vector in r0 + 4 001612 004737 010236 call @#doisr ; print message for vector in r0 + 5 001616 012600 mov (sp)+,r0 + 6 001620 000002 rti + 2 000057 n=n+1 + 1 isr \n + 1 isr57: + 2 001622 010046 mov r0,-(sp) + 3 001624 012700 000274 mov #57*4,r0 ; vector in r0 + 4 001630 004737 010236 call @#doisr ; print message for vector in r0 + 5 001634 012600 mov (sp)+,r0 + 6 001636 000002 rti + 2 000060 n=n+1 + 1 isr \n + 1 isr60: + 2 001640 010046 mov r0,-(sp) + 3 001642 012700 000300 mov #60*4,r0 ; vector in r0 + 4 001646 004737 010236 call @#doisr ; print message for vector in r0 + 5 001652 012600 mov (sp)+,r0 + 6 001654 000002 rti + 2 000061 n=n+1 + 1 isr \n + 1 isr61: + 2 001656 010046 mov r0,-(sp) + 3 001660 012700 000304 mov #61*4,r0 ; vector in r0 + 4 001664 004737 010236 call @#doisr ; print message for vector in r0 + 5 001670 012600 mov (sp)+,r0 + 6 001672 000002 rti + 2 000062 n=n+1 + 1 isr \n + 1 isr62: + 2 001674 010046 mov r0,-(sp) + 3 001676 012700 000310 mov #62*4,r0 ; vector in r0 + 4 001702 004737 010236 call @#doisr ; print message for vector in r0 + 5 001706 012600 mov (sp)+,r0 + 6 001710 000002 rti + 2 000063 n=n+1 + 1 isr \n + 1 isr63: + 2 001712 010046 mov r0,-(sp) + 3 001714 012700 000314 mov #63*4,r0 ; vector in r0 + 4 001720 004737 010236 call @#doisr ; print message for vector in r0 + 5 001724 012600 mov (sp)+,r0 + 6 001726 000002 rti + 2 000064 n=n+1 + 1 isr \n + 1 isr64: + 2 001730 010046 mov r0,-(sp) + 3 001732 012700 000320 mov #64*4,r0 ; vector in r0 + 4 001736 004737 010236 call @#doisr ; print message for vector in r0 + 5 001742 012600 mov (sp)+,r0 + 6 001744 000002 rti + 2 000065 n=n+1 + 1 isr \n + 1 isr65: + 2 001746 010046 mov r0,-(sp) + 3 001750 012700 000324 mov #65*4,r0 ; vector in r0 + 4 001754 004737 010236 call @#doisr ; print message for vector in r0 + 5 001760 012600 mov (sp)+,r0 + 6 001762 000002 rti + 2 000066 n=n+1 + 1 isr \n + 1 isr66: + 2 001764 010046 mov r0,-(sp) + 3 001766 012700 000330 mov #66*4,r0 ; vector in r0 + 4 001772 004737 010236 call @#doisr ; print message for vector in r0 + 5 001776 012600 mov (sp)+,r0 + 6 002000 000002 rti + 2 000067 n=n+1 + 1 isr \n + 1 isr67: + 2 002002 010046 mov r0,-(sp) + 3 002004 012700 000334 mov #67*4,r0 ; vector in r0 + 4 002010 004737 010236 call @#doisr ; print message for vector in r0 + 5 002014 012600 mov (sp)+,r0 + 6 002016 000002 rti + 2 000070 n=n+1 + 1 isr \n + 1 isr70: + 2 002020 010046 mov r0,-(sp) + 3 002022 012700 000340 mov #70*4,r0 ; vector in r0 + 4 002026 004737 010236 call @#doisr ; print message for vector in r0 + 5 002032 012600 mov (sp)+,r0 + 6 002034 000002 rti + 2 000071 n=n+1 + 1 isr \n + 1 isr71: + 2 002036 010046 mov r0,-(sp) + 3 002040 012700 000344 mov #71*4,r0 ; vector in r0 + 4 002044 004737 010236 call @#doisr ; print message for vector in r0 + 5 002050 012600 mov (sp)+,r0 + 6 002052 000002 rti + 2 000072 n=n+1 + 1 isr \n + 1 isr72: + 2 002054 010046 mov r0,-(sp) + 3 002056 012700 000350 mov #72*4,r0 ; vector in r0 + 4 002062 004737 010236 call @#doisr ; print message for vector in r0 + 5 002066 012600 mov (sp)+,r0 + 6 002070 000002 rti + 2 000073 n=n+1 + 1 isr \n + 1 isr73: + 2 002072 010046 mov r0,-(sp) + 3 002074 012700 000354 mov #73*4,r0 ; vector in r0 + 4 002100 004737 010236 call @#doisr ; print message for vector in r0 + 5 002104 012600 mov (sp)+,r0 + 6 002106 000002 rti + 2 000074 n=n+1 + 1 isr \n + 1 isr74: + 2 002110 010046 mov r0,-(sp) + 3 002112 012700 000360 mov #74*4,r0 ; vector in r0 + 4 002116 004737 010236 call @#doisr ; print message for vector in r0 + 5 002122 012600 mov (sp)+,r0 + 6 002124 000002 rti + 2 000075 n=n+1 + 1 isr \n + 1 isr75: + 2 002126 010046 mov r0,-(sp) + 3 002130 012700 000364 mov #75*4,r0 ; vector in r0 + 4 002134 004737 010236 call @#doisr ; print message for vector in r0 + 5 002140 012600 mov (sp)+,r0 + 6 002142 000002 rti + 2 000076 n=n+1 + 1 isr \n + 1 isr76: + 2 002144 010046 mov r0,-(sp) + 3 002146 012700 000370 mov #76*4,r0 ; vector in r0 + 4 002152 004737 010236 call @#doisr ; print message for vector in r0 + 5 002156 012600 mov (sp)+,r0 + 6 002160 000002 rti + 2 000077 n=n+1 + 1 isr \n + 1 isr77: + 2 002162 010046 mov r0,-(sp) + 3 002164 012700 000374 mov #77*4,r0 ; vector in r0 + 4 002170 004737 010236 call @#doisr ; print message for vector in r0 + 5 002174 012600 mov (sp)+,r0 + 6 002176 000002 rti + 2 000100 n=n+1 + 68 + 69 + 70 + 71 + 72 ; ---- foreground thread --------- + 73 000000 .=0 + 74 000000 000137 010000 jmp @#start ; easier manual start from 0 + 75 + 76 007776 .=7776 + 77 007776 000000 inchr: .word ; input alternative to DL11 + 78 + 79 010000 .=10000 + 80 + 81 007776 stack = . - 2 ; stack growns down from start + 82 + 83 start: + 84 010000 012706 007776 mov #stack,sp ; init stack + 85 010004 005037 177776 clr @#psw ; clear priority, allow all interupts + 86 + 87 010010 000005 reset ; disable INTR on all devices + 88 + 89 010012 005037 011506 clr @#logptr ; default: slow mode + 90 + 91 ; 1. print "Hello" msg + 92 010016 012701 010640 mov #shello,r1 + 93 010022 004737 010412 call @#puts + 94 + 95 ; test vecnum printout + 96 ; mov #123456,r0 + 97 ; call @#isrmsg + 98 + 99 ; 2. echo chars until ^C hit + 100 1$: + 101 010026 004737 010560 call @#getc ; wait for char, return in r0 + 102 010032 042700 177600 bic #177600,r0 ; make 7bit: clear bits <15:7> + 103 010036 120027 000003 cmpb r0,#3 ; break by ^C ? + 104 010042 001467 beq 9$ ; yes: leave loop + 105 010044 120027 000060 cmpb r0,#60 + 106 010050 103432 blo 2$ ; char is < '0' + 107 010052 120027 000067 cmpb r0,#67 + 108 010056 101027 bhi 2$ ; char is > '7' + 109 010060 110002 movb r0,r2 ; save + 110 + 111 ; key is 0..7: change priority + 112 010062 012701 011433 mov #sprio0,r1 ; print info + 113 010066 004737 010412 call @#puts + 114 010072 110200 movb r2,r0 ; restore digit + 115 010074 004737 010540 call @#putc ; print level digit + 116 010100 012701 011465 mov #sprio1,r1 + 117 010104 004737 010412 call @#puts + 118 ; change PSW + 119 010110 110200 movb r2,r0 + 120 010112 142700 000370 bicb #370,r0 ; ASCII -> integer + 121 010116 006300 asl r0 ; move level to "priority" field in PSW + 122 010120 006300 asl r0 ; in bits 7,6,5 + 123 010122 006300 asl r0 + 124 010124 006300 asl r0 + 125 010126 006300 asl r0 + 126 010130 010037 177776 mov r0,@#psw + 127 + 128 010134 000734 br 1$ ; OK, next char + 129 + 130 2$: ; -- eval "S", F" + 131 010136 120027 000123 cmpb r0,#'S + 132 010142 001007 bne 3$ + 133 010144 012701 011300 mov #sslwmd,r1 ; "slow mode" + 134 010150 004737 010412 call @#puts + 135 010154 005037 011506 clr @#logptr + 136 010160 000722 br 1$ ; OK, next char + 137 010162 120027 000106 3$: cmpb r0,#'F + 138 010166 001012 bne 8$ ; + 139 010170 012701 011357 mov #sfstmd,r1 ; "fast mode" + 140 010174 004737 010412 call @#puts + 141 010200 004737 010340 call @#prtlog ; print logged vectors, if any + 142 010204 012737 011510 011506 mov #logbuf,@#logptr; pointer to biuffer start + 143 010212 000705 br 1$ ; OK, next char + 144 + 145 8$: + 146 010214 004737 010540 call @#putc ; no: echo char in r0 and loop + 147 010220 000702 br 1$ + 148 + 149 9$: + 150 + 151 ; 3. print "Bye bye" msg and HALT + 152 010222 012701 011470 mov #sbye,r1 + 153 010226 004737 010412 call @#puts + 154 010232 000000 halt + 155 + 156 ; 4. loop on CONT + 157 010234 000661 br start + 158 + 159 + 160 ; ---------------------- + 161 ; Common code for all ISRs + 162 ; print vector number in r0 + 163 ; called on interrupt level + 164 + 165 doisr: + 166 010236 010446 mov r4,-(sp) + 167 010240 010346 mov r3,-(sp) + 168 010242 010246 mov r2,-(sp) + 169 010244 010146 mov r1,-(sp) + 170 010246 010046 mov r0,-(sp) + 171 + 172 010250 013701 011506 mov @#logptr,r1 + 173 010254 001404 beq 1$ + 174 ; Fast mode: log vector + 175 010256 010021 mov r0,(r1)+ ; store vector in array + 176 010260 010137 011506 mov r1,@#logptr ; save updated list pointer + 177 + 178 010264 000417 br 9$ + 179 1$: ; "Slow mode: print msg + 180 010266 012701 010626 mov #sisr1,r1 ; "ISR " + 181 010272 004737 010412 call @#puts + 182 010276 011600 mov (sp),r0 ; restore vecnum + 183 010300 004737 010446 call @#putnum + 184 010304 012701 010634 mov #sisr2,r1 ; "cr lf + 185 010310 004737 010412 call @#puts + 186 ; make ISR 100ms + 187 010314 012700 000144 mov #144,r0 ; 100 + 188 010320 004737 010610 call @#waitms + 189 + 190 9$: + 191 010324 012600 mov (sp)+,r0 + 192 010326 012601 mov (sp)+,r1 + 193 010330 012602 mov (sp)+,r2 + 194 010332 012603 mov (sp)+,r3 + 195 010334 012604 mov (sp)+,r4 + 196 010336 000207 return + 197 + 198 + 199 ; ---------------------- + 200 ; prtlog - print log of isr vectors + 201 ; word list from logbuf to logptr + 202 prtlog: + 203 010340 005737 011506 tst @#logptr + 204 010344 001421 beq 9$ ; ptr 0, slow mode, nothing logged + 205 010346 012702 011510 mov #logbuf,r2 + 206 1$: + 207 010352 020237 011506 cmp r2,@#logptr + 208 010356 103010 bhis 8$ ; end of list reached + 209 010360 012200 mov (r2)+,r0 ; print vector from list + 210 010362 004737 010446 call @#putnum + 211 010366 012700 000040 mov #40,r0 ; print space separator + 212 010372 004737 010540 call @#putc + 213 010376 000765 br 1$ + 214 8$: + 215 010400 012701 011503 mov #scrlf,r1 ; CR/LF + 216 010404 004737 010412 call @#puts + 217 + 218 9$: + 219 010410 000207 return + 220 + 221 ; ---------------------- + 222 ; puts - print a string + 223 ; r1 = pointer, r0,r1 changed + 224 puts: + 225 010412 112100 movb (r1)+,r0 ; load xmt char + 226 010414 001403 beq 1$ ; string ends with 0 + 227 010416 004737 010540 call @#putc + 228 010422 000773 br puts ; transmit nxt char of string + 229 010424 000207 1$: return + 230 + 231 + 232 ; ---------------------- + 233 ; putnum - print the octal number in r0 + 234 010426 numbf0: .blkw 10 ; space to mount number string + 235 010446 numbf1 =. + 236 putnum: + 237 010446 010046 mov r0,-(sp) + 238 010450 010146 mov r1,-(sp) + 239 010452 010246 mov r2,-(sp) + 240 010454 010346 mov r3,-(sp) + 241 010456 010002 mov r0,r2 ; r2 = shifter + 242 010460 012701 010446 mov #numbf1,r1 ; r1 = buffer pointer, backwards + 243 010464 112741 000000 movb #0,-(r1) ; set terminating 0 + 244 ; repeat 6 times + 245 010470 012703 000006 mov #6,r3 + 246 1$: + 247 010474 010200 mov r2,r0 + 248 ; extract lower 3 bits = octal digit + 249 010476 042700 177770 bic #177770,r0 ; r0 &= 0x07 + 250 010502 062700 000060 add #60,r0 ; r0 += '0' + 251 010506 110041 movb r0,-(r1) ; write in buffer + 252 010510 000241 clc + 253 010512 006202 asr r2 ; shift to next digit + 254 010514 006202 asr r2 + 255 010516 006202 asr r2 + 256 010520 077313 sob r3,1$ ; loop for all 6 digits + 257 + 258 010522 004737 010412 call @#puts + 259 010526 012603 mov (sp)+,r3 + 260 010530 012602 mov (sp)+,r2 + 261 010532 012601 mov (sp)+,r1 + 262 010534 012600 mov (sp)+,r0 + 263 010536 000207 return + 264 + 265 + 266 ; DEC DL11 console I/O + 267 ; ---------------------- + 268 ; putc - output a single char + 269 ; r0 = char, r4 changed + 270 putc: + 271 010540 012704 177560 mov #dladr,r4 ; set base addr + 272 010544 110064 000006 movb r0,6(r4) ; char into transmit buffer + 273 010550 105764 000004 1$: tstb 4(r4) ; XMT RDY? + 274 010554 100375 bpl 1$ ; no, loop + 275 010556 000207 return + 276 + 277 ; ---------------------- + 278 ; getc - input a single char + 279 ; result in r0, r4 changed + 280 getc: + 281 010560 012704 177560 mov #dladr,r4 ; set base addr + 282 010564 013700 007776 1$: mov @#inchr,r0 ; external DEPOSIT into inchr? + 283 010570 001004 bne 9$ ; yes: process as input + 284 010572 105714 tstb (r4) ; else: RCVR DONE? + 285 010574 100373 bpl 1$ ; no, loop + 286 010576 016400 000002 mov 2(r4),r0 ; return data + 287 9$: + 288 010602 005037 007776 clr @#inchr + 289 010606 000207 return + 290 + 291 ; ---------------------- + 292 ; waitms - wait r0 milli seconds + 293 waitms: + 294 010610 010146 mov r1,-(sp) + 295 + 296 1$: ; -- outer loop + 297 010612 012701 000764 mov #764,r1 ; 500 + 298 2$: ; -- inner loop: 1ms @ 1MHz + 299 010616 077101 sob r1,2$ ; 1 cycle,2 us per loop (11/34) + 300 + 301 010620 077004 sob r0,1$ + 302 010622 012601 mov (sp)+,r1 + 303 010624 000207 return + 304 + 305 ; ---- Test strings, each 256 chars max --------- + 306 + 307 sisr1: ; start of ISR message + 308 010626 074 111 123 .ascii / message + 311 010634 076 .ascii />/ + 312 010635 015 012 .byte 15,12 ; CR, LF, + 313 010637 000 .byte 0 ; NUL=end marker + 314 + 315 + 316 shello: + 317 010640 015 012 .byte 15,12 ; CR, LF, + 318 010642 052 052 052 .ascii /*** Interrupt and DMA test ***/ + 010645 040 111 156 + 010650 164 145 162 + 010653 162 165 160 + 010656 164 040 141 + 010661 156 144 040 + 010664 104 115 101 + 010667 040 164 145 + 010672 163 164 040 + 010675 052 052 052 + 319 010700 015 012 .byte 15,12 ; CR, LF, + 320 010702 124 150 145 .ascii /The INTR vectors 0..77 print the vector num./ + 010705 040 111 116 + 010710 124 122 040 + 010713 166 145 143 + 010716 164 157 162 + 010721 163 040 060 + 010724 056 056 067 + 010727 067 040 160 + 010732 162 151 156 + 010735 164 040 164 + 010740 150 145 040 + 010743 166 145 143 + 010746 164 157 162 + 010751 040 156 165 + 010754 155 056 + 321 010756 015 012 .byte 15,12 ; CR, LF, + 322 010760 124 150 145 .ascii /The foreground thread echoes typed chars, ^C HALTs./ + 010763 040 146 157 + 010766 162 145 147 + 010771 162 157 165 + 010774 156 144 040 + 010777 164 150 162 + 011002 145 141 144 + 011005 040 145 143 + 011010 150 157 145 + 011013 163 040 164 + 011016 171 160 145 + 011021 144 040 143 + 011024 150 141 162 + 011027 163 054 040 + 011032 136 103 040 + 011035 110 101 114 + 011040 124 163 056 + 323 011043 015 012 .byte 15,12 ; CR, LF, + 324 011045 103 150 141 .ascii /Chars 0..7 set the new processor priority level./ + 011050 162 163 040 + 011053 060 056 056 + 011056 067 040 163 + 011061 145 164 040 + 011064 164 150 145 + 011067 040 156 145 + 011072 167 040 160 + 011075 162 157 143 + 011100 145 163 163 + 011103 157 162 040 + 011106 160 162 151 + 011111 157 162 151 + 011114 164 171 040 + 011117 154 145 166 + 011122 145 154 056 + 325 011125 015 012 .byte 15,12 ; CR, LF, + 326 011127 123 040 075 .ascii /S = slow mode: ISR prints message (default)/ + 011132 040 163 154 + 011135 157 167 040 + 011140 155 157 144 + 011143 145 072 040 + 011146 111 123 122 + 011151 040 160 162 + 011154 151 156 164 + 011157 163 040 155 + 011162 145 163 163 + 011165 141 147 145 + 011170 040 050 144 + 011173 145 146 141 + 011176 165 154 164 + 011201 051 + 327 011202 015 012 .byte 15,12 ; CR, LF, + 328 011204 106 040 075 .ascii /F = fast mode: ISR logs vector, print and clr current log/ + 011207 040 146 141 + 011212 163 164 040 + 011215 155 157 144 + 011220 145 072 040 + 011223 111 123 122 + 011226 040 154 157 + 011231 147 163 040 + 011234 166 145 143 + 011237 164 157 162 + 011242 054 040 160 + 011245 162 151 156 + 011250 164 040 141 + 011253 156 144 040 + 011256 143 154 162 + 011261 040 143 165 + 011264 162 162 145 + 011267 156 164 040 + 011272 154 157 147 + 329 011275 015 012 .byte 15,12 ; CR, LF, + 330 011277 000 .byte 0 ; NUL=end marker + 331 + 332 sslwmd: + 333 011300 123 154 157 .ascii /Slow mode: called ISR prints vector directly/ + 011303 167 040 155 + 011306 157 144 145 + 011311 072 040 143 + 011314 141 154 154 + 011317 145 144 040 + 011322 111 123 122 + 011325 040 160 162 + 011330 151 156 164 + 011333 163 040 166 + 011336 145 143 164 + 011341 157 162 040 + 011344 144 151 162 + 011347 145 143 164 + 011352 154 171 + 334 011354 015 012 .byte 15,12 ; CR, LF, + 335 011356 000 .byte 0 ; NUL=end marker + 336 + 337 sfstmd: + 338 011357 106 141 163 .ascii /Fast mode: called ISR vectors are logged:/ + 011362 164 040 155 + 011365 157 144 145 + 011370 072 040 143 + 011373 141 154 154 + 011376 145 144 040 + 011401 111 123 122 + 011404 040 166 145 + 011407 143 164 157 + 011412 162 163 040 + 011415 141 162 145 + 011420 040 154 157 + 011423 147 147 145 + 011426 144 072 + 339 011430 015 012 .byte 15,12 ; CR, LF, + 340 011432 000 .byte 0 ; NUL=end marker + 341 + 342 sprio0: + 343 011433 015 012 .byte 15,12 ; CR, LF, + 344 011435 103 120 125 .ascii /CPU priority level now / + 011440 040 160 162 + 011443 151 157 162 + 011446 151 164 171 + 011451 040 154 145 + 011454 166 145 154 + 011457 040 156 157 + 011462 167 040 + 345 011464 000 .byte 0 + 346 + 347 sprio1: + 348 011465 015 012 .byte 15,12 ; CR, LF, + 349 011467 000 .byte 0 ; NUL=end marker + 350 + 351 sbye: + 352 011470 015 012 .byte 15,12 + 353 011472 107 157 157 .ascii /Good Bye!/ + 011475 144 040 102 + 011500 171 145 041 + 354 scrlf: + 355 011503 015 012 000 .byte 15,12,0 ; CR, LF, NUL=end marker + 356 + 357 ; .byte 0 ; make addr even + 358 + 359 ; in "fast" mode, ISR calls fill this array with called vectors + 360 logptr: + 361 011506 000000 .word ; addr of next logentry to fill. 0 in "slow" mode + 362 + 363 logbuf: + 364 + 365 + 366 .end + 366 diff --git a/10.01_base/3_test/intrtst.lst.hex b/10.01_base/3_test/intrtst.lst.hex index bf2585d..eb49368 100644 --- a/10.01_base/3_test/intrtst.lst.hex +++ b/10.01_base/3_test/intrtst.lst.hex @@ -7,50 +7,57 @@ 7 ; 1.1. print a start message, 8 ; 1.2. echoes chars typed to the output until ^C is hit 9 ; Chars 0..7 set the new processor priority level. - 10 ; 1.3. prints an end message and HALTs. + 10 ; 1.3 prints an end message and HALTs. 11 ; 1.4. on CONT it repeats. 12 ; 13 ; 2. For INTR test, the 256 vectors 0,4,10,14,..374 each print 14 ; a string on interrupt. - 15 ; - 16 ; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com - 17 - 18 FF70h dladr = 177560 ; DL11 console base address - 19 FFFEh psw = 177776 ; processor status - 20 - 21 - 22 ; count of automatically generated interrupt vectors - 23 0040h veccnt = 100 + 15 ; In "Slow" mode, the ISR prints the vector directly + 16 ; (ISR rountine > 100ms) + 17 ; In "Fast" mode, the ISR logs the vector in a list + 18 ; (It can be printed later with "F" + 19 ; + 20 ; As alternative to input over DL11, + 21 ; "keyboard input chars" can be deposited into 7776 + 22 ; + 23 ; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com 24 - 25 ; ------- macro to define interrupt vector # ------ - 26 .macro vector vecidx - 27 .=4*vecidx ; vector #vecidx - 28 .word isr'vecidx ; new PC of ISR - 29 .word 340 ; new PSW: priority is max = 7 - 30 .endm + 25 FF70h dladr = 177560 ; DL11 console base address + 26 FFFEh psw = 177776 ; processor status + 27 + 28 + 29 ; count of automatically generated interrupt vectors + 30 0040h veccnt = 100 31 - 32 ; ----- macro to define ISR for vector #n ------- - 33 .macro isr vecidx - 34 isr'vecidx: - 35 mov r0,-(sp) - 36 mov #vecidx*4,r0 ; vector in r0 - 37 call @#doisr ; print message for vector in r0 - 38 mov (sp)+,r0 - 39 rti - 40 .endm - 41 - 42 - 43 - 44 - 45 .asect - 46 - 47 0000h . = 0 - 48 ; ---- "veccnt" Interrupt Vectors --------- - 49 0000h n=0 - 50 .rept veccnt - 51 vector \n - 52 n=n+1 - 53 .endr + 32 ; ------- macro to define interrupt vector # ------ + 33 .macro vector vecidx + 34 .=4*vecidx ; vector #vecidx + 35 .word isr'vecidx ; new PC of ISR + 36 .word 340 ; new PSW: priority is max = 7 + 37 .endm + 38 + 39 ; ----- macro to define ISR for vector #n ------- + 40 .macro isr vecidx + 41 isr'vecidx: + 42 mov r0,-(sp) + 43 mov #vecidx*4,r0 ; vector in r0 + 44 call @#doisr ; print message for vector in r0 + 45 mov (sp)+,r0 + 46 rti + 47 .endm + 48 + 49 + 50 + 51 + 52 .asect + 53 + 54 0000h . = 0 + 55 ; ---- "veccnt" Interrupt Vectors --------- + 56 0000h n=0 + 57 .rept veccnt + 58 vector \n + 59 n=n+1 + 60 .endr 1 vector \n 1 0000h .=4*0 ; vector #0 2 0000h 0100h .word isr0 ; new PC of ISR @@ -371,18 +378,18 @@ 2 00FCh 0472h .word isr77 ; new PC of ISR 3 00FEh 00E0h .word 340 ; new PSW: priority is max = 7 2 0040h n=n+1 - 54 - 55 ; ---- veccnt ISRs --------- - 56 0000h n=0 - 57 .rept veccnt - 58 isr \n - 59 n=n+1 - 60 .endr + 61 + 62 ; ---- veccnt ISRs --------- + 63 0000h n=0 + 64 .rept veccnt + 65 isr \n + 66 n=n+1 + 67 .endr 1 isr \n 1 isr0: 2 0100h 1026h mov r0,-(sp) 3 0102h 15C0h 0000h mov #0*4,r0 ; vector in r0 - 4 0106h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0106h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 010Ah 1580h mov (sp)+,r0 6 010Ch 0002h rti 2 0001h n=n+1 @@ -390,7 +397,7 @@ 1 isr1: 2 010Eh 1026h mov r0,-(sp) 3 0110h 15C0h 0004h mov #1*4,r0 ; vector in r0 - 4 0114h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0114h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0118h 1580h mov (sp)+,r0 6 011Ah 0002h rti 2 0002h n=n+1 @@ -398,7 +405,7 @@ 1 isr2: 2 011Ch 1026h mov r0,-(sp) 3 011Eh 15C0h 0008h mov #2*4,r0 ; vector in r0 - 4 0122h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0122h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0126h 1580h mov (sp)+,r0 6 0128h 0002h rti 2 0003h n=n+1 @@ -406,7 +413,7 @@ 1 isr3: 2 012Ah 1026h mov r0,-(sp) 3 012Ch 15C0h 000Ch mov #3*4,r0 ; vector in r0 - 4 0130h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0130h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0134h 1580h mov (sp)+,r0 6 0136h 0002h rti 2 0004h n=n+1 @@ -414,7 +421,7 @@ 1 isr4: 2 0138h 1026h mov r0,-(sp) 3 013Ah 15C0h 0010h mov #4*4,r0 ; vector in r0 - 4 013Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 013Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0142h 1580h mov (sp)+,r0 6 0144h 0002h rti 2 0005h n=n+1 @@ -422,7 +429,7 @@ 1 isr5: 2 0146h 1026h mov r0,-(sp) 3 0148h 15C0h 0014h mov #5*4,r0 ; vector in r0 - 4 014Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 014Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0150h 1580h mov (sp)+,r0 6 0152h 0002h rti 2 0006h n=n+1 @@ -430,7 +437,7 @@ 1 isr6: 2 0154h 1026h mov r0,-(sp) 3 0156h 15C0h 0018h mov #6*4,r0 ; vector in r0 - 4 015Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 015Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 015Eh 1580h mov (sp)+,r0 6 0160h 0002h rti 2 0007h n=n+1 @@ -438,7 +445,7 @@ 1 isr7: 2 0162h 1026h mov r0,-(sp) 3 0164h 15C0h 001Ch mov #7*4,r0 ; vector in r0 - 4 0168h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0168h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 016Ch 1580h mov (sp)+,r0 6 016Eh 0002h rti 2 0008h n=n+1 @@ -446,7 +453,7 @@ 1 isr10: 2 0170h 1026h mov r0,-(sp) 3 0172h 15C0h 0020h mov #10*4,r0 ; vector in r0 - 4 0176h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0176h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 017Ah 1580h mov (sp)+,r0 6 017Ch 0002h rti 2 0009h n=n+1 @@ -454,7 +461,7 @@ 1 isr11: 2 017Eh 1026h mov r0,-(sp) 3 0180h 15C0h 0024h mov #11*4,r0 ; vector in r0 - 4 0184h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0184h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0188h 1580h mov (sp)+,r0 6 018Ah 0002h rti 2 000Ah n=n+1 @@ -462,7 +469,7 @@ 1 isr12: 2 018Ch 1026h mov r0,-(sp) 3 018Eh 15C0h 0028h mov #12*4,r0 ; vector in r0 - 4 0192h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0192h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0196h 1580h mov (sp)+,r0 6 0198h 0002h rti 2 000Bh n=n+1 @@ -470,7 +477,7 @@ 1 isr13: 2 019Ah 1026h mov r0,-(sp) 3 019Ch 15C0h 002Ch mov #13*4,r0 ; vector in r0 - 4 01A0h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01A0h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01A4h 1580h mov (sp)+,r0 6 01A6h 0002h rti 2 000Ch n=n+1 @@ -478,7 +485,7 @@ 1 isr14: 2 01A8h 1026h mov r0,-(sp) 3 01AAh 15C0h 0030h mov #14*4,r0 ; vector in r0 - 4 01AEh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01AEh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01B2h 1580h mov (sp)+,r0 6 01B4h 0002h rti 2 000Dh n=n+1 @@ -486,7 +493,7 @@ 1 isr15: 2 01B6h 1026h mov r0,-(sp) 3 01B8h 15C0h 0034h mov #15*4,r0 ; vector in r0 - 4 01BCh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01BCh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01C0h 1580h mov (sp)+,r0 6 01C2h 0002h rti 2 000Eh n=n+1 @@ -494,7 +501,7 @@ 1 isr16: 2 01C4h 1026h mov r0,-(sp) 3 01C6h 15C0h 0038h mov #16*4,r0 ; vector in r0 - 4 01CAh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01CAh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01CEh 1580h mov (sp)+,r0 6 01D0h 0002h rti 2 000Fh n=n+1 @@ -502,7 +509,7 @@ 1 isr17: 2 01D2h 1026h mov r0,-(sp) 3 01D4h 15C0h 003Ch mov #17*4,r0 ; vector in r0 - 4 01D8h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01D8h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01DCh 1580h mov (sp)+,r0 6 01DEh 0002h rti 2 0010h n=n+1 @@ -510,7 +517,7 @@ 1 isr20: 2 01E0h 1026h mov r0,-(sp) 3 01E2h 15C0h 0040h mov #20*4,r0 ; vector in r0 - 4 01E6h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01E6h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01EAh 1580h mov (sp)+,r0 6 01ECh 0002h rti 2 0011h n=n+1 @@ -518,7 +525,7 @@ 1 isr21: 2 01EEh 1026h mov r0,-(sp) 3 01F0h 15C0h 0044h mov #21*4,r0 ; vector in r0 - 4 01F4h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 01F4h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 01F8h 1580h mov (sp)+,r0 6 01FAh 0002h rti 2 0012h n=n+1 @@ -526,7 +533,7 @@ 1 isr22: 2 01FCh 1026h mov r0,-(sp) 3 01FEh 15C0h 0048h mov #22*4,r0 ; vector in r0 - 4 0202h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0202h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0206h 1580h mov (sp)+,r0 6 0208h 0002h rti 2 0013h n=n+1 @@ -534,7 +541,7 @@ 1 isr23: 2 020Ah 1026h mov r0,-(sp) 3 020Ch 15C0h 004Ch mov #23*4,r0 ; vector in r0 - 4 0210h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0210h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0214h 1580h mov (sp)+,r0 6 0216h 0002h rti 2 0014h n=n+1 @@ -542,7 +549,7 @@ 1 isr24: 2 0218h 1026h mov r0,-(sp) 3 021Ah 15C0h 0050h mov #24*4,r0 ; vector in r0 - 4 021Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 021Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0222h 1580h mov (sp)+,r0 6 0224h 0002h rti 2 0015h n=n+1 @@ -550,7 +557,7 @@ 1 isr25: 2 0226h 1026h mov r0,-(sp) 3 0228h 15C0h 0054h mov #25*4,r0 ; vector in r0 - 4 022Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 022Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0230h 1580h mov (sp)+,r0 6 0232h 0002h rti 2 0016h n=n+1 @@ -558,7 +565,7 @@ 1 isr26: 2 0234h 1026h mov r0,-(sp) 3 0236h 15C0h 0058h mov #26*4,r0 ; vector in r0 - 4 023Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 023Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 023Eh 1580h mov (sp)+,r0 6 0240h 0002h rti 2 0017h n=n+1 @@ -566,7 +573,7 @@ 1 isr27: 2 0242h 1026h mov r0,-(sp) 3 0244h 15C0h 005Ch mov #27*4,r0 ; vector in r0 - 4 0248h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0248h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 024Ch 1580h mov (sp)+,r0 6 024Eh 0002h rti 2 0018h n=n+1 @@ -574,7 +581,7 @@ 1 isr30: 2 0250h 1026h mov r0,-(sp) 3 0252h 15C0h 0060h mov #30*4,r0 ; vector in r0 - 4 0256h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0256h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 025Ah 1580h mov (sp)+,r0 6 025Ch 0002h rti 2 0019h n=n+1 @@ -582,7 +589,7 @@ 1 isr31: 2 025Eh 1026h mov r0,-(sp) 3 0260h 15C0h 0064h mov #31*4,r0 ; vector in r0 - 4 0264h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0264h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0268h 1580h mov (sp)+,r0 6 026Ah 0002h rti 2 001Ah n=n+1 @@ -590,7 +597,7 @@ 1 isr32: 2 026Ch 1026h mov r0,-(sp) 3 026Eh 15C0h 0068h mov #32*4,r0 ; vector in r0 - 4 0272h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0272h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0276h 1580h mov (sp)+,r0 6 0278h 0002h rti 2 001Bh n=n+1 @@ -598,7 +605,7 @@ 1 isr33: 2 027Ah 1026h mov r0,-(sp) 3 027Ch 15C0h 006Ch mov #33*4,r0 ; vector in r0 - 4 0280h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0280h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0284h 1580h mov (sp)+,r0 6 0286h 0002h rti 2 001Ch n=n+1 @@ -606,7 +613,7 @@ 1 isr34: 2 0288h 1026h mov r0,-(sp) 3 028Ah 15C0h 0070h mov #34*4,r0 ; vector in r0 - 4 028Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 028Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0292h 1580h mov (sp)+,r0 6 0294h 0002h rti 2 001Dh n=n+1 @@ -614,7 +621,7 @@ 1 isr35: 2 0296h 1026h mov r0,-(sp) 3 0298h 15C0h 0074h mov #35*4,r0 ; vector in r0 - 4 029Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 029Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02A0h 1580h mov (sp)+,r0 6 02A2h 0002h rti 2 001Eh n=n+1 @@ -622,7 +629,7 @@ 1 isr36: 2 02A4h 1026h mov r0,-(sp) 3 02A6h 15C0h 0078h mov #36*4,r0 ; vector in r0 - 4 02AAh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02AAh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02AEh 1580h mov (sp)+,r0 6 02B0h 0002h rti 2 001Fh n=n+1 @@ -630,7 +637,7 @@ 1 isr37: 2 02B2h 1026h mov r0,-(sp) 3 02B4h 15C0h 007Ch mov #37*4,r0 ; vector in r0 - 4 02B8h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02B8h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02BCh 1580h mov (sp)+,r0 6 02BEh 0002h rti 2 0020h n=n+1 @@ -638,7 +645,7 @@ 1 isr40: 2 02C0h 1026h mov r0,-(sp) 3 02C2h 15C0h 0080h mov #40*4,r0 ; vector in r0 - 4 02C6h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02C6h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02CAh 1580h mov (sp)+,r0 6 02CCh 0002h rti 2 0021h n=n+1 @@ -646,7 +653,7 @@ 1 isr41: 2 02CEh 1026h mov r0,-(sp) 3 02D0h 15C0h 0084h mov #41*4,r0 ; vector in r0 - 4 02D4h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02D4h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02D8h 1580h mov (sp)+,r0 6 02DAh 0002h rti 2 0022h n=n+1 @@ -654,7 +661,7 @@ 1 isr42: 2 02DCh 1026h mov r0,-(sp) 3 02DEh 15C0h 0088h mov #42*4,r0 ; vector in r0 - 4 02E2h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02E2h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02E6h 1580h mov (sp)+,r0 6 02E8h 0002h rti 2 0023h n=n+1 @@ -662,7 +669,7 @@ 1 isr43: 2 02EAh 1026h mov r0,-(sp) 3 02ECh 15C0h 008Ch mov #43*4,r0 ; vector in r0 - 4 02F0h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02F0h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 02F4h 1580h mov (sp)+,r0 6 02F6h 0002h rti 2 0024h n=n+1 @@ -670,7 +677,7 @@ 1 isr44: 2 02F8h 1026h mov r0,-(sp) 3 02FAh 15C0h 0090h mov #44*4,r0 ; vector in r0 - 4 02FEh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 02FEh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0302h 1580h mov (sp)+,r0 6 0304h 0002h rti 2 0025h n=n+1 @@ -678,7 +685,7 @@ 1 isr45: 2 0306h 1026h mov r0,-(sp) 3 0308h 15C0h 0094h mov #45*4,r0 ; vector in r0 - 4 030Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 030Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0310h 1580h mov (sp)+,r0 6 0312h 0002h rti 2 0026h n=n+1 @@ -686,7 +693,7 @@ 1 isr46: 2 0314h 1026h mov r0,-(sp) 3 0316h 15C0h 0098h mov #46*4,r0 ; vector in r0 - 4 031Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 031Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 031Eh 1580h mov (sp)+,r0 6 0320h 0002h rti 2 0027h n=n+1 @@ -694,7 +701,7 @@ 1 isr47: 2 0322h 1026h mov r0,-(sp) 3 0324h 15C0h 009Ch mov #47*4,r0 ; vector in r0 - 4 0328h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0328h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 032Ch 1580h mov (sp)+,r0 6 032Eh 0002h rti 2 0028h n=n+1 @@ -702,7 +709,7 @@ 1 isr50: 2 0330h 1026h mov r0,-(sp) 3 0332h 15C0h 00A0h mov #50*4,r0 ; vector in r0 - 4 0336h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0336h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 033Ah 1580h mov (sp)+,r0 6 033Ch 0002h rti 2 0029h n=n+1 @@ -710,7 +717,7 @@ 1 isr51: 2 033Eh 1026h mov r0,-(sp) 3 0340h 15C0h 00A4h mov #51*4,r0 ; vector in r0 - 4 0344h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0344h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0348h 1580h mov (sp)+,r0 6 034Ah 0002h rti 2 002Ah n=n+1 @@ -718,7 +725,7 @@ 1 isr52: 2 034Ch 1026h mov r0,-(sp) 3 034Eh 15C0h 00A8h mov #52*4,r0 ; vector in r0 - 4 0352h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0352h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0356h 1580h mov (sp)+,r0 6 0358h 0002h rti 2 002Bh n=n+1 @@ -726,7 +733,7 @@ 1 isr53: 2 035Ah 1026h mov r0,-(sp) 3 035Ch 15C0h 00ACh mov #53*4,r0 ; vector in r0 - 4 0360h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0360h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0364h 1580h mov (sp)+,r0 6 0366h 0002h rti 2 002Ch n=n+1 @@ -734,7 +741,7 @@ 1 isr54: 2 0368h 1026h mov r0,-(sp) 3 036Ah 15C0h 00B0h mov #54*4,r0 ; vector in r0 - 4 036Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 036Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0372h 1580h mov (sp)+,r0 6 0374h 0002h rti 2 002Dh n=n+1 @@ -742,7 +749,7 @@ 1 isr55: 2 0376h 1026h mov r0,-(sp) 3 0378h 15C0h 00B4h mov #55*4,r0 ; vector in r0 - 4 037Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 037Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0380h 1580h mov (sp)+,r0 6 0382h 0002h rti 2 002Eh n=n+1 @@ -750,7 +757,7 @@ 1 isr56: 2 0384h 1026h mov r0,-(sp) 3 0386h 15C0h 00B8h mov #56*4,r0 ; vector in r0 - 4 038Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 038Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 038Eh 1580h mov (sp)+,r0 6 0390h 0002h rti 2 002Fh n=n+1 @@ -758,7 +765,7 @@ 1 isr57: 2 0392h 1026h mov r0,-(sp) 3 0394h 15C0h 00BCh mov #57*4,r0 ; vector in r0 - 4 0398h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0398h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 039Ch 1580h mov (sp)+,r0 6 039Eh 0002h rti 2 0030h n=n+1 @@ -766,7 +773,7 @@ 1 isr60: 2 03A0h 1026h mov r0,-(sp) 3 03A2h 15C0h 00C0h mov #60*4,r0 ; vector in r0 - 4 03A6h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03A6h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03AAh 1580h mov (sp)+,r0 6 03ACh 0002h rti 2 0031h n=n+1 @@ -774,7 +781,7 @@ 1 isr61: 2 03AEh 1026h mov r0,-(sp) 3 03B0h 15C0h 00C4h mov #61*4,r0 ; vector in r0 - 4 03B4h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03B4h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03B8h 1580h mov (sp)+,r0 6 03BAh 0002h rti 2 0032h n=n+1 @@ -782,7 +789,7 @@ 1 isr62: 2 03BCh 1026h mov r0,-(sp) 3 03BEh 15C0h 00C8h mov #62*4,r0 ; vector in r0 - 4 03C2h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03C2h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03C6h 1580h mov (sp)+,r0 6 03C8h 0002h rti 2 0033h n=n+1 @@ -790,7 +797,7 @@ 1 isr63: 2 03CAh 1026h mov r0,-(sp) 3 03CCh 15C0h 00CCh mov #63*4,r0 ; vector in r0 - 4 03D0h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03D0h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03D4h 1580h mov (sp)+,r0 6 03D6h 0002h rti 2 0034h n=n+1 @@ -798,7 +805,7 @@ 1 isr64: 2 03D8h 1026h mov r0,-(sp) 3 03DAh 15C0h 00D0h mov #64*4,r0 ; vector in r0 - 4 03DEh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03DEh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03E2h 1580h mov (sp)+,r0 6 03E4h 0002h rti 2 0035h n=n+1 @@ -806,7 +813,7 @@ 1 isr65: 2 03E6h 1026h mov r0,-(sp) 3 03E8h 15C0h 00D4h mov #65*4,r0 ; vector in r0 - 4 03ECh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03ECh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03F0h 1580h mov (sp)+,r0 6 03F2h 0002h rti 2 0036h n=n+1 @@ -814,7 +821,7 @@ 1 isr66: 2 03F4h 1026h mov r0,-(sp) 3 03F6h 15C0h 00D8h mov #66*4,r0 ; vector in r0 - 4 03FAh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 03FAh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 03FEh 1580h mov (sp)+,r0 6 0400h 0002h rti 2 0037h n=n+1 @@ -822,7 +829,7 @@ 1 isr67: 2 0402h 1026h mov r0,-(sp) 3 0404h 15C0h 00DCh mov #67*4,r0 ; vector in r0 - 4 0408h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0408h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 040Ch 1580h mov (sp)+,r0 6 040Eh 0002h rti 2 0038h n=n+1 @@ -830,7 +837,7 @@ 1 isr70: 2 0410h 1026h mov r0,-(sp) 3 0412h 15C0h 00E0h mov #70*4,r0 ; vector in r0 - 4 0416h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0416h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 041Ah 1580h mov (sp)+,r0 6 041Ch 0002h rti 2 0039h n=n+1 @@ -838,7 +845,7 @@ 1 isr71: 2 041Eh 1026h mov r0,-(sp) 3 0420h 15C0h 00E4h mov #71*4,r0 ; vector in r0 - 4 0424h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0424h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0428h 1580h mov (sp)+,r0 6 042Ah 0002h rti 2 003Ah n=n+1 @@ -846,7 +853,7 @@ 1 isr72: 2 042Ch 1026h mov r0,-(sp) 3 042Eh 15C0h 00E8h mov #72*4,r0 ; vector in r0 - 4 0432h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0432h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0436h 1580h mov (sp)+,r0 6 0438h 0002h rti 2 003Bh n=n+1 @@ -854,7 +861,7 @@ 1 isr73: 2 043Ah 1026h mov r0,-(sp) 3 043Ch 15C0h 00ECh mov #73*4,r0 ; vector in r0 - 4 0440h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0440h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0444h 1580h mov (sp)+,r0 6 0446h 0002h rti 2 003Ch n=n+1 @@ -862,7 +869,7 @@ 1 isr74: 2 0448h 1026h mov r0,-(sp) 3 044Ah 15C0h 00F0h mov #74*4,r0 ; vector in r0 - 4 044Eh 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 044Eh 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0452h 1580h mov (sp)+,r0 6 0454h 0002h rti 2 003Dh n=n+1 @@ -870,7 +877,7 @@ 1 isr75: 2 0456h 1026h mov r0,-(sp) 3 0458h 15C0h 00F4h mov #75*4,r0 ; vector in r0 - 4 045Ch 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 045Ch 09DFh 109Eh call @#doisr ; print message for vector in r0 5 0460h 1580h mov (sp)+,r0 6 0462h 0002h rti 2 003Eh n=n+1 @@ -878,7 +885,7 @@ 1 isr76: 2 0464h 1026h mov r0,-(sp) 3 0466h 15C0h 00F8h mov #76*4,r0 ; vector in r0 - 4 046Ah 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 046Ah 09DFh 109Eh call @#doisr ; print message for vector in r0 5 046Eh 1580h mov (sp)+,r0 6 0470h 0002h rti 2 003Fh n=n+1 @@ -886,263 +893,430 @@ 1 isr77: 2 0472h 1026h mov r0,-(sp) 3 0474h 15C0h 00FCh mov #77*4,r0 ; vector in r0 - 4 0478h 09DFh 106Ah call @#doisr ; print message for vector in r0 + 4 0478h 09DFh 109Eh call @#doisr ; print message for vector in r0 5 047Ch 1580h mov (sp)+,r0 6 047Eh 0002h rti 2 0040h n=n+1 - 61 - 62 - 63 - 64 - 65 ; ---- foreground thread --------- - 66 1000h .=10000 - 67 - 68 0FFEh stack = . - 2 ; stack growns down from start + 68 69 - 70 start: - 71 1000h 15C6h 0FFEh mov #stack,sp ; init stack - 72 1004h 0A1Fh FFFEh clr @#psw ; clear priority, allow all interupts - 73 - 74 ; 1. print "Hello" msg - 75 1008h 15C1h 1104h mov #shello,r1 - 76 100Ch 09DFh 1096h call @#puts - 77 - 78 ; test vecnum printout - 79 ; mov #123456,r0 - 80 ; call @#isrmsg - 81 - 82 ; 2. echo chars until ^C hit - 83 1$: - 84 1010h 09DFh 10ECh call @#getc ; wait for char, return in r0 - 85 1014h 45C0h FF80h bic #177600,r0 ; make 7bit: clear bits <15:7> - 86 1018h A017h 0003h cmpb r0,#3 ; break by ^C ? - 87 101Ch 0320h beq 9$ ; yes: leave loop - 88 101Eh A017h 0030h cmpb r0,#60 - 89 1022h 871Ah blo 2$ ; char is < '0' - 90 1024h A017h 0037h cmpb r0,#67 - 91 1028h 8217h bhi 2$ ; char is > '7' - 92 102Ah 9002h movb r0,r2 ; save - 93 - 94 ; key is 0..7: change priority - 95 102Ch 15C1h 11BCh mov #sprio0,r1 ; print info - 96 1030h 09DFh 1096h call @#puts - 97 1034h 9080h movb r2,r0 ; restore digit - 98 1036h 09DFh 10DCh call @#putc ; print level digit - 99 103Ah 15C1h 11D0h mov #sprio1,r1 - 100 103Eh 09DFh 1096h call @#puts - 101 ; change PSW - 102 1042h 9080h movb r2,r0 - 103 1044h C5C0h 00F8h bicb #370,r0 ; ASCII -> integer - 104 1048h 0CC0h asl r0 ; move level to "priority" field in PSW - 105 104Ah 0CC0h asl r0 ; in bits 7,6,5 - 106 104Ch 0CC0h asl r0 - 107 104Eh 0CC0h asl r0 - 108 1050h 0CC0h asl r0 - 109 1052h 101Fh FFFEh mov r0,@#psw + 70 + 71 + 72 ; ---- foreground thread --------- + 73 0000h .=0 + 74 0000h 005Fh 1000h jmp @#start ; easier manual start from 0 + 75 + 76 0FFEh .=7776 + 77 0FFEh 0000h inchr: .word ; input alternative to DL11 + 78 + 79 1000h .=10000 + 80 + 81 0FFEh stack = . - 2 ; stack growns down from start + 82 + 83 start: + 84 1000h 15C6h 0FFEh mov #stack,sp ; init stack + 85 1004h 0A1Fh FFFEh clr @#psw ; clear priority, allow all interupts + 86 + 87 1008h 0005h reset ; disable INTR on all devices + 88 + 89 100Ah 0A1Fh 1346h clr @#logptr ; default: slow mode + 90 + 91 ; 1. print "Hello" msg + 92 100Eh 15C1h 11A0h mov #shello,r1 + 93 1012h 09DFh 110Ah call @#puts + 94 + 95 ; test vecnum printout + 96 ; mov #123456,r0 + 97 ; call @#isrmsg + 98 + 99 ; 2. echo chars until ^C hit + 100 1$: + 101 1016h 09DFh 1170h call @#getc ; wait for char, return in r0 + 102 101Ah 45C0h FF80h bic #177600,r0 ; make 7bit: clear bits <15:7> + 103 101Eh A017h 0003h cmpb r0,#3 ; break by ^C ? + 104 1022h 0337h beq 9$ ; yes: leave loop + 105 1024h A017h 0030h cmpb r0,#60 + 106 1028h 871Ah blo 2$ ; char is < '0' + 107 102Ah A017h 0037h cmpb r0,#67 + 108 102Eh 8217h bhi 2$ ; char is > '7' + 109 1030h 9002h movb r0,r2 ; save 110 - 111 1056h 01DCh br 1$ - 112 - 113 2$: - 114 1058h 09DFh 10DCh call @#putc ; no: echo char in r0 and loop - 115 105Ch 01D9h br 1$ - 116 - 117 9$: - 118 - 119 ; 3. print "Bye bye" msg and HALT - 120 105Eh 15C1h 11D3h mov #sbye,r1 - 121 1062h 09DFh 1096h call @#puts - 122 1066h 0000h halt - 123 - 124 ; 4. loop on CONT - 125 1068h 01CBh br start - 126 + 111 ; key is 0..7: change priority + 112 1032h 15C1h 131Bh mov #sprio0,r1 ; print info + 113 1036h 09DFh 110Ah call @#puts + 114 103Ah 9080h movb r2,r0 ; restore digit + 115 103Ch 09DFh 1160h call @#putc ; print level digit + 116 1040h 15C1h 1335h mov #sprio1,r1 + 117 1044h 09DFh 110Ah call @#puts + 118 ; change PSW + 119 1048h 9080h movb r2,r0 + 120 104Ah C5C0h 00F8h bicb #370,r0 ; ASCII -> integer + 121 104Eh 0CC0h asl r0 ; move level to "priority" field in PSW + 122 1050h 0CC0h asl r0 ; in bits 7,6,5 + 123 1052h 0CC0h asl r0 + 124 1054h 0CC0h asl r0 + 125 1056h 0CC0h asl r0 + 126 1058h 101Fh FFFEh mov r0,@#psw 127 - 128 ; ---------------------- - 129 ; Common code for all ISRs - 130 ; print vector number in r0 - 131 ; called on interrupt level - 132 - 133 doisr: - 134 106Ah 1126h mov r4,-(sp) - 135 106Ch 10E6h mov r3,-(sp) - 136 106Eh 10A6h mov r2,-(sp) - 137 1070h 1066h mov r1,-(sp) - 138 1072h 1026h mov r0,-(sp) - 139 - 140 ; print msg - 141 1074h 15C1h 10FAh mov #sisr1,r1 ; "ISR " - 142 1078h 09DFh 1096h call @#puts - 143 107Ch 1380h mov (sp),r0 ; restore vecnum - 144 107Eh 09DFh 10B2h call @#putnum - 145 1082h 15C1h 1100h mov #sisr2,r1 ; "cr lf - 146 1086h 09DFh 1096h call @#puts - 147 - 148 108Ah 1580h mov (sp)+,r0 - 149 108Ch 1581h mov (sp)+,r1 - 150 108Eh 1582h mov (sp)+,r2 - 151 1090h 1583h mov (sp)+,r3 - 152 1092h 1584h mov (sp)+,r4 - 153 1094h 0087h return - 154 + 128 105Ch 01DCh br 1$ ; OK, next char + 129 + 130 2$: ; -- eval "S", F" + 131 105Eh A017h 0053h cmpb r0,#'S + 132 1062h 0207h bne 3$ + 133 1064h 15C1h 12C0h mov #sslwmd,r1 ; "slow mode" + 134 1068h 09DFh 110Ah call @#puts + 135 106Ch 0A1Fh 1346h clr @#logptr + 136 1070h 01D2h br 1$ ; OK, next char + 137 1072h A017h 0046h 3$: cmpb r0,#'F + 138 1076h 020Ah bne 8$ ; + 139 1078h 15C1h 12EFh mov #sfstmd,r1 ; "fast mode" + 140 107Ch 09DFh 110Ah call @#puts + 141 1080h 09DFh 10E0h call @#prtlog ; print logged vectors, if any + 142 1084h 15DFh 1348h 1346h mov #logbuf,@#logptr; pointer to biuffer start + 143 108Ah 01C5h br 1$ ; OK, next char + 144 + 145 8$: + 146 108Ch 09DFh 1160h call @#putc ; no: echo char in r0 and loop + 147 1090h 01C2h br 1$ + 148 + 149 9$: + 150 + 151 ; 3. print "Bye bye" msg and HALT + 152 1092h 15C1h 1338h mov #sbye,r1 + 153 1096h 09DFh 110Ah call @#puts + 154 109Ah 0000h halt 155 - 156 ; ---------------------- - 157 ; puts - print a string - 158 ; r1 = pointer, r0,r1 changed - 159 puts: - 160 1096h 9440h movb (r1)+,r0 ; load xmt char - 161 1098h 0303h beq 1$ ; string ends with 0 - 162 109Ah 09DFh 10DCh call @#putc - 163 109Eh 01FBh br puts ; transmit nxt char of string - 164 10A0h 0087h 1$: return - 165 - 166 - 167 ; ---------------------- - 168 ; putnum - print the octal number in r0 - 169 10A2h numbf0: .blkw 10 ; space to mount number string - 170 10B2h numbf1 =. - 171 putnum: - 172 10B2h 1002h mov r0,r2 ; r2 = shifter - 173 10B4h 15C1h 10B2h mov #numbf1,r1 ; r1 = buffer pointer, backwards - 174 10B8h 95E1h 0000h movb #0,-(r1) ; set terminating 0 - 175 ; repeat 6 times - 176 10BCh 15C3h 0006h mov #6,r3 - 177 1$: - 178 10C0h 1080h mov r2,r0 - 179 ; extract lower 3 bits = octal digit - 180 10C2h 45C0h FFF8h bic #177770,r0 ; r0 &= 0x07 - 181 10C6h 65C0h 0030h add #60,r0 ; r0 += '0' - 182 10CAh 9021h movb r0,-(r1) ; write in buffer - 183 10CCh 00A1h clc - 184 10CEh 0C02h ror r2 ; shift to next digit - 185 10D0h 0C02h ror r2 - 186 10D2h 0C02h ror r2 - 187 10D4h 7ECBh sob r3,1$ ; loop for all 6 digits - 188 - 189 10D6h 09DFh 1096h call @#puts - 190 10DAh 0087h return - 191 - 192 - 193 ; DEC DL11 console I/O - 194 ; ---------------------- - 195 ; putc - output a single char - 196 ; r0 = char, r4 changed - 197 putc: - 198 10DCh 15C4h FF70h mov #dladr,r4 ; set base addr - 199 10E0h 9034h 0006h movb r0,6(r4) ; char into transmit buffer - 200 10E4h 8BF4h 0004h 1$: tstb 4(r4) ; XMT RDY? - 201 10E8h 80FDh bpl 1$ ; no, loop - 202 10EAh 0087h return - 203 - 204 ; ---------------------- - 205 ; getc - input a single char - 206 ; result in r0, r4 changed - 207 getc: - 208 10ECh 15C4h FF70h mov #dladr,r4 ; set base addr - 209 10F0h 8BCCh 1$: tstb (r4) ; RCVR DONE? - 210 10F2h 80FEh bpl 1$ ; no, loop - 211 10F4h 1D00h 0002h mov 2(r4),r0 ; return data - 212 10F8h 0087h return - 213 - 214 - 215 ; ---- Test strings, each 256 chars max --------- - 216 - 217 sisr1: ; start of ISR message - 218 10FAh 3Ch 49h 53h .ascii / message - 221 1100h 3Eh .ascii />/ - 222 1101h 0Dh 0Ah .byte 15,12 ; CR, LF, - 223 1103h 00h .byte 0 ; NUL=end marker - 224 - 225 - 226 shello: - 227 1104h 0Dh 0Ah .byte 15,12 ; CR, LF, - 228 1106h 2Ah 2Ah 2Ah .ascii /*** Interrupt and DMA test ***/ - 1109h 20h 49h 6Eh - 110Ch 74h 65h 72h - 110Fh 72h 75h 70h - 1112h 74h 20h 61h - 1115h 6Eh 64h 20h - 1118h 44h 4Dh 41h - 111Bh 20h 74h 65h - 111Eh 73h 74h 20h - 1121h 2Ah 2Ah 2Ah - 229 1124h 0Dh 0Ah .byte 15,12 ; CR, LF, - 230 1126h 54h 68h 65h .ascii /The INTR vectors 0..77 print the vector num./ - 1129h 20h 49h 4Eh - 112Ch 54h 52h 20h - 112Fh 76h 65h 63h - 1132h 74h 6Fh 72h - 1135h 73h 20h 30h - 1138h 2Eh 2Eh 37h - 113Bh 37h 20h 70h - 113Eh 72h 69h 6Eh - 1141h 74h 20h 74h - 1144h 68h 65h 20h - 1147h 76h 65h 63h - 114Ah 74h 6Fh 72h - 114Dh 20h 6Eh 75h - 1150h 6Dh 2Eh - 231 1152h 0Dh 0Ah .byte 15,12 ; CR, LF, - 232 1154h 54h 68h 65h .ascii /The foreground thread echoes typed chars, ^C HALTs./ - 1157h 20h 66h 6Fh - 115Ah 72h 65h 67h - 115Dh 72h 6Fh 75h - 1160h 6Eh 64h 20h - 1163h 74h 68h 72h - 1166h 65h 61h 64h - 1169h 20h 65h 63h - 116Ch 68h 6Fh 65h - 116Fh 73h 20h 74h - 1172h 79h 70h 65h - 1175h 64h 20h 63h - 1178h 68h 61h 72h - 117Bh 73h 2Ch 20h - 117Eh 5Eh 43h 20h - 1181h 48h 41h 4Ch - 1184h 54h 73h 2Eh - 233 1187h 0Dh 0Ah .byte 15,12 ; CR, LF, - 234 1189h 43h 68h 61h .ascii /Chars 0..7 set the new processor priority level./ - 118Ch 72h 73h 20h - 118Fh 30h 2Eh 2Eh - 1192h 37h 20h 73h - 1195h 65h 74h 20h - 1198h 74h 68h 65h - 119Bh 20h 6Eh 65h - 119Eh 77h 20h 70h - 11A1h 72h 6Fh 63h - 11A4h 65h 73h 73h - 11A7h 6Fh 72h 20h - 11AAh 70h 72h 69h - 11ADh 6Fh 72h 69h - 11B0h 74h 79h 20h - 11B3h 6Ch 65h 76h - 11B6h 65h 6Ch 2Eh - 235 11B9h 0Dh 0Ah .byte 15,12 ; CR, LF, - 236 11BBh 00h .byte 0 ; NUL=end marker - 237 - 238 sprio0: - 239 11BCh 0Dh 0Ah .byte 15,12 ; CR, LF, - 240 11BEh 43h 50h 55h .ascii /CPU priority now / - 11C1h 20h 70h 72h - 11C4h 69h 6Fh 72h - 11C7h 69h 74h 79h - 11CAh 20h 6Eh 6Fh - 11CDh 77h 20h - 241 11CFh 00h .byte 0 - 242 - 243 sprio1: - 244 11D0h 0Dh 0Ah .byte 15,12 ; CR, LF, - 245 11D2h 00h .byte 0 ; NUL=end marker - 246 - 247 sbye: - 248 11D3h 0Dh 0Ah .byte 15,12 - 249 11D5h 47h 6Fh 6Fh .ascii /Good Bye!/ - 11D8h 64h 20h 42h - 11DBh 79h 65h 21h - 250 11DEh 0Dh 0Ah 00h .byte 15,12,0 ; CR, LF, NUL=end marker - 251 - 252 - 253 .end - 253 + 156 ; 4. loop on CONT + 157 109Ch 01B1h br start + 158 + 159 + 160 ; ---------------------- + 161 ; Common code for all ISRs + 162 ; print vector number in r0 + 163 ; called on interrupt level + 164 + 165 doisr: + 166 109Eh 1126h mov r4,-(sp) + 167 10A0h 10E6h mov r3,-(sp) + 168 10A2h 10A6h mov r2,-(sp) + 169 10A4h 1066h mov r1,-(sp) + 170 10A6h 1026h mov r0,-(sp) + 171 + 172 10A8h 17C1h 1346h mov @#logptr,r1 + 173 10ACh 0304h beq 1$ + 174 ; Fast mode: log vector + 175 10AEh 1011h mov r0,(r1)+ ; store vector in array + 176 10B0h 105Fh 1346h mov r1,@#logptr ; save updated list pointer + 177 + 178 10B4h 010Fh br 9$ + 179 1$: ; "Slow mode: print msg + 180 10B6h 15C1h 1196h mov #sisr1,r1 ; "ISR " + 181 10BAh 09DFh 110Ah call @#puts + 182 10BEh 1380h mov (sp),r0 ; restore vecnum + 183 10C0h 09DFh 1126h call @#putnum + 184 10C4h 15C1h 119Ch mov #sisr2,r1 ; "cr lf + 185 10C8h 09DFh 110Ah call @#puts + 186 ; make ISR 100ms + 187 10CCh 15C0h 0064h mov #144,r0 ; 100 + 188 10D0h 09DFh 1188h call @#waitms + 189 + 190 9$: + 191 10D4h 1580h mov (sp)+,r0 + 192 10D6h 1581h mov (sp)+,r1 + 193 10D8h 1582h mov (sp)+,r2 + 194 10DAh 1583h mov (sp)+,r3 + 195 10DCh 1584h mov (sp)+,r4 + 196 10DEh 0087h return + 197 + 198 + 199 ; ---------------------- + 200 ; prtlog - print log of isr vectors + 201 ; word list from logbuf to logptr + 202 prtlog: + 203 10E0h 0BDFh 1346h tst @#logptr + 204 10E4h 0311h beq 9$ ; ptr 0, slow mode, nothing logged + 205 10E6h 15C2h 1348h mov #logbuf,r2 + 206 1$: + 207 10EAh 209Fh 1346h cmp r2,@#logptr + 208 10EEh 8608h bhis 8$ ; end of list reached + 209 10F0h 1480h mov (r2)+,r0 ; print vector from list + 210 10F2h 09DFh 1126h call @#putnum + 211 10F6h 15C0h 0020h mov #40,r0 ; print space separator + 212 10FAh 09DFh 1160h call @#putc + 213 10FEh 01F5h br 1$ + 214 8$: + 215 1100h 15C1h 1343h mov #scrlf,r1 ; CR/LF + 216 1104h 09DFh 110Ah call @#puts + 217 + 218 9$: + 219 1108h 0087h return + 220 + 221 ; ---------------------- + 222 ; puts - print a string + 223 ; r1 = pointer, r0,r1 changed + 224 puts: + 225 110Ah 9440h movb (r1)+,r0 ; load xmt char + 226 110Ch 0303h beq 1$ ; string ends with 0 + 227 110Eh 09DFh 1160h call @#putc + 228 1112h 01FBh br puts ; transmit nxt char of string + 229 1114h 0087h 1$: return + 230 + 231 + 232 ; ---------------------- + 233 ; putnum - print the octal number in r0 + 234 1116h numbf0: .blkw 10 ; space to mount number string + 235 1126h numbf1 =. + 236 putnum: + 237 1126h 1026h mov r0,-(sp) + 238 1128h 1066h mov r1,-(sp) + 239 112Ah 10A6h mov r2,-(sp) + 240 112Ch 10E6h mov r3,-(sp) + 241 112Eh 1002h mov r0,r2 ; r2 = shifter + 242 1130h 15C1h 1126h mov #numbf1,r1 ; r1 = buffer pointer, backwards + 243 1134h 95E1h 0000h movb #0,-(r1) ; set terminating 0 + 244 ; repeat 6 times + 245 1138h 15C3h 0006h mov #6,r3 + 246 1$: + 247 113Ch 1080h mov r2,r0 + 248 ; extract lower 3 bits = octal digit + 249 113Eh 45C0h FFF8h bic #177770,r0 ; r0 &= 0x07 + 250 1142h 65C0h 0030h add #60,r0 ; r0 += '0' + 251 1146h 9021h movb r0,-(r1) ; write in buffer + 252 1148h 00A1h clc + 253 114Ah 0C82h asr r2 ; shift to next digit + 254 114Ch 0C82h asr r2 + 255 114Eh 0C82h asr r2 + 256 1150h 7ECBh sob r3,1$ ; loop for all 6 digits + 257 + 258 1152h 09DFh 110Ah call @#puts + 259 1156h 1583h mov (sp)+,r3 + 260 1158h 1582h mov (sp)+,r2 + 261 115Ah 1581h mov (sp)+,r1 + 262 115Ch 1580h mov (sp)+,r0 + 263 115Eh 0087h return + 264 + 265 + 266 ; DEC DL11 console I/O + 267 ; ---------------------- + 268 ; putc - output a single char + 269 ; r0 = char, r4 changed + 270 putc: + 271 1160h 15C4h FF70h mov #dladr,r4 ; set base addr + 272 1164h 9034h 0006h movb r0,6(r4) ; char into transmit buffer + 273 1168h 8BF4h 0004h 1$: tstb 4(r4) ; XMT RDY? + 274 116Ch 80FDh bpl 1$ ; no, loop + 275 116Eh 0087h return + 276 + 277 ; ---------------------- + 278 ; getc - input a single char + 279 ; result in r0, r4 changed + 280 getc: + 281 1170h 15C4h FF70h mov #dladr,r4 ; set base addr + 282 1174h 17C0h 0FFEh 1$: mov @#inchr,r0 ; external DEPOSIT into inchr? + 283 1178h 0204h bne 9$ ; yes: process as input + 284 117Ah 8BCCh tstb (r4) ; else: RCVR DONE? + 285 117Ch 80FBh bpl 1$ ; no, loop + 286 117Eh 1D00h 0002h mov 2(r4),r0 ; return data + 287 9$: + 288 1182h 0A1Fh 0FFEh clr @#inchr + 289 1186h 0087h return + 290 + 291 ; ---------------------- + 292 ; waitms - wait r0 milli seconds + 293 waitms: + 294 1188h 1066h mov r1,-(sp) + 295 + 296 1$: ; -- outer loop + 297 118Ah 15C1h 01F4h mov #764,r1 ; 500 + 298 2$: ; -- inner loop: 1ms @ 1MHz + 299 118Eh 7E41h sob r1,2$ ; 1 cycle,2 us per loop (11/34) + 300 + 301 1190h 7E04h sob r0,1$ + 302 1192h 1581h mov (sp)+,r1 + 303 1194h 0087h return + 304 + 305 ; ---- Test strings, each 256 chars max --------- + 306 + 307 sisr1: ; start of ISR message + 308 1196h 3Ch 49h 53h .ascii / message + 311 119Ch 3Eh .ascii />/ + 312 119Dh 0Dh 0Ah .byte 15,12 ; CR, LF, + 313 119Fh 00h .byte 0 ; NUL=end marker + 314 + 315 + 316 shello: + 317 11A0h 0Dh 0Ah .byte 15,12 ; CR, LF, + 318 11A2h 2Ah 2Ah 2Ah .ascii /*** Interrupt and DMA test ***/ + 11A5h 20h 49h 6Eh + 11A8h 74h 65h 72h + 11ABh 72h 75h 70h + 11AEh 74h 20h 61h + 11B1h 6Eh 64h 20h + 11B4h 44h 4Dh 41h + 11B7h 20h 74h 65h + 11BAh 73h 74h 20h + 11BDh 2Ah 2Ah 2Ah + 319 11C0h 0Dh 0Ah .byte 15,12 ; CR, LF, + 320 11C2h 54h 68h 65h .ascii /The INTR vectors 0..77 print the vector num./ + 11C5h 20h 49h 4Eh + 11C8h 54h 52h 20h + 11CBh 76h 65h 63h + 11CEh 74h 6Fh 72h + 11D1h 73h 20h 30h + 11D4h 2Eh 2Eh 37h + 11D7h 37h 20h 70h + 11DAh 72h 69h 6Eh + 11DDh 74h 20h 74h + 11E0h 68h 65h 20h + 11E3h 76h 65h 63h + 11E6h 74h 6Fh 72h + 11E9h 20h 6Eh 75h + 11ECh 6Dh 2Eh + 321 11EEh 0Dh 0Ah .byte 15,12 ; CR, LF, + 322 11F0h 54h 68h 65h .ascii /The foreground thread echoes typed chars, ^C HALTs./ + 11F3h 20h 66h 6Fh + 11F6h 72h 65h 67h + 11F9h 72h 6Fh 75h + 11FCh 6Eh 64h 20h + 11FFh 74h 68h 72h + 1202h 65h 61h 64h + 1205h 20h 65h 63h + 1208h 68h 6Fh 65h + 120Bh 73h 20h 74h + 120Eh 79h 70h 65h + 1211h 64h 20h 63h + 1214h 68h 61h 72h + 1217h 73h 2Ch 20h + 121Ah 5Eh 43h 20h + 121Dh 48h 41h 4Ch + 1220h 54h 73h 2Eh + 323 1223h 0Dh 0Ah .byte 15,12 ; CR, LF, + 324 1225h 43h 68h 61h .ascii /Chars 0..7 set the new processor priority level./ + 1228h 72h 73h 20h + 122Bh 30h 2Eh 2Eh + 122Eh 37h 20h 73h + 1231h 65h 74h 20h + 1234h 74h 68h 65h + 1237h 20h 6Eh 65h + 123Ah 77h 20h 70h + 123Dh 72h 6Fh 63h + 1240h 65h 73h 73h + 1243h 6Fh 72h 20h + 1246h 70h 72h 69h + 1249h 6Fh 72h 69h + 124Ch 74h 79h 20h + 124Fh 6Ch 65h 76h + 1252h 65h 6Ch 2Eh + 325 1255h 0Dh 0Ah .byte 15,12 ; CR, LF, + 326 1257h 53h 20h 3Dh .ascii /S = slow mode: ISR prints message (default)/ + 125Ah 20h 73h 6Ch + 125Dh 6Fh 77h 20h + 1260h 6Dh 6Fh 64h + 1263h 65h 3Ah 20h + 1266h 49h 53h 52h + 1269h 20h 70h 72h + 126Ch 69h 6Eh 74h + 126Fh 73h 20h 6Dh + 1272h 65h 73h 73h + 1275h 61h 67h 65h + 1278h 20h 28h 64h + 127Bh 65h 66h 61h + 127Eh 75h 6Ch 74h + 1281h 29h + 327 1282h 0Dh 0Ah .byte 15,12 ; CR, LF, + 328 1284h 46h 20h 3Dh .ascii /F = fast mode: ISR logs vector, print and clr current log/ + 1287h 20h 66h 61h + 128Ah 73h 74h 20h + 128Dh 6Dh 6Fh 64h + 1290h 65h 3Ah 20h + 1293h 49h 53h 52h + 1296h 20h 6Ch 6Fh + 1299h 67h 73h 20h + 129Ch 76h 65h 63h + 129Fh 74h 6Fh 72h + 12A2h 2Ch 20h 70h + 12A5h 72h 69h 6Eh + 12A8h 74h 20h 61h + 12ABh 6Eh 64h 20h + 12AEh 63h 6Ch 72h + 12B1h 20h 63h 75h + 12B4h 72h 72h 65h + 12B7h 6Eh 74h 20h + 12BAh 6Ch 6Fh 67h + 329 12BDh 0Dh 0Ah .byte 15,12 ; CR, LF, + 330 12BFh 00h .byte 0 ; NUL=end marker + 331 + 332 sslwmd: + 333 12C0h 53h 6Ch 6Fh .ascii /Slow mode: called ISR prints vector directly/ + 12C3h 77h 20h 6Dh + 12C6h 6Fh 64h 65h + 12C9h 3Ah 20h 63h + 12CCh 61h 6Ch 6Ch + 12CFh 65h 64h 20h + 12D2h 49h 53h 52h + 12D5h 20h 70h 72h + 12D8h 69h 6Eh 74h + 12DBh 73h 20h 76h + 12DEh 65h 63h 74h + 12E1h 6Fh 72h 20h + 12E4h 64h 69h 72h + 12E7h 65h 63h 74h + 12EAh 6Ch 79h + 334 12ECh 0Dh 0Ah .byte 15,12 ; CR, LF, + 335 12EEh 00h .byte 0 ; NUL=end marker + 336 + 337 sfstmd: + 338 12EFh 46h 61h 73h .ascii /Fast mode: called ISR vectors are logged:/ + 12F2h 74h 20h 6Dh + 12F5h 6Fh 64h 65h + 12F8h 3Ah 20h 63h + 12FBh 61h 6Ch 6Ch + 12FEh 65h 64h 20h + 1301h 49h 53h 52h + 1304h 20h 76h 65h + 1307h 63h 74h 6Fh + 130Ah 72h 73h 20h + 130Dh 61h 72h 65h + 1310h 20h 6Ch 6Fh + 1313h 67h 67h 65h + 1316h 64h 3Ah + 339 1318h 0Dh 0Ah .byte 15,12 ; CR, LF, + 340 131Ah 00h .byte 0 ; NUL=end marker + 341 + 342 sprio0: + 343 131Bh 0Dh 0Ah .byte 15,12 ; CR, LF, + 344 131Dh 43h 50h 55h .ascii /CPU priority level now / + 1320h 20h 70h 72h + 1323h 69h 6Fh 72h + 1326h 69h 74h 79h + 1329h 20h 6Ch 65h + 132Ch 76h 65h 6Ch + 132Fh 20h 6Eh 6Fh + 1332h 77h 20h + 345 1334h 00h .byte 0 + 346 + 347 sprio1: + 348 1335h 0Dh 0Ah .byte 15,12 ; CR, LF, + 349 1337h 00h .byte 0 ; NUL=end marker + 350 + 351 sbye: + 352 1338h 0Dh 0Ah .byte 15,12 + 353 133Ah 47h 6Fh 6Fh .ascii /Good Bye!/ + 133Dh 64h 20h 42h + 1340h 79h 65h 21h + 354 scrlf: + 355 1343h 0Dh 0Ah 00h .byte 15,12,0 ; CR, LF, NUL=end marker + 356 + 357 ; .byte 0 ; make addr even + 358 + 359 ; in "fast" mode, ISR calls fill this array with called vectors + 360 logptr: + 361 1346h 0000h .word ; addr of next logentry to fill. 0 in "slow" mode + 362 + 363 logbuf: + 364 + 365 + 366 .end + 366 diff --git a/10.01_base/3_test/intrtst.mac b/10.01_base/3_test/intrtst.mac index 2f88bcd..710c4e7 100644 --- a/10.01_base/3_test/intrtst.mac +++ b/10.01_base/3_test/intrtst.mac @@ -7,11 +7,18 @@ ; 1.1. print a start message, ; 1.2. echoes chars typed to the output until ^C is hit ; Chars 0..7 set the new processor priority level. - ; 1.3. prints an end message and HALTs. + ; 1.3 prints an end message and HALTs. ; 1.4. on CONT it repeats. ; ; 2. For INTR test, the 256 vectors 0,4,10,14,..374 each print ; a string on interrupt. + ; In "Slow" mode, the ISR prints the vector directly + ; (ISR rountine > 100ms) + ; In "Fast" mode, the ISR logs the vector in a list + ; (It can be printed later with "F" + ; + ; As alternative to input over DL11, + ; "keyboard input chars" can be deposited into 7776 ; ; Contact: Joerg Hoppe / j_hoppe@t-online.de / www.retromcp.com @@ -63,6 +70,12 @@ isr'vecidx: ; ---- foreground thread --------- + .=0 + jmp @#start ; easier manual start from 0 + + .=7776 +inchr: .word ; input alternative to DL11 + .=10000 stack = . - 2 ; stack growns down from start @@ -71,6 +84,10 @@ start: mov #stack,sp ; init stack clr @#psw ; clear priority, allow all interupts + reset ; disable INTR on all devices + + clr @#logptr ; default: slow mode + ; 1. print "Hello" msg mov #shello,r1 call @#puts @@ -108,9 +125,24 @@ start: asl r0 mov r0,@#psw - br 1$ + br 1$ ; OK, next char -2$: +2$: ; -- eval "S", F" + cmpb r0,#'S + bne 3$ + mov #sslwmd,r1 ; "slow mode" + call @#puts + clr @#logptr + br 1$ ; OK, next char +3$: cmpb r0,#'F + bne 8$ ; + mov #sfstmd,r1 ; "fast mode" + call @#puts + call @#prtlog ; print logged vectors, if any + mov #logbuf,@#logptr; pointer to biuffer start + br 1$ ; OK, next char + +8$: call @#putc ; no: echo char in r0 and loop br 1$ @@ -137,14 +169,25 @@ doisr: mov r1,-(sp) mov r0,-(sp) - ; print msg + mov @#logptr,r1 + beq 1$ + ; Fast mode: log vector + mov r0,(r1)+ ; store vector in array + mov r1,@#logptr ; save updated list pointer + + br 9$ +1$: ; "Slow mode: print msg mov #sisr1,r1 ; "ISR " call @#puts mov (sp),r0 ; restore vecnum call @#putnum mov #sisr2,r1 ; "cr lf call @#puts + ; make ISR 100ms + mov #144,r0 ; 100 + call @#waitms +9$: mov (sp)+,r0 mov (sp)+,r1 mov (sp)+,r2 @@ -153,6 +196,28 @@ doisr: return + ; ---------------------- + ; prtlog - print log of isr vectors + ; word list from logbuf to logptr +prtlog: + tst @#logptr + beq 9$ ; ptr 0, slow mode, nothing logged + mov #logbuf,r2 +1$: + cmp r2,@#logptr + bhis 8$ ; end of list reached + mov (r2)+,r0 ; print vector from list + call @#putnum + mov #40,r0 ; print space separator + call @#putc + br 1$ +8$: + mov #scrlf,r1 ; CR/LF + call @#puts + +9$: + return + ; ---------------------- ; puts - print a string ; r1 = pointer, r0,r1 changed @@ -169,6 +234,10 @@ puts: numbf0: .blkw 10 ; space to mount number string numbf1 =. putnum: + mov r0,-(sp) + mov r1,-(sp) + mov r2,-(sp) + mov r3,-(sp) mov r0,r2 ; r2 = shifter mov #numbf1,r1 ; r1 = buffer pointer, backwards movb #0,-(r1) ; set terminating 0 @@ -181,12 +250,16 @@ putnum: add #60,r0 ; r0 += '0' movb r0,-(r1) ; write in buffer clc - ror r2 ; shift to next digit - ror r2 - ror r2 + asr r2 ; shift to next digit + asr r2 + asr r2 sob r3,1$ ; loop for all 6 digits call @#puts + mov (sp)+,r3 + mov (sp)+,r2 + mov (sp)+,r1 + mov (sp)+,r0 return @@ -206,11 +279,28 @@ putc: ; result in r0, r4 changed getc: mov #dladr,r4 ; set base addr -1$: tstb (r4) ; RCVR DONE? +1$: mov @#inchr,r0 ; external DEPOSIT into inchr? + bne 9$ ; yes: process as input + tstb (r4) ; else: RCVR DONE? bpl 1$ ; no, loop mov 2(r4),r0 ; return data +9$: + clr @#inchr return + ; ---------------------- + ; waitms - wait r0 milli seconds +waitms: + mov r1,-(sp) + +1$: ; -- outer loop + mov #764,r1 ; 500 +2$: ; -- inner loop: 1ms @ 1MHz + sob r1,2$ ; 1 cycle,2 us per loop (11/34) + + sob r0,1$ + mov (sp)+,r1 + return ; ---- Test strings, each 256 chars max --------- @@ -233,11 +323,25 @@ shello: .byte 15,12 ; CR, LF, .ascii /Chars 0..7 set the new processor priority level./ .byte 15,12 ; CR, LF, + .ascii /S = slow mode: ISR prints message (default)/ + .byte 15,12 ; CR, LF, + .ascii /F = fast mode: ISR logs vector, print and clr current log/ + .byte 15,12 ; CR, LF, + .byte 0 ; NUL=end marker + +sslwmd: + .ascii /Slow mode: called ISR prints vector directly/ + .byte 15,12 ; CR, LF, + .byte 0 ; NUL=end marker + +sfstmd: + .ascii /Fast mode: called ISR vectors are logged:/ + .byte 15,12 ; CR, LF, .byte 0 ; NUL=end marker sprio0: .byte 15,12 ; CR, LF, - .ascii /CPU priority now / + .ascii /CPU priority level now / .byte 0 sprio1: @@ -247,7 +351,16 @@ sprio1: sbye: .byte 15,12 .ascii /Good Bye!/ +scrlf: .byte 15,12,0 ; CR, LF, NUL=end marker + ; .byte 0 ; make addr even + + ; in "fast" mode, ISR calls fill this array with called vectors +logptr: + .word ; addr of next logentry to fill. 0 in "slow" mode + +logbuf: + .end diff --git a/10.01_base/3_test/latchtest.LPF b/10.01_base/3_test/latchtest.LPF deleted file mode 100644 index ac0be68..0000000 --- a/10.01_base/3_test/latchtest.LPF +++ /dev/null @@ -1,2171 +0,0 @@ -Version2.172364 CAUTION: Do not change the contents of this file. File corruption is probable! -DebugData498035514 -SampleData342048330330 -{ - D0,D1,D2,D3,D4,D5,D6,D7,D8,D9,D10,D11,D12,D13,D14,D15,D16,D17,D18,D19,D20,D21,D22,D23,D24,D25,D26,D27,D28,D29,D30,D31,CLK1,CLK2,Count - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,1,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,0,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,0,0,1,0,0,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,0,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,1,1,1,0,0,0,0,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 0,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,0,0,0,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 - 1,1,0,1,1,U,U,U,0,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,U,U,U,U,U,U,U,U,U,U,1 -} -ShowGraticuleTrue -ShowTriggerTrue -ShowCursorsTrue -LastVisibleCursor2 -UnitsTimeTrue -ColumnsVisibleTrue -ScaleRelativeToReferenceTrue -ScaleFactor0.00000002 -ReferenceOffset0 -ReferencePosition1 -StatesOnlyTrue -CursorSnapTrue -SaveOnAcqFalse -SaveOnAcqAction0 -SaveOnAcqMaxFiles1 -SaveOnAcqHoldoff0 -USB_ErrorCount14 -InvertedChannelListFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalseFalse -ExportFormat2 -ExportRadix1 -ExportFile -CursorOffsetSamples0-52555412577742 -CursorOffsetTime0-0.000000010.000005110.0000008250.0000011550.000001485 -CursorInterlock-1-1-1-1-1-1 -ControlSelections032445674567 -ControlValues1717161.532301 -SignalsSEL0SEL1SEL2WRITEPRUTESTData5Data6Data7Data8Data9Data10Data11Data12Data13Data14Data15Data16Data17Data18Data19Data20Data21Data22Data23Data24Data25Data26Data27Data28Data29Data30Data31Clock1Clock2 -GroupData[3..0]121-1True0,1,2,3False0-1 -GroupData[7..0]121-1True0,1,2,3,4,5,6,7False0-1 -GroupData[15..0]121-1True0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15False0-1 -GroupData[31..0]121-1True0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31False0-1 -GroupI2C Example121-1True0,1False00022True00 -GroupSPI Example121-1True0,1,2False0108True0False0.00000100.0000102200FalseFalseFalseTrue -GroupQuad SPI Example121-1True4,5False0502True0False0.00000100.0000102500FalseFalseFalseTrue0False -GroupRS232 Example121-1True0False028False009600True12False00 -GroupCAN Example121-1True0False04125000012True0.750True0.25 -Group1-Wire Example121-1True0False06False200 -GroupSmart Card Example121-1True0False038False219600True02False00 -GroupREG_SEL12-12True0,1,2False0-1 -GroupDataToPRU12-16True8,9,10,11,12,13,14,15False0-1 -GroupDataFromPRU12-115True16,17,18,19,20,21,22,23False0-1 -Row3False-13900False1True-1 -Row11True-1390-1False2True-1 -Row2False23900False1True-1 -Row1False23900False1True-1 -Row0False23900False1True-1 -Row12False-1390-1False2True-1 -Row15False63900False1False-1 -Row14False63900False1False-1 -Row13False63900False1False-1 -Row12False63900False1False-1 -Row11False63900False1False-1 -Row10False63900False1False-1 -Row9False63900False1False-1 -Row8False63900False1False-1 -Row13False-1390-1False2True-1 -Row23False153900False1False-1 -Row22False153900False1False-1 -Row21False153900False1False-1 -Row20False153900False1False-1 -Row19False153900False1False-1 -Row18False153900False1False-1 -Row17False153900False1False-1 -Row16False153900False1False-1 -Row4False-13900False1True-1 -Column40650 -Column50650 -Column10650 -Column20650 -Column306500 -Pattern00000000000000000000000000000000000 -Pattern10000000000000000000000000000000000 -Edge00000100000000000000000000000000000 -Edge10000000000000000000000000000000000 -IsDemoDataFalse -AcquiredSampleMode1 -AcquiredSamplePeriod0.000000002 -AcquiredChannelListTrueTrueTrueTrueTrueFalseFalseFalseTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueTrueFalseFalseFalseFalseFalseFalseFalseFalseFalseFalse -PrintCaptionTrue -PrintDateTrue -PrintMeasurementsTrue -PrintCaptionType2 -PrintCaptionStringCaption -CombineMode1 -PrequalifyFalseFalse -EdgeTerm11 -EdgeCount11 -EdgeCountMode00 -PatternTerm00 -PatternMode00 -PatternCount22 -PatternCountMode00 -RangeTerm00 -RangeGroup21 -RangeMode04 -RangeLeft50h80h -RangeRight60h100h -DurationTerm00 -DurationMode12 -DurationLeft1E-61E-6 -DurationRight2E-62E-6 -DurationUnits00 -TimingModeTrue -StateClockDelay1 -StateClockPolarity0 -StateClockSelect0 -StateClockRate1.000000E+3 -StateClockUnits1 -UseCompression1 -PrefillTimeout2 -PostfillTimeout2 -QualifyStateSamplingFalse -QualifierPolarity1 -MeasurementType2122 -MeasurementLeftTerm0222 -MeasurementRightTerm2334 -NotesString// diff --git a/10.02_devices/1_doc/DL11W/DL11-W Serial line Unit - Real-Time Clock Option Operator's Manual (May 1977, EK-DL11W-OP-001).pdf b/10.02_devices/1_doc/DL11W/DL11-W Serial line Unit - Real-Time Clock Option Operator's Manual (May 1977, EK-DL11W-OP-001).pdf new file mode 100644 index 0000000..730b434 Binary files /dev/null and b/10.02_devices/1_doc/DL11W/DL11-W Serial line Unit - Real-Time Clock Option Operator's Manual (May 1977, EK-DL11W-OP-001).pdf differ diff --git a/10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf b/10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf new file mode 100644 index 0000000..ba84d85 Binary files /dev/null and b/10.02_devices/1_doc/DL11W/UART AY-5-1013.pdf differ diff --git a/10.02_devices/2_src/cpu.cpp b/10.02_devices/2_src/cpu.cpp index d004ed0..3664a7b 100644 --- a/10.02_devices/2_src/cpu.cpp +++ b/10.02_devices/2_src/cpu.cpp @@ -36,6 +36,11 @@ #include "unibusdevice.hpp" // definition of class device_c #include "cpu.hpp" +/* Adapter procs to Angelos CPU are not members of cpu_c calss + and need one global reference. + */ +static cpu_c *the_cpu = NULL; + cpu_c::cpu_c() : unibusdevice_c() // super class constructor { @@ -46,62 +51,77 @@ cpu_c::cpu_c() : default_base_addr = 0; // none default_intr_vector = 0; default_intr_level = 0; + priority_slot.value = 1 ; + + dma_request.set_priority_slot(priority_slot.value); // init parameters runmode.value = false; 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; + + assert(the_cpu == NULL); // only one possible + the_cpu = this; // Singleton } cpu_c::~cpu_c() { + the_cpu = NULL; +} +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); - return !timeout; + uint16_t wordbuffer = (uint16_t) data; + unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATO, addr, &wordbuffer, 1); + return the_cpu->dma_request.success; } int cpu_datob(unsigned addr, unsigned data) { + uint16_t wordbuffer = (uint16_t) data; // TODO DATOB als 1 byte-DMA ! - bool timeout; - mailbox->dma.words[0] = data; - timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATOB, addr, 1); - return !timeout; - + unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATOB, addr, &wordbuffer, 1); + return the_cpu->dma_request.success; } int cpu_dati(unsigned addr, unsigned *data) { - bool timeout; - - timeout = !unibus->dma(unibus_c::ARBITRATION_MODE_MASTER, UNIBUS_CONTROL_DATI, addr, 1); - *data = mailbox->dma.words[0]; - return !timeout; + uint16_t wordbuffer; + unibusadapter->DMA(the_cpu->dma_request, true, UNIBUS_CONTROL_DATI, addr, &wordbuffer, 1); + *data = wordbuffer; + // printf("DATI; ba=%o, data=%o\n", addr, *data) ; + return the_cpu->dma_request.success; } } // background worker. -// udpate LEDS, poll switches direct to register flipflops -void cpu_c::worker(void) { +void cpu_c::worker(unsigned instance) { + UNUSED(instance); // only one timeout_c timeout; bool nxm; unsigned pc = 0; unsigned dr = 0760102; unsigned opcode = 0; (void) opcode; - while (!worker_terminate) { + while (!workers_terminate) { // run full speed! timeout.wait_us(1); @@ -115,11 +135,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 +174,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 } @@ -170,7 +184,7 @@ void cpu_c::on_init_changed(void) { // write all registers to "reset-values" if (init_asserted) { reset_unibus_registers(); - INFO("demo_io_c::on_init()"); + INFO("cpu::on_init()"); } } diff --git a/10.02_devices/2_src/cpu.hpp b/10.02_devices/2_src/cpu.hpp index 05e34f5..4ea43b0 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,26 +44,28 @@ private: public: cpu_c(); - ~cpu_c() ; + ~cpu_c(); + + // CPU accesses memory actively + dma_request_c dma_request = dma_request_c(this); + + 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 + false, "1 = CPU initializing"); + struct Bus bus; // UNIBU Sinterface of CPU + struct KA11 ka11; // Angelos CPU state // background worker function - void worker(void) override; + void worker(unsigned instance) override; // called by unibusadapter on emulated register access 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..2aac192 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, 31, 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()) { @@ -179,9 +182,10 @@ void demo_io_c::gpio_set_output(unsigned output_index, unsigned value) { // background worker. // udpate LEDS, poll switches direct to register flipflops -void demo_io_c::worker(void) { +void demo_io_c::worker(unsigned instance) { + UNUSED(instance); // only one timeout_c timeout; - while (!worker_terminate) { + while (!workers_terminate) { timeout.wait_ms(100); unsigned i; @@ -201,7 +205,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 +228,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..0bc4e71 100644 --- a/10.02_devices/2_src/demo_io.hpp +++ b/10.02_devices/2_src/demo_io.hpp @@ -50,20 +50,20 @@ 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; + void worker(unsigned instance) override; // called by unibusadapter on emulated register access 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 deleted file mode 100644 index 2b55ae0..0000000 --- a/10.02_devices/2_src/demo_regs.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* demo_regs.cpp: sample UNIBUS controller with register logic - - 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: - - 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. - - - 12-nov-2018 JH entered beta phase - - demo_regs: - a device to test the event and - implements 64 registers at start of IOpage - 0760000.. 0760076 - all registers are marked as "active": - DATI and DATO are routed via events into the controller logic - UNIBUS is stopped with long SSYN - - Registers have no functions, are simple memory cells. - */ -#include -#include -#include -//#include - -#include "utils.hpp" -#include "logger.hpp" -#include "unibus.h" -#include "unibusadapter.hpp" -#include "unibusdevice.hpp" // definition of class device_c -#include "demo_regs.hpp" - -demo_regs_c::demo_regs_c() : - unibusdevice_c() // super class constructor -{ - unsigned i; - - // static config - 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; - - register_count = 32; // up to 760076 - // registers are "active": receive "on_after_register_access" - for (i = 0; i < this->register_count; i++) { - unibusdevice_register_t *reg = &(this->registers[i]); - sprintf(reg->name, "reg[0%03o]", i); // name is register offset - reg->active_on_dati = true; // controller state change on read - reg->active_on_dato = true; // writing changes controller state - reg->reset_value = 0; - reg->writable_bits = 0xffff; // all registers are memory cells - } - // dynamic state - access_count.value = 0; - -} - -// background worker. -// Just print a heart beat -void demo_regs_c::worker(void) { - timeout_c timeout; - while (!worker_terminate) { - timeout.wait_ms(1000); - cout << "."; - } -} - -// process DATI/DATO access to one of my "active" registers -// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS. -// The time between PRU event and program flow into this callback -// is determined by ARM Linux context switch -// -// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc: -// do not read back dati_flipflops. -void demo_regs_c::on_after_register_access(unibusdevice_register_t *device_reg, - uint8_t unibus_control) { - - // emulate a plain memory cell: written values can be read unchanged - if (unibus_control == UNIBUS_CONTROL_DATI) { - } - - if (unibus_control == UNIBUS_CONTROL_DATO) - set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); - // this is also called for some DATIs, no action anyhow. - - access_count.value++; - // DEBUG writes to disk & console ... measured delay up to 30ms ! - //DEBUG(LL_DEBUG, LC_demo_regs, "[%6u] reg +%d @ %06o %s", accesscount, (int ) device_reg->index, - // 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 - } -} - -// UNIBUS INIT: clear all registers -void demo_regs_c::on_init_changed(void) { - // write all registers to "reset-values" - if (init_asserted) { - reset_unibus_registers(); - INFO("demo_regs_c::on_init()"); - } -} - diff --git a/10.02_devices/2_src/dl11w.cpp b/10.02_devices/2_src/dl11w.cpp new file mode 100644 index 0000000..9f82576 --- /dev/null +++ b/10.02_devices/2_src/dl11w.cpp @@ -0,0 +1,638 @@ +/* DL11W.cpp: sample UNIBUS controller with SLU & LTC logic + + 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: + + 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. + + 12-nov-2018 JH entered beta phase + 20/12/2018 djrm copied to make slu device + 14/01/2019 djrm adapted to use UART2 serial port + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.hpp" +#include "gpios.hpp" +#include "logsource.hpp" +#include "unibusadapter.hpp" +#include "unibusdevice.hpp" // definition of class device_c +#include "unibus.h" +#include "dl11w.hpp" + +#include "rs232.hpp" +#include "rs232adapter.hpp" + +//------------------------------------------------- + +slu_c::slu_c() : + unibusdevice_c() { + set_workers_count(2); // receiver and transmitte have own threads + + //ip_host.value = IP_HOST; // not used + //ip_port.value = IP_PORT; // not used + + // static config + name.value = "DL11"; + type_name.value = "slu_c"; + log_label = "slu"; + + break_enable.value = 1; // SW4-1 default ON + error_bits_enable.value = 1; // SE4-7 default ON + + // SLU has 2 Interrupt vectors: base = RCV, base+= XMT + // put in slot 1, closest to CPU + set_default_bus_params(SLU_ADDR, SLU_SLOT, SLU_VECTOR, SLU_LEVEL); // base addr, intr-vector, intr level + rcvintr_request.set_priority_slot(default_priority_slot); + rcvintr_request.set_level(default_intr_level); + rcvintr_request.set_vector(default_intr_vector); + // XMT INTR: lower priority->netx slot, and next vector + xmtintr_request.set_priority_slot(default_priority_slot + 1); + xmtintr_request.set_level(default_intr_level); + xmtintr_request.set_vector(default_intr_vector + 4); + + // init parameters + + // controller has some register + register_count = slu_idx_count; + + reg_rcsr = &(this->registers[slu_idx_rcsr]); // @ base addr + strcpy(reg_rcsr->name, "RCSR"); // Receiver Status Register + reg_rcsr->active_on_dati = false; + reg_rcsr->active_on_dato = true; + reg_rcsr->reset_value = 0; + reg_rcsr->writable_bits = 0xff; + + reg_rbuf = &(this->registers[slu_idx_rbuf]); // @ base addr + strcpy(reg_rbuf->name, "RBUF"); // Receiver Buffer Register + reg_rbuf->active_on_dati = true; + reg_rbuf->active_on_dato = true; // required for "active on dati"" + reg_rbuf->reset_value = 0; + reg_rbuf->writable_bits = 0x00; + + reg_xcsr = &(this->registers[slu_idx_xcsr]); // @ base addr + strcpy(reg_xcsr->name, "XCSR"); // Transmitter Status Register + reg_xcsr->active_on_dati = false; + reg_xcsr->active_on_dato = true; + reg_xcsr->reset_value = XCSR_XMIT_RDY; // set + reg_xcsr->writable_bits = 0xff; + + reg_xbuf = &(this->registers[slu_idx_xbuf]); // @ base addr + strcpy(reg_xbuf->name, "XBUF"); //Transmitter Buffer Register + reg_xbuf->active_on_dati = false; // no controller state change + reg_xbuf->active_on_dato = true; + reg_xbuf->reset_value = 0; + reg_xbuf->writable_bits = 0xff; + + // initialize serial format + serialport.value = "ttyS2"; // labeled "UART2" on PCB + baudrate.value = 9600; + mode.value = "8N1"; + + rs232adapter.rs232 = &rs232; +} + +slu_c::~slu_c() { +} + +bool slu_c::on_param_changed(parameter_c *param) { + if (param == &enabled) { + if (enabled.new_value) { + // enable SLU: setup COM serial port + // setup for BREAK and parity evaluation + rs232adapter.rcv_termios_error_encoding = true; + if (rs232.OpenComport(serialport.value.c_str(), baudrate.value, mode.value.c_str(), + true)) { + ERROR("Can not open serial port %s", serialport.value.c_str()); + return false; // reject "enable" + } + + // lock serial port and settings + serialport.readonly = true; + baudrate.readonly = true; + mode.readonly = true; + + INFO("Serial port %s opened", serialport.value.c_str()); + char buff[256]; + sprintf(buff, "Serial port %s opened\n\r", serialport.value.c_str()); + rs232.cputs(buff); + } else { + // disable SLU + rs232.CloseComport(); + // unlock serial port and settings + serialport.readonly = false; + baudrate.readonly = false; + mode.readonly = false; + INFO("Serial port %s closed", serialport.value.c_str()); + } + } + return unibusdevice_c::on_param_changed(param); // more actions (for enable) +} + +//-------------------------------------------- + +// calc static INTR condition level. +// Change of that condition calculated by intr_request_c.is_condition_raised() +bool slu_c::get_rcv_intr_level(void) { + return rcv_done && rcv_intr_enable; +} + +// Update RCSR and optionally generate INTR +void slu_c::set_rcsr_dati_value_and_INTR(void) { + uint16_t val = (rcv_active ? RCSR_RCVR_ACT : 0) | (rcv_done ? RCSR_RCVR_DONE : 0) + | (rcv_intr_enable ? RCSR_RCVR_INT_ENB : 0); + switch (rcvintr_request.edge_detect(get_rcv_intr_level())) { + case intr_request_c::INTERRUPT_EDGE_RAISING: + // set register atomically with INTR, if INTR not blocked + unibusadapter->INTR(rcvintr_request, reg_rcsr, val); + break; + case intr_request_c::INTERRUPT_EDGE_FALLING: + // BR4 is tied to monitor and enable, so raised INTRs may get canceled + unibusadapter->cancel_INTR(rcvintr_request); + set_register_dati_value(reg_rcsr, val, __func__); + break; + default: + set_register_dati_value(reg_rcsr, val, __func__); + } +} + +// PDP-11 writes into RCSR +void slu_c::eval_rcsr_dato_value(void) { + uint16_t val = get_register_dato_value(reg_rcsr); + + rcv_intr_enable = val & RCSR_RCVR_INT_ENB ? 1 : 0; + rcv_rdr_enb = val & RCSR_RDR_ENB ? 1 : 0; + if (rcv_rdr_enb) + rcv_done = 0; // raising edge clears rcv_done + // if rcvr_done and int enable goes high: INTR + set_rcsr_dati_value_and_INTR(); +} + +// Update RBUF, readonly +void slu_c::set_rbuf_dati_value(void) { + uint16_t val = 0; + if (error_bits_enable.value) { + val = (rcv_or_err ? RBUF_OR_ERR : 0) | (rcv_fr_err ? RBUF_FR_ERR : 0) + | (rcv_p_err ? RBUF_P_ERR : 0); + if (val) // set general error flag + val |= RBUF_ERROR; + } + val |= rcv_buffer; // received char in bits 7..0 + set_register_dati_value(reg_rbuf, val, __func__); +} + +bool slu_c::get_xmt_intr_level() { + return xmt_ready && xmt_intr_enable; +} + +// Update Transmit Status Register XCSR and optionally generate INTR +void slu_c::set_xcsr_dati_value_and_INTR(void) { + uint16_t val = (xmt_ready ? XCSR_XMIT_RDY : 0) | (xmt_intr_enable ? XCSR_XMIT_INT_ENB : 0) + | (xmt_maint ? XCSR_MAINT : 0) | (xmt_break ? XCSR_BREAK : 0); + switch (xmtintr_request.edge_detect(get_xmt_intr_level())) { + case intr_request_c::INTERRUPT_EDGE_RAISING: + // set register atomically with INTR, if INTR not blocked + unibusadapter->INTR(xmtintr_request, reg_xcsr, val); + break; + case intr_request_c::INTERRUPT_EDGE_FALLING: + // BR4 is tied to monitor and enable, so raised INTRs may get canceled + unibusadapter->cancel_INTR(xmtintr_request); + set_register_dati_value(reg_xcsr, val, __func__); + break; + default: + set_register_dati_value(reg_xcsr, val, __func__); + } + +} + +void slu_c::eval_xcsr_dato_value(void) { + uint16_t val = get_register_dato_value(reg_xcsr); + bool old_break = xmt_break; + xmt_intr_enable = val & XCSR_XMIT_INT_ENB ? 1 : 0; + xmt_maint = val & XCSR_MAINT ? 1 : 0; + xmt_break = val & XCSR_BREAK ? 1 : 0; + // if xmt_ready and int enable goes high: INTR + set_xcsr_dati_value_and_INTR(); + + if (old_break != xmt_break) { + // re-evaluate break state on bit change + if (break_enable.value) + rs232.SetBreak(xmt_break); + else + rs232.SetBreak(0); + } +} + +void slu_c::eval_xbuf_dato_value(void) { + // transmit data buffer contains only the character in bits 7..0 + xmt_buffer = get_register_dato_value(reg_xbuf) & 0xff; +} + +// process DATI/DATO access to one of my "active" registers +// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS. +// The time between PRU event and program flow into this callback +// is determined by ARM Linux context switch +// +// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc: +// do not read back dati_flipflops. +void slu_c::on_after_register_access(unibusdevice_register_t *device_reg, + uint8_t unibus_control) { + +// if (unibus_control == UNIBUS_CONTROL_DATO) // bus write +// set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); + + if (unibusadapter->line_INIT) + return; // do nothing wile reset + + switch (device_reg->index) { + + case slu_idx_rcsr: + if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write into RCSR + pthread_mutex_lock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses + eval_rcsr_dato_value(); // may generate INTR + set_rcsr_dati_value_and_INTR(); + // ignore reader enable + pthread_mutex_unlock(&on_after_rcv_register_access_mutex); + } + break; + case slu_idx_rbuf: { // DATI/DATO: is read only, but write also clears "rcvr_done" + // signal data has been read from bus + pthread_mutex_lock(&on_after_rcv_register_access_mutex); + rcv_done = 0; + set_rcsr_dati_value_and_INTR(); + pthread_mutex_unlock(&on_after_rcv_register_access_mutex); + } + break; + case slu_idx_xcsr: + if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write + pthread_mutex_lock(&on_after_xmt_register_access_mutex); + eval_xcsr_dato_value(); // may trigger INTR + set_xcsr_dati_value_and_INTR(); + pthread_mutex_unlock(&on_after_xmt_register_access_mutex); + } + break; + case slu_idx_xbuf: + if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write into XBUF + pthread_mutex_lock(&on_after_xmt_register_access_mutex); + eval_xbuf_dato_value(); + xmt_ready = 0; // signal worker: xmt_data pending + set_xcsr_dati_value_and_INTR(); + // on_after_register_access_cond used for xmt worker + pthread_cond_signal(&on_after_xmt_register_access_cond); + pthread_mutex_unlock(&on_after_xmt_register_access_mutex); + } + break; + default: + break; + } + +} + +void slu_c::on_power_changed(void) { + if (power_down) { // power-on defaults + } +} + +// UNIBUS INIT: clear all registers +void slu_c::on_init_changed(void) { + // write all registers to "reset-values" + if (init_asserted) { + reset_unibus_registers(); + rcv_active = 0; + rcv_done = 0; + rcv_intr_enable = 0; + rcv_or_err = 0; + rcv_fr_err = 0; + rcv_p_err = 0; + rcv_buffer = 0; + xmt_ready = 1; + xmt_intr_enable = 0; + xmt_maint = 0; + xmt_break = 0; + rcvintr_request.edge_detect_reset(); + xmtintr_request.edge_detect_reset(); + // INFO("slu_c::on_init()"); + } +} + +// background worker. +void slu_c::worker_rcv(void) { + timeout_c timeout; + int n; + unsigned char buffer[BUFLEN + 1]; + + // poll with frequency > baudrate, to see single bits + //unsigned poll_periods_us = 1000000 / baudrate.value; + + /* Receiver not time critical? UARTS are buffering + So if thread is swapped out and back a burst of characters appear. + -> Wait after each character for transfer time before polling + RS232 again. + */ + unsigned poll_periods_us = (rs232.CharTransmissionTime_us * 9) / 10; + // poll a bit faster to be ahead of char stream. + // don't oversample: PDP-11 must process char in that time + + // worker_init_realtime_priority(rt_device); + + while (!workers_terminate) { + timeout.wait_us(poll_periods_us); + if (unibusadapter->line_INIT) + continue; // do nothing while reset + // "query + // rcv_active: can only be set by polling the UART input GPIO pin? + // at the moments, it is only sent on maintenance loopback xmt + /* read serial data, if any */ + if (rs232adapter.byte_rcv_poll(buffer)) { + pthread_mutex_lock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses + rcv_or_err = rcv_fr_err = rcv_p_err = 0; + if (rcv_done) // not yet cleared? overrun! + rcv_or_err = 1; + if (buffer[0] == 0xff) { + /* How to receive framing and parity errors: see termios(3) + If IGNPAR=0, PARMRK=1: error on received as \377 \0 + \377 received as \377 \377 + */ + n = rs232adapter.byte_rcv_poll(buffer); + assert(n); // next char after 0xff escape immediately available + + if (buffer[0] == 0) { // error flags + rcv_fr_err = rcv_p_err = 1; + n = rs232adapter.byte_rcv_poll(buffer); + assert(n); // next char after 0xff 0 seq is data" + rcv_buffer = buffer[0]; + } else if (buffer[0] == 0xff) { // enocoded 0xff + rcv_buffer = 0xff; + } else { + WARNING("Received 0xff seqeuence"); + rcv_buffer = buffer[0]; + } + } else + // received non escaped data byte + rcv_buffer = buffer[0]; + rcv_done = 1; + rcv_active = 0; + set_rbuf_dati_value(); + set_rcsr_dati_value_and_INTR(); // INTR! + pthread_mutex_unlock(&on_after_rcv_register_access_mutex); // signal changes atomic against UNIBUS accesses + } + } +} + +void slu_c::worker_xmt(void) { + timeout_c timeout; + + assert(!pthread_mutex_lock(&on_after_register_access_mutex)); + + // Transmitter not time critical + // worker_init_realtime_priority(rt_device); + + while (!workers_terminate) { + // 1. wait for xmt signal + int res = pthread_cond_wait(&on_after_xmt_register_access_cond, + &on_after_xmt_register_access_mutex); + // on_after_xmt_register_access_mutex remains locked all the time + if (res != 0) { + ERROR("SLU::worker_xmt() pthread_cond_wait = %d = %s>", res, strerror(res)); + continue; + } + + // 2. transmit + rs232adapter.byte_xmt_send(xmt_buffer); + xmt_ready = 0; + set_xcsr_dati_value_and_INTR(); + if (xmt_maint) { // loop back: simulate data byte coming in + pthread_mutex_lock(&on_after_rcv_register_access_mutex); + rcv_active = 1; + set_rcsr_dati_value_and_INTR(); + pthread_mutex_unlock(&on_after_rcv_register_access_mutex); + } + + // 3. wait for data byte being shifted out + pthread_mutex_unlock(&on_after_xmt_register_access_mutex); + timeout.wait_us(rs232.CharTransmissionTime_us); + pthread_mutex_lock(&on_after_xmt_register_access_mutex); + if (xmt_maint) + // put sent byte into rcv buffer, receiver will poll it + rs232adapter.byte_loopback(xmt_buffer); + xmt_ready = 1; + set_xcsr_dati_value_and_INTR(); + + // has rcv or xmt interrupt priority on maintennace loop back + } + assert(!pthread_mutex_unlock(&on_after_xmt_register_access_mutex)); +} + +void slu_c::worker(unsigned instance) { + // 2 parallel worker() instances: 0 and 1 + if (instance == 0) + worker_rcv(); + else + worker_xmt(); + +} + +//-------------------------------------------------------------------------------------------------- + +ltc_c::ltc_c() : + unibusdevice_c() // super class constructor +{ + + // static config + name.value = "KW11"; + type_name.value = "ltc_c"; + log_label = "ltc"; + // slot = 3: + set_default_bus_params(LTC_ADDR, LTC_SLOT, LTC_VECTOR, LTC_LEVEL); // base addr, intr-vector, intr level + intr_request.set_priority_slot(default_priority_slot); + intr_request.set_level(default_intr_level); + intr_request.set_vector(default_intr_vector); + + // controller has only one register + register_count = 1; + reg_lks = &(this->registers[0]); // @ base addr + strcpy(reg_lks->name, "LKS"); // Line Clock Status Register + reg_lks->active_on_dati = false; // status polled by CPU, not active + reg_lks->active_on_dato = true; + reg_lks->reset_value = 0; + reg_lks->writable_bits = LKS_INT_ENB; // interrupt enable + + // init parameters + frequency.value = 50; + ltc_enable.value = true; + + // init controller state + intr_enable = 0; + intr_monitor = 0; +} + +ltc_c::~ltc_c() { +} + +bool ltc_c::on_param_changed(parameter_c *param) { + // no own parameter or "enable" logic here + if (param == &frequency) { + // accept only these + return (frequency.new_value == 50 || frequency.new_value == 60); + } else + return unibusdevice_c::on_param_changed(param); // more actions (for enable) +} + +// calc static INTR condition level. +// Change of that condition calculated by intr_request_c.is_condition_raised() +// on raising edge. +bool ltc_c::get_intr_signal_level() { + return intr_monitor && intr_enable; +} + +// set status register, and optionally generate INTR +// intr_raise: if inactive->active transition of interrupt condition detected. +void ltc_c::set_lks_dati_value_and_INTR(void) { + uint16_t val = (intr_monitor ? LKS_INT_MON : 0) | (intr_enable ? LKS_INT_ENB : 0); + switch (intr_request.edge_detect(get_intr_signal_level())) { + case intr_request_c::INTERRUPT_EDGE_RAISING: + // set register atomically with INTR, if INTR not blocked + unibusadapter->INTR(intr_request, reg_lks, val); + break; + case intr_request_c::INTERRUPT_EDGE_FALLING: + // BR6 is tied to monitor and enable, so raised INTRs may get canceled + unibusadapter->cancel_INTR(intr_request); + set_register_dati_value(reg_lks, val, __func__); + break; + default: + set_register_dati_value(reg_lks, val, __func__); + } +} + +// process DATI/DATO access to one of my "active" registers +void ltc_c::on_after_register_access(unibusdevice_register_t *device_reg, + uint8_t unibus_control) { + pthread_mutex_lock(&on_after_register_access_mutex); + + if (unibus_control == UNIBUS_CONTROL_DATO) // bus write + set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); + + switch (device_reg->index) { + + case 0: // LKS + if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write + intr_enable = !!(reg_lks->active_dato_flipflops & LKS_INT_ENB); + intr_monitor = !!(reg_lks->active_dato_flipflops & LKS_INT_MON); + set_lks_dati_value_and_INTR(); + } + break; + + default: + break; + } + pthread_mutex_unlock(&on_after_register_access_mutex); + +} + +void ltc_c::on_power_changed(void) { + if (power_down) { // power-on defaults + } +} + +// UNIBUS INIT: clear all registers +void ltc_c::on_init_changed(void) { + // write all registers to "reset-values" + if (init_asserted) { + reset_unibus_registers(); + intr_enable = 0; + intr_monitor = 1; + + intr_request.edge_detect_reset(); + // INFO("ltc_c::on_init()"); + } +} + +/* background worker. + Freqnecy of clock signal edges is tied to system time, not to "wait" periods + This worker may get delayed arbitray amount of time (as every thread), + lost edges are compensated + */ +void ltc_c::worker(unsigned instance) { + UNUSED(instance); // only one + timeout_c global_time; + timeout_c timeout; + int64_t global_next_edge_ns; + + INFO("KW11 time resolution is < %u us", + (unsigned )(global_time.get_resolution_ns() / 1000)); + global_time.start_ns(0); + global_next_edge_ns = global_time.elapsed_ns(); + uint64_t global_edge_count = 0; + while (!workers_terminate) { + + // signal egde period may change if 50/60 Hz is changed + uint64_t edge_period_ns = BILLION / (2 * frequency.value); + uint64_t wait_ns; + // overdue_ns: time which signal edge is too late + int64_t overdue_ns = (int64_t) global_time.elapsed_ns() - global_next_edge_ns; + // INFO does not work on 64 ints + // printf("elapsed [ms] =%u, overdue [us] =%u\n", (unsigned) global_time.elapsed_ms(), (unsigned) overdue_ns/1000) ; + // if overdue_ns positive, next signal edge should have occured + if (overdue_ns < 0) { + wait_ns = -overdue_ns; // wait until next edge time reached + } else { + // time for next signal edge reached + if (ltc_enable.value) { + global_edge_count++; + clock_signal = !clock_signal; // square wave + if (clock_signal) { + intr_monitor = 1; + pthread_mutex_lock(&on_after_register_access_mutex); + set_lks_dati_value_and_INTR(); + pthread_mutex_unlock(&on_after_register_access_mutex); + } + } else + // clock disconnected + clock_signal = 0; + + // time of next signal edge + global_next_edge_ns += edge_period_ns; + // overdue_ns now time which next signal edge is too late + overdue_ns -= edge_period_ns; + + if (overdue_ns < 0) + // next edge now in future: wait exact + wait_ns = -overdue_ns; + else + // next edge still in past: + // wait shorter than signal edge period to keep up slowly + wait_ns = edge_period_ns / 2; + //if ((global_edge_count % 100) == 0) + // INFO("LTC: %u secs by edges", (unsigned)(global_edge_count/100) ) ; + } + timeout.wait_ns(wait_ns); + } +} + diff --git a/10.02_devices/2_src/dl11w.hpp b/10.02_devices/2_src/dl11w.hpp new file mode 100644 index 0000000..7b61613 --- /dev/null +++ b/10.02_devices/2_src/dl11w.hpp @@ -0,0 +1,243 @@ +/* DL11W.hpp: sample UNIBUS controller with SLU & LTC logic + + 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: + + 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. + + + 12-nov-2018 JH entered beta phase + 20/12/2018 djrm copied to make DL11-W device + */ +#ifndef _DL11W_HPP_ +#define _DL11W_HPP_ + +#include + +using namespace std; + +#include "utils.hpp" +#include "unibusdevice.hpp" +#include "parameter.hpp" +#include "rs232.hpp" +#include "rs232adapter.hpp" + + +// socket console settings +//#define IP_PORT 5001 +//#define IP_HOST "localhost" + +// bus properties +#define DL11A 1 +#if DL11A // console (teletype keyboard & printer) +#define SLU_ADDR 0777560 +#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT +#define SLU_LEVEL 04 +#define SLU_VECTOR 060 // RCV +0, XMT +4 +#elif DL11B // paper tape punch and reader +#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT +#define SLU_ADDR 0777550 +#define SLU_LEVEL 04 +#define SLU_VECTOR 070 +#else // other serial device +#define SLU_ADDR 0776500 +#define SLU_SLOT 1 // Close to CPU. RCV, also SLOT+1 is used for XMT +#define SLU_LEVEL 04 +#define SLU_VECTOR 0300 +#endif +#define LTC_ADDR 0777546 +#define LTC_SLOT (SLU_SLOT+2) +#define LTC_LEVEL 06 +#define LTC_VECTOR 0100 + +// global text buffer size for hostname etc +#define BUFLEN 32 + +// register bit definitions +#define RCSR_RCVR_ACT 0004000 +#define RCSR_RCVR_DONE 0000200 +#define RCSR_RCVR_INT_ENB 0000100 +#define RCSR_RDR_ENB 0000001 + +#define RBUF_ERROR 0100000 +#define RBUF_OR_ERR 0040000 +#define RBUF_FR_ERR 0020000 +#define RBUF_P_ERR 0010000 + +#define XCSR_XMIT_RDY 0000200 +#define XCSR_XMIT_INT_ENB 0000100 +#define XCSR_MAINT 0000004 +#define XCSR_BREAK 0000001 + +#define LKS_INT_ENB 0000100 +#define LKS_INT_MON 0000200 + +// background task sleep times +#define SLU_MSRATE_MS 10 +#define LTC_MSRATE_MS 50 + +// unibus register indices +enum slu_reg_index { + slu_idx_rcsr = 0, slu_idx_rbuf, slu_idx_xcsr, slu_idx_xbuf, slu_idx_count, +}; + +// ------------------------------------------ SLU ----------------------------- +class slu_c: public unibusdevice_c { +private: + rs232_c rs232; /// COM port interface +public: + rs232adapter_c rs232adapter; /// stream router + + private: + unibusdevice_register_t *reg_rcsr; + unibusdevice_register_t *reg_rbuf; + unibusdevice_register_t *reg_xcsr; + unibusdevice_register_t *reg_xbuf; + + // two interrupts of same level, need slot and slot+1 + intr_request_c rcvintr_request = intr_request_c(this); + intr_request_c xmtintr_request = intr_request_c(this); + + /*** SLU is infact 2 independend devices: RCV and XMT ***/ + pthread_cond_t on_after_rcv_register_access_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t on_after_rcv_register_access_mutex = PTHREAD_MUTEX_INITIALIZER; + + // bits in registers + bool rcv_active; /// while a char is receive ... not available + bool rcv_done; // char received. INTR. cleared by rdr_enable, access to rbuf, init + bool rcv_overrun;bool rcv_intr_enable; // receiver interrupt enabled + bool rcv_or_err; // receiver overrun: rcv_done 1 on receive + bool rcv_fr_err; // framing error. high on received BREAK + bool rcv_p_err; // parity error + uint8_t rcv_buffer;bool rcv_rdr_enb; // reader enable. Cleared by receive or init + + pthread_cond_t on_after_xmt_register_access_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t on_after_xmt_register_access_mutex = PTHREAD_MUTEX_INITIALIZER;bool xmt_ready; // transmitter ready. INTR, cleared on XBUF access + bool xmt_intr_enable; // receiver interrupt enabled + + bool xmt_maint; // set 1 for local loop back + bool xmt_break; // transmit continuous break + uint8_t xmt_buffer; + + // convert between register ansd state variables + bool get_rcv_intr_level(void);bool get_xmt_intr_level(void); + + void set_rcsr_dati_value_and_INTR(void); + void eval_rcsr_dato_value(void); + void set_rbuf_dati_value(void); + void set_xcsr_dati_value_and_INTR(void); + void eval_xcsr_dato_value(void); + void eval_xbuf_dato_value(void); + +public: + + slu_c(); + ~slu_c(); + + //parameter_string_c ip_host = parameter_string_c( this, "SLU socket IP host", "host", /*readonly*/ false, "ip hostname"); + //parameter_unsigned_c ip_port = parameter_unsigned_c(this, "SLU socket IP serialport", "serialport", /*readonly*/ false, "", "%d", "ip serialport", 32, 10); + parameter_string_c serialport = parameter_string_c(this, "serialport", "p", /*readonly*/ + false, "Linux serial port: \"ttyS1\" or \"ttyS2\""); + + parameter_unsigned_c baudrate = parameter_unsigned_c(this, "baudrate", "b", /*readonly*/ + false, "", "%d", "Baudrate: 110, 300, ... 38400", 38400, 10); + // 40kbaud -> 25us bit polling period needed + + parameter_string_c mode = parameter_string_c(this, "mode", "m", /*readonly*/false, + "Mode: 8N1, 7E1, ... "); + + parameter_bool_c error_bits_enable = parameter_bool_c(this, "errorbits", "eb", /*readonly*/ + false, "Enable error bits (M7856 SW4-7)"); + + parameter_bool_c break_enable = parameter_bool_c(this, "break", "b", /*readonly*/false, + "Enable BREAK transmission (M7856 SW4-1)"); + +#ifdef USED + // @David: duplicating device registers as parameters is not necessary ... + // they can be seen with "exam" anyhow. + parameter_unsigned_c rcsr = parameter_unsigned_c(this, "Receiver Status Register", "rcsr", /*readonly*/ + false, "", "%o", "Internal state", 32, 8); + parameter_unsigned_c rbuf = parameter_unsigned_c(this, "Receiver Buffer Register", "rbuf", /*readonly*/ + false, "", "%o", "Internal state", 32, 8); + parameter_unsigned_c xcsr = parameter_unsigned_c(this, "Transmitter Status Register", + "xcsr", /*readonly*/false, "", "%o", "Internal state", 32, 8); + parameter_unsigned_c xbuf = parameter_unsigned_c(this, "Transmitter Buffer Register", + "xbuf", /*readonly*/false, "", "%o", "Internal state", 32, 8); + + parameter_bool_c xmit_interrupt_enable = parameter_bool_c(this, "XMIT interrupt enable", + "xie",/*readonly*/false, "1 = enable interrupt"); + parameter_bool_c rcvr_interrupt_enable = parameter_bool_c(this, "RCVR interrupt enable", + "rie",/*readonly*/false, "1 = enable interrupt"); + parameter_bool_c slu_maint = parameter_bool_c(this, "XCSR Maintenance", "maint",/*readonly*/ + false, "1 = enable Maintenance mode enabled"); + parameter_bool_c rdr_enable = parameter_bool_c(this, "RDR enable", "rdre",/*readonly*/false, + "1 = enable reader enable"); +#endif + // background worker function + void worker(unsigned instance) override; + void worker_rcv(void); + void worker_xmt(void); + + // called by unibusadapter on emulated register access + 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; +}; + +//-------------------------------------------- LTC ------------------------------------- +class ltc_c: public unibusdevice_c { +private: + + unibusdevice_register_t *reg_lks; + + // KW11 has one interrupt + intr_request_c intr_request = intr_request_c(this); + + bool clock_signal; // value of 50Hz square wave signal + bool intr_enable; // interrupt enable, LKS bit 6 + bool intr_monitor ; // LKS bit 7 + + bool get_intr_signal_level(void); + void set_lks_dati_value_and_INTR(void); + +public: + + ltc_c(); + ~ltc_c(); + + parameter_unsigned_c frequency = parameter_unsigned_c(this, "Line clock frequency", "freq", /*readonly*/ + false, "", "%d", "50/60 Hz", 32, 10); + parameter_bool_c ltc_enable = parameter_bool_c(this, "LTC input enable", "ltc",/*readonly*/ + false, "1 = enable update of LKS by LTC Input"); + + // background worker function + void worker(unsigned instance) override; + + // called by unibusadapter on emulated register access + 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; +}; + +#endif diff --git a/10.02_devices/2_src/mscp_drive.cpp b/10.02_devices/2_src/mscp_drive.cpp index 4d07a45..e2a2afc 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,44 @@ 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) { + set_workers_count(0) ; // needs no worker() + 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 +70,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 +82,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 +139,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 +148,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 +157,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 +166,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 +208,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 +223,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 +235,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 +279,46 @@ 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. -} - // // 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..1b7200a 100644 --- a/10.02_devices/2_src/mscp_drive.hpp +++ b/10.02_devices/2_src/mscp_drive.hpp @@ -1,129 +1,107 @@ /* - 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 #include #include +#include // unique_ptr #include "parameter.hpp" #include "storagedrive.hpp" // // 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 worker(void) override; + void on_power_changed(void) override; + void on_init_changed(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 8ff0559..cc4e17f 100644 --- a/10.02_devices/2_src/mscp_server.cpp +++ b/10.02_devices/2_src/mscp_server.cpp @@ -69,11 +69,16 @@ mscp_server::mscp_server( polling_mutex(PTHREAD_MUTEX_INITIALIZER), _credits(INIT_CREDITS) { - name.value = "mscp_server" ; - type_name.value = "mscp_server_c" ; + set_workers_count(0) ; // no std worker() + 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 +88,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..669d5a8 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); @@ -152,8 +153,6 @@ public: 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..d79dee8 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) { @@ -438,10 +438,11 @@ void paneldriver_c::i2c_sync_all_params() { Query all used I2C chip register, Update controls and parameters */ -void paneldriver_c::worker(void) { +void paneldriver_c::worker(unsigned instance) { + UNUSED(instance); // only one timeout_c timeout; - while (!worker_terminate) { + while (!workers_terminate) { // poll in endless round i2c_sync_all_params(); timeout.wait_ms(10); @@ -468,7 +469,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 +491,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..2d48ae8 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 @@ -110,18 +112,15 @@ public: void i2c_sync_all_params(); // background worker function - void worker(void) override; + void worker(unsigned instance) 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..077860d 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,250 @@ 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(unsigned instance) { + UNUSED(instance) ; // only one + 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 old mode 100755 new mode 100644 index 45adf35..3d72d9b --- a/10.02_devices/2_src/rk05.hpp +++ b/10.02_devices/2_src/rk05.hpp @@ -91,12 +91,13 @@ 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; void on_init_changed(void) override; // background worker function - void worker(void) override; + void worker(unsigned instance) override; }; #endif diff --git a/10.02_devices/2_src/rk11.cpp b/10.02_devices/2_src/rk11.cpp index 09c0431..5d99707 100755 --- a/10.02_devices/2_src/rk11.cpp +++ b/10.02_devices/2_src/rk11.cpp @@ -25,10 +25,13 @@ 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 + // base addr, intr-vector, intr level + set_default_bus_params(0777400, 10, 0220, 5) ; + dma_request.set_priority_slot(default_priority_slot) ; + intr_request.set_priority_slot(default_priority_slot) ; + intr_request.set_level(default_intr_level) ; + intr_request.set_vector(default_intr_vector) ; // The RK11 controller has seven registers, // We allocate 8 because one address in the address space is unused. @@ -117,6 +120,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; @@ -133,23 +144,24 @@ void rk11_c::dma_transfer(DMARequest &request) { // Write FROM buffer TO unibus memory, IBA on: // We only need to write the last word in the buffer to memory. - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, request.address, request.buffer + request.count - 1, - 1, NULL); + 1); + request.timeout = !dma_request.success ; } else { // Read FROM unibus memory TO buffer, IBA on: // We read a single word from the unibus and fill the // entire buffer with this value. - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, request.address, request.buffer, - 1, - NULL); + 1); + request.timeout = !dma_request.success ; } } else @@ -158,22 +170,22 @@ void rk11_c::dma_transfer(DMARequest &request) if (request.write) { // Write FROM buffer TO unibus memory - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, request.address, request.buffer, - request.count, - NULL); + request.count); + request.timeout = !dma_request.success ; } else { // Read FROM unibus memory TO buffer - request.timeout = !unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, request.address, request.buffer, - request.count, - NULL); + request.count); + request.timeout = !dma_request.success ; } } @@ -190,8 +202,9 @@ void rk11_c::dma_transfer(DMARequest &request) // Background worker. // Handle device operations. -void rk11_c::worker(void) +void rk11_c::worker(unsigned instance) { + UNUSED(instance) ; // only one worker_init_realtime_priority(rt_device); @@ -200,7 +213,7 @@ void rk11_c::worker(void) bool do_interrupt = true; timeout_c timeout; - while (!worker_terminate) + while (!workers_terminate) { switch (_worker_state) { @@ -952,7 +965,7 @@ void rk11_c::invoke_interrupt(void) // if (_ide) { - interrupt(); + unibusadapter->INTR(intr_request, NULL, 0); // todo: link to interupt register } } diff --git a/10.02_devices/2_src/rk11.hpp b/10.02_devices/2_src/rk11.hpp index c306b7d..a98908b 100755 --- a/10.02_devices/2_src/rk11.hpp +++ b/10.02_devices/2_src/rk11.hpp @@ -14,6 +14,7 @@ using namespace std; #include "utils.hpp" +#include "unibusadapter.hpp" #include "unibusdevice.hpp" #include "storagecontroller.hpp" #include "rk05.hpp" @@ -149,19 +150,27 @@ private: Worker_Finish = 2, } _worker_state; + // Unibusadapter: RK11 has one INTR and DMA + // should be merged with RK11::DMARequest + dma_request_c dma_request = dma_request_c(this) ; // operated by unibusadapter + intr_request_c intr_request = intr_request_c(this) ; + + public: rk11_c(); virtual ~rk11_c(); // background worker function - void worker(void) override; + void worker(unsigned instance) override; // called by unibusadapter on emulated register access 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; diff --git a/10.02_devices/2_src/rl0102.cpp b/10.02_devices/2_src/rl0102.cpp index b641733..111f220 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,33 +621,38 @@ 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; } // thread -void RL0102_c::worker(void) { +void RL0102_c::worker(unsigned instance) { + UNUSED(instance); // only one timeout_c timeout; // set prio to RT, but less than RL11 controller worker_init_realtime_priority(rt_device); - while (!worker_terminate) { + while (!workers_terminate) { // worker_mutex.lock() ; // collision with cmd_seek() and on_xxx_changed() // states have set error flags not in RL11 CSR: just update 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..f8e1b88 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; @@ -188,7 +187,7 @@ public: void clear_error_register(void); // background worker function - void worker(void) override; + void worker(unsigned instance) override; }; #endif diff --git a/10.02_devices/2_src/rl11.cpp b/10.02_devices/2_src/rl11.cpp index 5d6b3bf..405be10 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,12 @@ RL11_c::RL11_c(void) : type_name.value = "RL11"; log_label = "rl"; - default_base_addr = 0774400; - default_intr_vector = 0160; - default_intr_level = 5; + // base addr, intr-vector, intr level + set_default_bus_params(0774400, 15, 0160, 5); + dma_request.set_priority_slot(default_priority_slot); + intr_request.set_priority_slot(default_priority_slot); + intr_request.set_level(default_intr_level); + intr_request.set_vector(default_intr_vector); // add 4 RL disk drives drivecount = 4; @@ -194,6 +197,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() @@ -263,9 +279,10 @@ void RL11_c::reset(void) { interrupt_enable = 0; unibus_address_msb = 0; clear_errors(); + intr_request.edge_detect_reset(); change_state(RL11_STATE_CONTROLLER_READY); // or do_command_done() ? - do_controller_status(__func__); + do_controller_status(false, __func__); } void RL11_c::clear_errors() { @@ -328,6 +345,7 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg, // on drive select: // move status of new drive to controller status register // on command: signal worker thread + switch (device_reg->index) { case 0: { // CS if (unibus_control == UNIBUS_CONTROL_DATO) { @@ -359,7 +377,7 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg, // accept only command if controller ready if (new_controller_ready) { // GO not set - do_controller_status(__func__); // UNIBUS sees still "controller ready" + do_controller_status(false, __func__); // UNIBUS sees still "controller ready" } else { RL0102_c *drive; // some funct need the selected drive bool execute_function_delayed; @@ -415,7 +433,7 @@ void RL11_c::on_after_register_access(unibusdevice_register_t *device_reg, // signal worker() with pthread condition variable // transition from high priority "unibusadapter thread" to // standard "device thread". - do_controller_status(__func__); // UNIBUS sees now "controller not ready" + do_controller_status(false, __func__); // UNIBUS sees now "controller not ready" // wake up worker() pthread_cond_signal(&on_after_register_access_cond); } @@ -451,12 +469,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(); @@ -484,15 +496,19 @@ void RL11_c::on_drive_status_changed(storagedrive_c *drive) { if (drive->unitno.value != selected_drive_unitno) return; // show status lines in CS for selected drive - do_controller_status(__func__); + do_controller_status(false, __func__); } // issue interrupt. // do not set CONTROLLER READY bit void RL11_c::do_command_done(void) { - bool do_int = false; + // bool do_int = false; if (interrupt_enable && state != RL11_STATE_CONTROLLER_READY) - do_int = true; + change_state_INTR(RL11_STATE_CONTROLLER_READY); + else + // no intr + change_state(RL11_STATE_CONTROLLER_READY); +#ifdef OLD if (do_int) { // first set visible "controller ready" @@ -501,20 +517,16 @@ void RL11_c::do_command_done(void) { * RDY interrupt too late and at wrong program position */ // worker_boost_realtime_priority(); -// SET_DEBUG_PIN1(1) - ; - change_state(RL11_STATE_CONTROLLER_READY); // scheduler may inject time here, if called from low prio worker() ! // pending interrupt triggered - interrupt(); + //TODO: connect to interrupt register busreg_CS + unibusadapter->INTR(intr_request, NULL, 0); DEBUG("Interrupt!"); -// SET_DEBUG_PIN1(0) - ; // worker_restore_realtime_priority(); } else - // no intr - change_state(RL11_STATE_CONTROLLER_READY); + // no intr + change_state(RL11_STATE_CONTROLLER_READY); /* { // interrupt on leading edge of "controller-ready" signal @@ -524,28 +536,30 @@ void RL11_c::do_command_done(void) { do_controller_status(__func__); change_state(RL11_STATE_CONTROLLER_READY); */ +#endif } // CS read/Write access different registers. // write current status into CS, for next read operation // must be done after each DATO -void RL11_c::do_controller_status(const char *debug_info) { +void RL11_c::do_controller_status(bool do_intr, const char *debug_info) { RL0102_c *drive = selected_drive(); uint16_t tmp = 0; bool drive_error_any = drive->drive_error_line; // save, may change + bool controller_ready = (state == RL11_STATE_CONTROLLER_READY); //bit 0: drive ready if (drive->drive_ready_line) - tmp |= 0x0001; + tmp |= BIT(0); // bits <1:3>: function code tmp |= (function_code << 1); // bits <4:5>: bus_address <17:16> tmp |= (unibus_address_msb & 3) << 4; // bit 6: IE if (interrupt_enable) - tmp |= (1 << 6); + tmp |= BIT(6); // bit 7: CRDY - if (state == RL11_STATE_CONTROLLER_READY) - tmp |= (1 << 7); + if (controller_ready) + tmp |= BIT(7); // bits <8:9>: drive select tmp |= (selected_drive_unitno << 8); // bit <10:13>: error code. Only some possible errors @@ -559,15 +573,21 @@ void RL11_c::do_controller_status(const char *debug_info) { tmp |= (0x08) << 10; // error code "NXM" = 1000 // bit 14 is drive error if (drive_error_any) - tmp |= (1 << 14); + tmp |= BIT(14); // bit 15 is composite error if (error_dma_timeout || error_operation_incomplete || error_writecheck || error_header_not_found || drive_error_any) { - tmp |= (1 << 15); + tmp |= BIT(15); } - set_register_dati_value(busreg_CS, tmp, debug_info); - // now visible om UNIBUS + if (do_intr) { + // set CSR atomically with INTR signal lines + assert(interrupt_enable); + assert(controller_ready); + unibusadapter->INTR(intr_request, busreg_CS, tmp); + } else + set_register_dati_value(busreg_CS, tmp, debug_info); + // now visible on UNIBUS } @@ -579,7 +599,6 @@ void RL11_c::do_operation_incomplete(const char *info) { timeout.wait_ms(200 / emulation_speed.value); error_operation_incomplete = true; do_command_done(); - } // separate proc, to have a testpoint @@ -587,7 +606,14 @@ void RL11_c::change_state(unsigned new_state) { if (state != new_state) DEBUG("Change RL11 state from 0x%x to 0x%x.", state, new_state); state = new_state; - do_controller_status(__func__); + do_controller_status(false, __func__); +} + +void RL11_c::change_state_INTR(unsigned new_state) { + if (state != new_state) + DEBUG("Change RL11 state from 0x%x to 0x%x.", state, new_state); + state = new_state; + do_controller_status(true, __func__); } // start seek operation, then interrupt @@ -718,26 +744,32 @@ 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); + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, unibus_address, silo, + dma_wordcount); + error_dma_timeout = !dma_request.success; + unibus_address = dma_request.unibus_end_addr; } 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); + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, unibus_address, + silo_compare, dma_wordcount); + error_dma_timeout = !dma_request.success; + unibus_address = dma_request.unibus_end_addr; } 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); + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, unibus_address, silo, + dma_wordcount); + error_dma_timeout = !dma_request.success; + unibus_address = dma_request.unibus_end_addr; } - - // 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) + + // 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: yes, current addr is addr AFTER illegal address (verified) update_unibus_address(unibus_address); // set addr msb to cs if (error_dma_timeout) { @@ -796,13 +828,14 @@ void RL11_c::state_readwrite() { // thread // excutes commands -void RL11_c::worker(void) { +void RL11_c::worker(unsigned instance) { + UNUSED(instance); // only one assert(!pthread_mutex_lock(&on_after_register_access_mutex)); // set prio to RT, but less than unibus_adapter worker_init_realtime_priority(rt_device); - while (!worker_terminate) { + while (!workers_terminate) { /* process command state machine in parallel with "active register" state changes */ @@ -810,11 +843,15 @@ void RL11_c::worker(void) { // wait for "cmd" signal of on_after_register_access() int res; + // CRDY in busreg_CS->active_dati_flipflops & 0x80)) + // may still be inactive, when PRU updatesit with iNTR delayed. // enable operation of pending on_after_register_access() + /* if (!(busreg_CS->active_dati_flipflops & 0x80)) { // CRDY must be set ERROR("CRDY not set, CS=%06o", busreg_CS->active_dati_flipflops); logger->dump(logger->default_filepath); } + */ res = pthread_cond_wait(&on_after_register_access_cond, &on_after_register_access_mutex); if (res != 0) { diff --git a/10.02_devices/2_src/rl11.hpp b/10.02_devices/2_src/rl11.hpp index 26a1dbd..1c24e48 100644 --- a/10.02_devices/2_src/rl11.hpp +++ b/10.02_devices/2_src/rl11.hpp @@ -1,40 +1,41 @@ /* 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 "unibusadapter.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" */ + /** everything shared 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 +48,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 @@ -58,6 +59,10 @@ private: uint16_t silo[128]; // buffer from/to drive uint16_t silo_compare[128]; // memory data to be compared with silo + // RL11 has one INTR and DMA + dma_request_c dma_request = dma_request_c(this); // operated by unibusadapter + intr_request_c intr_request = intr_request_c(this); + // only 16*16 = 256 byte buffer from drive (SILO) // one DMA transaction per sector, must be complete within one sector time // (2400 rpm = 40 rounds per second -> 1/160 sec per sector = 6,25 millisecs @@ -66,24 +71,27 @@ 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); // set readable value for busreg_CS - void do_controller_status(const char *debug_info); + void do_controller_status(bool do_intr, const char *debug_info); void do_operation_incomplete(const char *info); // state machines - void change_state(unsigned new_state) ; + void change_state(unsigned new_state); + void change_state_INTR(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 +103,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; + void worker(unsigned instance) 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/rs232.cpp b/10.02_devices/2_src/rs232.cpp new file mode 100644 index 0000000..0b664ee --- /dev/null +++ b/10.02_devices/2_src/rs232.cpp @@ -0,0 +1,498 @@ +/* + *************************************************************************** + * + * Author: Teunis van Beelen + * + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen + * + * Email: teuniz@gmail.com + * + *************************************************************************** + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** + */ + +/* 2019, June: made C++, added parity/frame/BREAK option, Joerg Hoppe */ +/* Last revision Teunis van Beelen: November 22, 2017 */ + +/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */ + +#include "rs232.hpp" + +rs232_c::rs232_c() { + CharTransmissionTime_us = 0; +} + +// devname without leading "/dev/" +// returns 0 on success, else error +int rs232_c::OpenComport(const char *devname, int baudrate, const char *mode, + bool par_and_break) { + char full_devname[256]; + + int baudr; + int status; + + strcpy(full_devname, "/dev/"); + strncat(full_devname, devname, 255); + full_devname[255] = 0; + + switch (baudrate) { + case 50: + baudr = B50; + break; + case 75: + baudr = B75; + break; + case 110: + baudr = B110; + break; + case 134: + baudr = B134; + break; + case 150: + baudr = B150; + break; + case 200: + baudr = B200; + break; + case 300: + baudr = B300; + break; + case 600: + baudr = B600; + break; + case 1200: + baudr = B1200; + break; + case 1800: + baudr = B1800; + break; + case 2400: + baudr = B2400; + break; + case 4800: + baudr = B4800; + break; + case 9600: + baudr = B9600; + break; + case 19200: + baudr = B19200; + break; + case 38400: + baudr = B38400; + break; + case 57600: + baudr = B57600; + break; + case 115200: + baudr = B115200; + break; + case 230400: + baudr = B230400; + break; + case 460800: + baudr = B460800; + break; + case 500000: + baudr = B500000; + break; + case 576000: + baudr = B576000; + break; + case 921600: + baudr = B921600; + break; + case 1000000: + baudr = B1000000; + break; + case 1152000: + baudr = B1152000; + break; + case 1500000: + baudr = B1500000; + break; + case 2000000: + baudr = B2000000; + break; + case 2500000: + baudr = B2500000; + break; + case 3000000: + baudr = B3000000; + break; + case 3500000: + baudr = B3500000; + break; + case 4000000: + baudr = B4000000; + break; + default: + printf("invalid baudrate\n"); + return (1); + break; + } + + int cbits = CS8; + int cpar = 0; + int ipar = IGNPAR; + int bstop = 0; + + if (strlen(mode) != 3) { + printf("invalid mode \"%s\"\n", mode); + return (1); + } + unsigned bitcount = 1; // start bit + switch (mode[0]) { + case '8': + cbits = CS8; + bitcount += 8; + break; + case '7': + cbits = CS7; + bitcount += 7; + break; + case '6': + cbits = CS6; + bitcount += 6; + break; + case '5': + cbits = CS5; + bitcount += 5; + break; + default: + printf("invalid number of data-bits '%c'\n", mode[0]); + return (1); + break; + } + + switch (mode[1]) { + case 'N': + case 'n': + cpar = 0; + ipar = IGNPAR; + break; + case 'E': + case 'e': + cpar = PARENB; + ipar = INPCK; + bitcount += 1; + break; + case 'O': + case 'o': + cpar = (PARENB | PARODD); + ipar = INPCK; + bitcount += 1; + break; + default: + printf("invalid parity '%c'\n", mode[1]); + return (1); + break; + } + + switch (mode[2]) { + case '1': + bstop = 0; + bitcount += 1; + break; + case '2': + bstop = CSTOPB; + bitcount += 2; + break; + default: + printf("invalid number of stop bits '%c'\n", mode[2]); + return (1); + break; + } + // bit count is 10 for 8N1 + // Calc time to transmit on character + CharTransmissionTime_us = (1000000 * bitcount) / baudrate; + + /* scan for BREAK and frame/parity errors? + To read BREAK not as \0: + PARMRK=1 and parity checking -> BREAK violates frame pattern -> is recieved as \377 \0 \0 + */ + int iflag; + if (par_and_break) + iflag = PARMRK | INPCK; + else + iflag = ipar; + + /* + http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html + + http://man7.org/linux/man-pages/man3/termios.3.html + */ + + Cport = open(full_devname, O_RDWR | O_NOCTTY | O_NDELAY); + if (Cport == -1) { + perror("unable to open comport "); + return (1); + } + + /* lock access so that another process can't also use the port */ + if (flock(Cport, LOCK_EX | LOCK_NB) != 0) { + close(Cport); + perror("Another process has locked the comport."); + return (1); + } + + error = tcgetattr(Cport, &old_port_settings); + if (error == -1) { + close(Cport); + flock(Cport, LOCK_UN); /* free the port so that others can use it. */ + perror("unable to read portsettings "); + return (1); + } + memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */ + + new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD; + new_port_settings.c_iflag = iflag; + new_port_settings.c_oflag = 0; + new_port_settings.c_lflag = 0; + new_port_settings.c_cc[VMIN] = 0; /* block until n bytes are received */ + new_port_settings.c_cc[VTIME] = 0; /* block until a timer expires (n * 100 mSec.) */ + + cfsetispeed(&new_port_settings, baudr); + cfsetospeed(&new_port_settings, baudr); + + error = tcsetattr(Cport, TCSANOW, &new_port_settings); + if (error == -1) { + tcsetattr(Cport, TCSANOW, &old_port_settings); + close(Cport); + flock(Cport, LOCK_UN); /* free the port so that others can use it. */ + perror("unable to adjust portsettings "); + return (1); + } + + /* http://man7.org/linux/man-pages/man4/tty_ioctl.4.html */ + + if (ioctl(Cport, TIOCMGET, &status) == -1) { + tcsetattr(Cport, TCSANOW, &old_port_settings); + flock(Cport, LOCK_UN); /* free the port so that others can use it. */ + perror("unable to get portstatus"); + return (1); + } + + status |= TIOCM_DTR; /* turn on DTR */ + status |= TIOCM_RTS; /* turn on RTS */ + + if (ioctl(Cport, TIOCMSET, &status) == -1) { + tcsetattr(Cport, TCSANOW, &old_port_settings); + flock(Cport, LOCK_UN); /* free the port so that others can use it. */ + perror("unable to set portstatus"); + return (1); + } + + return (0); +} + +int rs232_c::PollComport(unsigned char *buf, int size) { + int n; + + n = read(Cport, buf, size); + + if (n < 0) { + if (errno == EAGAIN) + return 0; + } + + return (n); +} + +int rs232_c::SendByte(unsigned char byte) { + int n = write(Cport, &byte, 1); + if (n < 0) { + if (errno == EAGAIN) { + return 0; + } else { + return 1; + } + } + return (0); +} + +int rs232_c::SendBuf(unsigned char *buf, int size) { + int n = write(Cport, buf, size); + if (n < 0) { + if (errno == EAGAIN) { + return 0; + } else { + return -1; + } + } + + return (n); +} + +// put byte in to rcv queue +void rs232_c::LoopbackByte(unsigned char byte) { + if (ioctl(Cport, TIOCSTI, &byte) == -1) { + perror("unable to insert byte into input queue"); + } +} + +void rs232_c::SetBreak(int break_state) { + if (ioctl(Cport, break_state ? TIOCSBRK : TIOCCBRK) == -1) { + perror("unable to set break status"); + } +} + +void rs232_c::CloseComport(void) { + int status; + CharTransmissionTime_us = 0; + + if (ioctl(Cport, TIOCMGET, &status) == -1) { + perror("unable to get portstatus"); + } + + status &= ~TIOCM_DTR; /* turn off DTR */ + status &= ~TIOCM_RTS; /* turn off RTS */ + + if (ioctl(Cport, TIOCMSET, &status) == -1) { + perror("unable to set portstatus"); + } + + tcsetattr(Cport, TCSANOW, &old_port_settings); + close(Cport); + + flock(Cport, LOCK_UN); /* free the port so that others can use it. */ +} + +/* + Constant Description + TIOCM_LE DSR (data set ready/line enable) + TIOCM_DTR DTR (data terminal ready) + TIOCM_RTS RTS (request to send) + TIOCM_ST Secondary TXD (transmit) + TIOCM_SR Secondary RXD (receive) + TIOCM_CTS CTS (clear to send) + TIOCM_CAR DCD (data carrier detect) + TIOCM_CD see TIOCM_CAR + TIOCM_RNG RNG (ring) + TIOCM_RI see TIOCM_RNG + TIOCM_DSR DSR (data set ready) + + http://man7.org/linux/man-pages/man4/tty_ioctl.4.html + */ + +int rs232_c::IsDCDEnabled(void) { + int status; + + ioctl(Cport, TIOCMGET, &status); + + if (status & TIOCM_CAR) + return (1); + else + return (0); +} + +int rs232_c::IsCTSEnabled(void) { + int status; + + ioctl(Cport, TIOCMGET, &status); + + if (status & TIOCM_CTS) + return (1); + else + return (0); +} + +int rs232_c::IsDSREnabled(void) { + int status; + + ioctl(Cport, TIOCMGET, &status); + + if (status & TIOCM_DSR) + return (1); + else + return (0); +} + +void rs232_c::enableDTR(void) { + int status; + + if (ioctl(Cport, TIOCMGET, &status) == -1) { + perror("unable to get portstatus"); + } + + status |= TIOCM_DTR; /* turn on DTR */ + + if (ioctl(Cport, TIOCMSET, &status) == -1) { + perror("unable to set portstatus"); + } +} + +void rs232_c::disableDTR(void) { + int status; + + if (ioctl(Cport, TIOCMGET, &status) == -1) { + perror("unable to get portstatus"); + } + + status &= ~TIOCM_DTR; /* turn off DTR */ + + if (ioctl(Cport, TIOCMSET, &status) == -1) { + perror("unable to set portstatus"); + } +} + +void rs232_c::enableRTS(void) { + int status; + + if (ioctl(Cport, TIOCMGET, &status) == -1) { + perror("unable to get portstatus"); + } + + status |= TIOCM_RTS; /* turn on RTS */ + + if (ioctl(Cport, TIOCMSET, &status) == -1) { + perror("unable to set portstatus"); + } +} + +void rs232_c::disableRTS(void) { + int status; + + if (ioctl(Cport, TIOCMGET, &status) == -1) { + perror("unable to get portstatus"); + } + + status &= ~TIOCM_RTS; /* turn off RTS */ + + if (ioctl(Cport, TIOCMSET, &status) == -1) { + perror("unable to set portstatus"); + } +} + +void rs232_c::flushRX(void) { + tcflush(Cport, TCIFLUSH); +} + +void rs232_c::flushTX(void) { + tcflush(Cport, TCOFLUSH); +} + +void rs232_c::flushRXTX(void) { + tcflush(Cport, TCIOFLUSH); +} + +void rs232_c::cputs(const char *text) /* sends a string to serial port */ +{ + while (*text != 0) + SendByte(*(text++)); +} + diff --git a/10.02_devices/2_src/rs232.hpp b/10.02_devices/2_src/rs232.hpp new file mode 100644 index 0000000..36df9ac --- /dev/null +++ b/10.02_devices/2_src/rs232.hpp @@ -0,0 +1,90 @@ +/* + *************************************************************************** + * + * Author: Teunis van Beelen + * + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen + * + * Email: teuniz@gmail.com + * + *************************************************************************** + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** + */ + +/* Last revision: August 5, 2017 */ + +/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */ + +#ifndef rs232_INCLUDED +#define rs232_INCLUDED + +#include +#include + +#if defined(__linux__) || defined(__FreeBSD__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include + +#endif + +#define RS232_PORTNR 38 + +class rs232_c { +private: + + int Cport; // file handle of COM port + int error; + + struct termios new_port_settings, old_port_settings; + +public: + rs232_c(); + unsigned CharTransmissionTime_us; + int OpenComport(const char *devname, int baudrate, const char *mode, bool par_and_break); + int PollComport(unsigned char *buf, int size); + int SendByte(unsigned char byte); + void LoopbackByte(unsigned char byte); + int SendBuf(unsigned char *buf, int size); + void SetBreak(int break_state); + void CloseComport(void); + void cputs(const char *); + int IsDCDEnabled(void); + int IsCTSEnabled(void); + int IsDSREnabled(void); + void enableDTR(void); + void disableDTR(void); + void enableRTS(void); + void disableRTS(void); + void flushRX(void); + void flushTX(void); + void flushRXTX(void); +}; + +#endif + diff --git a/10.02_devices/2_src/rs232adapter.cpp b/10.02_devices/2_src/rs232adapter.cpp new file mode 100644 index 0000000..d545b2d --- /dev/null +++ b/10.02_devices/2_src/rs232adapter.cpp @@ -0,0 +1,157 @@ +/* rs232adapter.cpp: route byte xmt/rcv interface to stream and RS232 + + Copyright (c) 2019, 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: + + 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. + + 8-aug-2019 JH edit start + + This is a stream router. + - Main interface is a byte-port with sent/poll functions + - the bytestream can be routed to a RS232 object for TxD and RxD + - the bytestream can be routed to two streams: rcv/xmt + - for the xmt stream a pattern matcher is implemented, which search for strigns in the stream + + To be used to router DL11 RCV/XMT ports to RS232 and/or program functions + + + . stream_rcv stream_xmt upper end "STREAM" . + . \ / / \ . + . | | . + . | +---> ringbuffer "PATTERN" . + . | | . + . | loopback | . + . rcv <----------|---< byte_loopback() . + . decoder | . + . buffer | . + . | | . + . +-----<--------|---< rs232.Poll()---< RxD "RS232" . + . | | . + . | +---> rs232.Send()---> TxD "RS232" . + . | | . + . \ / / \ . + . byte_rcv_poll() byte_xmt_send() lower end "BYTE" . + . . + . DL11 RCVR DL11 XMT DL11 . + . DATI DATO UNIBUS . + + + + */ +#include +#include +#include +#include "rs232adapter.hpp" + +rs232adapter_c::rs232adapter_c() { + log_label = "ADP232"; + + rs232 = NULL; + stream_rcv = NULL; + stream_xmt = NULL; + rcv_termios_error_encoding = false; + rcv_decoder.clear(); + pattern_stream_data[0] = 0; + pattern[0] = 0; + pattern_found = false; + baudrate = 0; // default: no delay + rcv_baudrate_delay.start_us(0); // start with elapsed() == true" +} + +// BYTE interface: check for received char (from stream or RS232) +// Attention: must produce 0xff 0 sequences for termios encoded byte errors +// and 0xff 0xff for \ff +// If IGNPAR=0, PARMRK=1: error on received as \377 \0 +// \377 received as \377 \377 +bool rs232adapter_c::byte_rcv_poll(unsigned char *rcvchar) { + + pthread_mutex_lock(&mutex); + bool result = false; + // mixing input streams, with RS232 priority + + // loopback or part of previous 0xff,0xff sequence ? + int c = rcv_decoder.get(); + if (c != EOF) { + *rcvchar = c; + result = true; + } + if (!result && rs232) { + // rs2323 must be programmed to generate 0xff 0xff sequences + result = rs232->PollComport(rcvchar, 1); + } + + if (stream_rcv && !result) { + // deliver next char from stream delayed, with simulated baudrate + if (baudrate == 0 || rcv_baudrate_delay.reached()) { + int c = stream_rcv->get(); + if (c != EOF) { + *rcvchar = c; + if (rcv_termios_error_encoding && c == 0xff) { + // mark 2nd 0xff for output on next call + rcv_decoder.clear(); + rcv_decoder.put(0xff); + } + result = true; + if (baudrate != 0) + rcv_baudrate_delay.start_us(10 * MILLION / baudrate); // assume 10 bits per char + } + } + } + pthread_mutex_unlock(&mutex); + return result; +} + +void rs232adapter_c::byte_xmt_send(unsigned char xmtchar) { + if (rs232) + rs232->SendByte(xmtchar); + if (stream_xmt) + stream_xmt->put(xmtchar); + // pattern ring buffer + unsigned n = strlen(pattern); + if (n) { + // put new chars at end of string + unsigned m = strlen(pattern_stream_data); + assert(m < pattern_max_len); + pattern_stream_data[m] = xmtchar; + pattern_stream_data[m + 1] = 0; + // only keep the last chars in buffer. + while ((m = strlen(pattern_stream_data)) > n) + // strip first char, should loop only once + memmove(pattern_stream_data, pattern_stream_data + 1, m); + if (strstr(pattern_stream_data, pattern)) + pattern_found = true; // user must clear + } +// pattern_buffer. +} + +void rs232adapter_c::byte_loopback(unsigned char xmtchar) { + // not a queue, only single char (DL11 loopback) + rcv_decoder.clear(); + rcv_decoder.put(xmtchar); + if (rcv_termios_error_encoding && xmtchar == 0xff) + rcv_decoder.put(0xff); + // fill intermediate buffer with seqeunce to receive +} + +void rs232adapter_c::set_pattern(char *pattern) { + strncpy(this->pattern, pattern, pattern_max_len); + pattern_found = false; + pattern_stream_data[0] = 0; +} + diff --git a/10.02_devices/2_src/rs232adapter.hpp b/10.02_devices/2_src/rs232adapter.hpp new file mode 100644 index 0000000..e6c903d --- /dev/null +++ b/10.02_devices/2_src/rs232adapter.hpp @@ -0,0 +1,86 @@ +/* rs232adapter.hpp: route byte xmt/rcv interface to stream and RS232 + + Copyright (c) 2019, 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: + + 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. + + 8-aug-2019 JH edit start + */ + +#ifndef _RS232ADAPTER_HPP_ +#define _RS232ADAPTER_HPP_ + +#include +#include +#include +#include "utils.hpp" +#include "logsource.hpp" +#include "rs232.hpp" + +class rs232adapter_c: public logsource_c { +private: + // for loopback and to decode 0xff to 0xff,0xff + std::stringstream rcv_decoder; + + // last sequence of xmt data for pattern matching + static const int pattern_max_len = 256 ; + char pattern[pattern_max_len+1]; // if != "", this is search for + char pattern_stream_data[pattern_max_len+1]; + + // deliver rcv chars delayed by this "baudrate" + timeout_c rcv_baudrate_delay ; + +public: + + rs232adapter_c(); + + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + + unsigned baudrate ; // deliver rcv chars throttled by this "baudrate" + + // if true, an inject 0xff is delivered as 0xff,0xff. + // this is compatible with termios(3) encoding of error flags + // If IGNPAR=0, PARMRK=1: error on received as \377 \0 + // \377 received as \377 \377 + bool rcv_termios_error_encoding; + + /*** RS232 interface ***/ + rs232_c *rs232; // if assigned, routing to initialized RS232 port + + /*** BYTE interface ***/ + bool byte_rcv_poll(unsigned char *rcvchar); + void byte_xmt_send(unsigned char xmtchar); + void byte_loopback(unsigned char xmtchar); + + /*** STREAM interface ***/ + std::istream *stream_rcv; // users sets this to a stream which producess chars + // may be a stringstream to inject characters + + std::ostream *stream_xmt; // users sets this to a stream in which + // chars are written to be transferred. + // may be "cout", or an stringstream + + /*** PATTERN detection ***/ + void set_pattern(char *pattern); + bool pattern_found; // switches ture on match, user must clear + +}; + +#endif // _RS232ADAPTER_HPP_ + diff --git a/10.02_devices/2_src/testcontroller.cpp b/10.02_devices/2_src/testcontroller.cpp new file mode 100644 index 0000000..87bf27d --- /dev/null +++ b/10.02_devices/2_src/testcontroller.cpp @@ -0,0 +1,238 @@ +/* testcontroller.cpp: sample UNIBUS controller with selftest logic + + Copyright (c) 2018-2019, 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: + + 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. + + 23-jul-2019 JH added interrupt and DMA functions + 12-nov-2018 JH entered beta phase + + "Tester" is a device to test the event, stress INTR and DMA, and + implements 32 registers at start of IOpage + + Controller registers: + ------------------------ + 32 registers @ 0760200.. 0760276 + all registers are marked as "active": + DATI and DATO are routed via events into the controller logic + UNIBUS is stopped with long SSYN + + +0 = CSR: write command, read = status + + other registers have no functions, are simple memory cells. + + Backplane Slots: 10,11,12 + + DMA + --- + has 2 DMA channels on different priority_slots + Used for priroity test of parallel DMA req with different slot priority + + INTR + --- + 4 x 3 interrupts (BR4,5,6,7 at slot 10,11,12) + To test slot priority and level priroity + raise all simulataneously with CPU level = 7 + -> no INTR triggered + lower CPU lvel to 6 -> 2 INTR in increasing slot priority triggered + + + + + Test #1 DMA priority test + ------------------------ + Write of 1 into CSR triggers test + First a long 1K DMA "A" with lower slot priority is started (DEPOSIT) + 2nd a DMA "B" with higher slot priority is started + After some A-chunks, B get priorized and is completed earlier, despit started later. + Verify: At mem start, "B" values re found (B later), + at mem end "A" values are found (runs later) + + + + + */ +#include +#include +#include +//#include + +#include "utils.hpp" +#include "logger.hpp" +#include "unibus.h" +#include "unibusadapter.hpp" +#include "unibusdevice.hpp" // definition of class device_c +#include "testcontroller.hpp" + +testcontroller_c::testcontroller_c() : + unibusdevice_c() // super class constructor +{ + unsigned i; + + // static config + name.value = "Test controller"; + type_name.value = "testcontroller_c"; + log_label = "tc"; + + // mem at 160000: RT11 crashes? + set_default_bus_params(0760200, 16, 0, 0); // base addr, priority_slot, intr-vector, intr level + // + + register_count = 32; // up to 760200 .. 760276 + + // CSR + CSR = &(this->registers[0]); + strcpy(CSR->name, "CSR"); + CSR->active_on_dati = true; // controller state change on read + CSR->active_on_dato = true; // writing changes controller state + CSR->reset_value = 0; + CSR->writable_bits = 0xffff; // all registers are memory cells + + // Other registers are "active": receive "on_after_register_access" + for (i = 1; i < this->register_count; i++) { + unibusdevice_register_t *reg = &(this->registers[i]); + sprintf(reg->name, "reg%02o", i); // name is register offset: "reg07" + reg->active_on_dati = true; // controller state change on read + reg->active_on_dato = true; // writing changes controller state + reg->reset_value = 0; + reg->writable_bits = 0xffff; // all registers are memory cells + } + + // create DMA requests. + for (unsigned i = 0; i < dma_channel_count; i++) { + dma_request_c *dmareq = dma_channel_request[i] = new dma_request_c(this); + // lowest index = highest slot priority + dmareq->set_priority_slot(i + 15); + dma_channel_buffer[i] = new memoryimage_c(); + + } + // create INTR requests. + for (unsigned slot = 1; slot < PRIORITY_SLOT_COUNT; slot++) + for (unsigned level_index = 0; level_index < 4; level_index++) { + intr_request_c *intreq = intr_request[slot][level_index] = new intr_request_c(this); + intreq->set_priority_slot(slot); + intreq->set_level(level_index+4); + // "vector" uninitialized, must be set on use! + } + + // dynamic state + access_count.value = 0; + +} + +testcontroller_c::~testcontroller_c() { + for (unsigned i = 0; i < dma_channel_count; i++) { + delete dma_channel_request[i]; + delete dma_channel_buffer[i]; + } + + for (unsigned slot = 0; slot < PRIORITY_SLOT_COUNT; slot++) + for (unsigned level_index = 0; level_index < 4; level_index++) + delete intr_request[slot][level_index]; +} + +bool testcontroller_c::on_param_changed(parameter_c *param) { + // no own parameter or "enable" logic + return unibusdevice_c::on_param_changed(param); // more actions (for enable) +} + +// process DATI/DATO access to one of my "active" registers +// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS. +// The time between PRU event and program flow into this callback +// is determined by ARM Linux context switch +// +// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc: +// do not read back dati_flipflops. +void testcontroller_c::on_after_register_access(unibusdevice_register_t *device_reg, + uint8_t unibus_control) { + + // emulate a plain memory cell: written values can be read unchanged + if (unibus_control == UNIBUS_CONTROL_DATI) { + } + + if (unibus_control == UNIBUS_CONTROL_DATO) + set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__); + if (device_reg == CSR) { + /// CSR has been written: signal worker() + pthread_cond_signal(&on_after_register_access_cond); + // worker now executes CSR command and clears datao lfipflops + } + // this is also called for some DATIs, no action anyhow. + + access_count.value++; + // DEBUG writes to disk & console ... measured delay up to 30ms ! + //DEBUG(LL_DEBUG, LC_demo_regs, "[%6u] reg +%d @ %06o %s", accesscount, (int ) device_reg->index, + // device_reg->addr, unibus_c::control2text(unibus_control)); +} + +void testcontroller_c::on_power_changed(void) { + if (power_down) { // power-on defaults + } +} + +// UNIBUS INIT: clear all registers +void testcontroller_c::on_init_changed(void) { + // write all registers to "reset-values" + if (init_asserted) { + reset_unibus_registers(); + INFO("testcontroller_c::on_init()"); + } +} + +// background worker. +// Just print a heart beat +void testcontroller_c::worker(unsigned instance) { + UNUSED(instance); // only one + timeout_c timeout; + assert(!pthread_mutex_lock(&on_after_register_access_mutex)); + + // set prio to RT, but less than unibus_adapter + worker_init_realtime_priority(rt_device); + + while (!workers_terminate) { + + int res = pthread_cond_wait(&on_after_register_access_cond, + &on_after_register_access_mutex); + if (res != 0) { + ERROR("testcontroller_c::worker() pthread_cond_wait = %d = %s>", res, + strerror(res)); + continue; + } + // execute command in CSR, + uint16_t cmd = CSR->active_dato_flipflops; + // mark as processed + CSR->active_dato_flipflops = 0; + set_register_dati_value(CSR, 0, __func__); // no status + switch (cmd) { + case 1: + test_dma_priority(); + break; + + } + + timeout.wait_ms(1000); + cout << "."; + } + + assert(!pthread_mutex_unlock(&on_after_register_access_mutex)); +} + +void testcontroller_c::test_dma_priority() { +} + diff --git a/10.02_devices/2_src/demo_regs.hpp b/10.02_devices/2_src/testcontroller.hpp similarity index 61% rename from 10.02_devices/2_src/demo_regs.hpp rename to 10.02_devices/2_src/testcontroller.hpp index b8b0b2d..ff3d434 100644 --- a/10.02_devices/2_src/demo_regs.hpp +++ b/10.02_devices/2_src/testcontroller.hpp @@ -1,6 +1,6 @@ -/* demo_regs.hpp: sample UNIBUS controller with register logic +/* testcontroller.hpp: sample UNIBUS controller with selftest logic - Copyright (c) 2018, Joerg Hoppe + Copyright (c) 2018-2019, Joerg Hoppe j_hoppe@t-online.de, www.retrocmp.com Permission is hereby granted, free of charge, to any person obtaining a @@ -21,36 +21,53 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + 23-jul-2019 JH added interrupt and DMA functions 12-nov-2018 JH entered beta phase */ -#ifndef _DEMO_REGS_HPP_ -#define _DEMO_REGS_HPP_ +#ifndef _TESTCONTROLLER_HPP_ +#define _TESTCONTROLLER_HPP_ #include "utils.hpp" #include "unibusdevice.hpp" +#include "memoryimage.hpp" #include "parameter.hpp" -class demo_regs_c: public unibusdevice_c { +class testcontroller_c: public unibusdevice_c { private: public: - parameter_unsigned_c access_count = parameter_unsigned_c(this, "access_count", - "ac",/*readonly*/ - true, "", "%u", "Total # of register accesses", 32, 10); + unibusdevice_register_t *CSR; // command and status register - demo_regs_c(); + parameter_unsigned_c access_count = parameter_unsigned_c(this, "access_count", "ac",/*readonly*/ + true, "", "%u", "Total # of register accesses", 32, 10); + + // For arbitray tests of the priority request system, we have + // one reqiest for every slot/level combination + static const unsigned dma_channel_count = 2; + dma_request_c *dma_channel_request[dma_channel_count]; + // for concurrent DMA, testcontroller needs one data buffer per possible DMA. + // these are n*4MB ! + memoryimage_c *dma_channel_buffer[dma_channel_count]; + + intr_request_c *intr_request[PRIORITY_SLOT_COUNT][4]; // 3 slots, 4 levels + + testcontroller_c(); + ~testcontroller_c(); + + bool on_param_changed(parameter_c *param) override; // must implement // background worker function - void worker(void) override; + void worker(unsigned instance) override; // called by unibusadapter on emulated register access 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; + + void test_dma_priority(void); }; #endif diff --git a/10.02_devices/2_src/uda.cpp b/10.02_devices/2_src/uda.cpp index b6af444..2fbef44 100644 --- a/10.02_devices/2_src/uda.cpp +++ b/10.02_devices/2_src/uda.cpp @@ -46,9 +46,12 @@ uda_c::uda_c() : type_name.value = "UDA50"; log_label = "uda"; - default_base_addr = 0772150; - default_intr_vector = 0154; - default_intr_level = 5; + // base addr, intr-vector, intr level + set_default_bus_params(0772150, 20, 0154, 5) ; + dma_request.set_priority_slot(default_priority_slot) ; + intr_request.set_priority_slot(default_priority_slot) ; + intr_request.set_level(default_intr_level) ; + intr_request.set_vector(default_intr_vector) ; // The UDA50 controller has two registers. register_count = 2; @@ -94,6 +97,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. @@ -158,13 +167,15 @@ void uda_c::StateTransition( // worker(): // Implements the initialization state machine. // -void uda_c::worker(void) +void uda_c::worker(unsigned instance) { + UNUSED(instance) ; // only one + worker_init_realtime_priority(rt_device); timeout_c timeout; - while (!worker_terminate) + while (!workers_terminate) { // // Wait to be awoken. @@ -801,7 +812,7 @@ uda_c::Interrupt(void) { if ((_interruptEnable || _initStep == InitializationStep::Complete) && _interruptVector != 0) { - interrupt(); + unibusadapter->INTR(intr_request, NULL, 0); // todo: link to interupt register } } @@ -934,11 +945,12 @@ uda_c::DMAWrite( assert ((lengthInBytes % 2) == 0); assert (address < 0x40000); - return unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATO, address, reinterpret_cast(buffer), - lengthInBytes >> 1, NULL); + lengthInBytes >> 1); + return dma_request.success ; } // @@ -965,13 +977,13 @@ uda_c::DMARead( memset(reinterpret_cast(buffer), 0xc3, bufferSize); - bool success = unibusadapter->request_client_DMA( + unibusadapter->DMA(dma_request, true, UNIBUS_CONTROL_DATI, address, buffer, - lengthInBytes >> 1, NULL); + lengthInBytes >> 1); - if (success) + if (dma_request.success) { return reinterpret_cast(buffer); } diff --git a/10.02_devices/2_src/uda.hpp b/10.02_devices/2_src/uda.hpp index aa9557f..b7e861c 100644 --- a/10.02_devices/2_src/uda.hpp +++ b/10.02_devices/2_src/uda.hpp @@ -9,6 +9,7 @@ #include #include "utils.hpp" +#include "unibusadapter.hpp" #include "unibusdevice.hpp" #include "storagecontroller.hpp" #include "mscp_server.hpp" @@ -61,7 +62,9 @@ public: uda_c(); virtual ~uda_c(); - void worker(void) override; + bool on_param_changed(parameter_c *param) override; + + void worker(unsigned instance) override; void on_after_register_access( unibusdevice_register_t *device_reg, @@ -71,6 +74,12 @@ public: void on_init_changed(void) override; void on_drive_status_changed(storagedrive_c *drive) override; + + + // As every storage controller UDA has one INTR and DMA + dma_request_c dma_request = dma_request_c(this) ; // operated by unibusadapter + intr_request_c intr_request = intr_request_c(this) ; + public: // diff --git a/10.02_devices/3_test/dl11w/ZDLDI0.BIN b/10.02_devices/3_test/dl11w/ZDLDI0.BIN new file mode 100644 index 0000000..baa3577 Binary files /dev/null and b/10.02_devices/3_test/dl11w/ZDLDI0.BIN differ diff --git a/10.02_devices/3_test/dl11w/test.cmd b/10.02_devices/3_test/dl11w/test.cmd new file mode 100644 index 0000000..a5abfac --- /dev/null +++ b/10.02_devices/3_test/dl11w/test.cmd @@ -0,0 +1,73 @@ +# "demo" cmd Testsequence for DL11W +dc # "demo" menu: device without cpu +sd dl11 +.print Test of DL11-W, without CPU. +.print Put UniBone in emtpy backplane. +.print Connect serial terminal emulator to UniBone "UART2", set to 9600 8N1 +.input +en dl11 + +INIT +E +.print expected: +.print EXAM reg #0 RCSR 777560 -> 000000 +.print EXAM reg #1 RBUF 777562 -> 000000 +.print EXAM reg #2 XCSR 777564 -> 000200 +.print EXAM reg #3 XBUF 777566 -> 000000 + + +. print Send chars "Hello". Fast, chars may get lost +d xbuf 110 +d xbuf 145 +d xbuf 154 +d xbuf 154 +d xbuf 157 +d xbuf 15 +d xbuf 12 + +.print Now press an "a" on terminal keyboard +.input +e rcsr +e rbuf +e rcsr +.print Expected: rcsr = 200, rbuf = 141, rcsr = 000 (reading rbuf cleared "RCV_DONE") + +.print Now press a "yz" on terminal keyboard +.input +e rcsr +e rbuf +e rcsr +.print Expected: rcsr = 200, rbuf = 14172 ("z"), rcsr = 000 + +.print switch terminal to 7NO. press "c" +e rbuf +.print Expected: rbuf = 373 (c +parity) + +.print Check BREAK receive: +.print Set terminal to 300 baud, send a space (0x20) -> looots of 00s +e rbuf +.print Expected: a "0" with framing error: 170000 +.print Set terminal back to 9600 baud +.input + +.print Maintenance self receive +d xcsr 4 # MAINT ON +d xbuf 123 # sent "S" +e +.print Expected: rcsr = 200, rbuf = 123 (sent char "S" received back) + + + + + + + + + + + + + + + + diff --git a/10.02_devices/3_test/dl11w/zdld.cmd b/10.02_devices/3_test/dl11w/zdld.cmd new file mode 100644 index 0000000..55255b0 --- /dev/null +++ b/10.02_devices/3_test/dl11w/zdld.cmd @@ -0,0 +1,33 @@ +# "demo" cmd Testsequence for DL11W +d # "demo" menu: device with cpu +sd dl11 +.print Test of DL11-W, with PDP-11 CPU. +.print Connect serial terminal emulator to UniBone "UART2", set to 9600 8N1 +.input + +en dl11 +en kw11 + +pwr + +.wait 1000 +.print must see Bootloader prompt on UART2 now! + +m i + +en rl +en rl0 +sd rl0 +p emulation_speed 10 +p runstopbutton 0 # released: "LOAD" +p powerswitch 1 # power on, now in "load" state +p image xxdp25.rl02 # mount image file + p runstopbutton 1 + + +m lp /root/10.02_devices/3_test/dl11w/ZDLDI0.BIN +.print Now start ZDLD at address 200 +.print Or boot XXDP from DL, then execute "R ZDLDI0" +.print Set switch register with "D 176" +.print 100000 = HALT on error, 2000=error flags, 400= test BREAK, 100=disable KW11 +# d 176 100000 # HALT on error diff --git a/10.02_devices/3_test/rl02/ZRLJB1.BIC b/10.02_devices/3_test/rl02/ZRLJB1.BIC new file mode 100644 index 0000000..aaa65ff Binary files /dev/null and b/10.02_devices/3_test/rl02/ZRLJB1.BIC differ diff --git a/10.02_devices/3_test/rl02/diagnostics.txt b/10.02_devices/3_test/rl02/diagnostics.txt deleted file mode 100644 index 6699014..0000000 --- a/10.02_devices/3_test/rl02/diagnostics.txt +++ /dev/null @@ -1,103 +0,0 @@ -Diagnostics for RL11/RL02 - -Diagnostic versions are choosen by listing availability - - Listing XXDP Info - -ZRLG E0 x 25 RL11 controller test 1 OK 31.8. 46min - -ZRLH B0 x 22 RL11 controller test 2 OK - TST 035 OP FLAG MODE - -ZRLI D0 x 22 RL01/RL02 drive test 1 OK 30 min 12.9. - manual intervention test OK 13.9. - -ZRLJ B0 x - RL01/RL02 drive test 2 -ZRLJ B1 22 RL01/RL02 drive test 2 - test1: wartet 8ms, soll 3ms - Fehler im test? - 1-2x pro pass: INTR TOO LATE - 11ms Verzögerung bis Ende von_after_register_access() -ZRLJ C0 25 RL01/RL02 drive test 2 - 17:46 - - -ZRLN B0 x RL01/RL02 drive test 3 (seek & read) - A2 22 - C0 X 25 - - -ZRLK B0 x RL01/RL02 performance exerciser - B3 25 - -ZRLL C0 x 22 RL01/RL02 drive compatibilty test - -ZRLM B0 x 22 RL01/RL02 bad sector file utility - -------------------------------- -Run 21.9.: -MIT DEBUG MESSAGES -ZRLGE0: 5 Min 12 passes OK -ZRLHB1: 7 Min 5 passes OK -ZRLID1: 9 Min 3 passes OK -ZRLJC0: 16:50 HALT - 3 passes, 20x INTR TOO LATE -ZRLNC0: 8 passes, 20x INTRP TOO LATE -ZRLKb3: ------------- -Run 22.9. -LA OFF, DEBUG AUS: - -ZRLGE0: 18095 Min 12 passes OK -ZRLHB1: 6 min 5 passes OK -ZRLID1: 10 min 3 passes 1x head switch "4 sb 5" -ZRLJC0: 7 min 2 * "intrp too late -hang mit HALT - -4 passes, 1x INTR TOO LATE -ZRLNC0: 30 min 4 passes OK -ZRLKB3: ------------- -Run 25.10.18 -Neues PCB, langsameres reg write timing -100% RT priority, DEBUG, LA only on UNIBUS -ZRLGE0: 5 Min 14 passes OK -ZRLHB1: 8 Min 5 passes OK -ZRLID1: 12 Min 4 passes 2 ERR - 2x HRD ERR 01002 SEEK SGN SWITCH TEST -ZRLJC0: 25 Min 1 pass - 2x INTRPT TOO LATE -ZRLNC0: 18:12 - 19:45: "Eclispe: java error - remote connection to debugger closed" - 29 MIn 2+passes - 2x INTRPT TOO LATE -ZRLKB3: 23 Min - 3 x GARBLED DATA - - - - - - - -Running Diagnostics unter XXDP-DRS - -copy xxdp.rl02 -> unibone2 - -Boot XXDP -.R ZRLH?? - -Unibone: clear error log ->>>dl c - - - HALT on ERROR, PRINT test numbers - -DR>STA/FLA:HOE - Nur bestimmter Test: -DR>STA/FLA:HOE/TES:35 - - - - - - -dl f <= save to bbb diff --git a/10.02_devices/3_test/rl02/testrl11.lst b/10.02_devices/3_test/rl02/testrl11.lst index f6c685b..385fe89 100644 --- a/10.02_devices/3_test/rl02/testrl11.lst +++ b/10.02_devices/3_test/rl02/testrl11.lst @@ -15,265 +15,393 @@ 15 16 165020 monitr = 165020 ; entry into M9312 monitor 17 - 18 ;; RL11 commands - 19 000004 cmstat = 2*2 ; get status - 20 000006 cmseek = 3*2 ; seek - 21 000010 cmrdhd = 4*2 ; read header - 22 000012 cmwrda = 5*2 ; write data - 23 000014 cmrdda = 6*2 ; read data - 24 + 18 ; RL11 commands + 19 000000 cmnop = 2*0 ; no op + 20 000004 cmstat = 2*2 ; get status + 21 000006 cmseek = 3*2 ; seek + 22 000010 cmrdhd = 4*2 ; read header + 23 000012 cmwrda = 5*2 ; write data + 24 000014 cmrdda = 6*2 ; read data 25 - 26 .asect - 27 - 28 000160 .=160 ; addr for vector 160 - 29 rlvect: - 30 000160 001302 .word isr ; new PC of ISR - 31 000162 000340 .word 340 ; new PSW: priority is max = 7 - 32 + 26 + 27 .asect + 28 + 29 000160 .=160 ; addr for vector 160 + 30 rlvect: + 31 000160 001612 .word isr ; new PC of ISR + 32 000162 000340 .word 340 ; new PSW: priority is max = 7 33 34 - 35 001000 .=1000 - 36 - 37 000776 stack = . - 2 ; stack grows down from start - 38 - 39 ; --- main() - 40 start: - 41 001000 012706 000776 mov #stack,sp ; init stack - 42 001004 012704 174400 mov #rlbase,r4 ; r4 points to RL11 register space - 43 - 44 001010 012701 001453 mov #shello,r1 - 45 001014 004737 001326 call @#puts + 35 + 36 001000 .=1000 + 37 + 38 000776 stack = . - 2 ; stack grows down from start + 39 + 40 ; --- main() + 41 start: + 42 001000 012706 000776 mov #stack,sp ; init stack + 43 001004 012704 174400 mov #rlbase,r4 ; r4 points to RL11 register space + 44 + 45 001010 000005 reset 46 - 47 001020 004737 001030 call @#test1 - 48 ; call @#test2 + 47 001012 012701 002022 mov #shello,r1 + 48 001016 004737 001670 call @#puts 49 - 50 001024 000137 165020 jmp @#monitr - 51 - 52 - 53 - 54 ; --- TEST1 do a "seek", like the PDP11GIUI driver does - 55 test1: - 56 ; wait until controller ready - 57 001030 105714 0$: tstb (r4) ; wait for "controller ready" (csr.7) - 58 001032 100376 bpl 0$ - 59 - 60 ; clear and get drive status - 61 001034 012764 000013 000004 mov #013,da(r4) ; subcmd reset+getstatus - 62 001042 012700 000004 mov #cmstat,r0 - 63 ; test: command acceptet dirclty aufter INIT singal - 64 ; -> multiple event forwarding in ubibusadapter_c::worker() - 65 001046 000005 reset - 66 - 67 001050 010014 mov r0,(r4) ; GO - 68 001052 105714 1$: tstb (r4) ; wait for "controller ready" (csr.7) - 69 001054 100376 bpl 1$ - 70 - 71 ; call @#wait65 ; - 72 - 73 ; AGAIN: clear and get drive status - 74 001056 012764 000013 000004 mov #013,da(r4) ; subcmd reset+getstatus - 75 001064 012700 000004 mov #cmstat,r0 - 76 001070 010014 mov r0,(r4) ; GO - 77 001072 105714 2$: tstb (r4) ; wait for "controller ready" (csr.7) - 78 001074 100376 bpl 2$ - 79 - 80 ; call @#wait65 ; - 81 - 82 ; seek sector: read header - 83 001076 012700 000010 mov #cmrdhd,r0 ; read header cmd - 84 001102 010014 mov r0,(r4) ; execute - 85 001104 105714 3$: tstb (r4) ; wait for "controller ready" (csr.7) - 86 001106 100376 bpl 3$ - 87 001110 016403 000006 mov mp(r4),r3 ; retrieve cyl/head/sector - 88 - 89 ; call @#wait65 ; - 90 + 50 ; call @#test1 + 51 ; call @#test2 + 52 001022 004737 001302 call @#test3 + 53 001026 000137 165020 jmp @#monitr + 54 + 55 + 56 + 57 ; --- TEST1 do a "seek", like the PDP11GIUI driver does + 58 test1: + 59 ; wait until controller ready + 60 001032 105714 0$: tstb (r4) ; wait for "controller ready" (csr.7) + 61 001034 100376 bpl 0$ + 62 + 63 ; clear and get drive status + 64 001036 012764 000013 000004 mov #013,da(r4) ; subcmd reset+getstatus + 65 001044 012700 000004 mov #cmstat,r0 + 66 ; test: command acceptet dirclty aufter INIT singal + 67 ; -> multiple event forwarding in ubibusadapter_c::worker() + 68 001050 000005 reset + 69 + 70 001052 010014 mov r0,(r4) ; GO + 71 001054 105714 1$: tstb (r4) ; wait for "controller ready" (csr.7) + 72 001056 100376 bpl 1$ + 73 + 74 ; call @#wait65 ; + 75 + 76 ; AGAIN: clear and get drive status + 77 001060 012764 000013 000004 mov #013,da(r4) ; subcmd reset+getstatus + 78 001066 012700 000004 mov #cmstat,r0 + 79 001072 010014 mov r0,(r4) ; GO + 80 001074 105714 2$: tstb (r4) ; wait for "controller ready" (csr.7) + 81 001076 100376 bpl 2$ + 82 + 83 ; call @#wait65 ; + 84 + 85 ; seek sector: read header + 86 001100 012700 000010 mov #cmrdhd,r0 ; read header cmd + 87 001104 010014 mov r0,(r4) ; execute + 88 001106 105714 3$: tstb (r4) ; wait for "controller ready" (csr.7) + 89 001110 100376 bpl 3$ + 90 001112 016403 000006 mov mp(r4),r3 ; retrieve cyl/head/sector 91 - 92 ; seek ... distance = 0 - 93 ; assume a 0 track seek - 94 001114 012764 000000 000004 mov #0,da(r4) ; clear disk address - 95 001122 012700 000006 mov #cmseek,r0 - 96 001126 010014 mov r0,(r4) ; execute 0 track seek - 97 001130 105714 4$: tstb (r4) ; wait for "controller ready" (csr.7) - 98 001132 100376 bpl 4$ - 99 - 100 001134 000207 return - 101 + 92 ; call @#wait65 ; + 93 + 94 + 95 ; seek ... distance = 0 + 96 ; assume a 0 track seek + 97 001116 012764 000000 000004 mov #0,da(r4) ; clear disk address + 98 001124 012700 000006 mov #cmseek,r0 + 99 001130 010014 mov r0,(r4) ; execute 0 track seek + 100 001132 105714 4$: tstb (r4) ; wait for "controller ready" (csr.7) + 101 001134 100376 bpl 4$ 102 - 103 ; --- TEST2 - read sector 0 into mem at 10000 and do interrupt - 104 test2: + 103 001136 000207 return + 104 105 - 106 001136 005037 177776 clr @#psw ; enable all interrupt levels - 107 ; wait until controller ready - 108 001142 105714 0$: tstb (r4) ; wait for "controller ready" (csr.7) - 109 001144 100376 bpl 0$ - 110 - 111 - 112 ; ; clear and get drive status - 113 ; mov #013,da(r4) ; subcmd reset+getstatus - 114 ; mov #cmstat,r0 - 115 ; mov r0,(r4) ; GO - 116 ;1$: tstb (r4) ; wait for "controller ready" (csr.7) - 117 ; bpl 1$ - 118 - 119 - 120 - 121 ; seek max inward , to cyl0, hd 0 - 122 001146 012764 177601 000004 mov #177600+1,da(r4) - 123 001154 012700 000006 mov #cmseek,r0 - 124 001160 010014 mov r0,(r4) ; execute 0 track seek - 125 001162 105714 2$: tstb (r4) ; wait for "controller ready" (csr.7) - 126 001164 100376 bpl 2$ - 127 ; test for "drive ready" - 128 001166 032714 000001 bit #1,(r4) - 129 001172 001773 beq 2$ - 130 - 131 001174 012764 100000 000002 mov #buffer,ba(r4) ; setup memory address - 132 001202 012764 000000 000004 mov #0,da(r4) ; disk address: cyl=0, hd=0, sec=0 - 133 001210 012764 177600 000006 mov #177600,mp(r4) ; load wordcount -128 = 0400 - 134 - 135 001216 005037 001300 clr @#isrcnt - 136 001222 012764 000114 000000 mov #100+cmrdda,cs(r4) ; IE=1, function=6 read data - 137 ; mov #cmrdda,cs(r4) ; IE=0, function=6 read data - 138 ; wait for ISR - 139 3$: - 140 001230 005737 001300 tst @#isrcnt ; wait for ISR - 141 001234 001775 beq 3$ - 142 ; tstb cs(r4) ; wait for CRDY - 143 ; bpl 3$ - 144 - 145 001236 012701 001476 mov #sba,r1 ; "BA=" - 146 001242 004737 001326 call @#puts - 147 001246 016400 000002 mov ba(r4),r0 - 148 001252 004737 001362 call @#putnum ; content of BA register - 149 001256 012701 001450 mov #scrlf,r1 ; end of line - 150 001262 004737 001326 call @#puts - 151 - 152 - 153 001266 012701 001502 mov #sready,r1 ; print "READY" - 154 001272 004737 001326 call @#puts - 155 001276 000207 return - 156 - 157 - 158 - 159 ; -------------- - 160 ; --- isr - called on interupt - 161 ; print incremented BA ... is DMA really ready? - 162 001300 000000 isrcnt: .word ; flag: ISR hit - 163 isr: - 164 001302 005237 001300 inc @#isrcnt ; signal "done" - 165 001306 000002 rti - 166 - 167 + 106 ; --- TEST2 - read sector 0 into mem at 10000 and do interrupt + 107 test2: + 108 + 109 001140 005037 177776 clr @#psw ; enable all interrupt levels + 110 ; wait until controller ready + 111 001144 105714 0$: tstb (r4) ; wait for "controller ready" (csr.7) + 112 001146 100376 bpl 0$ + 113 + 114 + 115 ; ; clear and get drive status + 116 ; mov #013,da(r4) ; subcmd reset+getstatus + 117 ; mov #cmstat,r0 + 118 ; mov r0,(r4) ; GO + 119 ;1$: tstb (r4) ; wait for "controller ready" (csr.7) + 120 ; bpl 1$ + 121 + 122 + 123 + 124 ; seek max inward , to cyl0, hd 0 + 125 001150 012764 177601 000004 mov #177600+1,da(r4) + 126 001156 012700 000006 mov #cmseek,r0 + 127 001162 010014 mov r0,(r4) ; execute 0 track seek + 128 001164 105714 2$: tstb (r4) ; wait for "controller ready" (csr.7) + 129 001166 100376 bpl 2$ + 130 ; test for "drive ready" + 131 001170 032714 000001 bit #1,(r4) + 132 001174 001773 beq 2$ + 133 + 134 001176 012764 100000 000002 mov #buffer,ba(r4) ; setup memory address + 135 001204 012764 000000 000004 mov #0,da(r4) ; disk address: cyl=0, hd=0, sec=0 + 136 001212 012764 177600 000006 mov #177600,mp(r4) ; load wordcount -128 = 0400 + 137 + 138 001220 005037 001610 clr @#isrcnt + 139 001224 012764 000114 000000 mov #100+cmrdda,cs(r4) ; IE=1, function=6 read data + 140 ; mov #cmrdda,cs(r4) ; IE=0, function=6 read data + 141 ; wait for ISR + 142 3$: + 143 001232 005737 001610 tst @#isrcnt ; wait for ISR + 144 001236 001775 beq 3$ + 145 ; tstb cs(r4) ; wait for CRDY + 146 ; bpl 3$ + 147 + 148 001240 012701 002052 mov #sba,r1 ; "BA=" + 149 001244 004737 001670 call @#puts + 150 001250 016400 000002 mov ba(r4),r0 + 151 001254 004737 001724 call @#putnum ; content of BA register + 152 001260 012701 002047 mov #scrlf,r1 ; end of line + 153 001264 004737 001670 call @#puts + 154 + 155 + 156 001270 012701 002056 mov #sready,r1 ; print "READY" + 157 001274 004737 001670 call @#puts + 158 001300 000207 return + 159 + 160 + 161 ; --- TEST3 - ZRLG test 24 + 162 ; execute NOP with Interrupt, with multiple CPU intr levels + 163 ; INTR accepted for level 5 and 4 + 164 test3: + 165 001302 005037 001610 clr @#isrcnt + 166 ; mov #1,r0 ; idle: expected CRDY and not INTR + 167 ; call @#test3b 168 - 169 - 170 ; -- wait 65ms, uses r0 - 171 wait65: - 172 001310 005000 clr r0 - 173 0$: - 174 001312 077001 sob r0,0$ : subtract one, loop until zero - 175 001314 000207 return - 176 - 177 - 178 ; --- check for error - 179 chkerr: - 180 ; verify controller ready - 181 001316 105714 0$: tstb (r4) ; wait for "controller ready" (csr.7) - 182 001320 100376 bpl 0$ ; + 169 001306 012737 000340 177776 mov #340,@#psw ; level 7 + 170 001314 004737 001462 call @#test3a ; NOP + 171 001320 012700 000001 mov #1,r0 ; expected CRDY and not INTR + 172 001324 004737 001476 call @#test3b + 173 + 174 001330 012737 000300 177776 mov #300,@#psw ; level 6 + 175 001336 004737 001462 call @#test3a ; NOP + 176 001342 012700 000001 mov #1,r0 ; expected CRDY and not INTR + 177 001346 004737 001476 call @#test3b + 178 + 179 001352 012737 000240 177776 mov #240,@#psw ; level 5 + 180 001360 004737 001462 call @#test3a ; NOP + 181 001364 012700 000001 mov #1,r0 ; expected CRDY and not INTR + 182 001370 004737 001476 call @#test3b 183 - 184 001322 011400 mov (r4),r0 ; return status CSR - 185 ; bic #1777,r0 ; ignore bits 9:0, error flags are in 15:10 - 186 ; bne 1$ - 187 ; clc - 188 001324 000207 return ; CSR = R1 = 0: no error - 189 - 190 - 191 ; ---------------------- - 192 ; puts - print a string - 193 ; r1 = pointer, r0,r1 changed - 194 puts: - 195 001326 112100 movb (r1)+,r0 ; load xmt char - 196 001330 001403 beq 1$ ; string ends with 0 - 197 001332 004737 001434 call @#putc - 198 001336 000773 br puts ; transmit nxt char of string - 199 001340 000207 1$: return + 184 001374 012737 000200 177776 mov #200,@#psw ; level 4, below BR5, pending triggered + 185 001402 012700 000003 mov #3,r0 ; expected CRDY and INTR + 186 001406 004737 001476 call @#test3b + 187 + 188 001412 005037 001610 clr @#isrcnt + 189 001416 004737 001462 call @#test3a ; NOP at level 4 + 190 001422 012700 000003 mov #3,r0 ; expected CRDY and INTR + 191 001426 004737 001476 call @#test3b + 192 + 193 001432 005037 001610 clr @#isrcnt + 194 001436 012737 000000 177776 mov #0,@#psw ; level 0 + 195 001444 004737 001462 call @#test3a ; NOP + 196 001450 012700 000003 mov #3,r0 ; expected CRDY and INTR + 197 001454 004737 001476 call @#test3b + 198 001460 000207 return + 199 200 - 201 - 202 ; ---------------------- - 203 ; putnum - print the octal number in r0 - 204 001342 numbf0: .blkw 10 ; space to mount number string - 205 001362 numbf1 =. - 206 putnum: - 207 001362 010002 mov r0,r2 ; r2 = shifter - 208 001364 012701 001362 mov #numbf1,r1 ; r1 = buffer pointer, backwards - 209 001370 112741 000000 movb #0,-(r1) ; set terminating 0 - 210 ; repeat 6 times - 211 001374 012703 000006 mov #6,r3 - 212 1$: - 213 001400 010200 mov r2,r0 - 214 ; extract lower 3 bits = octal digit - 215 001402 042700 177770 bic #177770,r0 ; r0 &= 0x07 - 216 001406 062700 000060 add #60,r0 ; r0 += '0' - 217 001412 110041 movb r0,-(r1) ; write in buffer - 218 001414 000241 clc - 219 001416 006002 ror r2 ; shift to next digit - 220 001420 006002 ror r2 - 221 001422 006002 ror r2 - 222 001424 077313 sob r3,1$ ; loop for all 6 digits - 223 - 224 001426 004737 001326 call @#puts - 225 001432 000207 return - 226 - 227 - 228 ; DEC DL11 console I/O - 229 ; ---------------------- - 230 ; putc - output a single char - 231 ; r0 = char - 232 putc: - 233 001434 110037 177566 movb r0,@#dladr+6 ; char into transmit buffer - 234 001440 105737 177564 1$: tstb @#dladr+4 ; XMT RDY? - 235 001444 100375 bpl 1$ ; no, loop - 236 001446 000207 return - 237 - 238 - 239 ; --- string constants - 240 001450 015 012 scrlf: .byte 15,12 ; CR, LF, - 241 001452 000 .byte 0 - 242 - 243 - 244 shello: - 245 001453 015 012 .byte 15,12 ; CR, LF, - 246 001455 123 164 141 .ascii /Starting test!/ - 001460 162 164 151 - 001463 156 147 040 - 001466 164 145 163 - 001471 164 041 - 247 001473 015 012 .byte 15,12 ; CR, LF, - 248 001475 000 .byte 0 - 249 - 250 001476 102 101 075 sba: .ascii /BA=/ - 251 001501 000 .byte 0 ; NUL=end marker - 252 - 253 001502 123 145 143 sready: .ascii /Sector 0 transfered to 100000, ISR hit./ - 001505 164 157 162 - 001510 040 060 040 - 001513 164 162 141 - 001516 156 163 146 - 001521 145 162 145 - 001524 144 040 164 - 001527 157 040 061 - 001532 060 060 060 - 001535 060 060 054 - 001540 040 111 123 - 001543 122 040 150 - 001546 151 164 056 - 254 001551 015 012 .byte 15,12 ; CR, LF, - 255 001553 000 .byte 0 - 256 - 257 ; ---- 32kb page - 258 100000 . = 100000 - 259 buffer: - 260 .end ; - 261 - 262 .end - 262 + 201 ; send NOP cmd and wait for crdy + 202 test3a: + 203 ; pending interupt condition not cleared + 204 001462 005037 001610 clr @#isrcnt + 205 001466 012700 000100 mov #cmnop!100,r0 ; nop with Interrupt Enable + 206 001472 010014 mov r0,(r4) ; NOOP + 207 001474 000207 return + 208 + 209 ; check CRDY and ISR + 210 ; r0: expected result + 211 ; 0 = neither ISR nor CRDY + 212 ; 1 = CRDY without ISR + 213 ; 2 = ISR without CRDY + 214 ; 3 = ISR and CRDY + 215 test3b: + 216 001476 010046 mov r0,-(sp) ; push + 217 ; pending interupt condition not cleared + 218 001500 005003 clr r3 ; result + 219 001502 012700 001750 mov #1750,r0 ; wait 1000us for ISR and CRDY + 220 001506 004737 001620 call @#wtcrdy + 221 001512 103402 bcs 1$ + 222 001514 052703 000001 bis #1,r3 ; Carry clear = NO timeout: CRDY r3 = 1 + 223 1$: + 224 001520 005737 001610 tst @#isrcnt + 225 001524 001402 beq 2$ + 226 001526 052703 000002 bis #2,r3 ; ISR detected: result |= 2 + 227 2$: + 228 001532 012602 mov (sp)+,r2 ; pop + 229 ; r2 = crd+isr code as expected, r3 = as measured + 230 001534 020203 cmp r2,r3 + 231 001536 001401 beq 3$ + 232 001540 000000 halt + 233 3$: + 234 ; print "CRDY+ISR = ..., expected .... + 235 001542 012701 002130 mov #scrdy1,r1 ; "CRDY+ISR =" + 236 001546 004737 001670 call @#puts + 237 001552 010300 mov r3,r0 + 238 001554 004737 001724 call @#putnum + 239 001560 012701 002144 mov #scrdy2,r1 ; "expected" + 240 001564 004737 001670 call @#puts + 241 001570 010200 mov r2,r0 + 242 001572 004737 001724 call @#putnum + 243 001576 012701 002047 mov #scrlf,r1 ; print end of line + 244 001602 004737 001670 call @#puts + 245 001606 000207 return + 246 + 247 + 248 ; -------------- + 249 ; --- isr - called on interupt + 250 ; print incremented BA ... is DMA really ready? + 251 001610 000000 isrcnt: .word ; flag: ISR hit + 252 isr: + 253 001612 005237 001610 inc @#isrcnt ; signal "done" + 254 001616 000002 rti + 255 + 256 ; - wait for "controller ready", but max r0 microseconds + 257 ; result: carry clear = OK, + 258 ; carry set = timeout + 259 + 260 wtcrdy: + 261 001620 006200 asr r0 ; wait loop is 4 cycles + 262 001622 006200 asr r0 + 263 1$: + 264 001624 105714 tstb (r4) ; 2 cycles + 265 001626 100403 bmi 9$ ; bit 7 set -> controller ready + 266 001630 077003 sob r0,1$ + 267 + 268 001632 000261 sec ; "timeout" + 269 001634 000207 return + 270 9$: + 271 001636 000241 clc ; "OK" + 272 001640 000207 return + 273 + 274 + 275 ; -- wait 65ms, uses r0 + 276 wait65: + 277 001642 005000 clr r0 + 278 0$: + 279 001644 077001 sob r0,0$ : subtract one, loop until zero + 280 001646 000207 return + 281 + 282 ; -- wait 1ms, uses r0 + 283 wait1: + 284 001650 012700 001750 mov #1750,r0 ; 1000 us + 285 0$: + 286 001654 077001 sob r0,0$ : subtract one, loop until zero + 287 001656 000207 return + 288 + 289 + 290 ; --- check for error + 291 chkerr: + 292 ; verify controller ready + 293 001660 105714 0$: tstb (r4) ; wait for "controller ready" (csr.7) + 294 001662 100376 bpl 0$ ; + 295 + 296 001664 011400 mov (r4),r0 ; return status CSR + 297 ; bic #1777,r0 ; ignore bits 9:0, error flags are in 15:10 + 298 ; bne 1$ + 299 ; clc + 300 001666 000207 return ; CSR = R1 = 0: no error + 301 + 302 + 303 ; ---------------------- + 304 ; puts - print a string + 305 ; r1 = pointer, r0,r1 changed + 306 puts: + 307 001670 112100 movb (r1)+,r0 ; load xmt char + 308 001672 001403 beq 1$ ; string ends with 0 + 309 001674 004737 002006 call @#putc + 310 001700 000773 br puts ; transmit nxt char of string + 311 001702 000207 1$: return + 312 + 313 + 314 ; ---------------------- + 315 ; putnum - print the octal number in r0 + 316 001704 numbf0: .blkw 10 ; space to mount number string + 317 001724 numbf1 =. + 318 putnum: + 319 001724 010246 mov r2,-(sp) + 320 001726 010346 mov r3,-(sp) + 321 001730 010002 mov r0,r2 ; r2 = shifter + 322 001732 012701 001724 mov #numbf1,r1 ; r1 = buffer pointer, backwards + 323 001736 112741 000000 movb #0,-(r1) ; set terminating 0 + 324 ; repeat 6 times + 325 001742 012703 000006 mov #6,r3 + 326 1$: + 327 001746 010200 mov r2,r0 + 328 ; extract lower 3 bits = octal digit + 329 001750 042700 177770 bic #177770,r0 ; r0 &= 0x07 + 330 001754 062700 000060 add #60,r0 ; r0 += '0' + 331 001760 110041 movb r0,-(r1) ; write in buffer + 332 001762 000241 clc + 333 001764 006202 asr r2 ; shift to next digit + 334 001766 006202 asr r2 + 335 001770 006202 asr r2 + 336 001772 077313 sob r3,1$ ; loop for all 6 digits + 337 + 338 001774 004737 001670 call @#puts + 339 002000 012603 mov (sp)+,r3 + 340 002002 012602 mov (sp)+,r2 + 341 002004 000207 return + 342 + 343 + 344 ; DEC DL11 console I/O + 345 ; ---------------------- + 346 ; putc - output a single char + 347 ; r0 = char + 348 putc: + 349 002006 110037 177566 movb r0,@#dladr+6 ; char into transmit buffer + 350 002012 105737 177564 1$: tstb @#dladr+4 ; XMT RDY? + 351 002016 100375 bpl 1$ ; no, loop + 352 002020 000207 return + 353 + 354 + 355 ; --- string constants + 356 shello: + 357 002022 015 012 .byte 15,12 ; CR, LF, + 358 002024 123 164 141 .ascii /Starting RL11 test!/ + 002027 162 164 151 + 002032 156 147 040 + 002035 122 114 061 + 002040 061 040 164 + 002043 145 163 164 + 002046 041 + 359 scrlf: + 360 002047 015 012 .byte 15,12 ; CR, LF, + 361 002051 000 .byte 0 + 362 + 363 002052 102 101 075 sba: .ascii /BA=/ + 364 002055 000 .byte 0 ; NUL=end marker + 365 + 366 002056 123 145 143 sready: .ascii /Sector 0 transfered to 100000, ISR hit./ + 002061 164 157 162 + 002064 040 060 040 + 002067 164 162 141 + 002072 156 163 146 + 002075 145 162 145 + 002100 144 040 164 + 002103 157 040 061 + 002106 060 060 060 + 002111 060 060 054 + 002114 040 111 123 + 002117 122 040 150 + 002122 151 164 056 + 367 002125 015 012 .byte 15,12 ; CR, LF, + 368 002127 000 .byte 0 + 369 + 370 + 371 002130 103 122 104 scrdy1: .ascii /CRDY+ISR = / + 002133 131 053 111 + 002136 123 122 040 + 002141 075 040 + 372 002143 000 .byte 0 + 373 002144 054 040 145 scrdy2: .ascii /, expected / + 002147 170 160 145 + 002152 143 164 145 + 002155 144 040 + 374 002157 000 .byte 0 + 375 + 376 + 377 ; ---- 32kb page + 378 100000 . = 100000 + 379 buffer: + 380 .end ; + 381 + 382 .end + 382 diff --git a/10.02_devices/3_test/rl02/testrl11.lst.hex b/10.02_devices/3_test/rl02/testrl11.lst.hex index 708ab1e..8e92cd2 100644 --- a/10.02_devices/3_test/rl02/testrl11.lst.hex +++ b/10.02_devices/3_test/rl02/testrl11.lst.hex @@ -15,265 +15,393 @@ 15 16 EA10h monitr = 165020 ; entry into M9312 monitor 17 - 18 ;; RL11 commands - 19 0004h cmstat = 2*2 ; get status - 20 0006h cmseek = 3*2 ; seek - 21 0008h cmrdhd = 4*2 ; read header - 22 000Ah cmwrda = 5*2 ; write data - 23 000Ch cmrdda = 6*2 ; read data - 24 + 18 ; RL11 commands + 19 0000h cmnop = 2*0 ; no op + 20 0004h cmstat = 2*2 ; get status + 21 0006h cmseek = 3*2 ; seek + 22 0008h cmrdhd = 4*2 ; read header + 23 000Ah cmwrda = 5*2 ; write data + 24 000Ch cmrdda = 6*2 ; read data 25 - 26 .asect - 27 - 28 0070h .=160 ; addr for vector 160 - 29 rlvect: - 30 0070h 02C2h .word isr ; new PC of ISR - 31 0072h 00E0h .word 340 ; new PSW: priority is max = 7 - 32 + 26 + 27 .asect + 28 + 29 0070h .=160 ; addr for vector 160 + 30 rlvect: + 31 0070h 038Ah .word isr ; new PC of ISR + 32 0072h 00E0h .word 340 ; new PSW: priority is max = 7 33 34 - 35 0200h .=1000 - 36 - 37 01FEh stack = . - 2 ; stack grows down from start - 38 - 39 ; --- main() - 40 start: - 41 0200h 15C6h 01FEh mov #stack,sp ; init stack - 42 0204h 15C4h F900h mov #rlbase,r4 ; r4 points to RL11 register space - 43 - 44 0208h 15C1h 032Bh mov #shello,r1 - 45 020Ch 09DFh 02D6h call @#puts + 35 + 36 0200h .=1000 + 37 + 38 01FEh stack = . - 2 ; stack grows down from start + 39 + 40 ; --- main() + 41 start: + 42 0200h 15C6h 01FEh mov #stack,sp ; init stack + 43 0204h 15C4h F900h mov #rlbase,r4 ; r4 points to RL11 register space + 44 + 45 0208h 0005h reset 46 - 47 0210h 09DFh 0218h call @#test1 - 48 ; call @#test2 + 47 020Ah 15C1h 0412h mov #shello,r1 + 48 020Eh 09DFh 03B8h call @#puts 49 - 50 0214h 005Fh EA10h jmp @#monitr - 51 - 52 - 53 - 54 ; --- TEST1 do a "seek", like the PDP11GIUI driver does - 55 test1: - 56 ; wait until controller ready - 57 0218h 8BCCh 0$: tstb (r4) ; wait for "controller ready" (csr.7) - 58 021Ah 80FEh bpl 0$ - 59 - 60 ; clear and get drive status - 61 021Ch 15F4h 000Bh 0004h mov #013,da(r4) ; subcmd reset+getstatus - 62 0222h 15C0h 0004h mov #cmstat,r0 - 63 ; test: command acceptet dirclty aufter INIT singal - 64 ; -> multiple event forwarding in ubibusadapter_c::worker() - 65 0226h 0005h reset - 66 - 67 0228h 100Ch mov r0,(r4) ; GO - 68 022Ah 8BCCh 1$: tstb (r4) ; wait for "controller ready" (csr.7) - 69 022Ch 80FEh bpl 1$ - 70 - 71 ; call @#wait65 ; - 72 - 73 ; AGAIN: clear and get drive status - 74 022Eh 15F4h 000Bh 0004h mov #013,da(r4) ; subcmd reset+getstatus - 75 0234h 15C0h 0004h mov #cmstat,r0 - 76 0238h 100Ch mov r0,(r4) ; GO - 77 023Ah 8BCCh 2$: tstb (r4) ; wait for "controller ready" (csr.7) - 78 023Ch 80FEh bpl 2$ - 79 - 80 ; call @#wait65 ; - 81 - 82 ; seek sector: read header - 83 023Eh 15C0h 0008h mov #cmrdhd,r0 ; read header cmd - 84 0242h 100Ch mov r0,(r4) ; execute - 85 0244h 8BCCh 3$: tstb (r4) ; wait for "controller ready" (csr.7) - 86 0246h 80FEh bpl 3$ - 87 0248h 1D03h 0006h mov mp(r4),r3 ; retrieve cyl/head/sector - 88 - 89 ; call @#wait65 ; - 90 + 50 ; call @#test1 + 51 ; call @#test2 + 52 0212h 09DFh 02C2h call @#test3 + 53 0216h 005Fh EA10h jmp @#monitr + 54 + 55 + 56 + 57 ; --- TEST1 do a "seek", like the PDP11GIUI driver does + 58 test1: + 59 ; wait until controller ready + 60 021Ah 8BCCh 0$: tstb (r4) ; wait for "controller ready" (csr.7) + 61 021Ch 80FEh bpl 0$ + 62 + 63 ; clear and get drive status + 64 021Eh 15F4h 000Bh 0004h mov #013,da(r4) ; subcmd reset+getstatus + 65 0224h 15C0h 0004h mov #cmstat,r0 + 66 ; test: command acceptet dirclty aufter INIT singal + 67 ; -> multiple event forwarding in ubibusadapter_c::worker() + 68 0228h 0005h reset + 69 + 70 022Ah 100Ch mov r0,(r4) ; GO + 71 022Ch 8BCCh 1$: tstb (r4) ; wait for "controller ready" (csr.7) + 72 022Eh 80FEh bpl 1$ + 73 + 74 ; call @#wait65 ; + 75 + 76 ; AGAIN: clear and get drive status + 77 0230h 15F4h 000Bh 0004h mov #013,da(r4) ; subcmd reset+getstatus + 78 0236h 15C0h 0004h mov #cmstat,r0 + 79 023Ah 100Ch mov r0,(r4) ; GO + 80 023Ch 8BCCh 2$: tstb (r4) ; wait for "controller ready" (csr.7) + 81 023Eh 80FEh bpl 2$ + 82 + 83 ; call @#wait65 ; + 84 + 85 ; seek sector: read header + 86 0240h 15C0h 0008h mov #cmrdhd,r0 ; read header cmd + 87 0244h 100Ch mov r0,(r4) ; execute + 88 0246h 8BCCh 3$: tstb (r4) ; wait for "controller ready" (csr.7) + 89 0248h 80FEh bpl 3$ + 90 024Ah 1D03h 0006h mov mp(r4),r3 ; retrieve cyl/head/sector 91 - 92 ; seek ... distance = 0 - 93 ; assume a 0 track seek - 94 024Ch 15F4h 0000h 0004h mov #0,da(r4) ; clear disk address - 95 0252h 15C0h 0006h mov #cmseek,r0 - 96 0256h 100Ch mov r0,(r4) ; execute 0 track seek - 97 0258h 8BCCh 4$: tstb (r4) ; wait for "controller ready" (csr.7) - 98 025Ah 80FEh bpl 4$ - 99 - 100 025Ch 0087h return - 101 + 92 ; call @#wait65 ; + 93 + 94 + 95 ; seek ... distance = 0 + 96 ; assume a 0 track seek + 97 024Eh 15F4h 0000h 0004h mov #0,da(r4) ; clear disk address + 98 0254h 15C0h 0006h mov #cmseek,r0 + 99 0258h 100Ch mov r0,(r4) ; execute 0 track seek + 100 025Ah 8BCCh 4$: tstb (r4) ; wait for "controller ready" (csr.7) + 101 025Ch 80FEh bpl 4$ 102 - 103 ; --- TEST2 - read sector 0 into mem at 10000 and do interrupt - 104 test2: + 103 025Eh 0087h return + 104 105 - 106 025Eh 0A1Fh FFFEh clr @#psw ; enable all interrupt levels - 107 ; wait until controller ready - 108 0262h 8BCCh 0$: tstb (r4) ; wait for "controller ready" (csr.7) - 109 0264h 80FEh bpl 0$ - 110 - 111 - 112 ; ; clear and get drive status - 113 ; mov #013,da(r4) ; subcmd reset+getstatus - 114 ; mov #cmstat,r0 - 115 ; mov r0,(r4) ; GO - 116 ;1$: tstb (r4) ; wait for "controller ready" (csr.7) - 117 ; bpl 1$ - 118 - 119 - 120 - 121 ; seek max inward , to cyl0, hd 0 - 122 0266h 15F4h FF81h 0004h mov #177600+1,da(r4) - 123 026Ch 15C0h 0006h mov #cmseek,r0 - 124 0270h 100Ch mov r0,(r4) ; execute 0 track seek - 125 0272h 8BCCh 2$: tstb (r4) ; wait for "controller ready" (csr.7) - 126 0274h 80FEh bpl 2$ - 127 ; test for "drive ready" - 128 0276h 35CCh 0001h bit #1,(r4) - 129 027Ah 03FBh beq 2$ - 130 - 131 027Ch 15F4h 8000h 0002h mov #buffer,ba(r4) ; setup memory address - 132 0282h 15F4h 0000h 0004h mov #0,da(r4) ; disk address: cyl=0, hd=0, sec=0 - 133 0288h 15F4h FF80h 0006h mov #177600,mp(r4) ; load wordcount -128 = 0400 - 134 - 135 028Eh 0A1Fh 02C0h clr @#isrcnt - 136 0292h 15F4h 004Ch 0000h mov #100+cmrdda,cs(r4) ; IE=1, function=6 read data - 137 ; mov #cmrdda,cs(r4) ; IE=0, function=6 read data - 138 ; wait for ISR - 139 3$: - 140 0298h 0BDFh 02C0h tst @#isrcnt ; wait for ISR - 141 029Ch 03FDh beq 3$ - 142 ; tstb cs(r4) ; wait for CRDY - 143 ; bpl 3$ - 144 - 145 029Eh 15C1h 033Eh mov #sba,r1 ; "BA=" - 146 02A2h 09DFh 02D6h call @#puts - 147 02A6h 1D00h 0002h mov ba(r4),r0 - 148 02AAh 09DFh 02F2h call @#putnum ; content of BA register - 149 02AEh 15C1h 0328h mov #scrlf,r1 ; end of line - 150 02B2h 09DFh 02D6h call @#puts - 151 - 152 - 153 02B6h 15C1h 0342h mov #sready,r1 ; print "READY" - 154 02BAh 09DFh 02D6h call @#puts - 155 02BEh 0087h return - 156 - 157 - 158 - 159 ; -------------- - 160 ; --- isr - called on interupt - 161 ; print incremented BA ... is DMA really ready? - 162 02C0h 0000h isrcnt: .word ; flag: ISR hit - 163 isr: - 164 02C2h 0A9Fh 02C0h inc @#isrcnt ; signal "done" - 165 02C6h 0002h rti - 166 - 167 + 106 ; --- TEST2 - read sector 0 into mem at 10000 and do interrupt + 107 test2: + 108 + 109 0260h 0A1Fh FFFEh clr @#psw ; enable all interrupt levels + 110 ; wait until controller ready + 111 0264h 8BCCh 0$: tstb (r4) ; wait for "controller ready" (csr.7) + 112 0266h 80FEh bpl 0$ + 113 + 114 + 115 ; ; clear and get drive status + 116 ; mov #013,da(r4) ; subcmd reset+getstatus + 117 ; mov #cmstat,r0 + 118 ; mov r0,(r4) ; GO + 119 ;1$: tstb (r4) ; wait for "controller ready" (csr.7) + 120 ; bpl 1$ + 121 + 122 + 123 + 124 ; seek max inward , to cyl0, hd 0 + 125 0268h 15F4h FF81h 0004h mov #177600+1,da(r4) + 126 026Eh 15C0h 0006h mov #cmseek,r0 + 127 0272h 100Ch mov r0,(r4) ; execute 0 track seek + 128 0274h 8BCCh 2$: tstb (r4) ; wait for "controller ready" (csr.7) + 129 0276h 80FEh bpl 2$ + 130 ; test for "drive ready" + 131 0278h 35CCh 0001h bit #1,(r4) + 132 027Ch 03FBh beq 2$ + 133 + 134 027Eh 15F4h 8000h 0002h mov #buffer,ba(r4) ; setup memory address + 135 0284h 15F4h 0000h 0004h mov #0,da(r4) ; disk address: cyl=0, hd=0, sec=0 + 136 028Ah 15F4h FF80h 0006h mov #177600,mp(r4) ; load wordcount -128 = 0400 + 137 + 138 0290h 0A1Fh 0388h clr @#isrcnt + 139 0294h 15F4h 004Ch 0000h mov #100+cmrdda,cs(r4) ; IE=1, function=6 read data + 140 ; mov #cmrdda,cs(r4) ; IE=0, function=6 read data + 141 ; wait for ISR + 142 3$: + 143 029Ah 0BDFh 0388h tst @#isrcnt ; wait for ISR + 144 029Eh 03FDh beq 3$ + 145 ; tstb cs(r4) ; wait for CRDY + 146 ; bpl 3$ + 147 + 148 02A0h 15C1h 042Ah mov #sba,r1 ; "BA=" + 149 02A4h 09DFh 03B8h call @#puts + 150 02A8h 1D00h 0002h mov ba(r4),r0 + 151 02ACh 09DFh 03D4h call @#putnum ; content of BA register + 152 02B0h 15C1h 0427h mov #scrlf,r1 ; end of line + 153 02B4h 09DFh 03B8h call @#puts + 154 + 155 + 156 02B8h 15C1h 042Eh mov #sready,r1 ; print "READY" + 157 02BCh 09DFh 03B8h call @#puts + 158 02C0h 0087h return + 159 + 160 + 161 ; --- TEST3 - ZRLG test 24 + 162 ; execute NOP with Interrupt, with multiple CPU intr levels + 163 ; INTR accepted for level 5 and 4 + 164 test3: + 165 02C2h 0A1Fh 0388h clr @#isrcnt + 166 ; mov #1,r0 ; idle: expected CRDY and not INTR + 167 ; call @#test3b 168 - 169 - 170 ; -- wait 65ms, uses r0 - 171 wait65: - 172 02C8h 0A00h clr r0 - 173 0$: - 174 02CAh 7E01h sob r0,0$ : subtract one, loop until zero - 175 02CCh 0087h return - 176 - 177 - 178 ; --- check for error - 179 chkerr: - 180 ; verify controller ready - 181 02CEh 8BCCh 0$: tstb (r4) ; wait for "controller ready" (csr.7) - 182 02D0h 80FEh bpl 0$ ; + 169 02C6h 15DFh 00E0h FFFEh mov #340,@#psw ; level 7 + 170 02CCh 09DFh 0332h call @#test3a ; NOP + 171 02D0h 15C0h 0001h mov #1,r0 ; expected CRDY and not INTR + 172 02D4h 09DFh 033Eh call @#test3b + 173 + 174 02D8h 15DFh 00C0h FFFEh mov #300,@#psw ; level 6 + 175 02DEh 09DFh 0332h call @#test3a ; NOP + 176 02E2h 15C0h 0001h mov #1,r0 ; expected CRDY and not INTR + 177 02E6h 09DFh 033Eh call @#test3b + 178 + 179 02EAh 15DFh 00A0h FFFEh mov #240,@#psw ; level 5 + 180 02F0h 09DFh 0332h call @#test3a ; NOP + 181 02F4h 15C0h 0001h mov #1,r0 ; expected CRDY and not INTR + 182 02F8h 09DFh 033Eh call @#test3b 183 - 184 02D2h 1300h mov (r4),r0 ; return status CSR - 185 ; bic #1777,r0 ; ignore bits 9:0, error flags are in 15:10 - 186 ; bne 1$ - 187 ; clc - 188 02D4h 0087h return ; CSR = R1 = 0: no error - 189 - 190 - 191 ; ---------------------- - 192 ; puts - print a string - 193 ; r1 = pointer, r0,r1 changed - 194 puts: - 195 02D6h 9440h movb (r1)+,r0 ; load xmt char - 196 02D8h 0303h beq 1$ ; string ends with 0 - 197 02DAh 09DFh 031Ch call @#putc - 198 02DEh 01FBh br puts ; transmit nxt char of string - 199 02E0h 0087h 1$: return + 184 02FCh 15DFh 0080h FFFEh mov #200,@#psw ; level 4, below BR5, pending triggered + 185 0302h 15C0h 0003h mov #3,r0 ; expected CRDY and INTR + 186 0306h 09DFh 033Eh call @#test3b + 187 + 188 030Ah 0A1Fh 0388h clr @#isrcnt + 189 030Eh 09DFh 0332h call @#test3a ; NOP at level 4 + 190 0312h 15C0h 0003h mov #3,r0 ; expected CRDY and INTR + 191 0316h 09DFh 033Eh call @#test3b + 192 + 193 031Ah 0A1Fh 0388h clr @#isrcnt + 194 031Eh 15DFh 0000h FFFEh mov #0,@#psw ; level 0 + 195 0324h 09DFh 0332h call @#test3a ; NOP + 196 0328h 15C0h 0003h mov #3,r0 ; expected CRDY and INTR + 197 032Ch 09DFh 033Eh call @#test3b + 198 0330h 0087h return + 199 200 - 201 - 202 ; ---------------------- - 203 ; putnum - print the octal number in r0 - 204 02E2h numbf0: .blkw 10 ; space to mount number string - 205 02F2h numbf1 =. - 206 putnum: - 207 02F2h 1002h mov r0,r2 ; r2 = shifter - 208 02F4h 15C1h 02F2h mov #numbf1,r1 ; r1 = buffer pointer, backwards - 209 02F8h 95E1h 0000h movb #0,-(r1) ; set terminating 0 - 210 ; repeat 6 times - 211 02FCh 15C3h 0006h mov #6,r3 - 212 1$: - 213 0300h 1080h mov r2,r0 - 214 ; extract lower 3 bits = octal digit - 215 0302h 45C0h FFF8h bic #177770,r0 ; r0 &= 0x07 - 216 0306h 65C0h 0030h add #60,r0 ; r0 += '0' - 217 030Ah 9021h movb r0,-(r1) ; write in buffer - 218 030Ch 00A1h clc - 219 030Eh 0C02h ror r2 ; shift to next digit - 220 0310h 0C02h ror r2 - 221 0312h 0C02h ror r2 - 222 0314h 7ECBh sob r3,1$ ; loop for all 6 digits - 223 - 224 0316h 09DFh 02D6h call @#puts - 225 031Ah 0087h return - 226 - 227 - 228 ; DEC DL11 console I/O - 229 ; ---------------------- - 230 ; putc - output a single char - 231 ; r0 = char - 232 putc: - 233 031Ch 901Fh FF76h movb r0,@#dladr+6 ; char into transmit buffer - 234 0320h 8BDFh FF74h 1$: tstb @#dladr+4 ; XMT RDY? - 235 0324h 80FDh bpl 1$ ; no, loop - 236 0326h 0087h return - 237 - 238 - 239 ; --- string constants - 240 0328h 0Dh 0Ah scrlf: .byte 15,12 ; CR, LF, - 241 032Ah 00h .byte 0 - 242 - 243 - 244 shello: - 245 032Bh 0Dh 0Ah .byte 15,12 ; CR, LF, - 246 032Dh 53h 74h 61h .ascii /Starting test!/ - 0330h 72h 74h 69h - 0333h 6Eh 67h 20h - 0336h 74h 65h 73h - 0339h 74h 21h - 247 033Bh 0Dh 0Ah .byte 15,12 ; CR, LF, - 248 033Dh 00h .byte 0 - 249 - 250 033Eh 42h 41h 3Dh sba: .ascii /BA=/ - 251 0341h 00h .byte 0 ; NUL=end marker - 252 - 253 0342h 53h 65h 63h sready: .ascii /Sector 0 transfered to 100000, ISR hit./ - 0345h 74h 6Fh 72h - 0348h 20h 30h 20h - 034Bh 74h 72h 61h - 034Eh 6Eh 73h 66h - 0351h 65h 72h 65h - 0354h 64h 20h 74h - 0357h 6Fh 20h 31h - 035Ah 30h 30h 30h - 035Dh 30h 30h 2Ch - 0360h 20h 49h 53h - 0363h 52h 20h 68h - 0366h 69h 74h 2Eh - 254 0369h 0Dh 0Ah .byte 15,12 ; CR, LF, - 255 036Bh 00h .byte 0 - 256 - 257 ; ---- 32kb page - 258 8000h . = 100000 - 259 buffer: - 260 .end ; - 261 - 262 .end - 262 + 201 ; send NOP cmd and wait for crdy + 202 test3a: + 203 ; pending interupt condition not cleared + 204 0332h 0A1Fh 0388h clr @#isrcnt + 205 0336h 15C0h 0040h mov #cmnop!100,r0 ; nop with Interrupt Enable + 206 033Ah 100Ch mov r0,(r4) ; NOOP + 207 033Ch 0087h return + 208 + 209 ; check CRDY and ISR + 210 ; r0: expected result + 211 ; 0 = neither ISR nor CRDY + 212 ; 1 = CRDY without ISR + 213 ; 2 = ISR without CRDY + 214 ; 3 = ISR and CRDY + 215 test3b: + 216 033Eh 1026h mov r0,-(sp) ; push + 217 ; pending interupt condition not cleared + 218 0340h 0A03h clr r3 ; result + 219 0342h 15C0h 03E8h mov #1750,r0 ; wait 1000us for ISR and CRDY + 220 0346h 09DFh 0390h call @#wtcrdy + 221 034Ah 8702h bcs 1$ + 222 034Ch 55C3h 0001h bis #1,r3 ; Carry clear = NO timeout: CRDY r3 = 1 + 223 1$: + 224 0350h 0BDFh 0388h tst @#isrcnt + 225 0354h 0302h beq 2$ + 226 0356h 55C3h 0002h bis #2,r3 ; ISR detected: result |= 2 + 227 2$: + 228 035Ah 1582h mov (sp)+,r2 ; pop + 229 ; r2 = crd+isr code as expected, r3 = as measured + 230 035Ch 2083h cmp r2,r3 + 231 035Eh 0301h beq 3$ + 232 0360h 0000h halt + 233 3$: + 234 ; print "CRDY+ISR = ..., expected .... + 235 0362h 15C1h 0458h mov #scrdy1,r1 ; "CRDY+ISR =" + 236 0366h 09DFh 03B8h call @#puts + 237 036Ah 10C0h mov r3,r0 + 238 036Ch 09DFh 03D4h call @#putnum + 239 0370h 15C1h 0464h mov #scrdy2,r1 ; "expected" + 240 0374h 09DFh 03B8h call @#puts + 241 0378h 1080h mov r2,r0 + 242 037Ah 09DFh 03D4h call @#putnum + 243 037Eh 15C1h 0427h mov #scrlf,r1 ; print end of line + 244 0382h 09DFh 03B8h call @#puts + 245 0386h 0087h return + 246 + 247 + 248 ; -------------- + 249 ; --- isr - called on interupt + 250 ; print incremented BA ... is DMA really ready? + 251 0388h 0000h isrcnt: .word ; flag: ISR hit + 252 isr: + 253 038Ah 0A9Fh 0388h inc @#isrcnt ; signal "done" + 254 038Eh 0002h rti + 255 + 256 ; - wait for "controller ready", but max r0 microseconds + 257 ; result: carry clear = OK, + 258 ; carry set = timeout + 259 + 260 wtcrdy: + 261 0390h 0C80h asr r0 ; wait loop is 4 cycles + 262 0392h 0C80h asr r0 + 263 1$: + 264 0394h 8BCCh tstb (r4) ; 2 cycles + 265 0396h 8103h bmi 9$ ; bit 7 set -> controller ready + 266 0398h 7E03h sob r0,1$ + 267 + 268 039Ah 00B1h sec ; "timeout" + 269 039Ch 0087h return + 270 9$: + 271 039Eh 00A1h clc ; "OK" + 272 03A0h 0087h return + 273 + 274 + 275 ; -- wait 65ms, uses r0 + 276 wait65: + 277 03A2h 0A00h clr r0 + 278 0$: + 279 03A4h 7E01h sob r0,0$ : subtract one, loop until zero + 280 03A6h 0087h return + 281 + 282 ; -- wait 1ms, uses r0 + 283 wait1: + 284 03A8h 15C0h 03E8h mov #1750,r0 ; 1000 us + 285 0$: + 286 03ACh 7E01h sob r0,0$ : subtract one, loop until zero + 287 03AEh 0087h return + 288 + 289 + 290 ; --- check for error + 291 chkerr: + 292 ; verify controller ready + 293 03B0h 8BCCh 0$: tstb (r4) ; wait for "controller ready" (csr.7) + 294 03B2h 80FEh bpl 0$ ; + 295 + 296 03B4h 1300h mov (r4),r0 ; return status CSR + 297 ; bic #1777,r0 ; ignore bits 9:0, error flags are in 15:10 + 298 ; bne 1$ + 299 ; clc + 300 03B6h 0087h return ; CSR = R1 = 0: no error + 301 + 302 + 303 ; ---------------------- + 304 ; puts - print a string + 305 ; r1 = pointer, r0,r1 changed + 306 puts: + 307 03B8h 9440h movb (r1)+,r0 ; load xmt char + 308 03BAh 0303h beq 1$ ; string ends with 0 + 309 03BCh 09DFh 0406h call @#putc + 310 03C0h 01FBh br puts ; transmit nxt char of string + 311 03C2h 0087h 1$: return + 312 + 313 + 314 ; ---------------------- + 315 ; putnum - print the octal number in r0 + 316 03C4h numbf0: .blkw 10 ; space to mount number string + 317 03D4h numbf1 =. + 318 putnum: + 319 03D4h 10A6h mov r2,-(sp) + 320 03D6h 10E6h mov r3,-(sp) + 321 03D8h 1002h mov r0,r2 ; r2 = shifter + 322 03DAh 15C1h 03D4h mov #numbf1,r1 ; r1 = buffer pointer, backwards + 323 03DEh 95E1h 0000h movb #0,-(r1) ; set terminating 0 + 324 ; repeat 6 times + 325 03E2h 15C3h 0006h mov #6,r3 + 326 1$: + 327 03E6h 1080h mov r2,r0 + 328 ; extract lower 3 bits = octal digit + 329 03E8h 45C0h FFF8h bic #177770,r0 ; r0 &= 0x07 + 330 03ECh 65C0h 0030h add #60,r0 ; r0 += '0' + 331 03F0h 9021h movb r0,-(r1) ; write in buffer + 332 03F2h 00A1h clc + 333 03F4h 0C82h asr r2 ; shift to next digit + 334 03F6h 0C82h asr r2 + 335 03F8h 0C82h asr r2 + 336 03FAh 7ECBh sob r3,1$ ; loop for all 6 digits + 337 + 338 03FCh 09DFh 03B8h call @#puts + 339 0400h 1583h mov (sp)+,r3 + 340 0402h 1582h mov (sp)+,r2 + 341 0404h 0087h return + 342 + 343 + 344 ; DEC DL11 console I/O + 345 ; ---------------------- + 346 ; putc - output a single char + 347 ; r0 = char + 348 putc: + 349 0406h 901Fh FF76h movb r0,@#dladr+6 ; char into transmit buffer + 350 040Ah 8BDFh FF74h 1$: tstb @#dladr+4 ; XMT RDY? + 351 040Eh 80FDh bpl 1$ ; no, loop + 352 0410h 0087h return + 353 + 354 + 355 ; --- string constants + 356 shello: + 357 0412h 0Dh 0Ah .byte 15,12 ; CR, LF, + 358 0414h 53h 74h 61h .ascii /Starting RL11 test!/ + 0417h 72h 74h 69h + 041Ah 6Eh 67h 20h + 041Dh 52h 4Ch 31h + 0420h 31h 20h 74h + 0423h 65h 73h 74h + 0426h 21h + 359 scrlf: + 360 0427h 0Dh 0Ah .byte 15,12 ; CR, LF, + 361 0429h 00h .byte 0 + 362 + 363 042Ah 42h 41h 3Dh sba: .ascii /BA=/ + 364 042Dh 00h .byte 0 ; NUL=end marker + 365 + 366 042Eh 53h 65h 63h sready: .ascii /Sector 0 transfered to 100000, ISR hit./ + 0431h 74h 6Fh 72h + 0434h 20h 30h 20h + 0437h 74h 72h 61h + 043Ah 6Eh 73h 66h + 043Dh 65h 72h 65h + 0440h 64h 20h 74h + 0443h 6Fh 20h 31h + 0446h 30h 30h 30h + 0449h 30h 30h 2Ch + 044Ch 20h 49h 53h + 044Fh 52h 20h 68h + 0452h 69h 74h 2Eh + 367 0455h 0Dh 0Ah .byte 15,12 ; CR, LF, + 368 0457h 00h .byte 0 + 369 + 370 + 371 0458h 43h 52h 44h scrdy1: .ascii /CRDY+ISR = / + 045Bh 59h 2Bh 49h + 045Eh 53h 52h 20h + 0461h 3Dh 20h + 372 0463h 00h .byte 0 + 373 0464h 2Ch 20h 65h scrdy2: .ascii /, expected / + 0467h 78h 70h 65h + 046Ah 63h 74h 65h + 046Dh 64h 20h + 374 046Fh 00h .byte 0 + 375 + 376 + 377 ; ---- 32kb page + 378 8000h . = 100000 + 379 buffer: + 380 .end ; + 381 + 382 .end + 382 diff --git a/10.02_devices/3_test/rl02/testrl11.mac b/10.02_devices/3_test/rl02/testrl11.mac index d745147..08f8bad 100644 --- a/10.02_devices/3_test/rl02/testrl11.mac +++ b/10.02_devices/3_test/rl02/testrl11.mac @@ -15,7 +15,8 @@ psw = 177776 ; processor status monitr = 165020 ; entry into M9312 monitor - ;; RL11 commands + ; RL11 commands +cmnop = 2*0 ; no op cmstat = 2*2 ; get status cmseek = 3*2 ; seek cmrdhd = 4*2 ; read header @@ -41,12 +42,14 @@ start: mov #stack,sp ; init stack mov #rlbase,r4 ; r4 points to RL11 register space + reset + mov #shello,r1 call @#puts - call @#test1 + ; call @#test1 ; call @#test2 - + call @#test3 jmp @#monitr @@ -155,6 +158,92 @@ test2: return + ; --- TEST3 - ZRLG test 24 + ; execute NOP with Interrupt, with multiple CPU intr levels + ; INTR accepted for level 5 and 4 +test3: + clr @#isrcnt + ; mov #1,r0 ; idle: expected CRDY and not INTR + ; call @#test3b + + mov #340,@#psw ; level 7 + call @#test3a ; NOP + mov #1,r0 ; expected CRDY and not INTR + call @#test3b + + mov #300,@#psw ; level 6 + call @#test3a ; NOP + mov #1,r0 ; expected CRDY and not INTR + call @#test3b + + mov #240,@#psw ; level 5 + call @#test3a ; NOP + mov #1,r0 ; expected CRDY and not INTR + call @#test3b + + mov #200,@#psw ; level 4, below BR5, pending triggered + mov #3,r0 ; expected CRDY and INTR + call @#test3b + + clr @#isrcnt + call @#test3a ; NOP at level 4 + mov #3,r0 ; expected CRDY and INTR + call @#test3b + + clr @#isrcnt + mov #0,@#psw ; level 0 + call @#test3a ; NOP + mov #3,r0 ; expected CRDY and INTR + call @#test3b + return + + + ; send NOP cmd and wait for crdy +test3a: + ; pending interupt condition not cleared + clr @#isrcnt + mov #cmnop!100,r0 ; nop with Interrupt Enable + mov r0,(r4) ; NOOP + return + + ; check CRDY and ISR + ; r0: expected result + ; 0 = neither ISR nor CRDY + ; 1 = CRDY without ISR + ; 2 = ISR without CRDY + ; 3 = ISR and CRDY +test3b: + mov r0,-(sp) ; push + ; pending interupt condition not cleared + clr r3 ; result + mov #1750,r0 ; wait 1000us for ISR and CRDY + call @#wtcrdy + bcs 1$ + bis #1,r3 ; Carry clear = NO timeout: CRDY r3 = 1 +1$: + tst @#isrcnt + beq 2$ + bis #2,r3 ; ISR detected: result |= 2 +2$: + mov (sp)+,r2 ; pop + ; r2 = crd+isr code as expected, r3 = as measured + cmp r2,r3 + beq 3$ + halt +3$: + ; print "CRDY+ISR = ..., expected .... + mov #scrdy1,r1 ; "CRDY+ISR =" + call @#puts + mov r3,r0 + call @#putnum + mov #scrdy2,r1 ; "expected" + call @#puts + mov r2,r0 + call @#putnum + mov #scrlf,r1 ; print end of line + call @#puts + return + ; -------------- ; --- isr - called on interupt @@ -164,7 +253,23 @@ isr: inc @#isrcnt ; signal "done" rti + ; - wait for "controller ready", but max r0 microseconds + ; result: carry clear = OK, + ; carry set = timeout +wtcrdy: + asr r0 ; wait loop is 4 cycles + asr r0 +1$: + tstb (r4) ; 2 cycles + bmi 9$ ; bit 7 set -> controller ready + sob r0,1$ + + sec ; "timeout" + return +9$: + clc ; "OK" + return ; -- wait 65ms, uses r0 @@ -174,6 +279,13 @@ wait65: sob r0,0$ : subtract one, loop until zero return + ; -- wait 1ms, uses r0 +wait1: + mov #1750,r0 ; 1000 us +0$: + sob r0,0$ : subtract one, loop until zero + return + ; --- check for error chkerr: @@ -204,6 +316,8 @@ puts: numbf0: .blkw 10 ; space to mount number string numbf1 =. putnum: + mov r2,-(sp) + mov r3,-(sp) mov r0,r2 ; r2 = shifter mov #numbf1,r1 ; r1 = buffer pointer, backwards movb #0,-(r1) ; set terminating 0 @@ -216,12 +330,14 @@ putnum: add #60,r0 ; r0 += '0' movb r0,-(r1) ; write in buffer clc - ror r2 ; shift to next digit - ror r2 - ror r2 + asr r2 ; shift to next digit + asr r2 + asr r2 sob r3,1$ ; loop for all 6 digits call @#puts + mov (sp)+,r3 + mov (sp)+,r2 return @@ -237,13 +353,10 @@ putc: ; --- string constants -scrlf: .byte 15,12 ; CR, LF, - .byte 0 - - shello: .byte 15,12 ; CR, LF, - .ascii /Starting test!/ + .ascii /Starting RL11 test!/ +scrlf: .byte 15,12 ; CR, LF, .byte 0 @@ -254,6 +367,13 @@ sready: .ascii /Sector 0 transfered to 100000, ISR hit./ .byte 15,12 ; CR, LF, .byte 0 + +scrdy1: .ascii /CRDY+ISR = / + .byte 0 +scrdy2: .ascii /, expected / + .byte 0 + + ; ---- 32kb page . = 100000 buffer: diff --git a/10.02_devices/3_test/rl02/xxdp.cmd b/10.02_devices/3_test/rl02/xxdp.cmd index 548985a..ff63562 100644 --- a/10.02_devices/3_test/rl02/xxdp.cmd +++ b/10.02_devices/3_test/rl02/xxdp.cmd @@ -1,21 +1,36 @@ # inputfile for demo to select a rl1 device in the "device test" menu. # Read in with command line option "demo --cmdfile ..." -td # device test menu +d # device test menu + +# first, make a serial port. Default ist +# sd dl11 +# p p ttyS2 # use "UART2 connector +# en dl11 + + +pwr .wait 3000 # wait for PDP-11 to reset m i # install max UNIBUS memory -# mount XXDP22 in RL02 #0 and start -sd rl0 # select drive #0 +# Deposit bootloader into memory +m ll dl.lst + +en rl # enable RL11 controller + +# mount scratch 0 in RL02 #0 and start +en rl0 # enable drive #0 +sd rl0 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" p powerswitch 1 # power on, now in "load" state -p image xxdp22.rl02 # mount image file with test pattern +p image scratch2.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start #.end # mount XXDP25 in RL02 #1 and start -sd rl1 # select drive #1 +en rl1 # enable drive #1 +sd rl1 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -23,17 +38,19 @@ p powerswitch 1 # power on, now in "load" state p image xxdp25.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start -# mount scratch2 in RL02 #2 and start -sd rl2 # select drive #2 +# mount XXDP22 in RL02 #2 and start +en rl2 # enable drive #2 +sd rl2 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" p powerswitch 1 # power on, now in "load" state -p image scratch2.rl02 # mount image file with test pattern +p image xxdp22.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch3 in RL02 #3 and start -sd rl3 # select drive #3 +en rl3 # enable drive #3 +sd rl3 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -45,4 +62,7 @@ p runstopbutton 1 # press RUN/STOP, will start .wait 6000 # wait until drive spins up p # show all params of RL1 - +.print RL drives ready. +.print RL11 boot loader installed. +.print Start 10000 to boot from drive 0, 10010 for drive 1, ... +.print Reload with "m ll" diff --git a/10.02_devices/3_test/rl02/xxdp25_boot.als b/10.02_devices/3_test/rl02/xxdp25_boot.als deleted file mode 100644 index 883ed65..0000000 Binary files a/10.02_devices/3_test/rl02/xxdp25_boot.als and /dev/null differ diff --git a/10.02_devices/3_test/rl02/zrlg.als b/10.02_devices/3_test/rl02/zrlg.als deleted file mode 100644 index fbb189d..0000000 Binary files a/10.02_devices/3_test/rl02/zrlg.als and /dev/null differ diff --git a/10.02_devices/3_test/rl02/zrlg_22.als b/10.02_devices/3_test/rl02/zrlg_22.als deleted file mode 100644 index 8b5c6bc..0000000 Binary files a/10.02_devices/3_test/rl02/zrlg_22.als and /dev/null differ diff --git a/10.03_app_demo/2_src/application.cpp b/10.03_app_demo/2_src/application.cpp index e2100c6..b0b56f0 100644 --- a/10.03_app_demo/2_src/application.cpp +++ b/10.03_app_demo/2_src/application.cpp @@ -59,6 +59,7 @@ using namespace std; #include "getopt2.hpp" #include "inputline.h" +#include "kbhit.h" #include "pru.hpp" #include "mailbox.h" #include "gpios.hpp" @@ -222,9 +223,8 @@ void application_c::parse_commandline(int argc, char **argv) { } // configure all hardware related subsystems: -// PUR, shard memory, GPIOs, +// PRU, shard memory, GPIOs void application_c::hardware_startup(enum pru_c::prucode_enum prucode_id) { - INFO("Connecting to PRU."); /* initialize the library, PRU and interrupt; launch our PRU program */ @@ -247,6 +247,13 @@ void application_c::hardware_startup(enum pru_c::prucode_enum prucode_id) { iopageregisters_init(); } +// if prucode_id is UNIBUS, define also wether Abritration ignored, doen by remote CPU or emulated +void application_c::hardware_startup(enum pru_c::prucode_enum prucode_id, + enum unibus_c::arbitration_mode_enum arbitration_mode) { + hardware_startup(prucode_id); + unibus_c::set_arbitration_mode(arbitration_mode); +} + // disable all hardware related subsystems: void application_c::hardware_shutdown() { pru->stop(); @@ -320,7 +327,7 @@ static void factory() { pru = new pru_c(); gpios = new gpios_c(); - unibus_signals = new unibus_signals_c() ; + unibus_signals = new unibus_signals_c(); ddrmem = new ddrmem_c(); // paneldriver before all devices who use lamps or buttons @@ -329,12 +336,18 @@ static void factory() { membuffer = new memoryimage_c(); unibus = new unibus_c(); + // unibusadapter.worker() needs initialized mailbox unibusadapter = new unibusadapter_c(); app = new application_c(); } int main(int argc, char *argv[]) { + + // flush stuff on stdin. (Eclipse remote debugging) + while (os_kbhit()) + ; + factory(); return app->run(argc, argv); } diff --git a/10.03_app_demo/2_src/application.hpp b/10.03_app_demo/2_src/application.hpp index 4d5620b..30fb385 100644 --- a/10.03_app_demo/2_src/application.hpp +++ b/10.03_app_demo/2_src/application.hpp @@ -43,7 +43,7 @@ #include "unibusdevice.hpp" #define PROGNAME "demo" -#define VERSION "v1.1.0" +#define VERSION "v1.5.0" class application_c: public logsource_c { public: @@ -68,19 +68,27 @@ public: void parse_commandline(int argc, char **argv); void hardware_startup(enum pru_c::prucode_enum prucode_id); + void hardware_startup(enum pru_c::prucode_enum prucode_id, + enum unibus_c::arbitration_mode_enum arbitration_mode); + void hardware_shutdown(void); // UniBone should emulate this address range uint32_t emulated_memory_start_addr; uint32_t emulated_memory_end_addr; - void print_arbitration_info(enum unibus_c::arbitration_mode_enum arbitration_mode, const char *indent); - char *getchoice(void); - bool emulate_memory(enum unibus_c::arbitration_mode_enum arbitration_mode); + void print_arbitration_info(enum unibus_c::arbitration_mode_enum arbitration_mode, + const char *indent); + char *getchoice(void);bool emulate_memory( + enum unibus_c::arbitration_mode_enum arbitration_mode); void print_params(parameterized_c *parameterized, parameter_c *p); unibusdevice_register_t * device_register_by_id(unibusdevice_c *device, char *specifier); + bool parse_word(char *txt, uint16_t *word);bool parse_addr18(char *txt, uint32_t *addr);bool parse_level( + char *txt, uint8_t *level);bool parse_vector(char *txt, uint16_t max_vector, + uint16_t *vector);bool parse_slot(char *txt, uint8_t *priority_slot); + void menu_info(void); void menu_gpio(void); void menu_panel(void); diff --git a/10.03_app_demo/2_src/makefile b/10.03_app_demo/2_src/makefile index a566175..dbfdcda 100644 --- a/10.03_app_demo/2_src/makefile +++ b/10.03_app_demo/2_src/makefile @@ -23,8 +23,8 @@ PRUSS_INCLUDE_DIR = $(PRU_PACKAGE_ROOT)/include # ARM LIBRARIES PRUSS_DRV_LIB = $(PRU_PACKAGE_ROOT)/bin-bbb/libprussdrv.a -# -static: do not used shared libs, include all coide into the binary -# (big binary, but BBB neds no shared libs of certain versions installed) +# -static: do not used shared libs, include all code into the binary +# (big binary, but BBB needs no shared libs of certain versions installed) # Example: demo binary goes from 594K to 12.3MB ! LDFLAGS+= -static -lstdc++ -lpthread $(PRUSS_DRV_LIB) @@ -104,20 +104,25 @@ OBJECTS = $(OBJDIR)/application.o \ $(OBJDIR)/uda.o \ $(OBJDIR)/mscp_server.o \ $(OBJDIR)/mscp_drive.o \ + $(OBJDIR)/rs232.o \ + $(OBJDIR)/rs232adapter.o \ + $(OBJDIR)/dl11w.o \ $(OBJDIR)/storagedrive.o \ $(OBJDIR)/storagecontroller.o \ $(OBJDIR)/demo_io.o \ - $(OBJDIR)/demo_regs.o \ + $(OBJDIR)/testcontroller.o \ $(OBJDIR)/unibusdevice.o \ $(OBJDIR)/device.o \ $(OBJDIR)/parameter.o \ $(OBJDIR)/panel.o \ + $(OBJDIR)/priorityrequest.o \ $(OBJDIR)/unibusadapter.o \ $(OBJDIR)/unibus.o \ $(OBJDIR)/gpios.o \ $(OBJDIR)/stringgrid.o \ $(OBJDIR)/mcout.o \ $(OBJDIR)/inputline.o \ + $(OBJDIR)/kbhit.o \ $(OBJDIR)/bitcalc.o \ $(OBJDIR)/pru.o \ $(OBJDIR)/mailbox.o \ @@ -148,7 +153,7 @@ clean: # TODO -# uato dependcies +# auto dependencies of header files # http://scottmcpeak.com/autodepend/autodepend.html # gcc -MM $(CCFLAGS) $< >$(OBJDIR)*.c > ***.d @@ -233,6 +238,15 @@ $(OBJDIR)/mscp_server.o : $(DEVICE_SRC_DIR)/mscp_server.cpp $(DEVICE_SRC_DIR)/ $(OBJDIR)/mscp_drive.o : $(DEVICE_SRC_DIR)/mscp_drive.cpp $(DEVICE_SRC_DIR)/mscp_drive.hpp $(CC) $(CCFLAGS) $< -o $@ +$(OBJDIR)/rs232.o : $(DEVICE_SRC_DIR)/rs232.cpp $(DEVICE_SRC_DIR)/rs232.hpp + $(CC) $(CCFLAGS) $< -o $@ + +$(OBJDIR)/rs232adapter.o : $(DEVICE_SRC_DIR)/rs232adapter.cpp $(DEVICE_SRC_DIR)/rs232adapter.hpp + $(CC) $(CCFLAGS) $< -o $@ + +$(OBJDIR)/dl11w.o : $(DEVICE_SRC_DIR)/dl11w.cpp $(DEVICE_SRC_DIR)/dl11w.hpp + $(CC) $(CCFLAGS) $< -o $@ + $(OBJDIR)/storagedrive.o : $(BASE_SRC_DIR)/storagedrive.cpp $(BASE_SRC_DIR)/storagedrive.hpp $(CC) $(CCFLAGS) $< -o $@ @@ -242,7 +256,7 @@ $(OBJDIR)/storagecontroller.o : $(BASE_SRC_DIR)/storagecontroller.cpp $(BASE_SR $(OBJDIR)/demo_io.o : $(DEVICE_SRC_DIR)/demo_io.cpp $(DEVICE_SRC_DIR)/demo_io.hpp $(CC) $(CCFLAGS) $< -o $@ -$(OBJDIR)/demo_regs.o : $(DEVICE_SRC_DIR)/demo_regs.cpp $(DEVICE_SRC_DIR)/demo_regs.hpp +$(OBJDIR)/testcontroller.o : $(DEVICE_SRC_DIR)/testcontroller.cpp $(DEVICE_SRC_DIR)/testcontroller.hpp $(CC) $(CCFLAGS) $< -o $@ $(OBJDIR)/unibusdevice.o : $(BASE_SRC_DIR)/unibusdevice.cpp $(BASE_SRC_DIR)/unibusdevice.hpp @@ -272,6 +286,9 @@ $(OBJDIR)/mcout.o : $(COMMON_SRC_DIR)/mcout.c $(COMMON_SRC_DIR)/mcout.h $(OBJDIR)/inputline.o : $(COMMON_SRC_DIR)/inputline.c $(COMMON_SRC_DIR)/inputline.h $(CC) $(CCFLAGS) -xc++ $< -o $@ +$(OBJDIR)/kbhit.o : $(COMMON_SRC_DIR)/kbhit.c $(COMMON_SRC_DIR)/kbhit.h + $(CC) $(CCFLAGS) -xc++ $< -o $@ + $(OBJDIR)/bitcalc.o : $(COMMON_SRC_DIR)/bitcalc.cpp $(COMMON_SRC_DIR)/bitcalc.h $(CC) $(CCFLAGS) $< -o $@ @@ -309,6 +326,9 @@ $(OBJDIR)/ddrmem.o : $(BASE_SRC_DIR)/ddrmem.cpp $(SHARED_SRC_DIR)/ddrmem.h $(OBJDIR)/iopageregister.o : $(BASE_SRC_DIR)/iopageregister.cpp $(SHARED_SRC_DIR)/iopageregister.h $(CC) $(CCFLAGS) $< -o $@ +$(OBJDIR)/priorityrequest.o : $(BASE_SRC_DIR)/priorityrequest.cpp $(BASE_SRC_DIR)/priorityrequest.hpp + $(CC) $(CCFLAGS) $< -o $@ + $(OBJDIR)/unibusadapter.o : $(BASE_SRC_DIR)/unibusadapter.cpp $(BASE_SRC_DIR)/unibusadapter.hpp $(CC) $(CCFLAGS) $< -o $@ diff --git a/10.03_app_demo/2_src/menu_buslatches.cpp b/10.03_app_demo/2_src/menu_buslatches.cpp index 8680f56..65c87f5 100644 --- a/10.03_app_demo/2_src/menu_buslatches.cpp +++ b/10.03_app_demo/2_src/menu_buslatches.cpp @@ -77,7 +77,7 @@ static bool buslatches_oscillate_pin(buslatches_wire_info_t *si, unsigned timeou timeout_c timeout; unsigned count; - timeout.start(1000000L * timeout_ms); + timeout.start_ms(timeout_ms); SIGINTcatchnext(); // high speed loop diff --git a/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp b/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp index 6d5de50..8514aa5 100644 --- a/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp +++ b/10.03_app_demo/2_src/menu_ddrmem_slave_only.cpp @@ -47,7 +47,7 @@ void application_c::menu_ddrmem_slave_only() { bool ready; int n_fields; - hardware_startup(pru_c::PRUCODE_UNIBUS) ; + hardware_startup(pru_c::PRUCODE_UNIBUS, unibus_c::ARBITRATION_MODE_NONE); ready = false; while (!ready) { @@ -93,8 +93,9 @@ void application_c::menu_ddrmem_slave_only() { else printf("Use \"f a\" or \"f p\"\n"); } else if (!strcasecmp(s_opcode, "u") && (n_fields == 3)) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); if (ddrmem->set_range(start_addr, end_addr)) { emulated_memory_start_addr = start_addr; emulated_memory_end_addr = end_addr; @@ -117,7 +118,7 @@ void application_c::menu_ddrmem_slave_only() { show_help = true; } } - hardware_shutdown() ; - + hardware_shutdown(); + } 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..827965b 100644 --- a/10.03_app_demo/2_src/menu_device_exercisers.cpp +++ b/10.03_app_demo/2_src/menu_device_exercisers.cpp @@ -40,9 +40,10 @@ #include "parameter.hpp" #include "unibus.h" #include "memoryimage.hpp" - #include "unibusadapter.hpp" -#include "unibusdevice.hpp" + +//#include "unibusadapter.hpp" +//#include "unibusdevice.hpp" #include "devexer_rl.hpp" @@ -58,14 +59,10 @@ void application_c::menu_device_exercisers(void) { // iopageregisters_init(); // UNIBUS activity - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, unibus_c::ARBITRATION_MODE_CLIENT); 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 @@ -86,8 +83,7 @@ void application_c::menu_device_exercisers(void) { } else printf(" No current device selected\n"); if (memory_installed) { - printf( - " UNIBUS memory emulated from %06o to %06o.\n", + printf(" UNIBUS memory emulated from %06o to %06o.\n", emulated_memory_start_addr, emulated_memory_end_addr); } else printf(" NO UNIBUS memory installed ... device test limited!\n"); @@ -111,7 +107,7 @@ void application_c::menu_device_exercisers(void) { printf("e Examine all device registers\n"); printf("d Deposit octal val into UNIBUS address.\n"); printf("e Deposit octal val into UNIBUS address.\n"); - printf("dl c|s|f Debug log: Clear, Show on console, dump to File.\n"); + printf("dbg c|s|f Debug log: Clear, Show on console, dump to File.\n"); printf(" (file = %s)\n", logger->default_filepath.c_str()); printf("init Pulse UNIBUS INIT\n"); printf("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); @@ -128,7 +124,7 @@ void application_c::menu_device_exercisers(void) { unibus->init(); } else if (!strcasecmp(s_opcode, "pwr")) { unibus->powercycle(); - } else if (!strcasecmp(s_opcode, "dl") && n_fields == 2) { + } else if (!strcasecmp(s_opcode, "dbg") && n_fields == 2) { if (!strcasecmp(s_param[0], "c")) { logger->clear(); printf("Debug log cleared.\n"); @@ -147,17 +143,14 @@ void application_c::menu_device_exercisers(void) { bool timeout; uint16_t fillword = 0; if (n_fields == 3) - fillword = strtol(s_param[1], NULL, 8); + parse_word(s_param[1], &fillword); membuffer->set_addr_range(emulated_memory_start_addr, emulated_memory_end_addr); membuffer->fill(fillword); // write buffer-> UNIBUS - printf( - "Fill memory with %06o, writing UNIBUS memory[%06o:%06o] with blocksize %u words\n", - fillword, emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount); + printf("Fill memory with %06o, writing UNIBUS memory[%06o:%06o]\n", fillword, + emulated_memory_start_addr, emulated_memory_end_addr); unibus->mem_write(arbitration_mode, membuffer->data.words, - emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount, &timeout); + emulated_memory_start_addr, emulated_memory_end_addr, &timeout); if (timeout) printf("Error writing UNIBUS memory!\n"); } else if (memory_installed && !strcasecmp(s_opcode, "m") && n_fields == 2 @@ -167,14 +160,13 @@ void application_c::menu_device_exercisers(void) { bool timeout; // 1. read UNIBUS memory uint32_t end_addr = unibus->test_sizer(arbitration_mode) - 2; - printf("Reading UNIBUS memory[0:%06o] with DMA blocks of %u words\n", end_addr, - unibus->dma_wordcount); + printf("Reading UNIBUS memory[0:%06o] with DMA\n", end_addr); // clear memory buffer, to be sure content changed membuffer->set_addr_range(0, end_addr); membuffer->fill(0); unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, - unibus->dma_wordcount, &timeout); + &timeout); if (timeout) printf("Error reading UNIBUS memory!\n"); else { @@ -230,27 +222,27 @@ void application_c::menu_device_exercisers(void) { } } else if (!strcasecmp(s_opcode, "d") && n_fields == 3) { uint32_t addr; + uint16_t wordbuffer; // interpret as 18 bit address - addr = strtol(s_param[0], NULL, 8); - - mailbox->dma.words[0] = strtol(s_param[1], NULL, 8); - bool timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATO, addr, 1); - printf("DEPOSIT %06o <- %06o\n", addr, mailbox->dma.words[0]); + parse_addr18(s_param[0], &addr); + parse_word(s_param[1], &wordbuffer); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, addr, + &wordbuffer, 1); + printf("DEPOSIT %06o <- %06o\n", addr, wordbuffer); if (timeout) - printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); + printf("Bus timeout at %06o.\n", unibus->dma_request->unibus_end_addr); } else if (!strcasecmp(s_opcode, "e") && n_fields <= 2) { - unsigned blocksize = 0; // default: no EXAM - bool timeout; + uint16_t wordbuffer; + bool timeout=false; uint32_t addr; if (n_fields == 2) { // single reg number given - blocksize = 1; // exam 1 word - addr = strtol(s_param[0], NULL, 8); // interpret as 18 bit address - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, - blocksize); - printf("EXAM %06o -> %06o\n", addr, mailbox->dma.words[0]); + parse_addr18(s_param[0], &addr); // interpret as 18 bit address + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, addr, + &wordbuffer, 1); + printf("EXAM %06o -> %06o\n", addr, wordbuffer); } if (timeout) - printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); + printf("Bus timeout at %06o.\n", addr); // cur_addr now on last address in block } else { diff --git a/10.03_app_demo/2_src/menu_devices.cpp b/10.03_app_demo/2_src/menu_devices.cpp index c855121..61f0af8 100644 --- a/10.03_app_demo/2_src/menu_devices.cpp +++ b/10.03_app_demo/2_src/menu_devices.cpp @@ -48,47 +48,67 @@ #include "storagedrive.hpp" #include "panel.hpp" #include "demo_io.hpp" -#include "demo_regs.hpp" +#include "testcontroller.hpp" #include "rl11.hpp" #include "rk11.hpp" #include "uda.hpp" +#include "dl11w.hpp" #include "cpu.hpp" /*** handle loading of memory content from macro-11 listing ***/ static char memory_filename[PATH_MAX + 1]; // entry_label is program start, tpyically "start" -static void load_memory(enum unibus_c::arbitration_mode_enum arbitration_mode, char *fname, - const char *entry_label) { +// format: 0 = macrop11, 1 = papertape +static void load_memory(enum unibus_c::arbitration_mode_enum arbitration_mode, + memory_fileformat_t format, char *fname, const char *entry_label) { uint32_t firstaddr, lastaddr; - bool load_ok = membuffer->load_macro11_listing(fname, entry_label); + bool load_ok; bool timeout; + switch (format) { + case fileformat_macro11_listing: + load_ok = membuffer->load_macro11_listing(fname, entry_label); + break; + case fileformat_papertape: + load_ok = membuffer->load_papertape(fname); + break; + default: + load_ok = false; + } if (load_ok) { strcpy(memory_filename, fname); membuffer->get_addr_range(&firstaddr, &lastaddr); printf( "Loaded MACRO-11 listing from file \"%s\" into memory: %d words from %06o to %06o.\n", fname, membuffer->get_word_count(), firstaddr, lastaddr); - if (membuffer->entry_address != MEMORY_ADDRESS_INVALID) + if (entry_label == NULL) + printf(" No entry address label.\n"); + else if (membuffer->entry_address != MEMORY_ADDRESS_INVALID) printf(" Entry address at \"%s\" label is %06o.\n", entry_label, membuffer->entry_address); else printf(" No entry address at \"%s\" label is %06o.\n", entry_label, membuffer->entry_address); + unibus->mem_write(arbitration_mode, membuffer->data.words, firstaddr, lastaddr, + &timeout); + if (timeout) + printf(" Error writing UNIBUS memory\n"); } - unibus->mem_write(arbitration_mode, membuffer->data.words, firstaddr, lastaddr, - unibus->dma_wordcount, &timeout); - if (timeout) - printf(" Error writing UNIBUS memory\n"); +} + +static void print_device(device_c *device) { + unibusdevice_c *ubdevice = dynamic_cast(device); + if (ubdevice) + printf("- %-12s Type %s, %s.\n", ubdevice->name.value.c_str(), + ubdevice->type_name.value.c_str(), ubdevice->get_unibus_resource_info()); + else + printf("- %-12s Type %s.\n", device->name.value.c_str(), + device->type_name.value.c_str()); } // 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; @@ -100,11 +120,12 @@ void application_c::menu_devices(bool with_CPU) { unibusdevice_c *unibuscontroller = NULL; unsigned n_fields; char *s_choice; - char s_opcode[256], s_param[2][256]; + char s_opcode[256], s_param[3][256]; // with_CPU: the emulated CPU is answering BR and NPR requests. if (with_CPU) - arbitration_mode = unibus_c::ARBITRATION_MODE_MASTER; + arbitration_mode = unibus_c::ARBITRATION_MODE_NONE; +// arbitration_mode = unibus_c::ARBITRATION_MODE_MASTER; else arbitration_mode = unibus_c::ARBITRATION_MODE_CLIENT; @@ -113,58 +134,43 @@ void application_c::menu_devices(bool with_CPU) { // iopageregisters_init(); // UNIBUS activity // assert(unibus->arbitrator_client) ; // External Bus Arbitrator required - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, arbitration_mode); // 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_regs_c demo_regs; // mem at 160000: RT11 crashes? - cpu_c *cpu = NULL; - RL11_c *RL11 = NULL; - paneldriver->reset(); // reset I2C, restart worker() - rk11_c *RK11 = NULL; - uda_c *UDA50 = NULL; + demo_io_c *demo_io = new demo_io_c(); - if (with_DEMOIO) { - demo_io = new demo_io_c(); - demo_io->install(); - demo_io->worker_start(); - } + // uses all slot resource, can onyl run alone + //testcontroller_c *test_controller = new testcontroller_c(); + + cpu_c *cpu = NULL; + // create RL11 + also 4 RL01/02 drives + RL11_c *RL11 = new RL11_c(); + paneldriver->reset(); // reset I2C, restart worker() + // create RK11 + drives + rk11_c *RK11 = new rk11_c(); + // Create UDA50 + uda_c *UDA50 = new uda_c(); + // Create SLU+ LTC + slu_c *DL11 = new slu_c(); + // to inject characters into DL11 receiver + std::stringstream dl11_rcv_stream(std::ios::app | std::ios::in | std::ios::out); + DL11->rs232adapter.stream_rcv = &dl11_rcv_stream; + DL11->rs232adapter.stream_xmt = NULL; // do not echo output to stdout + DL11->rs232adapter.baudrate = DL11->baudrate.value; // limit speed of injected chars + + ltc_c *LTC = new ltc_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) { @@ -205,8 +211,14 @@ void application_c::menu_devices(bool with_CPU) { if (strlen(memory_filename)) printf("m ll Reload last memory content from file \"%s\"\n", memory_filename); + printf("m lp Load memory content from absolute papertape image\n"); + printf("m lp 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"); @@ -220,7 +232,18 @@ void application_c::menu_devices(bool with_CPU) { printf("e Examine octal UNIBUS address.\n"); printf("d Deposit octal val into UNIBUS address.\n"); } - printf("dl c|s|f Debug log: Clear, Show on console, dump to File.\n"); + if (DL11->enabled.value) { + printf( + "dl11 rcv [] inject characters as if DL11 received them.\n"); + printf( + " Before output there's an optional pause of milliseconds.\n"); + printf( + " uses C-escapes: \"\\r\"= CR, \040 = space, etc.\n"); + printf( + "dl11 wait wait time until DL11 was ordered to transmit .\n"); + printf(" On timeout, script execution is terminated.\n"); + } + printf("dbg c|s|f Debug log: Clear, Show on console, dump to File.\n"); printf(" (file = %s)\n", logger->default_filepath.c_str()); printf("init Pulse UNIBUS INIT\n"); printf("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); @@ -230,14 +253,15 @@ void application_c::menu_devices(bool with_CPU) { printf("\n"); try { - n_fields = sscanf(s_choice, "%s %s %s", s_opcode, s_param[0], s_param[1]); + n_fields = sscanf(s_choice, "%s %s %s %s", s_opcode, s_param[0], s_param[1], + s_param[2]); if (!strcasecmp(s_opcode, "q")) { ready = true; } else if (!strcasecmp(s_opcode, "init")) { unibus->init(); } else if (!strcasecmp(s_opcode, "pwr")) { unibus->powercycle(); - } else if (!strcasecmp(s_opcode, "dl") && n_fields == 2) { + } else if (!strcasecmp(s_opcode, "dbg") && n_fields == 2) { if (!strcasecmp(s_param[0], "c")) { logger->clear(); printf("Debug log cleared.\n"); @@ -256,17 +280,14 @@ void application_c::menu_devices(bool with_CPU) { bool timeout; uint16_t fillword = 0; if (n_fields == 3) - fillword = strtol(s_param[1], NULL, 8); + parse_word(s_param[1], &fillword); membuffer->set_addr_range(emulated_memory_start_addr, emulated_memory_end_addr); membuffer->fill(fillword); // write buffer-> UNIBUS - printf( - "Fill memory with %06o, writing UNIBUS memory[%06o:%06o] with blocksize %u words\n", - fillword, emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount); + printf("Fill memory with %06o, writing UNIBUS memory[%06o:%06o]\n", fillword, + emulated_memory_start_addr, emulated_memory_end_addr); unibus->mem_write(arbitration_mode, membuffer->data.words, - emulated_memory_start_addr, emulated_memory_end_addr, - unibus->dma_wordcount, &timeout); + emulated_memory_start_addr, emulated_memory_end_addr, &timeout); if (timeout) printf("Error writing UNIBUS memory!\n"); } else if (!strcasecmp(s_opcode, "m") && n_fields == 2 @@ -276,14 +297,13 @@ void application_c::menu_devices(bool with_CPU) { bool timeout; // 1. read UNIBUS memory uint32_t end_addr = unibus->test_sizer(arbitration_mode) - 2; - printf("Reading UNIBUS memory[0:%06o] with DMA blocks of %u words\n", end_addr, - unibus->dma_wordcount); + printf("Reading UNIBUS memory[0:%06o] with DMA\n", end_addr); // clear memory buffer, to be sure content changed membuffer->set_addr_range(0, end_addr); membuffer->fill(0); unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, - unibus->dma_wordcount, &timeout); + &timeout); if (timeout) printf("Error reading UNIBUS memory!\n"); else { @@ -294,31 +314,65 @@ void application_c::menu_devices(bool with_CPU) { } else if (!strcasecmp(s_opcode, "m") && n_fields == 3 && !strcasecmp(s_param[0], "ll")) { // m ll - load_memory(arbitration_mode, s_param[1], "start"); + load_memory(arbitration_mode, fileformat_macro11_listing, s_param[1], "start"); } else if (!strcasecmp(s_opcode, "m") && n_fields == 2 && !strcasecmp(s_param[0], "ll") && strlen(memory_filename)) { // m ll - load_memory(arbitration_mode, memory_filename, "start"); + load_memory(arbitration_mode, fileformat_macro11_listing, memory_filename, + "start"); + } else if (!strcasecmp(s_opcode, "m") && n_fields == 3 + && !strcasecmp(s_param[0], "lp")) { + // m lp + load_memory(arbitration_mode, fileformat_papertape, s_param[1], NULL); + } else if (!strcasecmp(s_opcode, "m") && n_fields == 2 + && !strcasecmp(s_param[0], "lp") && strlen(memory_filename)) { + // m lp + load_memory(arbitration_mode, fileformat_papertape, memory_filename, NULL); } 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++; + print_device(*it); } - 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++; + print_device(*it); + } + 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)) @@ -361,63 +415,121 @@ void application_c::menu_devices(bool with_CPU) { } } else if (unibuscontroller && !strcasecmp(s_opcode, "d") && n_fields == 3) { uint32_t addr; + uint16_t wordbuffer; unibusdevice_register_t *reg = unibuscontroller->register_by_name(s_param[0]); if (reg) // register name given addr = reg->addr; else // interpret as 18 bit address - addr = strtol(s_param[0], NULL, 8); + parse_addr18(s_param[0], &addr); - mailbox->dma.words[0] = strtol(s_param[1], NULL, 8); - bool timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATO, addr, 1); + parse_word(s_param[1], &wordbuffer); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, addr, + &wordbuffer, 1); if (reg) { assert( reg == unibuscontroller->register_by_unibus_address( - mailbox->dma.cur_addr)); + unibus->dma_request->unibus_end_addr)); printf("DEPOSIT reg #%d \"%s\" %06o <- %06o\n", reg->index, reg->name, - reg->addr, mailbox->dma.words[0]); + reg->addr, wordbuffer); } else - printf("DEPOSIT %06o <- %06o\n", addr, mailbox->dma.words[0]); + printf("DEPOSIT %06o <- %06o\n", addr, wordbuffer); if (timeout) 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 - blocksize = 1; // exam 1 word + uint16_t wordbuffer; // exam single word reg = unibuscontroller->register_by_name(s_param[0]); if (reg) addr = reg->addr; else - addr = strtol(s_param[0], NULL, 8); // interpret as 18 bit address - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, - blocksize); - printf("EXAM %06o -> %06o\n", addr, mailbox->dma.words[0]); + parse_addr18(s_param[0], &addr); // interpret as 18 bit address + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, addr, + &wordbuffer, 1); + printf("EXAM %06o -> %06o\n", addr, wordbuffer); } else { // list all regs + unsigned wordcount = 0; // default: no EXAM + uint16_t wordbuffer[MAX_REGISTERS_PER_DEVICE]; addr = unibuscontroller->base_addr.value; // all device registers - blocksize = unibuscontroller->register_count; - if (blocksize) { + wordcount = unibuscontroller->register_count; + if (wordcount) { unsigned i; - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, addr, - blocksize); + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, + addr, wordbuffer, wordcount); for (i = 0; addr <= mailbox->dma.cur_addr; i++, addr += 2) { reg = unibuscontroller->register_by_unibus_address(addr); assert(reg); printf("EXAM reg #%d %s %06o -> %06o\n", reg->index, reg->name, - reg->addr, mailbox->dma.words[i]); + reg->addr, wordbuffer[i]); } } else { - timeout = false ; + timeout = false; printf("Device has no UNIBUS registers.\n"); } } if (timeout) printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); // cur_addr now on last address in block + } else if (DL11->enabled.value && !strcasecmp(s_opcode, "dl11")) { + if ((n_fields == 3 || n_fields == 4) && !strcasecmp(s_param[0], "rcv")) { + // dl11 rcv [] + char buff[256]; + unsigned wait_ms; + timeout_c timeout; + char *s; + if (n_fields == 3) { + wait_ms = 0; + s = s_param[1]; + } else { + wait_ms = strtol(s_param[1], NULL, 10); + s = s_param[2]; + } + if (!str_decode_escapes(buff, sizeof(buff), s)) { + printf("Error in escape sequences.\n"); + inputline_init(); + continue; + } + timeout.wait_ms(wait_ms); + // let DL11 produce chars in 'buff' + pthread_mutex_lock(&DL11->rs232adapter.mutex); + dl11_rcv_stream.clear(); + dl11_rcv_stream.write(buff, strlen(buff)); // add endlessly to string + // dl11_rcv_stream.str(buff); + pthread_mutex_unlock(&DL11->rs232adapter.mutex); +// printf("AAA %d\n", (int)dl11_rcv_stream.get()) ; + } else if (n_fields == 4 && !strcasecmp(s_param[0], "wait")) { + // dl11 wait + timeout_c timeout, timeout2; + unsigned ms = strtol(s_param[1], NULL, 10); + char buff[256]; + if (!str_decode_escapes(buff, sizeof(buff), s_param[2])) { + printf("Error in escape sequences.\n"); + inputline_init(); + continue; + } + // while waiting echo to stdout, for diag + DL11->rs232adapter.stream_xmt = &cout; + DL11->rs232adapter.set_pattern(buff); + timeout.start_ms(ms); + while (!timeout.reached() && !DL11->rs232adapter.pattern_found) + timeout2.wait_ms(1); + DL11->rs232adapter.stream_xmt = NULL; // stop echo + if (!DL11->rs232adapter.pattern_found) { + printf( + "\nPDP-11 did not xmt \"%s\" over DL11 within %u ms, aborting script\n", + s_param[2], ms); + inputline_init(); + } + } else { + printf("Unknown DL11 command \"%s\"!\n", s_choice); + show_help = true; + } } else { printf("Unknown command \"%s\"!\n", s_choice); show_help = true; @@ -428,40 +540,31 @@ 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; - } + LTC->enabled.set(false); + delete LTC; + DL11->enabled.set(false); + delete DL11; - if (with_RK) { - RK11->worker_stop(); - RK11->uninstall(); - delete RK11; - } + RL11->enabled.set(false); + delete RL11; - if (with_MSCP) { - UDA50->worker_stop(); - UDA50->uninstall(); - delete UDA50; - } + RK11->enabled.set(false); + delete RK11; -// //demo_regs.worker_stop(); -// //demo_regs.uninstall(); + UDA50->enabled.set(false); + delete UDA50; - if (with_DEMOIO) { - demo_io->worker_stop(); - demo_io->uninstall(); - delete demo_io; - } + //test_controller->enabled.set(false); + //delete test_controller; - unibusadapter->worker_stop(); + demo_io->enabled.set(false); + delete demo_io; + + 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..dba9b77 100644 --- a/10.03_app_demo/2_src/menu_interrupts.cpp +++ b/10.03_app_demo/2_src/menu_interrupts.cpp @@ -39,25 +39,30 @@ #include "memoryimage.hpp" #include "unibusadapter.hpp" +#include "testcontroller.hpp" void application_c::menu_interrupts(void) { // needs physical CPU enum unibus_c::arbitration_mode_enum arbitration_mode = unibus_c::ARBITRATION_MODE_CLIENT; - const char *testprogram_fname = "intrtst.lst"; bool show_help = true; // show cmds on first screen, then only on error or request bool active = false; // 1 if PRU executes slave&master logic bool ready; + bool test_loaded; char *s_choice; char s_opcode[256], s_param[5][256]; int n_fields; + testcontroller_c *test_controller = new testcontroller_c(); + test_controller->enabled.value = true; + // These test need active PRUs // and an PDP-11 CPU as Arbitrator - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, arbitration_mode); buslatches_output_enable(true); ready = false; + test_loaded = false; while (!ready) { if (show_help) { show_help = false; // only once @@ -74,26 +79,38 @@ 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; } - - printf( - "a Automatic setup: emulate memory, download \"%s\" onto PDP-11.\n", - testprogram_fname); - printf( - "i Issue interrupt at priority to < [octal]\n"); - printf(" = 0..7, = 0,4,10,...,374\n"); - printf(" The test program \"%s\" must be running!\n", - testprogram_fname); - printf( - " Then interrupts cause print-out, and processor priority\n"); - printf(" can be set with keys 0..7.\n"); - printf(" Examples:\n"); - printf(" \"i 5 164\" calls vector 164 at level 5.\n"); - printf( - " If processor level < 5, INTR is accepted, a message is printed.\n"); - printf(" Else INTR is pending until level is lowered.\n"); + printf("m emulate all missing memory\n"); + printf("e EXAMINE the word at . [octal]\n"); + printf("d DEPOSIT into [octal]\n"); + printf("ll Load test program from MACRO-11 listing\n"); + if (test_loaded) { + printf( + "i Issue interrupt at priority to < [octal]\n"); + printf(" = 0..7, = 0,4,10,...,374\n"); + printf( + " Then interrupts cause print-out, and processor priority\n"); + printf(" can be set with keys 0..7.\n"); + printf(" Example:\n"); + printf(" \"i 5 164\" calls vector 164 at level 5.\n"); + printf( + " If processor level < 5, INTR is accepted, a message is printed.\n"); + printf(" Else INTR is pending until level is lowered.\n"); + printf(" = 0..7, = 0,4,10,...,374\n"); + printf( + "i Variant, additional a backplane slot for priority\n"); + printf(" within same level group is given\n"); + printf("dma (addr & data word octal)\n"); + printf( + " DEPOSIT memory range. Non-blocking, subsequent script commands\n"); + printf( + " are executed in parallel. is backplane slot for priority\n"); + printf(" 0..%u possible.\n", + (unsigned) test_controller->dma_channel_count); + } + printf("dbg c|s|f Debug log: Clear, Show on console, dump to File.\n"); printf("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); printf("q Quit\n"); } @@ -106,61 +123,118 @@ void application_c::menu_interrupts(void) { ready = true; } else if (!strcasecmp(s_opcode, "pwr")) { unibus->powercycle(); - } else if (!strcasecmp(s_opcode, "a")) { - timeout_c timer; - bool load_ok, timeout; + } else if (!strcasecmp(s_opcode, "m") && n_fields == 1) { + emulate_memory(arbitration_mode); + } else if (!strcasecmp(s_opcode, "e") && n_fields == 2) { + uint32_t cur_addr; + uint16_t wordbuffer; + parse_addr18(s_param[0], &cur_addr); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, cur_addr, + &wordbuffer, 1); + if (timeout) + printf("Bus timeout at %06o.\n", cur_addr); + else + printf("EXAM %06o -> %06o\n", cur_addr, wordbuffer); + } else if (!strcasecmp(s_opcode, "d") && n_fields == 3) { + uint32_t cur_addr; + uint16_t wordbuffer; + parse_addr18(s_param[0], &cur_addr); + parse_word(s_param[1], &wordbuffer); + bool timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, cur_addr, + &wordbuffer, 1); + if (timeout) + printf("Bus timeout at %06o.\n", cur_addr); + else + printf("DEPOSIT %06o <- %06o\n", cur_addr, wordbuffer); + } else if (!strcasecmp(s_opcode, "ll") && n_fields == 2) { uint32_t start_addr, end_addr; - printf("Trying to start PDP-11 CPU...\n"); - unibus->powercycle(); - timer.wait_ms(1000); // shpw prompt - unibus->powercycle(); - timer.wait_ms(1000); // shpw prompt - - printf("Loading memory content from MACRO-11 listing \"%s\".\n", testprogram_fname); + bool timeout; + test_loaded = false; + printf("Loading memory content from MACRO-11 listing %s\n", s_param[0]); membuffer->init(); - load_ok = membuffer->load_macro11_listing(testprogram_fname, "start"); + bool load_ok = membuffer->load_macro11_listing(s_param[0], "start"); if (!load_ok) { printf("File load failed, aborting.\n"); - } else { - // SET_DEBUG_PIN0(1) ; - emulate_memory(arbitration_mode); - // SET_DEBUG_PIN0(0) ; - membuffer->get_addr_range(&start_addr, &end_addr); - printf( - "Loaded %u words, writing UNIBUS memory[%06o:%06o] with blocksize %u words.\n", - membuffer->get_word_count(), start_addr, end_addr, - unibus->dma_wordcount); - // SET_DEBUG_PIN1(1) ; - unibus->mem_write(arbitration_mode, membuffer->data.words, start_addr, end_addr, - unibus->dma_wordcount, &timeout); - // SET_DEBUG_PIN0(0) ; - // SET_DEBUG_PIN1(0) ; - if (timeout) - printf("Memory write failed with UNIBUS timeout, aborting.\n"); - else { - if (membuffer->entry_address == MEMORY_ADDRESS_INVALID) - printf( - "Start program manually on PDP-11 console (entry address not found).\n"); - else - printf( - "Start program manually on PDP-11 console, entry address is %06o.\n", - membuffer->entry_address); - } + continue; } - } else if (!strcasecmp(s_opcode, "i") && n_fields == 3) { - unsigned priority = strtol(s_param[0], NULL, 8); - unsigned vector = strtol(s_param[1], NULL, 8); - if (priority < 4 || priority > 7) { - printf("Illegal interrupt priority %u, must be 4..7.\n", priority); - } else if ((vector & 3) != 0) { - printf("Illegal interrupt vector %06o, must be multiple of 4.\n", vector); - } else if (vector >= 0374) { // artificial limit, still far to big - printf("Illegal interrupt vector %06o, must be <= 0374.\n", vector); - } else { - unibus->interrupt((uint8_t) priority, (uint16_t) vector); - printf("Interrupt with priority=%d, vector=%03o generated.\n", priority, - vector); + membuffer->get_addr_range(&start_addr, &end_addr); + printf("Loaded %u words, writing UNIBUS memory[%06o:%06o].\n", + membuffer->get_word_count(), start_addr, end_addr); + unibus->mem_write(arbitration_mode, membuffer->data.words, start_addr, end_addr, + &timeout); + if (timeout) + printf("Memory write failed with UNIBUS timeout, aborting.\n"); + else { + if (membuffer->entry_address == MEMORY_ADDRESS_INVALID) + printf( + "Start program manually on PDP-11 console (entry address not found).\n"); + else + printf("Start program manually on PDP-11 console, entry address is %06o.\n", + membuffer->entry_address); + test_loaded = true; } + } else if (test_loaded && !strcasecmp(s_opcode, "i") && n_fields >= 3 + && n_fields <= 4) { + + uint8_t priority_slot = 16; // default + uint8_t level; + uint16_t vector; + if (!parse_level(s_param[0], &level)) + continue; + if (n_fields == 3) { // i + if (!parse_vector(s_param[1], 0374, &vector)) // artificial limit, still far to big + continue; + } else if (n_fields == 4) { // i + if (!parse_slot(s_param[1], &priority_slot)) + continue; + if (!parse_vector(s_param[2], 0374, &vector)) // artificial limit, still far to big + continue; + } + // use request from testcontroller + unsigned level_idx = level - 4; // 4,5,6,7 -> 0,1,2,3 + intr_request_c *intr_request = + test_controller->intr_request[priority_slot][level_idx]; + intr_request->set_vector(vector); + unibusadapter->INTR(*intr_request, NULL, 0); + printf("Interrupt with level=%d, priority slot=%d, vector=%03o generated.\n", level, + priority_slot, vector); + } else if (test_loaded && !strcasecmp(s_opcode, "dma") && n_fields == 5) { + // dma + uint8_t dma_channel; + uint32_t addr_from, addr_to; + uint16_t fillword; + dma_channel = strtol(s_param[0], NULL, 10); + if (dma_channel >= test_controller->dma_channel_count) { + printf("Only DMA channels 0..%u possible.\n", + (unsigned) test_controller->dma_channel_count); + continue; + } + parse_addr18(s_param[1], &addr_from); + parse_addr18(s_param[2], &addr_to); + parse_word(s_param[3], &fillword); + if (addr_to < addr_from) + addr_to = addr_from; + unsigned wordcount = (addr_to - addr_from + 2) / 2; + + // do not use single global "membuffer": need independent buffer per concurrent DMA with different data + memoryimage_c *dma_buffer = test_controller->dma_channel_buffer[dma_channel]; + dma_buffer->set_addr_range(addr_from, addr_to); + dma_buffer->fill(fillword); + + // DMA running asynchronically parallel, no info on BUS timeout yet. User has to wait. + dma_request_c *dma_request = test_controller->dma_channel_request[dma_channel]; + unibusadapter->DMA(*dma_request, false, UNIBUS_CONTROL_DATO, addr_from, + &(dma_buffer->data.words[addr_from / 2]), wordcount); + printf("DEPOSIT in slot %d started for %06o..%06o\n", + dma_request->get_priority_slot(), addr_from, addr_to); + } else if (!strcasecmp(s_opcode, "dbg") && n_fields == 2) { + if (!strcasecmp(s_param[0], "c")) { + logger->clear(); + printf("Debug log cleared.\n"); + } else if (!strcasecmp(s_param[0], "s")) + logger->dump(); + else if (!strcasecmp(s_param[0], "f")) + logger->dump(logger->default_filepath); } else { printf("Unknown command \"%s\"!\n", s_choice); show_help = true; @@ -170,9 +244,10 @@ 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; } + delete test_controller; // Switch off bus drivers buslatches_output_enable(false); diff --git a/10.03_app_demo/2_src/menu_mailbox.cpp b/10.03_app_demo/2_src/menu_mailbox.cpp index d76dddd..7878c45 100644 --- a/10.03_app_demo/2_src/menu_mailbox.cpp +++ b/10.03_app_demo/2_src/menu_mailbox.cpp @@ -45,7 +45,7 @@ void application_c::menu_mailbox() { char s_id[256], s_opcode[256]; ready = false; // test PRUs - hardware_startup(pru_c::PRUCODE_TEST) ; + hardware_startup(pru_c::PRUCODE_TEST); while (!ready) { if (show_help) { show_help = false; // only once @@ -70,6 +70,6 @@ void application_c::menu_mailbox() { show_help = true; } } - hardware_shutdown() ; + hardware_shutdown(); } diff --git a/10.03_app_demo/2_src/menu_masterslave.cpp b/10.03_app_demo/2_src/menu_masterslave.cpp index 2bb5595..2a009d2 100644 --- a/10.03_app_demo/2_src/menu_masterslave.cpp +++ b/10.03_app_demo/2_src/menu_masterslave.cpp @@ -44,14 +44,18 @@ #include "memoryimage.hpp" #include "unibusadapter.hpp" -#include "demo_regs.hpp" + +#include "testcontroller.hpp" + +#define WORDBUFFER_LEN 256 // "arbitration_active": operate in an environment without // arbitration and interrupt fielding? void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitration_mode) { // UniBone uses this test controller: // memory cells at start of IO page, can be tested with ZKMA - demo_regs_c demo_regs; + testcontroller_c testcontroller; + uint16_t wordbuffer[WORDBUFFER_LEN]; // for exam/deposit bool testcontroller_enabled = false; bool show_help = true; // show cmds on first screen, then only on error or request @@ -66,12 +70,14 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr // These test need active PRUs //TODO: rewrite. - hardware_startup(pru_c::PRUCODE_UNIBUS); + hardware_startup(pru_c::PRUCODE_UNIBUS, arbitration_mode); buslatches_output_enable(true); // PRUCODE_UNIBUS can raise events (INIT,ACLO,DCLO) // handle & clear these - unibusadapter->worker_start() ; + unibusadapter->enabled.set(true); + if (testcontroller_enabled) + testcontroller.enabled.set(true); ready = false; @@ -87,25 +93,15 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr else printf(" UniBone emulates memory from %06o to %06o.\n", emulated_memory_start_addr, emulated_memory_end_addr); - printf(" DMA uses %u%% of Unibus band width. DMA block size is %u words.\n", - unibus->dma_bandwidth_percent, unibus->dma_wordcount); if (arbitration_mode == unibus_c::ARBITRATION_MODE_CLIENT && !active) { - // Old: physical PDP_11 CPU -> test of demo_regs? + // Old: physical PDP_11 CPU -> test of testcontroller? printf("***\n"); printf("*** Starting full UNIBUS master/slave logic on PRU\n"); printf("***\n"); - if (testcontroller_enabled) { - demo_regs.install(); - demo_regs.worker_start(); - } unibusadapter->print_shared_register_map(); active = true; } - printf( - "bw bus bandwidth to be used by DMA in percent [1..100, decimal].\n"); - printf("bs DMA block word count [16..%u, decimal].\n", - MAX_DMA_WORDCOUNT); printf( "m [ ] memory range emulated by UniBone. No args = all upper. [octal]\n"); printf( @@ -133,6 +129,10 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("tr [ ] Test memory random\n"); printf("init Pulse UNIBUS INIT\n"); printf("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); + printf( + "dbg c|s|f Debug log: Clear, Show on console, dump to File.\n"); + printf(" (file = %s)\n", + logger->default_filepath.c_str()); printf("i Info about address tables\n"); printf("< Read command form \n"); @@ -160,21 +160,10 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr iopageregisters_print_tables(); } else if (!strcasecmp(s_opcode, "pwr")) { unibus->powercycle(); - } else if (!strcasecmp(s_opcode, "bw") && n_fields == 2) { - unibus->dma_bandwidth_percent = strtol(s_param[0], NULL, 10); - if (unibus->dma_bandwidth_percent > 100) - unibus->dma_bandwidth_percent = 100; - if (unibus->dma_bandwidth_percent < 1) - unibus->dma_bandwidth_percent = 1; - } else if (!strcasecmp(s_opcode, "bs") && n_fields == 2) { - unibus->dma_wordcount = strtol(s_param[0], NULL, 10); - if (unibus->dma_wordcount < 16) - unibus->dma_wordcount = 16; - if (unibus->dma_wordcount > MAX_DMA_WORDCOUNT) - unibus->dma_wordcount = MAX_DMA_WORDCOUNT; } else if (!strcasecmp(s_opcode, "m") && n_fields == 3) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); if (ddrmem->set_range(start_addr, end_addr)) { emulated_memory_start_addr = start_addr; emulated_memory_end_addr = end_addr; @@ -197,8 +186,9 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr 0, end_addr); unibus->test_mem(arbitration_mode, 0, end_addr, 1); } else if (!strcasecmp(s_opcode, "ta") && n_fields == 3) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); uint32_t last_addr = unibus->test_sizer(arbitration_mode) - 2; if (end_addr > last_addr) end_addr = last_addr; // trunc to memory size @@ -211,31 +201,35 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("Testing %06o..%06o randomly (stop with ^C) ...\n", 0, end_addr); unibus->test_mem(arbitration_mode, 0, end_addr, 2); } else if (!strcasecmp(s_opcode, "tr") && n_fields == 3) { - uint32_t start_addr = strtol(s_param[0], NULL, 8); - uint32_t end_addr = strtol(s_param[1], NULL, 8); + uint32_t start_addr, end_addr; + parse_addr18(s_param[0], &start_addr); + parse_addr18(s_param[1], &end_addr); uint32_t last_addr = unibus->test_sizer(arbitration_mode) - 2; if (end_addr > last_addr) end_addr = last_addr; // trunc to memory size printf("Testing %06o..%06o randomly (stop with ^C) ...\n", start_addr, end_addr); unibus->test_mem(arbitration_mode, start_addr, end_addr, 2); } else if (!strcasecmp(s_opcode, "e")) { - unsigned blocksize = 1; + unsigned wordcount = 1; unsigned i; bool timeout; if (n_fields == 1) { // auto inc addr cur_addr = (cur_addr + 2) & 0777777; - blocksize = 1; + wordcount = 1; } else if (n_fields == 2) { // examine addr 1 - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - blocksize = 1; + parse_addr18(s_param[0], &cur_addr); + wordcount = 1; } else if (n_fields == 3) { // examine addr n - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - blocksize = strtol(s_param[1], NULL, 8); + parse_addr18(s_param[0], &cur_addr); + wordcount = strtol(s_param[1], NULL, 8); + if (wordcount > WORDBUFFER_LEN) + wordcount = WORDBUFFER_LEN; } - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATI, cur_addr, blocksize); - for (i = 0; cur_addr <= mailbox->dma.cur_addr; i++, cur_addr += 2) - printf("EXAM %06o -> %06o\n", cur_addr, mailbox->dma.words[i]); - cur_addr = mailbox->dma.cur_addr; + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATI, cur_addr, + wordbuffer, wordcount); + for (i = 0; cur_addr <= unibus->dma_request->unibus_end_addr; i++, cur_addr += 2) + printf("EXAM %06o -> %06o\n", cur_addr, wordbuffer[i]); + cur_addr = unibus->dma_request->unibus_end_addr; if (timeout) printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); // cur_addr now on last address in block @@ -248,10 +242,10 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr cur_addr = (cur_addr + 2) & 0777777; blocksize = 1; } else if (n_fields == 2) { // examine addr 1 - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; + parse_addr18(s_param[0], &cur_addr); blocksize = 1; } else if (n_fields == 3) { // examine addr n - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; + parse_addr18(s_param[0], &cur_addr); blocksize = strtol(s_param[1], NULL, 8); } for (access_error = false, i = 0; !access_error && i < blocksize; @@ -263,27 +257,28 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("DDRMEM EXAM %06o -> %06o\n", cur_addr, w); } } else if (!strcasecmp(s_opcode, "d")) { - unsigned blocksize = 1; + unsigned wordcount = 1; unsigned i; bool timeout; if (n_fields >= 3) { // deposit [ ...] - blocksize = n_fields - 2; - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - mailbox->dma.words[0] = strtol(s_param[1], NULL, 8); - mailbox->dma.words[1] = strtol(s_param[2], NULL, 8); - mailbox->dma.words[2] = strtol(s_param[3], NULL, 8); - mailbox->dma.words[3] = strtol(s_param[4], NULL, 8); + wordcount = n_fields - 2; + parse_addr18(s_param[0], &cur_addr); + parse_word(s_param[1], &wordbuffer[0]); + parse_word(s_param[2], &wordbuffer[1]); + parse_word(s_param[3], &wordbuffer[2]); + parse_word(s_param[4], &wordbuffer[3]); } else if (n_fields == 2) { // deposit cur_addr = (cur_addr + 2) & 0777777; - mailbox->dma.words[0] = strtol(s_param[0], NULL, 8); - blocksize = 1; + parse_word(s_param[0], &wordbuffer[0]); + wordcount = 1; } - timeout = !unibus->dma(arbitration_mode, UNIBUS_CONTROL_DATO, cur_addr, blocksize); - for (i = 0; cur_addr <= mailbox->dma.cur_addr; i++, cur_addr += 2) - printf("DEPOSIT %06o <- %06o\n", cur_addr, mailbox->dma.words[i]); - cur_addr = mailbox->dma.cur_addr; + timeout = !unibus->dma(arbitration_mode, true, UNIBUS_CONTROL_DATO, cur_addr, + wordbuffer, wordcount); + for (i = 0; cur_addr <= unibus->dma_request->unibus_end_addr; i++, cur_addr += 2) + printf("DEPOSIT %06o <- %06o\n", cur_addr, wordbuffer[i]); + cur_addr = unibus->dma_request->unibus_end_addr; if (timeout) - printf("Bus timeout at %06o.\n", mailbox->dma.cur_addr); + printf("Bus timeout at %06o.\n", cur_addr); } else if (!strcasecmp(s_opcode, "xd")) { uint16_t w[256]; unsigned blocksize = 1; @@ -291,14 +286,14 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr bool access_error; if (n_fields >= 3) { // deposit [ ...] blocksize = n_fields - 2; - cur_addr = strtol(s_param[0], NULL, 8) & 0777777; - w[0] = strtol(s_param[1], NULL, 8); - w[1] = strtol(s_param[2], NULL, 8); - w[2] = strtol(s_param[3], NULL, 8); - w[3] = strtol(s_param[4], NULL, 8); + parse_addr18(s_param[0], &cur_addr); + parse_word(s_param[1], &w[0]); + parse_word(s_param[2], &w[1]); + parse_word(s_param[3], &w[2]); + parse_word(s_param[4], &w[3]); } else if (n_fields == 2) { // deposit cur_addr = (cur_addr + 2) & 0777777; - w[0] = strtol(s_param[0], NULL, 8); + parse_word(s_param[0], &w[0]); blocksize = 1; } for (access_error = false, i = 0; !access_error && i < blocksize; @@ -344,21 +339,26 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr } else { wordcount = membuffer->get_word_count(); membuffer->get_addr_range(&startaddr, &endaddr); - printf( - "Loaded %u words, writing UNIBUS memory[%06o:%06o] with blocksize %u words\n", - wordcount, startaddr, endaddr, unibus->dma_wordcount); + printf("Loaded %u words, writing UNIBUS memory[%06o:%06o].\n", wordcount, + startaddr, endaddr); unibus->mem_write(arbitration_mode, membuffer->data.words, startaddr, endaddr, - unibus->dma_wordcount, &timeout); + &timeout); } } else if (!strcasecmp(s_opcode, "s") && (n_fields == 2)) { bool timeout; uint32_t end_addr = unibus->test_sizer(arbitration_mode) - 2; - printf("Reading UNIBUS memory[0:%06o] with DMA blocks of %u words\n", end_addr, - unibus->dma_wordcount); - unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, - unibus->dma_wordcount, &timeout); + printf("Reading UNIBUS memory[0:%06o] with DMA.\n", end_addr); + unibus->mem_read(arbitration_mode, membuffer->data.words, 0, end_addr, &timeout); printf("Saving to file %s\n", s_param[0]); membuffer->save_binary(s_param[0], end_addr + 2); + } else if (!strcasecmp(s_opcode, "dbg") && n_fields == 2) { + if (!strcasecmp(s_param[0], "c")) { + logger->clear(); + printf("Debug log cleared.\n"); + } else if (!strcasecmp(s_param[0], "s")) + logger->dump(); + else if (!strcasecmp(s_param[0], "f")) + logger->dump(logger->default_filepath); } else { printf("Unknown command \"%s\"!\n", s_choice); show_help = true; @@ -371,12 +371,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(); + testcontroller.enabled.set(false); } active = false; } - unibusadapter->worker_stop(); + unibusadapter->enabled.set(false); // Switch off bus drivers buslatches_output_enable(false); diff --git a/10.03_app_demo/2_src/menu_unibus_signals.cpp b/10.03_app_demo/2_src/menu_unibus_signals.cpp index 2eb8ce4..3dc5485 100644 --- a/10.03_app_demo/2_src/menu_unibus_signals.cpp +++ b/10.03_app_demo/2_src/menu_unibus_signals.cpp @@ -64,7 +64,7 @@ void application_c::menu_unibus_signals(void) { ready = false; while (!ready) { if (show_all) { - show_all = false ; // only once + show_all = false; // only once // display all known signals mcout_init(&mcout, unibus_signals->signals.size()); // put all panel controls into array view diff --git a/10.03_app_demo/2_src/menus.cpp b/10.03_app_demo/2_src/menus.cpp index 20be42f..c516e9c 100644 --- a/10.03_app_demo/2_src/menus.cpp +++ b/10.03_app_demo/2_src/menus.cpp @@ -44,6 +44,65 @@ using namespace std; * User Interface *********************************************/ +// octal, or '' +bool application_c::parse_word(char *txt, uint16_t *word) { + *word = 0 ; + if (!txt || *txt == 0) + return false ; + + if (*txt == '\'') { + txt++ ; + if (*txt) + *word = *txt ; // ASCII code of first char after '' + } else + *word = strtol(txt, NULL, 8); // octal literal + return true ; +} + +// octal, trunc to 18 bit +bool application_c::parse_addr18(char *txt, uint32_t *addr) { + *addr = strtol(txt, NULL, 8) ; + if (*addr > 0777777) { + *addr = 0777777 ; + return false ; + } + return true ; +} + + + +bool application_c::parse_level(char *txt, uint8_t *level) { + *level = strtol(txt, NULL, 8); + if (*level < 4 || *level > 7) { + printf("Illegal interrupt level %u, must be 4..7.\n", *level); + return false; + } + return true; +} + +bool application_c::parse_vector(char *txt, uint16_t max_vector, uint16_t *vector) { + *vector = strtol(txt, NULL, 8); + if (*vector > max_vector) { + printf("Illegal interrupt vector %06o, must be <= %06o.\n", (unsigned) *vector, + (unsigned) max_vector); + return false; + } else if ((*vector & 3) != 0) { + printf("Illegal interrupt vector %06o, must be multiple of 4.\n", *vector); + return false; + } + return true; +} + +bool application_c::parse_slot(char *txt, uint8_t *priority_slot) { + *priority_slot = strtol(txt, NULL, 10); + if (*priority_slot <= 0 || *priority_slot > 31) { + printf("Illegal priority slot %u, must be 1..31.\n", *priority_slot); + return false; + } + return true; +} + + void application_c::print_arbitration_info( enum unibus_c::arbitration_mode_enum arbitration_mode, const char *indent) { switch (arbitration_mode) { diff --git a/10.03_app_demo/5_applications/cpu/cpu20hello.cmd b/10.03_app_demo/5_applications/cpu/cpu20hello.cmd index c29b4fe..0bbdb57 100644 --- a/10.03_app_demo/5_applications/cpu/cpu20hello.cmd +++ b/10.03_app_demo/5_applications/cpu/cpu20hello.cmd @@ -6,7 +6,8 @@ dc # "device with cpu" menu m i # emulate missing memory -sd cpu20 # selected emualted 11/20 CPU +en cpu20 # switch on emulated 11/20 CPU +sd cpu20 # select m ll serial.lst # load test program diff --git a/10.03_app_demo/5_applications/cpu/cpu20hellodl11.cmd b/10.03_app_demo/5_applications/cpu/cpu20hellodl11.cmd new file mode 100644 index 0000000..3ab7d72 --- /dev/null +++ b/10.03_app_demo/5_applications/cpu/cpu20hellodl11.cmd @@ -0,0 +1,30 @@ +# Inputfile for demo to execute "Hello world" +# Uses emulated CPU and (physical or emulated) DL11 +# Read in with command line option "demo --cmdfile ..." + +dc # "device with cpu" menu + +m i # emulate missing memory + +en dl11 # switch on emulated DL11 + +en cpu20 # switch on emulated 11/20 CPU +sd cpu20 # select + +m ll serial.lst # load test program + +p + +init + +.print Emulated PDP-11/20 CPU will now output "Hello world" +.print and enter a serial echo loop on DL11 at 177650. +.print Make sure physical CPU is disabled. + +.input + +p run 1 + +.print CPU20 started + + diff --git a/10.03_app_demo/5_applications/cpu/cpu20hellodl11.sh b/10.03_app_demo/5_applications/cpu/cpu20hellodl11.sh new file mode 100644 index 0000000..af74568 --- /dev/null +++ b/10.03_app_demo/5_applications/cpu/cpu20hellodl11.sh @@ -0,0 +1,6 @@ +# starts PDP11/20 emulation +# and executes a "Hello world" on an emualted DL11 card +# Main PDP-1120 must be HALTed +cd ~/10.03_app_demo/5_applications/cpu +~/10.03_app_demo/4_deploy/demo --arb 0 --verbose --debug --cmdfile cpu20hellodl11.cmd + diff --git a/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd b/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd index 7edc2ff..733313c 100644 --- a/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd +++ b/10.03_app_demo/5_applications/mini-unix.rk05/mini-unix_dk_05.cmd @@ -10,13 +10,19 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dk.lst -sd rk0 # select drive #0 +en rk # enable RK11 controller + + +en rk0 # enable drive #0 +sd rk0 # select p image mini-unix-tape1.rk05 # The BIN disk (RK05) -sd rk1 # select drive #1 +en rk1 # enable drive #1 +sd rk1 # select p image mini-unix-tape2.rk05 # The SRC disk (RK05) -sd rk2 # select drive #2 +en rk2 # enable drive #2 +sd rk2 # select p image mini-unix-tape3.rk05 # The MAN disk (RK05) .print Disk drive now on track after 5 secs diff --git a/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd b/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd index 4358dc6..ad5a411 100644 --- a/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd +++ b/10.03_app_demo/5_applications/rsx11.rl02/rsx11m4.1_dl_34.cmd @@ -8,8 +8,11 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dl.lst +en rl # enable RL11 controller + # mount RSX v4.1 in RL02 #0 and start -sd rl0 # select drive #0 +en rl0 # enable drive #0 +sd rl0 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -17,8 +20,9 @@ p powerswitch 1 # power on, now in "load" state p image rsx11m4.1_sys_34.rl02 # mount image p runstopbutton 1 # press RUN/STOP, will start - # mount disk #1 start -sd rl1 # select drive #1 +# mount disk #1 start +en rl1 # enable drive #1 +sd rl1 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -27,7 +31,8 @@ p image rsx11m4.1_user.rl02 # mount image p runstopbutton 1 # press RUN/STOP, will start # mount scratch2 in RL02 #2 and start -sd rl2 # select drive #2 +en rl2 # enable drive #2 +sd rl2 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -36,7 +41,8 @@ p image rsx11m4.1_hlpdcl.rl02 # mount image p runstopbutton 1 # press RUN/STOP, will start # mount scratch3 in RL02 #3 and start -sd rl3 # select drive #3 +en rl3 # enable drive #3 +sd rl3 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds p type rl02 p runstopbutton 0 # released: "LOAD" @@ -47,7 +53,7 @@ p runstopbutton 1 # press RUN/STOP, will start .print Disk drive now on track after 5 secs .wait 6000 # wait until drive spins up -p # show all params of RL1 +p # show all params of RL3 .print RL drives ready. .print RL11 boot loader installed. diff --git a/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd b/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd index 46e74ba..8f2459b 100644 --- a/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd +++ b/10.03_app_demo/5_applications/rt11.mscp/rt11v5.5_du_34.cmd @@ -8,15 +8,18 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll du.lst -# mount RT11 v5.5 in drive #0 and start -sd uda0 # select drive #0 +en uda # enable UDA50 controller +# mount RT11 v5.5 in drive #0 and start +en uda0 # enable drive #0 +sd uda0 # select # set type to "RA80" p type RA80 p image rt11v5.5_34.ra80 # mount image file with test pattern # empty scratch disk in uda1: -sd uda1 +en uda1 # enable drive #1 +sd uda1 # select p type RA80 p image scratch1.ra80 diff --git a/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd b/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd index dea1b63..9bd29da 100644 --- a/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd +++ b/10.03_app_demo/5_applications/rt11.rl02/rt11v5.5_dl_34.cmd @@ -8,8 +8,11 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dl.lst +en rl # enable RL11 controller + # mount RT11 v5.5 in RL02 #0 and start -sd rl0 # select drive #0 +en rl0 # enable drive #0 +sd rl0 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -18,7 +21,8 @@ p image rt11v5.5_34.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount RT11 GAMES in RL02 #1 and start -sd rl1 # select drive #1 +en rl1 # enable drive #1 +sd rl1 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -27,7 +31,8 @@ p image rt11v5.5_games_34.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch2 in RL02 #2 and start -sd rl2 # select drive #2 +en rl2 # enable drive #2 +sd rl2 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -36,7 +41,8 @@ p image scratch2.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch3 in RL02 #3 and start -sd rl3 # select drive #3 +en rl3 # enable drive #3 +sd rl3 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" diff --git a/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd b/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd index 7898412..6d012c7 100644 --- a/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd +++ b/10.03_app_demo/5_applications/unixv6.rk05/unixv6_dk_34.cmd @@ -9,13 +9,18 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dk.lst -sd rk0 # select drive #0 +en rk # enable RK11 controller + +en rk0 # enable drive #0 +sd rk0 # select p image v6bin.rk -sd rk1 # select drive #1 +en rk1 # enable drive #1 +sd rk1 # select p image v6doc.rk -sd rk2 # select drive #2 +en rk2 # enable drive #2 +sd rk2 # select p image v6src.rk .print Disk drive now on track after 5 secs diff --git a/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd b/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd index ca2481f..47eb7be 100644 --- a/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd +++ b/10.03_app_demo/5_applications/xxdp.rl02/xxdp.cmd @@ -1,6 +1,13 @@ # inputfile for demo to select a rl1 device in the "device test" menu. # Read in with command line option "demo --cmdfile ..." d # device test menu + +# first, make a serial port. Default ist +# sd dl11 +# p p ttyS2 # use "UART2 connector +# en dl11 + + pwr .wait 3000 # wait for PDP-11 to reset m i # install max UNIBUS memory @@ -8,8 +15,11 @@ m i # install max UNIBUS memory # Deposit bootloader into memory m ll dl.lst +en rl # enable RL11 controller + # mount XXDP22 in RL02 #0 and start -sd rl0 # select drive #0 +en rl0 # enable drive #0 +sd rl0 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -19,7 +29,8 @@ p runstopbutton 1 # press RUN/STOP, will start #.end # mount XXDP25 in RL02 #1 and start -sd rl1 # select drive #1 +en rl1 # enable drive #1 +sd rl1 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -28,7 +39,8 @@ p image xxdp25.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch2 in RL02 #2 and start -sd rl2 # select drive #2 +en rl2 # enable drive #2 +sd rl2 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" @@ -37,7 +49,8 @@ p image scratch2.rl02 # mount image file with test pattern p runstopbutton 1 # press RUN/STOP, will start # mount scratch3 in RL02 #3 and start -sd rl3 # select drive #3 +en rl3 # enable drive #3 +sd rl3 # select p emulation_speed 10 # 10x speed. Load disk in 5 seconds # set type to "rl02" p runstopbutton 0 # released: "LOAD" diff --git a/10.04_device_exerciser/2_src/devexer.hpp b/10.04_device_exerciser/2_src/devexer.hpp index cd74ffc..d553d70 100644 --- a/10.04_device_exerciser/2_src/devexer.hpp +++ b/10.04_device_exerciser/2_src/devexer.hpp @@ -111,7 +111,7 @@ public: // working position blockaddr_c cur_blockaddr; - // initalize drive + // initialize drive virtual void init(unsigned unitnr) = 0; // read access virtual void readtrack(unsigned unitnr, uint8_t *data) = 0; diff --git a/10.04_device_exerciser/2_src/devexer_rl.hpp b/10.04_device_exerciser/2_src/devexer_rl.hpp index 6d80a08..cf1fb73 100644 --- a/10.04_device_exerciser/2_src/devexer_rl.hpp +++ b/10.04_device_exerciser/2_src/devexer_rl.hpp @@ -39,7 +39,7 @@ public: // implement abstracts - // initalize drive + // initialize drive virtual void init(unsigned unitnr); // read access virtual void readtrack(unsigned unitnr, uint8_t *data); diff --git a/90_common/src/inputline.c b/90_common/src/inputline.c index 110076f..ff3efa3 100644 --- a/90_common/src/inputline.c +++ b/90_common/src/inputline.c @@ -35,6 +35,7 @@ #include #include +#include "kbhit.h" #include "inputline.h" /* @@ -99,6 +100,9 @@ static int inputline_internal(char *line) { } else if (!strncasecmp(line, ".input", 6)) { char buffer[100] ; printf("<<< Press ENTER to continue.\n"); + // flush stuff on stdin. (Eclipse remote debugging) + while (os_kbhit()) ; + fgets(buffer, sizeof(buffer), stdin) ; return 1 ; } else if (!strncasecmp(line, ".end", 3)) { diff --git a/90_common/src/logger.cpp b/90_common/src/logger.cpp index 45e6ea0..ab6af87 100644 --- a/90_common/src/logger.cpp +++ b/90_common/src/logger.cpp @@ -259,6 +259,7 @@ void logger_c::message_render(char *buffer, unsigned buffer_size, logmessage_t * // very long text? 10000 = reserve for % place holder expansion assert(buffer_size > (strlen(msg->printf_format) + 1000)); + assert(strlen(msg->logsource->log_label .c_str())) ; // forgotten? switch (style) { case RENDER_STYLE_CONSOLE: diff --git a/github-sync.sh b/github-sync.sh new file mode 100644 index 0000000..fca14cb --- /dev/null +++ b/github-sync.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# +GITURL=https://github.com/j-hoppe/UniBone.git +echo "This script updates local files from GitHub" +echo " $GITURL" +echo "It forces all local files also present on GitHub to latest version," +echo "then a full recompile is started." +echo "This will both:" +echo " - update all sources and scripts to latest published state." +echo " - roll back local changes made in scripts and some disk images." +echo "Files not (anymore) on GitHub are not touched." +read -p "Are you sure [y/*] ? " +if [[ ! $REPLY =~ ^[Yy]$ ]] ; then + echo "OK, abort." + exit 1 +fi + +# make sure we have svn +sudo apt install subversion + +# download from github without creating repository +svn export --force ${GITURL}/trunk . +# This will not clear outdated files, they will remain as junk. + +# Assure all shell scripts are executable +find . -name '*.sh' -exec chmod +x '{}' \; + +# Start recompile. +./compile.sh -a