文章目录
SHU汇编程序设计常见考点、易错点总结与综合实例、汇编学习资源
今天刚刚考完汇编,决定将这个学期记录的一些要点进行总结与归纳,希望给需要的朋友一些帮助。
读完后,自己再加以练习,考试肯定不会挂(😀)
文章内容主要包含以下几部分:
- 常见考点
- 常见易错点
- 综合实例
- 友情资源放送
一、常见考点
1、寻址方式
8086cpu寻址方式主要有7种,分别为:
1、立即寻址:MOV AL,5
2、直接寻址:MOV AX,[2000H]
3、寄存器寻址:MOV AX,BX
4、寄存器间接寻址:MOV AX,[BX]
5、寄存器相对寻址:MOV AX,[BX+2000H]
6、基址变址寻址:MOV AX,[BX+SI]
7、相对基址变址寻址:MOV AX,[BX+SI+2000H] ;使用基址寄存器与变址寄存器
几者的主要区别在于使用的寄存器与立即数。
2、移位
(1)移位重点要掌握算数右移与逻辑右移的区别:算数右移时,最高有效位不变。(可以将其理解成,操作有符号数,算数右移时,其符号位仍不变,只是数据大小改变。)
(2)逻辑左移与算数左移操作完全相同,最高位移入CF标志位,最低位补0。
(3)值得注意的是,在考试的时候要注意题目给的数据格式,例如题目给的是:
AX = 4CH,CL=4,问其SAL CL之后,AX等于多少?
这时你要注意到题目给的AX是16位,为2个字节,而其左边一个字节的内容没有写出来(都为0),所以做题的时候需要把左边的8个0补全之后进行移位操作,这样不会出错。
(4)逻辑移位针对的是无符号数,而算数移位针对的是有符号数。
3、乘除运算
乘除运算需要注意的地方有三个:
(1)两数相乘时,两者的位数需要相同,例如AL只能和BL相乘,而不能和BX相乘。两个8位相乘,结果保存在AX中;两个16位相乘,结果高位保存在DX中,低位保存在AX中。
(2)两数相除时,被除数的位数为除数的两倍。即除以一个8位字节数据时,被除数需要是16位。此时余数保存在AH中,商保存在AL中。被除数为32位时,余数保存在DX中,商保存在AX中。
(3)MUL和DIV针对无符号数,IMUL和IDIV针对有符号数。
4、加减操作
加减操作需要注意的地方在于进行两个数据的加减时,注意进/借位。
比如两个低字节相加,可能产生进位,这时需要高字节需要加上可能的进位:
ADD AL,BL
ADC AX,0 ;ADC表示加0之后再加CF标志位,也即只加CF标志位,即可能产生的进位。
5、取址操作
取址操作常与LOOP循环等结合用于编程题中一个字符串或者数组的遍历。
可以使用两种方式:
假设数据段已经定义了字符串STRING
1、MOV AX,OFFSET STRING
2、LEA AX,STRING
两者等价。但是OFFSET只能与简单的符号地址相连,而不能和诸如STRING[SI]等复杂操作数相连。
6、出入栈
栈在一些初学汇编语言时用的不多。作者用到栈这一数据结构主要是在考试大题里,用来保护现场(后面会讲到)。
这块需要注意的主要是出入栈时,SP指针的变化。
1、PUSH入栈:SP = SP - 2
2、POP出栈: SP = SP + 2
注:PUSH,POP都不改变标志位。
7、中断
中断这部分主要需要掌握:
(1)中断向量的定义(笔者考试没答出来):
在实模式下,将中断服务程序的入口地址称为中断向量。
(2)中断向量表的定义、位置,大小,以及如何找到中断向量所在位置。
1、存放256个中断向量的内存区域称为中断向量表。
2、中断向量表位于内存地址00000H ~ 003FFH,共1KB(256*4B,256个中断向量,每个占4字节)。
3、对于中断号n,中断向量所在地址为4n ~ 4n+3。例如,0号中断的向量位于地址00000H,1号中断的向量位于地址00004H,等等。
(3)设置与获取中断向量(一般在选择题)
1、设置中断向量:AH=25H(INT 21H)
2、获取中断向量:AH=35H(INT 21H)
8、标志位
标志位一般是通过执行指令来改变的,需要在平时使用常见指令,如MOV,ADD等这些操作时,记住他们对标志位的影响。由于指令的数量比较多,也比较难记全,所以需要一些方法,比如将常见的同一类型的指令记在一起:
1、INC、DEC,JMP,LOOP,CALL,MOV,LEA,PUSH和POP对标志位无影响
9、常见的字符的ASCII码
这部分必须要掌握,因为编程题里一般都会涉及到字母、数字判断和统计等,如果不掌握常见字符的ASCII码,那么便很难下笔。
常用字符的ASCII码如下:
数字'0'~'9':30H~39H
字母'A'~'Z':41H~5AH
字母'a'~'z':61H~7AH
空格:20H
回车CR:0DH
换行LF:0AH
注意回车与换行的差别: CR用来控制光标回到当前行的最左端;LF用来移动光标到下一行,而所在列不变。
10、IO端口
8086的端口有64K个(2^16)个。寻址方式有两种:
直接寻址
:只用于寻址00H ~ FFH前256个端口,操作数i8表示端口号
间接寻址
:可用于寻址全部64K个端口,DX寄存器的值就是端口号
对大于FFH的端口只能采用间接寻址方式。
常用指令如下:
;输入输出一个字节,利用AL,若为字,则利用AX。
;例:向300H端口输入/出一个字节
mov al,bvar ;bvar是字节变量
mov dx,300h
in dx,al;输入
out dx,al;输出
;此处端口号大于256,只能采用间接寻址。
注:IN,OUT指令对标志位无影响。
11、输入输出
输入输出这部分在编程大题里属于必考部分(笔者今天最后一道24分大题就用到了)。
这部分重点需要掌握四个部分:
1、从键盘上输入一个字符
参数:
AH = 01H AL = 输入字符后,字符的ASCII 码值
MOV AH ,01H
INT 21H
2 、向显示器输出一个字符
参数:
AH = 02H, DL = '欲输出的字符ASCII码‘
示例:
MOV DL ,'C'
MOV AH , 02H
INT 21H
注:在题目里面,一般将寄存器内的值输送到DL中。
3 、 向显示器输出一个字符串
参数:
AH = 09H,DS = 欲输出字符串的段地址 DX = 欲输出字符串的偏移地址
MOV DX , OFFSET STRING ;或者LEA DX,STRING
MOV AH, 09H;
INT 21H
此处需特别注意,字符串必须以’$'字符结尾。
4、输入一段内容(利用缓冲区)
这部分主要包含两个部分:缓冲区与输入。
<1>缓冲区
8086中缓冲区的设置方法如下:
在数据段定义一个指定长度的内存区域。其示例格式如下:
BUFFER DB 128,?,128 dup(?)
其中,BUFFER为缓冲区的名字,DB为缓冲区中数据的数据类型,第一个128表示缓冲区的指定长度(可调),问号表示缓冲区实际输入内容的长度,第二个128 dup表示在内存中开拓128字节的空间,与第一个128对应。
这里的一个技巧在于,在大题里,定义缓冲区后,可以将BUFFER[1],即缓冲区实际内容长度,送入到CX中,对缓冲区内容进行遍历。
注意:缓冲区实际内容从BUFFER[2]开始。
<2>输入一段内容
输入一段内容参数和前面输出字符串基本相同,不同的地方在于AH的值设定不同,此处为0AH
参数:
AH = 0AH,DS = 欲输出字符串的段地址 DX = 欲输出字符串的偏移地址
MOV DX , OFFSET STRING ;或者LEA DX,STRING
MOV AH, 09H;
INT 21H
二、常见易错点
1、汇编语言中,数据不能以字母开头。
例:
A000H应记为0A000H
2、8086CPU所有寄存器为16位,为了兼容之前8位cpu,一些寄存器可以拆分为两个8位(高8位H,低8位L)使用,例如:
AX寄存器可以拆分成AH和AL分开使用。但是一些寄存器不能拆分为两个8位来使用,例如:SI与DI。
3、若使用BP寄存器,则段地址默认在SS中。且寻址时BX、BP,SI、DI两两二选一搭配使用。
4、PUSH和POP指令操作数据类型为:字。
5、只修改IP为段内转移,同时修改CS和IP为段间转移。
三、综合实例
综合实例这部分我主要给出两个实例,一小一大,并提供一些注释,供大家参考。
1、实现双字同时左移四位
此处示例为:(DX,AX)双字逻辑左移4位
分析:这里主要需要注意AH高四位的移动。具体代码如下:
MOV CL,4 ;设置移位次数
SHL DX,CL
MOV BL,AH
SHL AX,CL ;AX左移4位
SHR BL,CL
OR DL,BL ;第3、5、6行的作用是将AH的高四位移动到DL的低4位
2、输入一段字符串,其以$字符结尾,统计其中数字、大写字母、小写字母、其他字符的个数,并输出至屏幕,要求使用子程序。
分析:这里我把题目写的很简洁,但是其中包含了许多的知识点(输入输出,字符计数,子程序等),属于综合题。
具体代码如下:
;默认采用ML6.11汇编程序
DATAS SEGMENT
string db 80,?,80 dup(?) ;设定缓冲区
up_letter db 0 ;大写字母计数
down_letter db 0 ;小写字母计数
digit db 0 ;数字计数
other dw 0 ;其他字符计数
input db 'input a line:$' ;输入提示
up_letter_count db 0dh,0ah,'UPletter count:$' ;此句及以下4句为输出提示
down_letter_count db 0dh,0ah,'downletter count:$'
digit_count db 0dh,0ah,'digit count:$'
other_count db 0dh,0ah,'other count:$'
CRLF db,0dh,0ah,'$'
DATAS ENDS
STACKS SEGMENT
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
MAIN PROC FAR
START:
MOV AX,DATAS
MOV DS,AX
LEA DX,INPUT ;在屏幕上显示输入提示
MOV AH,9
INT 21H
LEA DX,STRING ;输入字符串
MOV AH,0AH
INT 21H
MOV CL,STRING[1] ;设置循环次数,注意将CH置为0
SUB CH,CH
LEA DI,STRING[2] ;将指针指到缓冲区第一个字符,注意这里的2
CHECK:
MOV AL,[DI]
CMP AL,'$' ;判断是否遍历结束,若是,则跳转输出
JE EXIT
CMP AL,30H ;判断是否为数字
JB O
CMP AL,39H
JNA D
CMP AL,41H ;判断是否为大写字母
JB O
CMP AL,5AH
JNA UP
CMP AL,61H ;判断是否为小写字母
JB O
CMP AL,7AH
JNA DOWN
INC OTHER ;若上面判断都不符合,则为其他字符。
INC DI
LOOP CHECK
O: INC OTHER
INC DI
LOOP CHECK
D: INC DIGIT
INC DI
LOOP CHECK
UP: INC UP_LETTER
INC DI
LOOP CHECK
DOWN:INC DOWN_LETTER
INC DI
LOOP CHECK
EXIT:
LEA DX,UP_LETTER_COUNT ;输出大写字母计数提示
MOV AH,09H
INT 21H
XOR AX,AX ;将AX清空
MOV AL,UP_LETTER
CALL DISPLAY ;调用子程序,输出大写字母个数
LEA DX,DOWN_LETTER_COUNT
MOV AH,09H
INT 21H
XOR AX,AX
MOV AL,DOWN_LETTER
CALL DISPLAY ;输出小写字母个数
LEA DX,DIGIT_COUNT
MOV AH,09H
INT 21H
XOR AX,AX
MOV AL,DIGIT
CALL DISPLAY ;输出数字个数
LEA DX,OTHER_COUNT
MOV AH,09H
INT 21H
XOR AX,AX
MOV AL,OTHER
CALL DISPLAY ;输出其他字符个数
LEA DX,CRLF ;回车,换行
MOV AH,09H
INT 21H
MOV AH,4CH
INT 21H
MAIN ENDP
DISPLAY PROC NEAR:
MOV BL,10
div BL
PUSH AX;保留现场
MOV DL,Al
ADD DL,30H
MOV AH,2
INT 21H
POP AX;恢复现场
MOV DL,AH
ADD DL,30H
MOV AH,2
INT 21H
RET
DISPLAY ENDP
CODES ENDS
END START
此段代码比较长,有几个点需要注意:
1、子程序DISPLAY中用到了PUSH和POP指令,这里的主要作用是保护现场,即保护AX中的内容。如果不进行此操作,下一步MOV AH,2时,AH的值就被修改,从而无法正确的输出统计的数字。
2、将统计的数字输出,先除以10,得到商和余数,此处先输出AL中的内容(商),再输出AH中的内容(余数)。
3、注意遍历字符串时,从STRING[2]开始。
输出结果:
四、友情资源放送
此部分资源主要包括:
- 《汇编语言》第三版,王爽著,pdf版本
- 《清华大学IBM-PC汇编语言程序设计》第二版课后习题解答,沈美明著,pdf版本
- SHU汇编语言复习提纲
王爽老师的汇编语言书确实通俗易懂,初学者友好。
这些资源都放在了我的GITHUB中,需要的朋友可以自取。
GITHUB地址:FM-Airknight的学习资源
祝所有读到这篇文章的朋友们都能有所收获!