Category 活动攻略

作为汇编语言的课程笔记,方便之后的复习与查阅

本篇为课程第十和第十一次课内容

目录

汇编语句格式标识符硬指令、执行性语句伪指令、说明性语句

变量定义变量名初值表变量定义伪指令助记符定义字节单元伪指令DB定义字单元伪指令DW定义双字单元伪指令DD

PTR操作符

顺序程序设计求两数之和移位代码转换 XLAT

分支程序设计二分支三分支多分支

循环程序设计子程序设计子程序的常见格式子程序的参数传送无参数传递的子程序通过寄存器传递参数通过变量传递参数通过堆栈传递参数

嵌套子程序综合例题显示有符号十进制数

计算有符号数平均值

汇编语句格式

标识符

标识符(Identifier)一般最多由31个字母、数字及规定的特殊符号(如 _、$、?、@)组成,不能以数字开头。默认情况下,汇编程序不区别标识符中的字母大小写一个程序中,每个标识符的定义是唯一的,还不能是汇编语言采用的保留字。保留字(Reserved Word)是汇编程序已经利用的标识符,主要有: 硬指令助记符——例如:MOV、ADD 伪指令助记符——例如:DB、EQU 操作符——例如:OFFSET、PTR 寄存器名——例如:AX、CS

硬指令、执行性语句

硬指令:使CPU产生动作、并在程序执行时才处理的语句

执行性语句——由硬指令构成的语句,它通常对应一条机器指令,出现在程序的代码段中,格式:

标号: 硬指令助记符 操作数,操作数 ;注释

again: mov dx, offset string ;注释

标号:反映硬指令位置(逻辑地址)的标识符,后跟一个冒号分隔硬指令助记符:可以是任何一条处理器指令处理器指令的操作数可以是立即数、寄存器和存储单元

伪指令、说明性语句

伪指令(Directive)——不产生CPU动作、在程序执行前由汇编程序处理的说明性语句 伪指令与具体的处理器类型无关,但与汇编程序的版本有关

说明性语句——由伪指令构成的语句,它通常指示汇编程序如何汇编源程序:

名字 伪指令助记符 参数,参数,… ;注释

string db ‘Hello,world’ ;注释

名字:反映伪指令位置(逻辑地址)和属性的标识符,后跟空格或制表符分隔,没有冒号伪指令助记符:定义字节数据和字符串的DB就是伪指令伪指令的参数可以是常数、变量名、表达式等,可以有多个,参数之间用逗号分隔

变量定义

变量定义(Define)伪指令为变量申请固定长度的存储空间,并可同时将相应的存储单元初始化

变量定义伪指令要放在.exit后,end之前,因为它不是可执行的语句

变量名

变量名为用户自定义标识符,表示初值表首元素的逻辑地址;用这个符号表示地址,常称为符号地址。设置变量名是为了方便存取它指示的存储单元。变量名可以没有。这种情况,汇编程序将直接为初值表分配空间。

初值表

初值表是用逗号分隔的参数

主要由数值常数、表达式或?、DUP组成

?——表示初值不确定,即未赋初值

BUFFER DB ?

DUP——表示重复初值 DUP的格式为:重复次数 DUP(重复参数)

BUFFER DB 10 dup(0)

BUFFER DB 10 dup(?)

变量定义伪指令助记符

DB——定义字节伪指令 DW——定义字伪指令 DD——定义双字伪指令

DF——定义3字伪指令 DQ——定义4字伪指令 DT——定义10字节伪指令

重点掌握前三个

定义字节单元伪指令DB

DB伪指令用于分配一个或多个字节单元,并可以将它们初始化为指定值

初值表中每个数据一定是字节量(Byte),存放一个8位数据:

0~255的无符号数-128~+127带符号数字符串常数

X db 'a',-5

db 2 dup(100),? ;第二行没有带名字,默认由x往下继续进行内存分配

Y db 'ABC'

定义字单元伪指令DW

初值表中每个数据一定是字量(Word),一个字单元可用于存放任何16位数据:

一个段地址一个偏移地址两个字符0~65535之间的无符号数-32768~+32767之间的带符号数

count dw 8000h,?,'AB' ; A在高地址处

maxint equ 64h ; equ是等价的意思,表示之后maxint就是64h

number dw maxint

array dw maxint dup(0)

定义双字单元伪指令DD

初值表中每个数据是一个32位的双字量(Double Word):

有符号或无符号的32位整数用来表达16位段地址(高位字)和16位的偏移地址(低位字)的远指针

vardd DD 0,?,12345678h

farpoint DD 00400078h

PTR操作符

PTR操作符使名字或标号具有指定的类型

类型名可以是 BYTE/WORD/DWORD/FWORD/QWORD/TBYTE

mov byte ptr [2000H],10H

使用PTR操作符,可以临时改变名字或标号的类型

顺序程序设计

求两数之和

NUM1和NUM2单元中各存放着一个双字无符号数,编制程序计算两数之和,将结果存于SUM双字单元中,进位保存到FSUM字节单元中

分析:最多一次处理16位,双字需分开处理

.model tiny

.code

.startup

mov ax,word ptr num1 ;处理低16位

add ax,word ptr num2

mov word ptr sum,ax ;保存低16位

mov ax,word ptr num1+2 ;处理高16位

adc ax,word ptr num2+2

mov word ptr sum+2,ax ;保存高16位

mov dx,0 ;最高进位

rcl dl,1

mov fsum,dl ;保存进位

.exit 0

num1 dd 82348567H

num2 dd 87658321H

sum dd ?

fsum db ?

end

也可以用串操作

移位

ddvarw为一个32位的数据12ab56cdH,编程将其整个循环左移4位(使用顺序结构)得到 2ab56cd1H

.model tiny

.code

.startup

mov ax,word ptr ddvar ;低16位

mov dx,word ptr ddvar+2 ;高16位

mov cl , 4

mov bh , dh ;保存高8位到BH

mov bl , ah ;保存低8位到BL

shl dx , cl ;高16位左移 4位

shl ax , cl ;低16位左移 4位

shr bh , cl ;高8位右移4位,高4位到bh中

shr bl , cl ;低8位右移 4位,低4位到bl中

or dl , bl ;原低4位转到高4位

or al, bh ;原高4位转到低4位

.exit 0

ddvar dd 12ab56cdh

end

代码转换 XLAT

用查表法,实现一个8位二进制数(00-0FH)转换为ASCII码显示

.model tiny

.code

.startup

mov bx,offset ASCII ;BX指向ASCII码表

mov al,hex ;AL取得一位16进制数,

and al,0fh ;只有低4位是有效的,高4位清0

xlat ; 换码:AL←DS:[BX+AL]

mov dl,al ;入口参数:DL←AL

mov ah,2 ;02号DOS功能调用

int 21h ;显示一个ASCII码字符

.exit 0

ASCII db 30h,31h,32h,33h,34h,35h

db 36h,37h,38h,39h ;0~9的ASCII码

db 41h,42h,43h,44h,45h,46h ;A~F的ASCII码

hex db 0bh ;任意设定了一个待转换的1位16进制数

end

分支程序设计

判断的条件是各种指令,如CMP、TEST等执行后形成的状态标志转移指令Jcc和JMP可以实现分支控制

二分支

比较两个数大小:在DATA1和DATA2单元中各有一个16位二进制无符号数,找出其中较小的数,存于MIN单元中

.model tiny

.code

.startup

mov ax,data1

cmp ax,data2

jb next

mov ax,data2 ;CF=0

next:

mov min,ax ;存结果

.exit 0

data1 dw 2000h

data2 dw 3f80h

min dw ?

end

三分支

判断变量var中的值,若为正数,在result中存入1;为负数,在result中存入-1;为零,在result中存入0

