;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; PSX-Pad for KolibriOS ; Copyright (C) Jeffrey Amelynck 2008. All rights reserved. ; ; hidnplayr@kolibrios.org ; ; v0.1 ; date: 4/09/2008 ; type: private beta ; functions implemented: Read raw data from Digital controller and Analog controller with red led. ; ; v0.2: ; date: 5/09/2008 ; type: public beta ; functions implemented: Same as above plus converting keycodes from keypad do keyboard scancodes. ; : To use this function you need a kernel wich can input scancodes using function 18,23. ; ; I also did some cleanup and speedup ; ; ; v0.2.1 ; by O. Bogomaz aka Albom, albom85@yandex.ru ; using of standart kernel function 72.1 ; ; ; TODO: - Multiple controllers ; - Analog controller(s) ; ; ; More info about PSX/PS2 gamepad protocol: ; http://curiousinventor.com/guides/ps2 ; http://www.geocities.com/digitan000/Hardware/22/e22_page.html ; ; How to connect your PSX pad to the PC: ; http://www.emulatronia.com/reportajes/directpad/psxeng/print.htm ; ; ; PSX-Pad for KolibriOS is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY. ; No author or distributor accepts responsibility to anyone for the ; consequences of using it or for whether it serves any particular purpose or ; works at all, unless he says so in writing. Refer to the GNU General Public ; License (the "GPL") for full details. ; ; Everyone is granted permission to copy, modify and redistribute KolibriOS, ; but only under the conditions described in the GPL. A copy of this license ; is supposed to have been given to you along with KolibriOS so you can know ; your rights and responsibilities. It should be in a file named COPYING. ; Among other things, the copyright notice and this notice must be preserved ; on all copies. ; use32 org 0x0 db 'MENUET01' ; 8 byte id dd 0x01 ; header version dd START ; start of code dd I_END ; size of image dd 0x100000 ; memory for app dd 0x100000 ; esp dd 0x0 , 0x0 ; I_Param , I_Icon ; Bits on Data Port (outputs for pc) command equ 0 attention equ 1 clock equ 2 vcc equ (1 shl 3 + 1 shl 4 + 1 shl 5 + 1 shl 6 + 1 shl 7) ; Bits on Status Port (inputs for PC) data equ 6 ack equ 5 __DEBUG__ equ 1 __DEBUG_LEVEL__ equ 2 include '../../macros.inc' ;include 'fdo.inc' START: mov eax, 40 ; Disable notification of all events xor ebx, ebx int 0x40 ; DEBUGF 2,"\nPSX-Pad for KolibriOS v0.2\n\n" mov eax, 46 ; Ask the kernel if wse may use the LPT port mov ebx, 0 movzx ecx, [BASE] movzx edx, [CONTROL] int 0x40 test eax, eax jz @f ; DEBUGF 2,"Could not reserve port!\n" jmp exit @@: mov dx, [CONTROL] ; disable bi-directional data port in al, dx and al, 0xdf out dx, al mov eax, 18 ; read CPU-speed, we'll need it for 100us delay mov ebx, 5 int 0x40 ; now we've got the cpuspeed in hz, we need it in Mhz xor edx, edx mov ecx, 1000000 div ecx mov [CPUSPEED], eax ; DEBUGF 2,"CPUspeed: %u\n",eax ; DEBUGF 1,"Raising attention line\n" call raise_att ; DEBUGF 1,"Raising Clock\n" call raise_clk ; DEBUGF 1,"Powering Up controller\n" call raise_vcc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; All things are ready to go, enter mainloop! ; ; This loop constantly poll's the PSX-pad for data ; mainloop: mov eax, 5 ; Lets start by giving the other applications some cpu time, we'll take ours later. mov ebx, 5 int 0x40 ; DEBUGF 1,"Lowering attention line\n" call lower_att ; We've got the attention from the PSX-Pad now :) (yes, it's active low..) ; DEBUGF 1,"Sending Startup byte.. " mov ah, 0x01 ; Startup code call tx_rx call wait_for_ack ; DEBUGF 1,"Rx: %x\n",bl ; DEBUGF 1,"Request for data.. " mov ah, 0x42 ; Request for data call tx_rx call wait_for_ack ; DEBUGF 1,"Rx: %x\n",bl cmp bl, 0x41 je digital_controller cmp bl, 0x73 je analog_red_controller ; cmp ah, 0x23 ; je negcon_controller ; cmp ah, 0x53 ; je analog_green_controller ; cmp ah, 0x12 ; je psx_mouse ; DEBUGF 2,"Unsupported controller/mode:%x !\n",bl jmp exit digital_controller: call command_idle call wait_for_ack ; Right now, we receive 0x5a from the controller, wich means: sending data! ; DEBUGF 1,"Receiving data.. " call command_idle call wait_for_ack mov byte [digital+1], bl call command_idle mov byte [digital+0], bl ; DEBUGF 1,"Digital data: %x\n",[digital]:4 mov ax, word [digital_] xor ax, word [digital] mov cx, word [digital] bt ax, 6 ; X jnc @f pusha and cx, 1 shl 6 shl cx, 1 add cl, 29 call sendkey popa @@: bt ax, 5 ; O jnc @f pusha mov cx, word [digital] and cx, 1 shl 5 shl cx, 2 add cl, 56 call sendkey popa @@: bt ax, 11 ; Start jnc @f pusha mov cx, word [digital] and cx, 1 shl 11 shr cx, 4 add cl, 28 call sendkey popa @@: bt ax, 8 ; Select jnc @f pusha mov cx, word [digital] and cx, 1 shl 8 shr cx, 1 add cl, 14 call sendkey popa @@: bt ax, 12 ; up jnc @f pusha mov cl, 224 call sendkey mov cx, word [digital] and cx, 1 shl 12 shr cx, 5 add cl, 72 call sendkey popa @@: bt ax, 13 ; right jnc @f pusha mov cl, 224 call sendkey mov cx, word [digital] and cx, 1 shl 13 shr cx, 6 add cl, 77 call sendkey popa @@: bt ax, 14 ; down jnc @f pusha mov cl, 224 call sendkey mov cx, word [digital] and cx, 1 shl 14 shr cx, 7 add cl, 80 call sendkey popa @@: bt ax, 15 ; left jnc @f pusha mov cl, 224 ; extended key call sendkey mov cx, word [digital] and cx, 1 shl 15 shr cx, 8 add cl, 75 ; left call sendkey popa @@: mov ax, word [digital] mov word [digital_],ax call raise_att jmp mainloop analog_red_controller: call command_idle call wait_for_ack ; Right now, we receive 0x5a from the controller, wich means: sending data! ; DEBUGF 1,"Receiving data.. " call command_idle call wait_for_ack mov byte [analog_red+5], bl call command_idle call wait_for_ack mov byte [analog_red+4], bl call command_idle call wait_for_ack mov byte [analog_red+3], bl call command_idle call wait_for_ack mov byte [analog_red+2], bl call command_idle call wait_for_ack mov byte [analog_red+1], bl call command_idle mov byte [analog_red+0], bl ; DEBUGF 2,"Analog data: %x%x\n",[analog_red]:8,[analog_red+4]:4 call raise_att jmp mainloop exit: mov eax, -1 int 0x40 sendkey: ; This function inserts Keyboard Scan-codes into the kernel's queue ; Scancode is in cl ; mov eax, 18 ; mov ebx, 23 ; int 0x40 pushad mov eax, 72 ; <-- standart function (by Albom) mov ebx, 1 mov edx, ecx and edx, 0xff mov ecx, 2 int 0x40 popad ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Low-level code starts here ; raise_att: mov al, [PORT_DATA] bts ax, attention mov [PORT_DATA], al mov dx, [BASE] out dx, al ret lower_att: mov al, [PORT_DATA] btr ax, attention mov [PORT_DATA], al mov dx, [BASE] out dx, al ret raise_clk: mov al, [PORT_DATA] bts ax, clock mov [PORT_DATA], al mov dx, [BASE] out dx, al ret lower_clk: mov al, [PORT_DATA] btr ax, clock mov [PORT_DATA], al mov dx, [BASE] out dx, al ret raise_vcc: mov al, [PORT_DATA] or al, vcc mov [PORT_DATA], al mov dx, [BASE] out dx, al ret lower_vcc: mov al, [PORT_DATA] and al, 0xff - vcc mov [PORT_DATA], al mov dx, [BASE] out dx, al ret wait_for_ack: mov dx, [STATUS] mov ecx, 10000 .loop: in al, dx bt ax, ack jnc .ack loop .loop ; DEBUGF 2,"ACK timeout!\n" ; pop eax ; balance the stack, we're not doing a ret like we should.. ; jmp mainloop .ack: ; DEBUGF 1,"ACK !\n" ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This code comes from Serge's audio driver. ; If you know a better way to do 100 us wait, please tell me. ; This RDTSC stuff is know to have a bug in the newer AMD processors. delay: push ecx push edx push ebx push eax mov eax, 100 mov ecx, [CPUSPEED] mul ecx mov ebx, eax ;low mov ecx, edx ;high rdtsc add ebx, eax adc ecx,edx @@: rdtsc sub eax, ebx sbb edx, ecx js @B pop eax pop ebx pop edx pop ecx ret tx_rx: ; ah = byte to send ; bl = received byte mov ecx, 8 mov bl, 0 tx_rx_loop: call delay call lower_clk mov dl, ah and dl, 1 ; DEBUGF 1,"OUTb:%u ", dl mov al, [PORT_DATA] and al, 0xfe or al, dl mov [PORT_DATA], al mov dx, [BASE] out dx, al shr ah, 1 call delay call raise_clk mov dx, [STATUS] in al, dx shl al, 1 and al, 1 shl 7 ; DEBUGF 1,"INb:%x\n", al shr bl, 1 or bl, al loop tx_rx_loop ret command_idle: ; bl = received byte mov bl, 0 mov ecx, 8 command_idle_loop: call delay call lower_clk call delay call raise_clk mov dx, [STATUS] in al, dx shl al, 1 and al, 1 shl 7 shr bl, 1 or bl, al loop command_idle_loop ret ; DATA AREA ;include_debug_strings ; ALWAYS present in data section ; Addresses to PORT BASE dw 0x378 STATUS dw 0x379 CONTROL dw 0x37a ; Buffer for data port PORT_DATA db 0 ; hmm, what would this be... CPUSPEED dd ? ; buffers for data from controller digital rb 2 digital_ rb 2 ; this buffer is used to find keychanges (if somebody just pressed/released a key) analog_red rb 6 analog_red_ rb 6 I_END: