From DaphneWiki

Jump to: navigation, search




As it stands today, the VLDP-HW solution consists of two parts:

  1. The controller
  2. The media server

These two parts communicate via a serial cable running at 115200 bits per second.

The Controller

This consists of a PCB with an Atmel AVR microcontroller on it, along with RCA video input and output jacks, and interfaces matching the laserdisc players that are being replaced. Its job is to communicate with original arcade game hardware (such as a Dragon's Lair PCB) and mimic the original laserdisc player's behavior which it (the controller) is replacing.

AVR Fuse Bits

Just for future reference, when hooking up an AVR to an external crystal, the fuse bit setting should be: Full Swing, Crystal Oscillator (and I prefer slowly rising power just to be safe). See the AVR datasheet for details about what these settings mean. Remember to disable the clock divider that is shipped by default (CLKDIV).

The media server

This consists of a typical linux-based PC running a stripped-down version of DAPHNE that will listen for commands on the serial port and display fields/frames and play audio as it receives commands. The speed of the PC will be more than adequate to present the video and audio at the full speed of the original laserdisc player, and any track/field can be skipped to out of sequence in order to support tricky requirements for games such as Firefox.

Enabled s-video on Beagleboard xM

There is a lot of conflicting documentation about how to enable s-video mode on the beagle broad.

Here is the current method I use until I can find a more permanent method:

  • Plug in a serial port to the beagleboard and fire up a term program at 115200 bps, 8N1.
  • Boot the beagle broad.
  • the serial port will show boot up information and allow you to interrupt the boot process
  • hit a key and interrupt the boot process
  • Then paste this information at the prompt:
setenv mmcargs 'setenv bootargs console=${console} ${optargs} mpurate=${mpurate} buddy=${buddy} vram=${vram} omapfb.mode=tv:ntsc omapdss.def_disp=tv root=${mmcroot} rootfstype=${mmcrootfstype}'

If you don't put in tv:ntsc (for example if you just put 'tv') it seems to default to PAL mode instead.

Type "printenv" by itself to see current environment variables. You may need to modify these instructions slightly depending on what install you're using.

Resources for OpenMAX jpeg decoding

I haven't figured out the best way to do hardware jpeg decoding, but OpenMAX may be a promising avenue.

Here are some links of sample source code:



Raspberry pi stuff:




Beagleboard/OMAP stuff:



Chromium has some openmax stuff in it:


Custom linux kernel notes

Config settings:

General setup->Kernel performance events and counters->(enable it)
General setup->Profiling support (yes)
General setup->OProfile system profiling (M)
Kernel hacking->Debug Filesystem (yes)
Kernel hacking->Enable dynamic printk() support (yes)

See http://elinux.org/RPi_Kernel_Compilation#Ubunt for mostly helpful instructions. Ignore most of the instructions for transferring the build except:

To prep the modules for installation:

mkdir /tmp/modules
make ARCH=arm modules_install INSTALL_MOD_PATH=/tmp/modules

To prep the kernel image for installation:

Copy $SRC/linux/arch/arm/boot/Image to /boot/kernel.img on the pi.

Serial Communications

TTL Serial format

The format that the AVR and Raspberry Pi use (and hence Dexter) is the TTL serial format described here: https://learn.sparkfun.com/tutorials/serial-communication/rules-of-serial

Specifically, the start bit is always low, the stop bit is always high, 0 data bits are low, 1 data bits are high, and the least significant bit comes first.

The protocol

The communication protocol used by the controller and media server is as follows:

All communication, regardless of which direction it is going, must conform to the following structure (with one exception, listed below):

An array of bytes:

 1 'sync' byte: must always will be 00
1 byte indicating length of data
1 byte which is the length of data XOR'd by 255 (to quickly verify correct length of packet)
 N bytes (the data itself)
 2 bytes representing CCITT CRC-16 of the preceding data

So for example, a complete "packet" might look like this:

00 01 FE 00 E1 F0

Exception to the above rule

The media server will send 2 length bytes and 2 XOR bytes instead of 1 and 1. This is because the media server may need to send a packet larger than 255 bytes. These 16-bit values will be in little endian format.

So for example, a complete "packet" from media server to controller might look like this:

00 01 00 FE FF 00 E1 F0

Typical Flow

The controller upon startup will load its previous settings from EEPROM and immediately begin emulating a laserdisc player so that the arcade hardware has something to talk to. If it has no settings in its EEPROM, then it will default to emulating a LD-V1000 using a laserdisc with a 2:2 VBI pattern. The controller will begin sending commands over the serial port to the media server, which will initially be lost while the media server boots. However, this is okay because the media server is designed to be tolerant of missed/dropped commands.

Meanwhile, the media server will be booting up. Once it finishes booting, it must send a S)tatus command to the controller to see what settings the controller is currently using. If the controller is found to be using the same settings as the media server, the media server will just start processing commands sent from the controller. This is the typical use case. If the media server finds that the controller is using different settings, then the media server will send a H)ello command which contains new information about which laserdisc player to emulate and what type of VBI patterns the emulated disc should have.

Commands from controller to media server

Each command starts with an ASCII letter and proceeds with one or more bytes.

Command letter Name Description Example
B Blank Screen A single ASCII 'B' will indicate to make the screen black, to simulate original behavior during seeking.
D Disc switch request The D will be followed by a single byte, which is the disc id of the disc that controller wishes media server to change to. This command must be idempotent.
E Error log message Same format as regular log command. None, too lazy
F Show Field The F will be followed by 4 bytes in little-endian format representing which absolute field to display with the top two bits indicating audio muting. The absolute field is computed by multiplying the track number by 2 and adding the relative field (will either be 0 for top or 1 for bottom). The field should be displayed during the next vsync on the media server. Audio, if enabled, should be played when the field is displayed. Assuming the media server's video is outputting at 60 Hz, once it gets in sync with the controller it should render a smooth stream of fields and audio because new field commands will be coming in between every field. NOTE: it doesn't matter that the media will be in effect lagged by 1 field, because the user won't notice. The main problem to avoid is displaying the wrong fields because the user will notice that. The audio bits (top two bits) are 0: both channels muted, 1: only left channel, 2: only right channel, 3: both channels active. 0x00 0x05 0xFA 0x46 0x01 0x00 0x00 0x00 0xBB 0x55 <-- display absolute field 0x00000001 with both audio channels muted
G Show Field with Text Overlay Just like the F command but accepts an additional byte on the end which corresponds to which text overlay ID to display. If the text overlay ID has not been received (due to a corrupted/dropped command), the media server must respond with a 'T' response so that the controller can re-send the text information.
H Hello The H byte will be sent alone and is used to tell the media server that the controller has just rebooted and may need to have its settings checked.
L Log Message The L will be followed by an ASCII string containing a log message from the controller to the media server. Since the controller's only way to communicate to the outside world is through the serial port, it will log messages through the media server. 0x00 0x03 0xFC 0x4c 0x48 0x69 0xdd 0xba <-- a log message that simply says "Hi"
l Log Message from AUX AVR Same as regular L but comes from the AUX AVR
N build Number Sends a 32-bit little endian build number so that media server knows whether to update the firmware of the controller or not. The lower 16-bits are the CRC of the current AVR firmware (little endian).
n build Number from AUX AVR Same as N, but comes from the AUX AVR, not the main AVR. A -1 may be returned instead of the build number to indicate the the AUX AVR is not currently available (if a serial player is active, the AUX AVR can't be running because it shares the same USART)
S Settings Message This sends back a byte with the value of 0x13 (version id corresponding to the information presented here in this wiki), followed by the 2-byte "persistent" identifier that the media server sent with the last hello command (see hello command described below). If there is no previous record of this, then two 0's will be sent instead. After this will be one byte to indicate the controller's DIAGNOSE state (0 is off, non-0 is on), followed one byte to indicate the controller's active laserdisc player (corresponding to the LDP type values in the HELLO packet), followed by one byte to indicate whether laserdisc player auto-detection is enabled (0 is disabled, non-zero is enabled), followed by one byte to indicate the disc id of the active disc (0 if not available), followed by the 2-byte "dynamic" identifier that the media server sent with the last hello command (0 if none). The active laserdisc player may be either the auto-detected player or the manually selected player. (none, too lazy hehe)
T Change LDP-1450 text overlay settings The T is followed by an arbitrary (but unique) ID byte, a byte offset indicating which byte of the buffer to start at (usually 0), a byte indicating X coordinate, a byte indicating Y coordinate, a byte indicating the LDP-1450 text 'mode', and then 32 bytes containing the actual buffer. Note that the text won't actually be displayed by this command; the 'G' command will display the text. The ID is only used so that the media server can detect whether it has missed the most recent text overlay command from the controller.
X Log byte sent to Dexter from game hardware The X is followed by a single byte. This is used to log I/O between Dexter and game hardware.
Y Log byte sent from Dexter to game hardware. The Y is followed by a single byte. This is used to log I/O between Dexter and game hardware.
z AUX AVR request firmware page This is in the exact same format as the Z command from the bootloader section that requests a page. The only difference is that this command is being passed through the controller from the AUX AVR to the media server.

Commands from media server to controller

Each command is a single ASCII letter (for now).

Command letter Name Description
A change Active disc The ASCII 'A' byte will be followed by a single byte indicating the new active disc's ID. (this is needed in case controller wants an active disc that the media server doesn't have)
H Hello (Apply new settings) The ASCII 'H' byte will be followed by new settings for the controller, detailed below.
D Disc switch response The ASCII 'D' byte followed by one byte indicating the disc id that was requested to change to, followed by one byte indicating success or failure (1 is success, 0 is failure). Should be throttled by media server so as to only send every .5 seconds or something.
N Request current build number Request controller's firmware's build number (hard-coded at compile time) to determine whether firmware update is needed.
n Request current build number of AUX AVR Same as 'N' command but gets passed through to the AUX AVR.
O Send next field that has a stop code in it The ASCII 'O' byte followed by the 32-bit field number that is the next field with a stop code. This is because the AVR doesn't have enough RAM to pre-cache all of this info so it must be sent as needed.
p Send firmware page to AUX AVR This is in the exact same format as the 'P' command from the bootloader section. The only difference is that this is to be passed through to the AUX AVR.
S Request current settings Requests from the controller which settings it is currently using. This is so the media server knows 1) the version of the serial protocol that the controller is using and 2) configurable settings that the controller is using such as the current laserdisc player type.
T Request most recent text overlay command be re-sent The media server has received a G command with a unique ID that it has not received, thus indicating that a former T command was dropped due to data corruption and needs to be re-sent. The controller will resend the most recent T command when it receives this.
Z Force firmware update Tells controller to go into firmware update mode (ie run the boot loader, see below).
z Force AUX avr firmware update Tells controller to tell the AUX avr (VBI injector) to go into firmware mode (ie run its boot loader, see boot loader section below).

