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}