Let the reset button reach a machine that has stopped
The gesture rebooted CosmOS and could not reboot picture.bin, which is the case it was added for. picture.asm ends in HALT, and a halted machine runs no instructions - so nothing ever reached the code that notices a reset, because a reset is noticed BETWEEN INSTRUCTIONS and there are none. It only ever worked because CosmOS was still going. Which is backwards: a machine that is not going anywhere is exactly the one worth restarting, and it is the one that cannot hear a request by itself. The restart is lifted out of the run loop into machineTakeReset, and the window asks every frame whether the machine is running or not. NAMED AS EMULATOR MAGIC, because it is. There is no reset line on this machine 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. The shape of that is already visible here: asking every frame rather than leaving it to the machine to notice is what a line does. A restart now clears the cycle limit as well, since a machine stopped for reaching one is another thing somebody would press the button over. The three existing reset tests still pass, and they are the ones that matter: they exercise the same restart through the machine port. What no test reaches is the gesture itself, which exists only when there is a window. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
8c6ed62044
commit
adefce975b
+42
-20
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user