汇编语言(8086为例)

—-基于《汇编语言第三版》王爽

1.基础知识

  • 8086 CPU的简介

(1) 16位微处理器;

(2) 采用高速运算性能HMOS 工艺制造,芯片上集成了2.9万只晶体管;

(3) 使用单一的 +5V 电源,40 条引脚双列直插式封装;

(4) 时钟频率为 5MHz~10MHz ,基本指令执行时间为0.3ms~0.6ms

(5) 16根数据线和 20根地址线,可寻址的地址空间达 1MB (毕竟 16根地址线只可以寻址 64KB,太小了) ;

  • 冯诺依曼架构

冯·诺伊曼结构(英语:Von Neumann architecture),也称范·诺伊曼模型(Von Neumann model)或普林斯顿结构(Princeton architecture),是一种将程序指令存储器和数据存储器合并在一起的电脑设计概念结构。该结构包含:运算器、控制器、存储器、输入设备、输出设备。

结构特点:

  1. 以运算单元为中心
  2. 采用存储程序原理
  3. 存储器是按地址访问、线性编址的空间
  4. 控制流由指令流产生
  5. 指令由操作码和地址码组成
  6. 数据以二进制编码
  • 存储单元

1 KB=1024 B(字节)

1 字(word)=2 字节

1 B=8 bit

  • 总线

​ 总线包含:数据总线,地址总线,控制总线

地址总线:假设一个CPU有N根地址总线(即地址总线宽度为N),最多可以寻找2的N次方个内存单元。

数据总线:N根数据总线一次可以传输N位二进制数据。

​ 8088 CPU数据总线宽度为8;8086 CPU数据总线宽度为16。

控制总线:N根代表N种控制。

2.寄存器

​ 一个典型的CPU由:运算器,控制器,寄存器等器件构成。同时他们通过总线在内部相连接(地址,数据,控制)。对于汇编程序员来说,CPU的主要器件就是寄存器。

​ 8086寄存器特征:16位 (数据最大为2^16-1)

​ 8086 CPU的寄存器种类:

register of Intel 8086

  • AX
  • BX
  • CX
  • DX // 以上四个为通用寄存器,用于存放一般性数据
  • SI
  • DI
  • SP
  • BP
  • IP
  • CS
  • SS
  • 通用寄存器

AX,BX,CX,DX四个寄存器通常用来存放一般性数据,因此内成为通用寄存器

8086的上一代CPU的寄存器是8位的,为了兼容性,这四个寄存器也可以当丞8个8位寄存器使用。

  • AX—-AH,AL

  • BX—-BH,BL

  • CX—-DH,DL

  • DX—-DH,DL (L和H指的是low和high,即低位和高位)

  • 8086 CPU的物理地址给出方法

物理地址=段地址*16+偏移地址 (段地址*16 可以理解为段地址向左移四位)

一个物理地址可以可以有不同的段地址和偏移地址

偏移地址为16位,寻址能力为2^16即 64 KB,所以一个段的长度不能超过 64 KB

  • 段寄存器

8086 CPU 的段寄存器:

  • CS
  • DS
  • SS
  • ES
  • CS 和 IP

​ 对于8086 CPU来说,CS和IP一起指示了当前要读取指令的地址:CS :IP ,执行该位置的语句后IP会自动增加,从而可以进行下一指令的执行。

  • CS 和 IP 寄存器的修改

  • *

    • 情况一:当你在写汇编脚本或者直接向内存中写入汇编指令时
    1
    2
    3
    4
    5
    6
    7

    使用 JMP 指令
    使用方法: JMP 段地址:偏移地址
    eg: JMP 2AE3:3 //CS=2AE3,IP= 0003(均为16进制)
    或者(不想改变段地址时)
    JMP ax, # 修改IP为 AX的值

    • 情况二: 在debug.exe(MASM32)中想要修改当前程序运行位置时(后面将会介绍)
    1
    2
    3
    4
    5
    例如在 dosBox中:
    debug.exe #进入debug模式
    -r CS
    CS 073f
    :XXXX(输入你需要位置) # IP寄存器同理,通用寄存器也同上
  • 代码段

