cpclib_asm/assembler/support/
cpr.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use cpclib_common::bitvec::vec::BitVec;
use cpclib_common::riff::{RiffChunk, RiffCode};
use cpclib_cpr::{CartridgeBank, Cpr};

use super::banks::DecoratedPages;
use crate::page_info::PageInformation;
use crate::support::banks::Bank;
use crate::AssemblerError;

/// A CPR contains several BANKS
/// However we store PAGES to allow the user to use any ORG value.
/// At the end only 16K will be selected
#[derive(Clone, Debug)]
pub struct CprAssembler {
    pages: DecoratedPages,
    codes: Vec<(u8, String)>
}

impl Default for CprAssembler {
    fn default() -> Self {
        Self {
            pages: DecoratedPages::default(),
            codes: Vec::with_capacity(32)
        }
    }
}

impl TryInto<Cpr> for &CprAssembler {
    type Error = AssemblerError;

    fn try_into(self) -> Result<Cpr, AssemblerError> {
        let mut chunks = Vec::with_capacity(self.codes.len());

        for (code, page) in self.codes.iter().zip(self.pages.pages.iter()) {
            let bank: Bank = page.try_into().map_err(|e: AssemblerError| {
                AssemblerError::AssemblingError {
                    msg: format!("Error when building CPR bloc {}. {}", code.1, e)
                }
            })?;
            let riff_code = RiffCode::from(code.1.as_str());
            let riff = RiffChunk::new(riff_code, bank.into());
            let chunk: CartridgeBank = riff.try_into().unwrap();
            chunks.push(chunk);
        }

        chunks.sort_by_key(|bloc| bloc.code().clone());

        Ok(chunks.into())
    }
}

impl CprAssembler {
    pub fn build_cpr(&self) -> Result<Cpr, AssemblerError> {
        self.try_into()
    }

    pub fn selected_written_bytes(&self) -> Option<&BitVec> {
        self.pages.selected_written_bytes()
    }

    pub fn selected_active_page_info_mut(&mut self) -> Option<&mut PageInformation> {
        self.pages.selected_active_page_info_mut()
    }

    pub fn selected_active_page_info(&self) -> Option<&PageInformation> {
        self.pages.selected_active_page_info()
    }

    pub fn page_infos(&self) -> impl Iterator<Item = &PageInformation> {
        self.pages.page_infos()
    }

    // pub fn selected_bank_mut(&mut self) -> Option<&mut CartridgeBank> {
    // self.selected_index.as_ref()
    // .map(|&idx| self.cpr.bank_mut(idx).unwrap())
    // }
    //
    // pub fn selected_bank(&self) -> Option<& CartridgeBank> {
    // self.selected_index.as_ref()
    // .map(|&idx| self.cpr.bank(idx).unwrap())
    // }

    pub fn select(&mut self, bank_number: u8) {
        if let Some(idx) = self.code_to_index(bank_number) {
            self.pages.selected_index = Some(idx);
        }
        else {
            self.add_bank(bank_number);
            self.pages.selected_index = Some(self.pages.pages.len() - 1);
        }
    }

    fn add_bank(&mut self, bank_number: u8) {
        let code = Self::number_to_code(bank_number);

        assert!(self.code_to_index(bank_number).is_none()); // TODO raise an error
        self.pages.add_new_and_select();
        self.codes.push((bank_number, code.into()));
    }

    fn number_to_code(bank_number: u8) -> &'static str {
        CartridgeBank::code_for(bank_number)
    }

    // assume the code is valid
    // TODO reduce String building if it is too slow
    fn code_to_index(&self, bank_number: u8) -> Option<usize> {
        let code = Self::number_to_code(bank_number);
        self.codes.iter().position(|c| c.1 == code)
    }

    pub fn selected_bloc(&self) -> Option<u8> {
        self.pages.selected_index().map(|idx| self.codes[idx].0)
    }

    pub fn get_byte(&self, address: u16) -> Option<u8> {
        self.pages.get_byte(address)
    }

    /// Write the byte in the page and save this information in written bytes
    pub fn set_byte(&mut self, address: u16, byte: u8) -> Result<(), AssemblerError> {
        // update the page limit to unsure that 16kb is used at max

        if let Some(first) = self.pages.selected_active_page_info().unwrap().startadr {
            let max = (first as u32 + 0x4000).min(0xFFFF) as u16;
            if max > self.pages.selected_active_page_info().unwrap().output_limit {
                dbg!(max, self.pages.selected_active_page_info());
                todo!()
            }
            else {
                self.pages
                    .selected_active_page_info_mut()
                    .unwrap()
                    .set_limit(max)?;
            }
        }

        self.pages.set_byte(address, byte);
        Ok(())
    }

    pub fn reset_written_bytes(&mut self) {
        self.pages.reset_written_bytes();
    }
}

impl CprAssembler {
    pub fn is_empty(&self) -> bool {
        self.pages.is_empty()
    }
}