在NASM汇编语言中为DOS编写TSR程序的帮助

9
我一直在尝试使用16位汇编语言为MS-DOS编写TSR(Terminate-Stay-Resident)程序。我阅读了维基百科关于TSR的页面,以及有关在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代码的一般建议吗?非常感谢您的帮助!

2
我刚才是不是进入了时空隧道,回到了20年前? - Keith
@Keith Yep. 别误以为我是用这个作为主要编程语言(我也会编写Java),我只是需要知道如何用汇编语言编写TSR以进行演示。 - Alex Ozer
2
老兄,你正在进行编程考古学...为此点赞! - Nils Pipenbrinck
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

1
  1. 您需要使用cs:段覆盖来访问通用中断处理程序中的TSR数据,因为ds值是任意用户寄存器。

  2. 您不需要将下一个处理程序的地址推入堆栈,然后使用retf跳转。更简单的方法是使用jmp far [cs:...](编码更短)。但是您的方法也可以正常工作。

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

  4. 要计算常驻进程的大小,请使用NASM标签。为了允许移位(或除法)操作以确定段落长度,只使用标签的差值。差值(差异)对于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

大小计算会计算出两个标签之间的差值,加上 256 作为进程的 PSP(与 org 256 相同),再加上 15 以进行移位除法取整,然后向下移位成为段数。

1
我是点赞。您还可以在中断处理程序中编码一个虚拟的JMP ptr16:ptr16,并在“init”中使用自修改代码来修改JMP的段和偏移量。然后,当发生中断时,跳转到的地址将被编码在指令中,而不需要从另一个内存位置读取jmp地址。与相关缓存行失效相比,会有一次性的影响。 - Michael Petch
@Michael Petch:如果你确实想采用这种方法(自修改代码),你可以让 NASM 编码 jmp 0:0,然后在跳转指令之后添加一行类似于 int21old: equ $ - 4 的代码。但是,如果您想要向 TSR 添加 IBM 中断共享协议(或完整的 AMIS)支持,则需要使用 jmp far [mem],因为中断共享协议头不允许远程 jmp 操作码。请参阅 http://www.ctyme.com/intr/rb-4214.htm 查看中断共享协议头。 - ecm
1
@Michael Petch:你可以在PSP:5Ch处重复使用内存,稍后也可以使用FCB,但不能在PSP中的两个特定“默认FCB”插槽中使用FCB。如果我没记错的话,打开第一个默认FCB实际上会覆盖第二个默认FCB的一部分;想要同时使用这两个程序需要将它们复制到内存的其他位置。 - ecm
我应该明确一下,我是指PSP中的两个FCB。当然您并不局限于它们。我想不出可能存在这种情况的场景,所以我通常使用5ch而不是80h。 - Michael Petch
我在想为什么很难找到它。这是与Sep Roland的评论讨论的一部分,他发布的代码作为此答案的一部分:https://dev59.com/KrTma4cB1Zd3GeqP3Dvt#56418301。 - Michael Petch
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接