​ 即定义一片区域用于输入需要执行的代码。

  • debug的使用
1
2
3
4
5
6
7
8
9
10
11
-r              #显示CPU各个寄存器的值
-r 寄存器名字 #修改寄存器的值

-d #查看当前地址的内容
-d 段地址:偏移地址 #查看该段的地址

-a #从当前位置开始写入汇编指令
-a 段地址:偏移地址 #从该位置开始写入汇编指令

-e #从当前位置开始写入机器码
-e 段地址:偏移地址 #从该位置开始写入机器码

3.寄存器(内存访问)

  • 字型数据

​ CPU用16位寄存器存在出一个字。高八位存放高位字节,低八位存放低位字节。

  • DS和[address]

物理地址=段地址*16+偏移地址 (段地址16 可以理解为段地址向左移四位),因此如果想要读写某个地址,首先应该知道其段地址和偏移地址,8086 CPU通过寄存器DS来获取段地址,[N]来表示偏移地址,*[ … ]用来表示一个内存单元()**。

注意:8086CPU 不支持将数据直接送入段寄存器。

  • 字型数据的传送

​ eg:

1
2
3
mov bx,1000H
mov ds,bx
mov ax,[0]

​ 使用8086 CPU编程时,可以将一段内存当做栈来使用。提供入栈和出栈指令,即PUSH 和 POP

任意时刻,SS:SP 指向栈顶元素

同时,可以通过设置 SS,SP 寄存器的数据来修改栈的容量,地址。

​ push pop指令

1
2
push [0]         ;将 DS :0 处的字压入栈
pop [2] ;出栈,将数据送入 DS:0处

4.第一个程序

  • 源程序(后缀 .asm)的编写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
;1.asm
assume cs:codesg ;将用作代码段的段codesg和段寄存器cs联系起来。

codesg segment ;定义一个段,段的名称为“codesg”,这个段从此开始
;codesg是一个标号,作为一个段的名称,最终被编译连接成一个段的段地址

mov ax, 0123H
mov bx, 0456H
add ax, bx
add ax, ax

mov ax, 4c00H
int 21H ;这两条指令实现程序的返回 ,相当于 return 0

codesg ends ;名称为“codesg”的段到此结束

end ;编译器在编译汇编程序的过程中,碰到了伪指令end,结束对源程序的编译

5.[bx]和loop指令

bx

​ [bx]也表示一个内存单元,它的偏移地址在bx中

loop

​ 通用寄存器CX存储循环次数,循环一次:(cx)= (cx)- 1;

案例程序:

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

code segment
mov ax, 2

mov cx, 11 ;循环次数
s: add ax, ax
loop s ;在汇编语言中,标号代表一个地址,标号s实际上标识了一个地址,
;这个地址处有一条指令:add ax,ax。
;执行loop s时,首先要将(cx)减1,然后若(cx)不为0,则向前
;转至s处执行add ax,ax。所以,可以利用cx来控制add ax,ax的执行次数。

mov ax,4c00h
int 21h
code ends
end

  1. 实现将内存ffff:0 ~ ffff:b单元中的数据复制到0:200 ~ 0:20b单元中。
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 

code segment
mov ax, 0ffffh
mov ds, ax ;(ds)= 0ffffh
mov ax, 0020h
mov es, ax ;(es)= 0020h 0:200 等效于 0020:0
mov bx, 0 ;(bx)= 0,此时ds:bx指向ffff:0,es:bx指向0020:0

mov cx,12 ;(cx)=12,循环12次


s: mov dl,[bx] ;(d1)=((ds)* 16+(bx)),将ffff:bx中的字节数据送入dl
mov es:[bx],dl ;((es)*16+(bx))=(d1),将dl中的数据送入0020:bx
inc bx ;(bx)=(bx)+1
loop s

