TIMERA.ASM 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. ;
  2. ; Command & Conquer Red Alert(tm)
  3. ; Copyright 2025 Electronic Arts Inc.
  4. ;
  5. ; This program is free software: you can redistribute it and/or modify
  6. ; it under the terms of the GNU General Public License as published by
  7. ; the Free Software Foundation, either version 3 of the License, or
  8. ; (at your option) any later version.
  9. ;
  10. ; This program is distributed in the hope that it will be useful,
  11. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ; GNU General Public License for more details.
  14. ;
  15. ; You should have received a copy of the GNU General Public License
  16. ; along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ;
  18. ;***************************************************************************
  19. ;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S **
  20. ;***************************************************************************
  21. ;* *
  22. ;* Project Name : Timer stuff *
  23. ;* *
  24. ;* File Name : TIMERA.ASM *
  25. ;* *
  26. ;* Programmer : Scott K. Bowen *
  27. ;* *
  28. ;* Start Date : July 6, 1994 *
  29. ;* *
  30. ;* Last Update : March 14, 1995 [PWG] *
  31. ;* *
  32. ;*-------------------------------------------------------------------------*
  33. ;* Functions: *
  34. ;* Get_RM_Timer_Address -- Return address of real mode code for copy. *
  35. ;* Get_RM_Timer_Size -- return size of real mode timer code. *
  36. ;* Increment_Tick_Count -- Increments WW system timer. *
  37. ;* Timer_Interrupt -- Temp routine to mimic a timer system calling our. *
  38. ;* Install_Timer_Interrupt -- Installs the timer interrupt routine. *
  39. ;* Remove_Timer_Interrupt -- Removes the timer interrupt vectors. **
  40. ;* Set_Timer_Frequency -- Set the frequency of the timer. *
  41. ;* Set_Timer_Rate -- Set the rate of the timer. *
  42. ;* Get_System_Tick_Count -- Returns the system tick count. *
  43. ;* Get_User_Tick_Count -- Get tick count of user clock. *
  44. ;* Increment_Timers -- Increments system and user timers. *
  45. ;* Get_Num_Interrupts -- Returns the number of interrupts that have occured*
  46. ;* Timer_Interrupt_Func -- Handles core timer code *
  47. ;* Disable_Timer_Interrupt -- Disables at the hardware level *
  48. ;* Enable_Timer_Interrupt -- Enables at the hardware level *
  49. ;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
  50. IDEAL
  51. P386
  52. MODEL USE32 FLAT
  53. DPMI_INTR equ 31h
  54. TRUE equ 1 ; Boolean 'true' value
  55. FALSE equ 0 ; Boolean 'false' value
  56. LOCALS ??
  57. ;//////////////////////////////////////////////////////////////////////////////////////
  58. ;///////////////////////////////////// Equates ////////////////////////////////////////
  59. SYSTEM_TIMER_FREQ equ 60 ; Frequency of system timer.
  60. ;********************************************************************
  61. ; There are two ways to call our interrupt chain in protected mode.
  62. ; The obvious way it to call the address that we replaced in the
  63. ; PM interrupt chain. This method is a little difficult but works and
  64. ; should always work.
  65. ;********************************************************************
  66. ; The RM and PM interrupts can be installed at the same time or seperately.
  67. ; Installing at the same time is the best method, the other method
  68. ; can be used to faciliated debugging when you only want one or the other
  69. ; called. Both methods work.
  70. ; -SKB July 21, 1994.
  71. INSTALL_SEPERATE equ FALSE
  72. INTCHIP0 EQU 20h ; 8259 interrupt chip controller 0
  73. CLEARISR EQU 20h ; Value to write to 8259 to reenable interrupts.
  74. IRQ0INTNUM EQU 08h ; IRQ0 interrupt vector number.
  75. DOS_SYS_CALL EQU 21h ; to do TNT DOS-XNDR system calls.
  76. LOCK_PAGES EQU 5 ; Lock pages subfunction using DX_MEM_MGT
  77. UNLOCK_PAGES EQU 6 ; Unlock pages subfunction using DX_MEM_MGT
  78. TIMER_CONST EQU 1193180 ; TIMER_CONST / FREQ = rate to set 8259 chip
  79. TIMERCMDREG EQU 043H ; timer command register port.
  80. TIMER0PORT EQU 040H ; timer channel 0 port
  81. TIMETYPE EQU 036H ; type of timer.
  82. ; 36H = 0011 0110
  83. ; -- select channel 0
  84. ; -- read/load low byte then high byte for timer
  85. ; --- timer mode 3
  86. ; - 0 - binary , 1 - BCD data
  87. ;//////////////////////////////////////////////////////////////////////////////////////
  88. ;///////////////////////////////////// Structs ////////////////////////////////////////
  89. ; Structure of memory in real mode handler.
  90. ; This is at the very start of the real mode code.
  91. ; This information may not change unless the real mode version is also changed.
  92. STRUC TimerType
  93. ; For speed, PM uses a DD while RM used DW
  94. ; For speed, SysRate and SysError are DD in PM and are DW in real mode.
  95. TrueRate DD ? ; True rate of clock. (only use word)
  96. SysTicks DD ? ; Tick count of timer.
  97. SysRate DD ? ; Desired rate of timer.
  98. SysError DD ? ; Amount of error in clock rate for desired frequency.
  99. SysCurRate DW ? ; Keeps track of when to increment timer.
  100. SysCurError DW ? ; Keeps track of amount of error in timer.
  101. UserTicks DD ? ; Tick count of timer.
  102. UserRate DD ? ; Desired rate of timer.
  103. UserError DD ? ; Amount of error in clock rate for desired frequency.
  104. UserCurRate DW ? ; Keeps track of when to increment timer.
  105. UserCurError DW ? ; Keeps track of amount of error in timer.
  106. DosAdder DW ? ; amount to add to DosFraction each interrupt.
  107. DosFraction DW ? ; Call dos when overflowed.
  108. OldRMI DD ? ; The origianl RM interrupt seg:off.
  109. OldPMIOffset DD ? ; The origianl PM interrupt offset
  110. OldPMISelector DD ? ; The original PM interrupt segment.
  111. CodeOffset DW ? ; Offset of the code in the RM stuff.
  112. CallRMIntOffset DW ? ; Offset of function to call DOS timer interrupt.
  113. CallRMIntAddr DD ? ; PM address of CallRealIntOffset for speed.
  114. PMIssuedInt DD 0 ; PM signals RM to just call Int chain.
  115. ; These are just used for information on testing. When all is done, they can
  116. ; be removed, but why? The don't add too much proccessing time and can
  117. ; be useful.
  118. NumPMInts DD ? ; Number of PM interrupts
  119. NumRMInts DD ? ; Number of RM interrupts.
  120. ENDS
  121. ;//////////////////////////////////////////////////////////////////////////////////////
  122. ;/////////////////////////////////// Prototypes //////////////////////////////////////
  123. GLOBAL Get_System_Tick_Count:NEAR
  124. GLOBAL Get_User_Tick_Count:NEAR
  125. GLOBAL Get_RM_Timer_Address:NEAR
  126. GLOBAL Get_RM_Timer_Size:NEAR
  127. GLOBAL Set_Timer_Frequency:NEAR
  128. GLOBAL Timer_Interrupt:NEAR
  129. GLOBAL Install_Timer_Interrupt:NEAR
  130. GLOBAL Remove_Timer_Interrupt:NEAR
  131. GLOBAL Get_Num_Interrupts:NEAR
  132. GLOBAL Timer_Interrupt_Func:FAR
  133. GLOBAL Disable_Timer_Interrupt:NEAR
  134. GLOBAL Enable_Timer_Interrupt:NEAR
  135. ;//////////////////////////////////////////////////////////////////////////////////////
  136. ;//////////////////////////////////////// Data ////////////////////////////////////////
  137. DATASEG
  138. ; The current time we will just include the real mode stuff
  139. ; into the protected mode code and then copy it down. The C side of
  140. ; this will handle this method or reading it off of disk in the real
  141. ; method.
  142. LABEL RealBinStart BYTE
  143. include "timereal.ibn"
  144. LABEL RealBinEnd BYTE
  145. LABEL LockedDataStart BYTE
  146. RealModeSel DD 0
  147. RealModePtr DD 0 ; Pointer to real mode memory.
  148. RealModeSize DD 0 ; Pointer to real mode memory.
  149. LABEL LockedDataEnd BYTE
  150. InitFlags DD 0 ; Flags to indicate what has been initialized.
  151. ; InitFlags that are set to have a fully functional interrupt.
  152. IF_ALLOC_RM equ 1h ; Allocation of RM was successful.
  153. IF_SET_VECTORS equ 2h ; Vectors have been set.
  154. IF_LOCKED_PM_CODE equ 4h ; Locked PM code for DPMI.
  155. IF_LOCKED_PM_DATA equ 8h ; Locked PM code for DPMI.
  156. IF_RATE_CHANGE equ 10h ; Timer rate was changed.
  157. IF_FUNCTIONAL equ 20h ; Timer is in and functional.
  158. IF_LOCKED_RM_CODE equ 40h ; Timer is in and functional.
  159. ;//////////////////////////////////////////////////////////////////////////////////////
  160. ;//////////////////////////////////////// Code ////////////////////////////////////////
  161. CODESEG
  162. ;//////////////////////////////////////////////////////////////////////////////////////
  163. ;/////////////////////////////// Init routines ////////////////////////////////////////
  164. ;***************************************************************************
  165. ;* GET_RM_TIMER_ADDRESS -- Return address of real mode code for copy. *
  166. ;* *
  167. ;* *
  168. ;* *
  169. ;* INPUT: *
  170. ;* *
  171. ;* OUTPUT: *
  172. ;* *
  173. ;* WARNINGS: *
  174. ;* *
  175. ;* HISTORY: *
  176. ;* 07/06/1994 SKB : Created. *
  177. ;*=========================================================================*
  178. PROC Get_RM_Timer_Address C Near
  179. mov eax, OFFSET RealBinStart
  180. ret
  181. ENDP
  182. ;***************************************************************************
  183. ;* GET_RM_TIMER_SIZE -- return size of real mode timer code. *
  184. ;* *
  185. ;* *
  186. ;* *
  187. ;* INPUT: *
  188. ;* *
  189. ;* OUTPUT: *
  190. ;* *
  191. ;* WARNINGS: *
  192. ;* *
  193. ;* HISTORY: *
  194. ;* 07/06/1994 SKB : Created. *
  195. ;*=========================================================================*
  196. PROC Get_RM_Timer_Size C Near
  197. mov eax, OFFSET RealBinEnd - OFFSET RealBinStart
  198. ret
  199. ENDP
  200. ;***************************************************************************
  201. ;* SET_TIMER_RATE -- Set the rate of the timer. *
  202. ;* *
  203. ;* *
  204. ;* INPUT: ebx = rate to set timer were rate = 1193180 / freq *
  205. ;* *
  206. ;* OUTPUT: none *
  207. ;* *
  208. ;* WARNINGS: *
  209. ;* *
  210. ;* HISTORY: *
  211. ;* 07/11/1994 SKB : Created. *
  212. ;*=========================================================================*
  213. PROC Set_Timer_Rate Near
  214. push eax
  215. pushf ; to save int enable flag
  216. cli ; disable interupts while setting up swapper
  217. mov al,TIMETYPE ; setup to modify timer 0
  218. out TIMERCMDREG,al ; send command.
  219. mov eax,ebx ; get rate.
  220. out TIMER0PORT,al ; output low byte
  221. mov al,ah
  222. out TIMER0PORT,al ; output high byte
  223. sti
  224. popf ; get int enable flag.
  225. pop eax
  226. ret
  227. ENDP Set_Timer_Rate
  228. ;***************************************************************************
  229. ;* SET_TIMER_FREQUENCY -- Set the frequency of the timer. *
  230. ;* *
  231. ;* *
  232. ;* INPUT: INT Frequency of user timer. *
  233. ;* *
  234. ;* OUTPUT: *
  235. ;* *
  236. ;* WARNINGS: *
  237. ;* *
  238. ;* *
  239. ;* HISTORY: *
  240. ;* 07/06/1994 SKB : Created. *
  241. ;*=========================================================================*
  242. PROC Set_Timer_Frequency C NEAR
  243. USES eax,ebx,ecx,edx
  244. ARG freq:DWORD
  245. LOCAL clockrate:DWORD
  246. LOCAL clockfreq:DWORD
  247. test [InitFlags],IF_FUNCTIONAL ; Is our timer system installed?
  248. jz ??timer_not_installed ; if not, this is not legal.
  249. ; Find out the greater of the frequencies (user or system.)
  250. ; Assign the True rate value based of of that.
  251. ; store the max frequency in ecx.
  252. mov ecx,[freq] ; get user frequency.
  253. cmp ecx,SYSTEM_TIMER_FREQ ; compare it with system frequency
  254. jg ??user_is_fastest ; is user frequency faster?
  255. mov ecx,SYSTEM_TIMER_FREQ ; no, set clock freq to system frequency.
  256. ??user_is_fastest:
  257. ; now get the rate that the clock will be set at.
  258. ; ecx is still max frequency.
  259. mov esi,[RealModePtr]
  260. mov eax,TIMER_CONST ; get the clock constant value.
  261. xor edx,edx ; zero for divide.
  262. div ecx ; rate = TC/freq => eax = eax/ecx;
  263. mov [(TimerType PTR esi).TrueRate],eax ; Set our true rate.
  264. mov ebx,eax ; save for later. DO NOT USE UNTIL CALL
  265. ; Set up variables to call DOS correctly.
  266. ; When DosFraction overflows, DOS is called.
  267. mov [(TimerType PTR esi).DosAdder],ax ; Init count to until call dos.
  268. mov [(TimerType PTR esi).DosFraction],0 ; init the fraction.
  269. ; now set up the system timer.
  270. mov ecx,SYSTEM_TIMER_FREQ ; get frequency.
  271. mov eax,TIMER_CONST ; get constant for formula rate=C/freq.
  272. xor edx,edx ; make sure zero for divide
  273. div ecx ; calculate rate
  274. mov [(TimerType PTR esi).SysCurRate],ax ; Init current stuff.
  275. mov [(TimerType PTR esi).SysCurError],ax ; Init current stuff.
  276. mov [(TimerType PTR esi).SysRate],eax ; Save rate of timer
  277. mov [(TimerType PTR esi).SysError],edx ; Save error of timer
  278. ; Do not set SysTicks to zero since it always has the same frequency. It
  279. ; should be zero out only when the system clock is installed.
  280. ; now set up the user timer.
  281. mov ecx,[freq] ; get frequency of user timer.
  282. mov eax,TIMER_CONST ; get constant for formula rate=C/freq.
  283. xor edx,edx ; make sure zero for divide
  284. div ecx ; calculate rate
  285. mov [(TimerType PTR esi).UserCurRate],ax ; Init current stuff.
  286. mov [(TimerType PTR esi).UserCurError],ax; Init current stuff.
  287. mov [(TimerType PTR esi).UserRate],eax ; Save rate of timer
  288. mov [(TimerType PTR esi).UserError],edx ; Save error of timer
  289. mov [(TimerType PTR esi).UserTicks],0 ; User timer sets to zero when freq change.
  290. ; Call function to set the rate of the chip, ebx = rate from above.
  291. call Set_Timer_Rate
  292. ??timer_not_installed:
  293. ret
  294. ENDP
  295. ;***************************************************************************
  296. ;* INSTALL_TIMER_Interrupt -- Installs the timer interrupt routine. *
  297. ;* *
  298. ;* *
  299. ;* INPUT: VOID * pointer to RM binary in PM memory. *
  300. ;* LONG Size of RM binary. *
  301. ;* INT frequency of user timer. *
  302. ;* *
  303. ;* OUTPUT: *
  304. ;* *
  305. ;* WARNINGS: *
  306. ;* *
  307. ;* HISTORY: *
  308. ;* 07/06/1994 SKB : Created. *
  309. ;*=========================================================================*
  310. PROC Install_Timer_Interrupt C Near
  311. USES ebx,ecx,edx
  312. ARG rm_ptr:DWORD
  313. ARG rm_size:DWORD
  314. ARG freq:DWORD
  315. ARG partial:DWORD
  316. ; Are they attempting to set timer again?
  317. cmp [RealModePtr],0
  318. jnz ??error
  319. ; Make sure all flags are cleared.
  320. cmp [InitFlags],0
  321. jnz ??error
  322. ; Before setting the interrupt vectors, the code needs to be locked
  323. ; for DPMI compatability. Any code or data accessed must be lockded
  324. ; so that no page faults accure during an interrupt.
  325. ; First lock the code, then the data. The stack will already be locked.
  326. ; The real mode code is also already locked be default.
  327. ; To lock a page set up registers :
  328. ; AX = 0600h
  329. ; BX:CX = starting linear address of memory block
  330. ; SI:DI = size of region
  331. mov eax,0600h ; function number.
  332. mov ecx,OFFSET LockedCodeStart ; ecx must have start of memory.
  333. mov edi,OFFSET LockedCodeEnd ; edi will have size of region in bytes.
  334. shld ebx,ecx,16
  335. sub edi, ecx
  336. shld esi,edi,16
  337. int DPMI_INTR ; do call.
  338. jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
  339. or [InitFlags],IF_LOCKED_PM_CODE
  340. mov eax,0600h ; function number.
  341. mov ecx,OFFSET LockedDataStart ; ecx must have start of memory.
  342. mov edi,OFFSET LockedDataEnd ; edi will have size of region in bytes.
  343. shld ebx,ecx,16
  344. sub edi, ecx
  345. shld esi,edi,16
  346. int DPMI_INTR ; do call.
  347. jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
  348. or [InitFlags],IF_LOCKED_PM_DATA
  349. ; now allocate real mode memory and copy the rm binary down to it.
  350. mov eax,0100h ; set function number
  351. mov ebx,[rm_size] ; get size of RM binary.
  352. add ebx,15 ; round up
  353. shr ebx,4 ; convert to pages.
  354. int DPMI_INTR ; do call.
  355. jc ??error ; check for error.
  356. mov [ RealModeSel ] , edx
  357. shl eax,4 ; convert segment to offset.
  358. mov [RealModePtr],eax ; save offset to global variable.
  359. ; now lock the real mode memory that we allocated, just in
  360. ; case it needs to be.
  361. mov eax,0600h ; function number.
  362. mov ecx,[RealModePtr] ; ecx must have start of memory.
  363. mov edi,[rm_size] ; edi will have size of region in bytes.
  364. mov [RealModeSize],edi ; save off the size for the unlock
  365. shld ebx,ecx,16
  366. shld esi,edi,16
  367. int DPMI_INTR ; do call.
  368. jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
  369. or [InitFlags],IF_LOCKED_RM_CODE
  370. ; set up source and destination pointers for the copy.
  371. mov eax,[RealModePtr]
  372. mov esi,[rm_ptr] ; Set up our source pointer.
  373. or [InitFlags],IF_ALLOC_RM ; set successful
  374. mov edi,eax ; put it into esi for copy.
  375. mov ecx,[rm_size]
  376. rep movsb ; write RM bin to RM memory.
  377. ; restore esi to point to data and initialize some of it.
  378. mov esi,[RealModePtr]
  379. mov eax,esi ; get real mode 32 offset.
  380. shl eax,12 ; make seg in high eax.
  381. mov ax,[(TimerType PTR esi).CallRMIntOffset] ; create RM addr of call chain.
  382. mov [(TimerType PTR esi).CallRMIntAddr],eax ; save it for use in PM int.
  383. cmp [partial],0
  384. jne ??partial_exit
  385. ;==========================================================================
  386. ; Get the protected mode interrupt vector keyboard.
  387. ; input ax = 0204
  388. ; bl = number of interrupt to get
  389. ; output: cf error
  390. ;==========================================================================
  391. mov eax,0204h ; Get proteced mode
  392. mov bl,IRQ0INTNUM ; IRQ1 interrupt vector
  393. int DPMI_INTR ; do call.
  394. jc ??error
  395. mov [(TimerType PTR esi).OldPMIOffset],edx ; save offset.
  396. mov [(TimerType PTR esi).OldPMISelector],ecx ; save selector.
  397. ;==========================================================================
  398. ; Get the real mode interrupt vector keyboard
  399. ; input ax = 2503, cl = number of interrupt to get
  400. ; output cf error, CX:DX = address (seg:off) of RM int handler routine.
  401. ; cl set above
  402. ;==========================================================================
  403. mov eax,0200h
  404. mov bl,IRQ0INTNUM ; IRQ1 interrupt vector
  405. int DPMI_INTR ; do call.
  406. jc ??error
  407. shl edx,16
  408. shld ecx,edx,16
  409. mov [(TimerType PTR esi).OldRMI],ecx
  410. ;==========================================================================
  411. ; only separate method of installation is posible.
  412. ; Now it is time to set the Protected mode interrupt Keyboard
  413. ; ax = function number (0205
  414. ; bl = number of interrupt to set
  415. ; cx:edx = address of PM interrupt handler
  416. ;==========================================================================
  417. mov eax, 0205h
  418. mov bl,IRQ0INTNUM
  419. mov cx , cs
  420. lea edx, [Timer_Interrupt] ; get address of protected code int hand.
  421. int DPMI_INTR ; do call.
  422. jc ??error
  423. or [InitFlags],IF_SET_VECTORS
  424. ;==========================================================================
  425. ; Now it is time to set the real Interrupt Keyboard
  426. ; ax = function number (0201
  427. ; bl = number of interrupt to set
  428. ; cx:dx = address of RM interrupt handler
  429. ;==========================================================================
  430. mov eax, 0201h
  431. mov bl,IRQ0INTNUM
  432. mov ecx,[RealModePtr] ; get address of real code int hand.
  433. shr ecx,4 ; put segment in hi word.
  434. mov dx,[(TimerType PTR esi).CodeOffset] ; Get address of code
  435. int DPMI_INTR ; do call.
  436. jc ??error
  437. or [InitFlags],IF_SET_VECTORS
  438. ; now set the frequency.
  439. mov eax,[freq]
  440. or [InitFlags],IF_FUNCTIONAL
  441. push eax
  442. call NEAR Set_Timer_Frequency
  443. mov [(TimerType PTR esi).SysTicks],0 ; Only place SysTicks in inited.
  444. mov [(TimerType PTR esi).UserTicks],0 ; Timers start off on same foot.
  445. pop eax
  446. or [InitFlags],IF_RATE_CHANGE
  447. ; we have finished with success.
  448. ??partial_exit:
  449. mov eax,1 ; signal success.
  450. ret
  451. ??error:
  452. xor eax,eax ; signal an error.
  453. ret
  454. ENDP
  455. ;***************************************************************************
  456. ;* REMOVE_TIMER_INTERRUPT -- Removes the timer interrupt vectors. *
  457. ;* *
  458. ;* *
  459. ;* *
  460. ;* INPUT: *
  461. ;* *
  462. ;* OUTPUT: *
  463. ;* *
  464. ;* WARNINGS: *
  465. ;* *
  466. ;* HISTORY: *
  467. ;* 07/06/1994 SKB : Created. *
  468. ;*=========================================================================*
  469. PROC Remove_Timer_Interrupt C NEAR
  470. USES ebx,ecx,edx
  471. ; check if timer was previosly install
  472. mov esi,[RealModePtr]
  473. test esi,esi
  474. jz ??error
  475. test [InitFlags],IF_SET_VECTORS
  476. jz ??vectors_not_set
  477. ;==========================================================================
  478. ; Now it is time to set the real Interrupt Keyboard
  479. ; ax = function number (0201
  480. ; bl = number of interrupt to set
  481. ; cx:dx = address of RM interrupt handler
  482. ;====================================================================
  483. mov eax, 0201h
  484. mov bl,IRQ0INTNUM
  485. mov edx,[(TimerType esi).OldRMI] ; get the RM address.
  486. shld ecx , edx , 16
  487. int DPMI_INTR ; do call.
  488. jc ??error
  489. ;==========================================================================
  490. ; Now it is time to set the Protected mode interrupt
  491. ; ax = function number (0205
  492. ; bl = number of interrupt to set
  493. ; cx:edx = address of PM interrupt handler
  494. ;==========================================================================
  495. mov eax, 0205h
  496. mov bl,IRQ0INTNUM
  497. mov ecx,[(TimerType esi).OldPMISelector] ; Get PM segment to put int ds later.
  498. mov edx,[(TimerType esi).OldPMIOffset] ; Get PM offset
  499. int DPMI_INTR ; do call.
  500. jc ??error
  501. ??vectors_not_set:
  502. ; Call function to set the rate of the chip, ebx = rate.
  503. test [InitFlags],IF_RATE_CHANGE ; was it changed?
  504. jz ??rate_not_changed
  505. mov ebx,0FFFFh ; back to 18.2 time per second.
  506. call Set_Timer_Rate ; call function to set timer.
  507. ??rate_not_changed:
  508. ; now free up the real mode memory.
  509. test [InitFlags],IF_LOCKED_RM_CODE
  510. jz ??rm_not_locked
  511. mov eax , 0601h
  512. mov ecx, [RealModePtr]
  513. mov edi, [RealModeSize]
  514. shld ebx , ecx , 16
  515. shld esi , edi , 16
  516. int DPMI_INTR ; do call.
  517. jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
  518. ??rm_not_locked:
  519. test [InitFlags],IF_ALLOC_RM
  520. jz ??mem_not_allocated
  521. mov eax , 0101h
  522. mov edx,[ RealModeSel ] ; get physical address of real mode buffer.
  523. int DPMI_INTR ; do call.
  524. jc ??error
  525. ??mem_not_allocated:
  526. ; Now we can unlock all stuff needed for the interrupt.
  527. ; Unlock code
  528. test [InitFlags],IF_LOCKED_PM_CODE
  529. jz ??code_not_locked
  530. mov eax , 0601h
  531. mov ecx,OFFSET LockedCodeStart ; ecx must have start of memory.
  532. mov edi,OFFSET LockedCodeEnd ; edx will have size of region in bytes.
  533. sub edi,ecx ; - figure size.
  534. shld ebx , ecx , 16
  535. shld esi , edi , 16
  536. int DPMI_INTR ; do call.
  537. jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
  538. ??code_not_locked:
  539. ; Unlock data
  540. test [InitFlags],IF_LOCKED_PM_DATA
  541. jz ??data_not_locked
  542. mov ax,0601h ; set es to descriptor of data.
  543. mov ecx,OFFSET LockedDataStart ; ecx must have start of memory.
  544. mov edi,OFFSET LockedDataEnd ; edx will have size of region in bytes.
  545. sub edi,ecx ; - figure size.
  546. shld ebx , ecx , 16
  547. shld esi , edi , 16
  548. int DPMI_INTR ; do call.
  549. jc ??error ; eax = 8 if mem err, eax = 9 if invalid mem region.
  550. ??data_not_locked:
  551. ; we have finished with success.
  552. mov eax,1 ; signal success.
  553. mov [RealModePtr],0 ; To say we can do it again sometime.
  554. mov [InitFlags],0 ; To say we can do it again sometime.
  555. ret
  556. ??error:
  557. xor eax,eax ; signal an error.
  558. ret
  559. ENDP
  560. ;//////////////////////////////////////////////////////////////////////////////////////
  561. ;/////////////////////////////// Access routines //////////////////////////////////////
  562. ;***************************************************************************
  563. ;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured*
  564. ;* *
  565. ;* INPUT: TRUE - returns num RM ints. *
  566. ;* FALSE - return num PM ints. *
  567. ;* *
  568. ;* OUTPUT: *
  569. ;* *
  570. ;* WARNINGS: *
  571. ;* *
  572. ;* HISTORY: *
  573. ;* 07/12/1994 SKB : Created. *
  574. ;*=========================================================================*
  575. PROC Get_Num_Interrupts C Near
  576. USES esi
  577. ARG realmode:DWORD
  578. mov esi,[RealModePtr]
  579. cmp [realmode],0
  580. je ??prot_mode
  581. mov eax,[(TimerType PTR esi).NumRMInts]
  582. ret
  583. ??prot_mode:
  584. mov eax,[(TimerType PTR esi).NumPMInts]
  585. ret
  586. ENDP
  587. ;***************************************************************************
  588. ;* GET_SYSTEM_TICK_COUNT -- Returns the system tick count. *
  589. ;* *
  590. ;* *
  591. ;* INPUT: *
  592. ;* *
  593. ;* OUTPUT: *
  594. ;* *
  595. ;* WARNINGS: *
  596. ;* *
  597. ;* HISTORY: *
  598. ;* 07/06/1994 SKB : Created. *
  599. ;*=========================================================================*
  600. PROC Get_System_Tick_Count C Near
  601. USES esi
  602. mov esi,[RealModePtr]
  603. mov eax,[(TimerType PTR esi).SysTicks]
  604. ret
  605. ENDP
  606. ;***************************************************************************
  607. ;* GET_USER_TICK_COUNT -- Get tick count of user clock. *
  608. ;* *
  609. ;* *
  610. ;* INPUT: *
  611. ;* *
  612. ;* OUTPUT: *
  613. ;* *
  614. ;* WARNINGS: *
  615. ;* *
  616. ;* HISTORY: *
  617. ;* 07/12/1994 SKB : Created. *
  618. ;*=========================================================================*
  619. PROC Get_User_Tick_Count C Near
  620. USES esi
  621. mov esi,[RealModePtr]
  622. mov eax,[(TimerType PTR esi).UserTicks]
  623. ret
  624. ENDP
  625. ;//////////////////////////////////////////////////////////////////////////////////////
  626. ;/////////////////////////////// Interrupt routines ///////////////////////////////////
  627. ; These macros are placed here to handle to duplicate code in 2 methods in
  628. ; the Timer_Interrupt function.
  629. MACRO SET_DS_ESI_TO_RM
  630. ; Sets DS : ES to point to DGROUP _DATA selector
  631. ; Set esi to point to RealModePtr
  632. ; Corrupts eax
  633. mov ax , _DATA
  634. mov ds , ax
  635. mov es , ax
  636. mov esi,[RealModePtr] ; Point to start of RM data.
  637. ENDM
  638. MACRO INCREMENT_TIMERS
  639. ; Expects ESI to point to real mode memory.
  640. inc [(TimerType PTR esi).NumPMInts] ; For testing.
  641. mov eax,[(TimerType PTR esi).TrueRate] ; Get the rate of the PC clock.
  642. sub [(TimerType PTR esi).SysCurRate],ax ; Sub from our rate counter.
  643. ja ??end_sys ; If !below zero, do not inc.
  644. mov ebx,[(TimerType PTR esi).SysRate] ; Get rate of timer.
  645. mov ecx,[(TimerType PTR esi).SysError] ; Get amount of error.
  646. add [(TimerType PTR esi).SysCurRate],bx ; Add rate to the current.
  647. sub [(TimerType PTR esi).SysCurError],cx ; Subtract err from error count.
  648. jb ??error_adj_sys ; If wrapped don't inc.
  649. inc [(TimerType PTR esi).SysTicks] ; increment the timer.
  650. jmp short ??end_sys
  651. ??error_adj_sys:
  652. add [(TimerType PTR esi).SysCurError],bx ; reajust the error by timer rate.
  653. ??end_sys:
  654. sub [(TimerType PTR esi).UserCurRate],ax ; Sub from our rate counter.
  655. ja ??end_user ; If !below zero, do not inc.
  656. mov ebx,[(TimerType PTR esi).UserRate] ; Get rate of timer.
  657. mov ecx,[(TimerType PTR esi).UserError] ; Get amount of error.
  658. add [(TimerType PTR esi).UserCurRate],bx ; Add rate to the current.
  659. sub [(TimerType PTR esi).UserCurError],cx; Subtract err from error count.
  660. jb ??error_adj_user ; If wrapped don't inc.
  661. inc [(TimerType PTR esi).UserTicks] ; increment the timer.
  662. jmp short ??end_user
  663. ??error_adj_user:
  664. add [(TimerType PTR esi).UserCurError],bx; reajust the error by timer rate.
  665. ??end_user:
  666. ENDM
  667. MACRO ENABLE_CLOCK_INT
  668. ; Signal 8259 that we are done and that it can bug us again.
  669. ; Corrupts al.
  670. mov al,CLEARISR ; Signal EOI
  671. sti ; enable interrupts.
  672. out INTCHIP0,al ; 8259 interrupt chip controller 0
  673. ENDM
  674. LABEL LockedCodeStart BYTE
  675. ;***************************************************************************
  676. ;* TIMER_INTERRUPT -- Temp routine to mimic a timer system calling ours *
  677. ;* This is a temp routine to call our tick count routine. It will be *
  678. ;* replaced once another timer system is put in (such as HMI). *
  679. ;* *
  680. ;* INPUT: *
  681. ;* *
  682. ;* OUTPUT: *
  683. ;* *
  684. ;* WARNINGS: *
  685. ;* *
  686. ;* HISTORY: *
  687. ;* 07/06/1994 SKB : Created. *
  688. ;*=========================================================================*
  689. PROC Timer_Interrupt Near
  690. ;////////////////////////////// Call prot mode interrupt vector ////////////////////////////////////////
  691. ; This routine will do what it needs to,
  692. ; then it will decide if the old vector should be called.
  693. ; if so, it calls it and never returns to this function.
  694. ; if not, we do our own return.
  695. ; the method for doing this is found in:
  696. ; "Phar Lap TNT DOS-Extender Reference Manual, First Addition, p. 142"
  697. ; It says:
  698. ; 1 - Execute a PUSHFD to save the current flags.
  699. ; 2 - Dec the stack ptr by 8 bytes to save room for the addr of orig handler
  700. ; 3 - Push register I use.
  701. ; 4 - Do any processing.
  702. ; 5 - Put the address of the original handler in the reserved slot.
  703. ; 6 - Pop saved register values
  704. ; 7 - Execute an IRETD to transfer control to original handler.
  705. pushfd ; Step 1
  706. sub esp,8 ; Step 2
  707. push ebp ; Step 3
  708. mov ebp,esp ; Set up a stack frame to know where to poke address.
  709. ; Step 3 continued. Push used varables.
  710. pushad
  711. push fs gs es ds
  712. ; Step 4. Now do processing before I chain.
  713. ; Set up ds:esi to point at start of real memory block (data is first)
  714. call far Timer_Interrupt_Func
  715. SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff.
  716. ; Now take care of calling the old DOS timer interrupt vector.
  717. ; This must be the last operation of this module since if we call
  718. ; DOS, we will never return.
  719. mov ax,[(TimerType PTR ds:esi).DosAdder]
  720. add [(TimerType PTR esi).DosFraction],ax
  721. jnc ??no_dos_call ; if not, skip the call.
  722. ; Tell RM that we forced the int and not to update timers.
  723. mov [(TimerType PTR esi).PMIssuedInt],1 ; Make it TRUE
  724. ; Step 5.
  725. ; Now it is time to set up for the call by returning by poking
  726. ; the old interrupt handle address in.
  727. mov eax,[(TimerType PTR esi).OldPMIOffset] ; Get orig offset.
  728. mov ebx,[(TimerType PTR esi).OldPMISelector] ; Get orig selector.
  729. mov [ss:ebp+4],eax ; Poke offset.
  730. mov [ss:ebp+8],ebx ; Poke selector.
  731. ; Step 6.
  732. pop ds es gs fs
  733. popad
  734. pop ebp
  735. ; Step 7.
  736. iretd ; transfer control to original handler.
  737. ??no_dos_call:
  738. ENABLE_CLOCK_INT
  739. ; Restore all registers.
  740. pop ds es gs fs
  741. popad
  742. pop ebp
  743. add esp,8
  744. popfd
  745. iretd
  746. ;////////////////////////////// Call prot mode interrupt vector ////////////////////////////////////////
  747. ;//////////////////////////////////////////////////////////////////////////////////////////////////////
  748. ENDP
  749. ;***************************************************************************
  750. ;* TIMER_INTERRUPT_FUNC -- Handles core timer code *
  751. ;* *
  752. ;* This function exists so that we can call it from the core HMI driver *
  753. ;* code when our timer interrupt has not been installed. *
  754. ;* *
  755. ;* INPUT: none *
  756. ;* *
  757. ;* OUTPUT: none *
  758. ;* *
  759. ;* PROTO: *
  760. ;* *
  761. ;* WARNINGS: *
  762. ;* *
  763. ;* HISTORY: *
  764. ;* 03/14/1995 PWG : Created. *
  765. ;*=========================================================================*
  766. PROC Timer_Interrupt_Func C Far
  767. pushfd ; save off the flags
  768. pushad ; save off the main registers
  769. push fs gs es ds ; save off the seg registers
  770. SET_DS_ESI_TO_RM ; Set ds:esi to point to real mode stuff.
  771. INCREMENT_TIMERS ; Increment Westwoods timers
  772. pop ds es gs fs
  773. popad
  774. popfd
  775. retf
  776. ENDP
  777. LABEL LockedCodeEnd BYTE
  778. ;***************************************************************************
  779. ;* DISABLE_TIMER_INTERRUPT_ONLY -- Disables at the hardware level *
  780. ;* *
  781. ;* *
  782. ;* INPUT: none *
  783. ;* *
  784. ;* OUTPUT: none *
  785. ;* *
  786. ;* PROTO: *
  787. ;* *
  788. ;* WARNINGS: *
  789. ;* *
  790. ;* HISTORY: *
  791. ;* 06/07/1995 DRD : Created. *
  792. ;*=========================================================================*
  793. PROC Disable_Timer_Interrupt C Near
  794. push eax
  795. pushf
  796. sti ; disable all interrupts if not disabled
  797. in al,021h ; read interrupt Mask register bits 0-7
  798. ; apply to irq's 0-7
  799. ; value 0 of enabled
  800. ; value 1 of disabled
  801. or al,001h ; setup to disable irq 0
  802. out 021h,al ; disable irq 0
  803. popf ; possibly enable all interrupts (except 0)
  804. pop eax
  805. retf
  806. ENDP
  807. ;***************************************************************************
  808. ;* ENABLE_TIMER_INTERRUPT_ONLY -- Enables at the hardware level *
  809. ;* *
  810. ;* *
  811. ;* INPUT: none *
  812. ;* *
  813. ;* OUTPUT: none *
  814. ;* *
  815. ;* PROTO: *
  816. ;* *
  817. ;* WARNINGS: *
  818. ;* *
  819. ;* HISTORY: *
  820. ;* 06/07/1995 DRD : Created. *
  821. ;*=========================================================================*
  822. PROC Enable_Timer_Interrupt C Near
  823. push eax
  824. pushf
  825. sti ; disable all interrupts if not disabled
  826. in al,021h ; read interrupt Mask register bits 0-7
  827. ; apply to irq's 0-7
  828. ; value 0 of enabled
  829. ; value 1 of disabled
  830. and al,0FEh ; setup to enable irq 0
  831. out 021h,al ; enable irq 0
  832. popf ; possibly enable all interrupts
  833. pop eax
  834. retf
  835. ENDP
  836. END