Program Design

May 11th, 2020

1. 程序结构

1.1 分段式程序结构

以一个排序的汇编语言为例:

STACK        SEGMENT    PARA STACK
STACK_AREA  DW      100h DUP(?)
STACK_TOP    EQU    $-STACK_AREA
STACK        ENDS

DATA        SEGMENT    PARA
TABLE_LEN       DW 16
TABLE           DW 200,300,400,10,20,0,1,8
                DW 41H,40,42H,3321h,60,0FFFFH,2,3

MY_NAME         DB 'My name is 17061517373492LiuQuqi','$'

ADD1            DD 20003000H
ADD2            DD 12345678h
DATA        ENDS

CODE        SEGMENT
        ASSUME    CS:CODE,DS:DATA
        ASSUME    SS:STACK
MAIN        PROC    FAR

START:          MOV     AX,STACK            
                MOV        SS,AX
                MOV        SP,STACK_TOP
                MOV        AX,DATA
                MOV     DS,AX               ;SET SS,SP,DS

                JMP     START1
                LDS     SI,ADD1
                LES     DI,ADD2

                JMP     short l1
                JMP     NEAR ptr  l1
                JMP     FAR ptr   l1
                JMP     BX
                JMP     BX
                JMP     Word PTR  [BX]
....

上面是一个节选的堆栈的定义部分,汇编语言是一个横竖三段式结构:

  • 横三段式是指:[标号:] 指令助记符 指令操作数 [;注释]

    • 标号是用来确定偏移地址,后面一定紧跟冒号

    • 指令助记符就是常见的指令 MOV, ADD 等

    • 指令操作数由指令助记符确定,有零操作数,一个操作数和两个操作数

    • 注释在行尾用分号表示开始,在汇编的时候会自动忽略

  • 竖三段是指:段起始 段内指令 段结束

    • 段起始如上面的:STACK SEGMENT PARA STACK

    • 段结束如上面的:STACK ENDS

    • 在段起始位置和段结束位置之间可以编写段内指令

1.1.1 语句格式

  • 指令语句

    • 汇编后生成一个可以供机器执行的机器目标代码

    • 格式:[标号:] 指令助记符 指令操作数 [;注释],如:

      • START: MOV AX,STACK

      • MOV DS,AX ;SET SS,SP,DS

  • 伪指令语句

    • 伪指令语句在汇编后不产生对应的目标机器代码,但是在程序中起到指示的作用,只有通过伪指令的组织,指令序列才能正常地放置和执行

    • 格式:[符号名] 伪指令 操作数 [;注释],如:

      • STACK SEGMENT PARA STACK

      • STACK ENDS

      • TABLE_LEN DW 16

    • 符号名有很多种,可以是段名、过程名、变量名、常量名等,可以根据伪指令的需要加上符号名

  • 注释行

    • 用分号开头的一行或多行,常用于说明下面的一段程序的作用,或阐明编程者的思路,以便阅读者理解。

  • 字段对齐

    • 指令的书写应该按照分横向分段的格式,对于有空缺的位置空出

    • 汇编语言程序中的指令语句不允许跨行或拆行,这与高级语言程序不同

      • 如定义一个比较长的数组的时候,实在一行放不下可以分开两行但是两行都必须要有,如:

        TABLE   DW 200,300,400,10,20,0,1,8
              DW 41H,40,42H,3321h,60,0FFFFH,2,3

