教材:《汇编语言》(2008年4月第2版),王爽著,清华大学出版社。
长期以来,汇编语言被认为是一门枯燥难学的课程,但王爽老师的著作《汇编语言》解决了这个问题。
本人仅将自己在学习此书时所做的答案及一些个人笔记进行了整理并编辑成集,方便日后查阅。
后因朋友要求,发于网上共享。
由于本人水平有限,制作仓促,不能保证解析完全正确。
如果你在对照的过程中,发现了错误的地方,可以写信告知本人,在此先表示感谢。
邮箱:gray_always@qq.com;
QQ群:100354217(群主不友善,慎入)
如有其它疑问,可到专门为《汇编语言》读者开设的论坛中寻求帮助。
网站地址:http://www.asmedu.net/bbs/forum.jsp
制作者:灰色依然
制作日期:2008年7月
如需转载,请注明来源和出处,谢谢。
实验10
(1)show_str子程序
效果图
;在屏幕的8行3例,用绿色显示data段中的字符串。
assume cs:code,ds:data
data segment
db 'welcome to masm!',0
data ends
code segment
start: mov dh,8
mov dl,3
mov cl,2
mov ax,data
mov ds,ax
mov si,0
call show_str
mov ax,4c00h
int 21h
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
;参数:(dh)=行号(取值范围0~24);
; (dl)=列号(取值范围0~79);
; (cl)=颜色;
; ds:si指向字符串的首地址。
;返回:无。
show_str:
push ax
push bx
push es
push si
mov ax,0b800h
mov es,ax
mov ax,160
mul dh
mov bx,ax ;bx=160*dh
mov ax,2
mul dl ;ax=dl*2
add bx,ax ;mov bx,(160*dh+dl*2)设置es:bx指向显存首地址
mov al,cl ;把颜色cl赋值al
mov cl,0
show0:
mov ch,[si]
jcxz show1 ;(ds:si)=0时,转到show1执行
mov es:[bx],ch
mov es:[bx].1,al
inc si ;ds:si指向下一个字符地址
add bx,2 ;es:bx指向下一个显存地址
jmp show0
show1:
pop si
pop es
pop bx
pop ax
ret
code ends
end start
实验10
(2) divdw子程序
效果图
;应用举例:计算1000000/10(f4240h/0ah)
assume cs:code
code segment
start: mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
;名称:divdw
;功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
;参数:(ax)=dword型数据的低16位;
; (dx)=dword型数据的高16位;
; (cx)=除数。
;返回:(dx)=结果的高16位;
; (ax)=结果的低16位;
; (cx)=余数。
divdw:
push si
push bx
push ax
mov ax,dx
mov dx,0
div cx ;被除数的高位/cx,高位在ax,余数在dx
mov si,ax
pop ax
div cx ;(被除数高位的商+低位)/cx,高位在ax,余数在dx
mov cx,dx ;余数入cx
mov dx,si ;高位的商入dx
pop bx
pop si
ret
code ends
end start
公式x/n=int(h/n)*65536+[rem(h/n)*65536+l]/n解析:
把一个会溢出的除法 变成几个除法来做!
如果高位除法有商,那么商就是结果的高位值,如果会有余数,那么余数自然不能丢弃,余数就作为低位除法的dx(也就是高位的被除数,因为他是从高位除法中余下的)
做低位除法的时候,就拿余下的dx+低位数的ax除以除数,会得到 一个低位的商 和 余数
高位的商+低位数的商+余数 就是结果
这个程序的理解和完成,应该是这章比较重要的一个点。咱得多注意这种变通和分解的方式来处理问题的思维方式。
实验10
(3) dtoc1子程序
效果图
;应用举例:将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。
;在显示时调用子程序show_str。
assume cs:code,ds:data
data segment
db 10 dup (0)
data ends
code segment
start: mov ax,12666
mov bx,data
mov ds,bx
mov si,0 ;ds:si指向data首地址
call dtoc1
mov dh,8
mov dl,3
mov cl,2
call show_str
mov ax,4c00h
int 21h
;名称:dtoc1
;功能:将word型数据转变为表示十进制的字符串,字符串以0为结尾符。
;参数:(ax)=word型数据;
; ds:si指向字符串首地址。
;返回:无。
dtoc1: push ax
push bx
push cx
push dx
push si
push di
mov di,0
d10: mov dx,0 ;设置被除数高位为0
mov bx,10 ;除数为10
div bx
add dx,30h ;ax/10的余数+30h,转为字符
push dx ;字符入栈
inc di ;记录字符个数
mov cx,ax
jcxz d11 ;当ax/10的商=0时,转到d11执行
jmp d10
d11: mov cx,di
d12: pop dx ;字符出栈
mov [si],dl
inc si ;ds:si指向下一单元
loop d12
mov dl,0
mov [si],dl ;设置结尾符0
pop si
pop di
pop dx
pop cx
pop bx
pop ax
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
;参数:(dh)=行号(取值范围0~24);
; (dl)=列号(取值范围0~79);
; (cl)=颜色;
; ds:si指向字符串的首地址。
;返回:无。
show_str:
push ax
push bx
mov ax,0b800h
mov es,ax
mov ax,160
mul dh
mov bx,ax ;bx=160*dh
mov ax,2
mul dl ;ax=dl*2
add bx,ax ;mov bx,(160*dh+dl*2)设置es:bx指向显存首地址
mov al,cl ;把颜色cl赋值al
mov cl,0
show0:
mov ch,[si]
jcxz show1 ;(ds:si)=0时,转到show1执行
mov es:[bx],ch
mov es:[bx].1,al
inc si ;ds:si指向下一个字符地址
add bx,2 ;es:bx指向下一个显存地址
jmp show0
show1:
pop bx
pop ax
ret
code ends
end start
实验10
(4) dtoc2子程序
效果图
;应用举例:将数据2494967295以十进制的形式在屏幕的8行3列,用绿色显示出来。
assume cs:code,ds:data
data segment
db 10 dup (0)
data ends
code segment
start: mov ax,0ffffh
mov dx,0ffffh
mov bx,data
mov ds,bx
mov si,0 ;ds:si指向字符串首地址
call dtoc2
mov dh,8
mov dl,3
mov cl,2
call show_str
mov ax,4c00h
int 21h
;名称:dtoc2
;功能:将dword型数据转变为表示十进制的字符串,字符串以0为结尾符。
;参数:(ax)=dword型数据的低16位;
; (dx)=dword型数据的高16位;
; ds:si指向字符串首地址。
;返回:无。
dtoc2:
push ax
push bx
push cx
push dx
push si
push di
mov di,0
d20: mov cx,10 ;除数为10
call divdw
add cx,30h ;余数+30h,转为字符
push cx ;字符入栈
inc di ;记录字符个数
mov cx,ax
jcxz d21 ;低位商=0时,转到d21检测高位商
jmp d20
d21: mov cx,dx
jcxz d22 ;高低位商全=0时,转到d22执行
jmp d20
d22: mov cx,di
d23: pop ax ;字符出栈
mov [si],al
inc si ;ds:si指向下一单元
loop d23
mov al,0
mov [si],al ;设置结尾符0
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
;参数:(dh)=行号(取值范围0~24);
; (dl)=列号(取值范围0~79);
; (cl)=颜色;
; ds:si指向字符串的首地址。
;返回:无。
show_str:
push ax
push bx
mov ax,0b800h
mov es,ax
mov ax,160
mul dh
mov bx,ax ;bx=160*dh
mov ax,2
mul dl ;ax=dl*2
add bx,ax ;mov bx,(160*dh+dl*2)设置es:bx指向显存首地址
mov al,cl ;把颜色cl赋值al
mov cl,0
show0:
mov ch,[si]
jcxz show1 ;(ds:si)=0时,转到show1执行
mov es:[bx],ch
mov es:[bx].1,al
inc si ;ds:si指向下一个字符地址
add bx,2 ;es:bx指向下一个显存地址
jmp show0
show1:
pop bx
pop ax
ret
;名称:divdw
;功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
;参数:(ax)=dword型数据的低16位;
; (dx)=dword型数据的高16位;
; (cx)=除数。
;返回:(dx)=结果的高16位;
; (ax)=结果的低16位;
; (cx)=余数。
divdw:
push si
push bx
push ax
mov ax,dx
mov dx,0
div cx ;被除数的高位/cx
mov si,ax
pop ax
div cx ;(被除数高位的商+低位)/cx
mov cx,dx ;余数入cx
mov dx,si ;高位的商入dx
pop bx
pop si
ret
code ends
end start
检测点10.1
补全程序,实现从内存1000:0000处开始执行指令。
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax, 1000h
push ax
mov ax, 0
push ax
retf
code ends
end start
执行reft指令时,相当于进行:
pop ip
pop cs
根据栈先进后出原则,应先将段地址cs入栈,再将偏移地址ip入栈。
C:\DOCUME~1\SNUSER>debug jc10-1.exe
-u
0C50:0000 B84F0C MOV AX,0C4F
0C50:0003 8ED0 MOV SS,AX
0C50:0005 BC1000 MOV SP,0010
0C50:0008 B80010 MOV AX,1000
0C50:000B 50 PUSH AX
0C50:000C B80000 MOV AX,0000
0C50:000F 50 PUSH AX
0C50:0010 CB RETF
0C50:0011 3986FEFE CMP [BP+FEFE],AX
0C50:0015 737D JNB 0094
-g 0010
AX=0000 BX=0000 CX=0021 DX=0000 SP=000C BP=0000 SI=0000 DI=0000
DS=0C3F ES=0C3F SS=0C4F CS=0C50 IP=0010 NV UP EI PL NZ NA PO NC
0C50:0010 CB RETF
-t
AX=0000 BX=0000 CX=0021 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0C3F ES=0C3F SS=0C4F CS=1000 IP=0000 NV UP EI PL NZ NA PO NC
1000:0000 6E DB 6E
-