注意事项
1)本笔记内容整理自网络,感谢网友无私分享,我们也将注明引用地址
2)由于篇幅原因,本笔记不再复述原题,请参考原书(ISBN 978-7-302-33314-2)
实验环境
使用 Windows XP 实验会遇到其他问题,最后还是选择使用 DOSBox 环境。
汇编语言答案(王爽)
检测点 1.1
1)1个CPU的寻址能力为8KB,那么它的地址总线的宽度为 13 位。
2)1KB的存储器有 1024 个存储单元,存储单元的编号从 0 到 1023 。
3)1KB的存储器可以存储 8192(2^13)个bit, 1024个Byte。
4)1GB是 1073741824(2^30)个Byte、1MB是 1048576(2^20)个Byte、1KB是 1024(2^10)个Byte。
5)8080、8088、80296、80386的地址总线宽度分别为16根、20根、24根、32根,则它们的寻址能力分别为: 64(KB)、1(MB)、16(MB)、4(GB)。
6)8080、8088、8086、80286、80386的数据总线宽度分别为8根、8根、16根、16根、32根。则它们一次可以传送的数据为: 1(B)、1(B)、2(B)、2(B)、4(B)。
7)从内存中读取1024字节的数据,8086至少要读 512 次,80386至少要读 256 次。
8)在存储器中,数据和程序以 二进制 形式存放。
解题过程:
1)1KB=1024B,8KB=1024B*8=2^N,N=13。
2)存储器的容量是以字节为最小单位来计算的,1KB=1024B。
3)8Bit=1Byte,1024Byte=1KB(1KB=1024B=1024B*8Bit)。
4)1GB=1073741824B(即2^30)1MB=1048576B(即2^20)1KB=1024B(即2^10)。
5)一个CPU有N根地址线,则可以说这个CPU的地址总线的宽度为N。这样的CPU最多可以寻找2的N次方个内存单元。(一个内存单元=1Byte)。
6)8根数据总线一次可以传送8位二进制数据(即一个字节)。
7)8086的数据总线宽度为16根(即一次传送的数据为2B)1024B/2B=512,同理1024B/4B=256。
8)在存储器中指令和数据没有任何区别,都是二进制信息。
检测点 2.1
1)写出每条汇编指令执行后相关寄存器中的值。
mov ax,62627 AX=F4A3H
mov ah,31H AX=31A3H
mov al,23H AX=3123H
add ax,ax AX=6246H
mov bx,826CH BX=826CH
mov cx,ax CX=6246H
mov ax,bx AX=826CH
add ax,bx AX=04D8H
mov al,bh AX=0482H
mov ah,bl AX=6C82H
add ah,ah AX=D882H
add al,6 AX=D888H
add al,al AX=D810H
mov ax,cx AX=6246H
2)只能使用目前学过的汇编指令,最多使用4条指令,编程计算2的4次方。
mov ax,2 AX=2 add ax,ax AX=4 add ax,ax AX=8 add ax,ax AX=16
检测点 2.2
1)给定段地址为0001H,仅通过变化偏移地址寻址,CPU的寻址范围为 0010H 到 1000FH 。
解题过程:
物理地址=SA*16+EA
EA的变化范围为0h~ffffh
物理地址范围:(SA*16+0h) ~ (SA*16+ffffh)
现在SA=0001h,那么寻址范围:(0001h*16+0h)~(0001h*16+ffffh) = 0010h~1000fh
2)有一数据存放在内存 20000H 单元中,现给定段地址为SA,若想用偏移地址寻到此单元。则SA应满足的条件是:最小为 1001H ,最大为 2000H 。
当段地址给定为 1001H 以下和 2000H 以上,CPU无论怎么变化偏移地址都无法寻到20000H单元。
解题过程:
物理地址 = SA * 16 + EA
20000h = SA * 16 + EA
SA = (20000h – EA) / 16 = 2000h – EA / 16
EA取最大值时, SA=2000h-ffffh/16=1001h,SA 为最小值
EA取最小值时,SA=2000h-0h/16=2000h,SA为最大值
检测点 2.3
下面的3条指令执行后,cpu几次修改IP?都是在什么时候?最后IP中的值是多少?
mov ax,bx
sub ax,ax
jmp ax
答:一共修改四次
第一次:读取 mov ax,bx 之后
第二次:读取 sub ax,ax 之后
第三次:读取 jmp ax 之后
第四次:执行 jmp ax 修改IP
最后IP的值为0000H,因为最后ax中的值为0000H,所以IP中的值也为0000H
检测点 3.1
1) 在DEBUG中,用 “D 0:0 lf” 查看内存,结果如下:
0000:0000 70 80 F0 30 EF 60 30 E2-00 80 80 12 66 20 22 60
0000:0010 62 26 E6 D6 CC 2E 3C 3B-AB BA 00 00 26 06 66 88
下面的程序执行前,AX=0,BX=0,写出每条汇编指令执行完后相关寄存器中的值
mov ax,1
mov ds,ax
mov ax,[0000] ax= 2662H
mov bx,[0001] bx= E626H
mov ax,bx ax= E626H
mov ax,[0000] ax= 2662H
mov bx,[0002] bx= D6E6H
add ax,bx ax= FD48H
add ax,[0004] ax= 2C14H
mov ax,0 ax= 0
mov al,[0002] ax= 00e6H
mov bx,0 bx= 0
mov bl,[000c] bx= 0026H
add al,bl ax= 000CH
2)内存中的情况如图3.6所示
各寄存器的初始值:cs=2000h,ip=0,ds=1000h,ax=0,bx=0;
检测点 3.2
1)补全下面的程序,使其可以将10000H-1000FH中的8个字,逆序拷贝到20000H-2000FH中。
mov ax,1000H
mov ds,ax
mov ax,2000H
mov ss,ax
mov sp,10h
push [0]
push [2]
push [4]
push [6]
push [8]
push [A]
push [C]
push [E]
2)补全下面的程序,使其可以将10000H-1000FH中的8个字,逆序拷贝到20000H-2000FH中。
mov ax,2000H
mov ds,ax
mov ax,1000H
mov ss,ax
mov sp,0
pop [e]
pop [c]
pop [a]
pop [8]
pop [6]
pop [4]
pop [2]
pop [0]
检测点 6.1
1)下面的程序实现依次用内存0:0~0:15单元中的内容改写程序中的数据,完成程序:
assume cs:codesg codesg segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h start: mov ax,0 mov ds,ax mov bx,0 mov cx,8 s: mov ax,[bx] mov cs:[bx],ax ; 这是需要填写的内容 add bx,2 loop s mov ax,4c00h int 21h codesg ends end start
2)下面的程序实现依次用内存0:0~0:15单元中的内容改写程序中的数据,数据的传送用栈来进行。栈空间设置在程序内。完成程序:
assume cs:codesg codesg segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h dw 0,0,0,0,0,0,0,0,0,0 start: mov ax, codesg ; 或 mov ax, cs mov ss,ax mov sp, 24h ; 或 mov sp, 36 ;(第一版填 1ah 或 26) mov ax,0 mov ds,ax mov bx,0 mov cx,8 s: push [bx] pop cs:[bx] ; 或 pop ss:[bx] add bx,2 loop s mov ax,4c00h int 21h codesg ends end start
1)程序如下。
assume cs:code
data segment
dw 2 dup (0)
data ends
code segment
start: mov ax,dtat
code ends
end start
若要使jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?
答案①db 3 dup (0)
答案②dw 2 dup (0)
答案③dd 0
jmp word ptr [bx+1]为段内转移,要CS:IP指向程序的第一条指令,应设置ds:[bx+1]的字单元(2个字节)存放数据应为0,则(ip)=ds:[bx+1]=0
简单来说就是,只要ds:[bx+1]起始地址的两个字节为0就可以了
检测点 9.1
(2)
答案:
assume cs:code data segment dd 12345678h data ends code segment start: mov ax,data mov ds,ax mov bx,0 mov [bx], bx ; 或 mov [bx], word ptr 0 ; 或 mov [bx], offset start mov [bx+2], cs ; 或 mov [bx+2], cs ; 或 mov [bx+2], seg code jmp dword ptr ds:[0] code ends end start
解析:
jmp dword ptr ds:[0]为段间转移,(cs)=(内存单元地址+2),(ip)=(内存单元地址),要CS:IP指向程序的第一条指令,第一条程序地址cs:0,应设置CS:IP指向cs:0
程序中的mov [bx],bx这条指令,是将ip设置为0
mov [bx+2],cs,将cs这个段地址放入内存单元
执行后,cs应该不变,只调整ip为0,(ip)=ds:[0]=0
(3)
答案:
(cs)= 0006H ,(ip)= 00BEH
解析:
jmp dword ptr为段间转移,高位存放段地址,低位存放偏移地址
(cs)=(内存单元地址+2),(ip)=(内存单元地址)
根据书P16,对于寄存器AX,AH为高位(前1字节为高位),AL为低位(后1字节为低位)
推算出(内存单元地址)=00beh,(内存单元地址+2)=0006h
根据书P182,高位存放段地址(后2个字节为高位),低位存放偏移地址(前2个字节为低位)
(cs)=(内存单元地址+2),(ip)=(内存单元地址)
推算出(cs)=0006h,(ip)=00beh
检测点 9.2
补全编程,利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中。
assume cs:code code segment start: mov ax, 2000h mov ds, ax mov bx, 0 s: mov ch, 0 mov cl, [bx] jcxz ok ;当cx=0时,CS:IP指向OK inc bx jmp short s ok: mov dx,bx mov ax ,4c00h int 21h code ends end start
检测点 9.3
补全编程,利用loop指令,实现在内存2000H段中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中。
assume cs:code
code segment
start: mov ax,2000h
code ends
end start
书P101,执行loop s时,首先要将(cx)减1。
“loop 标号”相当于
dec cx
if((cx)≠0) jmp short 标号
检测点 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, _____ push ax mov ax, _____ push ax retf code ends end start
答案
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 cs
根据栈先进后出原则,应先将段地址cs入栈,再将偏移地址ip入栈。
检测点 10.2
题目
下面的程序执行后,ax中的数值为多少?
内存地址 机器码 汇编指令 1000:0 b8 00 00 mov ax, 0 1000:3 e8 01 00 call s 1000:6 40 inc ax 1000:7 58 s:pop ax
答案
ax=6
解析
内存地址 机器码 汇编指令 执行后情况 1000:0 b8 00 00 mov ax,0 ax=0 ip指向1000:3 1000:3 e8 01 00 call s pop ip ip指向1000:7 1000:6 40 inc ax 1000:7 58 s:pop ax ax=6
用debug进行跟踪确认,“call 标号”是将该指令后的第一个字节偏移地址入栈,再转到标号处执行指令。
assume cs:code code segment start: mov ax,0 call s inc ax s: pop ax mov ax,4c00h int 21h code ends end start
检测点 10.3
题目
下面的程序执行后,ax中的数值为多少?
内存地址 机器码 汇编指令 1000:0 b8 00 00 mov ax,0 1000:3 9a 09 00 00 10 call far ptr s 1000:8 40 inc ax 1000:9 58 s:pop ax add ax,ax pop bx add ax,bx
答案
ax=1010H
解析
内存地址 机器码 汇编指令 执行后情况 1000:0 b8 00 00 mov ax,0 ax=0,ip指向1000:3 1000:3 9a 09 00 00 10 call far ptr s pop cs,pop ip,ip指向1000:9 1000:8 40 inc ax 1000:9 58 s:pop ax ax=8h add ax,ax ax=10h pop bx bx=1000h add ax,bx ax=1010h
用debug进行跟踪确认,“call far ptr s”是先将该指令后的第一个字节段地址cs=1000h入栈,再将偏移地址ip=8h入栈,最后转到标号处执行指令。
出栈时,根据栈先进后出的原则,先出的为ip=8h,后出的为cs=1000h
检测点 10.4
问题
下面的程序执行后,ax中的数值为多少?
内存地址 机器码 汇编指令 1000:0 b8 06 00 mov ax,6 1000:3 ff d0 call ax 1000:5 40 inc ax 1000:6 58 mov bp,sp add ax,[bp]
答案
ax=0bH
解析
内存地址 机器码 汇编指令 执行后情况 1000:0 b8 06 00 mov ax,6 ax=6,ip指向1000:3 1000:3 ff d0 call ax pop ip,ip指向1000:6 1000:5 40 inc ax 1000:6 58 mov bp,sp bp=sp=fffeh add ax,[bp] ax=[6+ds:(fffeh)]=6+5=0bh
用debug进行跟踪确认,“call ax(16位reg)”是先将该指令后的第一个字节偏移地址ip入栈,再转到偏移地址为ax(16位reg)处执行指令。
检测点10.5
题目
1)下面的程序执行后,ax中的数值为多少?
assume cs:code stack segment dw 8 dup (0) stack ends code segment start: mov ax,stack mov ss,ax mov sp,16 mov ds,ax mov ax,0 call word ptr ds:[0eh] inc ax inc ax inc ax mov ax,4c00h int 21h code ends end start
答案
ax=3
解析
执行call word ptr ds:[0eh]指令时,先cs入栈,再ip=11入栈,最后ip转移到(ds:[0eh])。(ds:[0eh])=11h,执行inc ax……最终ax=3
题中特别关照别用debug跟踪,跟踪结果不一定正确,但还是忍不住去试试,看是什么结果。
根据单步跟踪发现,执行call word ptr ds:[0eh]指令时,显示ds:[0eh]=065D。
ds:0000~ds:0010不是已设置成stack数据段了嘛,不是应该全都是0的嘛。
于是进行了更详细的单步跟踪,发现初始数据段中数据确实为0,但执行完mov ss,ax;mov sp,16这两条指令后,数据段中数据发生改变。这是为什么呢?中断呗~~~~
题目
2)下面的程序执行后,ax和bx中的数值为多少?
assume cs:codesg stack segment dw 8 dup(0) stack ends codesg segment start: mov ax,stack mov ss,ax mov sp,10h mov word ptr ss:[0],offset s mov ss:[2],cs call dword ptr ss:[0] nop s: mov ax,offset s sub ax,ss:[0ch] mov bx,cs sub bx,ss:[0eh] mov ax,4c00h int 21h codesg ends end start
答案
ax=1, bx=0
解析
assume cs:codesg stack segment dw 8 dup(0) stack ends codesg segment start: mov ax,stack mov ss,ax mov sp,10h mov word ptr ss:[0],offset s ;(ss:[0])=1ah mov ss:[2],cs ;(ss:[2])=cs call dword ptr ss:[0] ;cs入栈,ip=19h入栈,转到cs:1ah处执行指令 ;(ss:[4])=cs,(ss:[6])=ip nop s: mov ax,offset s ;ax=1ah sub ax,ss:[0ch] ;ax=1ah-(ss:[0ch])=1ah-19h=1 mov bx,cs ;bx=cs=0c5bh sub bx,ss:[0eh] ;bx=cs-cs=0 mov ax,4c00h int 21h codesg ends end start
;应用举例:计算ffh*10
assume cs:code
code segment
start: mov ax,0ffh
code ends
end start
PS:
左移1位,N=(N)*2
左移2位,N=(N)*4
左移3位,N=(N)*8
左移4位,N=(N)*16
左移5位,N=(N)*32
实验 1 查看 CPU 和内存,用机器指令和汇编指令编程
实验 2 用机器指令和汇编指令编程
实验 3 编程、编译、连接、跟踪
实验 4 [bx] 和 loop 的使用
实验 5 编写、调试具有多个段的程月
(1)
1.保持不变
2.<考虑不同机子环境不同,答案无法统一>
3.X-2,X-1
(2)
1.保持不变
2.<考虑不同机子环境不同,答案无法统一>
3.X-2,X-1
4.(N/16+1)*16 [说明:N/16 只取整数部分]
(3)
1.保持不变
2.<考虑不同机子环境不同,答案无法统一>
3.X+3,X+4
(4)
答:第3个仍然可以正确执行。因为如果把end指令后的标号start去掉后,编译器便会顺序执行程序。换句话说:当未给编译器预先的通知,要求其从哪开始执行程序时,编译器就自动以’至上向下’的顺序进行编译执行源程序。
(5)
完整程序如下:
assume cs:code a segment db 1,2,3,4,5,6,7,8 a ends b segment db 1,2,3,4,5,6,7,8 b ends c segment db 0,0,0,0,0,0,0,0 c ends code segment start: mov ax,a mov es,ax mov ax,c mov ds,ax mov bx,0 mov cx,8 s1: mov ax,es:[bx] add [bx],ax add bx,2 loop s1 mov ax,b mov es,ax mov ax,c mov ds,ax mov bx,0 mov cx,8 s2: mov ax,es:[bx] add [bx],ax add bx,2 loop s2 mov ax,4c00h int 21h code ends end start
(6)
完整程序如下:
assume cs:code a segment dw 1,2,3,4,5,6,7,8 a ends b segment dw 0,0,0,0,0,0,0,0 b ends code segment start: mov sp, 10H mov ax, b mov ss, ax mov ax, a mov ds, ax mov bx, 0H mov cx, 8 s: push ds:[bx] add bx, 2 loop s mov ax, 4c00H int 21H code ends end start
实验 6 实践课程中的程序
实验 8 分析一个奇怪的程序
实验 9 根据材料编程
assume cs:codesg, ds:datasg datasg segment db 'welcome to masm!' datasg ends codesg segment start: mov cx, 16 mov bx, 0 mov si, 0 mov ax, datasg mov ds, ax mov ax, 0B86EH mov es, ax s0: mov al, ds:[bx] mov ah, 00000010B mov es:[40H+si], ax mov ah, 00100100B mov es:[40H + 0A0H +si], ax mov ah, 00010111B mov es:[40H + 0A0H + 0A0H +si], ax inc bx add si, 2 loop s0 mov ax, 4c00h int 21h codesg ends end start
参考文献
CSDN/汇编语言王爽第三版答案
百度文库/汇编语言实验答案 (王爽)