This document provides a small user guide for the Asm6502 library.
- Table of Contents
- Quick Start
- Assembling 6502 Code
- Disassembling 6502 Code
- Advanced Usage
- Tips and Best Practices
- Supported instructions
using Asm6502;
using static Asm6502.Mos6502Factory;
// Create an assembler
using var asm = new Mos6502Assembler();
asm.Begin(0xC000); // With a base address (e.g., $C000)
asm.LDX_Imm(0x00); // LDX #$00
asm.LDY_Imm(0x10); // LDY #$10
asm.LDA(0x0200, X); // LDA $0200,X
asm.STA(0x0200, X); // STA $0200,X
asm.RTS(); // RTS
asm.End(); // Finalize assembly (resolves labels)
// Get the assembled machine code
var buffer = asm.Buffer;With a more fluent syntax:
using Asm6502;
using static Asm6502.Mos6502Factory;
// Create an assembler with a base address (e.g., $C000)
using var asm = new Mos6502Assembler(0xC000);
asm
.Begin() // Start assembling
.LDX_Imm(0x00) // LDX #$00
.LDY_Imm(0x10) // LDY #$10
.LDA(0x0200, X) // LDA $0200,X
.STA(0x0200, X) // STA $0200,X
.RTS() // RTS
.End(); // Finalize assembly (resolves labels)
// Get the assembled machine code
var buffer = asm.Buffer;using Asm6502;
using static Asm6502.Mos6502Factory;
var dis = new Mos6502Disassembler(new Mos6502DisassemblerOptions {
PrintLabelBeforeFirstInstruction = false,
PrintAddress = true,
PrintAssemblyBytes = true,
});
string asmText = dis.Disassemble(buffer);
Console.WriteLine(asmText);The Mos6502Assembler class provides a fluent API for generating 6502 machine code. Each method corresponds to a 6502 instruction. Labels and branches are supported and resolved automatically.
using var asm = new Mos6502Assembler();
asm
.Org(0xC000); // Start assembling at address $C000
asm.Label(out var start)
.LDX_Imm(0x00) // X = 0
.LDY_Imm(0x10); // Y = 16
asm.Label(out var loop)
.LDA(0x0200, X) // LDA $0200,X
.CMP(0xFF) // CMP #$FF
.BEQ(out var skip) // BEQ skip (forward label)
.CLC() // CLC
.ADC_Imm(0x01) // ADC #$01
.STA(0x0200, X); // STA $0200,X
asm.Label(skip)
.INX() // INX
.DEY() // DEY
.BNE(loop); // BNE loop
asm.Label(out var endLabel)
.JMP(endLabel)
.End();You can append arbitrary bytes or fill memory regions:
asm
.AppendBuffer([0x01, 0x02, 0x03]) // Appends 3 bytes
.AppendBytes(5, 0xFF); // Appends 5 bytes of value 0xFFThe Mos6502Disassembler class converts machine code back to readable assembly. Output formatting is highly customizable via Mos6502DisassemblerOptions.
var dis = new Mos6502Disassembler();
string asmText = dis.Disassemble(buffer);
Console.WriteLine(asmText);Options include:
- Show addresses and bytes
- Label formatting
- Indentation and padding
- Custom comments
Example:
var options = new Mos6502DisassemblerOptions {
PrintAddress = true,
PrintAssemblyBytes = true,
IndentSize = 4,
InstructionTextPaddingLength = 20,
PrintLabelBeforeFirstInstruction = false,
};
var dis = new Mos6502Disassembler(options);The assembler supports all 6502 addressing modes. Below is a summary of the addressing modes with examples in both assembly and C# syntax:
| Addressing Mode | ASM Example | C# Example | Description |
|---|---|---|---|
| Immediate | LDA #$10 |
asm.LDA_Imm(0x10) |
Load immediate value into A |
| Zero Page | LDA $10 |
asm.LDA(0x10) |
Load from zero page address |
| Zero Page,X | LDA $10,X |
asm.LDA(0x10, X) |
Load from zero page address + X |
| Zero Page,Y | LDX $10,Y |
asm.LDX(0x10, Y) |
Load from zero page address + Y |
| Absolute | LDA $1234 |
asm.LDA(0x1234) |
Load from absolute address |
| Absolute,X | LDA $1234,X |
asm.LDA(0x1234, X) |
Load from absolute address + X |
| Absolute,Y | LDA $1234,Y |
asm.LDA(0x1234, Y) |
Load from absolute address + Y |
| Indirect | JMP ($1234) |
asm.JMP(_[0x1234]) |
Jump to address stored at $1234 |
| (Indirect,X) | LDA ($10,X) |
asm.LDA(_[0x10, X]) |
Load from address at (zero page + X) |
| (Indirect),Y | LDA ($10),Y |
asm.LDA(_[0x10], Y) |
Load from address at (zero page) + Y |
Notice that immediate values are suffixed with _Imm, and indirect addressing uses the _[] syntax. The registers X and Y are used directly in the method calls.
In order to access the X and Y registers as well as the indirect addressing modes, the following syntax is used in C#:
using static Asm6502.Mos6502Factory;Labels can be created and bound to addresses. Branch instructions (e.g., BEQ, BNE) can reference labels, even forward-declared ones. The assembler will resolve all label addresses when End() is called.
asm
.Label(out var loop)
.BNE(loop); Note that the name of the label is inferred from the variable name if not explicitly provided. In the example above, the label will be named "loop". You can also provide a custom name:
asm
.Label("CUSTOM_LABEL", out var customLabel)
.BNE(customLabel);
⚠️ The label name is only used when reporting errors when resolving label addresses.
You can also create forward labels that are resolved later:
asm.LabelForward(out var skipLabel)
// ... some instructions ...
.BEQ(skipLabel) // Branch to SKIP if condition met
.LDA_Imm(0xFF); // Load accumulator with 0xFF
asm.Label(skipLabel); // Bind SKIP label laterOr directly within the branch instruction:
asm
.BEQ(out var skipLabel) // Branch to SKIP if condition met
.LDA_Imm(0xFF); // Load accumulator with 0xFF
asm.Label(skipLabel) // Bind SKIP label laterIt is possible to use expressions for memory addresses and immediate values, such as:
- Storing the low byte and high byte of an address
asm .Begin() .LabelForward(out var forwardLabel) .LDA_Imm(forwardLabel.LowByte()) .STA(0x1000) .LDA_Imm(forwardLabel.HighByte()) .STA(0x1001) .RTS() .Label(forwardLabel) .End();
- Storing the difference of value between two labels
asm .Begin() .Label(out var startLabel) .LabelForward(out var endLabel) .LDA_Imm((endLabel - startLabel).LowByte()) // Store the size of this code .STA(0x1000) .RTS() .Label(endLabel) .End();
- The address of label + a const value
asm .Begin() .LabelForward(out var endLabel) .LDA(endLabel + 1) // Load A with the address of endLabel + 1 .RTS() .Label(endLabel) .End();
- Appending as raw data an address or the difference between two labels
asm .Begin() .Label(out var startLabel) .LabelForward(out var endLabel) .RTS() .Label(endLabel) .Append(startLabel) // The address of the label .Append((endLabel - startLabel).LowByte()) // The size of the code .End();
The disassembler supports extensive customization:
- Custom label formatting: Provide a delegate to format labels.
- Instruction comments: Add comments per instruction.
- Pre/Post instruction hooks: Inject text before/after each instruction.
Example:
var options = new Mos6502DisassemblerOptions {
TryFormatLabel = (offset, span, out int charsWritten) => {
// Custom label formatting logic
charsWritten = $"LBL_{offset:X4}".AsSpan().CopyTo(span) ? 9 : 0;
return charsWritten > 0;
},
TryFormatComment = (offset, inst, span, out int charsWritten) => {
// Add a comment for each instruction
charsWritten = $"; Offset {offset:X4}".AsSpan().CopyTo(span) ? 13 : 0;
return charsWritten > 0;
}
};The assembler can generate debug line information that includes C# source file names and line numbers. It simply requires to add a derived class from IMos6502AssemblerDebugMap or use the default Mos6502AssemblerDebugMap implementation.
var debugMap = new Mos6502AssemblerDebugMap();
var asm = new Mos6502Assembler()
{
DebugMap = debugMap
};
var forwardLabel = new Mos6502Label();
asm
.Begin(0xC000)
.LDA(0x5) // Zero page load
.STA(0x1000)
.Label(out var label)
.LDA(_[0x1, X])
.LDA(_[0x2], Y)
.BEQ(label)
.BCC(forwardLabel)
.RTS()
.Label(forwardLabel)
.End();
var toString = asm.DebugMap.ToString();
Console.WriteLine(toString);will print something like:
$c000 ORG BEGIN: TestSimple
$c000 LINE: {ProjectDirectory}Mos6502AssemblerSpecialTests.cs:59
$c002 LINE: {ProjectDirectory}Mos6502AssemblerSpecialTests.cs:60
$c005 LINE: {ProjectDirectory}Mos6502AssemblerSpecialTests.cs:62
$c007 LINE: {ProjectDirectory}Mos6502AssemblerSpecialTests.cs:63
$c009 LINE: {ProjectDirectory}Mos6502AssemblerSpecialTests.cs:64
$c00b LINE: {ProjectDirectory}Mos6502AssemblerSpecialTests.cs:65
$c00d LINE: {ProjectDirectory}Mos6502AssemblerSpecialTests.cs:66
$c00e END
The org directive allows to set the current assembly address to a specific value.
using var asm = new Mos6502Assembler();
asm.Begin(0xC000); // Resets the assembler state and sets the org to 0xC000
asm.LDA_Imm(0);
asm.Label(out var label1);
asm.LabelForward(out var label2);
asm.JMP(label2);
// Fill with NOPs to reach address 0xC010
asm.AppendBytes(0x10 - asm.SizeInBytes, (byte)Mos6502OpCode.NOP_Implied);
asm.Org(0xC010); // Usage of org directive to set the base address to 0xC010
asm.LDA_Imm(1);
asm.Label(label2);
asm.JMP(label1);
asm.End();This will produce the following code:
C000 A9 00 LDA #$00
LL_02:
C002 4C 12 C0 JMP LL_01
C005 EA NOP
C006 EA NOP
C007 EA NOP
C008 EA NOP
C009 EA NOP
C00A EA NOP
C00B EA NOP
C00C EA NOP
C00D EA NOP
C00E EA NOP
C00F EA NOP
C010 A9 01 LDA #$01
LL_01:
C012 4C 02 C0 JMP LL_02
Asm6502 includes a code relocation analyzer, Asm6502.Relocator.CodeRelocator, that emulates your 6502/6510 program to:
- discover accessed addresses and zero-page usage
- determine which code bytes are self-modified or contribute to addressing
- compute a safe relocation mapping and optionally remap zero-page usage
The relocator embeds its own 64 KiB RAM and a 6502/6510 CPU. It can trace memory reads/writes, branches, indirect accesses, and stack usage, and it can relocate your code to another base address while keeping it functional.
Key types:
CodeRelocator– main API to analyze and relocate a program bufferCodeRelocationConfig– input buffer and configuration (program start, analysis range, ZP relocation)CodeRelocationTarget– where to relocate (new base address and zero-page range)CodeRelocationDiagnosticBag– diagnostics, warnings and errors produced during analysis/relocation
Minimal example:
using Asm6502.Relocator;
// Prepare configuration
var config = new CodeRelocationConfig
{
ProgramAddress = 0xC000,
ProgramBytes = programBytes, // your assembled bytes
ZpRelocate = true, // enable zero-page relocation if needed (the default)
};
var relocator = new CodeRelocator(config);
// Analyze by running a subroutine from its entry point until RTS
// Optionally cap cycles to guard against infinite loops
relocator.RunSubroutineAt(0xC000, maxCycles: 500_000);
// Request relocation to a new address and ZP range
var relocated = relocator.Relocate(new CodeRelocationTarget
{
Address = 0x2000, // target base, same low-byte as original recommended
ZpRange = new RamZpRange(0x80, 0x20) // relocate ZP usage to $80-$9F
});
// Diagnostics and mapping can be inspected or printed
if (relocator.Diagnostics.Messages.Count > 0)
{
foreach (var m in relocator.Diagnostics.Messages) Console.WriteLine(m);
}Notes and constraints:
- Analysis range defaults to
[ProgramAddress, ProgramAddress + ProgramBytes.Length - 1]. You can override it inCodeRelocationConfig. - The relocator emulates code paths you run via
RunSubroutineAt. Unreached code won’t be analyzed. - Write-then-read self-modifying sequences are tracked, so absolute operands modified at runtime will be resolved correctly when relocating.
- If
ZpRelocate = true, a non-emptyZpRangemust be provided at relocation time.
Recommended workflow:
- Initialize with original buffer (
Initializeor constructor). - Execute entry points with
RunSubroutineAt(address, maxCycles)for init/play/IRQ/NMI. - Call
Relocate(target)once analysis is done. - Optionally use
SafeRamRangesto mark hardware or memory areas that must not be used during analysis (e.g.,$D400SID regs).
Advanced usage pattern (SID-like init/play/IRQ):
// High-level flow similar to a SID driver:
// - Run Init(A = subtune) once
// - Run Play many times; optionally detect IRQ/NMI vectors (e.g., $0314/$0318 or $FFFE/$FFFA)
relocator.Diagnostics.LogLevel = CodeRelocationDiagnosticKind.Info;
for (int song = 0; song < songs; song++)
{
relocator.Cpu.A = (byte)song; // pass subtune in A
relocator.RunSubroutineAt(initAddress, maxCycles: 1_000_000);
for (int i = 0; i < playSteps; i++)
{
// Optionally probe vectors written by the code to discover dynamic play entry
if (relocator.CheckIndirectAddressAndProbeIfRelocatable(0x0314, out var irqAddr) ||
relocator.CheckIndirectAddressAndProbeIfRelocatable(0xFFFE, out irqAddr))
{
relocator.RunSubroutineAt(irqAddr, maxCycles: 20_000);
}
else
{
relocator.RunSubroutineAt(playAddress, maxCycles: 20_000);
}
// Optionally analyze an NMI “digi” routine if detected
if (relocator.CheckIndirectAddressAndProbeIfRelocatable(0x0318, out var nmiAddr) ||
relocator.CheckIndirectAddressAndProbeIfRelocatable(0xFFFA, out nmiAddr))
{
relocator.RunSubroutineAt(nmiAddr, maxCycles: 1_000);
}
}
}
// Once analyzed, relocate
var target = new CodeRelocationTarget
{
Address = 0x2000,
ZpRange = new RamZpRange(0x80, 0x10)
};
var relocatedBytes = relocator.Relocate(target);
// Optionally print relocation map and diagnostics
relocator.PrintRelocationMap(Console.Out);Asm6502 provides a cycle-accurate CPU core with two variants:
Mos6502Cpuimplements the documented 6502 instruction setMos6510Cpuextends it with all undocumented opcodes (recommended)
Both cores use a pluggable 64 KiB memory bus defined by IMos6502CpuMemoryBus.
Quick start:
using Asm6502;
// Minimal 64 KiB RAM bus
public sealed class RamBus : IMos6502CpuMemoryBus
{
private readonly byte[] _ram;
public RamBus(byte[] ram) => _ram = ram;
public byte Read(ushort address) => _ram[address];
public void Write(ushort address, byte value) => _ram[address] = value;
}
// Create memory and a tiny program at $C000: LDA #$01; ADC #$01;
var mem = new byte[65536];
mem[0xC000] = 0xA9;
mem[0xC001] = 0x01; // LDA #$01
mem[0xC002] = 0x69;
mem[0xC003] = 0x01; // ADC #$01
// Set Reset vector to $C000
mem[0xFFFC] = 0x00;
mem[0xFFFD] = 0xC0;
var cpu = new Mos6510Cpu(new RamBus(mem));
cpu.Reset(); // fetch reset vector and begin executing
cpu.Steps(2); // Run 2 instructions (LDA and ADC)
var a = cpu.A; // 2
// cpu.PC is at 0xC004 (next instruction)Useful members:
Step()/Steps(n): step by instruction;Cycle()/Cycles(n): step by bus cycleInstructionCycles: cycles consumed by the last completed instructionTimestampCounter: monotonically increasing bus-cycle counterNmi(),Irq(),Reset()convenience helpers;RaiseNmi/Irq/Reset()to request on next cycle
Memory-bus expectations:
- Read-Modify-Write instructions perform read + two writes (read, write-back, write-modified)
- Stack activity occurs in page $0100–$01FF; vectors are read from $FFFA/$FFFC/$FFFE
- Unattached bus reads default to NOP (0xEA) internally
The library also supports the 6510 CPU, a 6502 variant used in the Commodore 64. In addition to the assembler and disassembler, you can emulate and run code with the CPU core:
Mos6510Assembler/Mos6510Disassembler: assembly and disassembly with undocumented opcodesMos6510Cpu: cycle-accurate execution with full opcode coverage
Use Mos6510Cpu when you need to execute code and measure exact cycle counts; if you only need documented opcodes, Mos6502Cpu is available as well.
- Always call
End()after assembling to resolve labels and finalize the buffer. - Instead of raw addresses you can use labels for all branch targets; the assembler will resolve them.
- Customize disassembly output for integration with tools or documentation.
- Use
AppendBufferandAppendBytesfor embedding data or padding. - Dispose the assembler (
using var asm = ...) to release internal buffers.
The following instructions are supported by the Mos6502Assembler and Mos6510Assembler classes:
| Byte | Instruction | C# Syntax | Description |
|---|---|---|---|
0x6d |
ADC address |
asm.ADC(address); |
Add with carry |
0x7d |
ADC address, X |
asm.ADC(address, X); |
Add with carry |
0x79 |
ADC address, Y |
asm.ADC(address, Y); |
Add with carry |
0x69 |
ADC #value |
asm.ADC_Imm(value); |
Add with carry |
0x61 |
ADC (zp, X) |
asm.ADC(_[zp, X]); |
Add with carry |
0x71 |
ADC (zp), Y |
asm.ADC(_[zp], Y); |
Add with carry |
0x65 |
ADC zp |
asm.ADC(zp); |
Add with carry |
0x75 |
ADC zp, X |
asm.ADC(zp, X); |
Add with carry |
0x2d |
AND address |
asm.AND(address); |
Logical AND |
0x3d |
AND address, X |
asm.AND(address, X); |
Logical AND |
0x39 |
AND address, Y |
asm.AND(address, Y); |
Logical AND |
0x29 |
AND #value |
asm.AND_Imm(value); |
Logical AND |
0x21 |
AND (zp, X) |
asm.AND(_[zp, X]); |
Logical AND |
0x31 |
AND (zp), Y |
asm.AND(_[zp], Y); |
Logical AND |
0x25 |
AND zp |
asm.AND(zp); |
Logical AND |
0x35 |
AND zp, X |
asm.AND(zp, X); |
Logical AND |
0x0e |
ASL address |
asm.ASL(address); |
Arithmetic shift left |
0x1e |
ASL address, X |
asm.ASL(address, X); |
Arithmetic shift left |
0x0a |
ASL A |
asm.ASL(A); |
Arithmetic shift left |
0x06 |
ASL zp |
asm.ASL(zp); |
Arithmetic shift left |
0x16 |
ASL zp, X |
asm.ASL(zp, X); |
Arithmetic shift left |
0x90 |
BCC label |
asm.BCC(label); |
Branch if carry clear |
0xb0 |
BCS label |
asm.BCS(label); |
Branch if carry set |
0xf0 |
BEQ label |
asm.BEQ(label); |
Branch if equal |
0x2c |
BIT address |
asm.BIT(address); |
Bit test |
0x24 |
BIT zp |
asm.BIT(zp); |
Bit test |
0x30 |
BMI label |
asm.BMI(label); |
Branch if minus |
0xd0 |
BNE label |
asm.BNE(label); |
Branch if not equal |
0x10 |
BPL label |
asm.BPL(label); |
Branch if positive |
0x00 |
BRK |
asm.BRK(); |
Break / Software Interrupt |
0x50 |
BVC label |
asm.BVC(label); |
Branch if overflow clear |
0x70 |
BVS label |
asm.BVS(label); |
Branch if overflow set |
0x18 |
CLC |
asm.CLC(); |
Clear carry |
0xd8 |
CLD |
asm.CLD(); |
Clear decimal mode |
0x58 |
CLI |
asm.CLI(); |
Clear interrupt disable |
0xb8 |
CLV |
asm.CLV(); |
Clear overflow flag |
0xcd |
CMP address |
asm.CMP(address); |
Compare |
0xdd |
CMP address, X |
asm.CMP(address, X); |
Compare |
0xd9 |
CMP address, Y |
asm.CMP(address, Y); |
Compare |
0xc9 |
CMP #value |
asm.CMP_Imm(value); |
Compare |
0xc1 |
CMP (zp, X) |
asm.CMP(_[zp, X]); |
Compare |
0xd1 |
CMP (zp), Y |
asm.CMP(_[zp], Y); |
Compare |
0xc5 |
CMP zp |
asm.CMP(zp); |
Compare |
0xd5 |
CMP zp, X |
asm.CMP(zp, X); |
Compare |
0xec |
CPX address |
asm.CPX(address); |
Compare X register |
0xe0 |
CPX #value |
asm.CPX_Imm(value); |
Compare X register |
0xe4 |
CPX zp |
asm.CPX(zp); |
Compare X register |
0xcc |
CPY address |
asm.CPY(address); |
Compare Y register |
0xc0 |
CPY #value |
asm.CPY_Imm(value); |
Compare Y register |
0xc4 |
CPY zp |
asm.CPY(zp); |
Compare Y register |
0xce |
DEC address |
asm.DEC(address); |
Decrement memory |
0xde |
DEC address, X |
asm.DEC(address, X); |
Decrement memory |
0xc6 |
DEC zp |
asm.DEC(zp); |
Decrement memory |
0xd6 |
DEC zp, X |
asm.DEC(zp, X); |
Decrement memory |
0xca |
DEX |
asm.DEX(); |
Decrement X register |
0x88 |
DEY |
asm.DEY(); |
Decrement Y register |
0x4d |
EOR address |
asm.EOR(address); |
Logical Exclusive OR (XOR) |
0x5d |
EOR address, X |
asm.EOR(address, X); |
Logical Exclusive OR (XOR) |
0x59 |
EOR address, Y |
asm.EOR(address, Y); |
Logical Exclusive OR (XOR) |
0x49 |
EOR #value |
asm.EOR_Imm(value); |
Logical Exclusive OR (XOR) |
0x41 |
EOR (zp, X) |
asm.EOR(_[zp, X]); |
Logical Exclusive OR (XOR) |
0x51 |
EOR (zp), Y |
asm.EOR(_[zp], Y); |
Logical Exclusive OR (XOR) |
0x45 |
EOR zp |
asm.EOR(zp); |
Logical Exclusive OR (XOR) |
0x55 |
EOR zp, X |
asm.EOR(zp, X); |
Logical Exclusive OR (XOR) |
0xee |
INC address |
asm.INC(address); |
Increment memory |
0xfe |
INC address, X |
asm.INC(address, X); |
Increment memory |
0xe6 |
INC zp |
asm.INC(zp); |
Increment memory |
0xf6 |
INC zp, X |
asm.INC(zp, X); |
Increment memory |
0xe8 |
INX |
asm.INX(); |
Increment X register |
0xc8 |
INY |
asm.INY(); |
Increment Y register |
0x4c |
JMP address |
asm.JMP(address); |
Unconditional Jump |
0x6c |
JMP (address) |
asm.JMP(_[address]); |
Unconditional Jump |
0x20 |
JSR address |
asm.JSR(address); |
Jump to subroutine |
0xad |
LDA address |
asm.LDA(address); |
Load accumulator |
0xbd |
LDA address, X |
asm.LDA(address, X); |
Load accumulator |
0xb9 |
LDA address, Y |
asm.LDA(address, Y); |
Load accumulator |
0xa9 |
LDA #value |
asm.LDA_Imm(value); |
Load accumulator |
0xa1 |
LDA (zp, X) |
asm.LDA(_[zp, X]); |
Load accumulator |
0xb1 |
LDA (zp), Y |
asm.LDA(_[zp], Y); |
Load accumulator |
0xa5 |
LDA zp |
asm.LDA(zp); |
Load accumulator |
0xb5 |
LDA zp, X |
asm.LDA(zp, X); |
Load accumulator |
0xae |
LDX address |
asm.LDX(address); |
Load X register |
0xbe |
LDX address, Y |
asm.LDX(address, Y); |
Load X register |
0xa2 |
LDX #value |
asm.LDX_Imm(value); |
Load X register |
0xa6 |
LDX zp |
asm.LDX(zp); |
Load X register |
0xb6 |
LDX zp, Y |
asm.LDX(zp, Y); |
Load X register |
0xac |
LDY address |
asm.LDY(address); |
Load Y register |
0xbc |
LDY address, X |
asm.LDY(address, X); |
Load Y register |
0xa0 |
LDY #value |
asm.LDY_Imm(value); |
Load Y register |
0xa4 |
LDY zp |
asm.LDY(zp); |
Load Y register |
0xb4 |
LDY zp, X |
asm.LDY(zp, X); |
Load Y register |
0x4e |
LSR address |
asm.LSR(address); |
Logical shift right |
0x5e |
LSR address, X |
asm.LSR(address, X); |
Logical shift right |
0x4a |
LSR A |
asm.LSR(A); |
Logical shift right |
0x46 |
LSR zp |
asm.LSR(zp); |
Logical shift right |
0x56 |
LSR zp, X |
asm.LSR(zp, X); |
Logical shift right |
0xea |
NOP |
asm.NOP(); |
No operation |
0x0d |
ORA address |
asm.ORA(address); |
Logical Inclusive OR |
0x1d |
ORA address, X |
asm.ORA(address, X); |
Logical Inclusive OR |
0x19 |
ORA address, Y |
asm.ORA(address, Y); |
Logical Inclusive OR |
0x09 |
ORA #value |
asm.ORA_Imm(value); |
Logical Inclusive OR |
0x01 |
ORA (zp, X) |
asm.ORA(_[zp, X]); |
Logical Inclusive OR |
0x11 |
ORA (zp), Y |
asm.ORA(_[zp], Y); |
Logical Inclusive OR |
0x05 |
ORA zp |
asm.ORA(zp); |
Logical Inclusive OR |
0x15 |
ORA zp, X |
asm.ORA(zp, X); |
Logical Inclusive OR |
0x48 |
PHA |
asm.PHA(); |
Push accumulator |
0x08 |
PHP |
asm.PHP(); |
Push processor status |
0x68 |
PLA |
asm.PLA(); |
Pull accumulator |
0x28 |
PLP |
asm.PLP(); |
Pull processor status |
0x2e |
ROL address |
asm.ROL(address); |
Rotate left |
0x3e |
ROL address, X |
asm.ROL(address, X); |
Rotate left |
0x2a |
ROL A |
asm.ROL(A); |
Rotate left |
0x26 |
ROL zp |
asm.ROL(zp); |
Rotate left |
0x36 |
ROL zp, X |
asm.ROL(zp, X); |
Rotate left |
0x6e |
ROR address |
asm.ROR(address); |
Rotate right |
0x7e |
ROR address, X |
asm.ROR(address, X); |
Rotate right |
0x6a |
ROR A |
asm.ROR(A); |
Rotate right |
0x66 |
ROR zp |
asm.ROR(zp); |
Rotate right |
0x76 |
ROR zp, X |
asm.ROR(zp, X); |
Rotate right |
0x40 |
RTI |
asm.RTI(); |
Return from interrupt |
0x60 |
RTS |
asm.RTS(); |
Return from subroutine |
0xed |
SBC address |
asm.SBC(address); |
Subtract with carry |
0xfd |
SBC address, X |
asm.SBC(address, X); |
Subtract with carry |
0xf9 |
SBC address, Y |
asm.SBC(address, Y); |
Subtract with carry |
0xe9 |
SBC #value |
asm.SBC_Imm(value); |
Subtract with carry |
0xe1 |
SBC (zp, X) |
asm.SBC(_[zp, X]); |
Subtract with carry |
0xf1 |
SBC (zp), Y |
asm.SBC(_[zp], Y); |
Subtract with carry |
0xe5 |
SBC zp |
asm.SBC(zp); |
Subtract with carry |
0xf5 |
SBC zp, X |
asm.SBC(zp, X); |
Subtract with carry |
0x38 |
SEC |
asm.SEC(); |
Set carry |
0xf8 |
SED |
asm.SED(); |
Set decimal flag |
0x78 |
SEI |
asm.SEI(); |
Set interrupt disable |
0x8d |
STA address |
asm.STA(address); |
Store accumulator |
0x9d |
STA address, X |
asm.STA(address, X); |
Store accumulator |
0x99 |
STA address, Y |
asm.STA(address, Y); |
Store accumulator |
0x81 |
STA (zp, X) |
asm.STA(_[zp, X]); |
Store accumulator |
0x91 |
STA (zp), Y |
asm.STA(_[zp], Y); |
Store accumulator |
0x85 |
STA zp |
asm.STA(zp); |
Store accumulator |
0x95 |
STA zp, X |
asm.STA(zp, X); |
Store accumulator |
0x8e |
STX address |
asm.STX(address); |
Store X register |
0x86 |
STX zp |
asm.STX(zp); |
Store X register |
0x96 |
STX zp, Y |
asm.STX(zp, Y); |
Store X register |
0x8c |
STY address |
asm.STY(address); |
Store Y register |
0x84 |
STY zp |
asm.STY(zp); |
Store Y register |
0x94 |
STY zp, X |
asm.STY(zp, X); |
Store Y register |
0xaa |
TAX |
asm.TAX(); |
Transfer acc to X |
0xa8 |
TAY |
asm.TAY(); |
Transfer acc to Y |
0xba |
TSX |
asm.TSX(); |
Transfer stack pointer to X |
0x8a |
TXA |
asm.TXA(); |
Transfer X to acc |
0x9a |
TXS |
asm.TXS(); |
Transfer X to SP |
0x98 |
TYA |
asm.TYA(); |
Transfer Y to acc |
The following instructions are supported by the Mos6510Assembler class:
| Byte | Instruction | C# Syntax | Aliases | Description | Unstable |
|---|---|---|---|---|---|
0x4b |
ALR #value |
asm.ALR_Imm(value); |
ALR, ASR |
AND then LSR | |
0x0b |
ANC #value |
asm.ANC_Imm(value); |
ANC |
AND then set carry | |
0x2b |
ANC #value |
asm.ANC_2B_Imm(value); |
ANC, ANC2 |
AND then set carry | |
0x8b |
ANE #value |
asm.ANE_Imm(value); |
ANE, XAA |
Undocumented: AND with X then AND operand | ❌ |
0x6b |
ARR #value |
asm.ARR_Imm(value); |
ARR |
AND then ROR | |
0xcf |
DCP address |
asm.DCP(address); |
DCP, DCM |
DEC then CMP | |
0xdf |
DCP address, X |
asm.DCP(address, X); |
DCP, DCM |
DEC then CMP | |
0xdb |
DCP address, Y |
asm.DCP(address, Y); |
DCP, DCM |
DEC then CMP | |
0xc3 |
DCP (zp, X) |
asm.DCP(_[zp, X]); |
DCP, DCM |
DEC then CMP | |
0xd3 |
DCP (zp), Y |
asm.DCP(_[zp], Y); |
DCP, DCM |
DEC then CMP | |
0xc7 |
DCP zp |
asm.DCP(zp); |
DCP, DCM |
DEC then CMP | |
0xd7 |
DCP zp, X |
asm.DCP(zp, X); |
DCP, DCM |
DEC then CMP | |
0xef |
ISC address |
asm.ISC(address); |
ISC, ISB, INS |
INC then SBC | |
0xff |
ISC address, X |
asm.ISC(address, X); |
ISC, ISB, INS |
INC then SBC | |
0xfb |
ISC address, Y |
asm.ISC(address, Y); |
ISC, ISB, INS |
INC then SBC | |
0xe3 |
ISC (zp, X) |
asm.ISC(_[zp, X]); |
ISC, ISB, INS |
INC then SBC | |
0xf3 |
ISC (zp), Y |
asm.ISC(_[zp], Y); |
ISC, ISB, INS |
INC then SBC | |
0xe7 |
ISC zp |
asm.ISC(zp); |
ISC, ISB, INS |
INC then SBC | |
0xf7 |
ISC zp, X |
asm.ISC(zp, X); |
ISC, ISB, INS |
INC then SBC | |
0x02 |
JAM |
asm.JAM(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x12 |
JAM |
asm.JAM_12(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x22 |
JAM |
asm.JAM_22(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x32 |
JAM |
asm.JAM_32(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x42 |
JAM |
asm.JAM_42(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x52 |
JAM |
asm.JAM_52(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x62 |
JAM |
asm.JAM_62(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x72 |
JAM |
asm.JAM_72(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0x92 |
JAM |
asm.JAM_92(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0xb2 |
JAM |
asm.JAM_B2(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0xd2 |
JAM |
asm.JAM_D2(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0xf2 |
JAM |
asm.JAM_F2(); |
JAM, KIL, HLT |
Jam the CPU (halt) | |
0xbb |
LAS address, Y |
asm.LAS(address, Y); |
LAS, LAR |
Load accumulator and transfer SP to X | |
0xaf |
LAX address |
asm.LAX(address); |
LAX |
LDA then LDX | |
0xbf |
LAX address, Y |
asm.LAX(address, Y); |
LAX |
LDA then LDX | |
0xa3 |
LAX (zp, X) |
asm.LAX(_[zp, X]); |
LAX |
LDA then LDX | |
0xb3 |
LAX (zp), Y |
asm.LAX(_[zp], Y); |
LAX |
LDA then LDX | |
0xa7 |
LAX zp |
asm.LAX(zp); |
LAX |
LDA then LDX | |
0xb7 |
LAX zp, Y |
asm.LAX(zp, Y); |
LAX |
LDA then LDX | |
0xab |
LXA #value |
asm.LXA_Imm(value); |
LXA, LAX, immediate |
LDA then LDX | ❌ |
0x0c |
NOP address |
asm.NOP(address); |
NOP |
No operation | |
0x1c |
NOP address, X |
asm.NOP(address, X); |
NOP |
No operation | |
0x3c |
NOP address, X |
asm.NOP_3C(address, X); |
NOP |
No operation | |
0x5c |
NOP address, X |
asm.NOP_5C(address, X); |
NOP |
No operation | |
0x7c |
NOP address, X |
asm.NOP_7C(address, X); |
NOP |
No operation | |
0xdc |
NOP address, X |
asm.NOP_DC(address, X); |
NOP |
No operation | |
0xfc |
NOP address, X |
asm.NOP_FC(address, X); |
NOP |
No operation | |
0x80 |
NOP #value |
asm.NOP_Imm(value); |
NOP |
No operation | |
0x82 |
NOP #value |
asm.NOP_82_Imm(value); |
NOP |
No operation | |
0x89 |
NOP #value |
asm.NOP_89_Imm(value); |
NOP |
No operation | |
0xc2 |
NOP #value |
asm.NOP_C2_Imm(value); |
NOP |
No operation | |
0xe2 |
NOP #value |
asm.NOP_E2_Imm(value); |
NOP |
No operation | |
0x1a |
NOP |
asm.NOP_1A(); |
NOP |
No operation | |
0x3a |
NOP |
asm.NOP_3A(); |
NOP |
No operation | |
0x5a |
NOP |
asm.NOP_5A(); |
NOP |
No operation | |
0x7a |
NOP |
asm.NOP_7A(); |
NOP |
No operation | |
0xda |
NOP |
asm.NOP_DA(); |
NOP |
No operation | |
0xfa |
NOP |
asm.NOP_FA(); |
NOP |
No operation | |
0x04 |
NOP zp |
asm.NOP(zp); |
NOP |
No operation | |
0x44 |
NOP zp |
asm.NOP_44(zp); |
NOP |
No operation | |
0x64 |
NOP zp |
asm.NOP_64(zp); |
NOP |
No operation | |
0x14 |
NOP zp, X |
asm.NOP(zp, X); |
NOP |
No operation | |
0x34 |
NOP zp, X |
asm.NOP_34(zp, X); |
NOP |
No operation | |
0x54 |
NOP zp, X |
asm.NOP_54(zp, X); |
NOP |
No operation | |
0x74 |
NOP zp, X |
asm.NOP_74(zp, X); |
NOP |
No operation | |
0xd4 |
NOP zp, X |
asm.NOP_D4(zp, X); |
NOP |
No operation | |
0xf4 |
NOP zp, X |
asm.NOP_F4(zp, X); |
NOP |
No operation | |
0x2f |
RLA address |
asm.RLA(address); |
RLA |
ROL then AND | |
0x3f |
RLA address, X |
asm.RLA(address, X); |
RLA |
ROL then AND | |
0x3b |
RLA address, Y |
asm.RLA(address, Y); |
RLA |
ROL then AND | |
0x23 |
RLA (zp, X) |
asm.RLA(_[zp, X]); |
RLA |
ROL then AND | |
0x33 |
RLA (zp), Y |
asm.RLA(_[zp], Y); |
RLA |
ROL then AND | |
0x27 |
RLA zp |
asm.RLA(zp); |
RLA |
ROL then AND | |
0x37 |
RLA zp, X |
asm.RLA(zp, X); |
RLA |
ROL then AND | |
0x6f |
RRA address |
asm.RRA(address); |
RRA |
ROR then ADC | |
0x7f |
RRA address, X |
asm.RRA(address, X); |
RRA |
ROR then ADC | |
0x7b |
RRA address, Y |
asm.RRA(address, Y); |
RRA |
ROR then ADC | |
0x63 |
RRA (zp, X) |
asm.RRA(_[zp, X]); |
RRA |
ROR then ADC | |
0x73 |
RRA (zp), Y |
asm.RRA(_[zp], Y); |
RRA |
ROR then ADC | |
0x67 |
RRA zp |
asm.RRA(zp); |
RRA |
ROR then ADC | |
0x77 |
RRA zp, X |
asm.RRA(zp, X); |
RRA |
ROR then ADC | |
0x8f |
SAX address |
asm.SAX(address); |
SAX, AXS, AAX |
Store accumulator AND X | |
0x83 |
SAX (zp, X) |
asm.SAX(_[zp, X]); |
SAX, AXS, AAX |
Store accumulator AND X | |
0x87 |
SAX zp |
asm.SAX(zp); |
SAX, AXS, AAX |
Store accumulator AND X | |
0x97 |
SAX zp, Y |
asm.SAX(zp, Y); |
SAX, AXS, AAX |
Store accumulator AND X | |
0xcb |
SBX #value |
asm.SBX_Imm(value); |
SBX, AXS, SAX |
Compute (A AND X) then subtract with carry | |
0x9f |
SHA address, Y |
asm.SHA(address, Y); |
SHA, AHX, AXA |
Store A AND X AND (high address + 1) | |
0x93 |
SHA (zp), Y |
asm.SHA(_[zp], Y); |
SHA, AHX, AXA |
Store A AND X AND (high address + 1) | ❌ |
0x9e |
SHX address, Y |
asm.SHX(address, Y); |
SHX, A11, SXA, XAS |
Store A AND X AND (high address + 1) | ❌ |
0x9c |
SHY address, X |
asm.SHY(address, X); |
SHY, A11, SYA, SAY |
Store Y AND (high address + 1) | ❌ |
0x0f |
SLO address |
asm.SLO(address); |
SLO, ASO |
ASL then ORA | |
0x1f |
SLO address, X |
asm.SLO(address, X); |
SLO, ASO |
ASL then ORA | |
0x1b |
SLO address, Y |
asm.SLO(address, Y); |
SLO, ASO |
ASL then ORA | |
0x03 |
SLO (zp, X) |
asm.SLO(_[zp, X]); |
SLO, ASO |
ASL then ORA | |
0x13 |
SLO (zp), Y |
asm.SLO(_[zp], Y); |
SLO, ASO |
ASL then ORA | |
0x07 |
SLO zp |
asm.SLO(zp); |
SLO, ASO |
ASL then ORA | |
0x17 |
SLO zp, X |
asm.SLO(zp, X); |
SLO, ASO |
ASL then ORA | |
0x4f |
SRE address |
asm.SRE(address); |
SRE, LSE |
LSR then EOR | |
0x5f |
SRE address, X |
asm.SRE(address, X); |
SRE, LSE |
LSR then EOR | |
0x5b |
SRE address, Y |
asm.SRE(address, Y); |
SRE, LSE |
LSR then EOR | |
0x43 |
SRE (zp, X) |
asm.SRE(_[zp, X]); |
SRE, LSE |
LSR then EOR | |
0x53 |
SRE (zp), Y |
asm.SRE(_[zp], Y); |
SRE, LSE |
LSR then EOR | |
0x47 |
SRE zp |
asm.SRE(zp); |
SRE, LSE |
LSR then EOR | |
0x57 |
SRE zp, X |
asm.SRE(zp, X); |
SRE, LSE |
LSR then EOR | |
0x9b |
TAS address, Y |
asm.TAS(address, Y); |
TAS, XAS, SHS |
Transfer A AND X to SP, store A AND X AND (high address + 1) | ❌ |
0xeb |
USBC #value |
asm.USBC_Imm(value); |
USBC, SBC |
SBC with NOP behavior |