.model tiny

.code

.startup

mov ax,var

cmp ax,0

jz zero ;等于0

jg great ;大于0

mov ax,0ffffh ;小于0

jmp ext

zero: mov ax,0

jmp next

great: mov ax,0001h

next: mov result,ax

.exit 0

var dw 8001h

result dw 0

end

多分支

(了解即可)

可用跳跃表法,使程序根据不同的条件转移到多个程序分支中去执行

需要在数据段事先安排一个按顺序排列的转移地址表。因为转移地址为16位偏移地址,所以表地址偏移量需要加2(type=2)调整。可用JMP指令的变址寻址,或寄存器间接寻址,或基址变址寻址方式来实现跳跃表法的程序

Table dw disp1,disp2,disp3,disp4,disp5,disp6,disp7,disp8

例:根据键盘输入的1-8数字跳转到8个不同的处理程序段

.model tiny

.code

.startup

start1:

mov dx,offset msg ;9号功能显示字符串

mov ah,9

int 21h

mov ah,1 ;1号功能获取用户输入

int 21h

cmp al,'1' ;输入不是1~8,则跳转回去

jb start1

cmp al,'8'

ja start1

and ax,000fh ;设置地址偏移

dec ax

shl ax,1 ;因为是双字,所以地址偏移需要用shl来乘2

mov bx,ax

jmp table[bx]

start2:

mov ah,9

int 21h

.exit 0

disp1:

mov dx,offset msg1

jmp start2

disp2:

mov dx,offset msg2

jmp start2

……

disp7:

mov dx,offset msg7

jmp start2

disp8:

mov dx,offset msg8

jmp start2

Msg db 'Input Number(1~8):',0dh,0ah,'$'

Msg1 db 'Chapter 1!',0dh,0ah,'$'

Msg2 db 'Chapter 2!',0dh,0ah,'$'

Msg3 db 'Chapter 3!',0dh,0ah,'$'

Msg4 db 'Chapter 4!',0dh,0ah,'$'

Msg5 db 'Chapter 5!',0dh,0ah,'$'

Msg6 db 'Chapter6!',0dh,0ah,'$'

Msg7 db 'Chapter 7!',0dh,0ah,'$'

Msg8 db 'Chapter 8!',0dh,0ah,'$'

Table dw disp1,disp2,disp3,disp4,

dw disp5,disp6,disp7,disp8

end

循环程序设计

循环指令和转移指令可以实现循环控制

例1: 求1+2+3+…+100的和,存在sum字单元

.model tiny

.code

.startup

xor ax,ax ;被加数AX清0

mov cx,100

again:

add ax,cx ;从100,99,...,2,1倒序累加

loop again

mov sum,ax ;将累加和送入指定单元

.exit 0

sum dw ?

end

例2:把BX中的数以十六进制的形式显示在屏幕上。比如BX=8F6AH,屏幕显示 8F6AH 这个例子要重点理解

要把bx中数据转成ascii码用循环移位,先显示高4位的16进制数,接着继续移位处理其他位

mov ch, 4 ;循环4次

rotate:

mov cl, 4 ;每次移位4位

rol bx, cl

mov al, bl ;只取bx低4位

and al, 0fh

add al, 30h ;’0’-’9’ ASCII 30H-39H

cmp al, 3ah

jl printit ;有符号数的比较

add al, 7h ;’A’-’F’ ASCII 41H-46H

printit:

mov dl, al

mov ah, 2

int 21h

dec ch

jnz rotate

例3:在数据段中从buffer单元开始存放10个16位二进制有符号数,把其中最大数找出来存于MAX单元中

.model tiny

.code

.startup

mov bx,offset buffer

mov cx,10

mov ax,[bx]

circle:

inc bx ;16位,地址一次要加2

inc bx

cmp ax,[bx]

jge next

mov ax,[bx]

next:

loop circle

