Looking at the internals of the 6502 microprocessor will allow us to better understand the code we write.

Image for post

Photo by Magnus Engø on Unsplash

A microprocessor has a very important role inside your computer. Fabricated on a single chip, it has the responsibility of controlling the functions of the machine. In this article, we will look specifically at the MOS Technology 6502, an incredibly popular chip from the seventies. Though the technology is old, a lot of what we will say will be applicable to modern machines too. This article also doubles as part 3 in my learning assembly series (Part 12). However, I aim to make each piece as standalone as possible — there might be a few things that rely on the previous parts, but if all you’re interested in is how a microprocessor works hopefully this piece is still for you!

The 6502 (sixty-five-oh-two)

From the perspective of learning the 6502 Assembly language, we can recall that is strongly linked to the architecture it is written on. Everything you write will, on some level, depend on the instruction set and hardware of the processor. To write a program you may not require a deep knowledge of how the 6502 works, but to write an efficient one you will. We have begun to see this already. In the first part of this series, we saw how instructions can be different on different machines and in Part 2 we saw how the hardware enables us to compute things more easily. We will start to be able to piece everything we have learned so far together. As we continue in the following weeks we will be able to refer back to here as well, and further appreciate the links between the hardware and software we come to write.

To understand the architecture of a microprocessor this article will centre around the following figure:

A generic layout of a microprocessor

Generic microprocessor layout. Credits: Zaks.

This is the layout of a standard microprocessor of the era. Let’s go through it bit by bit, trying to understand it all.

Data Buses

We will begin outside of the 6502 microprocessor unit (MPU), with the data buses. A computer bus has the job of transferring data and signal around the machine. For a 6502, they are three of them:

  • The data-bus: As the name might suggest, this transfers data around. The data-bus is 8-bits wide, which means it can only carry 8 bits at a time. If you want to carry more data than that two separate signals will have to be sent down the bus. This means you can transfer the number 00011000 in one trip, but 00110001 00110101 will take two. Usually, it will carry data from memory to the MPU, from the MPU to memory, or the MPU to an Input/ Output device.
  • The address-bus: This bus carries addresses. These address will usually be a source, or a destination, for data. The address bus also differs from the data-bus as it is 16-bits wide. This means you can access any 16-bit address. Practically, this means you only have access to ~64000 addresses. After you have stored something in them all, that’s it! No more memory! Interestingly, this has been a problem up to quite recently — even modern 32-bit computers can only natively access around 3gb of ram (this problem is called the 3gb barrier).
  • The control-bus: The control bus will try to keep everything inside the machine synced up. Here, it has been removed for simplicity.

MPU and Registers

The microprocessor (MPU) in the above figure, in our case, is the 6502. It contains the arithmetic-logical-unit (ALU), the control-unit (CU) and the registers (some of which are the flags we saw in week 2). The CU has control over how the processor operates. It decides when it receives an instruction, data or an address how to interpret it.

Below, is a figure showing the internals of the 6502 (sans the CU). Outside of this, it will also require precise timing. This is why it is connected to a timer in the above figure.

Internals of a MPU (in out case, a 6502)

Internals of the MPU (the 6502) Credits.

Let’s go through this diagram, right to left, and discuss what is going on.

The ALU has a recognisable ‘V’ shape. Its role in the 6502 is to perform the calculations. It will accept two inputs, one in the ‘left input’ and one into the ‘right input’. With this, you can do addition (the ADC instruction) or subtraction (the SBC instruction). The left input of the ALU is connected to the accumulator, the A register. When doing logical or arithmetic operations usually one of the values is stored in the accumulator and the other somewhere in memory. The result of whatever operation will then also, usually, be stored in the accumulator. It is after this accumulating behaviour that the register gets its name.

The next two parts are the 8-bit X and Y registers. These are used frequently as ways to store values. They are useful as the 6502 comes with several instructions to manipulate them. Among these are INX which will increment the value in X up by 1, DEY which reduces Y by 1 and TXA which transfers the contents of X to A. Having this variety registers gives us ways to move and modify data without having to specify a memory location. We will later this article see that this can improve the speed of the code we write.

Then, there is a range of registers stored in P:

  • N: keeps track if the result in the ALU is negative.
  • V: the overflow flag (remember, from last week!)
  • B: used to handle breaks (we will discuss this more in a later post).
  • D: this helps handle BCD numbers. This is a different way to represent data. We will not discuss it here.
  • I: this tracks how interrupts will be handled (we will discuss this more in a later post).
  • Z: keeps track if the result of a calculation is zero.
  • C: the carry flag (from last week as well!)

Having all these registers allow us to keep track of data and make comparisons. For example, we might want to branch to a different part of our program if the result of a calculation is zero. By checking the Z register we have a way to know if we should do this. Some of these registers make use of certain instructions (BRK sets B to 1). As we encounter these I will describe the effect on the register.

The Stack-Pointer (SP), points to a specific bit in a place in memory called the stack. Using it we can keep track of where in the stack we are. I will go into more detail about this when we discuss the stack later in this post.

Whatever program we write will have to be kept somewhere in memory. As we run it, we will have to identify where in memory each instruction is and pass it to the processor to be interpreted and executed. The Program-Counter (PC) is a 16-bit registry. By checking here we should be able to learn the location of the next instruction the program will need to carry out. Let’s look more into this right now!

Instructions and the Program Counter

As we run a program the processor will be in a constant cycle of fetching instructions then decoding and executing them.

Fetch: In this step, the contents of the program counter (PC) are deposited onto the address bus. By checking here we should be able to learn the next instruction the program will need to carry out. This will then be deposited in a special internal register called the instruction-register (IR). The fetch cycle is now finished!

Decode and execute: With the appropriate instruction in the IR, the control unit (CU) can begin to decode it. At this point, it can generate the signals needed to carry out the instruction. The length of time this takes will differ depending on what is being generated. Some instructions can happen inside the MPU (for example, INX), whereas some will need something else from memory (ie, data or a memory address). The latter type of instruction will take longer, as you need to take a trip to somewhere in memory to compute it. It’s worth then writing code in a way that will use the first type of instruction. The time it takes to do an instruction is measured in clock cycles. The 6502 uses a one-megahertz clock, therefore a clock cycle takes 1 microsecond.

Finally, the PC will be incremented so it points to the next instruction. Now, the process can begin again.

Memory Maps

We have discussed how with a 16-bit address bus we can reach 65,535 different locations in memory. While we access each of these addresses with the same methods that does not mean that every section of memory is used for the same purpose. The 64,535 addresses are divided into pages. Each page is a block of 256. This means that from address 0 to 255 (or 0 to 100 in hexadecimal) is page 0, address 256 to 511 is page 1, and so on. In your code you will, therefore, be tracking the page you are on and your location within a page. As you cross a page boundary (eg, go from page 12 to 13) it can result in needing to execute an extra instruction. This is because you are not just updating your location within the page, but the page number as well.

Certain pages within the 6502’s memory are there to perform specific tasks. A memory map is a figure that describes how the locations in your system are meant to be used. Regardless of what 6502 system you are using (Apple ii, c64, NES, etc…) a lot of the functionality will be the same. However, the memory map can be different. Below is a very generic 6502 memory map

Zero Page

Generic 6502 memory map

Generic 6502 memory map Credits.

The first page in the 6502 has an important position. The zero-page, which ranges from 0 to 255, is the only area in memory that can be accessed with an 8-bit address. Since parsing an 8-bit address is quicker than parsing a 16-bit one, the zero-page becomes an area in memory where data can be read and written to more efficiently. Therefore, it is sensible to store important data there that will need to be read a lot.

The Stack

The second page is also very important. The Stack ranges from 256 to 511. This area of memory is a last-in-first-out (LIFO) list. Effectively, this means that you can only retrieve from the stack the last thing you deposited into it. It is like a neat pile of papers on a desk, you can only ever access the top one. We then have a stack pointer (the register S) that points us to the memory address that corresponds to the top of this pile. The 6502 even gives us some manual control, allowing us to push and pull things to the stack at will using PHA and PLA. It might seem strange why you would want an area of memory like this. Let’s try to give an example where it would be useful.

Firstly, some terminology — we will call a set of instructions we use a lot a subroutine. You could imagine this kinda like a function you would write in Python. Now, let’s write a bit of pseudocode:

1\. a = 1
2\. b = 2
4\. c = subroutine1()
5\. x = a + b + c
7\. subroutine1:
8\.     y = subroutine2()
9\.     return y
10\. subroutine2:
11\.     z = 2
12\.     return z

We will imagine the numbers we see on the left side to be locations in memory. At memory location 1 a = 1 will be stored, and so on. As we go through each line our program counter will be incremented upward, so we know what will happen next. However, eventually, we will get to line 4 and we will have to call subroutine1(). To resolve this, we will have to jump to line 7. When this subroutine is over, how will we know how to get back home to line 5?

What will happen, to ensure this information is retrievable, is that the location where we will need to return will be pushed to the stack. We can now continue as normal from line 7, knowing that when we hit a return statement we can pull that information from the stack, add the location of line 5 to the program counter, and return home.

#technology #learning-to-code #computer-science #programming #coding

How do Processors Actually Work?
1.50 GEEK