Thursday, May 21, 2015

How to auto-detect the laserdisc player type for Dexter

One of the "killer" features I want to add to Dexter is the ability for it to auto-detect the player type that the game is configured for.  For most games, this is easy as the game only supported one laserdisc player.  For a few games like Cobra Command, this is hard because the game supported at least four different player types.  And then there's the Rick Dyer games which support the LD-V1000 and PR-7820 and it's these games that I want to discuss in this blog entry.

Detecting whether the game is configured for LD-V1000 or PR-7820 mode is a little tricky because one of the data lines is an input for one mode and an output for another mode, and I don't want Dexter to be in output mode at the same time as the game is in output mode.  Some good news is that the programmer(s) of Dragon's Lair were lazy when it came to designing their LD-V1000 code so I can use that to my advantage when coming up with a strategy.

Here is the code in Dragon's Lair that sends a byte to the LD-V1000:

ROM:0231 SendA021ToLDV1000:                      ; CODE XREF: SendA021ToLdp+15 p
ROM:0231                                         ; SendA021ToLdp+1C p
ROM:0231                 push    hl
ROM:0232                 ld      a, (A021LdpCmdByte)
ROM:0235                 ld      (E020LaserdiscData), a
ROM:0238
ROM:0238 WaitForStatusStrobeLow:                 ; CODE XREF: SendA021ToLDV1000+C j
ROM:0238                 ld      hl, C010SelCpB  ; bit 6: status strobe (for LD-V1000), not used for PR-7820
ROM:0238                                         ; bit 7: cmd strobe for LD-V1000, READY' for PR-7820
ROM:023B                 bit     6, (hl)
ROM:023D                 jp      nz, WaitForStatusStrobeLow
ROM:0240
ROM:0240 WaitForStatusStrobeHigh:                ; CODE XREF: SendA021ToLDV1000+14 j
ROM:0240                 ld      hl, C010SelCpB  ; bit 6: status strobe (for LD-V1000), not used for PR-7820
ROM:0240                                         ; bit 7: cmd strobe for LD-V1000, READY' for PR-7820
ROM:0243                 bit     6, (hl)
ROM:0245                 jp      z, WaitForStatusStrobeHigh
ROM:0248                 ld      hl, A020CurrentValOfE008 ; caches current value that has been written to E008 (since E008 cannot be read)
ROM:024B                 res     5, (hl)         ; enable output to laserdisc
ROM:024D                 res     7, (hl)         ; enable ENTER' line for LD-V1000
ROM:024F                 ld      a, (hl)
ROM:0250                 ld      (E008LoadMisc), a ; bit 5: OutDiscData', 0=enable output to laserdisc, 1=disable output
ROM:0250                                         ; bit 6: pin 11. PR-7820 ENTER (for PR-7820 only, can be disconnected on board for LD-V1000 operation)
ROM:0250                                         ; bit 7: pin 17. INT/EXT for PR-7820, ENTER for LD-V1000
ROM:0253
ROM:0253 WaitForCmdStrobeLow:                    ; CODE XREF: SendA021ToLDV1000+27 j
ROM:0253                 ld      hl, C010SelCpB  ; bit 6: status strobe (for LD-V1000), not used for PR-7820
ROM:0253                                         ; bit 7: cmd strobe for LD-V1000, READY' for PR-7820
ROM:0256                 bit     7, (hl)
ROM:0258                 jp      nz, WaitForCmdStrobeLow
ROM:025B
ROM:025B WaitForCmdStrobeHigh:                   ; CODE XREF: SendA021ToLDV1000+2F j
ROM:025B                 ld      hl, C010SelCpB  ; bit 6: status strobe (for LD-V1000), not used for PR-7820
ROM:025B                                         ; bit 7: cmd strobe for LD-V1000, READY' for PR-7820
ROM:025E                 bit     7, (hl)
ROM:0260                 jp      z, WaitForCmdStrobeHigh
ROM:0263                 ld      hl, A020CurrentValOfE008 ; caches current value that has been written to E008 (since E008 cannot be read)
ROM:0266                 set     5, (hl)         ; disable output
ROM:0268                 ld      a, (hl)
ROM:0269                 ld      (E008LoadMisc), a ; bit 5: OutDiscData', 0=enable output to laserdisc, 1=disable output
ROM:0269                                         ; bit 6: pin 11. PR-7820 ENTER (for PR-7820 only, can be disconnected on board for LD-V1000 operation)
ROM:0269                                         ; bit 7: pin 17. INT/EXT for PR-7820, ENTER for LD-V1000
ROM:026C                 pop     hl
ROM:026D                 ret
ROM:026D ; End of function SendA021ToLDV1000