mov max,ax

.exit 0

buffer dw -100,3000,-1,22,8000h

dw 9232,0,-3632,-3144,6322

max dw ?

end

例4:字符串比较——比较内存中的两个字符串string1和string2,字符串长度都为COUNT个。若完全相同,则将RESULT单元清0;不同则将RESULT单元送ffh

.model tiny

.code

.startup

mov si, offset string1

mov di, offset string2

mov cx, count

cld

repz cmpsb ;重复比较两个字符

jnz unmat ;zf=0,字符串不等,转移

mov al,0 ;zf=1,字符串相等,设置00h

jmp output ;转向output

unmat: mov al,0ffh ;设置ffh

output: mov result,al ;输出结果标记

.exit 0

string1 db ‘Hello,Everybody!’

string2 db ‘Hello,everybody!’

count equ $-string2 ;$代表count的地址,因此count就为string2字符串的个数

end

有不同字符时,SI,DI中分别存放第一个不相同字符的下一个地址;CX中存放剩下还未比较的字符个数

例5:字符串搜索——从一个字符串中查找一个指定的字符T。若找到,则将该字符存入char单元,并记录该字符所在位置的偏移地址,存于caddr中;若未找到,则将char置为0ffh(全1)。

.model tiny

.code

.startup

lea di, mess ;lea 获取一个变量的有效地址 等价于mov di, offset mess

mov al, ‘T’

mov cx, count

cld

repne scasb

jnz over ;zf=0,未找到

;zf=1,找到

mov char, al ;存字符值

dec di

mov caddr,di ;存字符地址

over: .exit

mess db ‘COMPUTER’

count equ $-mess

char db 0ffh

caddr dw ?

end

例6:将首地址为array的字节数组从小到大排序——冒泡法

mov cx,count ;CX←数组元素个数

dec cx ;元素个数减1为外循环次数

outlp: mov dx, cx ;DX←内循环次数

mov bx, offset array

inlp: mov al,[bx] ;取前一个元素

cmp al,[bx+1] ;与后一个元素比较

jna next ;前一个不大于后一个元素,则不进行交换

xchg al,[bx+1] ;否则,进行交换

mov [bx],al

next: inc bx ;下一对元素

dec dx

jnz inlp ;内循环尾

loop outlp ;外循环尾

子程序设计

子程序的常见格式

subname proc ;具有缺省属性的subname过程

push ax ;保护寄存器:顺序压入堆栈

push bx ;ax/bx/cx仅是示例

push cx

… ;过程体

pop cx ;恢复寄存器:逆序弹出堆栈

pop bx

pop ax

ret ;过程返回

subname endp ;过程结束

子程序的参数传送

入口参数(输入参数):主程序提供给子程序出口参数(输出参数):子程序返回给主程序参数的形式: ① 数据本身(传值) ② 数据的地址(传址)传递的方法: ① 寄存器 ② 变量 ③ 堆栈

无参数传递的子程序

;子程序功能:实现光标回车换行

dpcrlf proc ;过程开始

push ax ;保护寄存器AX和DX

push dx

mov dl,0dh ;显示回车

mov ah,2

int 21h

mov dl,0ah ;显示换行

mov ah,2

int 21h

pop dx ;恢复寄存器DX和AX

pop ax

ret ;子程序返回

dpcrlf endp ;过程结束

通过寄存器传递参数

把参数存于约定的寄存器中,可以传值,也可以传址。子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护)子程序对带有入口参数的寄存器可以保护,也可以不保护(视具体情况而定)

例1:将AL中存放的低4位二进制数转换为相应的ASCII码子程序。 入口参数: (AL) 存放二进制数; 出口参数: (AL)存放ASCII码。

htoasc proc

and al,0fh

add al,30h

cmp al,3ah

jl htoasc;0~9

add al,7;A~F

htoasc1: ret

htoasc endp

