《汇编语言》检测点笔记

之前一直觉得《汇编语言》很秀。Assembly Language 阿。本来下个学期选修想选那门“微机接口和汇编语言”。但这个学期以为要挂的选修课,老师还是给了面子。所以学分讲道理是够了。斟酌了一下,决定自己看书爽一爽《汇编》。

果断选择了王爽的《汇编语言 (第三版) 》。

因为是有习题的,所以把习题就顺便记录了下来。

前面 11 章的答案我都 debug 验证过了,网上的答案有很多错误。从 12 章开始,我开始瞎屁股浏览了。主要是因为前面 11 章的内容让我比较喜欢,后面的较多的理论元素和一些指令完善,我就粗略的学习了。

检测点1.1

  1. 1个 CPU 的寻址能力为 8KB,那么它的地址总线的宽度为 13。 (8KB = 8 * 1024 = 2 ^ 13。 1字节为一个存储单元)
  2. 1KB 的存储器有 1024个存储单元。存储单元的编号从 01023
  3. 1KB 的存储器可以存储 2^13个bit,个 1024Byte。
  4. 1GB, 1MB, 1KB 分别是 2^30, 2^20, 2^10Byte。
  5. 8080,8088,80286,80386 的地址总线宽度分别为 16 根, 20 根, 24 根, 32 根,则它们的寻址能力分别为: 2^6 (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. 在存储器中, 数据和程序以 二进制形式存放。

检测点2.1

  1. 写出每条汇编指令执行后相关寄存器中的值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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 次方。

    1
    2
    3
    4
    mov ax,2
    add ax,ax
    add ax,ax
    add ax,ax

检测点2.2

  1. 给定段地址为 0001H, 仅通过变化偏移地址寻址,CPU 的寻址范围为 00010H1000FH
  2. 有一数据存放在内存 20000H 单元中,现给定段地址为 SA,若想用偏移地址寻到此单元。则 SA应满足的条件是: 最小为 1001H,最大为 20000H

检测点2.3

下面的三条指令执行后,CPU几次修改IP?都是在什么时候?最后IP中的值是多少?

1
2
3
mov ax,bx
sub ax,ax
jmp ax

4次。每条执行完都修改一次加上一次 jmp 的修改。0。

检测点3.1

  1. 在Debug中, 用“d 0:0 1f” 查看内存,结果如下:
    1
    2
    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,写出每条汇编指令执行完后相关寄存器中的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mov ax,1
mov ds,ax ; 将段地址ds设置为0001
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=`0000H`
mov al,[0002] ; AX=`00E6H`
mov bx,0 ; BX=`0000H`
mov bl,[000C] ; BX=`0026H`
add al,bl ; AX=`000CH`
  1. 内存中的情况如图3.6所示。
    各寄存器的初始值: CS=2000H, IP=0, DS=1000H, AX=0, BX=0;
    图3.6.png
  • 写出CPU执行的指令序列 (用汇编指令写出)
  • 写出CPU执行每条指令后,CS,IP和相关寄存器中的数值。
    1
    2
    3
    4
    5
    6
    mov ax,6622H    ; CS=2000H, IP=0003H, DS=1000H, AX=6622H, BX=0000H
    jmp 0ff0:0100 ; CS=0FF0H, IP=0100H, DS=1000H, Ax=6622H, BX=0000H
    mov ax,2000H ; CS=0FF0H, IP=0103H, DS=1000H, AX=2000H, BX=0000H
    mov ds,ax ; CS=0FF0H, IP=0105H, DS=2000H, Ax=2000H, BX=0000H
    mov ax,[0008] ; CS=0FF0H, IP=0108H, DS=2000H, AX=C389H, BX=0000H
    mov ax,[0002] ; CS=0FF0H, IP=010BH, Ds=2000H, Ax=EA66H, BX=0000H
  • 再次体会: 数据和程序有区别吗?如何确定内存中的信息哪些是数据,哪些是程序?

没有区别。存储在数据段中就是数据,存储在程序段中就是程序。

检测点3.2

  1. 补全下面的程序,使其可以将 10000H~1000FH 中的八个字,逆序复制到 20000H~2000FH 中。逆序复制的含义如图3.17所示 (图中内存里的数据均为假设) 。 (图我就不贴了。不重要)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    mov ax,1000H
    mov ds,ax

    mov ax,2000H
    mov ss,ax
    mov sp,0

    push [0]
    push [2]
    push [4]
    push [6]
    push [8]
    push [A]
    push [C]
    push [E]
  2. 补全下面的程序,使其可以将 10000H~1000FH 中的 8 个字,逆序复制到20000H~2000FH中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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

  3. 下面的程序实现依次用内存 0:0~0:15 单元中的内容改写程序中数据,完成程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
  1. 下面的程序实现依次用内存 0:0~0:15 单元中的内容改写程序中数据,数据的传送用栈来进行。栈空间设置在程序内。完成程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 ;10个字单元用作栈空间

start: mov ax,cs
mov ss,ax
mov sp,36 ; (网上的答案不是36,虽然跑起来没问题,但36才是最好的)

mov ax,0
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]

pop cs:[bx]

add bx,2
loop s

mov ax,4c00h
int 21h
codesg ends
end start

ps:我调试的时候发现一件很骚的事情,就是调试的时候,栈空间的值会发生改变。主要是因为中断机制,中断的时候某些关键值要存在内存里。。就用了程序的栈空间。我还纳闷了半天。。

实验5

这个实验做完主要有两点总结:

  • 第二题的第4小道: 16 * (N / 16 + 1)。关于内存的问题。在16位机器上,不足16字节的数据按16字节存储,意思就是17个字节,在程序中其实占的是32字节。其它机器应该可以递推。

  • 编译器编译汇编文件,从上往下编译。同理,数据段要是写在代码段后面,执行加载到内存中时,在内存中的位置也在代码段的后面。

检测点9.1

  1. 程序如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    assume cs:code

    data segment
    db 8 dup(0)
    data ends

    code segment
    start: mov ax,data
    mov ds,ax
    mov bx,0
    jmp word ptr [bx + 1]
    code ends
    end start

若要使程序中的 jmp 指令执行后, CS:IP 指向程序的第一条指令,在 data 段中应该定义哪些数据?

  1. 程序如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    assume cs:code

    data segment
    dd 12345678H
    data ends

    code segment

    start: mov ax,data
    mov ds,ax
    mov bx,0
    mov [bx],offset start
    mov [bx+2],cs
    jmp dword ptr ds:[0]
    code ends

    end start

    补全程序,使 jmp 指令执行后,CS: IP 指向程序的第一条指令。

  2. 用 Debug 查看内存,结果如下:

    1
    2000:1000 BE 00 06 00 00 00 ...

    则此时,CPU 执行指令:

    1
    2
    3
    mov ax,2000H
    mov es,ax
    jmp dword ptr es:[1000H]

    后, (CS) = 0060h, (IP) = 00BE

检测点9.2

补全程序,利用 jcxz 指令,实现在内存 2000H 段中查找第一个值为 0 的字节,找到后,将它的偏移地址存储在 dx 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
start: mov ax,2000H
mov ds,ax
mov bx,0

s: mov cl,[bx]
mov ch,0 ; 注意寻找的是字节
jcxz short 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 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assume cs:code
code segment
start: mov ax,2000H
mov ds,ax
mov bx,0
s: mov cl,[bx]
mov ch,0

inc cx ; loop指令,将 cx 加一在判定是否为0

inc bx
loop s
ok: dec bx
mov dx,bx
mov ax,4c00h
int 21h
code ends
end start

检测点10.1

补全程序,实现从内存 1000:0000 处开始执行指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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,1000
push ax
mov ax,0000
push ax
retf
code ends

end start

检测点10.2

下面的程序执行后,ax 中的数值为多少?
内存地址 | 机器码 | 汇编指令
—|—|—
1000:0 | b8 00 00 | mov ax,0
1000:3 | e8 01 00 | call s
1000:6 | 40 | inc ax
1000:8 | 58 | s:pop ax

0006h

检测点10.3

下面的程序执行后,ax 中的数值为多少?

1
2
3
4
5
6
7
8
9
内存地址 | 机器码   | 汇编指令
---|---|---
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

1010h

检测点10.4

下面的程序执行后,ax 中的数值为多少?

1
2
3
4
5
6
7
内存地址 | 机器码   | 汇编指令
---|---|---
1000:0 | b8 06 00 | mov ax,6
1000:3 | ff d0 | call ax
1000:5 | 40 | inc ax
1000:6 | | mov bp,sp
add ax,[bp]

000Bh

检测点10.5

  1. 下面的程序执行后,ax 中的数值为多少? (注意: 用 call 指令的原理来分析,不要在 Debug 中单步跟踪来验证你的结论。对于此程序,在Debug中单步跟踪的结果,不能代表 CPU 的实际执行结果。)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    assume cs:code
    stack segment
    dw 8 dup(0)
    stack segment
    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

0003h

  1. 下面的程序执行后,ax 和 bx 中的数值为多少?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    assume cs:code
    data segment
    dw 8 dup(0)
    data ends
    code segment
    start: mov ax,data
    mov ss,ax
    mov sp,16
    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
    code ends
    end start

ax = 0001h, bx = 0000h

检测点11.1

写出下面每条指令执行后,ZF,PF,SF等标志位的值。

1
2
3
4
5
6
7
sub al,al           ; ZF= 1 ,PF= 1  ,SF= 0
mov al,1 ; ZF= 1 ,PF= 1 ,SF= 0
push ax ; ZF= 1 ,PF= 1 ,SF= 0
pop bx ; ZF= 1 ,PF= 1 ,SF= 0
add al,bl ; ZF= 0 ,PF= 0 ,SF= 0
add al,10 ; ZF= 0 ,PF= 1 ,SF= 0
mul al ; ZF= 0 ,PF= 1 ,SF= 0

检测点11.2

写出下面每条指令执行后,ZF,PF,SF,CF,OF等标志位的值。

汇编代码 CF OF SF ZF PF
sub al,al 0 0 0 1 1
mov al,10H 0 0 0 1 1
add al,90H 0 0 1 0 0
mov al,80H 0 0 1 0 0
add al,80H 1 1 0 1 1
mov al,0FCH 1 1 0 1 1
add al,05H 1 0 0 0 0
mov al,7DH 1 0 0 0 0
add al,0BH 0 1 1 0 1

检测点11.3

  1. 补全下面的程序,统计 F:000:0 处 32 个字节中,大小在[32,128]的数据的个数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    mov ax,0f000h
    mov ds,ax

    mov bx,0
    mov dx,0
    mov cx,32
    s: mov al,[bx]
    cmp al,32
    jb s0
    cmp al,128
    ja s0
    inc dx
    s0: inc bx
    loop s
  2. 补全下面程序中,统计 F:000:0 处 32 个字节中,大小在 (32,128) 的数据的个数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    mov ax,0f000h
    mov ds,ax
    mov bx,0
    mov dx,0
    mov cx,32
    s: mov al,[bx]
    cmp al,32
    jna s0
    cmp al,128
    jnb s0
    inc dx
    s0: inc bx
    loop s

检测点11.4

下面的程序执行后: (ax) =?

1
2
3
4
5
6
7
8
9
mov ax,0
push ax
popf
mov ax,0fff0h
add ax,0010h
pushf
pop ax
and al,11000101B
and ah,00001000B

0045h