mov ax,4c00h
int 21h
code ends
end

6.包含多个段的程序

    1. 在代码中使用数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
;计算 8 个数据的和存到 ax 寄存器
assume cs:code

code segment

dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h ;define word 定义8个字形数据

start: mov bx, 0 ;标号start
mov ax, 0

mov cx, 8
s: add ax, cs:[bx]
add bx, 2
loop s

mov ax, 4c00h
int 21h
code ends
end start ;end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方
;用end指令指明了程序的入口在标号start处,也就是说,“mov bx,0”是程序的第一条指令。

  1. 在代码中使用栈
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
;利用栈,将程序中定义的数据逆序存放。
assume cs:codesg

codesg segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h ; 0-15单元
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; 16-47单元作为栈使用

start: mov ax, cs
mov ss, ax
mov sp, 30h ;将设置栈顶ss:sp指向栈底cs:30。 30h = 48d
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 ;指明程序的入口在start处

  1. 将数据、代码、栈放入不同的段
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
assume cs:code,ds:data,ss:stack 

data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h ;0-15单元
data ends

stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;0-31单元
stack ends

code segment
start: mov ax, stack;将名称为“stack”的段的段地址送入ax
mov ss, ax
mov sp, 20h ;设置栈顶ss:sp指向stack:20。 20h = 32d

mov ax, data ;将名称为“data”的段的段地址送入ax
mov ds, ax ;ds指向data段

mov bx, 0 ;ds:bx指向data段中的第一个单元

mov cx, 8
s: push [bx]
add bx, 2
loop s ;以上将data段中的0~15单元中的8个字型数据依次入栈

mov bx, 0

mov cx, 8
s0: pop [bx]
add bx, 2
loop s0 ;以上依次出栈8个字型数据到data段的0~15单元中

mov ax, 4c00h
int 21h
code ends
end start
;“end start”说明了程序的入口,这个入口将被写入可执行文件的描述信息,
;可执行文件中的程序被加载入内存后,CPU的CS:IP被设置指向这个入口,从而开始执行程序中的第一条指令

7.更灵活灵活的定位内存地址的方法

* **and 和 or 指令**
1
2
3
4
5
mov al , 01100011B
and al , 00111011B ;按位与,执行后:al=00100011B

mov al, 01100011B
or al, 00111011B ;按位或,执行后:al=01111011B
  • ASCII
  1. 以字符的形式给出数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
assume cs:code,ds:data 

data segment
db 'unIx' ;相当于“db 75H,6EH,49H,58H”
db 'foRK'
data ends

code segment
start: mov al, 'a' ;相当于“mov al, 61H”,“a”的ASCI码为61H;
mov b1, 'b'

mov ax, 4c00h
int 21h
code ends
end start
  1. 大小写转换

小写字母的ASCII码值比大写字母的ASCII码值大20H

大写字母ASCII码的第5位为0,小写字母的第5位为1(其他一致)

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
assume cs:codesg,ds:datasg 

datasg segment
db 'BaSiC'
db 'iNfOrMaTion'
datasg end

codesg segment
start: mov ax, datasg
mov ds, ax ;设置ds 指向 datasg段

mov bx, 0 ;设置(bx)=0,ds:bx指向’BaSic’的第一个字母

mov cx, 5 ;设置循环次数5,因为’Basic'有5个字母
s: mov al, [bx] ;将ASCII码从ds:bx所指向的单元中取出
and al, 11011111B;将al中的ASCII码的第5位置为0,变为大写字母
mov [bx], al ;将转变后的ASCII码写回原单元
inc bx ;(bx)加1,ds:bx指向下一个字母
loop s

mov bx, 5 ;设置(bx)=5,ds:bx指向,iNfOrMaTion'的第一个字母

mov cx, 11 ;设置循环次数11,因为‘iNfOrMaTion'有11个字母
s0: mov al, [bx]
or al, 00100000B;将a1中的ASCII码的第5位置为1,变为小写字母
mov [bx], al
inc bx
loop s0

mov ax, 4c00h
int 21h
codesg ends

  1. SI 和 DI

​ SI 和 DI 是8086 CPU中和 bx 功能相近的寄存器,但是 SI 和 DI 不能分成连个该8为寄存器使用。

idata : 立即数,直接的数据

1
2
3
4
mov ax, 1                 ;对于直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中)
add bx, 2000h ;在汇编语言中称为:立即数(idata)
or bx, 00010000b
mov al, 'a'
  1. 处理数据的长度

8086CPU的指令,可以处理两种尺寸的数据,byte和word(字节和字)。

通过寄存器名指明要处理的数据的尺寸。
例如: mov al, ds:[0] 寄存器al指明了数据为1字节

在没有寄存器名存在的情况下,用操作符X ptr指明内存单元的长度,X在汇编指令中可以为word或byte。
例如:mov byte ptr ds:[0], 1 byte ptr 指明了指令访问的内存单元是一个字节单元

有些指令默认了访问的是字单元还是字节单元
例如,push [1000H],push 指令只进行操作。

  1. div指令、dd、dup、mul指令

div :除法指令。

除数:有8位和16位两种,在一个寄存器或内存单元中。

被除数:默认放在AX或DX和AX中,
如果除数为8位,被除数则为16位,默认在AX中存放;
如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位。

结果:
如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数;
如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数。

1
2
3
4
5
6
7
8
9
10
11
;利用除法指令计算100001/100。
;100001D = 186A1H
mov dx, 1
mov ax, 86A1H ;(dx)*10000H+(ax)=100001
mov bx, 100
div bx

;利用除法指令计算1001/100
mov ax, 1001
mov bl, 100
div b1

dd:

  • db 和 dw 定义字节型数据和字型数据。

  • dd 是用来定义dword(double word,双字(四个字节))型数据的伪指令

1
2
3
4
5
6
dd 100001
dw 100
dw 0



操作符:

与db、dw、dd等数据定义伪指令配合使用,用来进行数据的重复

1
2
3
4
db 3 dup (0)       ;定义了3个字节,它们的值都是0,相当于db 0,0,0。
db 3 dup (0, 1, 2) ;定义了9个字节,它们是0、1、2、0、1、2、0、1、2,相当于db 0,1,2,0,1,2,0,1,2。
db 3 dup ('abc', 'ABC') ;定义了18个字节,它们是abcABCabcABCabcABCC,相当于db 'abc', 'ABC' ,'abc' , 'ABC, 'abc', 'ABC'。

mul 指令 :

  • mul是乘法指令,使用 mul 做乘法的时候:相乘的两个数:要么都是8位,要么都是16位。

  • 乘数:

      1. 8 位: AL中和 8位寄存器内存字节单元中;
      2. 16 位: AX中和 16 位寄存器内存字单元中。
  • 结果:

    • 8位:AX中;
    • 16位:DX(高位)和 AX(低位)中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
; 8 位
;计算100*10
;100和10小于255,可以做8位乘法
mov al,100
mov bl,10
mul bl

;结果: (ax)=1000(03E8H)

; 16 位置

;计算100*10000
;100小于255,可10000大于255,所以必须做16位乘法,程序如下:
mov ax,100
mov bx,10000
mul bx

;结果: (ax)=4240H,(dx)=000FH (F4240H=1000000)


8.数据处理的两个基本问题

寻址方式

只有四个寄存器可以通过 [ … ] 进行内存单元的寻址

  • 以下代码为错误代码
1
2
3
4
mov ax,[cx]
mov ax,[dx]
mov ax,[ax]
mov ax,[ds]
  • 在 [ … ] 中四个寄存器可以单独出现,或只能以四种组合出现
  1. bx 和 si
  2. bx 和 di
  3. bp 和 si
  4. bp 和 di
