cpclib_asm/assembler/support/
cpr.rs

1use cpclib_common::bitvec::vec::BitVec;
2use cpclib_common::riff::{RiffChunk, RiffCode};
3use cpclib_cpr::{CartridgeBank, Cpr};
4
5use super::banks::DecoratedPages;
6use crate::AssemblerError;
7use crate::page_info::PageInformation;
8use crate::support::banks::Bank;
9
10/// A CPR contains several BANKS
11/// However we store PAGES to allow the user to use any ORG value.
12/// At the end only 16K will be selected
13#[derive(Clone, Debug)]
14pub struct CprAssembler {
15    pages: DecoratedPages,
16    codes: Vec<(u8, String)>
17}
18
19impl Default for CprAssembler {
20    fn default() -> Self {
21        Self {
22            pages: DecoratedPages::default(),
23            codes: Vec::with_capacity(32)
24        }
25    }
26}
27
28impl TryInto<Cpr> for &CprAssembler {
29    type Error = AssemblerError;
30
31    fn try_into(self) -> Result<Cpr, AssemblerError> {
32        let mut chunks = Vec::with_capacity(self.codes.len());
33
34        for (code, page) in self.codes.iter().zip(self.pages.pages.iter()) {
35            let bank: Bank = page.try_into().map_err(|e: AssemblerError| {
36                AssemblerError::AssemblingError {
37                    msg: format!("Error when building CPR bloc {}. {}", code.1, e)
38                }
39            })?;
40            let riff_code = RiffCode::from(code.1.as_str());
41            let riff = RiffChunk::new(riff_code, bank.into());
42            let chunk: CartridgeBank = riff.try_into().unwrap();
43            chunks.push(chunk);
44        }
45
46        chunks.sort_by_key(|bloc| bloc.code().clone());
47
48        Ok(chunks.into())
49    }
50}
51
52impl CprAssembler {
53    pub fn build_cpr(&self) -> Result<Cpr, AssemblerError> {
54        self.try_into()
55    }
56
57    pub fn selected_written_bytes(&self) -> Option<&BitVec> {
58        self.pages.selected_written_bytes()
59    }
60
61    pub fn selected_active_page_info_mut(&mut self) -> Option<&mut PageInformation> {
62        self.pages.selected_active_page_info_mut()
63    }
64
65    pub fn selected_active_page_info(&self) -> Option<&PageInformation> {
66        self.pages.selected_active_page_info()
67    }
68
69    pub fn page_infos(&self) -> impl Iterator<Item = &PageInformation> {
70        self.pages.page_infos()
71    }
72
73    // pub fn selected_bank_mut(&mut self) -> Option<&mut CartridgeBank> {
74    // self.selected_index.as_ref()
75    // .map(|&idx| self.cpr.bank_mut(idx).unwrap())
76    // }
77    //
78    // pub fn selected_bank(&self) -> Option<& CartridgeBank> {
79    // self.selected_index.as_ref()
80    // .map(|&idx| self.cpr.bank(idx).unwrap())
81    // }
82
83    pub fn select(&mut self, bank_number: u8) {
84        if let Some(idx) = self.code_to_index(bank_number) {
85            self.pages.selected_index = Some(idx);
86        }
87        else {
88            self.add_bank(bank_number);
89            self.pages.selected_index = Some(self.pages.pages.len() - 1);
90        }
91    }
92
93    fn add_bank(&mut self, bank_number: u8) {
94        let code = Self::number_to_code(bank_number);
95
96        assert!(self.code_to_index(bank_number).is_none()); // TODO raise an error
97        self.pages.add_new_and_select();
98        self.codes.push((bank_number, code.into()));
99    }
100
101    fn number_to_code(bank_number: u8) -> &'static str {
102        CartridgeBank::code_for(bank_number)
103    }
104
105    // assume the code is valid
106    // TODO reduce String building if it is too slow
107    fn code_to_index(&self, bank_number: u8) -> Option<usize> {
108        let code = Self::number_to_code(bank_number);
109        self.codes.iter().position(|c| c.1 == code)
110    }
111
112    pub fn selected_bloc(&self) -> Option<u8> {
113        self.pages.selected_index().map(|idx| self.codes[idx].0)
114    }
115
116    pub fn get_byte(&self, address: u16) -> Option<u8> {
117        self.pages.get_byte(address)
118    }
119
120    /// Write the byte in the page and save this information in written bytes
121    pub fn set_byte(&mut self, address: u16, byte: u8) -> Result<(), AssemblerError> {
122        // update the page limit to unsure that 16kb is used at max
123
124        if let Some(first) = self.pages.selected_active_page_info().unwrap().startadr {
125            let max = (first as u32 + 0x4000).min(0xFFFF) as u16;
126            if max > self.pages.selected_active_page_info().unwrap().output_limit {
127                dbg!(max, self.pages.selected_active_page_info());
128                todo!()
129            }
130            else {
131                self.pages
132                    .selected_active_page_info_mut()
133                    .unwrap()
134                    .set_limit(max)?;
135            }
136        }
137
138        self.pages.set_byte(address, byte);
139        Ok(())
140    }
141
142    pub fn reset_written_bytes(&mut self) {
143        self.pages.reset_written_bytes();
144    }
145}
146
147impl CprAssembler {
148    pub fn is_empty(&self) -> bool {
149        self.pages.is_empty()
150    }
151}