11# Assembler6502
22
3- A minimal 6502 assembler written in Rust. It generates raw machine code from 6502 assembly source and, optionally, prints a human‑ readable assembly listing when the ` listing ` feature is enabled.
3+ A minimal 6502 assembler written in Rust for inline assembly and lightweight compilation . It generates raw machine code from 6502 assembly source and optionally prints a human- readable assembly listing when the ` listing ` feature is enabled.
44
55## Features
66
7- * Hex‑only syntax (` $ ` prefix for numbers)
8- * Zero Page vs Absolute auto‑detection, with explicit overrides using operand prefixes:
7+ * ** Multiple number formats:**
8+ * Hexadecimal: ` $FF ` , ` 0xFF ` , ` 0xFFh `
9+ * Binary: ` %11111111 ` , ` 0b11111111 `
10+ * Decimal: ` 255 `
11+ * ** Expression arithmetic:**
12+ * Addition: ` $10+5 ` , ` LABEL+1 `
13+ * Subtraction: ` $FF-10 ` , ` *-2 `
14+ * Multiplication: ` 10*2 `
15+ * Division: ` 100/5 `
16+ * Mixed formats: ` $10+10+%00000101 `
17+ * Operator precedence: ` *,/ ` before ` +,- `
18+ * ** Constants:** Define reusable values with ` LABEL = value ` syntax
19+ * Simple: ` SCREEN = $0400 `
20+ * Expressions: ` OFFSET = BASE+$10 `
21+ * Current address: ` HERE = * ` , ` NEXT = *+1 `
22+ * ** Label arithmetic:** Use labels in expressions (` LDA buffer+1 ` , ` JMP start+3 ` )
23+ * ** Current address symbol:** ` * ` represents the current program counter
24+ * ** Addressing mode control:**
25+ * Auto-detection of Zero Page vs Absolute
26+ * Explicit override with operand prefixes:
27+ * ` <$80 ` → force Zero Page addressing
28+ * ` >$80 ` → force Absolute addressing
29+ * ** Adaptive long-branch expansion:** Out-of-range branches automatically become ` BRANCH skip ` + ` JMP target `
30+ * ** Optional listing output:** Print to stdout and/or save to file (feature-gated)
31+ * ** Symbol table & address mapping helpers**
32+
33+ ## Syntax Guide
34+
35+ ### Number Formats
36+ ``` asm
37+ LDA #$FF ; Hexadecimal
38+ LDA #255 ; Decimal
39+ LDA #%11111111 ; Binary
40+ LDA #0xFF ; Alternative hex format
41+ LDA #0b11111111 ; Alternative binary format
42+ ```
43+
44+ ### Expressions
45+ ``` asm
46+ ; Simple arithmetic
47+ LDA #$02+1 ; = $03
48+ LDA #10*2 ; = 20
49+ LDA #100/5 ; = 20
50+ LDA #$FF-10 ; = $F5
51+
52+ ; Complex expressions
53+ LDA #10*2+5 ; = 25 (precedence: * before +)
54+ LDA #$10+10+%00000101 ; Mixed formats = $1F
55+
56+ ; With labels
57+ LDA buffer+1 ; Address of buffer + 1
58+ JMP start+3 ; Jump to start + 3 bytes
59+ ```
60+
61+ ### Constants
62+ ``` asm
63+ ; Simple constants
64+ SCREEN = $0400
65+ SPRITE_X = 100
66+ MAX_LIVES = 3
67+
68+ ; Expression constants
69+ BASE = $1000
70+ OFFSET = BASE+$10 ; = $1010
71+ DOUBLE = SPRITE_X*2 ; = 200
72+
73+ ; Current address
74+ HERE = * ; Current program counter
75+ NEXT = *+1 ; Current PC + 1
76+
77+ ; Usage
78+ LDA #SPRITE_X
79+ STA SCREEN
80+ JMP HERE
81+ ```
82+
83+ ### Directives
84+ ``` asm
85+ *=$0800 ; Set origin (ORG)
86+ DCB $01 $02 $03 ; Define bytes
87+ ```
88+
89+ ### Labels
90+ ``` asm
91+ start: ; Define label
92+ LDA #$42
93+ JMP start ; Forward/backward references work
994
10- * ` <$80 ` → force Zero Page addressing
11- * ` >$80 ` → force Absolute addressing
12- * Adaptive long‑branch expansion (out‑of‑range branches become ` BRANCH skip ` + ` JMP target ` )
13- * Optional listing output (print to stdout and/or save to file)
14- * Symbol table & address mapping helpers
95+ buffer:
96+ DCB $00 $00
97+ LDA buffer+1 ; Label arithmetic
98+ ```
1599
16100## Workspace Layout
17101
18102This repository is a Cargo ** workspace** with a library crate and a runnable example crate:
19103
20104```
21- asm6502/ # ← workspace root (this dir)
105+ asm6502/ # workspace root
22106├─ Cargo.toml # [workspace] only
23107├─ asm6502/ # library crate (the assembler)
24108│ ├─ Cargo.toml
25109│ └─ src/
26- │ └─ Assembler6502.rs # library source (or use src/lib.rs if you prefer)
27- └─ example/ # example binary crate (demo / tests)
110+ │ ├─ lib.rs
111+ │ ├─ assembler.rs
112+ │ ├─ opcodes.rs
113+ │ ├─ symbol.rs
114+ │ ├─ error.rs
115+ │ ├─ addressing.rs
116+ │ ├─ parser/
117+ │ │ ├─ mod.rs
118+ │ │ ├─ lexer.rs
119+ │ │ ├─ expression.rs
120+ │ │ └─ number.rs
121+ │ └─ eval/
122+ │ ├─ mod.rs
123+ │ └─ expression.rs
124+ └─ example/ # example binary crate (tests/demo)
28125 ├─ Cargo.toml
29126 └─ src/
30127 └─ main.rs
@@ -33,14 +130,7 @@ asm6502/ # ← workspace root (this dir)
33130### Library crate ` asm6502 `
34131
35132* Exposes the public API (` Assembler6502 ` , ` AsmError ` , etc.)
36- * Listing helpers are gated behind the Cargo feature ` listing ` .
37- * If you keep the file name ` Assembler6502.rs ` , ensure your ` asm6502/Cargo.toml ` contains:
38-
39- ``` toml
40- [lib ]
41- name = " asm6502"
42- path = " src/Assembler6502.rs"
43- ```
133+ * Listing helpers are gated behind the Cargo feature ` listing `
44134
45135## Using the library from another project
46136
@@ -57,13 +147,74 @@ Optionally enable the listing feature:
57147asm6502 = { git = " https://github.com/tommyo123/asm6502" , features = [" listing" ] }
58148```
59149
60- Minimal example:
150+ ### Basic Example
151+
152+ ``` rust
153+ use asm6502 :: Assembler6502 ;
154+
155+ fn main () -> Result <(), asm6502 :: AsmError > {
156+ let mut asm = Assembler6502 :: new ();
157+
158+ let code = r # "
159+ *=$0800
160+ SCREEN = $0400
161+
162+ start:
163+ LDA #$42
164+ STA SCREEN
165+ JMP start
166+ " # ;
167+
168+ let bytes = asm . assemble_bytes (code )? ;
169+ println! (" Assembled {} bytes" , bytes . len ());
170+
171+ Ok (())
172+ }
173+ ```
174+
175+ ### Advanced Example with Expressions
176+
177+ ``` rust
178+ use asm6502 :: Assembler6502 ;
179+
180+ fn main () -> Result <(), asm6502 :: AsmError > {
181+ let mut asm = Assembler6502 :: new ();
182+ asm . set_origin (0x1000 );
183+
184+ let code = r # "
185+ ; Constants
186+ BASE = $2000
187+ OFFSET = BASE+$100
188+ COUNT = 10*2
189+
190+ ; Code with expressions
191+ start:
192+ LDA #COUNT
193+ STA BASE
194+ LDA OFFSET+5
195+ JMP start
196+ " # ;
197+
198+ let (bytes , symbols ) = asm . assemble_with_symbols (code )? ;
199+
200+ println! (" Assembled {} bytes" , bytes . len ());
201+ println! (" \ n Symbols:" );
202+ for (name , addr ) in symbols . iter () {
203+ println! (" {} = ${:04X}" , name , addr );
204+ }
205+
206+ Ok (())
207+ }
208+ ```
209+
210+ ### With Listing (feature-gated)
61211
62212``` rust
63213use asm6502 :: Assembler6502 ;
64214
65- fn main () {
215+ fn main () -> Result <(), asm6502 :: AsmError > {
66216 let mut asm = Assembler6502 :: new ();
217+
67218 let code = r # "
68219 *=$0800
69220 start:
@@ -72,66 +223,80 @@ fn main() {
72223 RTS
73224 " # ;
74225
75- let machine = asm . assemble_bytes (code ). unwrap ();
76- println! (" Assembled {} bytes" , machine . len ());
226+ #[cfg(feature = " listing" )]
227+ {
228+ let (bytes , items ) = asm . assemble_full (code )? ;
229+ asm . print_assembly_listing (& items );
230+ asm . save_listing (& items , " output.lst" )? ;
231+ }
232+
233+ #[cfg(not(feature = " listing" ))]
234+ {
235+ let bytes = asm . assemble_bytes (code )? ;
236+ }
237+
238+ Ok (())
77239}
78240```
79241
80242## Example crate (this repository)
81243
82- The workspace includes a runnable ` example ` crate demonstrating the API.
244+ The workspace includes a comprehensive test suite demonstrating all features:
83245
84246### Run the example
85247
86248From the workspace root:
87249
88250``` bash
89- # default run (no listing)
251+ # Run test suite
90252cargo run -p asm6502-example
91253
92- # run with listing enabled in the example (Option A: feature forwarding)
254+ # Run with listing enabled
93255cargo run -p asm6502-example --features listing
94256```
95257
96- ### Option A: feature forwarding (recommended)
97-
98- ` example/Cargo.toml ` forwards its own ` listing ` feature to the library's feature:
258+ The test suite covers:
259+ 1 . Number formats (hex, decimal, binary)
260+ 2 . Expression arithmetic
261+ 3 . Label arithmetic
262+ 4 . Mixed number formats
263+ 5 . Constants (` LABEL = value ` )
264+ 6 . Current address usage (` * ` )
265+ 7 . Complete 6502 program (all addressing modes)
99266
100- ``` toml
101- [package ]
102- name = " asm6502-example"
103- version = " 1.0.0"
104- edition = " 2021"
105-
106- [dependencies ]
107- asm6502 = { path = " ../asm6502" }
108-
109- [features ]
110- default = []
111- listing = [" asm6502/listing" ] # forward the feature
112- ```
267+ ## API Overview
113268
114- In ` example/src/main.rs ` you can use:
269+ ### Core Methods
115270
116271``` rust
117- #[cfg(feature = " listing" )]
118- { /* print or save listing */ }
119- ```
272+ // Simple assembly
273+ fn assemble_bytes (& mut self , src : & str ) -> Result <Vec <u8 >, AsmError >
120274
121- ### Option B: depend directly on the lib feature (no example-local features)
275+ // Assembly with symbol table
276+ fn assemble_with_symbols (& mut self , src : & str )
277+ -> Result <(Vec <u8 >, HashMap <String , u16 >), AsmError >
122278
123- In ` example/Cargo.toml ` :
279+ // Full assembly with items (for listing)
280+ fn assemble_full (& mut self , src : & str )
281+ -> Result <(Vec <u8 >, Vec <Item >), AsmError >
124282
125- ``` toml
126- [dependencies ]
127- asm6502 = { path = " ../asm6502" , features = [" listing" ] }
283+ // Configuration
284+ fn set_origin (& mut self , addr : u16 )
285+ fn reset (& mut self )
286+
287+ // Symbol inspection
288+ fn symbols (& self ) -> & HashMap <String , u16 >
289+ fn lookup (& self , name : & str ) -> Option <u16 >
128290```
129291
130- And in ` example/src/main.rs ` gate on the dependency feature:
292+ ### Listing Methods ( feature - gated )
131293
132294```rust
133- #[cfg(feature = " asm6502/listing" )]
134- { /* print or save listing */ }
295+ #[cfg (feature = " listing" )]
296+ fn print_assembly_listing (& self , items : & [Item ])
297+
298+ #[cfg (feature = " listing" )]
299+ fn save_listing (& self , items : & [Item ], filename : & str ) -> io :: Result <()>
135300```
136301
137302## Building & Docs
@@ -148,13 +313,43 @@ Build only the library:
148313cargo build - p asm6502
149314```
150315
316+ Run tests :
317+
318+ ```bash
319+ cargo test
320+ ```
321+
151322Generate local API docs :
152323
153324```bash
154325cargo doc -- open
155326```
156327
328+ ## Design Philosophy
329+
330+ This assembler is designed for :
331+ - * * Inline assembly ** : Quick compilation of small code snippets
332+ - * * JIT compilation ** : Runtime generation of 6502 code
333+ - * * Emulator testing ** : Dynamic test case generation
334+ - * * Educational tools ** : Interactive 6502 learning
335+ - * * Simplicity ** : Minimal dependencies , clear code structure
336+
337+ It intentionally * * does not ** include :
338+ - Multi - file projects or linking
339+ - Macro systems
340+ - Complex multi - pass constant resolution
341+ - Object file formats
342+
343+ Constants and expressions are evaluated in - order , requiring definitions before use (except labels which support forward references ).
344+
345+ ## Notes
346+
347+ - * * Forward references : ** Labels support forward references , constants do not
348+ - * * Best practice : ** Define constants at the top of your source
349+ - * * Expression evaluation : ** Left - to - right with standard precedence (`* `, `/ ` before `+ `, `- `)
350+ - * * Branch range : ** Automatic long - branch expansion for out - of - range branches
351+
157352## License
158353
159354This project is released under [The Unlicense ](LICENSE ).
160- It is free and unencumbered software released into the public domain.
355+ It is free and unencumbered software released into the public domain .
0 commit comments