Start to write the PS2 controller code in ASM

Since I mentioned it in a different thread, I thought I would try to write some of the PS2 code in Atom Pro assembly (H8/3694) language, to see if by using the modified SPI interface that PS2 appears to use where bytes are output and input at the same time and making use of the ACK line, I could make the PS2 code work on more of the different controllers.

So far I am getting some input and output, but I don’t think my timings are correct. If I understand correctly bits are output at a rate of 250,000 bits per second which if my calculation is correct that implies I have 64 clocks per bit (32 high and 32 low).

My first attempt of using some of Nathan’s (AcidTechs) macros to make the code reasonably independant of which IO pins are used made the code reasonably easy to read, but it is eating up way to many cycles inside the main bit loop… Looks like I will need to pre-calculate these and save them away in memory instead of generating them on the fly…

Kurt

P.S. - the main bit loop in this round looked like:

[code]

_PS2_C1:
xor.b r6h, r6h ; setup for the next byte
mov.w #8, e3 ; We need to do 8 bits.

push.l		er4			; save away er4 as the _usdelay macro uses er4

_PS2_BIT_LOOP:

; map the CMD pin to the right port/pin and then store the next bit into it.
mov	#PS2CMD, r0l		; (2)setup to use the CMD bit
_GETPINMAC				; (18)
shlr.b	r6l				; (2)ok shift our CMD bit into the carry
_SETSTATEMAC			; (24) use the carry to set the pin state

mov.b	#PS2CLK, r0l    ; (2)
_LOWFASTFUNCMAC			; (26) set the clock state low

;_usdelay   5,0
nop						; 2
nop						
nop
nop
nop
nop
nop
nop						; 8*2

_HIGHFASTFUNCMAC		; (26) set clock  back high again

mov	#PS2DAT, r0l		; (2)
_GETPINMAC				; (18)
_GETSTATEMAC			; (12) Ok lets get the DAT value into the carry bit
rotxr.b	r6h				; (2)

;_usdelay   5,0

dec.w	#1,e3			; (2) decrement our bit loop counter
bne		#_PS2_BIT_LOOP:16	; (4) and go to the next bit if we have not done all of them

…[/code]
FYI - the number in () is the number of clocks… If you start to add up different areas you see I need to cut a lot of clocks…

Pre-calculating the pin port and bits is what I do with the hservo system to keep the interrupt cycles to a minimum.

Also, you could try using the syncronous serial mode of the SCI3 hardware to do the SPI communications in hardware. Of course you’d be limited to the designated pins in that case.

Hi Nathan,

A quick question, I was looking over code to count instructions and I looked out your macro:

;******************************** ; SetStateMac ;******************************** ;R0L = Pin number ;******************************** .macro _SETSTATEMAC ;(24) bcc .+6:8 ;4 bset r3l,@er2 ;8 bcs .+6:8 ;4 bclr r3l,@er2 ;8 .endm
When I first called this I took your 24 as the cycles needed… However looking over the code should it not be 16? You would either do the BSET or the BCLR but not both…

Kurt

You are correct. Since only one of the 8 cycle ops will run the max cycle time is 4+8+4. My mistake. I double checked the library though and found I don’t use that macro in any timing sensitive functions so no harm done.

Thanks Nathan,

I am not sure how many other people are interested in this and/or if I should instead move this to the PS2 forum, but I updated the inner loop to have the clock on 48 and off 48 cycles and am getting better results:

When I first start up, when I start off with the query command: 1, 42:
I am getting the resulting bytes back that I expect. FF,41,5A,FF,FF.

So I am now working on the ACK code. I have the ack on P8 which has a pull-up. I am getting ACKs back for each byte as I expect. There appear to be several rules associated with them. My first version of the ack code would simply timeout if the ack did not happen, turn on LED on P5 and continue to try the next byte.

Now I am trying to use the timeout to see if I can try the new command and see if it times out as an indication it is not ready. So I changed the code that when the timeout happens to simply exit the send/receive, set the SEL line back high and then see which byte I am erroring on. The first one at first surprised me. It was on the intial query on about byte 5. It looks like I am going to need to add a bit more smarts to this loop. I probably need to check the mode byte that the PS2 returned and process it. In this case: 0x41 says High nibble of 4 -> Digital, low nibble of 1 implies 1 word of data after the 3 byte header will be returned and it does not ack the last byte…

Hopefully after this one is solved, the next time out will be on the 3 byte header for a config command, to tell me to wait and try again.

Kurt

Good luck. I can’t help much with the ACK signalling because I nvere looked into it. Also none of the documents I had translated talked about it in any deatil either. They all just amounted to saying the ACK pin was used to acknowledge each transmition without any detail as to how.

Also, once you have something working I would be happy to add it as a command in the library. Same goes for any other interesting functions you work out.

Hello Kurte,

Here is another reverse engineering take on the PS2 interface I found. It has a more informative explanation of the ack pin. Maybe it could be useful. :wink:

curiousinventor.com/guides/ps2

Way cool! A PS2 command for the Atom Pro would be awesome! Thanks for the generous offer!

