X-Git-Url: http://4ch.mooo.com/gitweb/?a=blobdiff_plain;f=16%2Fscrasm%2FLZTIMER.ASM;fp=16%2Fscrasm%2FLZTIMER.ASM;h=0000000000000000000000000000000000000000;hb=4b23f27092a9470a741e3a18261ad389fd1929db;hp=5fed7be109c5f1a5772c8df2a87e8c68037a020f;hpb=5d8d1deb6c3520abadbad86d202ea453df77bfc2;p=16.git diff --git a/16/scrasm/LZTIMER.ASM b/16/scrasm/LZTIMER.ASM deleted file mode 100755 index 5fed7be1..00000000 --- a/16/scrasm/LZTIMER.ASM +++ /dev/null @@ -1,636 +0,0 @@ -; -; *** Listing 2-5 *** -; -; The long-period Zen timer. (LZTIMER.ASM) -; 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. -; Because interrupts are left on (in order to allow the timer -; interrupt to be recognized), this is less accurate than the -; precision Zen timer, so it is best used only to time code that takes -; more than about 54 milliseconds to execute (code that the precision -; Zen timer reports overflow on). Resolution is limited by the -; occurrence of timer interrupts. -; -; By Michael Abrash 4/26/89 -; -; Externally callable routines: -; -; ZTimerOn: Saves the BIOS time of day count and starts the -; long-period Zen timer. -; -; ZTimerOff: Stops the long-period Zen timer and saves the timer -; count and the BIOS time-of-day count. -; -; ZTimerReport: Prints the time that passed between starting and -; stopping the timer. -; -; Note: If either more than an hour passes or midnight falls between -; calls to ZTimerOn and ZTimerOff, an error is reported. For -; timing code that takes more than a few minutes to execute, -; either the DOS TIME command in a batch file before and after -; execution of the code to time or the use of the DOS -; time-of-day function in place of the long-period Zen timer is -; more than adequate. -; -; Note: The PS/2 version is assembled by setting the symbol PS2 to 1. -; PS2 must be set to 1 on PS/2 computers because the PS/2's -; timers are not compatible with an undocumented timer-stopping -; feature of the 8253; the alternative timing approach that -; must be used on PS/2 computers leaves a short window -; during which the timer 0 count and the BIOS timer count may -; not be synchronized. You should also set the PS2 symbol to -; 1 if you're getting erratic or obviously incorrect results. -; -; Note: When PS2 is 0, the code relies on an undocumented 8253 -; feature to get more reliable readings. It is possible that -; the 8253 (or whatever chip is emulating the 8253) may be put -; into an undefined or incorrect state when this feature is -; used. -; -; *************************************************************** -; * If your computer displays any hint of erratic behavior * -; * after the long-period Zen timer is used, such as the floppy * -; * drive failing to operate properly, reboot the system, set * -; * PS2 to 1 and leave it that way! * -; *************************************************************** -; -; 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, keys, 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 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. -; - DOSSEG - .model small - .code - public ZTimerOn, ZTimerOff, ZTimerReport - -; -; Set PS2 to 0 to assemble for use on a fully 8253-compatible -; system; when PS2 is 0, the readings are more reliable if the -; computer supports the undocumented timer-stopping feature, -; but may be badly off if that feature is not supported. In -; fact, timer-stopping may interfere with your computer's -; overall operation by putting the 8253 into an undefined or -; incorrect state. Use with caution!!! -; -; Set PS2 to 1 to assemble for use on non-8253-compatible -; systems, including PS/2 computers; when PS2 is 1, readings -; may occasionally be off by 54 ms, but the code will work -; properly on all systems. -; -; A setting of 1 is safer and will work on more systems, -; while a setting of 0 produces more reliable results in systems -; which support the undocumented timer-stopping feature of the -; 8253. The choice is yours. -; -PS2 equ 1 -; -; Base address of the 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 segment. -; -TIMER_COUNT equ 46ch -; -; Macro to emulate a POPF instruction in order to fix the bug in some -; 80286 chips which allows interrupts to occur during a POPF even when -; interrupts remain disabled. -; -MPOPF macro - local p1, p2 - jmp short p2 -p1: iret ;jump to pushed address & pop flags -p2: push cs ;construct far return address to - call p1 ; the next instruction - endm - -; -; 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. -; -DELAY macro - jmp $+2 - jmp $+2 - jmp $+2 - endm - -StartBIOSCountLow dw ? ;BIOS count low word at the - ; start of the timing period -StartBIOSCountHigh dw ? ;BIOS count high word at the - ; start of the timing period -EndBIOSCountLow dw ? ;BIOS count low word at the - ; end of the timing period -EndBIOSCountHigh dw ? ;BIOS count high word at the - ; end of the timing period -EndTimedCount dw ? ;timer 0 count at the end of - ; the timing period -ReferenceCount dw ? ;number of counts required to - ; execute timer overhead code -; -; String printed to report results. -; -OutputStr label byte - db 0dh, 0ah, 'Timed count: ' -TimedCountStr db 10 dup (?) - db ' microseconds', 0dh, 0ah - db '$' -; -; Temporary storage for timed count as it's divided down by powers -; of ten when converting from doubleword binary to ASCII. -; -CurrentCountLow dw ? -CurrentCountHigh dw ? -; -; Powers of ten table used to perform division by 10 when doing -; doubleword conversion from binary to ASCII. -; -PowersOfTen label word - dd 1 - dd 10 - dd 100 - dd 1000 - dd 10000 - dd 100000 - dd 1000000 - dd 10000000 - dd 100000000 - dd 1000000000 -PowersOfTenEnd label word -; -; String printed to report that the high word of the BIOS count -; changed while timing (an hour elapsed or midnight was crossed), -; and so the count is invalid and the test needs to be rerun. -; -TurnOverStr label byte - db 0dh, 0ah - db '****************************************************' - db 0dh, 0ah - db '* Either midnight passed or an hour or more passed *' - db 0dh, 0ah - db '* while timing was in progress. If the former was *' - db 0dh, 0ah - db '* the case, please rerun the test; if the latter *' - db 0dh, 0ah - db '* was the case, the test code takes too long to *' - db 0dh, 0ah - db '* run to be timed by the long-period Zen timer. *' - db 0dh, 0ah - db '* Suggestions: use the DOS TIME command, the DOS *' - db 0dh, 0ah - db '* time function, or a watch. *' - db 0dh, 0ah - db '****************************************************' - db 0dh, 0ah - db '$' - -;******************************************************************** -;* Routine called to start timing. * -;******************************************************************** - -ZTimerOn proc near - -; -; Save the context of the program being timed. -; - push ax - pushf -; -; Set 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 -; -; In case interrupts are disabled, enable interrupts briefly to allow -; the interrupt generated when switching from mode 3 to mode 2 to be -; recognized. Interrupts must be enabled for at least 210 ns to allow -; time for that interrupt to occur. Here, 10 jumps are used for the -; delay to ensure that the delay time will be more than long enough -; even on a very fast PC. -; - pushf - sti - rept 10 - jmp $+2 - endm - MPOPF -; -; Store the timing start BIOS count. -; (Since the timer count was just set to 0, the BIOS count will -; stay the same for the next 54 ms, so we don't need to disable -; interrupts in order to avoid getting a half-changed count.) -; - push ds - sub ax,ax - mov ds,ax - mov ax,ds:[TIMER_COUNT+2] - mov cs:[StartBIOSCountHigh],ax - mov ax,ds:[TIMER_COUNT] - mov cs:[StartBIOSCountLow],ax - pop ds -; -; 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 -; -; Restore the context of the program being timed and return to it. -; - MPOPF - pop ax - ret - -ZTimerOn endp - -;******************************************************************** -;* Routine called to stop timing and get count. * -;******************************************************************** - -ZTimerOff proc near - -; -; Save the context of the program being timed. -; - pushf - push ax - push cx -; -; In case interrupts are disabled, enable interrupts briefly to allow -; any pending timer interrupt to be handled. Interrupts must be -; enabled for at least 210 ns to allow time for that interrupt to -; occur. Here, 10 jumps are used for the delay to ensure that the -; delay time will be more than long enough even on a very fast PC. -; - sti - rept 10 - jmp $+2 - endm - -; -; Latch the timer count. -; - -if PS2 - - mov al,00000000b - out MODE_8253,al ;latch timer 0 count -; -; This is where a one-instruction-long window exists on the PS/2. -; The timer count and the BIOS count can lose synchronization; -; since the timer keeps counting after it's latched, it can turn -; over right after it's latched and cause the BIOS count to turn -; over before interrupts are disabled, leaving us with the timer -; count from before the timer turned over coupled with the BIOS -; count from after the timer turned over. The result is a count -; that's 54 ms too long. -; - -else - -; -; Set timer 0 to mode 2 (divide-by-N), waiting for a 2-byte count -; load, which stops timer 0 until the count is loaded. (Only works -; on fully 8253-compatible chips.) -; - mov al,00110100b ;mode 2 - out MODE_8253,al - DELAY - mov al,00000000b ;latch timer 0 count - out MODE_8253,al - -endif - - cli ;stop the BIOS count -; -; Read the BIOS count. (Since interrupts are disabled, the BIOS -; count won't change.) -; - push ds - sub ax,ax - mov ds,ax - mov ax,ds:[TIMER_COUNT+2] - mov cs:[EndBIOSCountHigh],ax - mov ax,ds:[TIMER_COUNT] - mov cs:[EndBIOSCountLow],ax - pop ds -; -; Read the timer count and save it. -; - in al,TIMER_0_8253 ;lsb - DELAY - mov ah,al - in al,TIMER_0_8253 ;msb - xchg ah,al - neg ax ;convert from countdown - ; remaining to elapsed - ; count - mov cs:[EndTimedCount],ax -; -; Restart timer 0, which is still waiting for an initial count -; to be loaded. -; - -ife PS2 - - DELAY - mov al,00110100b ;mode 2, waiting to load a - ; 2-byte count - out MODE_8253,al - DELAY - sub al,al - out TIMER_0_8253,al ;lsb - DELAY - mov al,ah - out TIMER_0_8253,al ;msb - DELAY - -endif - - sti ;let the BIOS count continue -; -; Time a zero-length code fragment, to get a reference for how -; much overhead this routine has. Time it 16 times and average it, -; for accuracy, rounding the result. -; - mov cs:[ReferenceCount],0 - mov cx,16 - cli ;interrupts off to allow a - ; precise reference count -RefLoop: - call ReferenceZTimerOn - call ReferenceZTimerOff - loop RefLoop - sti - add cs:[ReferenceCount],8 ;total + (0.5 * 16) - mov cl,4 - shr cs:[ReferenceCount],cl ;(total) / 16 + 0.5 -; -; Restore the context of the program being timed and return to it. -; - pop cx - pop ax - MPOPF - ret - -ZTimerOff endp - -; -; Called by ZTimerOff to start the timer for overhead measurements. -; - -ReferenceZTimerOn proc near -; -; Save the context of the program being timed. -; - push ax - pushf -; -; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause -; linear counting rather than count-by-two counting. -; - mov al,00110100b ;mode 2 - out MODE_8253,al -; -; Set the timer count to 0. -; - DELAY - sub al,al - out TIMER_0_8253,al ;lsb - DELAY - out TIMER_0_8253,al ;msb -; -; Restore the context of the program being timed and return to it. -; - MPOPF - pop ax - ret - -ReferenceZTimerOn endp - -; -; Called by ZTimerOff to stop the timer and add the result to -; ReferenceCount for overhead measurements. Doesn't need to look -; at the BIOS count because timing a zero-length code fragment -; isn't going to take anywhere near 54 ms. -; - -ReferenceZTimerOff proc near -; -; Save the context of the program being timed. -; - pushf - push ax - push cx - -; -; Match the interrupt-window delay in ZTimerOff. -; - sti - rept 10 - jmp $+2 - endm - - mov al,00000000b - out MODE_8253,al ;latch timer -; -; Read the count and save it. -; - DELAY - in al,TIMER_0_8253 ;lsb - DELAY - mov ah,al - in al,TIMER_0_8253 ;msb - xchg ah,al - neg ax ;convert from countdown - ; remaining to elapsed - ; count - add cs:[ReferenceCount],ax -; -; Restore the context and return. -; - pop cx - pop ax - MPOPF - ret - -ReferenceZTimerOff endp - -;******************************************************************** -;* Routine called to report timing results. * -;******************************************************************** - -ZTimerReport proc near - - pushf - push ax - push bx - push cx - push dx - push si - push di - push ds -; - push cs ;DOS functions require that DS point - pop ds ; to text to be displayed on the screen - assume ds:_TEXT -; -; See if midnight or more than an hour passed during timing. If so, -; notify the user. -; - mov ax,[StartBIOSCountHigh] - cmp ax,[EndBIOSCountHigh] - jz CalcBIOSTime ;hour count didn't change, - ; so everything's fine - inc ax - cmp ax,[EndBIOSCountHigh] - jnz TestTooLong ;midnight or two hour - ; boundaries passed, so the - ; results are no good - mov ax,[EndBIOSCountLow] - cmp ax,[StartBIOSCountLow] - 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 or midnight 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: - mov ah,9 - mov dx,offset TurnOverStr - int 21h - jmp short ZTimerReportDone -; -; Convert the BIOS time to microseconds. -; -CalcBIOSTime: - mov ax,[EndBIOSCountLow] - sub ax,[StartBIOSCountLow] - 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. -; - mov ax,[EndTimedCount] - mov si,8381 - mul si - mov si,10000 - div si ;* .8381 = * 8381 / 10000 -; -; Add timer and BIOS counts together to get an overall time in -; microseconds. -; - add bx,ax - adc cx,0 -; -; Subtract the timer overhead and save the result. -; - mov ax,[ReferenceCount] - mov si,8381 ;convert the reference count - mul si ; to microseconds - mov si,10000 - div si ;* .8381 = * 8381 / 10000 - sub bx,ax - sbb cx,0 - mov [CurrentCountLow],bx - mov [CurrentCountHigh],cx -; -; Convert the result to an ASCII string by trial subtractions of -; powers of 10. -; - mov di,offset PowersOfTenEnd - offset PowersOfTen - 4 - mov si,offset TimedCountStr -CTSNextDigit: - mov bl,'0' -CTSLoop: - mov ax,[CurrentCountLow] - mov dx,[CurrentCountHigh] - sub ax,PowersOfTen[di] - sbb dx,PowersOfTen[di+2] - jc CTSNextPowerDown - inc bl - mov [CurrentCountLow],ax - mov [CurrentCountHigh],dx - jmp CTSLoop -CTSNextPowerDown: - mov [si],bl - inc si - sub di,4 - jns CTSNextDigit -; -; -; Print the results. -; - mov ah,9 - mov dx,offset OutputStr - int 21h -; -ZTimerReportDone: - pop ds - pop di - pop si - pop dx - pop cx - pop bx - pop ax - MPOPF - ret - -ZTimerReport endp - - end - \ No newline at end of file