1.1.2 标号和符号名

  • 指令语句中的标号和伪指令语句中的符号名统称为标识符(identifier)。标号的定义出现在指令语句中,它后面有冒号。符号名(变量名、常量名、段名、过程名)在定义时则没有冒号,且出现在伪指令中。

  • 在命名规则上,标号和符号名相同。标号名和符号名都必须以字母或专用字符(?, @, 一)打头,中间不能有空格或运算符号,其长度会因汇编器的厂家和版本的不同而不同。

  • 为了不与符号名和标号相混淆,指令和伪指令语句中出现的常数,当以A~F打头时,必须在前面加上一个0,否则就会将它当作变量名或常量名而出现错误。

  • 在给标号和符号名命名时,还必须注意与汇编语言中的保留字相区别。保留字主要有:

    • CPU中的寄存器名

    • 指令助记符

    • 伪指令

    • 表达式中的运算符

    • 指令或伪指令操作数中的属性操作符

  • 在汇编语言中,无论是常数、常量、标号、符号名、指令助记符、伪指令、操作符、参数等,都不区分大小写

1.1.3 程序中的段和过程

  • 堆栈段和数据段中定义的都是数据,主要是通过 EQU(常量定义) 以及 DB, DW, DD 等实现的。本例中在堆栈段定义的有:

    STACK_AREA  DW    100h DUP(?)
    STACK_TOP      EQU    $-STACK_AREA
    ...
    TABLE_LEN 16
    TABLE   DW 200,300,400,10,20,0,1,8
            DW 41H,40,42H,3321h,60,0FFFFH,2,3
  • 在代码段中:

    ASSUME CS:CODE1, DS:DATA1, SS:STACK1

    则告诉了汇编器要将CS设置成CODE1的段首地址,DS设置成DATA1的段首地址,SS设置成STACK1的段首地址,这种ASSUME并没有直接将段寄存器附上初值,在程序中还是要通过显示的指令指定,但在程序代码段的开始还是需要放上这样的一条ASSUME语句。

    查阅官方论坛得到的ASSUME的理解:

    • After an ASSUME is put into effect, the assembler watches for changes to the values of the given registers. ERROR generates an error if the register is used. NOTHING removes register error checking. You can combine different kinds of assumptions in one statement.

    • 尝试修改CS为STACK,SS为CODE,在编译阶段就会发生报错:

    • 尝试修改SS为DATA,DS为STACK,汇编和连接都可以通过:

      但是运行会死循环,通过DEBUG发现初始化后唯一的不同:

  • 代码段中的内容由主程序和子过程组成,两者都是统一的过程,都要用 PROCENDP 一对伪指令来指定。过程必须有名字

    • 在主程序的开始阶段的这几条初始化段寄存器的语句是每个程序必不可少的:

      MOV AX, STACK
      MOV SS, AX ;由于STACK在汇编后是立即数,而段寄存器不能使用立即数赋值,所以需要经过一个AX寄存器中转
      MOV SP, STACK_TOP ;SP是栈顶指针,不是段寄存器所以可以使用立即数赋值
      MOV AX, DATA
      MOV DS, AX
    • 程序末尾的返回语句也是不可缺少的:

      EXIT: MOV AX, 4C00H ;AH放入功能号4CH,AL放入返回码00H
          INT 21H       ;中断触发,debug时要用p单步跳过

      上面两句命令的结果是使程序执行正常返回到MS-DOS命令提示符,返回值为0。

      注意:如果使用 RET 语句代替这两条语句不能正常返回到MS-DOS提示符。

    • 程序最末尾用一个 END MAIN 结束,表示汇编程序的结束,并且告诉编译器执行从MAIN开始,这里的MAIN是一个标识符,可以自己选择。编译器将会忽略后面的所有内容。

1.2 定义程序结构的伪指令

1.2.1 段定义伪指令

段名 SEGMENT [对齐类型][组合类型][类别名]
     ...     ;段中的程序指令和数据定义指令
