Serial and Timer class, cycle counting
Also a little bit of refactoring. Move VRAM to the display object
This commit is contained in:
parent
94e73b2eda
commit
396e87304e
155
src/cpu.rs
155
src/cpu.rs
@ -24,6 +24,8 @@ pub struct CPU {
|
|||||||
sp: u16,
|
sp: u16,
|
||||||
|
|
||||||
interconnect: interconnect::Interconnect,
|
interconnect: interconnect::Interconnect,
|
||||||
|
|
||||||
|
interrupts_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_u16(bytes: Box<[u8]>) -> u16 {
|
fn to_u16(bytes: Box<[u8]>) -> u16 {
|
||||||
@ -39,7 +41,8 @@ impl CPU {
|
|||||||
regs: [0, 0, 0, 0, 0, 0, 0],
|
regs: [0, 0, 0, 0, 0, 0, 0],
|
||||||
ip: 0,
|
ip: 0,
|
||||||
sp: 0xFFFE,
|
sp: 0xFFFE,
|
||||||
interconnect: interconnect
|
interconnect: interconnect,
|
||||||
|
interrupts_enabled: false, // Is this correct?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,56 +237,80 @@ impl CPU {
|
|||||||
print!("C={} ", self.flags & FLAG_C != 0);
|
print!("C={} ", self.flags & FLAG_C != 0);
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
|
|
||||||
|
let mut cycles: u16;
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
|
0x00 => {
|
||||||
|
println!("NOP");
|
||||||
|
cycles = 4;
|
||||||
|
},
|
||||||
|
0x01 => {
|
||||||
|
println!("LD (BC), A");
|
||||||
|
let addr: u16 = self.get_pair_value(REG_B, REG_C);
|
||||||
|
let val: u8 = self.regs[REG_A];
|
||||||
|
self.interconnect.write_byte(addr, val);
|
||||||
|
cycles = 12;
|
||||||
|
}
|
||||||
0x04 => {
|
0x04 => {
|
||||||
println!("INC B");
|
println!("INC B");
|
||||||
self.reg_inc(REG_B);
|
self.reg_inc(REG_B);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x05 => {
|
0x05 => {
|
||||||
println!("DEC B");
|
println!("DEC B");
|
||||||
self.reg_dec(REG_B);
|
self.reg_dec(REG_B);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x06 => {
|
0x06 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD B, {:02x}", args[0]);
|
println!("LD B, {:02x}", args[0]);
|
||||||
self.regs[REG_B] = args[0];
|
self.regs[REG_B] = args[0];
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x0C => {
|
0x0C => {
|
||||||
println!("INC C");
|
println!("INC C");
|
||||||
self.reg_inc(REG_C);
|
self.reg_inc(REG_C);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x0D => {
|
0x0D => {
|
||||||
println!("DEC C");
|
println!("DEC C");
|
||||||
self.reg_dec(REG_C);
|
self.reg_dec(REG_C);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x0E => {
|
0x0E => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD C, {:02x}", args[0]);
|
println!("LD C, {:02x}", args[0]);
|
||||||
self.regs[REG_C] = args[0];
|
self.regs[REG_C] = args[0];
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x11 => {
|
0x11 => {
|
||||||
let args = self.load_args(2);
|
let args = self.load_args(2);
|
||||||
let val = to_u16(args);
|
let val = to_u16(args);
|
||||||
println!("LD DE, {:04x}", val);
|
println!("LD DE, {:04x}", val);
|
||||||
self.set_pair_value(REG_D, REG_E, val);
|
self.set_pair_value(REG_D, REG_E, val);
|
||||||
|
cycles = 12;
|
||||||
},
|
},
|
||||||
0x13 => {
|
0x13 => {
|
||||||
println!("INC DE");
|
println!("INC DE");
|
||||||
let old_value = self.get_pair_value(REG_D, REG_E);
|
let old_value = self.get_pair_value(REG_D, REG_E);
|
||||||
self.set_pair_value(REG_D, REG_E, old_value + 1);
|
self.set_pair_value(REG_D, REG_E, old_value + 1);
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x14 => {
|
0x14 => {
|
||||||
println!("INC D");
|
println!("INC D");
|
||||||
self.reg_inc(REG_D);
|
self.reg_inc(REG_D);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x15 => {
|
0x15 => {
|
||||||
println!("DEC D");
|
println!("DEC D");
|
||||||
self.reg_dec(REG_D);
|
self.reg_dec(REG_D);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x16 => {
|
0x16 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD D, {:02x}", args[0]);
|
println!("LD D, {:02x}", args[0]);
|
||||||
self.regs[REG_D] = args[0];
|
self.regs[REG_D] = args[0];
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x17 => {
|
0x17 => {
|
||||||
println!("RLA");
|
println!("RLA");
|
||||||
@ -300,6 +327,7 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
self.regs[REG_A] = self.regs[REG_A] << 1 | 1;
|
self.regs[REG_A] = self.regs[REG_A] << 1 | 1;
|
||||||
}
|
}
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x18 => {
|
0x18 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
@ -310,23 +338,28 @@ impl CPU {
|
|||||||
} else {
|
} else {
|
||||||
self.ip += off as u16;
|
self.ip += off as u16;
|
||||||
}
|
}
|
||||||
|
cycles = 12;
|
||||||
},
|
},
|
||||||
0x1A => {
|
0x1A => {
|
||||||
println!("LD A, (DE)");
|
println!("LD A, (DE)");
|
||||||
self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E));
|
self.regs[REG_A] = self.interconnect.read_byte(self.get_pair_value(REG_D, REG_E));
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x1C => {
|
0x1C => {
|
||||||
println!("INC E");
|
println!("INC E");
|
||||||
self.reg_inc(REG_E);
|
self.reg_inc(REG_E);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x1D => {
|
0x1D => {
|
||||||
println!("DEC E");
|
println!("DEC E");
|
||||||
self.reg_dec(REG_E);
|
self.reg_dec(REG_E);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x1E => {
|
0x1E => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD E, {:02x}", args[0]);
|
println!("LD E, {:02x}", args[0]);
|
||||||
self.regs[REG_E] = args[0];
|
self.regs[REG_E] = args[0];
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x20 => {
|
0x20 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
@ -338,36 +371,45 @@ impl CPU {
|
|||||||
} else {
|
} else {
|
||||||
self.ip += offset as u16;
|
self.ip += offset as u16;
|
||||||
}
|
}
|
||||||
|
cycles = 12;
|
||||||
|
} else {
|
||||||
|
cycles = 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x21 => {
|
0x21 => {
|
||||||
let value = to_u16(self.load_args(2));
|
let value = to_u16(self.load_args(2));
|
||||||
println!("LD HL, {:04x}", value);
|
println!("LD HL, {:04x}", value);
|
||||||
self.set_pair_value(REG_H, REG_L, value);
|
self.set_pair_value(REG_H, REG_L, value);
|
||||||
|
cycles = 12;
|
||||||
},
|
},
|
||||||
0x22 => {
|
0x22 => {
|
||||||
println!("LD (HL+), A");
|
println!("LD (HL+), A");
|
||||||
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
||||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
self.set_pair_value(REG_H, REG_L, addr + 1);
|
self.set_pair_value(REG_H, REG_L, addr + 1);
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x23 => {
|
0x23 => {
|
||||||
println!("INC HL");
|
println!("INC HL");
|
||||||
let old_value = self.get_pair_value(REG_H, REG_L);
|
let old_value = self.get_pair_value(REG_H, REG_L);
|
||||||
self.set_pair_value(REG_H, REG_L, old_value + 1);
|
self.set_pair_value(REG_H, REG_L, old_value + 1);
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x24 => {
|
0x24 => {
|
||||||
println!("INC H");
|
println!("INC H");
|
||||||
self.reg_inc(REG_H);
|
self.reg_inc(REG_H);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x25 => {
|
0x25 => {
|
||||||
println!("DEC H");
|
println!("DEC H");
|
||||||
self.reg_dec(REG_H);
|
self.reg_dec(REG_H);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x26 => {
|
0x26 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD H, {:02x}", args[0]);
|
println!("LD H, {:02x}", args[0]);
|
||||||
self.regs[REG_H] = args[0];
|
self.regs[REG_H] = args[0];
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x28 => {
|
0x28 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
@ -377,26 +419,35 @@ impl CPU {
|
|||||||
} else {
|
} else {
|
||||||
target = self.ip + args[0] as u16;
|
target = self.ip + args[0] as u16;
|
||||||
}
|
}
|
||||||
println!("LR Z, {:04X}", target);
|
println!("JR Z, {:04X}", target);
|
||||||
self.ip = target;
|
if self.flags & FLAG_Z > 0 {
|
||||||
|
self.ip = target;
|
||||||
|
cycles = 12;
|
||||||
|
} else {
|
||||||
|
cycles = 8;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
0x2C => {
|
0x2C => {
|
||||||
println!("INC L");
|
println!("INC L");
|
||||||
self.reg_inc(REG_L);
|
self.reg_inc(REG_L);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x2D => {
|
0x2D => {
|
||||||
println!("DEC L");
|
println!("DEC L");
|
||||||
self.reg_dec(REG_L);
|
self.reg_dec(REG_L);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x2E => {
|
0x2E => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD L, {:02x}", args[0]);
|
println!("LD L, {:02x}", args[0]);
|
||||||
self.regs[REG_L] = args[0];
|
self.regs[REG_L] = args[0];
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x31 => {
|
0x31 => {
|
||||||
let args = self.load_args(2);
|
let args = self.load_args(2);
|
||||||
self.sp = to_u16(args);
|
self.sp = to_u16(args);
|
||||||
println!("LD SP, {:02x}", self.sp);
|
println!("LD SP, {:02x}", self.sp);
|
||||||
|
cycles = 12;
|
||||||
},
|
},
|
||||||
0x32 => {
|
0x32 => {
|
||||||
println!("LD (HL-), A");
|
println!("LD (HL-), A");
|
||||||
@ -405,25 +456,30 @@ impl CPU {
|
|||||||
|
|
||||||
addr -= 1;
|
addr -= 1;
|
||||||
self.set_pair_value(REG_H, REG_L, addr);
|
self.set_pair_value(REG_H, REG_L, addr);
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0x36 => {
|
0x36 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD (HL), {:02x}", args[0]);
|
println!("LD (HL), {:02x}", args[0]);
|
||||||
let addr = self.get_pair_value(REG_H, REG_L);
|
let addr = self.get_pair_value(REG_H, REG_L);
|
||||||
self.interconnect.write_byte(addr, args[0]);
|
self.interconnect.write_byte(addr, args[0]);
|
||||||
|
cycles = 12;
|
||||||
},
|
},
|
||||||
0x3C => {
|
0x3C => {
|
||||||
println!("INC A");
|
println!("INC A");
|
||||||
self.reg_inc(REG_A);
|
self.reg_inc(REG_A);
|
||||||
|
cycles = 4;
|
||||||
}
|
}
|
||||||
0x3D => {
|
0x3D => {
|
||||||
println!("DEC A");
|
println!("DEC A");
|
||||||
self.reg_dec(REG_A);
|
self.reg_dec(REG_A);
|
||||||
|
cycles = 4;
|
||||||
}
|
}
|
||||||
0x3E => {
|
0x3E => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LD A, {:02x}", args[0]);
|
println!("LD A, {:02x}", args[0]);
|
||||||
self.regs[REG_A] = args[0];
|
self.regs[REG_A] = args[0];
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
|
|
||||||
// LDs
|
// LDs
|
||||||
@ -431,44 +487,52 @@ impl CPU {
|
|||||||
let reg_id = (instruction - 0x40) as usize;
|
let reg_id = (instruction - 0x40) as usize;
|
||||||
println!("LD B, {}", REG_NAMES[reg_id]);
|
println!("LD B, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_B] = self.get_8bit_reg(reg_id);
|
self.regs[REG_B] = self.get_8bit_reg(reg_id);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x48 ... 0x4F => {
|
0x48 ... 0x4F => {
|
||||||
let reg_id = (instruction - 0x48) as usize;
|
let reg_id = (instruction - 0x48) as usize;
|
||||||
println!("LD C, {}", REG_NAMES[reg_id]);
|
println!("LD C, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_C] = self.get_8bit_reg(reg_id);
|
self.regs[REG_C] = self.get_8bit_reg(reg_id);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x50 ... 0x57 => {
|
0x50 ... 0x57 => {
|
||||||
let reg_id = (instruction - 0x50) as usize;
|
let reg_id = (instruction - 0x50) as usize;
|
||||||
println!("LD D, {}", REG_NAMES[reg_id]);
|
println!("LD D, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_D] = self.get_8bit_reg(reg_id);
|
self.regs[REG_D] = self.get_8bit_reg(reg_id);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x58 ... 0x5F => {
|
0x58 ... 0x5F => {
|
||||||
let reg_id = (instruction - 0x58) as usize;
|
let reg_id = (instruction - 0x58) as usize;
|
||||||
println!("LD E, {}", REG_NAMES[reg_id]);
|
println!("LD E, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_E] = self.get_8bit_reg(reg_id);
|
self.regs[REG_E] = self.get_8bit_reg(reg_id);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x60 ... 0x67 => {
|
0x60 ... 0x67 => {
|
||||||
let reg_id = (instruction - 0x60) as usize;
|
let reg_id = (instruction - 0x60) as usize;
|
||||||
println!("LD H, {}", REG_NAMES[reg_id]);
|
println!("LD H, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_H] = self.get_8bit_reg(reg_id);
|
self.regs[REG_H] = self.get_8bit_reg(reg_id);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x68 ... 0x6F => {
|
0x68 ... 0x6F => {
|
||||||
let reg_id = (instruction - 0x68) as usize;
|
let reg_id = (instruction - 0x68) as usize;
|
||||||
println!("LD L, {}", REG_NAMES[reg_id]);
|
println!("LD L, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_L] = self.get_8bit_reg(reg_id);
|
self.regs[REG_L] = self.get_8bit_reg(reg_id);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0x70 ... 0x75 | 0x77 => {
|
0x70 ... 0x75 | 0x77 => {
|
||||||
let reg_id = (instruction - 0x70) as usize;
|
let reg_id = (instruction - 0x70) as usize;
|
||||||
println!("LD (HL), {}", REG_NAMES[reg_id]);
|
println!("LD (HL), {}", REG_NAMES[reg_id]);
|
||||||
let reg_value: u8 = self.get_8bit_reg(reg_id);
|
let reg_value: u8 = self.get_8bit_reg(reg_id);
|
||||||
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
let addr: u16 = self.get_pair_value(REG_H, REG_L);
|
||||||
|
cycles = 8;
|
||||||
|
|
||||||
self.interconnect.write_byte(addr, reg_value);
|
self.interconnect.write_byte(addr, reg_value);
|
||||||
},
|
},
|
||||||
0x78 ... 0x7F => {
|
0x78 ... 0x7F => {
|
||||||
let reg_id = (instruction - 0x78) as usize;
|
let reg_id = (instruction - 0x78) as usize;
|
||||||
println!("LD A, {}", REG_NAMES[reg_id]);
|
println!("LD A, {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_A] = self.get_8bit_reg(reg_id);
|
self.regs[REG_A] = self.get_8bit_reg(reg_id);
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
|
|
||||||
// ADD
|
// ADD
|
||||||
@ -477,6 +541,7 @@ impl CPU {
|
|||||||
println!("ADD {}", REG_NAMES[reg_id]);
|
println!("ADD {}", REG_NAMES[reg_id]);
|
||||||
self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id));
|
self.regs[REG_A] = self.regs[REG_A].wrapping_add(self.get_8bit_reg(reg_id));
|
||||||
self.flags &= !FLAG_N;
|
self.flags &= !FLAG_N;
|
||||||
|
cycles = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ADC
|
// ADC
|
||||||
@ -488,6 +553,7 @@ impl CPU {
|
|||||||
self.regs[REG_A] = self.regs[REG_A].wrapping_add(1);
|
self.regs[REG_A] = self.regs[REG_A].wrapping_add(1);
|
||||||
}
|
}
|
||||||
self.flags &= !FLAG_N;
|
self.flags &= !FLAG_N;
|
||||||
|
cycles = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SUBs
|
// SUBs
|
||||||
@ -502,6 +568,7 @@ impl CPU {
|
|||||||
self.flags &= !FLAG_Z;
|
self.flags &= !FLAG_Z;
|
||||||
}
|
}
|
||||||
// TODO: H, C
|
// TODO: H, C
|
||||||
|
cycles = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SBC
|
// SBC
|
||||||
@ -513,6 +580,7 @@ impl CPU {
|
|||||||
self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1);
|
self.regs[REG_A] = self.regs[REG_A].wrapping_sub(1);
|
||||||
}
|
}
|
||||||
self.flags |= FLAG_N;
|
self.flags |= FLAG_N;
|
||||||
|
cycles = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XOR
|
// XOR
|
||||||
@ -528,6 +596,7 @@ impl CPU {
|
|||||||
self.flags &= !FLAG_C;
|
self.flags &= !FLAG_C;
|
||||||
self.flags &= !FLAG_N;
|
self.flags &= !FLAG_N;
|
||||||
self.flags &= !FLAG_H;
|
self.flags &= !FLAG_H;
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
|
|
||||||
// CP
|
// CP
|
||||||
@ -545,66 +614,127 @@ impl CPU {
|
|||||||
self.flags &= !FLAG_Z;
|
self.flags &= !FLAG_Z;
|
||||||
self.flags &= !FLAG_C;
|
self.flags &= !FLAG_C;
|
||||||
}
|
}
|
||||||
|
cycles = 4;
|
||||||
},
|
},
|
||||||
0xC1 => {
|
0xC1 => {
|
||||||
println!("POP BC");
|
println!("POP BC");
|
||||||
let val: u16 = self.interconnect.read_word(self.sp);
|
let val: u16 = self.interconnect.read_word(self.sp);
|
||||||
self.sp += 2;
|
self.sp += 2;
|
||||||
self.set_pair_value(REG_B, REG_C, val);
|
self.set_pair_value(REG_B, REG_C, val);
|
||||||
|
cycles = 12;
|
||||||
|
},
|
||||||
|
0xC2 => {
|
||||||
|
let addr: u16 = to_u16(self.load_args(2));
|
||||||
|
println!("JP NZ {:04X}", addr);
|
||||||
|
if self.flags & FLAG_Z == 0 {
|
||||||
|
self.ip = addr;
|
||||||
|
cycles = 16;
|
||||||
|
} else {
|
||||||
|
cycles = 12;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0xC3 => {
|
||||||
|
let addr: u16 = to_u16(self.load_args(2));
|
||||||
|
println!("JP {:04X}", addr);
|
||||||
|
self.ip = addr;
|
||||||
|
cycles = 16;
|
||||||
|
}
|
||||||
|
0xC4 => {
|
||||||
|
let target = to_u16(self.load_args(2));
|
||||||
|
println!("CALL NZ {:04X}", &target);
|
||||||
|
if self.flags & FLAG_Z == 0 {
|
||||||
|
// Push current IP to the stack
|
||||||
|
self.interconnect.write_word(self.sp - 1, self.ip);
|
||||||
|
self.sp -= 2;
|
||||||
|
self.ip = target;
|
||||||
|
cycles = 24;
|
||||||
|
} else {
|
||||||
|
cycles = 12;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
0xC5 => {
|
0xC5 => {
|
||||||
println!("PUSH BC");
|
println!("PUSH BC");
|
||||||
let val: u16 = self.get_pair_value(REG_B, REG_C);
|
let val: u16 = self.get_pair_value(REG_B, REG_C);
|
||||||
self.interconnect.write_word(self.sp - 2, val);
|
self.interconnect.write_word(self.sp - 2, val);
|
||||||
self.sp -= 2;
|
self.sp -= 2;
|
||||||
|
cycles = 16;
|
||||||
},
|
},
|
||||||
0xC9 => {
|
0xC9 => {
|
||||||
println!("RET");
|
println!("RET");
|
||||||
self.dump_stack();
|
self.dump_stack();
|
||||||
self.ip = self.interconnect.read_word(self.sp+1);
|
self.ip = self.interconnect.read_word(self.sp+1);
|
||||||
self.sp += 2;
|
self.sp += 2;
|
||||||
|
cycles = 16;
|
||||||
},
|
},
|
||||||
0xCB => {
|
0xCB => {
|
||||||
// Prefix CB. This is annoying.
|
// Prefix CB. This is annoying.
|
||||||
self.run_prefix_instruction();
|
self.run_prefix_instruction();
|
||||||
|
cycles = 12; // TODO: Verify that this is the case for all prefix instructions.
|
||||||
|
},
|
||||||
|
0xCC => {
|
||||||
|
let target = to_u16(self.load_args(2));
|
||||||
|
println!("CALL Z {:04X}", &target);
|
||||||
|
if self.flags & FLAG_Z > 0 {
|
||||||
|
// Push current IP to the stack
|
||||||
|
self.interconnect.write_word(self.sp - 1, self.ip);
|
||||||
|
self.sp -= 2;
|
||||||
|
self.ip = target;
|
||||||
|
cycles = 24;
|
||||||
|
} else {
|
||||||
|
cycles = 12;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
0xCD => {
|
0xCD => {
|
||||||
let target = to_u16(self.load_args(2));
|
let target = to_u16(self.load_args(2));
|
||||||
println!("CALL {:04X}", &target);
|
println!("CALL {:04X}", &target);
|
||||||
// Push current IP to the stack
|
// Push current IP to the stack
|
||||||
// self.interconnect.write_byte(self.sp, (self.ip & 0xFF) as u8);
|
|
||||||
// self.interconnect.write_byte(self.sp - 1, (self.ip >> 8) as u8);
|
|
||||||
self.interconnect.write_word(self.sp - 1, self.ip);
|
self.interconnect.write_word(self.sp - 1, self.ip);
|
||||||
self.sp -= 2;
|
self.sp -= 2;
|
||||||
self.dump_stack();
|
|
||||||
|
|
||||||
self.ip = target;
|
self.ip = target;
|
||||||
|
cycles = 24;
|
||||||
},
|
},
|
||||||
0xE0 => {
|
0xE0 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LDH {:02X}, A", args[0]);
|
println!("LDH {:02X}, A", args[0]);
|
||||||
self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]);
|
self.interconnect.write_byte(0xFF00 + args[0] as u16, self.regs[REG_A]);
|
||||||
|
cycles = 12;
|
||||||
},
|
},
|
||||||
0xE2 => {
|
0xE2 => {
|
||||||
println!("LD (C), A");
|
println!("LD (C), A");
|
||||||
println!("[{:04X}] = {:02X}", 0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]);
|
println!("[{:04X}] = {:02X}", 0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]);
|
||||||
self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A])
|
self.interconnect.write_byte(0xFF00 + self.regs[REG_C] as u16, self.regs[REG_A]);
|
||||||
|
cycles = 8;
|
||||||
},
|
},
|
||||||
0xEA => {
|
0xEA => {
|
||||||
let addr = to_u16(self.load_args(2));
|
let addr = to_u16(self.load_args(2));
|
||||||
println!("LD ({:04X}), A", addr);
|
println!("LD ({:04X}), A", addr);
|
||||||
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
self.interconnect.write_byte(addr, self.regs[REG_A]);
|
||||||
|
cycles = 16;
|
||||||
}
|
}
|
||||||
0xF0 => {
|
0xF0 => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
println!("LDH A, {:02X}", args[0]);
|
println!("LDH A, {:02X}", args[0]);
|
||||||
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16);
|
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + args[0] as u16);
|
||||||
|
cycles = 12;
|
||||||
},
|
},
|
||||||
|
0xF2 => {
|
||||||
|
println!("LD A, (C)");
|
||||||
|
self.regs[REG_A] = self.interconnect.read_byte(0xFF00 + self.regs[REG_C] as u16);
|
||||||
|
cycles = 8;
|
||||||
|
},
|
||||||
|
0xF3 => {
|
||||||
|
println!("DI");
|
||||||
|
self.interrupts_enabled = false;
|
||||||
|
cycles = 4;
|
||||||
|
}
|
||||||
0xFB => {
|
0xFB => {
|
||||||
// Enable interrupts - TODO
|
// Enable interrupts - TODO
|
||||||
println!("EI");
|
println!("EI");
|
||||||
self.interconnect.write_byte(0xFFFF, 0x01);
|
self.interrupts_enabled = true;
|
||||||
panic!("Uh uh");
|
// self.interconnect.write_byte(0xFFFF, 0x01);
|
||||||
|
// panic!("Uh uh");
|
||||||
|
cycles = 4;
|
||||||
|
panic!("ENABLING INTERRUPTS - TODO");
|
||||||
},
|
},
|
||||||
0xFE => {
|
0xFE => {
|
||||||
let args = self.load_args(1);
|
let args = self.load_args(1);
|
||||||
@ -622,10 +752,11 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO H
|
// TODO H
|
||||||
|
cycles = 8;
|
||||||
}
|
}
|
||||||
_ => panic!("Unknown instruction: {:02x}", instruction)
|
_ => panic!("Unknown instruction: {:02x}", instruction)
|
||||||
}
|
}
|
||||||
// self.dump_stack();
|
// self.dump_stack();
|
||||||
self.interconnect.tick();
|
self.interconnect.tick(cycles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
118
src/display.rs
118
src/display.rs
@ -1,43 +1,159 @@
|
|||||||
|
const VRAM_SIZE: usize = 0x2000;
|
||||||
|
|
||||||
|
const TICKS_END_SCANLINE: u16 = 80;
|
||||||
|
const TICKS_END_READMODE: u16 = 172;
|
||||||
|
const TICKS_END_HBLANK: u16 = 204;
|
||||||
|
const TICKS_END_VBLANK: u16 = 456;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum DisplayMode {
|
||||||
|
Scanline,
|
||||||
|
Readmode,
|
||||||
|
HBlank,
|
||||||
|
VBlank,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DisplayMode {
|
||||||
|
fn default() -> DisplayMode {
|
||||||
|
DisplayMode::Scanline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Display {
|
pub struct Display {
|
||||||
control: u8,
|
control: u8,
|
||||||
status: u8,
|
status: u8,
|
||||||
background_palette: u8,
|
background_palette: u8,
|
||||||
|
object_palette_0: u8,
|
||||||
|
object_palette_1: u8,
|
||||||
scrollx: u8,
|
scrollx: u8,
|
||||||
scrolly: u8,
|
scrolly: u8,
|
||||||
|
windowx: u8,
|
||||||
|
windowy: u8,
|
||||||
curline: u8,
|
curline: u8,
|
||||||
|
|
||||||
|
vram: Box<[u8]>,
|
||||||
|
|
||||||
|
current_ticks: u16,
|
||||||
|
current_mode: DisplayMode,
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display {
|
impl Display {
|
||||||
pub fn new() -> Display {
|
pub fn new() -> Display {
|
||||||
Display::default()
|
Display {
|
||||||
|
control: 0,
|
||||||
|
status: 0,
|
||||||
|
background_palette: 0,
|
||||||
|
object_palette_0: 0,
|
||||||
|
object_palette_1: 0,
|
||||||
|
scrollx: 0,
|
||||||
|
scrolly: 0,
|
||||||
|
windowx: 0,
|
||||||
|
windowy: 0,
|
||||||
|
curline: 0,
|
||||||
|
|
||||||
|
current_ticks: 0,
|
||||||
|
current_mode: DisplayMode::default(),
|
||||||
|
vram: vec![0; VRAM_SIZE].into_boxed_slice(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
|
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize] = val,
|
||||||
0xFF40 => self.control = val,
|
0xFF40 => self.control = val,
|
||||||
0xFF41 => self.status = val,
|
0xFF41 => self.status = val,
|
||||||
0xFF42 => self.scrolly = val,
|
0xFF42 => self.scrolly = val,
|
||||||
0xFF43 => self.scrollx = val,
|
0xFF43 => self.scrollx = val,
|
||||||
0xFF44 => self.curline = 0,
|
0xFF44 => self.curline = 0,
|
||||||
0xFF47 => self.background_palette = val,
|
0xFF47 => self.background_palette = val,
|
||||||
|
0xFF48 => self.object_palette_0 = val,
|
||||||
|
0xFF49 => self.object_palette_1 = val,
|
||||||
|
0xFF4A => self.windowy = val,
|
||||||
|
0xFF4B => self.windowx = val,
|
||||||
_ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr),
|
_ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_byte(&self, addr: u16) -> u8 {
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
|
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize],
|
||||||
0xFF40 => self.control,
|
0xFF40 => self.control,
|
||||||
0xFF41 => self.status,
|
0xFF41 => self.status,
|
||||||
0xFF42 => self.scrolly,
|
0xFF42 => self.scrolly,
|
||||||
0xFF43 => self.scrollx,
|
0xFF43 => self.scrollx,
|
||||||
0xFF44 => self.curline,
|
0xFF44 => self.curline,
|
||||||
0xFF47 => self.background_palette,
|
0xFF47 => self.background_palette,
|
||||||
|
0xFF48 => self.object_palette_0,
|
||||||
|
0xFF49 => self.object_palette_1,
|
||||||
|
0xFF4A => self.windowy,
|
||||||
|
0xFF4B => self.windowx,
|
||||||
_ => panic!("Display: Read from {:04X} unsupported", addr),
|
_ => panic!("Display: Read from {:04X} unsupported", addr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do we want to have a time delta here?
|
||||||
|
pub fn tick(&mut self, ticks: u16) {
|
||||||
|
self.current_ticks += ticks;
|
||||||
|
match self.current_mode {
|
||||||
|
DisplayMode::Scanline => {
|
||||||
|
if self.current_ticks > TICKS_END_SCANLINE {
|
||||||
|
self.current_ticks = 0;
|
||||||
|
self.current_mode = DisplayMode::Readmode;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DisplayMode::Readmode => {
|
||||||
|
if self.current_ticks > TICKS_END_READMODE {
|
||||||
|
self.current_ticks = 0;
|
||||||
|
self.current_mode = DisplayMode::HBlank;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DisplayMode::HBlank => {
|
||||||
|
if self.current_ticks > TICKS_END_HBLANK {
|
||||||
|
self.current_ticks = 0;
|
||||||
|
self.curline += 1;
|
||||||
|
if self.curline == 143 {
|
||||||
|
self.current_mode = DisplayMode::VBlank;
|
||||||
|
} else {
|
||||||
|
self.current_mode = DisplayMode::Scanline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DisplayMode::VBlank => {
|
||||||
|
if self.current_ticks > TICKS_END_VBLANK {
|
||||||
|
self.current_ticks = 0;
|
||||||
|
self.curline += 1;
|
||||||
|
if self.curline > 153 {
|
||||||
|
self.current_mode = DisplayMode::Scanline;
|
||||||
|
self.curline = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderscan(&mut self) {
|
||||||
|
// TODO: Allow switching of tile map
|
||||||
|
let tilemap: u16 = 0x1800;
|
||||||
|
let map_offset_y: u8 = self.curline.wrapping_add(self.scrolly);
|
||||||
|
let map_offset_x: u8 = self.scrollx;
|
||||||
|
|
||||||
|
let tile_index_y: u8 = map_offset_y / 8;
|
||||||
|
|
||||||
|
// Render line
|
||||||
|
for render_x in 0 .. 159 {
|
||||||
|
let tile_index_x: u8 = render_x / 8;
|
||||||
|
let tile_offset_x: u8 = render_x % 8;
|
||||||
|
|
||||||
|
// TODO: Draw bit
|
||||||
|
// let tile_base_addr = tilemap + tile_id*128
|
||||||
|
// pixel(render_x, map_offset_y) := *tile_base_addr + 2* *(tile_base_addr+1)
|
||||||
|
//[tile_index_x][tile_index_y]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pub fn vblank_interrupt(&mut self) {
|
pub fn vblank_interrupt(&mut self) {
|
||||||
self.curline += 1;
|
self.curline += 1;
|
||||||
if self.curline > 153 {
|
if self.curline > 153 {
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
const RAM_SIZE: usize = 0x2000;
|
const RAM_SIZE: usize = 0x2000;
|
||||||
const VRAM_SIZE: usize = 0x2000;
|
|
||||||
const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1;
|
const HIRAM_SIZE: usize = (0xFFFE - 0xFF80) + 1;
|
||||||
|
|
||||||
const INTERRUPT_DISPLAY: u8 = 1 << 1;
|
const INTERRUPT_DISPLAY: u8 = 1 << 1;
|
||||||
@ -7,18 +6,23 @@ const INTERRUPT_DISPLAY_VBLANK: u8 = 1 << 0;
|
|||||||
|
|
||||||
use super::display;
|
use super::display;
|
||||||
use super::sound;
|
use super::sound;
|
||||||
|
use super::timer;
|
||||||
|
use super::serial;
|
||||||
|
|
||||||
pub struct Interconnect {
|
pub struct Interconnect {
|
||||||
bios: Box<[u8]>,
|
bios: Box<[u8]>,
|
||||||
rom: Box<[u8]>,
|
rom: Box<[u8]>,
|
||||||
ram: Box<[u8]>,
|
ram: Box<[u8]>,
|
||||||
vram: Box<[u8]>,
|
|
||||||
hiram: Box<[u8]>,
|
hiram: Box<[u8]>,
|
||||||
sound: sound::Sound,
|
sound: sound::Sound,
|
||||||
display: display::Display,
|
display: display::Display,
|
||||||
interrupt: u8,
|
interrupt: u8,
|
||||||
timer: u8,
|
iflags: u8,
|
||||||
|
itimer: u8,
|
||||||
disable_bootrom: u8,
|
disable_bootrom: u8,
|
||||||
|
infrared_com_port: u8,
|
||||||
|
serial: serial::Serial,
|
||||||
|
timer: timer::Timer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interconnect {
|
impl Interconnect {
|
||||||
@ -27,23 +31,30 @@ impl Interconnect {
|
|||||||
bios: bios,
|
bios: bios,
|
||||||
rom: rom,
|
rom: rom,
|
||||||
ram: vec![0; RAM_SIZE].into_boxed_slice(),
|
ram: vec![0; RAM_SIZE].into_boxed_slice(),
|
||||||
vram: vec![0; VRAM_SIZE].into_boxed_slice(),
|
|
||||||
hiram: vec![0; HIRAM_SIZE].into_boxed_slice(),
|
hiram: vec![0; HIRAM_SIZE].into_boxed_slice(),
|
||||||
sound: sound::Sound::new(),
|
sound: sound::Sound::new(),
|
||||||
display: display::Display::new(),
|
display: display::Display::new(),
|
||||||
|
// Refactor those
|
||||||
|
iflags: 0,
|
||||||
interrupt: 0,
|
interrupt: 0,
|
||||||
timer: 0,
|
infrared_com_port: 0,
|
||||||
|
itimer: 0,
|
||||||
disable_bootrom: 0,
|
disable_bootrom: 0,
|
||||||
|
timer: timer::Timer::new(),
|
||||||
|
serial: serial::Serial::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Somehow we need different timers for this.
|
// Somehow we need different timers for this.
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self, cycles: u16) {
|
||||||
self.timer += 1;
|
/*
|
||||||
if self.timer == 5 {
|
self.itimer += 1;
|
||||||
|
if self.itimer == 5 {
|
||||||
self.display_blank_interrupt();
|
self.display_blank_interrupt();
|
||||||
self.timer = 0;
|
self.itimer = 0;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
self.display.tick(cycles * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_blank_interrupt(&mut self) {
|
pub fn display_blank_interrupt(&mut self) {
|
||||||
@ -71,12 +82,15 @@ impl Interconnect {
|
|||||||
self.rom[addr as usize]
|
self.rom[addr as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x8000 ... 0x9FFF => {
|
0x8000 ... 0x9FFF => self.display.read_byte(addr),
|
||||||
self.vram[(addr - 0x8000) as usize]
|
|
||||||
},
|
|
||||||
0xC000 ... 0xDFFF => {
|
0xC000 ... 0xDFFF => {
|
||||||
self.ram[(addr - 0xC000) as usize]
|
self.ram[(addr - 0xC000) as usize]
|
||||||
},
|
},
|
||||||
|
0xFF01 ... 0xFF02 => self.serial.read_byte(addr),
|
||||||
|
0xFF04 ... 0xFF07 => self.timer.read_byte(addr),
|
||||||
|
0xFF0F => {
|
||||||
|
self.iflags
|
||||||
|
},
|
||||||
0xFF10 ... 0xFF26 => {
|
0xFF10 ... 0xFF26 => {
|
||||||
self.sound.read_byte(addr)
|
self.sound.read_byte(addr)
|
||||||
},
|
},
|
||||||
@ -86,6 +100,9 @@ impl Interconnect {
|
|||||||
0xFF50 => {
|
0xFF50 => {
|
||||||
self.disable_bootrom
|
self.disable_bootrom
|
||||||
},
|
},
|
||||||
|
0xFF56 => {
|
||||||
|
self.infrared_com_port
|
||||||
|
}
|
||||||
0xFF80 ... 0xFFFE => {
|
0xFF80 ... 0xFFFE => {
|
||||||
self.hiram[(addr - 0xFF80) as usize]
|
self.hiram[(addr - 0xFF80) as usize]
|
||||||
},
|
},
|
||||||
@ -100,7 +117,6 @@ impl Interconnect {
|
|||||||
|
|
||||||
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
// TODO: Make this more beautful
|
// TODO: Make this more beautful
|
||||||
// TODO: Write byte
|
|
||||||
/*
|
/*
|
||||||
0000 7FFF Cartridge
|
0000 7FFF Cartridge
|
||||||
8000 9FFF Video RAM
|
8000 9FFF Video RAM
|
||||||
@ -109,18 +125,21 @@ impl Interconnect {
|
|||||||
E000 FCFF Copy of the work RAM
|
E000 FCFF Copy of the work RAM
|
||||||
FE00 FE9F OAM (Sprite Attribute Table)
|
FE00 FE9F OAM (Sprite Attribute Table)
|
||||||
FEA0 FEFF Unused
|
FEA0 FEFF Unused
|
||||||
FF00 FF7F Hardware IO
|
FF00 FF7F Hardware IO
|
||||||
FF80 FFFE High RAM
|
FF80 FFFE High RAM
|
||||||
FFFF FFFF Interrupt switch
|
FFFF FFFF Interrupt switch
|
||||||
*/
|
*/
|
||||||
|
|
||||||
match addr {
|
match addr {
|
||||||
0x8000 ... 0x9FFF => {
|
0x8000 ... 0x9FFF => self.display.write_byte(addr, val),
|
||||||
self.vram[(addr - 0x8000) as usize] = val;
|
|
||||||
},
|
|
||||||
0xC000 ... 0xDFFF => {
|
0xC000 ... 0xDFFF => {
|
||||||
self.ram[(addr - 0xC000) as usize] = val;
|
self.ram[(addr - 0xC000) as usize] = val;
|
||||||
},
|
},
|
||||||
|
0xFF01 ... 0xFF02 => self.serial.write_byte(addr, val),
|
||||||
|
0xFF04 ... 0xFF07 => self.timer.write_byte(addr, val),
|
||||||
|
0xFF0F => {
|
||||||
|
self.iflags = val;
|
||||||
|
}
|
||||||
0xFF10 ... 0xFF26 => {
|
0xFF10 ... 0xFF26 => {
|
||||||
self.sound.write_byte(addr, val);
|
self.sound.write_byte(addr, val);
|
||||||
},
|
},
|
||||||
@ -129,7 +148,10 @@ impl Interconnect {
|
|||||||
},
|
},
|
||||||
0xFF50 => {
|
0xFF50 => {
|
||||||
self.disable_bootrom = val;
|
self.disable_bootrom = val;
|
||||||
}
|
},
|
||||||
|
0xFF56 => {
|
||||||
|
self.infrared_com_port = val;
|
||||||
|
},
|
||||||
0xFF80 ... 0xFFFE => {
|
0xFF80 ... 0xFFFE => {
|
||||||
self.hiram[(addr - 0xFF80) as usize] = val;
|
self.hiram[(addr - 0xFF80) as usize] = val;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -10,7 +10,8 @@ mod cpu;
|
|||||||
mod display;
|
mod display;
|
||||||
mod interconnect;
|
mod interconnect;
|
||||||
mod sound;
|
mod sound;
|
||||||
|
mod timer;
|
||||||
|
mod serial;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let bios_path = env::args().nth(1).unwrap();
|
let bios_path = env::args().nth(1).unwrap();
|
||||||
|
|||||||
27
src/serial.rs
Normal file
27
src/serial.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Serial {
|
||||||
|
data: u8,
|
||||||
|
control: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serial {
|
||||||
|
pub fn new() -> Serial {
|
||||||
|
Serial::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
|
match addr {
|
||||||
|
0xFF01 => self.data = val,
|
||||||
|
0xFF02 => self.control = val,
|
||||||
|
_ => panic!("Serial: Write {:02X} to {:04X} unsupported", val, addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
|
match addr {
|
||||||
|
0xFF01 => self.data,
|
||||||
|
0xFF02 => self.control,
|
||||||
|
_ => panic!("Serial: Read from {:04X} unsupported", addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/timer.rs
Normal file
30
src/timer.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Timer {
|
||||||
|
tima: u8,
|
||||||
|
tma: u8,
|
||||||
|
tac: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
pub fn new() -> Timer {
|
||||||
|
Timer::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_byte(&mut self, addr: u16, val: u8) {
|
||||||
|
match addr {
|
||||||
|
0xFF05 => self.tima = val,
|
||||||
|
0xFF06 => self.tma = val,
|
||||||
|
0xFF07 => self.tac = val,
|
||||||
|
_ => panic!("Timer: Write {:02X} to {:04X} unsupported", val, addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_byte(&self, addr: u16) -> u8 {
|
||||||
|
match addr {
|
||||||
|
0xFF05 => self.tima,
|
||||||
|
0xFF06 => self.tma,
|
||||||
|
0xFF07 => self.tac,
|
||||||
|
_ => panic!("Timer: Read from {:04X} unsupported", addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user