rustzx_core/emulator/fastload/tap.rs
1// emulator
2use crate::{emulator::Emulator, host::Host, zx::tape::TapeImpl, Result};
3use rustzx_z80::{RegName16, Z80Bus, FLAG_CARRY, FLAG_ZERO};
4
5pub fn fast_load_tap<H: Host>(emulator: &mut Emulator<H>) -> Result<()> {
6    // So, at current moment we at 0x056C in 48K Rom.
7    // AF contains some garbage. so we need to swap if with A'F'
8    emulator.cpu.regs.swap_af_alt();
9    // now we have type of block at A and flags before LD-BYTES at F
10    let mut f = emulator.cpu.regs.get_flags();
11    let mut acc = emulator.cpu.regs.get_acc();
12    // variable to store resulting flags
13    let mut result_flags;
14    // destination address in RAM
15    let mut dest = emulator.cpu.regs.get_reg_16(RegName16::IX);
16    // remaining length
17    let mut length = emulator.cpu.regs.get_reg_16(RegName16::DE);
18    // parity accumulator and current byte (h, l) regs
19    let (mut parity_acc, mut current_byte) = (0, 0);
20    // move to next block
21    if !emulator.controller.tape.next_block()? {
22        return Ok(());
23    }
24
25    'loader: loop {
26        // if we still on block
27        if let Some(byte) = emulator.controller.tape.next_block_byte()? {
28            // set current byte, shift position and do parity check iteration
29            current_byte = byte;
30            parity_acc ^= current_byte;
31            // no bytes left, set A to parity accumulator (works as in ROM)
32            // and check parity last time
33            if length == 0 {
34                acc = parity_acc;
35                // consider we CAN have parity error
36                result_flags = Some(0);
37                // if checksum correct set carry to prevent error
38                if acc == 0 {
39                    result_flags = Some(FLAG_CARRY);
40                }
41                break 'loader;
42            }
43            // block type check, first byte
44            if (f & FLAG_ZERO) == 0 {
45                acc ^= current_byte;
46                // if type wrong
47                if acc != 0 {
48                    result_flags = Some(0);
49                    break 'loader;
50                }
51                // type check passed, go to next byte;
52                f |= FLAG_ZERO;
53                continue;
54            }
55            // LOAD
56            if (f & FLAG_CARRY) != 0 {
57                emulator.controller.write_internal(dest, current_byte);
58            // VERIFY
59            } else {
60                // check for parity each byte, if this fails - set flags to error state
61                acc = emulator.controller.memory.read(dest) ^ current_byte;
62                if acc != 0 {
63                    result_flags = Some(0);
64                    break 'loader;
65                }
66            }
67            // move destination pointer and decrease count of remaining bytes
68            dest = dest.wrapping_add(1);
69            length -= 1;
70        } else {
71            // this happens if requested length and provided are not matched
72            result_flags = Some(FLAG_ZERO);
73            break 'loader;
74        }
75    }
76    // set regs to new state
77    emulator.cpu.regs.set_reg_16(RegName16::IX, dest);
78    emulator.cpu.regs.set_reg_16(RegName16::DE, length);
79    emulator
80        .cpu
81        .regs
82        .set_hl(u16::from_le_bytes([current_byte, parity_acc]));
83    emulator.cpu.regs.set_acc(acc);
84    // set new flag, if something changed
85    if let Some(new_flags) = result_flags {
86        f = new_flags;
87        // perform RET
88        emulator.cpu.pop_pc_from_stack(&mut emulator.controller);
89    }
90    emulator.cpu.regs.set_flags(f);
91    Ok(())
92}