@henols/c64-re-tools 2.0.0-rc.5 → 2.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/host/main.ts +6 -5
- package/src/host/server.ts +12 -4
- package/src/host/tools/index.ts +6 -2
- package/src/host/tools/status.ts +12 -4
- package/src/host/vice/process.ts +2 -1
- package/src/host/vice/session.ts +2 -2
package/package.json
CHANGED
package/src/host/main.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { DEFAULT_HOST_PORT } from "../protocol/messages.ts";
|
|
|
6
6
|
import { ProcessSupervisor } from "../native/processes.ts";
|
|
7
7
|
import { startTrace, trace } from "../trace.ts";
|
|
8
8
|
import { ListenError, parsePort, startHostServer, stopSignal } from "./server.ts";
|
|
9
|
-
import { viceFlaws } from "../native/discover.ts";
|
|
9
|
+
import { viceFlaws, type ViceVersion } from "../native/discover.ts";
|
|
10
10
|
import { createToolDispatcher } from "./tools/index.ts";
|
|
11
11
|
import { checkViceStarts, findVice } from "./vice/process.ts";
|
|
12
12
|
import { viceSessionFactory } from "./vice/session.ts";
|
|
@@ -70,15 +70,16 @@ async function main(): Promise<number> {
|
|
|
70
70
|
supervisor.startWatchdog("host");
|
|
71
71
|
|
|
72
72
|
// Finding x64sc is not enough: VICE without its ROM files exits at once.
|
|
73
|
-
let
|
|
73
|
+
let vice: ViceVersion;
|
|
74
74
|
try {
|
|
75
|
-
|
|
75
|
+
vice = await checkViceStarts({ supervisor });
|
|
76
76
|
} catch (error) {
|
|
77
77
|
process.stderr.write(`${(error as Error).message}\n`);
|
|
78
78
|
await supervisor.stopAll();
|
|
79
79
|
return 1;
|
|
80
80
|
}
|
|
81
|
-
log(
|
|
81
|
+
log(`VICE ${vice.major}.${vice.minor}${vice.revision === undefined ? "" : ` r${vice.revision}`} starts`);
|
|
82
|
+
const flaws = viceFlaws(vice);
|
|
82
83
|
// A known VICE flaw is named, not worked around (D56).
|
|
83
84
|
for (const flaw of flaws) log(`Known VICE flaw: ${flaw}`);
|
|
84
85
|
|
|
@@ -89,7 +90,7 @@ async function main(): Promise<number> {
|
|
|
89
90
|
port,
|
|
90
91
|
log,
|
|
91
92
|
createViceSession: viceSessionFactory({ supervisor, log, flaws }),
|
|
92
|
-
tools: createToolDispatcher({ supervisor, log }),
|
|
93
|
+
tools: createToolDispatcher({ supervisor, log, vice }),
|
|
93
94
|
});
|
|
94
95
|
} catch (error) {
|
|
95
96
|
if (error instanceof ListenError && error.code === "EADDRINUSE") {
|
package/src/host/server.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface ViceSessionHandle {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** Starts a VICE session. Throws WireFailure with an actionable code when it cannot. */
|
|
23
|
-
export type ViceSessionFactory = (options: { videoStandard: VideoStandard }) => Promise<ViceSessionHandle>;
|
|
23
|
+
export type ViceSessionFactory = (options: { videoStandard: VideoStandard; log?: (line: string) => void }) => Promise<ViceSessionHandle>;
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Runs one native-tool request. `signal` aborts when the client goes away; the
|
|
@@ -95,9 +95,12 @@ class Connection {
|
|
|
95
95
|
constructor(socket: Socket, options: HostServerOptions, log: (line: string) => void) {
|
|
96
96
|
this.#socket = socket;
|
|
97
97
|
this.#options = options;
|
|
98
|
-
|
|
98
|
+
// Every line names its connection, so the console shows which client starts and stops what.
|
|
99
|
+
this.#log = (line) => log(`connection ${this.#id}: ${line}`);
|
|
99
100
|
this.closed = new Promise((resolve) => (this.#resolveClosed = resolve));
|
|
100
|
-
|
|
101
|
+
const remote = `${socket.remoteAddress}:${socket.remotePort}`;
|
|
102
|
+
this.#log(`opened from ${remote}`);
|
|
103
|
+
trace().event("connection.open", { connection: this.#id, remote, local: socket.localAddress });
|
|
101
104
|
this.#handshakeTimer = setTimeout(
|
|
102
105
|
() => this.#abort("no hello before the handshake timeout"),
|
|
103
106
|
options.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS,
|
|
@@ -180,11 +183,13 @@ class Connection {
|
|
|
180
183
|
this.#isTool = true;
|
|
181
184
|
this.#state = "open";
|
|
182
185
|
this.#send({ type: "ready" });
|
|
186
|
+
this.#log("ready for tool requests");
|
|
183
187
|
trace().event("connection.ready", { connection: this.#id, role: "tool" });
|
|
184
188
|
this.#restartHeartbeat();
|
|
185
189
|
return;
|
|
186
190
|
}
|
|
187
191
|
this.#state = "starting";
|
|
192
|
+
this.#log(`starts a VICE session (${message.videoStandard ?? "pal"})`);
|
|
188
193
|
this.#contain(this.#startSession(message.videoStandard ?? "pal"));
|
|
189
194
|
return;
|
|
190
195
|
}
|
|
@@ -206,6 +211,7 @@ class Connection {
|
|
|
206
211
|
this.#session = session;
|
|
207
212
|
this.#state = "open";
|
|
208
213
|
this.#send({ type: "ready" });
|
|
214
|
+
this.#log(`VICE session ready after ${Math.round(performance.now() - started)} ms`);
|
|
209
215
|
trace().event("connection.ready", { connection: this.#id, role: "vice-session", videoStandard, ms: elapsedMs(started) });
|
|
210
216
|
this.#restartHeartbeat();
|
|
211
217
|
}
|
|
@@ -213,7 +219,7 @@ class Connection {
|
|
|
213
219
|
/** The new session, or undefined after the failure is sent to the client. Never rejects, also when the factory throws at once. */
|
|
214
220
|
async #createSession(videoStandard: VideoStandard): Promise<ViceSessionHandle | undefined> {
|
|
215
221
|
try {
|
|
216
|
-
return await this.#options.createViceSession({ videoStandard });
|
|
222
|
+
return await this.#options.createViceSession({ videoStandard, log: this.#log });
|
|
217
223
|
} catch (error) {
|
|
218
224
|
this.#log(`VICE session failed to start: ${error instanceof Error ? error.message : String(error)}`);
|
|
219
225
|
trace().event("connection.session-failed", { connection: this.#id, error });
|
|
@@ -275,11 +281,13 @@ class Connection {
|
|
|
275
281
|
this.#toolAbort.abort();
|
|
276
282
|
const session = this.#session ?? (await this.#starting);
|
|
277
283
|
this.#session = undefined;
|
|
284
|
+
if (session !== undefined) this.#log("the client is gone; stopping its VICE session");
|
|
278
285
|
try {
|
|
279
286
|
await session?.close();
|
|
280
287
|
} catch (error) {
|
|
281
288
|
this.#log(`session close failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
282
289
|
}
|
|
290
|
+
this.#log("closed");
|
|
283
291
|
this.#resolveClosed();
|
|
284
292
|
}
|
|
285
293
|
}
|
package/src/host/tools/index.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// ACME, DXA and Ghidra themselves. One entry per operation, never a
|
|
4
4
|
// generic executable route.
|
|
5
5
|
|
|
6
|
+
import type { ViceVersion } from "../../native/discover.ts";
|
|
6
7
|
import type { ProcessSupervisor } from "../../native/processes.ts";
|
|
7
8
|
import type { ToolContext } from "../../native/run.ts";
|
|
8
9
|
import type { ToolOperation, ToolOperations } from "../../protocol/tools.ts";
|
|
@@ -24,8 +25,11 @@ const ADAPTERS: { [O in ToolOperation]: Adapter<O> } = {
|
|
|
24
25
|
"petcat.decode": (_params, attachments, context) => decode(attachments[0]!, context),
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
/** `vice`: the version that the host read from the emulator at start, for host.status. */
|
|
29
|
+
export function createToolDispatcher(options: { supervisor: ProcessSupervisor; env?: NodeJS.ProcessEnv; log?: (line: string) => void; vice?: ViceVersion }): ToolDispatcher {
|
|
28
30
|
return (op, params, attachments, signal) => {
|
|
29
|
-
|
|
31
|
+
const context = { supervisor: options.supervisor, signal, env: options.env, log: options.log };
|
|
32
|
+
if (op === "host.status") return hostStatus(context, options.vice) as ReturnType<ToolDispatcher>;
|
|
33
|
+
return ADAPTERS[op](params, attachments, context);
|
|
30
34
|
};
|
|
31
35
|
}
|
package/src/host/tools/status.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// host.status: VICE and the tools that come with it, each found the way its
|
|
2
2
|
// operations find it and run once with a version option. A tool from a
|
|
3
3
|
// VICE older than MIN_VICE is reported as not working, as its operations refuse it.
|
|
4
|
+
// VICE itself is reported with the version that the host read from the
|
|
5
|
+
// running emulator at start: the Homebrew x64sc of VICE 3.10 on macOS stops
|
|
6
|
+
// on -version ("argv[0] is NULL") although the emulator runs.
|
|
4
7
|
|
|
5
8
|
import type { ToolStatus } from "../../protocol/tools.ts";
|
|
6
|
-
import { C1541, findTool, isOlderThanMinimum, MIN_VICE, PETCAT, VICE, viceFlaws, viceVersionOf, type ToolSpec } from "../../native/discover.ts";
|
|
9
|
+
import { C1541, findTool, isOlderThanMinimum, MIN_VICE, PETCAT, VICE, viceFlaws, viceVersionOf, type ToolSpec, type ViceVersion } from "../../native/discover.ts";
|
|
7
10
|
import type { ToolContext } from "../../native/run.ts";
|
|
8
11
|
import { probeTools } from "../../native/status.ts";
|
|
9
12
|
import { findVice } from "../vice/process.ts";
|
|
@@ -16,16 +19,21 @@ function requireMinimum(status: ToolStatus, tool: ToolSpec): ToolStatus {
|
|
|
16
19
|
return { ...status, runs: false, problem: `${found}, and c64-re-tools needs VICE ${MIN_VICE.major}.${MIN_VICE.minor} or later. ${tool.remedy}` };
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
/** The version text of a VICE version, as x64sc -version prints it. */
|
|
23
|
+
const versionText = (version: ViceVersion) => `x64sc (VICE ${version.major}.${version.minor}${version.revision === undefined ? "" : ` SVN r${version.revision}`})`;
|
|
24
|
+
|
|
25
|
+
/** `started`: the version that the host read from the emulator at start; without it, VICE is probed with -version too. */
|
|
26
|
+
export async function hostStatus(context: ToolContext, started?: ViceVersion): Promise<{ result: { tools: ToolStatus[] } }> {
|
|
20
27
|
const specs = [VICE, C1541, PETCAT];
|
|
21
|
-
const
|
|
28
|
+
const probed = await probeTools(
|
|
22
29
|
[
|
|
23
|
-
{ name: VICE.name, find: findVice, args: ["-version"], pattern: /^x64sc \(VICE \d/ },
|
|
30
|
+
...(started === undefined ? [{ name: VICE.name, find: findVice, args: ["-version"], pattern: /^x64sc \(VICE \d/ }] : []),
|
|
24
31
|
{ name: C1541.name, find: (env) => findTool(C1541, env), args: ["-version"], pattern: /^c1541 \(VICE \d/ },
|
|
25
32
|
{ name: PETCAT.name, find: (env) => findTool(PETCAT, env), args: ["-version"], pattern: /^petcat \(VICE \d/ },
|
|
26
33
|
],
|
|
27
34
|
context,
|
|
28
35
|
);
|
|
36
|
+
const tools = started === undefined ? probed : [{ name: VICE.name, found: true, path: findVice(context.env ?? process.env), version: versionText(started), runs: true }, ...probed];
|
|
29
37
|
const checked = tools.map((status, index) => requireMinimum(status, specs[index]!));
|
|
30
38
|
// VICE runs with its known flaws; status names them (D56).
|
|
31
39
|
const vice = checked[0]!;
|
package/src/host/vice/process.ts
CHANGED
|
@@ -307,6 +307,7 @@ async function launchOnce(options: LaunchOptions): Promise<ViceProcess> {
|
|
|
307
307
|
clearInterval(slow);
|
|
308
308
|
}
|
|
309
309
|
if (failure !== undefined) throw failure;
|
|
310
|
+
log(`VICE stopped (pid ${child.pid}, in ${Date.now() - started} ms)`);
|
|
310
311
|
})();
|
|
311
312
|
return stopping;
|
|
312
313
|
};
|
|
@@ -325,7 +326,7 @@ async function launchOnce(options: LaunchOptions): Promise<ViceProcess> {
|
|
|
325
326
|
if (error instanceof WireFailure) throw error;
|
|
326
327
|
throw new WireFailure("machine-unavailable", "The emulator could not be started on the host.");
|
|
327
328
|
}
|
|
328
|
-
log(`VICE started (pid ${child.pid}, monitor ports ${port} and ${textPort})`);
|
|
329
|
+
log(`VICE started (${mode}, pid ${child.pid}, monitor ports ${port} and ${textPort})`);
|
|
329
330
|
trace().event("vice.ready", { pid: child.pid, ms: elapsedMs(launched) });
|
|
330
331
|
return { pid: child.pid, monitor, text, exited: child.exited, outputTail, stop };
|
|
331
332
|
}
|
package/src/host/vice/session.ts
CHANGED
|
@@ -988,6 +988,7 @@ export class ViceSession implements ViceSessionHandle {
|
|
|
988
988
|
if (this.#held !== undefined) throw new WireFailure("machine-unavailable", this.#heldWhy);
|
|
989
989
|
if (this.#launch === undefined) throw new WireFailure("operation-failed", "This session cannot start another emulator.");
|
|
990
990
|
const running = this.#state === "running";
|
|
991
|
+
this.#log(mode === "window" ? "moving the machine to a VICE with a window" : "moving the machine back to a headless VICE");
|
|
991
992
|
await this.#stop();
|
|
992
993
|
const handover: Handover = {
|
|
993
994
|
file: join(this.#workDir, mode === "window" ? "window-open.vsf" : "window-close.vsf"),
|
|
@@ -1354,8 +1355,7 @@ export function viceSessionFactory(options: {
|
|
|
1354
1355
|
/** The known flaws of the VICE version that the host checked at start. */
|
|
1355
1356
|
flaws?: readonly string[];
|
|
1356
1357
|
}): ViceSessionFactory {
|
|
1357
|
-
|
|
1358
|
-
return async ({ videoStandard }) => {
|
|
1358
|
+
return async ({ videoStandard, log = options.log ?? (() => {}) }) => {
|
|
1359
1359
|
const launch: ViceLauncher = (mode, launchLog) =>
|
|
1360
1360
|
launchVice({
|
|
1361
1361
|
videoStandard,
|