George Fabishhttps://georgefabish.com
Fri, 13 Mar 2020 18:15:59 +0000en-US
hourly
1 https://wordpress.org/?v=5.4Enumerations for the Reduction of Complexityhttps://georgefabish.com/enumerations-for-the-reduction-of-complexity/
https://georgefabish.com/enumerations-for-the-reduction-of-complexity/#respondFri, 13 Mar 2020 01:10:57 +0000https://georgefabish.com/?p=2317Introduction One of the ever present problems faced by developers is the concept of complexity. Complexity is everywhere; it makes things difficult to analyze and even more difficult to control. When writing programs, complexity is introduced exponentially every time a conditional path is added. With every ‘IF’ statement, you are introducing two possible paths your ... Read moreEnumerations for the Reduction of Complexity

One of the ever present problems faced by developers is the concept of complexity. Complexity is everywhere; it makes things difficult to analyze and even more difficult to control. When writing programs, complexity is introduced exponentially every time a conditional path is added. With every ‘IF’ statement, you are introducing two possible paths your program could take. This article will start first with principles of conditions. I will then examine possible use cases for enumerations.

https://www.pexels.com/photo/jellyfish-2615541/

First Principles

In 500 BC a philosopher named Democritus proposed this theory that the universe could be broken down into these tiny discrete geometric units that he called ‘atoms’. This theory during it’s conception was no more founded on science than any of the other competing theories at the time. Of course, now we know that this theory was much closer to the truth than the other theories. For the sake of argument, lets just assume that Democritus’s theory of atoms is correct. What has he just done? He has taken the complexity of the universe and broken it down into it’s principle elements. This process has been, and continues to be attempted by scientists all over the world. In order to fully describe something, you must be able to break it down into its atomic components. This is one of the reasons I enjoy programming so much. It forces you to describe a problem in it’s most fundamental building blocks. After all, at the end of the day the only tool in your tool-box is a ‘0’ and a ‘1’. It’s up to the programmer to describe arbitrary complexity with those two digits. One of the most common ways that this is done is through conditionals. Say you are trying to write the behavior for a car. You would probably have the following idea:

If the traffic light is red then stop

With this simple condition you have defined a branch of execution. The car will either encounter a red light and stop, or the light won’t be red and the car can continue. This is one building block required in describing the behavior of a car. But if you look a little closer at this statement, it doesn’t take long to realize that it is not precise enough. According to the ‘IF’ statement the traffic light can only be ‘red’ or ‘not red’. We of course know that traffic lights can be yellow as well as green. Therefore, there are situations where a simple boolean is not descriptive enough to describe a condition. What does this tell us? It means we haven’t broken the problem down into it’s most atomic parts yet. There is still some complexity hiding in the condition that needs to be defined. So the obvious thing you would do next is change the statement to look something like this:

If the traffic light is red then stop
else if the traffic light is yellow slow down
else if the traffic light is green continue

This works and is much more precise, but it’s a little verbose and unnecessary. Let’s say the light is green. When the program hits this section of execution it is now keeping track of the following booleans:

IsGreen

IsYellow

IsRed

In some situations this might be required, but not in the traffic light scenario, because the light can never be in more than one of those states. This gives us a chance to use another tactic to reduce the complexity of our behavior. Give a warm welcome to our friend enumerations.

Enumerations

Enumerations1 at their heart are simple integers. Their intended purpose is to describe all possible states that an object could be in. Thinking back to the traffic light problem, we know that a stop light can either be Red, Yellow, Or Green2. So what if we made an agreement that when the light was green we would represent that as a ‘1’ , yellow as a ‘2’ and red as a ‘3’. We could now rewrite the ‘IF’ statement above to say

If traffic light is currently 3 then stop

This is perfectly precise and we are only keeping track of one variable now. Much better! The downside here is that we have lost some readability, because we now have to remember that 3 actually means red. This will get annoying very quickly. Time to take a look at enumerations and kill two birds with one stone. Enumerations map an integer to a name. So if I wanted to create an enumeration to represent our traffic light state it would look like this:

enumeration named TrafficLight{
Green = 1,
Yellow = 2,
Red = 3
}

With this enumeration defined, we could then redefine our ‘IF’ statement:

If traffic light is TrafficLight.Red then stop

Voila! We now have a statement that is precise and easy to read.

The Fountain of YouthLucas Cranach

Flags

So are enumerations a magic elixir that cures all our ails of conditional complexity? Well sadly, no. But when used judiciously they can go a long way in managing complexity. Let me leave you with one more way you can use enumerations, which is probably not immediately obvious to you. Consider the following system:

This system contains 3 animal components. Each animal can do different things, but many of the actions have similarities. So imagine that you had to write a controller that would be responsible for receiving and processing events from these three components. Let’s make an enumeration that contains all of the events that the system can contain.

Wow! I am out of breath just writing that! The complexity is killing me and that’s only for three animals! Imagine if we added a rabbit to the system? Surely there has to be way to cut down this complexity even more. Indeed there is. We need to use enumerations in a different way. So far we have used enumerations to represent state. Like in our traffic light example, the state of the traffic light can neatly be described as ‘red’,’yellow’, or ‘green’. We also have just defined the state of the Animal System explicitly. One way to rephrase it is that we have described what the current state “IS”. What if we could ask a different question. What if we could describe the current state in terms of what the current state “HAS”. Well in the traffic light scenario it would make no difference. But it would drastically reduce the complexity of the animal system. If we redesign the enumeration to answer the “HAS” question we would get an enumeration like this:

The way this works is probably not immediately obvious unless you’ve seen this type of thing before. The reason this works is due to the numbers we assigned the names. You’ll notice that each of those numbers is a power of 2. As we learned in the post What is Binary, every time you raise a binary number by a power of 2 you are essentially adding a zero to the right of the number. So lets rewrite the enumeration in binary

So now that we have cleverly organized numbers, we can use these values to quickly filter Animal System events. We can now know that if the first bit of the event is a ‘1’ then the event is going to be about the dog. If the fourth bit is also ‘1’ we now know that the dog is supposed to eat. If this is sounding similar to Bit Masking, you are absolutely correct. It is the same technique only a little easier to think about, because the numbers now have names. So how would we create a Dog-Eat event for our animal system? Well, we would use the logical “OR” operator to combine bits.

So now our AnimalSystem sends out the event that has the code ‘0001001’. The people listening for the event then can process that event by asking ‘HAS’ questions about the event. So the dog component in this case would say – does this event have anything to do with dogs? Well the first bit is ‘1’ so the answer is yes! This means the dog is now interested in processing the rest of this event. You can ask this event what it ‘HAS’ by using the reverse of the ‘OR’ operation which is the ‘AND’ operation.

As you can see, you can use the ‘AND’ operation to ask an event if it ‘HAS’ a certain flag.

Conclusion

