汇编语言:寄存器

汇编语言学习笔记

前言

本来是想学基于 ARM 架构的汇编语言,但苦于基础薄弱,既不会 ARM ,又没有系统的学过计算机组成原理。正巧,手上有本王爽的《汇编语言》,略作翻阅,竟发现内容如此全面。故今天开一文来撰写本书的学习笔记。前几年是想着笔耕不辍,坚持写博文的;可这几年因种种原因渐渐放弃了技术文章的写作,那就让这篇学习笔记成为下一个阶段的开始吧。

本文的组织形式是由一小篇一小篇的博文拼凑好的,最后会统一写好上传到博客上。如果博文太长,我将会把整个文章拆成好几段的形式。

才疏学浅,再加上基础薄弱,学习新知识难免会有理解不到位的地方。如有错误,欢迎斧正。

——2022.3.6 kenlig 于武汉

第一章

这一章大概是非常基础的,总体讲了下计算机是什么,内存是什么和我们应该干什么。注意,这本书是针对 8086 的汇编语言教学,我们通过学习 8086 CPU 的汇编语言来理解概念。

寻址能力和总线的宽度有关,具体算法就是 2 的总线宽度次方。

注意内存地址空间的概念。没学过之前还是有点错觉的,实际上是把所有的存储器看成了一个逻辑处理器,我们只需要操作内存地址空间就够了。这个东西实际上是被 CPU 拼凑出来的。

约定:十六进制数后面加H。

第二章

本章内容主要是寄存器和CPU如何执行指令的内容。

通用寄存器

有四个: AX BX CX DZ 每个又能分为 H L 这两段。比如, AX=AH+AL 。这四个寄存器都是 16 位的,分开的两个则是 8 位的。

所以,我们可以和前面学过的联系起来:字节 = 8 位,字 = 两个字节。一个字可以存在一个 16 位的寄存器中,当然也可以拆开来看。

mov add指令

movadd 指令:第一个是把右边的移到左边,第二个是把右边的加在左边上。

注意,进位得到的要被截取,如果运算出来了 1044C 这种东西,应该把第一位的 1 去掉。同时,操作位数要一致。如果不一致的话,肯定不会通过(

物理地址

和第一章呼应,内存地址空间是被生成的。

8086机是16位的 CPU 。 这代表着:寄存器、处理器处理的数据规模、数据通路都是16位的。然而他有20位的地址总线,如果我们只传16位,那显然只有 2^16 = 64 KB 的寻址能力。 8086 将两个 16 位地址合成为一个 20 位的物理地址,那么我们的寻址能力就是2^20=1024KB=1MB。算法见下文。

物理地址=段地址*16+偏移地址

你可以把它看成“物理地址=基础地址+偏移地址”,基础地址就是段地址<< 4(*16)。为什么要这么做?因为我们要有很大的寻址空间(64KB 在现在显然不是很够用),但总线的数量是有限的,所以我们采用16+16=20的方式,来让我们的寻址空间更大一些。

为什么要*16?

对于16进制的数来说,数 x 乘以16就等于后边加一个0。这样的话,和偏移地址相加,刚好能组成 20 位。

你懂吧,什么都是被设计出来的(

数据都是存在内存里的,但是内存是一大块东西。我们为了协调“诶这段内存到底是干啥的“之类的问题,为什么不把内存划分成一块块的呢?

好,那我们就把内存划成一块一块的。但是,这不代表物理上被划分成一块块的,我们只需要在逻辑上划分就可以了。这就是内存段,你可以理解为这是一块一块的内存,而16位地址的寻址能力是 64KB ,所以一个段的大小也就是 64KB 。

段寄存器

刚刚我们提到了段,而且通过物理地址的计算方法我们也能轻松寻址。而计算物理地址的两个值:段地址和偏移地址,也被存储在8086CPU中的寄存器中。我们先来看下指令寄存器: CSIPCS 是代码段寄存器, IP 是指令指针寄存器。

执行指令的过程

  1. 从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器。
  2. IP+=指令的长度。(为了读取下条指令)
  3. 执行

修改CS:IP

8086CPU当然会提供工具来修改CS:IP的值,否则的话怎么控制运行哪段代码呢?

jmp 指令

用法:jmp CS:IP 或者是 jmp IP

上述的寄存器处填上你需要把这个寄存器变为什么值即可。

代码段

我们已经知道了的概念,那我们的代码也应该存在代码段里。(同理,数据也是这样的,但代码就是数据,CPU得知道那个地方是代码)

CPU只认为CS:IP指向的地方为代码段。

第三章

老实说,以前基础打得不牢,看第三章有点难受

本章从CPU访问内存的角度分析了几个寄存器。也就是说,这一章着重于讲解“数据在哪里”

大小端

在《深入理解计算机系统》里已经详细的学习过了。

详细一点:一个字符占用一个字节,但是一个字(是16位)要占用两个字节。如果我们把数字的高位存储在内存的高位中,就是小端;反之则是大端。

举个例子:4E20H如果用小端序就是20H,4EH

DS和[]

这是啥?参考第二章的CS:IP,我们要是访问数据也应该可以用一个什么寄存器:什么寄存器的方式来访问。同样,依然按照物理地址的那条规律来算出实际上的存储位置。

8086CPU中用DS寄存器来存储某个数据段的地址,用[]来访问偏移地址。

举个例子:DS=10000H,[0]就是10000H,[2]就是10002H。

但是,DS寄存器不能直接放入。比如,mov ds,1000H这句话是是错的!我们只能用

mov ax,1000H
mov ds,ax

的方式来修改DS寄存器的内容。

修改偏移地址的方法为mov [0],ax。这个偏移地址是用DS寄存器送过来的。

mov add sub指令的详细理解

前面我们已经写了mov和add指令,但是这个指令到底能修改什么呢?

我的理解是:左参数可以为寄存器、内存单元;右参数不仅可以是寄存器和内存单元,也可以是数据。

sub指令:左边的减去右边的。比如:sub ax,[2]的意思是把ax寄存器的内容减去DS:2寄存器的内容,数据存储在ax寄存器中。

数据段

第二章的“段-代码段”这一节已经讲过了代码段,而数据段其实就是一段一段的内存,里头装着数据。我们用DS:[]的访问方式来访问数据段。

栈(Stack)

我们不仅可以在C语言中实现栈数据结构,也可以学习8086CPU中有的系统栈。爆栈了就是因为这个

8086提供一段内存充当栈的功能。

SS:SP寄存器

这两个寄存器依然可以用CS:IP的方式来理解。不同的是,SS:SP是来访问栈地址的。任意时刻,SS:SP指向栈顶元素。如果栈为空,那么指向栈顶的更后一个元素。这样的话,我们在任意时刻访问到栈顶元素的下个地址,来push进元素。

SP在每次push后会把自己的地址+2,这样才能访问到下一个地址。

超界怎么办

无解。写代码的时候注意一下。

push和pop指令

这玩意和C语言描述的Stack功能一样。我们直接说一下语法就可以了。

push 寄存器把一个寄存器的元素入栈

pop 寄存器把栈顶元素拿到这个寄存器中

怎么执行

执行push的时候,CPU先改变SP,然后往SS:SP的地方扔数据。

执行pop的时候,CPU先把SS:SP的数据拿出来,然后改变SP。

栈段

和数据段代码段一样,不同的是用SS:SP访问。一个栈的容量就是偏移地址的寻址范围:64KB。

为了确定这个段到底是干什么的,CPU只需要确定CS IP DS SS SP这几个寄存器指向的位置。实际上,他们都是数据(二进制)存储在内存中的。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