Improve timer; Add 16 px sprite support
This commit is contained in:
parent
1ad3d47c88
commit
b71397d16a
@ -106,6 +106,15 @@ impl Cartridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_byte_none(&mut self, addr: u16, val: u8) {
|
||||||
|
match addr {
|
||||||
|
0xA000 ... 0xBFFF => {
|
||||||
|
self.ram[addr as usize - 0xA000] = val;
|
||||||
|
}
|
||||||
|
_ => println!("No MBC, unable to write {:02X} to {:04X}", val, addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn write_byte_mbc3(&mut self, addr: u16, val: u8) {
|
fn write_byte_mbc3(&mut self, addr: u16, val: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
0x0000 ... 0x1FFF => {
|
0x0000 ... 0x1FFF => {
|
||||||
@ -143,7 +152,7 @@ impl Cartridge {
|
|||||||
|
|
||||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
match self.mbc_type {
|
match self.mbc_type {
|
||||||
MemoryBankControllerType::None => println!("Cartridge: No MBC found, can not write to ROM ({:02X} to {:04X})", val, addr),
|
MemoryBankControllerType::None => self.write_byte_none(addr, val),
|
||||||
MemoryBankControllerType::MBC3 => self.write_byte_mbc3(addr, val),
|
MemoryBankControllerType::MBC3 => self.write_byte_mbc3(addr, val),
|
||||||
_ => panic!("MBC not supported.")
|
_ => panic!("MBC not supported.")
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/cpu.rs
36
src/cpu.rs
@ -742,7 +742,7 @@ impl CPU {
|
|||||||
let e_pending = pending & enabled;
|
let e_pending = pending & enabled;
|
||||||
|
|
||||||
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
|
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
|
||||||
println!("Handling vblank interrupt");
|
// println!("Handling vblank interrupt");
|
||||||
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
|
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
|
||||||
} else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 {
|
} else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 {
|
||||||
println!("Handling display stat interrupt");
|
println!("Handling display stat interrupt");
|
||||||
@ -906,7 +906,39 @@ impl CPU {
|
|||||||
0x24 => self.reg_inc(REG_N_H),
|
0x24 => self.reg_inc(REG_N_H),
|
||||||
0x25 => self.reg_dec(REG_N_H),
|
0x25 => self.reg_dec(REG_N_H),
|
||||||
0x26 => self.ld_r_v(REG_N_H),
|
0x26 => self.ld_r_v(REG_N_H),
|
||||||
0x27 => panic!("DAA not implemented!"),
|
0x27 => {
|
||||||
|
// Logic copied from some other emulator
|
||||||
|
let mut v = self.regs[REG_A] as u16;
|
||||||
|
|
||||||
|
if self.flags & FLAG_N > 0{
|
||||||
|
// Lower nibble
|
||||||
|
if self.flags & FLAG_H > 0 || v & 0xF > 9 {
|
||||||
|
v += 0x06;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Higher nibble
|
||||||
|
if self.flags & FLAG_C > 0 || v > 0x9F {
|
||||||
|
v += 0x60;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Lower nibble
|
||||||
|
if self.flags & FLAG_H > 0 {
|
||||||
|
v = v.wrapping_sub(6) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Higher nibble
|
||||||
|
if self.flags & FLAG_C > 0 {
|
||||||
|
v = v.wrapping_sub(0x60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.clear_flag(FLAG_H);
|
||||||
|
self.set_clear_flag(FLAG_C, v == 0x100);
|
||||||
|
self.set_clear_flag(FLAG_Z, v & 0xFF == 0);
|
||||||
|
self.regs[REG_A] = v as u8;
|
||||||
|
|
||||||
|
4
|
||||||
|
},
|
||||||
0x28 => {
|
0x28 => {
|
||||||
let c = self.flags & FLAG_Z > 0;
|
let c = self.flags & FLAG_Z > 0;
|
||||||
self.jmp_r_condition("Z".to_owned(), c)
|
self.jmp_r_condition("Z".to_owned(), c)
|
||||||
|
|||||||
@ -327,33 +327,56 @@ impl Display {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.control & CTRL_BG_SPRITE_ENABLE > 0 && false {
|
if self.control & CTRL_BG_SPRITE_ENABLE > 0 {
|
||||||
// panic!("Sprites not supported");
|
|
||||||
// Let's draw sprites.
|
// Let's draw sprites.
|
||||||
// TODO: Sprites with smaller X coordinate should
|
// TODO: Sprites with smaller X coordinate should
|
||||||
// should be in front
|
// should be in front
|
||||||
// TODO: Draw only up to 10 sprites per line
|
// TODO: Draw only up to 10 sprites per line
|
||||||
|
if self.control & CTRL_BG_SPRITE_SIZE > 0 {
|
||||||
|
println!("Wide sprites not tested!");
|
||||||
|
}
|
||||||
for i in 0 .. 39 {
|
for i in 0 .. 39 {
|
||||||
let y: u8 = self.oam[i * 4 + 0];
|
let mut y: u8 = self.oam[i * 4 + 0];
|
||||||
let x: u8 = self.oam[i * 4 + 1];
|
let mut x: u8 = self.oam[i * 4 + 1];
|
||||||
let t_num: u8 = self.oam[i * 4 + 2];
|
let t_num: u8 = self.oam[i * 4 + 2];
|
||||||
let flags: u8 = self.oam[i * 4 + 3];
|
let flags: u8 = self.oam[i * 4 + 3];
|
||||||
|
|
||||||
|
if x == 0 || y == 0 {
|
||||||
|
// This sprite is hidden
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = x.wrapping_sub(8);
|
||||||
|
y = y.wrapping_sub(16);
|
||||||
|
|
||||||
// Is this sprite on the current line?
|
// Is this sprite on the current line?
|
||||||
if (y + 8) >= render_y && y <= render_y {
|
if y.wrapping_add(8) >= render_y && y <= render_y {
|
||||||
let tile_offset_y: usize = render_y as usize - y as usize;
|
let tile_offset_y: usize = render_y as usize - y as usize;
|
||||||
let tile_base_addr: usize = 0x1000 + t_num as usize * 16;
|
let tile_base_addr: usize = 0 + t_num as usize * 16;
|
||||||
|
|
||||||
let tile_line_1 = self.vram[tile_base_addr + tile_offset_y * 2];
|
let tile_line_1 = self.vram[tile_base_addr + tile_offset_y * 2];
|
||||||
let tile_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1];
|
let tile_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1];
|
||||||
|
let tile_line_3 = self.vram[tile_base_addr + tile_offset_y * 2 + 2];
|
||||||
|
let tile_line_4 = self.vram[tile_base_addr + tile_offset_y * 2 + 3];
|
||||||
|
|
||||||
// We need to draw this.
|
// We need to draw this.
|
||||||
for x_o in 0 .. 8 {
|
let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0;
|
||||||
let b1: bool = (tile_line_1 & 1 << (7 - x_o)) > 0;
|
let limit = match wide_mode {
|
||||||
let b2: bool = (tile_line_2 & 1 << (7 - x_o)) > 0;
|
true => 16,
|
||||||
|
false => 8
|
||||||
|
};
|
||||||
|
for x_o in 0 .. limit {
|
||||||
|
let mut b1: bool;
|
||||||
|
let mut b2: bool;
|
||||||
let mut factor = 0;
|
let mut factor = 0;
|
||||||
|
if wide_mode && x_o > 7 {
|
||||||
|
b1 = (tile_line_3 & 1 << (14 - x_o)) > 0;
|
||||||
|
b2 = (tile_line_4 & 1 << (14 - x_o)) > 0;
|
||||||
|
} else {
|
||||||
|
b1 = (tile_line_1 & 1 << (7 - x_o)) > 0;
|
||||||
|
b2 = (tile_line_2 & 1 << (7 - x_o)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
if b1 {
|
if b1 {
|
||||||
factor += 64;
|
factor += 64;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -210,6 +210,7 @@ impl Interconnect {
|
|||||||
0xFF10 ... 0xFF26 => {
|
0xFF10 ... 0xFF26 => {
|
||||||
self.sound.read_byte(addr)
|
self.sound.read_byte(addr)
|
||||||
},
|
},
|
||||||
|
0xFF30 ... 0xFF3F => self.sound.read_byte(addr),
|
||||||
0xFF40 ... 0xFF4B => {
|
0xFF40 ... 0xFF4B => {
|
||||||
self.display.read_byte(addr)
|
self.display.read_byte(addr)
|
||||||
},
|
},
|
||||||
@ -278,18 +279,17 @@ impl Interconnect {
|
|||||||
0xFF10 ... 0xFF26 => {
|
0xFF10 ... 0xFF26 => {
|
||||||
self.sound.write_byte(addr, val);
|
self.sound.write_byte(addr, val);
|
||||||
},
|
},
|
||||||
|
0xFF30 ... 0xFF3F => self.sound.write_byte(addr, val),
|
||||||
// Exclude DMA transfer, we will do this below
|
// Exclude DMA transfer, we will do this below
|
||||||
0xFF40 ... 0xFF45 | 0xFF47 ... 0xFF4B => {
|
0xFF40 ... 0xFF45 | 0xFF47 ... 0xFF4B => {
|
||||||
self.display.write_byte(addr, val);
|
self.display.write_byte(addr, val);
|
||||||
},
|
},
|
||||||
0xFF46 => {
|
0xFF46 => {
|
||||||
println!("OAM DMA transfer: ");
|
// println!("OAM DMA transfer");
|
||||||
for x in 0x00 .. 0x9F {
|
for x in 0x00 .. 0x9F {
|
||||||
let dma_b = self.read_byte(((val as u16) << 8) | x);
|
let dma_b = self.read_byte(((val as u16) << 8) | x);
|
||||||
self.write_byte(0xFE00 | x, dma_b);
|
self.write_byte(0xFE00 | x, dma_b);
|
||||||
print!("{:02X} ", val);
|
|
||||||
}
|
}
|
||||||
println!("");
|
|
||||||
}
|
}
|
||||||
0xFF50 => {
|
0xFF50 => {
|
||||||
println!("Disabling boot rom.");
|
println!("Disabling boot rom.");
|
||||||
|
|||||||
@ -23,7 +23,9 @@ impl Sound {
|
|||||||
0xFF24 => self.sound_channel_volume_control = val,
|
0xFF24 => self.sound_channel_volume_control = val,
|
||||||
0xFF25 => self.sound_output_terminal_selector = val,
|
0xFF25 => self.sound_output_terminal_selector = val,
|
||||||
0xFF26 => self.enabled = val,
|
0xFF26 => self.enabled = val,
|
||||||
_ => println!("Sound: Write {:02X} to {:04X} unsupported", val, addr),
|
_ => {
|
||||||
|
// println!("Sound: Write {:02X} to {:04X} unsupported", val, addr);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
src/timer.rs
41
src/timer.rs
@ -1,10 +1,12 @@
|
|||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Timer {
|
pub struct Timer {
|
||||||
tima: u8,
|
tima: u8, // Timer counter, interrupt on overflow
|
||||||
tma: u8,
|
tma: u8,
|
||||||
tac: u8,
|
tac: u8, // 00 = 4096 Hz [10240 ticks], 01 = 262144 Hz [160 ticks], 10 = 65536 Hz [640], 11 = 16384 Hz[2560]. Bit 2 0 = stop, 1 = start
|
||||||
|
div: u8, // Incremented at speed of 16384Hz
|
||||||
timer_interrupt: bool,
|
timer_interrupt: bool,
|
||||||
ctr: u16,
|
tick_counter: u16,
|
||||||
|
div_tick_counter: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Timer {
|
impl Timer {
|
||||||
@ -13,15 +15,39 @@ impl Timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self, ticks: u16) {
|
pub fn tick(&mut self, ticks: u16) {
|
||||||
self.ctr += ticks;
|
// One tick 1/4.194304MHz on regular speed?
|
||||||
if self.ctr > 0x1000 {
|
if self.tac & 1 << 2 == 1 { // Is timer enabled?
|
||||||
self.ctr = 0;
|
self.tick_counter += ticks;
|
||||||
self.timer_interrupt = true;
|
let req_ticks = match self.tac & 3 {
|
||||||
|
0 => 10240,
|
||||||
|
1 => 160,
|
||||||
|
2 => 640,
|
||||||
|
3 => 2560,
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.tick_counter > req_ticks {
|
||||||
|
if self.tima == 0xFF {
|
||||||
|
self.timer_interrupt = true;
|
||||||
|
self.tima = 0;
|
||||||
|
} else {
|
||||||
|
self.tima += 1;
|
||||||
|
}
|
||||||
|
self.tick_counter -= req_ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The div reg will always tick
|
||||||
|
self.div_tick_counter += ticks;
|
||||||
|
if self.div_tick_counter > 2560 {
|
||||||
|
self.div = self.div.wrapping_add(1);
|
||||||
|
self.div_tick_counter -= 2560;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
|
0xFF04 => self.div = 0,
|
||||||
0xFF05 => self.tima = val,
|
0xFF05 => self.tima = val,
|
||||||
0xFF06 => self.tma = val,
|
0xFF06 => self.tma = val,
|
||||||
0xFF07 => self.tac = val,
|
0xFF07 => self.tac = val,
|
||||||
@ -31,6 +57,7 @@ impl Timer {
|
|||||||
|
|
||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
|
0xFF04 => self.div,
|
||||||
0xFF05 => self.tima,
|
0xFF05 => self.tima,
|
||||||
0xFF06 => self.tma,
|
0xFF06 => self.tma,
|
||||||
0xFF07 => self.tac,
|
0xFF07 => self.tac,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user