课堂速记
# 课堂速记
本笔记使用教材《汇编语言(第三版)》,王爽,清华大学出版社
# 引入 进制转换
# 十六转十进制
就用乘法,每一位乘以 16^0, 16^1, 16^2 ... 然后加在一起。
举个例子,ff bf 是几?答:65471
# 十六转二进制
更简单了,只需把每一位,变成二进制的四位数,然后拼在一起。
看个例子就懂了,ff bf 是二进制的几?答:1111 1111 1011 1111
# 十进制转十六
稍微复杂些,用短除法。每次除以 16,把余数从下到上拼起来,就得到了 16 进制的数。
来一起试试,65471 是十六进制的几?答:ff bf
# 二进制转十六
又容易了,只需切成 4 个 4 个的小段,把每段对应的字母 / 数字拼在一起,就可以了。
比如这个例子,11 1111 1011 1111 是几?答:3f bf
# 十进制转二进制
除二取余,倒序排列,高位补零。
# 二进制转十进制
同十六转十进制,但 16 改成 2
# 巧算法
如何快速把 2^n 的十进制数,转换为二进制?
只需把 n 除以 4,得到 j 余 i。把 i 变成 2^i 做为第一位,其余的就是,j 是几就跟几个零。
# 第二章 寄存器
# 8086 访问地址
一个段的最大大小为 2^16=65536=64K,此为偏移地址的最大表示大小 FFFFH。
# CS:IP
代码段的段地址存放在 CS 中,指令指针寄存器 IP 指示代码段中指令的偏移地址,处理器利用 CS:IP 取得下一条要执行的指令。
# 第三章 寄存器(内存访问)
# 内存中字的存储
(1) 20H (2) 4E20H
字节型数据<字型数据。一个字型数据 (如 1234H) 存放在内存中,由 2 个连续的地址的内存单元组成。高地址内存单元存放字型数据的高位字节,低地址内存单元存放字型数据的低位字节。
mov 的大小就是 al 的大小
此处 “一般的寄存器” 就是上例中的 bx
# 例 1
# 例 2
1200+7C0A+4532+A963(舍弃溢出)
# 堆栈
8086CPU 入栈出栈都以字为单位,不能 push/pop 一个 al
# 先进后出
# SS:SP
入栈出栈时 SP 会先进行 - 2/+2 的操作,push 时 SP 向上(低位)移动 - 2。然后将数据送入 SS:SP 指向的内存单元处。
# 第四章
# 源程序中的 “程序”
汇编源程序:
- 伪指令(编译器处理)
- 汇编指令(编译为机器码)
程序:源程序中最终由计算机执行、处理的指令或数据。
# 汇编程序 & 伪指令
codesg:标号,放在 segment 的前面,作为一个段的名称,这个段的名称最终将被编译、连接程序处理为一个段的段地址。
# 编译 & 连接
当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序将它们连接到一起,生成一个可执行文件;
程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件;
一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。
所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。
# 谁将可执行文件中的程序装载进入内存并使它运行?
在 DOS 中,可执行文件中的程序 P1 若要运行,必须有一个正在运行的程序 P2 将 P1 从可执行文件中加载入内存,将 CPU 的控制权交给它,P1 才能得以运行;当 P1 运行完毕后,应该将 CPU 的控制权交还给使它得以运行的程序 P2。
(1)我们在 DOS 中直接执行 1.exe 时,是正在运行的 command 将 1.exe 中的程序加载入内存。
(2)command 设置 CPU 的 CS:IP 指向程序的第一条指令(即程序的入口),从而使程序得以运行。
(3)程序运行结束后,返回到 command 中,CPU 继续运行 command
# EXE 文件中的程序的加载过程 DS
程序加载后,ds 中存放着程序所在内存区的段地址,这个内存区的偏移地址为 0,则程序所在的内存区的地址为:ds:0;
这个内存区的前 256 个字节中存放的是 PSP,dos 用来和程序进行通信。
所以,我们从 ds 中可以得到 PSP 的段地址 SA,PSP 的偏移地址为 0,则物理地址为 SAX16+0。
因为 PSP 占 256(100H)字节,所以程序的物理地址是:
SA×16+0+256= SA×16+16×16= (SA+16)×16+0
可用段地址和偏移地址表示为:SA+10:0
# 程序执行过程的跟踪
用 R 命令查看各个寄存器的设置情况
用 U 命令查看其他指令
使用 P 命令执行 int 21
使用 Q 命令退出 Debug
# 第六章 包含多个段的程序
# 在代码段中使用数据
程序解读见书 P124。 mov ax,4c00h
代表终止。
程序 6.2
end start 除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。
# 在代码段中使用栈
程序 6.3
30h 是 48 字节,正好对应 dw 分配的 16 个字型数据,用于栈的空间。
# 将数据、代码、栈放入不同的段
程序 6.4
cs 是自动装载的,不用在代码段中指定 cs 的指向。
为什么
mov bx,0
可以将 ds:bx 指向 data 段中的第一个单元?因为 0 被认为是 ds,[0]
为什么
mov cx,5
表示循环 5 次?cs 用来控制循环次数,每次执行 loop 指令时,都会检查 cs 的值是否为 0
# 第七章 更灵活的定位内存地址的方法
# ASCII 码
程序 7.1
inc 加 1,而不是加 2,因为一个 ASCII 码占一个字节
# [bx+idata] 寄存器相对寻址
[bx+idata] 表示一个内存单元,它的偏移地址为 (bx)+idata(bx 中的数值加上 idata)
mov ax,[bx+200]
数学化描述:(ax)=((ds)*16+(bx)+200)
# SI 和 DI 基址变址寻址
类似于 bx,但是不能分成两个 8 位寄存器
段寄存器、两个内存单元之间、两个段之间都不能直接 mov,需要用寄存器中转
SI 元变址寄存器
DI 目的变址寄存器
# 相对基址变址寻址([bx+si+idata] 和 [bx+di+idata])
问题 7.1、7.3、7.4、7.5 的分析很实用
# 第八章 数据处理的两个基本问题
用 reg 表示一个寄存器,sreg 表示段寄存器
# bx,si,di,bp
bp 用于在堆栈段上寻址,bs 默认用于数据段寻址。都是基址 (base)
“两个 i 不相见,两个 b 不相见”
错误指令:
mov ax,[bx+bp]
mov ax,[si+di]
只要在 [...] 中使用寄存器 bp,且指令中没有显性地给出段地址,段地址就默认在 ss 中。
# 寻址方式
P164 表 8.2
# 指令要处理的数据有多长?
word ptr
和 byte ptr
来显式的指定内存单元的长度
P166: mov word ptr [1000H],1
-> 0100FF
# div 指令 书 P169
div byte ptr ds:[0]
(al) = (ax) / ((ds) * 16 + 0) 的商
(ah) = (ax) / ((ds) * 16 + 0) 的余数
div word ptr es:[0]
(ax) = [(dx) * 10000H + (ax)] / ((es) * 16 + 0) 的商
(dx) = [(dx) * 10000H + (ax)] / ((es) * 16 + 0) 的余数
低商高余
例题
mov ax,data
mov ds,ax
mov ax,ds[0]
mov dx,ds[2]
div word ptr ds:[4]
# 第九章 转移指令的原理
# offset
P175、176
问题 9.1 为什么要加
cs:
?
不加冒号复制的是默认 ds 段
# jmp
讲得太快
# 第十一章
# PF 标志(Parity)
表示奇偶性,1 的个数为奇数时 PF=0,为偶数个时 PF 为 1
# SF 标志(Sign)
结果为负那么 SF=1,结果非负数则 SF=0;
# ZF 标志(Zero)
结果为 0 那么 ZF=1, 结果不为 0 则 ZF=0;
# CF 标志(Carry)
mov al,97H
sub al,98H
执行后:(al)=FFH, CF=1, CF 记录了向更高位的借位值
10010111
10011000
(-1)11111111
FFH
# OF 标志(Overflow)
mov al,98
add al,99
2
2
执行后 CF=0, OF=1
对于无符号数运算,没有进位,CF=0;对于有符号数运算,发生了溢出(数值位向符号位进了一位,虽然污染了符号位,但此时数还是八位。如果进到第九位,则产生了进位),OF=1
# adc 指令
mov ax,1
add ax,ax
adc ax,3
2
3
2
3
adc 利用了 CF,执行时相当于计算 (ax)+3+CF=2+3+0=5
计算 1EF000H+201000H,结果放在 ax(高 16 位)和 bx(低 16 位)中
mov ax,001EH
mov bx,F000H
add bx,1000H
adc ax,0020H
2
3
4
2
3
4
# sbb 指令
adc 的减法版
# cmp 指令
不保存结果的减法比较,仅仅根据结果设置标志位
cmp 比较大小不能仅靠 SF,因为可能溢出,还需要借助 OF
SF=1, OF=0,说明没有溢出,逻辑上结果正负 = 实际上结果正负,即 ah<bh
SF=1, OF=1,溢出会污染符号位,正负性颠倒,即 ah>bh
SF=0, OF=1,跟刚才逻辑一样,ah<bh
SF=0, OF=0,ah>bh;若 ZF=0,则 ah=bh
任意一个为 1,另一个为 0 时,前者<后者
# je 指令等
有符号位的是 jl(小于)、jg(大于)
# DF 标志和串传送指令
df=0 每次操作后 si、di 递增
df=1 每次操作后 si、di 递减
movsb 将 ds:si 指向的内存单元的一个字节送入 es:di 中,然后根据 df 位的值,将 si 和 di 递增或递减
movsw
rep
# 第十二章 内中断
中断向量表 四个单元
do0
# 第十三章 int 指令
int n,n 是中断类型码,功能是引发中断,相当于引发一个 n 号中断的中断过程,执行过程:
标志寄存器入栈,IF=0,TF=0;CS、IP 入栈;(IP)=(n*4), (CS)=(n*4+2)
# 13.5 BIOS 和 DOS 中断例程的安装过程
编程时可以用 int 指令调用 BIOS 和 DOS 提供的中断例程
//不重要
mov ah,9 //调用设置光标的子程序
mov al,'a'
mov bh,7 //颜色设置
2
3
4
2
3
4
# 第十四章
# shl 和 shr 指令
逻辑移位指令。将一个寄存器或内存单元中的数据向左移位,最后移出的一位写入 CF 中,最低为用 0 来补充。移动位数大于 1 时,必须把移动位数放在 cl 中。
shl 左移,shr 右移。会影响到符号位。(算术右移不会影响符号位)
# 期末考试复习
选择,可能有判断题
写一些指令,进行纠正
2 道编程题
宏不作要求
一直到系统调用,比较重要
https://github.com/sanmianti/AssemblyLanguageTest/blob/master/%E3%80%8A%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80%E3%80%8B%E7%AC%AC%E4%B8%89%E7%89%88%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0.md