270 likes | 488 Views
宏指令及其使用 宏指令、宏定义和宏调用 宏指令 源程序中具有独立功能的一段程序代码 宏定义 宏指令名 MACRO < 形式参数 > … ; … 宏体 … ; ENDM 宏调用 宏调用的格式为: 宏指令名 < 实际参数 >. 例: 1 ) PUSHREG MACRO PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI
E N D
宏指令及其使用 • 宏指令、宏定义和宏调用 • 宏指令 源程序中具有独立功能的一段程序代码 • 宏定义 宏指令名 MACRO <形式参数> … ; … 宏体 … ; ENDM • 宏调用 宏调用的格式为: 宏指令名<实际参数>
例: 1)PUSHREG MACRO PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI ENDM 2)LOADW MACRO PR,VAR MOV PR,VAR MOV AX,[PR] ENDM 3)SHIFT MACRO N,REG,CC MOV CL,N S&CC REG,CL ENDM 4)SAVEW MACRO PR,REG,OPC MOV [PR],REG OPC PR ENDM LOADW SI,WVAR SHIFT 4,AX,AR SAVEW SI,AX,INC … 将某变量指针WVAR指向的内存单元的内容送AX寄存器。右移4位后,再存入WVAR变量处。
宏嵌套 … MULTIPLY FIRST1,FIRST2,MULT1 +MOV AL,FIRST1 +IMUL FIRST2 +MOV MULT1,AX MULTIPLY SECOND1,SECOND2,MULT2 +MOV AL,SECOND1 +IMUL SECOND2 +MOV MULT2,AX ADDMULT AX,MULT1,MULT2 +MOV AX,MULT1 +ADD AX,MULT2 +MOV SUM,AX … MULTIPLY MACRO OPR1,OPR2,RESULT MOV AL,OPR1 IMUL OPR2 MOV RESULT,AX ENDM ADDMULT MACRO REG,VAR1,VAR2 MULTIPLY FIRST 1,FIRST2,MULT1 MULTIPLY SECOND1,SECOND2,MULT2 MOV REG,VAR1 ADD REG,VAR2 MOV SUM,REG ENDM 宏调用: ADDMULT AX,MULT1,MULT2
DELAY 6789H,0FFFFH + PUSH AX + PUSH CX + MOV CX,6789H +??0000:MOV AX, OFFFFH +??0001 DEC AX + JNZ ??0001 + LOOP ??0000 + POP CX + POP AX … DELAY 0FFF0H,8000H + PUSH AX + PUSH CX + MOV CX,0FFF0H +??0002:MOV AX,8000H +??0003:DEC AX + JNZ ??0003 + LOOP ??0002 + POP CX + POP AX • 宏定义中的标号与变量 DELAY MACRO VALUE1,VALUE2 LOCAL AGAIN1,AGAIN2 PUSH AX PUSH CX MOV CX,VALUE1 AGAIN1: MOV AX,VALUE2 AGAIN2: DEC AX JNZ AGAIN2 LOOP AGAIN1 POP CX POP AX ENDM 宏调用: … DELAY 6789H,0FFFFH … DELAY 0FFF0H,8000H …
小结 • 宏指令与子程序的异同 • 相同点:均可用来简化源程序,并可使程序对它们多次进行调用。 • 不同点: • 定义方法及格式不同。 • 子程序省内存,宏指令则不省。 • 子程序执行速度慢,而宏运行速度快。
6.2 汇编语言程序设计 编制汇编程序步骤: • 明确任务,确定算法 • 绘流程图 • 根据流程图编写汇编语言程序 • 上机调试程序 程序的基本结构: • 顺序结构 • 分支结构 • 循环结构 • 子程序结构
EDIT ABC.ASM 编辑源程序 MASM ABC.ASM 汇编源程序 Y 有汇编错误信息 ? N ABC.OBJ 形成目标程序 连接目标程序 LINK ABC.OBJ Y 有连接错误信息 ? N 形成可执行程序 ABC.EXE 装入可执行程序到内存 并执行 ABC Y 运行结果正确 ? 下一程序 N N DEBUG ? 用 调试程序查错 Y DEBUG 用 调试可执行程序 找到原因 DEBUG ABC.EXE
1.顺序程序设计 例: TITLE EXAMPLE 1 DATA SEGMENT 2 VARX DW 6 3 VARY DW 7 4 RESULT DW ? 5 DATA ENDS 6 STACK1 SEGMENT PARA STACK 7 DW 20H DUP(0) 8 STACK1 ENDS 9 COSEG SEGMENT 10 PROC1 PROC FAR 11 ASSUME CS:COSEG,DS:DATA,SS:STACK1 12 START: PUSH DS 13 MOV AX,0
续: • 14 PUSH AX • 15 MOV AX,DATA • 16 MOV DS,AX • 17 MOV DX,VARX ;DX←X • 18 ADD DX,VARY ;DX←(X+Y) • 19 MOV CL,3 • 20 SAL DX,CL ;DX←(X+Y)*8 • 21 SUB DX,VARX ;DX←(X+Y)*8-X • 22 SAR DX,1 ;DX←((X+Y)*8-X)/2 • 23 MOV RESULT,DX ;存结果 • 24 RET • 25 PROC1 ENDP • 26 COSEG ENDS • END START • 源程序代码段中: • 第15,16语句是为数据段寄存器赋值,让DS指向本程序的数据段DATA。 • 第17~第23语句完成公式计算并存储结果是代码段中。 • 第12~第14语句是为用户程序结束,返回DOS操作系统而作的准备。
DOS的装入功能 (又称EXEC系统功能) • 可执行文件.exe,应装入内存方能执行。 • 由DOS的装入功能完成。 • 在DOS的提示符后输入可执行文件的文件名, • 按回车键,DOS系统即调用装入功能 , • 将可执行程序装入内存。
当一个用户程序的可执行文件(.EXE)装入内存后,存储器分配情况如图所示当一个用户程序的可执行文件(.EXE)装入内存后,存储器分配情况如图所示 0H DS,ES 共100H个字节 SS 用户空间 CS 0FFFFFH
内 存 0000: 0000H 1KB 中断向量表 DOS 系统 COMMAND.COM xxxx: 0000H CD 20 … 程序段前缀 … … 可用 xxxx: 00FFH 内存 用户程序 hello.exe 空间 ROM BIOS FFFF:0000H 系统检测程序 • 完成以下操作: • 确定内存可用部分, • 以便存放要执行的 .exe 文件。 • 建立程序段前缀PSP • (Program Segment Prefix) • 程序段前缀大小100H, • 即256个字节。 • 存放进程间的控制信息。 • PSP最开始的两个字节CD 20, • 是一条 INT 20H指令。 • 装入可执行程序.exe
内 存 0000: 0000H 1KB 中断向量表 DOS 系统 COMMAND.COM xxxx: 0000H CD 20 … 程序段前缀 … … 可用 xxxx: 00FFH 内存 CS:IP hello.exe 空间 ROM BIOS FFFF:0000H 系统检测程序 • 修改以下寄存器的值 • DS、ES设置为 • 程序段前缀所在内存的段值; • (DS)=xxxxH • (ES)=xxxxH • SS、SP 设置为 • 由连接程序传过来的值; • CS、IP 设置为 • 程序的入口地址, • 即伪操作END后跟的 • 符号名对应的物理地址; • 此时 CS:IP 指向用户程序, • 开始执行用户程序。
20H 中断程序的功能: • 处理程序结束,返回系统。 • 调用20H中断程序是有条件的: • 要求当前的CS应为程序段前缀在内存的段值
CD 20 xxxx:0 程序段 PSP 前缀 …. 用户程序 0000 PSP 的偏值 SS:SP xxxx PSP 的段值 SS:SP CS IP • 采用下面的程序框架, • 可保证执行INT 20H时, • 当前的CS值为程序段前缀在内存的段值。 • code SEGMENT • ASSUME CS:code • main PROC FAR ;使RET为远返回 • start: PUSH DS ;入栈保存地址 • MOV AX, 0 ;程序段前缀的首地址 • PUSH AX • … ;程序主体部分 • … • RET ;取程序段前缀首地址 • main ENDP • code ENDS • END start
注意: 不可在汇编语言程序的最后用 INT 20H返回DOS。原因是20h中断子程的执行是有条件的。采用上述返回DOS的程序结构,才能满足该条件,否则无法返回。
结束用户程序,返回操作系统的另一个办法是用中断指令“INT 21H”。如使用这种办法,用户程序可以不设置过程,只要在用户程序结束时,用以下两条指令即可: MOV AH,4CH INT 21H 这样,上述程序的代码段可以修改为: COSEG SEGMENT ASSUME CS:COSEG,DS:DATA ASSUME SS:STACK1 START: MOV AX,DATA MOV DS,AX MOV DX,VARX ADD DX,VAY MOV CL,3 SAL DX,CL SUB DX,VARX SAR DX,1 MOV RESULT,DX MOV AH,4CH INT 21H COSEG ENDS END START
… 多个分支 Y N 两个分支 2.分支程序设计 分支程序结构也称条件结构,通常有两种形式,见图。
例:求补码数[X]补的绝对值,并送回原处。 STACK SEGMENT STACK ;定义堆栈段, DW 256 DUP(?) ;预留256个单元 TOP LABEL WORD STACK ENDS DATA SEGMENT XADR DW 3456H ;设[X]补=3456H DATA ENDS CODE SEGMENT MAIN PROC FAR ASSUME CS:CODE,DS:DATA,SS:STACK START: MOV AX,STACK ;将堆栈段段址送SS。 MOV SS,AX MOV SP,OFFSET TOP ;设置栈指针,使其指向栈顶地址。 PUSH DS ;将PSP中INT 20H指令的存放地址压入栈。 MOV AX,0 PUSH AX MOV AX,DATA ;将数据段段址送DS。 MOV DS,AX MOV AX,XADR ;取[X]补到AX。 AND AX,AX;设置标志位。 JNS DONE;若X≥0,转DONE。 NEG AX;若X<0,求补得|X|。 MOV XADR,AX;将|X|送回原处。 DONE: RET ;返回PC-DOS状态。 MAIN ENDP CODE ENDS END START
例:设有一组(8个)选择项存于AL寄存器中,试根据AL中哪一位为1把程序分别转移到相应的分支去。 BRANCH_ADDR SEGMENT ;定义数据段。 BRANCH_TAB DW ROUTINE1 ;定义数据表(跳转表)。 DW ROUTINE2 DW ROUTINE3 DW ROUTINE4 DW ROUTINE5 DW ROUTINE6 DW ROUTINE7 DW ROUTINE8 BRANCH_ADDR ENDS
续: ROUTINE_SELECT SEGMENT ;定义代码段。 MAIN PROC FAR ASSUME CS:ROUTINE_SELECT,DS:BRANCH_ADDR START: PUSH DS ;将PSP中INT 20的存放地址压入栈顶。 SUB BX,BX PUSH BX MOV BX,BRANCH_ADDR MOV DS,BX CMP AL,0 ;判AL中是否有置1的位。 JE DONE ;若AL全零,及早退出选择结构。 LEA BX,BRANCH_TAB ;跳转表首址送BX。 COUTINUE: SHR AL,l ;AL最低位移至CF。 JNC NOT_YET ;CF=0,转去检查下一位。 JMP WORD PTR [BX] ;CF=1,转相应分支程序。 NOT_YET: ADD BX,TYPE BRANCH_TAB ;修改BX内容,为转入下一分支作好准备。 JMP COUTINUE ;继续检查下一选择项。 DONE: RET ;若无选择项置位转此处,执行其它程序。 ROUTINE1: … ROUTINE2: … … … RET MAIN ENDP ROUTINE_SELECT ENDS END START
循环初始设置 循环体 循环初始设置 N N 循环条件判断? 循环条件判断? Y Y 循环体 3.循环程序设计 常见的循环程序结构有两种:WHILE_DO结构和DO_UNTIL结构,见图。 当型循环 (当条件成立进入循环) 直到型循环 (直到条件成立退出循环)
例:将内存的二进制数转化成以压缩的BCD码形式存储的十进制数。 .MODEL SMALL .386 DATA SEGMENT BINNUM DD 12345678H ;内存中的二进制数。 DECINUM DB 5 DUP(0) ;转化成压缩的BCD码的存储区。 NUMBER DD 1000000000,100000000,10000000,1000000 DD 100000,10000,1000,100,10,1 ;减数。 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA MAIN PROC FAR START: PUSH DS MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX ;初始化。 MOV EAX,BINNUM ;将数放入EAX。 MOV SI,OFFSET DECINUM ;SI指向BCD码存储区。 MOV DI,OFFSET NUMBER ;DI指向减数区。 MOV CX,5 ;循环5次。 ROTATE: CALL BINDECI ;调用二进制转化成十进制子程序。 ADD DI,8 ;DI下移两个双宇。 INC SI ;SI下移一个字节。 LOOP ROTATE ;若CX≠0则循环,输出下两位。 RET MAIN ENDP
BINDECI PROC NEAR PUSHF PUSH CX ;入栈。 MOV CX,0 ;CX存放商,初始清0。 CLC ;清除CF。 DO_AGAIN1: SUB EAX,DWORD PTR[DI] ;EAX减去相应的减数。 JC NEXT1 ;若不够减,则跳出循环。 INC CL ;否则,CL加l(商)。 JMP DO_AGAIN1 ;继续减。 NEXT1: MOV CH,CL ;将商移入CH。 MOV CL,0 ;CL清0。 ADD EAX,DWORD PTR[DI] ;恢复最后一次减前的值。 CLC ;清除CF。 DO-AGAIN2: SUB EAX,DWORD PTR[DI+4] ;EAX减去下一个数。 JC NEXT2 ;若不够减,则跳出循环。 INC CL ;否则,CL加1(商)。 JMP DO_AGAIN2 ;继续减。 NEXT2: ADD EAX,DWORD PTR[DI+4] ;恢复最后一次减前的值。 ;CH:CL中放的是高两位商,是非压缩的BCD码。 ;以下将它们转化成压缩的BCD码并放入存储单元。 SHL CH,1 SHL CH,1 SHL CH,1 SHL CH,1 ;CH左移4位。 OR CH,CL ;CH中存放压缩的BCD码。 MOV BYTE PTR[SI],CH ;放入存储区。 POP CX POPF RET BINDECI ENDP CODE ENDS END START
例:将内存中以压缩的BCD码形式存储的十进制数转化成二制数 .MODEL SMALL .386 DATA SEGMENT BINNUM DD 0 DECINUM DB 12H,34H,56H,78H,90H ;用十六进制数表示压缩的BCD码。 DATA ENDS ;十进制数是1234567890。 CODE SEGMENT ASSUME CS:CODE,DS:DATA MAIN PROC FAR START: PUSH DS MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX ;初始化 MOV EAX,0 ;32位二进制数初始值为0。 MOV SI OFFSET DECINUM ;DS:SI指向 DECINUM。 MOV CX,5 ;循环5次。 ROTATE: CALL DECIBIN INC SI ;SI指向下一个字节 LOOP ROTATE MOV DWORD PTR BINNUM,EAX ;将32位二进制数放入 BINNUM。 RET MAIN ENDP
DECIBIN PROC NEAR PUSHF PUSH ECX PUSH EBX BEGIN: MOV ECX,100 ;ECX放置乘数 100。 MUL ECX ;EAX乘100 MOV CH,BYTE PTR[SI] ;取出一个压缩的BCD码到CH。 MOV CL,CH ;复制到CL。 AND CL,0FH ;CL中保留低4位。 AND CH,0F0H ;CH中保留高4位,这时CH相当于非压缩的BCD码*16。 SHR CH,1 ;算术右移1位,相当于非压缩的BCD 码乘8。 MOV BH,CH ;移入BH暂时保存。 SHR CH,1 ;再算术右移1位。 SHR CH,1 ;再算术右移1位,相当于非压缩的 BCD码乘2 ADD CH,BH ;相当于非压缩的BCD码乘10。 ADD CL,CH ;加到CL上。 MOV CH,0 ;ECX保存两位BCD码转化成二进制数的结果。 ADD EAX,ECX ;加到EAX上去。 POP EBX POP ECX POPF RET DECIBIN ENDP CODE ENDS END START
气泡排序算法 DSEG SEGMENT ADDR DW N DUP(?) DSEG ENDS CSEG SEGMENT MAIN PROC FAR START:PUSH DS SUB AX,AX PUSH AX MOV AX,DSEG MOV DS,AX MOV CX,N ;内循环变量存于CX中,初值为N-l。 DEC CX LOOP1:MOV DI,CX ;外循环变量存于DI中,初值为N-l。 MOV BX,0 ;地址指针预置为0。 LOOP2:MOV AX,ADDR[BX] ;取相邻两数比较。ADDR[BX] CMP AX,ADDR[BX+2] JGE COTINUM ;若符合排列次序,转移。 XCHG AX,ADDR[BX+2] ;若不符合排列次序,二数交换。 MOV ADDR[BX],AX ;存大数。 COTINUE: ADD BX,2 ;修改地址指针。 LOOP LOOP2 ;若一遍未比较完,继续。 MOV CX,DI LOOP LOOP1 ;若N-l遍未作完,继续。 RET MAIN ENDP CSEG ENDS END START