Yep, this is the one I have been mostly using along with the one it references from Dowty’sdocs.google.com/View?docid=ddbmmwds_5cw4pk3

I am also getting a little from: nearfuturelaboratory.com/2008/06/19/playstation2-logic-analysis/

But this keeps reminding me how much I could use a logic analyzer…

Kurt

Of course that sort of impies I would need to get this to work :laughing:

Well I keep saying this, and while trying to figure out the PS2, I found the site near future laboratory.com which was using a different logic analyzer to show some of the PS2 protocol, but browing around on this site I found the page: nearfuturelaboratory.com/2008/10/24/a-new-logic-analyzer-and-an-i2c-compass/, where he was using a real inexpensive PC based 8 input logic analyzer (~ $150), from Seleae Logic which he said he used for a few months and recommends it).

I checked it out on their website and ordered one: saleae.com/logic/. It is also sold by SparkFun (who was out of stock this morning).

I felt it was worth giving it a shot for that price…

Kurt

Nice acquisition! 8)

Hi Kurt,

Great found! I know the feeling of not knowing what’s really going on. I’ve also looked at different sides for logic analyzers but they are so … expensive! If this one is accurate enough it looks like a good “hobbyâ€

Another good analyzer is:

tech-tools.com/dv_specs.htm

It’s more expensive($499 msrp) but has 18channels and samples at 100mhz.

Hi Nathan, I looked at that one as well and was tempted, because it had the additional channels and probably more advanced. I also considered:

securedwithssl.com/HobbyLab-us/product/15ce6b41-1ad9-41d8-85f2-26c8587ed712.aspx, but I have not seen any reviews of it to know how well it works.

Also looked at:pctestinstruments.com/ that has 34 channels for $389 and appeared to be liked by the guy at Nearfuture…

But in the end I chose the cheap KISS product. The good news is it should be delivered to my mailbox location today, so hopefully I will time to pick it up today (9 miles away) and play with it.

In the mean time I have been playing with the code and have it partially limping along. I am using the LEDS to help me know if I am getting timeouts. I also added a short delay between each packets going out to the controller and last night I was able to get it to configure it properly into the right mode without receiving any timeouts. For the fun of it I then put in a logitech controller in to see if it would work, it didn’t. I did receive timeout of the ACK on the third configuration packet in the packet header. Now I need to add the code that when it errors like this to retry so many times… But before I do that I need to clean up the code some as I am using/trashing too many of the registers which makes it hard to change and maintain the code. I should probably figure out a proper parameter passing scheme and use temporary stack variables internal to some of my internal functions. But in case anyone would like to see the current code that I am a not proud of:

[code];Project Lynxmotion PS2 Test program
;Hardware setup: ABB2 with ATOM 28 Pro, SSC32 V2, PS2 remote (See further for connections)
;
;NEW IN V1.3
; - Changed controls L1+ right stick
; - Balance calculations Thanks to KÃ¥re Halvorsen (aka Zenta)
;
;
;====================================================================
;[CONSTANDS]
TRUE con 1
FALSE con 0

BUTTON_DOWN con 0
BUTTON_UP con 1
;--------------------------------------------------------------------
;[SERIAL CONNECTIONS]
SSC_LM_SETUP con 1 ;Changes the SSC pins corresponding to the setup
;1 = Setup with connector to the front
;0 = Setup with connector to the back

SSC_OUT con P11 ;Output pin for (SSC32 RX) on BotBoard (Yellow)
SSC_IN con P10 ;Input pin for (SSC32 TX) on BotBoard (Blue)
SSC_BAUTE con i38400 ;SSC32 Baute rate
;--------------------------------------------------------------------
;[PS2 Controller]
PS2DAT con P12 ;PS2 Controller DAT (Brown)
PS2CMD con P13 ;PS2 controller CMD (Orange)
PS2SEL con P14 ;PS2 Controller SEL (Blue)
PS2CLK con P15 ;PS2 Controller CLK (White)
PadMode con $79

PS2ACK con P8 ;PS2 Controller try to work with ACK (Violet)
;--------------------------------------------------------------------
;--------------------------------------------------------------------
;--------------------------------------------------------------------
;[INPUTS]1
butA var bit
butB var bit
butC var bit

prev_butA var bit
prev_butB var bit
prev_butC var bit
;--------------------------------------------------------------------
;[OUTPUTS]
LedA var bit ;Red
LedB var bit ;Green
LedC var bit ;Orange
;--------------------------------------------------------------------
;[VARIABLES]
;--------------------------------------------------------------------
;[Ps2 Controller]
DualShock var Byte(70) ; Made much bigger to debug with return of init stuff…
PS2InitSent var byte

Ps2Save var Byte(9)
i var byte
j var byte
fChanged var byte

LastButton var Byte(2)
PS2Index var byte
BodyYShift var sbyte

_PS2InCnt var byte ; internal to PS2 function
_PS2OutCnt var byte ; internal to PS2 function

