home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / lang / pascal / 5386 < prev    next >
Encoding:
Internet Message Format  |  1992-09-13  |  15.3 KB

  1. Path: sparky!uunet!news.centerline.com!noc.near.net!ds5000!cschmidt
  2. From: cschmidt@ds5000.DAC.Northeastern.edu (Christopher Schmidt)
  3. Newsgroups: comp.lang.pascal
  4. Subject: Re: Ctrl-Break in TP 6.0 programs
  5. Message-ID: <7403@ds5000.DAC.Northeastern.edu>
  6. Date: 14 Sep 92 04:57:50 GMT
  7. Reply-To: cschmidt@ds5000 (Christopher Schmidt)
  8. Organization: Leelanau Computing Inc
  9. Lines: 495
  10.  
  11. This message contains a Turbo Pascal unit that includes a solution to
  12. the CTRL-BREAK problem.  The unit also includes basic routines for
  13. reading the keyboard, and a list of constants for identifying the
  14. control keys and special keys.
  15.  
  16. It is surprising to see in this newsgroup how many programmers use the
  17. TP library function "readkey".  The comment at the top of the keyboard
  18. unit included below contains my explanation of why programmers should
  19. not use "readkey".
  20.  
  21. Here is a simple program that demonstrates the "key_read" and
  22. "key_break" routines defined in the keyboard unit included below.
  23. When you run this program, try pressing CTRL-BREAK a few times.  If
  24. this program works on your machine as it does on mine, then you will
  25. not see "^C" on the screen when the program exits.  I should note that
  26. I use TP 5.5, and if you find that my CTRL-BREAK solution does not
  27. work with TP 6.0, I would appreciate it if you would send me a note.
  28.  
  29.         uses keyunit;
  30.         var
  31.             keycode : word;
  32.         begin
  33.             key_break;
  34.             writeln ('Press F10 to exit');
  35.             repeat
  36.                 keycode := key_read;
  37.                 writeln (hi (keycode) : 4, lo (keycode) : 4);
  38.             until keycode = KEY_F10;
  39.         end.
  40.  
  41. ------------------------------ cut here ------------------------------
  42. {/////////////////////////////////////////////////////////////////////
  43. KEYUNIT.PAS         Keyboard routines for Turbo Pascal
  44.  
  45. General purpose routines:
  46.  
  47.     key_clear       Flush the keyboard buffer
  48.     key_next        Return the next keycode if there is one
  49.     key_press       Return TRUE if the keyboard buffer is not empty
  50.     key_read        Read and return the next keycode
  51.  
  52. Control-break handling routines:
  53.  
  54.     key_break       Install an ISR for CTRL-BREAK
  55.     key_reset       Remove the CTRL-BREAK ISR installed by key_break
  56.  
  57. Author:
  58.  
  59.     Christopher Schmidt
  60.     Waltham, Massachusetts, USA
  61.     cschmidt@lynx.northeastern.edu
  62.  
  63. This unit is public domain.
  64.  
  65.  
  66. KEYCODES
  67.  
  68. The "key_read" and "key_next" functions return a single, unsigned,
  69. two-byte integer called a "keycode", representing the key the user
  70. pressed.  Keys and keycodes are organized in two groups:
  71.  
  72.     o   ORDINARY KEYS are those that generate ASCII characters (or
  73.         more precisely, characters in the IBM PC 8-bit character set).
  74.         The low byte of the keycode contains the ASCII code and the
  75.         high byte contains the scan code.  You can ignore the scan
  76.         code except when you want to distinguish between, say, the
  77.         number keys on the main keypad and the number keys on the
  78.         numeric keypad, or between CTRL-M and ENTER.
  79.  
  80.     o   SPECIAL KEYS are those that do not represent any ASCII
  81.         characters.  Special keys include the function keys, the ALT
  82.         keys, the cursor keys, and a few others.  The low byte of the
  83.         keycode is zero and the high byte is the scan code.
  84.  
  85. Here is an example showing how to use the returned keycode:
  86.  
  87.         keycode := key_read;
  88.         case keycode of
  89.         KEY_F1 : ... process function key ...
  90.         KEY_F2 : ... process function key ...
  91.         KEY_F3 : ... process function key ...
  92.         else
  93.             if lo (keycode) <> 0 then
  94.                 ... process "lo (keycode)" as an ASCII character ...
  95.         end;
  96.  
  97. Here is why it is better to have the basic keyboard input routines
  98. return a 2-byte keycode rather than a 1-byte char, and why the Turbo
  99. Pascal "readkey" function was badly conceived and should not be used.
  100. From the functional standpoint, there are two kinds of keys:
  101.  
  102.     o   TEXT KEYS are those that generate character codes that are
  103.         usually stored in strings as textual data.  Most of the
  104.         ordinary keys are text keys.
  105.  
  106.     o   COMMAND KEYS are those that instruct the program to perform
  107.         some operation (other than storing a character).  All special
  108.         keys are command keys.  Some ordinary keys are command keys,
  109.         such as ESC, ENTER, BACKSPACE, and most of the control keys,
  110.         depending on the application.
  111.  
  112. In practice, the use of text keys in program code is usually
  113. restricted to a few, isolated, general-purpose functions that read
  114. user input or that let the user modify text.  In contrast, most
  115. program code that deals with keyboard input deals with command keys,
  116. not text keys.  This is the essential fact.
  117.  
  118. The Turbo Pascal "readkey" function, which returns a 1-byte char and
  119. must be called twice to read most command keys, is optimized for
  120. dealing with text keys rather than command keys, which is exactly the
  121. wrong way about.  Perhaps the "readkey" designers were influenced by
  122. the greater number of text keys than command keys on the physical
  123. keyboard, or by the hypothesis that users press text keys more often
  124. on average than command keys.  These are irrelevent factors.
  125.  
  126.  
  127. ROM BIOS EXTENDED KEYBOARD SERVICES
  128.  
  129. This unit does not use the ROM BIOS extended keyboard services
  130. (services $10, $11, and $12), which simply do not work on a large
  131. percentage of machines.  To my knowledge, the only advantages of using
  132. the extended services are that you can read the F11 and F12 keys, and
  133. you can distinguish beteen the "duplicate" ENTER keys, ALT keys, and
  134. CTRL keys.  Any program that uses these features cannot run properly
  135. on the older machines.
  136.  
  137.  
  138. }
  139. unit keyunit;
  140.  
  141. interface
  142.  
  143. const
  144.     { code for general-purpose undefined keycode }
  145.  
  146.     KEY_NIL         = $FFFF;
  147.  
  148.     { codes for ordinary keys (low byte non-zero) }
  149.  
  150.     KEY_BACKSP      = $0E08;
  151.     KEY_TAB         = $0F09;
  152.     KEY_ENTER       = $1C0D;
  153.     KEY_ESC         = $011B;
  154.     KEY_SPACE       = $3920;
  155.  
  156.     KEY_CTRL_BACKSP = $0E7F;
  157.     KEY_CTRL_ENTER  = $1C0A;
  158.  
  159.     KEY_CTRL_A      = $1E01;
  160.     KEY_CTRL_B      = $3002;
  161.     KEY_CTRL_C      = $2E03;
  162.     KEY_CTRL_D      = $2004;
  163.     KEY_CTRL_E      = $1205;
  164.     KEY_CTRL_F      = $2106;
  165.     KEY_CTRL_G      = $2207;
  166.     KEY_CTRL_H      = $2308;
  167.     KEY_CTRL_I      = $1709;
  168.     KEY_CTRL_J      = $240A;
  169.     KEY_CTRL_K      = $250B;
  170.     KEY_CTRL_L      = $260C;
  171.     KEY_CTRL_M      = $320D;
  172.     KEY_CTRL_N      = $310E;
  173.     KEY_CTRL_O      = $180F;
  174.     KEY_CTRL_P      = $1910;
  175.     KEY_CTRL_Q      = $1011;
  176.     KEY_CTRL_R      = $1312;
  177.     KEY_CTRL_S      = $1F13;
  178.     KEY_CTRL_T      = $1414;
  179.     KEY_CTRL_U      = $1615;
  180.     KEY_CTRL_V      = $2F16;
  181.     KEY_CTRL_W      = $1117;
  182.     KEY_CTRL_X      = $2D18;
  183.     KEY_CTRL_Y      = $1519;
  184.     KEY_CTRL_Z      = $2C1A;
  185.     KEY_CTRL_LBRACK = $1A1B;
  186.     KEY_CTRL_BSLASH = $2B1C;
  187.     KEY_CTRL_RBRACK = $1B1D;
  188.     KEY_CTRL_CARET  = $071E;
  189.     KEY_CTRL_UNDER  = $0C1F;
  190.  
  191.     { codes for numeric keypad keys }
  192.  
  193.     KEY_NPAD_ASTER  = $372A;
  194.     KEY_NPAD_MINUS  = $4A2D;
  195.     KEY_NPAD_PLUS   = $4E2B;
  196.     KEY_NPAD_PERIOD = $532E;
  197.     KEY_NPAD_0      = $5230;
  198.     KEY_NPAD_1      = $4F31;
  199.     KEY_NPAD_2      = $5032;
  200.     KEY_NPAD_3      = $5133;
  201.     KEY_NPAD_4      = $4B34;
  202.     KEY_NPAD_5      = $4C35;
  203.     KEY_NPAD_6      = $4D36;
  204.     KEY_NPAD_7      = $4737;
  205.     KEY_NPAD_8      = $4838;
  206.     KEY_NPAD_9      = $4939;
  207.  
  208.     { codes for special keys (low byte zero) }
  209.  
  210.     KEY_UP          = $4800;
  211.     KEY_LEFT        = $4B00;
  212.     KEY_RIGHT       = $4D00;
  213.     KEY_DOWN        = $5000;
  214.     KEY_HOME        = $4700;
  215.     KEY_END         = $4F00;
  216.     KEY_PGUP        = $4900;
  217.     KEY_PGDN        = $5100;
  218.     KEY_INSERT      = $5200;
  219.     KEY_DELETE      = $5300;
  220.  
  221.     KEY_CTRL_LEFT   = $7300;
  222.     KEY_CTRL_RIGHT  = $7400;
  223.     KEY_CTRL_HOME   = $7700;
  224.     KEY_CTRL_END    = $7500;
  225.     KEY_CTRL_PGUP   = $8400;    { cannot appear in case label before TP 5.5 }
  226.     KEY_CTRL_PGDN   = $7600;
  227.  
  228.     KEY_F1          = $3B00;
  229.     KEY_F2          = $3C00;
  230.     KEY_F3          = $3D00;
  231.     KEY_F4          = $3E00;
  232.     KEY_F5          = $3F00;
  233.     KEY_F6          = $4000;
  234.     KEY_F7          = $4100;
  235.     KEY_F8          = $4200;
  236.     KEY_F9          = $4300;
  237.     KEY_F10         = $4400;
  238.  
  239.     KEY_SHFT_F1     = $5400;
  240.     KEY_SHFT_F2     = $5500;
  241.     KEY_SHFT_F3     = $5600;
  242.     KEY_SHFT_F4     = $5700;
  243.     KEY_SHFT_F5     = $5800;
  244.     KEY_SHFT_F6     = $5900;
  245.     KEY_SHFT_F7     = $5A00;
  246.     KEY_SHFT_F8     = $5B00;
  247.     KEY_SHFT_F9     = $5C00;
  248.     KEY_SHFT_F10    = $5D00;
  249.  
  250.     KEY_CTRL_F1     = $5E00;
  251.     KEY_CTRL_F2     = $5F00;
  252.     KEY_CTRL_F3     = $6000;
  253.     KEY_CTRL_F4     = $6100;
  254.     KEY_CTRL_F5     = $6200;
  255.     KEY_CTRL_F6     = $6300;
  256.     KEY_CTRL_F7     = $6400;
  257.     KEY_CTRL_F8     = $6500;
  258.     KEY_CTRL_F9     = $6600;
  259.     KEY_CTRL_F10    = $6700;
  260.  
  261.     KEY_ALT_F1      = $6800;
  262.     KEY_ALT_F2      = $6900;
  263.     KEY_ALT_F3      = $6A00;
  264.     KEY_ALT_F4      = $6B00;
  265.     KEY_ALT_F5      = $6C00;
  266.     KEY_ALT_F6      = $6D00;
  267.     KEY_ALT_F7      = $6E00;
  268.     KEY_ALT_F8      = $6F00;
  269.     KEY_ALT_F9      = $7000;
  270.     KEY_ALT_F10     = $7100;
  271.  
  272.     KEY_ALT_1       = $7800;
  273.     KEY_ALT_2       = $7900;
  274.     KEY_ALT_3       = $7A00;
  275.     KEY_ALT_4       = $7B00;
  276.     KEY_ALT_5       = $7C00;
  277.     KEY_ALT_6       = $7D00;
  278.     KEY_ALT_7       = $7E00;
  279.     KEY_ALT_8       = $7F00;
  280.     KEY_ALT_9       = $8000;    { cannot appear in case label before TP 5.5 }
  281.     KEY_ALT_0       = $8100;    { cannot appear in case label before TP 5.5 }
  282.     KEY_ALT_MINUS   = $8200;    { cannot appear in case label before TP 5.5 }
  283.     KEY_ALT_EQUAL   = $8300;    { cannot appear in case label before TP 5.5 }
  284.  
  285.     KEY_ALT_Q       = $1000;
  286.     KEY_ALT_W       = $1100;
  287.     KEY_ALT_E       = $1200;
  288.     KEY_ALT_R       = $1300;
  289.     KEY_ALT_T       = $1400;
  290.     KEY_ALT_Y       = $1500;
  291.     KEY_ALT_U       = $1600;
  292.     KEY_ALT_I       = $1700;
  293.     KEY_ALT_O       = $1800;
  294.     KEY_ALT_P       = $1900;
  295.  
  296.     KEY_ALT_A       = $1E00;
  297.     KEY_ALT_S       = $1F00;
  298.     KEY_ALT_D       = $2000;
  299.     KEY_ALT_F       = $2100;
  300.     KEY_ALT_G       = $2200;
  301.     KEY_ALT_H       = $2300;
  302.     KEY_ALT_J       = $2400;
  303.     KEY_ALT_K       = $2500;
  304.     KEY_ALT_L       = $2600;
  305.  
  306.     KEY_ALT_Z       = $2C00;
  307.     KEY_ALT_X       = $2D00;
  308.     KEY_ALT_C       = $2E00;
  309.     KEY_ALT_V       = $2F00;
  310.     KEY_ALT_B       = $3000;
  311.     KEY_ALT_N       = $3100;
  312.     KEY_ALT_M       = $3200;
  313.  
  314.     KEY_SHFT_TAB    = $0F00;
  315.     KEY_CTRL_BREAK  = $0000;
  316.     KEY_CTRL_PRTSC  = $7200;
  317.  
  318.     {   Here is a workaround for keycodes in case statement labels
  319.         prior to TP 5.5.  Note that keycodes are normally unsigned
  320.         integers.
  321.  
  322.         o   In TP 4.0 and 5.0, an unsigned integer constant greater
  323.             than 32767 (most significant bit set) may not appear in
  324.             case statement labels, which are signed two-byte integers
  325.             and must be in the range -32768 to 32767.
  326.  
  327.         o   Starting with TP 5.5, negative values in case statement
  328.             labels are out of range when the case statement expression
  329.             is unsigned.
  330.  
  331.         The following five constants are for case statement labels
  332.         prior to TP 5.5.
  333.     }
  334.  
  335.     KEY_CTRL_PGUP_  = -31744;
  336.     KEY_ALT_9_      = -32768;
  337.     KEY_ALT_0_      = -32512;
  338.     KEY_ALT_MINUS_  = -32256;
  339.     KEY_ALT_EQUAL_  = -32000;
  340.  
  341. procedure key_clear;
  342. function key_next : word;
  343. function key_press : boolean;
  344. function key_read : word;
  345.  
  346. procedure key_break;
  347. {$f+}
  348. procedure key_reset;
  349. {$f-}
  350.  
  351. implementation
  352.  
  353. {////////////////////////////////////////////////////////////////////}
  354. uses dos;
  355.  
  356. const
  357.     KEY_INTKEY  = $16;          { BIOS keyboard services interrupt }
  358.     KEY_INTOFF  = $FA;          { disable interrupts }
  359.     KEY_INTON   = $FB;          { enable interrupts }
  360.     key_iret    : word = $CF;   { an IRET instruction }
  361.  
  362. var
  363.     key_saveint : pointer;      { save interrupt 1B }
  364.     key_savepro : pointer;      { save turbo pascal exit procedure address }
  365.  
  366. {/////////////////////////////////////////////////////////////////////
  367. key_clear       -- Flush the keyboard buffer
  368.  
  369. This routine removes all remaining unread keycodes from the keyboard
  370. buffer.  It is not an error if the keyboard buffer is already empty.
  371.  
  372. }
  373. procedure key_clear;
  374.  
  375. var
  376.     head        : integer absolute $0040:$001A;
  377.     tail        : integer absolute $0040:$001C;
  378.  
  379. begin
  380.     { disable interrupts while modifying the BIOS keyboard buffer }
  381.     inline (KEY_INTOFF);
  382.     tail := head;
  383.     inline (KEY_INTON);
  384. end;
  385.  
  386. {/////////////////////////////////////////////////////////////////////
  387. key_next        -- Return the next keycode if there is one
  388.  
  389. There are two cases:
  390.  
  391.     o   If the BIOS keyboard buffer is not empty, the function returns
  392.         the oldest unread keycode currently stored in the BIOS
  393.         keyboard buffer.
  394.  
  395.     o   If the BIOS keyboard buffer is empty, the function returns
  396.         KEY_NIL.
  397.  
  398. }
  399. function key_next : word;
  400.  
  401. var
  402.     regs        : registers;
  403.  
  404. begin
  405.     regs.flags := 0;
  406.     regs.ax := $0100;
  407.     intr (KEY_INTKEY, regs);
  408.     key_next := KEY_NIL;
  409.     if (regs.flags and $40) = 0 then
  410.         key_next := regs.ax;
  411. end;
  412.  
  413. {/////////////////////////////////////////////////////////////////////
  414. key_press       -- Return TRUE if the keyboard buffer is not empty
  415.  
  416. This routine returns TRUE if the keyboard buffer is not empty,
  417. otherwise FALSE.
  418.  
  419. }
  420. function key_press : boolean;
  421.  
  422. var
  423.     regs        : registers;
  424.  
  425. begin
  426.     regs.flags := 0;
  427.     regs.ax := $0100;
  428.     intr (KEY_INTKEY, regs);
  429.     key_press := (regs.flags and $40) = 0;
  430. end;
  431.  
  432. {/////////////////////////////////////////////////////////////////////
  433. key_read        -- Read and return the next keycode
  434.  
  435. This routine reads and returns the next unread keycode.  If the BIOS
  436. keyboard is empty, the routine waits for the user to press a key.
  437.  
  438. }
  439. function key_read : word;
  440.  
  441. var
  442.     regs        : registers;
  443.  
  444. begin
  445.     regs.ax := 0;
  446.     intr (KEY_INTKEY, regs);
  447.     key_read := regs.ax;
  448. end;
  449.  
  450. {/////////////////////////////////////////////////////////////////////
  451. key_break       -- Install an ISR for CTRL-BREAK
  452.  
  453. This routine installs an ISR (interrupt service routine) for the
  454. CTRL-BREAK interrupt so that when the user presses CTRL-BREAK the
  455. program does not halt and it does not echo ^C.  The routine also
  456. installs a Turbo Pascal exit routine to reset the previous CTRL-BREAK
  457. ISR address.
  458.  
  459. The ISR that this routine installs is simply a pointer to an IRET
  460. instruction.  When the user presses CTRL-BREAK, BIOS still clears the
  461. keyboard and inserts into the keyboard four bytes of zero.  The
  462. program can check for CTRL-BREAK like this:
  463.  
  464.         if key_next = 0 then
  465.             ... CTRL-BREAK was pressed ...
  466.  
  467. }
  468. procedure key_break;
  469.  
  470. begin
  471.     getintvec ($1B, key_saveint);
  472.     key_savepro := exitproc;
  473.  
  474.     inline (KEY_INTOFF);
  475.     setintvec ($1B, @key_iret);
  476.     exitproc := @key_reset;
  477.     inline (KEY_INTON);
  478. end;
  479.  
  480. {/////////////////////////////////////////////////////////////////////
  481. key_reset       -- Remove the CTRL-BREAK ISR installed by key_break
  482.  
  483. This routine removes the ISR installed by the key_break routine.  This
  484. routine must not be called before calling key_break.  It is usually
  485. not necessary to call this routine explicitly, because key_break
  486. installs this routine as an exit routine.  However, this routine
  487. should be called before spawning a subprocess, as in this example:
  488.  
  489.         key_reset;
  490.         errcode := execute_program (progname, progargs);
  491.         key_break;
  492.  
  493. }
  494. {$f+}
  495. procedure key_reset;
  496. {$f-}
  497.  
  498. begin
  499.     inline (KEY_INTOFF);
  500.     setintvec ($1B, key_saveint);
  501.     exitproc := key_savepro;
  502.     inline (KEY_INTON);
  503. end;
  504.  
  505. end.
  506.