Assembly 来自中断服务例程的奇怪行为
我正在编写一个中断服务例程,它应该使用int 70h和IRQ8来处理由RTC引起的中断,以便与一些计时器一起使用。不幸的是,我在这方面遇到了很多问题,所以我决定将问题分成几个较小的问题,然后独立解决每个问题。首先,我放弃了硬件部分,决定首先在软件中实现中断 现在,我正在使用NASM和DosBox 以下是ISR代码:Assembly 来自中断服务例程的奇怪行为,assembly,x86,interrupt,16-bit,interrupt-handling,Assembly,X86,Interrupt,16 Bit,Interrupt Handling,我正在编写一个中断服务例程,它应该使用int 70h和IRQ8来处理由RTC引起的中断,以便与一些计时器一起使用。不幸的是,我在这方面遇到了很多问题,所以我决定将问题分成几个较小的问题,然后独立解决每个问题。首先,我放弃了硬件部分,决定首先在软件中实现中断 现在,我正在使用NASM和DosBox 以下是ISR代码: segment .code ; ---------------------------------------------- ; writes a message on the sc
segment .code
; ----------------------------------------------
; writes a message on the screen
; every time interrupt is called
; ----------------------------------------------
INT_CODE equ 070h
my_int:
pusha ;saves all registers on stack so they get preserved
;EDIT1
xor ax, ax ;sets ax to zero
mov es, ax ;puts zero into extra segment register
mov bx, [es:INT_CODE*4+2] ;this should give us the sergment of the ISR
mov ds, bx ;the segment is now in ds
;END OF EDIT1
;mov ax, 0 ;cleans any garbage in ax
;mov ah, 09h ;preparing to call DOS system call, remove later
mov ax, string_s
mov si, ax
call _print_string
;int 021h ;this should hopefully work
mov al, 0Ch ; Accessing RTC
out 070h, al ; register C should be read
in al, 071h ;or there won't be any new interrupts (or so it's supposed to be)
;mov ax, 0 ; again we clear anything left in ax, just in case
;mov ah, 09h ; preparing to write string
mov ax, string_e
mov si, ax
call _print_string
;int 021h ; this should work
mov al, 20h ;we're letting PICs know the interrupt ended
out 0A0h, al ;notifying second PIC
out 020h, al ;notifying first PIC
popa ;application gets its registers back
iret
_inst_70:
cli ;hardware interrupts are now stopped
xor ax, ax
mov es, ax
mov bx, [es:INT_CODE*4]
mov [old_int70_off], bx
mov bx, [es:INT_CODE*4+2]
mov [old_int70_seg], bx
; adding our routine to interrupt vector table
mov dx, my_int
mov [es:INT_CODE*4], dx
mov ax, cs
mov [es:INT_CODE*4+2], ax
sti
;mov ah, 09h
mov ax, string_inst
mov si, ax
call _print_string
;int 021h
ret
; -----------------------------------------------------------------------------
; return old int 70 h
_uninst_70:
cli
xor ax, ax
mov es, ax
mov ax, [old_int70_seg]
mov [es:INT_CODE*4+2], ax
mov dx, [old_int70_off]
mov [es:INT_CODE*4], dx
sti
ret
_print_string:
pusha
mov ah, 0Eh ; BIOS INT 10h teletype (TTY) function
.Repeat:
lodsb ; takes one character from a string
cmp al, 0
je .End ; If it's zero, end of string
int 10h ; if not, call BIOS
jmp .Repeat ; and go to next character
.End:
popa
ret
segment .data
string_s: db 'We're in ISR',0
string_e: db 'It's working',0
string_inst: db 'Installed',0
old_int70_seg: dw 0
old_int70_off: dw 0
我正在使用以下程序测试此中断:
;myint
org 100h;installs the interrupt
segment .code
main:
call _inst_70
;call _uninst_70 ; THIS IS ON PURPOSE!
ret
%include "myint.asm"
及
现在我们进入有趣的部分。
当我调用安装程序时,我得到一条消息,即中断已安装,程序似乎结束得很好
当我打电话给INT70H.COM时,我得到的似乎是一个内存区域的转储。其中唯一可读的内容是:
测试!测试!int 70h工作
和C:\NASM-DOS\NASM.EXE
当我在INT70H中取消注释mov ax,0
和mov dx,0
行时,我得到测试代码>和DosBox挂起,有时崩溃。VMware和VirtualBox也是如此
当我用INT70H中的两个MOV注释掉读取RTC寄存器C的行时,我得到测试!测试!int 70h工作
且DosBox挂起。同样的事情也发生在VirtualBox和VMware上。如果INT70H中的两个mov没有注释,我将得到测试代码>并挂起
这让我相信,可能是一些DOS系统调用(我不应该在最终产品中使用)做了一些不好的事情,但即使它们被注释掉了,当我运行INT70H时,计算机挂起
我的主要问题是,现在我完全不知道如何开始处理这个问题。中断服务程序必须在执行依赖于段寄存器的任何操作之前设置段寄存器。当中断被调用时,它可以拥有系统中任何内容的上下文。打印字符串的调用尤其有问题,因为它们依赖ds:dx
作为字符串地址,但未设置ds
除此之外,它表面上看起来很好。查看设置ds
是否解决了挂起问题。如果没有,请进行后续操作。中断服务例程(ISR)必须保存它使用的所有寄存器并将其还原(以便被中断的软件不会看到寄存器被随机丢弃)。这包括段寄存器(如DS和ES)。您需要在ISR开始处附近的“推送ds
”和“推送es
”,以及“iret”之前相应的“pop”指令
没有任何BIOS功能是可重入的,因此在ISR中使用任何BIOS功能都是不安全的;除非您可以保证可能正在运行的其他代码都不会使用它们。这包括“int 0x10,ah=0x0E”函数(您在主代码和中断主代码的ISR中使用该函数)。如果只是为了测试;尝试直接写入以显示内存(例如,对于文本模式,“mov-ax,0xB800;mov-es,ax;inc-word[es:0]
”)
对于操作系统代码的测试,通常更容易测试操作系统代码。例如,如果这是在故意不返回(锁定)的引导扇区中实现的;这样,您就不必费心保存/恢复以前的IVT条目,也不必担心DOS和/或任何TSR在后台所做的可能会干扰您的测试的事情;您可以在类似Bochs的程序中调试它,而无需首先在虚拟机中安装DOS/FreeDOS。作为额外的好处,您可以在目标操作模式(例如,可能是32位保护模式)下执行此操作,而不是稍后重写16位实模式代码。这是一个非常奇怪的错误。非常感谢所有帮助过我的人,但最后,当我设置RTC时,我没有在写入之前将输出寄存器设置为B。现在它可以正常工作了。我没有时间给出完整的答案,但您需要将您的程序设置为a。@interjay这意味着我正在编写一个程序。这将稍后成为操作系统调度程序的一部分,我正在尝试在不使用PIT的情况下获得100毫秒的时基。因此,我不能依赖DOS函数来获得最终结果。:)您不能在硬件中断处理程序中使用任何DOS中断,它们不是可重入的。@Hans Passant即使我将它们完全注释掉,也会出现相同的结果。接下来我将尝试使用BIOS中断或直接写入视频内存。@Hans Passan我从DOS打印改为使用BIOS中断,现在我有了一些结果。我得到测试!int 70h工作
和随机内存块,其中一些内存块包括int 70h工作
,但比以前短得多。它仍然挂起。主要的改进是,当INT70H中的mov ax,0
被注释掉,而服务例程中的第一个mov ax,0
没有注释掉时,它打印内存的内容,然后挂起。它以前没有这样做过。
;int70h
org 100h ;calls the interrupt
segment .code
mov ah, 09h ; getting ready to print string
mov dx, string1
int 21h
;mov ax, 0 ;getting rid of the last message
;mov dx, 0
int 070h ;calling the interrupt
mov ah, 09h
mov dx, string2;
int 21h
ret
segment .data
string1: db 'Testing!',0
string2: db 'int 70h working',0
_print_string:
pusha
mov ah, 0Eh ; BIOS INT 10h teletype (TTY) function
.Repeat:
lodsb ; takes one character from a string
cmp al, 0
je .End ; If it's zero, end of string
int 10h ; if not, call BIOS
jmp .Repeat ; and go to next character
.End:
popa
ret