1

我对 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 是我计划构建的操作系统。

4

1 回答 1

3

TL;DR: FASM 的语法和语义可能与 MASM/TASM 完全不同。


在 Turbo Assembler (TASM) 中,FAR修饰符本身不应用于JMPorCALL指令。而是使用FAR PTR procnamewhereprocname是用 定义的过程的名称PROC。提供的代码使用:

call far [cs:bx]

由于这是一个CALL指示,因此人们可能倾向于尝试:

call far ptr [cs:bx]

如上所述,FAR PTR仅当操作数是标签时才应使用。[cs:bx]不是标签。问题是 - 可以使用什么语法来执行带标签的间接 FAR JMP?答案是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用于将消息打印到标准输出。完成后,它会继续执行初始化代码。打印已加载的消息后需要退出。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/硬件环境。考虑查看Int 2F多路复用器中断。它可用于检测现有 TSR 的存在。Randall Hyde 在他的《装配艺术》一书中对这个主题有很好的了解。第18.5 章安装 TSR详细介绍了这个主题。
  • 虽然不是错误,但有这段代码定义了getID

    getID proc far
            mov ax,'ID'
            retf
    endp getID
    

    我建议不要retf在这里使用。在使用 TASM/MASM 定义的任何函数中,如果该函数已定义,它们PROC都足够聪明,可以将ret遇到的任何函数转换为 a 。他们会将在函数中找到的任何内容转换为. 如果函数从to更改,反之亦然,这将很有用,因为汇编器将对正确的返回指令进行编码。retfFARretNEARretnNEARFAR

  • 跳转表g0定义为:

    g0:
            dw getID
            dw @code
    

    这行得通。相反,它可能是:

    g0:
            dw getID
            dw seg GetID
    

    此方法不依赖于显式使用固定段@code。如果GetID被放置在与将来不同的段@code中,则不需要对表进行修改。

  • 如果所有类似的函数getID都与中断处理程序在同一段中,那么跳转表中只需要附近的指针(偏移量)g0。仅使用 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]
    

    跳转表现g0在看起来像这样:

    g0:
            dw getID
    

    像这样的功能getID将被标记为NEAR

    getID proc near
    

    这仅在interruptroute中断处理程序调用的所有函数与getID. 我假设目的是有一个调用向量表,而BX是调用表的索引 ( g0)。在这段代码中它会起作用,但我不知道代码的设计意图是说这是否适合正在处理的实际项目。

于 2019-08-02T16:18:16.127 回答