8086/8088处理器、masm汇编语言程序设计
1. 程序结构
程序的段结构:使用伪指令来使用逻辑段,使用段寄存器CS,DS,ES,SS来访问
段名 SEGMENT [定位类型] [组合类型] [‘类别名']
…...
... ... 本段语句序列
… ...
段名 ENDS
分段结构的源程序框架:
STACK1 SEGMENT PARA STACK 'STACK0'
.....
STACK1 ENDS
DATA1 SEGMENT PARA 'DATA'
......
DATA1 ENDS
STACK2 SEGMENT PARA 'STACK0'
......
STACK2 ENDS
CODE SEGMENT PARA MEMORY
ASSUME CS:CODE,DS:DATA1,SS:STACK1
MAIN: ......
......
CODE ENDS
DATA2 SEGMENT BYTE 'DATA'
......
DATA2 ENDS
END MAIN ;注释
说明
- PAGE、PARA、WORD、BYTE表示定位类型,决定段的起始数据边界(第一个存放数据的位置)
- 单引号内的内容为类别名,可以不定义
- ASSUME:设置段与段寄存器的关联
- MAIN:对应的END MAIN 要放在最后
注意,要使用某段定义的数据,必须设置DS为该段的段基址:
MOV AX, DATA
MOV DS, AX ;往DS装上DATA的段基址
DS不直接接受常数,所以要用AX转发
过程:子程序。定义格式:
过程名 PROC [NEAR/FAR]
......
RET
......
过程名 ENDP
2. 指令和数据
指令:每一条都直接对应一个可执行语句
伪指令:由汇编器(这里是masm)处理,之后再由汇编器生成其他指令
常数:
- 二进制 B结尾
- 八进制 O或Q
- 十进制 D或无
- 十六进制 H
变量:
- 定义:
变量名 数据定义伪指令 表达式,表达式..
- 数据定义伪指令:DB:字节变量 DB:字变量 DD:双字(四字节)DQ:八字节 DT:10字节
- 一字节=8位
- 表达式:用来赋初值
- 例:DATA1 DB 32, 30H(定义DATA1内容为32,DATA1+1内容为30H);DATA2 DB ?(分配一个字节存储单元,内容为任意)
- 字符串:用单引号括起来,例:STRING1 DB ‘ABCDEF’ (一个字母在一个单元,String1指向A)
- 可用DUP表达式重复定义,例:DATAA DN 10H DUP(?) 表示分配10H个字节单元空间
标号:用于跳转。但是标号不阻碍正常的顺序执行。
例:
MOV CX, 100
LAB:MOV AX, BX
..
LOOP LAB
JNE NEXT
NEXT: ...
3. 寄存器
通用寄存器
AX、BX、EX、DX 为通用(数据)寄存器,8086的所有寄存器都是16位(两个字节)的。
每个通用寄存器可以拆成两个8位(一个字节)寄存器:
AX = AH+AL BX = BH+HL … 其中AH是高位,AL是低位。
AX一般作累加器,BX做基址寄存器,CX作计数器(也用于LOOP判断是否循环),DX存放数据。
变址寄存器:SI(源变址寄存器)、DI(目的变址寄存器)用于变址的时候,且只能用这两个寄存器进行变址寻址,如ARRAY[SI]。
还有地址指针寄存器SP(栈顶指针)和BP(基址指针),一般指向堆栈段
段寄存器
CS:代码段 DS:数据段 ES:附加段 SS:堆栈段
都存放相应段的段基地址。段基地址(*16)+偏移地址=实际地址
状态寄存器
FLAGS存放全部标志位:
- CF:进位标志位 有进位借位为1
- PF:奇偶标志位 用于奇偶验证,1的个数为偶数则为1
- ZF:零标志位,运算结果为0时为1
- SF:符号标志位,运算结果最高位为1时SF=1
- OF:溢出标志位,运算有溢出时为1
- AF:辅助进位标志
4. 算术运算指令
ADD
- ADD dest, src
- dest <- dest+src
- 不允许mem -> mem
INC
- INC OPRD
- oprd++
- 不允许操作段寄存器
SUB
- SUB dest, src
- dest<- dest-src
- 不允许mem->mem
NEG
- NEG OPRD
- 求补
MUL
- MUL OPRD
- AL * OPRD -> AX
- 无符号数
IMUL
- 同MUL,操作有符号数
DIV
- DIV OPRD
- AX/OPRD -> AH存放余数,AL存放商
- 无符号数
IDIV
- 同DIV,操作有符号数
算数运算符
+ - * / MOD SHL SHR []
如:
MOV AX, NUM / 2
MOV AX, 15*2
MOV AX, NUM MOD 3
MOV AX, NUM SHL 1 ;左移1位
其中SHL SHR也可以作单独指令使用;[]同+
5. 分支控制指令
无条件转移
JMP label
条件转移
判断单个标志位:
助记符 | 说明 | 标志位/寄存器 | 助记符 | 说明 | 标志位/寄存器 |
---|---|---|---|---|---|
JZ | 为零跳转 | ZF=1 | JNO | 无溢出跳转 | OF=0 |
JNZ | 非零跳转 | ZF=0 | JS | 有符号跳转 | SF=1 |
JC | 进位跳转 | CF=1 | JNS | 无符号跳转 | SF=0 |
JNC | 无进位跳转 | CF=0 | JP | 偶校验跳转 | PF=1 |
JO | 溢出跳转 | OF=1 | JNP | 奇校验跳转 | PF=0 |
比较无符号数(左边__右边):
- 大于:JA / JNBE
- 大于等于:JAE / JNB
- 小于:JB / JNAE
- 小于等于:JBE / JNA
比较有符号数(左边__右边):
- 大于: JG / JNLE
- 大于等于:JGE / JNL
- 小于:JL / JNGE
- 小于等于:JLE / JNG
测试指令
不一定要测试指令才能给J**条件转移指令进行测试,影响相应标志位的指令如SUB等也可以
CMP
- CMP DEST, SRC
- 与SUB效果相似,但不返回值给dest,只影响标志位
TEST
- TEST DEST, SRC
- 与AND(与)效果相似,但不返回结果给dest,只影响标志位
- 常用于
TEST X 1
判别X是否为0
分支跳转例子
CMP AX, 0
JG NEXT
NEXT: ;如AX>0则跳转
...
6. 循环控制
LOOP
汇编提供LOOP指令实现循环:LOOP label
作用是将CX
设为循环计数器,CX
的内容为循环次数,每次执行到LOOP时CX会减1,然后跳转到label位置。
例子:
MOV CX, 10
s:
dosth..
LOOP s ;循环10次
...
当然,也可以用条件控制相关指令手动实现循环:
MOV CX, 10
s:
dosth..
DEC CX
JNZ s ;循环10次
...
7. DOS功能调用
通过使用软中断指令,程序可以调用DOS操作系统提供的功能:
INT 21H
软中断之前需要将调用的功能号传入AH,并传入有关的参数到指定的寄存器。
#1 输入单个字符
功能:读取键盘输入的一个字符,存入AL
MOV AH, 1
INT 21H
#2 输出单个字符
功能:向显示输出一个字符,字符取自DL
MOV AH, 2
MOV DL, 'a'
INT 21H
#0AH 字符串输入
功能:从键盘输入一个字符串,直到输入回车键。
需要先简建立一个缓冲区:
- 第一字节:可输入的最大字符数+1
- 第二字节:由操作系统填入,实际输入的字符数
- 第三子节开始:存放输入字符的ASCII
然后把字符串首址的段基址和偏移量分别送到DS和DX里。
注意回车键也会被送入缓冲区,但是不会计入实际字符数
例子:
CHAR_BUF DB 31H ;最大长度
DB 0 ;实际字符数
DB 0FFH DUP(0); 输入缓存区
...
MOV DX,SEG CHAR_BUF
MOV DS, DX ;让DS指向CHAR_BUF所在数据段
MOV DX, OFFSET CHAR_BUF ;DX存放偏移地址
MOV AH, 0AH
INT 21H
#9 字符串输出
功能:输出一个字符串到屏幕。
同样需要一个缓冲区,为字符串+一个’$'作为结束标志。
然后把字符串首址的段基址和偏移量分别送到DS和DX里。
例子:
CHAR DB ‘This is a test.’,0AH,0DH,’$’
..........
MOV DX,OFFSET CHAR
MOV AH,9
INT 21H
其中0AH和0DH表示换行
8. 其他重点内容
堆栈段
声明例子:
STACK1 SEGMENT PARA STACK
DW 20H DUP(0)
STACK1 ENDS
声明了含有20个双字节空间的堆栈段。
在ASSUME时同时绑定SS:ASSMUE SS:STACK1
然后就可以在代码段使用堆栈了,一般用于调用子程序前先保存寄存器内容,以免被子程序覆盖:
PUSH DI
CALL ...
POP DI
栈的数据单元大小必须为两个字节(16bit);
对于每个程序/子程序,其结束运行时栈都必须清空,否则会报错。
子程序
子程序一般在代码段定义,关键字PROC
,一般通过寄存器/堆栈/地址表来和主程序交换数据。
在主程序通过指令CALL
来调用,然后通过指令RET
来结束子程序返回到主程序中。
例子:
COSEG SEGMENT
...
START:
...
PROCESSA PROC
... ;子程序内容
RET
PROCESSA ENDP
COSEG ENDS
END START
可以使用RET n 来进行n次POP操作清空该子程序的栈。
9. 简单例程
计算Z=(5X+2Y-7)/2:
DATA SEGMENT
VARX DB 5 ;VARX,VARY赋值
VARY DB 4
VARZ DB ? ;不分配常量,用于存放结果
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV AL, 5
MUL VARX
MOV VARX, AX
MOV AL, 2
MUL VARY
ADD AX, VARY
SUB AX, 7
MOV VARZ, AX
SHR VARZ, 1
CODE ENDS
END START
数组统计后输出:
统计定义好的20个数中<0,>=0且<=5、>5的数的个数,输出到屏幕上:
DATA SEGMENT
RES1 DB 0
RES2 DB 0
RES3 DB 0 ;定义字节单元
BUF DB -1, 20, 3, 30, -5, 15, 100, -54, 0, 4, 78, 99, -12, 32, 3, 23, -7, 24, 60,-51
; 要处理的数据
OUTPUT DB ' ',' ',' ',' ',' ',' ',' ','$' ;定义输出字符串
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV CX, 20
MOV AX, DATA
MOV DS, AX ;往DS装上DATA的段基址
l:
MOV SI, CX
MOV AH, BUF[SI-1] ;读取一个数
CMP AH, 0
JL a ;<0的情况
CMP AH, 5
JG b ;>5的情况
JMP c ;其他情况
a:
INC RES1
LOOP l
JMP final
b:
INC RES3
LOOP l
JMP final
c:
INC RES2
LOOP l
JMP final
final:
MOV AH, RES1
ADD AH, 30H ; 输出的是ASCII,要+30H
MOV OUTPUT[1], AH ;存进输出字符串中
MOV AH, RES2
ADD AH, 30H
MOV OUTPUT[3], AH
MOV AH, RES3
ADD AH, 30H
CMP AH, 9+30H ;如果超过9要截取,在前一位加上'1'
JG d
JNG e
d:
SUB AH, 10
MOV OUTPUT[5], 30H+1
e:
MOV OUTPUT[6], AH
MOV DX, OFFSET OUTPUT ;输出到屏幕
MOV AH, 9
INT 21H
CODE ENDS
END START
计算输入的两个十进制整数之和,以十六进制输出:
设计两个子程序,D2B:输入的十进制字符串转二进制真值;
B2H:将二进制真值转成十六进制表示的字符串
DATA SEGMENT
OUTPUT DB ' ',' ','$' ;定义输出字符串
DATA ENDS
STACK1 SEGMENT PARA STACK
DW 20H DUP(0)
STACK1 ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK1
START:
MOV AH, 1 ;读第一个数
INT 21H
MOV BH,AL
MOV AH, 1
INT 21H
MOV BL, AL
MOV AX, BX
CALL D2B ;转换
MOV DL, AL
MOV AH, 1 ;读空格
INT 21H
MOV AH, 1 ;读第二个数
INT 21H
MOV BH,AL
MOV AH, 1
INT 21H
MOV BL, AL
MOV AX, BX
CALL D2B ;转换
ADD AL, DL ;两数相加
CALL B2H ;转十六进制字符
MOV DL,AH
MOV DH,AL
MOV AH, 2 ;输出到屏幕
INT 21H
MOV DL, DH
INT 21H
D2B PROC ; 从AX获取两个字符,然后返回二进制真实值到栈,值在AL
; BH存低位,AL存高位
MOV BH, AL
MOV AL, AH
SUB BH, 30H
SUB AL, 30H
MOV BL, 10
MUL BL ;AL*10->AX
ADD AL, BH ; 加上低位
RET
D2B ENDP
B2H PROC ; 二进制转十六进制字符,从AL读,输出到AX
MOV AH, 0
MOV BH, 16
DIV BH
MOV BH,AL
MOV AL,AH
MOV AH,BH
CMP AL, 9 ; 如果>9 要换成字母样式,即+7到A以上
JNG a
ADD AL, 7
a: CMP AH, 9
JNG b
ADD AH, 7
b: ADD AL,30H
ADD AH,30H
RET
B2H ENDP
CODE ENDS
END START