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
// emulator
use crate::{emulator::Emulator, host::Host, zx::tape::TapeImpl, Result};
use rustzx_z80::{RegName16, Z80Bus, FLAG_CARRY, FLAG_ZERO};
pub fn fast_load_tap<H: Host>(emulator: &mut Emulator<H>) -> Result<()> {
// So, at current moment we at 0x056C in 48K Rom.
// AF contains some garbage. so we need to swap if with A'F'
emulator.cpu.regs.swap_af_alt();
// now we have type of block at A and flags before LD-BYTES at F
let mut f = emulator.cpu.regs.get_flags();
let mut acc = emulator.cpu.regs.get_acc();
// variable to store resulting flags
let mut result_flags;
// destination address in RAM
let mut dest = emulator.cpu.regs.get_reg_16(RegName16::IX);
// remaining length
let mut length = emulator.cpu.regs.get_reg_16(RegName16::DE);
// parity accumulator and current byte (h, l) regs
let (mut parity_acc, mut current_byte) = (0, 0);
// move to next block
if !emulator.controller.tape.next_block()? {
return Ok(());
}
'loader: loop {
// if we still on block
if let Some(byte) = emulator.controller.tape.next_block_byte()? {
// set current byte, shift position and do parity check iteration
current_byte = byte;
parity_acc ^= current_byte;
// no bytes left, set A to parity accumulator (works as in ROM)
// and check parity last time
if length == 0 {
acc = parity_acc;
// consider we CAN have parity error
result_flags = Some(0);
// if checksum correct set carry to prevent error
if acc == 0 {
result_flags = Some(FLAG_CARRY);
}
break 'loader;
}
// block type check, first byte
if (f & FLAG_ZERO) == 0 {
acc ^= current_byte;
// if type wrong
if acc != 0 {
result_flags = Some(0);
break 'loader;
}
// type check passed, go to next byte;
f |= FLAG_ZERO;
continue;
}
// LOAD
if (f & FLAG_CARRY) != 0 {
emulator.controller.write_internal(dest, current_byte);
// VERIFY
} else {
// check for parity each byte, if this fails - set flags to error state
acc = emulator.controller.memory.read(dest) ^ current_byte;
if acc != 0 {
result_flags = Some(0);
break 'loader;
}
}
// move destination pointer and decrease count of remaining bytes
dest = dest.wrapping_add(1);
length -= 1;
} else {
// this happens if requested length and provided are not matched
result_flags = Some(FLAG_ZERO);
break 'loader;
}
}
// set regs to new state
emulator.cpu.regs.set_reg_16(RegName16::IX, dest);
emulator.cpu.regs.set_reg_16(RegName16::DE, length);
emulator
.cpu
.regs
.set_hl(u16::from_le_bytes([current_byte, parity_acc]));
emulator.cpu.regs.set_acc(acc);
// set new flag, if something changed
if let Some(new_flags) = result_flags {
f = new_flags;
// perform RET
emulator.cpu.pop_pc_from_stack(&mut emulator.controller);
}
emulator.cpu.regs.set_flags(f);
Ok(())
}