9

我一直在尝试用汇编(16 位)为 MS-DOS 编写 TSR(终止驻留)程序(通常)。我已经阅读了有关 TSR 的 Wikipedia 页面以及专门在 DOS 中使用它的页面(但它似乎是用 C 语言而不是直接用汇编语言教授它)。我查看了一个包含大量 DOS 中断文档的站点,发现这个这个和另一个与 TSR 程序最相关的一个。我不能发布所有链接,因为作为一个新用户,我可以在一个帖子上拥有多达 2 个超链接。

因此,我尝试在 NASM 中以实模式平面模型(.COM 文件格式)编写一个(看似)非常简单的 TSR 程序。这是代码:

[BITS 16]
[ORG 0x0100]

[SECTION .text]

Start:
; Get current interrupt handler for INT 21h
mov AX,3521h                ; DOS function 35h GET INTERRUPT VECTOR for interrupt 21h
int 21h                     ; Call DOS  (Current interrupt handler returned in ES:BX)

mov WORD [v21HandlerSegment],ES     ; Store the current INT 21h handler segment
mov WORD [v21HandlerOffset],BX      ; Store the current INT 21h handler offset

; Write new interrupt handler for INT 21h
mov AX,2521h                ; DOS function 25h SET INTERRUPT VECTOR for interrupt 21h
mov DX,TSRStart             ; Load DX with the offset address of the start of this TSR program
;   DS already contains the segment address, it is the same as CS in this .COM file
int 21h                     ; Override the INT 21h handler with this TSR program

; The TSR program will be called even when this portion uses INT 21h to terminate and stay resident
mov AX,3100h                ; DOS function TSR, return code 00h
mov DX,00FFh                ; I don't know how many paragraphs to keep resident, so keep a bunch
int 21h                     ; Call our own TSR program first, then call DOS

TSRStart:
push WORD [v21HandlerSegment]       ; Push the far address of the original 
push WORD [v21HandlerOffset]        ;   INT 21h handler onto the stack
retf                                ; Jump to it!


[SECTION .data]
v21HandlerSegment dw 0000h
v21HandlerOffset  dw 0000h

当我组装它并在 DOS 中执行它时,它不会返回到 DOS 提示符,而是挂起系统(除了硬件光标仅在最后一个提示符下方闪烁之外,没有任何活动发生)。我猜内存垃圾可能正在执行,但你明白了。

任何人都可以帮助找出这段代码的问题和/或提供在 DOS 中编码 TSR 的一般建议吗?在此先感谢,非常感谢任何帮助!

4

2 回答 2

6

我想到了。在查看了更多资源后,我发现这段代码:

push WORD [v21HandlerSegment]       ; Push the far address of the original 
push WORD [v21HandlerOffset]        ;   INT 21h handler onto the stack

需要是这样的:

push WORD [CS:v21HandlerSegment]       ; Push the far address of the original 
push WORD [CS:v21HandlerOffset]        ;   INT 21h handler onto the stack

因为这些内存引用是从数据段引用的,而不是从 TSR 的调用者设置的。所以基本上我是从其他东西的数据块中引用数据......

这也可以通过将 CS 放入 DS(然后将 DS 的原始值放回)来完成,如下所示:

push DS
push CS
pop DS
; Memory references....
pop DS
于 2011-07-29T18:48:36.897 回答
1
  1. 您需要使用cs:段覆盖从通用中断处理程序中访问 TSR 的数据,因为该ds值是任意用户的寄存器。

  2. 您不需要将下一个处理程序的地址压入堆栈然后使用retf. 这样做更简单jmp far [cs:...](并且编码更短)。但是你的方法也很好用。

  3. 您可以将初始化处理(在常驻安装的处理程序中不需要)放在程序映像的末尾。这是对 TSR 大小的简单优化。

  4. 要计算驻留进程的大小,请使用 NASM 标签。要允许计算段落长度所需的移位(或除法)操作,请仅使用标签的增量。delta(差异)是 NASM 的标量值,因此可用于计算。单独的(非结构)标签不是标量。

这是一个使用所有这些的示例:

        cpu 8086
        bits 16
        org 256

start:
        jmp init

        align 4
int21old:
        dd 0

int21handler:
        jmp far [cs:int21old]

end_of_resident:

init:
        mov ax, 3521h
        int 21h
        mov word [int21old + 2], es
        mov word [int21old], bx

        mov ax, 2521h
        mov dx, int21handler
        int 21h

        mov ax, 3100h
        mov dx, (end_of_resident - start + 256 + 15) >> 4
        int 21h

大小计算计算两个标签的增量,为进程的 PSP 添加 256(与 相同org 256),添加 15 以使移位除法向上舍入,然后向下移动到一定数量的段落。

于 2019-09-02T21:01:08.010 回答