           ===========================================
           PSX Controller Port Device Programming Info
           ===========================================

psxport.device allows access to one PSX compatible controller, or a MultiTap
for access to up to four controllers and memory cards.

To use the PSX adapter directly, open the psxport.device as you would the
gameport.device.  If a controller is connected to the cable, only unit
0 will open; all others will fail.  If a MultiTap is connected, units
0 to 3 are the controller ports, and unit 4 to 7 are the memory cards.
If a controller or card is not connected to the MultiTap, that unit
will fail to open.  Please note that controllers do not have to be
consecutive: a user may have a controller at unit 0 and one in unit 2,
but not in unit 1; don't just stop if a unit fails to open, be sure to
check ALL units.

After a unit is opened, you should check the controller type (GPD_ASKCTYPE);
if it is GPCT_ALLOCATED, you should close the device and go on to the next
unit.  Note: you don't HAVE to... you could use an allocated controller,
but it is not recommended unless you know what you are doing; an example
would be to have one task open and allocate an analog stick controller,
but only use the digital part; another task could open the same controller
and use the analog sticks.

If the controller type is GPCT_NOCONTROLLER, it is not in use and you should
set the controller type (GPD_SETCTYPE) to GPCT_ALLOCATED.  At this point,
you have exclusive access to the controller or card.

Controllers are read in the same way as normal gameport controllers:
setup the IO for GPD_READEVENT and send it to the device; use DoIO()
if you wish to wait for the data, and use SendIO() if you don't.  A
typical usage would be to setup and open the device, allocate the
controller, then SendIO() the first GPD_READEVENT.  In the game input
checking code, you do GetMsg() on the port used for the IO you sent;
if you get a message, the device has finished reading the controller
and the input event structure holds the data.  If it returns NULL, the
packet transfer is still in pogress and input processing should be
skipped this time through the loop.  After handling the IO, use
SendIO() to start the next packet transfer.


The input event fields hold different data depending on the controller
connected:

Mouse:          ie_Class    = $12
                ie_SubClass = $5A
                ie_Code     = 1   1   1   1   1   1   1   1
                ie_Code+1   = 1   1   1   1   LMB RMB 1   1
                ie_X        = Delta X +=left, -=right (byte sign-extended to word)
                ie_Y        = Delta Y +=up, -=down (byte sign-extended to word)

Wheel:          ie_Class    = $23
                ie_SubClass = $5A
                ie_Code     = Lft D   Rt  U   S   1   1   Sel
                ie_Code+1   = 1   1   A   B   R   1   1   1
                ie_X        = Wheel Twist $00=left, $80=centered. $FF=right
                ie_X+1      = I Switch ADC data $00 to $FF
                ie_Y        = II Switch ADC data $00 to $FF
                ie_Y+1      = L Switch ADC data $00 to $FF

Joypad:         ie_Class    = $41
                ie_SubClass = $5A
                ie_Code     = L   D   R   U   S   1   1   Sel
                ie_Code+1   = []  X   O   <|  R1  L1  R2  L2

Dual Analog:    ie_Class    = $53
(mode 2)        ie_SubClass = $5A
                ie_Code     = L   D   R   U   S   1   1   Sel
                ie_Code+1   = []  X   O   <|  R1  L1  R2  L2
                ie_X        = Right Handle X $00=left, $80=centered, $FF=right
                ie_X+1      = Right Handle Y $00=up, $80=centered. $FF=down
                ie_Y        = Left Handle X $00=left, $80=centered, $FF=right
                ie_Y+1      = Left Handle Y $00=up, $80=centered. $FF=down

Dual Analog:    ie_Class    = $73 (or $F3)
                ie_SubClass = $5A
                ie_Code     = L   D   R   U   S   R3  L3  Sel
                ie_Code+1   = []  X   O   <|  R1  L1  R2  L2
                ie_X        = Right Handle X $00=left, $80=centered, $FF=right
                ie_X+1      = Right Handle Y $00=up, $80=centered. $FF=down
                ie_Y        = Left Handle X $00=left, $80=centered, $FF=right
                ie_Y+1      = Left Handle Y $00=up, $80=centered. $FF=down

