211 lines
9.8 KiB
Bash
Executable file
211 lines
9.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# make-iigs-game-disk.sh - Build ONE self-booting GS/OS volume that boots
|
|
# straight into a JoeyLib program, with the program in its own folder so
|
|
# that folder can be copied to a hard drive and run from there.
|
|
#
|
|
# Layout of the result:
|
|
# /<VOL>/System/... a GS/OS boot system stripped to what JoeyLib
|
|
# actually uses (see the drop list below)
|
|
# /<VOL>/System/Start tools/iigsStart, which sets prefix 0 and
|
|
# QuitGS's to the program below
|
|
# /<VOL>/<PROG>/ the program, its ntpplayer and its DATA tree
|
|
#
|
|
# The program folder is self-contained: JoeyLib opens "ntpplayer" and
|
|
# "DATA/..." relative to prefix 0, which GS/OS points at the launched
|
|
# program's directory. So the same folder runs from a hard drive after a
|
|
# plain Finder copy, and nothing in it names the boot volume.
|
|
#
|
|
# WHY STRIP: JoeyLib's whole toolbox usage on the IIgs is MMStartUp,
|
|
# NewHandle, DisposeHandle, HLock, GetTick, SetHeartBeat, DelHeartBeat,
|
|
# ReadBParam, SoundStartUp and SoundShutDown, plus GS/OS file calls and
|
|
# QuitGS. Those live in the Memory Manager, the Misc Tool Set and the
|
|
# Sound Tool Set, all three in ROM. Not one of the thirteen RAM tool sets
|
|
# in /System/Tools is referenced, and neither are the desk accessories,
|
|
# the control panels, ProDOS 8 or the Finder's icon database. On the stock
|
|
# disk that is over 300 KB read at every boot for nothing, and it is also
|
|
# the room the program needs in order to share the volume.
|
|
#
|
|
# Usage: scripts/make-iigs-game-disk.sh <out> <PROG> [volume] [data.2mg]
|
|
# out .po for an 800KB floppy, .2mg for anything larger
|
|
# PROG uppercase binary name, e.g. STAXI
|
|
# volume boot volume name, default = PROG
|
|
# data.2mg disk holding PROG + ntpplayer + DATA,
|
|
# default build/iigs/bin/joey.2mg
|
|
# Env: JOEY_GAME_DISK_SIZE cadius volume size, default "auto":
|
|
# 800KB when the program fits beside a stripped system on a
|
|
# floppy, otherwise 2048KB. cadius takes 800KB, 1440KB, 2048KB
|
|
# and 32MB, nothing else. 800KB reuses the stock system image
|
|
# in place; every other size builds a fresh volume and copies
|
|
# the ProDOS boot blocks onto it. NOTE: MAME's emulated 3.5
|
|
# drive is 800KB GCR and REJECTS a 1440KB image, so anything
|
|
# larger has to boot from a CFFA2 card:
|
|
# -sl7 cffa2 -hard1 <image>.
|
|
# Requires: ./install.sh install iigs (the toolchain and the GS/OS disk).
|
|
set -euo pipefail
|
|
|
|
out=${1:?usage: make-iigs-game-disk.sh <out> <PROG> [volume] [data.2mg]}
|
|
prog=${2:?usage: make-iigs-game-disk.sh <out> <PROG> [volume] [data.2mg]}
|
|
vol=${3:-$prog}
|
|
|
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
source "$repo/scripts/lib/joeyEnv.sh"
|
|
joeyNeed LLVM816_ROOT toolchain:iigs
|
|
joeyNeed CADIUS cadius
|
|
joeyNeed IIGS_CLANG_BUILD toolchain:iigs
|
|
joeyNeed GSOS_SYSTEM_DISK iigsFirmware
|
|
data=${4:-$repo/build/iigs/bin/joey.2mg}
|
|
sysDisk=$GSOS_SYSTEM_DISK
|
|
size=${JOEY_GAME_DISK_SIZE:-auto}
|
|
|
|
[ -x "$CADIUS" ] || { echo "make-iigs-game-disk.sh: cadius not found at $CADIUS" >&2; exit 2; }
|
|
[ -f "$sysDisk" ] || { echo "make-iigs-game-disk.sh: missing $sysDisk" >&2; exit 2; }
|
|
[ -f "$data" ] || { echo "make-iigs-game-disk.sh: missing data disk $data" >&2; exit 2; }
|
|
|
|
work=$(mktemp -d -t joeylib-gamedisk.XXXXXX)
|
|
trap 'rm -rf "$work"' EXIT
|
|
|
|
# Volume name of an image, from the one whole line of the form "/NAME/".
|
|
# An unanchored match also hits the absolute image path in cadius's header.
|
|
volumeName() {
|
|
"$CADIUS" CATALOG "$1" 2>/dev/null | grep -oE '^/[A-Za-z0-9._]+/$' | head -1 | tr -d '/'
|
|
}
|
|
freeBlocks() { "$CADIUS" CATALOG "$1" 2>/dev/null | grep -oE 'Free : [0-9]+' | grep -oE '[0-9]+'; }
|
|
totalBlocks() { "$CADIUS" CATALOG "$1" 2>/dev/null | grep -oE 'Block : [0-9]+' | grep -oE '[0-9]+'; }
|
|
|
|
sysVol=$(volumeName "$sysDisk")
|
|
[ -n "$sysVol" ] || { echo "make-iigs-game-disk.sh: cannot read system volume name" >&2; exit 1; }
|
|
dataVol=$(volumeName "$data")
|
|
[ -n "$dataVol" ] || { echo "make-iigs-game-disk.sh: cannot read data volume name" >&2; exit 1; }
|
|
|
|
# ----- the stripped system, staged on a copy of the stock disk -----
|
|
staged="$work/system.po"
|
|
cp "$sysDisk" "$staged"
|
|
|
|
drop=(
|
|
"BASIC.System" # ProDOS 8 BASIC
|
|
"Finder.Data"
|
|
"Icons/FType.Apple" # Finder file-type database
|
|
"System/Start" # the Finder itself
|
|
"System/P8" # the ProDOS 8 kernel
|
|
"System/Finder.Data"
|
|
"System/CDevs/CDev.Data"
|
|
"System/CDevs/Printer"
|
|
"System/CDevs/Time"
|
|
"System/Desk.Accs/ControlPanel"
|
|
"System/Drivers/AppleDisk5.25"
|
|
"System/Drivers/Finder.Data"
|
|
"System/Fonts/Font.Lists"
|
|
"System/Tools/Tool014" # Window Manager
|
|
"System/Tools/Tool015" # Menu Manager
|
|
"System/Tools/Tool016" # Control Manager
|
|
"System/Tools/Tool018" # QuickDraw II Auxiliary
|
|
"System/Tools/Tool019" # Print Manager
|
|
"System/Tools/Tool020" # LineEdit
|
|
"System/Tools/Tool021" # Dialog Manager
|
|
"System/Tools/Tool022" # Scrap Manager
|
|
"System/Tools/Tool023" # Standard File
|
|
"System/Tools/Tool025" # Note Synthesizer
|
|
"System/Tools/Tool027" # Font Manager
|
|
"System/Tools/Tool028" # List Manager
|
|
"System/Tools/Tool034" # TextEdit
|
|
)
|
|
# What canNOT come off, each one measured on a CFFA2 boot 2026-09-22:
|
|
# System/Error.Msg GS/OS halts with "Error loading
|
|
# ERROR.MSG file. Error=$0046".
|
|
# System.Setup/Tool.Setup + TS3 dropping both hangs in the ROM
|
|
# banner before GS/OS starts.
|
|
# System.Setup/TS2 the ROM 01 half of the same tool
|
|
# patches. Kept on purpose: plenty of
|
|
# ROM 01 machines are still out there
|
|
# and a game disk has to boot on them.
|
|
# It costs 72 blocks.
|
|
# System.Setup/Resource.Mgr,
|
|
# Drivers/Console.Driver,
|
|
# FSTs/Char.FST dropping all three hangs the same way.
|
|
# The two groups were bisected separately and each one is load-bearing, so
|
|
# the list above is the floor. Boot to the program went from 43.4 s to
|
|
# 21.8 s in MAME, and the freed space is what lets the program share the
|
|
# volume.
|
|
for f in "${drop[@]}"; do
|
|
"$CADIUS" DELETEFILE "$staged" "/$sysVol/$f" >/dev/null 2>&1 || true
|
|
done
|
|
|
|
# How many blocks the program folder will want, and what a stripped
|
|
# floppy has left after the strip above. 24 blocks of slack covers the
|
|
# launcher and the directory blocks the new subfolders need.
|
|
progBlocks=$(( $(totalBlocks "$data") - $(freeBlocks "$data") ))
|
|
floppyFree=$(freeBlocks "$staged")
|
|
if [ "$size" = "auto" ]; then
|
|
if [ $(( progBlocks + 24 )) -le "$floppyFree" ]; then
|
|
size=800KB
|
|
else
|
|
size=2048KB
|
|
fi
|
|
fi
|
|
|
|
# ----- the output volume -----
|
|
# Always a freshly created volume, whatever the size: that way cadius sets
|
|
# up the image container the output's extension calls for (.po raw, .2mg
|
|
# with its 64-byte header), and one code path covers every size. A
|
|
# cadius-created volume has no boot blocks, so blocks 0 and 1 are copied
|
|
# from the stock image. That code reads the volume directory and loads
|
|
# /PRODOS by name, so it does not care how large the volume is or what it
|
|
# is called.
|
|
"$CADIUS" CREATEVOLUME "$out" "$vol" "$size" >/dev/null
|
|
mkdir -p "$work/sys"
|
|
"$CADIUS" EXTRACTVOLUME "$staged" "$work/sys" >/dev/null
|
|
sysSrc="$work/sys/$sysVol"
|
|
[ -d "$sysSrc" ] || { echo "make-iigs-game-disk.sh: extraction produced no /$sysVol" >&2; exit 1; }
|
|
# ADDFOLDER copies a directory's CONTENTS into the named ProDOS folder,
|
|
# keeping nested subfolders, and reads back the file types that
|
|
# EXTRACTVOLUME recorded in each _FileInformation.txt. One call moves the
|
|
# whole system across.
|
|
"$CADIUS" ADDFOLDER "$out" "/$vol" "$sysSrc" >/dev/null
|
|
srcOff=0; dstOff=0
|
|
[ "$(head -c 4 "$staged")" = "2IMG" ] && srcOff=64
|
|
[ "$(head -c 4 "$out")" = "2IMG" ] && dstOff=64
|
|
dd if="$staged" of="$work/boot.blk" bs=1 skip=$srcOff count=1024 status=none
|
|
dd if="$work/boot.blk" of="$out" bs=1 seek=$dstOff count=1024 conv=notrunc status=none
|
|
|
|
# ----- the launcher -----
|
|
"$IIGS_CLANG_BUILD" \
|
|
-DJOEY_START_PATH="\"/$vol/$prog/$prog\"" \
|
|
-o "$work/Start#B30000" \
|
|
"$repo/tools/iigsStart/iigsStart.c" \
|
|
"$repo/tools/iigsStart/iigsStartQuit.s" >/dev/null
|
|
"$CADIUS" ADDFILE "$out" "/$vol/System" "$work/Start#B30000" >/dev/null
|
|
|
|
# ----- the program folder, lifted whole off the data disk -----
|
|
mkdir -p "$work/game"
|
|
"$CADIUS" EXTRACTVOLUME "$data" "$work/game" >/dev/null
|
|
src="$work/game/$dataVol"
|
|
[ -d "$src" ] || { echo "make-iigs-game-disk.sh: extraction produced no /$dataVol" >&2; exit 1; }
|
|
|
|
free=$(freeBlocks "$out")
|
|
if [ "$free" -lt "$progBlocks" ]; then
|
|
echo "make-iigs-game-disk.sh: /$prog needs $progBlocks blocks and a $size volume has only $free free." >&2
|
|
echo " Build a larger one (JOEY_GAME_DISK_SIZE=2048KB) and boot it from a CFFA2 card:" >&2
|
|
echo " mame apple2gs -sl7 cffa2 -hard1 <image>" >&2
|
|
exit 1
|
|
fi
|
|
"$CADIUS" CREATEFOLDER "$out" "/$vol/$prog" >/dev/null
|
|
"$CADIUS" ADDFOLDER "$out" "/$vol/$prog" "$src" >/dev/null
|
|
|
|
# Never trust an exit code here: cadius reports most failures on stdout.
|
|
# Assert the three things the disk is worthless without.
|
|
catalog=$("$CADIUS" CATALOG "$out" 2>/dev/null)
|
|
for want in "ProDOS" "Start" "$prog"; do
|
|
echo "$catalog" | grep -qE "^ +$want +" || {
|
|
echo "make-iigs-game-disk.sh: $want is NOT on $out" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
if [ "$size" = "800KB" ]; then
|
|
how="bootable 3.5\" floppy"
|
|
else
|
|
how="boot with: mame apple2gs -sl7 cffa2 -hard1 $(basename "$out")"
|
|
fi
|
|
echo "iigs-game-disk: $out"
|
|
echo " /$vol boots into /$vol/$prog/$prog ($size, $(freeBlocks "$out") blocks free)"
|
|
echo " $how"
|