154 lines
4.9 KiB
C
154 lines
4.9 KiB
C
// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
// See LICENSE.md for license information.
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
// iigsSerialPeer.c - the host end of the Apple IIgs serial gates. MAME's
|
|
// bitbanger socket connects out, so this listens on 127.0.0.1:PORT.
|
|
//
|
|
// iigsSerialPeer capture PORT FILE
|
|
// iigsSerialPeer loss PORT TOTAL LOG
|
|
//
|
|
// capture (verify-iigs-serial.sh): every byte the IIgs sends, added to FILE
|
|
// as it arrives (the gate kills this peer once MAME is done, and reads
|
|
// FILE). Waits CAPTURE_ACCEPT_SECONDS for MAME (FILE left empty when it
|
|
// never comes), then collects for CAPTURE_SECONDS or until MAME goes away.
|
|
// Exit 0.
|
|
//
|
|
// loss (verify-iigs-serial-loss.sh): once the probe has sent READY, TOTAL
|
|
// bytes (byte k = k & 0xFF) in one go; then drains whatever the probe
|
|
// sends until MAME goes away. LOG gets a line when the bytes went and one
|
|
// with the start of what came back. Exit 0; 1 when MAME never connects
|
|
// (within LOSS_ACCEPT_SECONDS).
|
|
//
|
|
// Bad arguments exit 2.
|
|
|
|
#define _DEFAULT_SOURCE
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "gateArg.h"
|
|
#include "gateNet.h"
|
|
#include "gateText.h"
|
|
#include "toolBuf.h"
|
|
#include "toolFile.h"
|
|
|
|
#define TOOL_NAME "iigsSerialPeer"
|
|
#define ARG_MODE 1
|
|
#define ARG_PORT 2
|
|
#define ARG_FILE 3
|
|
#define ARG_TOTAL 3
|
|
#define ARG_LOG 4
|
|
#define CAPTURE_ARGS 4
|
|
#define LOSS_ARGS 5
|
|
|
|
#define READ_SECONDS 1.0
|
|
#define READ_SIZE 256u
|
|
#define CAPTURE_ACCEPT_SECONDS 300.0
|
|
#define CAPTURE_SECONDS 280.0
|
|
#define LOSS_ACCEPT_SECONDS 600.0
|
|
#define READY "READY"
|
|
#define BYTE_MASK 0xFFu
|
|
#define SHOWN_BYTES 64u
|
|
|
|
static int capture(uint16_t port, const char *path);
|
|
static int loss(uint16_t port, long total, const char *logPath);
|
|
static int usage(void);
|
|
|
|
|
|
// Every byte MAME's connection carries, kept in path.
|
|
static int capture(uint16_t port, const char *path) {
|
|
FILE *out;
|
|
uint8_t chunk[READ_SIZE];
|
|
double deadline;
|
|
int32_t got;
|
|
int conn;
|
|
|
|
conn = gateNetAcceptOne(port, CAPTURE_ACCEPT_SECONDS);
|
|
out = fopen(path, "wb");
|
|
if (out == NULL) {
|
|
toolFail("cannot write %s: %s", path, strerror(errno));
|
|
}
|
|
deadline = gateNetNow() + CAPTURE_SECONDS;
|
|
while (conn >= 0 && gateNetNow() < deadline) {
|
|
got = gateNetRecv(conn, chunk, sizeof(chunk), READ_SECONDS);
|
|
if (got == GATE_NET_CLOSED) {
|
|
break;
|
|
}
|
|
if (got > 0 && (fwrite(chunk, 1u, (size_t)got, out) != (size_t)got || fflush(out) != 0)) {
|
|
toolFail("cannot write %s: %s", path, strerror(errno));
|
|
}
|
|
}
|
|
fclose(out);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// On READY, the stream; then drain until MAME closes the link.
|
|
static int loss(uint16_t port, long total, const char *logPath) {
|
|
ToolBufT seen;
|
|
ToolBufT pattern;
|
|
FILE *log;
|
|
uint8_t chunk[READ_SIZE];
|
|
bool sent;
|
|
int32_t got;
|
|
long k;
|
|
int conn;
|
|
|
|
log = fopen(logPath, "w");
|
|
if (log == NULL) {
|
|
toolFail("cannot write %s: %s", logPath, strerror(errno));
|
|
}
|
|
conn = gateNetAcceptOne(port, LOSS_ACCEPT_SECONDS);
|
|
if (conn < 0) {
|
|
toolFail("MAME never connected to port %u", (unsigned)port);
|
|
}
|
|
memset(&seen, 0, sizeof(seen));
|
|
memset(&pattern, 0, sizeof(pattern));
|
|
sent = false;
|
|
for (;;) {
|
|
got = gateNetRecv(conn, chunk, sizeof(chunk), READ_SECONDS);
|
|
if (got == GATE_NET_CLOSED) {
|
|
break;
|
|
}
|
|
if (got == GATE_NET_IDLE) {
|
|
continue;
|
|
}
|
|
toolBufAdd(&seen, chunk, (size_t)got);
|
|
if (!sent && gateTextFind(seen.data, seen.len, 0u, READY, strlen(READY)) != GATE_TEXT_NOT_FOUND) {
|
|
for (k = 0; k < total; k++) {
|
|
toolBufByte(&pattern, (uint32_t)k & BYTE_MASK);
|
|
}
|
|
if (!gateNetSendAll(conn, pattern.data, pattern.len)) {
|
|
toolFail("send: %s", strerror(errno));
|
|
}
|
|
sent = true;
|
|
fprintf(log, "peer: READY seen, sent %ld bytes\n", total);
|
|
fflush(log);
|
|
}
|
|
}
|
|
fprintf(log, "peer: received ");
|
|
gateNetShowBytes(log, seen.data, seen.len < SHOWN_BYTES ? seen.len : SHOWN_BYTES);
|
|
fprintf(log, "\n");
|
|
fclose(log);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int usage(void) {
|
|
fprintf(stderr, "usage: %s capture PORT FILE\n %s loss PORT TOTAL LOG\n", TOOL_NAME, TOOL_NAME);
|
|
return GATE_USAGE_EXIT;
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
gateNetStart(TOOL_NAME);
|
|
if (argc == CAPTURE_ARGS && strcmp(argv[ARG_MODE], "capture") == 0) {
|
|
return capture(gateArgPort(argv[ARG_PORT]), argv[ARG_FILE]);
|
|
}
|
|
if (argc == LOSS_ARGS && strcmp(argv[ARG_MODE], "loss") == 0) {
|
|
return loss(gateArgPort(argv[ARG_PORT]), gateArgLong("total", argv[ARG_TOTAL]), argv[ARG_LOG]);
|
|
}
|
|
return usage();
|
|
}
|