From f7be843ed9f5980c84abb22d1eeed849bba19571 Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Wed, 2 Sep 2026 23:36:05 -0400 Subject: [PATCH] Lunar Porter takes any controller, not the first one A controller does not always arrive on pad nought. The front end hands out the numbers the host gave it, so a game that reads only the first one works on the machine it was written on and silently does nothing on the next - which is the shape of "the pad is detected, Pad shows it, and the game ignores it". Four reads and three ORs. One person flies this and which socket they plugged into is not a thing they should have to know. Presence is any of the four bits rather than the low one, for the same reason. The manifest's pad column takes several fixtures now, comma separated, and they fill the pads in turn. So cosmosLanderPadOne holds nothing on pad nought and flies the whole landing on pad one - a test that fails on the version of this program that shipped an hour ago. Also confirmed while looking: raylib 6 does refresh which gamepads are ready every frame in PollInputEvents, so a hot-plugged pad should be seen. Whatever is stopping that is above us and worth a separate look. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW --- Programs/CosmOS/Apps/Lander.asm | 32 +++++++++++++++++++++----- SplitBit Test Manual.md | 6 +++-- Tests/expected/cosmosCrossDisk.out | 2 +- Tests/expected/cosmosDrives.out | 2 +- Tests/expected/cosmosFault.out | 2 +- Tests/expected/cosmosFlip.out | 2 +- Tests/expected/cosmosGrid.out | 2 +- Tests/expected/cosmosLanderPadOne.out | 8 +++++++ Tests/expected/cosmosMonitor.out | 2 +- Tests/expected/cosmosMonitorRun.out | 2 +- Tests/expected/cosmosRun.out | 2 +- Tests/expected/cosmosSlowDisk.out | 2 +- Tests/expected/cosmosSprite.out | 2 +- Tests/input/landerIdle.pad | Bin 0 -> 6000 bytes Tests/manifest | 10 ++++++++ Tests/run.sh | 18 ++++++++++++--- 16 files changed, 73 insertions(+), 21 deletions(-) create mode 100644 Tests/expected/cosmosLanderPadOne.out create mode 100644 Tests/input/landerIdle.pad diff --git a/Programs/CosmOS/Apps/Lander.asm b/Programs/CosmOS/Apps/Lander.asm index c62ef49..5bfbeca 100644 --- a/Programs/CosmOS/Apps/Lander.asm +++ b/Programs/CosmOS/Apps/Lander.asm @@ -61,14 +61,13 @@ start: ; ---- Is there a controller ---- ; - ; Asked once. If there is, the console's arrow keys are ignored: under a window the same - ; keypress reaches BOTH - the pad as a level and the console as a byte - and a thruster that - ; fired twice for one press would be a mystery to anybody tuning it. + ; Asked once, and about all four. If there is one anywhere, the console's arrow keys are + ; ignored: under a window the same keypress reaches BOTH - the pad as a level and the + ; console as a byte - and a thruster that fired twice for one press would be a mystery to + ; anybody tuning it. INA 0x64 - INIB 0x01 - AND SETD.1 HasPad - STQ.1 + STA.1 ; ANY bit, so any of the four counts as having one. everyFrame: CALL waitFrame @@ -331,10 +330,31 @@ putLander: ; One read, every button at once, and it does not go away when it is looked at. THIS IS THE ; THING THE CONSOLE CANNOT DO: a key that is down and staying down sends nothing, so a ; thruster driven by the console can only be pumped and never leaned on. +; ---- ANY of the four, not pad nought ---- +; +; One person flies this, and which socket they plugged into is not a thing they should have +; to know. A controller does not always land on nought - the front end hands out the numbers +; the host gave it - so a game that reads only the first one works on some machines and +; silently does nothing on others, which is the worst of both. +; +; Four reads and three ORs. A port is an immediate byte inside the instruction that names it, +; so it cannot be computed and the four are written out. readPad: INA 0x60 SETD.1 Held STA.1 + INA 0x61 + LDB.1 + OR + STQ.1 + INA 0x62 + LDB.1 + OR + STQ.1 + INA 0x63 + LDB.1 + OR + STQ.1 RET ; ---- The console, which is still worth reading ---- diff --git a/SplitBit Test Manual.md b/SplitBit Test Manual.md index bc9898c..5e483f6 100644 --- a/SplitBit Test Manual.md +++ b/SplitBit Test Manual.md @@ -101,7 +101,7 @@ from `make`, not from here. ### 1. Recorded output `Tests/run.sh` assembles each program named in `Tests/manifest`, runs it, and compares -everything it printed against a file in `Tests/expected`. 210 tests, of which 148 run, 35 +everything it printed against a file in `Tests/expected`. 211 tests, of which 149 run, 35 only assemble, 16 are expected to fail to assemble, and 11 boot from ROM with no image given at all. @@ -405,7 +405,9 @@ test the window: Voyager's own key queue is still out of reach, and so is anythi presenting frames. It tests the console, which is where the logic is. **pad** names a file in `Tests/input` to be held on a controller, one byte a frame, each -byte the buttons held during it. It exists for the same reason as **keys** and matters more: +byte the buttons held during it. **Several, comma separated**, fill the pads in turn - so a +first fixture holding nothing and a second that does something puts the flying on pad one, +which is how a game that reads only the first controller gets caught. It exists for the same reason as **keys** and matters more: a pad reports what is *held*, and a suite has no hands. **A byte a frame, not a byte a read.** A pad is a level, so a game that asks twice in one diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index d60d8cd..2779100 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -25,7 +25,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index d90c9aa..23c1db4 100644 --- a/Tests/expected/cosmosDrives.out +++ b/Tests/expected/cosmosDrives.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index f33f9ba..3e8753e 100644 --- a/Tests/expected/cosmosFault.out +++ b/Tests/expected/cosmosFault.out @@ -32,7 +32,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index 6946886..21227b9 100644 --- a/Tests/expected/cosmosFlip.out +++ b/Tests/expected/cosmosFlip.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index 95c3e3a..6806e0a 100644 --- a/Tests/expected/cosmosGrid.out +++ b/Tests/expected/cosmosGrid.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosLanderPadOne.out b/Tests/expected/cosmosLanderPadOne.out new file mode 100644 index 0000000..6cf07b0 --- /dev/null +++ b/Tests/expected/cosmosLanderPadOne.out @@ -0,0 +1,8 @@ +CosmOS +> Lander +Down safely. +finished +> exit +halted +Execution halted. +[exit 0] diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index f184536..f1fe9f9 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -108,7 +108,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index ac7eb47..155251e 100644 --- a/Tests/expected/cosmosMonitorRun.out +++ b/Tests/expected/cosmosMonitorRun.out @@ -27,7 +27,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 0fb1e29..5fb6079 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index 540626d..29d96a6 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -13,7 +13,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index 1886a6f..f00579b 100644 --- a/Tests/expected/cosmosSprite.out +++ b/Tests/expected/cosmosSprite.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 1553 +Lander.sbx 1571 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/input/landerIdle.pad b/Tests/input/landerIdle.pad new file mode 100644 index 0000000000000000000000000000000000000000..71bc1246781ceca0fde1d7d10d7ad2af594d67a2 GIT binary patch literal 6000 zcmZP=1*0J_8UmvsFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiRF0;3@?8Umvs PFd71*Aut*O6o&u+7jOUp literal 0 HcmV?d00001 diff --git a/Tests/manifest b/Tests/manifest index 643e1f2..433589d 100644 --- a/Tests/manifest +++ b/Tests/manifest @@ -1023,6 +1023,16 @@ padTest | testPrograms/padTest.asm | run | - # is the whole of the game's difficulty in two fixtures. cosmosLanderCrash | CosmOS/Source/cosmos.asm | run | - | 60000000 | disks/cosmos.img | lander.keys | landerFall.pad cosmosLanderSoft | CosmOS/Source/cosmos.asm | run | - | 60000000 | disks/cosmos.img | lander.keys | landerSoft.pad +# ---- And the same landing, flown from the second controller ---- +# +# Two pad fixtures, comma separated: the first holds nothing and the second is the recording +# that lands. So the flying is done entirely on pad ONE, and a game that read only pad nought +# would sit there and crash. +# +# Which is not hypothetical - it is what this did. A controller does not always arrive on +# nought; the front end hands out the numbers the host gave it, so reading only the first +# works on the machine it was written on and silently does nothing on the next one. +cosmosLanderPadOne | CosmOS/Source/cosmos.asm | run | - | 60000000 | disks/cosmos.img | lander.keys | landerIdle.pad,landerSoft.pad # ---- And the thing that says whether a controller is being seen at all ---- # # Three states look identical from inside a game that is not responding: a pad the front end diff --git a/Tests/run.sh b/Tests/run.sh index 45beb61..f46cc8c 100755 --- a/Tests/run.sh +++ b/Tests/run.sh @@ -255,12 +255,24 @@ while IFS='|' read -r name src mode stdin limit disk keys pad; do # what makes a device that only a person could otherwise exercise into one the # recordings cover - the same argument as the keyboard fixture above. if [ "$pad" != "-" ]; then - if [ ! -f "$INPUT/$pad" ]; then + # ---- Commas for more than one ---- + # + # --pad fills the pads in turn, so naming two puts the second on pad one. + # That is worth being able to say: a game that reads only the first pad works + # on the machine it was written on and silently does nothing on one where the + # host handed out a different number. + padMissing="" + IFS=',' read -r -a padList <<< "$pad" + for one in "${padList[@]}"; do + one="$(trim "$one")" + if [ ! -f "$INPUT/$one" ]; then padMissing="$one"; break; fi + EMUARGS+=(--pad "$INPUT/$one") + done + if [ -n "$padMissing" ]; then FAIL=$((FAIL + 1)); FAILED_NAMES+=("$name") - report "FAIL" "$name" "missing pad fixture $pad" + report "FAIL" "$name" "missing pad fixture $padMissing" continue fi - EMUARGS+=(--pad "$INPUT/$pad") fi [ "$limit" != "-" ] && EMUARGS+=(--cycles "$limit") # A disk starts fresh for every test, so a test cannot pass because of what a