检测点12.1

  1. 用 Debug 查看内存,情况如下:

    1
    0000:0000 68 10 A7 00 8B 01 70 00-16 00 9D 03 8B 01 70 00

    则 3 号中断源对应的中断处理程序的入口地址为: 0070h:018Bh

  2. 存储 N 号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为: [4N]
    存储 N 号中断源对应的中断处理程序入口的段地址的内存单元的地址为: [4N+2]

检测点13.1

  1. 在上面的内容中,我们用 7ch 中断例程实现 loop 的功能,则上面的 7ch 中断例程所能进行的最大转移位移是多少?
    -128~127

  2. 用 7ch 中断例程完成 jmp near ptr s 指令的功能,用 bx 向中断例程传送转移位移。
    应用举例: 在屏幕的第 12 行,显示 data 段中以 0 结尾的字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    assume cs:code,ds:data
    data segment
    db 'conversation',0
    data ends

    code segment
    start: mov ax,cs
    mov ds,ax
    mov si,offset show
    mov ax,0
    mov es,ax
    mov di,200h
    mov cx,offset showend-offset show
    cld
    rep movsb

    mov ax,0
    mov es,ax
    mov word ptr es:[7ch*4],200h
    mov word ptr es:[7ch*4+2],0

    mov ax,data
    mov ds,ax
    mov si,0
    mov ax,0b800h
    mov es,ax
    mov di,12*160

    s: cmp byte ptr [si],0
    je ok
    mov al,[si]
    mov es:[di],al
    mov al,2
    mov es:[di+1],al
    inc si
    add di,2
    mov bx,offset s-offset ok
    int 7ch

    ok: mov ax,4c00h
    int 21h
    show: push bp
    mov bp,sp
    add [bp+2],bx
    pop bp
    iret
    showend:nop
    code ends
    end start

检测点13.2

  1. 错误,BIOS在ROM中 (READ ONLY) ,不能被更改
  2. 错误,19h中断,操作系统还未被引导。

检测点14.1

  1. 编程,读取 CMOS RAM 的 2 号单元的内容

    1
    2
    3
    mov al,2
    out 70h,al
    in al,71h
  2. 编程,向 CMOS RAM 的 2 号单元写入 0。

    1
    2
    3
    4
    mov al,0
    out 71h,al
    mov al,2
    in 70h,al

检测点14.2

编程,用加法和移位指令计算 (ax) = (ax) 10。 (提示, (ax) 10= (ax)2+(ax)8)

1
2
3
4
5
6
mov ax,2
shl ax,1
mov bx,ax
shl ax,1
shl ax,1
add ax,bx

检测点15.1

  1. 精简
    1
    2
    pushf
    call dword ptr ds:[0]
    2.
    在中断向量表中设置新的int9中断入口地址的时候不让其发生中断。
    1
    2
    3
    4
    cli
    mov word ptr es:[9*4],offset int9
    mov word ptr es:[9*4+2],cs
    sti

恢复中断向量表int9的源地址时同理:

1
2
3
4
5
6
cli
push ds:[0]
pop es:[9*4]
push ds:[2]
pop es:[9*4+2]
sti

检测点16.1

下面的程序将 code 段中 a 处的 8 个数据累加,结果存储到 b 处的双字中,补全程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
a dw 1,2,3,4,5,6,7,8
b dd 0

start: mov si,0
mov cx,8
s: mov ax,a[si]
add b,ax
abc b,0
add si,1
loop s

mov ax,4c00h
int 21h
code ends
end start

检测点16.2

下面的程序将 data 段中 a 处的 8 个数据累加,结果存储到 b 处的双字中,补全程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
assume cs:code,es:data

data segment
a db 1,2,3,4,5,6,7,8
b dw 0
data ends

code segment
start: mov ax,data
mov es,ax
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

检测点17.1

当执行int16中断时是从缓冲区中读出字符,若缓冲区为空,则int16应该可以响应int9的中断,故IF不一定为1