The media server's Hello command

The media server's H)ello command will be as follows:

  1. 1 byte indicating whether these settings should be written to the controller's EEPROM+RAM or just stored to the controller's RAM. 0 = store only to RAM, non-zero = store to both EEPROM+RAM.
  2. 2 byte arbitrary "persistent" identifier (probably a checksum) chosen by the media server which will be returned when the media server requests a status. This allows the media server to determine whether or not to re-send settings to the controller.
  3. 1 byte indicating which type of laserdisc player to emulate
    1. 1 LD-V1000 Standard
    2. 2 LD-V1000 Badlands modified version
    3. 3 PR-7820
    4. 4 VP931 (Firefox)
    5. 5 PR-8210
    6. 6 PR-8210A
    7. 7 VP932 (Dragon's Lair euro)
    8. 8 VP-380 (Dragon's Lair 2 euro)
    9. 9 Simutrek (Cube Quest)
    10. 10 LD-V8000
    11. 11 LDP-1450
    12. 12 LDP-1000A
    13. 13 VIP9500SG (Astron Belt)
    14. 0 means 'none', which is a valid scenario when auto-detection is enabled.
  4. 1 byte indicating laserdisc player behavior
    1. Bit 0 is clear if disc spin-up delay is authentic, or 1 is spin-up time is minimal
    2. Bit 1 is clear if seek delay is authentic, or 1 if seek delay is minimal
    3. Bit 2 is clear if screen should go blank during seeks or 1 if screen should hold its last frame during seeks
    4. Bit 3 is set if diagnostics mode is enabled or clear if disabled
    5. Bit 4 is clear is video signal should NOT be muted during searching (default behavior), or set if video signal should be muted during seeking (MACH 3 required behavior). May be ignored for some laserdisc player modes signal but must be muted for PR-8210.
    6. Bit 5 is clear if baud rate should be 9600, and set if baud rate should be 4800. Only applies to serial LDPs and probably only matters for LDP-1450 mode where Time Traveler requires 4800 baud. Will be ignored for non-serial player modes.
    7. Bit 6 is clear if laserdisc player type auto-detection is disabled, or set if enabled.
    8. Bit 7 is clear if disc is NTSC or 1 if disc is PAL
    9. All other bits are reserved and should be 0
  5. 1 byte indicating auto-detect strategy for this disc type
    1. 0x00 No suggestions
    2. < 0x80 means that the disc can only be used with one player type, and thats type corresponds to the laserdisc player types listed above
    3. 0x81 LD-V1000/PR-7820
  6. 1 byte indicating the disc Id of the default disc (0 if not available)
  7. 9 bytes indicating which non-default discs (by disc Id) are available to be switched to (0 for none)
  8. 1 byte that is always 0 (conserves Dexter limited RAM by null terminating the array of available players)
  9. 2 byte arbitrary "dynamic" identifier (probably a checksum) chosen by the media server which will be returned when the media server requests a status. This allows the media server to determine whether or not to re-send settings to the controller.
  10. Arbitrary number of bytes containing the compacted VBI data which is described below:

The VBI data of each laserdisc is can usually be described by a few simple patterns and thus compacted down to a small number of bytes.

The first bytes of the compact VBI data will be:

Offset Description
0 Version identifier (will always be 0 for now)
1 The number of "pattern entries" needed to describe the entire VBI. As it will only be a byte, that means only 255 entries are possible. Typical cases show that only 3-5 entries are needed for each disc.
2-5 The total number of fields that the VBI patterns represents, in little-endian format.

After these bytes, 1 or more "pattern entries" will follow. Each entry is 12 bytes long:

  1. 4 bytes describing absolute field where the pattern begins. (unsigned, little endian)
  2. 4 bytes describing the picture number that corresponds to the start of the pattern. This number can be negative! (signed, little endian)
  3. 1 byte describing the pattern itself.
    1. 0=no change
    2. 1=2:2 pattern
    3. 2=2:2 pattern atari styled
    4. 3=2:3 pattern
    5. 4=lead-in pattern
    6. 5=lead-out pattern
    7. 6=repeating zeroes
    8. 7=repeating picture number
  4. 2 bytes describing stop-codes and chapter numbers. Bit 15 is reserved [should be 0], it used to represent stop codes but this quickly became problematic so I ditched the idea. Bit 14, if set, means this entry has a chapter number associated with it that will continue the duration of the entry. The lower bits (bits 0-bit 13) are the chapter number. If bit 14 is clear, it means there is no chapter number regardless of whether the pattern is "no change". In other words, if a chapter exists, it must always be specified for every entry. This 2-byte value is little endian.
  5. 1 byte indicating the pattern offset. 0 means there is no offset. For 2:2 patterns, this can be a 0 or a 1. For 2:3 patterns this can be a 0, 1, 2, 3, or 4. A non-zero offset means that the pattern will start later than at its usual beginning. For example, of the pattern would've been "PicNum0 Empty PicNum1 Empty" for a 2:2 pattern, and the offset was 1, then the pattern would instead be "Empty PicNum1 Empty PicNum2".

Serial Communications from controller to VBI injector

Communication protocol follows the same format as between the media server and controller. Max packet length from VBI injector (to controller) is 8-bits, max packet length to VBI injector (from controller) is 16-bits (little endian).

Messages from controller to VBI injector

Message letter Name Description Example
M Mute video signal Mutes video signal (for PR-8210 mode, MACH 3 requires it). Should be sent every field that the video signal is to be muted to protect against dropped packets. Sending a VBI update will implicitly unmute the video signal.
N Build number request Requests 32-bit little endian firmware build number from AUX AVR.
S Set stand by line [deprecated] Takes a 1 byte argument. If non-zero, the PR-8210A STAND BY line will be raised (if controlled by the AUX AVR, which is true is of rev 3b). If 0, stand by will be lowered. Should be sent every field during seek/spin-up to protect against dropped packets. Sending a VBI update will implicitly lower the stand by. This side effect is justified because we have to plan for and protect against dropped packets.
s PR-8210A search begin or continue Initiates or continues predictable PR-8210A search behavior where the stand by line is toggled and no VBI data is injected. Will be terminated by a VBI update. The reason that this command is idempotent is to protect against dropped packets.
0x56 (V) VBI Update [deprecated] This will always be 11 bytes. The first byte contains data about the field and the white flag. Bit 0 will be set if white flag is enabled. Bit 1 will be set if this data is for the bottom field, clear if the top. The rest of the bits are ignored and should be 0. The next 3 bytes represent line 16's data in big endian format (can be changed to little endian if this is more optimal). The next 3 bytes represent line 17's data, and the next 3 bytes represent line 18's data. 0x00 0x0B 00 0xF4 0xFF 0x56 0x01 0x00 0x00 0x00 0xF8 12 34 0xF8 12 34 ?? ?? (packet length is 0x0C, white flag is set for top field, line 16 is clear, line 17 is 0xF81234, line 18 is 0xF81234)
v VBI Update v2 Newer/better version of the VBI update packet. The first byte contains bits that each have their own meaning. Bit 0 will be set if white flag is enabled. Bit 1 will be set if this data is for the bottom field, clear if the top. Bit 2 will be set if the disc is paused. Bit 3 will be set if the proceeding number is a picture number. Bit 4 will be set if the proceeding number is a chapter number (picture number bit takes precedence). The rest of the bits are ignored and should be 0. The next 2 bytes represent picture number in unsigned binary format (0-65535) or chapter number in unsigned binary format, or undefined/ignored if the preceding picture number and chapter number bits are both 0.
Z Force firmware update Tells VBI injector to force its bootloader's firmware updater to run. (see bootloader section below)

Messages from VBI injector to controller

Byte indicating type of data Name Description Example
L Log message String from VBI injector AVR to be logged
N Build number response 32-bit little endian build number.

Serial Communications of Bootloader

When the bootloader boots (hehe), it will check the value of the last 16-bit word of the EEPROM. If it is 0xBEEF, the bootloader will assume that the last attempt to program the application succeeded and will boot the application. If this word is not found, the bootloader will assume that the last attempt to program the application failed and will enter the programming state.

If the application wants to force a reprogram, it has simply to erase these in the EEPROM, then call the bootloader.

The programming state

Memory is programmed one page at a time. The size of the page depends on the type of AVR.

For the 328p, the page address size is 6 bits (where each address is pointing to a word, not a byte). One page is 128 bytes (see 26.8.16 of datasheet), or 64 words. The 328p has 256 total pages (32k). The 328p's EEPROM size is 1k.

For the 644p, the page address size of 7 bits (where each address is pointing to a word, not a byte). The 644p has 256 total pages where each page is 256 bytes (64k). The 644p's EEPROM size is 2k.


Communication protocol follows the same format as between the media server and controller. Max packet length from boot loader is 8-bits, max packet length to boot loader is 16-bits (packet length will be indicated by 16-bit little endian value). This is because the packet size on the 644p is 256 bytes so we can't stay within 8-bit boundaries.

Message from boot loader

Command letter Name Description
L Log message Packet will be an arbitrary ASCII string sent from the bootloader to provide visibility into what's going on.
Z (uppercase) Page request First byte is the version of this protocol (00). Next byte is the bit size of the page (for example, 7 for 328p, 8 for 644p). Next two bytes are the index of the page (16-bit little endian), starting with 0x0000. It will send this also to indicate that the last page received was corrupt and should be resent. If no data is received after a timeout period, last request may be re-sent.

Sending pages to boot loader

Command letter Name Description
P Send a complete page Sends 2 bytes (little endian) indicating which page is being sent, followed by the full page worth of data.

What done looks like

When the boot loader successfully receives all the pages, it will:

- set the token at the back of the EEPROM to 0xBEEF
- boot the application

Notice that it does not indicate that it has successfully received all the pages. This is because of the possibility of the communication being corrupt over the serial communication system and thus not being able to rely on it being delivered anyway.

The peer talking to the boot loader should assume that any other valid communication received that is not part of the bootloader's protocol means that programming succeeded and the firmware has been booted.

Philips VP931


Pin Description
9 Ground
10 RDEN' (in) "RDEN lets the CPU know that the host wants to read data, and sets a latch for the player's data bus outputs (so the output is valid for as long as the host keeps RDEN active"
11 DAV (out)
12 DAV' (out)
13 OPRT' (out) [always low, connected through a resistor and transistor to vcc]
14 DATA0
15 DATA1
16 DATA2
17 DATA3
18 DATA4
19 DATA5
20 DATA6
21 DATA7
22 WREN' (in)
23 DAK (out)
24 RESET' (in)
25 5V - As far I can tell, Firefox, Freedom Fighter (dedicated), and Freedom Fighter (prototype) all leave this pin unconnected.

Timing Diagrams

Caveat: These are from an unknown source and should be compared against observed behavior from real hardware.

	Data Passing FROM the Player (6 bytes)

___   _________________________   _________________________
   \ /                         \ /
    X         Data Valid        X        Data Valid          Data Bus
___/ \_________________________/ \_________________________  

     |                          |

____ |         _________________|         _________________  ___
    |_________|                 |________|                   DAV

    |         |                 |

____|________ | ________________________   ________________  ____
             |_|                        |_|                  RDEN

    ||     ->||<- <50 nsec      | 

  ->||<- <400 nsec              |

              |         |<- 8 usec (Min)->|

    |<------14 usec (Min)------>|

      ___               ____

Note: DAV is cleared by RDEN but the data remains in the communication latch until another write from the player.

	Command Passing TO the Player (3 bytes)

     ||<- >30 nsec

__   ||__   _________________________   ____   ___________
  \ /    \ /                         \ /    \ /
   X      X      Data Bus Ignored     X      X               Data Bus
__/ \____/ \_________________________/ \____/ \___________



___   ________________________________   _________________
   \ /                                \ /
    X             Data Valid           X                     Stored Data
___/ \________________________________/ \_________________


___   |                         ______
   |___________________________|      |______________________|  DAK

   |  |                        |      |

__ |  |______________________________ |   ___________________  ____
  |___|                              |___|                     WREN

->||<- <50 nsec                |   ->|   |<- >50 nsec

  |                >400 nsec-->|     |<-

  |<-18.6-29.4 usec(1st byte)->|      |<- 8.9 usec (Min)---->|

                               |<---------14 usec (Min)----->|

                                        (2nd & 3rd bytes)

Other Philips info

Things that would be nice to capture

  1. What does a real VP-931 do with the DAV' line if nothing is connected to it? Does it timeout?
  2. When connected to freedom fighter, which does not acknowledge DAV' during the command phase, when does the VP-931 set DAV' high again? Is it during vsync?

RDEN' and WREN' width

Firefox RDEN' and WREN'

Measured by Neil Ward using scope:

Pin 22 DSK WR is 2.8uS active low
Pin 10 DSK RD is 3uS active low
Freedom Fighter RDEN' and WREN'

Measured by Warren and Matt O using logic analyzer.

RDEN' and WREN' map to a single CPU instructions in the PIF ROM (a load or a store).

WREN' is (only) 0.46-0.48 uS active low, which is really short.
RDEN' is .52 uS active low, also really short.

After WREN' goes high again, the data "floats" on the bus for about 3.5uS before it starts to "wobble".

RDEN' will be low for 8.4 cycles of an AVR running at 18.432 MHz. Really short timing window! :)

Ruben's Musings

(from http://www.d-l-p.com/community/forums/archives/default.asp?Action=View&MessageID=16218 ) First of all make sure that the voltages to the game PCB are right. If you are using the Atari "power supply of death" (2x Audio Reg) then get rid of it and use a switching supply (I use a 250W PC type).

The game board will not try to communicate to the LDP unless it knows the LDP is connected and turned on. How it knows it is connected is via the OPRT (Operational) signal, which is an active low open collector input on the LDP. Make sure that the OPRT signal is connected.

A quick way to check to see if the game thinks that the LDP is connected is to disconnect the video cable from the LDP while the communications cable is still connected. The game should watchdog reset and the video should go out of sync. This happens because the game uses the sync from the LDP video for video timing (when the LDP is connected) and if vsync is out by around 3ms or times-out (no video) the game resets.

If you have the signals wired properly (at least OPRT and ground) and the game does not reset with no LDP video connected then check the voltage level of OPRT. If it is +5V the problem is with your LDP (TR6112). If it is 0V then check for 0V on pin 15 on the buffer at 7J on the game CPU PCB. If it is 0V then the buffer is faulty otherwise you have wired OPRT wrong or there is a bad connection somewhere.

If everything above checks out okay then make sure you have the communications signals between the game and the LDP wired correctly. Make sure the reset signal is connected to the LDP as the LDP CPU can crash when the game boots or if it resets. If it still doesn't work then there is either a problem with your game PCB (7J, 6H, 7H, or 2F) or with your LDP (IC6206, IC6207, IC6208, or the processor is not running/crashed).

Firefox/General I/O

  1. For the top field, the VP-931 sends data about 3/4 of the way through line 22 (so not long after parsing the picture number from lines 17-18). For bottom field, it's about 3/4 of the way through line 287. These may fluctuate and are just what I observed for two fields. The point is that they happen not long after the picture number has been parsed from the VBI.
  2. The DAV' line goes low to indicate that data is available but it does not put anything on the bus.
  3. When RDEN' goes low, then the VP-931 will simultaneously 1) put the new data byte on the bus and 2) set DAV' to high. This is the only time when the bus will have stable data. IMPORTANT: On a real VP-931, the RDEN' line is connected to the enable pin of a LS374 which means that the data will be put on the bus almost immediately. Firefox exploits this fact by lowering RDEN' and immediately reading from the bus (see E4BB and E4BE in the Firefox ROM). Dexter does not have the luxury of being able to respond this quickly, so probably would need to have the data on the bus before lowering DAV'. Firefox seems to rely heavily on DAV' going high as soon as RDEN' goes low (see disassembly below) so this is something that Dexter must support.
  4. The game sets RDEN' to high shortly thereafter (about 3 uS), to indicate that the data has been safely read.
  5. After a small period of time (about 3uS after RDEN' has gone high), DAV' will go low again to indicate that another byte is available.
  6. This sequence iterates 6 times (6 bytes are sent).

NOTE : Firefox does one extra read even when DAV' is high (ie it reads 7 bytes, see E4C7 in the firefox ROM as shown below). As the RDEN' line is tied to a LS374, the data bus will be written to by the VP-931, although the data's meaning is probably undefined and unreliable.

NOTE #2 : Firefox does not ever check to make sure that DAV' has gone high and assumes that it has gone high as soon as RDEN' went low (see E4B7 in the disassembly).

ROM:E4A8                 ldb     #6              ; prepare to read 6 bytes from VP-931
ROM:E4AA                 stb     DQ_INC
ROM:E4AC                 ldx     #DQ_INB0        ; first byte of line 18 VBI
ROM:E4AF While6BytesNotRcvd:                     ; CODE XREF: ROM:E4C5?j
ROM:E4AF                 ldb     #$10            ; this value appears to be some kind of 'timeout'
ROM:E4AF                                         ; used to exit the FIRQ routine if the player isn't
ROM:E4AF                                         ; going to be sending the full 6 bytes.
ROM:E4AF                                         ; (during writes, the player echoes back a single byte)
ROM:E4B1 WhileDavNotActive:                      ; CODE XREF: ROM:E4B9?j
ROM:E4B1                 decb                    ; decrement timeout value
ROM:E4B2                 ble     FIRQDone        ; if we've timed out, abort FIRQ
ROM:E4B4                 lda     R_DAVD          ; bit 7: DAV'
ROM:E4B4                                         ; bit 6: DAK
ROM:E4B4                                         ; bit 5: OPRT'
ROM:E4B4                                         ; bits 0-4 : unconnected
ROM:E4B7                 anda    #$80 ; 'Ç'      ; isolate DAV' to see if it active (low)low.
ROM:E4B9                 bne     WhileDavNotActive ; decrement timeout value
ROM:E4BB                 sta     W_RDED          ; writing to this location lowers RDEN' (enables it)
ROM:E4BE                 lda     R_DATD          ; reading from this location grabs the data from the VP-931 and disabled the RDEN' line (raises it)
ROM:E4C1                 sta     ,x+             ; store into array
ROM:E4C3                 dec     DQ_INC
ROM:E4C5                 bgt     While6BytesNotRcvd ; this value appears to be some kind of 'timeout'
ROM:E4C5                                         ; used to exit the FIRQ routine if the player stops
ROM:E4C5                                         ; sending bytes in a timely manner.
ROM:E4C7 FIRQDone:                               ; CODE XREF: ROM:E4B2?j
ROM:E4C7                 sta     W_RDED          ; start read enable
ROM:E4CA                 lda     R_DATD          ; turn off any extra read
  1. The game sets WREN' to low, then puts data on the bus.
  2. The VP-931 then sets DAK to low (this is accomplished via a 74LS279, so it happens almost instantly; within 0.1uS).
  3. After DAK goes low, the game raises WREN'. This is how the byte actually gets stored to the VP-931 because WREN' is connected internally to the clock of an LS374 inside the VP-931. DAK seems to stay low for a minimum of 10uS, but can stay low for as much as 20uS (it seems to vary). This means that Firefox and Freedom Fighter can't be too picky about when this signal ends.
  4. The VP-931 appears to lower DAV' after receiving a byte in order to echo the received byte back. Firefox politely lowers RDEN' and reads this byte (which sets DAV') but ignores its value (see E67C and E67F). Freedom Fighter, however, seems to ignore trying to read and DAV' just stays low. I don't know when DAV' goes high again (ie when the VP-931 gives up) but I would assume it is around the next vsync. (this would be a good thing to capture!)

NOTE : Firefox performs one spurious read before it begins writing. This is because Firefox's loop that writes the 3 command bytes does the read (to acknowledge DAV') before the write. The value read is completely ignored and DAV' does not even need to go low during the command phase for Firefox to operate properly.

NOTE #2 : As with reading, Firefox expects DAK to instantly go low when WREN' goes low. It will then spend some time waiting for DAK to go high again. See E692-E69A. It eventually will time out if DAK doesn't go high in a timely manner (see E69C).

ROM:E677                 inc     DQ_WTO          ; write timeout counter
ROM:E679                 ldx     #DQ_CMD         ; output command buffer
ROM:E67C Send1CmdByte:                           ; CODE XREF: ROM:E6A1?j
ROM:E67C                 sta     W_RDED          ; writing to this location lowers RDEN' (enables it)
ROM:E67F                 lda     R_DATD          ; reading from this location grabs the data from the VP-931 and disabled the RDEN' line (raises it)
ROM:E682                 lda     ,x+             ; load from buffer
ROM:E684                 sta     DSKLATCH        ; the data stored in this latch will be sent to the LDP
ROM:E687                 ldd     #$FF
ROM:E68A                 sta     WRDSK           ; setting the high bit here lowers WREN'
ROM:E68A                                         ; (set up for disk write)
ROM:E68D                 stb     WRDSK           ; setting the high bit here lowers WREN'
ROM:E68D                                         ; (set up for disk write)
ROM:E690                 ldb     #$10            ; timeout value
ROM:E692 WhileDakIsLow:                          ; CODE XREF: ROM:E69A?j
ROM:E692                 lda     R_DAVD          ; bit 7: DAV'
ROM:E692                                         ; bit 6: DAK
ROM:E692                                         ; bit 5: OPRT'
ROM:E692                                         ; bits 0-4 : unconnected
ROM:E695                 anda    #$40 ; '@'      ; isolate DAK
ROM:E697                 bne     DakIsHigh       ; have we sent the three command bytes?
ROM:E699                 decb
ROM:E69A                 bgt     WhileDakIsLow
ROM:E69C                 bra     PostWriteTimeoutCounterClear ; we timed out, don't clear the write timeout counter,
ROM:E69C                                         ; or send any more command bytes
ROM:E69E ; ---------------------------------------------------------------------------
ROM:E69E DakIsHigh:                              ; CODE XREF: ROM:E697?j
ROM:E69E                 cmpx    #DQ_MFD0        ; have we sent the three command bytes?
ROM:E6A1                 bcs     Send1CmdByte
ROM:E6A3                 clr     DQ_WTO          ; write timeout counter
ROM:E6A5 PostWriteTimeoutCounterClear:           ; CODE XREF: ROM:E69C?j
ROM:E6A5                 sta     W_RDED          ; writing to this location lowers RDEN' (enables it)
ROM:E6A8                 lda     R_DATD          ; reading from this location grabs the data from the VP-931 and disabled the RDEN' line (raises it)

Data bus: The bus seems to be "floating" if RDEN' and WREN' are both inactive.

Status data

The first 3 bytes read are the VBI data (ie picture number, for example, but may include stuff like lead-in). The next 3 bytes are some kind of status code. There is no documentation for this, so one must reverse engineer:

  1. 02 00 10 : Powering up (using MAME as the source)
  2. 04 00 00 : Playing normally (confirmed on real player)
  3. 04 00 01 : Lead in (confirmed on real player)
  4. 04 00 10 : Unknown (MAME as the source during spin-up)
  5. 04 00 33 : Unknown (seen from MAME during lead-in)
  6. 04 00 37 : Unknown (seen from MAME during spin-up/lead-in)
  7. 05 00 00 : Seek complete, may also mean paused (confirmed on real player, only shows for one field if autoplay-after-search is enabled)
  8. 08 00 00 : Seeking (confirmed on real player)
  9. 08 60 00 : Seeking when close to target, may mean that current position is behind the target (confirmed on real player)
  10. 08 70 00 : Seeking when close to target, may mean that current position is ahead of the target (confirmed on real player)

NOTE: While MAME does (partially?) emulate the VP-931, its accuracy is unknown, so any values obtained from MAME should be verified independently if possible.

NOTE #2: While seeking, the VP-931 does report VBI data that it parses along the way.

The firefox ROM gives a small clue about what these mean:

ROM:E62D                 lda     DQ_STA          ; if JMPing or power up, then no lock to disk
ROM:E62F                 anda    #$A             ; bit 1 and 3 indicate JMP/power-up but I am not sure which is which
ROM:E631                 beq     loc_E635
ROM:E633                 clr     DQ_LCK          ; 0 = cannot lock to disc, 1 = can lock to disc

DQ_STA refers to the first status byte following the VBI data from line 18. This means that bit 3 and 1 of the first status byte are set if the disc is jumping or powering up. Using MAME as reference, it would seem that bit 1 is powering up and bit 3 is seeking.


We have captured the behavior of a real VP-931 when performing a seek. The target frame is 30,000 and the player was ahead of 30,000 and playing when the seek was initiated.

  1. First, F3 00 00 is sent to the player.
  2. The player's first response is 00 00 00 08 00 00
  3. The player's second response is AB 25 90 08 00 00 (it was near frame 32590 in this example)
  4. The next responses were the VBI picture number approaching FB 00 00 (frame 30,000) with the status still being 08 00 00
  5. As the player got close (FB 01 31, frame 30131), the status changed to 08 70 00 which seems to means that we are close to the target and ahead of the target.
  6. Subsequent updates still showed 08 70 00 until the player returned AA 99 98 (alternate field of frame 29998) with a status of 08 60 00 which seems to mean that we are close to the target but behind the target.
  7. Next status update is FB 00 00 (frame 30,000) with a status of 05 00 00 which seems to mean either 'seek complete' and/or 'disc paused'. I didn't do any seeks that keep the disc paused so I cannot settle this ambiguity at this time.
  8. Next status update is AB 00 00 (frame 30,000 second field) with a status of 04 00 00 (normal play status).
Skipping using seeking (Firefox attract mode)

Firefox uses seeking to essentially do skipping during its attract mode. By using a modified version of MAME, we can see what the real VP931 may send as its status during this time.

This is a known spot where Firefox does a "skip" :

Phase Value
Status a82155 040000
Cmd f02246
Status 000000 086000
Status f82246 050000
Cmd 000000
Status a82246 040000

Firefox will only send two commands to the VP931, either a GOTO+PLAY command or a skip 1 track backward command.

If performing a GOTO+PLAY while on the first field (which is actually the bottom field due to the way the disc is arranged), it will skip to 1 picture number beyond what is requested. This is because the VP931 will land on the field before the field that has the requested picture number. For example, if the VP931 had reported manchester codes of F8 00 01 and Firefox wanted to skip to picture number 10, it would issue a F0 00 11 command (skip to picture number 11) and the VP931's next manchester code report would be A8 00 10 followed by F8 00 11. This probably means that the VP931 performs an internal track jump in these situations which causes it to land 1 field early.

If skipping from an alt field (which is the top field), it will skip to the actual requested picture number and the VP931 would land in the right place. If the disc is supposed to be paused, it will skip backward 1 track if on the alternate field or just send the default play command if on the first field.

Freedom Fighter I/O

Reading from player

Reads are not interrupt driven. Freedom Fighter just polls the DAV (not DAV') line.

ROM:C302 ; =============== S U B R O U T I N E =======================================
ROM:C302 ; If data was read, carry will be returned clear and data will be in B.
ROM:C302 ; Else, carry will be set if data was not read.
ROM:C302 TryReadLDP:                             ; CODE XREF: sub_C257:loc_C266?p
ROM:C302                                         ; sub_C257:loc_C29E?p
ROM:C302                 lda     DAVDAK_RD       ; bit 7: DAK
ROM:C302                                         ; bit 6: DAV
ROM:C305                 eora    #$40            ; flip DAV so it becomes DAV'
ROM:C307                 asla
ROM:C308                 asla
ROM:C309                 bcs     locret_C30E     ; branch if DAV is inactive (no data available to read)
ROM:C30B                 ldb     LDP_DATA        ; read or write to the VP-931 data lines.
ROM:C30B                                         ; RDEN' and WREN' are lowered appropriately by the hardware.
ROM:C30E locret_C30E:                            ; CODE XREF: TryReadLDP+7?j
ROM:C30E                 rts
ROM:C30E ; End of function TryReadLDP
Writing to the player

The VP-931 will echo back commands sent to it. It does this by lowering DAV' shortly after raising DAK. Firefox obediently will lower RDEN' when this happens (in fact, any time DAV' is lowered, because it is interrupt driven). Freedom Fighter, however, does not perform a read here. The result is that DAV' stays low. Subsequent writes still work properly.


A closer look:


The Freedom Fighter PIF ROM seems to not only be aware of this behavior, but seems to rely on it.

ROM:C2F0 ; =============== S U B R O U T I N E =======================================
ROM:C2F0 WriteLDP:                               ; CODE XREF: sub_C217+6?p
ROM:C2F0                                         ; sub_C246+9?p ...
ROM:C2F0                 lda     DAVDAK_RD       ; bit 7: DAK
ROM:C2F0                                         ; bit 6: DAV
ROM:C2F3                 anda    #%11000000      ; isolate DAK and DAV
ROM:C2F5                 beq     WriteLDPError   ; Branch if neither are high.
ROM:C2F5                                         ; When writing, DAK needs to be high (first write),
ROM:C2F5                                         ;  or DAV needs to be high (subsequent write) because FFR does
ROM:C2F5                                         ;   not acknowledge DAVs that occur during writes.
ROM:C2F7                 bpl     WriteLDP        ; If DAK is not high, wait forever for DAK to go high before we write again
ROM:C2F7                                         ; (this can only happen on a subsequent write in which DAV is high)
ROM:C2F9                 stb     LDP_DATA        ; read or write to the VP-931 data lines.
ROM:C2F9                                         ; RDEN' and WREN' are lowered appropriately by the hardware.
ROM:C2FC                 lda     DAVDAK_RD       ; stall to give time for DAV to go high?
ROM:C2FF                 nop
ROM:C300                 nop
ROM:C301                 rts
ROM:C301 ; End of function WriteLDP

modifying MAME to get logging information

MAME has a working firefox driver as of the time of this writing and it may be useful to see what its timings are.

To setup a callback for vblank:

static INTERRUPT_GEN( mpo_vblank_interrupt )
UINT64 curr_cycles = device->machine().firstcpu->total_cycles();

and inside MACHINE_CONFIG_START, add

MCFG_CPU_VBLANK_INT("screen", mpo_vblank_interrupt)

To capture when RDEN goes active, look for firefox_disc_read_w (tied to 0x4218) and you can get the current cycles with:

UINT64 curr_cycles = space->machine().firstcpu->total_cycles();

To capture when WREN goes active, look for firefox_disc_write_w (tied to 0x4287) , again getting current cycles with:

UINT64 curr_cycles = space->machine().firstcpu->total_cycles();

IRQ's are fired within video_timer_callback function.

UINT64 u64CurCycles = timer.machine().firstcpu->total_cycles();

FIRQ's are fired within firq_gen callback function.

UINT64 curr_cycles = machine.firstcpu->total_cycles();

To convert total cycles to elapsed milliseconds, use this equation:

double dMilliseconds = (u64TotalElapsedCycles / ((double) (MASTER_XTAL/8))) * 1000.0;

I initially thought it would be MASTER_XTAL/2 for the previous equation but tests showed it was /8. I still don't know why.

To convert milliseconds to current NTSC line number, do:

double dLine = (dMilliseconds / 16.6833) * 262.5;

LD-V1000 Pin-Out

LD-V1000 Pin Description
1 A0
2 A1
3 A2
4 A3
7 Command Strobe
11 Status Strobe
12 GND
13 DIO5
14 DIO6
15 DIO7
16 DIO8
17 Enter Signal (must be connected or dragon's lair will not boot)
18 GND
19 GND
20 GND
21 GND
22 GND
23 GND
24 GND
25 GND

modifying MAME to get logging information

MAME emulates the LD-V1000 using the actual ROM so it may be instructive to observe the behavior of mame's emulated ld-v1000 from time to time. Note that MAME's emulation may have problems so it should not be exclusively relied upon.

Open ld-v1000.c in the mame src code and change the following two #defines:

#define LOG_COMMANDS				1

Then just run with the -debug option specified from the command line and the dlair.c driver will also spit out status reads.

Philips VP-932

Connection on DL Euro PCB

J2 on the DL Euro PCB:

Schematic Pin (legacy convention?) Molex Pin (marking on connector) Description Direction
1 3 TXD To player
2 2 GND
3 1 DTR to player, indicates game/computer is ready to receive data
4 6 CTS from player, indicates player is ready to receive data
5 5 GND
6 4 RXD From player

I/O description

From 22VP932 service manual (signals renamed for clarity)

Speed is 9600 baud. Data format is one start bit, eight data bits, one stop bit. No parity.

RTS is always active and will provide correct timing. DSR signal is software controlled (controlled by game/computer) because some computers don't make use of that signal. A message to LDP should consist of a single ascii-code plus carriage return, or a string of characters plus carriage returns. No line feed. Action in player will start after carriage return.

Reverse Engineered / Studied Behavior

Get command from CPU
 wait for DTR' low/active
 if command buffer ends in a '\r' (command termination)
  set RTS' low/active
  process the command
  set RTS' high/inactive
Send data to CPU
       wait for DTR' low/active
       send data

Dexter pins

Dexter DB25 Pin Direction from AVR Description
10 In RX1, connected to J2's TXD
11 Out RTS, connected to J2's CTS; indicates player is ready to receive data
12 In DSR, connected to J2's DTR; indicates game is ready to receive data
22 Out TX1, connected to J2's RXD

Commands that are used by game (from VP380 user manual)


  • All commands and responses are terminated by a carriage return (0x0D).
  • Digits must be in ASCII. Leading zeroes are optional.
Command pattern Description What is returned
'FxxxxxR' Go to picture number, then pause A0 on completion.
FxxxxxN Go to picture number, then play A1 on completion.
N Normal play forward (audio unmuted)
* Halt (still mode)
/ Pause (halt + all muted)
,1 On (Load) S (ACK, disc up to speed)
?F Picture number request Fxxxxx or 'X' if not available.
A0 Audio-1 off
A1 Audio-1 on
B0 Audio-2 off
B1 Audio-2 on
S Change play speed
U Slow motion forward (audio muted)
X Clear

PR-7820 interface

Programming manual here: http://www.dragons-lair-project.com/tech/manuals/pr7820-dva-system-manual.pdf Jeff (not Kinder) has also reverse engineered much of the command set here: http://www.dragons-lair-project.com/tech/docs/conv_dl.asp

PR-7820 Pin Description
1 DIO1
2 DIO2
3 DIO3
4 DIO4
11 ENTER' (low means that the PR-7820 is being sent a byte on the data bus)
12 GND
13 DIO5
14 DIO6
15 DIO7
16 DIO8
17 INT/EXT' (receives external commands when low)
18 GND
19 GND
20 GND
21 GND
22 GND
23 GND
24 GND

The ready line pulses low every field while the disc is stopped, paused, or playing. The ready line is held high with no pulsing during disc spin-up and searches.


Each pulse lasts about 20uS and happens about 3 ms after the field starts as seen in this capture.


A quick n' dirty way to emulate the PR-7820 is to hold READY' low when the player is not busy searching. This is good enough for Dragon's Lair but not accurate.

Dragon's Lair sends a search command and then waits for READY' to go low to discover when the search has completed (ie it does not wait for READY' to go high first). Therefore, READY' needs to go high quickly after a search command is received.

LD-V1000/PR-7820 extended commands and 'super' mode

LD-V1000 extended commands

Command Name Description Returns
0x90 Hello Tests for the presence of this extended command set. On Dexter, it will return a version number in the lower nibble and 0xA in the upper nibble. Will always return a result even if the player is busy searching or disc swapping. The current version (0xA2 at the time of this writing).
0x91 Query all images Queries Dexter for all available laserdisc images which Dexter can switch to. If received during a seek or a disc change, this command is ignored. An array of disc identifiers as single bytes which is null terminated. Disc IDs are here: http://www.daphne-emu.com/ldimage/discids.php . Example of what could get returned: 0x12 0x10 0x34 0x00 (8 16 52 00 decimal, which is dragon's lair, DL2, space ace). If no laserdisc image is available, it will just return a 00.
0x92 Query active image Query Dexter for the active disc image. If received during a seek or a disc change, this command is ignored. Single byte indicating the active disc image. If no laserdisc image is active, it will just return a 00.
0x93 [new image id] Switch to new image Behaves like a seek command but instead of seeking to a new frame, it switches to a new disc. When the switch begins, the screen will go blank and stay blank even after the switch has completed. After the switch has completed, play and seek commands can be sent to resume normal operation. If the 'get current frame' command is issued while in this state, the returned frame number is undefined. If any other 'switch to new image' commands come in before the first command has completed, they are ignored. 0x50 during transition to new disc, 0x90 on error, 0xD0 on success
0x94 Enable/disable laserdisc player spin-up delay Behaves like the audio enable/disable commands 0xF4 or 0xFC. The caller must pass in a number to indicate whether spin-up delay will be enabled or disabled. A number of 0 means to disable spin-up delay. A non-zero number enables spin-up delay. Passing in 0x94 without passing in a number first will be interpreted as a 0. The default is to be enabled. Examples: 0x3F 0x94 [to disable] 0x0F 0x94 [to enable] Does not affect status
0x95 Enable/disable laserdisc player seek delay Same as 0x94 except for seek delay instead of spin-up delay. Does not affect status
0x9D Disable super mode (see below) On next vsync, super mode will be disabled and either LD-V1000 or PR-7820 mode will be active again (depending on what was active before super mode became enabled). N/A
0x9E Enable super mode (see below) On next vsync, super mode will be enabled. N/A

PR-7820 extended commands

Command Name Description Returns
0x9E Enable super mode (see below) On next vsync, super mode will be enabled. N/A

Super Mode

Super mode aka Universal mode is a new custom I/O protocol that is compatible with Dragon's Lair boardsets that are either configured for PR-7820 or LD-V1000 modes. Unlike these aforementioned modes, 'super mode' only uses two control wires for communication instead of three (pins 7 and 17 on the centronics 24 connector, with pin 11 being ignored). It also operates much faster than either PR-7820 or LD-V1000 mode.

Super mode is 100% compatible with the Dexter LD-V1000 command set (including the extended LD-V1000 commands) except that 0xFF (NO ENTRY) never needs to be sent. The status code will always be in a 'ready' state (with high bit set) except when it is busy spinning up the disc or searching to a frame.

Unlike LD-V1000 mode, super mode does not have a concept of a command strobe but instead uses implicit timing rules for communication (explained below).

Communication Flow

The game hardware must wait to receive a status byte from the laserdisc player before it may send any commands. Once it receives a status byte, it may start sending one or more commands. It may continue to send an indefinite number of commands as long as these commands are sent faster than 8ms apart from each other. Once the next vsync occurs and the laserdisc player has not received a command for 8ms, it will resume sending status bytes every field until it receives the next command. This pattern continues indefinitely.

The laserdisc player always drives pin 7 and the game hardware always drives pin 17. Pin 7 is active low and pin 17 is active high (for historical reasons; pin 7 is usually high and pin 17 is usually low).

Here is how the timing/handshaking works:

Step Phase Event
1 Status On the next vsync, if no command has been received for 8ms, laserdisc player puts status byte on data lines and lowers pin 7 (in that order).
2 Status Laserdisc player waits ~25 uS (same length as ld-v1000 cmd and status strobes)
3 Status Laserdisc player stops driving data lines and raises pin 7 (in that order); this is so that laserdisc player and game hardware are never driving data lines at the same time.
4 Command Game hardware (if it wishes to send a command) immediately puts command byte on data lines and raises pin 17 (in that order). If the game hardware does not wish to send a command, go to step 1.
5 Command Laserdisc player lowers pin 7 to acknowledge command receipt. Game hardware should wait for this.
6 Command Game hardware lowers pin 17 and stops driving data lines (in that order) to acknowledge laserdisc player.
7 Command Laserdisc player raises pin 7 to acknowledge game hardware. Game hardware should wait for this.
8 Command Go to step 4.

The command phase ends (and status phase begins) when the next vsync occurs and a command has not been sent for 8ms.

DB25 serial port pin-out


  • Direction is from a PC to a modem (DTE to DCE)
  • The only DB25 pins that are typically used (based on observing which pins are swapped for a DB25 null modem cable) are 2, 3, 4, 5, 6, 7, 8, and 20.

Pin Name Direction Corresponding DB9 pin
1 GND (Shield)
2 TXD out 3
3 RXD in 2
4 RTS out 7
5 CTS in 8
6 Data Set Ready (DSR) in 6
7 GND 5
8 Carrier Detect (CD) in 1
11 Select Transmit Channel out
12 Secondary Carrier Detect in
13 Secondary Clear To Send in
14 Secondary Transmit Data out
15 Transmission Signal Element Timing (TCK) in (verify)
16 Secondary Receive Data in
17 Receiver Signal Element Timing (RCK) in (verify)
18 Local Loop Control out
19 Secondary Request to Send out
20 Data Terminal Ready (DTR) out 4
21 Remote Loop Control out
22 Ring Indicator in 9
23 Data signal rate selector out
24 Transmit signal element timing (XCK) out
25 Test Indicator in

PR-8210 interface

Every command starts with the pattern 0, 0, 1, followed by 5 bits (the actual command), followed by 0, 0 at the end.

Commands are sent serially over a wire. The wire is held at 5V when there is no communication and when a command is ready to be sent, the wire is pulled low to GROUND to initiate one PULSE. The wire is supposed to be held low for 260 microseconds (from PR-8210A service manual, page 37).

MACH 3: violates this by rapidly alternating between high and low during this 260 microsecond period.
Cobra Command (Cliff Hanger PR-8210 conversion): is compliant.
Cliff Hanger: We don't know yet

To determine which bit is being sent, one must observe the space of time between these pulses.

Space of time between falling edge of two adjacent pulses Interpretation
1.05 milliseconds 0 bit
2.11 milliseconds 1 bit
More than 3 milliseconds (just a guess) End of message

Cobra command has at least 7 milliseconds between the last pulse of a command and the first pulse of the next. MACH 3 can have up to nearly 50 milliseconds.

PR-8210 Remote Control Unit

The "DVA 8210 REMOTE CONTROL UNIT" does not seem to use "END OF COMMAND" separators. Pressing a button quickly will result in 3 of the same command being sent. Holding down a button will spam the command over and over again. So elapsed time may also serve as a separator but I have no idea what the official elapsed time is.

MACH 3 notes

From the owner's manual, page 17 and 18: The frame number stripper is U10 on the Color/Sync board. A potentiometer, R202, adjusts the comparator voltage level. (visible inspection and schematics suggest that it's actually R204 that is the pot, as R202 looks like a resistor) This tells me that it's okay the Raspberry Pi's voltage of 0.3V for black and 1.3V for white because this R202 can be adjusted to compensate. U10 also (supposedly) receives VSYNC and HSYNC although from the schematics it does not appear to use HSYNC to count up to a specific line (ie line 17/18). Also, picture number will only be present on the odd (top) field which is pretty standard. Output of the stripper is sent to J7 and then to interface board and appears to be a TTL version of the manchester coding. Game's test mode is used to adjust R202 so that it works reliably. Some pretty technical details of frame decoder on page 19.

From schematics, U10 is a 75107A Dual line receivers/drivers integrated circuit. From a very quick skim of a schematic I found, this appears to do nothing more than convert the manchester-coded line (such as line17 or 18) to TTL levels (from 0.3V/1.3V output by the raspberry pi and our injector board) and then hand that off to another piece of hardware. I could be wrong about this.

Hardware to decode each frame number bit is on interface board (labeled as "frame # decoder" on schematic). Once a bit is decoded, it is shifted into three 8-bit flip flops. Additional hardware detects whether the frame number is valid or not before exposing it to the cpu. The frame decoder will parse the first line that includes a white voltage level (on the mach 3 disc, this will always be line 17 for the top field because the white flag is never used; on something like the raspberry pi, this could be put on a line later than line 17 and the mach 3 hardware should still pick it up).

The Disc Ready Flag (page 18 and 19) will be high when VSYNC (from the laserdisc player) is received within a 16 ms period of time. So it seems that vsync must be removed for some time for a search to be recognized.

Conclusion: As Warren has long argued, I see nothing in the MACH 3 hardware that cares about which line number the frame number comes in on (although hsync pulses could be counted and I am just not seeing it), nor at what point on a given line the manchester code starts. It seems that the picture number _could_ potentially be stored on the first two visible lines of each field (necessary because JPEG is 4:2:0) and MACH 3 would pick it up.

Additional thoughts from Warren:

I think I understand how MACH3's frame decoder works now... and I think it *will* work with VBI on the first visible line, as long as *nothing* appears before it. It doesn't decode every line of field. Flip-flop U9 enables the comparator on the first HSYNC after VSYNC. When the comparator first outputs a logic high, this sends a clock pulse to the flip-flop, which causes it to disable the comparator after the next HSYNC. So it is assuming that it will not receive a high output from the comparator until it gets to the line with the VBI data. This means it doesn't care what line the VBI data is on, only that it is the first line that has white on it. To put it more succinctly... only the first line of each field that contains white is sent to the frame decoder. I just looked at the UvT cap I made on an LD-V1000 at Dave's last month. The 1000 suppresses the VBI lines above 17, so the frame data is indeed the first thing at white level. Assuming the PR-8210 works the same way, this means our injector should NOT include the white flag line.

Service mode video from Teo


Cliff Hanger notes

<Warren_O> I also looked at the Goal to Go schematics, and I believe the Cliff/GtG hardware *does* count lines, and *will* need VBI on line 17. I don't quite follow everything that it's doing but it has a 4-bit counter (74LS161) and a lot of logic gates controlling the shift registers and latches. Interestingly, it does not have a level adjustment like MACK3, but it is AC coupled, so the Pi's 0.3V offset shouldn't matter. well, *probably* won't matter :)

PR-8210A interface

See StarRiderNotes

Sony LDP-1000A

<Warren_O> well, one quirky thing with BB/RB/CC is that it has sync and color subcarrier connections to the ld player


Ruben's thoughts: http://www.d-l-p.com/community/forums/archives/default.asp?Action=View&MessageID=34767

<Warren_O> to tuck away for reference, I believe this is what the "SC" connection on the 1000A is for: http://alstephens.info/Tech%20Articles/SCH%20Phase.pdf

Sony LDP-1450

I've confirmed that this player won't accept commands unless DTR is enabled. When I say "won't accept commands", I mean that instead of returning 0xA after each byte, it returns 0xB.

It doesn't seem to care if flow control (CTS/RTS) is enabled or disabled.


When the player wants digits, it will take the last N digits that it wants. For example, if it wants a 5 digit frame number and you enter in 6 digits, it will take the last 5 digits. If it wants 2 digits and you enter 3, it will take the last 2. And so on.

The SEARCH command

The input after the search command (43) seems strict. Trying to send a PLAY (3a) command after a 43 resulted in a 0B being returned and "INVALID KEY" being displayed on the "index".

The REPEAT command

As the LDM-G1000 manual says, commands that affect playback speed do not cancel a REPEAT. These includes 3a (fwd 1X), 4f (still), and 3d (multispeed).

Commands that do cancel REPEAT include 43 (search) and 56 (clear all). I haven't found any other commands (so far) that will cancel repeat.

Undocumented repeat quirk: if REPEAT starts playing forward (for example, from frame 12000) and playback direction is changed (by playing in reverse) and passes beyond the initial starting point, it counts as an iteration and the disc will begin playing forward again. if no iterations are left, it pauses on the frame _before_ the start frame. (ie pauses on 11999 instead of 12000).

44 3a ... is valid, as are other commands to change the playback speed. However, it appears that only forward commands are valid (reverse not) at this point. 44 4a is invalid.

44 3c .. is valid. the multispeed setting is preserved across repeat loops.

Very quirky: 44 3d is valid, however it must be executed in a specific order: 44 3d [frame number] 40 [repeat iterations] 40 [multistep divisor] 40

More quirky: 44 3d [frame number] 3a [repeat iterations] 40 will start repeating at 1x forward. The 3a can cancel the 3d, just like it can without being part of a repeat command.

Undocumented quirks

The multispeed commands 0x3d and 0x4d will start playing the disc at 1/7X before receiving their arguments for the actual desired speed. For example, if I send a 0x3d, the disc will play at 1/7X forward, and then if I sent a 0x32 (2) and 0x40 then the disc will start playing at 1/2X.

3d can be canceled by sending a 3a (and probably other commands like still or play in reverse). In other words, 3d 3a is valid.

Time Traveler

BJZ's game was set to 4800 baud, not 9600 baud. I don't know whether this is typical of all Time Travelers (I hope it is consistent otherwise that is going to be a big pain for the end user to configure Dexter). It does seem to send a 0x41 at 4800 baud which shows up as something else at 9600 baud so maybe we can put in some auto-detection to save the user some pain. By the way, it is a pretty big pain to get to the laserdisc player. You have to open up the back panel and then open up a small black box inside the back section to get at the player.

Dexter ALG multi-ROM hack

To support Shaun Wood's ALG multi-ROM, Dexter will do the following when entering LDP-1450 mode:

Read LD-V1000 pin 17. If it is floating or high, do nothing. If it is being held low, read the LD-V1000 data lines and attempt to switch laserdisc images based on the following table:

0x7f Mad Dog
0xf7 Mad Dog 2
0xbf Johnny Rock
0xfb Gallagher's Gallery
0xdf Space Pirates
0xfd Crime Patrol
0xef Crime Patrol 2
0xfe Last Bounty Hunder


Ed Jones

Ed Jones was a young boy during the early 80's when arcade games were at their peak. Laserdisc games, in particular, left a deep impression on him which has never really gone away. As a boy he yearned to be in the arcade, watching others play the laserdisc games which were too expensive for him to afford. Now that he has a decent job, he longs to relive his childhood memories by attempting to recreate the atmosphere of the arcades he used to love. He also is interested in sharing this experience with his friends. If money were no object, he would have his own arcade filled with classic games from his childhood and he would regularly host parties in this arcade for his friends to see what Ed's childhood was like and why he loves it so much. He is very busy and does not have time to fix broken games. He needs his games to "just work" and look just like they did when he was a child: brand new.

Mitch Smith

Mitch also grew up in the early 80's and fondly remembers the arcades. He is painfully aware that arcade games do not last forever and is greatly concerned about his memories becoming lost forever. Therefore, he feels a deep sense to preserve them. Any time a new arcade ROM is dumped, he makes it a point to get it and to store it. To him, it doesn't matter whether a ROM is playable or otherwise usable; he collects all data relating to his past as insurance against it becoming lost forever. He is much like the custodian of a museum, trying to preserve everything as precisely and exactly as it was. He understands that if he can archive content from the arcade era, he is contributing to the effort to preserve it. He takes comfort in this sense of doing his part to help the effort. He very rarely, if ever, plays the games that he collects. (Example: One who goes to the effort to seed complete MAME ROM sets over bit torrent)

Lester Black

Lester is a tech support guru for a small business. His job primarily involves fixing other people's computer problems which he is very good at. He has plenty of spare time. However, he lives in a small apartment which has limited space. One of his favorite things to do is to tinker with the few arcade games he has managed to cram into his apartment. He isn't too concerned about the games being completely original; in fact, one of his favorite hobbies is to tinker with and modify the games. He has replaced the crusty old sound system in one of his games with a new state of the art sound system so that it sounds better than the original did. He has also installed a battery in one of his games so that it remembers high scores, a feat that the original never did.

Evan Holmes

Evan can build just about anything. He's also quite good at restoring broken-down things. One of his favorite things to do is take old broken arcade games and restore them so they look like new. The worse the shape he gets these "project" games in, the better. He takes satisfaction in showing people "before" and "after" pictures of his work. Once he is done restoring something, he is really done; he moves on to a new project rather than enjoying his finished work. For him, the joy is in the challenge of the restoration. Evan is also pretty fussy about how his finished projects look. They need to look really, really good. If he has to choose between a crappy set of original side art or a flawless reproduction set, he will choose the latter every time.

Interface Considerations

Target Audience

"If you want to achieve product-satisfaction level of 50%, you cannot do it by making a large population 50% happy with your product. You can only accomplish it by singling out 50% of the people and striving to make them 100% happy. It goes further than that. You can create an even bigger success by targeting 10% of your market and working to make them 100% ecstatic. It might seem counter-intuitive, but designing for a single user is the most effective way to satisfy a broad population." --The Inmates Are Running the Asylum page 126.

User Goals

  1. Users want authentic behavior
  2. Users want to be kept informed
  3. Users want things to "just work" (no configuration)
  4. Users want room to grow (beginner, intermediate, and advanced options)
  5. Users want things that are familiar
  6. Users want to explore/experiment (ie make mistakes)
  7. Users don't like to be wrong and don't want to be made to feel stupid.
  8. Users want convenience.
  9. Users want things that last.
  10. Users want instant gratification.

Design based on user goals

  1. Use same interfaces as original hardware
  2. Everything needed to get up and running should be included in the box
  3. Pre-configure PCB to match user preferences
  4. Lots of LEDs on the PCB so that troubleshooting can be done visually (different colored LEDs would make it even easier)
  5. One power plug that matches original interface
  6. Bundle 1 USB stick and 1 SD card in package, with preloaded software
  7. Integrate with Shaun Wood's MultiROM card
  8. Include ethernet and USB extension cables? (so they can be accessed from the coin door)
  9. Minimize writes to flash drives
  10. Keep the user informed about the status of things
  11. Recover gracefully from power outages
  12. (TODO.. instant gratification)
  13. Blinking lights means "thinking/searching", solid lights means "ready", the absence of a light means "error"

ffmpeg tips

Remove VBI from video, remove audio

ffmpeg -i sourcevideo -an -vcodec huffyuv -vf crop=720x480:0:44 outfile.avi

Extract S16LE audio

ffmpeg -i sourcevideo -acodec pcm_s16le -ar 44100 outfile.wav

exFAT tips

The USB drive should be formatted using the exFAT file system. If formatting on OSX, make sure to choose the "Master Boot Record" partition map scheme.

Personal tools