 In this video, we're going to start looking at memory. So far, all of the programs we've built have been reasonably small. They haven't needed a whole lot of memory, a whole lot of data. But if we want to implement larger programs, we're going to have larger amounts of data to work with too. So once you run out of space for your data in the registers, you have to start using memory. In the MIPS R2000 architecture, our memory is laid out like this. In the MIPS R2000 architecture, our memory is laid out like so. It's really a big long array of bytes. And this array is divided up into a series of different parts where each one corresponds to something we'd like to use our memory for. In contrast to some other architectures, we have separate regions for different types of data. So instead of throwing all of our code in with all of our data, we're going to put those in separate parts of memory. This has some advantages to it and some disadvantages. But for our purposes, this will make a lot of our memory really simple. We'll have this block at the bottom which is reserved for the operating system. We're not going to have to worry about it too much. We just know that the operating system will be using that. Then we have this text block. That's where everything that you mark text in your program goes. So all of the code goes into the text block. The static data region is for any pieces of data that you declared at assembly time. So all of those pieces of data that you actually put into your program, things like strings, will end up in the static data region. The dynamic data region is for everything that you generate at runtime. So this will be all of your local variables, all of your objects, everything that you don't know what that data is until you actually run the program. All of that will end up somewhere here in the dynamic data region. But our dynamic data region will have two different parts to it. We'll have a stack and a heap. The heap is pretty straightforward and we'll look at it in more depth later. But it's primarily for objects. This is where you would put objects, data structures, everything that you're going to use for a long time that's not really related to a single function. For now though, we're going to look more at the stack. So the stack has a very specific use to it. It's very much designed for function use. It's for keeping track of things like local variables, all of the data that you actually need to get a complex function to work. So our stack starts at the top of memory and moves downwards. This means that regions towards the top of memory are more likely to have been allocated. As we go farther down, we're more likely to run into unallocated memory. Each function will get its own frame. This is the region of memory that is available for this instance of the function. Each instance can have its own frame. And they will essentially just build on top of the stack. Each time we call a new function, we will allocate some more memory for that function's frame. You can put whatever data it needs into it. And when it's done running, it will deallocate that memory. So there are three things we commonly put into our stack frames. If our function requires more than four arguments, then the calling function could put those extra arguments onto the stack frame, technically an unallocated memory. But then the first thing that the called function would do would be to allocate memory to include that data so that it would be able to access those arguments. Next thing that we commonly put into our stack frame are local variables. This is all of the data that we commonly associate with the operations that are actually taking place in our function. So these could be variables, objects, data structures, whatever you need. The last thing that we put into our stack frame are the saved registers. The saved registers, $0 through $7, are considered callee saved registers. This means that any function that wants to use those registers needs to store that data someplace safe. Then it can use those registers as much as it likes. But once it's done, it needs to restore the data so that when we go back to the calling function, it doesn't think anything has changed with those. They still have the data that the calling function put into them. So in that case, we'll put the saved registers onto the stack because we know they'll be safe there. We'll be able to get those back at the end of our function call, restore them, and then everything will continue on as expected. In a high-level programming language, our stack would be managed for us automatically. But in assembly language, we're going to have to handle that ourselves. And the way we do that is with a couple of pointers. We will primarily be working with the stack pointer. And the stack pointer serves to tell us where the boundary between allocated memory and unallocated memory is at. In this case, we'll know that anything with a value greater than our stack pointer is somewhere in allocated memory. It's got some useful values in it. But everything in the unallocated memory, we don't know about. It's not being used. We can take it for ourselves if we need it. But we shouldn't expect that there's anything meaningful in that region. It's likely to just be left over data from the last function that used that memory. If you've built your function in such a way that you know all the data structures that are going to be in your stack frame, then you can actually just get by with using the stack pointer. It will give you a fixed reference point to access all of those known data structures. So any integers, doubles, slightly complex data structures that you put on your frame. So long as you know how large all of those are when you write the function, then we can get away with just using the stack pointer for all of our references. If you've got a really complex function though, you might want the frame pointer. The frame pointer serves to tell us where our stack frame starts. This will always be a fixed point in memory regardless of how much data we put into our stack frame. So if you wanted to have a dynamically allocated array that would add new elements each time you pulled one in, you could do this by using the frame pointer. The stack pointer would continue moving down, but the frame pointer would remain in the same place, so you'd have a fixed reference point to be able to find all of the stuff that you put into memory. Most of the time though we won't need the frame pointer. It's only for really rare occasions where you're dynamically allocating the amount of memory in your stack. But again, our stack pointer and our frame pointer are not going to be automatically managed for us. We're going to have to manage those ourselves. These are just going to be regular registers like all of the others we've worked with, but they have some numbers in them already. They have an integer in them, and we can work with that integer. So the primary complexity in writing assembly language programs that use the stack involves manipulating the stack pointer and remembering where you've put things on the stack as well.