📄 latency.asm
字号:
; This program measures the latency of an INT 08 ISR.
; It works by reading the timer chip immediately upon entering
; the INT 08 ISR By averaging this value for some number of
; executions, we can determine the average latency for this
; code.
.xlist
.386
option segment:use16
include stdlib.a
includelib stdlib.lib
.list
cseg segment para public 'code'
assume cs:cseg, ds:nothing
; All the variables are in the code segment in order to reduce ISR
; latency (we don't have to push and set up DS, saving a few instructions
; at the beginning of the ISR).
OldInt8 dword ?
SumLatency dword 0
Executions dword 0
Average dword 0
; This program reads the 8254 timer chip. This chip counts from
; 0FFFFh down to zero and then generates an interrupt. It wraps
; around from 0 to 0FFFFh and continues counting down once it
; generates the interrupt.
;
; 8254 Timer Chip port addresses:
Timer0_8254 equ 40h
Cntrl_8254 equ 43h
; The following ISR reads the 8254 timer chip, negates the result
; (because the timer counts backwards), adds the result to the
; SumLatency variable, and then increments the Executions variable
; that counts the number of times we execute this code. In the
; mean time, the main program is busy computing and displaying the
; average latency time for this ISR.
;
; To read the 16 bit 8254 counter value, this code needs to
; write a zero to the 8254 control port and then read the
; timer port twice (reads the L.O. then H.O. bytes). There
; needs to be a short delay between reading the two bytes
; from the same port address.
TimerISR proc near
push ax
mov eax, 0 ;Ch 0, latch & read data.
out Cntrl_8254, al ;Output to 8253 cmd register.
in al, Timer0_8254 ;Read latch #0 (LSB) & ignore.
mov ah, al
jmp SettleDelay ;Settling delay for 8254 chip.
SettleDelay: in al, Timer0_8254 ;Read latch #0 (MSB)
xchg ah, al
neg ax ;Fix, 'cause timer counts down.
add cseg:SumLatency, eax
inc cseg:Executions
pop ax
jmp cseg:OldInt8
TimerISR endp
Main proc
meminit
; Begin by patching in the address of our ISR into int 8's vector.
; Note that we must turn off the interrupts while actually patching
; the interrupt vector and we must ensure that interrupts are turned
; back on afterwards; hence the cli and sti instructions. These are
; required because a timer interrupt could come along between the two
; instructions that write to the int 8 interrupt vector. Since the
; interrupt vector is in an inconsistent state at that point, this
; could cause the system to crash.
mov ax, 0
mov es, ax
mov ax, es:[8*4]
mov word ptr OldInt8, ax
mov ax, es:[8*4 + 2]
mov word ptr OldInt8+2, ax
cli
mov word ptr es:[8*4], offset TimerISR
mov es:[8*4 + 2], cs
sti
; First, wait for the first call to the ISR above. Since we will be dividing
; by the value in the Executions variable, we need to make sure that it is
; greater than zero before we do anything.
Wait4Non0: cmp cseg:Executions, 0
je Wait4Non0
; Okay, start displaying the good values until the user presses a key at
; the keyboard to stop everything:
DisplayLp: mov eax, SumLatency
cdq ;Extends eax->edx.
div Executions
mov Average, eax
printf
byte "Count: %ld, average: %ld\n",0
dword Executions, Average
mov ah, 1 ;Test for keystroke.
int 16h
je DisplayLp
mov ah, 0 ;Read that keystroke.
int 16h
; Okay, restore the interrupt vector. We need the interrupts off
; here for the same reason as above.
mov ax, 0
mov es, ax
cli
mov ax, word ptr OldInt8
mov es:[8*4], ax
mov ax, word ptr OldInt8+2
mov es:[8*4+2], ax
sti
Quit: ExitPgm ;DOS macro to quit program.
Main endp
cseg ends
sseg segment para stack 'stack'
stk db 1024 dup ("stack ")
sseg ends
zzzzzzseg segment para public 'zzzzzz'
LastBytes db 16 dup (?)
zzzzzzseg ends
end Main
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -