commit
a17b3067ed
3 changed files with 88 additions and 88 deletions
|
|
@ -254,7 +254,7 @@ requires over two and one-half minutes to checksum *one* file!
|
||||||
These results make it clear that it's folly to rely on your compiler's
|
These results make it clear that it's folly to rely on your compiler's
|
||||||
optimization to make your programs fast. Listing 1.1 is simply poorly
|
optimization to make your programs fast. Listing 1.1 is simply poorly
|
||||||
designed, and no amount of compiler optimization will compensate for
|
designed, and no amount of compiler optimization will compensate for
|
||||||
that failing. To drive home the point, conListings 1.2 and 1.3, which
|
that failing. To drive home the point, Listings 1.2 and 1.3, which
|
||||||
together are equivalent to Listing 1.1 except that the entire checksum
|
together are equivalent to Listing 1.1 except that the entire checksum
|
||||||
loop is written in tight assembly code. The assembly language
|
loop is written in tight assembly code. The assembly language
|
||||||
implementation is indeed faster than any of the C versions, as shown in
|
implementation is indeed faster than any of the C versions, as shown in
|
||||||
|
|
@ -419,7 +419,7 @@ Listing 1.1.
|
||||||
How can we speed up Listing 1.1? It should be clear that we must somehow
|
How can we speed up Listing 1.1? It should be clear that we must somehow
|
||||||
avoid invoking DOS for every byte in the file, and that means reading
|
avoid invoking DOS for every byte in the file, and that means reading
|
||||||
more than one byte at a time, then buffering the data and parceling it
|
more than one byte at a time, then buffering the data and parceling it
|
||||||
out for examination one byte at a time. By gosh, that's a description of
|
out for examination one byte at a time. My gosh, that's a description of
|
||||||
C's stream I/O feature, whereby C reads files in chunks and buffers the
|
C's stream I/O feature, whereby C reads files in chunks and buffers the
|
||||||
bytes internally, doling them out to the application as needed by
|
bytes internally, doling them out to the application as needed by
|
||||||
reading them from memory rather than calling DOS. Let's try using stream
|
reading them from memory rather than calling DOS. Let's try using stream
|
||||||
|
|
@ -500,7 +500,7 @@ your programs that directly affect response time. Notice, for example,
|
||||||
that I haven't bothered to implement a version of the checksum program
|
that I haven't bothered to implement a version of the checksum program
|
||||||
entirely in assembly; Listings 1.2 and 1.6 call assembly subroutines
|
entirely in assembly; Listings 1.2 and 1.6 call assembly subroutines
|
||||||
that handle the time-critical operations, but C is still used for
|
that handle the time-critical operations, but C is still used for
|
||||||
checking command-line parameters, operning files, printing, and the
|
checking command-line parameters, opening files, printing, and the
|
||||||
like.
|
like.
|
||||||
|
|
||||||
> 
|
> 
|
||||||
|
|
@ -521,7 +521,7 @@ Listing 1.4 is good, but let's see if there are other—perhaps less
|
||||||
obvious—ways to get the same results faster. Let's start by considering
|
obvious—ways to get the same results faster. Let's start by considering
|
||||||
why Listing 1.4 is so much better than Listing 1.1. Like `read()`,
|
why Listing 1.4 is so much better than Listing 1.1. Like `read()`,
|
||||||
`getc()` calls DOS to read from the file; the speed improvement of
|
`getc()` calls DOS to read from the file; the speed improvement of
|
||||||
Listing 1.4 over Listing 1.1 occurs because `getc()` eads many bytes
|
Listing 1.4 over Listing 1.1 occurs because `getc()` reads many bytes
|
||||||
at once via DOS, then manages those bytes for us. That's faster than
|
at once via DOS, then manages those bytes for us. That's faster than
|
||||||
reading them one at a time using `read()`—but there's no reason to
|
reading them one at a time using `read()`—but there's no reason to
|
||||||
think that it's faster than having our program read and manage blocks
|
think that it's faster than having our program read and manage blocks
|
||||||
|
|
@ -563,7 +563,7 @@ The third reason is often fallacious. C library functions are not always
|
||||||
written in assembly, nor are they always particularly well-optimized.
|
written in assembly, nor are they always particularly well-optimized.
|
||||||
(In fact, they're often written for *portability*, which has nothing to
|
(In fact, they're often written for *portability*, which has nothing to
|
||||||
do with optimization.) What's more, they're general-purpose functions,
|
do with optimization.) What's more, they're general-purpose functions,
|
||||||
and often can be outperformed by well-but-not- brilliantly-written code
|
and often can be outperformed by well-but-not-brilliantly-written code
|
||||||
that is well-matched to a specific task. As an example, consider Listing
|
that is well-matched to a specific task. As an example, consider Listing
|
||||||
1.5, which uses internal buffering to handle blocks of bytes at a time.
|
1.5, which uses internal buffering to handle blocks of bytes at a time.
|
||||||
Table 1.1 shows that Listing 1.5 is 2.5 to 4 times faster than Listing
|
Table 1.1 shows that Listing 1.5 is 2.5 to 4 times faster than Listing
|
||||||
|
|
@ -667,7 +667,7 @@ indeed make a significant difference. Table 1.1 indicates that the
|
||||||
optimized version of Listing 1.5 produced by Microsoft C outperforms an
|
optimized version of Listing 1.5 produced by Microsoft C outperforms an
|
||||||
unoptimized version of the same code by more than 60 percent. What's
|
unoptimized version of the same code by more than 60 percent. What's
|
||||||
more, a mostly-assembly version of Listing 1.5, shown in Listings 1.6
|
more, a mostly-assembly version of Listing 1.5, shown in Listings 1.6
|
||||||
and 1.7, outperforms even the best-optimized C version of List1.5 by 26
|
and 1.7, outperforms even the best-optimized C version of Listing 1.5 by 26
|
||||||
percent. These are considerable improvements, well worth pursuing—once
|
percent. These are considerable improvements, well worth pursuing—once
|
||||||
the design has been maxed out.
|
the design has been maxed out.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ enough.
|
||||||
The single most critical aspect of the hardware, and the one about which
|
The single most critical aspect of the hardware, and the one about which
|
||||||
it is hardest to learn, is the CPU. The x86 family CPUs have a complex,
|
it is hardest to learn, is the CPU. The x86 family CPUs have a complex,
|
||||||
irregular instruction set, and, unlike most processors, they are neither
|
irregular instruction set, and, unlike most processors, they are neither
|
||||||
straightforward nor wellregarding true code performance. What's more,
|
straightforward nor well-documented true code performance. What's more,
|
||||||
assembly is so difficult to learn that most articles and books that
|
assembly is so difficult to learn that most articles and books that
|
||||||
present assembly code settle for code that just works, rather than code
|
present assembly code settle for code that just works, rather than code
|
||||||
that pushes the CPU to its limits. In fact, since most articles and
|
that pushes the CPU to its limits. In fact, since most articles and
|
||||||
|
|
@ -293,7 +293,7 @@ all the more so given that compilers are constrained by the limitations
|
||||||
of high-level languages and by the process of transformation from
|
of high-level languages and by the process of transformation from
|
||||||
high-level to machine language. Consequently, carefully optimized
|
high-level to machine language. Consequently, carefully optimized
|
||||||
assembly is not just the language of choice but the *only* choice for
|
assembly is not just the language of choice but the *only* choice for
|
||||||
the 1percent to 10 percent of code—usually consisting of small,
|
the 1 percent to 10 percent of code—usually consisting of small,
|
||||||
well-defined subroutines—that determines overall program performance,
|
well-defined subroutines—that determines overall program performance,
|
||||||
and it is the only choice for code that must be as compact as possible,
|
and it is the only choice for code that must be as compact as possible,
|
||||||
as well. In the run-of-the-mill, non-time-critical portions of your
|
as well. In the run-of-the-mill, non-time-critical portions of your
|
||||||
|
|
|
||||||
|
|
@ -671,7 +671,7 @@ count reaches zero, the timer turns over and starts counting down again
|
||||||
without stopping, and a pulse is generated for a single clock period.
|
without stopping, and a pulse is generated for a single clock period.
|
||||||
While the pulse is not held for nearly as long as in square wave mode,
|
While the pulse is not held for nearly as long as in square wave mode,
|
||||||
it doesn't matter, since the 8259 interrupt controller is configured in
|
it doesn't matter, since the 8259 interrupt controller is configured in
|
||||||
the PC to be edgeand hence cares only about the existence of a pulse
|
the PC to be edge-triggered and hence cares only about the existence of a pulse
|
||||||
from timer 0, not the duration of the pulse. As a result, timer 0
|
from timer 0, not the duration of the pulse. As a result, timer 0
|
||||||
continues to generate timer interrupts in divide-by-N mode, and the
|
continues to generate timer interrupts in divide-by-N mode, and the
|
||||||
system clock continues to maintain good time.
|
system clock continues to maintain good time.
|
||||||
|
|
@ -688,7 +688,7 @@ the Zen timer shown in Listing 3.1 supports.
|
||||||
In fact, the Zen timer shown in Listing 3.1 can only time intervals of
|
In fact, the Zen timer shown in Listing 3.1 can only time intervals of
|
||||||
up to about 54 ms in length, since that is the period of time that can
|
up to about 54 ms in length, since that is the period of time that can
|
||||||
be measured by timer 0 before its count turns over and repeats.
|
be measured by timer 0 before its count turns over and repeats.
|
||||||
fifty-four ms may not seem like a very long time, but even a CPU as slow
|
Fifty-four ms may not seem like a very long time, but even a CPU as slow
|
||||||
as the 8088 can perform more than 1,000 divides in 54 ms, and division
|
as the 8088 can perform more than 1,000 divides in 54 ms, and division
|
||||||
is the single instruction that the 8088 performs most slowly. If a
|
is the single instruction that the 8088 performs most slowly. If a
|
||||||
measured period turns out to be longer than 54 ms (that is, if timer 0
|
measured period turns out to be longer than 54 ms (that is, if timer 0
|
||||||
|
|
@ -730,11 +730,11 @@ restart until the timing interval ends, losing time all the while.
|
||||||
|
|
||||||
The effects on the system time of the Zen timer aren't a matter for
|
The effects on the system time of the Zen timer aren't a matter for
|
||||||
great concern, as they are temporary, lasting only until the next warm
|
great concern, as they are temporary, lasting only until the next warm
|
||||||
or cold boot. System that have batteryclocks, (AT-style machines; that
|
or cold boot. System that have battery-backed clocks, (AT-style machines; that
|
||||||
is, virtually all machines in common use) automatically reset the
|
is, virtually all machines in common use) automatically reset the
|
||||||
correct time whenever the computer is booted, and systems without
|
correct time whenever the computer is booted, and systems without
|
||||||
battery-clocks prompt for the correct date and time when booted.
|
battery-backed clocks prompt for the correct date and time when booted.
|
||||||
Also,repeated use of the Zen timer usually makes the system clock slow
|
Also, repeated use of the Zen timer usually makes the system clock slow
|
||||||
by at most a total of a few seconds, unless code that takes much longer
|
by at most a total of a few seconds, unless code that takes much longer
|
||||||
than 54 ms to run is timed (in which case the Zen timer will notify you
|
than 54 ms to run is timed (in which case the Zen timer will notify you
|
||||||
that the code is too long to time).
|
that the code is too long to time).
|
||||||
|
|
@ -789,8 +789,8 @@ from timer counts to microseconds, and prints the resulting time in
|
||||||
microseconds to the standard output.
|
microseconds to the standard output.
|
||||||
|
|
||||||
Note that `ZTimerReport` need not be called immediately after
|
Note that `ZTimerReport` need not be called immediately after
|
||||||
`ZTimerOff`. In fact, after a given call to `ZTimerOff,
|
`ZTimerOff`. In fact, after a given call to `ZTimerOff`,
|
||||||
ZTimerReport` can be called at any time right up until the next call to
|
`ZTimerReport` can be called at any time right up until the next call to
|
||||||
`ZTimerOn`.
|
`ZTimerOn`.
|
||||||
|
|
||||||
You may want to use the Zen timer to measure several portions of a
|
You may want to use the Zen timer to measure several portions of a
|
||||||
|
|
@ -880,7 +880,7 @@ performance will be similar even on different IBM models; in fact, quite
|
||||||
the opposite is true. For example, every PS/2 computer, even the
|
the opposite is true. For example, every PS/2 computer, even the
|
||||||
relatively slow Model 30, executes code much faster than does a PC or
|
relatively slow Model 30, executes code much faster than does a PC or
|
||||||
XT. As another example, I set out to do the timings for my earlier book
|
XT. As another example, I set out to do the timings for my earlier book
|
||||||
*Zen of Assembly Language* on an XTcomputer, only to find that the
|
*Zen of Assembly Language* on an XT-compatible computer, only to find that the
|
||||||
computer wasn't quite IBM-compatible regarding code performance. The
|
computer wasn't quite IBM-compatible regarding code performance. The
|
||||||
differences were minor, mind you, but my experience illustrates the risk
|
differences were minor, mind you, but my experience illustrates the risk
|
||||||
of assuming that a specific make of computer will perform in a certain
|
of assuming that a specific make of computer will perform in a certain
|
||||||
|
|
@ -996,7 +996,7 @@ timing interval.
|
||||||
|
|
||||||
Listing 3.3 is used by naming it TESTCODE, assembling both Listing 3.2
|
Listing 3.3 is used by naming it TESTCODE, assembling both Listing 3.2
|
||||||
(which includes TESTCODE) and Listing 3.1 with TASM or MASM, and linking
|
(which includes TESTCODE) and Listing 3.1 with TASM or MASM, and linking
|
||||||
the two resulting OBJ files together by way of the Borland orMicrosoft
|
the two resulting OBJ files together by way of the Borland or Microsoft
|
||||||
linker. Listing 3.4 shows a batch file, PZTIME.BAT, which does all that;
|
linker. Listing 3.4 shows a batch file, PZTIME.BAT, which does all that;
|
||||||
when run, this batch file generates and runs the executable file
|
when run, this batch file generates and runs the executable file
|
||||||
PZTEST.EXE. PZTIME.BAT (Listing 3.4) assumes that the file PZTIMER.ASM
|
PZTEST.EXE. PZTIME.BAT (Listing 3.4) assumes that the file PZTIMER.ASM
|
||||||
|
|
@ -1111,8 +1111,8 @@ pztime <filename>
|
||||||
In fact, that's exactly how I timed each of the listings in this book.
|
In fact, that's exactly how I timed each of the listings in this book.
|
||||||
Code fragments you write yourself can be timed in just the same way. If
|
Code fragments you write yourself can be timed in just the same way. If
|
||||||
you wish to time code directly in place in your programs, rather than in
|
you wish to time code directly in place in your programs, rather than in
|
||||||
the test-bed program of Listing 3.2, simply insert calls to `ZTimerOn,
|
the test-bed program of Listing 3.2, simply insert calls to `ZTimerOn`,
|
||||||
ZTimerOff`, and `ZTimerReport` in the appropriate places and link
|
`ZTimerOff`, and `ZTimerReport` in the appropriate places and link
|
||||||
PZTIMER to your program.
|
PZTIMER to your program.
|
||||||
|
|
||||||
### The Long-Period Zen Timer
|
### The Long-Period Zen Timer
|
||||||
|
|
@ -1303,8 +1303,8 @@ computers.
|
||||||
; All registers and all flags are preserved by all routines.
|
; All registers and all flags are preserved by all routines.
|
||||||
;
|
;
|
||||||
|
|
||||||
Code segment word public ‘CODE'
|
Code segment word public 'CODE'
|
||||||
assume cs: Code, ds:nothing
|
assume cs:Code, ds:nothing
|
||||||
public ZTimerOn, ZTimerOff, ZTimerReport
|
public ZTimerOn, ZTimerOff, ZTimerReport
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
@ -1326,24 +1326,24 @@ Code segment word public ‘CODE'
|
||||||
; which support the undocumented timer-stopping feature of the
|
; which support the undocumented timer-stopping feature of the
|
||||||
; 8253. The choice is yours.
|
; 8253. The choice is yours.
|
||||||
;
|
;
|
||||||
PS2 equ1
|
PS2 equ 1
|
||||||
;
|
;
|
||||||
; Base address of the 8253 timer chip.
|
; Base address of the 8253 timer chip.
|
||||||
;
|
;
|
||||||
BASE_8253 equ40h
|
BASE_8253 equ 40h
|
||||||
;
|
;
|
||||||
; The address of the timer 0 count registers in the 8253.
|
; The address of the timer 0 count registers in the 8253.
|
||||||
;
|
;
|
||||||
TIMER_0_8253 equBASE_8253 + 0
|
TIMER_0_8253 equ BASE_8253 + 0
|
||||||
;
|
;
|
||||||
; The address of the mode register in the 8253.
|
; The address of the mode register in the 8253.
|
||||||
;
|
;
|
||||||
MODE_8253 equBASE_8253 + 3
|
MODE_8253 equ BASE_8253 + 3
|
||||||
;
|
;
|
||||||
; The address of the BIOS timer count variable in the BIOS
|
; The address of the BIOS timer count variable in the BIOS
|
||||||
; data segment.
|
; data segment.
|
||||||
;
|
;
|
||||||
TIMER_COUNT equ46ch
|
TIMER_COUNT equ 46ch
|
||||||
;
|
;
|
||||||
; Macro to emulate a POPF instruction in order to fix the bug in some
|
; 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
|
; 80286 chips which allows interrupts to occur during a POPF even when
|
||||||
|
|
@ -1353,9 +1353,9 @@ MPOPF macro
|
||||||
local p1, p2
|
local p1, p2
|
||||||
jmp short p2
|
jmp short p2
|
||||||
p1: iret ;jump to pushed address & pop flags
|
p1: iret ;jump to pushed address & pop flags
|
||||||
p2: pushcs ;construct far return address to
|
p2: push cs ;construct far return address to
|
||||||
call p1 ; the next instruction
|
call p1 ; the next instruction
|
||||||
endm
|
endm
|
||||||
|
|
||||||
;
|
;
|
||||||
; Macro to delay briefly to ensure that enough time has elapsed
|
; Macro to delay briefly to ensure that enough time has elapsed
|
||||||
|
|
@ -1383,11 +1383,11 @@ ReferenceCount dw ? ;number of counts required to
|
||||||
;
|
;
|
||||||
; String printed to report results.
|
; String printed to report results.
|
||||||
;
|
;
|
||||||
OutputStr labelbyte
|
OutputStr label byte
|
||||||
db 0dh, 0ah, ‘Timed count: ‘
|
db 0dh, 0ah, 'Timed count: '
|
||||||
TimedCountStr db10 dup (?)
|
TimedCountStr db 10 dup (?)
|
||||||
db' microseconds', 0dh, 0ah
|
db ' microseconds', 0dh, 0ah
|
||||||
db ‘$'
|
db '$'
|
||||||
;
|
;
|
||||||
; Temporary storage for timed count as it's divided down by powers
|
; Temporary storage for timed count as it's divided down by powers
|
||||||
; of ten when converting from doubleword binary to ASCII.
|
; of ten when converting from doubleword binary to ASCII.
|
||||||
|
|
@ -1398,7 +1398,7 @@ CurrentCountHigh dw ?
|
||||||
; Powers of ten table used to perform division by 10 when doing
|
; Powers of ten table used to perform division by 10 when doing
|
||||||
; doubleword conversion from binary to ASCII.
|
; doubleword conversion from binary to ASCII.
|
||||||
;
|
;
|
||||||
PowersOfTenlabelword
|
PowersOfTen label word
|
||||||
dd 1
|
dd 1
|
||||||
dd 10
|
dd 10
|
||||||
dd 100
|
dd 100
|
||||||
|
|
@ -1409,33 +1409,33 @@ PowersOfTenlabelword
|
||||||
dd 10000000
|
dd 10000000
|
||||||
dd 100000000
|
dd 100000000
|
||||||
dd 1000000000
|
dd 1000000000
|
||||||
PowersOfTenEnd labelword
|
PowersOfTenEnd label word
|
||||||
;
|
;
|
||||||
; String printed to report that the high word of the BIOS count
|
; String printed to report that the high word of the BIOS count
|
||||||
; changed while timing (an hour elapsed or midnight was crossed),
|
; changed while timing (an hour elapsed or midnight was crossed),
|
||||||
; and so the count is invalid and the test needs to be rerun.
|
; and so the count is invalid and the test needs to be rerun.
|
||||||
;
|
;
|
||||||
TurnOverStrlabelbyte
|
TurnOverStr label byte
|
||||||
db 0dh, 0ah
|
|
||||||
db ‘****************************************************'
|
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db'* Either midnight passed or an hour or more passed *'
|
db '****************************************************'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db'* while timing was in progress. If the former was *'
|
db '* Either midnight passed or an hour or more passed *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db'* the case, please rerun the test; if the latter *'
|
db '* while timing was in progress. If the former was *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db'* was the case, the test code takes too long to *'
|
db '* the case, please rerun the test; if the latter *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db'* run to be timed by the long-period Zen timer. *'
|
db '* was the case, the test code takes too long to *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘* Suggestions: use the DOS TIME command, the DOS *'
|
db '* run to be timed by the long-period Zen timer. *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘* time function, or a watch. *'
|
db '* Suggestions: use the DOS TIME command, the DOS *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db ‘****************************************************'
|
db '* time function, or a watch. *'
|
||||||
db 0dh, 0ah
|
db 0dh, 0ah
|
||||||
db'$'
|
db '****************************************************'
|
||||||
|
db 0dh, 0ah
|
||||||
|
db '$'
|
||||||
|
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
;* Routine called to start timing. *
|
;* Routine called to start timing. *
|
||||||
|
|
@ -1447,7 +1447,7 @@ ZTimerOn proc near
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
push ax
|
push ax
|
||||||
pus hf
|
pushf
|
||||||
;
|
;
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
||||||
; linear counting rather than count-by-two counting. Also stops
|
; linear counting rather than count-by-two counting. Also stops
|
||||||
|
|
@ -1463,10 +1463,10 @@ ZTimerOn proc near
|
||||||
; clock count each time it is executed.
|
; clock count each time it is executed.
|
||||||
;
|
;
|
||||||
DELAY
|
DELAY
|
||||||
subal,al
|
sub al,al
|
||||||
outTIMER_0_8253,al ;lsb
|
out TIMER_0_8253,al ;lsb
|
||||||
DELAY
|
DELAY
|
||||||
outTIMER_0_8253,al ;msb
|
out TIMER_0_8253,al ;msb
|
||||||
;
|
;
|
||||||
; In case interrupts are disabled, enable interrupts briefly to allow
|
; In case interrupts are disabled, enable interrupts briefly to allow
|
||||||
; the interrupt generated when switching from mode 3 to mode 2 to be
|
; the interrupt generated when switching from mode 3 to mode 2 to be
|
||||||
|
|
@ -1488,12 +1488,12 @@ ZTimerOn proc near
|
||||||
; interrupts in order to avoid getting a half-changed count.)
|
; interrupts in order to avoid getting a half-changed count.)
|
||||||
;
|
;
|
||||||
push ds
|
push ds
|
||||||
subax, ax
|
sub ax, ax
|
||||||
movds, ax
|
mov ds, ax
|
||||||
movax, ds:[TIMER_COUNT+2]
|
mov ax, ds:[TIMER_COUNT+2]
|
||||||
movcs: [StartBIOSCountHigh],ax
|
mov cs:[StartBIOSCountHigh],ax
|
||||||
movax, ds:[TIMER_COUNT]
|
mov ax, ds:[TIMER_COUNT]
|
||||||
movcs: [StartBIOSCountLow],ax
|
mov cs:[StartBIOSCountLow],ax
|
||||||
pop ds
|
pop ds
|
||||||
;
|
;
|
||||||
; Set the timer count to 0 again to start the timing interval.
|
; Set the timer count to 0 again to start the timing interval.
|
||||||
|
|
@ -1501,7 +1501,7 @@ ZTimerOn proc near
|
||||||
mov al,00110100b ;set up to load initial
|
mov al,00110100b ;set up to load initial
|
||||||
out MODE_8253,al ; timer count
|
out MODE_8253,al ; timer count
|
||||||
DELAY
|
DELAY
|
||||||
subal, al
|
sub al, al
|
||||||
out TIMER_0_8253,al; load count lsb
|
out TIMER_0_8253,al; load count lsb
|
||||||
DELAY
|
DELAY
|
||||||
out TIMER_0_8253,al; load count msb
|
out TIMER_0_8253,al; load count msb
|
||||||
|
|
@ -1512,20 +1512,20 @@ ZTimerOn proc near
|
||||||
popax
|
popax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ZTimerOnendp
|
ZTimerOn endp
|
||||||
|
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
;* Routine called to stop timing and get count. *
|
;* Routine called to stop timing and get count. *
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
|
|
||||||
ZTimerOff procnear
|
ZTimerOff proc near
|
||||||
|
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
pushf
|
pushf
|
||||||
pushax
|
push ax
|
||||||
pushcx
|
push cx
|
||||||
;
|
;
|
||||||
; In case interrupts are disabled, enable interrupts briefly to allow
|
; In case interrupts are disabled, enable interrupts briefly to allow
|
||||||
; any pending timer interrupt to be handled. Interrupts must be
|
; any pending timer interrupt to be handled. Interrupts must be
|
||||||
|
|
@ -1618,7 +1618,7 @@ ife PS2
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
sti;let the BIOS count continue
|
sti ;let the BIOS count continue
|
||||||
;
|
;
|
||||||
; Time a zero-length code fragment, to get a reference for how
|
; Time a zero-length code fragment, to get a reference for how
|
||||||
; much overhead this routine has. Time it 16 times and average it,
|
; much overhead this routine has. Time it 16 times and average it,
|
||||||
|
|
@ -1633,14 +1633,14 @@ RefLoop:
|
||||||
call ReferenceZTimerOff
|
call ReferenceZTimerOff
|
||||||
loop RefLoop
|
loop RefLoop
|
||||||
sti
|
sti
|
||||||
add cs:[ReferenceCount],8; total + (0.5 * 16)
|
add cs:[ReferenceCount],8 ;total + (0.5 * 16)
|
||||||
mov cl,4
|
mov cl,4
|
||||||
shr cs:[ReferenceCount],cl;(total) / 16 + 0.5
|
shr cs:[ReferenceCount],cl ;(total) / 16 + 0.5
|
||||||
;
|
;
|
||||||
; Restore the context of the program being timed and return to it.
|
; Restore the context of the program being timed and return to it.
|
||||||
;
|
;
|
||||||
popcx
|
pop cx
|
||||||
popax
|
pop ax
|
||||||
MPOPF
|
MPOPF
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
@ -1650,11 +1650,11 @@ ZTimerOff endp
|
||||||
; Called by ZTimerOff to start the timer for overhead measurements.
|
; Called by ZTimerOff to start the timer for overhead measurements.
|
||||||
;
|
;
|
||||||
|
|
||||||
ReferenceZTimerOnprocnear
|
ReferenceZTimerOn proc near
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
pushax
|
push ax
|
||||||
pushf
|
pushf
|
||||||
;
|
;
|
||||||
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
; Set timer 0 of the 8253 to mode 2 (divide-by-N), to cause
|
||||||
|
|
@ -1677,7 +1677,7 @@ ReferenceZTimerOnprocnear
|
||||||
popax
|
popax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
ReferenceZTimerOnendp
|
ReferenceZTimerOn endp
|
||||||
|
|
||||||
;
|
;
|
||||||
; Called by ZTimerOff to stop the timer and add the result to
|
; Called by ZTimerOff to stop the timer and add the result to
|
||||||
|
|
@ -1686,20 +1686,20 @@ ReferenceZTimerOnendp
|
||||||
; isn't going to take anywhere near 54 ms.
|
; isn't going to take anywhere near 54 ms.
|
||||||
;
|
;
|
||||||
|
|
||||||
ReferenceZTimerOff procnear
|
ReferenceZTimerOff proc near
|
||||||
;
|
;
|
||||||
; Save the context of the program being timed.
|
; Save the context of the program being timed.
|
||||||
;
|
;
|
||||||
pushf
|
pushf
|
||||||
pushax
|
push ax
|
||||||
pushcx
|
push cx
|
||||||
|
|
||||||
;
|
;
|
||||||
; Match the interrupt-window delay in ZTimerOff.
|
; Match the interrupt-window delay in ZTimerOff.
|
||||||
;
|
;
|
||||||
sti
|
sti
|
||||||
rept10
|
rept 10
|
||||||
jmp$+2
|
jmp $+2
|
||||||
endm
|
endm
|
||||||
|
|
||||||
mov al,00000000b
|
mov al,00000000b
|
||||||
|
|
@ -1720,8 +1720,8 @@ ReferenceZTimerOff procnear
|
||||||
;
|
;
|
||||||
; Restore the context and return.
|
; Restore the context and return.
|
||||||
;
|
;
|
||||||
popcx
|
pop cx
|
||||||
popax
|
pop ax
|
||||||
MPOPF
|
MPOPF
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
@ -1731,7 +1731,7 @@ ReferenceZTimerOff endp
|
||||||
;* Routine called to report timing results. *
|
;* Routine called to report timing results. *
|
||||||
;********************************************************************
|
;********************************************************************
|
||||||
|
|
||||||
ZTimerReportprocnear
|
ZTimerReport proc near
|
||||||
|
|
||||||
pushf
|
pushf
|
||||||
push ax
|
push ax
|
||||||
|
|
@ -1741,7 +1741,7 @@ ZTimerReportprocnear
|
||||||
push si
|
push si
|
||||||
push di
|
push di
|
||||||
push ds
|
push ds
|
||||||
;
|
;
|
||||||
push cs ;DOS functions require that DS point
|
push cs ;DOS functions require that DS point
|
||||||
pop ds ; to text to be displayed on the screen
|
pop ds ; to text to be displayed on the screen
|
||||||
assume ds :Code
|
assume ds :Code
|
||||||
|
|
@ -1780,7 +1780,7 @@ TestTooLong:
|
||||||
; Convert the BIOS time to microseconds.
|
; Convert the BIOS time to microseconds.
|
||||||
;
|
;
|
||||||
CalcBIOSTime:
|
CalcBIOSTime:
|
||||||
mov ax,[EndBIOSCountLow]
|
mov ax,[EndBIOSCountLow]
|
||||||
sub ax,[StartBIOSCountLow]
|
sub ax,[StartBIOSCountLow]
|
||||||
mov dx,54925 ;number of microseconds each
|
mov dx,54925 ;number of microseconds each
|
||||||
; BIOS count represents
|
; BIOS count represents
|
||||||
|
|
@ -1808,7 +1808,7 @@ CalcBIOSTime:
|
||||||
mov si,8381 ;convert the reference count
|
mov si,8381 ;convert the reference count
|
||||||
mul si ; to microseconds
|
mul si ; to microseconds
|
||||||
mov si,10000
|
mov si,10000
|
||||||
div si;* .8381 = * 8381 / 10000
|
div si ;* .8381 = * 8381 / 10000
|
||||||
sub bx,ax
|
sub bx,ax
|
||||||
sbb cx,0
|
sbb cx,0
|
||||||
mov [CurrentCountLow],bx
|
mov [CurrentCountLow],bx
|
||||||
|
|
@ -1896,7 +1896,7 @@ substantially.
|
||||||
|
|
||||||
Finally, please note that the *precision* Zen timer works perfectly well
|
Finally, please note that the *precision* Zen timer works perfectly well
|
||||||
on both PS/2 and non-PS/2 computers. The PS/2 and 8253 considerations
|
on both PS/2 and non-PS/2 computers. The PS/2 and 8253 considerations
|
||||||
we've just discussed apply *only* to the longZen timer.
|
we've just discussed apply *only* to the long-period Zen timer.
|
||||||
|
|
||||||
### Example Use of the Long-Period Zen Timer
|
### Example Use of the Long-Period Zen Timer
|
||||||
|
|
||||||
|
|
@ -2126,7 +2126,7 @@ be dealt with here: small code model and large; I'll tackle the simpler
|
||||||
one, the small code model, first.
|
one, the small code model, first.
|
||||||
|
|
||||||
Altering the Zen timer for linking to a small code model C program
|
Altering the Zen timer for linking to a small code model C program
|
||||||
involves the following steps: `C` hange `ZTimerOn` to
|
involves the following steps: Change `ZTimerOn` to
|
||||||
`_ZTimerOn`, change `ZTimerOff` to `_ZTimerOff`, change
|
`_ZTimerOn`, change `ZTimerOff` to `_ZTimerOff`, change
|
||||||
`ZTimerReport` to `_ZTimerReport`, and change `Code` to
|
`ZTimerReport` to `_ZTimerReport`, and change `Code` to
|
||||||
`_TEXT` . Figure 3.2 shows the line numbers and new states of all
|
`_TEXT` . Figure 3.2 shows the line numbers and new states of all
|
||||||
|
|
@ -2187,16 +2187,16 @@ push cs
|
||||||
call near ptr ReferenceZTimerOn
|
call near ptr ReferenceZTimerOn
|
||||||
```
|
```
|
||||||
|
|
||||||
(and likewise for `ReferenceZTimerOff` ), which works because
|
(and likewise for `ReferenceZTimerOff`), which works because
|
||||||
`ReferenceZTimerOn` is in the same segment as the calling code. This
|
`ReferenceZTimerOn` is in the same segment as the calling code. This
|
||||||
is normally a great optimization, being both smaller and faster than a
|
is normally a great optimization, being both smaller and faster than a
|
||||||
far call. However, it's not so great for the Zen
|
far call.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
timer, because our purpose in calling the reference timing code is to
|
However, it's not so great for the Zen timer, because our purpose in calling the reference timing code is to
|
||||||
determine exactly how much time is taken by overhead code—including the
|
determine exactly how much time is taken by overhead code—including the
|
||||||
far calls to `ZTimerOn` and `ZTimerOf`f! By converting the far calls
|
far calls to `ZTimerOn` and `ZTimerOf`! By converting the far calls
|
||||||
to push/near call pairs within the Zen timer module, TASM makes it
|
to push/near call pairs within the Zen timer module, TASM makes it
|
||||||
impossible to emulate exactly the overhead of the Zen timer, and makes
|
impossible to emulate exactly the overhead of the Zen timer, and makes
|
||||||
timings slightly (about 16 cycles on a 386) less accurate.
|
timings slightly (about 16 cycles on a 386) less accurate.
|
||||||
|
|
@ -2255,7 +2255,7 @@ processor cache at the start of the code being timed, because the timing
|
||||||
code is not necessarily fetched and does not necessarily access memory
|
code is not necessarily fetched and does not necessarily access memory
|
||||||
in exactly the same time sequence as the code immediately preceding the
|
in exactly the same time sequence as the code immediately preceding the
|
||||||
code under measurement normally does. This prefetch effect can introduce
|
code under measurement normally does. This prefetch effect can introduce
|
||||||
as much as 3 to 4 µ of inaccuracy. Similarly, the state of the prefetch
|
as much as 3 to 4 µs of inaccuracy. Similarly, the state of the prefetch
|
||||||
queue at the end of the code being timed affects how long the code that
|
queue at the end of the code being timed affects how long the code that
|
||||||
stops the timer takes to execute. Consequently, the Zen timer tends to
|
stops the timer takes to execute. Consequently, the Zen timer tends to
|
||||||
be more accurate for longer code sequences, since the relative magnitude
|
be more accurate for longer code sequences, since the relative magnitude
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue