Mastering x86-64 Assembly: A Beginner’s Guide to CPU Registers

Mastering x86-64 Assembly: A Beginner’s Guide to CPU Registers

Ready to dive into x86-64 assembly? This beginner-friendly video breaks down CPU registers like RAX, RBX, and more in Yasm assembly. Learn how to use general-purpose registers, respect the ABI to avoid debugging nightmares, and optimize code by minimizing RAM hits. We’ll cover callee-saved registers, function arguments, and why you shouldn’t mess with the stack pointer. Perfect for coders looking to master low-level programming. Grab the free book mentioned and subscribe for more assembly tips! #AssemblyProgramming #x86

Introduction to Registers 00:00:00
x86-64 Yasm Assembly 00:00:04
Recommended Book 00:00:09
Move Instruction Example 00:01:04
64-bit Registers Overview 00:01:52
General Purpose Registers 00:02:24
Two’s Complement for Integers 00:03:37
Floating Point Registers Introduction 00:03:54
ABI Importance 00:04:55
Function Return Values 00:08:01
C++ to Assembly Translation 00:08:43
Avoiding System RAM 00:11:45
Optimizing with Registers 00:13:04
Callee Saved Registers 00:14:16
Preserving Registers with Push/Pop 00:16:40
Stack and Local Variables 00:22:00
Function Arguments in Registers 00:25:12
Stack Pointer and Base Pointer 00:31:29
Instruction Pointer (RIP) 00:36:30
Temporary Registers (R10, R11) 00:36:49
Accessing 32-bit Register Portions 00:38:29
Floating Point Registers Details 00:42:53
Conclusion and Call to Subscribe 00:45:35

Thanks for watching!

Find us on other social media here:

  • https://www.NeuralLantern.com/social

Please help support us!

  • Subscribing + Sharing on Social Media
  • Leaving a comment or suggestion
  • Subscribing to our Blog
  • Watching the main “pinned” video of this channel for offers and extras

Hello there.

Let’s talk about registers in assembly.

More specifically, x8664 Yasm assembly.

Okay, so first off, check out this book.

It’s a wonderful book.

The author is a genius.

It’s called x8664 assembly language programming with Ubuntu.

It’s free.

website and you know grab your copy it’s wonderful it’s released under a copy left license

author’s a genius whenever i’m trying to remember things about cpu registers for assembly

i always go to this section i say callie saved i search for callie saved i already had it there

callie saved and it ends up being section 12.8.2 register usage so before we do that let me just

that let me just give you a little quick example so you kind of know what I’m

talking about here hopefully everybody’s seen this instruction by now this is a

move instruction in Yasm x86 64 assembly it just moves data from one place to

another it takes two operands so you know we have like operand one and

operand two basically operand one is the destination operand so we could put

destination there if we wanted to operand two is the source we could put

that actually won’t, you know, compile or assemble,

but I’m just saying that’s what those positions are for.

So here’s an example of a register receiving the value 10.

We use the move instruction and we say,

we would like to move something into the REX register.

We would like to move the value 10 into the REX register.

And there you have it. There’s a move instruction. Okay.

So REX is a register.

that it is a 64-bit register in our 64-bit CPUs every single register or

sorry every single general purpose register and the plot registers they

have 64 bits available for us to use 64 bits like you know ones and zeros it’s

like a very very very big number a long time ago we had 32 bit registers and so

these are like you know twice as mathematically robust or whatever you

allows the registers to address, you know, the 64 bit memory addressing space,

which helps us go beyond four gigabytes of RAM in our modern computers,

helps us go far beyond what we can even achieve right now.

So we have a register called RAX and there’s a list of other registers on this

page here. So basically, you know,

we have the RAX register and the RBX register and the RCX register and so

forth. I have a hard time remembering all these names,

remembering all these names so I go to this book when I forget but otherwise

you know you can kind of think of like ABCD and then just surround them with

RNX you know our kind of for register and X for extra big maybe so we’ve got

the RAX RBX RCX and RDX registers and then we have a bunch of other ones

through R15 but first before we do that let me just emphasize that these are

general purpose registers to be used for integers or data you can put

characters in there for strings you can put just like you know whatever data you

want inside the machine we use twos complement to represent integers so it’s

it’s fine the instructions that you will do for addition and other mathematical

but don’t put floating point numbers in these registers because the computer the

system the machine won’t know how to operate on that data the thing is

integers are stored as twos complement I’m going to talk about that more in

another video and floats are stored in something called I triple E seven five

four standard which is just like a crazy different way of storing floating point

numbers that makes more sense for floating point numbers but because the

differently in the machine you can’t really use normal instructions like you

can’t add two floats together the same with the same instruction that you would

add two integers together you would have to use special floating point registers

and special floating point instructions I’ll talk about floats at the end of

this video I hope but basically just keep in mind these are general purpose

registers to be used for integers in data only okay so what am I talking

Okay, so what am I talking about?

What are we seeing here on the side here?

Why does it say return value?

What is going on with Kali saved and fourth argument and third argument and so forth?

So there’s a standard that you’re supposed to use when you program.

You can get yourself into a lot of hot water or you can dramatically increase your debugging

time if you don’t do this.

standard called the ABI which I think is short for application binary interface but basically I just

like to say the ABI respect the ABI there’s a plan that people came up with that says this is how you

should use these registers don’t use them in a different way and that way if everybody is writing

you know modules and they’re all going to interact together like I write a module you write a module

the abi then it’s it’s pretty much guaranteed that we can we can expect certain things like

our registers won’t become corrupted after a function call or that we’re not going to corrupt

someone else’s uh function or you know whatever right so you can also use this to benefit yourself

even if you’re the only person who is writing your code and you’re not calling on anyone else’s

function um then you know like how are you going to remember like what was i using this register

Was I supposed to save that one?

Was I supposed to preserve it?

How was I going to return data from that function?

Was I going to use this register or the other register?

It’s hard to remember what you,

what you yourself even did like a month ago.

So if you respect this plan,

then your functions will work not only

with other people’s functions,

but with your own functions from a month ago.

The other thing too, is we have system calls, right?

So you have like a system call.

Let’s say we want to move something into REX.

something into REX, maybe I want to exit the system or something.

Just forget about this.

This is not a system call video.

Suppose I want to do a system call, right?

The system call itself is also going to respect the ABI.

So if you start calling other

services in the system call instruction, like if you want to open a file,

close a file, read a file, write a file, whatever.

If you don’t respect the ABI and the system call is respecting the ABI,

might have some of your data corrupted so it’s a really really good idea to respect the abi

in fact think about it this way what if uh what if you wrote a bunch of functions for a long long

time and then like a year later you couldn’t you couldn’t remember what you were doing you know

last year oh what was the return register uh and it costs you a bunch of extra time debugging

eventually you’ll probably decide to write your write your own standard okay from now on i’m always

going to use this register for return values and i’m going to use this register for the first

and i’m going to use this register for the first argument and i’m going to write you probably

come up with a plan right that’s basically the abi the abi covers more than just how to use

registers but um register usage is covered in the abi so why would you put yourself through

a bunch of heartache and then eventually come up with your own plan when you could have just

been following the correct plan in the first place right so everybody should respect the abi

I wanted to talk next about the return value.

Okay, so let me write up a little function here.

Let me just say that we have a C++ function.

And we’ll just say it’s like void f.

And we’ll say it calls on g.

And then we’ll have another function called g.

And actually, maybe it’s not void.

Maybe it actually does return a long.

returns the number five or something. Okay.

Hopefully you’ve seen some kind of a higher level language before like C++

so that you can understand what’s going on here.

We’re just making two functions and one is calling the other.

And the second one is just returning a value to the first one.

So in assembly, the equivalent of this would be,

let’s make a label with the name of the function and then a little colon,

and then just put a return instruction at the end of it. Wham,

it, wham, you’ve got a function. It’s not going to work very well, but you do have a function at

this point. So F, uh, I’m going to try to copy the C++, uh, function. So it’s a really good idea.

Whenever you’re writing a function in assembly, try to imagine what the prototype would be for

C++ and just put it as a comment at the top. So I know how the F function is going to behave in

my assembly module because I put a comment up there just kind of reminding me what the prototype

for C++. It doesn’t really do anything. It just sort of runs. So that’s okay. And then I’ll make

another function down here. I’ll do the long G again in a comment, and then I’ll do the actual

assembly version. I’ll say G colon, and then I’ll return. So just by putting that comment up,

I kind of can remind myself now that the G function is supposed to return some kind of a value.

up here, you know, do how about let’s say, I don’t want to start adding a bunch of local

variables right now.

Pretend that we’re going to print the return value of the call to G somehow we’re going

to use C out or whatever.

We’re going to send it to a variable, whatever.

I’m just going to try to keep the assembly as simple as possible.

But anyway, the point is G returns something.

you know, a value in your assembly function.

Well, remember that higher level languages,

part of why they’re so awesome

is they do a lot of extra work for us under the hood

and they actually provide illusions to us

that make programming easier.

For example, there are no functions

going on inside the actual machine.

I mean, I guess from a certain point of view there are,

but basically the machine is just sort of like moving data

and jumping to instructions

and jumping back from somewhere.

And, you know, it’s just sort of like jumping around

and executing instructions.

and executing instructions there’s no actual function it’s so it’s like if you want to pretend

that you have a function in assembly well there is a return instruction that will help you jump

back to wherever you came from most recently but we have to we have to implement more is what i’m

saying so how do we return a value in an assembly function we just have to load up the return value

it as return value that just means we have to load rax up with something so for example we wanted to

return the number five so i’m just going to load it up with the number five and then return

now we’ve pretty much translated a c plus plus function into uh an assembly function i mean

that’s really what’s happening under the hood when you compile your c plus plus program

it’s um it’s just you know translating all the c plus plus into assembly

Another thing that I should probably point out is that you should use registers as often as possible

and you should try your best not to touch memory.

Of course at some point you have to touch memory when you want to save your final result or send

it off somewhere or whatever but you know imagine that you have an assembly function and you’re

doing lots and lots of calculations you’re performing an algorithm or something your

program will be so much more efficient by like a factor of a hundred or probably more if you don’t

if you don’t hit system ram because every time you hit system ram like a global variable or the

stack or something then um your cpu you know typically in the uncashed uh scenario your cpu

has to go talk to the system bus on your motherboard and then you know send a message

to system ram and then wait for the system ram to figure out what it’s doing and then get a response

back so when your program is executing on the cpu you can encounter a stall which is like your

like a hundred clock cycles or more like it’s a long time at least in the most basic case

and how do you avoid that just use registers only when you’re doing lots of calculations the

registers are built into the cpu they’re part of the cpu’s hardware so they are lightning fast

compared to system ram or your disk or whatever

i probably say this a lot but you know part of the reason you would even want to code an assembly

Maybe you have a big program that does a lot of stuff and eventually you profile the program

and you realize, Hey, this one part of the program is really slow because it gets called

constantly and it’s not super efficient.

That might be a good use case for writing a hybrid program where you have multiple modules.

Some of your modules are in a higher level language and some of your modules are in assembly.

So you take your most important function that slows down your program that gets called all

like a big loop or something and you rewrite it to assembly so that you can

have more control over how often you touch system RAM to try to make the

whole thing more efficient and you know reduce the number of instructions and

just like whatever and you can improve the product of the efficiency of your

program so anyway you want to try to avoid hitting system RAM your registers

are built in the CPU there’s 64 bits that gives us 64 bits of address space

can reach you know back in the day we had 32 bit systems that means you could only have a memory

stick that was about four gigabytes so now we can go much further so um yeah that’s what’s going on

with our registers on the cpu back to the return value okay so we have return value here goes into

the rax register and then the next thing here is it’s saying that the rbx register is something

remember we said that uh we have to respect the abi right like you have to respect this convention

uh because if you don’t you’re going to cost yourself debug time and your

functions probably won’t be interoperable interoperable with other people’s functions

or the system call uh instruction well so one of the things about the abi is some of the registers

are designated as the function that is being called has to be the thing that saves the register’s

registers value so that it doesn’t corrupt for the caller. Imagine this if I had a function

called f I’m going to do like f and g here suppose the function f is going to move something into

let’s say r12 or actually let’s do rbx move the value 10 into rbx and then it’ll call g for some

reason maybe g doesn’t take any arguments maybe g is just kind of doing stuff let’s just say that

g just moves a different value into rbx so the thing is when the call comes back like after we

get to this next line let’s say we’re going to do something with do something do something with

rbx by the time we get to this line dude can i get the line numbers on this oh yeah i forgot okay

so by the time we get to line seven uh the register rbx is ruined these registers are not

local variables they’re not like uh you know tied to any scope or function call these registers are

just basically global variables that are sitting on the cpu for lightning fast performance but that

means the rbx i use inside of f is the same rbx i use inside of g so that means if g messes up the

value of rbx and then if f calls g then f now has a bad value for rbx this is a broken program

broken program because we did not respect the ABI.

The ABI says that RBX is callee saved,

which means if I use RBX in the G,

then I have to preserve the value so that by the time I return,

the value is the same as it was when the call first came in.

So how do we do that?

We’ll do that with a push pop pair.

I’ll probably make more videos in the future about pushing and popping,

but I’m going to try to keep it simple for now.