There are always multiple ways to solve problems. Some are better than others at limited complexity and, more importantly, making it easier for us humans to think about. I believe the above example is a great way to encode/decode complex data in a very simple way. Using this new method, if you wanted to add a rabbit component to the animal system, you would only have to add one more entry into the enumeration instead of four entries. On top of that, there are no rules inherent in this data structure about what trait the animal components have to support. This allows a lot of flexibility. For example, right now a Dog-LickPaw event is not supported by our system specifications. But with this design it would be trivial to add support to that event. Again, one of the main goals of a programmer should be to describe problems as specifically as possible. We started out with a boolean that can only be true or false. Then moved to an enumeration which can have ‘N’ number of states. Finishing with a simple AnimalSystem. First using an enum that explicitly defined 12 different states the AnimalSystem could be in. We then used the enumeration as an implicit description of the state of the AnimalSystem that allowed us to represent 5,040 possible states with 7 entries.

Footnotes

1. are often called ‘enums’ in programming languages 2. This of course is assuming the traffic light is powered on and functioning correctly. This is a testament to how often you think you have a problem fully broken down, but you have overlooked certain states and edge conditions.

]]>https://georgefabish.com/enumerations-for-the-reduction-of-complexity/feed/0How Code Becomes Binaryhttps://georgefabish.com/how-code-becomes-binary/
https://georgefabish.com/how-code-becomes-binary/#commentsMon, 11 Nov 2019 01:25:24 +0000https://georgefabish.com/?p=2264Humans have been inventing programming languages since the 1940s. But what exactly is a “programming language” and how do computers understand different programming languages? Well read on to find out. The Basics Before we dive into things we have to break down what code is. Code is a series of instructions given to some sort ... Read moreHow Code Becomes Binary

]]>Humans have been inventing programming languages since the 1940s. But what exactly is a “programming language” and how do computers understand different programming languages? Well read on to find out.

The Basics

Before we dive into things we have to break down what code is. Code is a series of instructions given to some sort of CPU. It doesn’t matter if you are using Java, C-based language or something silly like PHP. At the end of the day it all becomes just an instruction (or more likely a series of instructions) that is passed to the CPU to be executed. Let’s take a look at a line of code that is written in JACK which is a high level Java-like language.

This is a great example of high level code doing what high level code does best. That is, making complicated things easy and saving the end user a lot of work. The above line of code simply prints to a window the words “The average is “. The high level code makes it almost human readable. You could walk up to your futuristic voice controlled computer (that neeeevvvverr misunderstands you) and say: “Hey computer do output print string the average is “. This is all well and good, but if you say that to a CPU you won’t get very far.

A computer can only understand binary. A CPU doesn’t understand the above line of code anymore than Rory understands Tristan’s stupid hair. It turns out that most modern high level languages deal with the problem in similar ways. In fact they all, generally speaking, follow this architecture:

This was probably a little more complicated than you were expecting. Especially since the problem seems so simple, right? You need to translate an instruction into binary, so why not do something like this:

The answer is: you totally could! But it may not be the best choice. The following diagram might help.

The jump between high level code to binary is a titanic leap, as we will see as we continue. To translate from high level code directly to binary would be a major task. The other design has broken that task down into more manageable chunks like this:

Having a design like this not only makes the job of building the translators easier, it also opens up all sorts of possibilities to swap any of the above components without compromising the whole system. Now if you want to implement a new high level coding language, you only have to jump 34 meters instead of the entire 100. The same could be said about swapping up different styles of assembly code or virtual machine code. This comes in really handy, because on every single level of this design there are thousands of different implementations in use today. It doesn’t matter if you are writing code for a toaster or a space station, if the compiler is set up correctly it will translate the code down to hardware specific needs. This design gives us much more flexibility than the simpler version. So now lets follow this line of code through the translation layers. It starts in jack like this:

It gets translated into the much less readable virtual machine language layer:

As a note, the multiple calls to ‘String.appendChar’ are creating the requested string “The average is “. Each of those constants represent a character code that is linked to a particular letter.

This is then translated into the much less easy to read Assembly code:

So in this case, a single line of code when translated into binary resulted in 917 separate instructions to the CPU. Although, even that number is low, because the instructions that write the characters to the screen were omitted; instead they were merely pointed to in the instructions. These instructions assume that the “Output.printString” function has been created elsewhere in code. So the instructions merely point to that function and say that it needs to be executed. Isn’t it nice to have high level languages?

]]>https://georgefabish.com/how-code-becomes-binary/feed/3Stack vs Heaphttps://georgefabish.com/stack-vs-heap/
https://georgefabish.com/stack-vs-heap/#commentsWed, 18 Sep 2019 00:13:36 +0000https://georgefabish.com/?p=2238Synopsis Stack and Heap refer to different memory segments. In general, the size of the stack will be much smaller than the heap, but the time required to access data on the stack will be much shorter than trying to access data that is stored in the heap. More Info This is an inherently confusing ... Read moreStack vs Heap

Stack and Heap refer to different memory segments. In general, the size of the stack will be much smaller than the heap, but the time required to access data on the stack will be much shorter than trying to access data that is stored in the heap.

More Info

This is an inherently confusing question. Why? Because in computer science there is a data structure called a “Stack” and a data structure called a “Heap.” When people are talking about the stack and the heap and computer memory, they are NOT referring to the data structures, but to memory segments inside the computer. To make it more confusing, the “stack” they are talking about actually employs the “Stack” data structure. For that reason, we will really quickly describe what a “Stack” data structure is before we investigate what people mean when they refer to the stack vs the heap.

Stack

A Stack is merely a way to sort data. The easiest way to think of it is to imagine stacks of things in the real world — like plates, for example.

So what can you do with these “stack of plates” data type? Well, in Computer Science there are three things you can do to stack:

Push

Pop

Peek

Push

When you add something to a stack it is called a “push.” It is the only way to add items to the stack data structure. And each time you add an item it must be placed at the top of the stack. Just as though you were stacking plates, each new plate would be stacked on the others. You are not allowed to insert a piece of data into the middle of a stack.

Pop

When you remove something from a stack it is called “popping” the item off the stack. Just like with pushing things to a stack, there is a rule that tells you that you are only allowed to take the item at the very top of the stack. Again going back to our stack of plates: if you want a plate you must take whatever plate is on the top of the stack. You cannot take a plate from the middle of the stack.

Peek

Peek is the ability to look at the item that is currently at the top of plates. Pretty simple (to beat a dead horse), going back to our plate example, the only plate you can really examine is the plate at the top of the stack.

Memory

Now that you understand how a stack data structure works we can proceed to explaining how these different memory segments are used. At it’s foundation all computing can be roughly broken into three categories. You have the program, which is a set of instructions. You have the processor, which receives those instructions and executes them. Then you have the memory, which stores the results of the execution.

Like I said before, when talking about Stack vs Heap we are talking memory segments, so let’s investigate the “Memory” part of this diagram a little closer.

If you remember from before, the stack section of memory actually uses the rules of a stack data structure. But since this function is so low level, it has to keep track of the stacks status itself. So in the first slot in the stack, memory is reserved for a variable called a stack pointer. This is in charge of keeping track of where the “top” of the stack is. Going back to our plate illustration, the stack pointer points to the position right above the top plate. That way, if you want to add a plate to the stack you know what position it needs to be added at. This data structure allows us to do everything that we do with computers today. Lets look at the following code and the way that it would be stored on the stack and the heap.

x = 1 + 2;

