Skip to content

ci: let Hatari close TT ACSI image cleanly #305

ci: let Hatari close TT ACSI image cleanly

ci: let Hatari close TT ACSI image cleanly #305

Workflow file for this run

name: qualification
on:
push:
pull_request:
workflow_dispatch:
jobs:
infrastructure:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build atari-runtime image
run: docker build -t atari-runtime:m2 -f Containerfile .
- name: Runtime metadata
run: docker run --rm atari-runtime:m2 tools/runtime-info.sh
- name: Hatari CLI capabilities
run: docker run --rm atari-runtime:m2 sh -c 'hatari --help 2>&1 | tee /tmp/hatari-help.txt; grep -Ei "auto|run|program|gemdos|harddrive" /tmp/hatari-help.txt || true'
- name: Hatari + EmuTOS smoke
run: docker run --rm atari-runtime:m2 tools/smoke.sh
- name: Verify M2 helper
run: docker run --rm atari-runtime:m2 sh -n tools/qualify-m2.sh
m2-emulator:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
profile: [st, ste, tt]
steps:
- uses: actions/checkout@v4
- name: Build atari-runtime image
run: docker build -t atari-runtime:m2 -f Containerfile .
- name: Install floppy image tools
run: sudo apt-get update && sudo apt-get install -y dosfstools mtools
- name: Download canonical atari-dev M2 artifact
env:
GH_TOKEN: ${{ github.token }}
run: |
mkdir -p artifacts
curl --fail --location \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
-o /tmp/atari-dev-m2.zip \
https://api.github.com/repos/Ploos-AS/atari-dev/actions/artifacts/10600359653/zip
unzip -q /tmp/atari-dev-m2.zip -d artifacts
test -s artifacts/HELLO.TOS
test -s artifacts/MINIMAL.PRG
sha256sum artifacts/HELLO.TOS artifacts/MINIMAL.PRG
- name: Prove EmuTOS reaches video boot
run: |
rm -f artifacts/xvfb-${{ matrix.profile }}.log artifacts/boot-hatari-${{ matrix.profile }}.log
docker run --rm \
-v "$PWD/artifacts:/runtime/artifacts" atari-runtime:m2 \
sh -c 'set -eu; Xvfb :99 -screen 0 1024x768x24 >/tmp/xvfb.log 2>&1 & x=$!; export DISPLAY=:99; sleep 1; timeout --signal=TERM --kill-after=2s 8s hatari --machine "${{ matrix.profile }}" --memsize ${{ matrix.profile == 'tt' && '4' || '1' }} --tos /runtime/roms/emutos.img >/tmp/hatari.log 2>&1 || rc=$?; rc=${rc:-0}; cp /tmp/xvfb.log /runtime/artifacts/xvfb-${{ matrix.profile }}.log; cp /tmp/hatari.log /runtime/artifacts/boot-hatari-${{ matrix.profile }}.log; kill "$x" 2>/dev/null || true; wait "$x" 2>/dev/null || true; case "$rc" in 0|124|137) exit 0 ;; *) exit "$rc" ;; esac'
cat artifacts/xvfb-${{ matrix.profile }}.log || true
cat artifacts/boot-hatari-${{ matrix.profile }}.log || true
test -e artifacts/boot-hatari-${{ matrix.profile }}.log
if grep -Eqi 'fatal|cannot open.*tos|no tos|failed to.*tos' artifacts/boot-hatari-${{ matrix.profile }}.log; then
echo "ERROR: Hatari reported a fatal TOS boot error" >&2
exit 1
fi
printf 'EmuTOS boot window %s: PASS\n' "${{ matrix.profile }}"
- name: Execute canonical probe from GEMDOS drive
run: |
rm -f artifacts/MINPASS.TXT
test -s artifacts/MINIMAL.PRG
set +e
docker run --rm \
-v "$PWD/artifacts:/runtime/artifacts" atari-runtime:m2 \
sh -c 'set +e; Xvfb :99 -screen 0 1024x768x24 >/tmp/xvfb.log 2>&1 & x=$!; export DISPLAY=:99; sleep 1; timeout --signal=TERM --kill-after=2s 45s hatari --machine "${{ matrix.profile }}" --memsize ${{ matrix.profile == 'tt' && '4' || '1' }} --tos /runtime/roms/emutos.img --harddrive /runtime/artifacts --gemdos-drive C --auto MINIMAL.PRG --trace os_base,gemdos --trace-file /runtime/artifacts/hatari-boot-trace-${{ matrix.profile }}.log --log-level debug --log-file /runtime/artifacts/hatari-boot-debug-${{ matrix.profile }}.log --run-vbls 1500 >/runtime/artifacts/hatari-stdout-${{ matrix.profile }}.log 2>/runtime/artifacts/hatari-stderr-${{ matrix.profile }}.log; rc=$?; kill "$x" 2>/dev/null || true; wait "$x" 2>/dev/null || true; exit "$rc"'
rc=$?
set -e
cat "artifacts/hatari-stderr-${{ matrix.profile }}.log" || true
cat "artifacts/hatari-stdout-${{ matrix.profile }}.log" || true
grep -Ei 'MINIMAL|PRG|Pexec|Fcreate|Fwrite|Fclose|GEMDOS|boot' "artifacts/hatari-boot-trace-${{ matrix.profile }}.log" 2>/dev/null | tail -n 300 || true
if [ "$rc" -ne 0 ] && [ ! -s artifacts/MINPASS.TXT ]; then
echo "ERROR: Hatari ended without guest marker (rc=$rc)" >&2
exit 1
fi
test -s artifacts/MINPASS.TXT
grep -F 'Ploos-AS atari-runtime minimal GEMDOS execution PASS' artifacts/MINPASS.TXT
printf 'Canonical GEMDOS execution probe %s: PASS\n' "${{ matrix.profile }}"
- name: Discover FreeMiNT TT snapshot payload
if: matrix.profile == 'tt'
run: |
set -eux
curl --fail --location --retry 3 https://atari.joska.no/snapshots/freemint/bootable/freemint-latest-aranym.zip -o /tmp/freemint-tt.zip
unzip -l /tmp/freemint-tt.zip | tee artifacts/freemint-tt-snapshot-inventory.txt
unzip -q /tmp/freemint-tt.zip -d artifacts/freemint-tt
find artifacts/freemint-tt -type f \( -iname 'mint*.prg' -o -iname 'mint.cnf' -o -iname 'xaaes.km' \) -print | tee artifacts/freemint-tt-candidates.txt
test -s artifacts/freemint-tt-candidates.txt
- name: Build FreeMiNT 68030 kernel for TT
if: matrix.profile == 'tt'
run: |
set -eux
sudo apt-get update
sudo apt-get install -y make bzip2 wget build-essential libc6-dev
git clone --depth 1 https://github.com/freemint/freemint.git /tmp/freemint-src
cd /tmp/freemint-src
export CROSS_TOOL=m68k-atari-mint
export PATH="/usr/${CROSS_TOOL}/bin:$PATH"
SYSROOT_DIR=/tmp/mint-toolchain ./.scripts/install-freemint.sh binutils gcc mintbin mintlib libcmini
compiler="$(find /tmp/mint-toolchain \( -type f -o -type l \) -name m68k-atari-mint-gcc -print -quit)"
if [ -z "$compiler" ]; then
echo "ERROR: m68k-atari-mint-gcc not found after toolchain install" >&2
find /tmp/mint-toolchain -maxdepth 4 \( -type f -o -type l \) -print | sort | tail -n 300
exit 1
fi
toolbin="$(dirname "$compiler")"
export PATH="$toolbin:$PATH"
mint_include="$(find /tmp/mint-toolchain -type f -path '*/include/mint/mintbind.h' -printf '%h\n' | sed 's#/mint$##' | head -n1)"
test -n "$mint_include"
echo "MiNT toolchain compiler: $compiler"
echo "MiNT include root: $mint_include"
"$compiler" --version
test -s "$mint_include/mint/mintbind.h"
cd sys
make 030 M68K_ATARI_MINT_CFLAGS="-isystem $mint_include"
test -s .compile_030/mint030.prg
cp .compile_030/mint030.prg "$GITHUB_WORKSPACE/artifacts/mint030.prg"
file "$GITHUB_WORKSPACE/artifacts/mint030.prg"
sha256sum "$GITHUB_WORKSPACE/artifacts/mint030.prg"
{
echo 'milestone=M4.3'
echo 'profile=tt-freemint'
echo 'emulator=Hatari'
echo 'machine=tt'
echo 'kernel=mint030.prg'
echo 'kernel_build=PASS'
} | tee "$GITHUB_WORKSPACE/artifacts/hatari-m4-3-tt-freemint-build.txt"
- name: Boot FreeMiNT 68030 on Hatari TT
if: matrix.profile == 'tt'
run: |
set -eux
# Use a real FAT12 Atari disk image. Once FreeMiNT takes over,
# Hatari GEMDOS host-directory emulation is no longer a valid
# filesystem qualification path for the kernel.
rm -f artifacts/tt-freemint-hd.img /tmp/tt-freemint-part.img
./tools/make-atari-hd-image.sh 16 artifacts/tt-freemint-hd.img PLOOSTT
dd if=artifacts/tt-freemint-hd.img of=/tmp/tt-freemint-part.img bs=512 skip=1 status=none
mmd -i /tmp/tt-freemint-part.img ::/AUTO
mmd -i /tmp/tt-freemint-part.img ::/MINT
mmd -i /tmp/tt-freemint-part.img ::/MINT/1-19-CUR
mcopy -i /tmp/tt-freemint-part.img artifacts/mint030.prg ::/AUTO/MINT030.PRG
cat > /tmp/mint.cnf <<'EOF'
# Ploos-AS atari-runtime M4.3 minimal TT FreeMiNT qualification.
# The EXEC line is intentionally guest-side and runs only after
# the FreeMiNT kernel has mounted its own FAT filesystem.
echo PLOOS-M4.3-BEFORE-EXEC
exec u:/c/PLOOSPRG.TOS --ploos-probe
echo PLOOS-M4.3-AFTER-EXEC
EOF
cat > /tmp/ploosprobe.S <<'EOF'
.text
.globl _start
_start:
pea msg_enter
move.w #9,-(sp)
trap #1
addq.l #6,sp
clr.w -(sp)
pea filename
move.w #0x3c,-(sp)
trap #1
addq.l #8,sp
tst.l d0
bmi.s fail
move.w d0,d2
pea msg_created
move.w #9,-(sp)
trap #1
addq.l #6,sp
pea marker
move.l #marker_len,-(sp)
move.w d2,-(sp)
move.w #0x40,-(sp)
trap #1
lea 12(sp),sp
cmp.l #marker_len,d0
bne.s fail
pea msg_written
move.w #9,-(sp)
trap #1
addq.l #6,sp
move.w d2,-(sp)
move.w #0x3e,-(sp)
trap #1
addq.l #4,sp
tst.l d0
bmi.s fail
/* FreeMiNT Sync(): flush guest filesystem state to media. */
move.w #0x0150,-(sp)
trap #1
addq.l #2,sp
/* FreeMiNT Shutdown(SHUT_NOPOWEROFF): orderly unmount. */
move.l #3,-(sp)
move.w #0x0151,-(sp)
trap #1
addq.l #6,sp
clr.w -(sp)
trap #1
fail:
move.w #1,-(sp)
move.w #0x4c,-(sp)
trap #1
.data
msg_enter:
.asciz "PLOOS-PROBE-ENTER\\r\\n"
msg_created:
.asciz "PLOOS-PROBE-CREATED\\r\\n"
msg_written:
.asciz "PLOOS-PROBE-WROTE\\r\\n"
filename:
.asciz "u:/c/PLOOSPASS.TXT"
marker:
.ascii "Ploos-AS atari-runtime FreeMiNT Hatari TT execution PASS\\r\\n"
.equ marker_len, .-marker
EOF
assembler="$(find /tmp/mint-toolchain \( -type f -o -type l \) -name m68k-atari-mint-as -print -quit)"
linker="$(find /tmp/mint-toolchain \( -type f -o -type l \) -name m68k-atari-mint-ld -print -quit)"
test -n "$assembler"
test -n "$linker"
"$assembler" -m68030 -o /tmp/ploosprobe.o /tmp/ploosprobe.S
"$linker" -s -e _start -o /tmp/PLOOSPRG.TOS /tmp/ploosprobe.o
test -s /tmp/PLOOSPRG.TOS
file /tmp/PLOOSPRG.TOS
mcopy -i /tmp/tt-freemint-part.img /tmp/mint.cnf ::/MINT/1-19-CUR/MINT.CNF
mcopy -i /tmp/tt-freemint-part.img /tmp/PLOOSPRG.TOS ::/PLOOSPRG.TOS
# Keep the libc-free probe as the FreeMiNT shell startup binary.
mcopy -i /tmp/tt-freemint-part.img /tmp/PLOOSPRG.TOS ::/MINT/1-19-CUR/sh.tos
mdir -i /tmp/tt-freemint-part.img -/ :: | tee artifacts/hatari-m4-3-tt-freemint-disk.txt
dd if=/tmp/tt-freemint-part.img of=artifacts/tt-freemint-hd.img bs=512 seek=1 conv=notrunc status=none
set +e
docker run --rm \
-v "$PWD/artifacts:/runtime/artifacts" atari-runtime:m2 \
sh -c 'set +e; Xvfb :99 -screen 0 1024x768x24 >/tmp/xvfb.log 2>&1 & x=$!; export DISPLAY=:99; sleep 1; timeout --signal=TERM --kill-after=3s 90s hatari --machine tt --memsize 4 --tos /runtime/roms/emutos.img --acsi /runtime/artifacts/tt-freemint-hd.img --fastfdc yes --run-vbls 3600 --trace os_base,gemdos --trace-file /runtime/artifacts/hatari-m4-3-tt-freemint-trace.log --log-level debug --log-file /runtime/artifacts/hatari-m4-3-tt-freemint-debug.log >/runtime/artifacts/hatari-m4-3-tt-freemint-stdout.log 2>/runtime/artifacts/hatari-m4-3-tt-freemint-stderr.log; rc=$?; cp /tmp/xvfb.log /runtime/artifacts/hatari-m4-3-tt-freemint-xvfb.log; kill "$x" 2>/dev/null || true; wait "$x" 2>/dev/null || true; exit "$rc"'
rc=$?
set -e
cat artifacts/hatari-m4-3-tt-freemint-stderr.log || true
grep -Ei 'FreeMiNT|MiNT|PLOOS|panic|fatal|error' artifacts/hatari-m4-3-tt-freemint-{stdout,stderr,debug,trace}.log 2>/dev/null | tail -n 400 | tee artifacts/hatari-m4-3-tt-freemint-visible.log || true
# The probe writes its evidence to the FAT16 hard disk (C:).
# Partition 1 starts at LBA 1, so inspect that partition directly.
sync artifacts/tt-freemint-hd.img || sync
sleep 2
dd if=artifacts/tt-freemint-hd.img of=/tmp/tt-freemint-part.img bs=512 skip=1 status=none
mdir -i /tmp/tt-freemint-part.img :: | tee artifacts/hatari-m4-3-tt-freemint-postboot-disk.txt
rm -f /tmp/PLOOSPASS.TXT
mcopy -i /tmp/tt-freemint-part.img ::/PLOOSPASS.TXT /tmp/PLOOSPASS.TXT
test -s /tmp/PLOOSPASS.TXT
tr -d '\r' < /tmp/PLOOSPASS.TXT | grep -Fxq 'Ploos-AS atari-runtime FreeMiNT Hatari TT execution PASS'
cp /tmp/PLOOSPASS.TXT artifacts/hatari-m4-3-tt-freemint-PLOOSPASS.TXT
grep -Fq 'This is FreeMiNT v1.19.0-ALPHA' artifacts/hatari-m4-3-tt-freemint-stdout.log
{
echo 'milestone=M4.3'
echo 'profile=tt-freemint'
echo 'emulator=Hatari'
echo 'machine=tt'
echo 'cpu=68030'
echo 'boot_media=FAT16-ACSI'
echo 'evidence_media=FAT16-ACSI'
echo 'kernel=mint030.prg'
echo 'kernel_build=PASS'
echo 'kernel_boot=PASS'
echo 'guest_exec=PASS'
echo 'guest_marker=PASS'
echo "hatari_exit=$rc"
} | tee artifacts/hatari-m4-3-tt-freemint-result.txt
- name: Verify Atari-side GEMDOS execution evidence
run: |
test -s artifacts/MINPASS.TXT
grep -F 'Ploos-AS atari-runtime minimal GEMDOS execution PASS' artifacts/MINPASS.TXT
file artifacts/MINIMAL.PRG