Code for handling word RAM, a 256KB block of RAM that can be exchanged between the main CPU and the sub CPU
Word RAM is 256KB
11pub const ADDRESS_MASK: u32 = 0x03FFFF;
Word RAM is mapped to $080000-$0BFFFF in sub CPU memory map
14pub const SUB_BASE_ADDRESS: u32 = 0x080000;
16const WORD_RAM_LEN: usize = 256 * 1024; 17 18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 19pub enum WordRamMode { 20 #[default] 21 TwoM, 22 OneM, 23} 24 25impl WordRamMode { 26 fn to_bit(self) -> bool { 27 self == Self::OneM 28 } 29 30 fn from_bit(bit: bool) -> Self { 31 if bit { Self::OneM } else { Self::TwoM } 32 } 33} 34 35#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 36pub enum WordRamPriorityMode { 37 #[default] 38 Off, 39 Underwrite, 40 Overwrite, 41 Invalid, 42} 43 44impl WordRamPriorityMode { 45 pub fn to_bits(self) -> u8 { 46 match self { 47 Self::Off => 0x00, 48 Self::Underwrite => 0x01, 49 Self::Overwrite => 0x02, 50 Self::Invalid => 0x03, 51 } 52 } 53 54 pub fn from_bits(bits: u8) -> Self { 55 match bits & 0x03 { 56 0x00 => Self::Off, 57 0x01 => Self::Underwrite, 58 0x02 => Self::Overwrite, 59 0x03 => Self::Invalid, 60 _ => unreachable!("value & 0x03 is always <= 0x03"), 61 } 62 } 63} 64 65#[derive(Debug, Clone, Copy, PartialEq, Eq)] 66pub enum Nibble { 67 High, 68 Low, 69} 70 71#[derive(Debug, Clone, Copy, PartialEq, Eq)] 72enum WordRamSubMapResult { 73 None, 74 Byte(u32), 75 Pixel(u32), 76} 77 78#[derive(Debug, Clone, Encode, Decode)] 79pub struct WordRam { 80 ram: BoxedByteArray<WORD_RAM_LEN>, 81 sub_buffered_writes: Vec<(u32, u8)>, 82 sub_blocked_read: bool, 83 mode: WordRamMode, 84 priority_mode: WordRamPriorityMode, 85 owner_2m: ScdCpu, 86 bank_0_owner_1m: ScdCpu, 87 swap_request: bool, 88}
N cells, 8 pixels vertically, 8 pixels horizontally, 2 pixels per byte
96const CELL_IMAGE_H_SIZE_BYTES: u32 = 64 * 8 / 2; 97 98impl WordRam { 99 #[must_use] 100 pub fn new() -> Self { 101 Self { 102 ram: BoxedByteArray::new(), 103 sub_buffered_writes: Vec::with_capacity(5), 104 sub_blocked_read: false, 105 mode: WordRamMode::default(), 106 priority_mode: WordRamPriorityMode::default(), 107 owner_2m: ScdCpu::Main, 108 bank_0_owner_1m: ScdCpu::Main, 109 swap_request: false, 110 } 111 } 112 113 #[must_use] 114 pub fn mode(&self) -> WordRamMode { 115 self.mode 116 } 117 118 #[must_use] 119 pub fn read_control(&self) -> u8 { 120 let (dmna, ret) = match self.mode { 121 WordRamMode::TwoM => { 122 let dmna = self.owner_2m == ScdCpu::Sub; 123 let ret = !dmna; 124 (dmna, ret) 125 } 126 WordRamMode::OneM => { 127 let dmna = self.swap_request; 128 let ret = self.bank_0_owner_1m == ScdCpu::Sub; 129 (dmna, ret) 130 } 131 }; 132 133 (u8::from(self.mode.to_bit()) << 2) | (u8::from(dmna) << 1) | u8::from(ret) 134 } 135 136 pub(crate) fn main_cpu_write_control(&mut self, value: u8) { 137 let dmna = value.bit(1); 138 139 // DMNA=1 always returns 2M word RAM to sub CPU, regardless of mode 140 if dmna { 141 self.owner_2m = ScdCpu::Sub; 142 self.flush_buffered_sub_writes(); 143 self.sub_blocked_read = false; 144 } 145 146 // In 1M mode, setting DMNA=0 sends a swap request to the sub CPU 147 if self.mode == WordRamMode::OneM && !dmna { 148 self.swap_request = true; 149 } 150 151 log::trace!("Main CPU control write; DMNA={}, mode={:?}", u8::from(dmna), self.mode); 152 } 153 154 pub(crate) fn sub_cpu_write_control(&mut self, value: u8) { 155 self.mode = WordRamMode::from_bit(value.bit(2)); 156 let ret = value.bit(0); 157 158 // RET=1 always returns 2M word RAM to main CPU, regardless of mode 159 if ret { 160 self.owner_2m = ScdCpu::Main; 161 } 162 163 let prev_bank_0_owner = self.bank_0_owner_1m; 164 // In 1M mode, RET returns the given bank to the main CPU 165 // RET=0 -> main owns bank 0, sub owns bank 1 166 // RET=1 -> main owns bank 1, sub owns bank 0 167 self.bank_0_owner_1m = if ret { ScdCpu::Sub } else { ScdCpu::Main }; 168 169 // Only clear swap request if 1M bank 0 owner changed 170 if prev_bank_0_owner != self.bank_0_owner_1m { 171 self.swap_request = false; 172 } 173 174 self.priority_mode = WordRamPriorityMode::from_bits(value >> 3); 175 176 log::trace!( 177 "Sub CPU control write; RET={}, mode={:?}, priority_mode={:?}", 178 u8::from(ret), 179 self.mode, 180 self.priority_mode 181 ); 182 } 183 184 fn main_cpu_map_address(&self, address: u32) -> Option<u32> { 185 let address = address & 0x3FFFF; 186 match self.mode { 187 WordRamMode::TwoM => (self.owner_2m == ScdCpu::Main).then_some(address), 188 WordRamMode::OneM => { 189 if address <= 0x1FFFF { 190 Some(determine_1m_address(address, ScdCpu::Main, self.bank_0_owner_1m)) 191 } else { 192 match address & 0x1FFFF { 193 address @ 0x00000..=0x0FFFF => { 194 // V32xH64 image 195 Some(map_cell_image_address( 196 address & 0xFFFF, 197 CELL_IMAGE_V32_SIZE_BYTES, 198 self.bank_0_owner_1m, 199 0x00000, 200 )) 201 } 202 address @ 0x10000..=0x17FFF => { 203 // V16xH64 image 204 Some(map_cell_image_address( 205 address & 0x7FFF, 206 CELL_IMAGE_V16_SIZE_BYTES, 207 self.bank_0_owner_1m, 208 0x10000, 209 )) 210 } 211 address @ 0x18000..=0x1BFFF => { 212 // V8xH64 image 213 Some(map_cell_image_address( 214 address & 0x3FFF, 215 CELL_IMAGE_V8_SIZE_BYTES, 216 self.bank_0_owner_1m, 217 0x18000, 218 )) 219 } 220 address @ 0x1C000..=0x1DFFF => { 221 // V4xH64 image #1 222 Some(map_cell_image_address( 223 address & 0x1FFF, 224 CELL_IMAGE_V4_SIZE_BYTES, 225 self.bank_0_owner_1m, 226 0x1C000, 227 )) 228 } 229 address @ 0x1E000..=0x1FFFF => { 230 // V4xH64 image #2 231 Some(map_cell_image_address( 232 address & 0x1FFF, 233 CELL_IMAGE_V4_SIZE_BYTES, 234 self.bank_0_owner_1m, 235 0x1E000, 236 )) 237 } 238 _ => unreachable!("address masked with 0x1FFFF"), 239 } 240 } 241 } 242 } 243 } 244 245 #[must_use] 246 pub fn main_cpu_read_ram(&self, address: u32) -> u8 { 247 match self.main_cpu_map_address(address) { 248 None => 0x00, 249 Some(addr) => self.ram[addr as usize], 250 } 251 } 252 253 pub(crate) fn main_cpu_write_ram(&mut self, address: u32, value: u8) { 254 match self.main_cpu_map_address(address) { 255 None => {} 256 Some(addr) => { 257 self.ram[addr as usize] = value; 258 } 259 } 260 } 261 262 fn sub_cpu_map_address(&self, address: u32) -> WordRamSubMapResult { 263 match (self.mode, address) { 264 (WordRamMode::TwoM, 0x080000..=0x0BFFFF) => { 265 WordRamSubMapResult::Byte(address & ADDRESS_MASK) 266 } 267 (WordRamMode::TwoM, 0x0C0000..=0x0DFFFF) => WordRamSubMapResult::None, 268 (WordRamMode::OneM, 0x080000..=0x0BFFFF) => { 269 let byte_addr = determine_1m_address( 270 (address & 0x3FFFF) >> 1, 271 ScdCpu::Sub, 272 self.bank_0_owner_1m, 273 ); 274 WordRamSubMapResult::Pixel((byte_addr << 1) | (address & 0x000001)) 275 } 276 (WordRamMode::OneM, 0x0C0000..=0x0DFFFF) => WordRamSubMapResult::Byte( 277 determine_1m_address(address & 0x1FFFF, ScdCpu::Sub, self.bank_0_owner_1m), 278 ), 279 _ => panic!("Invalid sub CPU word RAM address: {address:06X}"), 280 } 281 } 282 283 pub(crate) fn sub_cpu_read_ram(&mut self, address: u32) -> u8 { 284 match self.sub_cpu_map_address(address) { 285 WordRamSubMapResult::None => 0, 286 WordRamSubMapResult::Byte(addr) => { 287 self.sub_blocked_read |= self.is_sub_access_blocked(); 288 self.ram[addr as usize] 289 } 290 WordRamSubMapResult::Pixel(pixel_addr) => { 291 let byte_addr = (pixel_addr >> 1) as usize; 292 if pixel_addr.bit(0) { 293 self.ram[byte_addr] & 0x0F 294 } else { 295 self.ram[byte_addr] >> 4 296 } 297 } 298 } 299 } 300 301 #[must_use] 302 pub fn sub_cpu_peek_ram(&self, address: u32) -> u8 { 303 match self.sub_cpu_map_address(address) { 304 WordRamSubMapResult::None => 0, 305 WordRamSubMapResult::Byte(addr) => self.ram[addr as usize], 306 WordRamSubMapResult::Pixel(pixel_addr) => { 307 let byte_addr = (pixel_addr >> 1) as usize; 308 if pixel_addr.bit(0) { 309 self.ram[byte_addr] & 0x0F 310 } else { 311 self.ram[byte_addr] >> 4 312 } 313 } 314 } 315 } 316 317 pub(crate) fn sub_cpu_write_ram(&mut self, address: u32, value: u8) { 318 match self.sub_cpu_map_address(address) { 319 WordRamSubMapResult::None => {} 320 WordRamSubMapResult::Byte(byte_addr) => { 321 if !self.is_sub_access_blocked() { 322 self.ram[byte_addr as usize] = value; 323 } else { 324 self.sub_buffered_writes.push((byte_addr, value)); 325 } 326 } 327 WordRamSubMapResult::Pixel(pixel_addr) => { 328 self.write_1m_pixel(pixel_addr, value & 0x0F); 329 } 330 } 331 }
Is sub CPU access to word RAM currently blocked (i.e. in 2M mode and main CPU owns word RAM)
Did the sub CPU access word RAM while access was blocked
345 fn flush_buffered_sub_writes(&mut self) { 346 for (address, byte) in self.sub_buffered_writes.drain(..) { 347 self.ram[address as usize] = byte; 348 } 349 } 350 351 pub(crate) fn graphics_write_ram(&mut self, address: u32, nibble: Nibble, pixel: u8) { 352 match self.sub_cpu_map_address(address) { 353 WordRamSubMapResult::None => {} 354 WordRamSubMapResult::Byte(addr) => { 355 let current_value = self.ram[addr as usize]; 356 let current_nibble = match nibble { 357 Nibble::High => current_value >> 4, 358 Nibble::Low => current_value & 0x0F, 359 }; 360 361 let should_write = match self.priority_mode { 362 WordRamPriorityMode::Off | WordRamPriorityMode::Invalid => true, 363 WordRamPriorityMode::Underwrite => current_nibble == 0, 364 WordRamPriorityMode::Overwrite => pixel != 0, 365 }; 366 367 if should_write { 368 let new_value = match nibble { 369 Nibble::High => (pixel << 4) | (current_value & 0x0F), 370 Nibble::Low => pixel | (current_value & 0xF0), 371 }; 372 self.ram[addr as usize] = new_value; 373 } 374 } 375 WordRamSubMapResult::Pixel(addr) => { 376 // High nibble is ignored for pixel writes 377 if nibble == Nibble::Low { 378 self.write_1m_pixel(addr, pixel); 379 } 380 } 381 } 382 } 383 384 fn write_1m_pixel(&mut self, pixel_addr: u32, pixel: u8) { 385 let byte_addr = (pixel_addr >> 1) as usize; 386 let current_byte = self.ram[byte_addr]; 387 let current_nibble = 388 if pixel_addr.bit(0) { current_byte & 0x0F } else { current_byte >> 4 }; 389 390 let should_write = match self.priority_mode { 391 WordRamPriorityMode::Off | WordRamPriorityMode::Invalid => true, 392 WordRamPriorityMode::Underwrite => current_nibble == 0, 393 WordRamPriorityMode::Overwrite => pixel != 0, 394 }; 395 396 if should_write { 397 let new_value = if pixel_addr.bit(0) { 398 pixel | (current_byte & 0xF0) 399 } else { 400 (pixel << 4) | (current_byte & 0x0F) 401 }; 402 self.ram[byte_addr] = new_value; 403 } 404 } 405 406 pub(crate) fn dma_write(&mut self, address: u32, value: u8) { 407 // Word RAM DMA writes should go to $080000 in 2M mode and $0C0000 in 1M mode 408 // In 1M mode, $080000-$0BFFFF is a dot image of word RAM, and the raw bytes are at $0C0000-$0DFFFF 409 let base_address = match self.mode { 410 WordRamMode::TwoM => { 411 assert!(address <= 0x03FFFF); 412 SUB_BASE_ADDRESS 413 } 414 WordRamMode::OneM => { 415 assert!(address <= 0x01FFFF); 416 0x0C0000 417 } 418 }; 419 420 self.sub_cpu_write_ram(base_address | address, value); 421 } 422 423 #[must_use] 424 pub fn priority_mode(&self) -> WordRamPriorityMode { 425 self.priority_mode 426 } 427 428 #[must_use] 429 pub fn debug_view(&mut self) -> impl DebugMemoryView { 430 DebugBytesView(self.ram.as_mut_slice()) 431 } 432} 433 434impl Default for WordRam { 435 fn default() -> Self { 436 Self::new() 437 } 438} 439 440fn determine_1m_address(address: u32, cpu: ScdCpu, bank_0_owner: ScdCpu) -> u32 { 441 ((address & !0x01) << 1) | (u32::from(cpu != bank_0_owner) << 1) | (address & 0x01) 442} 443 444#[inline] 445fn map_cell_image_address( 446 masked_address: u32, 447 v_size_bytes: u32, 448 bank_0_owner: ScdCpu, 449 base_word_ram_addr: u32, 450) -> u32 { 451 let row = (masked_address & (v_size_bytes - 1)) >> 2; 452 let col = masked_address / v_size_bytes; 453 454 let byte_addr = 455 base_word_ram_addr | (row * CELL_IMAGE_H_SIZE_BYTES) | (col << 2) | (masked_address & 0x03); 456 determine_1m_address(byte_addr, ScdCpu::Main, bank_0_owner) 457} 458 459#[cfg(test)] 460mod tests { 461 use super::*; 462 463 #[test] 464 fn word_ram_main_cpu_2m_mapping() { 465 let mut word_ram = WordRam::new(); 466 word_ram.mode = WordRamMode::TwoM; 467 468 word_ram.owner_2m = ScdCpu::Main; 469 assert_eq!(Some(0x000000), word_ram.main_cpu_map_address(0x200000)); 470 assert_eq!(Some(0x03FFFF), word_ram.main_cpu_map_address(0x23FFFF)); 471 472 word_ram.owner_2m = ScdCpu::Sub; 473 assert_eq!(None, word_ram.main_cpu_map_address(0x200000)); 474 assert_eq!(None, word_ram.main_cpu_map_address(0x23FFFF)); 475 } 476 477 #[test] 478 fn word_ram_main_cpu_1m_mapping() { 479 let mut word_ram = WordRam::new(); 480 word_ram.mode = WordRamMode::OneM; 481 482 word_ram.bank_0_owner_1m = ScdCpu::Main; 483 assert_eq!(Some(0x000000), word_ram.main_cpu_map_address(0x200000)); 484 assert_eq!(Some(0x020001), word_ram.main_cpu_map_address(0x210001)); 485 assert_eq!(Some(0x020004), word_ram.main_cpu_map_address(0x210002)); 486 assert_eq!(Some(0x03FFFD), word_ram.main_cpu_map_address(0x21FFFF)); 487 488 word_ram.bank_0_owner_1m = ScdCpu::Sub; 489 assert_eq!(Some(0x000002), word_ram.main_cpu_map_address(0x200000)); 490 assert_eq!(Some(0x020003), word_ram.main_cpu_map_address(0x210001)); 491 assert_eq!(Some(0x03FFFF), word_ram.main_cpu_map_address(0x21FFFF)); 492 } 493 494 #[test] 495 fn word_ram_main_cpu_1m_cell_image_mapping() { 496 let mut word_ram = WordRam::new(); 497 word_ram.mode = WordRamMode::OneM; 498 499 // V32-cell 500 word_ram.bank_0_owner_1m = ScdCpu::Main; 501 assert_eq!(Some(0x000000), word_ram.main_cpu_map_address(0x220000)); 502 assert_eq!(Some(0x000001), word_ram.main_cpu_map_address(0x220001)); 503 assert_eq!(Some(0x000004), word_ram.main_cpu_map_address(0x220002)); 504 assert_eq!(Some(0x000005), word_ram.main_cpu_map_address(0x220003)); 505 506 assert_eq!(Some(0x000200), word_ram.main_cpu_map_address(0x220004)); 507 assert_eq!(Some(0x000201), word_ram.main_cpu_map_address(0x220005)); 508 assert_eq!(Some(0x000205), word_ram.main_cpu_map_address(0x220007)); 509 510 assert_eq!(Some(0x000008), word_ram.main_cpu_map_address(0x220400)); 511 assert_eq!(Some(0x000009), word_ram.main_cpu_map_address(0x220401)); 512 assert_eq!(Some(0x00000C), word_ram.main_cpu_map_address(0x220402)); 513 assert_eq!(Some(0x000208), word_ram.main_cpu_map_address(0x220404)); 514 515 assert_eq!(Some(0x000010), word_ram.main_cpu_map_address(0x220800)); 516 517 assert_eq!(Some(0x01FFFD), word_ram.main_cpu_map_address(0x22FFFF)); 518 519 // V16-cell 520 assert_eq!(Some(0x020000), word_ram.main_cpu_map_address(0x230000)); 521 assert_eq!(Some(0x020001), word_ram.main_cpu_map_address(0x230001)); 522 assert_eq!(Some(0x020004), word_ram.main_cpu_map_address(0x230002)); 523 524 assert_eq!(Some(0x020200), word_ram.main_cpu_map_address(0x230004)); 525 assert_eq!(Some(0x020201), word_ram.main_cpu_map_address(0x230005)); 526 527 assert_eq!(Some(0x020008), word_ram.main_cpu_map_address(0x230200)); 528 assert_eq!(Some(0x020009), word_ram.main_cpu_map_address(0x230201)); 529 530 assert_eq!(Some(0x020010), word_ram.main_cpu_map_address(0x230400)); 531 532 assert_eq!(Some(0x02FFFD), word_ram.main_cpu_map_address(0x237FFF)); 533 534 // V8-cell 535 assert_eq!(Some(0x030000), word_ram.main_cpu_map_address(0x238000)); 536 assert_eq!(Some(0x030001), word_ram.main_cpu_map_address(0x238001)); 537 538 assert_eq!(Some(0x030200), word_ram.main_cpu_map_address(0x238004)); 539 assert_eq!(Some(0x030201), word_ram.main_cpu_map_address(0x238005)); 540 541 assert_eq!(Some(0x030008), word_ram.main_cpu_map_address(0x238100)); 542 assert_eq!(Some(0x030009), word_ram.main_cpu_map_address(0x238101)); 543 544 assert_eq!(Some(0x030010), word_ram.main_cpu_map_address(0x238200)); 545 546 assert_eq!(Some(0x037FFD), word_ram.main_cpu_map_address(0x23BFFF)); 547 548 // V4-cell #1 549 assert_eq!(Some(0x038000), word_ram.main_cpu_map_address(0x23C000)); 550 assert_eq!(Some(0x038001), word_ram.main_cpu_map_address(0x23C001)); 551 552 assert_eq!(Some(0x038200), word_ram.main_cpu_map_address(0x23C004)); 553 assert_eq!(Some(0x038201), word_ram.main_cpu_map_address(0x23C005)); 554 555 assert_eq!(Some(0x038008), word_ram.main_cpu_map_address(0x23C080)); 556 assert_eq!(Some(0x038009), word_ram.main_cpu_map_address(0x23C081)); 557 558 assert_eq!(Some(0x038010), word_ram.main_cpu_map_address(0x23C100)); 559 560 assert_eq!(Some(0x03BFFD), word_ram.main_cpu_map_address(0x23DFFF)); 561 562 // V4-cell #2 563 assert_eq!(Some(0x03C000), word_ram.main_cpu_map_address(0x23E000)); 564 assert_eq!(Some(0x03C001), word_ram.main_cpu_map_address(0x23E001)); 565 566 assert_eq!(Some(0x03C200), word_ram.main_cpu_map_address(0x23E004)); 567 assert_eq!(Some(0x03C201), word_ram.main_cpu_map_address(0x23E005)); 568 569 assert_eq!(Some(0x03C008), word_ram.main_cpu_map_address(0x23E080)); 570 assert_eq!(Some(0x03C009), word_ram.main_cpu_map_address(0x23E081)); 571 572 assert_eq!(Some(0x03C010), word_ram.main_cpu_map_address(0x23E100)); 573 574 assert_eq!(Some(0x03FFFD), word_ram.main_cpu_map_address(0x23FFFF)); 575 576 // Test with main CPU having bank 1 577 word_ram.bank_0_owner_1m = ScdCpu::Sub; 578 assert_eq!(Some(0x000002), word_ram.main_cpu_map_address(0x220000)); 579 assert_eq!(Some(0x03FFFF), word_ram.main_cpu_map_address(0x23FFFF)); 580 } 581 582 #[test] 583 fn word_ram_sub_cpu_2m_mapping_sub_owner() { 584 use WordRamSubMapResult as R; 585 586 let mut word_ram = WordRam::new(); 587 word_ram.mode = WordRamMode::TwoM; 588 589 word_ram.owner_2m = ScdCpu::Sub; 590 assert_eq!(R::Byte(0x000000), word_ram.sub_cpu_map_address(0x080000)); 591 assert_eq!(R::Byte(0x03FFFF), word_ram.sub_cpu_map_address(0x0BFFFF)); 592 593 assert_eq!(R::None, word_ram.sub_cpu_map_address(0x0C0000)); 594 assert_eq!(R::None, word_ram.sub_cpu_map_address(0x0DFFFF)); 595 } 596 597 #[test] 598 fn word_ram_sub_cpu_2m_mapping_main_owner() { 599 let mut word_ram = WordRam::new(); 600 word_ram.ram.fill(0xFF); 601 word_ram.mode = WordRamMode::TwoM; 602 word_ram.owner_2m = ScdCpu::Main; 603 604 // Valid addresses but access is blocked 605 word_ram.sub_cpu_write_ram(0x080000, 0x01); 606 word_ram.sub_cpu_write_ram(0x0BFFFF, 0x02); 607 608 // Invalid addresses 609 word_ram.sub_cpu_write_ram(0x0C0000, 0x03); 610 word_ram.sub_cpu_write_ram(0x0DFFFF, 0x04); 611 612 assert!(word_ram.ram.iter().copied().all(|value| value == 0xFF)); 613 assert_eq!(word_ram.sub_buffered_writes, vec![(0x00000, 0x01), (0x3FFFF, 0x02)]); 614 615 word_ram.flush_buffered_sub_writes(); 616 assert_eq!(word_ram.ram[0x00000], 0x01); 617 assert_eq!(word_ram.ram[0x3FFFF], 0x02); 618 } 619 620 #[test] 621 fn word_ram_sub_cpu_1m_byte_mapping() { 622 use WordRamSubMapResult as R; 623 624 let mut word_ram = WordRam::new(); 625 word_ram.mode = WordRamMode::OneM; 626 627 word_ram.bank_0_owner_1m = ScdCpu::Sub; 628 assert_eq!(R::Byte(0x000000), word_ram.sub_cpu_map_address(0x0C0000)); 629 assert_eq!(R::Byte(0x010000), word_ram.sub_cpu_map_address(0x0C8000)); 630 assert_eq!(R::Byte(0x03FFFD), word_ram.sub_cpu_map_address(0x0DFFFF)); 631 632 word_ram.bank_0_owner_1m = ScdCpu::Main; 633 assert_eq!(R::Byte(0x000002), word_ram.sub_cpu_map_address(0x0C0000)); 634 assert_eq!(R::Byte(0x010002), word_ram.sub_cpu_map_address(0x0C8000)); 635 assert_eq!(R::Byte(0x03FFFF), word_ram.sub_cpu_map_address(0x0DFFFF)); 636 } 637 638 #[test] 639 fn word_ram_sub_cpu_1m_pixel_mapping() { 640 use WordRamSubMapResult as R; 641 642 let mut word_ram = WordRam::new(); 643 word_ram.mode = WordRamMode::OneM; 644 645 word_ram.bank_0_owner_1m = ScdCpu::Sub; 646 assert_eq!(R::Pixel(0x000000), word_ram.sub_cpu_map_address(0x080000)); 647 assert_eq!(R::Pixel(0x000001), word_ram.sub_cpu_map_address(0x080001)); 648 assert_eq!(R::Pixel(0x000002), word_ram.sub_cpu_map_address(0x080002)); 649 assert_eq!(R::Pixel(0x000003), word_ram.sub_cpu_map_address(0x080003)); 650 assert_eq!(R::Pixel(0x000008), word_ram.sub_cpu_map_address(0x080004)); 651 assert_eq!(R::Pixel(0x000009), word_ram.sub_cpu_map_address(0x080005)); 652 assert_eq!(R::Pixel(0x07FFFA), word_ram.sub_cpu_map_address(0x0BFFFE)); 653 assert_eq!(R::Pixel(0x07FFFB), word_ram.sub_cpu_map_address(0x0BFFFF)); 654 655 word_ram.bank_0_owner_1m = ScdCpu::Main; 656 assert_eq!(R::Pixel(0x000004), word_ram.sub_cpu_map_address(0x080000)); 657 assert_eq!(R::Pixel(0x000005), word_ram.sub_cpu_map_address(0x080001)); 658 assert_eq!(R::Pixel(0x000006), word_ram.sub_cpu_map_address(0x080002)); 659 assert_eq!(R::Pixel(0x000007), word_ram.sub_cpu_map_address(0x080003)); 660 assert_eq!(R::Pixel(0x00000C), word_ram.sub_cpu_map_address(0x080004)); 661 assert_eq!(R::Pixel(0x00000D), word_ram.sub_cpu_map_address(0x080005)); 662 assert_eq!(R::Pixel(0x07FFFE), word_ram.sub_cpu_map_address(0x0BFFFE)); 663 assert_eq!(R::Pixel(0x07FFFF), word_ram.sub_cpu_map_address(0x0BFFFF)); 664 } 665}