As good as working
This commit is contained in:
parent
fc15b398ba
commit
2367e891a4
172
src/cpu.rs
172
src/cpu.rs
@ -37,8 +37,25 @@ pub struct CPU {
|
||||
halted: bool,
|
||||
}
|
||||
|
||||
fn to_u16(bytes: Box<[u8]>) -> u16 {
|
||||
(bytes[1] as u16) << 8 | (bytes[0] as u16)
|
||||
fn to_u16(args: Args) -> u16 {
|
||||
match args {
|
||||
Args::Double(a, b) => (a as u16) | ((b as u16) << 8),
|
||||
_ => panic!("to_u16 only works with Args::Double")
|
||||
}
|
||||
}
|
||||
|
||||
enum Args {
|
||||
Single(u8),
|
||||
Double(u8, u8),
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn single_val(&self) -> u8 {
|
||||
match self {
|
||||
&Args::Single(x) => x,
|
||||
_ => panic!("single_val only works with Args::Single"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CPU {
|
||||
@ -61,13 +78,22 @@ impl CPU {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn load_args(&mut self, num_args: u8) -> Box<[u8]> {
|
||||
let mut args = Vec::new();
|
||||
for i in 0..num_args {
|
||||
args.push(self.read_byte(self.ip + i as u16));
|
||||
fn load_args(&mut self, num_args: u8) -> Args {
|
||||
match num_args {
|
||||
1 => {
|
||||
let val = self.read_byte(self.ip);
|
||||
self.ip += 1;
|
||||
Args::Single(val)
|
||||
}
|
||||
2 => {
|
||||
let b1 = self.read_byte(self.ip);
|
||||
self.ip += 1;
|
||||
let b2 = self.read_byte(self.ip);
|
||||
self.ip += 1;
|
||||
Args::Double(b1, b2)
|
||||
}
|
||||
_ => panic!("load_args only supports two bytes")
|
||||
}
|
||||
self.ip += num_args as u16;
|
||||
args.into_boxed_slice()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -572,7 +598,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn call_condition(&mut self, cond_str: String, cond: bool) -> u8 {
|
||||
fn call_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||
let dst = to_u16(self.load_args(2));
|
||||
if self.debug {
|
||||
println!("CALL {} {:04X}", cond_str, dst);
|
||||
@ -586,7 +612,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ret_condition(&mut self, cond_str: String, cond: bool) -> u8 {
|
||||
fn ret_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||
if self.debug {
|
||||
println!("RET {}", cond_str);
|
||||
}
|
||||
@ -609,8 +635,8 @@ impl CPU {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn jmp_r_condition(&mut self, cond_str: String, cond: bool) -> u8 {
|
||||
let t = self.load_args(1)[0];
|
||||
fn jmp_r_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||
let t = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("JR {} {:02X}", cond_str, t);
|
||||
}
|
||||
@ -628,7 +654,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn jmp_p_condition(&mut self, cond_str: String, cond: bool) -> u8 {
|
||||
fn jmp_p_condition(&mut self, cond_str: &'static str, cond: bool) -> u8 {
|
||||
let t = to_u16(self.load_args(2));
|
||||
if self.debug {
|
||||
println!("JP {} {:04X}", cond_str, t);
|
||||
@ -722,7 +748,7 @@ impl CPU {
|
||||
|
||||
#[inline]
|
||||
fn ld_r_v(&mut self, r: usize) -> u8 {
|
||||
let val: u8 = self.load_args(1)[0];
|
||||
let val: u8 = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("LD {}, {:02X}", REG_NAMES[r], val);
|
||||
}
|
||||
@ -859,20 +885,35 @@ impl CPU {
|
||||
self.halted = false;
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn run(&mut self) {
|
||||
let mut running_sum: i32 = 0;
|
||||
const INSTRUCTIONS_PER_ROUND: usize = 500;
|
||||
|
||||
loop {
|
||||
// Some attempt at limiting our speed
|
||||
let mut cycles: i32 = 0;
|
||||
let start = Instant::now();
|
||||
for _ in 0 .. 1000 {
|
||||
cycles += self.run_instruction() as i32;
|
||||
let mut cycles_executed: usize = 0;
|
||||
for _ in 0..INSTRUCTIONS_PER_ROUND {
|
||||
cycles_executed += self.run_instruction() as usize;
|
||||
}
|
||||
|
||||
let gb_dur = cycles * 238;
|
||||
// Time that the gameboy would have required to execute the
|
||||
// instructions above.
|
||||
//
|
||||
// The gameboy has a freq of 4.194304MHz
|
||||
// This means it takes it 238.418569ns to execute a single
|
||||
// cycle.
|
||||
let gb_dur = cycles_executed as i32 * 238;
|
||||
let our_dur = start.elapsed().subsec_nanos() as i32;
|
||||
let delta = gb_dur - our_dur;
|
||||
if delta > (20 * 238) { // We're at least 20 cycles faster.
|
||||
sleep(Duration::new(0, delta as u32));
|
||||
|
||||
let full_cycles: i32 = delta / 238;
|
||||
running_sum += full_cycles;
|
||||
if full_cycles > 0 {
|
||||
// println!("[+] [{}] {} cycles, gb: {}, we: {}", running_sum, cycles_executed, gb_dur, our_dur);
|
||||
sleep(Duration::new(0, (full_cycles - 10) as u32 * 238 as u32));
|
||||
} else {
|
||||
println!("[-] [{}] {} cycles, gb: {}, we: {}", running_sum, cycles_executed, gb_dur, our_dur);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -918,6 +959,16 @@ impl CPU {
|
||||
|
||||
pub fn run_instruction(&mut self) -> u8 {
|
||||
// self.debug = !self.interconnect.is_boot_rom();
|
||||
|
||||
/*
|
||||
if self.ip >= 100 && self.ip < 120 {
|
||||
self.debug = true;
|
||||
} else {
|
||||
self.debug = false;
|
||||
}
|
||||
*/
|
||||
|
||||
// self.debug = true;
|
||||
// Check for interrupts.
|
||||
if self.ime {
|
||||
self.check_interrupts(true);
|
||||
@ -927,7 +978,7 @@ impl CPU {
|
||||
}
|
||||
}
|
||||
|
||||
let mut cycles: u8 = 1;
|
||||
let mut cycles: u8 = 255;
|
||||
let instruction: u8;
|
||||
if !self.halted {
|
||||
// We need to double-check the flags
|
||||
@ -1017,7 +1068,7 @@ impl CPU {
|
||||
}
|
||||
|
||||
0x10 => {
|
||||
println!("STOP 0 {:02X} not implemented.", self.load_args(1)[0]);
|
||||
println!("STOP 0 {:02X} not implemented.", self.load_args(1).single_val());
|
||||
4
|
||||
},
|
||||
0x11 => self.ld_rr_vv(REG_N_D, REG_N_E),
|
||||
@ -1044,8 +1095,11 @@ impl CPU {
|
||||
4
|
||||
},
|
||||
0x18 => {
|
||||
let dst = self.load_args(1)[0];
|
||||
let dst = self.load_args(1).single_val();
|
||||
self.jmp_r(dst);
|
||||
if self.debug {
|
||||
println!("JMPR {:02X}", dst);
|
||||
}
|
||||
12
|
||||
},
|
||||
0x19 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_D, REG_N_E),
|
||||
@ -1081,7 +1135,7 @@ impl CPU {
|
||||
|
||||
0x20 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
self.jmp_r_condition("NZ".to_owned(), c)
|
||||
self.jmp_r_condition("NZ", c)
|
||||
}
|
||||
0x21 => self.ld_rr_vv(REG_N_H, REG_N_L),
|
||||
0x22 => {
|
||||
@ -1137,7 +1191,7 @@ impl CPU {
|
||||
},
|
||||
0x28 => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.jmp_r_condition("Z".to_owned(), c)
|
||||
self.jmp_r_condition("Z", c)
|
||||
},
|
||||
0x29 => self.add_rr_rr(REG_N_H, REG_N_L, REG_N_H, REG_N_L),
|
||||
0x2A => {
|
||||
@ -1166,7 +1220,7 @@ impl CPU {
|
||||
|
||||
0x30 => {
|
||||
let c = self.flags & FLAG_C == 0;
|
||||
self.jmp_r_condition("NC".to_owned(), c)
|
||||
self.jmp_r_condition("NC", c)
|
||||
},
|
||||
0x31 => {
|
||||
let args = self.load_args(2);
|
||||
@ -1207,7 +1261,7 @@ impl CPU {
|
||||
},
|
||||
0x38 => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
self.jmp_r_condition("C".to_owned(), c)
|
||||
self.jmp_r_condition("C", c)
|
||||
},
|
||||
0x39 => {
|
||||
if self.debug {
|
||||
@ -1379,12 +1433,12 @@ impl CPU {
|
||||
|
||||
0xC0 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
self.ret_condition("NZ".to_owned(), c)
|
||||
self.ret_condition("NZ", c)
|
||||
},
|
||||
0xC1 => self.pop_rr(REG_N_B, REG_N_C),
|
||||
0xC2 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
self.jmp_p_condition("NZ".to_owned(), c)
|
||||
self.jmp_p_condition("NZ", c)
|
||||
},
|
||||
0xC3 => {
|
||||
let dst = to_u16(self.load_args(2));
|
||||
@ -1396,11 +1450,11 @@ impl CPU {
|
||||
},
|
||||
0xC4 => {
|
||||
let c = self.flags & FLAG_Z == 0;
|
||||
self.call_condition("NZ".to_owned(), c)
|
||||
self.call_condition("NZ", c)
|
||||
}
|
||||
0xC5 => self.push_rr(REG_N_B, REG_N_C),
|
||||
0xC6 => {
|
||||
let val = self.load_args(1)[0];
|
||||
let val = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("ADD A, {:02X}", val);
|
||||
}
|
||||
@ -1412,7 +1466,7 @@ impl CPU {
|
||||
0xC7 => self.rst(0x00),
|
||||
0xC8 => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.ret_condition("Z".to_owned(), c)
|
||||
self.ret_condition("Z", c)
|
||||
},
|
||||
0xC9 => {
|
||||
if self.debug {
|
||||
@ -1423,7 +1477,7 @@ impl CPU {
|
||||
},
|
||||
0xCA => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.jmp_p_condition("Z".to_owned(), c)
|
||||
self.jmp_p_condition("Z", c)
|
||||
}
|
||||
0xCB => {
|
||||
self.run_prefix_instruction();
|
||||
@ -1431,11 +1485,11 @@ impl CPU {
|
||||
},
|
||||
0xCC => {
|
||||
let c = self.flags & FLAG_Z == FLAG_Z;
|
||||
self.call_condition("Z".to_owned(), c)
|
||||
self.call_condition("Z", c)
|
||||
},
|
||||
0xCD => self.call_condition("".to_owned(), true),
|
||||
0xCD => self.call_condition("", true),
|
||||
0xCE => {
|
||||
let arg = self.load_args(1)[0];
|
||||
let arg = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("ADC A, {:02X}", arg);
|
||||
}
|
||||
@ -1447,21 +1501,21 @@ impl CPU {
|
||||
|
||||
0xD0 => {
|
||||
let c = self.flags & FLAG_C == 0;
|
||||
self.ret_condition("NC".to_owned(), c)
|
||||
self.ret_condition("NC", c)
|
||||
},
|
||||
0xD1 => self.pop_rr(REG_N_D, REG_N_E),
|
||||
0xD2 => {
|
||||
let c = self.flags & FLAG_C == 0;
|
||||
self.jmp_p_condition("NC".to_owned(), c)
|
||||
self.jmp_p_condition("NC", c)
|
||||
}
|
||||
0xD3 => panic!("NON-EXISTING OPCODE"),
|
||||
0xD4 => {
|
||||
let c = self.flags & FLAG_C == 0;
|
||||
self.call_condition("NC".to_owned(), c)
|
||||
self.call_condition("NC", c)
|
||||
}
|
||||
0xD5 => self.push_rr(REG_N_D, REG_N_E),
|
||||
0xD6 => {
|
||||
let val = self.load_args(1)[0];
|
||||
let val = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("SUB {:02X}", val);
|
||||
}
|
||||
@ -1472,7 +1526,7 @@ impl CPU {
|
||||
0xD7 => self.rst(0x10),
|
||||
0xD8 => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
self.ret_condition("C".to_owned(), c)
|
||||
self.ret_condition("C", c)
|
||||
},
|
||||
0xD9 => {
|
||||
if self.debug {
|
||||
@ -1482,16 +1536,16 @@ impl CPU {
|
||||
}
|
||||
0xDA => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
self.jmp_p_condition("C".to_owned(), c)
|
||||
self.jmp_p_condition("C", c)
|
||||
},
|
||||
0xDB => panic!("NON-EXISTING OPCODE"),
|
||||
0xDC => {
|
||||
let c = self.flags & FLAG_C == FLAG_C;
|
||||
self.call_condition("C".to_owned(), c)
|
||||
self.call_condition("C", c)
|
||||
}
|
||||
0xDD => panic!("NON-EXISTING OPCODE"),
|
||||
0xDE => {
|
||||
let arg = self.load_args(1)[0];
|
||||
let arg = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("SBC {:02X}", arg);
|
||||
}
|
||||
@ -1501,11 +1555,11 @@ impl CPU {
|
||||
0xDF => self.rst(0x18),
|
||||
|
||||
0xE0 => {
|
||||
let args = self.load_args(1);
|
||||
let arg = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("LDH {:02X}, A", args[0]);
|
||||
println!("LDH {:02X}, A", arg);
|
||||
}
|
||||
self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]);
|
||||
self.interconnect.write_byte(0xFF00 + arg as u16, self.regs[REG_A]);
|
||||
12
|
||||
},
|
||||
0xE1 => self.pop_rr(REG_N_H, REG_N_L),
|
||||
@ -1520,7 +1574,7 @@ impl CPU {
|
||||
0xE3 | 0xE4 => panic!("NON-EXISTING OPCODE"),
|
||||
0xE5 => self.push_rr(REG_N_H, REG_N_L),
|
||||
0xE6 => {
|
||||
let val = self.load_args(1)[0];
|
||||
let val = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("AND {:02X}", val);
|
||||
}
|
||||
@ -1535,7 +1589,7 @@ impl CPU {
|
||||
},
|
||||
0xE7 => self.rst(0x20),
|
||||
0xE8 => {
|
||||
let arg = self.load_args(1)[0] as i8;
|
||||
let arg = self.load_args(1).single_val() as i8;
|
||||
if self.debug {
|
||||
println!("ADD SP, {:02X}", arg);
|
||||
}
|
||||
@ -1570,7 +1624,7 @@ impl CPU {
|
||||
},
|
||||
0xEB ... 0xED => panic!("NON-EXISTING OPCODE"),
|
||||
0xEE => {
|
||||
let arg = self.load_args(1)[0];
|
||||
let arg = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("XOR {:02X}", arg);
|
||||
}
|
||||
@ -1585,11 +1639,11 @@ impl CPU {
|
||||
0xEF => self.rst(0x28),
|
||||
|
||||
0xF0 => {
|
||||
let args = self.load_args(1);
|
||||
let arg = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("LDH A, {:02X}", args[0]);
|
||||
println!("LDH A, {:02X}", arg);
|
||||
}
|
||||
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16);
|
||||
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + arg as u16);
|
||||
12
|
||||
},
|
||||
0xF1 => self.pop_rr(REG_N_A, REG_N_F),
|
||||
@ -1611,7 +1665,7 @@ impl CPU {
|
||||
0xF4 => panic!("NON-EXISTING OPCODE"),
|
||||
0xF5 => self.push_rr(REG_N_A, REG_N_F),
|
||||
0xF6 => {
|
||||
let val = self.load_args(1)[0];
|
||||
let val = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("OR {:02X}", val);
|
||||
}
|
||||
@ -1625,7 +1679,7 @@ impl CPU {
|
||||
},
|
||||
0xF7 => self.rst(0x30),
|
||||
0xF8 => {
|
||||
let arg = self.load_args(1)[0] as i8;
|
||||
let arg = self.load_args(1).single_val() as i8;
|
||||
if self.debug {
|
||||
println!("LD HL, SP+{:02X}", arg);
|
||||
}
|
||||
@ -1672,12 +1726,12 @@ impl CPU {
|
||||
0xFC | 0xFD => panic!("NON-EXISTING OPCODE"),
|
||||
|
||||
0xFE => {
|
||||
let args = self.load_args(1);
|
||||
let arg = self.load_args(1).single_val();
|
||||
if self.debug {
|
||||
println!("CP {:02X}", args[0]);
|
||||
println!("CP {:02X}", arg);
|
||||
}
|
||||
|
||||
self.cp_r(args[0]);
|
||||
self.cp_r(arg);
|
||||
8
|
||||
},
|
||||
0xFF => self.rst(0x38),
|
||||
|
||||
@ -199,7 +199,7 @@ impl Interconnect {
|
||||
// TODO: if some flag set, use bios, otherwise only use rom
|
||||
// For now, just use bios
|
||||
match addr {
|
||||
0x0000 ... 0x100 => {
|
||||
0x0000 ... 0xFF => {
|
||||
if self.disable_bootrom == 0 {
|
||||
self.bios[addr as usize]
|
||||
} else {
|
||||
|
||||
126
src/sound/mod.rs
126
src/sound/mod.rs
@ -8,17 +8,15 @@ use self::pulse_simple::Playback;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
||||
//use std::f64::consts::PI;
|
||||
|
||||
//const TICK_RATE: usize = 4194304;
|
||||
const OUTPUT_SAMPLE_RATE: usize = 32768;
|
||||
//const OUTPUT_SAMPLE_RATE: usize = 1024;
|
||||
//const TICKS_PER_SAMPLE: usize = TICK_RATE / OUTPUT_SAMPLE_RATE;
|
||||
const OUTPUT_SAMPLE_RATE: usize = 48100;
|
||||
//const OUTPUT_SAMPLE_RATE: usize = 32768;
|
||||
|
||||
/// Represents an 'audio module', transforming an input sample to an output sample
|
||||
trait AudioModule<T> {
|
||||
fn transform(&self, sample: T) -> T;
|
||||
}
|
||||
|
||||
/// Clockable audio components
|
||||
trait AudioComponent {
|
||||
fn clock(&mut self);
|
||||
}
|
||||
@ -31,26 +29,33 @@ struct Channel1 {
|
||||
envelope: envelope::VolumeEnvelope,
|
||||
|
||||
tick_state: u8,
|
||||
|
||||
stored_regs: [u8; 5]
|
||||
}
|
||||
|
||||
impl Channel1 {
|
||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
match addr {
|
||||
0xFF10 => {
|
||||
println!("Channel1: Sweep not implemented yet :<");
|
||||
self.wave_gen.set_sweep_reg(val);
|
||||
self.stored_regs[0] = val;
|
||||
}
|
||||
0xFF11 => {
|
||||
self.length_counter.set_length(val & 0x3F);
|
||||
self.wave_gen.set_duty_cycle(val >> 6);
|
||||
self.stored_regs[1] = val;
|
||||
}
|
||||
0xFF12 => {
|
||||
self.envelope.set_register(val);
|
||||
self.stored_regs[2] = val;
|
||||
}
|
||||
0xFF13 => {
|
||||
// Set lower frequency
|
||||
self.wave_gen.set_lower_freq(val);
|
||||
self.stored_regs[3] = val;
|
||||
}
|
||||
0xFF14 => {
|
||||
self.stored_regs[4] = val;
|
||||
// Set higher
|
||||
self.wave_gen.set_higher_freq(val);
|
||||
|
||||
@ -67,7 +72,8 @@ impl Channel1 {
|
||||
|
||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
_ => panic!("Channel1 does not support reading ({:04X}", addr),
|
||||
0xFF10...0xFF14 => self.stored_regs[(addr - 0xFF10) as usize],
|
||||
_ => {println!("Channel1 does not support reading ({:04X})", addr); 0},
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +94,7 @@ impl Channel1 {
|
||||
}
|
||||
|
||||
if self.tick_state == 2 || self.tick_state == 6 {
|
||||
// TODO: Sweep
|
||||
self.wave_gen.sweep_clock();
|
||||
}
|
||||
|
||||
if self.tick_state == 7 {
|
||||
@ -144,7 +150,7 @@ impl Channel2 {
|
||||
|
||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
_ => panic!("Channel2 does not support reading ({:04X}", addr),
|
||||
_ => {println!("Channel2 does not support reading ({:04X})", addr); 0},
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,7 +198,7 @@ struct Channel3 {
|
||||
|
||||
impl Channel3 {
|
||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
println!("C3 WB: {:04X} = {:02X} ({:08b})", addr, val, val);
|
||||
// println!("C3 WB: {:04X} = {:02X} ({:08b})", addr, val, val);
|
||||
match addr {
|
||||
0xFF1A => {
|
||||
self.wave_gen.enabled = (val & (1 << 7)) > 0;
|
||||
@ -226,7 +232,7 @@ impl Channel3 {
|
||||
|
||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
_ => panic!("Channel3 does not support reading ({:04X}", addr),
|
||||
_ => panic!("Channel3 does not support reading ({:04X})", addr),
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,8 +243,7 @@ impl Channel3 {
|
||||
pub fn sample(&self) -> u8 {
|
||||
let input = self.wave_gen.sample();
|
||||
// Follow the chain
|
||||
let input = self.length_counter.transform(input);
|
||||
// self.envelope.transform(input)
|
||||
// let input = self.length_counter.transform(input);
|
||||
input
|
||||
}
|
||||
|
||||
@ -291,7 +296,10 @@ impl SoundManager {
|
||||
|
||||
pub fn launch_thread(&self) {
|
||||
let obj = self.sound_object.clone();
|
||||
thread::spawn(move || {
|
||||
if false {
|
||||
return;
|
||||
}
|
||||
thread::Builder::new().name("Audio".into()).spawn(move || {
|
||||
// PulseAudio playback object
|
||||
let playback = Playback::new("GBC", "Output", None, (OUTPUT_SAMPLE_RATE) as _ );
|
||||
|
||||
@ -299,20 +307,9 @@ impl SoundManager {
|
||||
let mut counter = 0;
|
||||
|
||||
loop {
|
||||
{
|
||||
let (s1, s2) = {
|
||||
let mut c_obj = obj.lock().unwrap();
|
||||
|
||||
c_obj.channel1.sample_clock();
|
||||
c_obj.channel2.sample_clock();
|
||||
c_obj.channel3.sample_clock();
|
||||
|
||||
// let s = c_obj.sample();
|
||||
let s1 = c_obj.channel1.sample();
|
||||
let s2 = c_obj.channel2.sample();
|
||||
let s3 = c_obj.channel3.sample();
|
||||
let samps = [[s3 as u8, s3 as u8]];
|
||||
playback.write(&samps[..]);
|
||||
|
||||
// Check for 512 Hz timer
|
||||
if counter >= (OUTPUT_SAMPLE_RATE / 512) {
|
||||
c_obj.clock();
|
||||
@ -320,11 +317,18 @@ impl SoundManager {
|
||||
} else {
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Sample clock
|
||||
c_obj.sample_clock();
|
||||
|
||||
// Get sample
|
||||
c_obj.sample()
|
||||
};
|
||||
let samps = [[s1, s2]];
|
||||
// No sleep needed, it seems like the playback.write is blocking
|
||||
// thread::sleep(time::Duration::new(0, sleep_duration));
|
||||
playback.write(&samps[..]);
|
||||
}
|
||||
});
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,9 +343,65 @@ impl Sound {
|
||||
self.channel3.clock();
|
||||
}
|
||||
|
||||
pub fn sample(&self) -> u8 {
|
||||
// TODO: Mixing
|
||||
(self.channel1.sample() >> 1) + (self.channel2.sample() >> 1) + (self.channel3.sample() >> 1)
|
||||
pub fn sample_clock(&mut self) {
|
||||
self.channel1.sample_clock();
|
||||
self.channel2.sample_clock();
|
||||
self.channel3.sample_clock();
|
||||
}
|
||||
|
||||
pub fn sample(&self) -> (u8, u8) {
|
||||
// Some basic mixing
|
||||
// Output value
|
||||
let mut s = [0u16; 2];
|
||||
let s01_volume = self.sound_channel_volume_control & 7;
|
||||
let s02_volume = (self.sound_channel_volume_control >> 4) & 7;
|
||||
|
||||
let c1_sample = self.channel1.sample();
|
||||
let c2_sample = self.channel2.sample();
|
||||
let c3_sample = self.channel3.sample();
|
||||
let c4_sample = 0;
|
||||
|
||||
if self.enabled != 0 {
|
||||
if s01_volume > 0 {
|
||||
if self.sound_output_terminal_selector & 1 > 0 {
|
||||
// Output Channel1
|
||||
s[0] += c1_sample as _;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 2 > 0 && false {
|
||||
// Output Channel2
|
||||
s[0] += c2_sample as _;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 4 > 0 {
|
||||
// Output Channel3
|
||||
s[0] += c3_sample as _;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 8 > 0 {
|
||||
// Output Channel4
|
||||
s[0] += c4_sample as _;
|
||||
}
|
||||
}
|
||||
|
||||
if s02_volume > 0 {
|
||||
if self.sound_output_terminal_selector & 0x10 > 0 && false {
|
||||
// Output Channel1
|
||||
s[1] += c1_sample as _;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x20 > 0 {
|
||||
// Output Channel2
|
||||
s[1] += c2_sample as _;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x40 > 0 {
|
||||
// Output Channel3
|
||||
s[1] += c3_sample as _;
|
||||
}
|
||||
if self.sound_output_terminal_selector & 0x80 > 0 {
|
||||
// Output Channel4
|
||||
s[1] += c4_sample as _;
|
||||
}
|
||||
}
|
||||
}
|
||||
// (self.channel1.sample() >> 1) + (self.channel2.sample() >> 1) + (self.channel3.sample() >> 1)
|
||||
((s[0] >> 2) as _, (s[1] >> 2) as _)
|
||||
}
|
||||
|
||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
|
||||
@ -23,6 +23,18 @@ pub struct SquareWaveGenerator {
|
||||
duty_cycle: DutyCycle,
|
||||
|
||||
gb_reg_freq: u16,
|
||||
|
||||
// Sweep settings (if existing)
|
||||
sweep_change_period: u8,
|
||||
// selects the inc/dec mode of the sweep. Dec if set
|
||||
sweep_dec: bool,
|
||||
// Sweep speed (n/128th)
|
||||
sweep_change: u8,
|
||||
|
||||
// Current sweep clock cycle
|
||||
sweep_clock: u8,
|
||||
|
||||
// sweep_freq_shadow: f32,
|
||||
}
|
||||
|
||||
impl SquareWaveGenerator {
|
||||
@ -42,6 +54,35 @@ impl SquareWaveGenerator {
|
||||
self.update_frequency();
|
||||
}
|
||||
|
||||
pub fn set_sweep_reg(&mut self, reg: u8) {
|
||||
//Bit 6-4 - Sweep Time
|
||||
//Bit 3 - Sweep Increase/Decrease
|
||||
// 0: Addition (frequency increases)
|
||||
// 1: Subtraction (frequency decreases)
|
||||
//Bit 2-0 - Number of sweep shift (n: 0-7)
|
||||
self.sweep_change_period = (reg >> 4) & 7;
|
||||
self.sweep_dec = reg & (1 << 3) > 0;
|
||||
self.sweep_change = reg & 7;
|
||||
self.sweep_clock = 0;
|
||||
}
|
||||
|
||||
pub fn sweep_clock(&mut self) {
|
||||
// self.sweep_freq_shadow = self.frequency;
|
||||
if self.sweep_change_period > 0 {
|
||||
if self.sweep_change > 0 {
|
||||
if self.sweep_clock >= self.sweep_change_period {
|
||||
self.sweep_clock = 0;
|
||||
let change = self.frequency / (2f32.powi(self.sweep_change as _));
|
||||
self.frequency += if self.sweep_dec {
|
||||
change
|
||||
} else {
|
||||
-change
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.time = 0f32;
|
||||
}
|
||||
|
||||
39
src/timer.rs
39
src/timer.rs
@ -11,6 +11,11 @@ pub struct Timer {
|
||||
gb_ticks: u16,
|
||||
}
|
||||
|
||||
// Timer speed contains 4 different scales:
|
||||
// [0] = 64 = 4096 Hz
|
||||
// [1] = 1 = 262144 Hz
|
||||
// [2] = 4 = 65536 Hz
|
||||
// [3] = 16 = 16384 Hz
|
||||
const TIMER_SPEED: [u16; 4] = [64, 1, 4, 16];
|
||||
const TIMER_ENABLE: u8 = (1 << 2);
|
||||
|
||||
@ -19,34 +24,38 @@ impl Timer {
|
||||
Timer::default()
|
||||
}
|
||||
|
||||
/// This clock is ticking at 262.144 Hz
|
||||
fn timer_clock_tick(&mut self) {
|
||||
// The div reg will always tick
|
||||
self.div_tick_counter += 1;
|
||||
if self.div_tick_counter >= TIMER_SPEED[3] {
|
||||
|
||||
// 262144 Hz / 16 = 16384 Hz
|
||||
if self.div_tick_counter >= 16 {
|
||||
self.div = self.div.wrapping_add(1);
|
||||
self.div_tick_counter -= TIMER_SPEED[3];
|
||||
self.div_tick_counter -= 16;
|
||||
}
|
||||
|
||||
if (self.tac & TIMER_ENABLE) == TIMER_ENABLE { // Is timer enabled?
|
||||
// Check if the timer is enabled
|
||||
if (self.tac & TIMER_ENABLE) != 0 {
|
||||
self.tick_counter += 1;
|
||||
let req_ticks = TIMER_SPEED[(self.tac & 3) as usize];
|
||||
|
||||
if self.tick_counter >= req_ticks {
|
||||
if self.tick_counter == req_ticks {
|
||||
if self.tima == 0xFF {
|
||||
self.timer_interrupt = true;
|
||||
self.tima = 0;
|
||||
self.tima = self.tma;
|
||||
} else {
|
||||
self.tima += 1;
|
||||
}
|
||||
self.tick_counter -= req_ticks;
|
||||
self.tick_counter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, ticks: u16) {
|
||||
// One tick 1/4.194304MHz on regular speed => 16 gb ticks
|
||||
// One tick 1/4.194304MHz on regular speed => 16 gb ticks = prescaler
|
||||
self.gb_ticks += ticks;
|
||||
// If we're in GBC fast mode, we'd require 32 ticks instead of 16
|
||||
|
||||
while self.gb_ticks >= 16 {
|
||||
self.timer_clock_tick();
|
||||
self.gb_ticks -= 16;
|
||||
@ -54,26 +63,26 @@ impl Timer {
|
||||
}
|
||||
|
||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||
// println!("Timer WR: {:04X} = {:02X}", addr, val);
|
||||
match addr {
|
||||
0xFF04 => self.div = 0,
|
||||
0xFF05 => self.tima = val,
|
||||
0xFF06 => self.tma = val,
|
||||
0xFF07 => self.tac = val,
|
||||
_ => println!("Timer: Write {:02X} to {:04X} unsupported", val, addr),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||
match addr {
|
||||
let r = match addr {
|
||||
0xFF04 => self.div,
|
||||
0xFF05 => self.tima,
|
||||
0xFF06 => self.tma,
|
||||
0xFF07 => self.tac,
|
||||
_ => {
|
||||
println!("Timer: Read from {:04X} unsupported", addr);
|
||||
0
|
||||
},
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
// println!("Timer RD: {:04X} = {:02X}", addr, r);
|
||||
r
|
||||
}
|
||||
|
||||
pub fn timer_interrupt(&mut self) -> bool {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user