We just say, let’s push RBX onto the stack.

at the beginning and then we’ll pop it off the stack at the very end.

This basically means we’re going to take the value of RBX.

We’re going to send it onto the stack.

So, so yeah, we are hitting system RAM.

It’s a little slower at that point.

The ABI will save us a little time for the other registers.

I’ll try to explain that in a second, but basically we’re preserving the value here

with push and then we’re popping it back off.

So we’re restoring it.

So that means even though we ruined the value at line 14, when F does its line

When F does its line 7, it’s going to have the correct value of RBX.

It’s going to have the value 10.

The book and I like to call this the prologue, just meaning this is a little section of code

where we’re going to set things up.

We’re going to start getting ready to do more instructions.

And then this at the bottom is going to be called the epilogue.

This is just like the finale.

We’re like, we’re kind of cleaning up.

We’re finishing up right before we return.

If you wanted to return a value from G, then you could put, you know, the move inside of

RAX somewhere else.

Like you could put it like below the epilogue or just above the epilogue.

It’s fine.

As long as you’re sure that RAX is not getting trampled upon.

So it’s important to note also that if I do a, let’s say a system call right after that,

let’s say I call G and then I do a system call with, you know, like, you know, some

you know some other number as the call code i want to open a file i want to close a file

i want to read or write a file whatever i want to ask the system to do something for me the

system is also going to respect the abi which means i’m guaranteed that rbx is not modified

when the system call comes back if if the system call you know if the syscall instruction didn’t

respect the abi then my rbx could be ruined i’d have to do a bunch of stuff to preserve it so this

some of these registers let me go down a little bit here like this R10 the

temporary register and also the RDI if you decided to use that in the body of

your program which you’re allowed to these are not designated as callee saved

which means system call could actually ruin the value of those registers so

suppose for the sake of argument I really decided that I needed to use I

10 because that’s marked as a temporary I’ll put a value in our 10 and then I’ll say you know

do something

With our 10 I’m sorry our 10

Then that means by the time I’m done with my call to G

I should assume that our 10 is ruined because even though you’re you can see right here that that G doesn’t actually do anything

To our 10 we should assume that we have to respect the ABI which means the other function could have ruined it

Ruined it. This is especially true if you call someone else’s module or system call or whatever same thing for the system call

so if I call system call

and I use

R10 at some point before and then after then I have to assume R10 is destroyed by the time I get back in system call

So that’s no good

The cure to that when we’re talking about a temporary or something that is not designated as callee saved is that the caller has to save

hit a system RAM just to make a call.

Anything that we think we need when we’re finished.

So I’m going to do a push.

This is not the only way to do it, but I’m going to do a push R10 here.

And then afterwards, I’m going to do pop R10.

So that sucks.

And then I have to do the same thing for the system call.

I can go push R10 and then pop R10.

And of course, if you were clever, you probably would, you know, put the call to G inside

of that system call push pop pair.

So you could have one less push pop pair.

push pop pair but I’m just saying there’s no guarantee r10 is going to survive so you always

have to preserve it if you ever want to use it again so this is like another way that the abi

kind of can help you save time notice how here in the uh in the g function which I probably should

have prototyped let me just say it looks like to me it’s a void and it doesn’t take any arguments

to prototype your functions in comments.

Maybe let me do the same thing to F up here.

So F looks like it’s not returning anything and it’s not taking anything.

Okay. Whoops.

Those are C++ comments that would not compile.

All right. So notice how I’m using RBX.

So I decided to preserve RBX, but there’s also other registers that are marked as

marked as Kali saved like R12 through 15 and RBP and whatever,

but I’m not preserving those.

The reason I don’t have to preserve those is because I’m not using them.

So the, uh, the, uh, the ABI can save you a little time here.

Like for example, I’m preserving R10 because I’m supposed to assume that I,

that I, that they could be destroyed by the time the function comes back.

What if instead of using R10, I used R12.

have to surround any calls with a push pop pair because I can trust that whoever is going to

modify R12 will preserve it for me. But then notice down here G is not actually using R12.

So G doesn’t even do push pop on R12. So we save hits to memory. We don’t even have to touch memory

to preserve R12 because the ABI is helping us sort of stay a little bit more efficient.

So this is great, right? F is a broken function at this point though, because I’m using

at this point though because I’m using two registers that are callee saved I

can’t assume that whoever called F is gonna know that I’m modifying RBX and

R12 so I should do a prolog and an epilog up here I’m gonna say push RBX

