assembly - IBM 5150 - int 21h 损坏段寄存器

标签 assembly x86 dos x86-16 masm

我一直在模拟 IBM 5150、PC-DOS 1.0、IBM Macro Assembler 1.0 上玩汇编。 (86box 3.7.1)

我从一个简单的“Hello World”开始:

stack    segment   stack                                                        
         db        80h dup(?)                                                   
stack    ends                                                                   
                                                                                
data     segment                                                                
msg      db        'Hello World!',13,10,'$'                                     
data     ends                                                                   
                                                                                
code     segment                                                                
         assume    cs:code,ds:data                                              
         mov       dx, seg msg                                                  
         mov       ds, dx                                                       
         mov       dx, offset msg                                               
         mov       ah, 09h                                                      
         int       21h                                             
         int       20h                                                          
code     ends                                                                   
                                                                                
         end

可悲的是,程序在输出后崩溃了。通过调试我发现,在 21h 中断之后,DS、CS 和 IP 寄存器已损坏。

a:debug hello.exe
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=0080  BP=0000  SI=0000  DI=0000  
DS=049F  ES=049F  SS=04B1  CS=04AF  IP=0000   NV UP DI PL NZ NA PO NC 
04AF:0000 BAB004        MOV     DX,04B0                 
-t

AX=0000  BX=0000  CX=0000  DX=04B0  SP=0080  BP=0000  SI=0000  DI=0000  
DS=049F  ES=049F  SS=04B1  CS=04AF  IP=0003   NV UP DI PL NZ NA PO NC 
04AF:0003 8EDA          MOV     DS,DX                   
-t

AX=0000  BX=0000  CX=0000  DX=04B0  SP=0080  BP=0000  SI=0000  DI=0000  
DS=04B0  ES=049F  SS=04B1  CS=04AF  IP=0005   NV UP DI PL NZ NA PO NC 
04AF:0005 BA0000        MOV     DX,0000                 
-t

AX=0000  BX=0000  CX=0000  DX=0000  SP=0080  BP=0000  SI=0000  DI=0000  
DS=04B0  ES=049F  SS=04B1  CS=04AF  IP=0008   NV UP DI PL NZ NA PO NC 
04AF:0008 B409          MOV     AH,09                   
-t

AX=0900  BX=0000  CX=0000  DX=0000  SP=0080  BP=0000  SI=0000  DI=0000  
DS=04B0  ES=049F  SS=04B1  CS=04AF  IP=000A   NV UP DI PL NZ NA PO NC 
04AF:000A CD21          INT     21                      
-t
Hello World!

AX=0040  BX=0000  CX=0000  DX=0000  SP=FD24  BP=0000  SI=0000  DI=0000  
DS=0040  ES=049F  SS=04B1  CS=FFFF  IP=FFFF   OV DN EI NG ZR AC PE CY 
FFFF:FFFF 00EA          ADD     21

谁能给我解释一下这是为什么?

我为 PC-DOS 2.0 写了一个类似的程序,它运行良好。 (只用21h,4Ch结束,PC-DOS 1.0不支持)

PS:我这样做是出于对历史的好奇,所以...

最佳答案

我可以明确地回答你问题的一部分:当 Int 21h/AH=4Ch 时,你如何正确地退出 DOS .EXE 程序?在 DOS 1.0 中不可用?

主要方法是使用 Int 20h,但有一点需要注意。当您使用 Int 20h 时,您必须将 CS 设置为 Program Segment Prefix (PSP) 所在的段在。问题是在 EXE 程序中 CS 不指向 PSP 段,它指向一个单独的代码段!

一般的解决方案是利用 DS(和 ES)在启动时指向 EXE 程序中的 PSP 段这一事实。您还可以利用在 PSP 的第一个字中有一个 Int 20h 指令这一事实。我们可以使用一个FAR Return to PSP_Segment:0000h来执行PSP中的Int 20h来退出程序。

终止 DOS .EXE 程序的代码可能类似于:

stack    segment   stack
         db        80h dup(?)
stack    ends

data     segment
data     ends

code     segment

main     proc far                ; Marking this `far` will make the
                                 ; assembler convert RET to a FAR RETURN

         push ds                 ; DS = PSP, save PSP segment on stack

         ; Insert program code here. 
         ; Make sure the stack is balanced when finished         
         ; -----------------
         assume cs:code,ds:data
         mov dx, data
         mov ds, dx
         ; -----------------

         ; Exit EXE program with FAR Return to PSP_Segment:0000h where
         ; an Into 20h resides in the word at offset 0000h in the PSP

         xor ax, ax              ; AX = 0
         push ax                 ; Push 0 on the stack
                                 ; Note: `PUSH` with an immediate value 
                                 ; wasn't a valid instruction on 
                                 ; Intel 8088/8086 processors

         ; At this point the DS (PSP) pushed at the beginning of the program
         ; and the value 0000h forms a CS:IP FAR pointer on the stack

                                 ; A FAR return is done since
                                 ; procedure main was declared as FAR

         ret                     ; Return to PSP_Segment:0000h via FAR pointer
                                 ; on the stack and execute
                                 ; the Int 20h at that address
main     endp                    ; End of procedure main
code     ends

         end

我相信 DEBUG 的原始版本中可能存在导致跟踪问题的错误,但我不能确定,而且我手头没有 PC-DOS 1.0 环境来尝试它。我开始认为 trace 正在跟踪不止一条指令,并且可能同时执行 Int 21hInt 20hint 20h 由于上一节中给出的原因而失败。


注意事项

在 COM 程序中,CS 是包含 PSP 和您的代码的段。 PSP 是段的前 256 个字节,也是 COM 程序的原点 (ORG) 为 100h 的原因。这就是为什么 Int 20h 可以在这种环境中工作,前提是您不在代码中自行更改 CS

关于assembly - IBM 5150 - int 21h 损坏段寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73587054/

相关文章:

linux - IN 指令段错误,内联汇编 GCC

c - 使用内联组件绘制像素 VGA

windows - 如何在DOS下显示汇编中的字符?

c - 如何在 vxworks 6.7 中设置 wdb 进行内核前调试?

sorting - 理解 assembly

编译包含包含 C 定义的头文件的汇编代码

windows - 根据批处理文件中的第二个变量调用变量

c - 从 gcc 中的内联汇编引用全局变量

x86 - 将 PE 加载到内存中进行仿真

assembly - 我可以从堆栈的中间弹出吗?