This code would result in instructions similar to this:

Push the value 0 to the stack to represent ‘x’

Push the value 1 to the stack

Push the value 2 to the stack

Call the Addition Function

Set the value of ‘x’ to the result of the function

What’s interesting about these instructions is that if you look close enough, you can see the main stack in action. Let’s visualize this process.

Step 1: Push the value 0 to the stack to represent ‘x’:

Step 2: Push the value 1 to the stack :

Step 3: Push the value 2 to the stack:

Step 4: Call the addition function :

This is the first interesting step that needs explanation. So in order for the function that handles addition to work, it needs two values to add. Where does it get those values? It pops them off the stack. So when the function is called, it will ‘Pop’ 2 and then 1 from the stack. It will then add them together and ‘Push’ the result back to the stack. After which the stack will look something like this:

Step 5: Set x to the result:

To achieve this, the ‘3’ is popped from the stack and placed in a local register. The ‘0’ is then popped from the stack and set as the current variable, and then it has the value 3 written to it. What you will notice is that this leaves us with an empty stack. As it turns out, all programs have to be written in a way where the stack is emptied at some point, or else you will run into the infamous “Stack Overflow Error” which is where the name for the popular computer science website came from.

Heap

So now that we know what the stack is used for we can answer what the heap is used for. Since the stack has more conservative size restrictions, it is common to store larger objects in the heap instead of the stack. Now what objects exactly get stored where is decided by the particular operating system that you are running. The most common thing to be stored on the heap are objects and arrays. To keep this conversation programming language agnostic, I will refer to these objects in the most general of terms. The easiest way to imagine an object is a box that holds values. It is important to note that the computer has no idea what an object is; objects are just things that humans use to help us write programs. Let’s imagine we created the following object:

As you can see, this object is very simple. It contains three number values. This is an example of an object that could be stored on the heap. The OS (operating system) would see that the object has three values, so it would allocate three slots in memory to store the object. The OS then pushes the address for the first memory slot of the object. This address is commonly referred to as a pointer, because it points to the address where the data is being stored. It would end up looking something like this:

So if we were to create a new object with the values 25, 37, 98 it would look like this:

What does that allow us to do? Well, now the stack only has to store the pointer value of ‘80001,’ instead of all three values (25,37,98). This helps to reduce the memory requirements of the stack. The pointer value at this point acts as a kind of bookmark that we can use to quickly navigate back to a particular section of memory and read values we stored there. Whereas this might not seem to be very important in the above example, you can imagine how handy this would be when storing large arrays or objects with many properties attached to them.

]]>https://georgefabish.com/stack-vs-heap/feed/1How Bit Shifting & Bit Masking Workhttps://georgefabish.com/how-bit-shifting-bit-masking-work/
https://georgefabish.com/how-bit-shifting-bit-masking-work/#commentsSat, 01 Jun 2019 20:26:16 +0000https://georgefabish.com/?p=2215Last post we talked about binary on a basic level. If you are not comfortable with reading binary or counting in binary I recommend you check out that post before reading this one. Using bit shifting and bit masking judiciously opens the door for massive performance optimizations in low level hardware and high level systems ... Read moreHow Bit Shifting & Bit Masking Work

]]>Last post we talked about binary on a basic level. If you are not comfortable with reading binary or counting in binary I recommend you check out that post before reading this one. Using bit shifting and bit masking judiciously opens the door for massive performance optimizations in low level hardware and high level systems alike. Read on to find out how.

Bit Shifting

There are two basic types of bit shifting. You can either shift left or right. Both act similarly to each other. Although the term bit shift sounds frightening, hopefully you will be able to see how you have used similar techniques in dealing with elementary arithmetic.

Left Shift

Left shift is probably the most common type of bit shifting. Check out the number two in binary.

To perform a ‘Left Bit Shift’ of one to this number you simply add a zero to the right. Which in turn shifts the number left by one place. Giving us this:

That syntax may look complicated, but you simply have the number you want to shift on the left. In our case that was ‘010.’ You then have the operation you want to perform on that number, which was a ‘Left Bit Shift’ represented by two less-than signs (<<). We use less-than signs to make it easier to understand which direction we want to shift. Less-than signs point left. Therefore we use them to represent a shift to the left. Finally, the right side of the equation shows how many places we want to shift the number left. Have a look at some more examples.

The clever ones of the bunch can probably already guess the math behind how this works. If you looked at the first example we started with the number 2. We shifted it left by one and ended up with the number 4. Well, in our second example we have the number 1 and we shift it left by two and also end up with the number 4. The formula for shifting a bit is as follows.

x = number we want to shift.k = number of places we want to shift.

Before you lose it with all the ‘math – E’ symbols, stop and think – where have we seen this before? Again, harkening back to our third grade math class, you will realize we have been doing things like this with decimal numbers for a long time. What did you do when you wanted to add a zero to a decimal number? You would multiply by ten! Similarly if you wanted to add two zeros you would multiply by one hundred. So the above formula works with decimals just by changing the ‘2’ into a ’10’. In binary if you want to add a zero just multiply a number by two. If you want to add two zeros to the left just multiply it by 4.

Logical Right Shift

It is the exact opposite of the ‘Left Shift,’ so I won’t spend much time on it. Instead of inserting a zero to the right of the number it inserts a zero to the left of the number. In doing so, this shifts the number right. To refer back to the decimal system, think of it like you were dividing a number by ten. 100 becomes 10 and 10 becomes one. The syntax is very similar except we use three greater-than (again think of the direction the arrows are pointing to represent a right shift) symbols to represent the function. The formula is as follows.

x = number we want to shift.k = number of places we want to shift.

Just a quick note: there is a second type of Right Shift called an ‘Arithmetic Right Shift.’ It serves a similar purpose to the logical right shift. The difference is that the logical shift doesn’t preserve a number’s sign where as the arithmetic right shift does preserve the sign.

Bit Masking

The easiest way to think of bit masking is to think of it as a filter function. The good news is there are no formulas to it!

The idea is simple enough; it is the process of taking two numbers and performing a logical operation to them. If you are not familiar with logical operations, here is a quick refresher on the two most often used ones.

Why do we need these functions?

So why would you want to do that? These operations are extremely handy when it comes to handling large sets of boolean data. Take for example a power strip that has six outlets in it. Let’s say you wanted to represent the empty outlets with a ‘0’ and the full outlets with a ‘1.’ An empty power strip would then look like this:

Lets plug something in and notice how the state updates.

That was simple. You just place a ‘1’ on the outlet that has been filled. Well, it’s actually not that simple. Because remember that to us we are letting zero and one represent true and false, but to a computer it is still just a zero and a one. So in computer memory we had a value of ‘0’ that represented the initial empty state of our power strip. But now we have used one of the outlets which has resulted in setting our state to ’16’ or ‘010000’. So how do we set the value? We would keep track of the outlets counting from right to left, starting at 0. Which would look like this:

We now see that the the outlet at position 4 has been used. This is where bit shifting comes in. We need to set the value of the bit in the fourth place to one. Now that you understand how bit shifting works, we can come up with a general formula that will give us the number we need to represent a filled outlet position.

state = 1 << position