and then I’m gonna push R12 and then down here at the bottom I’m gonna do an

epilog epilog and I’m gonna pop RBX and I’m gonna pop R12

but I’ve done something wrong.

The thing is, I’m not going to talk about the stack too much in this video,

but the stack is a particular type of data structure

that will return to you data in a reverse order than you sent into it.

This is great for helping us keep track of function calls and return addresses,

as I’ll probably try to explain in this video.

But basically, the data comes out backwards.

The data comes out backwards. So if I do it like this

Then actually by the time the return statement instruction gets executed

RBX will have the value that was intended for R12 and R12 will have the value that was intended for RBX

It’ll be backwards. So you have to do your push pops in the reverse order to notice how

It’s kind of like a shell that goes outwards. It’s like R12 is first on the inside and then RBX is next on the outside

That’s just what you have to do to preserve everything.

So the F function works now.

The G function, I think it works now.

We have a prologue and epilogue.

Okay, so we talked about the ABI.

We talked about calls.

We talked about the registers.

Talked about callee saved.

We talked about the fact that we don’t have to save the other ones.

I think I can talk about…

um so uh one thing that i should make sure to mention is that these are general purpose registers

they’re to be used for integers integer data or like characters or just like regular data

they use certain instructions that are not to be used with floating point data so you actually

should not store floating point numbers inside of your general purpose registers you should only

store floating points in special registers which i’ll talk about at the end of this video

So keep that in mind.

Floating point numbers are stored differently in the system.

They’re stored with a scheme called IEEE 754 floating point.

So like the numbers wouldn’t make sense.

Integers and floats are stored differently.

So the hardware is wired differently to operate on two different types of data.

So anyway, I just want to say that this is only for integers and general purpose data.

So now let’s look at the other types of registers.

rc x and rd x and rsi and rdi are designated as arguments first second third and fourth

okay so let me uh let me write like a quick c plus function here and this is going to be uh let’s

say we have like a function that returns some value we’ll call it uh we’ll call it f and we’ll

say that f takes in some arguments let’s say that it takes in three arguments um again uh

integers right now because we’re only using integer registers.

We can’t use floating point arguments or return values.

Just keep that in mind.

So I’m just going to do like three arguments long A and B and C.

OK, so we can take in three arguments and maybe we’re just going to return,

you know, a plus B plus C.

OK, simple C++ function.

Let’s do this in assembly.

We’ll make a label and we’ll do a little comment that just reminds us.

of sorry of the

Function that we’re trying to implement we stick a little return instruction at the end of it so that

it will jump back to the caller and

then we’ll say

Well when the caller of this function

Called us and gave us a and B and C. How do we get a and B and C?

Well a is the first integer argument, so it’s just going to be the RDI register

So we’ll literally just use a move instruction

just use a move instruction.

Let’s say we want to, for whatever reason, use the R12 register

and we’ll do something with it later.

We’ll print it, we’ll save it, we’ll do whatever.

Let’s just grab the incoming argument.

So we’ll just grab RDI.

So I’m going to put a little comment up above here, you know, grab

a and do something

with it and I’ll just write down here more instructions just to just to denote

Just to denote that we’re doing something with the a value, but I’m not going to write it down here because I don’t want this to get huge.

So we’ll do the same thing with the b.

Maybe I should remember that programming is case sensitive.

We’ll do something with the b.

It’s not RDI.

It’s the second argument.

So that’s actually RSI.

And then we’ll do something else.

We could have used R13 or other registers if we wanted to.

I’m just showing you how to grab incoming arguments.

So then we’ll grab the c and do something with it also.

C and do something with it also it’s not going to be RDI it’s going to be RDX and you can do this

up to like I think six arguments let me just double check here yeah so like R9 ends up being

the sixth argument and if you want more than that then the caller will have to put stuff on the stack

and in fact if you if you kind of like understand what’s happening here let me just do like a main

Let’s say we do cout whatever f returns.

You know, this is like a typical function call, right?

So we’ll call f.

f will jump in there.

It’ll jump into its body.

It’ll do some sort of a manipulation with the incoming arguments.

It’ll return a final value.

Then that final value gets printed out.

So what’s 5 plus 6 plus 7?

That’s 11 plus 7.

What is that, like 18?

I’m bad at math.

So let’s just say that’s 18 gets printed.

So what happens is, um, we return, Oh, I forgot to return.

I’m going to say compute the, uh,

compute the sum of the things.

Uh, and I’ll just say like block cause I don’t want to put a bunch of addition

instructions here. Uh, and we’ll just like store in, uh,

in R13, let’s say. So then when we have finally computed our result, I’ll just move R13 into the

return value. And what I’m really trying to say here is if you imagine a function that behaves

this way in C++, then what I’ve written down below is really what’s actually happening in

assembly. Your compiler compiles the code down to assembly language first, and then it assembles it

down to machine code. So really this is what’s happening under the hood. It’s not like a special

under the hood it’s not like a special trick when you call a function in C++

literally the A B and C have been loaded up just before the the jump

instruction to go to that you know line 9 they’ve been loaded up with the

appropriate values so that’s part of like you know the magic of what higher

level languages give us it’s just makes things a little bit easier okay so I’ve

If I had only used RDI and RSI and RDX, those are not designated as callee saved.

So I wouldn’t have had to preserve those, but because I chose to use R12 and R13, I

have to preserve those for the caller, even if the caller is not going to use them.

So I’m going to do, I’m going to do push R12 and I’m going to do push R13.

And then at the bottom, let me do like a prologue here.

is kind of helpful because it kind of helps you remember like, oh, did I forget the prologue?

Did I forget the epilogue? They got a match. And then I guess the fact that you can see the words

prologue and epilogue also helps remind you that everything is supposed to be in reverse order

in the epilogue. So again, notice how R13 is on the inside and R12 is on the outside

of the push pop pair. So now my program is good. And we’ve, you know, successfully translated

We’ve, you know, successfully translated a nice function call there.

And we’ve done a bunch of nonsense in the middle that I’m not writing down at this point.

We are respecting the ABI.

So if somebody else uses our function in their module, then they can be pretty confident

that we’re not going to ruin their registers and so forth.

We talked about the return value.

We talked about that these general purpose registers are not for floats.

We talked about pushes and pops.

and pops. We’ve talked about function arguments,

which right now we can only do integer arguments and return values.

We’ve talked about, uh, well, he talked about the stack a little bit.

So let me, let me just point out that, um,

suppose you have a function. I just want to mention this briefly.

Suppose you have a function F let’s say it’s, it’s void.

So it doesn’t return anything and it has an integer a equals five.

I think the reason that I wanted to do this is to show you how dangerous it is to mess

with the wrong register like the stack register.

So suppose we have two variables and then we’ll do something with a and b.

So now suppose we have two threads.

We have an execution thread one and an execution thread two.

If you’re not familiar with threads, that just basically means your computer is literally

executing your function or sorry, it’s executing twice at the exact same time.

Maybe not the exact same time if you only have a single core on your CPU, but you could

imagine that that’s possible, especially if you have many cores, that’s an operating systems

video.

But basically pretend that we have an execution thread.

So it’s like going through your program, executing one instruction at a time.

Then you launch another execution thread that also executes your program one instruction

at a time.

at a time so at some point it’s possible that um thread one and thread two could be executing f

at the exact same time right so i’m not going to talk too much about this because this is not an

os video but basically this could create a race condition where thread one and thread two kind

of step on each other’s toes and they both try to modify the value of a uh at the wrong time and

because the other thread modified it.

So this is why local variables in a function actually live on the stack.

A and B are not global variables.

They’re actually temporary local variables that are sitting on the stack.

And the way the stack works, I’m not going to talk about it too much in this video,

is that, well, every function kind of has its own area of the stack.

So when the function comes in, on the stack sits the return address

return address so that the function knows where to jump back to when it returns and any local

variable that the function makes. So if you think about it, when thread one calls F, we actually

end up with a different version of A and a different version of B. We could imagine them

as being, you know, A sub one and B sub one. But when thread two comes in and, you know,

creates those local variables, we can imagine that we get two different versions of A and B also.

of a and b also so this is how the computer prevents multiple threads from stepping on each

other’s toes if you’re using local variables local variables are just local to the function

this also helps if a function calls itself a bunch of times maybe one call or another might want to

see a different version of a or b and it would be just incredibly difficult to keep track of that

if you were using globals but when you use locals well they’re just basically different variables

trying to lead up to is the stack is pretty important if you corrupt the

stack then you probably are going to crash the entire program you might make

it so that a function doesn’t know where to return from like when you do the

return statement it might jump to some other part of the code that doesn’t even

make sense and then everything crashes or you might mess up some data in a local

variable like if you mess up if you mess up the stack you might be messing up the

that the caller is depending on and so then the caller continues to execute and

it sees the wrong value and it just crashes or you might even mess up your

own local variables so don’t miss with the stack the reason I’m saying that now

