use crate::*;
use crate::builder;
trait SingleInstructionModifier {
fn match(&self, token: &Token) -> bool;
fn replace(&self, token: &Token) -> Token;
fn replace_if_match(&self, token: &Token) -> Token {
if self.match(token) {
self.replace(token)
}
else {
*token
}
}
}
struct ResetA;
struct CompareA;
impl SingleInstructionModifier for ResetA {
fn match(&self, token: &Token) -> bool {
match token {
Token::OpCode(
Mnemonic::Ld,
Some(DataAccess::Register8(Register8::A)),
Some(DataAccess::Expression(Expression::Value(0))) ) => true,
_ => false
}
}
fn replace(&self, token: &Token) -> Token {
xor_a()
}
}
impl SingleInstructionModifier for CompareA {
fn match(&self, token: &Token) -> bool {
match token {
Token::OpCode(
Mnemonic::Cp,
Some(DataAccess::Expression(Expression::Value(0))), None
) => true,
_ => false
}
}
fn replace(&self, token: &Token) -> Token {
or_a()
}
}
trait MultipleInstructionsModifier {
fn rewrite_one(tokens: &[Token]) -> Option<Vec<Token>>;
pub fn rewrite_n(tokens: &[Token], n: usize) -> Option<Vec<Token>> {
let previous_run = None;
for i in 0..n {
let to_rewrite = match previous_run {
Some(ref tokens) => tokens,
None => tokens
};
let result = self.rewrite_one(to_rewrite);
if result.is_none() {
return previous_run;
}
else {
previous_run = result;
}
}
return previous_run;
}
pub fn rewrite(tokens: &[Token]) -> Option<Vec<Token>> {
let previous_run = None;
loop {
let tokens = match previous_run {
Some(ref tokens) => tokens,
None => tokens
};
let current_run = self.rewrite_one(tokens);
if current_run.is_none() {
return previous_run;
}
else {
previous_run = current_run;
}
}
}
}
#[derive(Default, Debug)]
struct FuseTwoSuccessiveDefbIntoDefb;
impl MultipleInstructionsModifier for FuseTwoSuccessiveDefbIntoDefb {
fn rewrite_one(tokens: &[Token]) -> Option<Vec<Token>> {
match tokens {
beginning @.., Token::Defb(ref a), Token::Defb(ref b), end @..
=> {
let mut bytes = Vec::new();
bytes.append(a);
bytes.append(b);
let mut tokens = Vec::new();
tokens.append_elements(beginning);
tokens.push(builder::defb_elements(bytes);
tokens.append_elements(end);
Some(tokens)
},
_ => None
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn optimize_consecutive_db_standard_case() {
let from = [builder::nop(), builder::defb(0xa), builder::defb(0xa), builder::nop()];
let to = [builder::nop(), builder::defb_elements([0xa, 0xb].into())];
let obtained = FuseTwoSuccessiveDefbIntoDefb::default().rewrite(&from);
assert_eq!(
to,
obtained.as_slice()
)
}
}