Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

899 lines
26 KiB

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;
  3. ; TIMER.ASM
  4. ;
  5. ; Copyright (c) Microsoft Corporation 1991. All rights reserved.
  6. ;
  7. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  8. ?PLM=1 ; pascal call convention
  9. ?WIN=0 ; Windows prolog/epilog code
  10. ?DF=1
  11. PMODE=1
  12. .xlist
  13. include cmacros.inc
  14. include windows.inc
  15. include mmddk.inc
  16. include mmsystem.inc
  17. include timer.inc
  18. .list
  19. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  20. ;
  21. ; External functions
  22. ;
  23. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  24. externNP GetCounterElement
  25. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  26. ;
  27. ; Local data segment
  28. ;
  29. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  30. sBegin DATA
  31. externW CurTime
  32. externW nInt8Count
  33. externW wProgTime
  34. externW wNextTime
  35. externW IntCount
  36. externD dTickUpdate
  37. ifdef NEC_98
  38. externB bClockFlag
  39. endif ; NEC_98
  40. public Events,wNextID
  41. ; This structure is used in keeping track of all the current events,
  42. ; including any BIOS event.
  43. ;
  44. Events EventStruct MAXEVENTS DUP (<>)
  45. ; This value is used as an ever incrementing counter that is OR'ed into
  46. ; handles returned from <f>tddSetTimerEvent<d>. This is so that events
  47. ; can be uniquely identified in <f>tddKillTimerEvent<d>.
  48. ;
  49. wNextID dw 0
  50. ;
  51. ; The following is a table of timer resolution byte counters. Each entry
  52. ; N represents an interest in having the timer resolution set to N+1 MS.
  53. ; Thus there are TDD_MAX386RESOLUTION to TDD_MINRESOLUTION entries to
  54. ; represent 1..55 MS. Each time <f>tddBeginMinPeriod<d> is called with
  55. ; a timer period, the appropriate entry is incremented, and each time
  56. ; <f>tddEndMinPeriod<d> is called with a timer period, that entry is
  57. ; decremented. Presumably there is a one to one match on the Begin and
  58. ; End minimum period calls.
  59. ;
  60. ; This is of course all a workaround for the fact that the timer chip
  61. ; cannot be immediately reprogrammed the way it is wired in PCs in the
  62. ; mode in which it needs to be run, thus a separate resolution table
  63. ; must be kept in order to allow applications to set up a minimum
  64. ; resolution before actually setting any events.
  65. ;
  66. tddIntPeriodTable db TDD_MINRESOLUTION dup (0)
  67. public wMaxResolution,wMinPeriod
  68. wMaxResolution dw TDD_MAX386RESOLUTION
  69. wMinPeriod dw TDD_MIN386PERIOD
  70. sEnd DATA
  71. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  72. ;
  73. ; Code segment
  74. ;
  75. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  76. sBegin Code286
  77. assumes cs,Code286
  78. assumes ds,data
  79. assumes es,nothing
  80. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  81. ;
  82. ; Public exported functions
  83. ;
  84. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  85. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  86. ;
  87. ;@doc INTERNAL TIMER
  88. ;
  89. ;@api DWORD | tddBeginMinPeriod |
  90. ; Increments sets the specified resolution in the table of period
  91. ; resolutions. This optionally programming the timer for a new
  92. ; higher resolution if the parameter passed is a new minimum.
  93. ;
  94. ;@parm WORD | wPeriod |
  95. ; Contains a resolution period from wMaxResolution through 55
  96. ; milliseconds.
  97. ;
  98. ;@rdesc Returns 0 for success, else TIMERR_NOCANDO if the resolution period
  99. ; passed was out of range.
  100. ;
  101. ;@uses ax,bx,dx.
  102. ;
  103. ;@xref tddEndMinPeriod,tddSetInterruptPeriod.
  104. ;
  105. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  106. assumes es,nothing
  107. assumes ds,Data
  108. cProc tddBeginMinPeriod <PUBLIC,FAR> <>
  109. parmW wPeriod
  110. cBegin
  111. mov ax,TIMERR_NOCANDO ; Initialize return to error return
  112. mov bx,wPeriod
  113. cmp bx,[wMaxResolution]
  114. jb tddBeginMinPeriodExit ; Return TIMERR_NOCANDO
  115. cmp bx,TDD_MINRESOLUTION
  116. ja tddBeginMinPeriodExit ; Return TIMERR_NOCANDO
  117. dec bx ; Zero based resolution slot entries
  118. cmp tddIntPeriodTable[bx],0FFh
  119. ifdef DEBUG
  120. jne tddBeginMinPeriodInRange
  121. inc bx ; Show correct period in error
  122. DOUT <tddBeginMinPeriod(#bx) overflow>
  123. jmp tddBeginMinPeriodExit ; Return TIMERR_NOCANDO
  124. tddBeginMinPeriodInRange:
  125. else
  126. je tddBeginMinPeriodExit ; Return TIMERR_NOCANDO
  127. endif
  128. inc tddIntPeriodTable[bx] ; Increment resolution[entry - 1]
  129. cmp tddIntPeriodTable[bx],1 ; Don't set period if entry is >1
  130. jne @f
  131. call tddSetInterruptPeriod
  132. @@:
  133. xor ax,ax ; Return ok (FALSE)
  134. tddBeginMinPeriodExit:
  135. cwd ; Set to zero
  136. cEnd
  137. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  138. ;
  139. ;@doc INTERNAL TIMER
  140. ;
  141. ;@api DWORD | tddEndMinPeriod |
  142. ; Decrements the specified resolution in the table of period resolutions
  143. ; that was presumably set previously with a <f>tddBeginMinPeriod<d> call.
  144. ; This optionally programming the timer for a new lower resolution if
  145. ; the parameter passed removed the current minimum.
  146. ;
  147. ;@parm WORD | wPeriod |
  148. ; Contains a resolution period from 1 through 55 milliseconds.
  149. ;
  150. ;@rdesc Returns 0 for success, else TIMERR_NOCANDO if the resolution period
  151. ; passed was out of range.
  152. ;
  153. ;@uses ax,bx,dx.
  154. ;
  155. ;@xref tddBeginMinPeriod,tddSetInterruptPeriod.
  156. ;
  157. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  158. assumes es,nothing
  159. assumes ds,Data
  160. cProc tddEndMinPeriod <PUBLIC,FAR> <>
  161. parmW wPeriod
  162. cBegin
  163. mov ax,TIMERR_NOCANDO ; Initialize return to error return
  164. mov bx,wPeriod
  165. cmp bx,[wMaxResolution]
  166. jb tddEndMinPeriodExit ; Return TIMERR_NOCANDO
  167. cmp bx,TDD_MINRESOLUTION
  168. ja tddEndMinPeriodExit ; Return TIMERR_NOCANDO
  169. dec bx ; Zero based resolution slot entries
  170. cmp tddIntPeriodTable[bx],0
  171. ifdef DEBUG
  172. jne tddEndMinPeriodInRange
  173. inc bx ; Show correct period in error
  174. DOUT <tddEndMinPeriod(#bx) underflow>
  175. jmp tddEndMinPeriodExit ; Return TIMERR_NOCANDO
  176. tddEndMinPeriodInRange:
  177. else
  178. je tddEndMinPeriodExit ; Return TIMERR_NOCANDO
  179. endif
  180. dec tddIntPeriodTable[bx] ; Decrement resolution[entry - 1]
  181. jnz @f ; No need to set interrupt period
  182. call tddSetInterruptPeriod
  183. @@:
  184. xor ax,ax ; Return ok (FALSE)
  185. tddEndMinPeriodExit:
  186. cwd ; Set to zero
  187. cEnd
  188. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  189. ;
  190. ;@doc INTERNAL TIMER
  191. ;
  192. ;@func void | tddSetInterruptPeriod |
  193. ; This function optionally programs the timer with a new interrupt
  194. ; period if the maximum resolution in the resolution table has changed.
  195. if 0 ; !!!
  196. ;
  197. ; If the function is being called outside of interrupt time, the function
  198. ; must first turn off interrupts so that the resolution table is not
  199. ; changed between the time the the function finds a resolution to set,
  200. ; and the time it starts to program the timer. Once the timer begins to
  201. ; be programmed, it won't send any more interrupts until programming is
  202. ; finished. The documentation does not specify that, but it was verified
  203. ; through testing the timer. If however the function is being called
  204. ; during a timer interrupt, there is no need to turn off interrupts, as
  205. ; the resolution table will not be changed at that time.
  206. ;
  207. endif
  208. ; In any case, the resolution table is searched, looking for the first
  209. ; non-zero entry, which is taken as the maximum resolution the timer
  210. ; should currently be programmed to. If nothing is set in the table,
  211. ; then the programming defaults to the minimum resolution of 55 MS.
  212. ;
  213. ; Once an entry is found, it is compared to the previous programmed
  214. ; time, not the currently programmed time. This is in case an interrupt
  215. ; has not occurred since the last time the timer was programmed using
  216. ; this function. Note that in converting to clock ticks, any period
  217. ; that overflows a single word is taken to be 65536 ticks, which is the
  218. ; maximum number allowable in the timer, and is equal to almost 55 MS.
  219. ;
  220. ; If a new time must be programmed, the new resolution is sent out to
  221. ; the timer, and eventually interrupts are set again.
  222. ;
  223. ;@rdesc Nothing.
  224. ;
  225. ;@uses ax,bx,dx.
  226. ;
  227. ;@xref tddBeginMinPeriod,tddEndMinPeriod.
  228. ;
  229. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  230. assumes es,nothing
  231. assumes ds,Data
  232. cProc tddSetInterruptPeriodFar <PUBLIC,FAR> <>
  233. cBegin
  234. call tddSetInterruptPeriod
  235. cEnd
  236. cProc tddSetInterruptPeriod <PUBLIC,NEAR> <>
  237. cBegin
  238. xor bx, bx ; Start at the beginning of the table
  239. EnterCrit ; !!!
  240. tdd_sip_loop:
  241. cmp bx,TDD_MINRESOLUTION ; Has the last entry been passed up?
  242. je tdd_sip_Set_This_Period ; Jump out using TDD_MINRESOLUTION
  243. inc bx
  244. cmp tddIntPeriodTable[bx-1],0
  245. je tdd_sip_loop
  246. tdd_sip_Set_This_Period:
  247. mov ax,bx
  248. call tddMsToTicks
  249. or dx,dx ; Check for overflow of WORD
  250. ifdef NEC_98
  251. jz short @f
  252. mov ax,0ffffh
  253. @@:
  254. cmp byte ptr bClockFlag,0
  255. mov dx,0f000h ; 5MHz tick count
  256. jz @f
  257. mov dx,0c300h ; 8MHz tick count
  258. @@:
  259. cmp ax,dx
  260. jc tdd_sip_period_ok
  261. mov ax,dx ; Set to 25msec tick count
  262. else ; NEC_98
  263. jz tdd_sip_period_ok
  264. xor ax,ax ; Set to 64k instead.
  265. endif ; NEC_98
  266. tdd_sip_period_ok:
  267. cmp ax,[wNextTime] ; Compare with last programmed time
  268. je tdd_sip_exit ; No need to reprogram
  269. DOUT <tddSetInterruptPeriod: ms=#bx ticks=#ax>
  270. mov bx,ax ; Save this value
  271. mov [wNextTime],bx ; This is now the last programmed time
  272. mov al, TMR_MODE2_RW ; Set counter 0 to mode 2
  273. out TMR_CTRL_REG, al
  274. mov al, bl
  275. out TMR_CNTR_0, al
  276. mov al, bh
  277. out TMR_CNTR_0, al
  278. tdd_sip_exit:
  279. LeaveCrit ; !!!
  280. cEnd
  281. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  282. ;
  283. ;@doc INTERNAL TIMER
  284. ;
  285. ;@api DWORD | tddSetTimerEvent |
  286. ; Adds a timer event, possibly periodic to the event queue.
  287. ;
  288. ; A timer event is set by first looking through the table of external
  289. ; event slots, trying to locate a currently vacant slot that is not
  290. ; currently being checked or deleted. If one is found, the Create flag
  291. ; is test-and-set in order to try and grab the slot.
  292. ;
  293. ; If this succeeds, the slot can be set up with the information, and the
  294. ; resolution entered into the event resolution table. The very last
  295. ; thing that occurs is setting the ID element of the slot. This is so
  296. ; that an inturrupt will not try to execute this event until all the
  297. ; parameters are set. This means that the event could be executed
  298. ; immediately after the ID is set, but before this function actually
  299. ; returns to the caller.
  300. ;
  301. ; If the function fails to grab the event slot, it means that either an
  302. ; interrupt occurred, and another event was created in this slot, or that
  303. ; this function is running during an interrupt that occurred while a new
  304. ; event was being created. In any case, the slot must be passed by.
  305. ;
  306. ; If an interrupt had occurred during this function, it also means that
  307. ; some other event could have been freed, but already passed by, so the
  308. ; function misses it. The function cannot go back though, because it
  309. ; might actually be processing during an interrupt, and the slot being
  310. ; passed by would continue in its present state, and thus cause an
  311. ; infinite loop to occur.
  312. ;
  313. ; When checking for a free event slot, not only is the ID checked, but
  314. ; also the state of the Destroy flag. This flag is used during the kill
  315. ; event function to indicate that an event slot is currently being
  316. ; checked or destroyed, or was destroyed during an interrupt while the
  317. ; slot was being checked. In either case, it indicates that this
  318. ; function is being called during interrupt time, and the slot cannot be
  319. ; re-used until the flag is removed by the kill event function. This
  320. ; means that during the kill event function, there is one less event
  321. ; slot that can be used than normal.
  322. ;
  323. ; Once the ID of the event slot is set, the event can be called. Note
  324. ; that the event may then be called before this function even returns.
  325. ;
  326. ;@rdesc Returns a handle which identifies the timer event, or NULL if the
  327. ; requested event is invalid, or the event queue is full.
  328. ;
  329. ;@xref tddKillTimerEvent
  330. ;
  331. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  332. assumes ds,Data
  333. assumes es,nothing
  334. cProc tddSetTimerEvent <PUBLIC,FAR> <si,di,es>
  335. parmD pTIMEREVENT
  336. localW wResolution
  337. localW wEventID
  338. cBegin
  339. les si,pTIMEREVENT ; timer event structure
  340. mov ax,es
  341. or ax,si
  342. ; (pTIMEREVENT != NULL)
  343. jz SetEventError ; NULL pointer, exit
  344. mov bx,es:[si].te_wDelay
  345. ; ((te_wDelay >= wMinPeriod) && (te_wDelay <= TDD_MAXPERIOD))
  346. cmp bx,[wMinPeriod] ; delay less than min period?
  347. jb SetEventError ; Yes, error
  348. cmp bx,TDD_MAXPERIOD ; delay greater than max period?
  349. ja SetEventError ; Yes, error
  350. ; (!te_wResolution)
  351. mov ax,es:[si].te_wResolution
  352. or ax,ax ; resolution not set?
  353. jz SetDefaultResolution ; Yes, set default resolution
  354. ; ((te_wResolution >= TDD_MINRESOLUTION) && (te_wResolution <= wMaxResolution))
  355. cmp ax,TDD_MINRESOLUTION ; resolution less than min resolution?
  356. jb @f ; No, skip to next check
  357. mov ax,TDD_MINRESOLUTION
  358. @@:
  359. cmp ax,[wMaxResolution] ; resolution greater than max resolution?
  360. ja @f ; No, skip to next check
  361. mov ax,[wMaxResolution]
  362. @@:
  363. ; (te_wResolution > te_wDelay)
  364. cmp bx,ax ; delay less than resolution?
  365. jb SetDefaultResolution ; Yes, set default resolution
  366. jmp short SetEventValidParms
  367. SetEventError:
  368. xor ax,ax ; Return NULL
  369. jmp SetEventExit
  370. SetDefaultResolution:
  371. ; te_wResolution = min(TDD_MINRESOLUTION, te_wDelay)
  372. mov ax,TDD_MINRESOLUTION
  373. cmp bx,ax ; delay less than min resolution?
  374. ja SetEventValidParms ; No, just use min resolution then
  375. mov ax,bx ; Yes, use the period as the resolution
  376. ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
  377. ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
  378. SetEventValidParms:
  379. mov wResolution,ax ; save calculated resolution
  380. lea di,Events ; DS:DI --> events
  381. xor ax,ax ; event slot = 0
  382. SetEventFindLoop:
  383. ; if (!pEvent->evID && !pEvent->evDestroy)
  384. cmp [di].evID,0
  385. jne SetEventFindLoopNext
  386. cmp BYTE PTR [di].evDestroy,0
  387. jne SetEventFindLoopNext
  388. mov bl,1
  389. xchg BYTE PTR [di].evCreate,bl ; Test and set Create flag
  390. or bl,bl
  391. jz SetEventFindLoopFound
  392. SetEventFindLoopNext:
  393. ; pEvent++, wEventID++
  394. add di,SizeEvent
  395. inc ax
  396. ; wEventID < MAXEVENTS
  397. cmp ax,MAXEVENTS
  398. jb SetEventFindLoop
  399. ; Return NULL
  400. xor ax,ax ; Slot not found, return NULL
  401. jmp SetEventExit
  402. SetEventFindLoopFound:
  403. ;
  404. ; combine the slot index and wNextID to produce a unique id to
  405. ; return to the caller
  406. ;
  407. add [wNextID],MASKINCREMENT
  408. jz SetEventFindLoopFound ; Ensure a non-zero mask
  409. or ax,[wNextID] ; Add in the mask
  410. mov wEventID,ax ; Save the event
  411. errnz MAXEVENTS-16
  412. ; tddBeginMinPeriod(pEvent->evResolution)
  413. mov ax,wResolution
  414. mov [di].evResolution,ax
  415. cCall tddBeginMinPeriod <ax>
  416. ; pEvent->evDelay = tddMsToTicks(pTIMEREVENT->te_wDelay)
  417. mov ax,es:[si].te_wDelay
  418. call tddMsToTicks
  419. mov [di].evDelay.lo,ax
  420. mov [di].evDelay.hi,dx
  421. ; pEvent->evCallback = pTIMEREVENT->te_lpFunction
  422. mov ax,es:[si].te_lpFunction.lo
  423. mov dx,es:[si].te_lpFunction.hi
  424. mov [di].evCallback.lo,ax
  425. mov [di].evCallback.hi,dx
  426. ; pEvent->evUser = pTIMEREVENT->te_dwUser
  427. mov ax,es:[si].te_dwUser.lo
  428. mov dx,es:[si].te_dwUser.hi
  429. mov [di].evUser.lo,ax
  430. mov [di].evUser.hi,dx
  431. ; pEvent->evFlags = pTIMEREVENT->te_wFlags
  432. mov ax,es:[si].te_wFlags
  433. mov [di].evFlags,ax
  434. @@:
  435. mov bx,[IntCount] ; check for interrupt occurring
  436. call GetCounterElement ; Get number of ticks passed
  437. xor cx,cx
  438. add ax,dTickUpdate.lo ; Add extra currently skipped.
  439. adc cx,dTickUpdate.hi
  440. cmp bx,[IntCount]
  441. jne @b ; If interrupt occurred try again
  442. ; pEvent->evTime = pEvent->evDelay + GetCounterElement + dTickUpdate
  443. mov bx,[di].evDelay.lo
  444. mov dx,[di].evDelay.hi
  445. add bx,ax
  446. adc dx,cx
  447. mov [di].evTime.lo,bx
  448. mov [di].evTime.hi,dx
  449. ; pEvent->evID = wEventID
  450. mov ax,wEventID
  451. mov [di].evID,ax
  452. ; Return wEventID
  453. SetEventExit:
  454. xor dx,dx
  455. cEnd
  456. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  457. ;
  458. ;@doc INTERNAL TIMER
  459. ;
  460. ;@api DWORD | tddKillTimerEvent |
  461. ; Removes a timer event from the event queue. If the event was periodic,
  462. ; this is the only way to discontinue operation. Otherwise, this may be
  463. ; used to remove an unwanted one shot event in case of application
  464. ; termination.
  465. ;
  466. ; A timer event it killed by trying to grab the Destroy flag in a two
  467. ; step process, which succeeds only if the function was able to grab
  468. ; the slot before any interrupt destroyed the event.
  469. ;
  470. ; After verifying that the event handle is valid, the function checks the
  471. ; Destroy flag to determine if this function is being called during
  472. ; interrupt time, and interrupted another process killing the same
  473. ; timer. If this is so, the function just aborts before wasting time
  474. ; doing any other flag setting.
  475. ;
  476. ; The function then sets the Destroy flag to a EVENT_CHECKING state,
  477. ; grabbing the current state of the flag in order to use when setting
  478. ; the final state of the Destroy flag if the function succeeds.
  479. ;
  480. ; If the event handles match, the Destroy flag is set to a
  481. ; EVENT_DESTROYING state. At this point, the Destroy flag is either in
  482. ; the state in which this function left it, or an interrupt occurred, and
  483. ; the flag was set to a EVENT_DESTROYED state durring interrupt time. If
  484. ; an interrupt ended up destroying the event out from under this call,
  485. ; the function is exited after clearing the Destroy flag so that the
  486. ; event slot can be used. Note that the event slot cannot be used until
  487. ; the function exits so that the EVENT_DESTROYED flag is not disturbed.
  488. ;
  489. ; If the flag is grabbed, no other call can destroy the event, and the
  490. ; event will not be executed during interrupt time. As was previously
  491. ; mentioned, the Destroy flag is either reset, or if this function was
  492. ; called during interrupt time while the event was being checked, the
  493. ; flag is set to EVENT_DESTROYED.
  494. ;
  495. ; The resolution entered into the event resolution table is removed.
  496. ; The very last thing to occur is resetting the Create flag. At that
  497. ; point the event slot could be re-used if the Destroy flag was reset.
  498. ;
  499. ; Note that if the event handles do not match, the Destroyed flag is also
  500. ; reset so that it can be used in creating a new event when this event
  501. ; is destroyed, which may have happened while checking the handles.
  502. ;
  503. ;@parm WORD | wID | The event handle returned by the <f>tddSetTimerEvent<d>
  504. ; function which identifies the event to destroy.
  505. ;
  506. ;@rdesc Returns 0 if timer event destroyed, or TIMERR_NOCANDO if the
  507. ; event was not registered in the system event queue.
  508. ;
  509. ;@xref tddSetTimerEvent
  510. ;
  511. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  512. assumes ds,Data
  513. assumes es,nothing
  514. cProc tddKillTimerEvent <PUBLIC,FAR> <si,di>
  515. parmW wID
  516. cBegin
  517. mov ax,wID
  518. and ax,MASKFILTER ; Remove ID mask first
  519. errnz MAXEVENTS-16
  520. imul ax,SizeEvent ; Retrieve slot address
  521. lea di,Events
  522. add di,ax
  523. ; if (pEvent->evDestroy == EVENT_DESTROYING)
  524. cmp BYTE PTR [di].evDestroy,EVENT_DESTROYING ; If interrupting a destroy,
  525. je KillEventError ; Leave with error
  526. mov bl,EVENT_CHECKING
  527. xchg BYTE PTR [di].evDestroy,bl ; Test and set Destroy check
  528. ; if (pEvent->evID == wID)
  529. mov ax,wID
  530. cmp [di].evID,ax
  531. jne KillEventRelease ; Wrong ID
  532. mov bh,EVENT_DESTROYING
  533. xchg BYTE PTR [di].evDestroy,bh ; Test and set Destroying
  534. cmp bh,EVENT_CHECKING ; Was destroy interrupted?
  535. jne KillEventRelease ; Slot has already been deleted
  536. mov [di].evID,0 ; Invalidate ID
  537. cmp bl,EVENT_CHECKING ; Did this interrupt a destroy?
  538. jne @f ; No, was already ZERO
  539. mov bl,EVENT_DESTROYED ; Let the interrupted destroy know
  540. @@:
  541. mov BYTE PTR [di].evDestroy,bl
  542. cCall tddEndMinPeriod,<[di].evResolution>
  543. ; pEvent->evCreate = FALSE
  544. mov BYTE PTR [di].evCreate,0 ; Free up slot
  545. xor ax,ax ; Return 0
  546. jmp KillEventExit
  547. KillEventRelease:
  548. ; Free up checking flag
  549. mov BYTE PTR [di].evDestroy,0
  550. KillEventError:
  551. ; Invalid ID or was deleted during interrupt time (test and set failed)
  552. mov ax,TIMERR_NOCANDO
  553. KillEventExit:
  554. cwd ; Set to zero
  555. cEnd
  556. assumes ds,Data
  557. assumes es,nothing
  558. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  559. public GetTickCount
  560. GetTickCount proc near
  561. @@:
  562. mov cx,[IntCount] ; Save current interrupt count
  563. call GetCounterElement ; Get number of ticks passed
  564. xor dx,dx
  565. xor bx,bx
  566. add ax,CurTime[0] ; Add total tick count to current number past
  567. adc dx,CurTime[2]
  568. adc bx,CurTime[4]
  569. cmp cx,[IntCount] ; Interrupt occurred while getting count
  570. jne @b ; Get the count again
  571. ret
  572. GetTickCount endp
  573. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  574. ;
  575. ;@doc INTERNAL TIMER
  576. ;
  577. ;@api DWORD | tddGetSystemTime |
  578. ; Returns a system time in milliseconds.
  579. ;
  580. ;@rdesc Returns a 32 bit value in dx:ax representing the number of milliseconds
  581. ; since the timer driver was started.
  582. ;
  583. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  584. assumes ds,Data
  585. assumes es,nothing
  586. cProc tddGetSystemTime <PUBLIC,FAR> <>
  587. cBegin
  588. call GetTickCount
  589. call tddTicksToMs
  590. cEnd
  591. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  592. ;
  593. ;@doc INTERNAL TIMER
  594. ;
  595. ;@asm tddGetTickCount |
  596. ; Returns a system time in clock ticks.
  597. ;
  598. ;@rdesc Returns a 48 bit value in bx:dx:ax representing the number of clock
  599. ; ticks since the timer driver was started. A C interface would only
  600. ; be able to access the lower 32 bits of this value.
  601. ;
  602. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  603. assumes ds,Data
  604. assumes es,nothing
  605. cProc tddGetTickCount <PUBLIC,FAR> <>
  606. cBegin
  607. call GetTickCount
  608. cEnd
  609. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  610. ;
  611. ;@doc INTERNAL TIMER
  612. ;
  613. ;@api DWORD | tddGetDevCaps |
  614. ; Fills in TIMECAPS structure.
  615. ;
  616. ;@parm <t>LPTIMECAPS<d> | lpTIMECAPS |
  617. ; Points to the structure to fill.
  618. ;
  619. ;@parm WORD | wSize |
  620. ; Indicates the size of the structure passed. Normally this should be
  621. ; the size of the <t>TIMECAPS<d> structure this module was compiled with.
  622. ;
  623. ;@rdesc Returns 0 on success, or TIMERR_NOCANDO on failure.
  624. ;
  625. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  626. assumes ds,nothing
  627. assumes es,nothing
  628. cProc tddGetDevCaps <PUBLIC,FAR> <si,ds>
  629. parmD lpTIMECAPS
  630. parmW wSize
  631. cBegin
  632. mov ax,TIMERR_NOCANDO ; Initialize return to an error state
  633. cmp wSize,(SIZE TIMECAPS) ; Check the size of the structure passed
  634. jne Caps_Exit
  635. lds si,lpTIMECAPS ; timer event structure
  636. push ds
  637. mov ax,DGROUP
  638. mov ds,ax
  639. assumes ds,Data
  640. mov ax,[wMinPeriod] ; Fill in the structure
  641. pop ds
  642. assumes ds,nothing
  643. mov dx,TDD_MAXPERIOD
  644. mov [si].tc_wPeriodMin,ax
  645. mov [si].tc_wPeriodMax,dx
  646. xor ax,ax ; Return success
  647. Caps_Exit:
  648. cwd ; Set to zero
  649. cEnd
  650. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  651. ;
  652. ;@doc INTERNAL TIMER
  653. ;
  654. ;@api DWORD | tddTicksToMs |
  655. ; Convert clock ticks (1.19318 MHz) to milliseconds (1000 Hz)
  656. ;
  657. ;@parm BX:DX:AX |
  658. ; Tick count to convert to milliseconds.
  659. ;
  660. ;@rdesc DX:AX |
  661. ; Converted millisecond count.
  662. ;
  663. ;@comm There is a 0.0000005% positive error in the approximation of
  664. ; 1193.18 ticks per millisecond by the process to avoid floating point
  665. ; arithmetic, which effectively divides by 1193.179993 instead.
  666. ;
  667. ; time `Ms' = clock ticks `T' / 1193.18
  668. ;
  669. ; In order to be able to use fixed point, the math actually done is:
  670. ;
  671. ; Ms = (T * 10000h) / (DWORD)(1193.18 * 10000h)
  672. ;
  673. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  674. assumes ds,Data
  675. assumes es,nothing
  676. cProc tddTicksToMs <PUBLIC,NEAR> <si,di>
  677. cBegin
  678. externNP qdiv ; In math.asm
  679. ; qdiv
  680. ;
  681. ; Entry:
  682. ; DX:CX:BX:AX = QUAD Numerator
  683. ; SI:DI = LONG Denominator
  684. ; Returns:
  685. ; DX:AX = quotient
  686. ; CX:BX = remainder
  687. ; multiply BX:DX:AX by 10000h and place result in DX:CX:BX:AX for qdiv
  688. mov cx,dx
  689. mov dx,bx
  690. mov bx,ax
  691. xor ax,ax
  692. ifdef NEC_98
  693. cmp byte ptr bClockFlag,0
  694. jnz set_8Mhz
  695. set_5Mhz:
  696. ; SI:DI = 2457.6 * 10000h (essentially in 16.16 fixed notation)
  697. mov si,2457 ; 2457 * 10000h
  698. mov di,39321 ; 0.6 * 10000h = 39321.6
  699. jmp short @f
  700. set_8Mhz:
  701. ; 8MHz,16MHz set
  702. ; SI:DI = 1996.8 * 10000h (essentially in 16.16 fixed notation)
  703. mov si,1996 ; 1996 * 10000h
  704. mov di,52428 ; 0.8 * 10000h = 52428.8
  705. @@:
  706. else ; NEC_98
  707. ; SI:DI = 1193.18 * 10000h (essentially in 16.16 fixed notation)
  708. mov si,1193 ; 1193 * 10000h
  709. mov di,11796 ; 0.18 * 10000h = 11796.48
  710. endif ; NEC_98
  711. call qdiv ; (T * 10000h) / (DWORD)(1193.18 * 10000h)
  712. cEnd
  713. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  714. ;
  715. ;@doc INTERNAL TIMER
  716. ;
  717. ;@api DWORD | tddMsToTicks |
  718. ; Convert milliseconds (1000 Hz) to clock ticks (1.193 MHz).
  719. ;
  720. ;@parm AX |
  721. ; Millisecond count to convert to clock ticks
  722. ;
  723. ;@rdesc DX:AX |
  724. ; Converted clock tick count.
  725. ;
  726. ;@comm There is a slight error in the approximation of 1193.18 ticks per
  727. ; millisecond by the process to avoid floating point arithmetic, which
  728. ; effectively multiplies by 1193.1875 instead.
  729. ;
  730. ; clock ticks `T' = time `Ms' * 1193.18
  731. ;
  732. ; In order to be able to use fixed point, the math actually done is
  733. ;
  734. ; T = (Ms * (WORD)(1193.18 * 20h)) / 20h
  735. ;
  736. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  737. assumes ds,Data
  738. assumes es,nothing
  739. cProc tddMsToTicks <PUBLIC,NEAR> <>
  740. cBegin
  741. ifdef NEC_98
  742. cmp byte ptr bClockFlag,0
  743. jnz set2_8Mhz
  744. set2_5Mhz:
  745. mov dx,39322 ; 2457.6 * 10h = 39321.6
  746. jmp short @f
  747. set2_8Mhz:
  748. ; 8MHz,16MHz set
  749. mov dx,31949 ; 1996.8 * 10h = 31948.8
  750. @@:
  751. mul dx ; Ms * (WORD)(1996.8 * 10h)
  752. shr ax,4 ; Divide the result by 10h
  753. mov cx,dx ; Save original first
  754. shl cx,12 ; Keep only the bottom part
  755. shr dx,4 ; Shift top part of return
  756. or ax,cx ; Put two halves of bottom part together
  757. else ; NEC_98
  758. mov dx,38182 ; 1193.18 * 20h = 38181.76
  759. mul dx ; Ms * (WORD)(1193.18 * 20h)
  760. shr ax,5 ; Divide the result by 20h
  761. mov cx,dx ; Save original first
  762. shl cx,11 ; Keep only the bottom part
  763. shr dx,5 ; Shift top part of return
  764. or ax,cx ; Put two halves of bottom part together
  765. endif ; NEC_98
  766. cEnd
  767. sEnd Code286
  768. end