段名 ENDS
  • 段名是用户自定义的,只需要保证一个短的开始SEGMENT和结束ENDS的段名相同即可,并且在段引用指令ASSUME语句中也要使用相同的段名。

  • 对齐类型是可选字段,表示段在内存中分配时的起始边界设定,可选的参数有:

    • PAGE(页):表示本段从页的边界开始。一页为256字节,所以段的起始地址(段基地址)的低8位为0。采用PAGE方式对齐时,段与段之间最大的空隙可能为255字节。

    • PARA(节):表示本段从节的边界开始。一节为16字节,所以段的起始地址的低4位为0。采用此种对齐方式,段内偏移值可以从0开始。

      • SEGMENT的缺省对位类型就是PARA,所以一般都省略此参数。采用PARA对齐时,段与段之间最大可能空隙是15字节。

    • WORD(字):表示本段从内存中的偶字节地址开始。段与段之间的空隙最多只有1字节。

    • BYTE(字节):表示本段从字节地址开始,段与段之间无任何空隙

      当只用BYTE对齐时:

  • 组合类型也是可选字段,用于确定段与段之间的关系。当程序有多个模块(或多个数据段、代码段)以及有特殊的要求时,可通过组合类型来确定各段之间段的组合方式。这些方式有:

    • NONE:这是缺省选项,表示本段是独立的,不需要与其他段组合。在装人内存时,本段有自己的段基址。不指定组合类型即是NONE。

    • PUBLIC:在满足定位类型的前提下,连接程序会将本段与其他具有相同段名的段组合成一个大的逻辑段,它们共用同一个段基址。此段基址是最早的那个同名段的段基址。

      • 所有段内的偏移量都要变为相对于新逻辑段的起始地址。采用PUBLIC组合类型,可将不同模块中相同段名的若干小段拼装成-个大的物理段

    • COMMON:该段在连接时与其他同名的段有相同的起始地址,所以会产生覆盖

      • COMMON连接的段长是各同名段中最长的段的长度。

      • 覆盖的实质是将同一内存块按不同的逻辑方式来组织。

    • STACK:说明该段为堆栈段的一部分。连接程序在连接时,会把所有同名的具有STACK组合类型的段连接成一个连续段,并将SS初始化成这个连续段的首地址,用段内的最大偏移地址初始化SP

      • 正确地定义了段的STACK属性后,可以在主程序中省略对SS,SP的初始化

    • MEMORY:表示本段在内存中应定位在所有其他连接在一起的段的前面(高地址上)

      • 如果连接时遇到几个指定了MEMORY的段,则遇到的第一个段被当作MEMORY段,其他段作为COMMON段。

    • AT表达式:表示本段可以直接定位在内存的某个位置,且以节边界对齐。例如:

      • S1 SEGMENT PARA AT 0B800H

      • 表示S1段在内存中的物理位置为B800H。

  • 类别名依然是可选字段,必须用单引号(')或双引号(")括起来。

    • 类别名可以是由编程人员指定的任何名字,但它不能与程序中的其他符号名和标号相重

    • 在程序被连接时,连接程序会将类别名相同的所有段存放在连续的内存区中,但它们仍然是不同的段

    • 这种组合与组合类型不同,而且当有组合类型时,组合类型先于类别名的组合

  • 举个例子,画出下面的程序的内存图:

    STACK1  SEGMENT PARA STACK 'STACK'
            ...
    STACK1  ENDS
    DATA1   SEGMENT PARA 'DATA'
            ...
    DATA1   ENDS
    STACK2  SEGMENT PARA 'STACK'
            ...
    STACK2  ENDS
    DATA2   SEGMENT PARA 'DATA'
            ...
    DATA2   ENDS
    DATA3   SEGMENT PARA AT 0B8000H
            ...
    DATA3   ENDS
    CODE    SEGMENT PARA
            ASSUME CS:CODE, DS:DATA1, SS:STACK1
    MAIN    PROC FAR
            ...
    MAIN    ENDP
    CODE    ENDS
            END  MAIN

    首先可以看到除了STACK1的STACK外没有其他的组合类型,但是大多都有类别名,所以将类别名相同的组织在连续的内存空间中,但因为没有相同的组合类型所以还是属于不同段,所以存放的顺序为:

    • STACK1

    • STACK2

    • DATA1

    • DATA2

    • CODE

    • DATA3(存放于0B8000H)

1.2.2 过程定义伪指令

过程名  PROC [NEAR|FAR]
        ...
        RET
过程名  ENDP
  • 过程的最后一条语句一般都是RET,用于返回至调用者;对于MAIN而言可能是 MOV AX, 4C00HINT 21H 表示返回至MS-DOS指令提示符。

  • 每个过程PROC必须有自己的过程名,其他位置可以通过CALL过程名进行跳转,过程名后直接跟PROC,没有冒号

  • NEAR|FAR 参数

    • NEAR 属于段内调用,CALL指令执行时只需要向堆栈中压入CALL指令下一条指令的偏移地址;RET指令在汇编后仍然是RET指令,会从堆栈中弹出一个字作为IP值

    • FAR 属于段间调用,当跳转的地址与当前的IP的偏移量大于16位有符号数范围(-32768~32767)时使用,此时CALL指令会向堆栈中压入CALL指令的下一条指令的段地址以及偏移值;RET在汇编后变成了RETF指令,会从堆栈中先后弹出两个字作为IP值以及CS值

    • 若该参数未定义则默认为NEAR,但如果实际上是FAR,在编译可以通过,连接会失败

1.2.3 定位伪指令 ORG

  • SEGMENT语句的各种属性会定义段中内存的分配方式,但内存的分配无论是数据还是指令都是从偏移量为0开始往后放置的。

  • 若想要为下一条指令或数据定义指定一个特定的偏移地址,可以使用ORG定位伪指令,参数为偏移量。

  • 样例:

    DATA3  SEGMENT PARA AT 0B800H
           ORG  2000H
    String DB 'ABCD'
    DATA3  ENDS

    String的逻辑地址首地址为 B800H:2000H,物理地址为 BA000H。

    • 测试:在TABLE_LEN后面加上ORG 20H

      • 未加入ORG:

      • 加入ORG:

2. 调试运行

本课程使用的是 MicroSoft 的 MASM v6.1 在 DOSBox v0.74 上进行实验。

2.1 汇编与连接过程

进入 MASM 的 BIN 可执行文件夹,执行汇编指令:

MASM program.ASM

再通过连接指令:

LINK program.OBJ

此时可以通过

program.EXE

2.2 文件

|文件后缀|文件类型|生成方式| |ASM|汇编程序文件|自己创建| |LST|清单文件|由宏汇编器生成| |MAP|内存映象文件|连接程序生成|

2.2.1 LST 文件

2.2.2 MAP 文件

31 0000  B8 ---- R        START:    MOV     AX,STACK

就是因为还不知道 STACK 的位置所以地址空出,但是经过连接阶段,内存映象确定后,每个段的起始位置,长度和终止位置已经获得,所以可以反填 LST 中空缺的部分。并且此时确定了程序的入口:0025:0000

2.3 调试

使用debug命令进入调试窗口:

debug program.EXE

会出现 - 光标,可以输入调试指令:

d -- 显示内存单元内容 display

常用来查看数据段,和DS配合使用

d 不带参数:延续上一次使用d之后的位置,第一次使用则是程序起始位置
d 偏移地址:隐含相对于DS而言的偏移地址
d ds:偏移地址
d 地址1 地址2:显示包含两端地址的地址范围内的字节内容 (其余默认128D/80H字节)

内存的显示分为三列,分别是:

  • 逻辑地址

    • 逻辑地址的表示为 高16位:低16位

    • 中间重叠部分为12bit,也就是3位十六进制

    • 真实地址为:高16位<<4 + 低16位

  • 单元内容

  • 对应的ASCII字符

在实际的编程中,可以通过使得地址1和地址2相等的方式来确定地址上的数据内容,比数格子要方便一些。

e -- 修改内存单元内容 edit

发现数据区的内容不符合我们的预期时,可以使用e进行修改,命令如下:

e 地址:回车后进行输入内容
e 地址 内容:直接将内容填充到地址中

r -- 显示或修改寄存器内容 register

r 不带参数:显示所有寄存器的内容
r 寄存器:显示某个寄存器名,随后还可以进行修改

在调试程序的过程中,如果发现指令结果不对且该结果暂存于寄存器中,则可以先修改寄存器的值得以继续进行调试,并在最终源程序中改正错误。

f -- 填写内存单元(批量修改) fill

t -- 单步跟踪 trace

单步跟踪又称为“单步进入”,即遇到CALL命令时会深入进子程序。t命令的用法为:

t 不带参数:表示从CS:IP处往下执行一条指令
t [=地址][值]:表示从 地址 开始执行 值 条指令

p -- 单步执行

单步执行又称“单步通过”,因为其在遇到CALL命令时会一次执行完,不会深入到子程序中。 实际程序调试的情况:

  • 需要进入子程序查看每条指令是否正确 —— t

  • 不需要进入子程序,希望一次执行完 —— p

  • 遇到 INT n 等中断调用,必须用 p 单步通过,因为系统调用是厂家编写,执行过程很长,一旦进入很难回到用户程序

g -- 连续运行 go

g [=起始地址][断点地址1][断点地址2]...
g 不带参数:执行到程序结束

u -- 反汇编

用来查看程序段,配合CS使用,使用方法同d指令

u 不带参数:反汇编从 CS:IP 处开始的代码
u 地址:反汇编从地址开始的代码
u 返回:反汇编包含两端的指定范围的代码 (默认反汇编长度为32字节)

a -- 汇编 assemble

用户修改代码段中的指令,可以在源代码的基础上修改也可以直接在某个代码位置处编一段程序。在修改前应该用u命令找到精确的修改的位置,在修改后应用u命令检查修改是否正确。

在程序中还可以输入伪指令,即数据定义,但是插入的数据定义没有标号和变量,只有地址。

n -- 命名文件 name

指定一个文件名,用于后面装入和写入命令使用,格式为:

n 文件名

l -- 装入文件 load

l [地址]:地址默认值为 DS:100H

将前面用n命令指定的文件装入到l命令后指定的地址处,装入后文件的长度使用 CX:DX 组成的 32位数表示。

举例:若装入后 CX=0001, DX=A000, DS=2000, 装入指定地址为 100,则装入的文件位于 2000:0100 ~ 3000:A0FF 内存区。

w -- 写回文件 write

w [地址]:地址默认值为 DS:100H

将内存中指定的地址处开始的内容写入到前面用n命令指定的文件中。文件的长度由 CX:DX 组成的 32位数定义。

q -- 退出DEBUG quit

退出DEBUG界面回到MS-DOS命令提示符界面。

Appendix

INT

AH

功能

调用参数

返回参数

00

程序终止(同INT 20H)

CS=程序段前缀

01

键盘输入并回显

AL=输入字符

02

显示输出

DL=输出字符

03

异步通迅输入

AL=输入数据

04

异步通迅输出

DL=输出数据

05

打印机输出

DL=输出字符

06

直接控制台I/O

DL=FF(输入)DL=字符(输出)

AL=输入字符

07

键盘输入(无回显)

AL=输入字符

08

键盘输入(无回显)检测Ctrl-Break

AL=输入字符

09

显示字符串

DS:DX=串地址'$'结束字符串

0A

键盘输入到缓冲区

DS:DX=缓冲区首地址(DS:DX)=缓冲区最大字符数

(DS:DX+1)=实际输入的字符数

0B

检验键盘状态

AL=00 有输入AL=FF 无输入

0C

清除输入缓冲区并请求指定的输入功能

AL=输入功能号(1,6,7,8,A)

0D

磁盘复位

清除文件缓冲区

0E

指定当前缺省的磁盘驱动器

DL=驱动器号 0=A,1=B,...

AL=驱动器数

0F

打开文件

DS:DX=FCB首地址

AL=00 文件找到AL=FF 文件未找到

10

关闭文件

DS:DX=FCB首地址

AL=00 目录修改成功AL=FF 目录中未找到文件

11

查找第一个目录项

DS:DX=FCB首地址

AL=00 找到AL=FF 未找到

12

查找下一个目录项

DS:DX=FCB首地址(文件中带有*或?)

AL=00 找到AL=FF 未找到

13

删除文件

DS:DX=FCB首地址

AL=00 删除成功AL=FF 未找到

14

顺序读

DS:DX=FCB首地址

AL=00 读成功 =01 文件结束,记录中无数据 =02 DTA空间不够 =03 文件结束,记录不完整

15

顺序写

DS:DX=FCB首地址

AL=00 写成功 =01 盘满 =02 DTA空间不够

16

建文件

DS:DX=FCB首地址

AL=00 建立成功 =FF 无磁盘空间

17

文件改名

DS:DX=FCB首地址(DS:DX+1)=旧文件名(DS:DX+17)=新文件名

AL=00 成功AL=FF 未成功

19

取当前缺省磁盘驱动器

AL=缺省的驱动器号 0=A,1=B,2=C,...

1A

置DTA地址

DS:DX=DTA地址

1B

取缺省驱动器FAT信息

AL=每簇的扇区数DS:BX=FAT标识字节CX=物理扇区大小DX=缺省驱动器的簇数

1C

取任一驱动器FAT信息

DL=驱动器号

同上

21

随机读

DS:DX=FCB首地址

AL=00 读成功 =01 文件结束 =02 缓冲区溢出 =03 缓冲区不满

22

随机写

DS:DX=FCB首地址

AL=00 写成功 =01 盘满 =02 缓冲区溢出

23

测定文件大小

DS:DX=FCB首地址

AL=00 成功(文件长度填入FCB)AL=FF 未找到

24

设置随机记录号

DS:DX=FCB首地址

25

设置中断向量

DS:DX=中断向量AL=中断类型号

26

建立程序段前缀

DX=新的程序段前缀

27

随机分块读

DS:DX=FCB首地址CX=记录数

AL=00 读成功 =01 文件结束 =02 缓冲区太小,传输结束 =03 缓冲区不满

28

随机分块写

DS:DX=FCB首地址CX=记录数

AL=00 写成功 =01 盘满 =02 缓冲区溢出

29

分析文件名

ES:DI=FCB首地址DS:SI=ASCIIZ串AL=控制分析标志

AL=00 标准文件 =01 多义文件 =02 非法盘符

2A

取日期

CX=年DH:DL=月:日(二进制)

2B

设置日期

CX:DH:DL=年:月:日

AL=00 成功 =FF 无效

2C

取时间

CH:CL=时:分DH:DL=秒:1/100秒

2D

设置时间

CH:CL=时:分DH:DL=秒:1/100秒

AL=00 成功 =FF 无效

2E

置磁盘自动读写标志

AL=00 关闭标志AL=01 打开标志

2F

取磁盘缓冲区的首址

ES:BX=缓冲区首址

30

取DOS版本号

AH=发行号,AL=版本

31

结束并驻留

AL=返回码DX=驻留区大小

33

Ctrl-Break检测

AL=00 取状态 =01 置状态(DL)DL=00 关闭检测 =01 打开检测

DL=00 关闭Ctrl-Break检测 =01 打开Ctrl-Break检测

35

取中断向量

AL=中断类型

ES:BX=中断向量

36

取空闲磁盘空间

DL=驱动器号 0=缺省,1=A,2=B,...

成功:AX=每簇扇区数 BX=有效簇数 CX=每扇区字节数 DX=总簇数失败:AX=FFFF

38

置/取国家信息

DS:DX=信息区首地址

BX=国家码(国际电话前缀码)AX=错误码

39

建立子目录(MKDIR)

DS:DX=ASCIIZ串地址

AX=错误码

3A

删除子目录(RMDIR)

DS:DX=ASCIIZ串地址

AX=错误码

3B

改变当前目录(CHDIR)

DS:DX=ASCIIZ串地址

AX=错误码

3C

建立文件

DS:DX=ASCIIZ串地址CX=文件属性

成功:AX=文件代号错误:AX=错误码

3D

打开文件

DS:DX=ASCIIZ串地址AL=0 读 =1 写 =3 读/写

成功:AX=文件代号错误:AX=错误码

3E

关闭文件

BX=文件代号

失败:AX=错误码

3F

读文件或设备

DS:DX=数据缓冲区地址BX=文件代号CX=读取的字节数

读成功: AX=实际读入的字节数 AX=0 已到文件尾读出错:AX=错误码

40

写文件或设备

DS:DX=数据缓冲区地址BX=文件代号CX=写入的字节数

写成功: AX=实际写入的字节数写出错:AX=错误码

41

删除文件

DS:DX=ASCIIZ串地址

成功:AX=00出错:AX=错误码(2,5)

42

移动文件指针

BX=文件代号CX:DX=位移量AL=移动方式(0:从文件头绝对位移,1:从当前位置相对移动,2:从文件尾绝对位移)

成功:DX:AX=新文件指针位置出错:AX=错误码

43

置/取文件属性

DS:DX=ASCIIZ串地址AL=0 取文件属性AL=1 置文件属性CX=文件属性

成功:CX=文件属性失败:CX=错误码

44

设备文件I/O控制

BX=文件代号AL=0 取状态 =1 置状态DX =2 读数据 =3 写数据 =6 取输入状态 =7 取输出状态

DX=设备信息

45

复制文件代号

BX=文件代号1

成功:AX=文件代号2失败:AX=错误码

46

人工复制文件代号

BX=文件代号1CX=文件代号2

失败:AX=错误码

47

取当前目录路径名

DL=驱动器号DS:SI=ASCIIZ串地址

(DS:SI)=ASCIIZ串失败:AX=出错码

48

分配内存空间

BX=申请内存容量

成功:AX=分配内存首地失败:BX=最大可用内存

49

释放内容空间

ES=内存起始段地址

失败:AX=错误码

4A

调整已分配的存储块

ES=原内存起始地址BX=再申请的容量

失败:BX=最大可用空间 AX=错误码

4B

装配/执行程序

DS:DX=ASCIIZ串地址ES:BX=参数区首地址AL=0 装入执行AL=3 装入不执行

失败:AX=错误码

4C

带返回码结束

AL=返回码

4D

取返回代码

AX=返回代码

4E

查找第一个匹配文件

DS:DX=ASCIIZ串地址CX=属性

AX=出错代码(02,18)

4F

查找下一个匹配文件

DS:DX=ASCIIZ串地址(文件名中带有?或*)

AX=出错代码(18)

54

取盘自动读写标志

AL=当前标志值

56

文件改名

DS:DX=ASCIIZ串(旧)ES:DI=ASCIIZ串(新)

AX=出错码(03,05,17)

57

置/取文件日期和时间

BX=文件代号AL=0 读取AL=1 设置(DX:CX)

DX:CX=日期和时间失败:AX=错误码

58

取/置分配策略码

AL=0 取码AL=1 置码(BX)

成功:AX=策略码失败:AX=错误码

59

取扩充错误码

AX=扩充错误码BH=错误类型BL=建议的操作CH=错误场所

5A

建立临时文件

CX=文件属性DS:DX=ASCIIZ串地址

成功:AX=文件代号失败:AX=错误码

5B

建立新文件

CX=文件属性DS:DX=ASCIIZ串地址

成功:AX=文件代号失败:AX=错误码

5C

控制文件存取

AL=00封锁 =01开启BX=文件代号CX:DX=文件位移SI:DI=文件长度

失败:AX=错误码

62

取程序段前缀

BX=PSP地址

Last updated