《汇编语言》王爽 实验
练习题目:
1.编程计算0123H、0456H,0abxH、0defH、0fesH、0cbaH、0987H这8个数据的和,结果存放在ax中。
assume cs:code
code segment
dw 0123h,0564h,0789H,0abcH,0defH,0fedH,0cbaH,0987H
;由于数据在代码段中,所以段地址是CS
;dw定义的数据在最开始的地方,所以偏移地址是0开始
start:
mov bx,0 ;存放偏移地址,最开始为0
mov ax,0 ;用于存放结果
mov cx,8
s:
add ax,cs:[bx]
add bx,2 ;读完一个数,偏移地址bx加2
loop s
mov ax,4c00h
int 21h
code ends
end start
2.利用栈编程将定义的数据逆序(联想栈的特性)存放:dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H。(有疑问 没看懂???)
assume cs:codesg
codesg segment
dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H;地址0~15
dw 0,0,0,0,0,0,0,0;定义8个字型空数据,后面当作栈来使用,地址是16~31
;由于数据在代码段中,所以段地址是CS
start:
mov ax,cs
mov ss,ax
mov sp,32;设置栈顶ss:sp指向cs:32,十进制的32
mov bx,0
mov cx,8
s:push cs:[bx];入栈
add bx,2
loop s; 以上代码段0~15个单元中的8个字型数据一次入栈
mov bx,0
mov cx,8
s0:pop cs:[bx];出栈
add bx,2
loop s0;依次出栈8个执行数据到代码段0~15单元中
mov ax,4c00h
int 21h
codesg ends
end start
在8086CPU中数据、栈和代码存储空间不能大于64KB。可以用像定义代码段一样的方法来定义多个段并在其中定义需要的数据,或者通过定义数据来取得栈空间。
改进后:
assume cs:codesg,ds:data,ss:stack;在源程序中为三个段进行有意义的名称
data segment
dw 0123H,0564H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
stack segment
dw 0,0,0,0,0,0,0,0 ;定义8个字型空数据,后面当作栈来使用
stack ends
code segment
start:
mov ax,stack
mov ss,ax
mov sp,16 ;设置栈底ss:sp指向stack:16
mov ax,data
mov ds,ax ;ds指向data段
mov bx,0 ;ds:bx指向data段中的第一个单元(ds:bx)
;为什么这里没有" mov cx,8 "
s:push cs:[bx]
add bx,2
loop s ;以上代码段0~16个单元中的8个字型数据依次入栈
mov bx,0
mov cx,8
s0:pop cs:[bx]
add bx,2
loop s0 ;依次出栈8个执行数据到代码段0~16单元中
mov ax,4c00h
int 21h
codesg ends
end start
3.大小写转换。
关键点: and al,11011111B ;小写转换成大写
or al,00100000B ;大写转换成小写
assume cs:codesg,ds:datasg
datasg segment
db'BaSiC' ;表示字符串
db'iNfOfMaTiOn'
datasg ends
codesg segment
start: mov ax,datasg
mov ds,ax ;设置ds执行datasg段
mov bx,0 ;设置(bx)=0,ds:bx指向'BaSiC'的第一个字母
mov cx,5 ;设置循环次数,因为BaSiC有5个字母
s:mov al,[bx+0] ;将ASCII码从ds:bx所指向的单元中取出
and al,11011111B ;口岸al中ASCII码的第5个位置变为0,变为大写字母
mov [bx],al ;转变后将ASCII码写回单元
mov [bx+5] ;定位第二个字符串的字符
or al,00100000B
mov [bx+5],al
inc bx
loop s
mov ax,4c00H
int 21H
codesg ends
end start
4.用DI和SI实现复制到它后面的数据区中。
一般地,ds:si指向要复制的原始空间,ds:di指向复制的目的空间。
;用数组的思维[bx(si或di)+idata]的方式优化程序
assume cs:codesg,ds:datasg
datasg segment
db'welcome to asm!'
db'................'
datasg ends
codesg segment
start :mov ax,datasg
mov ds,ax
mov si,0 ;ds:si指向第一个字符串
mov cx,8
s:mov ax,[si] ;第一个字符串的的第一个元素
mov [si+16],ax ;目标字符串的第二个元素
add si,2
loop s
mov ax,4c00h
int 21H
codesg ends
end start
用原始的方法可以这样写主程序:
start:mov ax,datasg
mov ds,ax
mov si,0
mov di,16 ;ds:si指向要复制的原始空间(第一个字符串),ds:di指向复制的目的空间(第二个字符串)
mov cx,8
s:mov ax,[si]
mov [di],ax
add si,2
add di,2
loop s
5.编程将数据段中每一个单词的头一个字母改为大写字母。
如图所示:
assume cs:codesg,ds:datasg
datasg segment
db'1. file ';长度刚好都是16个字节
db'2. edit '
db'3. search '
db'4. view '
db'5. options '
db'6. help '
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,6
s:
mov al,[bx+3]
and al,11011111B
mov [bx+3],al
add bx,16
loop s
mov ax,4c00h
int 21h
codesg ends
end start
6. 编程将数据段中每个单词改为大写字母
assume cs:codesg,ds:datasg
datasg segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0;用bx来定位行
mov cx,4
s0:
mov dx,cx;用dx寄存器来临时存放外层cx的值
mov si,0;用si来定位列
mov cx,3
s:
mov al,[bx+si]
and al,11011111B
mov [bx+si],al
inc si
loop s
add bx,16
mov cx,dx;在进行外层循环的时候回复cx的值
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
在上面的程序中,8086 CPU si、cx、ax、bx这些寄存器经常要使用到;cs、ip、ds也不能用,因为cs:ip时刻指向当前指令,ds指向datasg段;那么可用的寄存器就只用dx、di、es、ss、sp、bp等寄存器了。内存可以解决经常性的数据暂存问题。为了使程序结构清晰便于阅读,应该使用栈。
使用栈改进后的程序使用栈:
assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
datasg ends
stacksg segment
dw 0,0,0,0,0,0,0,0;定义一个段,用作栈段,容量为16个字节
stacksg ends
codesg segment
start:mov ax,stacksg
mov ss,ax
mov sp,16
mov ax,datasg
mov ds,ax
mov bx,0;用bx来定位行
mov cx,4
s0:push cx;datasg:40h单元存放外层cx的值
mov si,0;用si来定位列
mov cx,3
s:mov al,[bx+si]
and al,11011111B
mov [bx+si],al
inc si
loop s
add bx,16
pop cx;在进行外层循环的时候回复cx的值
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
7.利用除法指令计算 dd 100001H 除以 dw 100,商放在 dw 0中
data segment
dd 100001H
;低16位存储在ax中,高16位存储在dx中
;被除数100001大于2^16=65535(FFFF),不能用ax来存放,要用dx和ax两个寄存器联合存放。除数小于255,可用一个8位寄存器存放,但是被除数是32位的,除数应为16位,所以要用一个16位寄存器来存放除数。
;100001的十六进制为186A1H,100001的高16位(1)存放在dx,低16位(86AH)存放在ax中。
dw 100
dw 0
data ends
mov ax,data
mov ds,ax
mov ax,ds:[0];低16位存储在ax中
mov dx,ds:[2];高16位存储在dx中
div word ptr ds:[4]
mov ds:[6],ax
8.编写:安装中断7ch的中断例程实现求一word型数据的平方。
1、编程实现求平方功能的程序;
2、安装程序在0:200处;
3、设置中断向量表将程序的入口地址保存在7ch表项中,使其成为中断7ch的中断例程。
;计算
assume cs:code;主程序
code segment
start:
mov ax,3456
int 7ch
add ax,ax
adc ax,dx
mov ax,4c00h
int 21h
code ends
end start
assume cs:code
code segment
start:;安装程序
mov ax,cs
mov ds,ax
mov si offset sqr;设置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h;设置es:di指向目的地址
mov cx,offset sqrend- offset sqr;设置cx为传输长度
cld;设置传输方向为正
rep movsb;跳转
;设置中断向量表
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr ws:[7ch*4+2],0
mov ax,4c00h
int 21h
;中断例程(实现功能)
sqr:
mul ax
iret;中断例程实现返回指令iret(配套使用int···iret,子程序中配套使用call···ret)
sqrend:
nop
code ends
end start
老师布置的实验题目:
实验二
1.编程实现书上问题7.9
;通过编程将datasg段中每个单词前4个字母改写为大写字母
assume cs:codesg,ss:stacksg,ds:datasg
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends
datasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg ends
codesg segment
start:
codesg ends
end start
源代码:
;将datasg段中每个单词的前4个字母改成大写字母
assume cs:codesg,ss:stacksg,ds:datasg
;assume用来加上某一段寄存器和程序中的某一用segment……ends定义的段相关联。
;①segment是段的意思,是段定义伪指令
;②汇编中,有数据段,代码段,堆栈段以及附加段
;格式:
;段名 SEGMENT [定位类型] [组合类型] [类别名]
;段名 ENDS
;③功能:把程序分段,实现存储器的分段管理。
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends
;伪指令db、dw、dd都可以定义字符串,但最多的是用db来定义字符串,其中一个原因是dw、dd定义的字符串到了内存中排序是相反的。
;db定义 字节类型变量,一个字节数据占1个字节单元,读完一个,偏移量加1
;dw定义 字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2
;dd定义 双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4
;datasg中定义了4个字符串,每个字符串长度均为16字节,要求修改每个单词前4个字母,则要修改每个字符串的[3]~[6]
datasg segment
db '1. display '
db '2. brows '
db '3. replace '
db '4. modify '
datasg ends
codesg segment
start:mov ax,stacksg
mov ss,ax
mov sp,16
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,4
;用一个二重循环来解决,第一重循环用来循环字符串,第二重用来修改第一行字符串的【3】~【6】,共4*4次循环
;运用一个栈保存第一重循环数
s:push cx
mov di,3
mov cx,4
s0:mov al,[bx+di]
and al,11011111b
mov [bx+di],al
inc di
loop s0
pop cx
add bx,16
loop s
mov ax,4c00h
int 21 ;第21号中断
;这两条指令说实现的功能就是程序返回。
codesg ends
end start
;end是个伪指令,程序的结束标记
2.寻址方式在结构化数据访问中的应用
Power idea 公司从1975年成立一直到1995年的基本情况如下。
下面的程序中,已经定义好了这些数据:
assume cs:code
data segment
db '1975','1976','1977','1978','1979','1980', '1981'
db '1982', '1983','1984', '1985','1986', '1987','1988'
db '1989','1990','1991','1992','1993','1994','1995'
;以上是表示21年的21个字符串
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479
dd 140417,197514,345980,590827,803530,118300,1843000
dd 2759000,3753000,4649000,5937000
;以上是表示21年公司总收入的21个dword型数据
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037
dw 5635,8226,11542,14430,15257,17800
;以上是表示21年公司雇员人数的21个word数据
data ends
table segment
;预留位置,用于显示数据
db 21 dup ('year summ ne ?? ')
table ends
编程,将data段中的数据按如下格式写入到table段中,并计算21年中的人均收入(取整),结果也按照下面的格式保存到table段中:
提示,可将data段中的数据看成是多个数组,而将table中的数据看成是一个结构性数据的数组,每个结构性数据中包含多个数据项。可用bx定位每个结构性数据,用idata定位数据项,用si定位数组项中的每个元素,对于table中的数据的访问可采用[bx].idata和[bx].idata[si]的寻址方式。
源代码:
3.将任意输入的十进制数(<32768)转换成四位十六进制进行输出显示。
要求:1)有提示信息
2)按下ESC推出程序
3)将结果显示在屏幕中间