Jump to content

Your First Computer: Difference between revisions

From Logic World Wiki
No edit summary
Major rewrite to focus more on teaching the reader what it takes to design and build your own computer, as well as teaching the reader a little more theory. With the build guide serving more as an example than instructions to follow.
Line 1: Line 1:
{{stub}}
{{stub}}


If you're a new player, you might have already built a few circuits already: an [[Full Adder|adder]], some kind of memory, a [[decoder]]. But getting to the next level where you have a functioning and [https://en.wikipedia.org/wiki/Turing%20completeness turing complete] computer, is incredibly daunting. This article will serve as a guide to how you might build your first computer.
== Introduction ==
This guide is for anyone who is interested in learning how computers work, and wants to learn by building a computer for themselves, but doesn't know where to start.


== Planning The Computer ==
This guide assumes the reader has a basic understanding of Logic Worlds [[Building mechanic|building mechanics]], and has some experience building some of the more intermediate components, such as [[Full Adder|adders]], [[Random-access memory|memory]], and [[decoder|decoders]]. As well as at least some understanding of the [[wikipedia:Binary_number|binary numbers system]].
[[File:Computer Structure diagram.png|300px|right|thumb|The structure of the computer in this article]]While you might be inclined to jump head first into building, this results in at the very least messy building in Logic World, or a malfunctioning computer. In our computer we are going to first plan and '''document''' the capabilities our computer will have.


To start with the planning section, it helps to think about what this computer we are building is for. This could range from wanting to test out an [[wikipedia:Arithmetic_logic_unit|ALU]] you built, or to perform a specific program you have in mind, e.g. matrix multiplication.
The first part of this guide will go over the hardware of a very basic computer system. This guide will not be covering things like text and image processing, internet browsing, operating systems, file systems, windows and graphical user interfaces and other systems generally associated with personal computers. These systems are all built using software, and covering them, as well as software in general, is an entire subject unto itself. This guide will only be focusing on creating hardware that can execute software. Furthermore, this guide will only be focusing on ''core'' hardware, the bare minimum required to run software. This guide will not be covering peripheral hardware such as screens, graphics processor units, storage devices, co-processors, or multi-core setups. Nor will this guide be covering the topic of source code compilation. These are all topics that require their own study and as such are outside the scope of this guide.


In this guide, our computer will be built as a way to learn each of the different parts of a computer, without any bells or whistles. So, we are going to give the computer the bare minimum.
The second part of this guide will be covering the process of designing your computer. It will bring up key details you should consider when planning your computer build, as well as providing a basic layout that you can use if you don't want to design everything from scratch yourself.


=== Data Width ===
The third part will of this guide will cover building your computer.
Data width refers to how many bits the binary numbers in our computer will use. This doesn’t have to be universal across an entire computer; for example, the memory might store in 32 bits, but our ALU only 16. In our computer however, we will use the same data width across our entire computer. While you can definitely go smaller, 8 bits is a good number to make it less complicated while being able to do a lot with our computer, so our data width is going to be 8 bits.


=== The ROM ===
== Hardware Theory ==
The read only memory in our computer is where we are going to store all of the instructions for our computer. We aren’t looking for anything too big, so to keep with our data width of 8 bits, we will have 2^8 or 256 addresses in our ROM (this only needs to be addressed with 8 bits). And each address will store 8 bits of data for our instructions.
All computers, regardless of complexity, need at least these 4 components. Program memory typically made using [[ROM|read-only memory]], an [[Arithmetic logic unit|ALU]], working memory typically made using individual [[Register|registers]] and [[random-access memory]], and a [[Control Unit|control unit]].
[[File:Computer_Structure_diagram.png|right|thumb|300x300px|The structure of the computer in this article]]
Program memory is where the program to be executed is stored. It takes the form of a list of binary numbers called machine code. The computer will step through this list one number at a time, grabbing each number, interpreting it, and performing an action corresponding to that number. This process is referred to as the fetch-decode-execute cycle.


