Skip to content

Latest commit

History

169 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Version 1.0


Table of Contents

  1. Introduction
  2. Command Line Usage
  3. Source File Format
  4. Numbers and Literals
  5. Expressions
  6. Labels and Symbols
  7. Addressing Modes
  8. Assembler Directives
  9. Macros
  10. Conditional Assembly
  11. Loops
  12. Complete Instruction Reference

Introduction

PASM is a modern, feature-rich assembler for the MOS 6502 and WDC 65C02 microprocessors. It supports the full instruction set of both processors, including illegal/undocumented 6502 opcodes, and provides powerful macro and conditional assembly capabilities.

Features

  • Full 6502 instruction set support
  • Extended 65C02 instruction set support
  • Illegal/undocumented 6502 opcode support (optional)
  • Powerful expression evaluation with C-style operators
  • Macro definitions with parameter substitution
  • Conditional assembly (.if, .ifdef, .ifndef)
  • Loop constructs (.do/.while)
  • Local and anonymous labels
  • Multiple input file support
  • Include file directive
  • Commodore 64 PRG file output format

Command Line Usage

pasm inputfile1 [inputfile2 ...] [options]

Options

OptionDescription
-hPrint help.
-astPrint the Abstract Syntax Tree.
-vEnable verbose mode. (displays symbol table)
-allShow all symbols in listing. By default only used symbols are listed.
-c64Commodore64 program mode. Set first 2 bytes as load address.
-o <file>Specify output filename
-65c02Enable 65C02 extended instruction set
-ilAllow illegal/undocumented 6502 instructions
-nowarnSuppress warning messages
-liList all valid instructions with addressing modes and cycle counts

Examples

# Basic assembly
pasm program.asm -o program.bin
# 65C02 assembly with verbose output
pasm program.asm -65c02 -v -o program.bin
# Commodore 64 PRG file
pasm game.asm -c64 -o game.prg
# Multiple source files
pasm main.asm utils.asm data.asm -o program.bin

Source File Format

Source files are plain text files with one statement per line. The general format of a line is:

[label[:]] [instruction/directive [operands]] [; comment]

All components are optional. Blank lines and comment-only lines are permitted.

Case Sensitivity

  • Instructions and directives: Case-insensitive (LDA, lda, and Lda are equivalent)
  • Symbols and labels: Case-insensitive (MYVAR, MyVar, and myvar refer to the same symbol)

Comments

Comments begin with a semicolon (;) and extend to the end of the line:

LDA #$00 ; Load accumulator with zero; This entire line is a comment

Numbers and Literals

Numeric Formats

FormatPrefixExampleValue
Decimal(none)255255
Hexadecimal$$FF255
Binary%%11111111255
Character'c' or "c"'A'65

Hexadecimal with Spaces

For readability, hexadecimal and binary numbers may contain spaces between digit groups:

.BYTE$01020304 ; Equivalent to $01, $02, $03, $04MASK = %11110000 ; Equivalent to %11110000

Text Strings

Text strings are enclosed in single or double quotes:

.BYTE"Hello, World!".BYTE'SCORE: '

Expressions

PASM supports a rich expression syntax with C-style operators. Expressions are evaluated as signed integers.

Operators (by precedence, highest first)

PrecedenceOperatorsDescription
1( )Parentheses
2-+~Unary minus, plus, one's complement
3*/%Multiply, divide, modulo
4+-Add, subtract
5<<>>Shift left, shift right
6<><=>=Relational comparison
7==!=<>Equality, inequality
8&Bitwise AND
9^Bitwise XOR
10|Bitwise OR
11&&Logical AND
12||Logical OR

Special Symbols in Expressions

SymbolMeaning
*Current program counter (when not an operator)

Expression Examples

VALUE = 10*5+3 ; VALUE = 53MASK =$FF & %11110000 ; MASK = $F0OFFSET = LABEL -* ; Distance from current PC to LABELHIBYTE = ADDRESS >> 8 ; Extract high byteLOBYTE = ADDRESS &$FF ; Extract low byteFLAG = VALUE >= 100 ; FLAG = 0 (false) or 1 (true)

Labels and Symbols

Standard Labels

Labels mark locations in code and can be defined with or without a trailing colon:

START: LDA #$00 ; Label with colonLOOP DEX ; Label without colon BNE LOOP

Symbol Equates

Symbols can be assigned constant values using the = operator:

SCREEN =$0400BORDER_COLOR =$D020MAX_LIVES = 3

Program Counter Assignment

The program counter can be set directly:

* =$C000 ; Set PC to $C000

Local Labels

Local labels begin with @ and are scoped between two global labels:

ROUTINE1: LDX #$10@LOOP: DEX BNE @LOOP ; References local @LOOP RTSROUTINE2: LDX #$20@LOOP: DEX ; Different @LOOP, local to ROUTINE2 BNE @LOOP RTS

When a new global label is defined, all previous local labels go out of scope.

Anonymous Labels

Anonymous labels provide a lightweight way to create short-range branch targets:

Definition:

  • +: defines a forward anonymous label
  • -: defines a backward anonymous label

Reference:

  • + references the next forward anonymous label
  • ++ references the second forward anonymous label
  • - references the previous backward anonymous label
  • -- references the second-to-last backward anonymous label
-: LDA DATA,X BEQ + ; Branch to next + label JSR PROCESS+: INX CPX #$10 BNE - ; Branch to previous - label

Multiple levels of anonymous references:

-: ; First backward label-: ; Second backward label BEQ -- ; Branch to first backward label BNE - ; Branch to second backward label

Addressing Modes

PASM automatically selects zero-page addressing when operands fit in a single byte and the instruction supports it.

ModeSyntaxExampleDescription
ImpliedOPCNOPNo operand
AccumulatorOPC AASL AOperates on accumulator
ImmediateOPC #valLDA #$FF8-bit immediate value
AbsoluteOPC addrLDA $123416-bit address
Zero PageOPC zpLDA $808-bit zero page address
Absolute,XOPC addr,XLDA $1234,XAbsolute indexed by X
Absolute,YOPC addr,YLDA $1234,YAbsolute indexed by Y
Zero Page,XOPC zp,XLDA $80,XZero page indexed by X
Zero Page,YOPC zp,YLDX $80,YZero page indexed by Y
IndirectOPC (addr)JMP ($FFFC)Indirect addressing
Indexed IndirectOPC (zp,X)LDA ($80,X)Indexed indirect (X)
Indirect IndexedOPC (zp),YLDA ($80),YIndirect indexed (Y)
RelativeOPC labelBNE LOOPBranch relative
ZP RelativeOPC zp,labelBBR0 $10,DONE65C02 bit branch

Assembler Directives

.ORG - Set Origin

Sets the program counter to a specific address:

.ORG$C000 ; Code starts at $C000

.BYTE / .BYT - Define Bytes

Stores one or more bytes in memory:

.BYTE$00,$01,$02,$03.BYTE"Hello",0 ; String with null terminator.BYT 'A','B','C'

.WORD / .WRD - Define Words

Stores one or more 16-bit words (little-endian):

.WORD$1234 ; Stores $34, $12.WORD LABEL1, LABEL2 ; Address table

.DS - Define Storage

Reserves a block of bytes (advances PC without generating output):

BUFFER: .DS256 ; Reserve 256 bytes

.FILL - Fill Memory

Fills a region with a repeated byte value:

.FILL$EA,16 ; 16 bytes of NOPs ($EA).FILL 0,256 ; 256 zero bytes

.INCLUDE / .INC - Include File

Includes another source file:

.INCLUDE "macros.asm".INC"data.asm"

Macros

Macros allow you to define reusable code sequences with parameter substitution.

Defining Macros

.MACRO NAME ; Macro body ; Use \1, \2, \3... for parameters.ENDM

Or using the short forms:

.MAC NAME ; body.ENDMACRO

Macro Parameters

Parameters are referenced using \1, \2, \3, etc.:

.MACRO LOAD_XY LDX #\1 LDY #\2.ENDM

Calling Macros

Macros are called by name, optionally followed by arguments:

LOAD_XY 10,20 ; Expands to LDX #10 / LDY #20LOAD_XY$FF,$00

For macros without arguments:

.MACRO SAVE_REGS PHA TXA PHA TYA PHA.ENDMSAVE_REGS ; Call with no argumentsSAVE_REGS() ; Alternative syntax

Macro Example

; 16-bit increment macro.MACRO INC16INC \1 BNE +INC \1+1+:.ENDMCOUNTER: .WORD 0 INC16 COUNTER ; Increment 16-bit counter

Conditional Assembly

.IF / .ELSE / .ENDIF

Conditional assembly based on expression evaluation:

DEBUG = 1.IF DEBUG JSR PRINT_STATUS ; Only assembled if DEBUG != 0.ELSENOP ; Assembled if DEBUG == 0.ENDIF

.IFDEF / .IFNDEF

Test whether a symbol is defined:

.IFDEF USE_SOUND JSR INIT_SOUND.ENDIF.IFNDEF SKIP_INTRO JSR SHOW_INTRO.ENDIF

Nested Conditionals

Conditionals can be nested:

.IF PLATFORM == 1 .IFDEF HAS_SID JSR PLAY_MUSIC .ENDIF.ENDIF

Loops

.DO / .WHILE

Repeat a block of code while a condition is true. Useful with .VAR for assembly-time iteration:

.VAR I = 0.DO .BYTE I.VAR I = I +1.WHILE I < 10

This generates: .BYTE 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

.VAR - Assembly-Time Variables

Variables differ from equates in that they can be modified during assembly:

.VAR COUNT = 0 ; Define variableCOUNT = COUNT +1 ; Modify variable

Loop Example: Generate Sine Table

; Generate 256-byte sine table (conceptual example).VAR I = 0SINE_TABLE:.DO .BYTE (I * I) / 256 ; Simplified calculation I = I +1.WHILE I < 256

Complete Instruction Reference

Use the -li command line option to display all supported instructions with their addressing modes and cycle counts:

pasm -li

Standard 6502 Instructions

MnemonicDescription
ADCAdd with Carry
ANDLogical AND
ASLArithmetic Shift Left
BCCBranch if Carry Clear
BCSBranch if Carry Set
BEQBranch if Equal (Zero)
BITBit Test
BMIBranch if Minus
BNEBranch if Not Equal
BPLBranch if Plus
BRKBreak
BVCBranch if Overflow Clear
BVSBranch if Overflow Set
CLCClear Carry
CLDClear Decimal
CLIClear Interrupt Disable
CLVClear Overflow
CMPCompare Accumulator
CPXCompare X Register
CPYCompare Y Register
DECDecrement Memory
DEXDecrement X
DEYDecrement Y
EORExclusive OR
INCIncrement Memory
INXIncrement X
INYIncrement Y
JMPJump
JSRJump to Subroutine
LDALoad Accumulator
LDXLoad X Register
LDYLoad Y Register
LSRLogical Shift Right
NOPNo Operation
ORALogical OR
PHAPush Accumulator
PHPPush Processor Status
PLAPull Accumulator
PLPPull Processor Status
ROLRotate Left
RORRotate Right
RTIReturn from Interrupt
RTSReturn from Subroutine
SBCSubtract with Carry
SECSet Carry
SEDSet Decimal
SEISet Interrupt Disable
STAStore Accumulator
STXStore X Register
STYStore Y Register
TAXTransfer A to X
TAYTransfer A to Y
TSXTransfer SP to X
TXATransfer X to A
TXSTransfer X to SP
TYATransfer Y to A

65C02 Extended Instructions (requires -65c02)

MnemonicDescription
BRABranch Always
PHXPush X Register
PHYPush Y Register
PLXPull X Register
PLYPull Y Register
STZStore Zero
TRBTest and Reset Bits
TSBTest and Set Bits
BBR0-BBR7Branch on Bit Reset
BBS0-BBS7Branch on Bit Set
RMB0-RMB7Reset Memory Bit
SMB0-SMB7Set Memory Bit
STPStop Processor
WAIWait for Interrupt

Illegal/Undocumented Instructions (requires -il)

MnemonicDescription
SLOASL + ORA
RLAROL + AND
SRELSR + EOR
RRAROR + ADC
SAXStore A AND X
LAXLoad A and X
DCPDEC + CMP
ISCINC + SBC
ANCAND + set Carry
ALRAND + LSR
ARRAND + ROR
XAATXA + AND
AXSA AND X - operand
AHXStore A AND X AND high byte
SHYStore Y AND high byte
SHXStore X AND high byte
TASTransfer A AND X to SP
LASLoad A, X, SP with SP AND memory
USBCUnofficial SBC

Sample Program

; Hello World for Commodore 64; Assemble with: pasm hello.asm -c64 -o hello.prg .ORG$0801; BASIC stub: 10 SYS 2062 .WORD NEXT_LINE .WORD10 ; Line number .BYTE$9E ; SYS token .BYTE"2062",0 ; Address as stringNEXT_LINE: .WORD0 ; End of BASIC; Main program starts hereSTART: LDX #0@LOOP: LDA MESSAGE,X BEQ DONE JSR$FFD2 ; CHROUT INX BNE @LOOPDONE: RTSMESSAGE: .BYTE"HELLO, WORLD!",13,0

Error Messages

PASM provides descriptive error messages including file name and line number:

Error: Unknown opcode 'LDZ' at hello.asm:15
Error: Opcode 'LDA' does not support addressing mode Indirect at test.asm:23
Error: Division by zero at math.asm:10
Error: Relative branch target out of range (-128 to 127) at game.asm:156

Tips and Best Practices

  1. Use local labels for loop targets to avoid name collisions
  2. Use anonymous labels (+/-) for very short forward/backward branches
  3. Organize code with includes to separate data, macros, and main logic
  4. Use macros for common code patterns
  5. Use conditional assembly to build debug and release versions from the same source
  6. Use .VAR with .DO/.WHILE for generating data tables at assembly time

PASM - Written by Paul Baxter

About

A C++20-based 6502/65c02 Assembler. Completely written in C++. This uses rules, and a recursive descent parser.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages