Game Pak prefetcher-related code

3use crate::bus::Bus;
4use bincode::{Decode, Encode};
5use std::array;

Prefetch holds up to 8 halfwords

8const PREFETCH_LEN: u8 = 8;
10#[derive(Debug, Clone, Encode, Decode)]
11pub struct GamePakPrefetcher {
12    buffer: [u16; PREFETCH_LEN as usize],
13    read_address: u32,
14    write_address: u32,
15    read_idx: u8,
16    write_idx: u8,
17    len: u8,
18    active: bool,
19    fetch_cycles_remaining: u64,
20}
21
22impl GamePakPrefetcher {
23    pub fn new() -> Self {
24        Self {
25            buffer: array::from_fn(|_| 0),
26            read_address: 0,
27            write_address: 0,
28            read_idx: 0,
29            write_idx: 0,
30            len: 0,
31            active: false,
32            fetch_cycles_remaining: 0,
33        }
34    }
35
36    pub fn empty(&self) -> bool {
37        self.len == 0
38    }
39
40    pub fn full(&self) -> bool {
41        self.len == PREFETCH_LEN
42    }
43
44    pub fn can_use_for(&self, address: u32) -> bool {
45        self.read_address == address && (self.active || !self.empty())
46    }
47
48    fn push(&mut self, opcode: u16) {
49        self.buffer[self.write_idx as usize] = opcode;
50        self.write_address += 2;
51        self.write_idx = (self.write_idx + 1) % PREFETCH_LEN;
52        self.len += 1;
53    }
54
55    fn pop(&mut self) -> u16 {
56        let opcode = self.buffer[self.read_idx as usize];
57        self.read_address += 2;
58        self.read_idx = (self.read_idx + 1) % PREFETCH_LEN;
59        self.len -= 1;
60
61        opcode
62    }
63}
64
65impl Bus {
66    pub fn prepare_prefetch_read(&mut self, address: u32) {
67        if self.prefetch.can_use_for(address) {
68            // Prefetch is already in the right spot
69            return;
70        }
71
72        self.finish_in_progress_fetch();
73
74        self.prefetch.read_address = address;
75        self.prefetch.write_address = address;
76        self.prefetch.read_idx = 0;
77        self.prefetch.write_idx = 0;
78        self.prefetch.len = 0;
79        self.prefetch.active = true;
80        self.prefetch.fetch_cycles_remaining = self.memory.control().rom_n_cycles(address);
81    }
82
83    fn finish_in_progress_fetch(&mut self) {
84        if self.prefetch.fetch_cycles_remaining == 1 {
85            // 1-cycle delay when stopping prefetch during last cycle of a fetch
86            self.state.cycles += 1;
87        }
88        self.cartridge.end_rom_burst();
89    }
90
91    pub fn prefetch_read(&mut self) -> u16 {
92        if self.prefetch.empty() {
93            if !self.prefetch.active {
94                self.prepare_prefetch_read(self.prefetch.write_address);
95            }
96
97            // Block until the first fetch completes
98            self.state.cycles += self.prefetch.fetch_cycles_remaining;
99            self.advance_prefetch(self.prefetch.fetch_cycles_remaining);
100        }
101
102        self.prefetch.pop()
103    }
104
105    pub fn advance_prefetch(&mut self, mut cycles: u64) {
106        if !self.prefetch.active {
107            return;
108        }
109
110        while cycles != 0 {
111            if self.prefetch.full() {
112                // When prefetch fills up, it stops until a read occurs while the buffer is empty
113                self.pause_prefetch();
114                break;
115            }
116
117            if cycles >= self.prefetch.fetch_cycles_remaining {
118                cycles -= self.prefetch.fetch_cycles_remaining;
119
120                let opcode = self.cartridge.read_rom(self.prefetch.write_address);
121                self.prefetch.push(opcode);
122
123                if !self.memory.control().prefetch_enabled {
124                    self.pause_prefetch();
125                    break;
126                }
127
128                self.prefetch.fetch_cycles_remaining =
129                    self.rom_access_cycles(self.prefetch.write_address);
130            } else {
131                self.prefetch.fetch_cycles_remaining -= cycles;
132                break;
133            }
134        }
135    }
136
137    fn pause_prefetch(&mut self) {
138        self.prefetch.active = false;
139        self.prefetch.fetch_cycles_remaining = 0;
140        self.cartridge.end_rom_burst();
141    }
142
143    pub fn stop_prefetch(&mut self) {
144        if self.prefetch.active {
145            self.finish_in_progress_fetch();
146        }
147
148        self.prefetch.active = false;
149        self.prefetch.fetch_cycles_remaining = 0;
150        self.prefetch.read_address = 0;
151    }
152}