Link to video
More attempts, source code, etc.
This is
[4345] DOS The Adventures of Captain Comic: Episode 1 - Planet of Death by Sand & Kabuto in 06:30.98 played back on a laptop running
FreeDOS 1.3 RC4 connected to an
Arduino Leonardo. The Leonardo model has the special feature that it can act as a USB keyboard. The board is programmed with the sequence of timed keypresses from the emulator movie file. It presses the right keys at the right times, as if it were a USB keyboard plugged into the laptop.
The source code necessary to program the Arduino is available in
comic-arduino-verification.zip, or in the tas/arduino-verification subdirectory of
git clone https://www.bamsoftware.com/git/comic.git. There is a script there that parses a
JRSR file and outputs a C++ array of microsecond timestamps and key press/release events:
$ ./jrsr comic1.jrsr
const struct {
unsigned long delta_micros;
unsigned char press;
KeyboardKeycode keycode;
} TABLE[] PROGMEM = {
{ 0, 1, KEY_C },
{ 667, 1, KEY_O },
{ 666, 0, KEY_C },
{ 667, 0, KEY_O },
{ 8628995, 1, KEY_RIGHT_ARROW},
// ...
};
The Arduino is wired to a simple circuit with a button to begin playback. I copied the circuit from the Arduino
KeyboardMessage tutorial. I paid about USD 40 all together for the Arduino, breadboard, pushbutton, jumper wires, etc.
I made some changes to the original JRSR file, for convenience or to get the run to sync. You can see the changes by diffing tas/comic1.jrsr (original) and tas/arduino-verification/comic1.jrsr (modified) in the Git repo. Apart from the first two changes in the list, the modified file still syncs in JPC-RR.
- I was not interested in playing back the boot sequence, so I trimmed the inputs up to the title screen. You have to boot the computer and run COMIC.EXE yourself, then press the button to begin playback.
- I shortened the delay between the title screen and the first game inputs by about 165 ms (equivalent to 1.5 game ticks). This is for sync; without it, Comic always made the first jump too late. I attribute this to the real laptop running the game's initialization code faster than the emulated CPU does.
- After the first death in CASTLE0, I moved the release of the Right key to happen about 1 s earlier. I don't know why this was necessary, but without it, the game would not register this particular key release and would desync.
- The original JRSR file continues holding the Left and Space keys at the end of input. On a real PC, this led to automatic keyboard repeat and annoying beeping at the high score screen. I added release events for these keys at the end of the input.
Even with the above changes to the JRSR file, the playback does not sync every time. At the
archive page, you can see all 11 attempts I made, of which 3 synced to the end. The embedded video above is attempt #7.
I attribute the sync failures to a lack of synchronization between the externally timed keyboard inputs and the game loop. In the emulator, inputs occur at certain times relative to the game's tick cycle. Because the USB keyboard inputs are not locked to the game loop, they may occur earlier or later in each tick, perhaps sometimes even splitting inputs that are meant to be in the same tick across adjacent ticks. Captain Comic is a forgiving game in that it has ticks of long duration (110 ms). The same technique may not work directly with other games that require more precise timing—though an idea for the future is to connect the VSYNC pin of the laptop's VGA port to the Arduino, as a timing source for games whose game loop is locked to the screen refresh.