Skip to content

Commit 590d285

Browse files
committed
Updated documentation
1 parent c481bab commit 590d285

File tree

1 file changed

+250
-55
lines changed

1 file changed

+250
-55
lines changed

README.md

Lines changed: 250 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,127 @@
11
# Assembler6502
22

3-
A minimal 6502 assembler written in Rust. It generates raw machine code from 6502 assembly source and, optionally, prints a humanreadable 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

18102
This 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:
57147
asm6502 = { 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!("\nSymbols:");
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
63213
use 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

86248
From the workspace root:
87249

88250
```bash
89-
# default run (no listing)
251+
# Run test suite
90252
cargo run -p asm6502-example
91253

92-
# run with listing enabled in the example (Option A: feature forwarding)
254+
# Run with listing enabled
93255
cargo 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:
148313
cargo build -p asm6502
149314
```
150315

316+
Run tests:
317+
318+
```bash
319+
cargo test
320+
```
321+
151322
Generate local API docs:
152323

153324
```bash
154325
cargo 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

159354
This 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

Comments
 (0)