So if we apply that formula to our power strip we can see the following:

position = 4
state = 1 << 4
1 << 4 = 1 * 2^4 = 16
state = 16 = 010000

Very cool! Now lets see what happens when a second outlet is filled.

So our value has updated from 16 to 18. How should we handle this? If we follow the same strategy as before we will see that position 1 has been filled, and if we put that into our formula we end up getting 000010. That’s not what we need. This is where we mask that fool to get what we want. We can use the OR operation to keep our old values and add the new ones.

It is important to note that although they work the same way in this instance, the OR operation is different from a simple addition. Awesome! We have updated our power-strip state and we know how to update it in the future when someone else plugs into it. Well, what if someone unplugs? Consider the following:

Our state was 18 and now it is 2. What general formula can we use to govern this transition? This one is a little more tricky. Essentially what is happening is the reverse of plugging something in. To plug something in is represented by all zeroes except in the position where the plug is being added; that position is being represented by a one. The reverse of that would be represented by all ones except at the position where the plug is being removed. That should be represented by a zero. So how do we achieve this? We will use a NOT operation, which is great for this because it simply reverses 1’s into 0’s and vice versa. Let’s take a look:

plugAdded = 010000
NOT 010000
-----------
plugRemoved = 101111

Resulting in the tongue-in-cheek formula:

NOT plugAdded
-------------
plugRemoved

The final trick to update the state is to use the AND bit mask to the old state.

Whammy blammy, our power strip is now fully functional, thanks to our sweet bit masking and bit shifting skills! These techniques are used heavily in all sorts of AI as well as different graphical programming, and now you know how to use them!

]]>https://georgefabish.com/how-bit-shifting-bit-masking-work/feed/1What is Binary?https://georgefabish.com/what-is-binary/
https://georgefabish.com/what-is-binary/#respondThu, 18 Apr 2019 00:34:05 +0000https://georgefabish.com/?p=2183Learning how to understand Binary will change how you see all numbers. What is Binary? I am sure you have had that question at least once or twice before. Usually for me what happened in the past was, I would ask that question and then end up in some article written for a college Computer ... Read moreWhat is Binary?

]]>Learning how to understand Binary will change how you see all numbers.

What is Binary? I am sure you have had that question at least once or twice before. Usually for me what happened in the past was, I would ask that question and then end up in some article written for a college Computer Science course that would over-explain what is a pretty simple idea. Adding in poor un-intuitive examples to further muddy up the waters. So I want to remove the mystery about it once and for all. Taking your skills from zero to one when it comes to binary.

How to Count

In reality, binary is just as simple as the way we normally count, but at the same time it is harder to read. The reason for this is largely because we are just not used to it. Why learn it? Because as I said before, I believe having an understanding of binary will change the way you see numbers and number systems. If you look up ‘Binary Number’ on Wikipedia the definition is as follows:

Let’s Start with What We Know

See what I mean? That explanation makes things worse! So forget that definition for a moment and lets see if we can simplify things. Lets do a thought experiment. Okay, counting from 1 to 10 just like we normally do, how would you do it?

1…2…3…4…5…6…7…8…9…10

Great job! There is a part of that counting process that you might not have thought about before, or paid much attention to. I want to focus on the numbers ‘9’ and ’10’. What is going on here? Why is the number ’10’ the number that comes right after ‘9’? Well, there are a couple things going on here. First we see that the number ‘9’ is the highest single digit number we have in our system. So what do we need to do to get a higher number? We add another digit. So we add a digit but then we automatically reset the number ‘9’ to zero and the new digit is set to one. Let me re-write the sequence to see if I can help illustrate the process.

You see, conceptually as we are counting you can imagine an infinite amount of zeroes to the left of any number that you write. They wait there patiently until they are needed. They are needed every time we need an extra digit.

008…009…010………018…019…020……… 098…099…100……..

Armed with this perspective, lets look at binary. Binary works the exact same way, except instead of being able to use all the digits from 0-9 it is stuck with just using two digits 0-1. Lets start by looking at the number one.

Notice both are exactly the same. But the changes happen when we try to represent the number ‘2’. Remember Binary only has one’s and zero’s. So how do we proceed? Well we follow the same strategy as we do when counting from 9 to 10. We add a zero to the right of the number like so:

“There are 10 kinds of people in the world. People who understand binary, and people who don’t.”

Moving on, when trying to represent the number ‘3’ in binary we look at the number ‘2’ in binary. Since it still has space in it we don’t need to add a digit. We can represent it like so:

Can you guess what four would be?

Good guess, here is a GIF that will help to further cement your knowledge

Reading Binary

So now that you know how to count in binary I wanted to address what is meant by a ‘base-2’ number. Let’s first go back to how we were taught to count in kindergarten. We were taught to count in a base-10 system. You were taught about digits having a ‘place’ (i.e. tens, hundreds, thousands and so on). But what you might not have realized is that those places are directly correlated with the number 10 being raised to some power. So if you think about it in that way the following table emerges.

What we can deduce from the chart above is that every time a digit is added, we are essentially raising the value of the number by an additional power of ’10’. This results in the following equation that the number ‘n’ (where ‘n’ is equal to the number of digits) will always be equal to or greater than the number 10n-1. Why the negative one? That is because we are starting from zero. This very same principle can be applied to Binary. The difference is that Binary is a base-2 system, and therefore, instead of raising ’10’ to the power of some number we are raising ‘2’ to the power of something. Which results in the following table.

Converting Binary → Decimal

Knowing this allows us to use that information and relatively quickly convert Binary into our easier-to-read Decimal form. Lets take the 16-bit number 1010101010101010. For simplicity’s sake we will assume that it is an unsigned number. Meaning that this number represents a positive number. How would we count it? Well it’s actually pretty simple. Starting from the left of the number (what is called the most significant bit) every time you find a one you take its value and add it to a running sum. This is where a graphic might help.

This same strategy of conversion works for all types of number systems. In fact it works for converting decimal to decimal like so:

A little redundant, but this demonstrates the universality of this counting method. I will not discuss converting decimals into binary other than to say it is the same process, just in reverse.

Quiz

Here is a little puzzler for you… What would 16 ‘1’s in binary represent after being converted into decimal? (Hint: you don’t need to do any conversions just think about the exponents.)

Why don’t you have to do any fancy conversions? Because if we have all 16 ones you know that the next number is going to flip all those ones to zero’s and add a digit. So we would end up with a ‘1’ followed by 16 ‘0’s, which is……216 or 65,536. Therefore 16 ‘1’s must be one less than 1 followed by 16 ‘0’s giving us the result of 65,535.

There are many more tricks and shortcuts like that which can be done with binary due to its simplicity. I will probably try and tackle some of them in a later post. Hopefully this has been helpful in your pursuit of understanding what binary is and how to read it.

]]>https://georgefabish.com/what-is-binary/feed/0How does your Processor Process?https://georgefabish.com/how-does-your-processor-process/
https://georgefabish.com/how-does-your-processor-process/#respondSat, 30 Mar 2019 19:19:57 +0000https://georgefabish.com/?p=2161An exploration into how your Processor works. In previous posts I have described how computers store information and how the special ALU chip can run elementary operations on that information. In this post I will endeavor to bring it all together into a cohesive overview of how your processor works. Before I dive into the ... Read moreHow does your Processor Process?

