我一直在尝试在程序集(16位)中为MS-DOS编写TSR(Terminate-Stay-Resident)程序(一般)。我已经阅读了维基百科页面 关于TSR以及在DOS中专门使用它的页面(但它似乎是用C语言教学,而不是直接用汇编语言)。我查看了一个包含大量DOS中断文档的站点,找到了this one,this one和另一个与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的一般建议?在此先感谢,非常感谢任何帮助!
答案 0 :(得分:4)
我明白了。在浏览了几个来源之后,我发现了这段代码:
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
的值是一个任意的用户寄存器。
您不需要将下一个处理程序的地址压入堆栈,然后使用retf
进行跳转。进行jmp far [cs:...]
比较容易(并且编码较短)。但是您的方法也可以正常工作。
您可以将初始化处理(在常驻安装的处理程序中不需要)放在程序映像的末尾。这是对TSR大小的简单优化。
要计算驻留进程的大小,请使用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
大小计算将计算两个标签的增量,为流程的PSP加256(与org 256
相同),加15使上移分割向上舍入,然后向下移动成多个段落