diff --git a/emu/src/bus.rs b/emu/src/bus.rs index 8fc2470..6c6d8d2 100644 --- a/emu/src/bus.rs +++ b/emu/src/bus.rs @@ -503,33 +503,33 @@ impl Bus { fn read_lcd_raw(&self, address: usize) -> u8 { match address { - 0x04000000 => self.lcd.dispcnt.get_byte(0), - 0x04000001 => self.lcd.dispcnt.get_byte(1), - 0x04000002 => self.lcd.green_swap.get_byte(0), - 0x04000003 => self.lcd.green_swap.get_byte(1), - 0x04000004 => self.lcd.dispstat.get_byte(0), - 0x04000005 => self.lcd.dispstat.get_byte(1), - 0x04000006 => self.lcd.vcount.get_byte(0), - 0x04000007 => self.lcd.vcount.get_byte(1), - 0x04000008 => self.lcd.bg0cnt.get_byte(0), - 0x04000009 => self.lcd.bg0cnt.get_byte(1), - 0x0400000A => self.lcd.bg1cnt.get_byte(0), - 0x0400000B => self.lcd.bg1cnt.get_byte(1), - 0x0400000C => self.lcd.bg2cnt.get_byte(0), - 0x0400000D => self.lcd.bg2cnt.get_byte(1), - 0x0400000E => self.lcd.bg3cnt.get_byte(0), - 0x0400000F => self.lcd.bg3cnt.get_byte(1), + 0x04000000 => self.lcd.registers.dispcnt.get_byte(0), + 0x04000001 => self.lcd.registers.dispcnt.get_byte(1), + 0x04000002 => self.lcd.registers.green_swap.get_byte(0), + 0x04000003 => self.lcd.registers.green_swap.get_byte(1), + 0x04000004 => self.lcd.registers.dispstat.get_byte(0), + 0x04000005 => self.lcd.registers.dispstat.get_byte(1), + 0x04000006 => self.lcd.registers.vcount.get_byte(0), + 0x04000007 => self.lcd.registers.vcount.get_byte(1), + 0x04000008 => self.lcd.registers.bg0cnt.get_byte(0), + 0x04000009 => self.lcd.registers.bg0cnt.get_byte(1), + 0x0400000A => self.lcd.registers.bg1cnt.get_byte(0), + 0x0400000B => self.lcd.registers.bg1cnt.get_byte(1), + 0x0400000C => self.lcd.registers.bg2cnt.get_byte(0), + 0x0400000D => self.lcd.registers.bg2cnt.get_byte(1), + 0x0400000E => self.lcd.registers.bg3cnt.get_byte(0), + 0x0400000F => self.lcd.registers.bg3cnt.get_byte(1), 0x04000010..=0x04000047 => panic!("Reading a write-only LCD I/O register"), - 0x04000048 => self.lcd.winin.get_byte(0), - 0x04000049 => self.lcd.winin.get_byte(1), - 0x0400004A => self.lcd.winout.get_byte(0), - 0x0400004B => self.lcd.winout.get_byte(1), - 0x0400004C => self.lcd.mosaic.get_byte(0), - 0x0400004D => self.lcd.mosaic.get_byte(1), - 0x04000050 => self.lcd.bldcnt.get_byte(0), - 0x04000051 => self.lcd.bldcnt.get_byte(1), - 0x04000052 => self.lcd.bldalpha.get_byte(0), - 0x04000053 => self.lcd.bldalpha.get_byte(1), + 0x04000048 => self.lcd.registers.winin.get_byte(0), + 0x04000049 => self.lcd.registers.winin.get_byte(1), + 0x0400004A => self.lcd.registers.winout.get_byte(0), + 0x0400004B => self.lcd.registers.winout.get_byte(1), + 0x0400004C => self.lcd.registers.mosaic.get_byte(0), + 0x0400004D => self.lcd.registers.mosaic.get_byte(1), + 0x04000050 => self.lcd.registers.bldcnt.get_byte(0), + 0x04000051 => self.lcd.registers.bldcnt.get_byte(1), + 0x04000052 => self.lcd.registers.bldalpha.get_byte(0), + 0x04000053 => self.lcd.registers.bldalpha.get_byte(1), 0x04000054..=0x04000055 => panic!("Reading a write-only LCD I/O register"), 0x0400004E..=0x0400004F | 0x04000056..=0x0400005F => { log("read on unused memory"); @@ -541,91 +541,91 @@ impl Bus { fn write_lcd_raw(&mut self, address: usize, value: u8) { match address { - 0x04000000 => self.lcd.dispcnt.set_byte(0, value), - 0x04000001 => self.lcd.dispcnt.set_byte(1, value), - 0x04000002 => self.lcd.green_swap.set_byte(0, value), - 0x04000003 => self.lcd.green_swap.set_byte(1, value), - 0x04000004 => self.lcd.dispstat.set_byte(0, value), - 0x04000005 => self.lcd.dispstat.set_byte(1, value), - 0x04000008 => self.lcd.bg0cnt.set_byte(0, value), - 0x04000006 => self.lcd.vcount.set_byte(0, value), - 0x04000007 => self.lcd.vcount.set_byte(1, value), - 0x04000009 => self.lcd.bg0cnt.set_byte(1, value), - 0x0400000A => self.lcd.bg1cnt.set_byte(0, value), - 0x0400000B => self.lcd.bg1cnt.set_byte(1, value), - 0x0400000C => self.lcd.bg2cnt.set_byte(0, value), - 0x0400000D => self.lcd.bg2cnt.set_byte(1, value), - 0x0400000E => self.lcd.bg3cnt.set_byte(0, value), - 0x0400000F => self.lcd.bg3cnt.set_byte(1, value), - 0x04000010 => self.lcd.bg0hofs.set_byte(0, value), - 0x04000011 => self.lcd.bg0hofs.set_byte(1, value), - 0x04000012 => self.lcd.bg0vofs.set_byte(0, value), - 0x04000013 => self.lcd.bg0vofs.set_byte(1, value), - 0x04000014 => self.lcd.bg1hofs.set_byte(0, value), - 0x04000015 => self.lcd.bg1hofs.set_byte(1, value), - 0x04000016 => self.lcd.bg1vofs.set_byte(0, value), - 0x04000017 => self.lcd.bg1vofs.set_byte(1, value), - 0x04000018 => self.lcd.bg2hofs.set_byte(0, value), - 0x04000019 => self.lcd.bg2hofs.set_byte(1, value), - 0x0400001A => self.lcd.bg2vofs.set_byte(0, value), - 0x0400001B => self.lcd.bg2vofs.set_byte(1, value), - 0x0400001C => self.lcd.bg3hofs.set_byte(0, value), - 0x0400001D => self.lcd.bg3hofs.set_byte(1, value), - 0x0400001E => self.lcd.bg3vofs.set_byte(0, value), - 0x0400001F => self.lcd.bg3vofs.set_byte(1, value), - 0x04000020 => self.lcd.bg2pa.set_byte(0, value), - 0x04000021 => self.lcd.bg2pa.set_byte(1, value), - 0x04000022 => self.lcd.bg2pb.set_byte(0, value), - 0x04000023 => self.lcd.bg2pb.set_byte(1, value), - 0x04000024 => self.lcd.bg2pc.set_byte(0, value), - 0x04000025 => self.lcd.bg2pc.set_byte(1, value), - 0x04000026 => self.lcd.bg2pd.set_byte(0, value), - 0x04000027 => self.lcd.bg2pd.set_byte(1, value), - 0x04000028 => self.lcd.bg2x.set_byte(0, value), - 0x04000029 => self.lcd.bg2x.set_byte(1, value), - 0x0400002A => self.lcd.bg2x.set_byte(2, value), - 0x0400002B => self.lcd.bg2x.set_byte(3, value), - 0x0400002C => self.lcd.bg2y.set_byte(0, value), - 0x0400002D => self.lcd.bg2y.set_byte(1, value), - 0x0400002E => self.lcd.bg2y.set_byte(2, value), - 0x0400002F => self.lcd.bg2y.set_byte(3, value), - 0x04000030 => self.lcd.bg3pa.set_byte(0, value), - 0x04000031 => self.lcd.bg3pa.set_byte(1, value), - 0x04000032 => self.lcd.bg3pb.set_byte(0, value), - 0x04000033 => self.lcd.bg3pb.set_byte(1, value), - 0x04000034 => self.lcd.bg3pc.set_byte(0, value), - 0x04000035 => self.lcd.bg3pc.set_byte(1, value), - 0x04000036 => self.lcd.bg3pd.set_byte(0, value), - 0x04000037 => self.lcd.bg3pd.set_byte(1, value), - 0x04000038 => self.lcd.bg3x.set_byte(0, value), - 0x04000039 => self.lcd.bg3x.set_byte(1, value), - 0x0400003A => self.lcd.bg3x.set_byte(2, value), - 0x0400003B => self.lcd.bg3x.set_byte(3, value), - 0x0400003C => self.lcd.bg3y.set_byte(0, value), - 0x0400003D => self.lcd.bg3y.set_byte(1, value), - 0x0400003E => self.lcd.bg3y.set_byte(2, value), - 0x0400003F => self.lcd.bg3y.set_byte(3, value), - 0x04000040 => self.lcd.win0h.set_byte(0, value), - 0x04000041 => self.lcd.win0h.set_byte(1, value), - 0x04000042 => self.lcd.win1h.set_byte(0, value), - 0x04000043 => self.lcd.win1h.set_byte(1, value), - 0x04000044 => self.lcd.win0v.set_byte(0, value), - 0x04000045 => self.lcd.win0v.set_byte(1, value), - 0x04000046 => self.lcd.win1v.set_byte(0, value), - 0x04000047 => self.lcd.win1v.set_byte(1, value), - 0x04000048 => self.lcd.winin.set_byte(0, value), - 0x04000049 => self.lcd.winin.set_byte(1, value), - 0x0400004A => self.lcd.winout.set_byte(0, value), - 0x0400004B => self.lcd.winout.set_byte(1, value), - 0x0400004C => self.lcd.mosaic.set_byte(0, value), - 0x0400004D => self.lcd.mosaic.set_byte(1, value), + 0x04000000 => self.lcd.registers.dispcnt.set_byte(0, value), + 0x04000001 => self.lcd.registers.dispcnt.set_byte(1, value), + 0x04000002 => self.lcd.registers.green_swap.set_byte(0, value), + 0x04000003 => self.lcd.registers.green_swap.set_byte(1, value), + 0x04000004 => self.lcd.registers.dispstat.set_byte(0, value), + 0x04000005 => self.lcd.registers.dispstat.set_byte(1, value), + 0x04000008 => self.lcd.registers.bg0cnt.set_byte(0, value), + 0x04000006 => self.lcd.registers.vcount.set_byte(0, value), + 0x04000007 => self.lcd.registers.vcount.set_byte(1, value), + 0x04000009 => self.lcd.registers.bg0cnt.set_byte(1, value), + 0x0400000A => self.lcd.registers.bg1cnt.set_byte(0, value), + 0x0400000B => self.lcd.registers.bg1cnt.set_byte(1, value), + 0x0400000C => self.lcd.registers.bg2cnt.set_byte(0, value), + 0x0400000D => self.lcd.registers.bg2cnt.set_byte(1, value), + 0x0400000E => self.lcd.registers.bg3cnt.set_byte(0, value), + 0x0400000F => self.lcd.registers.bg3cnt.set_byte(1, value), + 0x04000010 => self.lcd.registers.bg0hofs.set_byte(0, value), + 0x04000011 => self.lcd.registers.bg0hofs.set_byte(1, value), + 0x04000012 => self.lcd.registers.bg0vofs.set_byte(0, value), + 0x04000013 => self.lcd.registers.bg0vofs.set_byte(1, value), + 0x04000014 => self.lcd.registers.bg1hofs.set_byte(0, value), + 0x04000015 => self.lcd.registers.bg1hofs.set_byte(1, value), + 0x04000016 => self.lcd.registers.bg1vofs.set_byte(0, value), + 0x04000017 => self.lcd.registers.bg1vofs.set_byte(1, value), + 0x04000018 => self.lcd.registers.bg2hofs.set_byte(0, value), + 0x04000019 => self.lcd.registers.bg2hofs.set_byte(1, value), + 0x0400001A => self.lcd.registers.bg2vofs.set_byte(0, value), + 0x0400001B => self.lcd.registers.bg2vofs.set_byte(1, value), + 0x0400001C => self.lcd.registers.bg3hofs.set_byte(0, value), + 0x0400001D => self.lcd.registers.bg3hofs.set_byte(1, value), + 0x0400001E => self.lcd.registers.bg3vofs.set_byte(0, value), + 0x0400001F => self.lcd.registers.bg3vofs.set_byte(1, value), + 0x04000020 => self.lcd.registers.bg2pa.set_byte(0, value), + 0x04000021 => self.lcd.registers.bg2pa.set_byte(1, value), + 0x04000022 => self.lcd.registers.bg2pb.set_byte(0, value), + 0x04000023 => self.lcd.registers.bg2pb.set_byte(1, value), + 0x04000024 => self.lcd.registers.bg2pc.set_byte(0, value), + 0x04000025 => self.lcd.registers.bg2pc.set_byte(1, value), + 0x04000026 => self.lcd.registers.bg2pd.set_byte(0, value), + 0x04000027 => self.lcd.registers.bg2pd.set_byte(1, value), + 0x04000028 => self.lcd.registers.bg2x.set_byte(0, value), + 0x04000029 => self.lcd.registers.bg2x.set_byte(1, value), + 0x0400002A => self.lcd.registers.bg2x.set_byte(2, value), + 0x0400002B => self.lcd.registers.bg2x.set_byte(3, value), + 0x0400002C => self.lcd.registers.bg2y.set_byte(0, value), + 0x0400002D => self.lcd.registers.bg2y.set_byte(1, value), + 0x0400002E => self.lcd.registers.bg2y.set_byte(2, value), + 0x0400002F => self.lcd.registers.bg2y.set_byte(3, value), + 0x04000030 => self.lcd.registers.bg3pa.set_byte(0, value), + 0x04000031 => self.lcd.registers.bg3pa.set_byte(1, value), + 0x04000032 => self.lcd.registers.bg3pb.set_byte(0, value), + 0x04000033 => self.lcd.registers.bg3pb.set_byte(1, value), + 0x04000034 => self.lcd.registers.bg3pc.set_byte(0, value), + 0x04000035 => self.lcd.registers.bg3pc.set_byte(1, value), + 0x04000036 => self.lcd.registers.bg3pd.set_byte(0, value), + 0x04000037 => self.lcd.registers.bg3pd.set_byte(1, value), + 0x04000038 => self.lcd.registers.bg3x.set_byte(0, value), + 0x04000039 => self.lcd.registers.bg3x.set_byte(1, value), + 0x0400003A => self.lcd.registers.bg3x.set_byte(2, value), + 0x0400003B => self.lcd.registers.bg3x.set_byte(3, value), + 0x0400003C => self.lcd.registers.bg3y.set_byte(0, value), + 0x0400003D => self.lcd.registers.bg3y.set_byte(1, value), + 0x0400003E => self.lcd.registers.bg3y.set_byte(2, value), + 0x0400003F => self.lcd.registers.bg3y.set_byte(3, value), + 0x04000040 => self.lcd.registers.win0h.set_byte(0, value), + 0x04000041 => self.lcd.registers.win0h.set_byte(1, value), + 0x04000042 => self.lcd.registers.win1h.set_byte(0, value), + 0x04000043 => self.lcd.registers.win1h.set_byte(1, value), + 0x04000044 => self.lcd.registers.win0v.set_byte(0, value), + 0x04000045 => self.lcd.registers.win0v.set_byte(1, value), + 0x04000046 => self.lcd.registers.win1v.set_byte(0, value), + 0x04000047 => self.lcd.registers.win1v.set_byte(1, value), + 0x04000048 => self.lcd.registers.winin.set_byte(0, value), + 0x04000049 => self.lcd.registers.winin.set_byte(1, value), + 0x0400004A => self.lcd.registers.winout.set_byte(0, value), + 0x0400004B => self.lcd.registers.winout.set_byte(1, value), + 0x0400004C => self.lcd.registers.mosaic.set_byte(0, value), + 0x0400004D => self.lcd.registers.mosaic.set_byte(1, value), // 0x0400004E, 0x0400004F are not used - 0x04000050 => self.lcd.bldcnt.set_byte(0, value), - 0x04000051 => self.lcd.bldcnt.set_byte(1, value), - 0x04000052 => self.lcd.bldalpha.set_byte(0, value), - 0x04000053 => self.lcd.bldalpha.set_byte(1, value), - 0x04000054 => self.lcd.bldy.set_byte(0, value), - 0x04000055 => self.lcd.bldy.set_byte(1, value), + 0x04000050 => self.lcd.registers.bldcnt.set_byte(0, value), + 0x04000051 => self.lcd.registers.bldcnt.set_byte(1, value), + 0x04000052 => self.lcd.registers.bldalpha.set_byte(0, value), + 0x04000053 => self.lcd.registers.bldalpha.set_byte(1, value), + 0x04000054 => self.lcd.registers.bldy.set_byte(0, value), + 0x04000055 => self.lcd.registers.bldy.set_byte(1, value), 0x0400004E..=0x0400004F | 0x04000056..=0x0400005F => { log("write on unused memory"); self.unused_region.insert(address, value); @@ -651,10 +651,10 @@ impl Bus { match unmasked_address { 0x05000000..=0x050001FF => { - self.lcd.bg_palette_ram[unmasked_address - 0x05000000] + self.lcd.memory.bg_palette_ram[unmasked_address - 0x05000000] } 0x05000200..=0x050003FF => { - self.lcd.obj_palette_ram[unmasked_address - 0x05000200] + self.lcd.memory.obj_palette_ram[unmasked_address - 0x05000200] } _ => unreachable!(), } @@ -664,9 +664,11 @@ impl Bus { // VRAM is 64k+32k+32k with the last two 32k being one mirrors of each other match unmasked_address { - 0x06000000..=0x06017FFF => self.lcd.video_ram[unmasked_address - 0x06000000], + 0x06000000..=0x06017FFF => { + self.lcd.memory.video_ram[unmasked_address - 0x06000000] + } 0x06018000..=0x0601FFFF => { - self.lcd.video_ram[unmasked_address - 0x06000000 - 0x8000] + self.lcd.memory.video_ram[unmasked_address - 0x06000000 - 0x8000] } _ => unreachable!(), } @@ -674,7 +676,7 @@ impl Bus { 0x7000000..=0x7FFFFFF => { let unmasked_address = get_unmasked_address(address, 0x00FFFF00, 0xFF0000FF, 8, 4); - self.lcd.obj_attributes[unmasked_address - 0x07000000] + self.lcd.memory.obj_attributes[unmasked_address - 0x07000000] } 0x0004000..=0x1FFFFFF | 0xE010000..=0xFFFFFFF | 0x10000000..=0xFFFFFFFF => { log(format!("read on unused memory {address:x}")); @@ -701,10 +703,10 @@ impl Bus { match unmasked_address { 0x05000000..=0x050001FF => { - self.lcd.bg_palette_ram[unmasked_address - 0x05000000] = value + self.lcd.memory.bg_palette_ram[unmasked_address - 0x05000000] = value } 0x05000200..=0x050003FF => { - self.lcd.obj_palette_ram[unmasked_address - 0x05000200] = value + self.lcd.memory.obj_palette_ram[unmasked_address - 0x05000200] = value } _ => unreachable!(), }; @@ -715,10 +717,10 @@ impl Bus { // VRAM is 64k+32k+32k with the last two 32k being one mirrors of each other match unmasked_address { 0x06000000..=0x06017FFF => { - self.lcd.video_ram[unmasked_address - 0x06000000] = value + self.lcd.memory.video_ram[unmasked_address - 0x06000000] = value } 0x06018000..=0x0601FFFF => { - self.lcd.video_ram[unmasked_address - 0x06000000 - 0x8000] = value + self.lcd.memory.video_ram[unmasked_address - 0x06000000 - 0x8000] = value } _ => unreachable!(), } @@ -726,7 +728,7 @@ impl Bus { 0x7000000..=0x7FFFFFF => { let unmasked_address = get_unmasked_address(address, 0x00FFFF00, 0xFF0000FF, 8, 4); - self.lcd.obj_attributes[unmasked_address - 0x07000000] = value + self.lcd.memory.obj_attributes[unmasked_address - 0x07000000] = value } 0x0004000..=0x1FFFFFF | 0xE010000..=0xFFFFFFF | 0x10000000..=0xFFFFFFFF => { log(format!("write on unused memory {address:x}")); @@ -924,12 +926,12 @@ mod tests { bus.write_raw(address, 10); - assert_eq!(bus.lcd.winin, 10); + assert_eq!(bus.lcd.registers.winin, 10); let address = 0x04000049; // WININ higher byte bus.write_raw(address, 5); - assert_eq!(bus.lcd.winin, (5 << 8) | 10); + assert_eq!(bus.lcd.registers.winin, (5 << 8) | 10); } #[test] @@ -937,7 +939,7 @@ mod tests { let mut bus = Bus::default(); let address = 0x04000048; // WININ lower byte - bus.lcd.winin = (5 << 8) | 10; + bus.lcd.registers.winin = (5 << 8) | 10; assert_eq!(bus.read_raw(address), 10); @@ -971,13 +973,13 @@ mod tests { let address = 0x05000008; bus.write_raw(address, 10); - assert_eq!(bus.lcd.bg_palette_ram[8], 10); + assert_eq!(bus.lcd.memory.bg_palette_ram[8], 10); } #[test] fn read_bg_palette_ram() { let mut bus = Bus::default(); - bus.lcd.bg_palette_ram[8] = 15; + bus.lcd.memory.bg_palette_ram[8] = 15; let address = 0x05000008; let value = bus.read_raw(address); @@ -992,7 +994,7 @@ mod tests { let address = 0x050001FF; bus.write_raw(address, 5); - assert_eq!(bus.lcd.bg_palette_ram[0x1FF], 5); + assert_eq!(bus.lcd.memory.bg_palette_ram[0x1FF], 5); } #[test] @@ -1001,13 +1003,13 @@ mod tests { let address = 0x05000208; bus.write_raw(address, 10); - assert_eq!(bus.lcd.obj_palette_ram[8], 10); + assert_eq!(bus.lcd.memory.obj_palette_ram[8], 10); } #[test] fn read_obj_palette_ram() { let mut bus = Bus::default(); - bus.lcd.obj_palette_ram[8] = 15; + bus.lcd.memory.obj_palette_ram[8] = 15; let address = 0x05000208; @@ -1023,7 +1025,7 @@ mod tests { let address = 0x050003FF; bus.write_raw(address, 5); - assert_eq!(bus.lcd.obj_palette_ram[0x1FF], 5); + assert_eq!(bus.lcd.memory.obj_palette_ram[0x1FF], 5); } #[test] @@ -1032,13 +1034,13 @@ mod tests { let address = 0x06000004; bus.write_raw(address, 23); - assert_eq!(bus.lcd.video_ram[4], 23); + assert_eq!(bus.lcd.memory.video_ram[4], 23); } #[test] fn read_vram() { let mut bus = Bus::default(); - bus.lcd.video_ram[4] = 15; + bus.lcd.memory.video_ram[4] = 15; let address = 0x06000004; let value = bus.read_raw(address); @@ -1053,13 +1055,13 @@ mod tests { let address = 0x06017FFF; bus.write_raw(address, 5); - assert_eq!(bus.lcd.video_ram[0x17FFF], 5); + assert_eq!(bus.lcd.memory.video_ram[0x17FFF], 5); } #[test] fn test_mirror_bg_palette() { let mut bus = Bus::default(); - bus.lcd.bg_palette_ram[0x134] = 5; + bus.lcd.memory.bg_palette_ram[0x134] = 5; assert_eq!(bus.read_raw(0x05000134), 5); assert_eq!(bus.read_raw(0x05000534), 5); @@ -1067,22 +1069,22 @@ mod tests { assert_eq!(bus.read_raw(0x05FFFD34), 5); bus.write_raw(0x05000134, 10); - assert_eq!(bus.lcd.bg_palette_ram[0x134], 10); + assert_eq!(bus.lcd.memory.bg_palette_ram[0x134], 10); bus.write_raw(0x05000534, 11); - assert_eq!(bus.lcd.bg_palette_ram[0x134], 11); + assert_eq!(bus.lcd.memory.bg_palette_ram[0x134], 11); bus.write_raw(0x05012534, 12); - assert_eq!(bus.lcd.bg_palette_ram[0x134], 12); + assert_eq!(bus.lcd.memory.bg_palette_ram[0x134], 12); bus.write_raw(0x05FFFD34, 13); - assert_eq!(bus.lcd.bg_palette_ram[0x134], 13); + assert_eq!(bus.lcd.memory.bg_palette_ram[0x134], 13); } #[test] fn test_mirror_obj_palette() { let mut bus = Bus::default(); - bus.lcd.obj_palette_ram[0x134] = 5; + bus.lcd.memory.obj_palette_ram[0x134] = 5; assert_eq!(bus.read_raw(0x05000334), 5); assert_eq!(bus.read_raw(0x05000734), 5); @@ -1090,22 +1092,22 @@ mod tests { assert_eq!(bus.read_raw(0x05FFFF34), 5); bus.write_raw(0x05000334, 10); - assert_eq!(bus.lcd.obj_palette_ram[0x134], 10); + assert_eq!(bus.lcd.memory.obj_palette_ram[0x134], 10); bus.write_raw(0x05000734, 11); - assert_eq!(bus.lcd.obj_palette_ram[0x134], 11); + assert_eq!(bus.lcd.memory.obj_palette_ram[0x134], 11); bus.write_raw(0x05012734, 12); - assert_eq!(bus.lcd.obj_palette_ram[0x134], 12); + assert_eq!(bus.lcd.memory.obj_palette_ram[0x134], 12); bus.write_raw(0x05FFFF34, 13); - assert_eq!(bus.lcd.obj_palette_ram[0x134], 13); + assert_eq!(bus.lcd.memory.obj_palette_ram[0x134], 13); } #[test] fn test_mirror_vram() { let mut bus = Bus::default(); - bus.lcd.video_ram[0x09345] = 5; + bus.lcd.memory.video_ram[0x09345] = 5; assert_eq!(bus.read_raw(0x06009345), 5); assert_eq!(bus.read_raw(0x06029345), 5); @@ -1113,18 +1115,18 @@ mod tests { assert_eq!(bus.read_raw(0x06FE9345), 5); bus.write_raw(0x06009345, 1); - assert_eq!(bus.lcd.video_ram[0x09345], 1); + assert_eq!(bus.lcd.memory.video_ram[0x09345], 1); bus.write_raw(0x06029345, 2); - assert_eq!(bus.lcd.video_ram[0x09345], 2); + assert_eq!(bus.lcd.memory.video_ram[0x09345], 2); bus.write_raw(0x06129345, 3); - assert_eq!(bus.lcd.video_ram[0x09345], 3); + assert_eq!(bus.lcd.memory.video_ram[0x09345], 3); bus.write_raw(0x06FE9345, 4); - assert_eq!(bus.lcd.video_ram[0x09345], 4); + assert_eq!(bus.lcd.memory.video_ram[0x09345], 4); - bus.lcd.video_ram[0x11345] = 10; + bus.lcd.memory.video_ram[0x11345] = 10; assert_eq!(bus.read_raw(0x06019345), 10); assert_eq!(bus.read_raw(0x06131345), 10); } @@ -1132,7 +1134,7 @@ mod tests { #[test] fn test_mirror_oam() { let mut bus = Bus::default(); - bus.lcd.obj_attributes[0x134] = 5; + bus.lcd.memory.obj_attributes[0x134] = 5; assert_eq!(bus.read_raw(0x07000134), 5); assert_eq!(bus.read_raw(0x07000534), 5); @@ -1140,15 +1142,15 @@ mod tests { assert_eq!(bus.read_raw(0x07FFFD34), 5); bus.write_raw(0x07000134, 10); - assert_eq!(bus.lcd.obj_attributes[0x134], 10); + assert_eq!(bus.lcd.memory.obj_attributes[0x134], 10); bus.write_raw(0x07000534, 11); - assert_eq!(bus.lcd.obj_attributes[0x134], 11); + assert_eq!(bus.lcd.memory.obj_attributes[0x134], 11); bus.write_raw(0x0700F534, 12); - assert_eq!(bus.lcd.obj_attributes[0x134], 12); + assert_eq!(bus.lcd.memory.obj_attributes[0x134], 12); bus.write_raw(0x07FFFD34, 13); - assert_eq!(bus.lcd.obj_attributes[0x134], 13); + assert_eq!(bus.lcd.memory.obj_attributes[0x134], 13); } } diff --git a/emu/src/cpu/arm7tdmi.rs b/emu/src/cpu/arm7tdmi.rs index d3a9a3a..42d7a1c 100644 --- a/emu/src/cpu/arm7tdmi.rs +++ b/emu/src/cpu/arm7tdmi.rs @@ -446,9 +446,6 @@ impl Arm7tdmi { .set_program_counter(new_pc + arm::operations::SIZE_OF_INSTRUCTION); self.fetched_arm = Some(self.fetch_arm()); - - self.registers - .set_program_counter(new_pc + arm::operations::SIZE_OF_INSTRUCTION * 2); } pub fn step(&mut self) { @@ -498,7 +495,7 @@ impl Arm7tdmi { } #[cfg(feature = "logger")] - let current_ins = self.registers.program_counter() - 4; + let current_ins = self.registers.program_counter() - 8; #[cfg(feature = "logger")] log(format!("PC: 0x{current_ins:X} {decoded}")); diff --git a/emu/src/cpu/hardware/lcd.rs b/emu/src/cpu/hardware/lcd.rs index dcdd7a5..be0efa0 100644 --- a/emu/src/cpu/hardware/lcd.rs +++ b/emu/src/cpu/hardware/lcd.rs @@ -11,10 +11,14 @@ use self::layers::layer_1::Layer1; use self::layers::layer_2::Layer2; use self::layers::layer_3::Layer3; use self::layers::layer_obj::LayerObj; +use self::memory::Memory; +use self::registers::Registers; mod layers; +mod memory; mod object_attributes; mod point; +mod registers; /// GBA display width const LCD_WIDTH: usize = 240; @@ -79,91 +83,8 @@ struct PixelInfo { #[serde_as] #[derive(Serialize, Deserialize)] pub struct Lcd { - /// LCD Control - pub dispcnt: u16, - /// Undocumented - Green Swap - pub green_swap: u16, - /// General LCD Status (STAT, LYC) - pub dispstat: u16, - /// Vertical Counter (LY) - pub vcount: u16, - /// BG0 Control - pub bg0cnt: u16, - /// BG1 Control - pub bg1cnt: u16, - /// BG2 Control - pub bg2cnt: u16, - /// BG3 Control - pub bg3cnt: u16, - /// BG0 X-Offset - pub bg0hofs: u16, - /// BG0 Y_Offset - pub bg0vofs: u16, - /// BG1 X-Offset - pub bg1hofs: u16, - /// BG1 Y_Offset - pub bg1vofs: u16, - /// BG2 X-Offset - pub bg2hofs: u16, - /// BG2 Y_Offset - pub bg2vofs: u16, - /// BG3 X-Offset - pub bg3hofs: u16, - /// BG3 Y_Offset - pub bg3vofs: u16, - /// BG2 Rotation/Scaling Parameter A (dx) - pub bg2pa: u16, - /// BG2 Rotation/Scaling Parameter B (dmx) - pub bg2pb: u16, - /// BG2 Rotation/Scaling Parameter C (dy) - pub bg2pc: u16, - /// BG2 Rotation/Scaling Parameter D (dmy) - pub bg2pd: u16, - /// BG2 Reference Point X-Coordinate - pub bg2x: u32, - /// BG2 Reference Point Y-Coordinate - pub bg2y: u32, - /// BG3 Rotation/Scaling Parameter A (dx) - pub bg3pa: u16, - /// BG3 Rotation/Scaling Parameter B (dmx) - pub bg3pb: u16, - /// BG3 Rotation/Scaling Parameter C (dy) - pub bg3pc: u16, - /// BG3 Rotation/Scaling Parameter D (dmy) - pub bg3pd: u16, - /// BG3 Reference Point X-Coordinate - pub bg3x: u32, - /// BG3 Reference Point Y-Coordinate - pub bg3y: u32, - /// Window 0 Horizontal Dimensions - pub win0h: u16, - /// Window 1 Horizontal Dimensions - pub win1h: u16, - /// Window 0 Vertical Dimensions - pub win0v: u16, - /// Window 1 Vertical Dimensions - pub win1v: u16, - /// Inside of Window 0 and 1 - pub winin: u16, - /// Inside of OBJ Window & Outside of Windows - pub winout: u16, - /// Mosaic Size - pub mosaic: u16, - /// Color Special Effects Selection - pub bldcnt: u16, - /// Alpha Blending Coefficients - pub bldalpha: u16, - /// Brightness (Fade-In/Out) Coefficient - pub bldy: u16, - - /// From 0x05000000 to 0x050001FF (512 bytes, 256 colors). - pub bg_palette_ram: Vec, - /// From 0x05000200 to 0x050003FF (512 bytes, 256 colors). - pub obj_palette_ram: Vec, - /// From 0x06000000 to 0x06017FFF (96 kb). - pub video_ram: Vec, - /// From 0x07000000 to 0x070003FF (1kbyte) - pub obj_attributes: Vec, + pub(crate) registers: Registers, + pub(crate) memory: Memory, #[serde_as(as = "[[_; 240]; 160]")] pub buffer: [[Color; LCD_WIDTH]; LCD_HEIGHT], @@ -181,54 +102,14 @@ pub struct Lcd { impl Default for Lcd { fn default() -> Self { Self { - dispcnt: 0, - green_swap: 0, - dispstat: 0, - vcount: 0, - bg0cnt: 0, - bg1cnt: 0, - bg2cnt: 0, - bg3cnt: 0, - bg0hofs: 0, - bg0vofs: 0, - bg1hofs: 0, - bg1vofs: 0, - bg2hofs: 0, - bg2vofs: 0, - bg3hofs: 0, - bg3vofs: 0, - bg2pa: 0, - bg2pb: 0, - bg2pc: 0, - bg2pd: 0, - bg2x: 0, - bg2y: 0, - bg3pa: 0, - bg3pb: 0, - bg3pc: 0, - bg3pd: 0, - bg3x: 0, - bg3y: 0, - win0h: 0, - win1h: 0, - win0v: 0, - win1v: 0, - winin: 0, - winout: 0, - mosaic: 0, - bldcnt: 0, - bldalpha: 0, - bldy: 0, - bg_palette_ram: vec![0; 0x200], - obj_palette_ram: vec![0; 0x200], - video_ram: vec![0; 0x18000], - obj_attributes: vec![0; 0x400], + registers: Registers::default(), + memory: Memory::default(), pixel_index: 0, buffer: [[Color::default(); LCD_WIDTH]; LCD_HEIGHT], should_draw: false, layer_0: Layer0, layer_1: Layer1, - layer_2: Layer2, + layer_2: Layer2::default(), layer_3: Layer3, layer_obj: LayerObj::default(), } @@ -246,41 +127,36 @@ impl Lcd { // This will be much more complex obviously let mut output = LcdStepOutput::default(); - if self.vcount < 160 { + if self.registers.vcount < 160 { // We either are in Vdraw or Hblank if self.pixel_index == 0 { // We're drawing the first pixel of the scanline, we're entering Vdraw - self.set_hblank_flag(false); - self.set_vblank_flag(false); + self.registers.set_hblank_flag(false); + self.registers.set_vblank_flag(false); self.should_draw = true; // Cache attributes and scanline - self.layer_obj.handle_enter_vdraw( - self.vcount, - self.get_obj_character_vram_mapping(), - self.video_ram.as_ref(), - self.obj_attributes.as_ref(), - self.obj_palette_ram.as_ref(), - ); + self.layer_obj + .handle_enter_vdraw(&self.memory, &self.registers); } else if self.pixel_index == 240 { // We're entering Hblank - self.set_hblank_flag(true); + self.registers.set_hblank_flag(true); - if self.get_hblank_irq_enable() { + if self.registers.get_hblank_irq_enable() { output.request_hblank_irq = true; } self.should_draw = false; } - } else if self.vcount == 160 && self.pixel_index == 0 { + } else if self.registers.vcount == 160 && self.pixel_index == 0 { // We're drawing the first pixel of the Vblank period - self.set_vblank_flag(true); + self.registers.set_vblank_flag(true); - if self.get_vblank_irq_enable() { + if self.registers.get_vblank_irq_enable() { output.request_vblank_irq = true; } @@ -288,24 +164,41 @@ impl Lcd { } if self.should_draw { - let pixel_y = self.vcount; + let pixel_y = self.registers.vcount; let pixel_x = self.pixel_index; - self.buffer[pixel_y as usize][pixel_x as usize] = self - .layer_obj - .render(pixel_x as usize, pixel_y as usize) - .unwrap_or_else(|| Color::from_rgb(31, 31, 31)); + // We get the enabled layers (depending on BG mode and registers), we call render on them + // we filter out the `None` and we sort by priority. + let mut layers_with_pixel = self + .get_enabled_layers() + .into_iter() + .filter_map(|layer| { + layer.render( + pixel_x as usize, + pixel_y as usize, + &self.memory, + &self.registers, + ) + }) + .collect::>(); + + layers_with_pixel.sort_unstable_by_key(|pixel| pixel.priority); + + let first_pixel = layers_with_pixel.first(); + + self.buffer[pixel_y as usize][pixel_x as usize] = + first_pixel.map_or_else(|| Color::from_rgb(31, 31, 31), |info| info.color); } log(format!( "mode: {:?}, BG2: {:?} BG3: {:?}, OBJ: {:?}, WIN0: {:?}, WIN1: {:?}, WINOJB: {:?}", - self.get_bg_mode(), - self.get_bg2_enabled(), - self.get_bg3_enabled(), - self.get_obj_enabled(), - self.get_win0_enabled(), - self.get_win1_enabled(), - self.get_winobj_enabled(), + self.registers.get_bg_mode(), + self.registers.get_bg2_enabled(), + self.registers.get_bg3_enabled(), + self.registers.get_obj_enabled(), + self.registers.get_win0_enabled(), + self.registers.get_win1_enabled(), + self.registers.get_winobj_enabled(), )); self.pixel_index += 1; @@ -313,20 +206,20 @@ impl Lcd { if self.pixel_index == 308 { // We finished to draw the scanline self.pixel_index = 0; - self.vcount += 1; + self.registers.vcount += 1; // We finished to draw the screen - if self.vcount == 228 { - self.vcount = 0; + if self.registers.vcount == 228 { + self.registers.vcount = 0; } } - self.set_vcounter_flag(false); + self.registers.set_vcounter_flag(false); - if self.vcount.get_byte(0) == self.get_vcount_setting() { - self.set_vcounter_flag(true); + if self.registers.vcount.get_byte(0) == self.registers.get_vcount_setting() { + self.registers.set_vcounter_flag(true); - if self.get_vcounter_irq_enable() { + if self.registers.get_vcounter_irq_enable() { output.request_vcount_irq = true; } } @@ -334,64 +227,32 @@ impl Lcd { output } - fn get_bg2_enabled(&self) -> bool { - self.dispcnt.get_bit(10) - } - - fn get_bg3_enabled(&self) -> bool { - self.dispcnt.get_bit(11) - } - - fn get_obj_enabled(&self) -> bool { - self.dispcnt.get_bit(12) - } - - fn get_win0_enabled(&self) -> bool { - self.dispcnt.get_bit(13) - } - - fn get_win1_enabled(&self) -> bool { - self.dispcnt.get_bit(14) - } - - fn get_winobj_enabled(&self) -> bool { - self.dispcnt.get_bit(15) - } - - /// Info about vram fields used to render display. - pub fn get_bg_mode(&self) -> u8 { - self.dispcnt.get_bits(0..=2).try_into().unwrap() - } + fn get_enabled_layers(&self) -> Vec<&dyn Layer> { + let mut result: Vec<&dyn Layer> = Vec::new(); - fn get_obj_character_vram_mapping(&self) -> ObjMappingKind { - self.dispcnt.get_bit(6).into() - } - - fn get_vcount_setting(&self) -> u8 { - self.dispstat.get_byte(1) - } + let current_mode = self.registers.get_bg_mode(); - fn get_vblank_irq_enable(&self) -> bool { - self.dispstat.get_bit(3) - } + if matches!(current_mode, 0 | 1) && self.registers.get_bg0_enabled() { + result.push(&self.layer_0); + } - fn get_hblank_irq_enable(&self) -> bool { - self.dispstat.get_bit(4) - } + if matches!(current_mode, 0 | 1) && self.registers.get_bg1_enabled() { + result.push(&self.layer_1) + } - fn get_vcounter_irq_enable(&self) -> bool { - self.dispstat.get_bit(5) - } + // BG2 is available in every mode + if self.registers.get_bg2_enabled() { + result.push(&self.layer_2) + } - fn set_vblank_flag(&mut self, value: bool) { - self.dispstat.set_bit(0, value); - } + if matches!(current_mode, 0 | 2) && self.registers.get_bg3_enabled() { + result.push(&self.layer_3) + } - fn set_hblank_flag(&mut self, value: bool) { - self.dispstat.set_bit(1, value); - } + if self.registers.get_obj_enabled() { + result.push(&self.layer_obj); + } - fn set_vcounter_flag(&mut self, value: bool) { - self.dispstat.set_bit(2, value); + result } } diff --git a/emu/src/cpu/hardware/lcd/layers.rs b/emu/src/cpu/hardware/lcd/layers.rs index 519808c..6359341 100644 --- a/emu/src/cpu/hardware/lcd/layers.rs +++ b/emu/src/cpu/hardware/lcd/layers.rs @@ -1,4 +1,4 @@ -use super::Color; +use super::{memory::Memory, registers::Registers, PixelInfo}; pub mod layer_0; pub mod layer_1; @@ -7,5 +7,11 @@ pub mod layer_3; pub mod layer_obj; pub trait Layer { - fn render(&self, x: usize, y: usize) -> Option; + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option; } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_0.rs b/emu/src/cpu/hardware/lcd/layers/layer_0.rs index 017f99d..e0507b9 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_0.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_0.rs @@ -1,3 +1,7 @@ +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::PixelInfo; + use super::Layer; use serde::Deserialize; use serde::Serialize; @@ -7,7 +11,13 @@ pub struct Layer0; impl Layer for Layer0 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { // TODO: To implement None } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_1.rs b/emu/src/cpu/hardware/lcd/layers/layer_1.rs index a186775..56eb0a6 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_1.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_1.rs @@ -1,3 +1,7 @@ +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::PixelInfo; + use super::Layer; use serde::Deserialize; use serde::Serialize; @@ -7,7 +11,13 @@ pub struct Layer1; impl Layer for Layer1 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { // TODO: To implement None } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_2.rs b/emu/src/cpu/hardware/lcd/layers/layer_2.rs index da514af..fc28aac 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_2.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_2.rs @@ -1,14 +1,44 @@ use super::Layer; +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::{Color, PixelInfo, LCD_WIDTH}; use serde::Deserialize; use serde::Serialize; +use serde_with::serde_as; -#[derive(Default, Serialize, Deserialize)] -pub struct Layer2; +#[serde_as] +#[derive(Serialize, Deserialize)] +pub struct Layer2 { + #[serde_as(as = "[_; 240]")] + bg_pixels_scanline: [Option; LCD_WIDTH], +} + +impl Default for Layer2 { + fn default() -> Self { + Self { + bg_pixels_scanline: [None; LCD_WIDTH], + } + } +} impl Layer for Layer2 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { - // TODO: To implement - None + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { + let idx: usize = y * LCD_WIDTH + x; + + let color_idx = memory.video_ram[idx] as usize; + let low_nibble = memory.bg_palette_ram[color_idx * 2] as u16; + let high_nibble = memory.bg_palette_ram[color_idx * 2 + 1] as u16; + + Some(PixelInfo { + color: Color::from_palette_color((high_nibble << 8) | low_nibble), + priority: 0, + }) } } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_3.rs b/emu/src/cpu/hardware/lcd/layers/layer_3.rs index 5a11ff0..e6dbf34 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_3.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_3.rs @@ -1,3 +1,7 @@ +use crate::cpu::hardware::lcd::memory::Memory; +use crate::cpu::hardware::lcd::registers::Registers; +use crate::cpu::hardware::lcd::PixelInfo; + use super::Layer; use serde::Deserialize; use serde::Serialize; @@ -7,7 +11,13 @@ pub struct Layer3; impl Layer for Layer3 { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { // TODO: To implement None } diff --git a/emu/src/cpu/hardware/lcd/layers/layer_obj.rs b/emu/src/cpu/hardware/lcd/layers/layer_obj.rs index 90bcf75..b68145c 100644 --- a/emu/src/cpu/hardware/lcd/layers/layer_obj.rs +++ b/emu/src/cpu/hardware/lcd/layers/layer_obj.rs @@ -1,6 +1,8 @@ use crate::cpu::hardware::lcd; +use crate::cpu::hardware::lcd::memory::Memory; use crate::cpu::hardware::lcd::object_attributes; use crate::cpu::hardware::lcd::point::Point; +use crate::cpu::hardware::lcd::registers::Registers; use crate::cpu::hardware::lcd::Color; use crate::cpu::hardware::lcd::{PixelInfo, LCD_WIDTH, WORLD_HEIGHT}; @@ -21,8 +23,6 @@ pub struct LayerObj { #[serde_as(as = "[_; 240]")] sprite_pixels_scanline: [Option; LCD_WIDTH], - - obj_mapping_kind: lcd::ObjMappingKind, } impl Default for LayerObj { @@ -31,15 +31,20 @@ impl Default for LayerObj { obj_attributes_arr: [object_attributes::ObjAttributes::default(); 128], rotation_scaling_params: [object_attributes::RotationScaling::default(); 32], sprite_pixels_scanline: [None; LCD_WIDTH], - obj_mapping_kind: lcd::ObjMappingKind::TwoDimensional, } } } impl Layer for LayerObj { #[allow(unused_variables)] - fn render(&self, x: usize, y: usize) -> Option { - self.sprite_pixels_scanline[x].map(|info| info.color) + fn render( + &self, + x: usize, + y: usize, + memory: &Memory, + registers: &Registers, + ) -> Option { + self.sprite_pixels_scanline[x] } } @@ -93,8 +98,9 @@ impl LayerObj { } } - fn process_sprites_scanline(&mut self, y: u16, video_ram: &[u8], obj_palette_ram: &[u8]) { + fn process_sprites_scanline(&mut self, registers: &Registers, memory: &Memory) { self.sprite_pixels_scanline = [None; LCD_WIDTH]; + let y = registers.vcount; for obj in self.obj_attributes_arr.into_iter() { if matches!( @@ -184,11 +190,13 @@ impl LayerObj { let y_tile_idx = pixel_texture_sprite_origin.y % 8; let x_tile_idx = pixel_texture_sprite_origin.x % 8; + let obj_character_vram_mapping = registers.get_obj_character_vram_mapping(); + let color_offset = match obj.attribute0.color_mode { object_attributes::ColorMode::Palette8bpp => { // We multiply *2 because in 8bpp tiles indeces are always even let tile_number = obj.attribute2.tile_number - + match self.obj_mapping_kind { + + match obj_character_vram_mapping { lcd::ObjMappingKind::OneDimensional => { // In this case memory is seen as a single array. // tile_number is the offset of the first tile in memory. @@ -208,11 +216,11 @@ impl LayerObj { tile_number as u32 * 32 + y_tile_idx as u32 * 8 + x_tile_idx as u32; // TODO: Move 0x10000 to a variable. It is the offset where OBJ VRAM starts in vram - video_ram[0x10000 + palette_offset as usize] + memory.video_ram[0x10000 + palette_offset as usize] } object_attributes::ColorMode::Palette4bpp => { let tile_number = obj.attribute2.tile_number - + match self.obj_mapping_kind { + + match obj_character_vram_mapping { lcd::ObjMappingKind::OneDimensional => { // In this case memory is seen as a single array. // tile_number is the offset of the first tile in memory. @@ -236,7 +244,7 @@ impl LayerObj { let palette_offset = (obj.attribute2.palette_number << 4) | (palette_offset_low as u8); - video_ram[0x10000 + palette_offset as usize] + memory.video_ram[0x10000 + palette_offset as usize] } }; @@ -247,7 +255,10 @@ impl LayerObj { } let get_pixel_info_closure = || PixelInfo { - color: self.read_color_from_obj_palette(color_offset as usize, obj_palette_ram), + color: self.read_color_from_obj_palette( + color_offset as usize, + memory.obj_palette_ram.as_slice(), + ), priority: obj.attribute2.priority, }; @@ -266,18 +277,10 @@ impl LayerObj { } } - pub fn handle_enter_vdraw( - &mut self, - y: u16, - obj_mapping_kind: lcd::ObjMappingKind, - video_ram: &[u8], - obj_attributes_ram: &[u8], - obj_palette_ram: &[u8], - ) { + pub fn handle_enter_vdraw(&mut self, memory: &Memory, registers: &Registers) { (self.obj_attributes_arr, self.rotation_scaling_params) = - object_attributes::get_attributes(obj_attributes_ram); - self.process_sprites_scanline(y, video_ram, obj_palette_ram); + object_attributes::get_attributes(memory.obj_attributes.as_slice()); - self.obj_mapping_kind = obj_mapping_kind; + self.process_sprites_scanline(registers, memory); } } diff --git a/emu/src/cpu/hardware/lcd/memory.rs b/emu/src/cpu/hardware/lcd/memory.rs new file mode 100644 index 0000000..5c4cddd --- /dev/null +++ b/emu/src/cpu/hardware/lcd/memory.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +// Using Box here to avoid stack overflow +#[serde_as] +#[derive(Serialize, Deserialize)] +pub struct Memory { + /// From 0x05000000 to 0x050001FF (512 bytes, 256 colors). + #[serde_as(as = "Box<[_; 512]>")] + pub bg_palette_ram: Box<[u8; 0x200]>, + /// From 0x05000200 to 0x050003FF (512 bytes, 256 colors). + #[serde_as(as = "Box<[_; 512]>")] + pub obj_palette_ram: Box<[u8; 0x200]>, + /// From 0x06000000 to 0x06017FFF (96 kb). + #[serde_as(as = "Box<[_; 98304]>")] + pub video_ram: Box<[u8; 0x18000]>, + /// From 0x07000000 to 0x070003FF (1kbyte) + #[serde_as(as = "Box<[_; 1024]>")] + pub obj_attributes: Box<[u8; 0x400]>, +} + +impl Default for Memory { + fn default() -> Self { + Self { + bg_palette_ram: Box::new([0; 0x200]), + obj_palette_ram: Box::new([8; 0x200]), + video_ram: Box::new([0; 0x18000]), + obj_attributes: Box::new([0; 0x400]), + } + } +} diff --git a/emu/src/cpu/hardware/lcd/registers.rs b/emu/src/cpu/hardware/lcd/registers.rs new file mode 100644 index 0000000..603f583 --- /dev/null +++ b/emu/src/cpu/hardware/lcd/registers.rs @@ -0,0 +1,156 @@ +use serde::{Deserialize, Serialize}; + +use crate::bitwise::Bits; + +use super::ObjMappingKind; + +#[derive(Default, Serialize, Deserialize)] +pub struct Registers { + /// LCD Control + pub dispcnt: u16, + /// Undocumented - Green Swap + pub green_swap: u16, + /// General LCD Status (STAT, LYC) + pub dispstat: u16, + /// Vertical Counter (LY) + pub vcount: u16, + /// BG0 Control + pub bg0cnt: u16, + /// BG1 Control + pub bg1cnt: u16, + /// BG2 Control + pub bg2cnt: u16, + /// BG3 Control + pub bg3cnt: u16, + /// BG0 X-Offset + pub bg0hofs: u16, + /// BG0 Y_Offset + pub bg0vofs: u16, + /// BG1 X-Offset + pub bg1hofs: u16, + /// BG1 Y_Offset + pub bg1vofs: u16, + /// BG2 X-Offset + pub bg2hofs: u16, + /// BG2 Y_Offset + pub bg2vofs: u16, + /// BG3 X-Offset + pub bg3hofs: u16, + /// BG3 Y_Offset + pub bg3vofs: u16, + /// BG2 Rotation/Scaling Parameter A (dx) + pub bg2pa: u16, + /// BG2 Rotation/Scaling Parameter B (dmx) + pub bg2pb: u16, + /// BG2 Rotation/Scaling Parameter C (dy) + pub bg2pc: u16, + /// BG2 Rotation/Scaling Parameter D (dmy) + pub bg2pd: u16, + /// BG2 Reference Point X-Coordinate + pub bg2x: u32, + /// BG2 Reference Point Y-Coordinate + pub bg2y: u32, + /// BG3 Rotation/Scaling Parameter A (dx) + pub bg3pa: u16, + /// BG3 Rotation/Scaling Parameter B (dmx) + pub bg3pb: u16, + /// BG3 Rotation/Scaling Parameter C (dy) + pub bg3pc: u16, + /// BG3 Rotation/Scaling Parameter D (dmy) + pub bg3pd: u16, + /// BG3 Reference Point X-Coordinate + pub bg3x: u32, + /// BG3 Reference Point Y-Coordinate + pub bg3y: u32, + /// Window 0 Horizontal Dimensions + pub win0h: u16, + /// Window 1 Horizontal Dimensions + pub win1h: u16, + /// Window 0 Vertical Dimensions + pub win0v: u16, + /// Window 1 Vertical Dimensions + pub win1v: u16, + /// Inside of Window 0 and 1 + pub winin: u16, + /// Inside of OBJ Window & Outside of Windows + pub winout: u16, + /// Mosaic Size + pub mosaic: u16, + /// Color Special Effects Selection + pub bldcnt: u16, + /// Alpha Blending Coefficients + pub bldalpha: u16, + /// Brightness (Fade-In/Out) Coefficient + pub bldy: u16, +} + +impl Registers { + pub(super) fn get_bg0_enabled(&self) -> bool { + self.dispcnt.get_bit(8) + } + + pub(super) fn get_bg1_enabled(&self) -> bool { + self.dispcnt.get_bit(9) + } + + pub(super) fn get_bg2_enabled(&self) -> bool { + self.dispcnt.get_bit(10) + } + + pub(super) fn get_bg3_enabled(&self) -> bool { + self.dispcnt.get_bit(11) + } + + pub(super) fn get_obj_enabled(&self) -> bool { + self.dispcnt.get_bit(12) + } + + pub(super) fn get_win0_enabled(&self) -> bool { + self.dispcnt.get_bit(13) + } + + pub(super) fn get_win1_enabled(&self) -> bool { + self.dispcnt.get_bit(14) + } + + pub(super) fn get_winobj_enabled(&self) -> bool { + self.dispcnt.get_bit(15) + } + + /// Info about vram fields used to render display. + pub(super) fn get_bg_mode(&self) -> u8 { + self.dispcnt.get_bits(0..=2).try_into().unwrap() + } + + pub(super) fn get_obj_character_vram_mapping(&self) -> ObjMappingKind { + self.dispcnt.get_bit(6).into() + } + + pub(super) fn get_vcount_setting(&self) -> u8 { + self.dispstat.get_byte(1) + } + + pub(super) fn get_vblank_irq_enable(&self) -> bool { + self.dispstat.get_bit(3) + } + + pub(super) fn get_hblank_irq_enable(&self) -> bool { + self.dispstat.get_bit(4) + } + + pub(super) fn get_vcounter_irq_enable(&self) -> bool { + self.dispstat.get_bit(5) + } + + pub(super) fn set_vblank_flag(&mut self, value: bool) { + self.dispstat.set_bit(0, value); + } + + pub(super) fn set_hblank_flag(&mut self, value: bool) { + self.dispstat.set_bit(1, value); + } + + pub(super) fn set_vcounter_flag(&mut self, value: bool) { + self.dispstat.set_bit(2, value); + } +} diff --git a/emu/src/render/mod.rs b/emu/src/render/mod.rs index 907091c..b3b590d 100644 --- a/emu/src/render/mod.rs +++ b/emu/src/render/mod.rs @@ -1,7 +1,6 @@ /// This module contains all the data structures used to render the GBA display. pub mod color; pub mod gba_lcd; -pub mod ppu; /// GBA display width pub const LCD_WIDTH: usize = 240; diff --git a/emu/src/render/ppu.rs b/emu/src/render/ppu.rs deleted file mode 100644 index 07cce38..0000000 --- a/emu/src/render/ppu.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::sync::{Arc, Mutex}; - -use crate::{ - cpu::arm7tdmi::Arm7tdmi, - render::{ - // color::{Color, PaletteType}, - gba_lcd::GbaLcd, - // LCD_HEIGHT, LCD_WIDTH, - MAX_COLORS_FULL_PALETTE, - MAX_COLORS_SINGLE_PALETTE, - MAX_PALETTES_BY_TYPE, - }, -}; - -use super::color::{Color, PaletteType}; - -#[derive(Default)] -pub struct PixelProcessUnit { - cpu: Arc>, - #[allow(dead_code)] - gba_lcd: Arc>>, -} - -impl PixelProcessUnit { - pub fn new(gba_lcd: Arc>>, cpu: Arc>) -> Self { - Self { cpu, gba_lcd } - } - - fn get_array_color(&self, index: usize, palette_type: &PaletteType) -> [u8; 2] { - debug_assert!(index % 2 == 0); - let lcd = &self.cpu.lock().unwrap().bus.lcd; - match palette_type { - PaletteType::BG => [lcd.bg_palette_ram[index], lcd.bg_palette_ram[index + 1]], - PaletteType::OBJ => [lcd.obj_palette_ram[index], lcd.obj_palette_ram[index + 1]], - } - } - - fn get_color_from_full_palette(&self, color_index: usize, palette_type: &PaletteType) -> Color { - debug_assert!(color_index < MAX_COLORS_FULL_PALETTE); - self.get_array_color(color_index * 2, palette_type).into() - } - - fn get_color_from_single_palette( - &self, - palette_index: usize, - color_index: usize, - palette_type: &PaletteType, - ) -> Color { - debug_assert!(palette_index < MAX_PALETTES_BY_TYPE); - debug_assert!(color_index < MAX_COLORS_FULL_PALETTE); - - self.get_color_from_full_palette( - (palette_index * MAX_COLORS_SINGLE_PALETTE) + color_index, - palette_type, - ) - } - - pub fn get_palettes(&self, palette_type: &PaletteType) -> Vec> { - let mut palettes = vec![]; - - for palette_index in 0..MAX_PALETTES_BY_TYPE { - palettes.push(vec![]); - for color_index in 0..MAX_COLORS_SINGLE_PALETTE { - palettes[palette_index].push(self.get_color_from_single_palette( - palette_index, - color_index, - palette_type, - )); - } - } - palettes - } -} diff --git a/ui/src/savegame.rs b/ui/src/savegame.rs index ef6afb9..8278974 100644 --- a/ui/src/savegame.rs +++ b/ui/src/savegame.rs @@ -1,7 +1,6 @@ use std::{ error::Error, io::{Read, Write}, - path::PathBuf, sync::{Arc, Mutex}, }; @@ -21,17 +20,13 @@ impl SaveGame { Self { gba } } - fn check_pathbuf(path: Option) -> Result { - path.map_or(Err("No file selected"), Ok) - } - fn save_state(&self) -> Result<(), Box> { let path = FileDialog::new() .set_location("~") .add_filter("Clementine save file", &["clm"]) .show_save_single_file()?; - let path = Self::check_pathbuf(path)?; + let path = path.ok_or("No file selected")?; let cpu = &self.gba.lock().unwrap().cpu; @@ -49,7 +44,7 @@ impl SaveGame { .add_filter("Clementine save file", &["clm"]) .show_open_single_file()?; - let path = Self::check_pathbuf(path)?; + let path = path.ok_or("No file selected")?; let mut file = fs::OpenOptions::new().read(true).open(path)?; let mut encoded = Vec::new();