In previous posts I have described how computers store information and how the special ALU chip can run elementary operations on that information. In this post I will endeavor to bring it all together into a cohesive overview of how your processor works. Before I dive into the details, I want to step back and talk about the overall architecture.

If we take a look at CPU it actually appears simple, and in some ways it is! We have the program which is written by the programmer. Think of the program like a list of commands. This list of commands is fed to the ALU which then executes the command that it is given. More than likely, that command will have something to do with data that has been stored in our data container – at which point the ALU will interact with the data and update it as needed. Once the command has been executed it moves to the next command or line in the program and begins executing that command. This brilliant architecture was invented by Von Neumann in the 1940s. It is such an effective design that it is still being utilized today. My current computer is running a Ryzen AMD processor that operates at peaks of 3.6GHZ. Which means that it is running 3.6 billion cycles per second.

The Program

In exploring the CPU I feel like the best part to start is the program. As I said before, you can think of the program as a series of instructions for the ALU. Since this CPU is built on a 16-bit architecture those commands come in the form of a 16-bit number. The following is a set of seven instructions to ALU:

Now that might not be very helpful and is definitely not human readable, so lets see if we can break it down into something more understandable. It turns out that these codes are encoded in the following way.

A or C Instruction

As you can see, there are 5 different groups of instructions packed into this 16 digit binary number. The bit on the far left dictates whether the instruction is a C instruction or an A instruction. If it is that the bit is set to one, then the instruction is a C instruction; otherwise the instruction is an A instruction. Don’t worry too much about the difference between an A and C instruction. An easy way to think about it is that if it is an A instruction, then it is referring to an address. In which case, none of the groups matter because the rest of the number is to be used as an address to get to a particular place in memory. On the other hand, if that bit is set to one, then the rest of the bits have instructions encoded into them. In fact, with just this knowledge you can look at the binary instructions I gave above and note that there are two A instructions and the rest appear to be C instructions. As a bonus, those A instructions are just zero, which means it is referencing the very first address in the RAM. As another teaser, that very first register of RAM is where the stack pointer resides. I won’t get into what exactly that does but I’ll leave that as an advertisement for a future post.

Write to the A or M Register

You’ll notice that we have two bits that are discarded. Now remember, if this system was built with a 64 bit architecture we could be sending 64 instructions to the CPU which could hold much more data than our current instructions. But as it turns out, 16-bit is more than sufficient for our purposes and in fact we end up not needing two of those bits, so those will just be ignored. That gets us to the fourth bit from the left signified by the grey box. There isn’t anything complicated about this bit. The state of this bit dictates whether the instruction will operate on the A register or the M register. What is the A and M register? Well, without getting too far into the weeds, the A register is the address of the currently selected RAM and the M register is the contents of the currently selected RAM. Probably easier to picture a bank vault that has many safety deposit boxes. Well when you pull a safety deposit box out to inspect it, the A register gets set to whatever number that box is. At the same time the M register is set to the contents of that safety deposit box.

ALU Function Code

The next 6 bits which have been grouped in blue are the meat of what the ALU is supposed to do with whatever number it is working on. Now if you read my post explaining how the ALU works, you will remember that part of the ALU’s construction was that it had six inputs. That is exactly how those bits are used. Here is how those bits are mapped:

Well, almost exactly. With one little catch that is actually ingenious. You use the above table in combination with the A or M register chip to produce the following behavior:

So in general, if that A or M register bit is 0 then the ALU will operate on the A and the D register. Otherwise it will operate on the D and M register. What is this D register? Well it is the third and only other register used by the CPU. Whereas the A register holds the address of the selected RAM and M holds the contents of the selected RAM, the D register is just a general purpose register where you can hold whatever you want in it. It comes in really useful when updating variables and things such as that. But that also is information that belongs in a separate post. From the above image you can see the usefulness of the CPU starting to show itself.

Destination Bits

Now lets look at the yellow Destination bits. The value of these bits dictates which register, if any, should receive that value that the ALU will output after completing the function. As I said before, there are only three registers that store information in the CPU. There are the A, D and M registers. So based on whatever values you set those destination bits to be, it will then either write to all three registers or to none of them.

So say we wanted the ALU to perform the function 3+2, we then wanted to store those values in the M and D registers. We would give the appropriate ALU instruction then we would set the destination bits to be “011”.

Jump Bits

Last but not least, lets look at the jump bits. Like the destination bits, we have only three values. But these three bits are a little more tricky than the destination bits in that they are conditional. Having this jump bit functionality provides the programmer with the ability to jump to any “line” or instruction. Say for example you were writing a program in which, when an error was encountered, you would want to jump to a safe part of the program. In order to do that, you would need to employ these three bits to achieve the desired effect. Having the ability to jump from one place in code to another place in code opens up huge opportunities like loops, switches and if-then logic. Lets take a look at the associated table that specifies the behavior.

So unlike the destination bits, these bits are set and then for the most part their behavior is dictated by the output of the ALU. It is important to note that you can set the bits to zero (this is most often the case) to indicate no jump or set all the bits to one for an unconditional jump. The word “out” in the diagram represents the output of the ALU. When using a “jump” statement it is assumed that the place where you want to jump to has been set in the A (address) Register. If the condition for jumping has been met, then the program will jump to the address currently stored in the A register.

Conclusion

Now with all this knowledge in our heads, lets decipher the cryptic instructions that were at the beginning of this post. Lets take the first instruction which was:

1110101010010000

Now lets drop it into our diagram to make it easier to read.

The first thing we see is that this command is a C instruction, so the following bits will not be associated with an address. Secondly, we note that the A or M register is set to zero. With that in mind, we refer to our ALU chart to find the command “101010.” What we find is that, when translated, those bits are telling the ALU to output a zero. Then looking to the destination bits we can figure out where we are sending that zero. Consulting the chart we find that “010” is setting the D register. So now we know that the first instruction was to set the D register to zero. Congratulations, you have just deciphered your first binary command!

The second command was all zeros which, as I pointed out before, sets the A register to zero. Which happens to be the address where we store our Stack Pointer, which I will discuss later.

Lets do one more and I will leave the last there for you to decipher at your leisure. The next command was:

Again we note that this is a C instruction that wants to operate on the M (contents of the currently selected RAM) register. Looking at the ALU code “110000” we match it up with function that outputs the contents of the M register. Now looking at the destination bits we see that “100” = A register. So effectively we are setting the A (address) register to whatever the contents of the M register are. If we remember the command previous to this was to select the RAM where our stack pointer was. So essentially what happened in those two lines of code was to select the Stack Pointer and then “go to” whatever address was stored in the stack pointer. But more on that when we look into Machine Language and how to actually write programs our new computer can understand. Here are the instructions left to be decoded.

]]>https://georgefabish.com/how-does-your-processor-process/feed/0How Computers Rememberhttps://georgefabish.com/how-computers-remember/
https://georgefabish.com/how-computers-remember/#respondThu, 21 Feb 2019 02:14:38 +0000https://georgefabish.com/?p=2146A brief explanation of Computer Memory In the last post I explained how the ALU in your CPU works. As impressive as that is it doesn’t do very much good unless you can store the results. Which brings us to the problem of how can computers store memory. In general there are two types of ... Read moreHow Computers Remember

