File:  [Isaki's NoNo m68k/m88k emulator] / nono / util / autoserial / autoserial.cpp
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:09 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, v025, v024, v023, v022, v021, v020, v019, v018, v017, v016, v015, HEAD
nono 0.3.0

#include "header.h"
#include "autofd.h"
#include "kevent.h"
#include <queue>
#include <string>
#include <vector>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>


//#define DEFAULT_DEV "/dev/ttyS0"
#define DEFAULT_DEV "/dev/dty00"

// ナノ秒に対するスケール。
// ミリ秒単位になるようなスケール値とする
#define DEFAULT_TIMESCALE (1000LL * 1000LL)

static long timescale = DEFAULT_TIMESCALE;
static bool opt_n = false;

[[noreturn]]
static void
usage()
{
	printf(
"%s [-d devname] [-t timescale] file\n"
"  -d devname: tty device (default:" DEFAULT_DEV ")\n"
"  -t timescale: timescale factor (default:%lld)\n"
"  -n: no transeive mode\n",
		getprogname(),
		DEFAULT_TIMESCALE
	);
	exit(1);
}

static void
Do(FILE *fp, int tty_fd)
{
	autofd kq;
	struct kevent kev;
	timespec timeout;
	int msec;
	int wait;
	int64 t;
	int r;

	std::queue<uint8> sbuf;
	std::vector<uint8> rbuf;

	kq = kqueue();
	if (kq == -1) {
		err(1, "kqueue");
	}

	if (tty_fd >= 0) {
		r = kevent_add(kq, tty_fd, EVFILT_READ, EV_ADD, 0);
		if (r == -1) {
			err(1, "kevent_add(READ)");
		}
		r = kevent_add(kq, tty_fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 1);
		if (r == -1) {
			err(1, "kevent_add(WRITE)");
		}
	}

	// 受信タイムアウトデフォルト 100msec
	int64 recv_timeout = 1 * 100 * 1000 * 1000;

	char buf[1024];
	bool complete = false;
	bool prompt = true;
	bool writable = true;

	for (;;) {
		if (prompt && !complete) {
			if (fgets(buf, sizeof(buf), fp) != NULL) {
				if (strncmp(buf, "#", 1) == 0) {
					printf("%s", buf);
					continue;
				}
				// ウェイト時間はデフォルトでミリ秒で指定
				if (sscanf(buf, "@%d", &wait)) {
					t = wait * timescale;
					timeout.tv_sec = t / (1000 * 1000 * 1000);
					timeout.tv_nsec = t % (1000 * 1000 * 1000);
					nanosleep(&timeout, NULL);
					continue;
				} else if (sscanf(buf, "@R%d", &msec)) {
					// 受信タイムアウト変更
					recv_timeout = msec * 1000 * 1000;
					continue;
				} else {
					for (char *p = buf; *p; p++) {
						if (*p == '\n') {
							sbuf.push('\r');
						} else {
							sbuf.push(*p);
						}
					}
					prompt = false;
				}
			} else {
				complete = true;
			}
		}

		if (writable && sbuf.size() > 0) {
			buf[0] = sbuf.front();
			sbuf.pop();
			r = write(tty_fd, buf, 1);
			if (r <= 0) {
				err(1, "tty_fd write");
			}
			writable = false;
		}

		t = recv_timeout;
		timeout.tv_sec = t / (1000 * 1000 * 1000);
		timeout.tv_nsec = t % (1000 * 1000 * 1000);

		r = kevent_poll(kq, &kev, 1, &timeout);
		if (r == -1) {
			err(1, "kevent_poll");
		}
		if (r == 0) {
			break;
		}

		int udata = EV_UDATA2INT(kev.udata);
		if (udata == 0) {
			r = read(tty_fd, buf, 1);
			if (r < 0) {
				err(1, "tty read");
			}
			if (r == 0) {
				break;
			}
			// CR -> LF, LF -> discard
			if (buf[0] == '\n') {
				rbuf.push_back('\n');
				fwrite(rbuf.data(), 1, rbuf.size(), stdout);
				fflush(stdout);
				rbuf.clear();
			} else if (buf[0] == '\r') {
			} else {
				rbuf.push_back(buf[0]);
			}

			if (rbuf.size() == 1 && rbuf[0] == '>') {
				prompt = true;
			}
			writable = true;
		}
	}

	if (!complete) {
		errx(1, "timeout");
	}
}

int
main(int ac, char *av[])
{
	int c;
	std::string dev = DEFAULT_DEV;

	while ((c = getopt(ac, av, "d:nt:")) >= 0) {
		switch (c) {
		 case 'd':
			dev = optarg;
			break;
		 case 'n':
			opt_n = true;
			break;
		 case 't':
			timescale = atol(optarg);
			break;
		 default:
			usage();
			break;
		}
	}

	ac -= optind;
	av += optind;

	if (ac == 0) {
		usage();
	}

	FILE *fp;

	if (strcmp(av[0], "-") == 0) {
		fp = stdin;
	} else {
		fp = fopen(av[0], "r");
		if (fp == NULL) {
			err(1, "fopen %s", av[0]);
		}
	}

	int tty_fd = -1;
	if (!opt_n) {
		struct termios t;
		tty_fd = open(dev.c_str(), O_RDWR);
		if (tty_fd < 0) {
			err(1, "open %s", dev.c_str());
		}
		tcgetattr(tty_fd, &t);
		cfmakeraw(&t);
		cfsetspeed(&t, B9600);
		t.c_iflag |= IGNBRK;
		t.c_oflag &= ~(OPOST);
		t.c_cflag |= CREAD | CLOCAL | HUPCL | CS8;
		t.c_lflag &= ~(ECHO | ICANON);
		t.c_cc[VTIME] = 0;
		t.c_cc[VMIN] = 1;
		tcsetattr(tty_fd, TCSAFLUSH, &t);

	}

	Do(fp, tty_fd);

	if (tty_fd >= 0) {
		close(tty_fd);
	}
	fclose(fp);
	return 0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.