rustzx_z80/opcode/
internal_block.rs

1use crate::{
2    opcode::BlockDir,
3    tables::{lookup8_r12, HALF_CARRY_SUB_TABLE, PARITY_TABLE, SZF3F5_TABLE},
4    RegName16, RegName8, Z80Bus, FLAG_CARRY, FLAG_F3, FLAG_F5, FLAG_HALF_CARRY, FLAG_PV, FLAG_SIGN,
5    FLAG_SUB, FLAG_ZERO, Z80,
6};
7
8/// ldi/ldd instruction group
9pub fn execute_ldi_ldd(cpu: &mut Z80, bus: &mut impl Z80Bus, dir: BlockDir) {
10    let src = bus.read(cpu.regs.get_hl(), 3);
11    let bc = cpu.regs.dec_reg_16(RegName16::BC);
12    bus.write(cpu.regs.get_de(), src, 3);
13    bus.wait_loop(cpu.regs.get_de(), 2);
14    match dir {
15        BlockDir::Inc => {
16            cpu.regs.inc_reg_16(RegName16::HL);
17            cpu.regs.inc_reg_16(RegName16::DE);
18        }
19        BlockDir::Dec => {
20            cpu.regs.dec_reg_16(RegName16::HL);
21            cpu.regs.dec_reg_16(RegName16::DE);
22        }
23    }
24    let mut flags = cpu.regs.get_flags();
25    flags &= !(FLAG_SUB | FLAG_HALF_CARRY | FLAG_PV | FLAG_F3 | FLAG_F5);
26    flags |= (bc != 0) as u8 * FLAG_PV;
27    let src_plus_a = src.wrapping_add(cpu.regs.get_acc());
28    flags |= (src_plus_a & 0x08 != 0) as u8 * FLAG_F3;
29    flags |= (src_plus_a & 0x02 != 0) as u8 * FLAG_F5;
30    cpu.regs.set_flags(flags);
31    // Clocks: <4 + 4> + 3 + 3 + 2 = 16
32}
33
34/// cpi/cpd instruction group
35pub fn execute_cpi_cpd(cpu: &mut Z80, bus: &mut impl Z80Bus, dir: BlockDir) -> bool {
36    let src = bus.read(cpu.regs.get_hl(), 3);
37    bus.wait_loop(cpu.regs.get_hl(), 5);
38    match dir {
39        BlockDir::Inc => {
40            cpu.regs.inc_reg_16(RegName16::HL);
41            cpu.regs.set_mem_ptr(cpu.regs.get_mem_ptr().wrapping_add(1));
42        }
43        BlockDir::Dec => {
44            cpu.regs.dec_reg_16(RegName16::HL);
45            cpu.regs.set_mem_ptr(cpu.regs.get_mem_ptr().wrapping_sub(1));
46        }
47    };
48    let bc = cpu.regs.dec_reg_16(RegName16::BC);
49    let acc = cpu.regs.get_acc();
50    let tmp = acc.wrapping_sub(src);
51    let mut flags = cpu.regs.get_flags() & FLAG_CARRY;
52    flags |= FLAG_SUB;
53    flags |= (bc != 0) as u8 * FLAG_PV;
54    flags |= (tmp == 0) as u8 * FLAG_ZERO;
55    flags |= tmp & FLAG_SIGN;
56    let lookup = lookup8_r12(acc, src, tmp);
57    let half_borrow = HALF_CARRY_SUB_TABLE[(lookup & 0x07) as usize];
58    flags |= half_borrow;
59    let tmp2 = if half_borrow != 0 {
60        tmp.wrapping_sub(1)
61    } else {
62        tmp
63    };
64    flags |= ((tmp2 & 0x08) != 0) as u8 * FLAG_F3;
65    flags |= ((tmp2 & 0x02) != 0) as u8 * FLAG_F5;
66    cpu.regs.set_flags(flags);
67    // Clocks: <4 + 4> + 3 + 5 = 16
68    tmp == 0
69}
70
71/// INI/IND instruction group
72///
73/// Returns last written value at (HL)
74pub fn execute_ini_ind(cpu: &mut Z80, bus: &mut impl Z80Bus, dir: BlockDir) -> u8 {
75    bus.wait_no_mreq(cpu.regs.get_ir(), 1);
76    let src = bus.read_io(cpu.regs.get_bc());
77    bus.write(cpu.regs.get_hl(), src, 3);
78    match dir {
79        BlockDir::Inc => {
80            cpu.regs.inc_reg_16(RegName16::HL);
81            cpu.regs.set_mem_ptr(cpu.regs.get_bc().wrapping_add(1));
82        }
83        BlockDir::Dec => {
84            cpu.regs.dec_reg_16(RegName16::HL);
85            cpu.regs.set_mem_ptr(cpu.regs.get_bc().wrapping_sub(1));
86        }
87    };
88    let b = cpu.regs.dec_reg_8(RegName8::B);
89    let mut flags = 0u8;
90    flags |= SZF3F5_TABLE[b as usize];
91    flags |= ((src & 0x80) != 0) as u8 * FLAG_SUB;
92    let c = match dir {
93        BlockDir::Inc => cpu.regs.get_reg_8(RegName8::C).wrapping_add(1),
94        BlockDir::Dec => cpu.regs.get_reg_8(RegName8::C).wrapping_sub(1),
95    };
96    // (HL) + ( C (+ or -) 1) & 0xFF
97    let (k, k_carry) = c.overflowing_add(src);
98    flags |= k_carry as u8 * (FLAG_CARRY | FLAG_HALF_CARRY);
99    // Parity of (k & 7) xor B is PV flag
100    flags |= PARITY_TABLE[((k & 0x07) ^ b) as usize];
101    cpu.regs.set_flags(flags);
102
103    src
104}
105
106/// OUTI/OUTD instruction group
107/// Returns last read value at (HL)
108pub fn execute_outi_outd(cpu: &mut Z80, bus: &mut impl Z80Bus, dir: BlockDir) -> u8 {
109    bus.wait_no_mreq(cpu.regs.get_ir(), 1);
110    let src = bus.read(cpu.regs.get_hl(), 3);
111    let b = cpu.regs.dec_reg_8(RegName8::B);
112
113    match dir {
114        BlockDir::Inc => {
115            cpu.regs.inc_reg_16(RegName16::HL);
116            cpu.regs.set_mem_ptr(cpu.regs.get_bc().wrapping_add(1));
117        }
118        BlockDir::Dec => {
119            cpu.regs.dec_reg_16(RegName16::HL);
120            cpu.regs.set_mem_ptr(cpu.regs.get_bc().wrapping_sub(1));
121        }
122    };
123
124    bus.write_io(cpu.regs.get_bc(), src);
125
126    let l = cpu.regs.get_l();
127    let mut flags = 0u8;
128    flags |= SZF3F5_TABLE[b as usize];
129    flags |= ((src & 0x80) != 0) as u8 * FLAG_SUB;
130    // L + (HL)
131    let (k, k_carry) = l.overflowing_add(src);
132    flags |= k_carry as u8 * (FLAG_CARRY | FLAG_HALF_CARRY);
133    // Parity of (k & 7) xor B is PV flag
134    flags |= PARITY_TABLE[((k & 0x07) ^ b) as usize];
135    cpu.regs.set_flags(flags);
136
137    src
138}