A Python-based emulator for learning and experimenting with 6502 assembly and machine language. This project simulates a full 6502 CPU, providing tools for disassembly, execution, and debugging of raw binary programs. It includes support for all standard 6502 instructions and addressing modes, a simple Apple I emulation with the Woz Monitor, and various utilities to inspect, modify, and execute machine code.
- Full 6502 CPU Emulation – Supports all standard 6502 instructions and addressing modes.
- Binary Execution – Run programs directly from raw binary files.
- Apple I Emulation – Includes a working Woz Monitor for interactive memory inspection and execution.
- Disassembler – Convert machine code into readable assembly with comments for better understanding.
- Instruction Listing Tool – Quickly look up implemented instructions and opcodes.
The emulator currently supports the following 6502 instructions:
- Transfer Instructions
LDA– Load AccumulatorLDX– Load X RegisterLDY– Load Y RegisterSTA– Store AccumulatorSTX– Store X RegisterSTY– Store Y RegisterTAX- Transfer accumulator to XTAY- Transfer accumulator to YTSX- Transfer stack pointer to XTXA- Transfer X to accumulatorTXS- Transfer X to stack pointerTYA- Transfer Y accumulator
- Stack Instructions
PHA- Push AccumulatorPLA- Pull AccumulatorPHP- Push Status RegisterPLP- Pull Status Register
- Decrements & Increments
DEC- Decrement memoryDEX- Decrement XDEY- Decrement YINC- Increment memoryINX- Increment XINY- Increment Y
- Arithmetic Operations
ADC- Add with CarrySBC- Subtract with Borrow
- Logical Operations
AND- AND with AccumulatorEOR- XOR with AccumulatorORA- OR with Accumulator
- Shift & Rotate Instructions
ASL- Arithmetic Shift LeftLSR- Logical Shift RightROL- Rotate LeftROR- Rotate Right
- Flag Instructions
CLC- Clear CarryCLD- Clear Decimal (BCD arithmetics disabled)CLI- Clear Interrupt Disable BitCLV- Clear Overflow FlagSEC- Set CarrySED- Set Decimal (BCD arithmetics disabled)SEI- Set Interrupt Disable Bit
- Compare Instructions
CMP- Compare accumulator and operandCPX- Compare X and operandCPY- Compare Y and operand
- Conditional Branch Instructions
BEQ- Branch on Equal (zero flag set)BNE- Branch on Not Equal (zero flag clear)BMI- Branch on Result Minus (negative flag set)BPL- Branch on Result Plus (negative flag clear)BCC- Branch on Carry ClearBCS- Branch on Carry SetBVC- Branch on Overflow ClearBVS- Branch on Overflow Set
- Jumps & Subroutines
JMP- JumpJSR- Jump to SubroutineRTS- Return from Subroutine
- Interrupts
BRK- Break / Software InterruptRTI- Return from Interrupt
- Other
BIT- Bit Test (Memory & Accumulator)NOP- No Operation
./list_instructions.py shows up to date list of instructions.
For other options, see ./list_instructions.py --help
usage: list_instructions.py [-h] [-i INSTRUCTION] [-c CODE] [-m MODE]
List implemented 6502 instructions
options:
-h, --help show this help message and exit
-i, --instruction INSTRUCTION
Search for a specific instruction (e.g. LDA)
-c, --code CODE Search for a specific op code (e.g. a5)
-m, --mode MODE Search for a specific addressing mode (e.g. abs)- Immediate (
#$BB) - Zero Page (
$LL) - Zero Page, X (
$LL,X) - Zero Page, Y (
$LL,Y) - Absolute (
$LLHH) - Absolute, X (
$LLHH,X) - Absolute, Y (
$LLHH,Y) - Implied
- X-indexed, indirect
- indirect, Y-indexed
To start 6502 emulation with the Woz Monitor, run:
./wozmon.pyThe following prompt will be shown:
Starting the Woz Monitor
Press q to quit
\To inspect memory, input the address (in capital letters):
EE
00EE: 00Use dot to inpect a range:
24.2F
0024: 24 00 24 00
0028: 2F 00 03 00 00 00 00 00It is also possible to change memory contents with ':'. The previous memory value is printed first:
30:EA
0030: 00
30
0030: EARun a program at given address with 'R':
0C00 RA simple disassembler is provided. It can be used to study opcodes and construct commented Python code e.g. for unit tests.
./disasm.py --help
usage: disasm.py [-h] [--log LOG] infile
Disassembler for 6502
positional arguments:
infile Binary file to disassemble
options:
-h, --help show this help message and exit
--log LOG Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)Disassemble a file:
cat f.txt
0xCE, 0x0C, 0x00, 0xCE, 0x0C, 0x00, 0xEE, 0x0C, 0x00, 0xEE, 0x0C, 0x00, 0x00
./disasm.py f.txt
0XCE, 0X0C, 0X00, # DEC, ABS
0XCE, 0X0C, 0X00, # DEC, ABS
0XEE, 0X0C, 0X00, # INC, ABS
0XEE, 0X0C, 0X00, # INC, ABS
0X00, # BRK, IMPL
13 bytesDisassemble from stdin:
echo "0xA0, 0x09, 0xB9, 0x00, 0x00, 0xC8, 0xB9, 0x00, 0x00, 0xCC, 0xDD" | ./disasm.py -
0XA0, 0X09, # LDY, IMM
0XB9, 0X00, 0X00, # LDA, ABS_Y
0XC8, # INY, IMPL
0XB9, 0X00, 0X00, # LDA, ABS_Y
0XCC, # ???
0XDD, # ???
11 bytesTo compile the example programs into raw binary format, run:
pushd asm && make && popdYou can run the emulator with a raw binary file by providing it as an argument:
./run_asm.py asm/test.binThis will output the status of the CPU, memory reads/writes and instruction decoding, such as:
Using file asm/test2.bin
0000 a9 01 aa ca ca
A: 0x00 X: 0x00 Y: 0x00 S: 0xff
P: 0x00 Z: 0 N: 0 V: 0 D: 0 I: 0 C: 0
PC: 0x0000
R 0x0000: a9
R 0x0001: 1
R 0x0002: aa
R 0x0003: ca
R 0x0004: ca
End of program
A: 0x01 X: 0xff Y: 0x00 S: 0xff
P: 0x80 Z: 0 N: 1 V: 0 D: 0 I: 0 C: 0
PC: 0x0005
0000 a9 01 aa ca caTo run the unit tests, use:
uv run pytest