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 {
|
impl CPUInstruction for COP {
|
||||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
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);
|
let (bytes, cycles) = cycles::increment_cycles_brk(registers.emulation_mode);
|
||||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
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.pc >> 8) as u8, registers.pc as u8]);
|
||||||
push_common::do_push(registers, bus, &[registers.p]);
|
push_common::do_push(registers, bus, &[registers.p]);
|
||||||
registers.set_irq_disable_flag(true);
|
registers.set_irq_disable_flag(true);
|
||||||
registers.pbr = 0x00;
|
registers.pbr = 0x00;
|
||||||
let vector = (bus.read(0x00FFE4) as u16) | ((bus.read(0x00FFE5) as u16) << 8);
|
if registers.emulation_mode {
|
||||||
registers.pc = vector;
|
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);
|
registers.set_decimal_mode_flag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ impl CPUInstruction for PLP {
|
|||||||
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||||
let bytes = pull_common::do_pull(registers, bus, 1, true);
|
let bytes = pull_common::do_pull(registers, bus, 1, true);
|
||||||
registers.p = bytes[0];
|
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();
|
let (bytes, cycles) = cycles::increment_cycles_plp();
|
||||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
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;
|
let mut is_zero = true;
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
registers.increment_sp(1);
|
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 {
|
if byte != 0 {
|
||||||
is_zero = false;
|
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]) {
|
pub fn do_push(registers: &mut Registers, bus: &mut Bus, bytes: &[u8]) {
|
||||||
for byte in bytes {
|
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);
|
bus.write(address, *byte);
|
||||||
registers.decrement_sp(1);
|
registers.decrement_sp(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ pub struct XCE {}
|
|||||||
impl CPUInstruction for XCE {
|
impl CPUInstruction for XCE {
|
||||||
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
fn execute(&self, registers: &mut Registers, _bus: &mut Bus) {
|
||||||
registers.exchange_carry_and_emulation();
|
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();
|
let (bytes, cycles) = cycles::increment_cycles_exchange();
|
||||||
registers.increment_pc(bytes); registers.cycles += cycles;
|
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||||
}
|
}
|
||||||
@@ -34,5 +42,44 @@ mod cpu_instructions_tests {
|
|||||||
instruction.execute(&mut registers, &mut bus);
|
instruction.execute(&mut registers, &mut bus);
|
||||||
assert_eq!(registers.pc, 0x0001);
|
assert_eq!(registers.pc, 0x0001);
|
||||||
assert_eq!(registers.cycles, 2);
|
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 is_move_next: bool, // TODO: refactor these states with an enum
|
||||||
pub cycles: usize,
|
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 {
|
impl Registers {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -214,7 +242,11 @@ impl Registers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_rep_byte(&mut self, byte: u8) {
|
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) {
|
pub fn set_sep_byte(&mut self, byte: u8) {
|
||||||
@@ -440,6 +472,7 @@ mod registers_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_reset_rep_byte() {
|
fn test_reset_rep_byte() {
|
||||||
let mut registers = Registers::new();
|
let mut registers = Registers::new();
|
||||||
|
registers.emulation_mode = false;
|
||||||
registers.p = 0xFF;
|
registers.p = 0xFF;
|
||||||
registers.reset_rep_byte(0b0000_0001);
|
registers.reset_rep_byte(0b0000_0001);
|
||||||
assert_eq!(registers.p, 0b1111_1110);
|
assert_eq!(registers.p, 0b1111_1110);
|
||||||
|
|||||||
@@ -209,8 +209,8 @@ impl PPURegisters {
|
|||||||
pub fn read(&mut self, address: u16) -> u8 {
|
pub fn read(&mut self, address: u16) -> u8 {
|
||||||
let result = self._read(address);
|
let result = self._read(address);
|
||||||
match address {
|
match address {
|
||||||
VMDATAH => self.handle_vram_addr_auto_increment(Some(result), None),
|
VMDATAH | RDVRAMH => self.handle_vram_addr_auto_increment(Some(result), None),
|
||||||
VMDATAL => self.handle_vram_addr_auto_increment(None, Some(result)),
|
VMDATAL | RDVRAML => self.handle_vram_addr_auto_increment(None, Some(result)),
|
||||||
RDCGRAM => {
|
RDCGRAM => {
|
||||||
let value = self.get_rdcgram();
|
let value = self.get_rdcgram();
|
||||||
self._write(RDCGRAM, value);
|
self._write(RDCGRAM, value);
|
||||||
@@ -230,6 +230,9 @@ impl PPURegisters {
|
|||||||
self._write(address, value);
|
self._write(address, value);
|
||||||
self.handle_write_vram(None, Some(value));
|
self.handle_write_vram(None, Some(value));
|
||||||
},
|
},
|
||||||
|
VMADDH | VMADDL => {
|
||||||
|
self._write(address, value);
|
||||||
|
},
|
||||||
RDVRAML | RDVRAMH => {},
|
RDVRAML | RDVRAMH => {},
|
||||||
CGADD => {
|
CGADD => {
|
||||||
self._write(address, value);
|
self._write(address, value);
|
||||||
@@ -241,14 +244,14 @@ impl PPURegisters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_write_vram(&mut self, byte_hi: Option<u8>, byte_lo: Option<u8>) {
|
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 address = (self.get_current_vram_address() & 0x7FFF) as usize;
|
||||||
let current_word = self.vram[address as usize];
|
let current_word = self.vram[address];
|
||||||
if let Some(byte) = byte_hi {
|
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);
|
self._write(RDVRAMH, byte);
|
||||||
}
|
}
|
||||||
if let Some(byte) = byte_lo {
|
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._write(RDVRAML, byte);
|
||||||
}
|
}
|
||||||
self.handle_vram_addr_auto_increment(byte_hi, byte_lo);
|
self.handle_vram_addr_auto_increment(byte_hi, byte_lo);
|
||||||
@@ -263,7 +266,18 @@ impl PPURegisters {
|
|||||||
0b11 => 128,
|
0b11 => 128,
|
||||||
_ => unreachable!(),
|
_ => 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 increment_when_hi = !increment_when_lo;
|
||||||
let current_value = self.get_current_vram_address();
|
let current_value = self.get_current_vram_address();
|
||||||
if increment_when_hi && byte_hi.is_some() {
|
if increment_when_hi && byte_hi.is_some() {
|
||||||
@@ -279,9 +293,8 @@ impl PPURegisters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_vram_address(&mut self, value: u16) {
|
fn set_current_vram_address(&mut self, value: u16) {
|
||||||
let bytes = value.to_be_bytes();
|
self._write(VMADDH, (value >> 8) as u8);
|
||||||
self._write(VMADDH, bytes[0]);
|
self._write(VMADDL, value as u8);
|
||||||
self._write(VMADDL, bytes[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 7 BG4 Tile Size (0=8x8, 1=16x16) ;\(BgMode0..4: variable 8x8 or 16x16)
|
/// 7 BG4 Tile Size (0=8x8, 1=16x16) ;\(BgMode0..4: variable 8x8 or 16x16)
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ fn main() -> Result<()> {
|
|||||||
emulator.tick();
|
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 is_16index = emulator.cpu.registers.is_16bit_index();
|
||||||
|
|
||||||
let emu_mode_sp = (emulator.cpu.registers.sp as usize & 0xFF) | 0x100;
|
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 {
|
for row in chunks {
|
||||||
let mut address_row = format!("{:04X} | ", row[0]);
|
let mut address_row = format!("{:04X} | ", row[0]);
|
||||||
for address in row {
|
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);
|
ui.monospace(address_row);
|
||||||
}
|
}
|
||||||
@@ -233,14 +234,16 @@ fn build_cgram_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugContr
|
|||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
let mut address: u8 = 0x00;
|
||||||
ui.label("0x00: ");
|
for row in 0x00..=0x0F {
|
||||||
paint_cgram_color_address(ui, 0x00, ppu_registers);
|
ui.horizontal(|ui| {
|
||||||
ui.label("0x01: ");
|
ui.label(format!("0x{:02X}: ", row));
|
||||||
paint_cgram_color_address(ui, 0x01, ppu_registers);
|
for _ in 0x00..=0x0F {
|
||||||
ui.label("0x02: ");
|
paint_cgram_color_address(ui, address, ppu_registers);
|
||||||
paint_cgram_color_address(ui, 0x02, ppu_registers);
|
address = address.wrapping_add(1);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user