帮助在NASM NAS for Assembly中编写TSR程序

时间:2011-07-28 04:32:32

标签: assembly dos nasm interrupt tsr

我一直在尝试在程序集(16位)中为MS-DOS编写TSR(Terminate-Stay-Resident)程序(一般)。我已经阅读了维基百科页面 关于TSR以及在DOS中专门使用它的页面(但它似乎是用C语言教学,而不是直接用汇编语言)。我查看了一个包含大量DOS中断文档的站点,找到了this onethis 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的一般建议?在此先感谢,非常感谢任何帮助!

2 个答案:

答案 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)

  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

大小计算将计算两个标签的增量,为流程的PSP加256(与org 256相同),加15使上移分割向上舍入,然后向下移动成多个段落