In the previous unit, we talked about working with registers and memory, which is the basis for doing everything in low level machine language. And in this unit we'll see things which are more familiar to high level programmers like branching variables and iteration. So let us begin with branching. Branching is this fundamental ability to tell the computer to evaluate a certain Boolean condition or Boolean expression. And based on this value, to decide whether or not we want to jump and execute some other section in our program or simply move on and execute the next instruction in the program. Any programming language has various benching mechanisms. For example, if you write programs in Java or in Python, you have things like if-else, while, repeat, switch, and so on. In machine language, we typically have only one branching apparatus, and this thing is called go to. And here is how we use this go to in a particular example. In this program, we want to look at a certain data value. And decide or determine whether or not this value is positive or not. So we decide, as a matter of convention. That the value will reside in RAM0, so the user of this program has a responsibility of putting a value in RAM0. And then when the user presses Execute, the program spins its wheels. And at the end of the execution, the program will put in R1, in RAM1, either the value 1 if RAM0 is positive or the value zero elsewhere. So this it the typical if else. And unfortunately, we don't have a way to express this thing directly in low level programming. Low level programming is much more spartan and minimal, and therefore we have to work hard to make this happen. In particular, here is some code that I've written to accomplish this operation. It's probably not the only way to do it but it works. And I've used red ink to highlight the areas of the code that actually refer to this computation of if R0 is greater than zero set R1 to one. Let us ignore the rest of the program for now. So in between instructions eight and nine, I'm taking care of setting R1 to one. And in instructions two and three, I check the value of the data input which is now residing in B, and based on this value, I decide whether or not I want to jump to instruction number eight, in which I will actually set R1 to 1. Now, if you are not sure exactly how this thing works, I recommend that you stop the video and simply run this thing in your head and simply convince yourself that you understand how the branching is actually working here. Now, as a matter of experiment, let me remove the line numbers and also remove the documentation, and just stare at this piece of code. And if I will also remove the preamble documentation, I would argue that what will remain is quite unreadable. This is an example of cryptic code very difficult to understand what this code is trying to do and yet the code will work perfectly. This is the flawless program if you load it into the computer, after you translate it into binary code, it will work perfectly. It will deliver the signum obstruction. The user will be very happily and yet the program is not readable. Now this is a perfect point to invoke a quote by Donald Knuth. We said that instead of imagining that our main task as programmers is to instruct the computer what to do. Let us concentrate rather on explaining to human beings, fellow programmers, what we intend the computer to do. This is super important because if we don't have this capability, if our programs will not be self documenting, we won't be able to fix and extend them. Alright? So, what can we do to make this program more readable? Well, fortunately, we have a very nice feature in assembly languages, which is called symbolic references. So let's take a look at what I did here. I've introduced a label that I've made up and I decided to call it positive because it's a sensible choice of a label because it describes roughly what I'm trying to do and this positive label appears twice in the code. In the first place that I want you to look at, I declare this label. And basically what I'm saying here is, here is a piece of code that I want to jump to from some other place in the program. And I'm going to call this location in the code positive. And when we designed the hack assembly language we decided that label declarations will be made by just writing the label and enclosing it into around parenthesis. So, this is how we declare a label in the Jack assembly language and this is how we use the label. So instead of saying at eight, we now say, at positive. And it is the job of the assembler and the translator to translate these labels into concrete numbers. And let us observe how this actually takes place. So. Once again, the general rule is that @LABEL will translate into @n, where n is the instruction number following the label declaration. Let us remember that instructions have implicit numbers, and here they are. So when I take this piece of code and I load it into the instruction memory, here's what happens. First of all, whoever makes the translation has to operate according to a certain contract. If you write the assembler, for example, something we'll do in week six. We will tell you that the language make some assumptions and some conventions and you have to write your assembler or to carry out the translation according to these contract. So first of all, notice that label declarations are not translated. Now we have two label declarations here, positive and end, one is just before line 8, and the other one is before line 10. They are not translated, they are ignored, and they generate no code, surprisingly enough. Okay, what about the references to the labels? Well the references to the labels definitely get translated And what used to be @POSITIVE becomes @8 because 8 is the instruction just after POSITIVE. And what used used to be @END becomes @10 for the same reason. So, the second part of our contract is that each reference to a label is replaced with a reference, to the instruction number, following that label's declaration. So, once we keep this thing in mind, we can happily use, invent and use, as many labels as we want in our program. And then we can use these labels to effect the notion of branching. Okay, so having dealt with branching, and obviously it's something that we'll return to, because you cannot write any non-retrieval program without branching. Let's move on and talk about another obstruction called variables. All right, so what is the variable? A variable is an obstruction of a container that has a name and a value. And in high level languages we have different types of data, different types of variables. And yet in the low level of machine language, or at least in the low level of the hack machine language, we have only one variable type. We have only 16-bit values to worry about, and this obstruction can be delivered or can be implemented by using a single register in our data memory. So, we use single registers to represent every one of our variables, if we want to create variables in our program. So here's an example in which variables come into play. And in this program, I want to flip the values of RAM[0] and RAM[1]. And I do it using the classical programming idiom that appears in practically every basic programming course. I create a variable, typically we call it something like temp to denote the fact that it's a temporary variable, and I need it only for this purpose. And I do some manipulations that end up delivering the necessary flip operation. So the question is how do you do it in machine language, where so far we didn't have the notion of variables, so this is a good opportunity to introduce them. Well, here's how I decided to do it and let us focus only on the red instructions in this program. And in particular in the first red instruction, I declare a variable called temp and then I immediately go on to use it. Now, what is the meaning of this cryptic command, @temp. And notice that, unlike labels and references, we don't have a corresponding label called temp. So what do we mean when we say @temp like this? Well, here's what we mean. Basically, we present the following pledge to the computer. Please go to the memory unit, find some available memory register. Let us assume that it's register number n, and use it from now on to represent what I prefer to call the variable temp. So from now on, whenever you see a symbolic instruction @temp, I request that you treat it as if it were an @n instruction that we already know how to handle. Okay, so this is the meaning or that's how I realized this variable obstruction. So let us observe what happens when you translate this program and load it into instruction memory. Well, the first observation is that when you look at the code, you see that what used to be @temp became @16. And because @temp appears twice in the program, we have two occurences of the @16 instruction. So how did this happen? Well, whoever wrote the assembler or implemented this translator worked according to a contract that we, as the language designers, have put together. So, the contract has tools as follows. First of all, a reference to a symbol that has no corresponding label declaration is treated as a reference to a variable. We assume that the programmer wanted to create a variable. And the second rule is that variables ate allocated to the RAM from address 16 onward. In this particular example, we have only one variable, so it ends up being allocated to RAM16. If we have more variables in a program, we'll use RAM location 17, 18, and so on and so forth. And practically, you can use as many variables as you want. And I can say safely because typically programmer use only half a dozen or a dozen variables, something like this. And in the hack language you can actually use thousands of variables, but no one actually comes up to this, to so many variables. So, in closing of this little section about variable, if you look at this piece of code, you will realize that this program is relatively easy to read and debug. We have a symbolic label, END, we have a symbolic variable, temp. And it's much easier to understand what is going on here compared to the program that we wrote before, when we used actual numbers to represent addresses. This program has another very nice virtue, which is more subtle. And this is the fact that this program is what is known as relocatable code. I can take this program and load it into memory, not necessarily to address zero. I can put it anywhere I want in memory, as long as I remember what is the base address that I used for this program. This is extremely important, because as you know, when you work with your personal computer, you typically have several programs executing at the same time. So, you can imply from this that several programs are loaded into your main memory, and once we write this program carefully using symbolic references, we don't have to worry about where they will be located in memory. We can write something called loader that takes care of this technical detail. So, symbolic programs are good. The next programming idiom that I would like to discuss is iteration. So for example, suppose we want to compute the sum of the series 1 + 2 + 3, all the way up to n, where n is some given input. How do we do it? Well, normally you use some accumulator variable to accumulate the running total of the series. So we set this variable to 0, and then you do 0 plus 1 gives you 1. 1 plus 2 gives you 3, 3 plus 3 gives you 6, and so on and so forth until you reach n. Now, if you have to write this program in low level language, it always pays off To begin with some low-level oriented pseudo code. And this is what I did here. So once you convince yourself that this pseudo code actually delivers the required functionality, we can move on and translate this pseudo code on paper into symbolic machine language. So let us begin with the first part of this program, in which we declare and initialize the three variables n, i and sum. So first of all notice that there's a one to one mapping between the name of the variable that they use in my pseudo code to the named variables in the symbolic language, which is kind of nice. So we have these three variables. And let us assume that I translate only this part of the program and load it into the instruction memory. Here is what I'm going to get. @m would be translated into @16, @i into @17, and @sum into @18. This should not be surprising, because it follows the contract that we discussed earlier. Variables are allocated to consecutive ram addresses from 16 onward. All right, so let's keep this in mind and complete the translation of the pseudo code into machine language. The details of this program are not terribly important. Well they are if you actually have to write this program. But they're not important to the thing that I want to emphasize in this unit. But I do expect you to, once again, stop the video now and convince yourself that you understand what this code is doing. And by the way, when you write such a program, if I gave you this program as an assignment, I highly recommend that you first write it in pseudo code. And then I recommend that you debug your pseudo code and convince yourself that the pseudo code actually works. And once you convince yourself that it works, you can reduce the task of writing the machine code to that of simply translating from pseudo code to machine language. See this is much easier to do. You look at the Pseudo command like N=R0 and you write a set of instructions in the machine language that do the same. Or take a look at the instruction that follows the LOOP directive in the pseudo code. It says if i > than n goto STOP. So it's much easier to simply focus on this instruction only and translate it into machine language. So this is one way to write a low level code and convince yourself that it works. And the second way to verify that the code works, which is no less important is, once your program is written, you have to simulate it, or we recommend that you simulate it on paper using some sort of what is known as a trace table. Now, a trace table is a piece of paper on which you write down the names of all the main players in your program or the main variables of main interest in your program. And then you record in the table the values of these programs throughout the program's execution. So these are the values of these variables of interest in iteration zero. And then you go through the loop and you figure out what happens after the first iteration, after the second iteration, the third iteration, and so on and so forth. So you do this for three or four iterations and you try to convince yourself that the program is actually working. So, in closing, here is our best practice advice for writing hack machine language programs. First of all, you have to design the program using some sort of pseudo code and you can follow the pseudo code example that we used in the previous slide. Second, you have to take your pseudo code and re-express it using the Hack assembly language. And finally we recommend that you test the resulting program on paper using some sort of a trace table. And notice that I did all these things without touching the computer. Everything was done on paper, but actually I probably wrote the program using some text editor, but once again text editor for me is like using a piece of paper. And its very important that you convince yourself the program is working before you actually unleash it on the actual computer. All right so this has been the unit in which we talked about branching variables and iteration. And in the next unit, we are going to talk about pointers and input/output. And this will be the end of our coverage of low level hack programming.