_PS2PDR var WORD ;PS2 Contoller (DAT/CMD/SEL/CLK) pins should all be on same PDR
_PS2DATPIN var byte ;PS2 Controller DAT (Brown)
_PS2CMDPIN var byte ;PS2 controller CMD (Orange)
_PS2SELPIN var byte ;PS2 Controller SEL (Blue)
_PS2CLKPIN var byte ;PS2 Controller CLK (White)

_PS2ACKPDR var word ;PS2 controller ACK pin may be on different PDR
_PS2ACKPIN var byte ;PS2 controller ACK (violet)

;--------------------------------------------------------------------
;[GLOABAL]
;====================================================================
;[INIT]
;Turning off all the leds
sound 9,[50\3960]

LedA = 0
LedB = 0
LedC = 0

;PS2 controller
gosub PS2Init

high p4
high p5
high p6

;SSC
;====================================================================
;[MAIN]
main:

;Read input
GOSUB Ps2Input

fChanged = 0
for i = 0 to 6
	if DualShock(i) <> ps2Save(i) then 
		fChanged = 1
		Ps2Save(i) = DualShock(i)
	endif
next

; if fChanged <> 0 then
serout S_OUT, i9600, [dec _PS2INCnt,":",hex DualShock(0)\2, hex DualShock(1)\2, hex DualShock(2)\2, hex DualShock(3)\2, |
hex DualShock(4)\2, hex DualShock(5)\2, hex DualSHock(6)\2, 13]
if ps2InitSent <> 0 then
serout S_OUT, i9600, “(”,dec ps2InitSent, “:”, dec _PS2Incnt,")", 13]
j = 7
for i = 0 to 4
serout S_OUT, i9600, [hex DualShock(j)\2, hex DualShock(j+1)\2, |
hex DualShock(j+2)\2, hex DualShock(j+3)\2, hex DualShock(j+4)\2, |
hex DualShock(j+5)\2,hex DualShock(j+6)\2, 13]
j = j + 7
next
serout S_OUT, i9600, [13]
endif

	high p4
	high p5
	high p6

; endif
pause 500

goto main
;====================================================================

;--------------------------------------------------------------------------------------------------
PS2Init:
; First let the basic system know what we are using each of the pins for
input PS2DAT ; P12 (P84)
output PS2CMD ; P13 P85
output PS2SEL ; P14 P86
output PS2CLK ; P15 P87
input PS2ACK ; p8 P80 also P15

high PS2CLK

jsr	_PS2INIT			; call the main ps2 init function
return

;---------------------------------------------------------------------------------------------------
; PS2Input - is a simple basic wrapper to assembly language for the assembly code
;
PS2Input:
Ps2InitSent = 0; ; did we have to send the init?
jsr _PS2INPUT:8
return

