如何在DOS程序集中正确挂接Interrupt 28h并将其还原?

Aar*_*es5 5 assembly dos interrupt x86-16

我试图将Interrupt 28h的处理程序设置为我自己的例程,恢复所有涉及的寄存器和标志,并恢复原始的Interrupt处理程序。我在VirtualBox中的DOSBox和MS-DOS 6.22下使用NASM汇编器。

我已经考虑过调试,但是在TSR程序上进行调试听起来似乎是不可能的。我尝试将数据段推到代码段上,并保存原始数据段以供以后还原,但是即使还原了数据段后,它似乎仍挂起了计算机。

section .text   ;Code Section
org 100h        ;DOS Executable Start
mov ah,35h      ;Get Interrupt Vector
mov al,28h      ;Of Interrupt 28h
int 21h         ;Call DOS Kernel
push cs         ;Push Code Segment
pop ds          ;Onto Data Segment
mov [oldseg],es ;Save Old Interrupt Vector Segment
mov [oldoff],bx ;Save Old Interrupt Vector Offset
mov ah,25h      ;Set Interrupt Vector
mov dx,resstart ;To Resstart
int 21h         ;Call DOS Kernel
mov dx,resend   ;Set Data Offset to Resend
sub dx,resstart ;Subtract Resstart
shr dx,4h       ;Shift Right 4 Bits for Paragraph
inc dx          ;One Extra Paragraph for PSP
mov ah,31h      ;Terminate and Stay Resident
xor al,al       ;Return Code
int 21h         ;Call DOS Kernel

resstart:       ;Resident Code Start
push ax         ;Save AX
push es         ;Save ES
push di         ;Save DI
push cx         ;Save CX
push ds         ;Save DS
push dx         ;Save DX
mov ah,00h      ;Set Video Mode
mov al,13h      ;To Mode 13h
int 10h         ;Call BIOS Video
mov ax,0A000h   ;VGA Segment
mov es,ax       ;Stored in ES
xor di,di       ;VGA Offset in DI
mov cx,0FA00h   ;Fill Entire Screen
mov al,09h      ;With Light Blue Color
rep stosb       ;Repeat Store AL at ES:DI
mov ah,25h      ;Set Interrupt Vector
mov al,28h      ;Of Interrupt 28h
mov ds,[oldseg] ;Restore Old Interrupt Vector Segment
mov dx,[oldoff] ;Restore Old Interrupt Vector Offset
int 21h         ;Call DOS Kernel
pop dx          ;Restore DX
pop ds          ;Restore DS
pop cx          ;Restore CX
pop di          ;Restore DI
pop es          ;Restore ES
pop ax          ;Restore AX
iret            ;Return and Restore Flags
resend:         ;Resident Code End

section .data
oldseg dw 0     ;Old Interrupt Vector Segment
oldoff dw 0     ;Old Interrupt Vector Offset
Run Code Online (Sandbox Code Playgroud)

返回原始中断向量地址并将新的中断向量地址设置为“重新启动”后,程序应终止并保持驻留状态。此后,由于DOS无需执行其他操作,因此将自动触发Interrupt 28h,这将反过来运行我的Interrupt处理程序。

中断处理程序将视频模式设置为13h,尝试用浅蓝色填充整个屏幕,还原原始的中断28h处理程序,还原涉及的所有寄存器和标志,然后返回DOS。执行该程序不会产生任何结果,系统甚至无法挂起。在运行设置视频模式13h的部分并单独用蓝色填充整个屏幕时,它工作得非常好。

Sep*_*and 3

mov dx,resend ;Set Data Offset to Resend
sub dx,resstart ;Subtract Resstart
shr dx,4h ;Shift Right 4 Bits for Paragraph
inc dx ;One Extra Paragraph for PSP
Run Code Online (Sandbox Code Playgroud)

在此 .COM 程序中,您将正确保存并设置中断向量。但是,您没有准确计算 DOS.TerminateAnd StayResident 函数要保留的段落数量。

需要inc dx向上舍入到最接近的段落。当然不是为了占PSP。由于 PSP 有 256 字节,因此需要 16 个段落。

分配给该 .COM 程序的内存从 PSP 开始,因此DX计数也必须从那里开始。

mov     dx, resend 
shr     dx, 4
inc     dx
mov     ax, 3100h   ; DOS.TerminateAndStayResident
int     21h
Run Code Online (Sandbox Code Playgroud)

提示如果将此重新发送标签与段落边界对齐,则inc dx不再需要。

如果您当前的代码在像 virtualbox 这样的模拟器中部分工作,那是因为您的程序以前占用的内存尚未被程序 shell 等覆盖。与 DOS 不同,仿真器可以远程执行命令解释器。

尽管系统挂起,但使用 virtualbox 屏幕确实充满了蓝色

如果有人在我写东西的时候关掉灯,我也会挂掉!这就是你的处理程序在突然改变视频模式时所做的事情......


对于TSR程序,我们通常会跳过需要常驻的部分,这样一次性设置占用的空间就可以被系统回收。

您可以使用的另一个技巧是将旧中断向量的偏移量和段直接写入将恢复向量的指令中。处理程序中的段寄存器不再有问题。

这是我对你的程序的重写:

    org     100h
Start:
    jmp     Setup

MyInt28:
    push    ax
    push    es
    push    di
    push    cx
    push    ds
    push    dx
    mov     ax, 0013h   ; BIOS.SetVideoMode
    int     10h
    mov     ax, 0A000h
    mov     es, ax
    xor     di, di
    mov     cx, 64000/2
    mov     ax, 0909h
    cld
    rep stosw
PatchA:
    mov     ax, 0       ; Don't change this to 'xor ax,ax'
    mov     ds, ax
PatchB:
    mov     dx, 0       ; Don't change this to 'xor dx,dx'
    mov     ax, 2528h   ; DOS.SetInterruptVector
    int     21h
    pop     dx
    pop     ds
    pop     cx
    pop     di
    pop     es
    pop     ax 
    iret

Setup:                  ; Resident part ends here.
    mov     ax, 3528h   ; DOS.GetInterruptVector
    int     21h         ; -> ES:BX
    mov     [PatchA + 1], es
    mov     [PatchB + 1], bx
    mov     dx, MyInt28
    mov     ah, 25h     ; DOS.SetInterruptVector
    int     21h
    mov     dx, (256+Setup-Start+15)/16
    mov     ax, 3100h   ; DOS.TerminateAndStayResident
    int     21h
Run Code Online (Sandbox Code Playgroud)