Software Portability and Optimization

Pratham GargPratham Garg
8 min read

Introduction

Do you remember the first computer you ever used? Chances are, it had an iconic 8-bit CPU called the 6502 processor humming away inside it. While it may seem like a relic of the past, the 6502 has an enduring legacy that has left an indelible mark on the history of computing. In this adventure through the world of technology, we'll dive deep into the heart of the 6502 and discover what made it tick.


Memory Architecture

To understand the magic of the 6502, let's start with its memory architecture. Imagine a world where you have a 16-bit address bus at your disposal, allowing you to access a whopping 64 kilobytes (64KB) of memory (that's 2^16 bytes!). Picture this vast memory space as 256 pages, each holding 256 bytes of information. Now, that's some serious data power!

In the 6502's memory landscape, even the way pointers are stored has a twist. It's a world where the Little Endian order reigns supreme, meaning the lowest-value byte comes first. So, when you see something like:

Memory $0010: $CD
Memory $0011: $AB

What you're looking at is a pointer at memory location $0010 pointing to memory location $ABCD. A bit of a memory treasure hunt, isn't it?

But wait, there's more! The memory isn't just a vast wilderness of bytes. Some pages are designated for special purposes:

0000 - 00FFZero PageReserved for variables that need lightning-fast access.
0100-01FFStack PageAddresses from $0100 to $01FF are used for the stack. Values are pushed onto and pulled from the stack in a first-in, last-out (FILO) order. The stack wraps around, so older values may be overwritten.
FF00-FFFFVector PageAddresses from $FF00 to $FFFF are reserved for the vector table. The last 6 bytes of this page contain three 2-byte addresses. These addresses are crucial for handling interrupt requests, resets, and non-maskable interrupts (NMIs).

Registers

The 6502 processor has six registers, both general-purpose and special-purpose:

General-Purpose Registers:

  1. Accumulator (A): The primary register for arithmetic operations.

  2. X Index (X): Used for limited math operations and indexed addressing modes.

  3. Y Index (Y): Similar to the X register, used for specific index operations.

Special-Purpose Registers:

  1. Program Counter (PC): Points to the currently executing instruction in memory.

  2. Stack Pointer (S or SP): Points to the current position in the stack.

  3. Processor Status (P or PS): A collection of bits (flags) that indicate or control various aspects of the processor's mode and status.

    • C - Carry: Used to carry or borrow during addition and subtraction operations.

    • Z - Zero flag: Signals when an operation produces a zero result.

    • I - Interrupt disable: Controls whether interrupts are allowed.

    • D - Decimal mode: Alters how bytes are interpreted during math operations.

    • B - Break: Indicates a software interrupt (BRK instruction) or hardware NMI.

    • V - Overflow: Set when a math operation overflows or underflows.

    • N - Negative Sign: Set when an operation produces a negative result.

Instruction Set and Addressing Modes

The 6502 instruction set consists of various single-byte opcodes, each followed by 0, 1, or 2 bytes of arguments, corresponding to an instruction, operation, and addressing mode. Here are some examples:

LDA #$05: Load the accumulator with the number 5.
LDA $05: Load the accumulator with the contents of memory location $05 in the zero page ($0005).
LDA $f005: Load the accumulator with the contents of memory location $f005.

These instructions employ 3-letter mnemonics to specify the operation and an argument syntax to specify the addressing mode. The 6502 assembly language is rich in functionality, allowing for a wide range of operations and data manipulations.

Performance

Every 6502 instruction consumes a certain number of machine cycles for execution. The number of cycles can vary depending on the instruction's context. Take, for example, the conditional branch instruction BRE (Branch if Equal):

  • 2 cycles if the branch is not taken.

  • 3 cycles if a branch is taken to an address in the same page.

  • 4 cycles if a branch is taken to an address in another page.

Remember, the Program Counter (PC register) is always one step ahead, pointing to the next instruction in line. Branch instructions work their magic by adding a signed integer value (ranging from -128 to +127) to the Program Counter. If this addition causes a leap into the high byte of the Program Counter, an extra cycle is needed to make the adjustment.

To figure out how long an instruction takes, you can look at charts that show the times for each instruction. To convert these times into something more understandable, like seconds, you can multiply them by the time it takes for the computer's clock to tick once. Many 6502 computers worked at 1 MHz, which means they did one million operations every second. So, one unit of time (cycle) is like one-millionth of a second or 1 microsecond (uS). If an instruction takes 4 cycles, it would need 4 uS to finish.

Credits to Princeton University

Instructions by Category: The 6502 Toolbox

