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}