1
0
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:
Joerg Hoppe
2019-08-10 10:19:05 +02:00
parent 313957631f
commit 39caffd6e6
17 changed files with 587 additions and 37 deletions

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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) {

View File

@@ -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;

View 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;
}

View 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_

View File

@@ -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

Binary file not shown.

View File

@@ -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 $@

View File

@@ -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

View File

@@ -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");

View File

@@ -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;

View File

@@ -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");

View File

@@ -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");

View 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

View 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