Skip to main content

portal_solutions_mos6502_assembler/
lib.rs

1#![no_std]
2extern crate alloc;
3
4use alloc::{collections::btree_map::BTreeMap, string::{String, ToString}, vec::Vec};
5use portal_solutions_mos6502_model::*;
6
7
8enum Data {
9    LiteralByte(u8),
10    LabelOffsetLe(String),
11    LiteralOffsetLe(Address),
12    LiteralAddressLe(Address),
13    LabelOffsetLo(String),
14    LabelOffsetHi(String),
15    LabelRelativeOffset(String),
16}
17
18struct DataAtOffset {
19    data: Data,
20    offset: Address,
21}
22
23pub struct Block {
24    cursor_offset: Address,
25    program: Vec<DataAtOffset>,
26    labels: BTreeMap<String, Address>,
27}
28
29pub trait ArgOperand {
30    type Operand: operand::Trait;
31    fn program(self, block: &mut Block);
32}
33
34impl ArgOperand for &'static str {
35    type Operand = operand::Address;
36    fn program(self, block: &mut Block) {
37        block.label_offset_le(self);
38    }
39}
40
41impl ArgOperand for String {
42    type Operand = operand::Address;
43    fn program(self, block: &mut Block) {
44        block.label_offset_le(self);
45    }
46}
47
48impl ArgOperand for Address {
49    type Operand = operand::Address;
50    fn program(self, block: &mut Block) {
51        block.literal_address_le(self);
52    }
53}
54
55impl ArgOperand for u8 {
56    type Operand = operand::Byte;
57    fn program(self, block: &mut Block) {
58        block.literal_byte(self);
59    }
60}
61
62impl ArgOperand for i8 {
63    type Operand = operand::Byte;
64    fn program(self, block: &mut Block) {
65        block.literal_byte(self as u8);
66    }
67}
68
69pub struct Addr(pub Address);
70
71impl ArgOperand for Addr {
72    type Operand = operand::Address;
73    fn program(self, block: &mut Block) {
74        block.literal_address_le(self.0);
75    }
76}
77
78// Inside 6502 "assembly" programs, rust infers int literals to
79// be i32 rather than u8. This treats i32 as u8 to prevent the
80// need for explicit type coersion in assembly programs.
81impl ArgOperand for i32 {
82    type Operand = operand::Byte;
83    fn program(self, block: &mut Block) {
84        // Allow the union of signed and unsigned byte ranges. This is to
85        // prevent mistakes such as writing 0x011011010 instead of 0b011011010.
86        assert!(self >= -128 && self <= 255, "{} is not a valid byte", self);
87        block.literal_byte((self as i8) as u8);
88    }
89}
90
91impl ArgOperand for () {
92    type Operand = operand::None;
93    fn program(self, _block: &mut Block) {}
94}
95
96pub struct LabelOffsetLo(pub &'static str);
97pub struct LabelOffsetHi(pub &'static str);
98pub struct LabelRelativeOffset(pub &'static str);
99pub struct LabelRelativeOffsetOwned(pub String);
100
101impl ArgOperand for LabelOffsetLo {
102    type Operand = operand::Byte;
103    fn program(self, block: &mut Block) {
104        block.label_offset_lo(self.0);
105    }
106}
107
108impl ArgOperand for LabelOffsetHi {
109    type Operand = operand::Byte;
110    fn program(self, block: &mut Block) {
111        block.label_offset_hi(self.0);
112    }
113}
114
115impl ArgOperand for LabelRelativeOffset {
116    type Operand = operand::Byte;
117    fn program(self, block: &mut Block) {
118        block.label_relative_offset(self.0);
119    }
120}
121
122impl ArgOperand for LabelRelativeOffsetOwned {
123    type Operand = operand::Byte;
124    fn program(self, block: &mut Block) {
125        block.label_relative_offset(self.0.as_str());
126    }
127}
128
129#[derive(Debug, Clone)]
130pub enum Error {
131    OffsetOutOfBounds,
132    UndeclaredLabel(String),
133    BranchTargetOutOfRange(String),
134}
135
136impl Block {
137    pub fn new() -> Self {
138        Self {
139            cursor_offset: 0,
140            program: Vec::new(),
141            labels: BTreeMap::new(),
142        }
143    }
144    pub fn set_offset(&mut self, offset: Address) {
145        self.cursor_offset = offset;
146    }
147    pub fn literal_byte(&mut self, byte: u8) {
148        self.program.push(DataAtOffset {
149            data: Data::LiteralByte(byte),
150            offset: self.cursor_offset,
151        });
152        self.cursor_offset = self.cursor_offset.wrapping_add(1);
153    }
154    pub fn literal_offset_le(&mut self, offset: Address) {
155        self.program.push(DataAtOffset {
156            data: Data::LiteralOffsetLe(offset),
157            offset: self.cursor_offset,
158        });
159        self.cursor_offset = self.cursor_offset.wrapping_add(2);
160    }
161    pub fn literal_address_le(&mut self, offset: Address) {
162        self.program.push(DataAtOffset {
163            data: Data::LiteralAddressLe(offset),
164            offset: self.cursor_offset,
165        });
166        self.cursor_offset = self.cursor_offset.wrapping_add(2);
167    }
168    pub fn label_offset_le<S: AsRef<str>>(&mut self, label: S) {
169        let string = label.as_ref().to_string();
170        self.program.push(DataAtOffset {
171            data: Data::LabelOffsetLe(string),
172            offset: self.cursor_offset,
173        });
174        self.cursor_offset = self.cursor_offset.wrapping_add(2);
175    }
176    pub fn label_offset_lo<S: AsRef<str>>(&mut self, label: S) {
177        let string = label.as_ref().to_string();
178        self.program.push(DataAtOffset {
179            data: Data::LabelOffsetLo(string),
180            offset: self.cursor_offset,
181        });
182        self.cursor_offset = self.cursor_offset.wrapping_add(1);
183    }
184    pub fn label_offset_hi<S: AsRef<str>>(&mut self, label: S) {
185        let string = label.as_ref().to_string();
186        self.program.push(DataAtOffset {
187            data: Data::LabelOffsetHi(string),
188            offset: self.cursor_offset,
189        });
190        self.cursor_offset = self.cursor_offset.wrapping_add(1);
191    }
192    pub fn label_relative_offset<S: AsRef<str>>(&mut self, label: S) {
193        let string = label.as_ref().to_string();
194        self.program.push(DataAtOffset {
195            data: Data::LabelRelativeOffset(string),
196            offset: self.cursor_offset,
197        });
198        self.cursor_offset = self.cursor_offset.wrapping_add(1);
199    }
200    pub fn label<S: AsRef<str>>(&mut self, s: S) {
201        let string = s.as_ref().to_string();
202        if self.labels.insert(string, self.cursor_offset).is_some() {
203            panic!("Multiple definitions of label {}", s.as_ref());
204        }
205    }
206    pub fn inst<
207        I: AssemblerInstruction,
208        A: ArgOperand<Operand = <I::AddressingMode as addressing_mode::Trait>::Operand>,
209    >(
210        &mut self,
211        instruction: I,
212        arg: A,
213    ) {
214        let _ = instruction;
215        self.literal_byte(I::opcode());
216        arg.program(self);
217    }
218    pub fn infinite_loop(&mut self) {
219        let offset = self.cursor_offset;
220        self.literal_byte(assembler_instruction::Jmp::<addressing_mode::Absolute>::opcode());
221        self.literal_offset_le(offset);
222    }
223    pub fn assemble(
224        &self,
225        base: Address,
226        size: usize,
227        buffer: &mut Vec<u8>,
228    ) -> Result<AssembledBlock, Error> {
229        let mut labels = BTreeMap::new();
230        for (label, address) in self.labels.iter() {
231            labels.insert(label.clone(), address + base);
232        }
233        buffer.resize(size, 0);
234        for &DataAtOffset { offset, ref data } in self.program.iter() {
235            match data {
236                &Data::LiteralByte(byte) => {
237                    if offset as usize >= size {
238                        return Err(Error::OffsetOutOfBounds);
239                    }
240                    buffer[offset as usize] = byte;
241                }
242                Data::LabelOffsetLe(label) => {
243                    if let Some(&label_offset) = self.labels.get(label) {
244                        if offset as usize + 1 >= size {
245                            return Err(Error::OffsetOutOfBounds);
246                        }
247                        let address = label_offset + base;
248                        buffer[offset as usize] = address::lo(address);
249                        buffer[offset as usize + 1] = address::hi(address);
250                    } else {
251                        return Err(Error::UndeclaredLabel(label.clone()));
252                    }
253                }
254                Data::LiteralOffsetLe(literal_offset) => {
255                    if offset as usize + 1 >= size {
256                        return Err(Error::OffsetOutOfBounds);
257                    }
258                    let address = literal_offset + base;
259                    buffer[offset as usize] = address::lo(address);
260                    buffer[offset as usize + 1] = address::hi(address);
261                }
262                &Data::LiteralAddressLe(address) => {
263                    buffer[offset as usize] = address::lo(address);
264                    buffer[offset as usize + 1] = address::hi(address);
265                }
266                Data::LabelOffsetLo(label) => {
267                    if let Some(&label_offset) = self.labels.get(label) {
268                        if offset as usize + 1 >= size {
269                            return Err(Error::OffsetOutOfBounds);
270                        }
271                        let address = label_offset + base;
272                        buffer[offset as usize] = address::lo(address);
273                    } else {
274                        return Err(Error::UndeclaredLabel(label.clone()));
275                    }
276                }
277                Data::LabelOffsetHi(label) => {
278                    if let Some(&label_offset) = self.labels.get(label) {
279                        if offset as usize + 1 >= size {
280                            return Err(Error::OffsetOutOfBounds);
281                        }
282                        let address = label_offset + base;
283                        buffer[offset as usize] = address::hi(address);
284                    } else {
285                        return Err(Error::UndeclaredLabel(label.clone()));
286                    }
287                }
288                Data::LabelRelativeOffset(label) => {
289                    if let Some(&label_offset) = self.labels.get(label) {
290                        let delta = label_offset as i16 - offset as i16 - 1;
291                        if delta < -128 || delta > 127 {
292                            return Err(Error::BranchTargetOutOfRange(label.clone()));
293                        }
294                        buffer[offset as usize] = (delta as i8) as u8;
295                    } else {
296                        return Err(Error::UndeclaredLabel(label.clone()));
297                    }
298                }
299            }
300        }
301        Ok(AssembledBlock { labels })
302    }
303}
304
305pub struct AssembledBlock {
306    labels: BTreeMap<String, Address>,
307}
308
309impl AssembledBlock {
310    pub fn address_of_label(&self, label: &str) -> Option<Address> {
311        self.labels.get(label).cloned()
312    }
313}