=== Instruction Set ===
Actions that a computer can perform can vary greatly, but two of the most common actions are moving data around, and performing operations using data.
This is the most influential part of planning. It’s tough for a beginner to get a feel for how to design an instruction set, and has the most impact on how easy it will be to build your computer. If you wanted to design a more advanced computer, you should design your own instruction set, and see how that influences your computer.


Every instruction has an opcode that tells the computer what instruction you ''actually'' want it to perform. There are roughly 4 main types:
The ALU is what allows a computer to perform these operations, and working memory is where the data being operated on is stored.


* Data management. This involves moving data from one register to another, loading or saving to memory, or loading an arbitrary value into your registers, etc.
Moving data around may seem pointless at first, but often ALU's don't have direct access to working memory, but instead must use whatever is stored in more local registers. Data must therefore be juggled between working memory and registers in order to give the ALU access to the entire dataset. It's also useful for organizing data into larger data structures.  
* Operations. These instructions use your ALU to transform data in a specific way, e.g adding two registers or using bitwise logic.
* Conditional logic. This is where jumps come into play. These instructions let you test registers against each other, and change the order the computer executes instructions based on tests.
* Interrupts. While you generally wouldn’t find interrupts in a computer in Logic World, these let you talk to computer hardware, or call specific kernel functions (don’t worry if you don’t understand what that means, it isn’t important for this guide).


With that, this is the instruction set we are going to base our computer around:
While it might be hard to wrap your head around this at first, everything that computers do can mostly be reduced to these two operations: moving data around, and operating on data. How these two operations can be structured to do things like browse the internet or play games requires a study of software, and is thus outside the scope of this article. But under the hood, millions of these operations are happening at a rate of billions a second, and it's this size and speed that allows computers to perform much more complicated tasks.
 
Coordinating these instruction fetches, data moves, and ALU operations is the control unit. It's job is to decode the binary integer fetched from program memory and decide what control signals to send out and when.
 
While this module can get very complicated, the simplest control unit consists of a [[Lookup Table|lookup table]] that takes the current instruction integer and the value of a [[counter]] as a combined input, and produces all the read, write, and function signals the computer needs as an output. A clock is used to increment the counter, and produce a [[Edge Detection|rising edge]] for the write signals, and the table is configured to read from program memory and write to an instruction register when the counter is 0, then increment an instruction pointer register when the counter is 1. From there, the table can be configured to do different things depending on the value of the instruction register. The counter register is reset when the instruction is complete, and the fetch-decode-execute cycle repeats.
 
On top of being able to move data around and perform operations, there is a third common type of action a computer can perform called a control flow instruction. These consist of instructions like jumping, conditional branching, calling and returning, and software interrupts. Though we'll only be focusing on jumping and conditional branching here. These all have one thing in common, and that is to break the linear sequence of the program.
 
Normally, as part of the fetch-decode-execute cycle, the instruction pointer used to index the program memory is incremented after each instruction fetch. Setting up the next instruction in the list to be executed when the current instruction is complete. By writing a new value to the instruction pointer, we queue up a different section of the program memory to be executed next.
 
This is often used to create infinite loops in your program, or finite loops if you use a conditional branch that resumes a normal execution sequence if a number comparison comes up not equal.
 
These three action types are the minimum required to create a [[wikipedia:Turing completeness|turing complete]] computer - a computer that can calculate anything.
 
== Planning A Computer ==
While you might be inclined to jump head-first into building, computers are complicated things, as the previous section shows. A little bit of forethought is required before you start building in order to help guide your building decisions. Otherwise you will end up with a computer that's too messy to modify, and if it ends up malfunctioning, it'll be too messy to troubleshoot as well. '''Documentation''' is key.
 
Before you begin, think about what you want this computer to do. What ALU functions do you want it to have? What special instructions or special hardware? You'll also want to consider key details such as the width of your ALU, the width of your working memory and program memory, as well as the size of these memory modules, as this will determine the width of your address bus and indexing registers.
 
=== Example Computer ===
Since you'll likely not know what to add, this section will provide for you a very basic plan for you to follow.
 
==== Component Sizing ====
Our computer will be very basic. We'll be using an 8 bit ALU and 8 bit working memory. We can do a lot with 8 bits without needing to make the hardware too big.
 
