自上次我在汇编时做了23年后,我现在正在编写一个DOS TSR程序,只是为了它的乐趣。
我有一个相当大的源文件,我决定将其拆分为较小的.asm文件。问题是我在计算TSR块的大小时遇到问题。使用单个.asm文件,我只做了类似的事情:
mov ax, offset end_of_code_label - offset start_of_code_label
但是现在我将部分代码分散在几个源文件中,这种方法不起作用。
我发现通过手动指定链接顺序并确保最后一个.obj文件是带有“end_of_code_label”的文件,玩链接器会有所帮助。
有没有一种优雅的方式来做到这一点,或者至少是一些不被认为是丑陋黑客的东西?
答案 0 :(得分:3)
控制事物顺序的最简单方法是根据需要在最终程序中将所有内容放入段中,然后使用首先链接的“模板”程序集文件来对段进行排序。默认情况下,链接器按照它们遇到的顺序对段进行排序,因此如果您在程序中使用的所有段都在它看到的第一个文件中,那么该文件将确定段的顺序。
您的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
指令的目的是通知汇编程序和链接器RTEXT
,REND
和ITEXT
都应该是一个实际的段。这很重要,因为他们需要知道所使用的任何地址都应该相对于组PSPSEG
,即程序正在使用的实际段。请注意,GROUP
指令创建了一个别名,它实际上对链接程序中的事物顺序没有任何影响。
因为COM程序总是在程序开始时开始执行,所以我把代码放在这个跳转到初始化代码的模板中。如果您创建的是EXE,则不需要这样做,您可以将RTEXT
留空,只使用END
代替END start
。具有入口点的文件将使用END
代替入口点的标签。
这是一个旨在与上述模板链接的最小TSR:
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编写和测试的(仅链接),如果您使用TASM,则每次使用{{1}时,您可能需要明确告诉汇编程序相对于PSPSEG
进行偏移。 }。例如OFFSET
。