(&mut self, addr: u64, n: usize)
| 200 | let (set_idx, way_idx, miss) = self.resolve_line(addr)?; |
| 201 | if miss { |
| 202 | self.stats.read_misses += 1; |
| 203 | } else { |
| 204 | self.stats.read_hits += 1; |
| 205 | } |
| 206 | let offset = (addr & ((1u64 << self.block_bits) - 1)) as usize; |
| 207 | let mut buf = [0u8; 8]; |
| 208 | buf[..n].copy_from_slice(&self.sets[set_idx].ways[way_idx].data[offset..offset + n]); |
| 209 | return Ok(u64::from_le_bytes(buf)); |
| 210 | } |
| 211 | |
| 212 | let block_mask = !((1u64 << self.block_bits) - 1); |
| 213 | let mut result = 0u64; |
| 214 | let mut last_block = u64::MAX; |
| 215 | let mut last_set = 0usize; |
| 216 | let mut last_way = 0usize; |
| 217 | |
| 218 | for i in 0..n { |
| 219 | let byte_addr = addr + i as u64; |
| 220 | let block = byte_addr & block_mask; |
| 221 | |
| 222 | let (set_idx, way_idx) = if block != last_block { |
| 223 | let (si, wi, miss) = self.resolve_line(byte_addr)?; |
| 224 | if miss { |
| 225 | self.stats.read_misses += 1; |
| 226 | } else { |
| 227 | self.stats.read_hits += 1; |
| 228 | } |
| 229 | last_block = block; |
| 230 | last_set = si; |
| 231 | last_way = wi; |
| 232 | (si, wi) |
| 233 | } else { |
| 234 | (last_set, last_way) |
| 235 | }; |
| 236 | |
| 237 | let offset = (byte_addr & !block_mask) as usize; |
| 238 | result |= (self.sets[set_idx].ways[way_idx].data[offset] as u64) << (i * 8); |
| 239 | } |
| 240 | Ok(result) |
| 241 | } |
| 242 | |
| 243 | fn write_n(&mut self, addr: u64, data: u64, n: usize) -> Result<(), VmError> { |
| 244 | if self.params.read_only { |
| 245 | return Err(VmError::WriteToRom); |
| 246 | } |
| 247 | |
| 248 | // Aligned 2/4/8-byte writes stay inside one line: resolve once and, when |
| 249 | // write-through, issue one sized store instead of a write_byte per byte. |
| 250 | if n > 1 && addr & (n as u64 - 1) == 0 { |
| 251 | let (set_idx, way_idx, miss) = self.resolve_line(addr)?; |
no test coverage detected