Light Gun:	ie_Class    = $63
		ie_SubClass = $5A
		ie_Code     = 1   1   1   1   A   1   1   1
		ie_Code+1   = 1   B   T   1   1   1   1   1
		ie_X        = X coord, <$10=off-screen, $10-$1E0=on-screen
		ie_Y        = Y coord, <$10=off-screen, $10-$130=on-screen

=====
Notes
=====

1 - The Sony Dual Analog controllers are VERY sensitive and tend not to
recenter very well; the MadCatz controller is less sensitive and recenters
well.  Be sure to check with a Sony Dual Analog controller, have sensitivity
scaling, and decent centering code.

2 - All dual analog controllers can be switched out of analog mode on the
fly; be sure to check the class before handling the data.  Don't assume
that because a controller WAS in analog mode, that it IS in analog mode.

3 - Although there is class definition for the MultiTap, you will never
see this code; the MultiTap returns this code the first time it is
accessed after power-on.  The device tests peripherals when opened,
so this message is received by the device during test phase.

4 - The Mad Catz Championship Racing Wheel changes modes constantly
between Joypad and Wheel to return other buttons than those defined
for a wheel. Centering code for a wheel should not clear the centered
flag when the mode changes.

5 - Light guns like the Namco Light Gun are now supported and appear
to return ($01,$0A) when pointed off-screen; please use ($10,$10)
for checking for off-screen.  The Namco Gun returns $02C to $1DA for
onscreen X coordinates, and $021 to $12F for on-screen Y coordinates
REGARDLESS of the screenmode (high-res, low-res, laced).

6 - If in doubt as to how a peripheral is working, use PSXPortTest to
observe it's behavior.  Please report unsupported peripherals.

===============
GPD_COMMUNICATE
===============

This extension command is used for shock and reading/writing
memory cards.

IO_COMMAND = GPD_COMMUNICATE
IO_DATA    = pointer to command to send, on return, holds the data returned
IO_LENGTH  = length of command to send/data to return
IO_ACTUAL  = retuned length of command accepted by the peripheral

If the unit was opened with Flags = 0, it Wait()s for each byte
with a maximum timeout of 1.5 milliseconds; if you send a command
that is too long for the controller, it will wait 1.5 milliseconds
for each byte beyond the length of the command.  If you are not sure
about the command length, check the io_Actual field when the IO is
returned; it will contain the actual length of the command packet.
You can open the unit with Flags = 1; this will cause the unit to busy
loop wait on each byte and timeout after 1ms.  This only applies to
GPD_COMMUNICATE and is no longer recommended with the addition of a
timeout for the normal operation.

===============
GPD_SETPORTRATE
===============

This extension command is used to change the timing of the packet
transmission and reception.

IO_COMMAND = GPD_SETPORTRATE
IO_DATA    = pointer to PSXSetPortRate structure
IO_LENGTH  = size of PSXSetPortRate structure (8)

The PSXSetPortRate structure has four fields.  The fields are
set to the previous value upon return; if a field is set to
-1, the current value is returned and not changed.  Setting all
fields to -1 allows you to examine the current timing for the
datastream.

SelDelay is the time from selecting the device until you
can start the data stream.

CmdDelay is the time from setting the command bit until the
clock bit is changed.

ClkDelay is the time from changing the clock until the data
bit is read and the clock changed.

DatDelay is the time from changing the clock after reading the
data until you can start the next bitcell.

All time values are the number of times - 1 to read the CIA.

If in doubt about port timing, don't mess with it; allow
the device to handle it using the preset values for the
user-selected device speed for that unit.  PSXPortTest allows
you to test the controller at different speeds and then set
the environment variable for that unit.

=====
Shock
=====

All I have figured out so far is how to turn on and off the shock
on a Mad Catz Dual Shock controller.  This uses GPD_COMMUNICATE.

