diff --git a/10.01_base/2_src/arm/unibus.cpp b/10.01_base/2_src/arm/unibus.cpp index b246a1e..4ee85d1 100644 --- a/10.01_base/2_src/arm/unibus.cpp +++ b/10.01_base/2_src/arm/unibus.cpp @@ -147,7 +147,7 @@ bool unibus_c::dma(enum unibus_c::arbitration_mode_enum arbitration_mode, bool b set_arbitration_mode(arbitration_mode); // changes PRU behaviour - timeout.start(0); // no timeout, just running timer + timeout.start_ns(0); // no timeout, just running timer unibusadapter->DMA(*dma_request, blocking, control, startaddr, buffer, wordcount); dmatime_ns = timeout.elapsed_ns(); diff --git a/10.01_base/2_src/arm/utils.cpp b/10.01_base/2_src/arm/utils.cpp index 5305c92..a3a0c10 100644 --- a/10.01_base/2_src/arm/utils.cpp +++ b/10.01_base/2_src/arm/utils.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -85,31 +86,38 @@ timeout_c::timeout_c() { uint64_t timeout_c::get_resolution_ns() { struct timespec res; clock_getres(CLOCK_MONOTONIC, &res); - return BILLION * res.tv_sec + res.tv_nsec ; + return BILLION * res.tv_sec + res.tv_nsec; } - -void timeout_c::start(uint64_t duration_ns) { +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); - uint64_t result = (uint64_t)BILLION * (now.tv_sec - starttime.tv_sec) + (uint64_t)now.tv_nsec - starttime.tv_nsec; - return result ; + 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 ; + return elapsed_ns() / 1000; } uint64_t timeout_c::elapsed_ms(void) { - return elapsed_ns() / MILLION ; + return elapsed_ns() / MILLION; } - bool timeout_c::reached() { return (elapsed_ns() > duration_ns); } @@ -264,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 50321ed..fc08390 100644 --- a/10.01_base/2_src/arm/utils.hpp +++ b/10.01_base/2_src/arm/utils.hpp @@ -66,7 +66,9 @@ private: public: timeout_c(); uint64_t get_resolution_ns(void) ; - void start(uint64_t duration_ns); + 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); @@ -109,4 +111,8 @@ struct timespec timespec_add_us(struct timespec ts, unsigned us); // add microseconds to current time 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.02_devices/2_src/dl11w.cpp b/10.02_devices/2_src/dl11w.cpp index e112fb3..68e4622 100644 --- a/10.02_devices/2_src/dl11w.cpp +++ b/10.02_devices/2_src/dl11w.cpp @@ -48,6 +48,7 @@ #include "dl11w.hpp" #include "rs232.hpp" +#include "rs232adapter.hpp" //------------------------------------------------- @@ -114,6 +115,8 @@ slu_c::slu_c() : serialport.value = "ttyS2"; // labeled "UART2" on PCB baudrate.value = 9600; mode.value = "8N1"; + + rs232adapter.rs232 = &rs232; } slu_c::~slu_c() { @@ -124,6 +127,7 @@ bool slu_c::on_param_changed(parameter_c *param) { 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()); @@ -136,7 +140,7 @@ bool slu_c::on_param_changed(parameter_c *param) { mode.readonly = true; INFO("Serial port %s opened", serialport.value.c_str()); - char buff[256] ; + char buff[256]; sprintf(buff, "Serial port %s opened\n\r", serialport.value.c_str()); rs232.cputs(buff); } else { @@ -225,7 +229,7 @@ void slu_c::set_xcsr_dati_value_and_INTR(void) { default: set_register_dati_value(reg_xcsr, val, __func__); } - + } void slu_c::eval_xcsr_dato_value(void) { @@ -342,7 +346,7 @@ void slu_c::on_init_changed(void) { void slu_c::worker_rcv(void) { timeout_c timeout; int n; - char buffer[BUFLEN + 1]; + unsigned char buffer[BUFLEN + 1]; // poll with frequency > baudrate, to see single bits //unsigned poll_periods_us = 1000000 / baudrate.value; @@ -366,7 +370,7 @@ void slu_c::worker_rcv(void) { // 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 (rs232.PollComport((unsigned char*) buffer, 1)) { + 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! @@ -376,12 +380,12 @@ void slu_c::worker_rcv(void) { If IGNPAR=0, PARMRK=1: error on received as \377 \0 \377 received as \377 \377 */ - n = rs232.PollComport((unsigned char*) buffer, 1); + 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 = rs232.PollComport((unsigned char*) buffer, 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 @@ -421,7 +425,7 @@ void slu_c::worker_xmt(void) { } // 2. transmit - rs232.SendByte(xmt_buffer); + rs232adapter.byte_xmt_sent(xmt_buffer); xmt_ready = 0; set_xcsr_dati_value_and_INTR(); if (xmt_maint) { // loop back: simulate data byte coming in @@ -437,7 +441,7 @@ void slu_c::worker_xmt(void) { pthread_mutex_lock(&on_after_xmt_register_access_mutex); if (xmt_maint) // put sent byte into rcv buffer, receiver will poll it - rs232.LoopbackByte(xmt_buffer); + rs232adapter.byte_loopback(xmt_buffer); xmt_ready = 1; set_xcsr_dati_value_and_INTR(); @@ -583,7 +587,7 @@ void ltc_c::worker(unsigned instance) { INFO("KW11 time resolution is < %u us", (unsigned )(global_time.get_resolution_ns() / 1000)); - global_time.start(0); + global_time.start_ns(0); global_next_edge_ns = global_time.elapsed_ns(); uint64_t global_edge_count = 0; while (!workers_terminate) { diff --git a/10.02_devices/2_src/dl11w.hpp b/10.02_devices/2_src/dl11w.hpp index 8e1fb04..7b61613 100644 --- a/10.02_devices/2_src/dl11w.hpp +++ b/10.02_devices/2_src/dl11w.hpp @@ -35,6 +35,8 @@ using namespace std; #include "unibusdevice.hpp" #include "parameter.hpp" #include "rs232.hpp" +#include "rs232adapter.hpp" + // socket console settings //#define IP_PORT 5001 @@ -98,7 +100,10 @@ enum slu_reg_index { 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; diff --git a/10.02_devices/2_src/rs232adapter.cpp b/10.02_devices/2_src/rs232adapter.cpp new file mode 100644 index 0000000..fba66ae --- /dev/null +++ b/10.02_devices/2_src/rs232adapter.cpp @@ -0,0 +1,215 @@ +/* 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" + | | + +--------------|----rs232.Poll()---< RxD "RS232" + | | + | +----rs232.Send()---> TxD + | | + | | + \ / / \ + byte_rcv_poll() byte_xmt_sent() lower end "BYTE" + + DL11 RCVR DL11 XMT DL11 + DATI DATO UNIBUS + + + + + // check RS232 or buffer for data bytes, + // which shall be "published" by DL11 RCVer + bool poll_receiver_input(unsigned char *rcvchar) { + if (input_queue.size()) { + // in character to send: has priority over RS232 input + *rcvchar = input_queue.pop() ; + return true ; + // should wait for one byte time before delivering next buffer char + } else + return rs232.PollComport(rcvchar, 1) ; + } + + + string pattern_buffer ; + string xmt_search // search expression in transmitter output + void send_transmitter_output(unsigned char xmtchar) { + + // show output on stdio ? + if (output_stream) + output_stream.out(xmtchar) ; + // save output in ring buffer + pattern_buffer += xmtchar ; + if (pattern_buffer.size() >= PATTERN_BUFFER_LEN) + pattern_buffer.erase(0,1) ; // delete first + // if search expression appears in pattern buffer: + if (pattern_buffer.find(xmt_search) !=std::string::npos ) { + // added char makes search found + + pattern_buffer.clear() ; + } + + } + + + demo: + + dl11 waitfor + dl11 waitfor 1000 @ + + dl11.xmt_monitor_clear() + dl11.xmt_monitor_pattern := + timeout.start_ms() + while(!timeout.reached && !dl11_xmt_monitor_pattern_match()) + sleep(1ms) ; + if (timeout. + + reached() { + // abort script + inputline_clearscript(); + } + + + -> will clear ringbuffer, montior dl11 output + + + + + + */ +#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_sent(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..4728c7d --- /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_sent(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/3_test/dl11w/zdld.cmd b/10.02_devices/3_test/dl11w/zdld.cmd index ca95ee2..55255b0 100644 --- a/10.02_devices/3_test/dl11w/zdld.cmd +++ b/10.02_devices/3_test/dl11w/zdld.cmd @@ -6,6 +6,7 @@ sd dl11 .input en dl11 +en kw11 pwr @@ -14,6 +15,19 @@ pwr 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 \ No newline at end of file +.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.03_app_demo/2_src/makefile b/10.03_app_demo/2_src/makefile index bded5b9..dbfdcda 100644 --- a/10.03_app_demo/2_src/makefile +++ b/10.03_app_demo/2_src/makefile @@ -105,6 +105,7 @@ OBJECTS = $(OBJDIR)/application.o \ $(OBJDIR)/mscp_server.o \ $(OBJDIR)/mscp_drive.o \ $(OBJDIR)/rs232.o \ + $(OBJDIR)/rs232adapter.o \ $(OBJDIR)/dl11w.o \ $(OBJDIR)/storagedrive.o \ $(OBJDIR)/storagecontroller.o \ @@ -240,6 +241,9 @@ $(OBJDIR)/mscp_drive.o : $(DEVICE_SRC_DIR)/mscp_drive.cpp $(DEVICE_SRC_DIR)/ms $(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 $@ 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_device_exercisers.cpp b/10.03_app_demo/2_src/menu_device_exercisers.cpp index 1eacc65..827965b 100644 --- a/10.03_app_demo/2_src/menu_device_exercisers.cpp +++ b/10.03_app_demo/2_src/menu_device_exercisers.cpp @@ -107,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"); @@ -124,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"); diff --git a/10.03_app_demo/2_src/menu_devices.cpp b/10.03_app_demo/2_src/menu_devices.cpp index 84ab102..61f0af8 100644 --- a/10.03_app_demo/2_src/menu_devices.cpp +++ b/10.03_app_demo/2_src/menu_devices.cpp @@ -82,7 +82,7 @@ static void load_memory(enum unibus_c::arbitration_mode_enum arbitration_mode, "Loaded MACRO-11 listing from file \"%s\" into memory: %d words from %06o to %06o.\n", fname, membuffer->get_word_count(), firstaddr, lastaddr); if (entry_label == NULL) - printf(" No entry address label.\n") ; + 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); @@ -99,11 +99,11 @@ static void load_memory(enum unibus_c::arbitration_mode_enum arbitration_mode, 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()) ; + 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()) ; + printf("- %-12s Type %s.\n", device->name.value.c_str(), + device->type_name.value.c_str()); } // CPU is enabled, act as ARBITRATION_MASTER @@ -120,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; @@ -156,6 +157,12 @@ void application_c::menu_devices(bool with_CPU) { 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(); @@ -225,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"); @@ -235,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"); @@ -456,7 +475,61 @@ void application_c::menu_devices(bool with_CPU) { 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; diff --git a/10.03_app_demo/2_src/menu_interrupts.cpp b/10.03_app_demo/2_src/menu_interrupts.cpp index 330ecdb..dba9b77 100644 --- a/10.03_app_demo/2_src/menu_interrupts.cpp +++ b/10.03_app_demo/2_src/menu_interrupts.cpp @@ -110,7 +110,7 @@ void application_c::menu_interrupts(void) { printf(" 0..%u possible.\n", (unsigned) test_controller->dma_channel_count); } - 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("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); printf("q Quit\n"); } @@ -227,7 +227,7 @@ void application_c::menu_interrupts(void) { &(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, "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"); diff --git a/10.03_app_demo/2_src/menu_masterslave.cpp b/10.03_app_demo/2_src/menu_masterslave.cpp index ce1412c..2a009d2 100644 --- a/10.03_app_demo/2_src/menu_masterslave.cpp +++ b/10.03_app_demo/2_src/menu_masterslave.cpp @@ -130,7 +130,7 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr printf("init Pulse UNIBUS INIT\n"); printf("pwr Simulate UNIBUS power cycle (ACLO/DCLO)\n"); printf( - "dl c|s|f Debug log: Clear, Show on console, dump to File.\n"); + "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"); @@ -351,7 +351,7 @@ void application_c::menu_masterslave(enum unibus_c::arbitration_mode_enum arbitr 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, "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"); 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 +