Now parsing and requiring branch delay slot markers.

Fixes #2.
This commit is contained in:
Filipe Rodrigues 2021-05-07 21:27:01 +01:00
parent 3fc494759e
commit 6b3528507f
5 changed files with 110 additions and 9 deletions

View File

@ -18,6 +18,9 @@ pub struct Line {
/// Labels
pub labels: Vec<LineLabel>,
/// Branch delay
pub branch_delay: bool,
/// Instruction
pub inst: Option<LineInst>,
}
@ -28,11 +31,22 @@ impl Line {
let mut line = line.trim();
// Parse all labels and then the mnemonic
let mut branch_delay = false;
let mut labels = vec![];
let mnemonic = loop {
// If the line starts with a comment or is empty, return all labels
if line.starts_with('#') || line.is_empty() {
return Ok(Self { labels, inst: None });
return Ok(Self {
labels,
branch_delay,
inst: None,
});
}
// If we get a `+`, it's a branch delay
if let Some(rest) = line.strip_prefix('+') {
branch_delay = true;
line = rest.trim_start();
}
// Parse a name
@ -41,7 +55,12 @@ impl Line {
// Check the character after the name
let mut rest = rest.chars();
match rest.next() {
// If we got ':', add a label and continue
// If we get a label after the branch delay, return Err
Some(':') if branch_delay => {
return Err(ParseLineError::LabelAfterBranchDelay);
},
// Else add the label
Some(':') => {
line = rest.as_str().trim_start();
let label = LineLabel { name: name.to_owned() };
@ -53,6 +72,7 @@ impl Line {
Some('#') | None => {
return Ok(Self {
labels,
branch_delay,
inst: Some(LineInst {
mnemonic: name.to_owned(),
args: vec![],
@ -60,12 +80,12 @@ impl Line {
});
},
// If we got a space or eof, we found the mnemonic.
// On a space, break and parse arguments
// If we got a space, we found the mnemonic.
Some(' ') => {
line = rest.as_str().trim_start();
break name.to_owned();
},
_ => return Err(ParseLineError::InvalidNameSuffix),
}
};
@ -90,7 +110,11 @@ impl Line {
// If we got eof or a comment, return
Some('#') | None => {
let inst = Some(LineInst { mnemonic, args });
return Ok(Self { labels, inst });
return Ok(Self {
labels,
branch_delay,
inst,
});
},
_ => return Err(ParseLineError::InvalidArgSuffix),

View File

@ -1,8 +1,8 @@
//! Errors
// Imports
use snailquote::UnescapeError;
/// Error for [`Line::parse`](super::Line::parse)
#[derive(PartialEq, Debug, thiserror::Error)]
pub enum ParseLineError {
@ -21,6 +21,10 @@ pub enum ParseLineError {
/// Invalid argument suffix
#[error("Invalid argument suffix")]
InvalidArgSuffix,
/// Found label after branch delay token
#[error("Labels must come before the branch delay token")]
LabelAfterBranchDelay,
}
/// Name parsing error

View File

@ -131,7 +131,7 @@ fn main() -> Result<(), anyhow::Error> {
.map_err(|err| (n, err))?
.size();
assert!(insts.insert(cur_pos, (n, inst)).is_none());
assert!(insts.insert(cur_pos, (n, line.branch_delay, inst)).is_none());
cur_pos += inst_size;
}
@ -160,17 +160,46 @@ fn main() -> Result<(), anyhow::Error> {
.context("Unable to seek stream to beginning of instructions")?;
// For each instruction, pack it and output it to the file
for (&pos, (n, inst)) in &insts {
let mut last_inst = None;
for (&pos, &(n, branch_delay, ref inst)) in &insts {
// Create the context
let ctx = Ctx {
pos,
labels_by_name: &labels_by_name,
};
// If this instruction has a branch delay marker, if the previous instruction wasn't
// a jump, return Err
if branch_delay && !last_inst.as_ref().map_or(false, Inst::may_jump) {
anyhow::bail!(
"Unable to parse line {}: Branch delay markers can only be used when the previous instruction is a \
jump",
n
);
}
// If this instruction doesn't have a branch delay marker, but the previous instruction
// was a jump, return Err
if !branch_delay && last_inst.as_ref().map_or(false, Inst::may_jump) {
anyhow::bail!(
"Unable to parse line {}: Must use a branch delay after a jump instruction",
n
);
}
let inst = Inst::parse(&inst.mnemonic, &inst.args, &ctx)
.with_context(|| format!("Unable to compile instruction at {} in line {}", pos, n + 1))?;
// If we got a pseudo instruction larger than 1 basic instruction after a jump, return Err
if branch_delay && inst.size() > 4 {
anyhow::bail!(
"Unable to parse line {}: Cannot use a pseudo instruction larger than 4 bytes as a branch delay",
n
);
}
inst.write(&mut output_file).context("Unable to write to file")?;
last_inst = Some(inst);
}
let size = output_file.stream_position().context("Unable to get stream position")? - 0x800;

View File

@ -227,12 +227,18 @@ fn main() -> Result<(), anyhow::Error> {
// If it's standalone, print it by it's own
ExeItem::Unknown { insts } => {
let mut prev_inst = None;
for (pos, inst) in insts {
// Write the position
if cli.print_inst_pos {
print!("{pos}: ");
}
// If we had a branch / jump instruction before this one, add a "+ "
if prev_inst.as_ref().map_or(false, Inst::may_jump) {
print!("+ ");
}
// Write the instruction
print!(
"{}",
@ -240,6 +246,8 @@ fn main() -> Result<(), anyhow::Error> {
);
println!();
prev_inst = Some(inst);
}
},
}

View File

@ -230,7 +230,7 @@
inline_comments:
0x80061910: "args: ($s1, $s0, c & 0xffff, d & 0xffff)"
start_pos: 0x800618e4
end_pos: 0x80061954
end_pos: 0x80061958
kind: Known
- name: modify_spu_delay1
@ -847,6 +847,42 @@
end_pos: 0x8004b394
kind: Known
- name: something43
labels:
0x8002d0f4: "0"
0x8002d104: "1"
0x8002d110: "2"
0x8002d120: "3"
0x8002d130: "4"
start_pos: 0x8002d0e4
end_pos: 0x8002d140
kind: Known
- name: something44
start_pos: 0x800316a8
end_pos: 0x800316b4
kind: Known
- name: something45
start_pos: 0x80063cb0
end_pos: 0x80063cbc
kind: Known
- name: something46
start_pos: 0x80063cc4
end_pos: 0x80063cd0
kind: Known
- name: something47
start_pos: 0x80063cd8
end_pos: 0x80063ce4
kind: Known
- name: something48
start_pos: 0x80063d04
end_pos: 0x80063d10
kind: Known
# A functions
- name: InitHeap
signature: "fn(addr: *u32, size: u32)"