<end>

Let me explain what it does.  First, it waits for the status strobe (pin 11) to go low (becomes active) and then high (becomes inactive) again.  This is because it does not want to go into output mode while the status strobe is active.  As soon as the status strobe ends (ie goes high), Dragon's Lair immediately goes into output mode, enables/lowers the LDV1000_ENTER' line (pin 17) and puts its command on the data bus. (side note: I think it would've been better if they had waited until after the command strobe started but maybe they were worried about performance)  It then waits for the command strobe (pin 7) to go low (become active) and then go high (become inactive).  Finally, it goes back into input mode.  NOTE that it never disables the LDV1000_ENTER' (pin 17) line.

Now let's examine how PR-7820 mode works:

ROM:026E SendA021ToLdpPR7820:                    ; CODE XREF: SendA021ToLdp+7 j
ROM:026E                 push    hl
ROM:026F                 ld      hl, A020CurrentValOfE008 ; caches current value that has been written to E008 (since E008 cannot be read)
ROM:0272                 res     7, (hl)         ; force INT/EXT' low
ROM:0274                 ld      a, (hl)
ROM:0275                 ld      (E008LoadMisc), a ; bit 5: OutDiscData', 0=enable output to laserdisc, 1=disable output
ROM:0275                                         ; bit 6: pin 11. PR-7820 ENTER (for PR-7820 only, can be disconnected on board for LD-V1000 operation)
ROM:0275                                         ; bit 7: pin 17. INT/EXT for PR-7820, ENTER for LD-V1000
ROM:0278                 ld      a, (A021LdpCmdByte)
ROM:027B                 ld      (E020LaserdiscData), a ; load output '374 with byte to be sent to PR-7820
ROM:027E                 ld      hl, A020CurrentValOfE008 ; caches current value that has been written to E008 (since E008 cannot be read)
ROM:0281                 res     5, (hl)         ; enable output of LDP data '374
ROM:0283                 ld      a, (hl)
ROM:0284                 ld      (E008LoadMisc), a ; bit 5: OutDiscData', 0=enable output to laserdisc, 1=disable output
ROM:0284                                         ; bit 6: pin 11. PR-7820 ENTER (for PR-7820 only, can be disconnected on board for LD-V1000 operation)
ROM:0284                                         ; bit 7: pin 17. INT/EXT for PR-7820, ENTER for LD-V1000
ROM:0287                 res     6, (hl)         ; enable ENTER' line for PR-7820 so it knows there is a command waiting
ROM:0289                 ld      a, (hl)
ROM:028A                 ld      (E008LoadMisc), a ; bit 5: OutDiscData', 0=enable output to laserdisc, 1=disable output
ROM:028A                                         ; bit 6: pin 11. PR-7820 ENTER (for PR-7820 only, can be disconnected on board for LD-V1000 operation)
ROM:028A                                         ; bit 7: pin 17. INT/EXT for PR-7820, ENTER for LD-V1000
ROM:028D                 call    MakeADelay      ; creates a delay (I haven't calculated how long)
ROM:0290                 ld      hl, A020CurrentValOfE008 ; caches current value that has been written to E008 (since E008 cannot be read)
ROM:0293                 set     6, (hl)         ; disable ENTER' line
ROM:0295                 ld      a, (hl)
ROM:0296                 ld      (E008LoadMisc), a ; bit 5: OutDiscData', 0=enable output to laserdisc, 1=disable output
ROM:0296                                         ; bit 6: pin 11. PR-7820 ENTER (for PR-7820 only, can be disconnected on board for LD-V1000 operation)
ROM:0296                                         ; bit 7: pin 17. INT/EXT for PR-7820, ENTER for LD-V1000
ROM:0299                 call    MakeADelay      ; creates a delay (I haven't calculated how long)
ROM:029C                 ld      hl, A020CurrentValOfE008 ; caches current value that has been written to E008 (since E008 cannot be read)
ROM:029F                 set     5, (hl)         ; disable output of LDP data '374
ROM:02A1                 ld      a, (hl)
ROM:02A2                 ld      (E008LoadMisc), a ; bit 5: OutDiscData', 0=enable output to laserdisc, 1=disable output
ROM:02A2                                         ; bit 6: pin 11. PR-7820 ENTER (for PR-7820 only, can be disconnected on board for LD-V1000 operation)
ROM:02A2                                         ; bit 7: pin 17. INT/EXT for PR-7820, ENTER for LD-V1000
ROM:02A5                 pop     hl
ROM:02A6                 ret
ROM:02A6 ; END OF FUNCTION CHUNK FOR SendA021ToLdp

<end>

The PR-7820 mode:

  • - Lowers/enables PR7820_INT/EXT' (pin 17)
  • - Puts command on data bus
  • - Goes into output mode
  • - Lowers/enables PR7820_ENTER' to tell the PR7820 to process the command (pin 11)
  • - Delays for a long time (I haven't measured how long, but Thayer's Quest waits for 20 ms)
  • - Raises/disables PR7820_ENTER' (pin 11)
  • - Goes into input mode
  • - NOTE : does not raise/disable PR7820_INT/EXT' (pin 17)


So what is a strategy for auto-detecting which player is plugged in?

Here's what I've come up with so far:

- Both modes lower pin 17 and keep it low so we can ignore that as it does not help us.
- PR-7820 mode treats pin 11 as an ouput while LD-V1000 reads pin 11 as an input.
- Both modes treat pin 7 as an input, but LD-V1000 will be outputting a command to the data bus while it is waiting for pin 7 to go low, while PR-7820 mode will be in input mode (see $1F9 in the ROM, I didn't post this disassembly).

So one possible algorithm to detect the player type is this:
- Enable input mode for pin 11 and enable its internal pull-up resistor.  Put pin 7 in output  mode.  Put data bus in input mode with pull-up resistors enabled on all data lines.
- Wait a long time (like the amount of time from dragon's lair powering on until it sends the first play command) for pin 11 to go low.
- If pin 11 goes low, it probably is PR-7820 mode.  Check the data bus to see if there is a command to be extra sure.
- If pin 11 hasn't gone low, go into ouput mode, lower pin 11, and quickly go back into input mode with internal pull-up enabled (enough time to simulate the status strobe).  Check to make sure that the line comes back to a high state within a reasonable amount of time.  If it doesn't come high, it probably is PR-7820 mode and Dexter and the game lowered the line at the same time (which is regrettable but I can't see an alternative).  If it comes high very quickly, it probably is still PR-7820 mode forcing the line high.  If it comes high a little slower, then it probably is LD-V1000 mode so lower pin 7 and check to see that a command is on the data bus.  If a command is present, then it is for sure LD-V1000 mode.  If the databus is still empty, it means that either it's LD-V1000 mode but we received a FF 'no entry' command or that nothing is plugged into Dexter and we have to start the test over again.
- If starting the test over again, wait less time the second iteration so that the boot delay isn't drawn out.

No comments:

Post a Comment