前言

本篇文章是对王爽老师的《汇编语言(第三版)》所做的笔记与总结,格式比较随意,权当复习参考之用

基础

编译器:将汇编语言转换为机器语言

汇编语言的组成

  • 汇编指令:机器码的助记符,有对应的机器码
  • 伪指令:没有对应的机器码,由编译器执行
  • 其他符号:如 +,-,*,/等,由编译器识别,没有对应的机器码

指令和数据:一段二进制信息,可以被看做数据或者看做程序

CPU 读写数据:CPU 读写数据时,与外部设备交互存储单元的地址 (地址信息),器件信息,读或写的命令 (控制信息),读或写的数据 (数据信息)

总线:CPU 与其他芯片的导线,分为地址总线,控制总线,数据总线

  • 地址总线

    地址总线宽度为 N,则 CPU 寻找的范围为 2N个内存单元

  • 数据总线

    数据总线的宽度决定了数据传输的速度

  • 控制总线

    控制总线的宽度决定了 CPU 对外部设备的控制能力,其中有一根读信号输出线,低电平表示将要读取数据,写信号输出线负责传输写信号

存储器芯片

  • 随机存储器 (RAM):用于存放 CPU 使用绝大部分程序和数据,分为主板上的 RAM 和插在扩展插槽上的 RAM
  • 装有 BOIS 的 ROM(装有基本输入输出系统的只读存储器):可以通过各类主板和接口卡上的 BOIS 使用该设备进行基本的输入输出
  • 接口卡上的 RAM:接口卡对大批量输入输出数据进行暂时存储,用于与设备进行输入输出

内存地址空间:CPU 地址总线可以寻到的所有存储单元组成 CPU 的内存地址空间,对于各类接口卡,CPU 通过总线控制它们,将它们看做一个由多个存储单元组成的逻辑存储器

寄存器

通过改变各种寄存器中的内容来实现对 CPU 的控制

通用寄存器:用于存放一般性数据

对于一个 16 位寄存器来说,存储的数据可以看做 16 位二进制,也可以看做一个字

字分为高位字节和低位字节,如 ax 分为 ah,al,两个字节为 8 位,可以将它们看做单独的两个寄存器,做运算时产生的进位不影响其他位

16 位机的含义:位数描述了 CPU 的几个结构特性

  • 运算器最多一次可以处理 16 为的数据
  • 寄存器的最大宽度为 16 位
  • 寄存器和运算器之间的通路为 16 位

CPU 给出物理地址的方法

CPU 在地址总线上传输的是内存单元的物理地址,CPU 首先需要在内部生成这个物理地址,CPU 给出物理地址的方法可以概括为基础地址 + 偏移地址=物理地址,基础地址 (段地址) 由段寄存器提供 (8086CPU 有 4 个段寄存器:CS,DS,SS,ES)

流程:CS,IP 中的段地址和偏移地址传入到地址加法器,将物理地址传入输入输出控制电路,从内存中读取指令,传回指令寄存器,执行指令,CPU 只认为 CS 和 IP 所指的地址为指令

修改 CS 和 IP 的指令:jmp 段地址:偏移地址该指令只能使用在调试模式

仅改变 IP 的指令:jmp ax

内存访问

内存中字的存储

字单元:存放一个字形数据 (16 位) 的内存单元,由两个地址连续的内存单元组成,高地址单元存放高位字节,低地址单元存放低位字节

DS 和 [address]

8086CPU 有一个 DS 寄存器,用于存放要访问数据的段地址

mov ax [0]:将 DS 中的段地址 + 中括号里的偏移地址所指定的内存单元传入 ax

设置 DS 中的地址:需要将地址传入其他寄存器,再从其他寄存器传入 DS,mov ds ax

mov,add,sub 指令

mov 指令

  • mov register data
  • mov register1 register2
  • mov register memory,内存单元用中括号包围
  • mov memory register
  • mov segment-register register

