diff --git a/README.md b/README.md index 0fc99c9..8ac1405 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,18 @@ imitate a login screen. On Windows an application cannot see it without a kernel on Linux the desktop takes it. It is unavailable for the same reason it seemed right. It does exactly what writing to the machine port does: the machine starts the way it started, -so the boot chain runs again and finds whatever the disk now says to run. +so the boot chain runs again and finds whatever the disk now says to run. **And it works on a +machine that has stopped** - one that halted, or faulted, or is a bare metal demo that ended. +That is the whole point of a button: a machine which is not going anywhere is exactly the one +worth restarting, and it is the one that cannot notice a request by itself, because a reset is +otherwise seen between instructions and a halted machine runs none. + +This part is **emulator magic and known to be**. There is no reset line on this machine yet +and no keyboard controller to assert one; the window reaches in and sets the same flag the +machine port sets. When those are designed, a keyboard controller will have to see the gesture +and pull reset regardless of what the CPU is doing - which is the property that matters, and +the one a port write can never have, since a port write needs a program willing and able to +make it. That is what makes a bare metal program escapable. `Once` puts one in front of the next start and deletes the request before jumping, so a demo that has taken the whole machine is one diff --git a/Source/Emulator/machine.c b/Source/Emulator/machine.c index 9b5d26c..cf238e0 100644 --- a/Source/Emulator/machine.c +++ b/Source/Emulator/machine.c @@ -108,6 +108,47 @@ static int keyboardHook(int mayWait) { return byte & 0xFF; } +// ---- Starting over ---- +// +// WHAT A RESET REPEATS IS HOW THIS MACHINE STARTED. Named an image, it is placed again; named +// none, the ROM is shadowed again and reads the disk for the rest. Anything else would mean a +// reset changed what the machine is, which is the one thing a reset must not do. +// +// The disk is not unplugged and its image keeps everything written to it. That is what warm +// means: the machine starts again, the world it starts into does not. +// +// The vector table goes, and that is a deliberate departure from leaving memory alone. A +// vector points into whatever installed it, and after this that program is not running - so a +// handler left behind would aim an interrupt at an address belonging to something gone. It is +// the argument CosmOS already makes when it takes a program's vectors back at exit. +static int machineRestart(Machine *m) { + memset(Program + SOFTWARE_VECTOR_BASE, 0, + (size_t)(0x10000 - SOFTWARE_VECTOR_BASE)); + uint8_t failed = (m->programFile != NULL) + ? loadFile(m->programFile, Program, Data) + : loadROM(bootROM, bootROMBytes, Program, Data); + if (failed) { + fprintf(stderr, "Error: The machine could not be started again.\n"); + m->restartFailed = 1; + return 0; + } + videoReset(); + consoleHome(); + consoleResetInput(); + initializeCPU(&m->cpu, Program, Data); + // A machine that had stopped is running again, which is the entire point of asking from + // outside: the interesting time to restart something is when it is not going anywhere. + m->limitReached = 0; + return 1; +} + +int machineTakeReset(Machine *m) { + if (!takeResetRequest()) { + return 0; + } + return machineRestart(m); +} + uint8_t machineStart(Machine *m, const EmulatorOptions *options, const char *programFile) { m->options = *options; m->programFile = programFile; @@ -218,26 +259,7 @@ void machineRunSlice(Machine *m) { // The disk is not unplugged and its image keeps everything written to it. That // is what warm means: the machine starts again, the world it starts into does // not. - if (takeResetRequest()) { - // The vector table goes, and that is a deliberate departure from leaving - // memory alone. A vector points into whatever installed it, and after this - // that program is not running - so a handler left behind would aim an - // interrupt at an address belonging to something gone. It is the argument - // CosmOS already makes when it takes a program's vectors back at exit. - memset(Program + SOFTWARE_VECTOR_BASE, 0, - (size_t)(0x10000 - SOFTWARE_VECTOR_BASE)); - uint8_t failed = (m->programFile != NULL) - ? loadFile(m->programFile, Program, Data) - : loadROM(bootROM, bootROMBytes, Program, Data); - if (failed) { - fprintf(stderr, "Error: The machine could not be started again.\n"); - m->restartFailed = 1; - return; - } - videoReset(); - consoleHome(); - consoleResetInput(); - initializeCPU(&m->cpu, Program, Data); + if (machineTakeReset(m)) { break; // Out of this batch; the loop above carries on with a new CPU. } if (m->cpu.Status & STATUS_HALT) { diff --git a/Source/Emulator/machine.h b/Source/Emulator/machine.h index 4cbdd2f..3f1a556 100644 --- a/Source/Emulator/machine.h +++ b/Source/Emulator/machine.h @@ -65,6 +65,17 @@ int machineRunning(const Machine *m); // is the point of cutting it here rather than anywhere else. void machineRunSlice(Machine *m); +// ---- Asking from outside, when nothing inside is asking ---- +// +// A reset is normally noticed between instructions, which works when there are instructions. +// A HALTED MACHINE RUNS NONE, so a program that has finished - or faulted, or is a bare metal +// demo that ended with HALT - could ask to be restarted for ever and nothing would come along +// to hear it. That is exactly the machine somebody wants to restart. +// +// So a front end calls this every time round its own loop, whether the machine is running or +// not. Returns 1 if it started over. +int machineTakeReset(Machine *m); + void machineStop(Machine *m); // Says how the run went and returns what the process should exit with. diff --git a/Source/Emulator/voyager.c b/Source/Emulator/voyager.c index 6433123..44f4779 100644 --- a/Source/Emulator/voyager.c +++ b/Source/Emulator/voyager.c @@ -128,6 +128,17 @@ static void drainKeyboard(void) { // ---- The reset button ---- // +// EMULATOR MAGIC, AND KNOWN TO BE. There is no reset line on this machine yet and no keyboard +// controller to assert one: the window reaches in and pokes the same flag a program pokes +// through the machine port. When those are designed, a keyboard controller will have to see +// this gesture and pull reset REGARDLESS OF WHAT THE CPU IS DOING - which is the property +// that matters and the one a port write cannot have, since a port write needs a program +// willing and able to make it. +// +// The shape of that is already visible here. A reset is normally noticed between +// instructions, and a halted machine runs none - so the window asks every frame rather than +// leaving it to the machine to notice, which is what real hardware would do with a line. +// // ON REAL HARDWARE THIS IS NOT A KEY AT ALL. A Voyager has a button on the case, and what a // window has instead of a case is a gesture. So the gesture wants two properties a single // key does not have. @@ -344,6 +355,9 @@ int main(int argc, char *argv[]) { // it drew is still there to look at - a program that ends should not take its output // off the screen with it. while (windowOpen && !WindowShouldClose()) { + // Before the running check, not after it: a machine that has stopped is the one + // worth restarting, and it is the one that cannot notice a reset by itself. + machineTakeReset(&machine); if (machineRunning(&machine)) { machineRunSlice(&machine); }