Now that we've learned the basics, let's explore the 6502's toolbox of instructions, neatly categorized for your convenience.

Loading and Storing Data (to/from Memory)

The 6502 processor knows how to move data efficiently between memory and registers. It has instructions like:

  • LDA: Load data into the accumulator.

  • LDX: Load data into the X register.

  • LDY: Load data into the Y register.

And when it's time to pack up and store your data:

  • STA: Store the accumulator in memory.

  • STX: Store the X register in memory.

  • STY: Store the Y register in memory.

Push/Pull on the Stack

The stack plays a pivotal role in 6502 programming. When values are pushed onto the stack, the selected register's contents are written to memory location $0100+SP (Stack Pointer), and SP is decremented. Conversely, when values are pulled from the stack, SP is incremented, and the selected register is loaded from memory location $0100+SP.

For example:

  • PHA: Push the accumulator onto the stack.

  • PHP: Push the processor status register (SR) onto the stack.

  • PLA: Pull data from the stack into the accumulator.

  • PLP: Pull data from the stack into the processor status register (SR).

Transferring Data between Registers

Need to transfer data between registers? The 6502's got you covered:

  • TAX: Transfer the accumulator (A) to the X register.

  • TAY: Transfer the accumulator (A) to the Y register.

  • TXA: Transfer the X register to the accumulator (A).

  • TYA: Transfer the Y register to the accumulator (A).

  • TSX: Transfer the Stack Pointer (SP) to the X register.

  • TXS: Transfer the X register to the Stack Pointer (SP).

Arithmetic and Bitwise Operations

Feeling mathematical? The 6502 supports basic arithmetic and bitwise operations on the accumulator (A), including:

  • ADC: Add with carry.

  • SBC: Subtract with carry.

And if you want to count by increments or decrements:

  • DEC: decrement memory

  • DEX: decrement X register

  • DEY: decrement Y register

  • INC: increment memory

  • INX: increment X register

  • INY: increment Y register

You can even perform left and right bit-shifts and rotations (equivalent to multiplication/division by 2), like a digital magician:

  • ASL: Arithmetic shift left.

  • LSR: Logical shift right.

  • ROL: Rotate left.

  • ROR: Rotate right.

And don't forget those bitwise operations, like AND, XOR, and OR:

  • AND: Bitwise AND with accumulator.

  • EOR: Bitwise exclusive-OR with accumulator.

  • ORA: Bitwise OR with accumulator.

Test and Comparison Operations

The 6502 processor knows how to compare:

  • CMP: Compare the accumulator (A).

  • CPX: Compare the X register.

  • CPY: Compare the Y register.

These operations are carried out via subtraction, setting appropriate condition flags. Conditional branch instructions can then be used to alter program flow based on the comparison results.

Additionally, the BIT instruction allows you to perform bit tests, helping you check specific bits within operands.

Program Flow

Want to control the flow of your program? The 6502's got your back:

  • Unconditional Jump: JMP allows you to set the address of the next instruction to be executed, effectively creating a "Goto" operation.

  • Jump to Subroutine: JSR initiates a jump to a subroutine, pushing the current PC value onto the stack for later return using RTS.

  • Conditional Branch: Instructions like BCC, BEQ, and others enable branching based on specific conditions.

Manipulating Flags

The 6502 loves manipulating flags, and it has a whole set of instructions for that:

  • CLC, CLD, CLI, CLV, SEC, SED, and SEI - These instructions let you set or clear flags like the carry flag (C), decimal flag (D), interrupt disable flag (I), and overflow flag (V) based on your program's whims.

Miscellaneous Instructions

As our journey nears its end, there are a few final instructions to mention:

  • BRK: Initiates a special version of the Non-Maskable Interrupt (NMI).

  • RTI: Returns from an interrupt.

  • NOP: A no-operation instruction that can be used for code alignment or to disable unwanted code.

Conclusion

In conclusion, mastering the 6502 processor is like uncovering the secrets of a hidden treasure chest. Understanding its addressing modes, registers, and its vast instruction set opens up a world of possibilities. With this versatile and powerful CPU, you can efficiently manipulate data, control program flow, and perform a wide range of arithmetic and bitwise operations. The 6502 may be a relic of the past, but its legacy lives on, forever shaping the world of computing. So, let's raise a digital toast to the incredible 6502 and its enduring impact on technology!

0
Subscribe to my newsletter

Read articles from Pratham Garg directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pratham Garg
Pratham Garg

I am a student learning Computer Programming ๐Ÿ‘จโ€๐Ÿ’ป.