summaryrefslogtreecommitdiffstats
path: root/board/MAI/bios_emulator/scitech/src/pm/dos/_lztimer.asm
diff options
context:
space:
mode:
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.asm438
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 0000000000..a4a9c7916e
--- /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
OpenPOWER on IntegriCloud