Skip to main content

c6000_disassembler/instruction/
branching.rs

1use std::{
2    cmp::min,
3    io::{self, Error, ErrorKind},
4};
5
6use crate::instruction::{
7    C6000Instruction, ConditionalOperation, InstructionData,
8    parser::{ParsedVariable, ParsingInstruction, parse},
9    register::{ControlRegister, Register},
10};
11
12pub enum BranchUsing {
13    Displacement(i32),
14    Register(Register),
15    Pointer(ControlRegister),
16}
17
18pub struct BranchInstruction {
19    instruction_data: InstructionData,
20    pub branch_using: BranchUsing,
21    pub side: bool,
22    pce1_address: u32,
23    pub nop_count: u8,
24}
25
26impl BranchInstruction {
27    pub fn calculate_displacement_address(&self) -> io::Result<u32> {
28        match self.branch_using {
29            BranchUsing::Displacement(displacement) => {
30                let displacement_abs = displacement.unsigned_abs();
31                if self.pce1_address == 0 {
32                    return Err(Error::other("PCE1 = 0"));
33                };
34                let branch_address = {
35                    if displacement.is_positive() {
36                        self.pce1_address + displacement_abs
37                    } else {
38                        self.pce1_address - displacement_abs
39                    }
40                };
41                return Ok(branch_address);
42            }
43            _ => return Err(Error::other("Not displacement")),
44        }
45    }
46}
47
48impl C6000Instruction for BranchInstruction {
49    fn new(input: &super::InstructionInput) -> std::io::Result<Self> {
50        let formats = [
51            vec![
52                ParsingInstruction::Bit {
53                    name: String::from("p"),
54                },
55                ParsingInstruction::Bit {
56                    name: String::from("s"),
57                },
58                ParsingInstruction::Match {
59                    size: 5,
60                    value: 0b100,
61                },
62                ParsingInstruction::Signed {
63                    size: 21,
64                    name: String::from("cst"),
65                },
66                ParsingInstruction::ConditionalOperation {
67                    name: String::from("cond"),
68                },
69            ],
70            vec![
71                ParsingInstruction::Bit {
72                    name: String::from("p"),
73                },
74                ParsingInstruction::Bit {
75                    name: String::from("s"),
76                },
77                ParsingInstruction::Match {
78                    size: 10,
79                    value: 0xD8,
80                },
81                ParsingInstruction::Bit {
82                    name: String::from("x"),
83                },
84                ParsingInstruction::Match { size: 5, value: 0 },
85                ParsingInstruction::RegisterCrosspath {
86                    size: 5,
87                    name: String::from("src"),
88                },
89                ParsingInstruction::Match { size: 5, value: 0 },
90                ParsingInstruction::ConditionalOperation {
91                    name: String::from("cond"),
92                },
93            ],
94            vec![
95                ParsingInstruction::Bit {
96                    name: String::from("p"),
97                },
98                ParsingInstruction::Match {
99                    size: 17,
100                    value: 0x71,
101                },
102                ParsingInstruction::Unsigned {
103                    size: 3,
104                    name: String::from("op"),
105                },
106                ParsingInstruction::Match { size: 7, value: 0 },
107                ParsingInstruction::ConditionalOperation {
108                    name: String::from("cond"),
109                },
110            ],
111            vec![
112                ParsingInstruction::Bit {
113                    name: String::from("p"),
114                },
115                ParsingInstruction::Bit {
116                    name: String::from("s"),
117                },
118                ParsingInstruction::Match {
119                    size: 11,
120                    value: 0x48,
121                },
122                ParsingInstruction::Unsigned {
123                    size: 3,
124                    name: String::from("nop"),
125                },
126                ParsingInstruction::Signed {
127                    size: 12,
128                    name: String::from("cst"),
129                },
130                ParsingInstruction::ConditionalOperation {
131                    name: String::from("cond"),
132                },
133            ],
134            vec![
135                ParsingInstruction::Bit {
136                    name: String::from("p"),
137                },
138                ParsingInstruction::BitMatch {
139                    name: String::from("s"),
140                    value: true,
141                },
142                ParsingInstruction::Match {
143                    size: 10,
144                    value: 0xD8,
145                },
146                ParsingInstruction::Bit {
147                    name: String::from("x"),
148                },
149                ParsingInstruction::Unsigned {
150                    size: 3,
151                    name: String::from("nop"),
152                },
153                ParsingInstruction::Match { size: 2, value: 0 },
154                ParsingInstruction::RegisterCrosspath {
155                    size: 5,
156                    name: String::from("src"),
157                },
158                ParsingInstruction::Match { size: 5, value: 1 },
159                ParsingInstruction::ConditionalOperation {
160                    name: String::from("cond"),
161                },
162            ],
163        ];
164        for format in formats {
165            let Ok(parsed_variables) = parse(input.opcode, format.as_slice()) else {
166                continue;
167            };
168            let p_bit = ParsedVariable::try_get(&parsed_variables, "p")?.get_bool()?;
169            let side = ParsedVariable::try_get(&parsed_variables, "s")?.get_bool()?;
170            let conditional_operation =
171                ParsedVariable::try_get(&parsed_variables, "cond")?.get_conditional_operation()?;
172            let nop_count = {
173                if let Ok(variable) = ParsedVariable::try_get(&parsed_variables, "nop") {
174                    variable.get_u8()?
175                } else {
176                    0
177                }
178            };
179            let branch_using = {
180                if let Ok(variable) = ParsedVariable::try_get(&parsed_variables, "cst") {
181                    BranchUsing::Displacement(
182                        variable.get_i32()? << {
183                            if nop_count > 0 && input.fphead.is_some() {
184                                1
185                            } else {
186                                2
187                            }
188                        },
189                    )
190                } else if let Ok(variable) = ParsedVariable::try_get(&parsed_variables, "src") {
191                    BranchUsing::Register(variable.get_register()?)
192                } else if let Ok(variable) = ParsedVariable::try_get(&parsed_variables, "op") {
193                    let opcode = variable.get_u8()?;
194                    match opcode {
195                        0b110 => BranchUsing::Pointer(ControlRegister::IRP),
196                        0b111 => BranchUsing::Pointer(ControlRegister::NRP),
197                        _ => continue,
198                    }
199                } else {
200                    continue;
201                }
202            };
203            return Ok(Self {
204                side,
205                branch_using,
206                pce1_address: input.pce1_address,
207                nop_count,
208                instruction_data: InstructionData {
209                    opcode: input.opcode,
210                    compact: false,
211                    conditional_operation,
212                    p_bit,
213                    ..Default::default()
214                },
215            });
216        }
217        Err(Error::new(
218            ErrorKind::InvalidInput,
219            "Not a branch instruction",
220        ))
221    }
222
223    fn new_compact(input: &super::InstructionInput) -> std::io::Result<Self> {
224        let Some(fphead) = &input.fphead else {
225            return Err(Error::new(ErrorKind::InvalidInput, "No fphead"));
226        };
227        if !fphead.decode_compact_branches {
228            return Err(Error::new(
229                ErrorKind::InvalidInput,
230                "Decoding compact branch instructions set to false (BR = 0)",
231            ));
232        }
233
234        let formats = [
235            (
236                "sbs7",
237                vec![
238                    ParsingInstruction::Bit {
239                        name: String::from("s"),
240                    },
241                    ParsingInstruction::Match {
242                        size: 5,
243                        value: 0x5,
244                    },
245                    ParsingInstruction::Signed {
246                        size: 7,
247                        name: String::from("cst"),
248                    },
249                    ParsingInstruction::Unsigned {
250                        size: 3,
251                        name: String::from("nop"),
252                    },
253                ],
254            ),
255            (
256                "sbu8",
257                vec![
258                    ParsingInstruction::Bit {
259                        name: String::from("s"),
260                    },
261                    ParsingInstruction::Match {
262                        size: 5,
263                        value: 0x5,
264                    },
265                    ParsingInstruction::Unsigned {
266                        size: 8,
267                        name: String::from("cst"),
268                    },
269                    ParsingInstruction::Match {
270                        size: 2,
271                        value: 0b11,
272                    },
273                ],
274            ),
275            (
276                "scs10",
277                vec![
278                    ParsingInstruction::Bit {
279                        name: String::from("s"),
280                    },
281                    ParsingInstruction::Match {
282                        size: 5,
283                        value: 0xD,
284                    },
285                    ParsingInstruction::Signed {
286                        size: 10,
287                        name: String::from("cst"),
288                    },
289                ],
290            ),
291            (
292                "sbs7c",
293                vec![
294                    ParsingInstruction::Bit {
295                        name: String::from("s"),
296                    },
297                    ParsingInstruction::Match {
298                        size: 3,
299                        value: 0x5,
300                    },
301                    ParsingInstruction::Bit {
302                        name: String::from("z"),
303                    },
304                    ParsingInstruction::Match { size: 1, value: 1 },
305                    ParsingInstruction::Signed {
306                        size: 7,
307                        name: String::from("cst"),
308                    },
309                    ParsingInstruction::Unsigned {
310                        size: 3,
311                        name: String::from("nop"),
312                    },
313                ],
314            ),
315            (
316                "sbu8c",
317                vec![
318                    ParsingInstruction::Bit {
319                        name: String::from("s"),
320                    },
321                    ParsingInstruction::Match {
322                        size: 3,
323                        value: 0x5,
324                    },
325                    ParsingInstruction::Bit {
326                        name: String::from("z"),
327                    },
328                    ParsingInstruction::Match { size: 1, value: 1 },
329                    ParsingInstruction::Unsigned {
330                        size: 8,
331                        name: String::from("cst"),
332                    },
333                    ParsingInstruction::Match {
334                        size: 2,
335                        value: 0b11,
336                    },
337                ],
338            ),
339            (
340                "sx1b",
341                vec![
342                    ParsingInstruction::Bit {
343                        name: String::from("s"),
344                    },
345                    ParsingInstruction::Match {
346                        size: 6,
347                        value: 0x37,
348                    },
349                    ParsingInstruction::Unsigned {
350                        size: 4,
351                        name: String::from("src"),
352                    },
353                    ParsingInstruction::Match { size: 2, value: 0 },
354                    ParsingInstruction::Unsigned {
355                        size: 3,
356                        name: String::from("nop"),
357                    },
358                ],
359            ),
360        ];
361
362        for (name, format) in formats {
363            let Ok(parsed_variables) = parse(input.opcode, format.as_slice()) else {
364                continue;
365            };
366            let side = ParsedVariable::try_get(&parsed_variables, "s")?.get_bool()?;
367            let branch_using = {
368                if name == "sx1b" {
369                    let num = ParsedVariable::try_get(&parsed_variables, "src")?.get_u8()?;
370                    BranchUsing::Register(Register::B(num))
371                } else {
372                    BranchUsing::Displacement(if name == "sbu8" || name == "sbu8c" {
373                        (ParsedVariable::try_get(&parsed_variables, "cst")?.get_u8()? as i32) << 1
374                    } else {
375                        ParsedVariable::try_get(&parsed_variables, "cst")?.get_i32()? << {
376                            if name == "scs10" { 2 } else { 1 }
377                        }
378                    })
379                }
380            };
381            let nop_count = {
382                if name == "sbs7" || name == "sbs7c" || name == "sx1b" {
383                    min(
384                        ParsedVariable::try_get(&parsed_variables, "nop")?.get_u8()?,
385                        5,
386                    )
387                } else {
388                    5
389                }
390            };
391            let conditional_operation = {
392                if name == "scs10" {
393                    Some(ConditionalOperation::ReservedLow)
394                } else if name == "sbs7c" || name == "sbu8c" {
395                    let z = ParsedVariable::try_get(&parsed_variables, "z")?.get_bool()?;
396                    if z {
397                        Some(ConditionalOperation::Zero(Register::from(0, side)))
398                    } else {
399                        Some(ConditionalOperation::NonZero(Register::from(0, side)))
400                    }
401                } else {
402                    None
403                }
404            };
405            return Ok(Self {
406                instruction_data: InstructionData {
407                    opcode: input.opcode,
408                    compact: true,
409                    conditional_operation,
410                    ..Default::default()
411                },
412                branch_using,
413                side,
414                pce1_address: input.pce1_address,
415                nop_count,
416            });
417        }
418
419        Err(Error::new(
420            ErrorKind::InvalidInput,
421            "Not a branch instruction",
422        ))
423    }
424
425    fn instruction_clean(&self) -> String {
426        if let Some(co) = self.conditional_operation()
427            && co == ConditionalOperation::ReservedLow
428        {
429            String::from("CALLP")
430        } else {
431            if self.nop_count > 0 {
432                String::from("BNOP")
433            } else {
434                String::from("B")
435            }
436        }
437    }
438
439    fn instruction(&self) -> String {
440        let unit_num = if self.side { 2 } else { 1 };
441        let mut instruction = format!("{}.S{unit_num}", self.instruction_clean());
442        if let BranchUsing::Register(register) = self.branch_using
443            && register.side() != self.side
444        {
445            instruction += "X";
446        }
447        instruction
448    }
449
450    fn operands(&self) -> String {
451        let operands = match self.branch_using {
452            BranchUsing::Displacement(displacement) => {
453                let displacement_abs = displacement.unsigned_abs();
454                let address_result = self.calculate_displacement_address();
455                let Ok(branch_address) = self.calculate_displacement_address() else {
456                    return address_result
457                        .map_err(|e| format!("ERROR {e}"))
458                        .unwrap_err();
459                };
460                format!(
461                    "0x{branch_address:08X} (PCE1{}0x{displacement_abs:08X})",
462                    if displacement.is_positive() { "+" } else { "-" }
463                )
464            }
465            BranchUsing::Register(register) => register.to_string(),
466            BranchUsing::Pointer(register) => register.to_string(),
467        };
468
469        if let Some(co) = self.conditional_operation()
470            && co == ConditionalOperation::ReservedLow
471        {
472            format!("{operands}, {}", Register::from(3, self.side).to_string())
473        } else if self.nop_count > 0 {
474            format!("{operands}, {}", self.nop_count)
475        } else {
476            operands
477        }
478    }
479
480    fn instruction_data(&self) -> &InstructionData {
481        &self.instruction_data
482    }
483
484    fn instruction_data_mut(&mut self) -> &mut InstructionData {
485        &mut self.instruction_data
486    }
487}