keyclick.pas 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. { This example demonstrates how to chain to a hardware interrupt.
  2. In more detail, it hooks the keyboard interrupt, calls a user
  3. procedure which in this case simply turns the PC speaker on and off.
  4. Then the old interrupt is called.
  5. }
  6. {$ASMMODE ATT}
  7. {$MODE FPC}
  8. uses
  9. crt,
  10. go32;
  11. const
  12. { keyboard is IRQ 1 -> interrupt 9 }
  13. kbdint = $9;
  14. var
  15. { holds old PM interrupt handler address }
  16. oldint9_handler : tseginfo;
  17. { new PM interrupt handler }
  18. newint9_handler : tseginfo;
  19. { pointer to interrupt handler }
  20. clickproc : pointer;
  21. { the data segment selector }
  22. backupDS : Word; external name '___v2prt0_ds_alias';
  23. { interrupt handler }
  24. procedure int9_handler; assembler;
  25. asm
  26. cli
  27. { save all registers, because we don't know which the compiler
  28. uses for the called procedure }
  29. pushl %ds
  30. pushl %es
  31. pushl %fs
  32. pushl %gs
  33. pushal
  34. { set up to call a FPC procedure }
  35. movw %cs:backupDS, %ax
  36. movw %ax, %ds
  37. movw %ax, %es
  38. movw dosmemselector, %ax
  39. movw %ax, %fs
  40. { call user procedure }
  41. call *clickproc
  42. { restore all registers }
  43. popal
  44. popl %gs
  45. popl %fs
  46. popl %es
  47. popl %ds
  48. { note: in go32v2 mode %cs=%ds=%es !!!}
  49. ljmp %cs:oldint9_handler { call old handler }
  50. { we don't need to do anything more, because the old interrupt
  51. handler does this for us (send EOI command, iret, sti...) }
  52. end;
  53. { dummy procedure to retrieve exact length of handler, for locking
  54. and unlocking functions }
  55. procedure int9_dummy; begin end;
  56. { demo user procedure, simply clicks on every keypress }
  57. procedure clicker;
  58. begin
  59. sound(500); delay(10); nosound;
  60. end;
  61. { dummy procedure to retrieve exact length of user procedure for
  62. locking and unlocking functions }
  63. procedure clicker_dummy; begin end;
  64. { installs our new handler }
  65. procedure install_click;
  66. begin
  67. clickproc := @clicker;
  68. { lock used code and data }
  69. lock_data(clickproc, sizeof(clickproc));
  70. lock_data(dosmemselector, sizeof(dosmemselector));
  71. lock_code(@clicker,
  72. longint(@clicker_dummy) - longint(@clicker));
  73. lock_code(@int9_handler,
  74. longint(@int9_dummy)-longint(@int9_handler));
  75. { fill in new handler's 48 bit pointer }
  76. newint9_handler.offset := @int9_handler;
  77. newint9_handler.segment := get_cs;
  78. { get old PM interrupt handler }
  79. get_pm_interrupt(kbdint, oldint9_handler);
  80. { set the new interrupt handler }
  81. set_pm_interrupt(kbdint, newint9_handler);
  82. end;
  83. { deinstalls our interrupt handler }
  84. procedure remove_click;
  85. begin
  86. { set old handler }
  87. set_pm_interrupt(kbdint, oldint9_handler);
  88. { unlock used code & data }
  89. unlock_data(dosmemselector, sizeof(dosmemselector));
  90. unlock_data(clickproc, sizeof(clickproc));
  91. unlock_code(@clicker,
  92. longint(@clicker_dummy)-longint(@clicker));
  93. unlock_code(@int9_handler,
  94. longint(@int9_dummy)-longint(@int9_handler));
  95. end;
  96. var
  97. ch : char;
  98. begin
  99. install_click;
  100. Writeln('Enter any message. Press return when finished');
  101. while (ch <> #13) do begin
  102. ch := readkey; write(ch);
  103. end;
  104. remove_click;
  105. end.