asm{
;======================================================================================================
;Macros from AcidTech
;======================================================================================================
.macro _ints_enable
bset #0,@SYSF:8
andc #0x7F,ccr
.endm

.macro _ints_disable
bclr #0,@SYSF:8
orc #0x80,ccr
.endm

.macro _ints_save_enable
andc #0x7F,ccr ;2
.endm

.macro _ints_save_disable
orc #0x80,ccr ;2
.endm

.macro _ints_restore
orc #0x80,ccr ;2 Disable ints
btst #0,@SYSF:8 ;6
beq .+4:8 ;4 If clear then dont enable ints
andc #0x7F,ccr ;2(14) Enable ints
.endm

.macro _ratedelay rate,offset
.ifdef _DEF20MHZ
delay = (20000000/\rate)
temp1 = 20000000/delay
temp2 = 20000000/(delay+1)
.else
delay = (16000000/\rate)
temp1 = 16000000/delay
temp2 = 16000000/(delay+1)
.endif
temp3 = \rate - temp1
temp4 = \rate - temp2
.if temp3 < 0
temp3 = temp3 * -1
.endif
.if temp4 < 0
temp4 = temp4 * -1
.endif
.if temp4 < temp3
delay = delay + 1
.endif
delay = delay - \offset
.if delay >= 4
delay = delay - 6
count = delay / 8
mov.l #count,er4 ;6
.if count > 0
nop ;2
dec.l #1,er4 ;2
bne .-4:8 ;4(8 clks)
.endif
.endif
rem = delay % 8
.if rem == 1
nop ;2
.endif
.if rem == 2
nop ;2
.endif
.if rem == 3
mov.b #TCRV0,r4l ;3
.endif
.if rem == 4
nop ;2
nop ;2(4)
.endif
.if rem == 5
mov.b #TCRV0,r4l ;3
nop ;2(5)
.endif
.if rem == 6
nop ;2
nop ;2
nop ;2(6)
.endif
.if rem == 7
mov.b #TCRV0,r4l ;3
nop ;2
nop ;2(7)
.endif
.endm

.macro _cycledelay delay
tdelay = \delay
.if tdelay >= 6
tdelay = tdelay - 6
count = tdelay / 8
mov.l #count,er4 ;6
.if count > 0
nop ;2
dec.l #1,er4 ;2
bne .-4:8 ;4(8 clks)
.endif
.endif
rem = tdelay % 8
.if rem == 1
nop ;2
.endif
.if rem == 2
nop ;2
.endif
.if rem == 3
mov.b #TCRV0,r4l ;3
.endif
.if rem == 4
nop ;2
nop ;2(4)
.endif
.if rem == 5
mov.b #TCRV0,r4l ;3
nop ;2(5)
.endif
.if rem == 6
nop ;2
nop ;2
nop ;2(6)
.endif
.if rem == 7
mov.b #TCRV0,r4l ;3
nop ;2
nop ;2(7)
.endif
.endm

.macro _usdelay delay,offset
.ifdef _DEF20MHZ
tdelay = (\delay * 20)-\offset
.else
tdelay = (\delay * 16)-\offset
.endif
.if tdelay >= 6
tdelay = tdelay - 6
count = tdelay / 8
mov.l #count,er4 ;6
.if count > 0
nop ;2
dec.l #1,er4 ;2
bne .-4:8 ;4(8 clks)
.endif
.endif
rem = tdelay % 8
.if rem == 1
nop ;2
.endif
.if rem == 2
nop ;2
.endif
.if rem == 3
mov.b #TCRV0,r4l ;3
.endif
.if rem == 4
nop ;2
nop ;2(4)
.endif
.if rem == 5
mov.b #TCRV0,r4l ;3
nop ;2(5)
.endif
.if rem == 6
nop ;2
nop ;2
nop ;2(6)
.endif
.if rem == 7
mov.b #TCRV0,r4l ;3
nop ;2
nop ;2(7)
.endif
.endm

.macro _msdelay delay,offset
.ifdef _DEF20MHZ
tdelay = (\delay * 20000)-\offset
.else
tdelay = (\delay * 16000)-\offset
.endif
.if tdelay >= 6
tdelay = tdelay - 6
count = tdelay / 8
mov.l #count,er4 ;6
.if tdelay >= 8
nop ;2
dec.l #1,er4 ;2
bne .-4:8 ;4(8 clks)
.endif
.endif
rem = tdelay % 8
.if rem == 1
nop ;2
.endif
.if rem == 2
nop ;2
.endif
.if rem == 3
mov.b #TCRV0,r4l ;3
.endif
.if rem == 4
nop ;2
nop ;2(4)
.endif
.if rem == 5
mov.b #TCRV0,r4l ;3
nop ;2(5)
.endif
.if rem == 6
nop ;2
nop ;2
nop ;2(6)
.endif
.if rem == 7
mov.b #TCRV0,r4l ;3
nop ;2
nop ;2(7)
.endif
.endm

;********************************
; Getpin
;********************************
;R0L = Pin number
;********************************
;ER1 = WORK
;R2 = PORT
;R3L = BIT
;********************************
.macro _GETPINMAC
;.equ _DEFGETPIN,0
;One table GETPIN macro ;(18)
and.w #0x3F,r0 ;4
shll.l er0 ;2
mov.w @(_PORTTABLE:16,er0),r2 ;6
shlr.l er0 ;2 Restore pin number
mov.b r2h,r3l ;2
mov.b #0xFF,r2h ;2
.endm

;********************************
; GetDirMac
;********************************
;R0L = Pin number
;********************************
.macro _GETDIRMAC ;(14)
xor.b r1h,r1h ;2 Clear Mask
bset r3l,r1h ;2 Set Mask
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
and.b r1h,r1l ;2 Isolate BIT
add.b #0xFF,r1l ;2 Move to CARRY
.endm

;********************************
; SetDirMac
;********************************
;R0L = Pin number
;********************************
.macro _SETDIRMAC ;(30)
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
bcs .+4:8 ;4
bclr r3l,r1l ;2 Clear PCR bit
bcc .+4:8 ;4
bset r3l,r1l ;2 Set PCR bit
mov.b r1l,@(0x70-0xD0,er2) ;6 Save PCRS
mov.b r1l,@(0x10,er2) ;6 Set PCR
.endm

;********************************
; GetStateMac
;********************************
;R0L = Pin number
;********************************
.macro _GETSTATEMAC ;(12)
xor.b r1h,r1h ;2 Clear Mask
bset r3l,r1h ;2 Set Mask
mov.b @er2,r1l ;4 Get PORT
and.b r1h,r1l ;2 Isolate BIT
add.b #0xFF,r1l ;2 Move to CARRY
.endm

;********************************
; SetStateMac
;********************************
;R0L = Pin number
;********************************
.macro _SETSTATEMAC ;(24 ?? 16)
bcc .+6:8 ;4
bset r3l,@er2 ;8
bcs .+6:8 ;4
bclr r3l,@er2 ;8
.endm

;********************************
; HighFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _HIGHFUNCMAC ;(46)
_GETPINMAC ;18
bset r3l,@er2 ;8 Set PDR bit
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
bset r3l,r1l ;2 Set PCR bit
mov.b r1l,@(0x70-0xD0,er2) ;6 Save PCRS
mov.b r1l,@(0x10,er2) ;6 Set PCR
.endm

;********************************
; HighFastFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _HIGHFASTFUNCMAC ;(26)
_GETPINMAC ;18
bset r3l,@er2 ;8
.endm

;********************************
; LowFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _LOWFUNCMAC ;(46)
_GETPINMAC ;18
bclr r3l,@er2 ;8 Set PDR bit
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
bset r3l,r1l ;2 Set PCR bit
mov.b r1l,@(0x70-0xD0,er2) ;6 Save PCRS
mov.b r1l,@(0x10,er2) ;6 Set PCR
.endm

;********************************
; LowFastFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _LOWFASTFUNCMAC ;(26)
_GETPINMAC ;18
bclr r3l,@er2 ;8
.endm

;********************************
; ToggleFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _TOGGLEFUNCMAC ;(46)
_GETPINMAC ;18
bnot r3l,@er2 ;8
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
bset r3l,r1l ;2 Set PCR bit
mov.b r1l,@(0x70-0xD0,er2) ;6 Save PCRS
mov.b r1l,@(0x10,er2) ;6 Set PCR
.endm

;********************************
; ToggleFastFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _TOGGLEFASTFUNCMAC ;(26)
_GETPINMAC ;18
bnot r3l,@er2 ;8
.endm

;********************************
; InputFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _INPUTFUNCMAC ;(38)
_GETPINMAC ;18
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
bclr r3l,r1l ;2 Clear PCR bit
mov.b r1l,@(0x70-0xD0,er2) ;6 Save PCRS
mov.b r1l,@(0x10,er2) ;6 Set PCR
.endm

;********************************
; OutputFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _OUTPUTFUNCMAC ;(38)
_GETPINMAC ;18
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
bset r3l,r1l ;2 Set PCR bit
mov.b r1l,@(0x70-0xD0,er2) ;6 Save PCRS
mov.b r1l,@(0x10,er2) ;6 Set PCR
.endm

;********************************
; ReverseFuncMac
;********************************
;R0L = Pin number
;********************************
.macro _REVERSEFUNCMAC ;(38)
_GETPINMAC ;18
mov.b @(0x70-0xD0,er2),r1l ;6 Get PCRS value
bnot r3l,r1l ;2 Toggle PCR bit
mov.b r1l,@(0x70-0xD0,er2) ;6 Save PCRS
mov.b r1l,@(0x10,er2) ;6 Set PCR
.endm

;********************************
; InputStaicMac
;********************************
;R0L = Pin number
;********************************
.macro _INPUTSTATICMAC pin ;(20)
mov.b @0x70-0xD0+(\pin&0xFF):8,r1l ;6 Get PCRS value
bclr #(\pin>>8),r1l ;2 Clear PCR bit
mov.b r1l,@0x70-0xD0+(\pin&0xFF):8 ;6 Save PCRS
mov.b r1l,@0x10+(\pin&0xFF):8 ;6 Set PCR
.endm

;********************************
; OutputStaticMac
;********************************
;R0L = Pin number
;********************************
.macro _OUTPUTSTATICMAC pin ;(20)
mov.b @0x70-0xD0+(\pin&0xFF):8,r1l ;6 Get PCRS value
bset #(\pin>>8),r1l ;2 Set PCR bit
mov.b r1l,@0x70-0xD0+(\pin&0xFF):8 ;6 Save PCRS
mov.b r1l,@0x10+(\pin&0xFF):8 ;6 Set PCR
.endm

;********************************
; ReverseStaticMac
;********************************
;R0L = Pin number
;********************************
.macro _REVERSESTATICMAC pin ;(20)
mov.b @0x70-0xD0+(\pin&0xFF):8,r1l ;6 Get PCRS value
bnot #(\pin>>8),r1l ;2 Reverse PCR bit
mov.b r1l,@0x70-0xD0+(\pin&0xFF):8 ;6 Save PCRS
mov.b r1l,@0x10+(\pin&0xFF):8 ;6 Set PCR
.endm
;=======================================================================================
; End of macros
;=======================================================================================
;=======================================================================================
; PS2 Init Assembly Function
; We will initialize the port pins here once as well as we will map the BAP pins to
; the H8 port and pin number to use in the main PS2 input function.
;=======================================================================================
_PS2INIT:
; Ok first let map all of our pins to the state we want…
mov.l #PCRS8, er2 ; try to init the ER2 register to use the shadow register locations
mov.b #PS2DAT, r0l
_INPUTFUNCMAC
mov.w r2, @_PS2PDR ; save away the PDR for later
mov.b r3l,@_PS2DATPIN

mov.b	#PS2CMD, r0l
_OUTPUTFUNCMAC
mov.b	r3l,@_PS2CMDPIN		

mov.b	#PS2CLK, r0l
_OUTPUTFUNCMAC
mov.b	r3l,@_PS2CLKPIN		

mov.b	#PS2SEL, r0l
_OUTPUTFUNCMAC
mov.b	r3l,@_PS2SELPIN		

mov.b	#PS2ACK, r0l
_INPUTFUNCMAC
mov.w	r2,@_PS2ACKPDR		; ack may easily be on different Data register...
mov.b	r3l,@_PS2ACKPIN		

; BUGBUG - Hack to know that our ACK pin as also on P15 which has pull-up...	

bclr.b	#5,@PMR1:8;
bclr.b	#5,@PCR1:8		; 	note BAP P8 has two 3694 pins connected P80 and P15.  P15 has pull-up
bset.b	#5,@PUCR1:8		; 	So try to use it pull up the ACK line without having to add external pull-up

rts

;=======================================================================================
; Main PS2 Input assembly function
;=======================================================================================
_PS2INPUT:

; now lets try a simple query
mov.b	#6, r1l			; length of return buffer.
mov.l	#_PS2QCMD,er4	; point to the standard output command table
mov.l	#DUALSHOCK, er5 ; point to where we want the output to go

; Now lets do the input and output of the command
jsr		PS2_INPUT_OUTPUT_CMD
mov.b	#0, r1h
mov.b	@_PS2INCNT, r1l
bne		_PS2_TIMEOUT:16

mov.b	@DUALSHOCK,r1l
cmp.b	#0x79,r1l	; see if we are in the correct mode.
beq		_PS2_END:16		; yes so go to the end

; Let our caller know that we had to output some init strings...
mov.b	#1, r1l
mov.b	r1l,@PS2INITSENT

; not in the right mode, so now lets output our init strings.
; hopefully the ACK processing will pace it correctly
_usdelay   100, 0
mov.l	#_PS2I1, er4
mov.l	#DUALSHOCK+7, er5 ; point to where we want the output to go
mov.b	#6, r1l			; length of return buffer.
jsr		PS2_INPUT_OUTPUT_CMD
mov.b	#1, r1h
mov.b	@_PS2INCNT, r1l
bne		_PS2_TIMEOUT:16

_usdelay   100, 0
mov.l	#_PS2I2, er4
mov.l	#DUALSHOCK+14, er5 ; point to where we want the output to go
mov.b	#6, r1l			; length of return buffer.
jsr		PS2_INPUT_OUTPUT_CMD
mov.b	#2, r1h
mov.b	@_PS2INCNT, r1l
bne		_PS2_TIMEOUT:16

_usdelay   100, 0
mov.l	#_PS2I3, er4
mov.l	#DUALSHOCK+21, er5 ; point to where we want the output to go
mov.b	#6, r1l			; length of return buffer.
jsr		PS2_INPUT_OUTPUT_CMD
mov.b	#3, r1h
mov.b	@_PS2INCNT, r1l
bne		_PS2_TIMEOUT:8

_usdelay   100, 0
mov.l	#_PS2I4, er4
mov.l	#DUALSHOCK+28, er5 ; point to where we want the output to go
mov.b	#6, r1l			; length of return buffer.
jsr		PS2_INPUT_OUTPUT_CMD
mov.b	#4, r1h
mov.b	@_PS2INCNT, r1l
bne		_PS2_TIMEOUT:8

_usdelay   100, 0
mov.l	#_PS2I5, er4
mov.l	#DUALSHOCK+35, er5 ; point to where we want the output to go
mov.b	#6, r1l			; length of return buffer.
jsr		PS2_INPUT_OUTPUT_CMD

mov.b	#5, r1h
mov.b	@_PS2INCNT, r1l
bne		_PS2_TIMEOUT:8

_PS2_END:
rts

_PS2_TIMEOUT:
mov.b r1h,@PS2INITSENT
rts

;=============================================================================================
; PS2_INPUT_OUTPUT_CMD: This function will do the standard input/output processing for a single
; command string. It will try to properly use the ACK line of the PS2 to
; to the proper pacing of the command. It will also look at the PS2 mode
; valuethat is returned as the second byte to update how many bytes that
; is returned.
; Input/Output Registers: (note all of these may be trashed…)
; r1l - Input - The number of bytes including Mode we wish to have returned -
; ER4 - (Input) The Command string with input/output size as first bytes
; ER5 - (Input) The location of where to save away the returned bytes.
; may be zero if we don’t wish to save…
; First byte returned will be mode, followed by the number of
; bytes the user asked for or less if the mode does not have that
; many bytes.
; Registers changed:
; r1 - scratch
; er2 - used to point to PDR register
; r3h - Used to hold the clock pin
; r3l - used to hold which PIN on the IO block some IO function (CMD/DAT…)
; e3 - bit counter.
; R6L - Byte to shift out using TXRX Byte
; R6H - The byte that TXRX returned

PS2_INPUT_OUTPUT_CMD:
mov.b r1l, @_PS2INCNT ; save away the requested number of bytes.
mov.b @er4+, r1l ; OK lets get the length of the cmd from the second byte
mov.b r1l, @_PS2OUTCNT ; how many bytes we want to output

; Now lets set the select to low
mov.b	#PS2SEL, r0l	; set the pin we want to use
_LOWFASTFUNCMAC

; OK we wish to input and output the first 3 bytes which are considered
; the packet header.
mov.b	@er4+, r6l		; get first byte of header
mov.b	#1, r1l			; Yes wait for ACK 
jsr		PS2_TXRX_BYTE:16	; Ok lets process the input and output of the byte...
							; Don't care about return value should be 0xff
bcs		_PS2_NO_ACK_ON_HEADER:16	; we did not get an ack on the first byte...	

mov.b	@er4+, r6l		; Get Second byte of header
mov.b	#1, r1l			; Yes wait for ACK 
jsr		PS2_TXRX_BYTE:16	; Ok lets process the input and output of the byte...
							; 
bcs		_PS2_NO_ACK_ON_HEADER:8	; we did not get an ack...	

cmp.l	#0,er5			; ok see if we wish to return info
beq		_PS2_IOC_NORET_MODE:8
mov.b	r6h,@er5		; save the mode byte away
inc.l	#1, er5			; increment to point to next byte

_PS2_IOC_NORET_MODE:
and.b #0xf,r6h ; only use the low nibble of mode which is number of words of data past header
shal.b r6h ; convert to bytes
mov.b @_PS2INCNT, r6l ; get how many bytes we think we wish to process
cmp.b r6l,r6h ; compare (num bytes avail - num bytes wanted)
bcc _PS2_IOC_CNT_OK:8 ; ok looks good
mov.b r6h,@_PS2INCNT ; nope update to only return the valid number of bytes…

_PS2_IOC_CNT_OK:
mov.b @er4+, r6l ; get first byte of header
mov.b #1, r1l ; Yes wait for ACK
jsr PS2_TXRX_BYTE:16 ; Ok lets process the input and output of the byte…
; Don’t care about return value should be 0xff
bcs _PS2_NO_ACK_ON_HEADER:8 ; we did not get an ack…

; Ok we are done with the 3 byte header, lets setup to input/output the rest of the bytes...		

_PS2_SETUP_NEXT_BYTE:
mov.b @_PS2OUTCNT, r1l
bne #_PS2_YES_MORE:8
xor r6l, r6l ; no more
bra #_PS2_C1:8
_PS2_YES_MORE:
mov.b @er4+, r6l ; get the next byte from the input string.
dec.b r1l ; decrement our count
mov.b r6l,@_PS2OUTCNT

_PS2_C1:
mov.b @_PS2INCNT, r1l ; We don’t expect an ack on the last byte
beq .+2:8 ; don’t decrement if we are zero already
dec.b r1l ; sets r1l to zero on last byte to say don’t look for ack

jsr		PS2_TXRX_BYTE:16	; Ok lets process the input and output of the byte...
bcs		_PS2_NO_ACK_ON_DATA:8	; we did not get an ack on the first byte...	

cmp.l	#0,er5			; ok see if we wish to return info
beq		_PS2_IOC_NORET_DATA:8
mov.b	r6h,@er5		; save the mode byte away
inc.l	#1, er5			; increment to point to next byte

; We do not look for ack if this is our last byte...

_PS2_IOC_NORET_DATA:
mov.b @_PS2INCNT, r1l ; lets get our input counter
beq _PS2_CHECK_OUT_COUNT:8
dec.b r1l ; decrement how many bytes we are going to input and if not zero go to process next byte
mov.b r1l, @_PS2INCNT ; save away the updated counter
bne _PS2_SETUP_NEXT_BYTE:16 ; and process the next byte

_PS2_CHECK_OUT_COUNT:
mov.b @_PS2OUTCNT, r1l ; make sure our output count is zero as well
bne _PS2_SETUP_NEXT_BYTE:16 ; and process the next byte
bra _PS2_IN_DONE:8

_PS2_NO_ACK_ON_HEADER:
bset.b #6, @PCR5:8 ; BUGBUG : debug
bclr.b #6,@PDR5:8 ; BUGBUG : debug
_PS2_NO_ACK_ON_DATA:

_PS2_IN_DONE:
mov.b #PS2SEL, r0l
_HIGHFASTFUNCMAC ; set it high
rts
; and return

;=============================================================================================
; PS2_TXRX_BYTE: This function will handle the internal processing for the PS2 code to
; transmit a byte on CMD and at the same type Receive it on DAT. The bytes are
; shifted in and out using a clock signal (CLK) This code works like a SPI except
; PS2 has another IO line ACK that the controller will ACK the receipt of most
; bytes. The code keeps the clock cycle consistent for both High and low…
; Input/Output Registers:
; r1l - (Input) should we wait for ack or not, will be trashed…
; R6L - (Input) The byte to shift out
; R6H - (Output) The byte that was shifted in
; Registers changed:
; r1 - scratch
; r2 - R2 is pointing to the PDR register that DAT/CMD/CLK/SEL is on
; r3h - Used to hold the clock pin
; r3l - used to hold which PIN on the IO block some IO function (CMD/DAT…)
; e3 - bit counter.
; return
; Carry false if OK
; Carry true if Timeout
PS2_TXRX_BYTE:
push.w r1 ; save away R1 so we can test if we should wait for timeouts R1l
xor.b r6h, r6h ; setup for the next byte
mov.w #8, e3 ; We need to do 8 bits.

; we assume that R2 is pointing to the PDR - IO data register for CLK/CMD/DAT/SEL when we get here...
mov.w	@_PS2PDR, r2	; Make sure the are pointing to the right IO Data register...
mov	@_PS2CLKPIN:16, r3h	; (6)Setup the CLK pin to use use r3h as to only set once for the loop

_PS2_BIT_LOOP:
; +++++ CLOCK IS HIGH ++++++(32+14=46)
; map the CMD pin to the right port/pin and then store the next bit into it.
mov @_PS2CMDPIN:16, r3l ; (6)Setup the CMD pin to use
shlr.b r6l ; (2)ok shift our CMD bit into the carry
_SETSTATEMAC ; (16) use the carry to set the pin state

bclr      r3h,@er2     	;8 
; ----- CLOCK IS LOW ------(48)
mov.b	#3,r1h			;2

nop                  	;2 (LOOP)
dec.b	r1h	         	;2 (LOOP)
bne     .-4:8        	;4(8 clks) 

nop						;2

; we broke apart the get state macro such that we can part of it while clock is low
; and we have spare cylcles..
mov		@_PS2DATPIN:16, r3l	; (6)Setup the CLK pin to use  
xor.b   r1h,r1h          	;2 Clear Mask 
bset    r3l,r1h           	;2 Set Mask 

bset	r3h,@er2			;8 		; set the clock high again
; +++++ CLOCK IS HIGH ++++++

mov.b	@er2,r1l            ;4 Get PORT 
and.b	r1h,r1l             ;2 Isolate BIT 
add.b   #0xFF,r1l           ;2 Move to CARRY 
rotxr.b	r6h					; (2)

dec.w	#1,e3				; (2) decrement our bit loop counter
bne		#_PS2_BIT_LOOP:8	; (4) and go to the next bit if we have not done all of them

pop		r1					; restore r1 which has our flag to say if we whould wait for ack
add.b	#0xff,r1l			; if it is non zero will set carry
bcc		_PS2_TXRX_RET:8		; Nope don't wait...

; yes we need to wait for an ack.	
mov.b	#PS2ACK, r0l	; setup to look for an ACK
_GETPINMAC

mov.w	#0xffff,e3			; lets have some form of counter so we can time out of waiting for ACK to happen...

_PS2_WAIT_FOR_ACK:
_GETSTATEMAC ; ok lets the the IO pin state
bcc #_PS2_HAVE_ACK:8
sub.w #1,e3 ; dont have ack, but lets try up to 65536 use sub not dec as sets carry…
bcc #_PS2_WAIT_FOR_ACK:8 ;
bset.b #5,@PCR5:8 ; BUGBUG: debug leaves carry…
bclr.b #5,@PDR5:8 ; BUGBUG: debug leaves carry
bra _PS2_TXRX_RET:8 ; we timed out without an ack
_PS2_HAVE_ACK:
bset.b #4, @PCR5:8 ; DEBUG - Carry left OK
bclr.b #4, @PDR5:8 ; DEBUG set an LED to show we received an ACK

_PS2_TXRX_RET:
rts ; and return

;=============================================================================================
; PS2 Command strings:
; first byte is length of the command - after the 3 byte header

_PS2QCMD:
.byte 0x0, 0x1, 0x42,0x0 ; 2 bytes in the command string… (changed to 3 to make code easier…

_PS2I1:
.byte 0x2, 0x1,0x43,0x0,0x1,0x0 ;CONFIG_MODE_ENTER
_PS2I2:
.byte 0x6, 0x01,0x44,0x00,0x01,0x03,0x00,0x00,0x00,0x00 ;SET_MODE_AND_LOCK
_PS2I3:
.byte 0x6, 0x01,0x4F,0x00,0xFF,0xFF,0x03,0x00,0x00,0x00 ;SET_PS2_NATIVE_MODE
_PS2I4:
.byte 0x6, 0x01,0x43,0x00,0x00,0x5A,0x5A,0x5A,0x5A,0x5A ;CONFIG_MODE_EXIT_PS2_NATIVE
_PS2I5:
.byte 0x6, 0x01,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ;CONFIG_MODE_EXIT

}
[/code]

Whoa! Man you must have to use a wheel barrel to carry around your brain!

No… make that a fork lift :open_mouth:

Thanks guys :blush: , but I don’t deserve it. I just have had many many years of experience programming on lots of different systems.

There are actually quite a few people up here who have a lot of tallent, including (but not limited to): Robot Dude (who makes all of these great things), Zenta, Xan, Nathan, MikeD, Laureatus, Eddie, …

But thanks for the complement.

Kurt

I was able to pick it up today, but so far I have not had much chance to do much. I was able to hook it up to the PS2 controller and did get a few samples that worked, using the SPI analyzer it shows the data pretty well.
http://i416.photobucket.com/albums/pp245/Kurts_Robots/Lynxmotion-PS2.jpg

I am not sure it showed it well in this snapshot, but the 5th line is the ACK coming back from the controller.

Kurt

Looking at this certainly suggests the ACK line is used to tell the host the data it is requesting is available.

If the controller always responds to a byte with an ACK pulse then you could use ACK on an interrupt and code the entire game control interface as a state machine in an ISR running in the background that just keeps a set of variables updated for the foreground application. :open_mouth:

Oh and you might get a visit from a… representative… of the brotherhood for partially exposing the roster to the general public. :stuck_out_tongue: :laughing: