Files
AnachronautandClaude Opus 5 adefce975b 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
2026-08-29 14:48:31 -04:00

85 lines
3.1 KiB
C

// machine.h
// The SplitBit machine, with nothing attached to look at it.
// Written by Anachronaut
#ifndef MACHINE_H
#define MACHINE_H
#include <stdint.h>
#include <time.h>
#include "cpu.h"
#include "utility.h"
// ---- Why the machine is not a main ----
//
// There are two front ends: SplitBit, which is a terminal, and Voyager, which is a window
// and a speaker. THEY ARE THE SAME MACHINE. Keeping it here means the difference between
// them is presentation and nothing else - neither can quietly acquire behaviour the other
// lacks, and the suite can hold them to it by running a program through both and comparing
// what came out.
//
// It also decides where the devices live. A video device that only existed in the windowed
// binary would be untestable, because the suite has no display; here it is part of the
// machine, advances on emulated cycles, and Raylib only presents what it already produced.
// How fast the machine runs when it is not being told to hurry.
#define CYCLE_RATE 1000000
typedef struct {
long long cycles_per_sec;
long long accumulator_ns;
struct timespec prev;
} CycleTimer;
void cycle_timer_init(CycleTimer *t, long long cycles_per_sec);
// Call once per host frame. Returns how many SplitBit cycles to execute.
int cycle_timer_tick(CycleTimer *t);
typedef struct {
CPURegisters cpu;
CycleTimer timer;
unsigned long cycleCount;
uint8_t limitReached;
uint8_t restartFailed;
const char *programFile;
EmulatorOptions options;
} Machine;
#define MACHINE_OK 0
#define MACHINE_ERROR 1
// No image and no disk. Told apart from an ordinary failure so that the front end can
// print its own help, which is the one thing here that knows what it is called.
#define MACHINE_NOTHING_TO_RUN 2
uint8_t machineStart(Machine *m, const EmulatorOptions *options, const char *programFile);
// Whether there is any more running to do. False once the machine has halted, faulted, or
// spent the cycle limit it was given.
int machineRunning(const Machine *m);
// ---- One slice, not the whole run ----
//
// A front end with a window has to get a turn: run a slice, present a frame, run another.
// A terminal simply runs slices until the machine stops. Both loops are three lines, which
// 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.
int machineReport(const Machine *m);
#endif // MACHINE_H