mirror of
https://github.com/FranLMSP/snes.git
synced 2026-01-01 07:21:35 -05:00
More WIP PPU rendering and fix a few more tests (#11)
This commit is contained in:
@@ -11,15 +11,22 @@ pub struct COP {}
|
||||
|
||||
impl CPUInstruction for COP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
push_common::do_push(registers, bus, &[registers.pbr]);
|
||||
if !registers.emulation_mode {
|
||||
push_common::do_push(registers, bus, &[registers.pbr]);
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_brk(registers.emulation_mode);
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
push_common::do_push(registers, bus, &[(registers.pc >> 8) as u8, registers.pc as u8]);
|
||||
push_common::do_push(registers, bus, &[registers.p]);
|
||||
registers.set_irq_disable_flag(true);
|
||||
registers.pbr = 0x00;
|
||||
let vector = (bus.read(0x00FFE4) as u16) | ((bus.read(0x00FFE5) as u16) << 8);
|
||||
registers.pc = vector;
|
||||
if registers.emulation_mode {
|
||||
let vector = (bus.read(0x00FFF4) as u16) | ((bus.read(0x00FFF5) as u16) << 8);
|
||||
registers.pc = vector;
|
||||
} else {
|
||||
let vector = (bus.read(0x00FFE4) as u16) | ((bus.read(0x00FFE5) as u16) << 8);
|
||||
registers.pc = vector;
|
||||
}
|
||||
registers.set_decimal_mode_flag(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,15 @@ impl CPUInstruction for PLP {
|
||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||
let bytes = pull_common::do_pull(registers, bus, 1, true);
|
||||
registers.p = bytes[0];
|
||||
if registers.emulation_mode {
|
||||
registers.set_memory_select_flag(true);
|
||||
registers.set_index_register_select_flag(true);
|
||||
} else {
|
||||
if !registers.is_16bit_index() {
|
||||
registers.x &= 0xFF;
|
||||
registers.y &= 0xFF;
|
||||
}
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_plp();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,11 @@ pub fn do_pull(registers: &mut Registers, bus: &mut Bus, count: usize, alter_fla
|
||||
let mut is_zero = true;
|
||||
for _ in 0..count {
|
||||
registers.increment_sp(1);
|
||||
let byte = bus.read(registers.sp as u32);
|
||||
let mut address = registers.sp;
|
||||
if registers.emulation_mode {
|
||||
address = (registers.sp | 0x100) & 0x1FF
|
||||
}
|
||||
let byte = bus.read(address as u32);
|
||||
if byte != 0 {
|
||||
is_zero = false;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@ use crate::cpu::{registers::Registers, bus::Bus};
|
||||
|
||||
pub fn do_push(registers: &mut Registers, bus: &mut Bus, bytes: &[u8]) {
|
||||
for byte in bytes {
|
||||
let address = registers.sp as u32;
|
||||
let mut address = registers.sp as u32;
|
||||
if registers.emulation_mode {
|
||||
address = (address & 0xFF) | 0x100;
|
||||
}
|
||||
bus.write(address, *byte);
|
||||
registers.decrement_sp(1);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,14 @@ pub struct XCE {}
|
||||
impl CPUInstruction for XCE {
|
||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||
registers.exchange_carry_and_emulation();
|
||||
if registers.emulation_mode {
|
||||
registers.set_memory_select_flag(true);
|
||||
registers.set_index_register_select_flag(true);
|
||||
registers.x &= 0xFF;
|
||||
registers.y &= 0xFF;
|
||||
registers.sp &= 0xFF;
|
||||
registers.sp |= 0x100;
|
||||
}
|
||||
let (bytes, cycles) = cycles::increment_cycles_exchange();
|
||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||
}
|
||||
@@ -34,5 +42,44 @@ mod cpu_instructions_tests {
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.get_memory_select_flag());
|
||||
assert!(registers.get_index_register_select_flag());
|
||||
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.emulation_mode = true;
|
||||
registers.set_carry_flag(false);
|
||||
let instruction = XCE{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(!registers.emulation_mode);
|
||||
assert!(registers.get_memory_select_flag());
|
||||
assert!(registers.get_index_register_select_flag());
|
||||
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.emulation_mode = false;
|
||||
registers.set_carry_flag(true);
|
||||
let instruction = XCE{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert_eq!(registers.pc, 0x0001);
|
||||
assert_eq!(registers.cycles, 2);
|
||||
assert!(registers.emulation_mode);
|
||||
assert!(registers.get_memory_select_flag());
|
||||
assert!(registers.get_index_register_select_flag());
|
||||
|
||||
let mut registers = Registers::new();
|
||||
let mut bus = Bus::new();
|
||||
registers.pc = 0x0000;
|
||||
registers.emulation_mode = true;
|
||||
registers.set_carry_flag(false);
|
||||
let instruction = XCE{};
|
||||
instruction.execute(&mut registers, &mut bus);
|
||||
assert!(registers.get_carry_flag());
|
||||
assert!(registers.get_memory_select_flag());
|
||||
assert!(registers.get_index_register_select_flag());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,34 @@ pub struct Registers {
|
||||
pub is_move_next: bool, // TODO: refactor these states with an enum
|
||||
pub cycles: usize,
|
||||
}
|
||||
/*
|
||||
P register flags
|
||||
|
||||
Emulation mode:
|
||||
NV-BDIZC
|
||||
N - Negative
|
||||
V - Overflow
|
||||
-
|
||||
B - Break Instruction
|
||||
D - Decimal Mode
|
||||
I - IRQ Disable
|
||||
Z - Zero
|
||||
C - Carry
|
||||
|
||||
Native Mode
|
||||
E - Emulation Mode
|
||||
NVMXDIZC
|
||||
|
||||
N - Negative
|
||||
V - Overflow
|
||||
M - Memory/Accumulator Select
|
||||
X - Index Register Select
|
||||
D - Decimal Mode
|
||||
I - IRQ Disable
|
||||
Z - Zero
|
||||
C - Carry
|
||||
|
||||
*/
|
||||
|
||||
impl Registers {
|
||||
pub fn new() -> Self {
|
||||
@@ -214,7 +242,11 @@ impl Registers {
|
||||
}
|
||||
|
||||
pub fn reset_rep_byte(&mut self, byte: u8) {
|
||||
self.p &= !byte;
|
||||
let mut effective_byte = byte;
|
||||
if self.emulation_mode {
|
||||
effective_byte &= 0b1100_1111;
|
||||
}
|
||||
self.p &= !effective_byte;
|
||||
}
|
||||
|
||||
pub fn set_sep_byte(&mut self, byte: u8) {
|
||||
@@ -440,6 +472,7 @@ mod registers_tests {
|
||||
#[test]
|
||||
fn test_reset_rep_byte() {
|
||||
let mut registers = Registers::new();
|
||||
registers.emulation_mode = false;
|
||||
registers.p = 0xFF;
|
||||
registers.reset_rep_byte(0b0000_0001);
|
||||
assert_eq!(registers.p, 0b1111_1110);
|
||||
|
||||
@@ -209,8 +209,8 @@ impl PPURegisters {
|
||||
pub fn read(&mut self, address: u16) -> u8 {
|
||||
let result = self._read(address);
|
||||
match address {
|
||||
VMDATAH => self.handle_vram_addr_auto_increment(Some(result), None),
|
||||
VMDATAL => self.handle_vram_addr_auto_increment(None, Some(result)),
|
||||
VMDATAH | RDVRAMH => self.handle_vram_addr_auto_increment(Some(result), None),
|
||||
VMDATAL | RDVRAML => self.handle_vram_addr_auto_increment(None, Some(result)),
|
||||
RDCGRAM => {
|
||||
let value = self.get_rdcgram();
|
||||
self._write(RDCGRAM, value);
|
||||
@@ -230,6 +230,9 @@ impl PPURegisters {
|
||||
self._write(address, value);
|
||||
self.handle_write_vram(None, Some(value));
|
||||
},
|
||||
VMADDH | VMADDL => {
|
||||
self._write(address, value);
|
||||
},
|
||||
RDVRAML | RDVRAMH => {},
|
||||
CGADD => {
|
||||
self._write(address, value);
|
||||
@@ -241,14 +244,14 @@ impl PPURegisters {
|
||||
}
|
||||
|
||||
fn handle_write_vram(&mut self, byte_hi: Option<u8>, byte_lo: Option<u8>) {
|
||||
let address = (((self.read(VMADDH) as u16) << 8) | (self.read(VMADDL) as u16)) & 0x7FFF;
|
||||
let current_word = self.vram[address as usize];
|
||||
let address = (self.get_current_vram_address() & 0x7FFF) as usize;
|
||||
let current_word = self.vram[address];
|
||||
if let Some(byte) = byte_hi {
|
||||
self.vram[address as usize] = (current_word & 0x00FF) | ((byte as u16) << 8);
|
||||
self.vram[address] = (current_word & 0x00FF) | ((byte as u16) << 8);
|
||||
self._write(RDVRAMH, byte);
|
||||
}
|
||||
if let Some(byte) = byte_lo {
|
||||
self.vram[address as usize] = (current_word & 0xFF00) | (byte as u16);
|
||||
self.vram[address] = (current_word & 0xFF00) | (byte as u16);
|
||||
self._write(RDVRAML, byte);
|
||||
}
|
||||
self.handle_vram_addr_auto_increment(byte_hi, byte_lo);
|
||||
@@ -263,7 +266,18 @@ impl PPURegisters {
|
||||
0b11 => 128,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let increment_when_lo = (register >> 7) != 1;
|
||||
let address_translation_rotate = match (register >> 2) & 0b11 {
|
||||
0b00 => 0,
|
||||
0b01 => 8,
|
||||
0b10 => 9,
|
||||
0b11 => 10,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if address_translation_rotate > 0 {
|
||||
println!("ADDRESS ROTATE {}", address_translation_rotate);
|
||||
// TODO: implement address translation
|
||||
}
|
||||
let increment_when_lo = (register >> 7) == 0;
|
||||
let increment_when_hi = !increment_when_lo;
|
||||
let current_value = self.get_current_vram_address();
|
||||
if increment_when_hi && byte_hi.is_some() {
|
||||
@@ -279,9 +293,8 @@ impl PPURegisters {
|
||||
}
|
||||
|
||||
fn set_current_vram_address(&mut self, value: u16) {
|
||||
let bytes = value.to_be_bytes();
|
||||
self._write(VMADDH, bytes[0]);
|
||||
self._write(VMADDL, bytes[1]);
|
||||
self._write(VMADDH, (value >> 8) as u8);
|
||||
self._write(VMADDL, value as u8);
|
||||
}
|
||||
|
||||
/// 7 BG4 Tile Size (0=8x8, 1=16x16) ;\(BgMode0..4: variable 8x8 or 16x16)
|
||||
|
||||
@@ -116,7 +116,7 @@ fn main() -> Result<()> {
|
||||
emulator.tick();
|
||||
}
|
||||
|
||||
let is_emu_mode = emulator.cpu.registers.emulation_mode;
|
||||
let is_emu_mode = emulator.cpu.registers.emulation_mode || (test.initial.e == 1);
|
||||
let is_16index = emulator.cpu.registers.is_16bit_index();
|
||||
|
||||
let emu_mode_sp = (emulator.cpu.registers.sp as usize & 0xFF) | 0x100;
|
||||
|
||||
@@ -190,7 +190,8 @@ fn build_vram_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugContro
|
||||
for row in chunks {
|
||||
let mut address_row = format!("{:04X} | ", row[0]);
|
||||
for address in row {
|
||||
address_row = format!("{}{:04X} ", address_row, ppu_registers.vram()[((*address) & 0x7FFF) as usize]);
|
||||
let effective_vram_address = ((*address) & 0x7FFF) as usize;
|
||||
address_row = format!("{}{:04X} ", address_row, ppu_registers.vram()[effective_vram_address]);
|
||||
}
|
||||
ui.monospace(address_row);
|
||||
}
|
||||
@@ -233,14 +234,16 @@ fn build_cgram_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugContr
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("0x00: ");
|
||||
paint_cgram_color_address(ui, 0x00, ppu_registers);
|
||||
ui.label("0x01: ");
|
||||
paint_cgram_color_address(ui, 0x01, ppu_registers);
|
||||
ui.label("0x02: ");
|
||||
paint_cgram_color_address(ui, 0x02, ppu_registers);
|
||||
});
|
||||
let mut address: u8 = 0x00;
|
||||
for row in 0x00..=0x0F {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("0x{:02X}: ", row));
|
||||
for _ in 0x00..=0x0F {
|
||||
paint_cgram_color_address(ui, address, ppu_registers);
|
||||
address = address.wrapping_add(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user