Coding style cleanup
[platform/kernel/u-boot.git] / board / MAI / bios_emulator / scitech / src / pm / dos / _lztimer.asm
1 ;****************************************************************************
2 ;*
3 ;*                  SciTech OS Portability Manager Library
4 ;*
5 ;*  ========================================================================
6 ;*
7 ;*    The contents of this file are subject to the SciTech MGL Public
8 ;*    License Version 1.0 (the "License"); you may not use this file
9 ;*    except in compliance with the License. You may obtain a copy of
10 ;*    the License at http://www.scitechsoft.com/mgl-license.txt
11 ;*
12 ;*    Software distributed under the License is distributed on an
13 ;*    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 ;*    implied. See the License for the specific language governing
15 ;*    rights and limitations under the License.
16 ;*
17 ;*    The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
18 ;*
19 ;*    The Initial Developer of the Original Code is SciTech Software, Inc.
20 ;*    All Rights Reserved.
21 ;*
22 ;*  ========================================================================
23 ;*
24 ;* Language:    NASM or TASM Assembler
25 ;* Environment: IBM PC (MS DOS)
26 ;*
27 ;* Description: Uses the 8253 timer and the BIOS time-of-day count to time
28 ;*              the performance of code that takes less than an hour to
29 ;*              execute.
30 ;*
31 ;*              The routines in this package only works with interrupts
32 ;*              enabled, and in fact will explicitly turn interrupts on
33 ;*              in order to ensure we get accurate results from the timer.
34 ;*
35 ;*  Externally 'C' callable routines:
36 ;*
37 ;*  LZ_timerOn:     Saves the BIOS time of day count and starts the
38 ;*                  long period Zen Timer.
39 ;*
40 ;*  LZ_timerLap:    Latches the current count, and keeps the timer running
41 ;*
42 ;*  LZ_timerOff:    Stops the long-period Zen Timer and saves the timer
43 ;*                  count and the BIOS time of day count.
44 ;*
45 ;*  LZ_timerCount:  Returns an unsigned long representing the timed count
46 ;*                  in microseconds. If more than an hour passed during
47 ;*                  the timing interval, LZ_timerCount will return the
48 ;*                  value 0xFFFFFFFF (an invalid count).
49 ;*
50 ;*  Note:   If either more than an hour passes between calls to LZ_timerOn
51 ;*          and LZ_timerOff, an error is reported. For timing code that takes
52 ;*          more than a few minutes to execute, use the low resolution
53 ;*          Ultra Long Period Zen Timer code, which should be accurate
54 ;*          enough for most purposes.
55 ;*
56 ;*  Note:   Each block of code being timed should ideally be run several
57 ;*          times, with at least two similar readings required to
58 ;*          establish a true measurement, in order to eliminate any
59 ;*          variability caused by interrupts.
60 ;*
61 ;*  Note:   Interrupts must not be disabled for more than 54 ms at a
62 ;*          stretch during the timing interval. Because interrupts are
63 ;*          enabled, key, mice, and other devices that generate interrupts
64 ;*          should not be used during the timing interval.
65 ;*
66 ;*  Note:   Any extra code running off the timer interrupt (such as
67 ;*          some memory resident utilities) will increase the time
68 ;*          measured by the Zen Timer.
69 ;*
70 ;*  Note:   These routines can introduce inaccuracies of up to a few
71 ;*          tenths of a second into the system clock count for each
72 ;*          code section being timed. Consequently, it's a good idea to
73 ;*          reboot at the conclusion of timing sessions. (The
74 ;*          battery-backed clock, if any, is not affected by the Zen
75 ;*          timer.)
76 ;*
77 ;*  All registers and all flags are preserved by all routines, except
78 ;*  interrupts which are always turned on
79 ;*
80 ;****************************************************************************
81
82         IDEAL
83
84 include "scitech.mac"
85
86 ;****************************************************************************
87 ;
88 ; Equates used by long period Zen Timer
89 ;
90 ;****************************************************************************
91
92 ; Base address of 8253 timer chip
93
94 BASE_8253       equ     40h
95
96 ; The address of the timer 0 count registers in the 8253
97
98 TIMER_0_8253    equ     BASE_8253 + 0
99
100 ; The address of the mode register in the 8253
101
102 MODE_8253       equ     BASE_8253 + 3
103
104 ; The address of the BIOS timer count variable in the BIOS data area.
105
106 TIMER_COUNT     equ     6Ch
107
108 ; Macro to delay briefly to ensure that enough time has elapsed between
109 ; successive I/O accesses so that the device being accessed can respond
110 ; to both accesses even on a very fast PC.
111
112 ifdef   USE_NASM
113 %macro  DELAY 0
114         jmp     short $+2
115         jmp     short $+2
116         jmp     short $+2
117 %endmacro
118 else
119 macro   DELAY
120         jmp     short $+2
121         jmp     short $+2
122         jmp     short $+2
123 endm
124 endif
125
126 header      _lztimer
127
128 begdataseg  _lztimer
129
130         cextern  _ZTimerBIOSPtr,DPTR
131
132 StartBIOSCount      dd  0       ; Starting BIOS count dword
133 EndBIOSCount        dd  0       ; Ending BIOS count dword
134 EndTimedCount       dw  0       ; Timer 0 count at the end of timing period
135
136 enddataseg  _lztimer
137
138 begcodeseg  _lztimer                ; Start of code segment
139
140 ;----------------------------------------------------------------------------
141 ; void LZ_timerOn(void);
142 ;----------------------------------------------------------------------------
143 ; Starts the Long period Zen timer counting.
144 ;----------------------------------------------------------------------------
145 cprocstart  LZ_timerOn
146
147 ; Set the timer 0 of the 8253 to mode 2 (divide-by-N), to cause
148 ; linear counting rather than count-by-two counting. Also stops
149 ; timer 0 until the timer count is loaded, except on PS/2 computers.
150
151         mov     al,00110100b        ; mode 2
152         out     MODE_8253,al
153
154 ; Set the timer count to 0, so we know we won't get another timer
155 ; interrupt right away. Note: this introduces an inaccuracy of up to 54 ms
156 ; in the system clock count each time it is executed.
157
158         DELAY
159         sub     al,al
160         out     TIMER_0_8253,al     ; lsb
161         DELAY
162         out     TIMER_0_8253,al     ; msb
163
164 ; Store the timing start BIOS count
165
166         use_es
167 ifdef   flatmodel
168         mov     ebx,[_ZTimerBIOSPtr]
169 else
170         les     bx,[_ZTimerBIOSPtr]
171 endif
172         cli                         ; No interrupts while we grab the count
173         mov     eax,[_ES _bx+TIMER_COUNT]
174         sti
175         mov     [StartBIOSCount],eax
176         unuse_es
177
178 ; Set the timer count to 0 again to start the timing interval.
179
180         mov     al,00110100b        ; set up to load initial
181         out     MODE_8253,al        ; timer count
182         DELAY
183         sub     al,al
184         out     TIMER_0_8253,al     ; load count lsb
185         DELAY
186         out     TIMER_0_8253,al     ; load count msb
187
188         ret
189
190 cprocend
191
192 ;----------------------------------------------------------------------------
193 ; void LZ_timerOff(void);
194 ;----------------------------------------------------------------------------
195 ; Stops the long period Zen timer and saves count.
196 ;----------------------------------------------------------------------------
197 cprocstart  LZ_timerOff
198
199 ; Latch the timer count.
200
201         mov     al,00000000b        ; latch timer 0
202         out     MODE_8253,al
203         cli                         ; Stop the BIOS count
204
205 ; Read the BIOS count. (Since interrupts are disabled, the BIOS
206 ; count won't change).
207
208         use_es
209 ifdef   flatmodel
210         mov     ebx,[_ZTimerBIOSPtr]
211 else
212         les     bx,[_ZTimerBIOSPtr]
213 endif
214         mov     eax,[_ES _bx+TIMER_COUNT]
215         mov     [EndBIOSCount],eax
216         unuse_es
217
218 ; Read out the count we latched earlier.
219
220         in      al,TIMER_0_8253     ; least significant byte
221         DELAY
222         mov     ah,al
223         in      al,TIMER_0_8253     ; most significant byte
224         xchg    ah,al
225         neg     ax                  ; Convert from countdown remaining
226                                     ;  to elapsed count
227         mov     [EndTimedCount],ax
228         sti                         ; Let the BIOS count continue
229
230         ret
231
232 cprocend
233
234 ;----------------------------------------------------------------------------
235 ; unsigned long LZ_timerLap(void)
236 ;----------------------------------------------------------------------------
237 ; Latches the current count and converts it to a microsecond timing value,
238 ; but leaves the timer still running. We dont check for and overflow,
239 ; where the time has gone over an hour in this routine, since we want it
240 ; to execute as fast as possible.
241 ;----------------------------------------------------------------------------
242 cprocstart  LZ_timerLap
243
244         push    ebx                 ; Save EBX for 32 bit code
245
246 ; Latch the timer count.
247
248         mov     al,00000000b        ; latch timer 0
249         out     MODE_8253,al
250         cli                         ; Stop the BIOS count
251
252 ; Read the BIOS count. (Since interrupts are disabled, the BIOS
253 ; count wont change).
254
255         use_es
256 ifdef   flatmodel
257         mov     ebx,[_ZTimerBIOSPtr]
258 else
259         les     bx,[_ZTimerBIOSPtr]
260 endif
261         mov     eax,[_ES _bx+TIMER_COUNT]
262         mov     [EndBIOSCount],eax
263         unuse_es
264
265 ; Read out the count we latched earlier.
266
267         in      al,TIMER_0_8253     ; least significant byte
268         DELAY
269         mov     ah,al
270         in      al,TIMER_0_8253     ; most significant byte
271         xchg    ah,al
272         neg     ax                  ; Convert from countdown remaining
273                                     ;  to elapsed count
274         mov     [EndTimedCount],ax
275         sti                         ; Let the BIOS count continue
276
277 ; See if a midnight boundary has passed and adjust the finishing BIOS
278 ; count by the number of ticks in 24 hours. We wont be able to detect
279 ; more than 24 hours, but at least we can time across a midnight
280 ; boundary
281
282         mov     eax,[EndBIOSCount]      ; Is end < start?
283         cmp     eax,[StartBIOSCount]
284         jae     @@CalcBIOSTime          ; No, calculate the time taken
285
286 ; Adjust the finishing time by adding the number of ticks in 24 hours
287 ; (1573040).
288
289         add     [DWORD EndBIOSCount],1800B0h
290
291 ; Convert the BIOS time to microseconds
292
293 @@CalcBIOSTime:
294         mov     ax,[WORD EndBIOSCount]
295         sub     ax,[WORD StartBIOSCount]
296         mov     dx,54925            ; Number of microseconds each
297                                     ;  BIOS count represents.
298         mul     dx
299         mov     bx,ax               ; set aside BIOS count in
300         mov     cx,dx               ;  microseconds
301
302 ; Convert timer count to microseconds
303
304         push    _si
305         mov     ax,[EndTimedCount]
306         mov     si,8381
307         mul     si
308         mov     si,10000
309         div     si                  ; * 0.8381 = * 8381 / 10000
310         pop     _si
311
312 ; Add the timer and BIOS counts together to get an overall time in
313 ; microseconds.
314
315         add     ax,bx
316         adc     cx,0
317 ifdef flatmodel
318         shl     ecx,16
319         mov     cx,ax
320         mov     eax,ecx             ; EAX := timer count
321 else
322         mov     dx,cx
323 endif
324         pop     ebx                 ; Restore EBX for 32 bit code
325         ret
326
327 cprocend
328
329 ;----------------------------------------------------------------------------
330 ; unsigned long LZ_timerCount(void);
331 ;----------------------------------------------------------------------------
332 ; Returns an unsigned long representing the net time in microseconds.
333 ;
334 ; If an hour has passed while timing, we return 0xFFFFFFFF as the count
335 ; (which is not a possible count in itself).
336 ;----------------------------------------------------------------------------
337 cprocstart  LZ_timerCount
338
339         push    ebx                 ; Save EBX for 32 bit code
340
341 ; See if a midnight boundary has passed and adjust the finishing BIOS
342 ; count by the number of ticks in 24 hours. We wont be able to detect
343 ; more than 24 hours, but at least we can time across a midnight
344 ; boundary
345
346         mov     eax,[EndBIOSCount]      ; Is end < start?
347         cmp     eax,[StartBIOSCount]
348         jae     @@CheckForHour          ; No, check for hour passing
349
350 ; Adjust the finishing time by adding the number of ticks in 24 hours
351 ; (1573040).
352
353         add     [DWORD EndBIOSCount],1800B0h
354
355 ; See if more than an hour passed during timing. If so, notify the user.
356
357 @@CheckForHour:
358         mov     ax,[WORD StartBIOSCount+2]
359         cmp     ax,[WORD EndBIOSCount+2]
360         jz      @@CalcBIOSTime      ; Hour count didn't change, so
361                                     ;  everything is fine
362
363         inc     ax
364         cmp     ax,[WORD EndBIOSCount+2]
365         jnz     @@TestTooLong       ; Two hour boundaries passed, so the
366                                     ;  results are no good
367         mov     ax,[WORD EndBIOSCount]
368         cmp     ax,[WORD StartBIOSCount]
369         jb      @@CalcBIOSTime      ; a single hour boundary passed. That's
370                                     ; OK, so long as the total time wasn't
371                                     ; more than an hour.
372
373 ; Over an hour elapsed passed during timing, which renders
374 ; the results invalid. Notify the user. This misses the case where a
375 ; multiple of 24 hours has passed, but we'll rely on the perspicacity of
376 ; the user to detect that case :-).
377
378 @@TestTooLong:
379 ifdef   flatmodel
380         mov     eax,0FFFFFFFFh
381 else
382         mov     ax,0FFFFh
383         mov     dx,0FFFFh
384 endif
385         jmp     short @@Done
386
387 ; Convert the BIOS time to microseconds
388
389 @@CalcBIOSTime:
390         mov     ax,[WORD EndBIOSCount]
391         sub     ax,[WORD StartBIOSCount]
392         mov     dx,54925            ; Number of microseconds each
393                                     ;  BIOS count represents.
394         mul     dx
395         mov     bx,ax               ; set aside BIOS count in
396         mov     cx,dx               ;  microseconds
397
398 ; Convert timer count to microseconds
399
400         push    _si
401         mov     ax,[EndTimedCount]
402         mov     si,8381
403         mul     si
404         mov     si,10000
405         div     si                  ; * 0.8381 = * 8381 / 10000
406         pop     _si
407
408 ; Add the timer and BIOS counts together to get an overall time in
409 ; microseconds.
410
411         add     ax,bx
412         adc     cx,0
413 ifdef flatmodel
414         shl     ecx,16
415         mov     cx,ax
416         mov     eax,ecx             ; EAX := timer count
417 else
418         mov     dx,cx
419 endif
420
421 @@Done: pop     ebx                 ; Restore EBX for 32 bit code
422         ret
423
424 cprocend
425
426 cprocstart   LZ_disable
427         cli
428         ret
429 cprocend
430
431 cprocstart   LZ_enable
432         sti
433         ret
434 cprocend
435
436 endcodeseg  _lztimer
437
438         END