例2:计算字符串长度子程序。字符串是以ASCII码值0(NULL)为结束标志的一组ASCII码字符序列 入口参数:si为字符串起始地址 出口参数:ax为字符串的长度

strlen proc

mov di,si

mov ax,ds

mov es,ax ;es:di指向字符串

xor al,al ;(al)清零

mov cx,0ffffh ;(cx)置初值 cx赋为ffffh=-1表示一直循环

cld

repnz scasb ;查找结束符

mov ax,cx ;cx最后为-cnt-1,cnt为循环次数。取反相当于1111 1111 – cx = -1 + cnt + 1 = cnt

not ax ;含结束符长度

dec ax ;去掉结束符

ret

strlen endp

通过变量传递参数

主程序与子程序公用一组变量,实现参数的传递。不同模块间共享时,需要声明。这种结构独立性差

通过堆栈传递参数

主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数子程序将出口参数压入堆栈,主程序弹出堆栈取得它们

要注意堆栈的分配情况,保证参数存取正确、子程序正确返回,并保持堆栈平衡

主程序实现平衡堆栈: add sp,n子程序实现平衡堆栈: ret n

例:求字数组array中所有元素的累加和(设结果无溢出),结果存于sum中

;主程序

.model tiny

.code

.startup

mov ax,offset array ;数组首地址

push ax

mov ax , count ;数组长度

push ax

mov ax,offset sum ;sum地址

push ax

call sumc ;调用子程序

.exit 0

array dw 100 dup(20)

count equ ($-array)/2

sum dw ?

end

;子程序

sumc proc

push bp

mov bp,sp ;利用BP间接寻址存取参数,SP为堆栈的栈顶指针

push ax

push cx

push si

push di

mov si,[bp+8] ;SS:[BP+8]指向array偏移地址

mov cx,[bp+6] ;SS:[BP+6]指向元素个数

mov di,[bp+4] ;SS:[BP+4]指向sum偏移地址

xor ax,ax

sumc1: add ax,[si]

add si,2

loop sumc1

mov [di],ax ;存放结果

pop di

pop si

pop cx

pop ax

pop bp

ret 6 ;表示将堆栈指针加6,之前主程序调用时入栈了3个字,现在加6使堆栈恢复原来的状态

sumc endp

end

嵌套子程序

;实现AL内容的显示

ALdisp proc

push ax

push cx

push ax ;暂存ax

mov cl,4

shr al,cl ;转换、显示al的高4位

call htoasc ;转换子程序调用(嵌套)

call dischar ;显示子程序调用(嵌套)

pop ax ;转换、显示al的低4位

call htoasc ;转换子程序调用(嵌套)

call dischar ;显示子程序调用(嵌套)

pop cx

pop ax

ret

ALdisp endp

;实现AX内容的显示

AXdisp proc

push ax ;保护ax

push ax ;暂存ax

mov al,ah

call ALdisp ;显示ah,子程序调用(嵌套)

pop ax ;恢复AX

call ALdisp ;显示al,子程序调用(嵌套)

pop ax ;恢复AX

ret

AXdisp endp

;将AL低4位表达的一位16进制数转换为ASCII码

htoasc proc

and al,0fh

cmp al,9

jbe htoasc1

add al,37h

ret

htoasc1: add al,30h

ret

htoasc endp

;将AL中的ASCII码表示的字符在屏幕上显示输出

dischar proc

push ax

push dx

mov dl,al ;显示

mov ah,2

int 21h

pop dx

pop ax

ret ;子程序返回

dischar endp

综合例题

显示有符号十进制数

子程序在屏幕上显示一个有符号十进制数子程序还包含将二进制数转换为ASCII码的过程显示时,负数用“-”引导,正数直接输出、没有前导字符子程序的入口参数用共享变量传递,主程序调用该子程序显示10个数据

程序处理过程:

