Compare commits

..

No commits in common. "b7c1d9b8a03390d4f44acc4035630619714d34e4" and "ab7ee0988f4e2dce7174989766f4f95546a87ca4" have entirely different histories.

9 changed files with 264 additions and 306 deletions

View File

@ -4,11 +4,7 @@ version = "0.1.0"
authors = ["Kevin Hamacher <kevin.hamacher@rub.de>"] authors = ["Kevin Hamacher <kevin.hamacher@rub.de>"]
edition = '2018' edition = '2018'
[features]
default = ["audio"]
audio = ["pulse-simple"]
[dependencies] [dependencies]
sdl2 = "*" sdl2 = "*"
libc = "*" libc = "*"
pulse-simple = { version = "*", optional = true } pulse-simple = "*"

View File

@ -141,7 +141,12 @@ impl CPU {
fn adc_r(&mut self, val: u8) { fn adc_r(&mut self, val: u8) {
let old: u8 = self.regs[REG_A]; let old: u8 = self.regs[REG_A];
let mut new: u8 = old; let mut new: u8 = old;
let c = if self.flags & FLAG_C == FLAG_C { 1 } else { 0 }; let c: u8;
if self.flags & FLAG_C == FLAG_C {
c = 1;
} else {
c = 0;
}
new = new.wrapping_add(val); new = new.wrapping_add(val);
new = new.wrapping_add(c); new = new.wrapping_add(c);
self.regs[REG_A] = new; self.regs[REG_A] = new;
@ -167,7 +172,12 @@ impl CPU {
fn sbc_r(&mut self, val: u8) { fn sbc_r(&mut self, val: u8) {
let old: u8 = self.regs[REG_A]; let old: u8 = self.regs[REG_A];
let mut new: u8 = old as u8; let mut new: u8 = old as u8;
let c = if self.flags & FLAG_C == FLAG_C { 1 } else { 0 }; let c: u8;
if self.flags & FLAG_C == FLAG_C {
c = 1;
} else {
c = 0;
}
new = new.wrapping_sub(val); new = new.wrapping_sub(val);
new = new.wrapping_sub(c); new = new.wrapping_sub(c);
@ -217,11 +227,12 @@ impl CPU {
println!("RLC {}", REG_NAMES[reg_id]); println!("RLC {}", REG_NAMES[reg_id]);
} }
let nval = if val & 0x80 == 0x80 { let nval: u8;
val << 1 | 1 if val & 0x80 == 0x80 {
nval = val << 1 | 1;
} else { } else {
val << 1 nval = val << 1;
}; }
self.set_8bit_reg(reg_id, nval); self.set_8bit_reg(reg_id, nval);
self.set_clear_flag(FLAG_C, val & 0x80 == 0x80); self.set_clear_flag(FLAG_C, val & 0x80 == 0x80);
@ -255,7 +266,12 @@ impl CPU {
} }
let carry = self.flags & FLAG_C > 0; let carry = self.flags & FLAG_C > 0;
let nval = if !carry { val << 1 } else { val << 1 | 1 }; let nval: u8;
if !carry {
nval = val << 1;
} else {
nval = val << 1 | 1;
}
self.set_8bit_reg(reg_id, nval); self.set_8bit_reg(reg_id, nval);
self.set_clear_flag(FLAG_C, val & 0x80 == 0x80); self.set_clear_flag(FLAG_C, val & 0x80 == 0x80);
self.set_clear_flag(FLAG_Z, nval == 0); self.set_clear_flag(FLAG_Z, nval == 0);
@ -271,7 +287,12 @@ impl CPU {
} }
let carry = self.flags & FLAG_C > 0; let carry = self.flags & FLAG_C > 0;
let v = if !carry { val >> 1 } else { (val >> 1) | 0x80 }; let v: u8;
if !carry {
v = val >> 1;
} else {
v = (val >> 1) | 0x80;
}
self.set_8bit_reg(reg_id, v); self.set_8bit_reg(reg_id, v);
self.set_clear_flag(FLAG_C, val & 1 == 1); self.set_clear_flag(FLAG_C, val & 1 == 1);
self.set_clear_flag(FLAG_Z, v == 0); self.set_clear_flag(FLAG_Z, v == 0);
@ -767,7 +788,7 @@ impl CPU {
self.clear_flag(FLAG_N); self.clear_flag(FLAG_N);
// Some magic formula // Some magic formula
self.set_clear_flag(FLAG_C, (val1 as usize + val2 as usize) & 0x10000 == 0x10000); self.set_clear_flag(FLAG_C, val1 as usize + val2 as usize & 0x10000 == 0x10000);
self.set_clear_flag(FLAG_H, (val1 ^ val2 ^ res) & 0x1000 == 0x1000); self.set_clear_flag(FLAG_H, (val1 ^ val2 ^ res) & 0x1000 == 0x1000);
8 8
} }
@ -927,8 +948,10 @@ impl CPU {
// Check for interrupts. // Check for interrupts.
if self.ime { if self.ime {
self.check_interrupts(true); self.check_interrupts(true);
} else if self.halted && self.check_interrupts(false) { } else if self.halted {
self.halted = false; if self.check_interrupts(false) {
self.halted = false;
}
} }
let mut cycles: u8 = 255; let mut cycles: u8 = 255;
@ -938,27 +961,13 @@ impl CPU {
instruction = self.read_byte(self.ip); instruction = self.read_byte(self.ip);
if self.debug { if self.debug {
print!( print!(
"{:#06X}: [SP: {:#04X}] i={:02X}. ", "{:#06x}: [SP: {:#04X}] i={:02X}. ",
&self.ip, &self.sp, &instruction &self.ip, &self.sp, &instruction
); );
/* for i in 0..6 {
for (idx, reg) in REG_NAMES.iter().enumerate() { print!("{}: {:02X} ", REG_NAMES[i], self.get_8bit_reg(i));
print!("{}: {:02X} ", reg, self.get_8bit_reg(idx));
} }
print!("A: {:02X} ", self.regs[REG_A]); print!("A: {:02X} ", self.regs[REG_A]);
*/
print!(
"AF={:02X}{:02X} BC={:02X}{:02X} DE={:02X}{:02X} HL={:02X}{:02X} ",
self.get_8bit_reg(REG_N_A),
self.get_8bit_reg(REG_N_F),
self.get_8bit_reg(REG_N_B),
self.get_8bit_reg(REG_N_C),
self.get_8bit_reg(REG_N_D),
self.get_8bit_reg(REG_N_E),
self.get_8bit_reg(REG_N_H),
self.get_8bit_reg(REG_N_L)
);
print!("I: {:02X} ", self.interconnect.read_byte(0xFFFF)); print!("I: {:02X} ", self.interconnect.read_byte(0xFFFF));
// Flags // Flags
@ -991,10 +1000,9 @@ impl CPU {
let carry = val & 0x80 == 0x80; let carry = val & 0x80 == 0x80;
self.set_clear_flag(FLAG_C, carry); self.set_clear_flag(FLAG_C, carry);
if !carry { if !carry {
self.regs[REG_A] <<= 1; self.regs[REG_A] = self.regs[REG_A] << 1;
} else { } else {
self.regs[REG_A] <<= 1; self.regs[REG_A] = self.regs[REG_A] << 1 | 1;
self.regs[REG_A] |= 1;
} }
self.clear_flag(FLAG_Z); self.clear_flag(FLAG_Z);
self.clear_flag(FLAG_N); self.clear_flag(FLAG_N);
@ -1030,10 +1038,9 @@ impl CPU {
let val = self.regs[REG_A]; let val = self.regs[REG_A];
self.set_clear_flag(FLAG_C, val & 1 == 1); self.set_clear_flag(FLAG_C, val & 1 == 1);
if val & 1 == 0 { if val & 1 == 0 {
self.regs[REG_A] >>= 1; self.regs[REG_A] = self.regs[REG_A] >> 1;
} else { } else {
self.regs[REG_A] >>= 1; self.regs[REG_A] = self.regs[REG_A] >> 1 | 0x80;
self.regs[REG_A] |= 0x80;
} }
self.clear_flag(FLAG_Z); self.clear_flag(FLAG_Z);
self.clear_flag(FLAG_N); self.clear_flag(FLAG_N);
@ -1062,10 +1069,9 @@ impl CPU {
let val = self.regs[REG_A]; let val = self.regs[REG_A];
self.set_clear_flag(FLAG_C, val & 0x80 == 0x80); self.set_clear_flag(FLAG_C, val & 0x80 == 0x80);
if !carry { if !carry {
self.regs[REG_A] <<= 1; self.regs[REG_A] = self.regs[REG_A] << 1;
} else { } else {
self.regs[REG_A] <<= 1; self.regs[REG_A] = self.regs[REG_A] << 1 | 1;
self.regs[REG_A] |= 1;
} }
self.clear_flag(FLAG_Z); self.clear_flag(FLAG_Z);
self.clear_flag(FLAG_N); self.clear_flag(FLAG_N);
@ -1102,10 +1108,9 @@ impl CPU {
let val = self.regs[REG_A]; let val = self.regs[REG_A];
self.set_clear_flag(FLAG_C, val & 1 == 1); self.set_clear_flag(FLAG_C, val & 1 == 1);
if !carry { if !carry {
self.regs[REG_A] >>= 1; self.regs[REG_A] = self.regs[REG_A] >> 1;
} else { } else {
self.regs[REG_A] >>= 1; self.regs[REG_A] = self.regs[REG_A] >> 1 | 0x80;
self.regs[REG_A] |= 0x80;
} }
self.clear_flag(FLAG_Z); self.clear_flag(FLAG_Z);
self.clear_flag(FLAG_N); self.clear_flag(FLAG_N);
@ -1572,11 +1577,12 @@ impl CPU {
if self.debug { if self.debug {
println!("ADD SP, {:02X}", arg); println!("ADD SP, {:02X}", arg);
} }
let t = if arg > 0 { let t: u16;
self.sp.wrapping_add(arg as u16) if arg > 0 {
t = self.sp.wrapping_add(arg as u16);
} else { } else {
self.sp.wrapping_sub((-arg) as u16) t = self.sp.wrapping_sub((-arg) as u16);
}; }
let sp = self.sp; let sp = self.sp;
self.clear_flag(FLAG_N); self.clear_flag(FLAG_N);
self.clear_flag(FLAG_Z); self.clear_flag(FLAG_Z);

View File

@ -65,7 +65,7 @@ const MONOCHROME_PALETTE: &'static [[u8; 3]; 4] = &[
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct CgbPalette([u8; 8]); struct CgbPalette([u8; 8]);
impl CgbPalette { impl CgbPalette {
fn get_color(self, n: usize) -> sdl2::pixels::Color { fn get_color(&self, n: usize) -> sdl2::pixels::Color {
if n == 0 { if n == 0 {
return sdl2::pixels::Color::RGB(255, 255, 255); return sdl2::pixels::Color::RGB(255, 255, 255);
} }
@ -73,34 +73,15 @@ impl CgbPalette {
let r = (v & 0b1_1111) as u8; let r = (v & 0b1_1111) as u8;
let g = ((v >> 5) & 0b1_1111) as u8; let g = ((v >> 5) & 0b1_1111) as u8;
let b = ((v >> 10) & 0b1_1111) as u8; let b = ((v >> 10) & 0b1_1111) as u8;
// According to some code:
if false { // Real colors:
sdl2::pixels::Color::RGB(r << 3, g << 3, b << 3) let r = r as u16;
} else { let g = g as u16;
// According to some code: let b = b as u16;
// Real colors: let mapped_r = ((r * 13 + g * 2 + b) >> 1) as u8;
let r = r as u16; let mapped_g = ((g * 3 + b) << 1) as u8;
let g = g as u16; let mapped_b = ((r * 3 + g * 2 + b * 11) >> 1) as u8;
let b = b as u16; sdl2::pixels::Color::RGB(mapped_r, mapped_g, mapped_b)
let mapped_r = ((r * 13 + g * 2 + b) >> 1) as u8;
let mapped_g = ((g * 3 + b) << 1) as u8;
let mapped_b = ((r * 3 + g * 2 + b * 11) >> 1) as u8;
sdl2::pixels::Color::RGB(mapped_r, mapped_g, mapped_b)
}
}
}
impl std::fmt::Display for CgbPalette {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Palette: ")?;
for n in 0..4 {
let v = ((self.0[2 * n + 1] as u16) << 8) | (self.0[2 * n] as u16);
let r = (v & 0b1_1111) as u8;
let g = ((v >> 5) & 0b1_1111) as u8;
let b = ((v >> 10) & 0b1_1111) as u8;
write!(f, "{:02X}{:02X}{:02X} ", r, g, b)?;
}
write!(f, "")
} }
} }
@ -146,7 +127,7 @@ impl Default for Pixel {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug)]
enum DisplayMode { enum DisplayMode {
ReadOAMMemory, ReadOAMMemory,
ReadFullMemory, ReadFullMemory,
@ -387,16 +368,11 @@ impl Display {
self.background_palette_autoinc = (val >> 7) != 0; self.background_palette_autoinc = (val >> 7) != 0;
} }
0xFF69 => { 0xFF69 => {
if self.current_mode == DisplayMode::ReadFullMemory {
println!("Trying to write to palette memory while being accessed!");
}
let idx = self.background_palette_index as usize; let idx = self.background_palette_index as usize;
if idx < 64 { if idx < 64 {
self.background_palette_cgb[idx / 8].0[idx % 8] = val; self.background_palette_cgb[idx / 8].0[idx % 8] = val;
} else { } else {
println!("OOB palette w/ autoinc"); panic!("OOB palette w/ autoinc");
return;
} }
if self.background_palette_autoinc { if self.background_palette_autoinc {
self.background_palette_index += 1; self.background_palette_index += 1;
@ -411,8 +387,7 @@ impl Display {
if idx < 64 { if idx < 64 {
self.object_palette_cgb[idx / 8].0[idx % 8] = val; self.object_palette_cgb[idx / 8].0[idx % 8] = val;
} else { } else {
println!("OOB obj palette w/ autoinc"); panic!("OOB obj palette w/ autoinc");
return;
} }
if self.object_palette_autoinc { if self.object_palette_autoinc {
self.object_palette_index += 1; self.object_palette_index += 1;
@ -430,7 +405,7 @@ impl Display {
} }
self.windowx = val; self.windowx = val;
} }
0xFF4F => self.vram_bank = val, 0xFF4f => self.vram_bank = val,
_ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr), _ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr),
} }
} }
@ -585,14 +560,15 @@ impl Display {
} }
} }
fn render_sprites(&mut self, sprites: &[Sprite]) { fn render_sprites(&mut self, sprites: &Vec<Sprite>) {
if self.control & CTRL_BG_SPRITE_ENABLE > 0 { if self.control & CTRL_BG_SPRITE_ENABLE > 0 {
let mut num_rendered: u8 = 0; let mut num_rendered: u8 = 0;
for sprite in sprites.iter().take(39) { for i in 0..39 {
// Gameboy limitation // Gameboy limitation
if num_rendered >= 10 { if num_rendered >= 10 {
break; break;
} }
let sprite = &sprites[i];
// Skip hidden sprites // Skip hidden sprites
if sprite.is_hidden() { if sprite.is_hidden() {
@ -607,10 +583,13 @@ impl Display {
// Is this sprite on the current line? // Is this sprite on the current line?
let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0; let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0;
let actual_h = if wide_mode { 16 } else { 8 }; let actual_h = match wide_mode {
true => 16,
false => 8,
};
if sprite.is_y_flipped() { if sprite.is_y_flipped() {
eprintln!("Sorry, no y flip support yet, rendering as-is"); panic!("Sorry, no y flip support yet");
} }
if y.wrapping_add(actual_h) > render_y && y <= render_y { if y.wrapping_add(actual_h) > render_y && y <= render_y {
@ -642,7 +621,10 @@ impl Display {
} }
} }
let x_maybe_flipped: u8 = if sprite.is_x_flipped() { x_o ^ 7 } else { x_o }; let x_maybe_flipped: u8 = match sprite.is_x_flipped() {
true => x_o ^ 7,
false => x_o,
};
b1 = (tile_line_1 & 1 << (7 - x_o)) > 0; b1 = (tile_line_1 & 1 << (7 - x_o)) > 0;
b2 = (tile_line_2 & 1 << (7 - x_o)) > 0; b2 = (tile_line_2 & 1 << (7 - x_o)) > 0;
@ -679,7 +661,7 @@ impl Display {
PixelOrigin::Sprite, PixelOrigin::Sprite,
); );
*/ */
let c = ((b2 as u8) * 2 + b1 as u8) as usize; let c = ((b1 as u8) * 2 + b2 as u8) as usize;
if c == 0 { if c == 0 {
continue; continue;
} }
@ -753,7 +735,16 @@ impl Display {
// This is the non-CGB priority. // This is the non-CGB priority.
// Smaller x coord = higher. // Smaller x coord = higher.
queue.sort_by(|x, y| x.x.cmp(&y.x)); use std::cmp;
queue.sort_by(|x, y| {
if x.x > y.x {
cmp::Ordering::Greater
} else if x.x < y.x {
cmp::Ordering::Less
} else {
cmp::Ordering::Equal
}
});
queue.reverse(); queue.reverse();
// Render background // Render background
@ -789,7 +780,8 @@ impl Display {
// Obtain tile information // Obtain tile information
let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id); let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id);
let addr = tile_base_addr + (tile_offset_y as usize) * 2; // two bytes per row // TODO: Colored background
let addr = tile_base_addr + (tile_offset_y as usize) * 2;
let tile_line_1 = vram[addr]; let tile_line_1 = vram[addr];
let tile_line_2 = vram[addr + 1]; let tile_line_2 = vram[addr + 1];
@ -809,7 +801,7 @@ impl Display {
let b2: bool = (tile_line_2 & b2) != 0; let b2: bool = (tile_line_2 & b2) != 0;
// Lookup the color // Lookup the color
let c = ((b2 as u8) * 2 + b1 as u8) as usize; let c = ((b1 as u8) * 2 + b2 as u8) as usize;
let origin = match c { let origin = match c {
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it. 0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Background, _ => PixelOrigin::Background,
@ -843,95 +835,87 @@ impl Display {
if (self.control & CTRL_WND_DISPLAY_ENABLE) == CTRL_WND_DISPLAY_ENABLE { if (self.control & CTRL_WND_DISPLAY_ENABLE) == CTRL_WND_DISPLAY_ENABLE {
// Draw 'window' over the background. // Draw 'window' over the background.
// Screen coordinates of the top left corner are WX-7, WY // Screen coordinates of the top left corner are WX-7, WY
// Quick check if the window is visible at all.
if self.windowx < 167 && self.windowy < 144 { if self.windowx < 167 && self.windowy < 144 {
let window_y_offset = render_y as i16 - self.windowy as i16; //let rx = self.windowx.wrapping_add(7);
if self.windowx < 7 { let rx = 7u8.wrapping_sub(self.windowx);
eprintln!("Window X position < 7, bailing out"); let ry = render_y.wrapping_add(self.windowy);
} else if window_y_offset >= 144 || window_y_offset < 0 { for r_x in 0..160u8 {
// Not visible let render_x = r_x.wrapping_add(rx);
} else { // Absolute render coordinates
let window_y_offset = window_y_offset as u8; let tile_index_x: u8 = render_x >> 3;
let window_x_offset = self.windowx - 7; let tile_index_y: u8 = ry >> 3;
let tile_offset_x: u8 = render_x & 7;
let tile_offset_y: u8 = ry & 7;
for r_x in 0..(160u8 - window_x_offset) { let vram_offset: usize =
let render_x = r_x.wrapping_add(window_x_offset); window_map + (tile_index_y as usize) * 32 + tile_index_x as usize;
// Absolute render coordinates
let tile_index_x: u8 = render_x >> 3;
let tile_index_y: u8 = window_y_offset >> 3;
let tile_offset_x: u8 = render_x & 7;
let tile_offset_y: u8 = window_y_offset & 7;
let vram_offset: usize = // Obtain tile ID in this area
window_map + (tile_index_y as usize) * 32 + tile_index_x as usize; let tile_id = self.vram0[vram_offset];
// Obtain tile ID in this area let bg_attribs = self.get_background_attribute(
let tile_id = self.vram0[vram_offset]; //tile_index_x as usize, tile_index_y as usize
vram_offset,
);
let bg_attribs = self.get_background_attribute( // Get BG map attributes
//tile_index_x as usize, tile_index_y as usize let vram = if bg_attribs.vram_bank_number() == 0 {
vram_offset, &*self.vram0
); } else {
&*self.vram1
};
// Get BG map attributes // TODO: Priority
let vram = if bg_attribs.vram_bank_number() == 0 { let tile_offset_x = if bg_attribs.vertical_flip() {
&*self.vram0 7 ^ tile_offset_x
} else { } else {
&*self.vram1 tile_offset_x
}; };
let tile_offset_y = if bg_attribs.horizontal_flip() {
7 ^ tile_offset_y
} else {
tile_offset_y
};
// TODO: Priority // Obtain tile information
let tile_offset_x = if bg_attribs.vertical_flip() { let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id);
7 ^ tile_offset_x let tile_addr = tile_base_addr + (tile_offset_y as usize) * 2;
} else { let tile_line_1 = vram[tile_addr];
tile_offset_x let tile_line_2 = vram[tile_addr + 1];
};
let tile_offset_y = if bg_attribs.horizontal_flip() {
7 ^ tile_offset_y
} else {
tile_offset_y
};
// Obtain tile information // Get the correct bit
let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id); let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0;
let tile_addr = tile_base_addr + (tile_offset_y as usize) * 2; let b2: bool = (tile_line_2 & 1 << (7 - tile_offset_x)) > 0;
let tile_line_1 = vram[tile_addr];
let tile_line_2 = vram[tile_addr + 1];
// Get the correct bit let c = (b1 as u8) * 2 + b2 as u8;
let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0; let origin = match c {
let b2: bool = (tile_line_2 & 1 << (7 - tile_offset_x)) > 0; 0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Window,
};
let c = self.background_palette_cgb[bg_attribs.palette_number()]
.get_color(c as usize);
self.set_pixel(render_x, render_y, c, origin);
/*
let lookup: [u8; 4] = [
self.background_palette & 3,
(self.background_palette >> 2) & 3,
(self.background_palette >> 4) & 3,
(self.background_palette >> 6) & 3,
];
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
let c = (b2 as u8) * 2 + b1 as u8; // Draw stuff. We're currently only in monochrome mode
let origin = match c { let origin = match c {
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it. 0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Window, _ => PixelOrigin::Window,
}; };
let c = self.background_palette_cgb[bg_attribs.palette_number()] self.set_pixel(
.get_color(c as usize); render_x,
self.set_pixel(render_x, render_y, c, origin); render_y,
/* sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]),
let lookup: [u8; 4] = [ origin,
self.background_palette & 3, );
(self.background_palette >> 2) & 3, */
(self.background_palette >> 4) & 3,
(self.background_palette >> 6) & 3,
];
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
// Draw stuff. We're currently only in monochrome mode
let origin = match c {
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Window,
};
self.set_pixel(
render_x,
render_y,
sdl2::pixels::Color::RGB(entry[0], entry[1], entry[2]),
origin,
);
*/
}
} }
} }
} }

View File

@ -82,7 +82,7 @@ pub struct Interconnect {
impl Interconnect { impl Interconnect {
pub fn new(bios: Box<[u8]>, rom: Box<[u8]>, save_file: Option<String>) -> Interconnect { pub fn new(bios: Box<[u8]>, rom: Box<[u8]>, save_file: Option<String>) -> Interconnect {
Interconnect { Interconnect {
bios, bios: bios,
cartridge: cartridge::Cartridge::new(rom, save_file), cartridge: cartridge::Cartridge::new(rom, save_file),
ram: vec![0; WRAM_SIZE].into_boxed_slice(), ram: vec![0; WRAM_SIZE].into_boxed_slice(),
hiram: vec![0; HIRAM_SIZE].into_boxed_slice(), hiram: vec![0; HIRAM_SIZE].into_boxed_slice(),

View File

@ -1,7 +1,5 @@
// let's try to write our own, awesome emulator. // let's try to write our own, awesome emulator.
// gameboy (color?) // gameboy (color?)
// To make things more readable at points
#![allow(clippy::identity_op)]
use std::env; use std::env;
use std::fs; use std::fs;
@ -26,11 +24,10 @@ fn main() {
} else { } else {
let bios_path = &args[1]; let bios_path = &args[1];
let rom_path = &args[2]; let rom_path = &args[2];
let save_file = if args.len() == 4 { let mut save_file: Option<String> = None;
Some(args[3].clone()) if args.len() == 4 {
} else { save_file = Some(args[3].clone());
None }
};
let bios = read_file(&bios_path).unwrap(); let bios = read_file(&bios_path).unwrap();
let rom = read_file(&rom_path).unwrap(); let rom = read_file(&rom_path).unwrap();

View File

@ -22,7 +22,7 @@ pub struct NoMBC {
impl NoMBC { impl NoMBC {
pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> NoMBC { pub fn new(rom: Box<[u8]>, ram: Box<[u8]>) -> NoMBC {
NoMBC { rom, ram } NoMBC { rom: rom, ram: ram }
} }
} }

View File

@ -71,9 +71,9 @@ impl VolumeEnvelope {
impl AudioModule<u8> for VolumeEnvelope { impl AudioModule<u8> for VolumeEnvelope {
fn transform(&self, sample: u8) -> u8 { fn transform(&self, sample: u8) -> u8 {
if sample > 0 { if sample > 0 {
128 + self.current_volume self.current_volume
} else { } else {
128 - self.current_volume 0
} }
} }
} }

View File

@ -1,21 +1,16 @@
#[cfg(feature = "audio")]
extern crate pulse_simple; extern crate pulse_simple;
mod envelope; mod envelope;
mod length; mod length;
mod square; mod square;
mod wave; mod wave;
#[cfg(feature = "audio")]
use self::pulse_simple::Playback; use self::pulse_simple::Playback;
use std::sync::{ use std::sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Mutex, Arc, Mutex,
}; };
#[cfg(feature = "audio")]
use std::thread; use std::thread;
use std::convert::TryFrom;
const OUTPUT_SAMPLE_RATE: usize = 48100; const OUTPUT_SAMPLE_RATE: usize = 48100;
//const OUTPUT_SAMPLE_RATE: usize = 32768; //const OUTPUT_SAMPLE_RATE: usize = 32768;
@ -238,11 +233,10 @@ impl Default for Channel3VolumeSetting {
impl Channel3VolumeSetting { impl Channel3VolumeSetting {
fn transform(&self, sample: u8) -> u8 { fn transform(&self, sample: u8) -> u8 {
match self { match self {
Self::Muted => 128, Self::Muted => 0,
// Actually making it louder as the wave src is quite silent. Self::Original => sample,
Self::Original => ((sample - 120) << 1) + 112, Self::Half => sample >> 1,
Self::Half => sample, Self::Quarter => sample >> 2,
Self::Quarter => ((sample - 120) >> 1) + 124,
} }
} }
} }
@ -302,7 +296,7 @@ impl Channel3 {
pub fn sample(&self) -> u8 { pub fn sample(&self) -> u8 {
let input = self.wave_gen.sample(); let input = self.wave_gen.sample();
// TODO(?): Follow the chain // TODO(?): Follow the chain
let input = self.length_counter.transform(input); // let input = self.length_counter.transform(input);
self.volume_ctrl.transform(input) self.volume_ctrl.transform(input)
} }
@ -315,6 +309,10 @@ impl Channel3 {
// TODO: Sweep // TODO: Sweep
} }
if self.tick_state == 7 {
// self.envelope.clock();
}
self.tick_state += 1; self.tick_state += 1;
if self.tick_state == 8 { if self.tick_state == 8 {
@ -338,7 +336,6 @@ pub struct SoundManager {
pub sound_object: Arc<Mutex<Sound>>, pub sound_object: Arc<Mutex<Sound>>,
handle: Option<std::thread::JoinHandle<()>>, handle: Option<std::thread::JoinHandle<()>>,
do_exit: Arc<AtomicBool>, do_exit: Arc<AtomicBool>,
sound_enabled: bool,
} }
impl Drop for SoundManager { impl Drop for SoundManager {
@ -354,7 +351,6 @@ impl SoundManager {
sound_object: Arc::new(Mutex::new(Sound::new())), sound_object: Arc::new(Mutex::new(Sound::new())),
handle: None, handle: None,
do_exit: Arc::new(AtomicBool::new(false)), do_exit: Arc::new(AtomicBool::new(false)),
sound_enabled: true,
}; };
res.launch_thread(); res.launch_thread();
@ -363,57 +359,48 @@ impl SoundManager {
fn launch_thread(&mut self) { fn launch_thread(&mut self) {
let obj = self.sound_object.clone(); let obj = self.sound_object.clone();
let do_exit = self.do_exit.clone(); if false {
#[cfg(feature = "audio")] return;
{
if self.sound_enabled {
self.handle = Some(
thread::Builder::new()
.name("Audio".into())
.spawn(move || {
// PulseAudio playback object
let playback =
Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _);
// Counter, used for calling the 512 Hz timer
let mut counter = 0;
while !do_exit.load(Ordering::Relaxed) {
for _ in 0..100 {
if let Some((s1, s2)) = {
let mut c_obj = obj.lock().unwrap();
// Check for 512 Hz timer
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
c_obj.clock();
counter = 0;
} else {
counter += 1;
}
// Sample clock
c_obj.sample_clock();
// Get sample
c_obj.sample()
} {
let samps = [[s1, s2]];
playback.write(&samps[..]);
} else {
let samps = [[128, 128]];
playback.write(&samps[..]);
}
}
std::thread::sleep(std::time::Duration::from_millis(1));
}
})
.unwrap(),
);
} else {
println!("Sound will be redirected to /dev/null ;)");
self.handle = None;
}
} }
let do_exit = self.do_exit.clone();
self.handle = Some(
thread::Builder::new()
.name("Audio".into())
.spawn(move || {
// PulseAudio playback object
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _);
// Counter, used for calling the 512 Hz timer
let mut counter = 0;
while !do_exit.load(Ordering::Relaxed) {
for _ in 0..100 {
let (s1, s2) = {
let mut c_obj = obj.lock().unwrap();
// Check for 512 Hz timer
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
c_obj.clock();
counter = 0;
} else {
counter += 1;
}
// Sample clock
c_obj.sample_clock();
// Get sample
c_obj.sample()
};
let samps = [[s1, s2]];
playback.write(&samps[..]);
}
std::thread::sleep(std::time::Duration::from_millis(1));
}
})
.unwrap(),
);
} }
} }
@ -434,11 +421,7 @@ impl Sound {
self.channel3.sample_clock(); self.channel3.sample_clock();
} }
pub fn sample(&self) -> Option<(u8, u8)> { pub fn sample(&self) -> (u8, u8) {
if self.enabled == 0 {
return None;
}
// Some basic mixing // Some basic mixing
// Output value // Output value
let mut s = (0, 0); let mut s = (0, 0);
@ -459,55 +442,47 @@ impl Sound {
let c1_sample = self.channel1.sample(); let c1_sample = self.channel1.sample();
let c2_sample = self.channel2.sample(); let c2_sample = self.channel2.sample();
let c3_sample = self.channel3.sample(); let c3_sample = self.channel3.sample();
let c4_sample = 128; let c4_sample = 0;
if self.sound_output_terminal_selector & 1 > 0 { if self.enabled != 0 {
// Output Channel1 if self.sound_output_terminal_selector & 1 > 0 {
s.0 += i16::try_from(c1_sample).unwrap() - 128; // Output Channel1
} s.0 += c1_sample;
if self.sound_output_terminal_selector & 2 > 0 { }
// Output Channel2 if self.sound_output_terminal_selector & 2 > 0 {
s.0 += i16::try_from(c2_sample).unwrap() - 128; // Output Channel2
} s.0 += c2_sample;
if self.sound_output_terminal_selector & 4 > 0 { }
// Output Channel3 if self.sound_output_terminal_selector & 4 > 0 {
s.0 += i16::try_from(c3_sample).unwrap() - 128; // Output Channel3
} s.0 += c3_sample;
if self.sound_output_terminal_selector & 8 > 0 { }
// Output Channel4 if self.sound_output_terminal_selector & 8 > 0 {
s.0 += i16::try_from(c4_sample).unwrap() - 128; // Output Channel4
s.0 += c4_sample;
}
if self.sound_output_terminal_selector & 0x10 > 0 {
// Output Channel1
s.1 += c1_sample;
}
if self.sound_output_terminal_selector & 0x20 > 0 {
// Output Channel2
s.1 += c2_sample;
}
if self.sound_output_terminal_selector & 0x40 > 0 {
// Output Channel3
s.1 += c3_sample;
}
if self.sound_output_terminal_selector & 0x80 > 0 {
// Output Channel4
s.1 += c4_sample;
}
} }
if self.sound_output_terminal_selector & 0x10 > 0 { s.0 *= s01_volume + 1;
// Output Channel1 s.1 *= s02_volume + 1;
s.1 += i16::try_from(c1_sample).unwrap() - 128; s
}
if self.sound_output_terminal_selector & 0x20 > 0 {
// Output Channel2
s.1 += i16::try_from(c2_sample).unwrap() - 128;
}
if self.sound_output_terminal_selector & 0x40 > 0 {
// Output Channel3
s.1 += i16::try_from(c3_sample).unwrap() - 128;
}
if self.sound_output_terminal_selector & 0x80 > 0 {
// Output Channel4
s.1 += i16::try_from(c4_sample).unwrap() - 128;
}
// TODO: This is too loud (overflowing u8) for some reason, so we're
// backing off a little bit from this.
/*
if s.0 > 30 || s.1 > 30 {
println!("Would overflow! s={:?} s1vol={} s2vol={}", s, s01_volume, s02_volume);
}
*/
//s.0 *= i16::try_from(s01_volume).unwrap(); // + 1;
//s.1 *= i16::try_from(s02_volume).unwrap(); // + 1;
let s = (128 + s.0, 128 + s.1);
let s = (u8::try_from(s.0).unwrap(), u8::try_from(s.1).unwrap());
Some(s)
} }
pub fn write_byte(&mut self, addr: u16, val: u8) { pub fn write_byte(&mut self, addr: u16, val: u8) {

View File

@ -6,11 +6,11 @@ use crate::sound::OUTPUT_SAMPLE_RATE;
struct SamplePair(u8); struct SamplePair(u8);
impl SamplePair { impl SamplePair {
fn first(self) -> u8 { fn first(&self) -> u8 {
self.0 >> 4 self.0 >> 4
} }
fn second(self) -> u8 { fn second(&self) -> u8 {
self.0 & 0x0F self.0 & 0x0F
} }
} }
@ -66,11 +66,11 @@ impl WaveGenerator {
let sample = &self.samples[position / 2]; let sample = &self.samples[position / 2];
match position % 2 { match position % 2 {
0 => sample.first() + 120, 0 => sample.first(),
_ => sample.second() + 120, _ => sample.second(),
} }
} else { } else {
128 0
} }
} }