soul3500 发表于 2023-6-1 18:00:22

关于jmp far ptr的问题

你好,小甲鱼和各位大佬。
      我在学习王爽老师的汇编语言那边书的时候,对第9章里面的jmp far ptr 标号有一些问题。书上说jmp far ptr 标号 实现的是远转移(段间转移)
因此,我自己写了一个简单的代码:

      assume cs:code
      code2 segment
                a:   mov ax,4c00h
                              int 21
      code2 ends

      code segment
                start:jmp far ptr a
      code ends

      end start      


定义了2个代码段:code和code2。并且将cs关联至code段,在code中实现jmp far ptr a。想跳转至code2段中的标号a处。

但是在用masm5.0编译的时候在a标号处报错:“missing or unreachable cs”。

就是想问一下是不是不能定义多个代码段,如果不能定义jmp far ptr的作用和意义是什么样的?

或者说,是不是我用错了,"jmp far ptr 标号"正确的用法是什么样?

谢谢大佬帮忙解答


soul3500 发表于 2023-6-1 18:08:05

我看到书中给的例子中的jmp far ptr s,其中标号s是属于段内的。那么段内的话用jmp short s实现近转移即可。所以我想弄清楚jmp far ptr 标号 具体是如何实现远转移的

sfqxx 发表于 2023-6-1 18:26:25

您好,关于您的问题,我可以给您一些解释。

首先,您所编写的汇编代码在语法和逻辑上是正确的。它定义了两个代码段code和code2,并将cs寄存器关联到了代码段code,然后在代码段code中使用jmp far指令跳转到代码段code2中的标号a处。但是,在代码段code2中的标号a处出现了“missing or unreachable cs”的错误,这是由于代码段code2没有被正确加载到内存中所导致的。

其次,汇编语言中的jmp far指令是用来实现跨段或者从当前代码段中跳转到其他代码段中的标号处。在实模式下,每个代码段最大只能使用64KB的空间,如果需要访问超过64KB的数据或者代码,则需要通过跨段跳转来实现。跨段跳转需要使用段寄存器和偏移地址来确定目标地址,因此jmp far指令需要提供一个16位的段地址和一个16位的偏移地址。jmp far指令的完整形式为:jmp far ptr 段地址:偏移地址,其中far ptr表示跳转地址是一个16位段地址和一个16位偏移地址的组合。

最后,关于您的问题,多个代码段的定义是允许的,而且jmp far指令是可以正常使用的。可能是由于代码段code2没有被正确加载到内存中导致跳转失败。您可以尝试使用调试工具来查看代码段的地址和内容,以便更好地理解jmp far指令的作用和实现方式。

希望能对您有所帮助,如果还有其他问题,欢迎继续咨询。

soul3500 发表于 2023-6-1 18:34:32

sfqxx 发表于 2023-6-1 18:26
您好,关于您的问题,我可以给您一些解释。

首先,您所编写的汇编代码在语法和逻辑上是正确的。它定义了 ...

您好,关于您说没有被正确加载到内存中导致。但是我是在编译阶段就报了missing or reachable cs的错误。{:10_266:} 无法编译成功

wuliangtdi 发表于 2023-6-1 19:04:28

