From e85a7746c053c46e50a05df6c48c6c394dcc88ee Mon Sep 17 00:00:00 2001 From: AK6DN Date: Sat, 20 May 2017 13:35:36 -0700 Subject: [PATCH] Major serial comm rewrite to fully support handling BREAK condition --- README.md | 40 ++++++++--- common.h | 12 ++-- cygwin/tu58em.exe | Bin 91349 -> 95004 bytes main.c | 6 +- makefile | 66 ++++++++--------- serial.c | 180 ++++++++++++++++++++++++---------------------- tu58drive.c | 107 +++++++++++---------------- 7 files changed, 207 insertions(+), 204 deletions(-) diff --git a/README.md b/README.md index 3328d84..8e6a19c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,36 @@ -tu58em is a software emulation of a DECtapeII TU-58 block addressable cartridge tape drive. It requires only a standard Windows PC as a host with an RS232 serial port to connect to the target system. +tu58em is a software emulation of a DECtapeII TU-58 block addressable cartridge tape drive. It requires only a standard Windows PC as a host with a (real or USB) RS232 serial comm port to connect to the target system. -tu58em was originally based on the 1984 Dan Ts'o tu58 program, but has been almost completely rewritten to make it compile error free, improve the program flow, and add new functionality. It has been compiled within the CYGWIN environment, and will run either within a CYGWIN window or an MSDOS command window with the associated cygwin1.dll helper file. tu58em has been tested under Win2K, WinXPsp3, and Win7sp1. tu58em has compiled error free under Linux (Ubuntu 12.02LTS) but has not yet been rigorously tested in that environment. +tu58em was originally based on the 1984 Dan Ts'o tu58 program, but has been almost completely rewritten to make it compile error free, improve the program flow, and add new functionality. It has been compiled within the CYGWIN environment, and will run either within a CYGWIN window or an MSDOS command window with the associated cygwin1.dll helper file. tu58em has been tested under WinXPsp3, Win7sp1(64b), and Linux (Ubuntu 12.02LTS). Each emulated .dsk image file is exactly 256KB (512 blocks of 512 bytes) of data and is a byte-for-byte image of the data on a TU-58 cartridge tape. As currently configured tu58em will support up to 8 drives per controller as DD0: to DD7: (altho this is easily changed in the source). tu58em will support disk image files as large as the TU58 protocol allows (ie, 32MB, or 64K 512B blocks); however most standard DEC operating system drivers restrict TU58 drives to 256KB each. The DEC driver must be patched to allow for larger than 256KB disk images. -tu58em has been tested using both native RS232 serial COM ports and serial ports emulated thru USB serial adapters. - -Note: tu58em is compiled using the CYGWIN serial interface library routines (ie, termios.h) and is the preferred version that should be used. tu58ew bypasses the CYGWIN serial interface layer and makes direct Windows serial comm routine calls. This version was done early on because of deficiencies in the CYGWIN serial library. However, the required features are now present so in reality tu58ew should be considered deprecated. - As of v1.4m Mark Blair's updates for VAX mode operation (for VAX-730 microcode boot support) and background mode are integrated. +As of v2.0a the serial support routines have been rewritten to, under Linux, use termios.h PARMRK mode to allow detecting BREAK inline within the rx byte stream, and respond correctly (ie, abort current command in process and return to init loop). PARMRK mode in CYGWIN appears not be be working correctly; a BREAK just gets turned into a NULL byte. Windows communication mode (-DWINCOMM) is preferred for CYGWIN, as BREAK is detected correctly. MACOS support of PARMRK mode is unknown. The makefile is setup to detect the operating system type and define the compilation options correctly. + +The following configurations have been tested: +``` +System Mode Port Status +------------ -------------- ------------ -------------------------------------- +WinXP/CYGWIN WINCOMM real comm PASS, BREAK detected +WinXP/CYGWIN WINCOMM USB ftdi PASS, BREAK detected +WinXP/CYGWIN WINCOMM USB prolific conditional PASS, BREAK not detected +------------ -------------- ------------ -------------------------------------- +WinXP/CYGWIN termios real comm conditional PASS; BREAK not detected +WinXP/CYGWIN termios USB ftdi conditional PASS; BREAK not detected +WinXP/CYGWIN termios USB prolific conditional PASS; BREAK not detected +------------ -------------- ------------ -------------------------------------- +WinXP/CYGWIN termios+PARMRK any FAIL, does not work at all +------------ -------------- ------------ -------------------------------------- +Linux* termios+PARMRK real comm PASS, BREAK detected +Linux* termios+PARMRK USB ftdi PASS, BREAK detected +Linux* termios+PARMRK USB prolific conditional PASS; BREAK not detected +------------ -------------- ------------ -------------------------------------- + +*Linux == "Ubuntu 12.04.5 LTS (GNU/Linux 3.2.0-124-generic x86_64)" +``` +So it appears that the drivers for real comm ports handle BREAK correctly, as does the driver for the USB ftdi adapter, on both WinXP and Linux. The USB prolific adapter hardware and/or driver do not handle a BREAK, altho all data transfers operate correctly. + A cygwin folder with a precompiled 32b cygwin executable (tu58em.exe) is included for those without cygwin environment access. Under Windows, just open a standard CMD.EXE window, change to the cygwin folder, and run the tu58em.exe executable as a command line program. If the emulator is run with no options, it prints a usage screen: @@ -18,7 +39,7 @@ If the emulator is run with no options, it prints a usage screen: E:\DEC> tu58em ERROR: no units were specified FATAL: illegal command line - tu58 tape emulator v1.4q + tu58 tape emulator v2.0a Usage: ./tu58em [-options] -[rwci] file1 ... -[rwci] file7 Options: -V | --version output version string -v | --verbose enable verbose output to terminal @@ -73,9 +94,10 @@ info: initialize RT-11 directory on 'rt11.dsk' info: unit 0 r file 'boot.dsk' info: unit 1 rwci file 'rt11.dsk' info: serial port 3 at 38400 baud 1 stop -info: TU58 emulation start +info: TU58 start info: R restart, S toggle send init, V toggle verbose, D toggle debug, Q quit -info: emulator started +info: TU58 emulator started +info: seen info: seen, sending info: read unit=0 sw=0x00 mod=0x00 blk=0x0000 cnt=0x0400 info: read unit=0 sw=0x00 mod=0x00 blk=0x0006 cnt=0x0400 diff --git a/common.h b/common.h index 631a6fc..b62457b 100644 --- a/common.h +++ b/common.h @@ -2,7 +2,7 @@ // tu58 - Emulate a TU58 over a serial line // // Original (C) 1984 Dan Ts'o -// Update (C) 2005-2016 Donald N North +// Update (C) 2005-2017 Donald N North // // All rights reserved. // @@ -80,10 +80,9 @@ typedef struct timespec timespec_t; #define FILERT11INIT 4 // file should be init'ed as RT11 structure #define FILEXXDPINIT 5 // file should be init'ed as XXDP structure -#define DEV_NYI -1 // not yet implemented -#define DEV_OK 0 // no error -#define DEV_BREAK 1 // BREAK on line -#define DEV_ERROR 2 // ERROR on line +#define DEV_NORMAL 0 // normal data byte +#define DEV_BREAK 1 // BREAK on line +#define DEV_ERROR 2 // ERROR on byte @@ -104,8 +103,7 @@ void devtxput (uint8_t); int32_t devtxwrite (uint8_t *, int32_t); void devrxinit (void); int32_t devrxavail (void); -int32_t devrxerror (void); -uint8_t devrxget (void); +uint8_t devrxget (uint8_t *); void devinit (char *, int32_t, int32_t); void devrestore (void); void coninit (void); diff --git a/cygwin/tu58em.exe b/cygwin/tu58em.exe index 6a7949f06f211987b34fe5cdea20a38e8ec4893f..5c49a521977787ed07073ea055cc0252695217ac 100644 GIT binary patch delta 27231 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 XY8&7|y%u`AG_R1Y86;TL=gt2B)P5+r delta 24244 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, " \ "(C) 1984 Dan Ts'o "; -static char version[] = "tu58 tape emulator v1.4q"; +static char version[] = "tu58 tape emulator v2.0a"; static char port[32] = "1"; // default port number (COM1, /dev/ttyS0) static long speed = 9600; // default line speed diff --git a/makefile b/makefile index 6c7b934..071e312 100644 --- a/makefile +++ b/makefile @@ -2,50 +2,46 @@ # tu58em emulator makefile # -ifeq ($(comm),mac) -# UNIX comms model, but on a Mac -PROG = tu58em -COMM = -UWINCOMM -DMACOSX -else ifeq ($(comm),win) -# WINDOWS comms model -PROG = tu58ew -COMM = -DWINCOMM -else # ifeq ($(comm),unix) -# UNIX comms model -PROG = tu58em -COMM = -UWINCOMM +# get operating system name +OPSYS = $(shell uname -s) + +ifeq ($(OPSYS),Darwin) +# mac: UNIX comms model, but on MACOSX +OPTIONS = -DMACOSX +LFLAGS = -lpthread +else ifeq ($(OPSYS:CYGWIN%=CYGWIN),CYGWIN) +# win: WINDOWS comms model under CYGWIN (any version) +OPTIONS = -DCYGWIN -DWINCOMM +LFLAGS = -lpthread -lrt +else ifeq ($(OPSYS),Linux) +# unix: UNIX comms model under LINUX, use PARMRK serial mode +OPTIONS = -DLINUX -DUSE_PARMRK +LFLAGS = -lpthread -lrt +else # unknown environment +OPTIONS = +LFLAGS = -lpthread -lrt endif +# default program name, redefine PROG=xxx on command line if wanted +PROG = tu58em + # put your binary installation directory here... -BIN = /cygdrive/e/DEC/tools/exe +BINDIR = /cygdrive/e/DEC/tools/exe # compiler flags and libraries CC = gcc -CFLAGS = -I. -O3 -Wall -c $(COMM) -ifeq ($(comm),mac) -LFLAGS = -lpthread -else -LFLAGS = -lpthread -lrt -endif +CFLAGS = -I. -O3 -Wall -c $(OPTIONS) $(PROG) : main.o tu58drive.o file.o serial.o $(CC) -o $@ main.o tu58drive.o file.o serial.o $(LFLAGS) -all : - make --always comm=win - make clean - make --always comm=unix - make clean - -mac : - make --always comm=mac - make --clean - -installall : - make --always comm=win install - make clean - make --always comm=unix install - make clean +config : + @echo " OPSYS = \"$(OPSYS)\"" + @echo " PROG = \"$(PROG)\"" + @echo " BINDIR = \"$(BINDIR)\"" + @echo " CC = \"$(CC)\"" + @echo " CFLAGS = \"$(CFLAGS)\"" + @echo " LFLAGS = \"$(LFLAGS)\"" clean : -rm -f *.o @@ -57,7 +53,7 @@ purge : clean -rm -f $(PROG) $(PROG).exe install : $(PROG) - [ -d $(BIN) ] && cp $< $(BIN) + [ -d $(BINDIR) ] && cp $< $(BINDIR) serial.o : serial.c common.h $(CC) $(CFLAGS) serial.c diff --git a/serial.c b/serial.c index 7b9b414..5230874 100644 --- a/serial.c +++ b/serial.c @@ -2,7 +2,7 @@ // tu58 - Emulate a TU58 over a serial line // // Original (C) 1984 Dan Ts'o -// Update (C) 2005-2016 Donald N North +// Update (C) 2005-2017 Donald N North // // All rights reserved. // @@ -56,21 +56,21 @@ #define IUCLC 0 // Not POSIX #define OLCUC 0 // Not POSIX #define CBAUD 0 // Not POSIX -#endif +#endif // MACOSX #include #define BUFSIZE 256 // size of serial line buffers (bytes, each way) // serial output buffer -static uint8_t wbuf[BUFSIZE]; +static uint8_t wbuf[BUFSIZE]; static uint8_t *wptr; -static int32_t wcnt; +static int32_t wcnt; // serial input buffer -static uint8_t rbuf[BUFSIZE]; +static uint8_t rbuf[BUFSIZE]; static uint8_t *rptr; -static int32_t rcnt; +static int32_t rcnt; #ifdef WINCOMM // serial device descriptor, default to nada @@ -78,6 +78,7 @@ static HANDLE hDevice = INVALID_HANDLE_VALUE; // async line parameters static DCB dcbSave; static COMMTIMEOUTS ctoSave; +static uint8_t rxBreakSeen; #else // !WINCOMM // serial device descriptor, default to nada static int32_t device = -1; @@ -197,6 +198,7 @@ void devrxinit (void) #ifdef WINCOMM if (!PurgeComm(hDevice, PURGE_RXABORT|PURGE_RXCLEAR)) error("devrxinit(): error=%d", GetLastError()); + rxBreakSeen = 0; #else // !WINCOMM tcflush(device, TCIFLUSH); #endif // !WINCOMM @@ -211,75 +213,26 @@ void devrxinit (void) // -// wait for an error on the serial line -// return NYI, OK, BREAK, ERROR flag -// -int32_t devrxerror (void) -{ -#ifdef WINCOMM - // enable BREAK and ERROR events - OVERLAPPED ovlp = { 0 }; - DWORD sts = 0; - if (!SetCommMask(hDevice, EV_BREAK|EV_ERR)) { - DWORD err = GetLastError(); - if (err != ERROR_OPERATION_ABORTED) - error("devrxerror(): SetCommMask() failed, error=%d", err); - } - // do the status check - ovlp.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!WaitCommEvent(hDevice, &sts, &ovlp)) { - DWORD err = GetLastError(); - if (err == ERROR_IO_PENDING) { - if (WaitForSingleObject(ovlp.hEvent, INFINITE) == WAIT_OBJECT_0) - GetOverlappedResult(hDevice, &ovlp, &sts, FALSE); - } else { - if (err != ERROR_OPERATION_ABORTED) - error("devrxerror(): WaitCommEvent() failed, error=%d", err); - } - } - // done - CloseHandle(ovlp.hEvent); - // indicate either a break or some other error or OK - return (sts & (CE_BREAK|CE_FRAME)) ? DEV_BREAK : (sts ? DEV_ERROR : DEV_OK); -#else // !WINCOMM - // not implemented - return DEV_NYI; -#endif // !WINCOMM -} - - - -// -// return number of characters available +// return number of characters available, get more if receive buffer is empty // int32_t devrxavail (void) { // get more characters if none available if (rcnt <= 0) { #ifdef WINCOMM - OVERLAPPED ovlp = { 0 }; COMSTAT stat; DWORD acnt = 0; + DWORD ncnt = 0; DWORD sts = 0; // clear state if (!ClearCommError(hDevice, &sts, &stat)) error("devrxavail(): ClearCommError() failed"); - if (debug && (sts || stat.cbInQue)) - info("devrxavail(): status=0x%04X avail=%d", sts, stat.cbInQue); - // do the read if something there - if (stat.cbInQue > 0) { - ovlp.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!ReadFile(hDevice, rbuf, sizeof(rbuf), &acnt, &ovlp)) { - DWORD err = GetLastError(); - if (err == ERROR_IO_PENDING) { - if (WaitForSingleObject(ovlp.hEvent, INFINITE) == WAIT_OBJECT_0) - GetOverlappedResult(hDevice, &ovlp, &acnt, FALSE); - } else { - error("devrxavail(): error=%d", err); - } - } - CloseHandle(ovlp.hEvent); - } + // do the read if something there, at most size of buffer + ncnt = stat.cbInQue > sizeof(rbuf) ? sizeof(rbuf) : stat.cbInQue; + if (!ReadFile(hDevice, rbuf, ncnt, &acnt, NULL)) + error("devrxavail(): error=%d", GetLastError()); + // check for break + if (sts & CE_BREAK) rxBreakSeen = 1; // done rcnt = acnt; #else // !WINCOMM @@ -296,7 +249,7 @@ int32_t devrxavail (void) // -// write characters direct to device +// write characters direct to device from transmit buffer // int32_t devtxwrite (uint8_t *buf, int32_t cnt) @@ -304,28 +257,16 @@ int32_t devtxwrite (uint8_t *buf, // write characters if asked, return number written if (cnt > 0) { #ifdef WINCOMM - OVERLAPPED ovlp = { 0 }; COMSTAT stat; DWORD acnt = 0; DWORD sts = 0; // clear state if (!ClearCommError(hDevice, &sts, &stat)) error("devtxwrite(): ClearCommError() failed"); - if (debug && (sts || stat.cbOutQue)) - info("devtxwrite(): status=0x%04X remain=%d", sts, stat.cbOutQue); // do the write - ovlp.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!WriteFile(hDevice, buf, cnt, &acnt, &ovlp)) { - DWORD err = GetLastError(); - if (err == ERROR_IO_PENDING) { - if (WaitForSingleObject(ovlp.hEvent, INFINITE) == WAIT_OBJECT_0) - GetOverlappedResult(hDevice, &ovlp, &acnt, FALSE); - } else { - error("devtxwrite(): error=%d", err); - } - } + if (!WriteFile(hDevice, buf, cnt, &acnt, NULL)) + error("devtxwrite(): error=%d", GetLastError()); // done - CloseHandle(ovlp.hEvent); return acnt; #else // !WINCOMM return write(device, buf, cnt); @@ -370,14 +311,71 @@ void devtxflush (void) // // return char from rbuf, wait until some arrive // -uint8_t devrxget (void) +uint8_t devrxget (uint8_t *flg) { - // get more characters if none available - while (devrxavail() <= 0) /*spin*/; + uint8_t c; - // count, return next character +#ifdef USE_PARMRK + // get more bytes if none available + while (rcnt <= 0) { (void)devrxavail(); } + // at least one available rcnt--; - return *rptr++; + // check if escaped or normal + if ((c = *rptr++) == 0377) { + // escape byte seen + while (rcnt <= 0) { (void)devrxavail(); } + // at least one available + rcnt--; + // check if escape or not + if ((c = *rptr++) == 0377) { + // 377,377 seen; return 377 + *flg = DEV_NORMAL; + return c; + } else { + // non-escape byte seen, so get one more byte + while (rcnt <= 0) { (void)devrxavail(); } + // at least one available + rcnt--; + // check if NULL + if ((c = *rptr++) == 0000) { + // 377,000,000 seen; signals a BREAK, return 000 byte + *flg = DEV_BREAK; + return c; + } else { + // 377,000,NNN seen; signals byte NNN parity/framing error + *flg = DEV_ERROR; + return c; + } + } + } else { + // return normal data byte + *flg = DEV_NORMAL; + return c; + } +#else // !USE_PARMRK + // get more characters if none available + while (rcnt <= 0) { (void)devrxavail(); } + // at least one available + rcnt--; + // get data byte + c = *rptr++; +#ifdef WINCOMM + // check if this byte should be flagged as a BREAK + // for lack of a better algorithm, we flag the first + // ZERO byte after the rxBreakSeen flag is set as BREAK + if (c == 000 && rxBreakSeen) { + *flg = DEV_BREAK; + rxBreakSeen = 0; + } else { + *flg = DEV_NORMAL; + } +#else // !WINCOMM + // return normal data byte + *flg = DEV_NORMAL; +#endif // !WINCOMM + // return data byte + return c; +#endif // !USE_PARMRK } @@ -521,9 +519,17 @@ void devinit (char *port, int32_t uid = getuid(); setreuid(euid, -1); if (sscanf(port, "%u", &n) == 1) sprintf(name, "\\\\.\\COM%d", n); else strcpy(name, port); - hDevice = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL); + // open port in non-overlapped I/O mode + hDevice = CreateFile(name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); if (hDevice == INVALID_HANDLE_VALUE) fatal("no serial line [%s]", name); + + // we own the port setreuid(uid, euid); // get current line params, error if not a serial port @@ -595,7 +601,11 @@ void devinit (char *port, line.c_iflag &= ~( IGNBRK | BRKINT | IMAXBEL | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IUCLC | IXANY | PARMRK | IGNPAR ); +#ifdef USE_PARMRK + line.c_iflag |= ( PARMRK | IGNPAR ); +#else // !USE_PARMRK line.c_iflag |= ( 0 ); +#endif // !USE_PARMRK // output param line.c_oflag &= ~( OPOST | OLCUC | OCRNL | ONLCR | ONOCR | diff --git a/tu58drive.c b/tu58drive.c index 2cfee57..e67b7e4 100644 --- a/tu58drive.c +++ b/tu58drive.c @@ -2,7 +2,7 @@ // tu58 - Emulate a TU58 over a serial line // // Original (C) 1984 Dan Ts'o -// Update (C) 2005-2016 Donald N North +// Update (C) 2005-2017 Donald N North // // All rights reserved. // @@ -48,6 +48,7 @@ #include "common.h" #include +#include #include "tu58.h" @@ -55,17 +56,13 @@ // clock_gettime() is not available under MACOSX #define CLOCK_REALTIME 1 #include -#include -#include - void clock_gettime (int dummy, struct timespec *t) { uint64_t mt; mt = mach_absolute_time(); t->tv_sec = mt / 1000000000; t->tv_nsec = mt % 1000000000; } -#endif - +#endif // MACOSX // delays for modeling device access @@ -92,7 +89,7 @@ uint8_t mrsp = 0; // set nonzero to indicate MRSP mode is active static uint8_t doinit = 0; // set nonzero to indicate should send INITs continuously static uint8_t runonce = 0; // set nonzero to indicate emulator has been run static pthread_t th_run; // emulator thread id -static pthread_t th_monitor; // monitor thread id +static jmp_buf rx_break_env; // longjmp state for when a BREAK is detected on rx @@ -140,6 +137,26 @@ static void reinit (void) +// +// read a byte from the host, process BREAK if seen +// +static uint8_t rxget (void) +{ + uint8_t state; + uint8_t c; + + // get a byte and state flag + c = devrxget(&state); + + // seen a BREAK on the rx line, so abort this packet + if (state == DEV_BREAK) longjmp(rx_break_env, c); + + // return the byte + return c; +} + + + // // read of boot is not packetized, is just raw data // @@ -150,7 +167,7 @@ static void bootio (void) uint8_t buffer[TU_BOOT_LEN]; // check unit number for validity - unit = devrxget(); + unit = rxget(); if (fileunit(unit)) { error("bootio bad unit %d", unit); return; @@ -240,7 +257,7 @@ static void wait4cont (uint8_t code) // wait for a CONT to arrive, but only so long do { - c = devrxget(); + c = rxget(); if (debug) info("wait4cont(): char=0x%02X", c); } while (c != TUF_CONT && --maxchar >= 0); @@ -293,7 +310,7 @@ static int32_t getpacket (tu_packet *pkt) uint16_t rcvchk, expchk; // get remaining packet bytes, incl two checksum bytes - while (--count >= 0) *ptr++ = devrxget(); + while (--count >= 0) *ptr++ = rxget(); // get checksum bytes rcvchk = (ptr[-1]<<8) | (ptr[-2]<<0); @@ -485,7 +502,7 @@ static void tuwrite (tu_cmdpkt *pk) // loop until we see data flag do { last = dk.flag; - dk.flag = devrxget(); + dk.flag = rxget(); if (debug) info("flag=0x%02X last=0x%02X", dk.flag, last); if (last == TUF_INIT && dk.flag == TUF_INIT) { // two in a row is special @@ -507,7 +524,7 @@ static void tuwrite (tu_cmdpkt *pk) } while (dk.flag != TUF_DATA); // byte following data flag is packet data length - dk.length = devrxget(); + dk.length = rxget(); // get remainder of the data packet if (getpacket((tu_packet *)&dk)) { @@ -579,7 +596,7 @@ static void command (int8_t flag) time_end.tv_nsec = 0; pk.flag = flag; - pk.length = devrxget(); + pk.length = rxget(); // check control packet length ... if too long flush it if (pk.length > sizeof(tu_cmdpkt)) { @@ -736,6 +753,13 @@ static void* run (void* none) // loop forever ... almost for (;;) { + // setup for when a BREAK is detected + if (setjmp(rx_break_env)) { + // return here when we get a BREAK on the rx input + if (debug) info(" seen"); + // fall thru to main loop + } + // loop while no characters are available while (devrxavail() == 0) { // delays and printout only if not VAX @@ -754,7 +778,7 @@ static void* run (void* none) // process received characters last = flag; - flag = devrxget(); + flag = rxget(); if (debug) info("flag=0x%02X last=0x%02X", flag, last); switch (flag) { @@ -820,51 +844,6 @@ static void* run (void* none) -// -// monitor for break/error on line, restart emulator if seen -// -static void* monitor (void* none) -{ - int32_t sts; - - info("TU58 monitor started"); - - for (;;) { - - // check for any error - switch (sts = devrxerror()) { - case DEV_ERROR: // error - case DEV_BREAK: // break - // kill and restart the emulator - if (verbose) info("BREAK detected"); -#ifdef THIS_DOES_NOT_YET_WORK_RELIABLY - if (pthread_cancel(th_run)) - error("unable to cancel emulation thread"); - if (pthread_join(th_run, NULL)) - error("unable to join on emulation thread"); - if (pthread_create(&th_run, NULL, run, NULL)) - error("unable to restart emulation thread"); -#endif // THIS_DOES_NOT_YET_WORK_RELIABLY - break; - case DEV_OK: // OK - break; - case DEV_NYI: // not yet implemented - delay_ms(200); - break; - default: // something else... - error("monitor(): unknown flag %d", sts); - break; - } - // bit of a delay, loop again - delay_ms(50); - - } - - return (void*)0; -} - - - // // start tu58 drive emulation // @@ -875,17 +854,13 @@ void tu58drive (void) fatal("illegal BLOCKSIZE (%d) / TU_DATA_LEN (%d) ratio", BLOCKSIZE, TU_DATA_LEN); // say hello - info("emulation start"); + info("TU58 start"); info("R restart, S toggle send init, V toggle verbose, D toggle debug, Q quit"); // run the emulator if (pthread_create(&th_run, NULL, run, NULL)) error("unable to create emulation thread"); - // run the monitor - if (pthread_create(&th_monitor, NULL, monitor, NULL)) - error("unable to create monitor thread"); - // loop on user input for (;;) { uint8_t c; @@ -917,8 +892,6 @@ void tu58drive (void) error("unable to restart emulation thread"); } else if (c == 'Q') { // kill the emulator and exit - if (pthread_cancel(th_monitor)) - error("unable to cancel monitor thread"); if (pthread_cancel(th_run)) error("unable to cancel emulation thread"); break; @@ -935,7 +908,7 @@ void tu58drive (void) error("unable to join on emulation thread"); // all done - info("TU58 emulation end"); + info("TU58 end"); return; }