Assembly 更改IRQ8 ISR
我想每隔55ns打印一次“你好”。我做错了什么Assembly 更改IRQ8 ISR,assembly,x86-16,Assembly,X86 16,我想每隔55ns打印一次“你好”。我做错了什么 .model small .stack 100h pusha MACRO push ax push bx push cx push dx push sp push bp push si push di ENDM popa MACRO pop di pop si pop bp pop sp pop dx pop cx pop b
.model small
.stack 100h
pusha MACRO
push ax
push bx
push cx
push dx
push sp
push bp
push si
push di
ENDM
popa MACRO
pop di
pop si
pop bp
pop sp
pop dx
pop cx
pop bx
pop ax
ENDM
.data
old_vec dw 1 dup(?)
old_seg dw 1 dup(?)
HELLO db "HELLO.$"
.code
start:
mov ax, @data
mov ds, ax
mov ax, es:[20h]
mov bx, es:[20h+2] ;saving the old ISR ADDRESS
mov old_vec, ax
mov old_seg, bx
mov es:[20h], OFFSET PRINT_HELLO ; setting the ivt to point to PRINT_HELLO
mov es:[20h + 2], SEG PRINT_HELLO
COMMENT @
mov bx, offset old_vec
mov ax, word ptr[bx]
mov es:[20h], ax
mov bx, offset old_seg
mov ax, word ptr[bx]
mov es:[20h+2], ax
@
;for1:
; jmp for1
.exit
mov ah, 4ch
int 21h
PRINT_HELLO proc far
pushf
pusha
mov ax, offset old_seg
mov bx, ax
mov es, word ptr[bx]
mov bx, offset old_vec
mov bx, word ptr[bx]
call far PTR es:[bx] ; calling the old interrupt handler
mov dx, offset HELLO
mov ah, 09h
int 21h
popa
iret
PRINT_HELLO endp
end
注释用于调试
还有两个问题-
CS
=DS
=ES
=SS
启动。ORG 256
指令是强制性的
; This program prints a message every 55 milliseconds
; until a key is pressed
ORG 256
xor ax, ax
mov es, ax
More: mov al, [es:046Ch] ; BIOS.Timer
Wait: cmp al, [es:046Ch]
je Wait
mov dx, Msg
mov ah, 09h ; DOS.PrintString
int 21h
mov ah, 01h ; BIOS.TestKey
int 16h ; -> AX ZF
jz More
mov ah, 00h ; BIOS.GetKey
int 16h ; -> AX
mov ax, 4C00h ; DOS.Terminate
int 21h
; --------------------------------
Msg db 'Hello, once every 55 milliseconds!', 13, 10, '$'
当然,可以通过挂接计时器向量来执行此操作。在没有更多特殊要求的情况下,您应该钩住1Ch向量并保留08h向量
; This program prints a message every 55 milliseconds
; until a key is pressed
ORG 256
mov ax, 351Ch ; DOS.GetInterruptVector
int 21h ; -> ES:BX
mov [chain+1], bx
mov [chain+3], es
mov dx, int1C
mov ax, 251Ch ; DOS.SetInterruptVector
int 21h
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; In the 'main' part you can freely change DS, ES, and SS segment registers
main: mov ah, 01h ; BIOS.TestKey
int 16h ; -> AX ZF
jz main
mov ah, 00h ; BIOS.GetKey
int 16h ; -> AX
; In the 'main' part you can freely change DS, ES, and SS segment registers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
mov dx, [cs:chain+1]
mov ds, [cs:chain+3]
mov ax, 251Ch ; DOS.SetInterruptVector
int 21h
mov ax, 4C00h ; DOS.Terminate
int 21h
; --------------------------------
int1C: push ax bx si
cld
mov bx, 0007h ; DisplayPage and GraphicsColor
mov si, Msg
.more: lods byte [cs:si]
mov ah, 0Eh ; BIOS.Teletype
int 10h
cmp al, 10
jne .more
pop si bx ax
chain: jmp far 0:0 ; Chaining to the old interrupt handler
Msg: db 'Hello, once every 55 milliseconds!', 13, 10, '$'
; --------------------------------
- 使用BIOS输出功能可避免DOS重入问题
- 将旧向量存储在远跳转指令上可以避免操作段寄存器
- 将消息放在代码段中便于访问
- DOS函数25h和35h在更改中断向量时易于使用
虽然可以通过TSR完成上述所有操作,但将如此多的文本字符快速连续地放在屏幕上将不可避免地造成真正的混乱!请记住,在使用屏幕时,前台程序(DOS命令行或用户应用程序)是首选程序。不要按下并弹出
sp
。由于8086和更新的处理器之间的push sp
行为不同,这可能会导致问题。这样做也是完全没有用的。还要注意的是,在信号处理程序中不能安全地调用DOS系统调用,因为另一个DOS系统调用可能已经在进行中。不要那样做。相反,考虑为此使用BIOS服务。更多的问题:你做代码> PUPF;按下按钮,但不要按下按钮。既然CPU已经在进入信号处理程序时保护了标志,我想知道你为什么要这么麻烦。还要注意的是,您不能安全地恢复ds
和es
,但是(a)假设ds
具有特定值,并且(b)覆盖es
。两者都是错误的。您没有初始化ES,您退出程序时ISR仍在原位(如果需要,请使用TSR程序),并且您正在访问ISR中的程序数据段,而不知道DS中的实际内容。要调用旧向量,您需要执行(NASM语法)pushf
\调用far[cs:old\u vec]
而不是你想做的任何事情。(不平衡的)pushf
是因为旧处理程序希望返回iret
。通过先按下标志,然后让程序远调用旧向量,您可以让它创建一个堆栈帧,就像为中断调用创建的堆栈帧一样,由一个标志字、一个cs
,然后是一个ip
。我向上投票。我想说的是BIOS也不一定是可重入的。报纸上有一条关于这一点的说明。“该程序负责匹配相应的等待和Post调用,并对设备驱动程序的访问进行序列化。BIOS代码不可重入。”