首先判断数据是零、正数或负数,是零显示“0”退出是负数,显示“-”,求数据的绝对值;接着数据除以10,余数加30H转换为ASCII码压入堆栈重复上一步,直到余数为0结束依次从堆栈弹出各位数字,进行显示

本例采用16位寄存器表达数据,所以只能显示+327677~-32768间的数值

.code

.startup

mov cx,count

mov bx,offset array

again: mov ax,[bx]

mov wtemp,ax ;将入口参数存入共享变量

call write ;调用子程序显示一个数据

inc bx

inc bx

call dpcrlf ;便于显示下一个数据

loop again

.exit 0

count = 10

array dw 1234,-1234,0,1,-1,32767

dw -32768,5678,-5678,9000

wtemp dw ? ;共享变量

end

;显示有符号10进制数的通用子程序

;入口参数:共享变量wtemp

write proc

push ax

push bx

push dx

mov ax,wtemp ;取出显示数据

test ax,ax ;判断零、正数或负数

jnz write1

mov dl,'0' ;是零,显示“0”后退出

mov ah,2

int 21h

jmp write5

write1: jns write2 ;是负数,显示“-”

mov bx,ax ;AX数据暂存于BX

mov dl,'-'

mov ah,2

int 21h

mov ax,bx

neg ax ;数据求补(求绝对值)

write2: mov bx,10 ;10压入堆栈,作为退出标志

push bx

write3: cmp ax,0 ;数据(余数)为零

jz write4 ;转向显示

sub dx,dx ;扩展被除数DX.AX

div bx ;数据除以10:DX.AX÷10

add dl,30h ;余数(0~9)转换为ASCII码

push dx ;数据各位先低位后高位压入堆栈

jmp write3

write4: pop dx ;数据各位先高位后低位弹出堆栈

cmp dl,10 ;是结束标志10,则退出

je write5

mov ah,2 ;进行显示

int 21h

jmp write4

write5: pop dx

pop bx

pop ax

ret ;子程序返回

write endp

end

计算有符号数平均值

子程序将16位有符号二进制数求和,然后除以数据个数得到平均值子程序的入口参数利用堆栈传递,主程序需要压入数据个数和数据缓冲区的偏移地址。子程序通过BP寄存器从堆栈段相应位置取出参数子程序的出口参数用寄存器AX传递主程序提供10个数据,并保存平均值

.code

.startup

mov ax,count

push ax ;压入数据个数

mov ax,offset array

push ax ;压入缓冲区偏移地址

call mean ;调用子程序求平均值

add sp,4 ;平衡堆栈

mov wmed,ax ;保存平均值(不含余数)

.exit 0

count = 10

array dw 1234,-1234,0,1,-1,32767

dw -32768,5678,-5678,9000

wmed dw ? ;存放平均值

;计算16位有符号数平均值子程序

;入口参数:顺序压入数据个数和缓冲区偏移地址

;出口参数:AX=平均值

mean proc

push bp

mov bp,sp

push bx ;保护寄存器

push cx

push dx

push si

push di

mov bx,[bp+4] ;从堆栈取出偏移地址

mov cx,[bp+6] ;从堆栈取数据个数

xor si,si ;SI保存求和的低16位值 用32位来存储求和结果,防止溢出

mov di,si ;DI保存求和的高16位值

mean1: mov ax,[bx] ;取出一个数据→AX

cwd ;AX符号扩展→DX

add si,ax ;求和低16位

adc di,dx ;求和高16位 注意是用adc指令

inc bx ;指向下一个数据

inc bx

loop mean1 ;循环

mov ax,si

mov dx,di ;累加和在DX.AX

mov cx,[bp+6] ;数据个数在CX

idiv cx ;有符号数除法,求的平均值在AX中、余数在DX中

pop di ;恢复寄存器

pop si

pop dx

pop cx

pop bx

pop bp

ret

mean endp

end

Copyright © 2088 炎龙游戏攻略网 - 活动副本全解析 All Rights Reserved.
友情链接
top