In the last post I explained how the ALU in your CPU works. As impressive as that is it doesn’t do very much good unless you can store the results. Which brings us to the problem of how can computers store memory. In general there are two types of memory that most computers employ. There is temporary memory and persistent memory. We will be spending our time explaining the temporary memory. Suffice it to say that persistent memory are things that are written to your hard drive or other storage device. Whereas the bulk of temporary memory is stored in your RAM(Random Access Memory). The difference is that when the power is removed from your RAM all the memory is effectively lost. With hard drives on the other hand the data persists even without power.

The RAM

It turns out that sometimes by solving a problem at its smallest point you can simultaneously solve the problem at its largest point. So the smallest version of the problem of computer memory is how can we store a single bit? We need to be able to set a zero or one and for that zero or one to persist until we need it. It turns out there is a chip for that! It is called the single bit register. It uses mostly familiar parts with one new chip inside it.

The way this chip works is similar to other chips I have described. It has an input and a single control bit. If the “Load” bit is set to one the incoming bit from the “IN” input will be written to memory. The new chip that enables this to happen is the data flip flop chip. Before I explain to you what it is doing I have to introduce one more new idea which is the idea of having a clock. Up to this point I have described chips that react instantly without any concept of time. The Data Flip Flop Chip or DFF relies on this idea of a clock because it essentially stores it’s state for a single tick and then emits it out of the output. I am not going to dive into the details about how it is able to achieve this functionality because it gets a little too dry even for this post. But basically if you hand it a one for an input, in the next clock cycle the output will be one. Similarly if the output is one currently and you hand it a zero it will output a zero in the next cycle. This concept of “delay” allows us to hook up a sort of feed back loop where we can know what the state was last tick. The state of last tick will be pumped out of the DFF chip. We take that output and route it around to the A input of the Mux chip. That way if we are not loading anything this cycle that value will go through the Mux chip back into the DFF’s input. Conversely if the load bit is one the value from the B input of the Mux chip will be piped to the DFF chip thereby updating the value. As per usual this behavior will be easier to conceptualize with the following diagrams:

Lets think of it in steps. So lets say for the first step we set the inputs to the above values. At this point we don’t know the output of the DFF chip and since we don’t know it’s value we don’t know what the output for the single bit register is either.

We see there have been a couple changes to the chip from the previous step. The DFF chip now has a state of one and therefore the output of the single bit register is one. We also see that the Load bit has been set to zero and thereby routing the output of the DFF chip back into the input of the DFF.

The next step shows us that the output continues to be one even though the current input is zero. If you wanted you could keep it in this state for as long as you want. But lets update the value and see how the value propagates through the system.

We have now set the Load bit to one. Therefore the value being piped into the DFF is now from the input of the chip, which at this point is zero. But as you will notice the internal state is still one because it updates on a one step delay. Therefore the output of the chip is still one during this step.

Now the DFF outputs the input from the previous step and therefore the output of the chip has now been updated to zero. This is how our system is able to store a single bit. This single bit register is the foundational building block of the RAM. From here the implementation details are trivial so I will spare you the detailed explanation and instead gloss over the details. The first step is to convert the above chip into a 16 bit register.

We then group those together into a group of 8, 16 bit registers.

Why stop there ? Why not make a group of 8 of these group of 8- 16 bit registers?

This makes a RAM64 chip. Make 8 of those into a group and…

Thusly the RAM512 is constructed. If this process is repeated again you will get a RAM4K and again gives you 32,768- 16 bit registers to store all the numbers! This is a supremely small RAM but gives you some sense of scale of what those little sticks of RAM in your computer are actually up to as well as solving the question “How do computers remember?”

]]>https://georgefabish.com/how-computers-remember/feed/0The ALUhttps://georgefabish.com/the-alu/
https://georgefabish.com/the-alu/#respondMon, 04 Feb 2019 02:51:28 +0000https://georgefabish.com/?p=2131The Arithmetic-Logic Unit All of mathematics solved with six bits. The ALU is the work-horse in the CPU. It is responsible for almost all computations. In previous posts I have described building various kinds of chips that have somewhat simple functionality. Each chip was required to have a specific behavior that can be repeated giving ... Read moreThe ALU

All of mathematics solved with six bits.

The ALU is the work-horse in the CPU. It is responsible for almost all computations. In previous posts I have described building various kinds of chips that have somewhat simple functionality. Each chip was required to have a specific behavior that can be repeated giving the same result every time. The last chip I described which was the Full-Adder was the first chip that had some immediate mathematical utility. Well this is the post where we bring everything together to create the most complicated chip we have made thus far, which will be able to do the following :

ALU -(Inputs x,y)

0

1

-1

x

y

!x

!y

-x

-y

x+1

y+1

x-1

y-1

x+y

x-y

y-x

x&y

x|y

ALU Description

So how does the chip know which function you want to execute? Well it employs the idea of control bits. This chip actually has 6 control bits that ingeniously tell the ALU which function to compute. Those inputs are named : ‘zx’, ‘nx’, ‘zy’, ‘ny’,’ f’, and ‘no’.

So what do these control bits do? Looks complicated but as with most things, when broken down it is super simple. The first four bits act directly on the x and y input. The ‘zx’ and ‘zy’ have the same functionality. They stand for zero-x and zero-y . As the name suggests if the ‘zx’ bit is set to one(true) then the x input will be set to zero. If the ‘zx’ bit is set to zero(false) then nothing will be done to the x input. The same thing happens with the ‘zy’ bit. It will either set the y input to zero or do nothing based on the value. The ‘nx’ and ‘ny’ stand for not-x and not-y. And……you guessed it! If the ‘nx’ bit is set to one it will ‘not’ the x input. If the input is zero then again nothing will be done to the x input. The ‘f’ or function control bit performs x+y if set to one. Otherwise it performs an ‘and’ operation on x and y if set to zero. Lastly, if the ‘no’ control bit is set to one then the entire output will be negated, otherwise it will be left as is. One last thing to note about this is that these functions are applied in order from left to right. So if the ‘no’ control bit is set to one the entire output will be negated only after all the other (if any) functions have been executed. So that’s all well and good but I am sure you are still skeptical about why this matters to you and your pursuit of mathematical superiority.

Commands

Well it turns out that if you wanted to add x to y you need only to feed the x and y inputs into the ALU and set the control bits to “000010”. If you refer to the illustration you would see that setting the control bits to that code would have disabled all control bits except for the ‘f’ bit. Which we have already said that if the ‘f’ bit is set to one then we perform addition to x and y. So that’s pretty awesome but also a little bit of a trivial example. Lets do something more complicated. Lets say we want to perform y-x. How would we do that?

So let’s see how this will work by defining our terms and running it through the ALU manually and seeing if the results match what we would expect. Let’s say that x=3 and y =5. At this point we would expect the output to be 2. Lets see what happens. First it looks like nothing happens until the ‘ny’ bit. So lets start by applying a ‘not’ operation to the y input. As we remember from before it simply just reverses the bits. One more thing to remember is that inputs are 16-bit. So even though there will be