Setup a buffer with the data:

ON:  $0n,$42,$00,$FF,$FF,$01
       ^      ^   ^   ^
       |      |   |   |
       |      |   |    - anything other than 0 will turn it on
       |      |   - can be any value
       |      - MUST be 0
       - unit + 1 (unit 0 = $01, etc)

OFF: $0n,$42,$00,$FF,$00,$01
       ^      ^   ^   ^
       |      |   |   |
       |      |   |    - MUST be 0 to turn it off
       |      |   - can be any value
       |      - MUST be 0
       - unit + 1 (unit 0 = $01, etc)

This data is overwritten by the GPD_COMMUNICATE command;
do not use embedded strings unless you wish to lose it. :^}

When on, the shock vibrates the controller for a second, turns
off for a second, then repeats.  It does not stay on constantly
unless you continually send the ON command.

There are two actuators in the PSX controllers: one can be
turned of or on, and the second can be set to one of 256
levels.  The actuator data is sent every so often (e.g., during
the vertical blank) as data embedded in the read controller
command.  The Mad Catz evidently defaults to the first actuator
which is why my commands above only turn it on and off.

This part will be updated as I figure out more about how the
shock capability works.

Sony Dual Shock controllers (and maybe others) require a 9V
battery to be hooked to the adapter cable to supply the shock
motor power.  Without the battery, the shock feature will not
work.


============
Memory Cards
============

Memory Cards are accessed using a MultiTap and opening units
4 to 7; if the unit won't open, there is no card at that
position in the MultiTap.

The GPD_COMMUNICATE command is used to read/write the card.
Remember that the data buffer WILL be overwritten.


Read:    $8n,'R',0,0,AddrHi,AddrLo,0,0,0,0,128 bytes of 0,0,0 (140 bytes)
 where n = 1 to 4 for units 4 to 7

returns: xx,xx,$5A,$50,xx,xx,$5C,$5D,AddrHi,AddrLo,128 bytes of data,XORcode,'G'


Write:   $8n,'W',0,0,AddrHi,AddrLo,128 bytes of data,XORcode,0,0,0 (138 bytes)
where n = 1 to 4 for units 4 to 7

returns: xx,xx,$5A,$5D,xx,AddrHi,AddrLo,128 bytes of data,$5C,$5D,'G' or 'N'
where 'G' means the write was good and 'N' means the write failed.


The XORcode is made by adding all the data as bytes along with
the address and then doing an xor on the result.


The following talks about the memory card format and hasn't been verified.


memory card format
==================

  memory start command = '81h'

write command

    fuction		count	
    ----------------------------
    command header        4	
    write address	  2	
    data block		128	
    XOR code		  1	
    write end mark	  2	
    write end flag	  1	
    ----------------------------

     command header			    address        
    |--------|--------|--------|--------|  |--------|--------|
CMD |   81h  | 57h 'W'|   00h  |   00h  |  | adrs H | adrs L |
    |--------|--------|--------|--------|  |--------|--------|
DAT |  ----  |*  00h  | 5Ah 'Z'| 5Dh ']'|  |* 00h   |*adrs H |
    |--------|--------|--------|--------|  |--------|--------|

     data block    				     XOR code
    |--------|--------|--------|--------|--------|  |--------|  
CMD |  dt 1  |  dt 2  |  ....  | dt 127 | dt 128 |  |   xor  |  
    |--------|--------|--------|--------|--------|  |--------|  
DAT |*adrs L |* dt 1  |* ....  |*dt 126 |*dt 127 |  |*dt 128 |  
    |--------|--------|--------|--------|--------|  |--------|  

     end mark      	 end flag   	     end flag(error)
    |--------|--------|	|--------|		|--------|
CMD |   00h  |   00h  |	|   00h  |		|   00h  |
    |--------|--------|	|--------|		|--------|