We'll also have 256 addresses in our program memory, so an 8 bit address bus and 8 bit indexing register is in order, since 2^8 is 256. The width of the program memory is determined by our computers instruction set.
 
==== Instruction Set ====
This is the most influential part of planning. This will not only dictate what your computer does, but also how it will be stored in program memory.
 
You can design your own instruction set from scratch if you want something more complex. But for our instruction set, we will be adding 4 types of instructions: load immediate, operate, move, and jumping/branching. These will be organized with the prefixes <code>00</code> <code>01</code> <code>10</code> and <code>11</code> respectively.
 
We will be squeezing our instructions into 8 bits, making our program memory 8 bits wide. With 2 of those bits being used as a prefix, that leaves 6 bits to be used as instruction arguments.
 
Below is the instruction set we will be using:
{| class="wikitable"
{| class="wikitable"
|+
|+
Line 35: Line 66:
|-
|-
|Immediate
|Immediate
|00 xxxxxxxx
|00 xxx xxx
|x: The immediate value.
|x: The immediate value.
|Puts a value given by x into register 0.
|Puts a value given by x into register 0.
Line 79: Line 110:
|Tests the values in register 4 and 5 against each other, putting the result into the flags register.
|Tests the values in register 4 and 5 against each other, putting the result into the flags register.
|-
|-
|Jump 0
|Jump zero
|11 001___
|11 001___
|
|
|Jumps to the instruction in register 6, if the 0 flag is {{On}}.
|Jumps to the instruction in register 6, if the zero flag is {{On}}.
|-
|-
|Jump carry  
|Jump carry  
Line 104: Line 135:
|Jumps to the instruction in register 6, regardless of an flags.
|Jumps to the instruction in register 6, regardless of an flags.
|}
|}
===Registers===
Notice that most instructions only use 3 of the 6 argument bits. Move immediate uses all 6, allowing the user to load an immediate value of 0-63, and move also uses all 6 to index 2 registers.
We will use 8 all-purpose registers, because of the 3-bit limit imposed by the Move instruction. Note that the program counter and instruction register is seperate to these all-purpose registers, so our program will not be able to ''directly'' edit the values in these registers.
 
==== Registers ====
Since we have 3 bits available to index registers with, our computer will include 8 all-purpose registers.
 
If you design your own computer, you will need to consider what registers to include, and what will they be used for.
 
In our case, while all of our registers will be general purpose, meaning you can use them for whatever purpose you like, most of them will be used in a specific way for specific instructions. The registers and their purposes are as follows:
 
* Register 0 - destination for immediate values
* Register 1 - source of one argument of an operation
* Register 2 - source of the other argument of an operation
* Register 3 - destination of the result of an operation
* Register 4 - source of one argument for a comparison
* Register 5 - source of the other argument for a comparison
* Register 6 - target address for a jump
* Register 7 - general purpose
 
Note that the instruction pointer and instruction register are separate to these all-purpose registers, so our program will not be able to ''directly'' edit the values in these registers.
 
Editing of these registers is done by the control unit either during the fetch-decode-execute cycle, or through the use of a jump instruction.
 
==== Flags ====
If you decide to include conditional branching in your instruction set, you will need to decide what conditions (called flags) you want to check for and how you're going to check them.


=== Flags ===
In our case, we will have the following 4 flags in our computer:
We will only have 3 flags in our computer, as can be seen in the instruction set:
* Zero flag -  This flag will be {{On}} only if the first value from the last test instruction was equal to 0.
* Carry flag - This flag will be {{On}} only if that last add (or subtract) instruction carried/overflowed.
* Negative flag - This flag will be {{On}} only if the first value from the last test instruction was negative (had its most significant bit {{On}}).
* Equal flag - This flag will be {{On}} only if the values from the last test instruction were equal to each other.
We will also be storing the states of these flags in another register called a flag register. Allowing us to capture the state of these flags with one instruction (test) and use the state of these flags in another instruction (jump).


