mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-02-27 01:00:00 +00:00
Emulated DL11: stream interface parallel to RS232.
demo: "dl11 rcv" and "dl11 wait" script extension cleanup
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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 <char> received as \377 \0 <char>
|
||||
\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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
215
10.02_devices/2_src/rs232adapter.cpp
Normal file
215
10.02_devices/2_src/rs232adapter.cpp
Normal file
@@ -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 <timeout_ms> <patternstring>
|
||||
dl11 waitfor 1000 @
|
||||
|
||||
dl11.xmt_monitor_clear()
|
||||
dl11.xmt_monitor_pattern := <patternstring>
|
||||
timeout.start_ms(<timeout_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 <string.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#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 <char> received as \377 \0 <char>
|
||||
// \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;
|
||||
}
|
||||
|
||||
86
10.02_devices/2_src/rs232adapter.hpp
Normal file
86
10.02_devices/2_src/rs232adapter.hpp
Normal file
@@ -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 <ostream>
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
#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 <char> received as \377 \0 <char>
|
||||
// \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_
|
||||
|
||||
@@ -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
|
||||
.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
|
||||
|
||||
BIN
10.02_devices/3_test/rl02/ZRLJB1.BIC
Normal file
BIN
10.02_devices/3_test/rl02/ZRLJB1.BIC
Normal file
Binary file not shown.
@@ -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 $@
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -107,7 +107,7 @@ void application_c::menu_device_exercisers(void) {
|
||||
printf("e Examine all device registers\n");
|
||||
printf("d <addr> <val> Deposit octal val into UNIBUS address.\n");
|
||||
printf("e <addr> 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");
|
||||
|
||||
@@ -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<unibusdevice_c *>(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 <addr> Examine octal UNIBUS address.\n");
|
||||
printf("d <addr> <val> 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 [<wait_ms>] <string> inject characters as if DL11 received them.\n");
|
||||
printf(
|
||||
" Before output there's an optional pause of <wait_ms> milliseconds.\n");
|
||||
printf(
|
||||
" <string> uses C-escapes: \"\\r\"= CR, \040 = space, etc.\n");
|
||||
printf(
|
||||
"dl11 wait <timeout_ms> <string> wait time until DL11 was ordered to transmit <string>.\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 [<wait_ms>] <string>
|
||||
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_ms> <string>
|
||||
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;
|
||||
|
||||
@@ -110,7 +110,7 @@ void application_c::menu_interrupts(void) {
|
||||
printf(" <channel> 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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
30
10.03_app_demo/5_applications/cpu/cpu20hellodl11.cmd
Normal file
30
10.03_app_demo/5_applications/cpu/cpu20hellodl11.cmd
Normal file
@@ -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
|
||||
|
||||
|
||||
6
10.03_app_demo/5_applications/cpu/cpu20hellodl11.sh
Normal file
6
10.03_app_demo/5_applications/cpu/cpu20hellodl11.sh
Normal file
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user