汇编语言程序设计笔记.md

汇编语言程序设计笔记.md

tk_sky 128 2023-02-14

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