1
2
3
4
5
6
7
8
9
mov ax,[bx+si]
mov ax,[bx+di]
mov ax,[bp+si]
mov ax,[bp+di]
;也可以加上data
mov ax,[bx+si+idata
mov ax,[bx+di+1]
mov ax,[bp+si+2
mov ax,[bp+di+3

处理的数据的大小

  • 一般是字或者字节

在没有寄存器名的情况下可以使用 X ptr指明内存单元的长度

1
2
3
4
5
6
7
mov word ptr ds:[0],1
inc word ptr []bx
add word ptr [bx],2

mov byte ptr ds:[0],1
inc byte ptr []bx
add byte ptr [bx],2

9.转移指令

可以修改IP,或同时修改CS和IP的指令统称为转移指令。概括地讲,转移指令就是可以控制CPU执行内存中某处代码的指令。

1
2
jmp ax        ; 段内转移
jmp 1000:0 ; 段间转移

offset

操作符offset在编语言中是由编译器处理的符号,它的功能是取得标号的偏移地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
                  ;将s处的一条指令复制到s0处
assume cs:codesg
codesg segment
s: mov ax, bx ;(mov ax,bx 的机器码占两个字节)
mov si, offset s ;获得标号s的偏移地址
mov di, offset s0 ;获得标号s0的偏移地址

mov ax, cs:[si]
mov cs:[di], ax
s0: nop ;(nop的机器码占一个字节)
nop
codesg ends
ends

jmp指令

jmp 为无条件转移,转到标号处执行指令可以只修改IP,也可以同时修改CS和IP;

jmp 指令要给出两种信息:

  1. 转移的目的地址

  2. 转移的距离(段间转移、段内短转移,段内近转移)

    jmp short 标号 jmp near ptr 标号 jcxz 标号 loop 标号 等几种汇编指令,它们对 IP的修改是根据转移目的地址和转移起始地址之间的位移来进行的。在它们对应的机器码中不包含转移的目的地址,而包含的是到目的地址的位移距离。

  1. jmp short 标号
  • 实现段内短转移修改范围(-128-127)
1
2
3
4
5
6
7
8
9
10
assume cs:code
code segment
start: mov ax,9527h
jmp short s ; 跳转到
add ax,1111h
s: mov ax,2222h ; 这里
mov ax 4c00h
int 21h
code ends
end start
  1. jmp near ptr
  • 实现段内近转移(修改范围大于(-128-127)但是小于-32768~32767,编译成功但链接失败),这是jmp short 实现不了的。
1
2
3
4
5
6
7
8
9
10
assume cs:code
code segment
start: mov ax,9527h
jmp near ptr s ; 跳转到
db 128 dup(0) ; 制造距离超过127
s: mov ax,2222h ; 这里
mov ax 4c00h
int 21h
code ends
end start
  1. jmp far ptr
  • 实现段间转移(修改范围超过-32768~32767)
1
2
3
4
5
6
7
8
9
10
assume cs:code
code segment
start: mov ax,9527h
jmp near ptr s ; 跳转到
db 32768 dup(0) ; 制造距离超过32767
s: mov ax,2222h ; 这里
mov ax 4c00h
int 21h
code ends
end start
  1. 转移地址在寄存器或内存中的jmp指令
  • jmp 寄存器,直接修改ip为寄存器的值
  • jmp word ptr 内存单元
1
2
3
mov ax, 0123H
mov ds:[0], ax
jmp word ptr ds:[0] ;执行后,(IP)=0123H

jxcz 标号

如果(cx)=0,转移到标号处执行;若(cx) != 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
assume cs:code,ds:data,ss:stack


data segment
db 0,0,0
data ends


stack segment

dw 16 dup(0)

stack ends


code segment
start: mov ax,2000H
mov ds,ax
mov bx,0

s: mov ch,0
mov cl,ds:[bx]
jcxz ok ;若(cx)==0,跳转至ok标号处执行
inc bx ;继续循环,直至查找到第一个值为0的字节
jmp short s

ok: mov dx,bx
mov ax,4c00H
int 21H
code ends

end start

10.call和ret指令

  • ret和retf
  • ret指令用栈中的数据,修改IP的内容,从而实现近转移

对于ret:相当于一次 pop,把数据放在ip里

  • retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移

对于ret:相当于两次 pop,第一次数据放在ip里,第二次放cs里

  • call指令

类似于jmp,但是多了一个功能:将当前的 IP 或 CS和IP 压入栈中,此外,call没有短转移

  • call和ret的连招
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
assume cs:code
code segment
start: mov ax,1
mov cx,3
call s ;(1)CPU指令缓冲器存放call指令,IP指向下一条指令(mov bx, ax),执行call指令,IP入栈,jmp

mov bx,ax;(4)IP重新指向这里 bx = 8


mov ax,4c00h
int 21h


s: add ax,ax
loop s;(2)循环3次ax = 8
ret;(3)return : pop IP
code ends
end start

10.错题

一、下列关于8086CPU的工作原理的描述错误的是__3__。
1、 汇编程序员可以通过对各种寄存器中内容的修改实现对CPU的控制。
2、 CPU在访问内存时,采用“段地址*16+偏移地址”的形式给出要访问的内存单元的物理地址。
3、 任意时刻,CS:IP指向的内容即是此刻CPU正在执行的指令。
4、 传送指令能够更改所有通用寄存器的内容。

注解:

 1、正确,见课本P13
 2、正确 
 3、错误。课本P24,8086机中,任意时刻,CPU将CS:IP指向的内容当作指令执行。此处的含义是**将要执行**,比如输入T,CPU读取CS:IP的内容,载入指令执行,而不是正在执行。
 4、通用寄存器是AX、BX、CX、DX,传送指令是mov,mov指令可以改变4个通用寄存器的内容

六、下列说法中正确的是:4
1、 8086CPU采用“段地址*16+偏移地址=物理地址”的寻址模式,所以内存是由一个一个的段组成,每一个段有一个段地址。
2、 物理地址为ffff0H的内存单元的偏移地址可能为ffffH。
3、 一个段的大小可以是8Byte。
4、 在一段没有任何跳转指令的程序中,超出64K的部分将不会被执行。

注解:

 1、第一句、第三句正确,第二句错误。内存没有分段,段的划分来自于CPU.见课本P22
 2、错误,不可能,若偏移地址为FFFFH,则段地址为EFFF1H,是不对的
 3、段大小最小16B(Byte)
 4、正确,CPU执行指令取决于CS;IP,IP最大65535,超过64K的IP不识别,CPU也就无法执行。

五、16位结构的CPU 不一定具备的特性是:4
1、 运算器一次最多可以处理16位的数据
2、 寄存器的最大宽度为16位
3、 寄存器和运算器之间的通路为16位
4、 地址总线为16根

注解:见课本P18
16位CPU特征:运算器一次最多可以处理16位的数据
寄存器的最大宽度为16位
寄存器和运算器之间的通路为16位
8086CPU有20根地址总线,可以传送20位地址。故选择4

5.若将以1000H为段地址的整个段空间当作栈使用,那么寄存器SP的初始值最合理的设置是_____。 (1)
1、 0000H
2、 0001H
3、 FFFFH
4、 FFFEH

注解:以1000H为段地址的整个段空间,偏移地址为0-FFFFH,当栈中只有1个字时,偏移地址是FFFEH,当栈为空时,SP=SP+2=FFFF+2=0000,所以选择1

下列指令的书写正确的是: 3 。
1、 push al
2、 mov cs:[0],ds:[10]
3、 pop si
4、 mov ds:[0],2

解析:答案1错误,压堆指令一次性操作一个字;答案2错误,内存空间之间不能互相传送;答案3正确;答案4错误,不能将立即数送入内存空间