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:
Anachronaut
2026-08-29 14:48:31 -04:00
co-authored by Claude Opus 5
parent 8c6ed62044
commit adefce975b
4 changed files with 79 additions and 21 deletions
+42 -20
View File
@@ -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) {