//5 in binary = 101//Convert it into 16 bits

0000000000000101

//Not operation flips each bit.

1111111111111010

//Which is -6 in decimal//Since f =1 we need to add x to y//x = 3 or 11 in binary// or 0000000000000011 in 16 bit format. Which we now add together.

Now that we have proven that works perfectly I will leave you with a list of commands you send to the ALU to get desired results and I will leave you to run examples to prove to yourself that the desired behavior will be achieved.

ALU Command Chart

So you can get any of those functions on the right-hand side of the table by feeding the appropriate values to the control bits. As shown on the left-hand side of the table.

Observation Bits

There are two outputs I have neglected to mention that complete the specification for the HACK ALU. Those are outputs ‘zr’ and ‘ng’. I call these observation bits because ‘zr’ will be one if and only if the output is equal to zero. While ‘ng’ will be one if and only if the output is less than zero. Why is that info important? Well with those magic bits we can now perform equality checks like greater than or less than. Knowing the equality of an object now allows us to have our first primitive condition. Having our first primitive condition allows us to implement jump commands (which I will talk about in a later post). With jump commands at our disposal we can take over the world. At this point in the past I usually would now dive into how you would design this chip using HDL. I wont be doing that in this post because of the level of complication. I feel as if the complete implementation would be dull. Instead I will leave you with this gorgeous schematic that you can inspect to see my proposed implementation of the ALU.

]]>https://georgefabish.com/the-alu/feed/0In Addition to That…https://georgefabish.com/in-addition-to-that/
https://georgefabish.com/in-addition-to-that/#respondSun, 27 Jan 2019 02:13:19 +0000https://georgefabish.com/?p=2111This is where we learn how to do addition in the lowest levels of computer architecture. After completing project one you are left with five, 16-bit chips and no real idea where to go next, or why you made them. Luckily, you don’t have to be as smart as the guys who invented the computer ... Read moreIn Addition to That…

]]>This is where we learn how to do addition in the lowest levels of computer architecture.

After completing project one you are left with five, 16-bit chips and no real idea where to go next, or why you made them. Luckily, you don’t have to be as smart as the guys who invented the computer in the first place, and you can always just continue on with the course. In project two you are commissioned to build five more chips. Four of them have to do with addition and the fifth one is the main ALU chip of CPU, which I will talk more about in a later post. One question you might have is what exactly do I mean by 16-bit chips. Well, in previous posts I have talked about the NOT and the MUX chip. Both of those would be considered single-bit chips, because you are feeding them one bit (a zero or one) at a time. So a 16-bit version of those can handle inputs of 16-bits at the same time. Why would you want to do that? Other than the obvious speed benefit when using 16 bits, all of the sudden you have the ability to start representing numbers greater than one. In fact, for an unsigned (only positive) 16-bit number can be as large as 65535, which is awesome because I don’t know about you but I am ready to get back to the real world where 1+1 = 2 despite what Boolean Algebra tries to tell you. But before we ascend to the world of comfort and normalcy that normal arithmetic offers, we must descend back into the weird world of binary. Let me give you a couple examples of how some of these 16-bit chips work.

‘NOT’ 16-bit

The single bit not chip is probably the easiest chip to comprehend it just takes what ever its given and returns the opposite. The 16-bit version does the same thing, it just does it 16 times.

Single Bit NotIn 0 Out 1In 1 Out 0

NOT16In 0101010101010101Out 1010101010101010

As you can see there is nothing crazy about it. It is literally just going through one bit at a time (actually instantly) and flipping each bit to its opposite.

MUX8WAY 16-bit

This chip does vary a bit (another bad computer pun) from its single bit counter part but the general idea is the same. Instead of writing any binary examples it will probably just be easier to explain from a higher level.

If you remember, the purpose of the MUX chip was to – based on the position of a selector switch – return either input ‘a’ or ‘b’. Well, as I eluded to before, being able to handle more bits has given us the ability to handle more numbers. So now the selector switch that used to only have two positions has eight positions and eight inputs. On top of that, each of those single bit inputs has been upgraded to 16-bit. Based on the position of the selector it will return the associated input. For example, if the selector was at zero the chip would return input ‘a’ and if the selector is at position one the chip will return input ‘b’. Then if the selector was flipped all the way to position eight it would return input ‘h’. This will come in extremely handy in the future when we start discussing RAM architecture. Of course since this is a 16-bit chip each of those inputs are 16-bits. So whichever input is selected, a 16-bit value will be emitted. Now that you have an idea what I mean by 16-bit chips let me proceed in explaining the first chip that handles addition. Namely the “Half-Adder.”

Half-Adder

The differences between adding binary numbers and the addition that you learned in the second grade have very few differences. Actually, adding binary is even more simple than what we are used to. Let me give you a couple examples.

When adding binary you start from the right hand side and work left – just like we have learned before. On the far right we have 1+1. Since there is no symbol for ‘2’ in binary we carry the one to the next column. Just like you would if you were adding a ‘9’ and a ‘1’. Moving one column left we now see a 0+1 but we also are carrying a one so that gets changed to 1+1. Therefore we carry the one again, facing yet another 1+1 scenario. This results in one more carry, which leaves us with the result of 1000 in binary or 8 in decimal form (decimal form of course being the way we usually count). Easy! One thing to keep in mind is that we will build this “adder” chip with the idea of it being part of and limited to our 16-bit architecture. So when adding two 16-bit numbers together, if there is a carry into a 17th bit, then that bit will just get thrown away. Like this:

As you can see above, the extra one that we were carrying just gets tossed out because our system can only handle 16-bits. Now the very observant ones reading this would realize that the result of the binary addition comes out to 26,559. Which is nowhere near the actual 92,095. This is, of course, because we dropped the extra digit. What is interesting to note about this number is that it turns out to be the modulus of our 16-bit max, or in other words when you divide 92,095 by 65,536 (216 which is our max unsigned 16-bit number) you will be left with the remainder of 26,559. I’ll leave you to ponder the significance of this fact and to consider how inextricably intertwined the universe and we as individuals are.

Moving Forward

So how does one go about building a chip the provides this behavior? Well, lets take a closer look at our previous example and see if there are any observations we can make about the different states we encounter in the process of addition. Looking back at our first simple example:

Let’s take it one column at a time. What do we need to communicate to successfully add two bits together? Well for the first number all we need to communicate is the sum, and then if there is any resulting carry or not. This is exactly the specification of the “Half-Adder” chip. The Half-Adder chip has two inputs and two outputs. The two inputs are the two bits that are being added together. One output represents the sum of the two bits, while the other output holds the value of the carry.

While at first trying to imagine how to design a chip that could handle addition might have seemed daunting, when broken down into this simple little part it is actually quite simple. With the above definition we derive the following truth table that fully specifies the necessary behavior of this chip:

From there we can use the logic that I have previously described to derive the following formula:

sum = (Not(a) & b) OR (a & Not(b))carry = a & b

It turns out that both sum and carry can be defined with one chip. The behavior of sum is described by the XOR (aka Exclusive Or) chip. This chip only returns true when the two inputs are different. So the resulting HDL file looks like this:

By now maybe you’ve figured out why this is called the Half-Adder. It is called this because it only handles half the functionality we need it to. Lets look at that example again.

The Half-Adder works perfectly for the first column but will it work for the second? No! It can’t work because there is no way for it to realize that the column before it had created a carry. Therefore in order to add two numbers together that are more than single bits, we will need a chip that has three inputs. One for the two bits that are being added together, plus a third to handle carries.

The behavior for this chip is slightly more complicated, and therefore results in a slightly more complicated truth table.

While we may be tempted to run off and try writing a formula that solves this table, it might be helpful to step back and remember what we are trying to do. We have already successfully created a chip that, as long as there are no incoming carries, will output a sum and resulting carry. So using that single chip we can chain that behavior together to add bits ‘a’ and ‘b’ together. From there we can take the sum of ‘ab’ and add it again to the ‘in-carry’ bit. Then we can define that out-carry bit by whether or not either of the first two sums had a carry bit.

Maybe a visualization would help:

Now the name Half-Adder makes sense. It takes two Half-Adders to make a full adder. The above chip will now be able to successfully add multi-bit numbers together. The only thing that is left to do is to convert this into a chip that can handle 16-bit calculations. To do that, you string together one half-adder and 15 full-adders together in a similar way to the above schematic. Resulting in a chip that is able to do 16-bit addition.

]]>https://georgefabish.com/in-addition-to-that/feed/0Muxtopiahttps://georgefabish.com/nand2tetris-muxtopia/
https://georgefabish.com/nand2tetris-muxtopia/#respondSun, 20 Jan 2019 17:55:38 +0000https://georgefabish.com/?p=2036My last post introduced the trivial but necessary “Not” chip. Before I move away from the first project I would like to mention one last chip that I found intriguing and especially difficult to solve. That chip is called a “Multiplexer” chip, or a “Mux” chip for short. This chip has a much more complicated ... Read moreMuxtopia

]]>My last post introduced the trivial but necessary “Not” chip. Before I move away from the first project I would like to mention one last chip that I found intriguing and especially difficult to solve. That chip is called a “Multiplexer” chip, or a “Mux” chip for short. This chip has a much more complicated specification than the “Not” chip.

How it works

The mux chip has three inputs which I will refer to as ‘a’, ‘b’, and ‘sel’ short for selector. The mux chip also has just a singular output. The functionality that we want from this chip is when the ‘sel’ input is 0 the out should equal ‘a’. If ‘sel’ is 1 the output should equal ‘b’. Therefore from that information we can derive the following truth table.

This is a more complicated table than the table that was generated from the “Not” chip. Because of this you probably won’t be able to come up with the solution just by looking at this table. You’ll need to break this information into chunks making it easier to digest.

Combinational Logic vs Sequential Logic

In order to break this problem down into chunks we must first understand the difference between these two forms of logic. As a programmer just about every problem we have faced has been sequential in nature. We apply filters that direct outcomes down different branches of logic. If we were to approach this chip with sequential logic it would look something like this:

1. if(sel ==0)2. {return a;}3. else 4. {return b;}

What makes this expressively sequential in nature is the fact that this logic has to be executed line by line, starting at the top and working towards the bottom. Therefore it has to have some sense of where it is currently and it also has to know where to go next. Essentially what is happening is the program starts at line one. If line one is true it will move to line two. If it is not true it will have to jump to line three. The problem with trying to build a low level chip with this type of logic is that we are working with voltage. Therefore for all intents and purposes there is not concept of time. Everything happens instantly. Well if sequential logic wont work then what do we need? This is where combinational Logic comes in to save the day.

These three discs are an excellent example of combinational logic. There is no sense of time or execution order. The color that you see is strictly based on the color or combination of colors that happen to overlap. So how can we take this logic and apply it to the mux chip? This is where we are forced to do some Boolean algebra. It’ll be fun though I promise.

Boolean Algebra

What is Boolean Algebra? Boolean Algebra is the a special kind of math that deals strictly with the numbers 0 and 1. Because the numbers have such a limited scope you will see that we can take complicated expressions and simplify them much more than would be expected.

First lets take another look at that truth table and see if we can pull out some useful info.

What we need is some sort of formula that will match up with this truth table. The first thing you want to do when trying to derive a formula (or design a chip) from a truth table is to extract the scenarios where the output or result was equal to one. Doing that results in a far simpler table

From here we can design a formula for each line. An easy way of thinking about is looking at a line’s inputs and then re-writing the variables to so that it works as a long “AND” statement. As we know an “AND” statement only returns true if both inputs are true so the first line would be converted into something like this:

So in this case we know that if we take the “NOT” of ‘a’ then “AND” that together with ‘b’ and ‘c’ we would get the desired out.The HDL for that would look like this:

If we were only given that one line the above chip would work perfectly. But there are actually three different scenarios where the output is also one. So how do we get a formula that defines each those occasions? We do the same thing as we did above but then we just “OR” those statements together. Like so:

As you can see this is getting somewhat unwieldy at this point. But I’ll still convert this as is into HDL to show that it works as is.

Sure enough if you put the above code into the compiler and run it against the tests it passes with flying colors. But something tells me we can simplify this chip quite a bit. So lets go back and write out the whole expression that we have discovered so far. If we try to make it a single statement it would look like this:

“In Boolean Algebra to “AND” something is equivalent to multiplying it, because since the two numbers you are multiplying can only be a one or a zero the result of the multiplication can only be one if both numbers being multiplied are one. To “OR” something is equivalent to addition because similarly you will only get one if one of the numbers you are adding together is one. Lastly anywhere where you see a “NOT” you can replace it with a ‘-‘ symbol to make it easier to read. So lets re-write the above expression replacing the “AND” and “OR” operations with multiplication and addition as well as removing the all “NOT” operations.

(-absel)+(a-b-sel)+(ab-sel)+(absel)

If we look at the above formula you can see that there are several repeating factors that can be simplified. Lets start by removing ‘sel’ where ever possible.

(-abSEL)+(a-b-sel)+(ab-sel)+(abSEL)sel(-ab+ab) + a-b-sel + ab-sel

It turns out we can simplify “sel(-ab+ab)” even more by factoring out the ‘b’

sel(-aB+aB) + a-b-sel + ab-selsel(b(-a+a)) + a-b-sel + ab- sel

Now we ended up with “-a + a”. Which will always equal one because if ‘a’ was equal to zero, then zero + NOT(zero) equals one and vice versa. Therefore we can simplify the expression even more.

Just like that we have cut a huge expression down into something manageable. Now how do we convert this back into HDL? Lets remember that anywhere you see multiplication replace that with an “AND” anywhere you see a negative symbol replace that with a “NOT”. Anywhere you see an addition symbol replace that with an “OR”. Like this:

sel(b) + -sel(a)

(sel & b) Or (NOT(sel) & a)

This is a much simpler expression which can be rewritten in HDL as follows.

Now if you run through any of the values from the truth table above into this formula you will find that they all magically work. Thus a triumph in combinational logic has led us to the creation of the useful mux chip.