Skip to content

Commit 65ddb98

Browse files
committed
Update documentation
1 parent 4faac8c commit 65ddb98

File tree

1 file changed

+162
-26
lines changed

1 file changed

+162
-26
lines changed

README.md

Lines changed: 162 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@ A minimal 6502 assembler written in Rust for inline assembly and lightweight com
88
* Hexadecimal: `$FF`, `0xFF`, `0xFFh`
99
* Binary: `%11111111`, `0b11111111`
1010
* Decimal: `255`
11+
* Extended range: Values up to 4,294,967,295 (u32) for calculations
1112
* **Expression arithmetic:**
1213
* Addition: `$10+5`, `LABEL+1`
1314
* Subtraction: `$FF-10`, `*-2`
1415
* Multiplication: `10*2`
1516
* Division: `100/5`
1617
* Mixed formats: `$10+10+%00000101`
1718
* Operator precedence: `*,/` before `+,-`
19+
* Parentheses: `($10000-$100)`
20+
* **Low/High byte extraction:**
21+
* `<expr` - Extract low byte (bits 0-7)
22+
* `>expr` - Extract high byte (bits 8-15)
23+
* Works with any expression: `#<($10000-RAM_SIZE)`
1824
* **Constants:** Define reusable values with `LABEL = value` syntax
1925
* Simple: `SCREEN = $0400`
2026
* Expressions: `OFFSET = BASE+$10`
2127
* Current address: `HERE = *`, `NEXT = *+1`
28+
* Memory calculations: `TOP_MEM = $10000-$100`
2229
* **Modern directives:**
2330
* `.byte` - Comma-separated bytes
2431
* `.word` - 16-bit words (little-endian)
@@ -32,6 +39,7 @@ A minimal 6502 assembler written in Rust for inline assembly and lightweight com
3239
* `<$80` → force Zero Page addressing
3340
* `>$80` → force Absolute addressing
3441
* **Adaptive long-branch expansion:** Out-of-range branches automatically become `BRANCH skip` + `JMP target`
42+
* **Whitespace-friendly:** Spaces allowed in operands: `LDA #<($10000 - $100)`
3543
* **Optional listing output:** Print to stdout and/or save to file (feature-gated)
3644
* **Symbol table & address mapping helpers**
3745

@@ -44,6 +52,35 @@ LDA #255 ; Decimal
4452
LDA #%11111111 ; Binary
4553
LDA #0xFF ; Alternative hex format
4654
LDA #0b11111111 ; Alternative binary format
55+
LDA #$10000 ; Extended range for calculations (65536)
56+
```
57+
58+
### Low/High Byte Operators
59+
```asm
60+
; Extract low byte (<)
61+
LDA #<$1234 ; Low byte = $34
62+
LDA #<SCREEN ; Low byte of address
63+
64+
; Extract high byte (>)
65+
LDA #>$1234 ; High byte = $12
66+
LDA #>SCREEN ; High byte of address
67+
68+
; Common pattern: Load 16-bit address
69+
BUFFER = $2000
70+
LDA #<BUFFER ; Low byte
71+
STA $FC
72+
LDA #>BUFFER ; High byte
73+
STA $FD
74+
75+
; With expressions (note: no spaces in operands for best compatibility)
76+
LDA #<($10000-$100) ; Low byte of $FF00 = $00
77+
LDX #>($10000-$100) ; High byte of $FF00 = $FF
78+
79+
; Real-world memory calculations
80+
RAM_SIZE = $0200
81+
TOP_ADDR = $10000-RAM_SIZE ; = $FE00
82+
LDA #<TOP_ADDR ; $00
83+
LDX #>TOP_ADDR ; $FE
4784
```
4885

4986
### Expressions
@@ -54,10 +91,15 @@ LDA #10*2 ; = 20
5491
LDA #100/5 ; = 20
5592
LDA #$FF-10 ; = $F5
5693
57-
; Complex expressions
94+
; Complex expressions (spaces allowed in operands)
5895
LDA #10*2+5 ; = 25 (precedence: * before +)
96+
LDA #($FF - $10) ; = $EF (parentheses supported)
5997
LDA #$10+10+%00000101 ; Mixed formats = $1F
6098
99+
; Extended range calculations
100+
LDA #<($10000 - RAM_SIZE) ; Top-of-memory calculations
101+
LDX #>($10000 - $500 + $100) ; Complex address math
102+
61103
; With labels
62104
LDA buffer+1 ; Address of buffer + 1
63105
JMP start+3 ; Jump to start + 3 bytes
@@ -75,6 +117,16 @@ BASE = $1000
75117
OFFSET = BASE+$10 ; = $1010
76118
DOUBLE = SPRITE_X*2 ; = 200
77119
120+
; Low/High byte constants
121+
SCREEN_LO = <SCREEN ; = $00
122+
SCREEN_HI = >SCREEN ; = $04
123+
124+
; Memory calculations
125+
RAM_SIZE = $0200
126+
TOP_MEM = $10000-RAM_SIZE ; = $FE00 (top of memory minus size)
127+
RAM_LO = <TOP_MEM ; = $00
128+
RAM_HI = >TOP_MEM ; = $FE
129+
78130
; Current address (*) constants
79131
HERE = * ; Current program counter
80132
NEXT = *+1 ; Current PC + 1
@@ -146,33 +198,47 @@ buffer:
146198
LDA buffer+1 ; Label arithmetic
147199
```
148200

