command.js: commandrc and AUTOEXEC.BAT split

This commit is contained in:
minjaesong
2026-06-03 23:15:34 +09:00
parent d223adda25
commit 35263eeaa4
5 changed files with 87 additions and 58 deletions

View File

@@ -1,20 +1,11 @@
echo "Starting TVDOS..."
rem put set-xxx commands here:
set PATH=\tvdos\installer;\tvdos\tuidev;\tbas;\hopper\bin;$PATH
set INCLPATH=\hopper\include;$INCLPATH
set HELPPATH=\hopper\help;$HELPPATH
set KEYBOARD=us_colemak
rem load Korean font / IME (font upload is global hardware)
rem AUTOEXEC.BAT -- per-console launch script. Run once for every console:
rem each virtual-console pane runs it (via vtmgr's bootstrap), and the boot
rem shell runs it as the fallback once vtmgr exits (Alt-0). Environment setup
rem (`set` commands) lives in \commandrc, which TVDOS.SYS runs before this.
rem
rem Korean IME registers a per-CONTEXT handler (unicode.uniprint), so it must
rem run per-console here rather than once at boot.
tvdos/i18n/korean
rem Boot into virtual consoles. vtmgr owns the keyboard and screen, and spawns
rem a `command -fancy` shell per VT (Alt-1..6 / chvt to switch, Alt-0 to exit).
rem It snapshots the environment set above and replays it into every pane.
rem NOTE: `fsh` is a graphical shell and must not run inside a VT pane; launch
rem it directly (not via vtmgr) if you want it. (Old boot line: fsh)
tvdos/sbin/vtmgr
rem Fallback shell once vtmgr exits (Alt-0), so the console is never left bare.
rem The interactive shell for this console.
command -fancy

9
assets/disk0/commandrc Normal file
View File

@@ -0,0 +1,9 @@
rem commandrc -- environment setup, run by TVDOS.SYS in EVERY context
rem (the boot shell AND every virtual-console pane). Put `set` commands and
rem other env-only configuration here. Do NOT launch apps from this file:
rem app launches belong in AUTOEXEC.BAT (run per-console by vtmgr).
set PATH=\tvdos\installer;\tvdos\tuidev;\tbas;\hopper\bin;$PATH
set INCLPATH=\hopper\include;$INCLPATH
set HELPPATH=\hopper\help;$HELPPATH
set KEYBOARD=us_colemak

View File

@@ -1471,17 +1471,40 @@ try {
serial.println("Warning: Could not load HSDPA driver: " + e.message)
}
// Boot script. When vtmgr re-evaluates TVDOS.SYS inside a per-VT pane
// context, the pane already has a SKIP flag set so we don't recursively
// kick off AUTOEXEC.BAT (which would itself invoke command -fancy and
// nest a shell underneath vtmgr).
if (typeof _TVDOS_SKIP_AUTOEXEC === "undefined" || !_TVDOS_SKIP_AUTOEXEC) {
serial.println(`TVDOS.SYS initialised on VM ${sys.getVmId()}, running boot script...`);
// Boot script. The work is split across two files:
// \commandrc -- environment (`set` commands); run in EVERY context.
// \AUTOEXEC.BAT -- per-console launch (IME + interactive shell).
// vtmgr re-evaluates TVDOS.SYS inside each per-VT pane; a pane sets
// _TVDOS_IS_VT_PANE so it only replays the environment here and leaves the
// AUTOEXEC launch to vtmgr's pane bootstrap (which avoids recursively
// spawning vtmgr inside a pane).
{
let cmdsrc = files.open("A:/tvdos/bin/command.js").sread()
let runBatch = (path) => eval(`var _BAT=function(exec_args){${cmdsrc}\n};_BAT`)(["", "-c", path])
let cmdfile = files.open("A:/tvdos/bin/command.js")
eval(`var _AUTOEXEC=function(exec_args){${cmdfile.sread()}\n};` +
`_AUTOEXEC`)(["", "-c", "\\AUTOEXEC.BAT"])
}
else {
serial.println(`TVDOS.SYS re-initialised in VT pane on VM ${sys.getVmId()}`);
// Environment first, boot and pane alike. Gives every pane the same
// PATH / KEYBOARD / etc. natively, with no env-snapshot replay needed.
// \commandrc has no .BAT extension (so command.js's batch-file path,
// which keys off the extension, won't pick it up); run it line-by-line.
// `set` mutates the shared _TVDOS.variables, so the effect persists across
// the per-line shell invocations. Skip blanks and `rem` comments.
let rcFile = files.open("A:/commandrc")
if (rcFile.exists) {
rcFile.sread().split('\n').forEach((line) => {
let t = line.trim()
if (t.length > 0 && !/^rem(\s|$)/i.test(t)) runBatch(line)
})
}
if (typeof _TVDOS_IS_VT_PANE === "undefined" || !_TVDOS_IS_VT_PANE) {
serial.println(`TVDOS.SYS initialised on VM ${sys.getVmId()}, running boot script...`);
// Boot console: hand the screen to the virtual-console multiplexer.
// When it exits (Alt-0), fall through to AUTOEXEC so the console is
// never left bare.
runBatch("tvdos/sbin/vtmgr")
runBatch("\\AUTOEXEC.BAT")
}
else {
serial.println(`TVDOS.SYS re-initialised in VT pane on VM ${sys.getVmId()}`);
}
}

View File

@@ -72,25 +72,23 @@ const TVDOS_SYS_SRC = files.open("A:/tvdos/TVDOS.SYS").sread()
// _BIOS is visible) and re-declare it in every pane bootstrap.
const BIOS_FIRST_BOOTABLE_PORT = JSON.stringify(_BIOS.FIRST_BOOTABLE_PORT)
// Snapshot the live environment from the main context. vtmgr runs after
// AUTOEXEC.BAT, so _TVDOS.variables already holds the fully expanded PATH,
// INCLPATH, HELPPATH, KEYBOARD, etc. Each pane is a fresh context whose
// TVDOS.SYS only sets the bare defaults, so we replay this snapshot over the
// pane's defaults — giving panes the same path/variable resolution as the
// boot shell without re-running AUTOEXEC (which would relaunch the GUI shell).
const ENV_JSON = JSON.stringify(_TVDOS.variables)
// Environment no longer needs snapshotting/replaying: each pane re-evaluates
// TVDOS.SYS, whose boot block runs \commandrc in every context, so the pane
// gets the same PATH / KEYBOARD / etc. natively. The pane then runs
// \AUTOEXEC.BAT (the per-console launch script: IME + interactive shell).
function makePaneBootstrap(vtNum) {
const TP_BASE = vtTextPlaneAddr(vtNum)
const VT_BLK = vtBlockAddr(vtNum)
// Shell-launcher code runs after TVDOS.SYS in the SAME eval scope, so
// `files`, `eval`, `_TVDOS` etc. resolve via lexical closure. Apply the
// captured environment before launching the shell.
// Launcher code runs after TVDOS.SYS in the SAME eval scope, so `files`,
// `eval`, `_TVDOS` etc. resolve via lexical closure. TVDOS.SYS's boot
// block already ran \commandrc (env) and skipped its own AUTOEXEC because
// the pane sets _TVDOS_IS_VT_PANE; here we run \AUTOEXEC.BAT to launch the
// per-console shell.
const SHELL_START = ";\n"
+ "Object.assign(_TVDOS.variables, " + ENV_JSON + ");\n"
+ "var _cmdfileSrc = files.open('A:/tvdos/bin/command.js').sread();\n"
+ "eval('var _VTSHELL=function(exec_args){' + _cmdfileSrc + '\\n};_VTSHELL')(['', '-fancy']);\n"
+ "eval('var _VTSHELL=function(exec_args){' + _cmdfileSrc + '\\n};_VTSHELL')(['', '-c', '\\\\AUTOEXEC.BAT']);\n"
const combined = TVDOS_SYS_SRC + SHELL_START
@@ -407,10 +405,9 @@ con.poll_keys = function() { return [0,0,0,0,0,0,0,0] }
// ── TVDOS.SYS init flags + BIOS stub ───────────────────────────────────────
globalThis._TVDOS_IS_VT_PANE = true
globalThis._TVDOS_SKIP_AUTOEXEC = true
globalThis._BIOS = { FIRST_BOOTABLE_PORT: ${BIOS_FIRST_BOOTABLE_PORT} }
// ── load TVDOS.SYS and start command -fancy in one direct-eval call ─────
// ── load TVDOS.SYS and run AUTOEXEC.BAT (the per-console shell) in one direct-eval ─────
// Strict-mode direct eval is scope-isolated, so TVDOS.SYS's \`const _TVDOS\`
// only survives within the eval scope. The shell launcher must run inside
// the same eval to access it (via lexical closure into nested evals).