is because this very this register right here the RBP sorry that’s not it the

RSP register that’s the stack pointer eventually you can learn how to

manipulate the stack pointer in order to create your own local variables that’s

variables that’s okay once you know what you’re doing but unless you know exactly what you’re

doing you probably shouldn’t touch the stack pointer especially you shouldn’t mess with it

right before you call a function or right before you return from your own function be very careful

with that the rbp uh register is similarly dangerous uh it won’t like automatically destroy

your program but it is uh it’s usually i mean it’s quite often used as sort of like a bookmark for

mark for where the stack pointer was pointing in other modules and other

functions.

So if you fail to preserve the base pointer,

the RBP and then you returned from your function,

then you might have actually messed up the stack pointer for the caller or some

other caller somewhere in like the ancestry of your call graph.

So be very careful. I mean,

I guess all callee preserved variables or registers you should be very careful,

careful about, but these are like kind of the two worst ones to forget about.

first ones to forget about.

There’s also another register that is not listed on this page called the

instruction pointer. It’s RIP.

It basically is a register that holds

the memory location of the next instruction to be executed.

So if you modify that, then you just told your program to go execute

in some random crazy place and probably everything is going to crash.

R10 and R11.

I can’t remember already if I explain this, but I’ll just say it again.

But I’ll just say it again, R10 and R11 are just temporary registers.

You don’t have to preserve them.

But the callee, if you call another function, they don’t have to preserve the registers either,

which means it can be really fast to use R10 and R12 if you’re kind of like already running out of registers.

If you don’t have to make a function call anywhere, so you know no one is going to ruin your R10 and R11,

then you can also use R10 and R11 to sort of like, you know,

reduce your hits to system RAM to speed up your program.

So R10 and R11, they should probably be used last.

R12, 13, 14, 15, I usually use those first.

Once I start running out of those, Kali saved.

Then I’ll start eating into the arguments.

I’ll say, you know, I’m going to start using RBX

and then I’m going to use RDI and RSI and whatever.

It’s okay if you use, you know, the argument registers.

as long as you’ve already done something with your incoming arguments or you don’t have any.

So just keep that in mind.

You’re allowed to use them.

I mean, this is all just a standard.

As long as you obey what the rule is, then you’ll be all right.

The rule is you got to callee save those.

The rule is that one’s just temporary.

The rule is this one’s an argument and so forth.

Okay.

locals and how we use these registers. Let me try to just show you one more

thing real fast. So I said before that the size of the REX register is 64 bits

because it takes up the whole available register bits in the CPU but there are

other versions of this register that we could use. What if you only wanted to use

32 bits in your register? What if you wanted to pack like two different 32

32-bit numbers in the same 64-bit register.

You could do that.

There are also older modules and instructions

that will operate only on 32-bits.

So how do you reference only 32-bits of a register?

Well, if I search for, oh shoot, what was it?

Is it EDI?

No, no.

Shoot.

It’s in here somewhere.

This is like a good lesson in actually preparing before you record a video.

So I’m going to do architecture overview, CPU registers.

Oh, it was at the beginning of this section.

I always skip down to 2.4, 12 point something, and I should have gone to 2.3.1.1.

So obviously I’m not going to remember all of this, but basically notice at the top,

we have the REX register, all 64 bits of it.

But if you wanted to access the lowest six, sorry, the lowest 32 bits, then in your code,

instead of putting REX, you put EAX.

So for example, you know, move REX, whoops, REX, let’s say the number five, but over

here we’re going to move EAX, the number five.

five uh here it’s going to set the whole 64 bits to just like a bunch of zeros and then there’s

going to be a few bits that help represent five the lowest bits so it’s going to erase like the

whole thing all 64 bits but if you use the eax instruction then only the lowest 32 bits will

actually be modified which means whatever data you had in the higher 60 the higher 32 bits of the

of the register itself they’re just going to stay there it’s going to be considered

point so sometimes if you’re going to work with something that’s going to be using only 32 bits

of your register you better make sure that the other bits are uh are cleared out if you intend

to use rax right after that and and vice versa so keep in mind that we have other forms of the same

register a designation but just to let the cpu know that we only want to touch 32 bits worth we

And this, this kind of table covers all of the registers,

REX, RBX, RCX, and so forth.

I think it doesn’t cover the instruction pointer

and something else in there, but you know, most of them.

However, you know, for my purposes,

I usually just use 64 bits

because I’m not like super advanced

unless I have to divide or do something.

Okay, so that’s the last thing I wanted to mention

about the general purpose registers.