* 0 flag. This flag will be {{On}} only if the first value from the last test instruction was equal to 0.
You do not need to store the state of these flags in a register, or perform a test and branch in two separate instructions. You can perform your comparison and use the results immediately, if you want. Doing so will just require a more complex execution sequence, and possibly bigger instructions.  
* Carry flag. This flag will be {{On}} only if that last add (or subtract) instruction carried/overflowed.
* Negative flag. This flag will be {{On}} only if the first value from the last test instruction was negative (had its most significant bit {{On}}).
* Equal flag. This flag will be {{On}} only if the values from the last test instruction were equal to each other.


=== Example Program ===
==== Example Program ====
This will show our instruction set using a fibonacci program.
Putting everything together, we can use our instruction set to write a Fibonacci program:
<syntaxhighlight lang="asm">
<syntaxhighlight lang="asm">
; Initialise registers
; Initialise registers
Line 128: Line 182:
Move 2, 1  ; 10 001 010
Move 2, 1  ; 10 001 010
Move 1, 3  ; 10 011 001
Move 1, 3  ; 10 011 001
Immediate 5 ; 00 000101
Immediate 4 ; 00 000100
Move 6, 0  ; 10 000 110
Move 6, 0  ; 10 000 110
Jump        ; 11 101 ___
Jump        ; 11 101 ___
</syntaxhighlight>
</syntaxhighlight>Visualizing the process, we can imagine the first instruction being copied into the instruction register, the instruction pointer being incremented, then the value being copied from the instruction register to register 0.
 
Then the step counter resets, and we begin again, fetching the instruction, incrementing the instruction counter, and moving the content of register 0 to register 2. Reset the step counter and on and on it goes.
 
This is what our computer ''should'' do when it's complete.
 
With the planning done, it's now time to move on to building.
 
==Building the Computer==
==Building the Computer==
{{Todo|Flesh this section out more}}
Start by building your major components first. Build your program memory, working memory, ALU, registers, and control unit all on their own circuit board.
This will make rearranging things easier.
Use sockets to create interfaces. This will make changing and swapping out a component easier.
It helps to build everything except the control unit first, then to build the control unit interface so you know what inputs and outputs your control unit needs to have.
Test as you go, and be prepared to do some troubleshooting. Nothing ever works perfectly the first time.

Revision as of 06:20, 12 October 2025

This article is a stub. Please help expand it by adding content.

Introduction

This guide is for anyone who is interested in learning how computers work, and wants to learn by building a computer for themselves, but doesn't know where to start.

This guide assumes the reader has a basic understanding of Logic Worlds building mechanics, and has some experience building some of the more intermediate components, such as adders, memory, and decoders. As well as at least some understanding of the binary numbers system.

The first part of this guide will go over the hardware of a very basic computer system. This guide will not be covering things like text and image processing, internet browsing, operating systems, file systems, windows and graphical user interfaces and other systems generally associated with personal computers. These systems are all built using software, and covering them, as well as software in general, is an entire subject unto itself. This guide will only be focusing on creating hardware that can execute software. Furthermore, this guide will only be focusing on core hardware, the bare minimum required to run software. This guide will not be covering peripheral hardware such as screens, graphics processor units, storage devices, co-processors, or multi-core setups. Nor will this guide be covering the topic of source code compilation. These are all topics that require their own study and as such are outside the scope of this guide.

The second part of this guide will be covering the process of designing your computer. It will bring up key details you should consider when planning your computer build, as well as providing a basic layout that you can use if you don't want to design everything from scratch yourself.

The third part will of this guide will cover building your computer.

Hardware Theory

All computers, regardless of complexity, need at least these 4 components. Program memory typically made using read-only memory, an ALU, working memory typically made using individual registers and random-access memory, and a control unit.

The structure of the computer in this article

Program memory is where the program to be executed is stored. It takes the form of a list of binary numbers called machine code. The computer will step through this list one number at a time, grabbing each number, interpreting it, and performing an action corresponding to that number. This process is referred to as the fetch-decode-execute cycle.

Actions that a computer can perform can vary greatly, but two of the most common actions are moving data around, and performing operations using data.

The ALU is what allows a computer to perform these operations, and working memory is where the data being operated on is stored.

Moving data around may seem pointless at first, but often ALU's don't have direct access to working memory, but instead must use whatever is stored in more local registers. Data must therefore be juggled between working memory and registers in order to give the ALU access to the entire dataset. It's also useful for organizing data into larger data structures.

While it might be hard to wrap your head around this at first, everything that computers do can mostly be reduced to these two operations: moving data around, and operating on data. How these two operations can be structured to do things like browse the internet or play games requires a study of software, and is thus outside the scope of this article. But under the hood, millions of these operations are happening at a rate of billions a second, and it's this size and speed that allows computers to perform much more complicated tasks.

Coordinating these instruction fetches, data moves, and ALU operations is the control unit. It's job is to decode the binary integer fetched from program memory and decide what control signals to send out and when.

While this module can get very complicated, the simplest control unit consists of a lookup table that takes the current instruction integer and the value of a counter as a combined input, and produces all the read, write, and function signals the computer needs as an output. A clock is used to increment the counter, and produce a rising edge for the write signals, and the table is configured to read from program memory and write to an instruction register when the counter is 0, then increment an instruction pointer register when the counter is 1. From there, the table can be configured to do different things depending on the value of the instruction register. The counter register is reset when the instruction is complete, and the fetch-decode-execute cycle repeats.

On top of being able to move data around and perform operations, there is a third common type of action a computer can perform called a control flow instruction. These consist of instructions like jumping, conditional branching, calling and returning, and software interrupts. Though we'll only be focusing on jumping and conditional branching here. These all have one thing in common, and that is to break the linear sequence of the program.

Normally, as part of the fetch-decode-execute cycle, the instruction pointer used to index the program memory is incremented after each instruction fetch. Setting up the next instruction in the list to be executed when the current instruction is complete. By writing a new value to the instruction pointer, we queue up a different section of the program memory to be executed next.

This is often used to create infinite loops in your program, or finite loops if you use a conditional branch that resumes a normal execution sequence if a number comparison comes up not equal.

These three action types are the minimum required to create a turing complete computer - a computer that can calculate anything.

Planning A Computer

While you might be inclined to jump head-first into building, computers are complicated things, as the previous section shows. A little bit of forethought is required before you start building in order to help guide your building decisions. Otherwise you will end up with a computer that's too messy to modify, and if it ends up malfunctioning, it'll be too messy to troubleshoot as well. Documentation is key.

Before you begin, think about what you want this computer to do. What ALU functions do you want it to have? What special instructions or special hardware? You'll also want to consider key details such as the width of your ALU, the width of your working memory and program memory, as well as the size of these memory modules, as this will determine the width of your address bus and indexing registers.

Example Computer

Since you'll likely not know what to add, this section will provide for you a very basic plan for you to follow.

Component Sizing

Our computer will be very basic. We'll be using an 8 bit ALU and 8 bit working memory. We can do a lot with 8 bits without needing to make the hardware too big.

We'll also have 256 addresses in our program memory, so an 8 bit address bus and 8 bit indexing register is in order, since 2^8 is 256. The width of the program memory is determined by our computers instruction set.

Instruction Set

This is the most influential part of planning. This will not only dictate what your computer does, but also how it will be stored in program memory.

You can design your own instruction set from scratch if you want something more complex. But for our instruction set, we will be adding 4 types of instructions: load immediate, operate, move, and jumping/branching. These will be organized with the prefixes 00 01 10 and 11 respectively.

We will be squeezing our instructions into 8 bits, making our program memory 8 bits wide. With 2 of those bits being used as a prefix, that leaves 6 bits to be used as instruction arguments.

Below is the instruction set we will be using:

Name Opcode (8 bits) Opperands Description
Immediate 00 xxx xxx x: The immediate value. Puts a value given by x into register 0.
Add 01 000___ Adds the values in register 1 and 2, and puts the result in register 3.
Subtract 01 001___ Subtracts the value in register 1 from register 2 and puts the result in register 3.
And 01 010___ Bit-wise ANDs the values in register 1 and 2, and puts the result in register 3.
Or 01 011___ Bit-wise ORs the values in register 1 and 2, and puts the result in register 3.
XOR 01 100___ Bit-wise XORs the values in register 1 and 2 and puts the result in register 3.
Not 01 101___ Bit-wise NOTs the values in register 1 and 2 and puts the result in register 3.
Move 10 xxx yyy x: The register to move data from. y: The register to move data to. Moves the data from register x into register y.
Test 11 000___ Tests the values in register 4 and 5 against each other, putting the result into the flags register.
Jump zero 11 001___ Jumps to the instruction in register 6, if the zero flag is ON.
Jump carry 11 010___ Jumps to the instruction in register 6, if the carry flag is ON.
Jump negative 11 011___ Jumps to the instruction in register 6, if the negative flag is ON.
Jump equal 11 100___ Jumps to the instruction in register 6, if the equal flag is ON.
Jump 11 101___ Jumps to the instruction in register 6, regardless of an flags.

Notice that most instructions only use 3 of the 6 argument bits. Move immediate uses all 6, allowing the user to load an immediate value of 0-63, and move also uses all 6 to index 2 registers.

Registers

Since we have 3 bits available to index registers with, our computer will include 8 all-purpose registers.

If you design your own computer, you will need to consider what registers to include, and what will they be used for.

In our case, while all of our registers will be general purpose, meaning you can use them for whatever purpose you like, most of them will be used in a specific way for specific instructions. The registers and their purposes are as follows:

  • Register 0 - destination for immediate values
  • Register 1 - source of one argument of an operation
  • Register 2 - source of the other argument of an operation
  • Register 3 - destination of the result of an operation
  • Register 4 - source of one argument for a comparison
  • Register 5 - source of the other argument for a comparison
  • Register 6 - target address for a jump
  • Register 7 - general purpose

Note that the instruction pointer and instruction register are separate to these all-purpose registers, so our program will not be able to directly edit the values in these registers.

Editing of these registers is done by the control unit either during the fetch-decode-execute cycle, or through the use of a jump instruction.

Flags

If you decide to include conditional branching in your instruction set, you will need to decide what conditions (called flags) you want to check for and how you're going to check them.

In our case, we will have the following 4 flags in our computer:

  • Zero flag - This flag will be ON only if the first value from the last test instruction was equal to 0.
  • Carry flag - This flag will be ON only if that last add (or subtract) instruction carried/overflowed.
  • Negative flag - This flag will be ON only if the first value from the last test instruction was negative (had its most significant bit ON).
  • Equal flag - This flag will be ON only if the values from the last test instruction were equal to each other.

We will also be storing the states of these flags in another register called a flag register. Allowing us to capture the state of these flags with one instruction (test) and use the state of these flags in another instruction (jump).

You do not need to store the state of these flags in a register, or perform a test and branch in two separate instructions. You can perform your comparison and use the results immediately, if you want. Doing so will just require a more complex execution sequence, and possibly bigger instructions.

Example Program

Putting everything together, we can use our instruction set to write a Fibonacci program:

; Initialise registers
Immediate 0 ; 00 000000
Move 2, 0   ; 10 000 010
Immediate 1 ; 00 000001
Move 1, 0   ; 10 000 001
;
; Main loop
Add         ; 01 000 ___
Move 2, 1   ; 10 001 010
Move 1, 3   ; 10 011 001
Immediate 4 ; 00 000100
Move 6, 0   ; 10 000 110
Jump        ; 11 101 ___

Visualizing the process, we can imagine the first instruction being copied into the instruction register, the instruction pointer being incremented, then the value being copied from the instruction register to register 0.

Then the step counter resets, and we begin again, fetching the instruction, incrementing the instruction counter, and moving the content of register 0 to register 2. Reset the step counter and on and on it goes.

This is what our computer should do when it's complete.

With the planning done, it's now time to move on to building.

Building the Computer

TODO: Flesh this section out more Start by building your major components first. Build your program memory, working memory, ALU, registers, and control unit all on their own circuit board.

This will make rearranging things easier.

Use sockets to create interfaces. This will make changing and swapping out a component easier.

It helps to build everything except the control unit first, then to build the control unit interface so you know what inputs and outputs your control unit needs to have.

Test as you go, and be prepared to do some troubleshooting. Nothing ever works perfectly the first time.