One byte a frame, in exactly the format the player takes, so a recording needs no conversion and there is no second format to keep in step. That symmetry is the feature, and it makes the strongest form of the claim testable: a recording is made OF a playback, and the bytes coming out have to be the bytes that went in. It exists because some inputs cannot sensibly be written by hand. Flying a lander from one base to another is a few hundred frames of steering that has to arrive somewhere eight cells wide, and several attempts at authoring one got within two columns and no closer. That is a piloting exercise rather than a test. Playing it once and keeping what happened is the answer. A BYTE FOR EVERY FRAME, written inside the loop that advances the recordings rather than after it, so a machine that jumped several frames at once still writes one for each. A recording is a timeline: one that skipped the frames nobody looked at would play back faster than it was flown. What is recorded is what the DEVICE WOULD REPORT, not the live state - a recording of a playback that wrote the live state would be a file of noughts. And it is flushed as it goes, because a recording is usually stopped by whoever is playing rather than by the program ending, and a demo lost to a buffer is a demo flown twice. Tests/replay.sh is where this and whatever follows it are checked. Twelve scripts now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
66 lines
2.8 KiB
C
66 lines
2.8 KiB
C
// utility.h
|
|
// Utilities for the SplitBit CPU Emulator
|
|
// Written by Anachronaut
|
|
// 10/15/2024
|
|
|
|
|
|
#ifndef UTILITY_H
|
|
#define UTILITY_H
|
|
|
|
#include <stdint.h>
|
|
#include "io.h"
|
|
#include "cpu.h"
|
|
|
|
// Results of reading the command line.
|
|
#define OPTIONS_OK 0 // Carry on and run the program.
|
|
#define OPTIONS_HELP 1 // The user asked for help, so stop, but not because of an error.
|
|
#define OPTIONS_ERROR 2 // The command line was no good, stop and complain.
|
|
|
|
// As many as the machine has pads.
|
|
#define PAD_DRIVE_COUNT 4
|
|
|
|
typedef struct {
|
|
uint8_t debug; // Step one instruction at a time, printing the registers.
|
|
uint8_t fast; // Ignore the cycle rate and run as fast as the host allows.
|
|
unsigned long cycles; // Stop after this many cycles. Zero means run until the program halts.
|
|
unsigned long diskCycles; // How long a block move takes. Zero is instant, and the default.
|
|
// ---- The drives, in the order they were named ----
|
|
//
|
|
// --disk given more than once fills them in turn, so the first is drive 0 and the machine
|
|
// has as many as were asked for. One name is the ordinary case and reads exactly as it
|
|
// did when there could only be one.
|
|
const char *disk; // Disk image to attach, or NULL for a machine with no disk.
|
|
const char *disks[DISK_DRIVE_COUNT];
|
|
int diskCount;
|
|
// A drive of this many blocks made of memory, taking the next drive number after the
|
|
// images above. Zero for a machine without one.
|
|
unsigned long ramDisk;
|
|
uint8_t writeProtect; // Attach the disk read only, the way a tab on a floppy would.
|
|
const char *screen; // Where to save a picture of the screen when the machine stops.
|
|
const char *keyboard; // Feed the console from this file as a keyboard, not a terminal.
|
|
const char *sound; // Where to save the samples the machine made, as raw 16 bit.
|
|
// ---- A controller made of a file ----
|
|
//
|
|
// One byte a frame, each byte the buttons held during it. Voyager reads a real pad, and
|
|
// its own tests run headless with nobody holding anything - so without this the device
|
|
// would be exercised only by somebody playing, which is exactly the state the console's
|
|
// line editing was in when it broke twice in two days.
|
|
const char *pads[PAD_DRIVE_COUNT];
|
|
int padCount; // --pad given more than once fills them in turn, like --disk.
|
|
// Where to write what each pad held, one byte a frame, in the format --pad reads.
|
|
const char *padRecord[PAD_DRIVE_COUNT];
|
|
int padRecordCount;
|
|
} EmulatorOptions;
|
|
|
|
uint8_t parseOptions(int argc, char *argv[], EmulatorOptions *options);
|
|
|
|
void printHelp(const char *programName);
|
|
|
|
uint8_t loadFile(const char *path, uint8_t *Program, uint8_t *Data);
|
|
|
|
void bootStrap(uint8_t *Program, uint8_t *Data);
|
|
|
|
void printRegisters(CPURegisters *cpu, uint8_t *Program, uint8_t *Data);
|
|
|
|
#endif // UTILITY_H
|