Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- I've been trying, off and on, to verify other movies using the Arduino USB system, with no luck so far. I have tried:
- * COOL LINK TO MOVIE 1718
- * COOL LINK TO MOVIE 1781
- * COOL LINK TO MOVIE 2796
- * COOL LINK TO MOVIE 3637
- * COOL LINK TO MOVIE 3706Here, you can see some failed attempts at syncing Dangerous Dave. Seek to 8:10 for the attempt that gets the farthest, into level 3. The farthest I think I've ever seen it get is the end of level 3, stopping just short of the door.
- ![Video](https://archive.org/details/20220124-dave-pc-tas-verification-attempts)
- There are two things I want to talk about:
- * It's not immediately apparent in the video, but the game is running slower than it should. This is because the game loop is locked to the screen refresh, and this computer's video card outputs a 60 Hz signal despite the game using [mode 13h](https://en.wikipedia.org/wiki/Mode_13h), which is supposed to be 320x200@70Hz.
- * The refresh rate discrepancy should be okay, because the Arduino playback device reads the VGA VSYNC signal and adjusts its own timing to the vertical refresh, rather than using absolute timestamps. But it still doesn't work.SIZE IS QUITE HARD -> 60 Hz video output
- My preliminary attempts were very far from syncing, more than can be explained by the gain or loss of a few milliseconds. The cause was unexpected to me: the video signal output by the PC's VGA port had a refresh rate of 60 Hz, not 70 Hz as it should have had. I checked this by enabling an option on my monitor to show the current video mode. You can also verify it by counting cycles of the trophy animation in level 1 of Dangerous Dave. On hardware the animation runs slower than in emulation with DOSBox.
- Every computer and graphics card I had at hand had the same problem, outputting a 60 Hz signal for graphics modes that are nominally 70 Hz:
- SKIPPED TABLES THEY ARE DIFFICULTI have not found many web pages talking about this phenomenon. Possibly it's not well known. Here are a few references:
- https://www.vogonswiki.com/index.php/General_monitor_advices ([archive](https://web.archive.org/web/20210724110253/http://www.vogonswiki.com/index.php/General_monitor_advices))
- > Many LCD monitors from the recommended time period already have digital DVI inputs that can provide better image quality than the analog VGA input. Still, using DVI is not recommended for DOS games because refresh rate issues may appear where VGA modes run at 60 Hz refresh rate instead of 70 Hz standard. A typical indication are speed and/or sound issues in games and demos that use the refresh rate for time synchronization.
- https://doomwiki.org/wiki/Aspect_ratio#Source_ports ([archive](https://web.archive.org/web/20220124051813/https://doomwiki.org/wiki/Aspect_ratio#Source_ports))
- > Support for Mode 13h, and especially for undocumented VGA tweaks like those used by Doom, has dropped to near non-existence in modern video hardware and operating systems.
- https://doomwiki.org/wiki/Talk:Aspect_ratio#Resolutions_Do_Not_Have_an_Aspect_Ratio ([archive](https://web.archive.org/web/20220124052010/https://doomwiki.org/wiki/Talk:Aspect_ratio#Resolutions_Do_Not_Have_an_Aspect_Ratio))
- > The problem arises under modern machines that do not actually support mode 13h - this support is becoming exceedingly rare, especially on newer ATi video cards, some of which will not even set into ANY screen mode lower than 640x480.
- https://www.reddit.com/r/dosgaming/comments/cc4ebh/best_way_to_play_dos_games/etlnltv/ ([archive](https://web.archive.org/web/20220124052141/https://old.reddit.com/r/dosgaming/comments/cc4ebh/best_way_to_play_dos_games/etlnltv/))
- > Modern video hardware will support a few lowest-common denominator video modes, but is unlikely to be fully compatible.
- Somewhat related: https://www.vogons.org/viewtopic.php?t=68506 ([archive](https://web.archive.org/web/20220124052218/https://www.vogons.org/viewtopic.php?t=68506))
- > TLDR: I patched the VGA BIOS ROM of my 9600 Pro to output 1280x1024@70.08 Hz instead of the VESA default of 75 Hz.
- I would have assumed that playing games on hardware under FreeDOS was a more authentic experience than playing in emulation, but that is not necessarily so. Whatever Crystal Caves does with the video mode fails in an especially noticeable way: it's like the top row of pixels gets stretched over the whole screen. I suppose the only remedy is to track down a vintage video card that implements the right timings. I'm curious what all-in-one games-oriented hardware packages like the [weeCee](https://www.pcbway.com/project/shareproject/weeCee___Tiny_DOS_Gaming_PC.html) ([LGR video](https://www.youtube.com/watch?v=USHvvSbYmJA)) do with video timing.
- SIZE IS QUITE HARD -> VGA VSYNC to the Arduino
- I altered the Arduino circuit and program to be able to read VGA VSYNC signals. I connect a [Y splitter](https://www.sfcable.com/hd15-vga-splitter-cables.html) to the PC's VGA port. One end of the splitter goes to the monitor and [pin 14 (the VSYNC pin)](https://en.wikipedia.org/wiki/VGA_connector) of the other end goes to one of the Arduino's digital inputs.
- [![Image](https://www.bamsoftware.com/computers/tasvideos/arduino-verification-board-20220124.sm.jpg)](https://www.bamsoftware.com/computers/tasvideos/arduino-verification-board-20220124.jpg)
- The [Wiki: JRSR format](https://tasvideos.org/EmulatorResources/JPC/JRSRFormat) does not store video refreshes, only nanosecond timestamps. But you can often infer where the video refreshes are, because of how authors tend to make TASes: hit frame advance, enter inputs for the frame, then hit frame advance again. Most input events occur at times that are multiples of the video refresh interval, plus a constant. The constant depends, usually, on non-frame-aligned activities at the start of the movie, the boot process and DOS prompt.
- For example, here are the event timestamps from COOL LINK TO MOVIE 1718, modulo the VGA refresh interval of 14.268 ms (= 800 × 449 / 25.175 MHz). Notice how most timestamps are aligned at an offset of about 9.507 ms. There is a secondary line of timestamps about 1.3 ms after the primary one—this happend when there are [Wiki: two events in the same frame](https://tasvideos.org/EmulatorResources/JPC/JRSRFormat#r1).
- [![Image](https://www.bamsoftware.com/computers/tasvideos/eventtimestamps-ilari-ddave.png)](https://www.bamsoftware.com/computers/tasvideos/eventtimestamps-ilari-ddave.png)
- Similarly, there are strong primary, secondary, and tertiary timing alignments in the events of COOL LINK TO MOVIE 3706. But there is also a substantial minority of events that are not so well aligned. I looked into this case—the unaligned events are where 200 ms timed advance was used, rather than frame advance.
- [![Image](https://www.bamsoftware.com/computers/tasvideos/eventtimestamps-dungeonfacts-crystalcaves1.png)](https://www.bamsoftware.com/computers/tasvideos/eventtimestamps-dungeonfacts-crystalcaves1.png)
- In contrast, the timestamps of COOL LINK TO MOVIE 2119 are mostly not aligned to the VGA refresh. This suggests that this TAS was not made using frame advance. (Another reason for a lack of alignment could be an incorrect refresh rate, but I'm pretty sure this game is 70 Hz like the rest.)
- [![Image](https://www.bamsoftware.com/computers/tasvideos/eventtimestamps-turskailari-avoidthenoid.png)](https://www.bamsoftware.com/computers/tasvideos/eventtimestamps-turskailari-avoidthenoid.png)
- I wrote a program, guess-vsync, that tries to infer VSYNC parameters, given the event timestamps in a JRSR file. With these VSYNC parameters, the program gen-table produce a table of input events; not only `RELEASE`, `PRESS`, and `WAIT` events, but also `VSYNC` which means to wait until the next VSYNC pulse from the VGA port. Having a reference for the PC's timing, the Arduino should (in principle) be able to cope with timing variations, like the 60 Hz for 70 Hz I talked about above. The source code of these programs is available in [20220124-dave-pc-tas-verification-attempts.zip](https://archive.org/download/20220124-dave-pc-tas-verification-attempts/20220124-dave-pc-tas-verification-attempts.zip), or from `git clone https://www.bamsoftware.com/git/pc-tas-arduino.git`.
- Here's what I did to program the Arduino for the video above. First, run gen-table, just to get a timestamp for when the boot-related events are over and we're ready to start the program. We will pass this timestamp to later commands as the argument to the `--skip` option.
- ```
- $ ./gen-table ilari-ddave.jrsr | less
- ...
- // 489061776ns
- RELEASE, KEY_D,
- WAIT, 0x9a, 0x02, // 666
- // 489728436ns
- PRESS, KEY_A,
- WAIT, 0x9b, 0x02, // 667
- // 490395096ns
- RELEASE, KEY_A,
- WAIT, 0x9b, 0x02, // 667
- // 491061756ns
- PRESS, KEY_V,
- WAIT, 0x9a, 0x02, // 666
- // 491728416ns
- RELEASE, KEY_V,
- WAIT, 0x9b, 0x02, // 667
- // 492395076ns
- PRESS, KEY_E,
- WAIT, 0x9b, 0x02, // 667
- // 493061736ns
- RELEASE, KEY_E,
- WAIT, 0x9a, 0x02, // 666
- // 493728396ns
- PRESS, KEY_ENTER,
- ```
- From this, we see that the timestamp of pressing Enter to start the program is `493728396ns`. Now we run guess-vsync, telling it to ignore the events before that timestamp:
- ```
- $ ./guess-vsync --skip 493728396ns ilari-ddave.jrsr
- 14268123.1380338ns+9507455.38628574ns
- average delay between VSYNC and event: 0.386ms
- ```
- The interval+offset `14268123.1380338ns+9507455.38628574ns` is the guess for the VSYNC parameters. We'll tweak this guess, appending `-10%`, to shift the VSYNC phase earlier by 10% of a frame—this is because we don't want our inputs to occur at the exact same time as a vertical refresh. Now run gen-table again, this time passing the VSYNC parameters and the skip timestamp, and save the output to playback/table.h.orig:
- ```
- $ ./gen-table --skip 493728396ns --vsync 14268123.1380338ns+9507455.38628574ns-10% ilari-ddave.jrsr > playback/table.h.orig
- ```
- playback/table.h.orig contains a schedule of events. `VSYNC, *n*,` means to wait for that many vertical refreshes.
- ```
- const uint8_t TABLE[] PROGMEM = {
- VSYNC, 1,
- WAIT, 0x13, 0x02, // 531
- // 493728396ns
- PRESS, KEY_ENTER,
- WAIT, 0x9b, 0x02, // 667
- // 494395056ns
- RELEASE, KEY_ENTER,
- VSYNC, 131,
- WAIT, 0xaf, 0x05, // 1455
- // 2363776362ns
- PRESS, KEY_ENTER,
- VSYNC, 1,
- WAIT, 0xae, 0x05, // 1454
- // 2378042886ns
- RELEASE, KEY_ENTER,
- VSYNC, 8,
- WAIT, 0xa1, 0x05, // 1441
- // 2492175078ns
- PRESS, KEY_RIGHT_ARROW,
- VSYNC, 10,
- WAIT, 0xd4, 0x05, // 1492
- // 2634906984ns
- PRESS, KEY_UP_ARROW,
- VSYNC, 60,
- ```
- We are not quite done yet. The timing of events is most uncertain at the beginning of the movie. I manually tuned the timing of the first few events and saved it as playback/table.h. Then I uploaded the program to the Arduino.
- ```diff
- --- playback/table.h.orig 2022-01-23 20:26:01.561697327 -0700
- +++ playback/table.h 2022-01-23 20:25:59.297830884 -0700
- @@ -18,15 +18,14 @@
- WAIT, 0x9b, 0x02, // 667
- // 494395056ns
- RELEASE, KEY_ENTER,
- - VSYNC, 131,
- + VSYNC, 2,
- WAIT, 0xaf, 0x05, // 1455
- // 2363776362ns
- PRESS, KEY_ENTER,
- - VSYNC, 1,
- WAIT, 0xae, 0x05, // 1454
- // 2378042886ns
- RELEASE, KEY_ENTER,
- - VSYNC, 8,
- + VSYNC, 150,
- WAIT, 0xa1, 0x05, // 1441
- // 2492175078ns
- PRESS, KEY_RIGHT_ARROW,
- ```
- SIZE IS QUITE HARD -> But it still doesn't work
- As the video proves, all these considerations are still not enough for successful verification. Here are some of the things I've tried:
- * Scheduling inputs early in the frame (`--vsync interval+offset-10%`)
- * Scheduling inputs late in the frame (`--vsync interval+offset+10%`)
- * Removing `WAIT` events and using only `VSYNC` for timing
- * Different debounce parameters in the VSYNC interrupt handlerI've noticed the first run after a reboot is likely to desync earlier. My guess as to the cause is that the program is slower to start up when the disk cache is cold. Timings are affected by disk delays more than I would have thought. The disk image of [[[Entering submission]]]2961[[[Exiting submission]]] contains only a single file, DAVE.EXE. Once, I mistakenly ran the input file in the emulator with a disk image containing three files: DAVE.EXE, DSCORES.DAV, and EGADAVE.DAV. The presence of those two extra files was enough to desync emulator playback in level 2, despite the fact that the main program does not even open those other files by that point (I think).
- I started the movie with pressing Enter at the DOS prompt. This is problematic because it means there is a video mode change in the middle of the event stream. I had initially tried to start playback at the title screen, or at the beginning of the first level, but that was worse. There's a sort of "frame rule" at the beginning of a level, while Dave is blinking. He must finish a blinking cycle before he can begin moving, which means that if you start the playback at a random point in the blinking cycle, the inputs that follow will be off by a random amount.
- I am pretty naive about electrical engineering. To give you an idea: I didn't know what a [pull-down resistor](https://en.wikipedia.org/wiki/Pull-up_resistor) was until a few months ago when I had to wire up a pushbutton for the first iteration of the playback device. It's possible that the circuit I'm using for reading VSYNC signals doesn't make sense, or is susceptible to noise that causes timing errors. I'm not sure it's right that the VSYNC GND pin is not used, nor that the VGA input and the rest of the circuit do not have the same ground reference potential. Here is the circuit:
- ![Image](https://www.bamsoftware.com/computers/tasvideos/arduino-verification-circuit-20220124.png)
- [arduino-verification-circuit-20220124.cddx](https://www.bamsoftware.com/computers/tasvideos/arduino-verification-circuit-20220124.cddx) from https://www.circuit-diagram.org/
- I'm using the [`attachInterrupt`](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) interface to react to changes on the VSYNC input pin; i.e., `attachInterrupt(digitalPinToInterrupt(VSYNC_PIN), vsync_isr, FALLING)`. I've read that [`attachinterrupt` goes through a lot of abstractions](https://forum.arduino.cc/t/due-attachinterrupt-is-a-bit-slow-example-code-with-less-overhead/288890) that add overhead, and that for high performance you need to program interrupts at a lower level. But the overhead estimates I've seen are in the realm of single-digit microseconds, which seems like it shouldn't matter much for VGA vertical refresh, which is in the realm of milliseconds. I think that the USB HID key events can be no more precise than 1 ms, anyway.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement