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