c64_assembler/builder/
application.rs

1use std::collections::HashMap;
2
3use crate::{
4    memory::{
5        define::{Define, Value},
6        label::AddressReference,
7        Address, ZeroPage,
8    },
9    validator::AssemblerResult,
10    Application, Module,
11};
12
13use super::finalize::finalize;
14
15#[derive(Clone)]
16pub struct ApplicationBuilder {
17    application: Application,
18}
19
20impl Default for ApplicationBuilder {
21    fn default() -> Self {
22        Self {
23            application: Application {
24                name: String::default(),
25                entry_point: 0x0800,
26                modules: vec![],
27                defines: vec![],
28                address_lookup: HashMap::default(),
29            },
30        }
31    }
32}
33
34impl ApplicationBuilder {
35    /// Set the name of the application.
36    ///
37    /// Is used in comments when exporting to a dasm source using [crate::generator::DasmGenerator]
38    ///
39    /// ```
40    /// use c64_assembler::builder::ApplicationBuilder;
41    /// let application = ApplicationBuilder::default()
42    ///     .name("My application")
43    ///     .build();
44    /// ```
45    pub fn name(&mut self, name: &str) -> &mut Self {
46        self.application.name = name.to_string();
47        self
48    }
49
50    /// Change the entry point of the application.
51    ///
52    /// NOTE: When not set to 0x0800 [crate::builder::instruction::InstructionBuilder::add_basic_header]
53    /// cannot be used.
54    ///
55    /// Default entry point is set to 0x0800.
56    ///
57    /// ```
58    /// use c64_assembler::builder::ApplicationBuilder;
59    /// let application = ApplicationBuilder::default()
60    ///     .entry_point(0x0800)
61    ///     .build();
62    /// ```
63    pub fn entry_point(&mut self, entry_point: Address) -> &mut Self {
64        self.application.entry_point = entry_point;
65        self
66    }
67
68    /// Define a static address.
69    ///
70    /// When using instructions each address needs to be accessed by its name. This function
71    /// allows to add a new named address. There is no difference between zeropaged and regular
72    /// addresses. When building the instruction stream the actual address will decide which
73    /// opcode is used.
74    ///
75    /// ```
76    /// use c64_assembler::builder::ApplicationBuilder;
77    /// let application = ApplicationBuilder::default()
78    ///     .define_address("VIC_BORDER_COLOR", 0xD020)
79    ///     .define_address("ZEROPAGE_FE", 0xFE)
80    ///     .build();
81    /// ```
82    pub fn define_address(&mut self, name: &str, address: Address) -> &mut Self {
83        self.application.address_lookup.insert(name.to_string(), address);
84        if address.is_zeropage() {
85            self.application
86                .defines
87                .push(Define::new(name, Value::Zeropage(address)));
88        } else {
89            self.application
90                .defines
91                .push(Define::new(name, Value::Address(address)));
92        }
93        self
94    }
95
96    /// Add the address defines useful when using the VIC2.
97    ///
98    /// | Address Name                    | Physical Address | Description                                      |
99    /// |---------------------------------|-----------------|--------------------------------------------------|
100    /// | VIC2_BASE                       | 0xD000          | Base address for VIC registers                  |
101    /// | VIC2_SPRITE_0_X                 | 0xD000          | X coordinate of sprite 0                        |
102    /// | VIC2_SPRITE_0_Y                 | 0xD001          | Y coordinate of sprite 0                        |
103    /// | VIC2_SPRITE_1_X                 | 0xD002          | X coordinate of sprite 1                        |
104    /// | VIC2_SPRITE_1_Y                 | 0xD003          | Y coordinate of sprite 1                        |
105    /// | VIC2_SPRITE_2_X                 | 0xD004          | X coordinate of sprite 2                        |
106    /// | VIC2_SPRITE_2_Y                 | 0xD005          | Y coordinate of sprite 2                        |
107    /// | VIC2_SPRITE_3_X                 | 0xD006          | X coordinate of sprite 3                        |
108    /// | VIC2_SPRITE_3_Y                 | 0xD007          | Y coordinate of sprite 3                        |
109    /// | VIC2_SPRITE_4_X                 | 0xD008          | X coordinate of sprite 4                        |
110    /// | VIC2_SPRITE_4_Y                 | 0xD009          | Y coordinate of sprite 4                        |
111    /// | VIC2_SPRITE_5_X                 | 0xD00A          | X coordinate of sprite 5                        |
112    /// | VIC2_SPRITE_5_Y                 | 0xD00B          | Y coordinate of sprite 5                        |
113    /// | VIC2_SPRITE_6_X                 | 0xD00C          | X coordinate of sprite 6                        |
114    /// | VIC2_SPRITE_6_Y                 | 0xD00D          | Y coordinate of sprite 6                        |
115    /// | VIC2_SPRITE_7_X                 | 0xD00E          | X coordinate of sprite 7                        |
116    /// | VIC2_SPRITE_7_Y                 | 0xD00F          | Y coordinate of sprite 7                        |
117    /// | VIC2_SPRITE_X_MSB               | 0xD010          | Most significant bit for sprite X coordinates   |
118    /// | VIC2_CONTROL_1                  | 0xD011          | Screen control register 1                       |
119    /// | VIC2_RASTER                     | 0xD012          | Raster line position                            |
120    /// | VIC2_LIGHT_PEN_X                | 0xD013          | Light pen X position                           |
121    /// | VIC2_LIGHT_PEN_Y                | 0xD014          | Light pen Y position                           |
122    /// | VIC2_SPRITE_ENABLE              | 0xD015          | Enables sprites                                |
123    /// | VIC2_CONTROL_2                  | 0xD016          | Screen control register 2                      |
124    /// | VIC2_SPRITE_EXPAND_X            | 0xD017          | Expands sprites horizontally                   |
125    /// | VIC2_MEMORY_SETUP               | 0xD018          | VIC memory setup                               |
126    /// | VIC2_IRQ_STATUS                 | 0xD019          | Interrupt request status                       |
127    /// | VIC2_IRQ_ENABLE                 | 0xD01A          | Interrupt request enable                       |
128    /// | VIC2_SPRITE_PRIORITY            | 0xD01B          | Sprite priority over background                |
129    /// | VIC2_SPRITE_MULTICOLOR          | 0xD01C          | Enables multicolor mode for sprites            |
130    /// | VIC2_SPRITE_EXPAND_Y            | 0xD01D          | Expands sprites vertically                     |
131    /// | VIC2_SPRITE_COLLISION           | 0xD01E          | Sprite-to-sprite collision detection           |
132    /// | VIC2_SPRITE_BG_COLLISION        | 0xD01F          | Sprite-to-background collision detection       |
133    /// | VIC2_BORDER_COLOR               | 0xD020          | Border color                                   |
134    /// | VIC2_BACKGROUND_COLOR           | 0xD021          | Background color                               |
135    /// | VIC2_BACKGROUND_COLOR_0         | 0xD021          | Background color 0                             |
136    /// | VIC2_BACKGROUND_COLOR_1         | 0xD022          | Background color 1                             |
137    /// | VIC2_BACKGROUND_COLOR_2         | 0xD023          | Background color 2                             |
138    /// | VIC2_BACKGROUND_COLOR_3         | 0xD024          | Background color 3                             |
139    /// | VIC2_SPRITE_MULTICOLOR_0        | 0xD025          | Multicolor mode color 0 for sprites           |
140    /// | VIC2_SPRITE_MULTICOLOR_1        | 0xD026          | Multicolor mode color 1 for sprites           |
141    /// | VIC2_SPRITE_0_COLOR             | 0xD027          | Color of sprite 0                              |
142    /// | VIC2_SPRITE_1_COLOR             | 0xD028          | Color of sprite 1                              |
143    /// | VIC2_SPRITE_2_COLOR             | 0xD029          | Color of sprite 2                              |
144    /// | VIC2_SPRITE_3_COLOR             | 0xD02A          | Color of sprite 3                              |
145    /// | VIC2_SPRITE_4_COLOR             | 0xD02B          | Color of sprite 4                              |
146    /// | VIC2_SPRITE_5_COLOR             | 0xD02C          | Color of sprite 5                              |
147    /// | VIC2_SPRITE_6_COLOR             | 0xD02D          | Color of sprite 6                              |
148    /// | VIC2_SPRITE_7_COLOR             | 0xD02E          | Color of sprite 7                              |
149    ///
150    /// ```
151    /// use c64_assembler::builder::ApplicationBuilder;
152    /// let application = ApplicationBuilder::default()
153    ///     .include_vic2_defines()
154    ///     .build();
155    /// ```
156    pub fn include_vic2_defines(&mut self) -> &mut Self {
157        self.define_address("VIC2_BASE", 0xD000)
158            .define_address("VIC2_SPRITE_0_X", 0xD000)
159            .define_address("VIC2_SPRITE_0_Y", 0xD001)
160            .define_address("VIC2_SPRITE_1_X", 0xD002)
161            .define_address("VIC2_SPRITE_1_Y", 0xD003)
162            .define_address("VIC2_SPRITE_2_X", 0xD004)
163            .define_address("VIC2_SPRITE_2_Y", 0xD005)
164            .define_address("VIC2_SPRITE_3_X", 0xD006)
165            .define_address("VIC2_SPRITE_3_Y", 0xD007)
166            .define_address("VIC2_SPRITE_4_X", 0xD008)
167            .define_address("VIC2_SPRITE_4_Y", 0xD009)
168            .define_address("VIC2_SPRITE_5_X", 0xD00A)
169            .define_address("VIC2_SPRITE_5_Y", 0xD00B)
170            .define_address("VIC2_SPRITE_6_X", 0xD00C)
171            .define_address("VIC2_SPRITE_6_Y", 0xD00D)
172            .define_address("VIC2_SPRITE_7_X", 0xD00E)
173            .define_address("VIC2_SPRITE_7_Y", 0xD00F)
174            .define_address("VIC2_SPRITE_X_MSB", 0xD010)
175            .define_address("VIC2_CONTROL_1", 0xD011)
176            .define_address("VIC2_RASTER", 0xD012)
177            .define_address("VIC2_LIGHT_PEN_X", 0xD013)
178            .define_address("VIC2_LIGHT_PEN_Y", 0xD014)
179            .define_address("VIC2_SPRITE_ENABLE", 0xD015)
180            .define_address("VIC2_CONTROL_2", 0xD016)
181            .define_address("VIC2_SPRITE_EXPAND_X", 0xD017)
182            .define_address("VIC2_MEMORY_SETUP", 0xD018)
183            .define_address("VIC2_IRQ_STATUS", 0xD019)
184            .define_address("VIC2_IRQ_ENABLE", 0xD01A)
185            .define_address("VIC2_SPRITE_PRIORITY", 0xD01B)
186            .define_address("VIC2_SPRITE_MULTICOLOR", 0xD01C)
187            .define_address("VIC2_SPRITE_EXPAND_Y", 0xD01D)
188            .define_address("VIC2_SPRITE_COLLISION", 0xD01E)
189            .define_address("VIC2_SPRITE_BG_COLLISION", 0xD01F)
190            .define_address("VIC2_BORDER_COLOR", 0xD020)
191            .define_address("VIC2_BACKGROUND_COLOR", 0xD021)
192            .define_address("VIC2_BACKGROUND_COLOR_0", 0xD021)
193            .define_address("VIC2_BACKGROUND_COLOR_1", 0xD022)
194            .define_address("VIC2_BACKGROUND_COLOR_2", 0xD023)
195            .define_address("VIC2_BACKGROUND_COLOR_3", 0xD024)
196            .define_address("VIC2_SPRITE_MULTICOLOR_0", 0xD025)
197            .define_address("VIC2_SPRITE_MULTICOLOR_1", 0xD026)
198            .define_address("VIC2_SPRITE_0_COLOR", 0xD027)
199            .define_address("VIC2_SPRITE_1_COLOR", 0xD028)
200            .define_address("VIC2_SPRITE_2_COLOR", 0xD029)
201            .define_address("VIC2_SPRITE_3_COLOR", 0xD02A)
202            .define_address("VIC2_SPRITE_4_COLOR", 0xD02B)
203            .define_address("VIC2_SPRITE_5_COLOR", 0xD02C)
204            .define_address("VIC2_SPRITE_6_COLOR", 0xD02D)
205            .define_address("VIC2_SPRITE_7_COLOR", 0xD02E)
206    }
207
208    /// Add the address defines useful when using the SID.
209    ///
210    /// | Address Name                  | Physical Address | Description                                    |
211    /// |-------------------------------|-----------------|-------------------------------------------------|
212    /// | SID_BASE                      | 0xD400          | Base address for SID registers                  |
213    /// | SID_VOICE_1_FREQ_LO           | 0xD400          | Voice 1 frequency (low byte)                    |
214    /// | SID_VOICE_1_FREQ_HI           | 0xD401          | Voice 1 frequency (high byte)                   |
215    /// | SID_VOICE_1_PW_LO             | 0xD402          | Voice 1 pulse width (low byte)                  |
216    /// | SID_VOICE_1_PW_HI             | 0xD403          | Voice 1 pulse width (high byte)                 |
217    /// | SID_VOICE_1_CTRL              | 0xD404          | Voice 1 control register                        |
218    /// | SID_VOICE_1_AD                | 0xD405          | Voice 1 attack/decay settings                   |
219    /// | SID_VOICE_1_SR                | 0xD406          | Voice 1 sustain/release settings                |
220    /// | SID_VOICE_2_FREQ_LO           | 0xD407          | Voice 2 frequency (low byte)                    |
221    /// | SID_VOICE_2_FREQ_HI           | 0xD408          | Voice 2 frequency (high byte)                   |
222    /// | SID_VOICE_2_PW_LO             | 0xD409          | Voice 2 pulse width (low byte)                  |
223    /// | SID_VOICE_2_PW_HI             | 0xD40A          | Voice 2 pulse width (high byte)                 |
224    /// | SID_VOICE_2_CTRL              | 0xD40B          | Voice 2 control register                        |
225    /// | SID_VOICE_2_AD                | 0xD40C          | Voice 2 attack/decay settings                   |
226    /// | SID_VOICE_2_SR                | 0xD40D          | Voice 2 sustain/release settings                |
227    /// | SID_VOICE_3_FREQ_LO           | 0xD40E          | Voice 3 frequency (low byte)                    |
228    /// | SID_VOICE_3_FREQ_HI           | 0xD40F          | Voice 3 frequency (high byte)                   |
229    /// | SID_VOICE_3_PW_LO             | 0xD410          | Voice 3 pulse width (low byte)                  |
230    /// | SID_VOICE_3_PW_HI             | 0xD411          | Voice 3 pulse width (high byte)                 |
231    /// | SID_VOICE_3_CTRL              | 0xD412          | Voice 3 control register                        |
232    /// | SID_VOICE_3_AD                | 0xD413          | Voice 3 attack/decay settings                   |
233    /// | SID_VOICE_3_SR                | 0xD414          | Voice 3 sustain/release settings                |
234    /// | SID_FILTER_CUTOFF_LO          | 0xD415          | Filter cutoff frequency (low byte)              |
235    /// | SID_FILTER_CUTOFF_HI          | 0xD416          | Filter cutoff frequency (high byte)             |
236    /// | SID_FILTER_CTRL               | 0xD417          | Filter control register                         |
237    /// | SID_VOLUME_FC                 | 0xD418          | Volume and filter control                       |
238    /// | SID_POT_X                     | 0xD419          | Paddle X position (read)                        |
239    /// | SID_POT_Y                     | 0xD41A          | Paddle Y position (read)                        |
240    /// | SID_OSC3_RANDOM               | 0xD41B          | Oscillator 3 output/random number generator     |
241    /// | SID_ENV3                      | 0xD41C          | Envelope generator for voice 3 (read)           |
242    ///
243    /// ```
244    /// use c64_assembler::builder::ApplicationBuilder;
245    /// let application = ApplicationBuilder::default()
246    ///     .include_sid_defines()
247    ///     .build();
248    /// ```
249    pub fn include_sid_defines(&mut self) -> &mut Self {
250        self.define_address("SID_BASE", 0xD400)
251            .define_address("SID_VOICE_1_FREQ_LO", 0xD400)
252            .define_address("SID_VOICE_1_FREQ_HI", 0xD401)
253            .define_address("SID_VOICE_1_PW_LO", 0xD402)
254            .define_address("SID_VOICE_1_PW_HI", 0xD403)
255            .define_address("SID_VOICE_1_CTRL", 0xD404)
256            .define_address("SID_VOICE_1_AD", 0xD405)
257            .define_address("SID_VOICE_1_SR", 0xD406)
258            .define_address("SID_VOICE_2_FREQ_LO", 0xD407)
259            .define_address("SID_VOICE_2_FREQ_HI", 0xD408)
260            .define_address("SID_VOICE_2_PW_LO", 0xD409)
261            .define_address("SID_VOICE_2_PW_HI", 0xD40A)
262            .define_address("SID_VOICE_2_CTRL", 0xD40B)
263            .define_address("SID_VOICE_2_AD", 0xD40C)
264            .define_address("SID_VOICE_2_SR", 0xD40D)
265            .define_address("SID_VOICE_3_FREQ_LO", 0xD40E)
266            .define_address("SID_VOICE_3_FREQ_HI", 0xD40F)
267            .define_address("SID_VOICE_3_PW_LO", 0xD410)
268            .define_address("SID_VOICE_3_PW_HI", 0xD411)
269            .define_address("SID_VOICE_3_CTRL", 0xD412)
270            .define_address("SID_VOICE_3_AD", 0xD413)
271            .define_address("SID_VOICE_3_SR", 0xD414)
272            .define_address("SID_FILTER_CUTOFF_LO", 0xD415)
273            .define_address("SID_FILTER_CUTOFF_HI", 0xD416)
274            .define_address("SID_FILTER_CTRL", 0xD417)
275            .define_address("SID_VOLUME_FC", 0xD418)
276            .define_address("SID_POT_X", 0xD419)
277            .define_address("SID_POT_Y", 0xD41A)
278            .define_address("SID_OSC3_RANDOM", 0xD41B)
279            .define_address("SID_ENV3", 0xD41C)
280    }
281
282    pub fn module(&mut self, module: Module) -> &mut Self {
283        self.application.modules.push(module);
284        self
285    }
286
287    /// Build the application
288    pub fn build(&mut self) -> AssemblerResult<Application> {
289        finalize(&mut self.application)?;
290        Ok(self.application.clone())
291    }
292}
293
294impl Application {
295    pub(crate) fn define_mut(&mut self, define_name: &String) -> &mut Define {
296        self.defines
297            .iter_mut()
298            .find(|define| &define.name == define_name)
299            .unwrap()
300    }
301
302    pub fn address(&self, address_reference: &AddressReference) -> Address {
303        self.address_lookup.get(&address_reference.name).unwrap() + address_reference.offset
304    }
305}