Let’s see.

Callie saved.

So the not sure if I said this before already, but basically the.

So just to emphasize the, uh, the RBP register is the base pointer register.

You’re probably going to mess up, uh, the caller because the caller often uses RBP as

a bookmark for the stack pointer.

mark for the stack pointer you definitely don’t want to mess with a stack pointer unless you

actually know what you’re doing in order to create local variables um there’s another variable

another register in here called rip which is the instruction pointer register so if you

if you mess that one up then the program is going to start executing in some crazy place that doesn’t

even make sense okay so now that we’ve talked about general purpose registers by the way i

called special purpose registers, not general purpose.

General purpose is like RAX, RBX, all these ones that you can just randomly use as long

as you respect the ABI.

But the base pointer and the stack pointer, those are kind of special.

And the RIP, the instruction pointer, that’s a special register.

You shouldn’t mess with those unless you really know what you’re doing or you’ll get yourself

in trouble.

But now that we’ve talked about those, let’s talk about the floating point registers just

real fast.

about using floats until a lot later but basically

i at least want to mention that we do have registers that will work with floating point

operations let me see there we go okay so it’s 18.2 floating point registers um essentially

we use xmm0 through xmm13 for floating point numbers there’s not going to be any

not going to be any crazy letters like RDI, RSI that you have to memorize. You can just use zero

through 15 and it’s totally fine. When you want to return a floating point number from a function,

you will load up XMM0 as the return value. You won’t even touch RAX. The arguments are the same.

They’re just kind of ordered. It’s like the first argument is going to be XMM0. The second argument

is going to be XMM1 and so forth all the way up to XMM15. If you need more than 15 arguments,

some sorcery with the stack or with some kind of system like the global variable or something

and also for floating point registers we use different instructions so what I really want

you to know because this is mostly about the regular registers is that if you have floating

point numbers and you want to multiply them or do some other operation on them the normal

instructions for regular registers won’t actually work you’ll have to look up a different set of

and i’m going to do this in another video some other time uh if you want to move uh data with

a floating point register you can’t just use the regular move instruction you got to use either

move ss or move sd um and and both operands have to be floats or actually i think the one on the

right can be memory but um move ss basically means a single piece of data single precision

So you can move like multiple pieces of data at a time.

I’m not going to really cover that.

Um, but you can move single precision floating points, which means there are 32 bit floating

points or you can move double precision floating points, which means they’re a full 64 bit

floating point number.

There’s also bigger registers, but I’m not going to talk about that.

Yeah.

Well, yeah.

Later processors 256.

I think mine has that, but I’m not going to talk about that.

I think mine are like YMM or something.

So yeah, with that, those are the basics of registers and some stuff to keep in mind.

In future videos, I’m going to talk more in depth about making functions and calling functions

and all that stuff, respecting the ABI.

But for now, here is your primer.

I hope it was useful on CPU registers.

Thank you for watching this video.

I’ll see you in the future.

I hope you had a little bit of fun and you learned a little bit of stuff.

hey everybody thanks for watching this video again from the bottom of my heart I really

appreciate it I do hope you did learn something and have some fun if you could do me a please a

small little favor could you please subscribe and follow this channel or these videos or whatever

it is you do on the current social media website that you’re looking at right now it would really

mean the world to me and it’ll help make more videos and grow this community so we’ll be able

longer videos, better videos, or just I’ll be able to keep making videos in general.

So please do me a kindness and subscribe. You know sometimes I’m sleeping in the

middle of the night and I just wake up because I know somebody subscribed or

followed. It just wakes me up and I get filled with joy. That’s exactly what

happens every single time. So you could do it as a nice favor to me or you could

you could troll me if you want to just wake me up in the middle of the night.

Just subscribe and then I’ll just wake up. I promise that’s what will happen.

Also, if you look at the middle of the screen right now, you should see a QR code which you can scan in order to go to the website

Which I think is also named somewhere at the bottom of this video

and it’ll take you to my main website where you can just kind of like see all the videos I published and

services and tutorials and things that I offer and all that good stuff and

If you have a suggestion for

Clarifications or errata or just future videos that you want to see

please leave a comment or if you just want to say, Hey, what’s up, what’s going on? You know,

just send me a comment, whatever. I also wake up for those in the middle of the night. I get,

I wake up in a cold sweat and I’m like, it would really, it really mean the world to me. I would

really appreciate it. So again, thank you so much for watching this video and, um, enjoy the cool

music as, as I fade into the darkness, which is coming for us all.

Thank you.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply