452. 7.3 以字符形式给出的数据assume ds:data
data segment
db 'unIX'
db 'foRK'
data ends
code segment
start:mov al,'a'
mov bl,'b'
mov ax,4c00h
int 21h
code ends
end start 程序7.1
455. 7.4 大小写转换的问题我们可以将所有的字母的大写字符和小写字符所对应的ASCII码列出来,进行对比,从中找到规律。
大写 二进制 小写 二进制
A 01000001 a 01100001
B 01000010 b 01100010
C 01000011 c 01100011
D 01000100 d 01100100
484. 7.7 SI和DI问题7.2
用寄存器SI和DI实现将字符串‘welcome to masm!’复制到它后面的数据区中。
assume cs:codesg,ds:datasg
datasg segment
db 'welcome to masm!'
db '................'
datasg ends
思考后看分析。
510. 7.10 不同的寻址方式的灵活应用问题7.6
编程,将datasg段中每个单词的头一个字母改为大写字母。assume cs:codesg,ds:datasg
datasg segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
datasg ends
codesg segment
start:……
codesg ends
end start
688. 9.10 编译器对转移位移超界的检测示例
下面的程序将引起编译错误:
jmp short s的转移范围是-128~127,IP最多向后移动127个字节。 assume cs:code
code segment
start: jmp short s
db 128 dup(0)
s: mov ax,0ffffh
code ends
end start
728. 10.7 call 和 ret 的配合使用程序的主要执行过程:
(5)CPU回到 cs:000EH处(即call指令后面的指令处)继续执行。
从上面的讨论中我们发现,可以写一个具有一定功能的程序段,我们称其为子程序,在需要的时候,用call指令转去执行。
729. 10.7 call 和 ret 的配合使用可是执行完子程序后,如何让CPU接着call指令向下执行?
call指令转去执行子程序之前,call指令后面的指令的地址将存储在栈中,所以可以在子程序的后面使用 ret 指令,用栈中的数据设置IP的值,从而转到 call 指令后面的代码处继续执行。
730. 10.7 call 和 ret 的配合使用这样,我们可以利用call和ret来实现子程序的机制。
子程序的框架
731. 10.7 call 和 ret 的配合使用子程序的框架:
标号:
指令
ret具有子程序的源程序的框架:
732. 10.7 call 和 ret 的配合使用现在,可以从子程序的角度,回过头来再看一下本节中的两个程序。
733. 10.8 mul 指令因下面要用到,我们介绍一下mul指令,mul是乘法指令,使用 mul 做乘法的时候:
(1)相乘的两个数:要么都是8位,要么都是16位。
8 位: AL中和 8位寄存器或内存字节单元中;
16 位: AX中和 16 位寄存器或内存字单元中。
734. 10.8 mul 指令使用mul座乘法的时候:
(2)结果
8位:AX中;
16位:DX(高位)和AX(低位)中。
格式如下:
mul reg
mul 内存单元
735. 10.8 mul 指令内存单元可以用不同的寻址方式给出,比如:
mul byte ptr ds:[0]
含义为: (ax)=(al)*((ds)*16+0);
mul word ptr [bx+si+8]
含义为:
(ax)=(al)*((ds)*16+(bx)+(si)+8)结果的低16位;
(dx)=(al)*((ds)*16+(bx)+(si)+8)结果的高16位;
736. 10.8 mul 指令例如:
(1)计算100*10
100和10小于255,可以做8位乘法,程序如下:
mov al,100
mov bl,10
mul bl
结果: (ax)=1000(03E8H)
737. 10.8 mul 指令例如:
(1)计算100*10000
100小于255,可10000大于255,所以必须做16位乘法,程序如下:
mov ax,100
mov bx,10000
mul bx
结果: (ax)=4240H,(dx)=000FH
(F4240H=1000000)
738. 10.9 模块化程序设计从上面我们看到 ,call 与 ret 指令共同支持了汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。
因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必须的解决方法。
758. 10.12 寄存器冲突的问题程序的应用
(1)将data段中字符串转化为大写
assume cs:code
data segment
db 'conversation',0
data ends
代码段中相关程序段如下:
mov ax,data
mov ds,ax
mov si,0
call capital
759. 10.12 寄存器冲突的问题子程序的应用
(2)将data段中字符串全部转化为大写
assume cs:code
data segment
db ‘word',0
db ‘unix',0
db ‘wind',0
db ‘good',0
data ends
可以看到,所有字符串的长度都是5(算上结尾符 0 ),我们使用循环 ,重复调用子程序capital完成对4个字符串的处理。
完整的程序代码
768. 10.12 寄存器冲突的问题问题10.2改进的子程序capital的设计capital: push cx
push si
change: mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
jmp short change
ok: pop si
pop cx
ret
要注意寄存器入栈和出栈的顺序。
891. 11.9 检测比较结果的条件转移指令我们来看一组程序:
data段中的8个字节如下:
data segment
db 8,11,8,1,8,5,63,38
data ends
(1)编程:统计data段中数值为8的字节的个数,用ax保存统计结果。
(2)编程:统计data段中数值大于8的字节的个数,用ax保存统计结果。
(3)编程:统计data段中数值小于8的字节的个数,用ax保存统计结果。
913. 11.10 DF标志和串传送指令编程:
用串传送指令,将data段中的第一个字符串复制到它后面的空间中。
data segment
db ‘Welcome to masm!’
db 16 dup (0)
data ends
我们分析一下
914. 11.10 DF标志和串传送指令我们分析一下,使用串传送指令进行数据的传送,需要给它提供一些必要的信息,它们是:
① 传送的原始位置:ds:si;
② 传送的目的位置:es:di;
③ 传送的长度:cx;
④ 传送的方向:DF。
915. 11.10 DF标志和串传送指令在这个问题中,这些信息如下:
① 传送的原始位置:data:0;
② 传送的目的位置:data:16;
③ 传送的长度:16;
④ 传送的方向: 因为正向传送(每次串传送指令执行后,si和di 递增)比较方便,所以设置DF=0。
明确了这些信息之后,我们来编写程序
1042. 13.2 编写供应用程序调用的中断例程应用举例:将data段中的字符转化为大写。
assume cs:code
data segment
db 'conversation',0
data ends
code segment
start: mov ax,data
mov ds,ax
mov si,0
int 7ch
mov ax,4c00h
int 21h
code ends
end start
1084. 14.1 端口的读写访问内存:
mov ax,ds:[8];假设执行前(ds)=0
执行时,与总线相关的操作:
① CPU通过地址线将地址信息8发出;
② CPU通过控制线发出内存读命令,选中存储器芯片,并通知它,将要从中读取数据;
③ 存储器将 8号单元中的数据通过数据线送入CPU。
1085. 14.1 端口的读写访问端口:
in al,60h;从60h号端口读入一个字节
执行时与总线相关的操作:
① CPU通过地址线将地址信息60h发出;
② CPU通过控制线发出端口读命令,选中端口所在的芯片,并通知它,将要从中读取数据;
③ 端口所在的芯片将60h端口中的数据通过数据线送入CPU。
1088. 14.1 端口的读写对0~255以内的端口进行读写:
in al,20h ;从20h端口读入一个字节
out 20h,al ;往20h端口写入一个字节
对256~65535的端口进行读写时,端口号放在dx中:
mov dx,3f8h ;将端口号3f8送入dx
in al,dx ;从3f8h端口读入一个字节
out dx,al ;向3f8h端口写入一个字节
1197. 16.1 描述了单元长度的标号assume cs:code
code segment
a : db 1,2,3,4,5,6,7,8
b : dw 0
start :mov si,offset a
mov bx,offset b
mov cx,8
s : mov al,cs:[si]
mov ah,0
add cs:[bx],ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
1199. 16.1 描述了单元长度的标号assume cs:code
code segment
a db 1,2,3,4,5,6,7,8
b dw 0
start : mov si,0
mov cx,8
s : mov al,a[si]
mov ah,0
add b,ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
1212. 16.2 在其他段中使用数据标号我们可以将标号当作数据来定义,此时,编译器将标号所表示的地址当作数据的值。
比如:
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw a,b
data ends
数据标号c处存储的两个字型数据为标号a、b 的偏移地址。
1213. 16.2 在其他段中使用数据标号相当于:
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw offset a, offset b
data ends
1214. 16.2 在其他段中使用数据标号再比如:
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dd a,b
data ends
数据标号c处存储的两个双字型数据为标号a的偏移地址和段地址、标号b 的偏移地址和段地址。
1215. 16.2 在其他段中使用数据标号相当于:
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw offset a, seg a, offset b, seg b
data ends
seg操作符,功能为取得某一标号的段地址。
1273. 17.3 字符串的输入(4)程序的处理过程。现在我们可以简单地确定程序的处理过程如下:
① 调用int 16h读取键盘输入;
② 如果是字符,进入字符栈,显示字符栈中的所有字符;继续执行① ;
③ 如果是退格键,从字符栈中弹出一个字符,显示字符栈中的所有字符;继续执行① ;
④ 如果是Enter 键,向字符栈中压入0,返回。