diff options
Diffstat (limited to 'board/MAI/bios_emulator/scitech/src/pm/dos/_lztimer.asm')
-rw-r--r-- | board/MAI/bios_emulator/scitech/src/pm/dos/_lztimer.asm | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/board/MAI/bios_emulator/scitech/src/pm/dos/_lztimer.asm b/board/MAI/bios_emulator/scitech/src/pm/dos/_lztimer.asm new file mode 100644 index 0000000..a4a9c79 --- /dev/null +++ b/board/MAI/bios_emulator/scitech/src/pm/dos/_lztimer.asm @@ -0,0 +1,438 @@ +;**************************************************************************** +;* +;* SciTech OS Portability Manager Library +;* +;* ======================================================================== +;* +;* The contents of this file are subject to the SciTech MGL Public +;* License Version 1.0 (the "License"); you may not use this file +;* except in compliance with the License. You may obtain a copy of +;* the License at http://www.scitechsoft.com/mgl-license.txt +;* +;* Software distributed under the License is distributed on an +;* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +;* implied. See the License for the specific language governing +;* rights and limitations under the License. +;* +;* The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc. +;* +;* The Initial Developer of the Original Code is SciTech Software, Inc. +;* All Rights Reserved. +;* +;* ======================================================================== +;* +;* Language: NASM or TASM Assembler +;* Environment: IBM PC (MS DOS) +;* +;* Description: Uses the 8253 timer and the BIOS time-of-day count to time +;* the performance of code that takes less than an hour to +;* execute. +;* +;* The routines in this package only works with interrupts +;* enabled, and in fact will explicitly turn interrupts on +;* in order to ensure we get accurate results from the timer. +;* +;* Externally 'C' callable routines: +;* +;* LZ_timerOn: Saves the BIOS time of day count and starts the +;* long period Zen Timer. +;* +;* LZ_timerLap: Latches the current count, and keeps the timer running +;* +;* LZ_timerOff: Stops the long-period Zen Timer and saves the timer +;* count and the BIOS time of day count. +;* +;* LZ_timerCount: Returns an unsigned long representing the timed count +;* in microseconds. If more than an hour passed during +;* the timing interval, LZ_timerCount will return the +;* value 0xFFFFFFFF (an invalid count). +;* +;* Note: If either more than an hour passes between calls to LZ_timerOn +;* and LZ_timerOff, an error is reported. For timing code that takes +;* more than a few minutes to execute, use the low resolution +;* Ultra Long Period Zen Timer code, which should be accurate +;* enough for most purposes. +;* +;* Note: Each block of code being timed should ideally be run several +;* times, with at least two similar readings required to +;* establish a true measurement, in order to eliminate any +;* variability caused by interrupts. +;* +;* Note: Interrupts must not be disabled for more than 54 ms at a +;* stretch during the timing interval. Because interrupts are +;* enabled, key, mice, and other devices that generate interrupts +;* should not be used during the timing interval. +;* +;* Note: Any extra code running off the timer interrupt (such as +;* some memory resident utilities) will increase the time +;* measured by the Zen Timer. +;* +;* Note: These routines can introduce inaccuracies of up to a few +;* tenths of a second into the system clock count for each +;* code section being timed. Consequently, it's a good idea to +;* reboot at the conclusion of timing sessions. (The +;* battery-backed clock, if any, is not affected by the Zen +;* timer.) +;* +;* All registers and all flags are preserved by all routines, except +;* interrupts which are always turned on +;* +;**************************************************************************** + + IDEAL + +include "scitech.mac" + +;**************************************************************************** +; +; Equates used by long period Zen Timer +; +;**************************************************************************** + +; Base address of 8253 timer chip + +BASE_8253 equ 40h + +; The address of the timer 0 count registers in the 8253 + +TIMER_0_8253 equ BASE_8253 + 0 + +; The address of the mode register in the 8253 + +MODE_8253 equ BASE_8253 + 3 + +; The address of the BIOS timer count variable in the BIOS data area. + +TIMER_COUNT equ 6Ch + +; Macro to delay briefly to ensure that enough time has elapsed between +; successive I/O accesses so that the device being accessed can respond +; to both accesses even on a very fast PC. + +ifdef USE_NASM +%macro DELAY 0 + jmp short $+2 + jmp short $+2 + jmp short $+2 +%endmacro +else +macro DELAY + jmp short $+2 + jmp short $+2 + jmp short $+2 +endm +endif + +header _lztimer + +begdataseg _lztimer + + cextern _ZTimerBIOSPtr,DPTR + +StartBIOSCount dd 0 ; Starting BIOS count dword +EndBIOSCount dd 0 ; Ending BIOS count dword +EndTimedCount dw 0 ; Timer 0 count at the end of timing period + +enddataseg _lztimer + +begcodeseg _lztimer ; Start of code segment + +;---------------------------------------------------------------------------- +; void LZ_timerOn(void); +;---------------------------------------------------------------------------- +; Starts the Long period Zen timer counting. +;---------------------------------------------------------------------------- +cprocstart LZ_timerOn + +; Set the timer 0 of the 8253 to mode 2 (divide-by-N), to cause +; linear counting rather than count-by-two counting. Also stops +; timer 0 until the timer count is loaded, except on PS/2 computers. + + mov al,00110100b ; mode 2 + out MODE_8253,al + +; Set the timer count to 0, so we know we won't get another timer +; interrupt right away. Note: this introduces an inaccuracy of up to 54 ms +; in the system clock count each time it is executed. + + DELAY + sub al,al + out TIMER_0_8253,al ; lsb + DELAY + out TIMER_0_8253,al ; msb + +; Store the timing start BIOS count + + use_es +ifdef flatmodel + mov ebx,[_ZTimerBIOSPtr] +else + les bx,[_ZTimerBIOSPtr] +endif + cli ; No interrupts while we grab the count + mov eax,[_ES _bx+TIMER_COUNT] + sti + mov [StartBIOSCount],eax + unuse_es + +; Set the timer count to 0 again to start the timing interval. + + mov al,00110100b ; set up to load initial + out MODE_8253,al ; timer count + DELAY + sub al,al + out TIMER_0_8253,al ; load count lsb + DELAY + out TIMER_0_8253,al ; load count msb + + ret + +cprocend + +;---------------------------------------------------------------------------- +; void LZ_timerOff(void); +;---------------------------------------------------------------------------- +; Stops the long period Zen timer and saves count. +;---------------------------------------------------------------------------- +cprocstart LZ_timerOff + +; Latch the timer count. + + mov al,00000000b ; latch timer 0 + out MODE_8253,al + cli ; Stop the BIOS count + +; Read the BIOS count. (Since interrupts are disabled, the BIOS +; count won't change). + + use_es +ifdef flatmodel + mov ebx,[_ZTimerBIOSPtr] +else + les bx,[_ZTimerBIOSPtr] +endif + mov eax,[_ES _bx+TIMER_COUNT] + mov [EndBIOSCount],eax + unuse_es + +; Read out the count we latched earlier. + + in al,TIMER_0_8253 ; least significant byte + DELAY + mov ah,al + in al,TIMER_0_8253 ; most significant byte + xchg ah,al + neg ax ; Convert from countdown remaining + ; to elapsed count + mov [EndTimedCount],ax + sti ; Let the BIOS count continue + + ret + +cprocend + +;---------------------------------------------------------------------------- +; unsigned long LZ_timerLap(void) +;---------------------------------------------------------------------------- +; Latches the current count and converts it to a microsecond timing value, +; but leaves the timer still running. We dont check for and overflow, +; where the time has gone over an hour in this routine, since we want it +; to execute as fast as possible. +;---------------------------------------------------------------------------- +cprocstart LZ_timerLap + + push ebx ; Save EBX for 32 bit code + +; Latch the timer count. + + mov al,00000000b ; latch timer 0 + out MODE_8253,al + cli ; Stop the BIOS count + +; Read the BIOS count. (Since interrupts are disabled, the BIOS +; count wont change). + + use_es +ifdef flatmodel + mov ebx,[_ZTimerBIOSPtr] +else + les bx,[_ZTimerBIOSPtr] +endif + mov eax,[_ES _bx+TIMER_COUNT] + mov [EndBIOSCount],eax + unuse_es + +; Read out the count we latched earlier. + + in al,TIMER_0_8253 ; least significant byte + DELAY + mov ah,al + in al,TIMER_0_8253 ; most significant byte + xchg ah,al + neg ax ; Convert from countdown remaining + ; to elapsed count + mov [EndTimedCount],ax + sti ; Let the BIOS count continue + +; See if a midnight boundary has passed and adjust the finishing BIOS +; count by the number of ticks in 24 hours. We wont be able to detect +; more than 24 hours, but at least we can time across a midnight +; boundary + + mov eax,[EndBIOSCount] ; Is end < start? + cmp eax,[StartBIOSCount] + jae @@CalcBIOSTime ; No, calculate the time taken + +; Adjust the finishing time by adding the number of ticks in 24 hours +; (1573040). + + add [DWORD EndBIOSCount],1800B0h + +; Convert the BIOS time to microseconds + +@@CalcBIOSTime: + mov ax,[WORD EndBIOSCount] + sub ax,[WORD StartBIOSCount] + mov dx,54925 ; Number of microseconds each + ; BIOS count represents. + mul dx + mov bx,ax ; set aside BIOS count in + mov cx,dx ; microseconds + +; Convert timer count to microseconds + + push _si + mov ax,[EndTimedCount] + mov si,8381 + mul si + mov si,10000 + div si ; * 0.8381 = * 8381 / 10000 + pop _si + +; Add the timer and BIOS counts together to get an overall time in +; microseconds. + + add ax,bx + adc cx,0 +ifdef flatmodel + shl ecx,16 + mov cx,ax + mov eax,ecx ; EAX := timer count +else + mov dx,cx +endif + pop ebx ; Restore EBX for 32 bit code + ret + +cprocend + +;---------------------------------------------------------------------------- +; unsigned long LZ_timerCount(void); +;---------------------------------------------------------------------------- +; Returns an unsigned long representing the net time in microseconds. +; +; If an hour has passed while timing, we return 0xFFFFFFFF as the count +; (which is not a possible count in itself). +;---------------------------------------------------------------------------- +cprocstart LZ_timerCount + + push ebx ; Save EBX for 32 bit code + +; See if a midnight boundary has passed and adjust the finishing BIOS +; count by the number of ticks in 24 hours. We wont be able to detect +; more than 24 hours, but at least we can time across a midnight +; boundary + + mov eax,[EndBIOSCount] ; Is end < start? + cmp eax,[StartBIOSCount] + jae @@CheckForHour ; No, check for hour passing + +; Adjust the finishing time by adding the number of ticks in 24 hours +; (1573040). + + add [DWORD EndBIOSCount],1800B0h + +; See if more than an hour passed during timing. If so, notify the user. + +@@CheckForHour: + mov ax,[WORD StartBIOSCount+2] + cmp ax,[WORD EndBIOSCount+2] + jz @@CalcBIOSTime ; Hour count didn't change, so + ; everything is fine + + inc ax + cmp ax,[WORD EndBIOSCount+2] + jnz @@TestTooLong ; Two hour boundaries passed, so the + ; results are no good + mov ax,[WORD EndBIOSCount] + cmp ax,[WORD StartBIOSCount] + jb @@CalcBIOSTime ; a single hour boundary passed. That's + ; OK, so long as the total time wasn't + ; more than an hour. + +; Over an hour elapsed passed during timing, which renders +; the results invalid. Notify the user. This misses the case where a +; multiple of 24 hours has passed, but we'll rely on the perspicacity of +; the user to detect that case :-). + +@@TestTooLong: +ifdef flatmodel + mov eax,0FFFFFFFFh +else + mov ax,0FFFFh + mov dx,0FFFFh +endif + jmp short @@Done + +; Convert the BIOS time to microseconds + +@@CalcBIOSTime: + mov ax,[WORD EndBIOSCount] + sub ax,[WORD StartBIOSCount] + mov dx,54925 ; Number of microseconds each + ; BIOS count represents. + mul dx + mov bx,ax ; set aside BIOS count in + mov cx,dx ; microseconds + +; Convert timer count to microseconds + + push _si + mov ax,[EndTimedCount] + mov si,8381 + mul si + mov si,10000 + div si ; * 0.8381 = * 8381 / 10000 + pop _si + +; Add the timer and BIOS counts together to get an overall time in +; microseconds. + + add ax,bx + adc cx,0 +ifdef flatmodel + shl ecx,16 + mov cx,ax + mov eax,ecx ; EAX := timer count +else + mov dx,cx +endif + +@@Done: pop ebx ; Restore EBX for 32 bit code + ret + +cprocend + +cprocstart LZ_disable + cli + ret +cprocend + +cprocstart LZ_enable + sti + ret +cprocend + +endcodeseg _lztimer + + END |