149-
### Complete Example
201+
### Complete Example with Memory Calculations
150202
```asm
151-
*=$0800
203+
*=$0801
152204
153-
; Constants
154-
SCREEN = $0400
155-
CHAR_CODE = 65 ; 'A'
205+
; BASIC stub: SYS 2061
206+
.byte $0B,$08,$0A,$00,$9E,$32,$30,$36,$31,$00,$00,$00
207+
208+
; Zero page pointers
209+
LZSA_SRC = $FC ; Source pointer (2 bytes)
210+
LZSA_DST = $FE ; Destination pointer (2 bytes)
156211
157-
; Data section
158-
message:
159-
.string "HELLO WORLD"
212+
; Calculate sizes
213+
RAM_DATA_SIZE = $0150
214+
RELOCATED_SIZE = $00BD
215+
216+
; Top-of-memory calculations
217+
TOP_MEM = $10000
218+
RAM_BLOCK = TOP_MEM - RAM_DATA_SIZE ; = $FEB0
219+
RELOC_ADDR = RAM_BLOCK + RELOCATED_SIZE ; = $FF6D
220+
221+
start:
222+
; Load source address (little-endian)
223+
LDA #<data_block
224+
STA LZSA_SRC
225+
LDA #>data_block
226+
STA LZSA_SRC+1
160227
161-
sprite_data:
162-
.byte $00,$3C,$42,$42,$7E,$42,$42,$00
228+
; Load destination at top of memory
229+
LDA #<RAM_BLOCK
230+
STA LZSA_DST
231+
LDA #>RAM_BLOCK
232+
STA LZSA_DST+1
163233
164-
lookup_table:
165-
.word $1000,$2000,$3000,$4000
234+
JSR decompress
235+
RTS
166236
167-
; Code section
168-
start:
169-
LDX #0
170-
loop:
171-
LDA message,X
172-
STA SCREEN,X
173-
INX
174-
CPX #11
175-
BNE loop
237+
data_block:
238+
.incbin "compressed.lzsa"
239+
240+
decompress:
241+
; Decompression routine here...
176242
RTS
177243
```
178244

@@ -251,6 +317,43 @@ fn main() -> Result<(), asm6502::AsmError> {
251317
}
252318
```
253319

320+
### Using Low/High Byte Operators
321+
322+
```rust
323+
use asm6502::Assembler6502;
324+
325+
fn main() -> Result<(), asm6502::AsmError> {
326+
let mut asm = Assembler6502::new();
327+
328+
let code = r#"
329+
*=$0800
330+
331+
; Define 16-bit address
332+
SCREEN = $D800
333+
334+
; Load address using low/high byte extraction
335+
load_address:
336+
LDA #<SCREEN ; Low byte = $00
337+
STA $FB
338+
LDA #>SCREEN ; High byte = $D8
339+
STA $FC
340+
341+
; Top-of-memory calculations
342+
RAM_SIZE = $0200
343+
TOP_ADDR = $10000-RAM_SIZE ; = $FE00
344+
345+
LDA #<TOP_ADDR ; = $00
346+
LDX #>TOP_ADDR ; = $FE
347+
RTS
348+
"#;
349+
350+
let bytes = asm.assemble_bytes(code)?;
351+
println!("Assembled {} bytes", bytes.len());
352+
353+
Ok(())
354+
}
355+
```
356+
254357
### Using New Directives
255358

256359
```rust
@@ -301,16 +404,23 @@ fn main() -> Result<(), asm6502::AsmError> {
301404
asm.set_origin(0x1000);
302405

303406
let code = r#"
304-
; Constants
407+
; Constants with u32 calculations
305408
BASE = $2000
306409
OFFSET = BASE+$100
307410
COUNT = 10*2
308411
412+
; Top-of-memory calculations
413+
RAM_SIZE = $0500
414+
TOP_MEM = $10000-RAM_SIZE ; = $FB00
415+
309416
; Code with expressions
310417
start:
311418
LDA #COUNT
312419
STA BASE
313-
LDA OFFSET+5
420+
LDA #<TOP_MEM
421+
STA $FC
422+
LDA #>TOP_MEM
423+
STA $FD
314424
JMP start
315425
"#;
316426

@@ -388,7 +498,9 @@ The test suite covers:
388498
5. Constants (`LABEL = value`)
389499
6. Current address usage (`*`)
390500
7. New directives (`.byte`, `.word`, `.string`)
391-
8. Complete 6502 program (all addressing modes)
501+
8. U32 support and memory calculations
502+
9. Low/high byte operators
503+
10. Complete 6502 program (all addressing modes)
392504

393505
## API Overview
394506

@@ -445,6 +557,19 @@ fn save_listing(&self, items: &[Item], filename: &str) -> io::Result<()>
445557
| `.incbin` | `.incbin "file"` | Include binary file | `.incbin "data.bin"` |
446558
| `LABEL =` | `CONST = $42` | Define constant | `SCREEN = $0400` |
447559

560+
## Operator Reference
561+
562+
| Operator | Description | Example | Result |
563+
|----------|-------------|---------|--------|
564+
| `+` | Addition | `$10+5` | `$15` |
565+
| `-` | Subtraction | `$FF-10` | `$F5` |
566+
| `*` | Multiplication | `10*2` | `20` |
567+
| `/` | Division | `100/5` | `20` |
568+
| `<` | Low byte (bits 0-7) | `<$1234` | `$34` |
569+
| `>` | High byte (bits 8-15) | `>$1234` | `$12` |
570+
| `*` | Current address | `LABEL=*` | Current PC |
571+
| `()` | Grouping | `($10+$20)*2` | `$60` |
572+
448573
## Building & Docs
449574

450575
Build the workspace:
@@ -478,6 +603,7 @@ This assembler is designed for:
478603
- **JIT compilation**: Runtime generation of 6502 code
479604
- **Emulator testing**: Dynamic test case generation
480605
- **Educational tools**: Interactive 6502 learning
606+
- **Real-world 6502 projects**: With extended u32 support for memory calculations
481607
- **Simplicity**: Minimal dependencies, clear code structure
482608

483609
It intentionally **does not** include:
@@ -497,6 +623,9 @@ Constants and expressions are evaluated in-order, requiring definitions before u
497623
- **Word endianness:** `.word` directive outputs little-endian (6502 native format)
498624
- **String encoding:** `.string` uses standard ASCII encoding
499625
- **Binary inclusion:** `.incbin` reads files relative to working directory
626+
- **U32 support:** Internal calculations use 32-bit unsigned integers, automatically wrapping to 16-bit for addresses
627+
- **Memory calculations:** Supports expressions like `$10000-RAM_SIZE` for top-of-memory calculations
628+
- **Whitespace in operands:** Spaces are allowed in operands (split on first whitespace only)
500629
- **Current address arithmetic:**
501630
- `LABEL = *` captures the current program counter
502631
- `LABEL = *+n` useful for calculating addresses of upcoming instructions
@@ -505,7 +634,14 @@ Constants and expressions are evaluated in-order, requiring definitions before u
505634

506635
## Version History
507636

508-
### v2.0 (Current)
637+
### v2.1 (Current)
638+
- Added u32 expression support for values > 65535 (e.g., `$10000`)
639+
- Added low/high byte operators (`<` and `>`)
640+
- Improved lexer to allow whitespace in operands
641+
- Enhanced long branch expansion algorithm
642+
- Better support for real-world memory calculations
643+
644+
### v2.0
509645
- Added `.byte`, `.word`, `.string`, and `.incbin` directives
510646
- Improved listing output for data directives
511647
- Enhanced address mapping support

0 commit comments

Comments
 (0)