soul3500 发表于 2023-6-1 18:34
您好,关于您说没有被正确加载到内存中导致。但是我是在编译阶段就报了missing or reachable cs的错误。{ ...

问的chatgpt罢了,可能是胡说八道的,别全信{:10_256:}

soul3500 发表于 2023-6-1 19:08:53

wuliangtdi 发表于 2023-6-1 19:04
问的chatgpt罢了,可能是胡说八道的,别全信

好的 大佬。就是这个问题,我实在想不出来。是否是masm编译器 不支持 定义多个段啊

wuliangtdi 发表于 2023-6-1 19:09:47

https://i.imgloc.com/2023/06/01/VkUSYd.png
试试看bing写的
      assume cs:code2 ; 指定cs寄存器为code2段
      code2 segment
                a:   mov ax,4c00h
                     int 21h
      code2 ends

      assume cs:code ; 指定cs寄存器为code段
      code segment
                start:jmp far ptr code2:a ; 远跳转到code2段中的a标号处
      code ends

      end start      

sfqxx 发表于 2023-6-1 19:16:39

wuliangtdi 发表于 2023-6-1 19:04
问的chatgpt罢了,可能是胡说八道的,别全信

6

wuliangtdi 发表于 2023-6-1 19:30:35

soul3500 发表于 2023-6-1 18:34
您好,关于您说没有被正确加载到内存中导致。但是我是在编译阶段就报了missing or reachable cs的错误。{ ...

刚刚试了一下bing给出的代码,能编译成obj文件
https://i.imgloc.com/2023/06/01/VkU1Gp.png

wuliangtdi 发表于 2023-6-1 19:46:57

soul3500 发表于 2023-6-1 19:08
好的 大佬。就是这个问题,我实在想不出来。是否是masm编译器 不支持 定义多个段啊

https://i.imgloc.com/2023/06/01/VknVt5.png
编译通过

soul3500 发表于 2023-6-2 11:05:00

wuliangtdi 发表于 2023-6-1 19:09
试试看bing写的

谢谢大佬,我增加了一行 assume cs:code2 就可以通过了

这是修改后的代码:
assume cs:code2

code2 segment
a:    mov ax,4c00h
      int 21h
code2 ends

assume cs:code
code segment
start:jmp far ptr a
code ends

end start


对于assume 这个伪指令还不是非常熟悉,以为只能定义一次assume

thinklf 发表于 2024-6-15 21:13:59

本帖最后由 thinklf 于 2024-6-16 09:37 编辑

这其实是一个很有意思的问题,很考验对masm中assume的理解。
masm在编译汇编代码的时候,只要代码需要确定标号、变量、或堆栈地址,会去确定三件事,cs、ds、ss都用哪个段,并根据段来确定代码、数据及栈偏移量。
如jmp 需要确定cs是哪个段,pop需要确定ss,mov 需要确定ds。assume表示从assume定义之后的代码按这个设定进行编译,因此可以定义无数次。也可以随时取消定义。
定义assume segment-register:segment-name
取消:assume segment-register:nothing
全部取消:assume nothing
assume就是用来告诉编译器的,编译器靠assume提供的段信息计算偏移量。
编译a: mov ax,4c00h时,mov ax,4c00h不需要计算偏移量。a标号(masm把标号和变量进行了区分,不像nasm,只有标号),说明是计算代码的偏移量。相对于哪个cs段呢,默认是code。其实这样指定并没有问题,你计算a与code开始处的偏移量不就完了。但masm认为这样你何必分段呢,所以,即使能执行,也不让你这么用。
你思考一下这段代码,编译后debug反编译看看(忽略掉没有stack的警告)。
assume cs:code
code segment
    start:mov ax,7788h
    jmp far ptr ab
    mov ax,ab
code ends

code2 segment
    ab dw 5566h
    mov ax,ab
    int 21h
code2 ends
end start

thinklf 发表于 2024-6-15 21:24:41

还有就是涉及到数据段的问题,数据段可以有2个段寄存器:ds、es,则masm会给你添加段前缀,或者你手动添加。你编译一下这段代码,研究一下就懂了。
assume cs:codesg,ss:stacksg,ds:datasg1,es:datasg2
;-------------------------------------------
stacksg segment stack
    db 100 dup (0)
stacksg ends
;-------------------------------------------
datasg1 segment
    tnum1 db 6
datasg1 ends
;-------------------------------------------
datasg2 segment
    tnum2 db 9
datasg2 ends
;-------------------------------------------
codesg segment
    start:
    mov ax,datasg1
    mov ds,ax
    mov ax,datasg2
    mov es,ax
;-------------------------------------------
    mov al,tnum1
    mov al,tnum2
;-------------------------------------------
    mov ax,4c00h
    int 21h
codesg ends
end start

thinklf 发表于 2024-6-15 21:27:25

本帖最后由 thinklf 于 2024-6-15 21:42 编辑

最后一个问题,当assume cs:code,ds:data后,为什么还要在代码中把data段的寄存器值手动赋值给ds和es呢?不是已经assume了么?那为什么cs和ss不用手动赋值呢呢?
你都想清楚了么{:5_109:}

thinklf 发表于 2024-6-15 22:11:10

本帖最后由 thinklf 于 2024-6-16 06:27 编辑

我突然得出一个总结:masm代码中,可能需要访问段内(cs段)代码,也可能需要跨段访问代码;数据也是,可能需要访问默认段内数据(就是ds段内),也可能需要访问其他段数据。因为代码段寄存器只有一个,只能通过改变cs和ip的值进行访问,但cs和ip不让直接改。手段只有jmp far或call far或push/pop等。那数据就要轻松很多,es和ds值可以直接改,偏移地址(对应是段ip)更是方法极多。那么多寻址方式,都是给数据访问准备的。跨段访问数据可以通过段前缀es:offset,或改变当前ds的值,然后访问(默认段为ds)。
堆栈可以多个么?也可以。但没必要,容易出错,且一个栈就够用了,就看你栈空间够不够。所以栈的段定义的方式都是stacgsg segment stack,有一个关键字专门支持栈。如果定义多个栈,只是把它们合并而已。栈里的数据,一般情况下也不重要。在简单段声明方式下,直接是.stack 100h,直接声明大小。你甚至都可以不定义stack,link时直接加参数/stack:100也行。

thinklf 发表于 2024-6-15 23:06:43

本帖最后由 thinklf 于 2024-6-15 23:29 编辑

thinklf 发表于 2024-6-15 21:13
这其实是一个很有意思的问题,很考验对masm中assume的理解。
masm在编译汇编代码的时候,只要代码需要确定 ...

label:定义一定要执行cs和段是否一致的检查,可通过下面手段跳过这个检查。
code segment和code2 segment空间不是连续的(我们之前讨论过,你的另一个帖子)。
啰嗦一句,为什么两个段位置调换了一下。因为编译时在计算a的偏移地址是负的(a先出现,code segment后出现,偏移地址是负的,可编译,不可链接)。
assume cs:code

code segment
    start:jmp far ptr a
code ends
code2 segment
    a label far
    mov ax,4c00h
    int 21h
code2 ends

end start   
assume cs:code

code segment
    start:jmp far ptr a
code ends
assume cs:code2
code2 segment
    a label far
    mov ax,4c00h
    int 21h
code2 ends

end start      

这2段代码,都可以通过编译,你debug发现,一个是段内调(是far条,但段地址没变),一个是跨段调。但结果都是一样的(一定要注意,两个segment没有紧邻,所以中间有很多0填充)

thinklf 发表于 2024-6-16 10:18:31

thinklf 发表于 2024-6-15 23:06
label:定义一定要执行cs和段是否一致的检查,可通过下面手段跳过这个检查。
code segment和code2 seg ...

为了验证我的说法,我做了一个实验,可证明确实这样:
assume cs:acode
bcode segment
    a label far
    mov ax,4c00h
    int 21h
bcode ends
acode segment
    start:jmp far ptr a
acode ends
end start
编译方法1:
masm b1.asm;
link b1.obj;
(出现错误)
编译方法2:
masm b1.asm /A;
link b1.asm;
(编译成功)
/A的意思是把segment名称按字母进行排序,结果吧acode排在了前面,跟代码写前面效果相同。
页: [1]
查看完整版本: 关于jmp far ptr的问题