我的 TSR 程序在第二次执行时冻结
My TSR program freezes when executed a second time
我对 FASM 有一些经验,并且学得很好。
现在,我想学习 TASM 语法。
我写了一个示例程序,它是 TSR。
这是我的代码
.model tiny
.8086
.stack 200h
.data
Message db 'Example0 is loaded in memory',0,'$'
.code
main proc ;'main' is proc and code region
mov ax,@data ;Initialize data segment
mov ds,ax
push es
xor bx,bx
mov es,bx
;Check interrupt vector 0f5h
cmp word ptr es:[3d6h],0
je init ;If null initialize program and stay resident
int 0f5h
cmp ax,'ID' ;Check string in ax
jne init ;If AX != 'GG' initialize TSR
mov ah,9
mov dx,offset Message
int 21h
init:
;Set interrupt vector 0f5h
mov word ptr es:[3d4h],offset interruptroute
mov word ptr es:[3d6h],cs
pop es
mov ax,3100h
mov dx,64 ;Reserve 1KB (My .exe is lower than this)
int 21h
interruptroute proc far
shl bx,2
add bx,offset g0
call far [cs:bx] ;I assume this array is in code segment
;Here maybe fault in call far
iret
endp interruptroute
g0:
dw getID
dw @code
getID proc far
mov ax,'ID'
retf
endp getID
endp
end main
还有我的 VirtualBox 截图:
另外我展示了我的命令行:
tasm src\gdos.asm,bin\gdos.obj
tlink bin\gdos.obj,bin\gdos.exe
注意:GDOS 是我计划 OS 建造的。
TL;DR: FASM 的语法和语义可能与 MASM/TASM.
的完全不同
在 Turbo 汇编程序 (TASM) 中,FAR
修饰符本身不应在 JMP
或 CALL
指令上使用。而是使用 FAR PTR procname
,其中 procname
是用 PROC
定义的过程的名称。提供的代码使用:
call far [cs:bx]
由于这是 CALL
一个人可能倾向于尝试的指令:
call far ptr [cs:bx]
正如我上面所说的 FAR PTR
应该只在操作数是标签时使用。 [cs:bx]
不是标签。问题是 - 什么语法可以用来做一个间接的 FAR JMP without a label?答案是DWORD PTR
:
call dword ptr [cs:bx]
第二个问题在这段代码中:
mov ah,9
mov dx,offset Message
int 21h
init:
;Set interrupt vector 0f5h
mov word ptr es:[3d4h],offset interruptroute
mov word ptr es:[3d6h],cs
Int 21h/AH=9
is being used to print a message to standard output. When finished it continues execution into the initialization code. An exit is needed after printing the already loaded message. Add a call to Int 21h/AH=4C
显示消息后。代码可能如下所示:
mov ah,9
mov dx,offset Message
int 21h
mov ax, 4c00h ; DOS exit function return 0
int 21h
init:
;Set interrupt vector 0f5h
mov word ptr es:[3d4h],offset interruptroute
mov word ptr es:[3d6h],cs
其他观察结果
- 在提供的代码中,TSR 通过检查中断 0F5h 的段(在 IVT 中)并与 0 进行比较来检查它是否已经加载。如果它是 0,则假定 TSR 未安装。这可能适用于正在测试的环境,但它可能不适用于更广泛的 DOS/hardware 环境。考虑查看
Int 2F
multiplexer interrupt. It can be used for detecting the presence of an existing TSR. Randall Hyde has good information on this subject in his book Art of Assembly. Chapter 18.5 Installing a TSR 详细介绍了该主题。
虽然不是bug,但是有这段代码定义了getID
:
getID proc far
mov ax,'ID'
retf
endp getID
我建议不要在这里使用 retf
。在用 PROC
定义的任何函数中,如果函数定义为 FAR
,TASM/MASM 足够聪明,可以将它们遇到的任何 ret
转换为 retf
。他们会将在 NEAR
函数中找到的任何 ret
转换为 retn
。这在函数从 NEAR
更改为 FAR
时很有用,反之亦然,因为汇编程序将编码正确的 return 指令。
跳转tableg0
定义为:
g0:
dw getID
dw @code
这行得通。相反,它可能是:
g0:
dw getID
dw seg GetID
此方法不依赖于明确使用固定段 @code
。如果将来 GetID
放在 @code
以外的不同段中,则不需要对 table.
进行修改
如果像getID
这样的所有函数都与中断处理程序在同一个段中,那么跳转中只需要近指针(偏移量)table g0
。仅使用 table 的 NEAR 可以使用 interruptroute
中的间接近跳跃:
shl bx,1 ; The table would have 2 byte NEAR pointers relative to CS
; So multiply the index by 2 instead of 4
add bx,offset g0
jmp [cs:bx] ; This does a NEAR jmp to the address at [cs:bx]
跳跃 table g0
现在看起来像这样:
g0:
dw getID
像 getID
这样的函数将被标记为 NEAR
并且:
getID proc near
这仅在 interruptroute
中断处理程序调用的所有函数与 getID
位于同一代码段中时才有效。我假设意图是有一个 table 的调用向量,而 BX 是一个调用 table (g0
) 的索引。在这段代码中它会起作用,但我不知道代码的设计意图是说这对于正在处理的实际项目来说是 suitable。
我对 FASM 有一些经验,并且学得很好。 现在,我想学习 TASM 语法。 我写了一个示例程序,它是 TSR。 这是我的代码
.model tiny
.8086
.stack 200h
.data
Message db 'Example0 is loaded in memory',0,'$'
.code
main proc ;'main' is proc and code region
mov ax,@data ;Initialize data segment
mov ds,ax
push es
xor bx,bx
mov es,bx
;Check interrupt vector 0f5h
cmp word ptr es:[3d6h],0
je init ;If null initialize program and stay resident
int 0f5h
cmp ax,'ID' ;Check string in ax
jne init ;If AX != 'GG' initialize TSR
mov ah,9
mov dx,offset Message
int 21h
init:
;Set interrupt vector 0f5h
mov word ptr es:[3d4h],offset interruptroute
mov word ptr es:[3d6h],cs
pop es
mov ax,3100h
mov dx,64 ;Reserve 1KB (My .exe is lower than this)
int 21h
interruptroute proc far
shl bx,2
add bx,offset g0
call far [cs:bx] ;I assume this array is in code segment
;Here maybe fault in call far
iret
endp interruptroute
g0:
dw getID
dw @code
getID proc far
mov ax,'ID'
retf
endp getID
endp
end main
还有我的 VirtualBox 截图:
另外我展示了我的命令行:
tasm src\gdos.asm,bin\gdos.obj tlink bin\gdos.obj,bin\gdos.exe
注意:GDOS 是我计划 OS 建造的。
TL;DR: FASM 的语法和语义可能与 MASM/TASM.
的完全不同在 Turbo 汇编程序 (TASM) 中,FAR
修饰符本身不应在 JMP
或 CALL
指令上使用。而是使用 FAR PTR procname
,其中 procname
是用 PROC
定义的过程的名称。提供的代码使用:
call far [cs:bx]
由于这是 CALL
一个人可能倾向于尝试的指令:
call far ptr [cs:bx]
正如我上面所说的 FAR PTR
应该只在操作数是标签时使用。 [cs:bx]
不是标签。问题是 - 什么语法可以用来做一个间接的 FAR JMP without a label?答案是DWORD PTR
:
call dword ptr [cs:bx]
第二个问题在这段代码中:
mov ah,9
mov dx,offset Message
int 21h
init:
;Set interrupt vector 0f5h
mov word ptr es:[3d4h],offset interruptroute
mov word ptr es:[3d6h],cs
Int 21h/AH=9
is being used to print a message to standard output. When finished it continues execution into the initialization code. An exit is needed after printing the already loaded message. Add a call to Int 21h/AH=4C
显示消息后。代码可能如下所示:
mov ah,9
mov dx,offset Message
int 21h
mov ax, 4c00h ; DOS exit function return 0
int 21h
init:
;Set interrupt vector 0f5h
mov word ptr es:[3d4h],offset interruptroute
mov word ptr es:[3d6h],cs
其他观察结果
- 在提供的代码中,TSR 通过检查中断 0F5h 的段(在 IVT 中)并与 0 进行比较来检查它是否已经加载。如果它是 0,则假定 TSR 未安装。这可能适用于正在测试的环境,但它可能不适用于更广泛的 DOS/hardware 环境。考虑查看
Int 2F
multiplexer interrupt. It can be used for detecting the presence of an existing TSR. Randall Hyde has good information on this subject in his book Art of Assembly. Chapter 18.5 Installing a TSR 详细介绍了该主题。 虽然不是bug,但是有这段代码定义了
getID
:getID proc far mov ax,'ID' retf endp getID
我建议不要在这里使用
retf
。在用PROC
定义的任何函数中,如果函数定义为FAR
,TASM/MASM 足够聪明,可以将它们遇到的任何ret
转换为retf
。他们会将在NEAR
函数中找到的任何ret
转换为retn
。这在函数从NEAR
更改为FAR
时很有用,反之亦然,因为汇编程序将编码正确的 return 指令。跳转table
g0
定义为:g0: dw getID dw @code
这行得通。相反,它可能是:
g0: dw getID dw seg GetID
此方法不依赖于明确使用固定段
@code
。如果将来GetID
放在@code
以外的不同段中,则不需要对 table. 进行修改
如果像
getID
这样的所有函数都与中断处理程序在同一个段中,那么跳转中只需要近指针(偏移量)tableg0
。仅使用 table 的 NEAR 可以使用interruptroute
中的间接近跳跃:shl bx,1 ; The table would have 2 byte NEAR pointers relative to CS ; So multiply the index by 2 instead of 4 add bx,offset g0 jmp [cs:bx] ; This does a NEAR jmp to the address at [cs:bx]
跳跃 table
g0
现在看起来像这样:g0: dw getID
像
getID
这样的函数将被标记为NEAR
并且:getID proc near
这仅在
interruptroute
中断处理程序调用的所有函数与getID
位于同一代码段中时才有效。我假设意图是有一个 table 的调用向量,而 BX 是一个调用 table (g0
) 的索引。在这段代码中它会起作用,但我不知道代码的设计意图是说这对于正在处理的实际项目来说是 suitable。