EA multitap

Called "4 Way Play", this is Electronic Arts' take on a multitap on the Mega Drive. Originally supported mostly by their games but eventually by other later games (along with Sega's own multitap). This is the easiest of the two to use, albeit only supports controllers and it takes up both ports (so it can't be used with anything else at the same time).

The protocol here is also supported by some Sega multitaps by configuring them into EXTRA mode.

Setting up the multitap

The EA multitap uses up both I/O ports: the first port is used to read the controllers, while the second port is used to select which controller to read. The ports should be configured as follows:

Writing to the second port (IoData2) makes different data show up in the first port (IoData1), so let's define the different values the multitap knows about:

EAMULTI_1P:   equ $0C  ; Controller #1
EAMULTI_2P:   equ $1C  ; Controller #2
EAMULTI_3P:   equ $2C  ; Controller #3
EAMULTI_4P:   equ $3C  ; Controller #4
EAMULTI_ID:   equ $7C  ; Multitap ID

Probably the best way to check if the multitap is there (after the above has been setup to try it) is by doing something like this (you should burn a few cycles after each write to give it time to work, e.g. three or four NOP will probably do):

  1. Write EAMULTI_1P to IoData2
  2. Read from IoData1
  3. Write EAMULTI_ID to IoData2
  4. Read from IoData1

Then look at bits 1-0 from each read: if they were 00 in the second read but something else in the first, chances are there's an EA multitap connected.

Remember to keep the Z80 out of the way while accessing I/O ports.

    ; Prevent the Z80 from touching
    ; the 68000 bus while we're busy
    ; with the I/O ports
    FastPauseZ80
    
    ; Set up ports to check for
    ; EA multitap
    move.b  #$40, (IoCtrl1)
    move.b  #$7F, (IoCtrl2)
    move.b  #$40, (IoData1)
    
    ; Read from controller #1
    move.b  #EAMULTI_1P, (IoData2)
    nop
    nop
    moveq   #%0000011, d0
    and.b   (IoData1), d0
    
    ; Read from multitap ID
    move.b  #EAMULTI_ID, (IoData2)
    nop
    nop
    moveq   #%0000011, d1
    and.b   (IoData1), d1
    
    ; We can let the Z80 run now
    ResumeZ80
    
    ; Check if both are valid
    tst.b   d0
    beq     @NotEA
    tst.b   d1
    bne     @NotEA
    
    ; EA multitap present...

Reading the controllers

The EA multitap is a glorified multiplexer. TH (bit 6) is shared among all controllers, while port 2 is used to pick which controller is being read from bits 5-0 in port 1.

So, go check how to read a controller normally (3-button or 6-button), except that instead of writing to the port and reading back from it, you do it this way (waiting a handful of NOP or so after every write to IoData2 as earlier):

  1. Write $40/$00 to IoData1
  2. Write EAMULTI_1P to IoData2
  3. Read back from IoData1 (reads controller 1)
  4. Write EAMULTI_2P to IoData2
  5. Read back from IoData1 (reads controller 2)
  6. Write EAMULTI_3P to IoData2
  7. Read back from IoData1 (reads controller 3)
  8. Write EAMULTI_4P to IoData2
  9. Read back from IoData1 (reads controller 4)

Maybe the easiest way is to blindly take all the reads and put them into a buffer, then read the values off the buffer to figure out what the controller is (toggling a 3-button controller too often won't hurt).

ReadEa:
    lea     (IoData1), a0
    lea     (IoData2), a1
    lea     (EaBuffer), a2
    
    ; Keep the Z80 out of the way
    ; while we touch the I/O ports
    FastPauseZ80
    
    ; Read all the controller values
    ; (see below for how many times)
    moveq   #@NUM_STEPS-1, d0
    moveq   #$40, d1
@Loop:
    ; Toggle select line
    move.b  d1, (a0)
    
    ; Read controller #1
    move.b  #EAMULTI_1P, (a1)
    nop
    nop
    nop
    move.b  (a0), (a2)+
    
    ; Read controller #2
    move.b  #EAMULTI_2P, (a1)
    nop
    nop
    nop
    move.b  (a0), (a2)+
    
    ; Read controller #3
    move.b  #EAMULTI_3P, (a1)
    nop
    nop
    nop
    move.b  (a0), (a2)+
    
    ; Read controller #4
    move.b  #EAMULTI_4P, (a1)
    nop
    nop
    nop
    move.b  (a0), (a2)+
    
    ; Onto next value
    ; Clever trick here: d1 will swap
    ; between $40 and $00 (remember
    ; we used moveq earlier!)
    swap    d1
    dbf     d0, @Loop
    
    ; Z80 can run again now
    ResumeZ80
    
    ; We're done
    rts

; The number of times to loop
; Use 2 if you only need 3-button pads
; Use 7 if you want 6-button pads too
; Buffer size will be @NUM_STEPS*4
@NUM_STEPS: equ 7

Multitap support in the ROM header is specified by adding "4" in the devices field (the one at $000190). Since it doesn't specify which multitap is supported, it's a good idea to consider supporting the Sega multitap as well.

Support for the extra buttons in the 6-button controller ("6") should also be specified if supported. On the other hand, if the game simply ignores them, you shouldn't add it.