You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Python is a great language to learn because it is easy to learn and safe to write in. It is also available for Windows, Linux and Mac, and it is free. It is also available on Raspberry Pi which is targetted for education and testing purposes. This makes computer science easy accessible to everyone. Also for writing a basic emulator to learn how computers work and what they do. The 6502 processor has a very simple design and a small instruction set that makes it easy to learn.
Learning how processors work also gives the possibility to understand why certain applications are so slow and how to optimize them, but also how to start doing security research by writing a fuzzer to find vulnerabilities. Lets start with the basics and write a simple 6502 emulator before we start with the assembly language.
The Introduction chapter is mainly based on the 6502 Instruction Set Guide by Andrew John Jacobs aka BitWise and full credit goes to him. Sadly he passed away in 2021 and I copied his work as a reference as his website is no longer available.
Introduction
The 6502 basic processor?
The Registers
Program Counter
Stack Pointer
Accumulator
Index registers X and Y
Processor Status
As instructions are executed a set of processor flags are set or clear to record the results of the operation. This flags and some additional control flags are held in a special status register. Each flag has a single bit within the register.
Instructions exist to test the values of the various bits, to set or clear some of them and to push or pull the entire set to or from the stack.
Carry Flag
The carry flag is set if the last operation caused an overflow from bit 7 of the result or an underflow from bit 0. This condition is set during arithmetic, comparison and during logical shifts. It can be explicitly set using the 'Set Carry Flag' (SEC) instruction and cleared with 'Clear Carry Flag' (CLC).
Zero Flag
The zero flag is set of the result of the last operation as was zero.
Interrupt Disable
The interrupt disable flag is set if the program has executed a 'Set Interrupt Disable' (SEI) instruction. While this flag is set the processor will not respond to interrupts from devices until it is cleared by a 'Clear Interrupt Disable' (CLI) instruction.
Decimal Mode
While the decimal mode flag is set the processor will obey the rules of Binary Coded Decimal (BCD) arithmetic during addition and subtraction. The flag can be explicitly set using 'Set Decimal Mode' (SED) and cleared with 'Clear Decimal Mode' (CLD).
The break command bit is set when a BRK instruction has been executed and an interrupt has been generated to process it.
Overflow Flag
The overflow flag is set during arithmetic operations if the result has yielded an invalid 2's complement result (e.g. adding two positive numbers and ending up with a negative result: 64 + 64 => -128). It is determined by looking at the carry between bits 6 and 7 and between bit 7 and the carry flag.
Negative Flag
The negative flag is set if the result of the last operation has bit 7 set to a one.
The Instruction Set
Load/Store Operations
Loading the Accumulator
Flag
Description
State
C
Carry Flag
Not affected
Z
Zero Flag
Set if A = 0
I
Interrupt Disable
Not affected
D
Decimal Mode Flag
Not affected
B
Break Command
Not affected
V
Overflow Flag
Not affected
N
Negative Flag
Set if bit 7 of A is set
Addressing Mode
Opcode
Bytes
Cycles
Immediate
0xA9
2
2
Zero Page
0xA5
2
3
Zero Page, X
0xB5
2
5
Absolute
0xAD
3
4
Absolute, X
0xBD
3
4 (+1 if page crossed)
Absolute, Y
0xB9
3
4 (+1 if page crossed)
(Indirect, X)
0xA1
2
6
(Indirect), Y
0xB1
2
5 (+1 if page crossed)
LDA #nn
def_ins_lda_imm(self) ->None:
""" LDA (0xA9) - Load Accumulator, Immediate. Load the value stored after the opcode directly into accumulator and then evaluate accumulator for flags Zero and Negative. Assembly example: ``` LDA #nn ``` Affected flags: - Zero Flag: Set if A = 0 - Negative Flag: Set if bit 7 of A is set The instruction costs 2 bytes and 2 cycles to complete. """self.reg_a=self._fetch_byte()
self._evaluate_flags_nz(self.reg_a)
Loading the X Register
Flag
Description
State
C
Carry Flag
Not affected
Z
Zero Flag
Set if X = 0
I
Interrupt Disable
Not affected
D
Decimal Mode Flag
Not affected
B
Break Command
Not affected
V
Overflow Flag
Not affected
N
Negative Flag
Set if bit 7 of X is set
Addressing Mode
Opcode
Bytes
Cycles
Immediate
0xA2
2
2
Zero Page
0xA6
2
3
Zero Page, Y
0xB6
2
5
Absolute
0xAE
3
4
Absolute, Y
0xBE
3
4 (+1 if page crossed)
LDX #$nn
def_ins_ldx_zp(self) ->None:
""" LDX (0xA6) - Load X Register, Zero Page. Load the value stored at the memory location that is after the opcode directly into register X and then evaluate register X for flags Zero and Negative. The memory location is a single byte and within the Zero Page memory range of 0-255. Assembly example: ``` LDX #$nn ``` Affected flags: - Zero Flag: Set if X = 0 - Negative Flag: Set if bit 7 of X is set The instruction costs 2 bytes and 3 cycles to complete. """self.reg_x=self._read_byte(self._fetch_byte())
self._evaluate_flags_nz(self.reg_x)
Loading the Y Register
Flag
Description
State
C
Carry Flag
Not affected
Z
Zero Flag
Set if Y = 0
I
Interrupt Disable
Not affected
D
Decimal Mode Flag
Not affected
B
Break Command
Not affected
V
Overflow Flag
Not affected
N
Negative Flag
Set if bit 7 of Y is set
Addressing Mode
Opcode
Bytes
Cycles
Immediate
0xA0
2
2
Zero Page
0xA4
2
3
Zero Page, X
0xB4
2
5
Absolute
0xAC
3
4
Absolute, X
0xBC
3
4 (+1 if page crossed)
LDY #$nn
def_ins_ldy_zpx(self) ->None:
""" LDY (0xB4) - Load Y Register, Zero Page, X. Load the value stored at the memory location that is stored after the opcode and increased with the value in register X before copied directly into register Y and then evaluate register Y for flags Zero and Negative. The memory location is a single byte and within the Zero Page memory range of 0-255. The value in register X is added to the memory location before the value is read. The value in register X is used to point to the memory location of the value to be read. The memory location is a single byte and within the Zero Page memory range of 0-255. The value in register X is added to the memory location before the value is read. The value in register X is used to point to the memory location of the value to be read. The value in register X is Assembly example: ``` LDY nn,X ``` Affected flags: - Zero Flag: Set if Y = 0 - Negative Flag: Set if bit 7 of Y is set The instruction costs 2 bytes and 5 cycles to complete. """self.reg_y=self._read_byte(
(self._fetch_byte() +self._read_register_x()) &0xFF
)
self._evaluate_flags_nz(self.reg_y)
self.cycles+=1# TODO: Why is the extra cycle required?
Storing the Accumulator
Flag
Description
State
C
Carry Flag
Not affected
Z
Zero Flag
Not affected
I
Interrupt Disable
Not affected
D
Decimal Mode Flag
Not affected
B
Break Command
Not affected
V
Overflow Flag
Not affected
N
Negative Flag
Not affected
Addressing Mode
Opcode
Bytes
Cycles
Zero Page
0x85
2
3
Zero Page, X
0x95
2
4
Absolute
0x8D
3
4
Absolute, X
0x9D
3
5
Absolute, Y
0x99
3
5
(Indirect, X)
0x81
2
6
(Indirect), Y
0x91
2
6
STA #$nn
def_ins_sta_zpx(self) ->None:
""" STA (0x95) - Store Accumulator, Zero Page, X. Store the value in the accumulator at the memory location that is stored after the opcode and increased with the value in register X. The memory location is a single byte and within the Zero Page memory range of 0-255. The value in the accumulator is written to the memory location without any modification. Assembly example: ``` STA nn,X ``` Affected flags: - None The instruction costs 2 bytes and 4 cycles to complete. """self._write_byte((self._fetch_byte() +self.reg_x) &0xFF, self.reg_a)
Storing the X Register
Flag
Description
State
C
Carry Flag
Not affected
Z
Zero Flag
Not affected
I
Interrupt Disable
Not affected
D
Decimal Mode Flag
Not affected
B
Break Command
Not affected
V
Overflow Flag
Not affected
N
Negative Flag
Not affected
Addressing Mode
Opcode
Bytes
Cycles
Zero Page
0x86
2
3
Zero Page, Y
0x96
2
4
Absolute
0x8E
3
4
STX #$nn
def_ins_stx_zpy(self) ->None:
""" STX (0x96) - Store X Register, Zero Page, Y. Store the value in register X at the memory location that is stored after the opcode and increased with the value in register Y. The memory location is a single byte and within the Zero Page memory range of 0-255. The value in register Y is added to the memory location before the value is read. The value in register Y is used to point to the memory location of the value to be read. The memory location is a single byte and within the Zero Page memory range of 0-255. The value in register Y is added to the memory location before the value is read. The value in register Y is used to point to the memory location of the value to be read. The value in register Y is added to the memory location before the value is read. The value in register Y is used to point to the memory location of the value to be read. Assembly example: ``` STX (nn),Y ``` Affected flags: - None The instruction costs 2 bytes and 5 cycles to complete. """self._write_byte(
(self._fetch_byte() +self._read_register_y()) &0xFF, self.reg_x
)
Storing the Y Register
Flag
Description
State
C
Carry Flag
Not affected
Z
Zero Flag
Not affected
I
Interrupt Disable
Not affected
D
Decimal Mode Flag
Not affected
B
Break Command
Not affected
V
Overflow Flag
Not affected
N
Negative Flag
Not affected
Addressing Mode
Opcode
Bytes
Cycles
Zero Page
0x84
2
3
Zero Page, X
0x94
2
4
Absolute
0x8C
3
4
STY #$nn
def_ins_sty_zpx(self) ->None:
""" STY (0x94) - Store Y Register, Zero Page, X. Store the value in register Y at the memory location that is stored after the opcode and increased with the value in register X. The memory location is a single byte and within the Zero Page memory range of 0-255. The value in register X is added to the memory location before the value is read. The value in register X is used to point to the memory location of the value to be read. The memory location is a single byte and within the Zero Page memory range of 0-255. The value in register X is added to the memory location before the value is read. The value in register X is used to point to the memory location of the value to be read. The value in register X is added to the memory location before the value is read. The value in register X is used to point to the memory location of the value to be read. Assembly example: ``` STY (nn),X ``` Affected flags: - None The instruction costs 2 bytes and 5 cycles to complete. """self._write_byte(
(self._fetch_byte() +self._read_register_x()) &0xFF, self.reg_y
)
Register Transfer Operations
TAX — Transfer Accumulator to X Register
Flag
Description
State
C
Carry Flag
Not affected
Z
Zero Flag
Set if X = 0
I
Interrupt Disable
Not affected
D
Decimal Mode Flag
Not affected
B
Break Command
Not affected
V
Overflow Flag
Not affected
N
Negative Flag
Set if bit 7 of X is set
Addressing Mode
Opcode
Bytes
Cycles
Implied
0xAA
1
2
Example:
; Copy the accumulator into X LDA #$42 ; A = $42 TAX ; X = $42, Z and N set from X
The state of the decimal flag is uncertain when the CPU is powered up and it
is not reset when an interrupt is generated. In both cases you should include
an explicit CLD to ensure that the flag is cleared before performing addition
or subtraction.