diff --git a/Programs/CosmOS/Apps/Lander.asm b/Programs/CosmOS/Apps/Lander.asm index 78cd9af..c3c8938 100644 --- a/Programs/CosmOS/Apps/Lander.asm +++ b/Programs/CosmOS/Apps/Lander.asm @@ -80,6 +80,7 @@ start: CALL putLander CALL putGauge CALL putOrbital + CALL putBoom ; The instrument, once, the way the tiles are. INIA 0x01 OUTA 0x02 ; Key mode. @@ -2534,7 +2535,9 @@ explode: STA.0 ; Getting on for a second, which is long enough to watch. SETD.0 SparkV CALL startBurst + CALL boom CALL runBurst + CALL boomOff RET ; ---- Dust, for a landing that went right ---- @@ -2656,6 +2659,119 @@ hideSprite: OUTA 0xE9 RET +; ---- The bang, set up once and played in two writes ---- +; +; A patch is twenty odd writes and a note is two, which is the whole point of the selector and +; value registers - so the instrument is built at startup like the tiles are, and a crash only +; has to say "this channel, this note". +; +; ---- Channel three, counting down ---- +; +; Effects take the top channel and work downwards, so music, if it ever arrives, can take +; nought and count up and the two never have to negotiate. There are four, and this is the +; first program to want any of them while also doing something else. +putBoom: + INIA 0x03 + OUTA 0x41 ; Channel three. Every parameter write below lands on it. + + ; ---- Noise, which is what an explosion is ---- + ; + ; Every other waveform here has a pitch, and a pitched bang is a note. Writing a port leaves + ; A alone, so the three that selected the channel would select parameter three; the wave is + ; parameter nought and has to be said. + RSTA + OUTA 0x42 + INIA 0d5 ; Noise. + OUTA 0x43 + INIA 0x01 ; Oscillator nought, gain. + OUTA 0x42 + INIA 0xFF + OUTA 0x43 + + ; ---- Instant, then a long way down ---- + ; + ; No attack at all, because a bang that fades in is not a bang. A sustain of nothing does NOT + ; end the note - a voice holding at nothing is what a held key is - so the gate still has to + ; be dropped afterwards, which the explosion does once its pieces have gone. + INIA 0x20 ; Amplitude envelope, attack. + OUTA 0x42 + RSTA + OUTA 0x43 + INIA 0x21 ; ---- Decay: about half a second ---- + OUTA 0x42 ; + ; A byte is not seconds. It is squared and scaled to four of them, + ; so 200 is two and a half - which over the eight tenths of a + ; second the pieces are in the air is not a bang fading, it is the + ; first third of one, and it read as a flat wash of noise. + INIA 0d90 + OUTA 0x43 + INIA 0x22 ; Sustain: nothing. + OUTA 0x42 + RSTA + OUTA 0x43 + INIA 0x23 ; Release. + OUTA 0x42 + INIA 0d40 + OUTA 0x43 + + ; ---- And a filter closing, which is what makes it a boom and not a hiss ---- + ; + ; Noise on its own is a cymbal. The modulation envelope shuts the low pass over the same + ; moment the level falls, so the bright part is only at the front of it. + INIA 0x40 ; Filter, on. + OUTA 0x42 + INIA 0x01 + OUTA 0x43 + INIA 0x41 ; Type: low pass. + OUTA 0x42 + RSTA + OUTA 0x43 + INIA 0x42 ; Cutoff, low, which is where it ends up. + OUTA 0x42 + INIA 0d40 + OUTA 0x43 + INIA 0x43 ; A little resonance, so the sweep can be heard. + OUTA 0x42 + INIA 0d120 + OUTA 0x43 + INIA 0x44 ; What opens it: the modulation envelope. + OUTA 0x42 + INIA 0d2 + OUTA 0x43 + INIA 0x45 ; And how far above where it sits. + OUTA 0x42 + INIA 0d200 + OUTA 0x43 + INIA 0x30 ; That envelope: open at once, + OUTA 0x42 + RSTA + OUTA 0x43 + INIA 0x31 ; shut over about a third of a second, + OUTA 0x42 + INIA 0d90 + OUTA 0x43 + INIA 0x32 ; and stay shut. + OUTA 0x42 + RSTA + OUTA 0x43 + RET + +; Two writes, which is what a note is meant to cost. +boom: + INIA 0x03 + OUTA 0x41 + INIA 0d24 ; Low: the filter is doing the work and this is only the body. + OUTA 0x44 ; Writing the note is what starts it. + RET + +; And letting go of it, once there is nothing left to watch. +boomOff: + INIA 0x03 + OUTA 0x41 + RSTA + OUTA 0x45 ; Dropping the gate is the only thing that ends a note. + RET + ; ---- A key, or a button ---- ; ; Somebody flying on a controller should not have to reach for the keyboard to say "yes, I diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 4f06415..12aa102 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, 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. A landing kicks up dust, sideways and up off the lander's feet, because that is where kicked dust goes and there is ground in the way of the rest of it. Letting go of the station vents gas instead, evenly in every direction, since nothing is in the way of a docking port - and that one does NOT stop the world the way the others do, because it goes off on the frame a thruster is pressed and freezing then would be felt as the controls sticking. There is none on docking: a dock is a catch and not a touchdown, and there is nothing under it to kick. A lander also has to get two tiles clear of the station before it can take hold of it again, because a docked one sits exactly one tile under it - so letting go upwards docked it straight back on, took the fuel again, and sat waiting to be told the message had been read. A crash takes the lander apart: it goes, and six pieces of it leave in a rough hexagon at its own colour for about a second before the verdict is said. It used to be a line of text and a lander still sitting there in one piece, so a watcher had to read the words to know what had happened. The world is not running while that plays - it is a loop of its own, so nothing else has to know how to be half destroyed. A plume hangs off whichever side the engine is pushing from - under the lander to lift, over it to retro, and on the far side from the way it is being pushed sideways, since that is where the gas leaves. Every other reading here is a number drawn as a bar and all of them say what is happening TO the lander, so without this a watcher has to read gauges to work out that a thruster is even lit. It is drawn while the BUTTON IS HELD rather than while the engine fires: the engine fires one frame in ten, because that is the tick gravity is applied on, and a flame that honest is a fault lamp rather than a rocket. An empty tank draws none, and neither does the retro thruster on the ground, because in both cases the button really is doing nothing. 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. A landing kicks up dust, sideways and up off the lander's feet, because that is where kicked dust goes and there is ground in the way of the rest of it. Letting go of the station vents gas instead, evenly in every direction, since nothing is in the way of a docking port - and that one does NOT stop the world the way the others do, because it goes off on the frame a thruster is pressed and freezing then would be felt as the controls sticking. There is none on docking: a dock is a catch and not a touchdown, and there is nothing under it to kick. A lander also has to get two tiles clear of the station before it can take hold of it again, because a docked one sits exactly one tile under it - so letting go upwards docked it straight back on, took the fuel again, and sat waiting to be told the message had been read. A crash also makes a noise, which is the first sound this game has: noise through a low pass that the modulation envelope shuts as the level falls, so the bright part is only at the front and it is a boom rather than a hiss. The instrument is built at startup the way the tiles are, because a patch is twenty odd writes and a note is two - which is what the selector and value registers are for. It plays on channel THREE: effects count down from the top so that music, if it ever arrives, can take nought and count up and the two never have to negotiate. A crash takes the lander apart: it goes, and six pieces of it leave in a rough hexagon at its own colour for about a second before the verdict is said. It used to be a line of text and a lander still sitting there in one piece, so a watcher had to read the words to know what had happened. The world is not running while that plays - it is a loop of its own, so nothing else has to know how to be half destroyed. A plume hangs off whichever side the engine is pushing from - under the lander to lift, over it to retro, and on the far side from the way it is being pushed sideways, since that is where the gas leaves. Every other reading here is a number drawn as a bar and all of them say what is happening TO the lander, so without this a watcher has to read gauges to work out that a thruster is even lit. It is drawn while the BUTTON IS HELD rather than while the engine fires: the engine fires one frame in ten, because that is the tick gravity is applied on, and a flame that honest is a fault lamp rather than a rocket. An empty tank draws none, and neither does the retro thruster on the ground, because in both cases the button really is doing nothing. 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 276f738..d48d18c 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index 0fd6abc..39b58f8 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 298732d..3a8f43a 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index c4264b5..c42347d 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index d8e5d0b..299f54b 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index b750427..1ea065d 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index ce614e2..1f48725 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index 1c87c55..d6c133a 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index c2715d6..566141e 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index c1eb696..92bb7af 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 8243 +Lander.sbx 8388 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/video.sh b/Tests/video.sh index 33ecfd7..e462b00 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -2661,6 +2661,59 @@ EOT && result ok "and are gone before the verdict" "nothing left over the words" \ || result no "and are gone before the verdict" "$AFTERBITS pixels still there" +# ---- And a bang to go with it ---- +# +# The first sound this game makes, and the first time anything has driven the sound device +# while also doing something else - the only other customer is the patch editor, whose whole +# job is the device. +# +# A patch is twenty odd writes and a note is two, which is what the selector and value +# registers are for: the instrument is built at startup like the tiles are, and a crash only +# says "this channel, this note". Channel THREE, effects counting down from the top, so music +# can one day take nought and count up without the two having to negotiate. +# +# Checked against silence, which is the useful half: a landing in the same conditions makes no +# sound at all, so this is measuring the crash and not the machine humming. +timeout 60 "$EMU" --fast --cycles 3000000 --keyboard "$BUILD/fly.keys" \ + --pad "$BUILD/none.pad" --sound "$BUILD/bang.raw" \ + --disk "$BUILD/crash.img" --ram-disk 2048 \ + "$BUILD/cosmos.bin" > "$BUILD/bang.out" 2>&1 || true +timeout 60 "$EMU" --fast --cycles 3000000 --keyboard "$BUILD/fly.keys" \ + --pad "$BUILD/none.pad" --sound "$BUILD/hush.raw" \ + --disk "$ROOT/Tests/build/disks/cosmos.img" --ram-disk 2048 \ + "$BUILD/cosmos.bin" > "$BUILD/hush.out" 2>&1 || true +read -r BANGPEAK BANGLOUD BANGFADE HUSHPEAK < 500) + # Louder at the front than at the back, which is what a bang is and a hiss is not. + half = len(s) // 2 + front = max((abs(v) for v in s[:half]), default=0) + back = max((abs(v) for v in s[half:]), default=0) + return peak, loud, (1 if front > back else 0) +b = look('$BUILD/bang.raw') +print(b[0], b[1], b[2], look('$BUILD/hush.raw')[0]) +" 2>/dev/null || echo "0 0 0 -1") +EOT +[ "$BANGPEAK" -gt 2000 ] && [ "$BANGLOUD" -gt 5000 ] && [ "$HUSHPEAK" = "0" ] \ + && result ok "a crash makes a noise, and a landing does not" "peak $BANGPEAK against silence" \ + || result no "a crash makes a noise, and a landing does not" "crash peak $BANGPEAK, landing peak $HUSHPEAK" +# ---- And it fades ---- +# +# A byte of envelope is not seconds: it is squared and scaled to four of them, so the first +# decay written here was 200, which is two and a half - and over the eight tenths of a second +# the pieces are in the air that is not a bang fading, it is the FRONT THIRD of one. It read +# as a flat wash of noise and measured as one. +[ "$BANGFADE" = "1" ] \ + && result ok "and it is loudest at the front" "which is what makes it a bang" \ + || result no "and it is loudest at the front" "louder in the second half than the first" + # ---- Dust on landing, and gas on letting go ---- # # The same six pieces as the explosion, thrown differently: dust goes sideways and UP off the