 Now that we know what the structure of our memory looks like, we're going to look at how we can actually access the memory. In the MIPS architecture, we have two types of instructions. We have load instructions, which take data out of memory and copy it into a register. We have store instructions, which take some data out of a register and copy it into memory. We have four different sizes of data that we can work with. We can work with just individual bytes, and because it turns out that our architecture is byte-addressed, this is the smallest element of data that we could access anyway. These are most likely to be useful if you're working with, say, a character string. Each of your characters is going to be one byte long, so you can just pull out individual characters and look at them. We also have half-words. These are 16 bits long, so you get twice as much data. These would be equivalent to a short in, say, C or C++. But we're mostly going to be interested in working with words. These are full 32 bits, so you get an entire integer in them. They allow you to copy all of the data to or from a register. And this works really well for a lot of the operations that we'll do. We can also access memory in terms of double-words, which are 64 bits long, so twice as large as a word. But these are a pseudo-instruction. Most of the time, these aren't going to be relevant to us. This would be equivalent to loading, say, long. But most of the time, we're going to be satisfied with just words. Now, one thing to keep in mind is that this terminology is specific to the MIPS architecture. In the x86 architecture, they don't have halves. A word is only 16 bits long, though. So a 32-bit piece of data would be considered a double-word. And a 64-bit piece of data would then be considered a quadruple word. So if you're going to be working with the x86 architecture after that, just keep that in mind. They don't have halves, so some of their nomenclature has been changed. The actual instructions that we'll work with are all pretty similar. They have one general format, and we just change some of the parameters according to what we're interested in doing. So the first thing is actually that L all the way over here, which tells us that we want to load some data. So we're interested in copying some data from memory into a register. If I replace that L with an S, this would be a store instruction. Then I'd be copying data from a register into memory. The W tells me that I'm interested in loading an entire word. So it will fetch 32 bits of data and place them into a register. And whether you're doing a load or a store instruction, they'll both have this format for the parameters. The first parameter is for the register that we're working with. This is the one that we're going to copy data to or from. The rest of the arguments serve to tell us where in memory our data is or should be, and to do this we use a base and an offset. So our base is contained in some register, this case just the stack pointer because that's all we're really working with for now, and then we have an offset of 16. So this tells us that we should go to wherever our stack pointer is, and then go up 16 more bytes. And this is where I will find the data that I want to put in T0. It will grab the four bytes starting there and moving up and copy all of those into T0. If I had a store word instruction instead, it would take the data out of T0, go find the piece of memory which was at stack pointer plus 16, choose stack pointer plus 19, and it would copy that data into that region. Chances are you've heard of addresses, pointers, or references before. You've probably worked with them a little bit, but it's good to go back and get a nice rigorous view of what these things really are. We'll start with an address. An address is just an integer that indicates a memory cell. We saw back here that we had a number of different addresses, and each of them corresponds to some different part of memory. We can put different things into different regions of memory, and all of those regions are indicated by some address. Our addresses can be any sort of number. We generally write them using hexadecimal just because that's easier. Frequently we're counting in terms of fours anyway. So hexadecimal addresses tend to look relatively nice. They look horrible in decimal. But all of these are just integers. As I mentioned at the beginning, we can think of our memory as just one big long array, and we can access any individual cell in that array by its address. Essentially, we just have an array that starts at zero and it's indexed based on its bytes. So in this case, we have a huge number of bytes in our array, which is great because we'd like to put lots of data in there. But we're always going to access this just using some number. It's essentially just an index. A pointer is really pretty similar. It's just a data structure. In most cases for us, that will be a register that contains an address. So our stack pointer is a register that has an integer in it. We've decided that that integer represents some region in memory. So as long as we use it like it's an address, it will work just fine. We could happily copy our stack pointer into memory, then we'd have a pointer that's in memory instead of in a register. It would still be the same thing. We've just placed that pointer into memory instead of a register. So a pointer is just going to be a data structure that contains an address. We won't have to worry about references in assembly language because they're really a high level concept. Pointers tend to be dangerous and error prone, as you will likely discover. So most high level languages give you references instead to keep you away from doing some of the more dangerous things with pointers. The primary thing that people have trouble with with pointers is doing arithmetic on them. They can make your code really fast, work really efficiently, but they tend to be really error prone. It's really easy to make a minor mistake and have to spend lots of time fixing that. In most cases, the benefits aren't worth it. So the majority of high level languages just decided they were going to do away with pointers and give you references instead. They're still serving the same purpose effectively. They'll let you indicate where in memory some piece of data is. But they won't let you directly manipulate it. You won't be able to say pointer plus 86. This might be relevant, but chances are it's not. But for our purposes, we're going to be using pointers a whole lot. And a lot of what we're going to do with pointers involves finding other things relative to a pointer. So our pointer may tell us where our stack frame starts, and we know that we put variable x at stack pointer plus eight. So we know if we take our stack pointer and we add eight more to it, we'll know we can find x there. So we're going to be doing that a whole lot in this section. And it will work just fine, but we'll have to be careful about it.