185 lines
7.4 KiB
Bash
185 lines
7.4 KiB
Bash
# shellcheck shell=bash
|
|
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
# See LICENSE.md for license information.
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
# Shared by the Win16 gates (scripts/bench-win16.sh, scripts/verify-win16-*.sh)
|
|
# and joey-run: run a Win16 program (make win16) on Windows for Workgroups
|
|
# 3.11 under DOSBox Staging (scripts/dosbox-win16.conf), headless on a
|
|
# private Xvfb display (or, WIN16_HEADLESS=0, in a window on $DISPLAY).
|
|
# Windows is a copy of the installer's (component win311, WIN16_C) made for
|
|
# the run, so a run never changes it; the program's folder is drive D:, so
|
|
# what it writes lands there, and it is Windows' shell, so Windows ends (and
|
|
# DOSBox with it) when the program does. The copy runs JoeyLib's own drivers:
|
|
# KPJOY.DRV (component kpjoy, KPJOY_DRV), the joystick driver Windows 3.1
|
|
# lacks, and KPCOMM.DRV (component kpcomm, KPCOMM_DRV) in place of its
|
|
# COMM.DRV. Source after joeyEnv.sh.
|
|
#
|
|
# win16Need exit 2 unless Windows, its drivers and DOSBox
|
|
# are installed
|
|
# win16Stage WORK FOLDER EXE [ARGS...]
|
|
# WORK/c: a copy of Windows whose shell is EXE
|
|
# in FOLDER (drive D:), given ARGS (WIN.COM
|
|
# hands its command line to the shell; a shell=
|
|
# line with arguments is not loaded at all),
|
|
# with JoeyLib's drivers, and WinG's runtime
|
|
# when WIN16_WING=1 (the default once the wing
|
|
# component is installed; 0 runs without it)
|
|
# win16Start WORK [NAME=VALUE...]
|
|
# boot it in the background, the NAME=VALUEs in
|
|
# the DOS environment Windows starts from (the
|
|
# port's JOEYLIB_SCALE and the like); sets
|
|
# WIN16_DISPLAY, WIN16_GUARD (the timeout
|
|
# wrapper, which is DOSBox itself when
|
|
# WIN16_WALL=0: no cap) and WIN16_PID (DOSBox).
|
|
# Sound is off unless WIN16_SOUND=1 (then SDL
|
|
# plays it, through SDL_AUDIODRIVER if set);
|
|
# WIN16_DOSBOX_ARGS are more DOSBox arguments
|
|
# (--set serial1=...)
|
|
# win16Log FOLDER print the program's joeylog in FOLDER, if any
|
|
# (a DOS program makes it JOEYLOG.TXT)
|
|
# win16Window print DOSBox's X window
|
|
# win16Stop stop DOSBox and the display
|
|
#
|
|
# Win16 code only ever runs on DOSBox's emulated CPU: running it natively
|
|
# (the 32-bit Wine's winevdm) crashed the host's kernel.
|
|
|
|
WIN16_DISPLAY=""
|
|
WIN16_GUARD=""
|
|
WIN16_PID=""
|
|
WIN16_XVFB_PID=""
|
|
WIN16_FOLDER=""
|
|
WIN16_ARGS=""
|
|
WIN16_HEADLESS="${WIN16_HEADLESS:-1}"
|
|
WIN16_SOUND="${WIN16_SOUND:-0}"
|
|
if [ -n "${WING_DIR:-}" ] && [ -f "$WING_DIR/WING.DLL" ]; then
|
|
WIN16_WING="${WIN16_WING:-1}"
|
|
else
|
|
WIN16_WING=0
|
|
fi
|
|
WIN16_DOSBOX_ARGS=()
|
|
# A hard cap on DOSBox in seconds, whatever happens to the script that
|
|
# started it; 0 for none.
|
|
WIN16_WALL="${WIN16_WALL:-900}"
|
|
|
|
|
|
win16Need() {
|
|
joeyNeed DOSBOX dosbox
|
|
joeyNeed WIN16_C emulator:win16
|
|
joeyNeed KPJOY_DRV kpjoy
|
|
joeyNeed KPCOMM_DRV kpcomm
|
|
}
|
|
|
|
|
|
win16Stage() {
|
|
local work="$1"
|
|
WIN16_FOLDER="$2"
|
|
WIN16_ARGS="${*:4}"
|
|
rm -rf "${work:?}/c"
|
|
mkdir -p "$work"
|
|
cp -r "$WIN16_C" "$work/c"
|
|
cp "$KPJOY_DRV" "$work/c/WINDOWS/SYSTEM/KPJOY.DRV"
|
|
cp "$KPCOMM_DRV" "$work/c/WINDOWS/SYSTEM/KPCOMM.DRV"
|
|
"$(joeyTool winIni)" "$work/c/WINDOWS/SYSTEM.INI" \
|
|
set boot shell "D:\\$3" \
|
|
set boot comm.drv kpcomm.drv \
|
|
set drivers joystick kpjoy.drv
|
|
if [ "$WIN16_WING" = "1" ]; then
|
|
win16StageWinG "$work"
|
|
fi
|
|
}
|
|
|
|
|
|
# WinG's runtime into SYSTEM and its DVA.386 into [386Enh], as WinG's own
|
|
# setup does on Windows 3.x (this one has no VFLATD). WinG profiles the
|
|
# display the first time a program uses it and keeps the result in WIN.INI
|
|
# (WIN16_WING_PROFILE), which on a copy made afresh each run would be every
|
|
# run: about 165 s at 386SX-16 speed. Its result for this Windows (the S3
|
|
# driver at 800x600 in 256 colours), taken at that speed, goes in instead,
|
|
# under WinG's key for the display: the driver, the mode, the driver's
|
|
# version, the driver file's date as DOS keeps it (year, month and day in
|
|
# one 16-bit word, in hex) and WinG's version. The copy is dated the day it
|
|
# is made, so the key is made from the copy's own date.
|
|
WIN16_WING_DRIVER="S3TRIO.DRV"
|
|
WIN16_WING_PROFILE_HEAD="s3trio.drv800x600x8(0,12)v1.70.4.0"
|
|
WIN16_WING_PROFILE_TAIL="1.0.0.37"
|
|
WIN16_WING_PROFILE="ECHGECHGECHGECHGDDFFDDFFDDFFDDFFZZZZZZZZZZZZZZZZDDFGDGFGDDFFDDFG1F"
|
|
win16StageWinG() {
|
|
local file
|
|
local dosDate
|
|
local year
|
|
local month
|
|
local day
|
|
for file in WING.DLL WINGDE.DLL WINGDIB.DRV WINGPAL.WND DVA.386; do
|
|
cp "$WING_DIR/$file" "$1/c/WINDOWS/SYSTEM/$file"
|
|
done
|
|
# The driver's date as a DOS date word: years from 1980, month, day.
|
|
read -r year month day <<< "$(date -r "$1/c/WINDOWS/SYSTEM/$WIN16_WING_DRIVER" '+%Y %m %d')"
|
|
dosDate=$(printf '%04X' $((((year - 1980) << 9) | (10#$month << 5) | 10#$day)))
|
|
"$(joeyTool winIni)" "$1/c/WINDOWS/SYSTEM.INI" \
|
|
add 386Enh device "C:\\WINDOWS\\SYSTEM\\DVA.386"
|
|
"$(joeyTool winIni)" "$1/c/WINDOWS/WIN.INI" \
|
|
set WinG "$WIN16_WING_PROFILE_HEAD-$dosDate-$WIN16_WING_PROFILE_TAIL" "$WIN16_WING_PROFILE"
|
|
}
|
|
|
|
|
|
win16Start() {
|
|
local work="$1"
|
|
local assign
|
|
local envArgs=()
|
|
local soundArgs=(--set "nosound=true")
|
|
local procEnv=(XDG_CONFIG_HOME="$work/xdg" SDL_AUDIODRIVER=dummy)
|
|
local wrapper=()
|
|
shift
|
|
for assign in "$@"; do
|
|
envArgs+=(-c "SET $assign")
|
|
done
|
|
if [ "$WIN16_SOUND" = "1" ]; then
|
|
soundArgs=()
|
|
procEnv=(XDG_CONFIG_HOME="$work/xdg")
|
|
fi
|
|
[ "$WIN16_WALL" = "0" ] || wrapper=(timeout -s KILL "$WIN16_WALL")
|
|
mkdir -p "$work/xdg"
|
|
if [ "$WIN16_HEADLESS" = "1" ]; then
|
|
# A second boot in the same WORK (the save gate's) must not read the
|
|
# last display's number before the new Xvfb has written its own.
|
|
rm -f "$work/display"
|
|
Xvfb -displayfd 3 -screen 0 1024x768x24 -nolisten tcp 3> "$work/display" > /dev/null 2>&1 &
|
|
WIN16_XVFB_PID=$!
|
|
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
[ -s "$work/display" ] && break
|
|
sleep 0.5
|
|
done
|
|
WIN16_DISPLAY=":$(cat "$work/display")"
|
|
else
|
|
WIN16_DISPLAY="$DISPLAY"
|
|
fi
|
|
env "${procEnv[@]}" DISPLAY="$WIN16_DISPLAY" "${wrapper[@]}" "$DOSBOX" "${JOEY_DOSBOX_ISOLATE[@]}" \
|
|
--conf "$JOEY_REPO/scripts/dosbox-win16.conf" "${soundArgs[@]}" "${WIN16_DOSBOX_ARGS[@]}" \
|
|
-c "MOUNT C \"$work/c\"" -c "MOUNT D \"$WIN16_FOLDER\"" "${envArgs[@]}" \
|
|
-c "C:" -c 'CD \WINDOWS' -c "WIN${WIN16_ARGS:+ $WIN16_ARGS}" -c "EXIT" > "$work/dosbox.log" 2>&1 &
|
|
WIN16_GUARD=$!
|
|
if [ ${#wrapper[@]} -eq 0 ]; then
|
|
WIN16_PID=$WIN16_GUARD
|
|
else
|
|
WIN16_PID="$(joeyChild "$WIN16_GUARD")"
|
|
fi
|
|
}
|
|
|
|
|
|
win16Log() {
|
|
find "$1" -maxdepth 1 -iname joeylog.txt -print -quit 2> /dev/null
|
|
}
|
|
|
|
|
|
win16Window() {
|
|
joeyFindWindow "$WIN16_DISPLAY" "$WIN16_PID"
|
|
}
|
|
|
|
|
|
win16Stop() {
|
|
joeyStop "$WIN16_PID" "$WIN16_GUARD" "$WIN16_XVFB_PID" 2> /dev/null
|
|
WIN16_PID=""
|
|
WIN16_GUARD=""
|
|
WIN16_XVFB_PID=""
|
|
}
|