帮助在 DOS 的 NASM 程序集中编写 TSR 程序

发布于 2024-11-26 14:56:39 字数 2033 浏览 2 评论 0原文

我一直在尝试用 MS-DOS 的程序集(16 位)编写 TSR(终止驻留)程序(一般而言)。我已阅读维基百科页面 关于 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 的一般建议吗?预先感谢,非常感谢任何帮助!

I've been trying to write TSR (Terminate-Stay-Resident) programs (in general) in Assembly (16-bit) for MS-DOS. I've read through a Wikipedia page
on TSR and also a page on using it specifically in DOS (but it seems to be teaching it in C and not Assembly directly). I've looked at a site with tons of DOS interrupt documentation and find this one, this one, and another most relevant to TSR programs. I can't post all of the links because as a new user I can have up to 2 hyperlinks on a post.

So, I've tried writing a (seemingly) very simple TSR program in real mode flat model (.COM file format) in NASM. Here's the code:

[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

When I assemble this and execute it inside DOS, instead of returning back to the DOS prompt it hangs the system (no activity occurs except the hardware cursor just blinks below the last prompt). I guess memory garbage might be executing but you get the point.

Could anybody please help to either figure out what the problem with this code is and / or offer general advice for coding TSR's in DOS? Thanks in advance, any help is very much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

强者自强 2024-12-03 14:56:39

我想通了。查看更多来源后,我发现这段代码:

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

I figured it out. After looking through a couple more sources, I discovered that this code:

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

needs to be something like this:

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

because those memory references are referencing from the data segment, which isn't set up from the caller of the TSR. So basically I was referencing data from something else's data block...

This can also be accomplished by putting CS in DS (and then putting DS's original value back) like this:

push DS
push CS
pop DS
; Memory references....
pop DS
如日中天 2024-12-03 14:56:39
  1. 您需要使用 cs: 段覆盖从通用中断处理程序中访问 TSR 的数据,因为 ds 值是任意用户的寄存器.

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

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

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

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

        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 以进行移位除法舍入,然后向下移动到一些段落。

  1. You need to use a cs: segment override to access the TSR's data from within a general-purpose interrupt handler, because the ds value is an arbitrary user's register then.

  2. You don't need to push the next handler's address onto the stack then jump with a retf. It is simpler to do jmp far [cs:...] (and that has a shorter encoding). But your method works fine too.

  3. You can put your initialisation handling (not needed in the resident installed handler) at the end of your program image. This is a trivial optimisation of the TSR's size.

  4. To calculate the size of your resident process, use NASM labels. To allow shift (or divide) operations needed to figure out a length in paragraphs, only use deltas of labels. A delta (difference) is a scalar value to NASM, so can be used in calculations. A (non-struc) label alone is not a scalar.

Here's an example using all of these:

        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

The size calculation computes the delta of two labels, adds in 256 for the process's PSP (same as the org 256), adds 15 to make the shift-division round up, then shifts down into an amount of paragraphs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文