Record every pad, not the one that happened to be first
The first recording ever made with this came back 1,766 frames of nothing. It recorded pad NOUGHT and the controller was somewhere else - which pad one lands on is an accident of the host, the same accident that made Lunar Porter read all four in the first place - and a flight flown for the purpose was lost to it. So every pad is or-ed into the byte. A demo is a record of what somebody DID, and on a machine one person is playing the number it arrived on is not part of that. It plays back on pad nought, where --pad puts the first file given, and any program that reads more than one pad reads them or-ed anyway for exactly the same reason. --record-pad takes one file now rather than filling pads in turn, because there is nothing left for the second one to mean. The check for it plays a recording on pad ONE with nought holding nothing and requires the bytes back. That is the case that was missing: the round trip was tested and passed, on pad nought, which is the only pad it could not have gone wrong on. 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
eb695a3f3b
commit
de1857f5f7
@@ -238,13 +238,13 @@ uint8_t machineStart(Machine *m, const EmulatorOptions *options, const char *pro
|
|||||||
padFromFile(n, pad);
|
padFromFile(n, pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int n = 0; n < options->padRecordCount; n++) {
|
if (options->padRecord != NULL) {
|
||||||
FILE *pad = fopen(options->padRecord[n], "wb");
|
FILE *pad = fopen(options->padRecord, "wb");
|
||||||
if (pad == NULL) {
|
if (pad == NULL) {
|
||||||
fprintf(stderr, "Error: Couldn't write pad file: %s\n", options->padRecord[n]);
|
fprintf(stderr, "Error: Couldn't write pad file: %s\n", options->padRecord);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
padRecordTo(n, pad);
|
padRecordTo(pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->keyboard != NULL) {
|
if (options->keyboard != NULL) {
|
||||||
|
|||||||
+9
-10
@@ -10,7 +10,7 @@ static uint8_t held[PAD_COUNT];
|
|||||||
static FILE *recorded[PAD_COUNT];
|
static FILE *recorded[PAD_COUNT];
|
||||||
static uint8_t live[PAD_COUNT];
|
static uint8_t live[PAD_COUNT];
|
||||||
static int connected[PAD_COUNT];
|
static int connected[PAD_COUNT];
|
||||||
static FILE *recording[PAD_COUNT];
|
static FILE *recording;
|
||||||
|
|
||||||
// ---- The frame the recordings advance on ----
|
// ---- The frame the recordings advance on ----
|
||||||
//
|
//
|
||||||
@@ -43,11 +43,8 @@ static uint8_t effective(int which) {
|
|||||||
return (recorded[which] != NULL) ? held[which] : live[which];
|
return (recorded[which] != NULL) ? held[which] : live[which];
|
||||||
}
|
}
|
||||||
|
|
||||||
void padRecordTo(int which, FILE *file) {
|
void padRecordTo(FILE *file) {
|
||||||
if (which < 0 || which >= PAD_COUNT) {
|
recording = file;
|
||||||
return;
|
|
||||||
}
|
|
||||||
recording[which] = file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void padFromFile(int which, FILE *file) {
|
void padFromFile(int which, FILE *file) {
|
||||||
@@ -96,11 +93,13 @@ void padTick(unsigned long now) {
|
|||||||
// Flushed as it goes, because a recording is usually stopped by whoever is playing
|
// 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 would be a demo
|
// rather than by the program ending, and a demo lost to a buffer would be a demo
|
||||||
// flown twice.
|
// flown twice.
|
||||||
for (int n = 0; n < PAD_COUNT; n++) {
|
if (recording != NULL) {
|
||||||
if (recording[n] != NULL) {
|
uint8_t all = 0;
|
||||||
fputc(effective(n), recording[n]);
|
for (int n = 0; n < PAD_COUNT; n++) {
|
||||||
fflush(recording[n]);
|
all |= effective(n);
|
||||||
}
|
}
|
||||||
|
fputc(all, recording);
|
||||||
|
fflush(recording);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -86,10 +86,20 @@ void padSet(int which, int connected, uint8_t held);
|
|||||||
// worth testing: play a file while recording it and the same bytes come out.
|
// worth testing: play a file while recording it and the same bytes come out.
|
||||||
//
|
//
|
||||||
// It exists because some inputs cannot sensibly be written by hand. Flying a lander from one
|
// 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 four cells
|
// base to another is a few hundred frames of steering that has to arrive somewhere eight cells
|
||||||
// wide, and hand-authoring one is a piloting exercise rather than a test. Playing it once and
|
// wide, and hand-authoring one is a piloting exercise rather than a test. Playing it once and
|
||||||
// keeping what happened is the whole answer.
|
// keeping what happened is the whole answer.
|
||||||
void padRecordTo(int which, FILE *file);
|
//
|
||||||
|
// EVERY PAD AT ONCE, or-ed into one byte, and not one pad chosen by number. Which pad a
|
||||||
|
// controller lands on is an accident of the host - the first recording made with this came
|
||||||
|
// back 1,766 frames of nothing, because it recorded pad nought and the controller was
|
||||||
|
// somewhere else. A demo is a record of what somebody DID, and on a machine one person is
|
||||||
|
// playing the number it arrived on is not part of that.
|
||||||
|
//
|
||||||
|
// A recording therefore plays back on pad nought, which is where --pad puts the first file
|
||||||
|
// given. Any program that reads more than one pad reads them or-ed anyway, for exactly the
|
||||||
|
// same reason.
|
||||||
|
void padRecordTo(FILE *file);
|
||||||
|
|
||||||
void padTick(unsigned long now);
|
void padTick(unsigned long now);
|
||||||
uint8_t padRead(uint8_t port);
|
uint8_t padRead(uint8_t port);
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ void printHelp(const char *programName) {
|
|||||||
printf(" 1 right, 2 left, 4 down, 8 up, 16 A, 32 B, 64 start,\n");
|
printf(" 1 right, 2 left, 4 down, 8 up, 16 A, 32 B, 64 start,\n");
|
||||||
printf(" 128 select.\n");
|
printf(" 128 select.\n");
|
||||||
printf(" -Y, --record-pad FILE\n");
|
printf(" -Y, --record-pad FILE\n");
|
||||||
printf(" Write what a controller held, one byte a frame, in the\n");
|
printf(" Write what the controllers held, one byte a frame, in the\n");
|
||||||
printf(" format --pad reads. Given again for the next pad. Play a\n");
|
printf(" format --pad reads. Every pad at once, because which one a\n");
|
||||||
printf(" thing once and keep what happened.\n");
|
printf(" controller lands on is the host's business. Play a thing\n");
|
||||||
|
printf(" once and keep what happened.\n");
|
||||||
printf(" -N, --sound FILE Save every sample the machine made, as raw signed 16 bit\n");
|
printf(" -N, --sound FILE Save every sample the machine made, as raw signed 16 bit\n");
|
||||||
printf(" at 48kHz. What --screen is for a picture: the only way to\n");
|
printf(" at 48kHz. What --screen is for a picture: the only way to\n");
|
||||||
printf(" check a sound on a machine with no speaker.\n");
|
printf(" check a sound on a machine with no speaker.\n");
|
||||||
@@ -142,9 +143,7 @@ uint8_t parseOptions(int argc, char *argv[], EmulatorOptions *options) {
|
|||||||
options->sound = optarg;
|
options->sound = optarg;
|
||||||
break;
|
break;
|
||||||
case 'Y':
|
case 'Y':
|
||||||
if (options->padRecordCount < PAD_DRIVE_COUNT) {
|
options->padRecord = optarg;
|
||||||
options->padRecord[options->padRecordCount++] = optarg;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
// Fills the pads in turn, the same way --disk fills the drives, so the first
|
// Fills the pads in turn, the same way --disk fills the drives, so the first
|
||||||
|
|||||||
@@ -47,9 +47,10 @@ typedef struct {
|
|||||||
// line editing was in when it broke twice in two days.
|
// line editing was in when it broke twice in two days.
|
||||||
const char *pads[PAD_DRIVE_COUNT];
|
const char *pads[PAD_DRIVE_COUNT];
|
||||||
int padCount; // --pad given more than once fills them in turn, like --disk.
|
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.
|
// Where to write what the controllers held, one byte a frame, in the format --pad reads.
|
||||||
const char *padRecord[PAD_DRIVE_COUNT];
|
// Every pad or-ed together, because which one a controller lands on is the host's business
|
||||||
int padRecordCount;
|
// and a demo is a record of what somebody did.
|
||||||
|
const char *padRecord;
|
||||||
} EmulatorOptions;
|
} EmulatorOptions;
|
||||||
|
|
||||||
uint8_t parseOptions(int argc, char *argv[], EmulatorOptions *options);
|
uint8_t parseOptions(int argc, char *argv[], EmulatorOptions *options);
|
||||||
|
|||||||
@@ -76,6 +76,13 @@ A recording is written one byte a frame and is only worth having if playing it b
|
|||||||
was recorded, so the strongest form of that claim is checked: a recording is made **of a
|
was recorded, so the strongest form of that claim is checked: a recording is made **of a
|
||||||
playback**, and the bytes coming out have to be the bytes that went in.
|
playback**, and the bytes coming out have to be the bytes that went in.
|
||||||
|
|
||||||
|
**Every pad is recorded at once**, or-ed into one byte, rather than one pad chosen by number.
|
||||||
|
Which pad a controller lands on is an accident of the host: the first recording ever made with
|
||||||
|
this came back 1,766 frames of nothing, because it recorded pad 0 and the controller was
|
||||||
|
somewhere else, and the flight had to be flown again for nothing. A demo is a record of what
|
||||||
|
somebody *did*, and on a machine one person is playing the number it arrived on is not part of
|
||||||
|
that.
|
||||||
|
|
||||||
It exists because some inputs cannot sensibly be written by hand. Flying a lander from one
|
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
|
base to another is a few hundred frames of steering that has to arrive somewhere eight cells
|
||||||
wide, and several attempts at authoring one by hand got within two columns and no closer. That
|
wide, and several attempts at authoring one by hand got within two columns and no closer. That
|
||||||
|
|||||||
@@ -65,6 +65,26 @@ print('yes' if again[:len(first)] == first else 'no')
|
|||||||
&& result ok "a recording of a playback is the playback" "byte for byte, in and out" \
|
&& result ok "a recording of a playback is the playback" "byte for byte, in and out" \
|
||||||
|| result no "a recording of a playback is the playback" "the bytes differ"
|
|| result no "a recording of a playback is the playback" "the bytes differ"
|
||||||
|
|
||||||
|
# ---- And it does not matter which controller it was ----
|
||||||
|
#
|
||||||
|
# The first recording ever made with this came back 1,766 frames of nothing. It recorded pad
|
||||||
|
# NOUGHT and the controller was somewhere else - which pad one lands on is an accident of the
|
||||||
|
# host, and the flight had to be flown again for nothing.
|
||||||
|
#
|
||||||
|
# So every pad is or-ed into the byte. Here the same recording is played on pad ONE, with
|
||||||
|
# nought holding nothing, and what comes out has to be what went in.
|
||||||
|
python3 -c "open('$BUILD/idle.pad','wb').write(b'\x00' * 64)"
|
||||||
|
"$EMU" --fast --pad "$BUILD/idle.pad" --pad "$ROOT/Tests/input/padTest.pad" \
|
||||||
|
--record-pad "$BUILD/padone.pad" "$BUILD/padTest.bin" > "$BUILD/padone.out" 2>&1
|
||||||
|
ELSEWHERE="$(python3 -c "
|
||||||
|
first = open('$ROOT/Tests/input/padTest.pad', 'rb').read()
|
||||||
|
again = open('$BUILD/padone.pad', 'rb').read()
|
||||||
|
print('yes' if again[:len(first)] == first else 'no')
|
||||||
|
" 2>/dev/null || echo error)"
|
||||||
|
[ "$ELSEWHERE" = "yes" ] \
|
||||||
|
&& result ok "a controller on any pad is recorded" "flown on pad one, written all the same" \
|
||||||
|
|| result no "a controller on any pad is recorded" "the bytes differ"
|
||||||
|
|
||||||
# ---- One byte a frame, and the frame is the machine's ----
|
# ---- One byte a frame, and the frame is the machine's ----
|
||||||
#
|
#
|
||||||
# A recording is a TIMELINE. If it were a byte a read, a program that polled twice in one
|
# A recording is a TIMELINE. If it were a byte a read, a program that polled twice in one
|
||||||
|
|||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user