 Hey guys, welcome back Let's get zone series another Friday another video topic today. It's going to be everything the basics at least of assembly language for x8664 Including in the API that we're gonna be using as well as syscalls and how they work for Linux and BSD at least Okay, usual disclaimer. This is not an educational resource. So no learning allowed. Don't do it now What's a register now in my opinion? registers are leprechauns and they are very small and they live in the computer and they keep track of numbers using switches Which is cool, but it means they only can count to two, you know It's off and on and so if they want to have the number like three They have to use two switches and they have to put in 11 in there. It doesn't make any sense But they can figure it out. I can't Anyway for us as far as we're concerned a register is basically a processor-wide global variable that you can use to do math You can add numbers and registers. You can put values from memory and registers You can put values from registers into memory and pretty much like all of something programming is just going to be using registers to move the numbers around so Yeah, and what can you tell is that it has to be shared So all right rx is a register that has to be shared across all functions And so you have to be careful You know about what you do with it at any given moment because it's not just yours in your program But it's also being used by other functions that you're calling inside your program So you have to keep that in mind on x64 you have 64 What am I saying? So you have a 16 General purpose registers not really you have 15 some doesn't really count in my opinion And these can be used for all that math stuff I mentioned before and the cool thing is is that you can use Parts of the register to the I mean the full one is 64 bits But you can also use the low 32 bits right here or even some of them 16 bits and 8 bits I think for all of me can do that and Yeah, so that's useful if you want to just perhaps save some file size Some instructions for you know the the computer have shorter encodings Like if you use certain register with a certain size, it might be smaller than using a larger one And we'll talk about that in a future video, but keep that in mind Now into the ABI. So what's an ABI? I have no idea. I think it stands for like application binary interface I'm not sure basically it's a series of rules that you have to well, you're supposed to follow when you write software that's supposed to be supported by You know everything else with the system fiber ABI And so people that do UNIX systems or UNIX like stuff like Linux BST maxillaris They're like usually try to keep following the system fiber ABI They don't do a very good job some of the time, but they do a pretty good job most of the time and It's a very boring stuff But like Zima Gwai said, but the most important part is the calling convention that's describes like Basically, if you call a function, let's say you call printf or you call Square root or you call Hi-pot and do something right it describes when you call that function how the registers and the stack are allowed to change With in the function call. So once you know what the function, if you want to be compatible, it has to end up a certain way Otherwise it's not going to be compatible with the rest of the ABI and that's fine As long as you're smart enough and you can keep track of what you did But if you're like me and you're very dumb, you can't keep track and you're supposed to follow an ABI That's the point all the other rules are useless. So screw the ABI except for this one one part Now Joe Biden has a good question here. What if I don't follow a calling convention? Good question Joe Here's a good example for you. So let's see at your drivers The red driver says I have to driven nearby the rightmost curb of this two-way roadway And the other car says me drive on left and of course, they crash Why because they follow different conventions? And so calling conventions save lives and lower insurance premiums Basically if it enables you to make sure that you follow the rules that someone else is following So when you when you call their code, they don't mess with your registers Basically and so now I'll look into the ABI Directly and I'll show you some key come some key features that are pretty cool. So let me pull that up So here is yeah, ABI stands for application binary interface, I guess I guessed right And this is an old one, but it hasn't changed really since then as far as I could tell at least not what I was looking at And there's everything is in here that you could ever imagine you got like function calling, you know Exceptions page size like that the initial stack, etc I'll look through different examples here that I thought were interesting So the first thing that's of value is this register usage table for your 3.4 That describes how those 16 registers and some other ones are supposed to be used And so we're not going to use any of these down here and today We're not going to talk about these either for any point registers. So ignore all of those for the time being We're only talking about rex through r15 and so for these registers you can see RDI is used for the first argument to function. So let's say you're computing the square Well, that's not a good example, but let's say you're computing like So you have a print function and you want to pass a file descriptor into that function and it's the first parameter it would be in RDI right so I got a parameter and a function would go into RSI Third would go into RDX then RCX then into RA and R9 So if you have six parameters, they go into the registers if you have more they go on the stack You can read about how that works. We'll talk about that later Then you have return value. So in this case, you have two return values typically only use one But you have one in RX and you have one in RDX So the first return value goes in RX and then in RDX usually that's not useful I can see some use cases, right? Maybe you wanted to have maybe an error value come out in RX And then the actual result in RDX Let's say you had a vector of integers and you wanted to find the maximum and also its location Maybe you put the maximum in RD in RX and location in RDX. Maybe that's an option Typically, I don't return two values I see I see ways that you would want to but I don't usually do that In general, I just I use data structures and we'll use that more in the series than this But either way you do have 128 bits of returning capability out of a function in this API And if you want to add more if you want to use RCX as a firm register, you can do that If you want to use R10, you can do that, but that's up to you to define your own calling convention Last night. I want to mention. Oh heart. Here's the stack pointer. We don't use that really That's just for the stack Is what it's called callee saved. You'll see that whenever it has the word callee saved, it also says yes So callee saved. Yes What I see it callee saved. Yes, callee saved. Yes, all those registers are callee saved What that means I'll talk about in a minute. It just means that in this case, they are preserved across a function call So you don't have to worry if you call function, you don't have to worry about that register changing values in that function Next thing I want to talk about is this process stack. We'll look here in the next video More closely, but basically certain things are passed in to the program on the stack including the arguments So if you had command line arguments, they'll pass in at or near RSP we'll talk about that in the next video and the last thing was The stack and so I'm not going to cover the stack in great detail in this video I have one slide I think on it basically all you have to know is that it grows downwards and whenever you push values onto the stack it grows down by that size and You can pop values back off the stack if you want to restore values to registers later on you can use it for extra parameters You can use it for like it has a red zone meaning you can use a certain number of bites for certain things people use that for like Printing short strings you can put them on the stack and then print them that kind of stuff You know it it's useful for We'll talk about that more in future videos Okay, with that out of the way, let's go back to our slide show So back to what is a call the verse caller save register? So I made I got this professionally commissioned by an artist Here's how this this lays out an example. So The screen guy says hey, it's time to change my fishbowl water I'll leave Goldie my goldfish in this bucket while I change the water. Oh No, something else came up. Hey red. Can you fill this bowl with fresh water while I check on the Tina's pizza rolls in the oven? Red says yeah, of course I can First thing red does is he takes the bucket and pours out whatever green left in there. Oh, I need this bucket I'll be poor. What's in here? I'll use it myself. Then he fills the bucket with fresh water Then he uses that bucket of fresh water to fill the bowl Great job. Green will be pleased. No, he won't be pleased. You just flushed his friend So this is a good like deep philosophic question, right? Whose job was it to save Goldie? Let me know in the comments in my opinion It was red's job because this red guy is total jerk. He just fussed a perfectly good goldfish But I can see arguing for green being the the responsible one too because he could have taken Goldie He could have put him in a in a big gulp container or you know Extra large McDonald's like cup and put him on a shelf and kept him safe But no, he left them in the bucket that he knew full well that red was gonna use so I can see an argument both ways Whose fault it was but either way, this is called the first color say register No, if you said it was red's job to save Goldie, that would mean that red would have said, okay Hmm. There's a goldfish in my bucket. I'm gonna take that that would be a call these saved I'm gonna take whatever is in the bucket. I'm gonna put it in a coffee cup And I'm gonna use this bucket to fill the bowl and then when I'm done I'm gonna get the coffee cup empty it back in the bucket and Give the bucket and the filled bowl back to green now if you said it was a caller saved register If it was green's job, that would be green coming along saying, hmm I know full well that red is gonna use my bucket and so I'm gonna take Goldie out of the bucket and put him in a Something else in a in a basin in the sink. I don't know with the drain up then Red can use the bucket and fill the bowl give the bucket back to me and once red is done I'll take Goldie out of the sink or out of the basin back in the bowl Back in my bucket and put it back in the ball, right? That's how that would work And so you can see the difference between the two in code It looks more like this and so I mentioned before this is kind of like a mass a matlab see like syntax basically registers are processor-wide globals, so they're defined outside functions and so with this basically this says Only one one function being executed, which is well one main function It's like print f so it prints out a number that number is the result of Function that uses RDI to compute something important with an input of three and you saw the calling convention Where RDI was the first parameter and so three is passed into RDI when you call that function and that function will return an integer in Rx and you can print that out with percent D. That's how this whole thing works And so this sub function Takes input parameter three And then it just seemingly just calls another function and gets a value for Rx It adds RDI, which was an input into Rx and Then it returns Rx And that's that would seemingly work Unfortunately, it won't work why because this is a processor wide global variable And so if you look at the sub function, which is some other function, which take no inputs. It's still tampered with RDI you see RDI was set to five and RSI was set to two and So even though you passed in three To the function the moment that this function is called RDI is set something else. You could have set RDI to a million at this point Once it gets to here, it's back to five So yeah, it computes the sum of that which would be seven Puts it into Rx for the addition, but the problem was it messed up RDI And so at this point, what is it Rx would just evaluate to what 12 Where you thought was going to evaluate to Three plus seven is 10. So you now your result is wrong And so how do you Avoid this? Well You use the stack and that's how push and pop work So there are two options So the first option, let me cross the the other option out while we're talking about this one. So ignore this The first option would be the caller save. So this would be green Green's responsibility. So he's gonna take RDI push it on the stack Call a function because he knows he's gonna need RDI later And he knows that it's very likely that this function is gonna break RDI It's gonna clobber RDI and so it's gonna push onto the stack Then pop it off the stack when it's done and do the math and that will work perfectly fine The other option would be not to do this it would be to Do a coley saved option So that would be doing nothing in the main function But then in the sub function knowing full well that we're going to tamper with RDI and RSI Let's just push them onto the stack first And then pop them off in reverse order when we're done, right? That would preserve the values as well. There's two options I'm more fond of this one in particular, but you can have your opinions So here's how the stack works with those two examples So whenever you call a function, you basically push the return address The address of the next instruction After that one was called onto the stack. So in this case, you have return address from the white function Then you've pushed RDI. This is for the color saved option the first one And then you got return address for the sub function And so the stack pointer points to the lowest element here in the stack and it follows it along. So as you get into the yellow function Then you return back from it, then you restore RDI and you return from the main function as well. So this is how that one works You can follow along, you know More closely with the debugger if you're curious And the caller call the same option is a little bit different. Basically, you don't push anything onto the stack But in the white function, you only do it in the yellow function And remember you've pushed RDI and then RSI. So when you're done, you pop RSI first Then RDI then you return from this address Then you turn from this address. So that's return to this address and this address. So that's how that works They're pretty much the same thing. Um, there's no real difference I think that this one is a little bit more More pure in my opinion a little bit easier to use A little bit nicer user friendly for someone that's just programming and not someone that's Making a huge like software Library Um So we're going to use our own custom I said a bi here, but it should be a column convention where we're just going to change a few things It should be compatible mostly with system five, but it May not be I'll I'll leave that on the table for now. Um So we're going to be extremists. We're going to make everything all registers that are not return values call a saved so Everything here is going to be yes Except if you return a value in rex then rex is going to obviously change If you use rex and rdx then those can both change. So Everything that's not returned value will be preserved. So it will make programming a lot A lot easier again, we're not going to use, um Boring registers like these ones in our code This is for like the old 20 point stuff and the new floating point stuff I like the the middle floating point stuff the X item registers. I don't like this huge stuff So yeah, we're going to do just the kind of like the basics of modern floating point And so this abi this column convention is going to be pretty much objectively worse because if you look we have to push and pop Every register all the time onto the stack that we're going to be using And that's going to add file size. It's going to add execution time. It's going to make our code slower and bigger Which is not good But it will also be easier to use and if we're smart about it It may not be all that much slower or bigger and i'll show you why in a couple of seconds and um Why is it easier to use it's because let's say you had a a function where you were calling a bunch of functions And all those functions took the same parameter. Let's say you had rdi was seven for all of these functions But those functions were all tampering with rdi Well, then you have to constantly be moving rdi Moving the value of seven into rdi before you call that function And that gets a little unwieldy when you have a bunch of functions with the same parameters And yeah, it's just a lot easier to expect that all Registers are preserved with the possible exception of return registers So yeah That's why it's better and worse. It's mostly worse, but also a bit better So here's a good example of a function that we're going to make in our own abi. And so Here's how I write functions in assembly. I kind of put a comment That's just what this colon means semi colon is a comment It's how I define functions. I always put the return value type where it is in brackets I always put my registers and brackets. I don't know why I would say I forgot for this just to save space but The function is called add up three numbers and it takes three inputs They're all longs and they're in rdi rsi rdx. So they follow the abi. They follow the convention system five Then you have this thing here. This is a label. This refers to this address in memory. This instruction Push is at address add up three numbers. This could this could be address 67 So if you if you ever jump to address 67, you'll jump to this instruction Or if you call This instruction It will do two things. It will push the return value of where you were and then it will jump here So that's that This function again just adds up three numbers symbol. It pushes three registers onto the stack And pushes three out. These are all the input registers, right rdi rsi rdx But let's look at the math, right? And again, we popped them off in the reverse order So rdx was the first to pop off. It was the it was the last push on So look at look at the math though. The first thing that happens was we take This is an add instruction So we're taking the value in rdx and adding it into the into the value in rsi and it stays in rsi So here's how that looks rsi equals rsi plus rdx. The next thing Takes the value in rsi adds that to rdi at this point You have rdi equals rdi plus the new value in rsi, which was rsi plus rdx So now rdi contains the entire sum Yeah And last thing is you take the value in rdi and you move it into rx And remember rx was our return value and so we can now pop everything and return That's how sort of this whole system is set up And this is fine. However, you'll notice that When in assembly at least in the intel syntax, I think that's what we're using These are the registers that are destination and these ones are the source meaning these will never change rdx, rsi, rdi are not changing in this part of the instruction But rsi, rdi, and rx are changing So in reality, we didn't remember we changed rsi here We changed rdi here and we changed rdx here, but we never actually changed rdx So you can get rid of this instruction entirely. You don't need this instruction It's useless. It pushes a value that we didn't change Now in addition So that's how you might think it's so bad. Wow, it calls slowbuses Like having to pop everything at the same all the time. It might be slow But the problem is if you don't change registers, you don't have to do that You don't need to you don't need to do these things Next This algorithm is just trash. I mean, why are we adding up things like this? If we did the computation in the return value, we could remove all of this And that's what this is on the right hand side. The first thing is we move the value in rdi into rx You can see Then we're incrementing it basically we're adding Um Rsi to it and we're adding rdx to it and they're returning. So again In this case, we were able to implement this instruction this uh this function without any pushes and pops yet We still follow our abi Our calling convention. So it's not all going to be always worse Okay next Topic is sys calls system calls. That's how we make the os do things that we can't do on our own Like we don't have the ability to write files without the system So the kernel can read files write files open and close files for us Remember printing to files is a right construction as well as reading from files That's how you reach from standard in that's you know, that's the same thing. Um file permissions Also a sys call stopping our program is a sys call. That's our topic today It's going to be stopping your program and some other stuff as well Here's a good depiction. You know, we are this arab slave master Our program is the whip he's using that we're using The kernel is the slave for us and the sys calls what we're forcing it to do in this case the wheat in the background So very simple depiction and very accurate about how this is working out Now there are different sys calls on Different os's on linux and bst. There's a there's a pretty good You know correspondence between the two like they pretty much correspond um And this guy has a website called uh the linux free bst sys call concordance and you can check it out if you're curious Basically, it kind of just highlights what it's the same and different between the two And here are a couple of major ones that we're going to use in this series We got read write open close exit and change mod ch mod You know, that's reading files writing files opening files closing files ending the process and changing permissions for a file And on linux here are the numbers we got zero one two three sixty ninety And then there are more between them. I just skipped them for this For the slide and free bst. They have different numbers. So you have three four five six one fifteen again um You know the orders different the numbers are different, but this they're pretty much the same instruction You'll see that the parameters are here in this column and the parameters look different, right? You see unsigned int file descriptor for read For linux on bst. It's int file descriptor on linux The buffer that you're reading bytes into is because a character pointer called user Or sorry, it's a character um pointer called buffer On linux. It's a void pointer called buffer This is the same thing. There's a different name and lastly you have the number of bytes Is called count of size tl linux and it's an n byte um Free bst so a different name, but it's the same thing in the same order. It was all that matters because How you use it is like this you put the syscall number in rax and then you put the Parameters following system five a bi so you put the first parameter in rdi x parameter and rsi etc So that's how that works and then all you do is you call the syscall instruction So you put the values in rx and rdi that you need in rsi and rdx whatever You call syscall and it will execute automatically. You don't have to do anything The os developer has already programmed all this stuff for you that you can use So it's like a library, but um It's a library that you actually need because you can't do this stuff the kernel won't let you you don't have the The right authority to do these kind of things So um one last thing about this is the syscall instruction There are a few instructions like this is that they clobber registers So rcx and r11 are going to change. So if you care about preserving those in which case, you know, we do Um, you have to preserve them because they're going to change during the function during the syscall during the kernel call It's going to change Um, and so you would do that again We would always do that except for exit because exit as a function doesn't um that syscall Doesn't ever return because it just exits the program, right? So you don't actually have to save registers in our abi In the exit syscall, you know, whatever you use to call that just because it's You know, it's not going to be useful because it's going to leave the program's going to end. So who who cares? And our last thing to cover here is a completely exhaustive assembly language tutorial for x86 Um, and here's how it looks now. This is the old architecture. You can tell it's old because it has you know The the co-processor, you know floating point stuff, which we're not going to use in this series We're going to use a little bit more modern stuff than that, but this is pretty accurate. Otherwise um So most instructions in x86 64 Still are well, they say it's a move instruction I would argue that the most common instruction is a knob instruction, which Doesn't do anything you just use for padding and putting space in your code making it aligned properly Um, but yeah move is the most common instruction by far over one-third of all Instructions are going to be moves move is basically move values from memory to registers from registers to memory from Immediates to registers between registers that kind of stuff. They can move values around One thing it can't do is it can't move values from memory to memory and It's interesting as to why that is if you think about the encoding, but I'll leave that for you on your own Then so that's move. Let me cross them out as we go Push and pop we already covered those use the stack to save values Call and return. That's how you enter a function. That's how you leave a function. Okay Then let's go to add and subtract those are just an increment Let's do what you think add adds things together subtract subtracts them and increment increases it by one um Compare and test those basically set flags. So one we'll talk about this later, but one of the big things in Like when you have loops and stuff and conditionals is You have to have a like say you had if a is greater than six Well, how does that actually work on the processor? Is everything about that? Well, basically you're doing a comparison or some cases a test And you can test if them if they are the same or if they're greater and you can set certain flags to use for conditional operations to come so In this case here, you can see there's jump equal and jump not equal Those are two conditional jump instructions. Also, there's jump below jump greater than or equal to jump above right, there's different ways to form this kind of stuff and um, yeah, either way it uh Compare and test set flags and then conditional jump Uses those flags to jump and then jump was our entire video one in this series That was our infinite loop. That's how you jump to addresses and it's a very common instruction I would say it's the most important instruction in in assembly language because it's the entire You know control flow of your program is isn't that XOR add and Sorry XOR and and or those are what you think they just do operations. We're not going to use these two We'll talk about floating point in a future video Shift left and shift right just move register values that number of bits So you can do shift left rex three that would move it to the left by three and infill zeros So yep, and the last thing here is lea I pretty much never use lea. I know what it does. I think used it once or twice It's used for array indexing. You can use it for fancy math tricks. I think that's what they could probably use it for mostly But we're not going to use it all that much when we do use it I'll put it out to you but not important really all that much Um, and then other things as well that you know, you might use are in the other So there you go. You covered pretty much 90 of all Assembly language in just you know a few minutes for everything else including these things check out this guy's website Let me get rid of those I'll show you it right now Basically, he has a listing from the intel manual that he tabulated really nicely with the script and it looks pretty cool Let me open it up for you for Now you know my hot key to change windows um Here so I just saw shift left somewhere shift left. Here's left. You can see a shift left in this case register Or memory I know you can do that By two. Okay, that's cool. No The multiplies up by two shifts at one right etc Interesting. I don't know you could do that Good to know Anyway, you know, there's you know push and pop are going to be here right push you got um Move sd. This is a 20 point instruction that you know use for that Everything you'd see here add right Oops Too many ads. I should put a space Add here's add. Yeah, so all the things you could click on them and see how they work And everything else you could ever be curious about is on this site how they recommend It's much better than the stupid manual manual sucks With that out of the way, let's go back to This so now we're going to do examples So I have some examples that cover the basics of assist calls and Most of these instructions not all of them, but most to give you an idea of how they work I'm not going to be great detail because you can always read the manual But just to give you a feel of how this stuff works. So yeah Let me let me show you this Make sure you can see it. So I have a couple of Examples here. So I have a I have an example to a so this is an this is a video two so example two a is the Way that we're going to use assist call to terminate our executable, right? That's just the basics example to be that's going to be the same thing just with a function and not Doing it manually and the last thing is going to be a vector sum That will kind of incorporate a lot more of the assembly language instructions that you need to know to write programs in x86 assembly, so that out of the way, let's open up the two a Oops Make sure I didn't even have anything extra. Okay, so let's open up everything And so what I've got here Is the same header as before as episode one All of the bytes are the same. I didn't change anything What I've got is I have a single include We're going to include this listing Which by the way, doesn't include any assembly language just has the definitions And then we have a couple of lines of code here before you enter this Let me get into what this sis call this thing is. So let me open up this so basically We're going to use a Bash script to evaluate whether you're on bsd or on linux And if you're on free bsd, it will include this directory if you're on linux It will include this directory and so you won't have to worry about manually changing things here and there If you download the code on free bsd or linux, it should just If it fits your ships, it should just work, you know, so on free bsd here, what's this calls defined Using the defined macro thing or whatever That that syntax on on nasa means you can define a value into a String and so on for bsd. We have these definitions on linux. We have these definitions I won't get into the details. These are for future videos Um, these are ones we're going to use, you know, upcoming With that out of the way, let me go back to the The code and so that's what this is. This includes that listing So now you have a bunch of strings that you can use and if you're on free bsd like I am It will it will use that whenever you call six exit It will put the right value in there if you're on linux, it will use the other value, right? I think on linux. It was um Hold on On linux it was 60 for exit on free bsd. I think it was one So we'll pull different value for different os Okay So all we're doing here is we're moving that syscall number from that table. Remember that table we showed Um into rex like we're supposed to actually let me show you that while i'm while i'm here I might as well We're putting this is called number in this case Uh 60 or one on free bsd into rex And then we put the return value or the error code on linux is the same thing Um into rdi, right? That's the first parameter. And so if I go back you can see We've put 1969 into into that so That's the year of the first faked moon landing But you know, there are a couple other ones as well And then we're going to call the syscall instruction Or I shouldn't say call. That's not very good to say we're going to Use the syscall instruction to execute that system call. And so This should return 1969 to our console. And how does that work? well If you if you ever if you've ever known if you if you echo out this This returns the output value output value of the previously executed command So that's how you can check the return values of things that aren't printing to the screen So we're going to use that if I execute that. Oh, sorry. Let me show you that. Um Let me show you that run that sh Basically, you can see it includes different if you're on linux It will put in linux here if you're on free bsd output free bsd in here So that's why it's kind of cost platform So yeah, if we run our code Again, this is going to assemble it with nasm change it to executable and then execute it And now if we echo out the results Wait, it was supposed to say One nine six nine. What's with with this garbage? Well, it's because it only Executes it only returns the low byte So yeah, this is the low byte of that number if you're curious. Anyway that out of the way Let's go example to be This is the same thing. It's just going to be with um a function call So Here you can see we've included sys calls once again. We also have another include and that is lib slash sys slash exit And let me show you how that works Here's exit Um And this is how we define functions at least how I do it I always put if not defined this basically means if I've not already included this file Let's say I had 10 different files and they all included This file. Well, this would only work If it was not yet Defined and so this is basically to make sure you don't include something more than once You'll have multiple things with the same label otherwise. So that won't work And so yeah, all programs I'm going to write all functions. I'm going to write are going to have um It's at the top if not defined in the name of the function Define the name of the function and then end that if So here's how it works. You have an exit label This is the address in memory just like start was remember start This is the address a memory of this function And so if we call exit it will it will jump here to this address And then all it does is it exits the program with return value in the low byte of rdi unsigned So it rdi already is going to contain that low byte return value And then we're just going to move rx We're going to move into rx this is exit sys call number Which on linux would be different and be for bsd and then we call this call So it's the same exact thing just in a function and not in the software itself Or not in the the main Program itself and so if I go back Make sure you can see Um, yeah, we move 69 Into the low byte of rdi and you could do rdi here if you want But I'm just going to refer to the low byte as di l which is you know what it's called Um, but it would work the same Um, it might be a little bit smaller to do it this way I'm not sure we could check that if you're curious and then we call exit. So let's let's do that. Let's um, let's run this And let's echo the output 69 so it worked it was a low byte so it worked now Let me look at the file size of that that was this is all live by the way. I've not prepared this, uh So the code of the binary was 135 if I go back in there I'm just curious if it's going to change if I go back in the code asm and I change this to rdi Is it going to change the file size? Yeah, so it's two bytes more So yeah, we just saved two bytes by using di l and not rdi So let me change that back before I forget You just saved two bytes did Tell your mom Okay, so that out of the way. Let's go to the third example. That was example c vector sum Let's check what we got here So in this case, I have three includes I have the syscall include I have exit and this is how we're going to be returning values until we can print things That will be in a video or two We'll talk about how to print things to the screen. That's actually not very easy and um Our other include is this lib slash math slash in slash packed sum Whatever that means it's a function that I just wrote that uh We'll compute the sum. I'll show you the function right now actually So here it is Again, we have the same if not def define and if you know And by the way, I should have also put something here I should have put in a line here. No, I'm thinking about it I should put a line 64 here or something like that But I'll get I'll do that in another video. I'll show you how that works So we have our function called packed sum What it does is it computes the sum of rdi packed signed Longs starting at the address rsi And so rsi points to the first address in memory of the first element Of the array rdi is the number of elements in the array and all the elements are longs So here's how it works Because we are possibly and in fact we are tampering with rdi and rsi I first push them onto the stack This is our api our own personal schizone api convention We're pushing these values on the stack and popping them off at the end And we're turning at the end And I have a label at the end called done that we can use to jump to if we ever want to jump to the end Now one thing I want to mention is that if you ever put a dot in front of a Label this is a label. It means an address. This might be a dress 2006, right? This would be a different address now The dot means that you can use this same word somewhere else You can't use the word packed sum 10 times in your code, but you can use dot Loop or dot packed sum. Why because this basically means Packed sum dot loop. It basically means From the last, you know real label. This is like a sub label And so whenever you have internal like Addresses that you're you want to you know jump to or use for loops and stuff Or for you know leaving the code or whatever you're doing whenever you have, you know control flow inside of a function Always start label with the dot Otherwise you'll have like conflicting labels if you use the word loop twice. So pro tip there So the first thing that happens is I Do this xor function Xor most of the time is not used for xor. Usually it's used for setting a register to zero Which is also how xor works if you think about, you know exclusive or So when I say xor rx rx that just sets rx to zero. So the whole three to four bits are all zero Then I have can you guys see this too small? Let me zoom in Then we have rax. We're good. Oh, sorry. Let me start here Then I'm so we made it zero Now rdi was supposed to contain the number of values in the array. So it could be seven could be a hundred Let's check if it makes sense if the ray has zero numbers You can't sum it up, right? It doesn't make any sense if it has negative 53 numbers. It doesn't make sense So it has to be at least You know one so we do is we use the compare instruction. We say compare rdi one And then certain flags are set such that when you'd say jump below That if rdi was below one This jump will execute and we will jump to done pop off the registers in return without summing anything And our return value will be zero right rx will be zero That will only happen if rdi was input with some bogus number Now if rdi was one or more We don't want to do this and so this won't actually execute because it's not going to be below one And it will fall into this dot loop Loop and this is how loops work in assembly. You usually have like a A condition that you're checking and then you have a um No operation that you're doing in the loop so In this case what we're doing is we're taking the value at the address rsi So this does not this does not add the value In rsi to the value in rex and put the value in rex like it did in our previous example This takes the value at the address in rsi and puts that in the register at rex after you know adding it So this will basically we're going to use this to add the value at the address in rsi to our running sum Which starts out to be zero, but it's going to become non-zero after the first iteration, right? Then our next instruction is add rsi eight. This moves our pointer rsi to the next element So if every element is a long Then every element is eight bytes And so if we if the first value was at address 16 the next one will be at address 24 So this will just allow us to push forward each time with the new element then Decrement rdi remember rdi was the number of numbers In our array and so let's say it was five Every time we execute the loop. Let's try to keep track of how many numbers are left So there was first five, but now it's going to be four then three it in two then one And decrement just does that it subtracts one every time And then when it hits zero decrement Also sets the flag it sets the flag There's that the you know jump non-zero will jump to loop if it's non-zero So if rdi was five will jump to loop if rdi is four will jump to loop if rdi ends up being three loop Two loop one loop once rdi hits zero this jump doesn't work anymore This condition fails. It's no longer non-zero zero is not non-zero. So we'll fall out into done and then you know We pop the registers in return. So this function as you would expect Does everything for summing up packed Insider vector With that out of the way Let me go back into our Code here and show you how this works and we're into the end of this video So we're almost done Couple of things remember we only have one Block of code whereas the boomers they like to have you know a data section and an instruction section like dot code section We had everything in the same section. And so We'll have instructions like move move call, right? Those are instructions. We'll have those in the Same section as we have value. So this vector Is is an address in memory, but it's not pointing to instructions like start and like Packed sum and exit those things point to instructions. This one points to memory as well But it's pointing to a number it points to number 11 And so what we're doing here is we're defining in this case One two three four five six quad words. So eight bytes long each numbers at address vector First one being 11 then 14 17 and so on Now what's our actual you know instructions doing first thing is we're putting the value six into rdi This is the number of values in our array. Now we put the address vector Into rsi Call packed some At this point rx will contain the packed sum of rdi elements at address rsi And now we can return that value by moving That returned value into rdi remember rdi was the return value for our sys exit and then we can call exit as before And so what this does Basically when we run this code and we say echo the previous result This will output the sum of those six numbers long story short. So if we close this And we run it and we echo 42 that was the sum of those six numbers. So With that out of the way, we've covered the basics of assembly. You guys now pretty much know Everything you need to know to do the basics of assembly programming, you know How to use sys calls how to read files write files how to exit programs You can look it up. Now you have resources to look it up. You know how the abi works You know how to interface with other programs, you know how to interface with printf You know how to interface with you know these timing functions if you really wanted to From assembly and I've also gone over just the very basics of assembly instructions And we'll get into a lot more detail in future videos, but I want to just leave it there for this video It's already getting kind of long 47 minutes. So With that out of the way, I hope you learned something. Um, actually, I hope you didn't that was against the rules I hope you didn't learn anything. Um And if not, that's good With that, I'll leave you guys and have fun playing around with the code on the suppository. I'll see you around