gbas/backend/object/
translate.rs

1use super::context::{EvalContext, SymbolTable};
2use super::{traverse_chunk_items, Chunk, Node};
3use backend::{BinarySection, RelocExpr};
4use diagnostics::{DiagnosticsListener, InternalDiagnostic, Message};
5use span::{Source, Span};
6use std::vec::IntoIter;
7use Width;
8
9impl<S: Span> Chunk<S> {
10    pub fn translate(
11        &self,
12        context: &mut EvalContext<&SymbolTable>,
13        diagnostics: &mut impl DiagnosticsListener<S>,
14    ) -> BinarySection {
15        let mut data = Vec::<u8>::new();
16        let origin = self.evaluate_origin(&context);
17        context.location = origin.clone();
18        traverse_chunk_items(&self.items, context, |item, context| {
19            data.extend(item.translate(context, diagnostics))
20        });
21        BinarySection {
22            origin: origin.exact().unwrap() as usize,
23            data,
24        }
25    }
26}
27
28impl<S: Span> Node<S> {
29    fn translate(
30        &self,
31        context: &EvalContext<&SymbolTable>,
32        diagnostics: &mut impl DiagnosticsListener<S>,
33    ) -> IntoIter<u8> {
34        match self {
35            Node::Byte(value) => vec![*value],
36            Node::Embedded(opcode, expr) => {
37                let n = expr.evaluate(context).exact().unwrap();
38                vec![opcode | ((n as u8) << 3)]
39            }
40            Node::Expr(expr, width) => {
41                resolve_expr_item(&expr, *width, context, diagnostics).into_bytes()
42            }
43            Node::Label(..) => vec![],
44            Node::LdInlineAddr(opcode, expr) => {
45                let addr = expr.evaluate(context).exact().unwrap();
46                let kind = if addr < 0xff00 {
47                    AddrKind::Low
48                } else {
49                    AddrKind::High
50                };
51                let opcode = opcode | match kind {
52                    AddrKind::Low => 0x0a,
53                    AddrKind::High => 0x00,
54                };
55                let mut bytes = vec![opcode];
56                let addr_repr = match kind {
57                    AddrKind::Low => Data::Word(addr as u16),
58                    AddrKind::High => Data::Byte((addr & 0xff) as u8),
59                };
60                bytes.extend(addr_repr.into_bytes());
61                bytes
62            }
63        }.into_iter()
64    }
65}
66
67#[derive(Clone, Copy)]
68enum AddrKind {
69    Low,
70    High,
71}
72
73#[derive(Clone, Copy)]
74enum Data {
75    Byte(u8),
76    Word(u16),
77}
78
79impl Data {
80    fn into_bytes(self) -> Vec<u8> {
81        match self {
82            Data::Byte(value) => vec![value],
83            Data::Word(value) => {
84                let low = (value & 0xff) as u8;
85                let high = ((value >> 8) & 0xff) as u8;
86                vec![low, high]
87            }
88        }
89    }
90}
91
92fn resolve_expr_item<S: Span>(
93    expr: &RelocExpr<S>,
94    width: Width,
95    context: &EvalContext<&SymbolTable>,
96    diagnostics: &mut impl DiagnosticsListener<S>,
97) -> Data {
98    let span = expr.span();
99    let value = expr
100        .evaluate_strictly(context, &mut |symbol, span| {
101            diagnostics.emit_diagnostic(InternalDiagnostic::new(
102                Message::UnresolvedSymbol {
103                    symbol: symbol.to_string(),
104                },
105                span.clone(),
106            ))
107        }).exact()
108        .unwrap_or(0);
109    fit_to_width((value, span), width, diagnostics)
110}
111
112fn fit_to_width<SR: Clone>(
113    (value, value_ref): (i32, SR),
114    width: Width,
115    diagnostics: &mut impl DiagnosticsListener<SR>,
116) -> Data {
117    if !is_in_range(value, width) {
118        diagnostics.emit_diagnostic(InternalDiagnostic::new(
119            Message::ValueOutOfRange { value, width },
120            value_ref,
121        ))
122    }
123    match width {
124        Width::Byte => Data::Byte(value as u8),
125        Width::Word => Data::Word(value as u16),
126    }
127}
128
129fn is_in_range(n: i32, width: Width) -> bool {
130    match width {
131        Width::Byte => is_in_byte_range(n),
132        Width::Word => true,
133    }
134}
135
136fn is_in_byte_range(n: i32) -> bool {
137    is_in_i8_range(n) || is_in_u8_range(n)
138}
139
140fn is_in_i8_range(n: i32) -> bool {
141    n >= i32::from(i8::min_value()) && n <= i32::from(i8::max_value())
142}
143
144fn is_in_u8_range(n: i32) -> bool {
145    n >= i32::from(u8::min_value()) && n <= i32::from(u8::max_value())
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use backend::{BinaryOperator, RelocAtom};
152    use diagnostics::IgnoreDiagnostics;
153    use expr::ExprVariant;
154    use std::borrow::Borrow;
155
156    #[test]
157    fn translate_ld_deref_addr_a_with_low_addr() {
158        test_translation_of_ld_inline_addr(0xe0, 0x2000, [0xea, 0x00, 0x20])
159    }
160
161    #[test]
162    fn translate_ld_a_deref_addr_with_low_addr() {
163        test_translation_of_ld_inline_addr(0xf0, 0x2000, [0xfa, 0x00, 0x20])
164    }
165
166    #[test]
167    fn translate_ld_deref_addr_a_with_high_addr() {
168        test_translation_of_ld_inline_addr(0xe0, 0xff77, [0xe0, 0x77])
169    }
170
171    #[test]
172    fn translate_ld_a_deref_addr_with_high_addr() {
173        test_translation_of_ld_inline_addr(0xf0, 0xff77, [0xf0, 0x77])
174    }
175
176    fn test_translation_of_ld_inline_addr(opcode: u8, addr: u16, expected: impl Borrow<[u8]>) {
177        let actual = translate_chunk_item(Node::LdInlineAddr(
178            opcode,
179            RelocAtom::Literal(addr.into()).into(),
180        ));
181        assert_eq!(actual, expected.borrow())
182    }
183
184    #[test]
185    fn translate_embedded() {
186        let actual = translate_chunk_item(Node::Embedded(0b01_000_110, 4.into()));
187        assert_eq!(actual, [0x66])
188    }
189
190    #[test]
191    fn translate_expr_with_subtraction() {
192        let actual = translate_chunk_item(Node::Expr(
193            ExprVariant::Binary(
194                BinaryOperator::Minus,
195                Box::new(4.into()),
196                Box::new(3.into()),
197            ).into(),
198            Width::Byte,
199        ));
200        assert_eq!(actual, [0x01])
201    }
202
203    fn translate_chunk_item<S: Span>(item: Node<S>) -> Vec<u8> {
204        use backend::object::resolve::Value;
205        use diagnostics;
206        item.translate(
207            &EvalContext {
208                symbols: &SymbolTable::new(),
209                location: Value::Unknown,
210            },
211            &mut diagnostics::IgnoreDiagnostics {},
212        ).collect()
213    }
214
215    #[test]
216    fn set_origin_of_translated_chunk() {
217        let addr = 0x7ff0;
218        let chunk = Chunk {
219            origin: Some(addr.into()),
220            items: Vec::new(),
221        };
222        let translated = translate_without_context(chunk);
223        assert_eq!(translated.origin, addr as usize)
224    }
225
226    #[test]
227    fn translate_expr_with_location_counter() {
228        let byte = 0x42;
229        let mut chunk = Chunk::new();
230        chunk.items.extend(vec![
231            Node::Byte(byte),
232            Node::Expr(RelocAtom::LocationCounter.into(), Width::Byte),
233        ]);
234        let binary = translate_without_context(chunk);
235        assert_eq!(binary.data, [byte, 0x02])
236    }
237
238    #[test]
239    fn location_counter_starts_from_chunk_origin() {
240        let mut chunk = Chunk::new();
241        chunk.origin = Some(0xffe1.into());
242        chunk
243            .items
244            .push(Node::Expr(RelocAtom::LocationCounter.into(), Width::Word));
245        let binary = translate_without_context(chunk);
246        assert_eq!(binary.data, [0xe3, 0xff])
247    }
248
249    fn translate_without_context<S: Span>(chunk: Chunk<S>) -> BinarySection {
250        let mut context = EvalContext {
251            symbols: &SymbolTable::new(),
252            location: 0.into(),
253        };
254        chunk.translate(&mut context, &mut IgnoreDiagnostics)
255    }
256}