如何从多个 .asm 文件计算 TSR 块的总大小?

How to calculate the total size of a TSR block from multiple .asm files?

自从我上次在汇编中做一些事情以来已经 23 年了,我现在正在编写一个 DOS TSR 程序,只是为了好玩。

我有一个相当大的源文件,我决定将它拆分成更小的 .asm 文件。问题是我在计算 TSR 块的大小时遇到​​了问题。对于单个 .asm 文件,我只是做了类似的事情:

mov ax, offset end_of_code_label - offset start_of_code_label

但现在我的部分代码分散在几个源文件中,这种方法行不通。

我发现使用 linker 会有所帮助,方法是手动指定 link 顺序并确保最后一个 .obj 文件是 "end_of_code_label".

是否有一种优雅的方式来做到这一点,或者至少有一些不会被视为丑陋的技巧?

控制事物顺序的最简单方法是根据它们在最终程序中的位置将所有事物分段,然后使用您首先 link 的 "template" 汇编文件订购段。默认情况下,linker 按照遇到它们的顺序对段进行排序,因此如果您将程序中使用的所有段都放在它看到的第一个文件中,那么该文件将确定段的顺序。

您的 TSR 应该是单段 COM 文件这一事实使事情变得复杂,但仍然可以使用多个汇编程序段。汇编程序段不必与您的程序使用的段一一对应。在这种情况下,您只需使用汇编程序段将事物组合在一起。

例如,您可以使用这样的模板文件:

    EXTERN  init_start:NEAR
    PUBLIC  resident_end

PSPSEG  GROUP   RTEXT, REND, ITEXT

RTEXT   SEGMENT PUBLIC PARA 'RESIDENT'
    ORG 100h
start:
    jmp init_start
RTEXT   ENDS

REND    SEGMENT PUBLIC BYTE 'REND'
resident_end:
REND    ENDS

ITEXT   SEGMENT PUBLIC BYTE 'INIT'
ITEXT   ENDS

    END start

如果你把所有的驻留代码放在RTEXT段,初始化代码放在ITEXT段,那么前面的代码将放在程序的开头,后面的代码放在结束。符号resident_end会在中间,将程序退出后需要保留在内存中的代码和不需要保留的代码分开。

GROUP 指令的目的是通知汇编程序和 linker RTEXTRENDITEXT 都应该是一个实际的段。这很重要,因为他们需要知道所使用的任何地址都应该相对于组 PSPSEG,即程序正在使用的实际段。请注意,GROUP 指令创建了相当于别名的内容,它实际上对 linked 程序中的事物顺序没有任何影响。

因为 COM 程序总是在程序的开头开始执行,所以我在这个模板中放置了代码,在最后跳转到初始化代码。如果您正在创建一个 EXE,则不需要它,您可以将 RTEXT 留空并只使用 END 而不是 END start。具有入口点的文件将使用 END 和入口点的标签。

这是一个最小的 TSR,旨在 link 使用上述模板编辑:

    EXTERN  resident_end:NEAR
    PUBLIC  init_start

PSPSEG  GROUP   RTEXT, ITEXT

RTEXT   SEGMENT PUBLIC PARA 'RESIDENT'
    ASSUME  DS:NOTHING, SS:NOTHING, CS:PSPSEG
old_handler DD 0cccccccch
interrupt_handler:
    jmp [old_handler]
RTEXT   ENDS

ITEXT   SEGMENT PUBLIC BYTE 'INIT'
    ASSUME  DS:PSPSEG, SS:PSPSEG, CS:PSPSEG
init_start:
    mov ax, 3508h
    int 21h        ; get old timer interrupt handler
    mov WORD PTR [old_handler], bx
    mov WORD PTR [old_handler + 1], es
    mov dx, OFFSET interrupt_handler
    mov ax, 2508
    int 21h        ; set new timer interrupt handler

    mov ax, 3100h
    mov dx, OFFSET resident_end + 15
    shr dx, 4
    int 21h        ; terminate and stay resident
ITEXT   ENDS

    END

有必要在每个文件中重复 GROUP 指令,尽管您只需列出文件中使用的段。此代码是使用现代版本的 MASM 编写和测试的(link-only),如果您使用的是 TASM,您可能需要明确告诉汇编器在每次使用时相对于 PSPSEG 进行偏移OFFSET。例如 mov dx, OFFSET PSPSEG:interrupt_handler.