c64_assembler/generator/
program.rs

1use c64_assembler_6502::{
2    instruction::InstructionDef,
3    opcodes::{NO_ZEROPAGE, NO_ZEROPAGE_X, NO_ZEROPAGE_Y},
4};
5
6use crate::{
7    instruction::{operation::Operation, Instruction},
8    memory::{
9        address_mode::{AddressMode, Immediate},
10        Address, ZeroPage,
11    },
12    validator::{AssemblerResult, Error},
13    Application, Instructions, Module,
14};
15
16use super::Generator;
17
18const PROGRAM_HEADER_BYTE_SIZE: Address = 2;
19
20/// .PRG byte code generator
21#[derive(Default, Debug)]
22pub struct ProgramGenerator {
23    output: Vec<u8>,
24}
25
26impl Generator for ProgramGenerator {
27    type Output = Vec<u8>;
28
29    fn generate(mut self, application: Application) -> AssemblerResult<Self::Output> {
30        self.add_u16(application.entry_point);
31        for module in &application.modules {
32            self.generate_module(&application, module)?;
33        }
34        Ok(self.output)
35    }
36}
37
38impl ProgramGenerator {
39    fn generate_module(&mut self, application: &Application, module: &Module) -> AssemblerResult<()> {
40        self.generate_instructions(application, &module.instructions)?;
41        for function in &module.functions {
42            self.generate_instructions(application, &function.instructions)?;
43        }
44        Ok(())
45    }
46
47    fn generate_instructions(&mut self, application: &Application, instructions: &Instructions) -> AssemblerResult<()> {
48        for instruction in &instructions.instructions {
49            self.generate_instruction(application, instruction)?;
50        }
51        Ok(())
52    }
53
54    fn generate_instruction(&mut self, application: &Application, instruction: &Instruction) -> AssemblerResult<()> {
55        match (&instruction.operation.definition(), &instruction.operation) {
56            (Some(definition), _) => self.add_byte_code(application, &instruction.address_mode, definition),
57            (None, Operation::Label(_)) => {
58                // Labels don't have bytes in the byte stream, they are only markers
59                Ok(())
60            }
61            (None, Operation::Raw(bytes)) => {
62                self.add_bytes(bytes);
63                Ok(())
64            }
65
66            (_, _) => Err(Error::InternalCompilerError),
67        }
68    }
69
70    fn add_byte_code(
71        &mut self,
72        application: &Application,
73        address_mode: &AddressMode,
74        instruction: &InstructionDef,
75    ) -> AssemblerResult<()> {
76        match address_mode {
77            AddressMode::Implied => {
78                self.add_u8(instruction.implied);
79            }
80            AddressMode::Immediate(Immediate::Byte(byte)) => {
81                self.add_u8(instruction.immediate);
82                self.add_u8(*byte);
83            }
84            AddressMode::Immediate(Immediate::Low(address_reference)) => {
85                self.add_u8(instruction.immediate);
86                self.add_u8(application.address(address_reference).low());
87            }
88            AddressMode::Immediate(Immediate::High(address_reference)) => {
89                self.add_u8(instruction.immediate);
90                self.add_u8(application.address(address_reference).high());
91            }
92            AddressMode::Accumulator => {
93                self.add_u8(instruction.accumulator);
94            }
95            AddressMode::Absolute(address_reference) => {
96                let address = application.address(address_reference);
97                if instruction.zeropage != NO_ZEROPAGE && address.is_zeropage() {
98                    self.add_u8(instruction.zeropage);
99                    self.add_u8(application.address(address_reference).low());
100                } else {
101                    self.add_u8(instruction.absolute);
102                    self.add_u16(address);
103                }
104            }
105            AddressMode::AbsoluteX(address_reference) => {
106                let address = application.address(address_reference);
107                if instruction.zeropage_x != NO_ZEROPAGE_X && address.is_zeropage() {
108                    self.add_u8(instruction.zeropage_x);
109                    self.add_u8(address.low());
110                } else {
111                    self.add_u8(instruction.absolute_x);
112                    self.add_u16(address);
113                }
114            }
115            AddressMode::AbsoluteY(address_reference) => {
116                let address = application.address(address_reference);
117                if instruction.zeropage_y != NO_ZEROPAGE_Y && address.is_zeropage() {
118                    self.add_u8(instruction.zeropage_y);
119                    self.add_u8(address.low());
120                } else {
121                    self.add_u8(instruction.absolute_y);
122                    self.add_u16(address);
123                }
124            }
125            AddressMode::Relative(address_reference) => {
126                let current_instruction =
127                    application.entry_point + self.output.len() as Address - PROGRAM_HEADER_BYTE_SIZE;
128                let address = application.address(address_reference);
129                let next_instruction = current_instruction + address_mode.byte_size(application)?;
130                let relative_address = (address as i32 - next_instruction as i32) as i8;
131
132                self.add_u8(instruction.relative);
133                self.add_u8(relative_address as u8);
134            }
135            AddressMode::Indirect(address_reference) => {
136                let address = application.address(address_reference);
137                self.add_u8(instruction.indirect);
138                self.add_u16(address);
139            }
140            AddressMode::IndexedIndirect(address_reference) => {
141                let address = application.address(address_reference);
142                assert!(address.is_zeropage());
143                self.add_u8(instruction.indexed_indirect);
144                self.add_u8(address.low());
145            }
146            AddressMode::IndirectIndexed(address_reference) => {
147                let address = application.address(address_reference);
148                self.add_u8(instruction.indirect_indexed);
149                self.add_u8(address.low());
150            }
151        };
152        Ok(())
153    }
154}
155
156impl ProgramGenerator {
157    fn add_u8(&mut self, byte: u8) {
158        self.output.push(byte);
159    }
160
161    fn add_u16(&mut self, address: Address) {
162        self.add_u8(address.low());
163        self.add_u8(address.high());
164    }
165
166    fn add_bytes(&mut self, bytes: &[u8]) {
167        for byte in bytes {
168            self.output.push(*byte);
169        }
170    }
171}
172
173/// Utility function to print the set of bytes into a hexdump kind of format to the console.
174pub fn print_hexdump(bytes: &[u8]) {
175    let mut address = 0;
176    bytes.chunks(16).for_each(|chunk| {
177        let mut line = Vec::new();
178
179        line.push(format!("{:04X}: ", address));
180        address += 16;
181
182        chunk.chunks(4).for_each(|chunk| {
183            chunk.iter().for_each(|byte| {
184                line.push(format!("{:02X}", byte));
185            });
186            line.push("".to_string());
187        });
188        println!("{}", line.join(" ").trim_end());
189    });
190}