feo3boy_opcodes/compiler/instr/
builder.rs1use crate::compiler::instr::flow::{Branch, Element};
5use crate::compiler::instr::{InstrDef, InstrId};
6use crate::microcode::Microcode;
7
8#[derive(Default, Debug, Clone)]
10pub struct InstrBuilder {
11 flow: Element,
13}
14
15impl InstrBuilder {
16 pub fn new() -> Self {
18 Default::default()
19 }
20
21 pub fn first(code: impl Into<InstrBuilder>) -> Self {
23 code.into()
24 }
25
26 pub fn r#yield() -> Self {
28 Self::first(Microcode::Yield)
29 }
30
31 pub fn cond(
35 code_if_true: impl Into<InstrBuilder>,
36 code_if_false: impl Into<InstrBuilder>,
37 ) -> Self {
38 Self {
39 flow: Element::Branch(Branch {
40 code_if_true: Box::new(code_if_true.into().flow),
41 code_if_false: Box::new(code_if_false.into().flow),
42 }),
43 }
44 }
45
46 pub fn if_true(code_if_true: impl Into<InstrBuilder>) -> Self {
48 InstrBuilder::cond(code_if_true, InstrBuilder::new())
49 }
50
51 pub fn if_false(code_if_false: impl Into<InstrBuilder>) -> Self {
53 InstrBuilder::cond(InstrBuilder::new(), code_if_false)
54 }
55
56 pub fn then_cond(
59 self,
60 code_if_true: impl Into<InstrBuilder>,
61 code_if_false: impl Into<InstrBuilder>,
62 ) -> Self {
63 self.then(InstrBuilder::cond(code_if_true, code_if_false))
64 }
65
66 pub fn then_if_true(self, code_if_true: impl Into<InstrBuilder>) -> Self {
68 self.then_cond(code_if_true, InstrBuilder::new())
69 }
70
71 pub fn then_if_false(self, code_if_false: impl Into<InstrBuilder>) -> Self {
73 self.then_cond(InstrBuilder::new(), code_if_false)
74 }
75
76 pub fn read<R: MicrocodeReadable>(from: R) -> Self {
78 from.to_read()
79 }
80
81 pub fn write<W: MicrocodeWritable>(to: W) -> Self {
83 to.to_write()
84 }
85
86 pub fn then(mut self, code: impl Into<InstrBuilder>) -> Self {
88 let other = code.into();
89 self.flow.extend_block(other.flow);
90 self
91 }
92
93 pub fn then_yield(self) -> Self {
95 self.then(Microcode::Yield)
96 }
97
98 pub fn then_read<R: MicrocodeReadable>(self, from: R) -> Self {
100 self.then(from.to_read())
101 }
102
103 pub fn then_write<W: MicrocodeWritable>(self, to: W) -> Self {
105 self.then(to.to_write())
106 }
107
108 pub fn build(mut self, id: InstrId) -> InstrDef {
110 add_fetch_next_to_end(&mut self.flow);
111 InstrDef {
112 id,
113 microcode: self.flow.flatten(),
114 flow: self.flow,
115 }
116 }
117}
118
119fn add_fetch_next_to_end(elem: &mut Element) {
121 match elem {
122 Element::Microcode(microcode) => {
123 match microcode {
124 Microcode::FetchNextInstruction
126 | Microcode::ParseOpcode
127 | Microcode::ParseCBOpcode => {}
128 _ => elem.extend_block(Element::Microcode(Microcode::FetchNextInstruction)),
130 }
131 }
132 Element::Block(block) => match block.elements.last_mut() {
133 Some(last) => {
134 if let Element::Microcode(microcode) = last {
135 match microcode {
137 Microcode::FetchNextInstruction
139 | Microcode::ParseOpcode
140 | Microcode::ParseCBOpcode => {}
141 _ => block
143 .elements
144 .push(Element::Microcode(Microcode::FetchNextInstruction)),
145 }
146 } else {
147 add_fetch_next_to_end(last)
148 }
149 }
150 None => {
151 *elem = Element::Microcode(Microcode::FetchNextInstruction)
153 }
154 },
155 Element::Branch(Branch {
156 code_if_true,
157 code_if_false,
158 }) => {
159 let true_end_terminal = code_if_true.ends_with_terminal();
160 let false_end_terminal = code_if_false.ends_with_terminal();
161 if !true_end_terminal && !false_end_terminal {
162 elem.extend_block(Element::Microcode(Microcode::FetchNextInstruction));
165 } else if !true_end_terminal {
166 add_fetch_next_to_end(code_if_true);
168 } else if !false_end_terminal {
169 add_fetch_next_to_end(code_if_false);
171 }
172 }
174 }
175}
176
177impl<T: Into<InstrBuilder>> From<Option<T>> for InstrBuilder {
178 fn from(value: Option<T>) -> Self {
179 value.map(Into::into).unwrap_or_default()
180 }
181}
182
183impl From<Microcode> for InstrBuilder {
184 fn from(value: Microcode) -> Self {
185 if let Microcode::Skip { .. } | Microcode::SkipIf { .. } = value {
186 panic!("InstrBuilder does not permit explicit skips. Use `cond`.");
187 }
188
189 Self {
190 flow: Element::Microcode(value),
191 }
192 }
193}
194
195pub trait MicrocodeReadable {
198 fn to_read(self) -> InstrBuilder;
199}
200
201pub trait MicrocodeWritable {
204 fn to_write(self) -> InstrBuilder;
205}