add 指令

  • add register data
  • add register1 register2
  • add register memory
  • add memory register

sub 指令

  • sub register data
  • sub register1 register2
  • sub register memory
  • sub memory register

8086CPU 提供出栈和入栈的指令:push/pop

出栈和入栈的单位为字

8086CPU 中的两个寄存器 SS(段寄存器) 和 SP 存储栈顶元素的地址,入栈时,栈顶从高地址向低地址增长

SS:SP 初始时指向栈空间最高地址 (栈底) 的下一个单元

SS:SP 不会记忆栈空间的大小,只知道当前执行的地址,需要注意操作时不要越界

创建栈 (创建 10000 到 1000f 为栈空间)

1
2
3
mov ax,1000
mov ss,ax
mov sp,0010

在 debug 模式调用 t 指令时,执行 mov ss,ax 后会继续执行 mov sp,0010

push 指令

  • push register
  • push segment-register
  • push memory,注意栈操作以字为单位 (16 位,两个 8 位字节)

pop 指令

  • pop register
  • pop segment-register
  • pop memory

Debug 模式

  • r 指令:查看当前寄存器状态,r register,修改寄存器中的值
  • d 指令:查看指定内存的内容,显示为十六进制
    • d 1000:0 f,显示 1000:0000 到 1000:000f 的内容
    • d cs:0,查看当前代码段中的内容
  • e 指令:改写内存中的内容
    • e 1000:0 data1 data2 ...,从 1000:0000 开始写入数据
    • e 1000:0,以提问形式逐个单元写入十六进制
  • u 指令:将内存中的机器码翻译为汇编语言,u 1000:0,查看 1000:0000 开始的汇编代码
  • a 指令:在内存中写入汇编代码,a 1000:0,从 1000:0000 开始写入汇编代码
  • t 指令:执行当前 CS:IP 指向的内存地址中的代码

程序编写

  1. 编写汇编代码源文件
  2. 对源文件进行编译和链接,产生机器码,存储在可执行文件
  3. 执行可执行文件

伪指令

伪指令没有对应的机器指令,由编译器执行,编译器根据伪指令来进行编译

segment-ends 用于标注一个段,格式为

1
2
3
code_name segment
...
code_name ends

end 标识整个程序的结束,在 end 处结束编译

assume 表示将某一段寄存器与程序中的某一个段相关联,assume segment_register:code_name

程序返回:mov ax,4c00h int 21h汇编中十六进制数字后面加 h,数据不能以字母开头,需要在前面加 0

程序执行过程

汇编程序从编写到执行的过程

编写 (1.asm)——编译 masm(1.obj)——连接 link(1.exe)——加载——内存中的程序——运行 (CPU)

调试程序指令:debug 1.exe

调试时使用 p 命令执行 int 21h,q 命令退出 debug 模式

[bx] 和 loop 指令

[bx]

[bx] 表示 bx 中的偏移地址,与 DS 的段地址组成物理地址

inc bx:将 bx 的内容 +1

dec bx:将 bx 的内容 -1

编译器和 debug 模式对 [idata] 的执行情况不同

  • debug 模式将 [idata] 解释为用常量表示偏移地址
  • 编译器将 [idata] 解释为数字常量,在 [idata] 前加段地址 mov ax,ds:[0] 即可表示偏移地址
  • 两个模式对 [bx] 的解释相同

loop

标号:s: add ax,ax,标号标识了一个地址,该地址处有一条指令

使用 loop:loop s,将 cx 作为循环计数器 (loop 指令默认 cx 为计数器),执行的代码段放在标号和 loop 之间,loop 放在标号之后,注意在 loop 之前,代码已经执行过一次,相当于 do-while

  1. (cx)=(cx)-1
  2. 判断 cx 的值,若不为 0,则跳转到标号处,否则跳出循环

debug 模式跳转到指定位置执行:g 偏移地址(ip)

