home *** CD-ROM | disk | FTP | other *** search
/ POINT Software Programming / PPROG1.ISO / asm / 80x0393 / joystick.asm < prev    next >
Encoding:
Assembly Source File  |  1993-10-30  |  17.4 KB  |  385 lines

  1. comment ^
  2.  
  3. JOYSTICK.ASM, part of Emil Gilliam's Guide to Game Port Programming.
  4. By Emil Gilliam, 10/30/93.
  5.  
  6.    (C) Copyright 1993 Emil Gilliam.  All rights reserved.  This is freeware, 
  7. not public domain.  Read the legal information in JOYSTICK.TXT, which must be 
  8. included with this file.  Emil Gilliam will not be held liable for any damages 
  9. or losses that result, directly, or indirectly, from the use or inability to 
  10. use this source code.  The legal information in JOYSTICK.TXT also applies to 
  11. this file.  
  12.  
  13.    This is a collection of routines for: Reading the status of controller(s) 
  14. connected to the game port, determining whether or not there is a game port, 
  15. and determining whether or not controller(s) are connected to the game port.  
  16. The routines are meant to be called from C, in small model.  The routines 
  17. read_1_joystick and read_2_joysticks won't work in protected mode without 
  18. modification, since they perform the sin of using segment registers to 
  19. temporarily hold values (for speed, so I sorta have an excuse ;-) ).
  20.  
  21.    Feel free to modify these routines and use them for anything you want, but 
  22. don't distribute a modified version of this source without my permission, and 
  23. distribute this file with the accompanying files JOYSTICK.TXT and FILE_ID.DIZ, 
  24. under the conditions described in JOYSTICK.TXT.  If you use them in a program 
  25. which you are distributing around, especially a shareware or commercial 
  26. program, some credit would be nice (please let me know so I can brag that 
  27. someone actually used this code ;-) ). 
  28.  
  29.    Here's a list of the procedures in this file:
  30.  
  31. int button_status();
  32. void read_1_joystick(int *x, int *y);
  33. void read_2_joysticks(int *x1, int *y1, int *x2, int *y2);
  34. int detect_game_port();
  35.  
  36.    read_1_joystick and read_2_joysticks now have a bug fix (SI and DI were not 
  37. saved and ES was not saved correctly), thanks to Kip Cooley at the Diamond Bar 
  38. BBS (909) 923-1031 (1:218/101).  detect_game_port now has a little 
  39. optimization in it, thanks to David Kirschbaum, "Toad Hall," at the Federal 
  40. Post BBS (1:3634/2).  Thanks, guys! 
  41.  
  42. ^
  43.  
  44. .model small,c
  45. .code
  46.  
  47. ;------------------------------------------------------------------------------;
  48. ;   Handy little macro to get the timer 0 value into AX                        ;
  49. ;------------------------------------------------------------------------------;
  50.  
  51. get_timer_0_value macro
  52. mov     al,0                    ;Latch timer 0
  53. out     43h,al
  54. jmp     $+2                     ;Wait for the hardware to respond
  55. in      al,40h                  ;Get LSB
  56. mov     ah,al
  57. jmp     $+2
  58. in      al,40h                  ;Get MSB
  59. xchg    ah,al                   ;Put LSB and MSB into their proper places
  60. endm
  61.  
  62. ;------------------------------------------------------------------------------;
  63. ;                                                                              ;
  64. ;   int button_status()                                                        ;
  65. ;                                                                              ;
  66. ;   This function returns the status of the joystick buttons in bits 0-3 of    ;
  67. ;   the return value.  (The rest of the bits are 0.)  0 means a button is      ;
  68. ;   not being pressed; 1 means a button is being pressed.  Note that this is   ;
  69. ;   the opposite of the meaning of the bits in the joystick port (where 1      ;
  70. ;   means that a button is not being pressed, and 0 means that it is being     ;
  71. ;   pressed).                                                                  ;
  72. ;                                                                              ;
  73. ;------------------------------------------------------------------------------;
  74.  
  75. public button_status
  76. button_status proc
  77.  
  78. mov     dx,201h                 ;Game port
  79. in      al,dx                   ;Get a value from it
  80. ;Note: Those are really the only two lines you need to read the status of the
  81. ;joystick; the rest of the stuff here is just to make the button status bits
  82. ;go into bits 0-3, and the top 8 bits of AX 0, and to make 1 stand for a button
  83. ;being pressed instead of 0.
  84.  
  85. not     al                      ;Make 1 stand for a button-press
  86. mov     cl,4
  87. shr     al,cl                   ;Put it into the lower 4 bits of AX
  88. mov     ah,0
  89.  
  90. ret
  91.  
  92. button_status endp
  93.  
  94. ;------------------------------------------------------------------------------;
  95. ;                                                                              ;
  96. ;   void read_1_joystick(int *x, int *y)                                       ;
  97. ;                                                                              ;
  98. ;   This procedure reads the x and y coordinates of joystick A and puts them   ;
  99. ;   them into the words pointed to by x and y (which are near pointers; DS     ;
  100. ;   is assumed to point to the data segment upon entry to this procedure).     ;
  101. ;   0FFFFh is returned as a coordinate of a bit in the joystick port that      ;
  102. ;   never responded.  This feature can make read_1_joystick useful as a        ;
  103. ;   joystick-detecting routine.  Call read_1_joystick, and if either           ;
  104. ;   coordinate is returned as 0FFFFh, joystick A is not connected.  Use this   ;
  105. ;   procedure instead of read_2_joysticks if you're only going to be reading   ;
  106. ;   joystick A because it's more accurate (since it only has to time 2 bits    ;
  107. ;   in the joystick port at once) and the code size is smaller.                ;
  108. ;                                                                              ;
  109. ;   Now SI and DI are saved.  (Thanks to Kip Cooley!  See above...)            ;
  110. ;                                                                              ;
  111. ;                                                                              ;
  112. ;------------------------------------------------------------------------------;
  113.  
  114. public read_1_joystick
  115. read_1_joystick proc
  116.  
  117. push    bp                      ;Save BP
  118. mov     bp,sp                   ;Set up stack framepointer
  119.  
  120. pushf                           ;Save the flags
  121. push    es                      ;Save ES
  122. push    si                      ;Save SI
  123. push    di                      ;Save DI
  124.  
  125. cli                             ;Disable interrupts which might mess up our
  126.                                 ; timing
  127.  
  128. mov     bx,-1                   ;We're using BX and ES to hold the x and y
  129. mov     es,bx                   ; coordinates
  130.  
  131. mov     dx,201h                 ;Joystick port
  132.  
  133. get_timer_0_value               ;Get the timer value
  134. mov     di,ax                   ;Save it
  135.  
  136. out     dx,al                   ;Fire the joystick one-shots (doesn't matter
  137.                                 ; what's in AX
  138.  
  139. in      al,dx                   ;Get the original joystick value
  140. and     al,00000011b            ;Get just the bits for the joystick A location
  141. mov     cl,al
  142.  
  143. r1j_loop:
  144. get_timer_0_value               ;Get the timer value
  145. mov     si,di                   ;Get the original timer value into SI
  146. sub     si,ax                   ;Get the elapsed time into SI
  147. cmp     si,1FF0h                ;Has enough time passed yet?
  148. ja      r1j_done                ;Yes
  149.  
  150. in      al,dx                   ;Get the joystick coordinate bits
  151. and     al,00000011b            ;Get just the bits for the joystick A location
  152.  
  153. cmp     al,cl                   ;Is the value the same as the last time?
  154. je      r1j_loop                ;Yes
  155.  
  156. xchg    al,cl                   ;New value goes into CL
  157. xor     al,cl                   ;Get the bits that have changed
  158.  
  159. test    al,1                    ;Has the joystick A x-coordinate bit changed?
  160. jz      r1j_loop_2
  161. mov     bx,si                   ;Yes, we now have the joystick A x-coordinate
  162.  
  163. r1j_loop_2:
  164. test    al,2                    ;Has the joystick A y-coordinate bit changed?
  165. jz      r1j_loop
  166. mov     es,si                   ;Yes, we now have the joystick A y-coordinate
  167.                                 ; (Yeah, I know, it's a sin to use a segment
  168.                                 ; register for holding a value like that, but I
  169.                                 ; couldn't care less unless I had to convert
  170.                                 ; this for protected mode...)
  171. jmp     r1j_loop
  172.  
  173. r1j_done:
  174. mov     cl,4
  175.  
  176. mov     si,[bp+4]               ;Get address of joystick A x-coordinate
  177. or      bx,bx                   ;Is BX -1?  (If so, don't shift right 4 bits)
  178. js      r1j_done_2
  179. shr     bx,cl                   ;Convert timings to 16/1193180's of a second
  180. r1j_done_2:
  181. mov     [si],bx                 ;Save the joystick A x-coordinate
  182.  
  183. mov     si,[bp+6]               ;Get address of joystick A y-coordinate
  184. mov     ax,es
  185. or      ax,ax
  186. js      r1j_done_3
  187. shr     ax,cl
  188. r1j_done_3:
  189. mov     [si],ax                 ;Save the joystick A y-coordinate
  190.  
  191. pop     di                      ;Restore DI
  192. pop     si                      ;Restore SI
  193. pop     es                      ;Restore ES
  194. popf                            ;Restore the interrupt flag
  195.  
  196. pop     bp                      ;Restore BP
  197. ret                             ;We're outta here!
  198.  
  199. read_1_joystick endp
  200.  
  201. ;------------------------------------------------------------------------------;
  202. ;                                                                              ;
  203. ;   void read_2_joysticks(int *x1, int *y1, int *x2, int *y2)                  ;
  204. ;                                                                              ;
  205. ;   This function reads the x and y coordinates of joysticks A and B and       ;
  206. ;   puts them into the words pointed by x1, y1, x2, and y2 (which are near     ;
  207. ;   pointers; DS is assumed to be the data segment where these words are).     ;
  208. ;   0FFFFh is returned as the coordinate for a bit in the joystick port        ;
  209. ;   never responded.  This can make read_2_joysticks useful as a joystick-     ;
  210. ;   detecting procedure.  If 0FFFFh is returned as either coordinate of a      ;
  211. ;   joystick, it can be assumed that that joystick isn't connected.            ;
  212. ;                                                                              ;
  213. ;   The code here was written for speed and can be 4 times as fast as the      ;
  214. ;   code in the ROM BIOS for doing the same thing!  The reason is that the     ;
  215. ;   ROM BIOS code usually times the 4 joystick bits separately (it will fire   ;
  216. ;   the joystick one-shots, time that one bit, fire them again, and so on).    ;
  217. ;   This routine fires the joystick one-shots ONCE and times all 4 bits at     ;
  218. ;   once!  Of course, this brings up some obvious difficulties, namely that    ;
  219. ;   every time the value given by the joystick port changes, the routine       ;
  220. ;   has to test for each bit that could have possibly changed and handle       ;
  221. ;   each one separately.  On slower computers, this would result in a loss     ;
  222. ;   of accuracy.  But that's too bad!  If you're a masochist and you need      ;
  223. ;   both speed and more accuracy on slower machines, you can modify this       ;
  224. ;   routine to push onto the stack timer chip values and joystick port         ;
  225. ;   values with each change of the joystick port value, and then sort          ;
  226. ;   through all this stuff after all the timing-critical stuff is over.        ;
  227. ;                                                                              ;
  228. ;   Now SI, DI, and ES are saved properly, thanks to Kip Cooley (see above).   ;
  229. ;   Previously, ES wasn't saved properly since it was pushed after it was      ;
  230. ;   set to -1.                                                                 ;
  231. ;                                                                              ;
  232. ;------------------------------------------------------------------------------;
  233.  
  234. public read_2_joysticks
  235. read_2_joysticks proc
  236.  
  237. push    bp                      ;Save BP
  238. mov     bp,sp                   ;Set up stack framepointer
  239.  
  240. mov     bx,-1                   ;Make some workspace on the stack
  241. push    bx                      ;Start out with coordinate values of -1
  242. push    bx                      ;If the corresponding bits in the joystick port
  243.                                 ; don't change, these will stay -1
  244.  
  245. pushf                           ;Save the flags
  246. push    es                      ;Save ES
  247. push    si                      ;Save SI
  248. push    di                      ;Save DI
  249.  
  250. mov     es,bx
  251.  
  252. cli                             ;Disable interrupts which might mess up our
  253.                                 ; timing
  254.  
  255. mov     dx,201h                 ;Joystick port
  256.  
  257. get_timer_0_value               ;Get the timer value
  258. mov     di,ax                   ;Save it
  259.  
  260. out     dx,al                   ;Fire the joystick one-shots (doesn't matter
  261.                                 ; what's in AX
  262.  
  263. in      al,dx                   ;Get the original joystick value
  264. and     al,00001111b            ;Ignore the button bits
  265. mov     cl,al
  266.  
  267. r2j_loop:
  268. get_timer_0_value               ;Get the timer value
  269. mov     si,di                   ;Get the original timer value into SI
  270. sub     si,ax                   ;Get the elapsed time into SI
  271. cmp     si,1FF0h                ;Has enough time passed yet?
  272. ja      r2j_done                ;Yes
  273.  
  274. in      al,dx                   ;Get the joystick coordinate bits
  275. and     al,00001111b            ;Ignore the button bits
  276.  
  277. cmp     al,cl                   ;Is the value the same as the last time?
  278. je      r2j_loop                ;Yes
  279.  
  280. xchg    al,cl                   ;New value goes into CL
  281. xor     al,cl                   ;Get the bits that have changed
  282.  
  283. test    al,1                    ;Has the joystick A x-coordinate bit changed?
  284. jz      r2j_loop_2
  285. mov     bx,si                   ;Yes, we now have the joystick A x-coordinate
  286.  
  287. r2j_loop_2:
  288. test    al,2                    ;Has the joystick A y-coordinate bit changed?
  289. jz      r2j_loop_3
  290. mov     es,si                   ;Yes, we now have the joystick A y-coordinate
  291.  
  292. r2j_loop_3:
  293. test    al,4                    ;Has the joystick B x-coordinate bit changed?
  294. jz      r2j_loop_4
  295. mov     [bp-4],si               ;Yes, we now have the joystick B x-coordinate
  296.  
  297. r2j_loop_4:
  298. test    al,8                    ;Has the joystick B y-coordinate bit changed?
  299. jz      r2j_loop
  300. mov     [bp-2],si               ;Yes, we now have the joystick B y-coordinate
  301. jmp     r2j_loop
  302.  
  303. r2j_done:
  304. mov     cl,4
  305.  
  306. mov     si,[bp+4]               ;Get address of joystick A x-coordinate
  307. or      bx,bx                   ;Don't shift BX by 4 bits if BX is -1
  308. js      r2j_done_2
  309. shr     bx,cl                   ;Convert timings to 16/1193180's of a second
  310. r2j_done_2:
  311. mov     [si],bx                 ;Save the joystick A x-coordinate
  312.  
  313. mov     si,[bp+6]               ;Get address of joystick A y-coordinate
  314. mov     ax,es
  315. or      ax,ax
  316. js      r2j_done_3
  317. shr     ax,cl
  318. r2j_done_3:
  319. mov     [si],ax                 ;Save the joystick A y-coordinate
  320.  
  321. mov     si,[bp+8]               ;Get address of joystick B x-coordinate
  322. mov     ax,[bp-4]
  323. or      ax,ax
  324. js      r2j_done_4
  325. shr     ax,cl
  326. r2j_done_4:
  327. mov     [si],ax                 ;Save the joystick B x-coordinate
  328.  
  329. mov     si,[bp+10]              ;Get address of joystick B y-coordinate
  330. mov     ax,[bp-2]
  331. or      ax,ax
  332. js      r2j_done_5
  333. shr     ax,cl
  334. r2j_done_5:
  335. mov     [si],ax                 ;Save the joystick B y-coordinate
  336.  
  337. pop     di                      ;Restore DI
  338. pop     si                      ;Restore SI
  339. pop     es                      ;Restore ES
  340. popf                            ;Restore interrupt flag
  341.  
  342. add     sp,4                    ;Discard temporary workspace on the stack
  343. pop     bp                      ;Restore BP
  344. ret                             ;We're outta here!
  345.  
  346. read_2_joysticks endp
  347.  
  348. ;------------------------------------------------------------------------------;
  349. ;                                                                              ;
  350. ;   int detect_game_port()                                                     ;
  351. ;                                                                              ;
  352. ;   This function determines whether or not a there is a game port             ;
  353. ;   (regardless of whether or not controllers are actually connected to it     ;
  354. ;   or not).  It returns 0FFFFh if there is a game port and 0 if there is      ;
  355. ;   no game port.  This may not be very reliable (see JOYSTICK.TXT) so it      ;
  356. ;   might be better just to call read_1_joystick or read_2_joysticks to        ;
  357. ;   detect whether or not joysticks are connected, because it's not much       ;
  358. ;   use knowing that there's a game port but no controller connected (other    ;
  359. ;   than for diagnostic purposes).                                             ;
  360. ;                                                                              ;
  361. ;   Now there's a little optimization in here, thanks to David Kirschbaum      ;
  362. ;   (see above).                                                               ;
  363. ;                                                                              ;
  364. ;------------------------------------------------------------------------------;
  365.  
  366. public detect_game_port
  367. detect_game_port proc
  368.  
  369. mov     dx,201h                 ;Game port number
  370. in      al,dx
  371. cmp     al,0FFh
  372. mov     ax,0                    ;Assume no game port (must use MOV AX,0 instead
  373.                                 ; of XOR AX,AX since XOR AX,AX would change
  374.                                 ; the flags set by CMP AL, 0FFh)
  375.  
  376. je      detect_ret              ;Jump if no game port
  377. not     ax                      ;Set AX to 0FFFFh since there is a game port
  378.  
  379. detect_ret:
  380. ret                             ;Return
  381.  
  382. detect_game_port endp
  383.  
  384. end
  385.