The CDC6504 is a limited 6502 emulator with PC, ACC, X, Y registers and status flags (Z, N). This tool uses authentic 6502 opcodes and instruction syntax.
The "Load Program" dropdown provides quick access to example programs and tools:
The "View Assembly" option opens a read-only modal that displays the current assembly code:
To write and assemble your own programs:
Once a program is loaded:
The emulator includes a "Narrate Execution" feature that speaks each instruction as it executes:
6502 architecture registers
Condition flags set by arithmetic, comparison, and transfer operations. Flags are set by any operation altering ACC, X, or Y.
The CDC6504 emulates a simplified 6502 processor. The accumulator (ACC) is the primary register for arithmetic operations. Use STA/STX/STY to store registers to memory, and LDA/LDX/LDY to load from immediate values.
Immediate values can be specified as single ASCII characters in quotes: LDA #'A' loads 65 (ASCII for 'A') into the accumulator.
This works for all immediate instructions: LDA, LDX, LDY, CMP, CPX, CPY, ADC, SBC.
Status flags are automatically updated by arithmetic, comparison, and transfer operations. Flags are set by any operation touching ACC, X, or Y. Use branch instructions (BEQ, BNE, BMI, BPL) to conditionally jump based on flag states.
| Assembly | Opcode | Description |
|---|---|---|
| BRK | 00000000 | Break/Halt - Stop program execution (automatically prints data memory) |
| Assembly | Opcode | Description |
|---|---|---|
| CLX | 11100010 | Clear X register (X = 0). Sets Z flag to 1, N flag to 0. (*) |
| CLY | 11000010 | Clear Y register (Y = 0). Sets Z flag to 1, N flag to 0s. (*) |
| INX | 11101000 | Increment X register by 1 |
| INY | 11001000 | Increment Y register by 1 |
| DEX | 11001010 | Decrement X register by 1 |
| DEY | 10001000 | Decrement Y register by 1 |
| Assembly | Opcode | Description |
|---|---|---|
| TAX | 10101010 | Transfer accumulator to X register |
| TAY | 10101000 | Transfer accumulator to Y register |
| TXA | 10001010 | Transfer X register to accumulator |
| TYA | 10011000 | Transfer Y register to accumulator |
16-bit instructions take a parameter that is an 8-bit constant value (immediate values) or data memory address. These parameter values are stored in the byte immediately following the instruction for a total of 16-bits. So we call them "immediate" instructions
| Assembly | Opcode | Description |
|---|---|---|
| LDA #value | 10101001 | Load accumulator with immediate value (ACC = value) |
| LDA $address | 10100101 | Load accumulator from zero-page memory address (ACC = memory[address]) |
| LDX #value | 10100010 | Load X register with immediate value (X = value) |
| LDX $address | 10100110 | Load X register from zero-page memory address (X = memory[address]) |
| LDY #value | 10100000 | Load Y register with immediate value (Y = value) |
| LDY $address | 10100100 | Load Y register from zero-page memory address (Y = memory[address]) |
| Assembly | Opcode | Description |
|---|---|---|
| STA $address | 10000101 | Store accumulator to zero-page memory address (memory[address] = ACC) |
| STX $address | 10000110 | Store X register to zero-page memory address (memory[address] = X) |
| STY $address | 10000100 | Store Y register to zero-page memory address (memory[address] = Y) |
| Assembly | Opcode | Description |
|---|---|---|
| LDA $address,X | 10110101 | Load accumulator from zero-page indexed address (ACC = memory[(address + X) & 0xFF]) |
| LDA $address,Y | 10111001 | Load accumulator from zero-page indexed address (ACC = memory[(address + Y) & 0xFF]) |
| STA $address,X | 10010101 | Store accumulator to zero-page indexed address (memory[(address + X) & 0xFF] = ACC) |
| STA $address,Y | 10011001 | Store accumulator to zero-page indexed address (memory[(address + Y) & 0xFF] = ACC) |
| Assembly | Opcode | Description |
|---|---|---|
| ADC #value | 01101001 | Add (immediate): ACC = ACC + value |
| ADC $address | 01100101 | Add (from memory): ACC = ACC + memory[address] |
| SBC #value | 11101001 | Subtract (immediate): ACC = ACC - value |
| SBC $address | 11100101 | Subtract (from memory): ACC = ACC - memory[address] |
| Assembly | Opcode | Description |
|---|---|---|
| CMP #value | 11001001 | Compare accumulator with immediate value, sets Z, N flags |
| CMP $address | 11000101 | Compare accumulator with value at the memory address, sets Z, N flags (ACC compared with memory[address]) |
| CPX #value | 11100000 | Compare X register with immediate value, sets Z, N flags |
| CPX $address | 11100100 | Compare X register with value at the memory address, sets Z, N flags (X compared with memory[address]) |
| CPY #value | 11000000 | Compare Y register with immediate value, sets Z, N flags |
| CPY $address | 11000100 | Compare Y register with value at the memory address, sets Z, N flags (Y compared with memory[address]) |
| Assembly | Opcode | Description |
|---|---|---|
| BEQ label | 11110000 | Branch if equal (Z flag set) |
| BNE label | 11010000 | Branch if not equal (Z flag clear) |
| BMI label | 00110000 | Branch if minus (N flag set) |
| BPL label | 00010000 | Branch if plus (N flag clear) |
| Assembly | Opcode | Description |
|---|---|---|
| JMP $address | 01001100 | Unconditional jump to absolute address (zero-page) |
| Assembly | Description |
|---|---|
| DATA 'string' | Pre-populates data memory (not instruction memory) with a null-terminated string. Data is loaded starting at memory address 0. |
| DATA 0x00 0x0A ... | Pre-populates data memory (not instruction memory) with a space-separated list of hexadecimal values. Data is loaded starting at memory address 0. |
Comments can be added to assembly code using ; (semicolon).
Everything from the semicolon to the end of the line is ignored by the assembler.
Note: The # character is used for immediate values (e.g., LDA #42), not for comments.
The clear instructions (CLX, CLY) are 8-bit emulator-only instructions that use unused 6502 opcodes (0xE2, 0xC2). In a real hardware 6502, you would use the 16-bit LDX #0 and LDY #0 instructions to clear the registers.
These example programs are available in the "Load Program" dropdown menu in the emulator. Each demonstrates different aspects of the CDC6504 instruction set.
This very basic example, computes 1+1 = 2 in the X register. It is kept simple to make it easy to examine the generated machine code.
This program demonstrates immediate loading and immediate addition. After computing 27+15 it stores the result (42) in memory location 0
This program demonstrates conditional branching and arithmetic to convert a character to upper case. It compares a character value with 'a' (lowercase), and if the value is greater than or equal to 'a', it subtracts 0x20 to convert to uppercase using the BMI (Branch if Minus) instruction.
A simple two-character string program that demonstrates loading ASCII characters using single quotes. It stores 'H' and 'i' into consecutive memory locations, creating the string "Hi" which is automatically printed when BRK executes.
This program manually constructs the string "Hello" by loading each character's ASCII value (using character constants) and storing them to consecutive memory addresses. The BRK instruction automatically prints the null-terminated string from memory.
This program demonstrates the DATA directive, which provides a convenient way to pre-populate data memory with a string. The DATA directive automatically places the string in memory starting at address 0 and adds a null terminator, making it much simpler than manually storing each character.
This program demonstrates the use of labels for program flow control. It uses a loop that increments the X register from 0 to 5, using labels "loop" and "end" for branching. The program compares X to 5 using CPX and branches when equal using BEQ, showing how labels make jump addresses easier to manage. The program is a simple for loop.
for(X=0; X<5; X++) ;
This program demonstrates loading values from memory addresses. It stores a value to memory, then loads it back into different registers (ACC, X, and Y) using the LDA, LDX, and LDY instructions with memory addressing.
This program demonstrates adding values stored in memory. It stores two values to different memory addresses, then loads one and adds the other from memory using ADC with memory addressing mode.
This program shows two methods for adding a register's value to the accumulator. Since arithmetic operations only work with the accumulator, you must transfer the register value to ACC first, or store it to memory and load it. Both approaches are demonstrated here.
This program constructs "Hello" using arithmetic operations to derive some characters. It stores 'H', 'e', and 'l' directly, then uses ADC and SBC to calculate 'o' from 'l' by adding 4 and subtracting 1, demonstrating how arithmetic can be used for character manipulation.
This program demonstrates indexed addressing and loops to convert a mixed-case string to uppercase. It uses the DATA directive to initialize a string, then loops through each character using X as an index. Each character is loaded using indexed addressing (LDA $00,X), converted to uppercase if needed, and stored back using indexed addressing (STA $00,X). The loop continues until it encounters the null terminator.
When the CPU encounters an error (such as an invalid instruction), it will:
The assembler supports a two-pass label system for easier programming.
Labels are defined by ending a line with a colon (:):
Labels can be used in jump instructions instead of hard-coded addresses:
For more information about the 6502 processor and instruction sets, please refer to these external resources:
The CDC6504 emulator uses authentic 6502 opcodes and instruction syntax. The 6502 was one of the most popular 8-bit microprocessors, used in systems like the Apple II, Commodore 64, and Atari 2600.
This CDC6504 CPU emulator is part of the Computer Architecture for Everybody course, available at www.ca4e.com