自动结束循环:p,在 loop 指令处调用

多个段的程序

定义字型数据:dw 0123h,0456h,系统在该指令地址处分配空间存放数据

定义字节型数据:db 'abc'

在程序前定义数据会出现程序入口不确定的问题

使用标号标注程序的入口,end start 表示结束 start 处的程序

定义多个段分别存放数据、代码、栈

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:code,ds:data,ss:stack
data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20h
mov ax,data
mov ds,ax
mov bx,0
mov cx,8

s: push [bx]
add bx,2
loop s
mov bx,0
mov cx,8

s0: pop [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
code ends
end start

定位内存地址

and 和 or 指令

1
2
and al,00111011b
or al,00111011b

将输入值与寄存器中的值进行按位与/或运算,存储在第一个寄存器参数中

字符串处理

字符和字符串以 ASCII 码的形式存储在寄存器中,数据段中使用 db 定义字符串空间

大小写转换:大写字母的二进制第 5 位为 0(从 0 开始),小写字母的二进制第 5 位为 1,运用 and 和 or 指令将字母的二进制第 5 位设置为 0 或 1,即可转换大小写

[bx+idata]

可以使用 bx 中的地址加上一个偏移量表示一个内存单元,物理地址为 (ds)*16+(bx)+idata

其他格式:[200+bx],200[bx],[bx].200

实现数组操作:idata[bx],ds 为起始段地址,idata 为数组相对于 ds 的起始地址,bx 作为索引

si,di 寄存器:si 和 di 为 8086CPU 中与 bx 功能相近的寄存器,它们不能分为两个 8 位寄存器
{: .prompt-info }

其他偏移格式

  • [bx+si] 表示一个内存单元,物理地址为 (ds)*16+(bx)+(si)

    其他格式:[bx][si]

  • [bx+si+idata],物理地址为 (ds)*16+(bx)+(si)+idata

    其他格式:[bx+si+200],[200+bx+si],200[bx][si],[bx].200[si],[bx][si].200

当程序中多个数据 (如多层循环计数器) 需要暂存时,应该使用栈

数据处理

寄存器类型

reg:ax,bx,cx,dx,ah,al,bh,bl,ch,cl,dh,dl,sp,bp,si,di

sreg:ds,ss,cs,es

bx,si,bp,di 可用于偏移地址寻址,bx 和 bp 不能同时在一个中括号中寻址,si 和 di 同理

数据的表达

数据位置的表达

  • 立即数

    在汇编指令中直接给出的数据,包括数字和字符串,执行前数据存放在 CPU 内部的指令缓冲器

  • 寄存器

    使用寄存器中存储的数据,执行前数据存储在 CPU 内部的寄存器

  • 段地址: 偏移地址 (SA:EA)

    使用 SA:EA 索引内存中的数据,执行前数据存储在内存中相应的内存单元中

    SA 默认为 ds 中的地址

数据尺寸的表达

8086CPU 可以处理两种尺寸的数据:byte,word,byte 为 8 位,word 为 16 位

  • 通过寄存器名指明操作的数据尺寸

    通过 ax,bx 等表明操作的是 word,通过 ah,al 等表明操作的是 byte

  • 使用操作符 X ptr 指明数据尺寸

    1
    2
    mov word ptr ds:[0],1	;按字操作
    mov byte ptr ds:[0],1 ;按字节操作
  • 操作默认的尺寸

    栈操作 push,pop 默认按 word 操作

其他指令

div 指令

用于做除法操作

  • 除数:除数为 8 位或 16 位,存储在内存或寄存器中
  • 被除数:若除数为 8 位,则被除数为 16 位,默认存放在 ax 中,若除数为 16 位,则被除数为 32 位,默认存放在 dx 和 ax 中,dx 存放高 16 位,ax 存放低 16 位
  • 结果:若除数为 8 位,则结果存放在 ax 中,al 存储商,ah 存储余数,若除数为 16 位,则结果存放在 dx 和 ax 中,dx 存储余数,ax 存储商
1
div reg/memory

提前在 ax 和 dx 中设置被除数,div 指令传入除数所在的寄存器或内存单元

伪指令 dd

dd 用于定义双字型数据,占两个 word 的大小 (32 位)

dup 操作符

dup 操作符同 db,dw,dd 等指令配合使用,用于数据重复

1
2
db 3 dup (0)		;定义了3个字节,内容都为0
db 3 dup (0,1,2) ;定义了9个字节,内容重复0,1,2

转移指令

可以修改 IP 或同时修改 CS 和 IP 的指令称为转移指令

  • 只修改 IP 时,称为段内转移
  • 同时修改 cs 和 ip,称为段间转移

转移种类:无条件转移,条件转移,循环指令,过程,中断

操作符 offset

offset 由编译器处理,用于取得标号的偏移地址

1
mov ax,offset s

jmp 指令

jmp 为无条件跳转指令,可只修改 IP 或同时修改 cs:ip

1
jmp short label

无条件跳转到标号处执行指令,实现段内短位移,转移的偏移量为 8 位 (-128~127)

CPU 在执行 jmp 指令时不需要知道转移的目的地址,该指令的机器码中包含了目的地址到 jmp 指令的下一条指令的偏移量

1
jmp near ptr label

该指令表示段内近转移,偏移量为 16 位 (-32768~32767)

1
jmp far ptr label

表示段间转移,机器码中包含标号所在的 cs 和 ip

1
jmp reg

reg 为 16 位寄存器,修改 ip 为寄存器中的地址

转移地址在内存中的 jmp 指令

  • jmp word ptr addr:指定一个内存单元地址,该内存单元存放一个字大小的目的偏移地址,实现段内转移
  • jmp dword ptr addr:指定一个内存单元地址,该内存单元存放两个字大小的目的偏移地址,实现段间转移,高位的字作为 cs,低位的字作为 ip

jcxz 指令

jcxz 为条件跳转指令,条件跳转指令都是短转移,机器码中包含偏移量 (同 jmp 指令)

1
jcxz label

当 cx=0 时,跳转到指定标号处,否则向下执行

call 和 ret 指令

call 和 ret 都是转移指令,他们都修改 ip 或同时修改 cs 和 ip,可用于子程序调用和返回

ret,retf

  • ret 指令用栈中的数据,修改 ip,实现近转移
  • retf 用栈中的数据,修改 cs 和 ip,实现远转移,先从栈中弹出 ip 再弹出 cs

两个指令使用栈中数据时会同时弹出栈元素

call

1
call label

执行 call 指令时,CPU 将当前的 ip 压入栈中,之后转移到标号处,实现段内近转移

call 指令转移通过偏移量转移类似 jmp short label

1
call far ptr label

该指令执行时,CPU 先压入当前 cs 再压入 ip,之后转移到标号处,实现段间转移

机器码中包含目的 cs 和 ip,类似 jmp far ptr label

1
call reg(16bit)

CPU 先压入 IP,再执行 jmp reg 跳转

转移地址在内存中的 call 指令

  • call word ptr memory:先压入当前 ip,再执行 jmp word ptr memory 跳转
  • call dword ptr memort:先压入 cs,再压入 ip,执行 jmp dword ptr memory 跳转

子程序设计

1
2
3
4
5
6
7
8
9
10
11
12
assume cs:code
code segment
main: statements
call s
statements
mov ax,4c00h
int 21h

s: statements
ret
code ends
end main

mul 指令

用于乘法,两个操作数应同时为 8 位或 16 位

同时为 8 位时,一个默认放在 al 中,一个放在 8 位寄存器或内存单元中,结果默认存放在 ax 中

同时为 16 位时,一个默认存放在 ax,一个放在 16 位寄存器或内存单元中,结果高位存放在 dx 中,低位存放在 ax 中


参数和结果传递

可以通过寄存器传递参数和返回值

对于多个参数,可以将数据存储在内存中,在子程序中通过数据首地址访问

当主程序寄存器和子程序寄存器冲突时,可以使用栈保存冲突的寄存器内容,返回时弹出内容

标志寄存器

标志寄存器用于存储相关指令的某些执行结果,为 CPU 执行相关指令提供行为依据,控制 CPU 的工作方式

标志寄存器中的信息被称为程序状态字

add,sub,mul,div,inc,or,and 等运算影响标志位

mov,push,pop 等传送指令不影响标志位

  • ZF 标志:零标志位,若执行结果为 0,则 ZF=1,否则为 0
  • PF 标志:奇偶标志位,若执行结果每一 bit 中 1 的个数为偶数,则 PF=1,若为奇数,则 PF=0
  • SF 标志:符号标志位,若执行结果为负数,则 SF=1,否则为 0
  • CF 标志:进位标志位,记录了无符号数运算低位向高位的进位值或借位值
  • OF 标志:溢出标志位,若有符号数运算发生溢出,则 OF=1,否则为 0

adc 指令

adc 指令为带进位加法指令,它利用了 CF 位的进位值

1
adc obj1,obj2	;obj1=obj1+obj2+CF

sbb 指令

sbb 指令为带借位减法指令

1
sbb obj1,obj2	;obj1=obj1-obj2-CF

cmp 指令

cmp 为比较指令,对标志寄存器产生影响从而影响其他指令的判断结果,立即数必须放在第二个操作数

1
cmp obj1,obj2

执行 obj1-obj2,根据结果设置标志寄存器

对无符号数比较的结果

  • zf=1:obj1=obj2
  • zf=0:obj1!=obj2
  • cf=1:obj1<obj2
  • cf=0:obj1>=obj2
  • cf=0&&zf=0:obj1>obj2
  • cf=1||zf=1:obj1<=obj2

对有符号数比较的结果

  • sf=1&&of=0:obj1<obj2
  • sf=1&&of=1:obj1>obj2
  • sf=0&&of=1:obj1<obj2
  • sf=0&&of=0:obj1>=obj2

条件转移指令

配合 cmp 指令跳转

  • je:相等跳转,zf=1
  • jne:不相等跳转,zf=0
  • jb:小于跳转,cf=1
  • jnb:不小于跳转,cf=0
  • ja:大于跳转,cf=0&&zf=0
  • jna:不大于跳转,cf=1||zf=1

DF 标志和串传送指令

DF 为方向标志位,在串传送指令中,控制每次操作后 si,di 的增减

  • df=0,si,di 递增
  • df=1,si,di 递减

movsb 指令

相当于 ((es)*16+(di))=((ds)*16+(si)),之后根据 df 标志,si,di 递增或递减,传输的单位为字节

movsw 指令

与 movsb 同理,传输的单位为字

两个指令与 rep 指令配合使用

1
2
rep movsb
rep movsw

表示根据 cx 的值循环执行 movsb/movsw

设置 df 的指令

  • cld:令 df=0
  • std:令 df=1

pushf 和 popf

pushf 将标志寄存器压栈,popf 将栈中数据弹出到标志寄存器中

flag 在 debug 中的表示

flag value=1 value=0
of OV NV
sf NG PL
zf ZR NZ
pf PE PO
cf CY NC
df DN UP

内中断

在 CPU 执行完当前的指令后,可以检测到从 CPU 外部或内部发送来的一种特殊信息,使 CPU 立即对这种特殊信息进行处理,而不继续进行接下来的指令

内中断是 CPU 内部产生了中断信息

当 CPU 内部发生以下情况时产生中断信息

  • 除法错误,执行 div 产生除法溢出
  • 单步执行,当 TF=1 时触发
  • 执行 into 指令
  • 执行 int 指令

中断类型码标识了中断信息的来源,用于定位中断处理程序的位置

  • 除法错误:0
  • 单步执行:1
  • into:4
  • int n:n 为提供给 CPU 的类型码

中断向量表:记录了中断类型码对应的中断处理程序的入口地址 (cs:ip),一个入口地址为两个字,高地址字存放段地址,低地址字存放偏移地址,设类型码为 n,偏移地址存放的偏移地址为 4n,段地址为 4n+2

中断过程

  1. 从中断信息中取得中断类型码
  2. 标志寄存器入栈
  3. 将 flag 的第 8 位 TF 和第 9 位 IF 设为 0
  4. cs 入栈
  5. ip 入栈
  6. 从中断向量表中读取处理程序的入口地址

编写中断处理程序

  1. 使用到的寄存器入栈
  2. 处理中断
  3. 使用到的寄存器出栈
  4. 用 iret 指令返回,相当于自动完成 pop ip pop cs popf

使一段程序成为中断处理程序

  1. 将一段中断处理子程序传输到内存中存储

    使用 rep movsb 传输,ds:si 指向源地址,es:di 指向目的地址,cx 为传输长度 (处理程序的代码长度),cld 设置传输方向为正

    编译器可以处理立即数表达式,使用加减乘除等符号,表达式中不能存在寄存器

    在处理程序的开头和结束设置一个标号,mov cx,offset func_end-offset func_begin 可以获取处理程序的长度

    处理程序中需要的字符串等数据应该存放在不会被覆盖的区域,可在处理程序的开头跳转到真正的处理程序,跳转指令后分配字符串等数据的内存空间

  2. 设置中断向量表,将对应中断类型的表项设置为子程序的入口地址

int 中断

1
int n

n 为中断类型码,int 指令可以引发指定的中断过程

可手动编写中断例程,将例程的入口地址设置到中断向量表中,通过 int 指令,手动触发中断,执行中断例程

BIOS 和 DOS 的中断例程

BIOS 主要包括

  • 硬件系统的检测和初始化程序
  • 外部中断和内部中断的中断例程
  • 用于对硬件设备进行 IO 操作的中断例程
  • 其他和硬件系统相关的中断例程

中断例程的安装过程

  1. CPU 从 ffff:0000 开始执行程序,ffff:0 有一条跳转指令,跳转到 BIOS 中的硬件系统检测和初始化
  2. 初始化程序建立 BIOS 的中断向量,记录在中断向量表中
  3. 调用 int 19h 进行操作系统的引导,此后计算机由操作系统控制
  4. DOS 启动后,将 DOS 提供的中断例程装入内存,建立中断向量

BIOS 和 DOS 的中断例程用 ah 来传递中断例程中执行的子程序的编号,用 int 指令触发


int 21h 程序返回例程

21h 号中断例程中的 4ch 号子程序为程序返回程序,使用 ah 传输子程序的编号,al 传输程序的返回值

端口

主板上的接口芯片和各种接口卡的接口芯片都有一组由 CPU 读写的寄存器,它们都和 CPU 的总线相连,CPU 对它们进行读写时通过控制线向它们所在的芯片发出端口读写命令

端口读写命令只有 in,out 两条指令,分别向端口读取数据和向端口写入数据

只能使用 ax 或 al 来存放从端口读取的数据和要向端口写入的数据,访问 8 位端口时使用 al,16 位端口使用 ax


shl 和 shr 指令

shl 和 shr 是逻辑移位指令

1
2
shl reg,reg1
shr reg,reg1

reg1 中的值为 n,将寄存器中的值左/右移 n 位,最后移出的一位写入 CF 中,最低/最高位补 0

移位位数必须放在 cl 中


CMOS RAM 芯片

该芯片包含一个实时钟和一个有 128 个存储单元的 RAM 存储器,RAM 中 0-0dh 用来保存时间信息,其余大部分用于保存系统配置信息,系统启动时供 BIOS 读取

该芯片提供两个端口,分别是 70h 和 71h,通过这两个端口来读写 CMOS,70h 为地址端口,存放要访问的 RAM 单元的地址,71h 为数据端口,存放从选定单元中读取的数据或向选定单元写入的数据

外中断

由 CPU 外部产生的中断信息,外中断源共有两类

  • 可屏蔽中断

    CPU 可以不响应的中断,根据 TF 来决定是否响应中断,IF=1 响应中断,IF=0 不响应中断,在进入中断例程时使 TF=0,IF=0 可以屏蔽其他可屏蔽中断,若需要在进入中断例程后处理可屏蔽中断,可将 IF=1

    • sti:IF=1
    • cli:IF=0
  • 不可屏蔽的中断

    CPU 必须处理的外中断,对 8086CPU,不可屏蔽中断的中断类型码固定为 2

键盘的处理过程

  1. 键盘扫描

    键盘中的芯片对键盘上的每一个键进行扫描,按下按键,芯片产生一个通码,松开按键,芯片产生一个断码,两种扫描码被送入芯片的寄存器中,寄存器端口地址为 60h,读取键盘输入 in al,60h

    扫描码的长度为一个字节,通码的第 7 位为 0,断码的第 7 位为 1,断码=通码 +80h

  2. 引发 9 号中断

  3. 执行 9 号中断例程

直接定址表

定义数据长度的标号

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

这种标号表示了地址,还表示了单元长度,如 a 表示了地址 code:0,还表示了从该地址开始之后的单元都是字节单元

加冒号的标号只能在代码段中使用,在其他段中不能使用

标号还可以表示一个或一组内存单元,在指令中标号 b 表示 code:[8] 的内存单元,标号 a 表示一组内存单元,通过类似数组索引的方式访问 a[idata],等价于 code:0[idata],标号 c 处存储 a 和 b 处的偏移地址和段地址,等价于

1
c dw offset a,seg a,offset b,seg b

seg 运算符取得某一标号的段地址

若为 dw 类型,则存储偏移地址

使用标号访问数据段中的数据必须将标号所在的段与段寄存器联系起来


直接定址表

直接获取或计算得到要查数据所在的位置的表称为直接定址表

将表定义在数据段中,通过标号可直接获取到数据,简化程序或加快运算速度

表中存储不同子程序的入口地址,从而实现方便地调用不同的子程序

BIOS 进行键盘输入和磁盘读写

int9 中断例程

int9 中断例程将从键盘读出的扫描码转化为相应的 ASCII 码或状态信息,存储在键盘缓冲区或状态字节中

键盘缓冲区的逻辑结构为循环队列

int9 例程从端口读出按键的通码,然后检测状态字节,查看是否有 Shift、Ctrl 等控制键按下,将按键的扫描码和对应的 ASCII 码存到缓冲区的字单元中

该例程在按键按下时调用

int16h 中断例程

int16h 中断例程中的 0 号子程序从键盘缓冲区中读取数据,读取的数据存放在 ax 中,高位存放扫描码,低位为 ASCII 码

例程先检查缓冲区中是否存在数据,有数据时读取第一个数据,存入 ax,同时将缓冲区中的数据删除

该例程在程序需要读取键盘时调用,与 int9 调用的时刻不同

int13h 中断例程

int13h 例程用于对磁盘进行访问,通过控制磁盘控制器来访问磁盘,读写以扇区为单位

参数

  • ah:功能号,2 表示读扇区,3 表示写扇区
  • al:操作的扇区数
  • ch:磁道号
  • cl:扇区号
  • dh:磁头号 (面号)
  • dl:驱动器号
  • es:bx:指向接收从扇区读入的数据的内存区
  • 返回值
    • 成功:ah=0,al=操作的扇区数
    • 失败:ah=出错代码