8086 汇编笔记(启发性的,欢迎指正)
我为什么学汇编
玩Turing Complete玩着玩着知道计算机各个部件的硬件实现了,同时也接触到了一些汇编代码,以及汇编代码的用处
如果你也在Turing Complete里面实现了一个图灵完备的overture架构计算机,那么你应该知道,通过一个字节的数据就可以对自己的计算机进行一些操作,比如寄存器之间数据的移动,用不同寄存器里的数来做 算术运算(如加减乘除),逻辑运算(如与或非)
汇编就是这么神奇,你可以对你的计算机进行最精细的操作(也是最麻烦的),帮助你更深入了解计算机(废话)
了解到玩单片机没高级语言用(但好像可以用c语言),为了玩通单片机也该学学吧
目前跟的课
因为只听理论不实践会当场睡着所以找了一直需要实践的课
目前跟的课是:BV1eG4y1S7R5
学习所需资源也可以在这获取
磕唠:
英雄哥没聊过汇编awa
几万年没做过算法题了,看看这次能撑几天(被打飞),几天发个汇编笔记水水(挨打)
寄存器中英名称对照表
AH&AL = AX | Accumulator | 累加寄存器 |
---|---|---|
BH&BL = BX | Base | 基址寄存器 |
CH&CL = CX | Count | 计数寄存器 |
DH&DL = DX | Data | 数据寄存器 |
SP | Stack Pointer | 堆栈指针寄存器 |
BP | Base Pointer | 基址指针寄存器 |
SI | Source Index | 源变址寄存器 |
DI | Destination Index | 目的变址寄存器 |
IP | Instruction Pointer | 指令指针寄存器 |
CS | Code Segment | 代码段寄存器 |
DS | Data Segment | 数据段寄存器 |
SS | Stack Segment | 堆栈段寄存器 |
ES | Extra Segment | 附加段寄存器 |
-
汇编学习刚开始可以只看ax, bx, cx, dx四个寄存器,并且可以暂时看作它们是通用(等价)的
-
现在的cpu里面的寄存器雀食有很多通用寄存器来着
汇编里的十六进制表示
ax : 0000hex ->
ax : 0000 0000 0000 0000binary
十六进制一个0是二进制四个0
类推:
二进制:一个二进制位,值为 0 ~ 1
四进制:两二进制位,值为 00 ~ 11(00 01 10 11)
八进制:三个二进制位,值为 000 ~111
十六进制:四个二进制位,值为 0000 ~ 1111
笔记简单规定
->
:表示->
前的东西会被存在->
后面 示例:ax ->
bx 也就是ax被存放在bx
->
: 还表示 前面的东西 可以被看作是 后面的东西 示例:0000hex ->
0000 0000 0000 0000binary
操作数
: 表示一个操作(或者叫指令吧, 例如add加法操作)后面跟的参数 示例:add ax,bx
高位和低位
al : ax寄存器的低位
ah : ax寄存器的高位
al : 0000 -> al : 0000 0000 0000 0000 高位是右边俩0,换成二进制就是右边俩0000
^^ ^^^^ ^^^^
ah : 0000 -> ah : 0000 0000 0000 0000 低位就是左边俩,同理
^^ ^^^^ ^^^^
mov ax,A
== mov ax,000A
这两个语句等价
mov指令
mov ax,0000
mov bx,1400
mov bx,ax
把0000赋值给ax
把1400赋值给bx
把ax的值赋给bx
add指令
mov ax,0010
mov bx,0001
add ax,bx
结果 : ax : 0011
下面我交换了最后一句add的操作数(把ax和bx调换位置了)
mov ax,0010
mov bx,0001
add bx,ax
结果 : bx : 0011
由此可知,结果被存在第一个操作数代表的寄存器(add后面第一个跟ax就存ax,跟bx就存bx)
操作数1 + 操作数2 -> 操作数1
- 如果 ax + bx 溢出了, 那么溢出部分会被舍去
sub指令
mov ax,0001
mov bx,0002
sub bx,ax
结果 : bx : 0001
mov ax,0001
mov bx,0002
sub ax,bx
结果 : ax : FFFF
操作数1 - 操作数2 -> 操作数1
- 如果 ax - bx 溢出了,那么整体会向最高位借位,例如10000 - 00001,最高位被借走了,变成01111
mul指令
mov al,64
mov bl,A
mul bl
结果 : ax : 03E8
hex | 64 | A | 3E8 |
---|---|---|---|
dec | 100 | 10 | 1000 |
al * bl -> ax
al乘以bl,结果存放在ax
mov al,64
mul al
ax : 2710
al * al -> ax
al乘以al,结果存放在ax
- 结果只会被存放在ax
div 指令
mov ax,2710
mov bl,64
div bl
hex | 2710 | 64 |
---|---|---|
dec | 10000 | 100 |
结果 : al : 64
因为 10000 / 100 = 100
如果除数是 8位 (hex 00), 那么被除数可以是16位 (hex 0000).
ax / bl -> al
ax除bl,结果存放在al
mov dx,F
mov ax,4241
mov bx,2710
div bx
hex | F4241 | 2710 | 64 | 1 |
---|---|---|---|---|
dec | 1,000,001 | 10,000 | 100 | 1 |
结果 : ax : 0064
dx : 0001
如果除数是 16位 (hex 0000), 那么被除数可以是32位 (hex 0000 0000).
dxax / bx -> ax 和 余数 -> dx
把dx和ax拼接成一个32位数
dxax除bx,结果存在ax,余数存在dx
DS(Data Segment)寄存器是干啥滴
mov ds,0001
^^^^ Error
不能用立即数给ds赋值
立即数:就我目前的理解,就比如说,你和计算机交互的指令有一字节长(字节Byte,位Bit,1Byte = 8bit),
也就是二进制的0000 0000,前两位用于指令:
00
000000
选择指令 内容
就比如01
表示寄存器间移动的指令,后面的六位就可以三位三位地表示源寄存器和目标寄存器
如果10
表示算术运算,逻辑运算,那后面六位就可以是你想选择的运算
如果11
表示条件判断,那后面六位就是你想选择用什么条件来判断
这时候如果00
表示立即数,那么后六位就会原封不动地被传给内存之类的地方,然后可以直接用作计算的数据,你可以把立即数赋值给ax,mov ax,000A
中000A就是立即数
mov ax,0001
mov ds,ax
可以用其他寄存器来给ds赋值
mov ax,0000
mov ds,ax
段地址和偏移地址的比喻(段地址Segment,偏移地址Offset):
你存放衣服的柜子在1000号,你兄弟的柜子都放你后面,是1001,1002,1003
那1000就是你的地址,以你的地址为基准,也就是把1000作为段地址,其他人的柜子也就通过你的柜子来找了,你的兄弟们就只用记住他在你柜子第几个就ok了,也就是偏移地址
-
我上面说的那个概念其实是基址(base)的概念(被打飞)
-
其实啊,物理地址 = 段地址 * 16 + 偏移地址,我们最终用到的会是物理地址
-
这个乘16其实是16进制数左移一位,也就是二进制数左移四位,例子:段地址:1000,偏移地址:000A,物理地址: 1 000A(1000左移一位得10000, 10000 + 000A = 1000A)
段地址和偏移地址的例子(segment-offset) :
0001 : 0000
^^^^ ^^^^
segment : offset
-e 0001 : 0000
这个操作进入内存内容修改的界面滴,修改的是从物理地址10开始的数据
0001 : 0000 -> 08 23 87 17 23 23 …
我从物理地址10开始存放数据,10存放08,11存放23,12存放87etc.
0001 : 0000
-> 08
0001 : 0001
-> 23
0001 : 0002
-> 87
0001 : 0003
-> 17
这时候,方括号[x]
里的数就是以ds作为段地址,x作为偏移地址的内存里的内容
ds : x
段地址:偏移地址
mov ax,[0]
ax : 2308
上面两句是从内存0001:0000
的位置开始向寄存器ax存放两字节的数据,也就是:
0001 : 0000
-> al
0001 : 0001
-> ah
mov ax,[1]
ax : 8723
mov ax,[2]
ax : 1787
etc.