iz80/
opcode.rs

1use super::environment::Environment;
2use super::registers::Reg16;
3
4type OpcodeFn = dyn Fn(&mut Environment) + Send + Sync;
5
6pub struct Opcode {
7    pub name: String,
8    pub cycles: u8,
9    pub cycles_conditional: u8,
10    pub action: Box<OpcodeFn>,
11}
12
13impl Opcode {
14    pub(crate) fn new<T>(name: String, action: T) -> Opcode
15        where T: Fn(&mut Environment) + Send + Sync + 'static {
16        Opcode {
17            name,
18            cycles: 0,
19            cycles_conditional: 0,
20            action: Box::new(action),
21        }
22    }
23
24    pub fn execute(&self, env: &mut Environment) {
25        (self.action)(env);
26    }
27
28    pub fn disasm(&self, env: &Environment) -> String {
29        let name = if self.name.contains("__index") {
30            self.name.replace("__index", &env.index_description())
31        } else {
32            self.name.clone()
33        };
34
35        if self.name.contains("nn") {
36            // Immediate argument 16 bits
37            let nn = env.peek16_pc();
38            let nn_str = format!("{nn:04x}h");
39            name.replace("nn", &nn_str)
40        } else if self.name.contains('n') {
41            // Immediate argument 8 bits
42            let n = env.peek_pc();
43            let n_str = format!("{n:02x}h");
44            name.replace('n', &n_str)
45        } else if self.name.contains('d') {
46            // Immediate argument 8 bits signed
47            // In assembly it's shown with 2 added as if it were from the opcode pc.
48            let d = env.peek_pc() as i8 as i16 + 2;
49            let d_str = format!("{d:+x}");
50            name.replace('d', &d_str)
51        } else {
52            name
53        }
54    }
55}
56
57pub fn build_not_an_opcode() -> Opcode {
58    Opcode::new(
59        "NOT_AN_OPCODE".to_string(),
60        |_: &mut Environment| {
61            panic!("Not an opcode")
62        }
63    )
64}
65
66pub fn build_nop() -> Opcode {
67    Opcode::new(
68        "NOP".to_string(),
69        |_: &mut Environment| {
70            // Nothing done
71        }
72    )
73}
74
75pub fn build_noni_nop() -> Opcode {
76    Opcode::new(
77        "NONINOP".to_string(),
78        |_: &mut Environment| {
79            // Nothing done
80        }
81    )
82}
83
84pub fn build_halt() -> Opcode {
85    Opcode::new(
86        "HALT".to_string(),
87        |env: &mut Environment| {
88            env.state.halted = true;
89        }
90    )
91}
92
93pub fn build_pop_rr(rr: Reg16) -> Opcode {
94    Opcode::new(
95        format!("POP {rr:?}"),
96        move |env: &mut Environment| {
97            let value = env.pop();
98            env.set_reg16(rr, value);
99        }
100    )
101}
102
103pub fn build_push_rr(rr: Reg16) -> Opcode {
104    Opcode::new(
105        format!("PUSH {rr:?}"),
106        move |env: &mut Environment| {
107            let value = env.reg16_ext(rr);
108            env.push(value);
109        }
110    )
111}
112
113pub fn build_disable_interrupts() -> Opcode {
114    Opcode::new(
115        "DI".to_string(),
116        |env: &mut Environment| {
117            env.state.reg.set_interrupts(false);
118        }
119    )
120}
121
122pub fn build_enable_interrupts() -> Opcode {
123    Opcode::new(
124        "EI".to_string(),
125        |env: &mut Environment| {
126            env.state.reg.set_interrupts(true);
127            env.state.int_just_enabled = true;
128        }
129    )
130}
131
132pub fn build_im(im: u8) -> Opcode {
133    Opcode::new(
134        format!("IM {im}"),
135        move |env: &mut Environment| {
136            env.state.reg.set_interrupt_mode(im);
137        }
138    )
139}