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