1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use super::environment::*;
use super::registers::*;

type OpcodeFn = dyn Fn(&mut Environment) -> ();

pub struct Opcode {
    pub name: String,
    pub action: Box<OpcodeFn>,
}

impl Opcode {
    pub fn execute(&self, env: &mut Environment) {
        (self.action)(env);
    }

    pub fn disasm(&self, env: &Environment) -> String {
        let name = format!("{} {}",
            env.index_description(), self.name);

        if self.name.contains("nn") {
            // Immediate argument 16 bits
            let nn = env.peek16_pc();
            let nn_str = format!("{:04x}h", nn);
            name.replace("nn", &nn_str)
        } else if self.name.contains("n") {
            // Immediate argument 8 bits
            let n = env.peek_pc();
            let n_str = format!("{:02x}h", n);
            name.replace("n", &n_str)
        } else if self.name.contains("d") {
            // Immediate argument 8 bits signed
            // In assembly it's shown with 2 added as if it were from the opcode pc.
            let d = env.peek_pc() as i8 as i16 + 2;
            let d_str = format!("{:+x}", d);
            name.replace("d", &d_str)
        } else {
            name
        }
    }
}

pub fn build_prefix(index: Reg16) -> Opcode {
    Opcode {
        name: format!("PREFIX {:?}", index),
        action: Box::new(move |env: &mut Environment| {
            // Change the index mode to IX or IY
            //let d = env.advance_pc() as i8;
            env.set_index(index /*, d*/);
        })
    }
}

pub fn build_nop() -> Opcode {
    Opcode {
        name: "NOP".to_string(),
        action: Box::new(|_: &mut Environment| {
            // Nothing done
        })
    }
}

pub fn build_noni_nop() -> Opcode {
    Opcode {
        name: "NONINOP".to_string(),
        action: Box::new(|_: &mut Environment| {
            // Nothing done
        })
    }
}

pub fn build_halt() -> Opcode {
    Opcode {
        name: "HALT".to_string(),
        action: Box::new(move |env: &mut Environment| {
            env.state.halted = true;
        })
    }
}

pub fn build_pop_rr(rr: Reg16) -> Opcode {
    Opcode {
        name: format!("POP {:?}", rr),
        action: Box::new(move |env: &mut Environment| {
            let value = env.pop();
            env.set_reg16(rr, value);
        })
    }
}

pub fn build_push_rr(rr: Reg16) -> Opcode {
    Opcode {
        name: format!("PUSH {:?}", rr),
        action: Box::new(move |env: &mut Environment| {
            let value = env.reg16_ext(rr);
            env.push(value);
        })
    }
}

pub fn build_conf_interrupts(enable: bool) -> Opcode {
    let name = if enable {"EI"} else  {"DI"};
    Opcode {
        name: name.to_string(),
        action: Box::new(move |env: &mut Environment| {
            env.state.reg.set_interrupts(enable);
        })
    }
}

pub fn build_im(im: u8) -> Opcode {
    Opcode {
        name: format!("IM {}", im),
        action: Box::new(move |env: &mut Environment| {
            env.state.reg.set_interrupt_mode(im);
        })
    }
}