前言
cpu 模拟器:添加链接
添加链接
一、最简单的程序
This
is because this gen processor is a 32 bit processor. So we’re going to be working in 32 bits. And that’s the constraint of how much we can store in a single register is 32 bits worth of data will often refer to 32 bits as a word in terms of the size.
这是因为这个 gen 处理器是 32 位处理器。 所以我们将使用 32 位。 这就是我们可以在单个寄存器中存储多少是 32 位数据的限制,就大小而言,通常将 32 位称为一个字。
So
if you ever hear me refer to as you know, changing a word worth of data. I’m referring to 32 bits of data, the idea of a word transitions to other assembly languages as well. And it generally represents the total like max size of data that can be storedn in a register.So for instance, if you were in a 64 bit processor, a word would be 64 bits in size. And then we have the concept of a half word, which is half the size of a word. So in 32 bits, a half word is 16 bits, because it’s half of 32 and 64 bits, that half word is 32 bits, because it’s half of 64.
因此,如果您听到我提到如您所知,更改一个字的数据。 我指的是 32 位数据,一个单词的概念也可以转换为其他汇编语言。 它通常表示可以存储在寄存器中的数据的总大小。例如,如果您使用的是 64 位处理器,则一个字的大小将是 64 位。 然后我们有了半字的概念,它是一个字的一半大小。 所以在 32 位中,半字是 16 位,因为它是 32 位和 64 位的一半,半字是 32 位,因为它是 64 位的一半。
所以,一个 Word 是多少个 bit,应该看这个处理器是多少位的处理器。
In
general, I can say that registers are zero to six are general purpose, we can use them for whatever storage we’d like to use them for our seven is going to have a special functionality to system calls. Essentially, when we are working with assembly, sometimes we need to talk to the operating system.And we need to ask it for maybe a resource or we need to ask it to terminate our program when we you know, call to the operating system to ask it to do something for us, it needs to know what we’re asking it to do.
一般来说,我可以说寄存器 0 到 6 是通用的,我们可以将它们用于我们想要使用的任何存储,因为我们的寄存器 7 将具有系统调用的特殊功能。 本质上,当我们使用汇编时,有时我们需要与操作系统对话。我们需要向它请求资源,或者当我们知道时我们需要请求它终止我们的程序,调用操作系统 要求它为我们做某事,它需要知道我们要求它做什么。
And
the way that we communicate that is by storing a value in Register seven, and that value will be some numerical value, that it will map in a table to some specifically action. So for instance, if I store the value one and Register seven, and then I interrupt the program, the operating system will read that and interpret it as enter the program. So that’s an example of how we might use that.So that’s one example of a special purpose register that we have.
我们交流的方式是在寄存器 7 中存储一个值,该值将是某个数值,它将在表格中映射到某个特定的操作。 例如,如果我存储值 1 和寄存器 7,然后我中断程序,操作系统将读取它并将其解释为进入程序。 这是我们如何使用它的一个例子。这是我们拥有的特殊用途寄存器的一个例子。
just
to get a really good fundamental feel of the structure of our programs, as well as the way that they actually run.So we’re gonna go over a lot of the important concepts of you know, where the starting point of our application is, where the ending point of our application is, and how can we do all of the things in between.So starting off with this idea,let’s discuss the starting point of our program, you’ll see that our emulator automatically puts in two lines for us.Now,if you’re programming outside of the emulator,you won’t have these two lines added by default, you have to add them yourself. But they are essential because they tell us the starting point of our application.So we break down the lines one by one here.
只是为了对我们程序的结构以及它们实际运行的方式有一个非常好的基本感觉。所以我们将回顾你知道的很多重要概念,我们的应用程序的起点在哪里 ,我们的应用程序的终点在哪里,以及我们如何在这之间做所有的事情。所以从这个想法开始,让我们讨论我们程序的起点,你会看到我们的模拟器自动为我们放入两行。现在,如果您在模拟器之外编程,默认情况下不会添加这两行,您必须自己添加它们。 但是它们是必不可少的,因为它们告诉我们应用程序的起点。所以我们在这里一一分解。
I’m
going to start backwards with this underscore start portion here.This is known as a label, a label is sort of synonymous with a function in higher level languages. It’s a way of being able to divide out segments of code, such that if you go to the label, you start to execute the code that is underneath that label. The idea of this is that we set up a start label, which then we are able to say okay, go to the start and start running this stuff that’s there.
我将从这里的下划线开始部分向后开始。这被称为标签,标签是高级语言中函数的同义词。 这是一种能够划分代码段的方法,这样,如果您转到标签,您就会开始执行该标签下的代码。 这样做的想法是我们设置一个开始标签,然后我们可以说好的,去开始并开始运行那里的东西。
That’s
the idea of what we’re looking to do.So that’s why we define our start label. And then our global underscore start here is a way of being able to tell people about this dirt label. So someone is going to be running our program. So at some point something is going to be running our program, it needs to know where the starting point is, as well.
这就是我们想要做的事情的想法。所以这就是我们定义开始标签的原因。然后我们的全局下划线从这里开始是一种能够告诉人们这个肮脏标签的方式。所以有人会运行我们的程序。所以在某些时候,某些东西会运行我们的程序,它也需要知道起点在哪里。
We
define labels by D faults, they aren’t available to anything external to our program. The dot global is telling everyone else about our start label. So if anyone ever approaches our program and says l want to run you, where’s the start, the start is exported via this global, which means that everything is able to see where the starting point is, therefore, things are able to go to the starting point to start executing our program. So this is the general idea of sort of like our main starting point of our application. So everything that we write is going to be underneath this start label. So that’s where we’re going to start writing our code.
我们通过 D 错误定义标签,它们不适用于我们程序外部的任何东西。 dot global 告诉其他人我们的起始标签。因此,如果有人接近我们的程序并说我想运行你,那么起点在哪里,起点是通过这个全局导出的,这意味着一切都可以看到起点在哪里,因此,事情可以去开始执行我们的程序的起点。所以这有点像我们应用程序的主要起点。所以我们写的所有东西都将在这个开始标签下。这就是我们要开始编写代码的地方。
And
the code that I’m going to write is going to be very, very simple. All it’s going to do is it’s going to move some data into registers zero, that way, you can see how we move data into registers. And then I’m going to move some data into register seven, it might seem a little bit weird that I’m jumping straight to register seven.The reason being is because register seven is a special purpose register. What it does is it stores information about system calls.
我要写的代码会非常非常简单。它要做的就是将一些数据移入寄存器零,这样,您就可以看到我们如何将数据移入寄存器。然后我要把一些数据移到寄存器 7 中,我直接跳到寄存器 7 可能看起来有点奇怪。原因是因为寄存器 7 是一个特殊用途的寄存器。它的作用是存储有关系统调用的信息。
When
we go to the operating system,we often want to ask it to do specific things for us, right, the operating system manages input and manages output, it manages the execution of programs. So if we need the operating system to do something for us, we need some method of communicating with it. This method of communication comes in two pieces. The first is in system interrupts. And the second is in system called numbers. So we placed a special number into register seven. And that tells the operating system what we would like it to do. So basically, we placed the number to ourself, and then we call an interrupt the interrupts go to the operating system, and it says, Hey, we need something done, the operating system comes in, and it reads register seven, which has some number inside of it. It takes that number and it compares it to like a lookup table, and it says, Okay, well that number corresponds with this task, and then it completes that task for us.
当我们去操作系统的时候,我们经常想请它为我们做一些具体的事情,对,操作系统管理输入和管理输出,它管理程序的执行。因此,如果我们需要操作系统为我们做某事,我们就需要某种与之通信的方法。这种沟通方式分为两部分。首先是系统中断。第二个是系统调用编号。所以我们在寄存器 7 中放置了一个特殊的数字。这告诉操作系统我们希望它做什么。所以基本上,我们把这个号码给自己,然后我们调用一个中断,中断转到操作系统,它说,嘿,我们需要做点什么,操作系统进来,它读取寄存器 7,其中有一些里面的号码。它获取该数字并将其与查找表进行比较,然后它说,好的,该数字与此任务相对应,然后它为我们完成了该任务。
In
our case, our task is very simple.We want our program to end.So if I want to terminater the execution of my program, what I do is I move the number one into our seven,and then l call an interrupt the operating system then goes to register seven, it says, Oh, I see the number one here, it goes to it’s a little table and it’s table says one corresponds to exit the program, and then it terminates the program for us.
在我们的例子中,我们的任务很简单。我们希望我们的程序结束。所以如果我想终止我的程序的执行,我所做的就是将数字 1 移动到我们的 7 中,然后调用中断操作系统然后去注册七,它说,哦,我看到这里的数字一,它去它是一个小表,它的表说“一” 对应于退出程序,然后它为我们终止程序。
So
that shows us how we end our program. So let’s take a look at how that’s actually written. So the first thing I want to do is move some data into our zero. That way we can see just generally how we can move data into registers and what that looks like during execution. So the way that we do this is as follows, we’re going to start by writing in what’s called an opcode, we’ll use it set in a number of different ways, either op code operation, pneumonic is also used, I’m going to refer to them as operations or op codes. So we’re gonna start by running in the operation that corresponds to moving data. And that is m o v.
这向我们展示了我们如何结束我们的程序。那么让我们来看看它实际上是如何编写的。所以我想做的第一件事就是将一些数据移到我们的零中。这样我们就可以大致了解如何将数据移动到寄存器中,以及在执行过程中会是什么样子。所以我们这样做的方式如下,我们将从编写所谓的操作码开始,我们将以多种不同的方式使用它,要么操作码操作,要么使用pneumonic,我’我将把它们称为操作或操作码。因此,我们将从运行与移动数据对应的操作开始。这就是 m o v。
So
I moved 30 into register zero. And now we’re going to do our portion that ends the program. So remember, I said that I wanted to move into registers seven, the value one, because one indicates that we’re going to exit our program. And then I want to do a an interrupt.The way I do this is as follows. l say SW, ei zero. SWi is a software interrupts what it does is it interrupts the program, and it lets the operating system take over. Like I said before, the operating system then reads the value in our seven, that value is one, it takes the one that checks the lookup table, it says, okay, well, one says that we should terminate the program. And so we’ire done.
所以我把 30 移到了零寄存器。 现在我们要做结束程序的部分。所以请记住,我说过我想移入寄存器 7,值 1,因为 1 表示我们将要退出程序。 然后我想做一个中断。我这样做的方式如下。 我说 SW,ei 为零。 SWi 是一个软件中断, 它的作用是中断程序,并让操作系统接管。 就像我之前说的,操作系统然后读取我们的 7 中的值,那个值是 1,它需要检查查找表的那个,它说,好吧,好吧,一个说我们应该终止程序。 这样我们就完成了。
Now
you can actually see it in action.So you can see in our next instruction, we stepped into our seven gets a value of one, and then we would end up doing a system interrupt, and that would end our program. And that’s really all there is to it.This is a really basic assembly program that we’ve now written.
现在你可以实际看到它在运行。所以你可以在我们的下一条指令中看到,我们进入 7 得到一个值,然后我们最终会进行系统中断,这将结束我们的程序。 这就是它的全部内容。这是我们现在编写的一个非常基本的汇编程序。
CPSR(当前程序状态寄存器) 可以在任何处理器模式下被访问。它包含了条件标志位、中断禁止位、当前处理器模式标志以及其他的一些控制和状态位。每一种处理器模式下都有一个专用的物理状态寄存器,称为SPSR(备份程序状态寄存器)。当特定的异常中断发生时,这个寄存器用于存放当前程序状态寄存器的内容。在异常中断程序退出时,可以用SPSR中保存的值来恢复CPSR。
由于用户模式和系统模式不是异常中断模式,所以它们没有SPSR。当在用户模式或系统模式中访问SPSR时,将会产生不可预知的结果。
CPSR的格式如下所示。SPSR格式与CPSR格式相同。
二、寻址类型
we’re
going to take a look at the different addressing types that exist inside of arm as well as other assembly languages. And the general idea of addressing types is that there are ways that we’re able to store and retrieve data from the various memory locations that we have.
我们将看看存在于 arm 以及其他汇编语言中的不同寻址类型。寻址类型的一般概念是,我们可以通过多种方式从我们拥有的各种内存位置存储和检索数据。
For
example, in the previous video, we used a type of addressing that is known as immediate addressing. And that’s when we want to move into a register a specific value that is constant. So whenever we have like a constant value like this five on this side being moved into a register zero, we call this immediate addressing, because we’re taking an immediate value, and we’re placing it into a register.
例如,在上一个视频中,我们使用了一种称为立即寻址的寻址。这就是我们想要将一个特定的常数值移入寄存器的时候。因此,每当我们在这一侧有一个像这样的常量值 5 被移动到寄存器 0 中时,我们称之为立即寻址,因为我们正在获取一个立即值,并且我们将它放入一个寄存器中。
A
similar type of register type movement that we have is moving between two different registers. So if l want to move now what’s an Register zero into Register one, this would be called registered direct addressing. So we’re directly moving a value from one register into another. So those are two types of addressing that we have.
我们拥有的类似类型的寄存器类型移动是在两个不同的寄存器之间移动。因此,如果我现在想将寄存器零移动到寄存器一,这将被称为寄存器直接寻址。所以我们直接将一个值从一个寄存器移动到另一个寄存器。这是我们拥有的两种寻址方式。
Now,
the more interesting type of addressing that we have has to deal with data that’s stored in the stock. So to demonstrate this, I’m going to go ahead and show you how to get data onto this stock first, and then we’ll take a look at how we can work with the data that’s on the step. So first off, how do we get data onto the stock” />To
do this, we have to use a data section in our application. And the way that we do that is we just type in data, this is going to come below all of our like global start portion here.
为此,我们必须在应用程序中使用 data section。我们这样做的方式是我们只需输入 data,这将低于我们所有类似的全局开始部分。
So
you say put it just right down here. And what we’re going to do is we’re going to declare any data that we want to put in our stack memory. And we do this in the way of giving it a label, which basically functions like a variable name, that we then declare the type of the variable, and then the data that’s actually stored inside of it. So for example, I’m going to declare some data, I’m going to name the data list, I’m going to put list colon, and then I’m going to go to the next line. And I usually like to indent to put the next portion here just organize it nicely. And in this case, I want to store a list of numbers. So in this case, when we’re dealing with numbers, numbers are going to be a specific sizes. In this case, I want to work with 32 bit numbers. And as we’ve discussed 32 bits is considered to be a word. So I’m going to type in dot word, this tells it that every single one of the values that follows should be treated as a word type, which means that they are 32 bits in size.
所以你说把它放在这里。我们要做的是声明我们想要放入堆栈内存的任何数据。我们这样做的方式是给它一个标签,它的功能基本上就像一个变量名,然后我们声明变量的类型,然后是实际存储在其中的数据。例如,我要声明一些数据,我要命名数据为 list,我要放 list:
,然后我要转到下一行。而且我通常喜欢缩进将下一部分放在这里,只是很好地组织它。在这种情况下,我想存储一个数字列表。所以在这种情况下,当我们处理数字时,数字将是一个特定的大小。在这种情况下,我想使用 32 位数字。正如我们所讨论的,32 位被认为是一个 word。所以我要输入 .word,这告诉它后面的每一个值都应该被视为一个 word 类型,这意味着它们的大小是 32 位。
Notice
that we don’t say like it’s an integer, or it’s a float or anything like that. It’s simply it’s very basic, it’s basically just got those basic sorts of data types, usually you’re gonna see things like ASCll, or you’re gonna see things like the actual size, like Word or halfword, or bytes or something like that.
请注意,我们不会说它是一个整数,或者它是一个浮点数或类似的东西。很简单,它基本上只是有那些基本的数据类型,通常你会看到像 ASCll 这样的东西,或者你会看到像实际大小这样的东西,比如 Word 或 halfword,或 bytes 或类似的东西.
So
in this case, now, we can just start listing off the numbers we want to put inside of our lists. So I’ll just put some random numbers in, it doesn’t really matter what numbers you use, I’m putting in some positives and some negatives, so you can see the different types that exist.
所以在这种情况下,现在,我们可以开始列出我们想要放入列表中的数字。所以我只输入一些随机数,你使用什么数字并不重要,我输入了一些正数和一些负数,所以你可以看到存在的不同类型。
Now,
the very first thing that I need to do is l need to be able to retrieve where this list is located in memory. And what we do is we typically look for the first entry in the list, and then everything is going to follow sequentially from there.
现在,我需要做的第一件事是我需要能够检索此列表在内存中的位置。我们所做的是我们通常在列表中查找第一个条目,然后一切都将从那里依次进行。
And
basically, what’s going to happen is that they’re going to appear in every single like slot in memory sequential from the first one. So you’ll see that when we actually load up this program. So in order to get the first memory location into a register, we want to enter registers that we can actually work with it and manipulate it, it’s easier to work in the registers than it is to work in the stock.
基本上,将要发生的事情是它们将出现在内存中的每个类似插槽中,从第一个插槽开始。所以当我们实际加载这个程序时你会看到。因此,为了将第一个内存位置放入寄存器,我们想要输入我们可以实际使用并操作它的寄存器,在寄存器中工作比在库存中工作更容易。
So
we want to get it into the register first. And to do this, we’re going to use an instruction known as L D R. And what ldr lets us do is it lets us load data from stuck into registers. That’s the main purpose of it. So I’m going to load into register R0, the location of the first value in our list variable. So this equals list indicates that I’m dealing with this list in my data section. And what it’s going to do is it’s going to find out where this first value is located. And it’s going to place it into registers zero. This is known as direct addressing. And this is how we essentially initialize the location of our list. So let’s Compile and load and see what.happens.
所以我们想先把它放进寄存器。 为此,我们将使用称为 L D R 的指令。ldr 让我们做的就是让我们将数据从卡住的寄存器加载到寄存器中。 这就是它的主要目的。 所以我要加载到寄存器 R0,我们 list 变量中第一个值的位置。 所以这个等于列表表明我正在我的数据部分处理这个列表。 它要做的是找出第一个值的位置。 它会将其放入寄存器零。 这称为直接寻址。 这就是我们本质上初始化列表位置的方式。 所以让我们编译并加载,看看发生了什么。
接着我们就可以利用 R0,去获取 list 中的数据。
在 R0 上面加地址偏移,获得 list 的值。
So
the last two types of addressing methods that we’ll talk about here are known as a pre increment and post increment. So I said previously, the first one that we have here is the same as doing like lists that Register 0 plus one. With a pre increment, what we would be doing is we would be incrementing, Register 0, and then we would be accessing the value of Register 0, so we essentially increment it, and then we check it. So that’s the way that a pre increment works. So it increments before it actually gets the value.
因此,我们将在这里讨论的最后两种寻址方法称为前增量和后增量。 所以我之前说过,我们这里的第一个与 【R0 + 1】 的类似列表相同。 对于预递增,我们要做的是递增 R0,然后我们将访问 R0 的值,所以我们基本上递增它,然后我们检查它。 这就是预增量的工作方式。 所以它在实际获得价值之前会增加。
And
the way that this works is we just put an exclamation mark after this. So it’s the same sort of syntax, we just put an exclamation mark that indicates this is a pre increment now.
它的工作方式是我们只是在这之后加上一个感叹号。 所以它是同一种语法,我们只是放了一个感叹号,表示现在这是一个预增量。
And
now finally, we’ll talk about our post increment,our post increment is very similar to our pre increment just different than when we actually increment the value, right” />
The reason why we got the value four is because remember, Register 0 started at the beginning,which is 10. And then we ended up accessing at that location giving us four and then we increment it afterwards.So remember, the increment comes afterwards, rather than before. So that’s the main thing to keep in mind with that.
我们得到值 4 的原因是因为请记住,寄存器 0 从开头开始,即 0x10。然后我们最终在那个位置访问给我们 4,然后我们将其递增。所以请记住,递增是在之后, 而不是以前。 所以这是要记住的主要事情。
三、算术运算
如下,0xfffffffe 可能是一个 “极大数” 0xFFFFFFFF 减去 “极小数” 1 的结果,也可能是 “小数“ 5 减去 “大数” 7 的结果的补码。
那么我们如何区分这两种情况呢?答案是使用 CPSR 寄存器。
So
how do we get that to actually set because if you notice, when l compile and load this and I step through these instructions, nothing happens to the CPSR register” />
答案是在这种情况下我们必须使用一种称为 a 的特殊指令,我将其称为带有标志的算术运算。 它的表示方式是我们只是在指令的末尾添加一个 S ,所以在这种情况下是 subs 。 所以 subs 将设置 CPSR 寄存器中的标志。 你可能会想,嗯,为什么它不总是这样做 为什么减法运算不总是设置 CPSR 寄存器?
And the answer is because to set the CPSR register requires at least one additional operation, right? It requires us to actually load data into that redister. And that, you know, adds a little bit of overhead to the operation. If we don’t need to do that, then we shouldn’t. So that’s why there’s two separate instructions for this because one of them is slightly more efficient than the other and slightly efficiencies do matter when you’re programming at such a low level.
答案是因为设置 CPSR 寄存器至少需要一个额外的操作,对吧? 它要求我们将数据实际加载到该重新存储器中。 你知道,这会给操作增加一点开销。 如果我们不需要这样做,那么我们不应该这样做。 所以这就是为什么有两个单独的指令的原因,因为其中一个比另一个效率略高,当你在如此低的水平上编程时,稍微效率确实很重要。
So when these sorts of things happen, we want to be able to catch them as we did here. But we may also want to use that carry at some point later on. So if we want to add a carry operation, then what we need to do is we need to use the ADC operation, what this will do is it will add a carry to a result.
所以当这些事情发生时,我们希望能够像我们在这里一样抓住它们。 但我们可能还想在以后的某个时候使用那个进位 carry。 因此,如果我们要添加进位操作,那么我们需要做的是我们需要使用 ADC 操作,这将做的是它会在结果中添加一个进位 carry。
And what I mean by that is basically so if we were doing this, this operation here, the result would be Register 2 equals Register 0 plus Register 1 plus the carry. So the carry will basically be a one if this flag is set equal to one, or it will be a zero otherwise. So that’s the way this works. So.it’s either set to zero if the carry is not set, or it’s set to one if the carry flag is set. So that’s the way this ADC operation works. And there are other ones as well.
我的意思是,如果我们这样做,这里的操作,结果将是寄存器 2 等于寄存器 0 加上寄存器 1 加上进位。 因此,如果该标志设置为 1,则进位基本上为 1,否则为 0。 这就是它的工作方式。 因此,如果未设置进位,则将其设置为零,如果设置了进位标志,则将其设置为一。 这就是这个 ADC 操作的工作方式。 还有其他的。
四、逻辑运算
mvn 指令:
and(与) orr(或) eor(异或)
五、逻辑移位运算符
lsl r0, #1 // logil shift left one time
lsr r0, #1 // logil shift right one time
Now,
rather than having to do the move and shift in separate instructions, we can actually do them together. So what I can do is I can add on a third piece here. l can see LSL, 1, just like this, what this is going to do is it’s going to move the value stored in Register 0 into Register 1. And then it’s going to shift it by one. So let’s take a look at what happens.
现在,我们不必在单独的指令中执行移动和移位,实际上可以一起执行它们。 所以我能做的是我可以在这里添加第三块。 我可以看到 LSL,1,就像这样,它要做的是将寄存器0中存储的值移动到寄存器1中。然后将它移位一个。 那么让我们来看看会发生什么。
我们不需要在多个单独的指令中多次执行它,我们可以在一条指令中完成所有操作。
rotation right:ror r0, #1
六、条件与分支
So
the main way that we do these branching instructions is using comparators and branches. So it can be error is something that allows us to compare two values to determine whether or not they are greater than, less than or equal to each other. Branches, on the other hand, allow us to move around our program to different locations based on the result of comparisons. So for example, we might want to move to a different, part of the program if a result is greater than another, or maybe if two results are equal, we move to a different location in the program.
所以我们执行这些分支指令的主要方式是使用比较器和分支。 所以它可能是错误的,它允许我们比较两个值以确定它们是否大于、小于或等于彼此。 另一方面,分支允许我们根据比较结果在程序中移动到不同的位置。 例如,如果一个结果大于另一个结果,我们可能想要移动到程序的不同部分,或者如果两个结果相等,我们可能会移动到程序中的不同位置。
And you can see it right here, you see all the numbers from one to 10.And then afterwards, we get all of these A’s repeated.
Now it turns out in the case of my memory. I have all A’s as a representation of an empty slot in memory. So it goes to serve that if we have this set of A’s, we’ve reached the end of the list. So I’m going to continue to move through the list iterating until l reach something that is equal to this.
Now how we do that check is actually a little bit complicated,we actually have to do something sort of interesting here. Now, when we work with assembly, you might think that’s easy. I can just compare, you know that value, and then well be done. But the way that assembly, specifically arm assembly, handles literals is a little bit weird. So when we have those immediate values, on literals, they can only be a specific size. And above that size is typically two hex values. So if we have something bigger than two hex values, we have to be a little bit more creative with how we deal with this. And the main way that we’re going to deal with this is we’re going to use constants.
你可以在这里看到它,你看到从1到10的所有数字。然后,我们得到所有这些A的重复。
现在事实证明,就我的内存中而言。我将所有 A 作为内存中空槽的表示。因此,如果我们有这组 A,我们已经到达列表的末尾。所以我将继续遍历列表,直到我达到与此相等的值。
现在我们如何进行检查实际上有点复杂,我们实际上必须在这里做一些有趣的事情。现在,当我们使用汇编时,您可能会认为这很容易。我可以比较一下,你知道那个值,然后做得很好。但是这个程序集,特别是 arm 程序集,处理文字的方式有点奇怪。因此,当我们在文字上拥有这些立即值时,它们只能是特定大小。高于该大小通常是两个十六进制值。所以如果我们有大于两个十六进制值的东西,我们必须在处理这个问题上更有创意。我们处理这个问题的主要方法是我们将使用常量。
And now we can actually do our loop. So I’m gonna start by creating a label for our loop. And what our loop is going to do is it’s going to load the next value in the list. So what this will do is we’ll increment by four, and then it will load the value of that address into Register 1. And then it’s going to take a look at this value and see if it is equal to that location in memory that is empty. So this all these A’s here. So remember, we stored that in Register 3, so we’re going to compare Register 1 to Register 3. If those are equal to each other.we want to leave the loop. So I’m going to say BEQ exit, I’m going to put a label “exit” here. And the label “exit” isn’t going to do anything, it’s just going to be the exit point of this loop. Alternatively, if we have not reached the end of the list, so what I’m going to do is I’m going to add the value into Register 2, and then I’m going to loop agains, so I’m going to branch to the top of the loop.
现在我们实际上可以做我们的循环了。所以我要开始为我们的循环创建一个标签。我们的循环要做的是加载列表中的下一个值。所以这将做的是我们将增加 4,然后它将该地址的值加载到寄存器 1。然后它会查看这个值,看看它是否等于内存中的那个位置是空的。所以所有这些A都在这里。所以请记住,我们将它存储在寄存器 3 中,所以我们将比较寄存器 1 和寄存器 3。如果它们彼此相等。我们想离开循环。所以我要说 BEQ 退出,我要在这里贴一个标签“退出”。而标签“exit”不会做任何事情,它只是这个循环的出口点。或者,如果我们还没有到达列表的末尾,那么我要做的是将值添加到寄存器 2,然后我将再次循环,所以我要分支到循环的顶部。
So to understand this, what’s going to happen is it’s going to load the next value in the list. If that value is equal to the end of the list, so this set of memory that sort of is initialized, then we’re going to go to the “exit”, otherwise, we’re going to add that value to the total. And then we’re going to loop again. Now this isn’t the only way that we could find the end of the list.
所以要理解这一点,将会发生的是它将加载列表中的下一个值。如果该值等于列表的末尾,那么这组内存就被初始化了,那么我们将进入“退出”,否则,我们将把该值添加到总数中。然后我们将再次循环。现在这不是我们可以找到列表末尾的唯一方法。
七、条件指令执行
八、使用链接寄存器进行分支并返回
九、从堆栈内存中保存和检索数据
One
other way that we can use this is actually with return values, right” />
And
then we come back here, we pop them, we’d currently be pointing at Register 2. So what we can do is we can place it in some unused register, right? So if I want to get that return address value, I could say, lef’s place it into an unused register. And maybe, you know, Register 9 is unused for instance. So that would be able to place us at Register 9, instead of having to be you know, somewhere else, right?
然后我们回到这里,弹出它们,我们当前指向寄存器 2。所以我们可以做的是我们可以将它放在一些未使用的寄存器中,对吗? 所以如果我想得到那个返回地址值,我可以说,把它放到一个未使用的寄存器中。 也许,你知道,例如,寄存器 9 是未使用的。 这样就可以将我们放在寄存器 9,而不必是你知道的,在其他地方,对吗?