From 7e82b64d471865129aeff7b2f3336d8adefb2653 Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Fri, 4 Sep 2026 11:23:54 -0400 Subject: [PATCH] A dock that slides into line, and settles squarely on the port Two things, and the second is the one that was actually wrong. The lander is slid into place at half a pixel a frame rather than put there. A dock is allowed eight pixels out in either direction, so snapping moved it a whole tile in a single frame - a jump, at the very moment the player was being told they had been careful. The worst gap closes in about half a second, which reads as the two of them settling together. And it settles SQUARELY now. It used to come to rest four pixels out however carefully it was flown, because the lander is drawn from half a screen less half a tile - which is what centres an eight pixel lander on the middle - while the station was drawn from half a screen exactly, so its left edge sat where the lander's centre was. Both come off the same origin now, and a gap of nothing puts one exactly above the other. stationGap is factored out along the way. Three callers wanted it: the one that draws the station, the one that decides whether it can be docked with, and now the one that slides the lander in under it. The sideways ease goes through that wrapped gap rather than the raw positions, because a dock made either side of the moon's seam has a raw difference of most of a moon. ---- And the fixtures moved to frame 400 ---- A dock waits to be told its message has been read, and reading a state file costs a disk read, so a placed run starts a good deal later than a plain one. The acknowledgement was at frame 100 and stopped working the day the state file arrived: the program had not reached the dock yet and the press went by unheard, which shows up as every sprite missing and reads like a drawing bug. Everything after it is sampled well clear of both ends. Two checks, each seen to fail on its own break - the alignment settles at 4,-8 without the shared origin, and the slide reads 0,-8 the whole way without the easing. Which of the two axes each one actually watches is written down beside them, because it is not the one you would guess. --- Programs/CosmOS/Apps/Lander.asm | 216 ++++++++++++++++++++-------- Programs/CosmOS/README.md | 2 +- 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/cosmosMonitor.out | 2 +- Tests/expected/cosmosMonitorRun.out | 2 +- Tests/expected/cosmosRun.out | 2 +- Tests/expected/cosmosSlowDisk.out | 2 +- Tests/expected/cosmosSprite.out | 2 +- Tests/video.sh | 69 +++++++-- 13 files changed, 230 insertions(+), 77 deletions(-) diff --git a/Programs/CosmOS/Apps/Lander.asm b/Programs/CosmOS/Apps/Lander.asm index 1531070..9d2ed10 100644 --- a/Programs/CosmOS/Apps/Lander.asm +++ b/Programs/CosmOS/Apps/Lander.asm @@ -1282,7 +1282,17 @@ moveStation: ; Off the edge needs no test. A sprite's X is signed and sixteen bits, so a station three ; hundred pixels to the left is asked for at minus a hundred and forty and the device declines ; to draw it, which is a comparison this does not have to make. -showStation: +; ---- How far round the moon the station is from the lander, and which way ---- +; +; Wrapped to fourteen bits like everything else here, which measures the long way round +; whenever the station is behind - so anything past the half way point is turned into a +; distance BACKWARDS before anybody uses it. Three callers want this now: the one that draws +; the station, the one that decides whether it can be docked with, and the one that slides the +; lander in under it afterwards. +; +; Half a moon is 8,192 sixteenths, which is 512 pixels - and that fits in the twelve signed +; bits toPixelsSigned hands back, so the drawing side can take this straight. +stationGap: SETD.0 StationLow LDA.0 SETD.1 AcrossLow @@ -1301,24 +1311,33 @@ showStation: AND SETD.1 GapHigh STQ.1 - - SETD.0 GapLow - CALL toPixels ; Nought to 1023 pixels, which is the whole moon. - SETD.0 PixelHigh - LDA.0 - INIB 0x02 + LDA.1 + INIB 0x20 AND - BRQ stationAhead ; Under 512, so it is ahead and the offset stands. + BRQ stationGapDone ; Under half a moon, so it is ahead and the gap stands. + SETD.0 GapHigh LDA.0 - INIB 0d4 + INIB 0x40 CCF SUB STQ.0 ; Less a whole moon, which makes it a distance backwards. -stationAhead: +stationGapDone: + RET +showStation: + CALL stationGap + SETD.0 GapLow + CALL toPixelsSigned + + ; ---- From the lander's own column, not from the middle ---- + ; + ; ShipX is half a screen LESS HALF A TILE, because that is what centres an eight pixel lander + ; on the middle. The station was drawn from the middle itself, so its left edge sat where the + ; lander's centre was and a perfectly aligned dock still looked four pixels out. Same origin + ; for both, and a gap of nothing puts one exactly above the other. SETD.0 PixelLow LDA.0 - SETD.1 HalfScreen + SETD.1 ShipX LDB.1 CCF ADD @@ -1326,7 +1345,7 @@ stationAhead: STQ.1 SETD.0 PixelHigh LDA.0 - SETD.1 HalfScreen + SETD.1 ShipX INCD.1 LDB.1 ADD @@ -1802,41 +1821,9 @@ dock: LDA.0 BNA dockRiding - ; ---- Close enough, going round ---- - ; - ; Wrapped to fourteen bits like everything else on this moon, which measures the long way - ; round when the station is behind, so past the half way point is turned into a distance - ; backwards before its size is taken. - SETD.0 StationLow - LDA.0 - SETD.1 AcrossLow - LDB.1 - CCF - SUB - SETD.1 DockGapLow - STQ.1 - SETD.0 StationHigh - LDA.0 - SETD.1 AcrossHigh - LDB.1 - SUB - MVQA - INIB 0x3F - AND - SETD.1 DockGapHigh - STQ.1 - LDA.1 - INIB 0x20 - AND - BRQ dockGapNear - SETD.0 DockGapHigh - LDA.0 - INIB 0x40 - CCF - SUB - STQ.0 -dockGapNear: - SETD.0 DockGapLow + ; Close enough, going round, which is the same gap the station is drawn from. + CALL stationGap + SETD.0 GapLow CALL magnitude SETD.0 DriftHigh LDA.0 @@ -1951,27 +1938,35 @@ dockRiding: AND BNQ dockLetGo - SETD.0 StationLow - LDA.0 - SETD.1 AcrossLow - STA.1 - SETD.0 StationHigh - LDA.0 - SETD.1 AcrossHigh - STA.1 + ; ---- Slid into place, not snapped to it ---- + ; + ; A dock is allowed at up to eight pixels out in either direction, so putting the lander + ; exactly under the station the instant it takes hold moved it a whole tile in one frame - + ; a jump, right at the moment the player was being told they had been careful. Half a pixel + ; a frame closes the worst of it in about half a second, which reads as the two of them + ; settling together. + ; + ; The sideways one goes through the WRAPPED gap rather than the raw positions, because a + ; dock made either side of the moon's seam has a raw difference of most of a moon. + CALL stationGap + CALL easeAcross + SETD.0 StationDownLow LDA.0 INIB 0d128 CCF ADD - SETD.1 DownLow + SETD.1 DockTargetLow STQ.1 SETD.0 StationDownHigh LDA.0 RSTB ADD ; One tile below it, carry included. - SETD.1 DownHigh + SETD.1 DockTargetHigh STQ.1 + SETD.0 DownLow + SETD.1 DockTargetLow + CALL easeToward INIA 0d64 SETD.0 SpeedAcross @@ -1991,6 +1986,100 @@ dockLetGo: STA.0 RET +; ---- Half a pixel of the way there, or the whole of it if that is nearer ---- +; +; DP0 names the word to move, DP1 the place it should end up. The gap is taken as a signed +; sixteen bit number, so "close enough to finish" is two cases and not one: a small gap +; forwards has a high byte of nought, and a small gap backwards has one of 0xFF. +easeToward: + LDA.1 + LDB.0 + CCF + SUB + SETD.2 EaseLow + STQ.2 + INCD.0 + INCD.1 + LDA.1 + LDB.0 + SUB ; And the borrow, which is the other half of the gap. + SETD.2 EaseHigh + STQ.2 + DECD.0 + DECD.1 ; Both pointers back at the low bytes they came in on. + + LDA.2 ; DP2 still names the high half of the gap. + BNA easeBack ; Not nought, so the gap runs backwards or is a long way off. + SETD.2 EaseLow + LDA.2 + INIB 0d8 + CCF + SUB + BRC easeArrive ; Borrowed: under a step, so put it exactly there. + SETD.1 EaseForward + CALL addWord + RET +easeBack: + SETD.2 EaseLow + LDA.2 + INIB 0d248 + CCF + SUB + BNC easeArrive ; No borrow: within a step of nought, going the other way. + SETD.1 EaseBackward + CALL addWord + RET +easeArrive: + LDA.1 + STA.0 + INCD.0 + INCD.1 + LDA.1 + STA.0 + RET + +; ---- The same, for a position that has to go round a moon to get there ---- +; +; The gap is already worked out and wrapped, in GapLow. Adding to a position and then wrapping +; it is the whole difference from the routine above: on a ring the target is "whatever closes +; that gap", not a place, because the place can be on the other side of the seam. +easeAcross: + SETD.0 GapHigh + LDA.0 + BNA easeAcrossBack ; Not nought, so the gap runs backwards or is a long way off. + SETD.0 GapLow + LDA.0 + INIB 0d8 + CCF + SUB + BRC easeAcrossWhole ; Borrowed: under a step, so close the whole of it at once. + SETD.0 AcrossLow + SETD.1 EaseForward + CALL addWord + BRI easeAcrossWrap +easeAcrossBack: + SETD.0 GapLow + LDA.0 + INIB 0d248 + CCF + SUB + BNC easeAcrossWhole + SETD.0 AcrossLow + SETD.1 EaseBackward + CALL addWord + BRI easeAcrossWrap +easeAcrossWhole: + SETD.0 AcrossLow + SETD.1 GapLow + CALL addWord +easeAcrossWrap: + SETD.0 AcrossHigh + LDA.0 + INIB 0x3F + AND + STQ.0 ; Round the moon, the way every other step along it is. + RET + ; ---- A key, or a button ---- ; ; Somebody flying on a controller should not have to reach for the keyboard to say "yes, I @@ -3104,6 +3193,19 @@ Landed: ; Holding onto the station, which is the same kind of rest as sitting on the ground. Docked: 0x00 +; Where the lander is sliding to, and how far it has to go to get there. +DockTargetLow: + 0x00 +DockTargetHigh: + 0x00 +EaseLow: + 0x00 +EaseHigh: + 0x00 +EaseForward: + 0x08 0x00 ; Half a pixel a frame, which settles the worst gap in about half +EaseBackward: + 0xF8 0xFF ; a second - slow enough to see and fast enough not to wait for. DockGapLow: 0x00 DockGapHigh: diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index e850d09..de98093 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -727,7 +727,7 @@ from every assembly file in it. Several are old programs written for the bare ma | Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls it diagonally a pixel at a time. | | Sprite | Moves a ball across the shell's own text, writing not one byte of the map to do it. It leaves the sprite in the table on the way out, because clearing them is the system's job - see below. | | Pad | Says what the controllers are doing, printing a line whenever one changes. It tells apart the three things that look identical from inside a game that is not responding: a pad nobody noticed, a pad mapped to nothing, and a mapping that is wrong. | -| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. Two bars read the speeds and are green while a landing would survive and red while it would not, so "can I put down" is a glance rather than a sum. A third runs up the left edge and is how much sky is under the lander, half a pixel of bar to a pixel of it; it reads nought the moment the lander is down, and it exists because the orbit takes the lander off the top of the screen and a panel that only works while the ground is in sight is no use above it. And gravity here is the pull MINUS the swing outwards, which is what makes an orbit rather than a one way trip: under four pixels a frame sideways the pull wins and the lander falls, over it the swing wins and the lander climbs, and at it they cancel and it circles. Falling buys sideways speed and climbing spends it - at a rate set by the product of the two, so the trade stops by itself as the sideways speed runs out - and that is the cycle: a fall carries the lander past orbital speed and becomes a climb, the climb pays it back and becomes a fall, periapse and apoapse, round and round. Orbital speed is marked on the drift bar, sixty four pixels either side of the middle, because a number nothing points at is folklore. Gravity never falls off and the moon wraps, so a circular orbit is the same length at any height and one orbital speed serves everywhere. There is a ceiling 192 pixels above the world's origin, which is the top of the wide view - it was sixty four while sixty four was all the sky a forty column screen had over the origin, which was a fact about the view rather than about the world. It pins rather than ends: leaving upward is recoverable, so the lander is held there and told so, and one sideways is spent every tick it tries to climb - without that the pin wiped the climb, the trade saw neither fall nor climb, and the speed pushing it up never changed. What kills you out here is running dry a long way from the ground. B, or z on the keyboard, swaps the screen between forty columns and eighty, which is the same map, the same cells and the same engine at half the size: nearly two thirds of the moon at once for the orbit, and twice as big for the landing. The twenty extra rows that buys go to the SKY and not to the moon - the wide view starts twenty four rows above the world's origin rather than at it, so the ground sits near the bottom and the whole flyable band is on the screen. Drawn down from the origin instead it was 71 per cent rock, which is a zoom that shows you more of the thing you cannot fly through. A lander at the ceiling is off the top of a forty column screen, which is where the orbit lives and why the altitude bar had to exist; zoomed out it is in the picture. And there is a station up there, twelve rows above the world's origin - about two thirds of the way from the ground to the ceiling, clear of work near the surface - going round at orbital speed - which needs no physics of its own, because a body at 64 sixteenths is exactly what a circular orbit IS under these rules, at any height, since gravity never falls off and the moon wraps. It is off the top of a forty column screen too, so it is somewhere to go that the zoom is needed to see. Docking it is landing on something that is moving: close enough, and slow enough RELATIVE TO THE STATION, or it is a wreck. Its speed is orbital speed, which is the number the marks on the drift bar point at, so the instrument for it was on the screen before there was anything to dock with. A dock pays eighty units of fuel and holds the lander a tile under the station until a thruster lets go. Getting there is a two burn manoeuvre and not a straight line: climbing SPENDS sideways speed, so a lander cannot rise while matched - it arrives slower than orbital every time, and has to raise the far side of its orbit and then circularise at the top. Which is also why it reads /lander.state if the disk has one: sixteen bytes of position, velocity, station, fuel and screen, laid straight over the numbers it would otherwise start with, so a rendezvous can be examined without flying one first. A disk without the file is the game as it always was. The buffer is a whole block wide because osFileRead lands a file in blocks of 256, and sixteen bytes of room for it wrote over everything that followed. Down is a retro thruster at half the strength of up - one sixteenth a tick against two, which is exactly gravity's own step, so it can stop a climb and hurry a descent and can never turn a landing approach into a crash faster than letting go would. Arresting a rise otherwise meant a sideways burn and a wait for the orbit to come round, which is how a rendezvous really is flown and a lot to ask of somebody who has not flown one. It does nothing at all to a lander on the ground, which is not politeness: touchdown has already had its say and will not speak again, so without that guard a landed lander holding it goes straight through the moon. | +| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. Two bars read the speeds and are green while a landing would survive and red while it would not, so "can I put down" is a glance rather than a sum. A third runs up the left edge and is how much sky is under the lander, half a pixel of bar to a pixel of it; it reads nought the moment the lander is down, and it exists because the orbit takes the lander off the top of the screen and a panel that only works while the ground is in sight is no use above it. And gravity here is the pull MINUS the swing outwards, which is what makes an orbit rather than a one way trip: under four pixels a frame sideways the pull wins and the lander falls, over it the swing wins and the lander climbs, and at it they cancel and it circles. Falling buys sideways speed and climbing spends it - at a rate set by the product of the two, so the trade stops by itself as the sideways speed runs out - and that is the cycle: a fall carries the lander past orbital speed and becomes a climb, the climb pays it back and becomes a fall, periapse and apoapse, round and round. Orbital speed is marked on the drift bar, sixty four pixels either side of the middle, because a number nothing points at is folklore. Gravity never falls off and the moon wraps, so a circular orbit is the same length at any height and one orbital speed serves everywhere. There is a ceiling 192 pixels above the world's origin, which is the top of the wide view - it was sixty four while sixty four was all the sky a forty column screen had over the origin, which was a fact about the view rather than about the world. It pins rather than ends: leaving upward is recoverable, so the lander is held there and told so, and one sideways is spent every tick it tries to climb - without that the pin wiped the climb, the trade saw neither fall nor climb, and the speed pushing it up never changed. What kills you out here is running dry a long way from the ground. B, or z on the keyboard, swaps the screen between forty columns and eighty, which is the same map, the same cells and the same engine at half the size: nearly two thirds of the moon at once for the orbit, and twice as big for the landing. The twenty extra rows that buys go to the SKY and not to the moon - the wide view starts twenty four rows above the world's origin rather than at it, so the ground sits near the bottom and the whole flyable band is on the screen. Drawn down from the origin instead it was 71 per cent rock, which is a zoom that shows you more of the thing you cannot fly through. A lander at the ceiling is off the top of a forty column screen, which is where the orbit lives and why the altitude bar had to exist; zoomed out it is in the picture. And there is a station up there, twelve rows above the world's origin - about two thirds of the way from the ground to the ceiling, clear of work near the surface - going round at orbital speed - which needs no physics of its own, because a body at 64 sixteenths is exactly what a circular orbit IS under these rules, at any height, since gravity never falls off and the moon wraps. It is off the top of a forty column screen too, so it is somewhere to go that the zoom is needed to see. Docking it is landing on something that is moving: close enough, and slow enough RELATIVE TO THE STATION, or it is a wreck. Its speed is orbital speed, which is the number the marks on the drift bar point at, so the instrument for it was on the screen before there was anything to dock with. A dock pays eighty units of fuel and holds the lander a tile under the station until a thruster lets go, sliding it into line at half a pixel a frame rather than snapping - a dock is allowed eight pixels out either way, so putting the lander exactly in place the instant it took hold moved it a whole tile in one frame, right at the moment the player was being told they had been careful. It settles squarely under the port, which it did not used to: the lander is drawn from half a screen LESS HALF A TILE, because that is what centres it, and the station was drawn from half a screen exactly, so a perfectly flown dock still looked four pixels out. Getting there is a two burn manoeuvre and not a straight line: climbing SPENDS sideways speed, so a lander cannot rise while matched - it arrives slower than orbital every time, and has to raise the far side of its orbit and then circularise at the top. Which is also why it reads /lander.state if the disk has one: sixteen bytes of position, velocity, station, fuel and screen, laid straight over the numbers it would otherwise start with, so a rendezvous can be examined without flying one first. A disk without the file is the game as it always was. The buffer is a whole block wide because osFileRead lands a file in blocks of 256, and sixteen bytes of room for it wrote over everything that followed. Down is a retro thruster at half the strength of up - one sixteenth a tick against two, which is exactly gravity's own step, so it can stop a climb and hurry a descent and can never turn a landing approach into a crash faster than letting go would. Arresting a rise otherwise meant a sideways burn and a wait for the orbit to come round, which is how a rendezvous really is flown and a lot to ask of somebody who has not flown one. It does nothing at all to a lander on the ground, which is not politeness: touchdown has already had its say and will not speak again, so without that guard a landed lander holding it goes straight through the moon. | | Depth | Four pillars at four distances and a ball walking past them, behind the near ones and in front of the far ones. The ball is sprite nought and every pillar is numbered after it, so table order puts it in front of all four - what actually decides is the depth buffer, asked a column at a time. | | Flip | Draws a whole screen into the bank that is not being shown, waits, and then shows it in one byte out of one port. It writes nothing else at all - not a tile, not a colour - so it does not ask for the screen to be saved, and the line it printed is still there when it comes back. It deliberately does not put the displayed screen back either, because that is the system's to restore: a program that faulted while flipped could not have. | | Edit | A line editor. | diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index 98e6376..8818efa 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index a354d6a..1142a05 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 62fde1c..449058f 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index 5363f2e..4950589 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index 08940b5..81612a2 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index 358afee..24915e1 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index 96670ca..f7e26f7 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 4227e64..f303482 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index f39f062..ed554fa 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index b680e8e..d4db527 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 6692 +Lander.sbx 6814 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/video.sh b/Tests/video.sh index d9dfafe..71652eb 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -2379,8 +2379,27 @@ for which in place dock wreck; do cp "$ROOT/Tests/build/disks/cosmos.img" "$BUILD/$which.img" "$ROOT/SplitDisk" put "$BUILD/$which.img" "$BUILD/$which.state" /lander.state > /dev/null done -python3 -c "open('$BUILD/ackonly.pad','wb').write(b'\x00' * 200 + b'\x10' * 8 + b'\x00' * 60000)" -for run in place:3000000 dock:6000000 dock2:9000000 wreck:3000000; do +# ---- The acknowledgement comes at frame 400, and that is not a round number by accident ---- +# +# A dock, like a landing, waits to be told its message has been read. Reading a state file +# costs a disk read, so a placed run starts a good deal later than a plain one - the press was +# at frame 100 and stopped working the day the state file arrived, because by then the program +# had not reached the dock yet and the press went by unheard. Everything after it is sampled +# well clear of both ends. +python3 -c "open('$BUILD/ackonly.pad','wb').write(b'\x00' * 400 + b'\x10' * 8 + b'\x00' * 60000)" +# A dock that is legally within tolerance but well out of line - six pixels back and six up - +# so there is a visible distance to slide. Sampled twice: once while it is still closing, and +# once after. The slide runs for the thirty or so frames after the acknowledgement at frame +# 400, and the middle sample sits at about frame 419; if it ever fails, check that before +# believing the code is broken. +python3 -c " +import struct +open('$BUILD/slide.state','wb').write(struct.pack(' /dev/null +for run in place:5000000 dock:7300000 dock2:9000000 wreck:5000000 slide:7000000 slide2:8000000; do which="${run%%:*}"; when="${run##*:}" timeout 60 "$EMU" --fast --cycles "$when" --keyboard "$BUILD/fly.keys" \ --pad "$BUILD/ackonly.pad" --screen "$BUILD/$which.ppm" \ @@ -2388,11 +2407,11 @@ for run in place:3000000 dock:6000000 dock2:9000000 wreck:3000000; do "$BUILD/cosmos.bin" > "$BUILD/$which.out" 2>&1 || true done # And the same moment with no state file at all, which is the contrast that says the file did it. -timeout 60 "$EMU" --fast --cycles 3000000 --keyboard "$BUILD/fly.keys" \ +timeout 60 "$EMU" --fast --cycles 5000000 --keyboard "$BUILD/fly.keys" \ --pad "$BUILD/ackonly.pad" --screen "$BUILD/nostate.ppm" \ --disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \ "$BUILD/cosmos.bin" > "$BUILD/nostate.out" 2>&1 || true -read -r PLACEDY PLACEDFUEL PLAINFUEL DOCKFUEL DOCK2FUEL RIDESHIP RIDESTATION </dev/null || echo "-1 -1 -1 -1 -1 none none") EOT # Placed at the world's origin with a hundred units: row 192 is the origin in the wide view, @@ -2424,10 +2458,27 @@ EOT [ "$DOCKFUEL" = "22" ] && [ "$DOCK2FUEL" = "22" ] \ && result ok "a matched approach docks and is paid" "a hundred became a hundred and eighty, once" \ || result no "a matched approach docks and is paid" "$DOCKFUEL cells, then $DOCK2FUEL" -# Held to it, one tile under, rather than merely paid by it. -[ "$RIDESHIP" = "316,104" ] && [ "$RIDESTATION" = "320,96" ] \ - && result ok "and rides it, a tile under" "lander $RIDESHIP, station $RIDESTATION" \ - || result no "and rides it, a tile under" "lander $RIDESHIP, station $RIDESTATION" +# ---- And it settles ALIGNED, which took finding ---- +# +# Nought across and eight up is the station squarely over the lander, a tile apart. It used to +# come to rest four pixels out however carefully it was flown, because the lander is drawn from +# half a screen LESS HALF A TILE - which is what centres an eight pixel lander on the middle - +# and the station was drawn from half a screen exactly. Same origin for both now. +[ "$SETTLED" = "0,-8" ] \ + && result ok "and rides it, squarely under the port" "the station directly above, a tile up" \ + || result no "and rides it, squarely under the port" "settled at $SETTLED, wanted 0,-8" +# Slid, not snapped. A dock is allowed eight pixels out either way, so putting the lander +# exactly in place the instant it took hold moved it a whole tile in one frame - a jump, at +# the very moment the player was being told they had been careful. +# +# THIS WATCHES THE VERTICAL ONE. Sideways has less ground to cover - eight pixels against +# sixteen, since the lander also has to come down a tile - so it has finished by the time this +# sample is taken, and deleting its easing does not show up here. Where it does is the check +# above: sideways is the axis alignment is measured on, so a snap there still has to land +# squarely. The gradualness of it alone is not separately guarded. +[ "$SLIDING" != "$SETTLED" ] && [ "$SLIDING" != "none" ] \ + && result ok "and slides in rather than snapping" "$SLIDING while closing, $SETTLED after" \ + || result no "and slides in rather than snapping" "$SLIDING while closing, $SETTLED after" grep -q "Wrecked against the station" "$BUILD/wreck.out" \ && result ok "and the same approach unmatched is a wreck" "64 sixteenths, eight times the limit" \ || result no "and the same approach unmatched is a wreck" "no wreck in the transcript"