====================================================== How to communicate with a Genesis Multi-tap. Version 1.0 Date: Thu Sep 25, 2008 Author: Raphael Assenat Source: http://www.raphnet.net/divers/documentation/genesis_multitap.txt ====================================================== -- TABLE OF CONTENT -- 1) Introduction 2) Pinout 3) Hardware handshake 4) Protocol 5) Auto-detecting the multi-tap (and other controllers) 6) Parallel port interface example 7) Sample C source code for parallel port interface --------------- 1) Introduction --------------- This document explains how the Sega multi-tap for the Genesis console communicates at the hardware level. It is hoped that this document will be useful to emulator programmers and hardware designers wishing to support the multi-tap. All the information here was figured out using widely available documentation from the emulation scene, by trial and error, and by probing a working multi-tap connected to a Genesis. I tested using the following models: - Sega MK-1654. Has only one cable going to the console. - Sega MK-1647. Has two cable going to the console. Uses only the cable labelled "Control1" for 4 player mode. (Control2 appears to be a pass-thru from port B when the switch is in the "Extra" position) Please let me know if you test with another model. References: - Sega Genesis hardware notes Version 0.8 (03/02/01), by Charles MacDonald http://cgfm2.emuviews.com - Sega Six Button Controller Hardware Info, Version 1.0 by Charles Rosenberg http://www.cs.cmu.edu/~chuck/infopg/segasix.txt --------- 2) Pinout --------- Pin Description/names 1 D-pad up. Interpreted as bit 0 in this document. 2 D-pad down. Interpreted as bit 1 in this document. 3 D-pad left. Interpreted as bit 2 in this document. 4 D-pad right. Interpreted as bit 3 in this document. 5 Vcc (5 volts) 6 Normally B/A. Real name: TL 7 normally Select. Real name: TH 8 Gnd (button common) 9 Normally C/Start. Real name: TR Normally, all buttons are active low (this includes the D-pad). In this document, data read from pins 1-4 is inverted (1 = pressed, 0 = released). When using a regular controller, TH is an output while TL and TR are inputs. With the multi-tap however, TR becomes an output. --------------------- 3) Hardware handshake --------------------- The multi-tap communicates by the mean of 4 data bits (pins 1-4) and 3 handshake bits. TH is normally high. During communication, it is held low. It appears that TH must rise and fall again before subsequent transfers. At the moment I don't know if there is a minimum high time. When the console changes the level of TR, the multi-tap latches new data. Once it is ready, the multi-tap changes the level of TL to let the console know that new data is ready. ________ __ __ __ __ ___________ TR |__| |__| |__ ... | |__| |_____| __________ __ __ __ __ _________ TL |__| |__| |__ ... | |__| |_____| ____ _____ TH |____________________________________________| _____ Data ---------<>-<>-<>-<>-<>- ... <>-<>-<>-<_____>--------- ----------- 4) Protocol ----------- Let's start with an example. Say after 18 cycles, we receive this from a multi-tap: ff e f e f 000 00 000 00 00 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^-- Unused (due do 3btn controllers) | | | | | | | | '----- Controller D status | | | | | | | '-------- Controller C status | | | | | | '------------ Controller B status | | | | | '--------------- Controller A status | | | | '------------------ Controller D Id | | | '-------------------- Controller C Id | | '---------------------- Controller B Id | '------------------------ Controller A Id '--------------------------- Fixed In this example, the controllers in port A to D are of the 6btn, 3btn, 6btn and 3btn types respectively. The multi-tap only sends the necessary bits for the type of controller. When a port is empty, no bits are transmitted for it. It it therefore mandatory to consider Id bits when interpreting the received data. The following controller Ids are known: Id Controller type -------------------------- 0xE 6 Button controller 0xF 3 Button controller 0x0 No controller Controller status is reported using 2 or 3 nibbles depending on the type of controller. The bits are to be interpreted this way (Most significant bit first): Bit Meaning ---------------------- 1 D-pad left 2 D-pad right 3 D-pad down 4 D-pad up 5 Start button 6 A button 7 C button 8 B button 9 Mode button (6btn only)* 10 X button (6btn only) 11 Y button (6btn only) 12 Z button (6btn only) Yes, the MODE button is usable as a regular button it would seem. Charles Rosenberg's Sega Six button controller documentation version 1.0 did not mention it, but it is available at Pulse-3 on pin 4. Maybe not on all 6 button controllers? For more information about the 6 Button controllers, consult: http://www.cs.cmu.edu/~chuck/infopg/segasix.txt ------------------------------------------------------- 5) Auto-detecting the multi-tap (and other controllers) ------------------------------------------------------- The level observed on D0-D3 with different different TH values (and sequence) depends on the type of controller connected. Applying 4 negative going pulses as described in the segasix.txt document and logging the D0-D3 levels during each cycle gives a unique result for all the peripherals I tried. 1 2 3 4 < cycle H L H L H L H L < TH level --------------------------------------- Nothing: f f f f f f f f 3 Button: f 3 f 3 f 3 f 3 6 Button: f 3 f 0 f f f 3 Multi-tap: 3 f 3 f 3 f 3 f Note: Unlike elsewhere in this document, the values in this section are not inverted. They represent the true logical levels on the pins. ---------------------------------- 6) Parallel port interface example ---------------------------------- For testing, I built the following parallel port Interface. The main difference with the 'Direct pad pro' interface is that DB9 pin 9 is connected to parallel port pin 6 instead of pin 13. A simple switch could be used to create a multi-purpose adapter. Parallel port pin DB9 pin ---------------------------------- 1 (nStrobe OUT used as IN) 1 (Up) 2 (Data0 OUT) 7 (TH (usually SELECT)) 6 (Data4 OUT) 9 (TR (usually C/Start)) 10 (nAck IN) 3 (Left) 11 (Busy IN) 4 (Right) 12 (Paper-out IN) 6 (TL (usually B/A)) 14 (LineFeed OUT used as IN) 2 (Down)) 18-18 (Gnd) 8 DB9 pin 5 (Vcc) is connected to the +5volt line of an USB port. I don't think that using diodes on data pins to power 4 controllers and the multi-tap would work. --------------------------------------------------- 7) Sample C source code for parallel port interface --------------------------------------------------- /******************************************************************** * Sample program for using the sega multi-tap with a parallel port.* * Protocol and parallel port interface information available * * in the following document: * * * * http://www.raphnet.net/divers/documentation/genesis_multitap.txt * * * * This source code is in the public domain. Compile using DJGPP * * under DOS. * ********************************************************************/ #include #include // for usleep #include // for outportb and inportb #define PORT_DATA 0x378 #define PORT_STATUS (PORT_DATA+1) #define PORT_CONTROL (PORT_DATA+2) #define TH_LOW() do { last_out &= ~0x01; outportb(PORT_DATA, last_out); } while(0) #define TH_HIGH() do { last_out |= 0x01; outportb(PORT_DATA, last_out); } while(0) #define TR_LOW() do { last_out &= ~0x10; outportb(PORT_DATA, last_out); } while(0) #define TR_HIGH() do { last_out |= 0x10; outportb(PORT_DATA, last_out); } while(0) static unsigned char last_out = 0x00; static unsigned char readGen() { unsigned char d,s,c; unsigned char res=0; d = inportb(PORT_DATA); s = inportb(PORT_STATUS); c = inportb(PORT_CONTROL); if (c & 0x01) // up res |= 0x01; if (c & 0x02) // dn res |= 0x02; if (s & 0x40) // lf res |= 0x04; if (s & 0x80) // rt res |= 0x08; if (s & 0x10) // C/Start TR res |= 0x10; if (s & 0x20) // B/A TL res |= 0x20; // let all buttons be active high res ^= 0x34; return res; } static void printnib(unsigned char tmp) { int j; for (j=0x08; j; j>>=1) putchar(tmp&j?'1':'0'); } static void printState(unsigned char nibs[18]) { int i,ctl; int pos = 6; // first data nibble /* Filter apparently bad packets */ if (nibs[0] != nibs[1]) return; if (nibs[0] != 0xf) return; for (ctl=0; ctl<4; ctl++) { switch(nibs[ctl+2]) { case 0xe: printf("[%d]6btn: ", ctl); for (i=0; i<3; i++,pos++) { printnib(nibs[pos]); } break; case 0xf: printf("[%d]3btn: ", ctl); for (i=0; i<2; i++,pos++) { printnib(nibs[pos]); } break; case 0x0: printf("[%d]None ", ctl); break; default: printf("???"); } printf(" "); } printf("\n"); } int main(void) { int i; TH_HIGH(); usleep(100000); for (;;) { static unsigned char tmp, last; unsigned char nibs[18]; TH_LOW(); usleep(100); for (i=0; i<18; i++) { if (i%2) TR_HIGH(); else TR_LOW(); // wait for handshake transition on TL. This // will hang forever if it is no multi-tap... do { tmp = readGen(); } while ((tmp&0x20) == last); nibs[i] = tmp & 0xf; last = tmp&0x20; } printState(nibs); TR_HIGH(); TH_HIGH(); usleep(16670); } return 0; }