home *** CD-ROM | disk | FTP | other *** search
/ Black Art of 3D Game Programming / Black_Art_of_3D_Game_Programming.iso / source / borland / chap_5 / black5.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-20  |  17.6 KB  |  637 lines

  1.  
  2. // BLACK5.C - Library Module
  3.  
  4. // I N C L U D E S ///////////////////////////////////////////////////////////
  5.  
  6. #include <io.h>
  7. #include <conio.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <dos.h>
  11. #include <bios.h>
  12. #include <fcntl.h>
  13. #include <memory.h>
  14. #include <malloc.h>
  15. #include <math.h>
  16. #include <string.h>
  17.  
  18. #include "black5.h"
  19.  
  20. // G L O B A L S  ////////////////////////////////////////////////////////////
  21.  
  22. void (_interrupt _far *Old_Keyboard_ISR)();  // holds old keyboard interrupt handler
  23.  
  24. int raw_scan_code=0;  // the global keyboard scan code
  25.  
  26. // this holds the keyboard state table which tracks the state of every key
  27. // on the keyboard
  28.  
  29. int keyboard_state[128];
  30.  
  31. // this tracks the number of keys that are currently pressed, helps
  32. // with keyboard testing logic
  33.  
  34. int keys_active = 0;
  35.  
  36. // these values hold the maximum, minimum and neutral joystick values for
  37. // both joysticks
  38.  
  39. unsigned int joystick_1_max_x,
  40.              joystick_1_max_y,
  41.              joystick_1_min_x,
  42.              joystick_1_min_y,
  43.              joystick_1_neutral_x,
  44.              joystick_1_neutral_y,
  45.  
  46.              joystick_2_max_x,
  47.              joystick_2_max_y,
  48.              joystick_2_min_x,
  49.              joystick_2_min_y,
  50.              joystick_2_neutral_x,
  51.              joystick_2_neutral_y;
  52.  
  53. // F U N C T I O N S /////////////////////////////////////////////////////////
  54.  
  55. unsigned char Get_Key(void)
  56. {
  57. // test if a key press has been buffered by system and if so, return the ASCII
  58. // value of the key, otherwise return 0
  59.  
  60. if (_bios_keybrd(_KEYBRD_READY))
  61.  return((unsigned char)_bios_keybrd(_KEYBRD_READ));
  62. else return(0);
  63.  
  64. } // end Get_Key
  65.  
  66. //////////////////////////////////////////////////////////////////////////////
  67.  
  68. unsigned int Get_Shift_State(unsigned int mask)
  69. {
  70. // return the shift state of the keyboard logically ANDed with the sent mask
  71.  
  72. return(mask & _bios_keybrd(_KEYBRD_SHIFTSTATUS));
  73.  
  74. } // end Get_Shift_State
  75.  
  76. //////////////////////////////////////////////////////////////////////////////
  77.  
  78. unsigned char Get_Scan_Code(void)
  79. {
  80. // use BIOS functions to retrieve the scan code of the last pressed key
  81. // if a key was pressed at all, otherwise return 0
  82.  
  83. _asm   {
  84.     mov ah,01h      // function #1 which is "key ready"
  85.     int 16h         // call the BIOS keyboard interrupt
  86.     jz buffer_empty // if there was no key ready then exit
  87.     mov ah,00h      // function #0: retrieve raw scan code
  88.     int 16h         // call the BIOS keyboard interrupt
  89.     mov al,ah       // result is placed by BIOS in ah, copy it to al
  90.     xor ah,ah       // zero out ah
  91.     jmp done        // jump to end so ax doesn't get reset
  92.  
  93. buffer_empty:
  94.       xor ax,ax     // a key was retrieved so write a 0 into ax to reflect this
  95. done:
  96.  
  97.     } // end asm
  98.  
  99. // 8 or 16 bit data is returned in AX, hence no need to explicitly say return()
  100.  
  101. } // end Get_Scan_Code
  102.  
  103. //////////////////////////////////////////////////////////////////////////////
  104.  
  105. void _interrupt _far Keyboard_Driver()
  106. {
  107. // this function is used as the new keyboard driver. once it is installed
  108. // it will continually update a keyboard state table that has an entry for
  109. // every key on the keyboard, when a key is down the appropriate entry will be
  110. // set to 1, when released the entry will be reset. any key can be querried by
  111. // accessing the keyboard_state[] table with the make code of the key as the
  112. // index
  113.  
  114. // need to use assembly for speed since this is an interrupt
  115.  
  116. _asm   {
  117.     sti                    // re-enable interrupts
  118.     in al, KEY_BUFFER      // get the key that was pressed from the keyboard
  119.     xor ah,ah              // zero out upper 8 bits of AX
  120.     mov raw_scan_code, ax  // store the key in global variable
  121.     in al, KEY_CONTROL     // set the control register to reflect key was read
  122.     or al, 82h             // set the proper bits to reset the keyboard flip flop
  123.     out KEY_CONTROL,al     // send the new data back to the control register
  124.     and al,7fh
  125.     out KEY_CONTROL,al     // complete the reset
  126.     mov al,20h             // reset command
  127.     out PIC_PORT,al        // tell PIC to re-enable interrupts
  128.  
  129.     } // end inline assembly
  130.  
  131. // update the keyboard table
  132.  
  133. // test if the scan code was a make code or a break code
  134.  
  135. if (raw_scan_code <128)
  136.    {
  137.    // index into table and set this key to the "on" state if it already isn't
  138.    // on
  139.  
  140.    if (keyboard_state[raw_scan_code]==KEY_UP)
  141.       {
  142.       // there is one more active key
  143.  
  144.       keys_active++;
  145.  
  146.       // update the state table
  147.  
  148.       keyboard_state[raw_scan_code] = KEY_DOWN;
  149.  
  150.       } // end if key wasn't already pressed
  151.  
  152.    } // end if a make code
  153. else
  154.    {
  155.    // must be a break code, therefore turn the key "off"
  156.    // note that 128 must be subtracted from the raw key to access the correct
  157.    // element of the state table
  158.  
  159.    if (keyboard_state[raw_scan_code-128]==KEY_DOWN)
  160.       {
  161.       // there is one less active key
  162.  
  163.       keys_active--;
  164.  
  165.       // update the state table
  166.  
  167.         keyboard_state[raw_scan_code-128] = KEY_UP;
  168.  
  169.       } // end if key wasn't already pressed
  170.  
  171.    } // end else
  172.  
  173. } // end Keyboard_Driver
  174.  
  175. ///////////////////////////////////////////////////////////////////////////////
  176.  
  177. void Keyboard_Install_Driver(void)
  178. {
  179. // this function installs the new keyboard driver
  180.  
  181. int index; // used as loop variable
  182.  
  183. // clear out the keyboard state table
  184.  
  185. for (index=0; index<128; index++)
  186.     keyboard_state[index]=0;
  187.  
  188. // save the old keyboard driver
  189.  
  190. Old_Keyboard_ISR = _dos_getvect(KEYBOARD_INTERRUPT);
  191.  
  192. // install the new keyboard driver
  193.  
  194. _dos_setvect(KEYBOARD_INTERRUPT, Keyboard_Driver);
  195.  
  196. } // end Keyboard_Install_Driver
  197.  
  198. ///////////////////////////////////////////////////////////////////////////////
  199.  
  200. void Keyboard_Remove_Driver(void)
  201. {
  202.  
  203. // this functions restores the old keyboard driver (DOS version) with the
  204. // previously saved vector
  205.  
  206. _dos_setvect(KEYBOARD_INTERRUPT, Old_Keyboard_ISR);
  207.  
  208. } // end Keyboard_Remove_Driver
  209.  
  210. //////////////////////////////////////////////////////////////////////////////
  211.  
  212. unsigned char Joystick_Buttons(unsigned char button)
  213. {
  214.  
  215. // this function reads the state of the joystick buttons by retrieving the
  216. // appropriate bit in the joystick port
  217.  
  218. outp(JOYPORT,0); // clear the joystick port and request a sample
  219.  
  220. // invert buttons then mask with request so that a button that is pressed
  221. // returns a "1" instead of a "0"
  222.  
  223. return( (unsigned char)(~inp(JOYPORT) & button));
  224.  
  225. } // end Joystick_Buttons
  226.  
  227. //////////////////////////////////////////////////////////////////////////////
  228.  
  229. unsigned int Joystick(unsigned char stick)
  230. {
  231. // this function will read a joystick by starting the timing circuits connected
  232. // to each joystick pot, when the timing circuit has charged the joystick
  233. // bit will revert to 0, the time this process takes is proportional to
  234. // the joystick position and is returned as the result
  235.  
  236.  _asm     {
  237.      cli                    // disable interupts for timing purposes
  238.  
  239.      mov ah, byte ptr stick // select joystick to read with bitmask
  240.      xor al,al              // zero out al
  241.      xor cx,cx              // clear cx i.e. set it to 0
  242.      mov dx,JOYPORT         // point dx to the joystick port
  243.      out dx,al              // start the 555 timers charging
  244. charged:
  245.      in al,dx               // read the joystick port and test of the bit
  246.      test al,ah             // has reverted back to 0
  247.      loopne charged         // if the joystick circuit isn't charged then
  248.                                     // decrement cx and loop
  249.  
  250.  
  251.      xor ax,ax              // zero out ax
  252.      sub ax,cx              // subtract cx from ax to get a number that increases
  253.                                     // as the joystick position in moved away from neutral
  254.  
  255.      sti                    // re-enable interrupts
  256.  
  257.      } // end asm
  258.  
  259. // ax has the 16 result, so no need for an explicit return
  260.  
  261. } // end Joystick
  262.  
  263. //////////////////////////////////////////////////////////////////////////////
  264.  
  265. unsigned int Joystick_Bios(unsigned char stick)
  266. {
  267. // read the joystick using bios interrupt 15h with the joystick function 84h
  268.  
  269. union REGS inregs, outregs; // used to hold CPU registers
  270.  
  271. inregs.h.ah = 0x84; // joystick function 84h
  272. inregs.x.dx = 0x01; // read joysticks subfunction 1h
  273.  
  274. // call the BIOS joystick interrupt
  275.  
  276. int86(0x15,&inregs, &outregs);
  277.  
  278. // return requested joystick
  279.  
  280. switch(stick)
  281.       {
  282.       case JOYSTICK_1_X:  // ax has joystick 1's X axis
  283.            {
  284.            return(outregs.x.ax);
  285.            } break;
  286.  
  287.       case JOYSTICK_1_Y:  // bx has joystick 1's Y axis
  288.            {
  289.            return(outregs.x.bx);
  290.            } break;
  291.  
  292.       case JOYSTICK_2_X:  // cx has joystick 2's X axis
  293.            {
  294.            return(outregs.x.cx);
  295.            } break;
  296.  
  297.         case JOYSTICK_2_Y:  // dx has joystick 2's Y axis
  298.            {
  299.            return(outregs.x.dx);
  300.            } break;
  301.  
  302.       default:break;
  303.  
  304.       } // end switch stick
  305.  
  306. } // end Joystick_Bios
  307.  
  308. //////////////////////////////////////////////////////////////////////////////
  309.  
  310. void Joystick_Calibrate(int stick,int method)
  311. {
  312.  
  313. // this function is used to calibrate the a joystick. the function will
  314. // query the user to move the joystick in circlular motion and then release
  315. // the stick back to the neutral position and press the fire button. using
  316. // this information the function will compute the max,min and neutral
  317. // values for both the X and Y axis of the joystick. these values will
  318. // then be stored in global variables so they can be used by other
  319. // functions, note the function can use either the BIOS joystick call
  320. // or the low level one we made
  321.  
  322. unsigned int x_value, // used to read values of X and Y axis in real-time
  323.              y_value;
  324.  
  325. // which stick does caller want to calibrate?
  326.  
  327. if (stick==JOYSTICK_1)
  328.    {
  329.    printf("\nCalibrating Joystick #1: Move the joystick in a circle, then");
  330.    printf("\nplace the stick into it's neutral position and press fire.");
  331.  
  332.    // set calibrations values to extremes
  333.  
  334.    joystick_1_max_x = 0;
  335.    joystick_1_max_y = 0;
  336.    joystick_1_min_x = 32000;
  337.     joystick_1_min_y = 32000;
  338.  
  339.    // process X and Y values in real time
  340.  
  341.    while(!Joystick_Buttons(JOYSTICK_BUTTON_1_1 | JOYSTICK_BUTTON_1_2))
  342.         {
  343.         // get the new values and try to update calibration
  344.  
  345.         // test if user wants to use bios or low level
  346.  
  347.         if (method==USE_BIOS)
  348.            {
  349.            x_value = Joystick_Bios(JOYSTICK_1_X);
  350.            y_value = Joystick_Bios(JOYSTICK_1_Y);
  351.            }
  352.         else
  353.            {
  354.            x_value = Joystick(JOYSTICK_1_X);
  355.            y_value = Joystick(JOYSTICK_1_Y);
  356.            }
  357.           // update globals with new extremes
  358.  
  359.         // process X - axis
  360.  
  361.         if (x_value >= joystick_1_max_x)
  362.             joystick_1_max_x = x_value;
  363.  
  364.         if (x_value <= joystick_1_min_x)
  365.             joystick_1_min_x = x_value;
  366.  
  367.         // process Y - axis
  368.  
  369.         if (y_value >= joystick_1_max_y)
  370.             joystick_1_max_y = y_value;
  371.  
  372.         if (y_value <= joystick_1_min_y)
  373.             joystick_1_min_y = y_value;
  374.  
  375.         } // end while
  376.  
  377.           // stick is now in neutral position so record the values here also
  378.  
  379.         joystick_1_neutral_x = x_value;
  380.         joystick_1_neutral_y = y_value;
  381.  
  382.    // notify user process is done
  383.  
  384.    printf("\nJoystick #1 Calibrated. Press the fire button to exit.");
  385.  
  386.    while(Joystick_Buttons(JOYSTICK_BUTTON_1_1 | JOYSTICK_BUTTON_1_2));
  387.    while(!Joystick_Buttons(JOYSTICK_BUTTON_1_1 | JOYSTICK_BUTTON_1_2));
  388.  
  389.    } // end calibrate joystick #1
  390.  
  391. else
  392. if (stick==JOYSTICK_2)
  393.    {
  394.    printf("\nCalibrating Joystick #1: Move the joystick in a circle, then");
  395.    printf("\nplace the stick into it's neutral position and press fire.");
  396.  
  397.     // set calibrations values to extremes
  398.  
  399.    joystick_2_max_x = 0;
  400.    joystick_2_max_y = 0;
  401.    joystick_2_min_x = 32000;
  402.    joystick_2_min_y = 32000;
  403.  
  404.    // process X and Y axis values in real time
  405.  
  406.    while(!Joystick_Buttons(JOYSTICK_BUTTON_2_1 | JOYSTICK_BUTTON_2_2))
  407.         {
  408.         // get the new values and try to update calibration
  409.  
  410.         // test if user wants to use bios or low level
  411.  
  412.         if (method==USE_BIOS)
  413.            {
  414.            x_value = Joystick_Bios(JOYSTICK_1_X);
  415.            y_value = Joystick_Bios(JOYSTICK_1_Y);
  416.            }
  417.           else
  418.            {
  419.            x_value = Joystick(JOYSTICK_1_X);
  420.            y_value = Joystick(JOYSTICK_1_Y);
  421.            }
  422.  
  423.         // update globals with new extremes
  424.  
  425.         // process X - axis
  426.  
  427.         if (x_value >= joystick_2_max_x)
  428.             joystick_2_max_x = x_value;
  429.         else
  430.         if (x_value <= joystick_2_min_x)
  431.             joystick_2_min_x = x_value;
  432.  
  433.         // process Y - axis
  434.  
  435.         if (y_value >= joystick_2_max_y)
  436.             joystick_2_max_y = y_value;
  437.           else
  438.         if (y_value <= joystick_2_min_y)
  439.             joystick_2_min_y = y_value;
  440.  
  441.         } // end while
  442.  
  443.         // stick is now in neutral position so record the values here also
  444.  
  445.         joystick_2_neutral_x = x_value;
  446.         joystick_2_neutral_y = y_value;
  447.  
  448.    // notify user process is done
  449.  
  450.    printf("\nJoystick #2 Calibrated. Press the fire button to exit.");
  451.  
  452.    while(Joystick_Buttons(JOYSTICK_BUTTON_1_1 | JOYSTICK_BUTTON_1_2));
  453.    while(!Joystick_Buttons(JOYSTICK_BUTTON_1_1 | JOYSTICK_BUTTON_1_2));
  454.  
  455.    } // end calibrate joystick #2
  456.  
  457. } // end Joystick_Calibrate
  458.  
  459. //////////////////////////////////////////////////////////////////////////////
  460.  
  461. int Joystick_Available(int stick_num)
  462. {
  463. // test if the joystick is plugged in that the user is requesting tested
  464. // note the use of the BIOS joystick function, it is very reliable
  465.  
  466. if (stick_num == JOYSTICK_1)
  467.    {
  468.    // test if joystick 1 is plugged in by testing the port values
  469.    // they will be 0,0 if there is no stick
  470.  
  471.    return(Joystick_Bios(JOYSTICK_1_X)+Joystick_Bios(JOYSTICK_1_Y));
  472.  
  473.    } // end if joystick 1
  474. else
  475.    {
  476.    // test if joystick 2 is plugged in by testing the port values
  477.     // they will be 0,0 if there is no stick
  478.  
  479.    return(Joystick_Bios(JOYSTICK_2_X)+Joystick_Bios(JOYSTICK_2_Y));
  480.  
  481.    } // end else joystick 2
  482.  
  483. } // end Joystick_Available
  484.  
  485. ///////////////////////////////////////////////////////////////////////////////
  486.  
  487. int Mouse_Control(int command, int *x, int *y,int *buttons)
  488. {
  489. union REGS inregs,  // CPU register unions to be used by interrupts
  490.             outregs;
  491.  
  492. // what is caller asking function to do?
  493.  
  494. switch(command)
  495.       {
  496.  
  497.         case MOUSE_RESET: // this resets the mouse
  498.            {
  499.  
  500.            // mouse subfunction 0: reset
  501.  
  502.            inregs.x.ax = 0x00;
  503.  
  504.            // call the mouse interrupt
  505.  
  506.            int86(MOUSE_INTERRUPT, &inregs, &outregs);
  507.  
  508.            // return number of buttons on this mouse
  509.  
  510.            *buttons = outregs.x.bx;
  511.  
  512.            // return success/failure of function
  513.  
  514.            return(outregs.x.ax);
  515.  
  516.            } break;
  517.  
  518.       case MOUSE_SHOW: // this shows the mouse
  519.            {
  520.            // this function increments the internal mouse visibility counter.
  521.            // when it is equal to 0 then the mouse will be displayed.
  522.  
  523.            // mouse subfunction 1: increment show flag
  524.  
  525.            inregs.x.ax = 0x01;
  526.  
  527.            // call the mouse interrupt
  528.  
  529.            int86(MOUSE_INTERRUPT, &inregs, &outregs);
  530.  
  531.            // return success always
  532.  
  533.            return(1);
  534.  
  535.            } break;
  536.  
  537.         case MOUSE_HIDE:  // this hides the mouse
  538.            {
  539.            // this function decrements the internal mouse visibility counter.
  540.            // when it is equal to -1 then the mouse will be hidden.
  541.  
  542.            // mouse subfunction 2: decrement show flag
  543.  
  544.            inregs.x.ax = 0x02;
  545.  
  546.            // call the interrupt
  547.  
  548.            int86(MOUSE_INTERRUPT, &inregs, &outregs);
  549.  
  550.            // return success
  551.  
  552.            return(1);
  553.  
  554.            } break;
  555.  
  556.       case MOUSE_POSITION_BUTTONS: // this gets both the position and
  557.                                               // state of buttons
  558.            {
  559.            // this functions computes the absolute position of the mouse
  560.            // and the state of the mouse buttons
  561.  
  562.            // mouse subfunction 3: get position and buttons
  563.  
  564.            inregs.x.ax = 0x03;
  565.  
  566.            // call the mouse interrupt
  567.  
  568.            int86(MOUSE_INTERRUPT, &inregs, &outregs);
  569.  
  570.            // extract the info and send back to caller via pointers
  571.  
  572.            *x       = outregs.x.cx;
  573.            *y       = outregs.x.dx;
  574.            *buttons = outregs.x.bx;
  575.  
  576.            // return success always
  577.  
  578.            return(1);
  579.  
  580.            } break;
  581.  
  582.       case MOUSE_MOTION_REL:  // this gets the relative motion of mouse
  583.            {
  584.  
  585.            // this functions gets the relative mouse motions from the last
  586.            // call, these values will range from -32768 to +32767 and
  587.            // be in mickeys which are 1/200 of inch or 1/400 of inch
  588.            // depending on the resolution of your mouse
  589.  
  590.            // subfunction 11: get relative motion
  591.  
  592.            inregs.x.ax = 0x0B;
  593.  
  594.            // call the interrupt
  595.  
  596.            int86(MOUSE_INTERRUPT, &inregs, &outregs);
  597.  
  598.            // extract the info and send back to caller via pointers
  599.  
  600.            *x       = outregs.x.cx;
  601.            *y       = outregs.x.dx;
  602.  
  603.            // return success
  604.  
  605.            return(1);
  606.  
  607.            } break;
  608.  
  609.       case MOUSE_SET_SENSITIVITY:
  610.            {
  611.            // subfunction 26: set sensitivity
  612.  
  613.            inregs.x.ax = 0x1A;
  614.  
  615.            // place the desired sensitivity and double speed values in place
  616.  
  617.               inregs.x.bx = *x;
  618.            inregs.x.cx = *y;
  619.            inregs.x.dx = *buttons;
  620.  
  621.            // call the interrupt
  622.  
  623.               int86(MOUSE_INTERRUPT, &inregs, &outregs);
  624.  
  625.            // always return success
  626.  
  627.            return(1);
  628.  
  629.            } break;
  630.  
  631.       default:break;
  632.  
  633.       } // end switch
  634.  
  635. } // end Mouse_Control
  636.  
  637.