DAT | 5Ch '\'| 5Dh ']'|	| 47h 'G'|		| 4Eh 'N'|
    |--------|--------|	|--------|		|--------|

    XOR CODE = (adrs H or L + data block) ex-or

   '*' = ignor data

 * write data example

	from PS data
	+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F   ASCII
   +00  81 57 00 00 00 80 53 43 11 01 82 71 82 68 82 63   .W....SC..‚q‚h‚c
   +10  82 66 82 64 81 40 82 71 82 60 82 62 82 64 82 71   ‚f‚d@‚q‚`‚b‚d‚q
   +20  81 40 83 5E 83 43 83 80 83 65 81 5B 83 75 83 8B   @ƒ^ƒCƒ€ƒe[ƒuƒ‹
   +30  00 CD 7B 7B 77 7B FB C7 FB D7 FB DB FB DB DD DB   .Í{{w{........ÝÛ
   +40  DB DB DB DB 7D C7 CB CD FD FD FF FC B7 CC FD DC   ÛÛÛÛ}ÇËÍ.....Ì.Ü
   +50  FF DE FF FC FF FC DB FF DD FD DD FD FD DF C7 77   .Þ......Ý.Ý..ßÇw
   +60  C7 77 B7 77 B7 77 BC F7 7A EF 38 EB F5 E2 B3 DE   Çw·w·w¼...8..â³Þ
   +70  71 D6 4F D2 EC C5 8A B9 48 AD E6 A0 DD D2 5D C6   qÖOÒ..Š¹H­æ ÝÒ]Æ
   +80  FE B9 7C AD 1A A1 1A 00 00 00                     .¹|­.¡....      

	to memory
	+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F   ASCII
   +00  01 00 5A 5D 00 00 80 53 43 11 01 82 71 82 68 82   ..Z]...SC..‚q‚h‚c
   +10  63 82 66 82 64 81 40 82 71 82 60 82 62 82 64 82    ‚f‚d@‚q‚`‚b‚d‚q
   +20  71 81 40 83 5E 83 43 83 80 83 65 81 5B 83 75 83    @ƒ^ƒCƒ€ƒe[ƒuƒ‹
   +30  8B 00 CD 7B 7B 77 7B FB C7 FB D7 FB DB FB DB DD    .Í{{w{........Ý 
   +40  DB DB DB DB DB 7D C7 CB CD FD FD FF FC B7 CC FD   ÛÛÛÛÛ}ÇËÍ.....Ì. 
   +50  DC FF DE FF FC FF FC DB FF DD FD DD FD FD DF C7   Ü.Þ......Ý.Ý..ßÇ 
   +60  77 C7 77 B7 77 B7 77 BC F7 7A EF 38 EB F5 E2 B3   wÇw·w·w¼...8..â³ 
   +70  DE 71 D6 4F D2 EC C5 8A B9 48 AD E6 A0 DD D2 5D   ÞqÖOÒ..Š¹H­æ ÝÒ] 
   +80  C6 FE B9 7C AD 1A A1 5C 5D 47                     Æ.¹|­.¡\]G       

read command

    fuction		count	
    ----------------------------
    command header        4	
    read address	  2	
    command acknowledge   1     
    data header           1     
    data address	  2     
    data block		128	
    XOR code		  1	
    read end flag	  1	
    ----------------------------

     command header			    address       	command ack
    |--------|--------|--------|--------|  |--------|--------|	|--------|
CMD |   81h  | 52h 'R'|   00h  |   00h  |  | adrs H | adrs L |	|   00h  |
    |--------|--------|--------|--------|  |--------|--------|	|--------|
DAT |  ----  |*  00h  | 5Ah 'Z'| 5Dh ']'|  |*  00h  |*adrs H |	| 5Ch '\'|
    |--------|--------|--------|--------|  |--------|--------|	|--------|

     data header     data address
    |--------|	    |--------|--------|
CMD |   00h  |	    |   00h  |   00h  |
    |--------|	    |--------|--------|
DAT | 5Dh ']'|	    | adrs H | adrs L |
    |--------|	    |--------|--------|

     data block					    XOR CODE      END FLAG
    |--------|--------|--------|--------|--------|  |--------|    |--------|
CMD |   00h  |   00h  |  ....  |   00h  |   00h  |  |   00h  |    |   00h  |
    |--------|--------|--------|--------|--------|  |--------|    |--------|
DAT |  dt 1  |  dt 2  |  ....  | dt 127 | dt 128 |  |   xor  |    | 47h 'G'|
    |--------|--------|--------|--------|--------|  |--------|    |--------|

    XOR CODE = (dara adrs H or L + data block) ex-or

    '*' = ignor data

 * read data example

	from PS
	+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F   ASCII
   +00  81 52 00 00 00 80 00 00 00 00 00 00 00 00 00 00   .R.............. 
   +10  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
   +20  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
   +30  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
   +40  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
   +50  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
   +60  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
   +70  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................ 
   +80  00 00 00 00 00 00 00 00 00 00 00 00               ............     

	to PS
	+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F   ASCII
   +00  01 00 5A 5D 00 00 5C 5D 00 80 53 43 11 01 82 71   ..Z]..\]..SC..‚q 
   +10  82 68 82 63 82 66 82 64 81 40 82 71 82 60 82 62   ‚h‚c‚f‚d@‚q‚`‚b 
   +20  82 64 82 71 81 40 83 5E 83 43 83 80 83 65 81 5B   ‚d‚q@ƒ^ƒCƒ€ƒe[ 
   +30  83 75 83 8B 00 CD 7B 7B 77 7B FB C7 FB D7 FB DB   ƒuƒ‹.Í{{w{...... 
   +40  FB DB DD DB DB DB DB DB 7D C7 CB CD FD FD FF FC   ..ÝÛÛÛÛÛ}ÇËÍ.... 
   +50  B7 CC FD DC FF DE FF FC FF FC DB FF DD FD DD FD   .Ì.Ü.Þ......Ý.Ý. 
   +60  FD DF C7 77 C7 77 B7 77 B7 77 BC F7 7A EF 38 EB   .ßÇwÇw·w·w¼...8. 
   +70  F5 E2 B3 DE 71 D6 4F D2 EC C5 8A B9 48 AD E6 A0   .â³ÞqÖOÒ..Š¹H­æ  
   +80  DD D2 5D C6 FE B9 7C AD 1A A1 1A 47               ÝÒ]Æ.¹|­.¡.G     

memory card block map
       block    block
	 No. 	name		fuction
	  0	directory	card ID , directory
	  1	data block 1	data
	  2	data blcok 2
	  |	      |
	 14	data block 14
	 15	data block 15

memory card frame map
=====================

    * directory block map
        frame No.	fuction
	  00h		memory card ID
	  01h-0fh	directory 0-14
	  10h-23h	reserve
	  24h-3eh	not use
	  3fh		write test

    * data block map(top)
        frame No.	fuction
	  00h		title & icon palete
	  01h-n		icon data
	  n+1		save data

    * data block map(link)
        frame No.	fuction
	  00h		save data

block detail
============

directory block detail

   * memory card ID
	+00,01	'MC'
	+02-7E	00h
	+7F	XOR CODE

  note : XOR Code = 00 - 7e data EX-or

   * directory frame
	+00	data flag
		bit 7-4		A  free(not use , delete)
				5  busy
				F  reserve
		bit 3-0		0  not use
				1  top block
				2  link block
				3  link end block
				F  reserve
	+01-03	????
	+04-07	byte length	06,05,04  (block count * 2000h)
				 0,00,00  not use , link , link end
				 0,20,00  1 block used

	+08,09	link directory number(0-14)
		FFFFh = no link block
	+0A,0B	'BI'
	+0C -	product code + game  ID + 00 (ASCIIZ string)
	+7F	XOR code

    * data flag type
	A0  not use(default , format)
	51  busy / link block top
	52         link block
	53         link end block
	Ax  delete block
	FF  reserve block

  * directory frame (reserve area)
	+00-03	FF * 4
	+04-07	00
	+08,09	FF * 2
	+0A-7E	00
	+7F	00	XOR CODE

data block detail
  * title & icon palete
	+00,01	data ID String 'SC'
	+02	7-4	icon display flag
		3-0	icon count
	+03	icon block count
	+04-5F	title(ASCIIZ string)
	+60-	icon palete data 16 / 65536 color

  * icon frame
	+00-7F	icon data(4bit=1pixel * 16 * 16 )


directory example

 * example 1
directory.0  +00  51 00 00 00 00 A0 00 00   Q.......
top	     +08  01 00 42 49 53 4C 50 53   ..BISLPS
	     +10  2D 30 30 31 37 35 54 50   -00175TP
	     +18  41 52 4B 2E 47 30 00 00   ARK.G0..

directory.1  +00  52 00 00 00 00 00 00 00   R.......
	     +08  02 00 00 00 00 00 00 00   ........
	     +10  00 00 00 00 00 00 00 00   ........
	     +18  00 00 00 00 00 00 00 00   ........

directory.2  +00  52 00 00 00 00 00 00 00   R.......
	     +08  03 00 00 00 00 00 00 00   ........
	     +10  00 00 00 00 00 00 00 00   ........
	     +18  00 00 00 00 00 00 00 00   ........

directory.3  +00  52 00 00 00 00 00 00 00   R.......
	     +08  04 00 00 00 00 00 00 00   ........
	     +10  00 00 00 00 00 00 00 00   ........
	     +18  00 00 00 00 00 00 00 00   ........

directory.4  +00  53 00 00 00 00 00 00 00   S.......
	     +08  FF FF 00 00 00 00 00 00   ........
	     +10  00 00 00 00 00 00 00 00   ........
	     +18  00 00 00 00 00 00 00 00   ........

 * example 2(wizadry)

dir.0      +00	51 00 00 00 00 20 00 00   Q.... ..	character data
	   +08	FF FF 42 49 53 43 50 53   ..BISCPS
	   +10	2D 31 30 30 31 30 50 43   -10010PC
	   +18	46 49 4C 45 2E 00 00 00   FILE....

dir.1      +00	51 00 00 00 00 60 00 00   Q.......	save data 1
	   +08  02 00 42 49 53 43 50 53   ..BISCPS
	   +10	2D 31 30 30 31 30 43 44   -10010CD
	   +18	53 31 00 00 00 00 00 00   S1......

dir.2      +00	52 00 00 00 00 00 00 00   R.......
	   +08	03 00 00 00 00 00 00 00   ........
	   +10	00 00 00 00 00 00 00 00   ........
	   +18	00 00 00 00 00 00 00 00   ........

dir.3      +00	53 00 00 00 00 00 00 00   S.......
	   +08	FF FF 00 00 00 00 00 00   ........
	   +10	00 00 00 00 00 00 00 00   ........
	   +18	00 00 00 00 00 00 00 00   ........

 * icon palete

  bit  15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
       ??  B4  B3  B2  B1  B0  G4  G3  G2  G1  G0  R4  R3  R2  R1  R0
       ----------------------  ------------------  ------------------
                Blue data           Green data           Red data


=========
Technical
=========

PSX controllers communicate serially in byte oriented packets of
varying lengths.  The adapter cable uses the Amiga parallel port
to communicate with the controller as the joyport on the Amiga
is too slow due to capacitive settling time.  The parallel port
also allows the use of an interrupt for the controller acknowledge
signal.

This fits well in the Amiga device architecture.  The device
task waits for an IO; once received, it sends and receives one
byte of data; if it is not the last byte, it waits for a signal
from the acknowledge interrupt before sending and receiving the
next byte in the packet.  The actual time the task is awake is
kept low, keeping the impact on system perfomance low.  Once a
packet is accumulated, the IO is replied to the sender.


=======
Reports
=======

Please send bug reports and suggestions to jlfenton@ctaz.com


