Model many things
Former-commit-id: b87fa2b16b395f497cf217ea043e68404ab2f65e
This commit is contained in:
parent
d1ef35646f
commit
3cc84b1b03
|
@ -75,7 +75,7 @@ pub struct Core {
|
||||||
|
|
||||||
pub pipeline_arm: PipelineContext<ArmInstruction, u32>,
|
pub pipeline_arm: PipelineContext<ArmInstruction, u32>,
|
||||||
pub pipeline_thumb: PipelineContext<ThumbInstruction, u16>,
|
pub pipeline_thumb: PipelineContext<ThumbInstruction, u16>,
|
||||||
cycles: usize,
|
pub cycles: usize,
|
||||||
|
|
||||||
// store the gpr before executing an instruction to show diff in the Display impl
|
// store the gpr before executing an instruction to show diff in the Display impl
|
||||||
gpr_previous: [u32; 15],
|
gpr_previous: [u32; 15],
|
||||||
|
@ -183,12 +183,12 @@ impl Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_cycle(&mut self) {
|
pub fn add_cycle(&mut self) {
|
||||||
println!("<cycle I-Cyclel> total: {}", self.cycles);
|
// println!("<cycle I-Cyclel> total: {}", self.cycles);
|
||||||
self.cycles += 1;
|
self.cycles += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_cycles(&mut self, addr: Addr, bus: &Bus, access: MemoryAccess) {
|
pub fn add_cycles(&mut self, addr: Addr, bus: &Bus, access: MemoryAccess) {
|
||||||
println!("<cycle {:#x} {}> total: {}", addr, access, self.cycles);
|
// println!("<cycle {:#x} {}> total: {}", addr, access, self.cycles);
|
||||||
self.cycles += bus.get_cycles(addr, access);
|
self.cycles += bus.get_cycles(addr, access);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ impl Core {
|
||||||
/// A step that returns only once an instruction was executed.
|
/// A step that returns only once an instruction was executed.
|
||||||
/// Returns the address of PC before executing an instruction,
|
/// Returns the address of PC before executing an instruction,
|
||||||
/// and the address of the next instruction to be executed;
|
/// and the address of the next instruction to be executed;
|
||||||
pub fn step_debugger(&mut self, bus: &mut Bus) -> CpuResult<DecodedInstruction> {
|
pub fn step_one(&mut self, bus: &mut Bus) -> CpuResult<DecodedInstruction> {
|
||||||
loop {
|
loop {
|
||||||
if let Some(i) = self.step(bus)? {
|
if let Some(i) = self.step(bus)? {
|
||||||
return Ok(i);
|
return Ok(i);
|
||||||
|
|
|
@ -11,7 +11,7 @@ use thumb::{ThumbDecodeError, ThumbInstruction};
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub use cpu::*;
|
pub use cpu::*;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
pub use bus::Bus;
|
pub use bus::*;
|
||||||
pub mod exception;
|
pub mod exception;
|
||||||
pub mod psr;
|
pub mod psr;
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,8 @@ impl fmt::Display for RegPSR {
|
||||||
};
|
};
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{{ mode: {mode}, state: {state}, irq: {irq}, fiq: {fiq}, condition_flags: (N={N} Z={Z} C={C} V={V}) }}",
|
"{{ [{raw:#010x}] mode: {mode}, state: {state}, irq: {irq}, fiq: {fiq}, condition_flags: (N={N} Z={Z} C={C} V={V}) }}",
|
||||||
|
raw = self.raw,
|
||||||
mode = self.mode(),
|
mode = self.mode(),
|
||||||
state = self.state(),
|
state = self.state(),
|
||||||
irq = disabled_string(self.irq_disabled()),
|
irq = disabled_string(self.irq_disabled()),
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::io;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
|
||||||
|
@ -7,45 +5,11 @@ use clap::{App, ArgMatches};
|
||||||
|
|
||||||
extern crate rustboyadvance_ng;
|
extern crate rustboyadvance_ng;
|
||||||
|
|
||||||
use rustboyadvance_ng::arm7tdmi;
|
use rustboyadvance_ng::arm7tdmi::Core;
|
||||||
use rustboyadvance_ng::debugger::{Debugger, DebuggerError};
|
|
||||||
use rustboyadvance_ng::cartridge::Cartridge;
|
use rustboyadvance_ng::cartridge::Cartridge;
|
||||||
use rustboyadvance_ng::sysbus::SysBus;
|
use rustboyadvance_ng::debugger::Debugger;
|
||||||
use rustboyadvance_ng::util::read_bin_file;
|
use rustboyadvance_ng::util::read_bin_file;
|
||||||
|
use rustboyadvance_ng::{GBAResult, GameBoyAdvance};
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum GBAError {
|
|
||||||
IO(io::Error),
|
|
||||||
ArmDecodeError(arm7tdmi::arm::ArmDecodeError),
|
|
||||||
CpuError(arm7tdmi::CpuError),
|
|
||||||
DebuggerError(DebuggerError),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type GBAResult<T> = Result<T, GBAError>;
|
|
||||||
|
|
||||||
impl From<io::Error> for GBAError {
|
|
||||||
fn from(err: io::Error) -> GBAError {
|
|
||||||
GBAError::IO(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<arm7tdmi::arm::ArmDecodeError> for GBAError {
|
|
||||||
fn from(err: arm7tdmi::arm::ArmDecodeError) -> GBAError {
|
|
||||||
GBAError::ArmDecodeError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<arm7tdmi::CpuError> for GBAError {
|
|
||||||
fn from(err: arm7tdmi::CpuError) -> GBAError {
|
|
||||||
GBAError::CpuError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DebuggerError> for GBAError {
|
|
||||||
fn from(err: DebuggerError) -> GBAError {
|
|
||||||
GBAError::DebuggerError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
let bios_bin = read_bin_file(matches.value_of("bios").unwrap_or_default())?;
|
let bios_bin = read_bin_file(matches.value_of("bios").unwrap_or_default())?;
|
||||||
|
@ -54,11 +18,13 @@ fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
let gamepak = Cartridge::new(rom_bin);
|
let gamepak = Cartridge::new(rom_bin);
|
||||||
println!("loaded rom: {:#?}", gamepak.header);
|
println!("loaded rom: {:#?}", gamepak.header);
|
||||||
|
|
||||||
let sysbus = SysBus::new(bios_bin, gamepak);
|
let mut core = Core::new();
|
||||||
let mut core = arm7tdmi::cpu::Core::new();
|
|
||||||
core.reset();
|
core.reset();
|
||||||
core.set_verbose(true);
|
core.set_verbose(true);
|
||||||
let mut debugger = Debugger::new(core, sysbus);
|
|
||||||
|
let gba = GameBoyAdvance::new(core, bios_bin, gamepak);
|
||||||
|
|
||||||
|
let mut debugger = Debugger::new(gba);
|
||||||
|
|
||||||
println!("starting debugger...");
|
println!("starting debugger...");
|
||||||
debugger.repl()?;
|
debugger.repl()?;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::arm7tdmi::bus::Bus;
|
use crate::arm7tdmi::bus::Bus;
|
||||||
use crate::arm7tdmi::{Addr, CpuState};
|
use crate::arm7tdmi::{Addr, CpuState};
|
||||||
use crate::disass::Disassembler;
|
use crate::disass::Disassembler;
|
||||||
|
use crate::GBAError;
|
||||||
|
|
||||||
use super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
|
use super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
|
||||||
|
|
||||||
|
@ -34,14 +35,14 @@ impl Command {
|
||||||
pub fn run(&self, debugger: &mut Debugger) {
|
pub fn run(&self, debugger: &mut Debugger) {
|
||||||
use Command::*;
|
use Command::*;
|
||||||
match *self {
|
match *self {
|
||||||
Info => println!("{}", debugger.cpu),
|
Info => println!("{}", debugger.gba.cpu),
|
||||||
Step(count) => {
|
Step(count) => {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
if let Some(bp) = debugger.check_breakpoint() {
|
if let Some(bp) = debugger.check_breakpoint() {
|
||||||
println!("hit breakpoint #0x{:08x}!", bp);
|
println!("hit breakpoint #0x{:08x}!", bp);
|
||||||
debugger.delete_breakpoint(bp);
|
debugger.delete_breakpoint(bp);
|
||||||
} else {
|
} else {
|
||||||
match debugger.cpu.step_debugger(&mut debugger.sysbus) {
|
match debugger.gba.step() {
|
||||||
Ok(insn) => {
|
Ok(insn) => {
|
||||||
print!(
|
print!(
|
||||||
"{}\t{}",
|
"{}\t{}",
|
||||||
|
@ -52,19 +53,23 @@ impl Command {
|
||||||
.paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)),
|
.paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)),
|
||||||
insn
|
insn
|
||||||
);
|
);
|
||||||
println!("{}", Colour::Purple.dimmed().italic().paint(format!(
|
println!(
|
||||||
"\t\t/// Next instruction at @0x{:08x}",
|
"{}",
|
||||||
debugger.cpu.get_next_pc()
|
Colour::Purple.dimmed().italic().paint(format!(
|
||||||
)))
|
"\t\t/// Next instruction at @0x{:08x}",
|
||||||
|
debugger.gba.cpu.get_next_pc()
|
||||||
|
))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(GBAError::CpuError(e)) => {
|
||||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||||
println!("cpu: {:x?}", debugger.cpu)
|
println!("cpu: {:x?}", debugger.gba.cpu)
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("{}\n", debugger.cpu);
|
println!("{}\n", debugger.gba.cpu);
|
||||||
}
|
}
|
||||||
Continue => loop {
|
Continue => loop {
|
||||||
if let Some(bp) = debugger.check_breakpoint() {
|
if let Some(bp) = debugger.check_breakpoint() {
|
||||||
|
@ -72,7 +77,7 @@ impl Command {
|
||||||
debugger.delete_breakpoint(bp);
|
debugger.delete_breakpoint(bp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
match debugger.cpu.step_debugger(&mut debugger.sysbus) {
|
match debugger.gba.step() {
|
||||||
Ok(insn) => {
|
Ok(insn) => {
|
||||||
println!(
|
println!(
|
||||||
"@0x{:08x}:\t{}",
|
"@0x{:08x}:\t{}",
|
||||||
|
@ -80,22 +85,23 @@ impl Command {
|
||||||
Colour::Yellow.italic().paint(format!("{} ", insn))
|
Colour::Yellow.italic().paint(format!("{} ", insn))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(GBAError::CpuError(e)) => {
|
||||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||||
println!("cpu: {:x?}", debugger.cpu);
|
println!("cpu: {:x?}", debugger.gba.cpu);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
HexDump(addr, nbytes) => {
|
HexDump(addr, nbytes) => {
|
||||||
let bytes = debugger.sysbus.get_bytes(addr);
|
let bytes = debugger.gba.sysbus.get_bytes(addr);
|
||||||
hexdump::hexdump(&bytes[0..nbytes]);
|
hexdump::hexdump(&bytes[0..nbytes]);
|
||||||
}
|
}
|
||||||
Disass(mode, addr, n) => {
|
Disass(mode, addr, n) => {
|
||||||
use crate::arm7tdmi::arm::ArmInstruction;
|
use crate::arm7tdmi::arm::ArmInstruction;
|
||||||
use crate::arm7tdmi::thumb::ThumbInstruction;
|
use crate::arm7tdmi::thumb::ThumbInstruction;
|
||||||
|
|
||||||
let bytes = debugger.sysbus.get_bytes(addr);
|
let bytes = debugger.gba.sysbus.get_bytes(addr);
|
||||||
match mode {
|
match mode {
|
||||||
DisassMode::ModeArm => {
|
DisassMode::ModeArm => {
|
||||||
let disass = Disassembler::<ArmInstruction>::new(addr, bytes);
|
let disass = Disassembler::<ArmInstruction>::new(addr, bytes);
|
||||||
|
@ -134,7 +140,7 @@ impl Command {
|
||||||
}
|
}
|
||||||
Reset => {
|
Reset => {
|
||||||
println!("resetting cpu...");
|
println!("resetting cpu...");
|
||||||
debugger.cpu.reset();
|
debugger.gba.cpu.reset();
|
||||||
println!("cpu is restarted!")
|
println!("cpu is restarted!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +165,7 @@ impl Debugger {
|
||||||
if let Some(Command::Disass(_mode, addr, n)) = &self.previous_command {
|
if let Some(Command::Disass(_mode, addr, n)) = &self.previous_command {
|
||||||
Ok((*addr + (4 * (*n as u32)), 10))
|
Ok((*addr + (4 * (*n as u32)), 10))
|
||||||
} else {
|
} else {
|
||||||
Ok((self.cpu.get_next_pc(), 10))
|
Ok((self.gba.cpu.get_next_pc(), 10))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -210,7 +216,7 @@ impl Debugger {
|
||||||
if let Some(Command::HexDump(addr, n)) = self.previous_command {
|
if let Some(Command::HexDump(addr, n)) = self.previous_command {
|
||||||
(addr + (4 * n as u32), 0x100)
|
(addr + (4 * n as u32), 0x100)
|
||||||
} else {
|
} else {
|
||||||
(self.cpu.get_reg(15), 0x100)
|
(self.gba.cpu.get_reg(15), 0x100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -224,7 +230,7 @@ impl Debugger {
|
||||||
"d" | "disass" => {
|
"d" | "disass" => {
|
||||||
let (addr, n) = self.get_disassembler_args(args)?;
|
let (addr, n) = self.get_disassembler_args(args)?;
|
||||||
|
|
||||||
let m = match self.cpu.cpsr.state() {
|
let m = match self.gba.cpu.cpsr.state() {
|
||||||
CpuState::ARM => DisassMode::ModeArm,
|
CpuState::ARM => DisassMode::ModeArm,
|
||||||
CpuState::THUMB => DisassMode::ModeThumb,
|
CpuState::THUMB => DisassMode::ModeThumb,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,9 +3,8 @@ use rustyline::Editor;
|
||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
||||||
use super::arm7tdmi;
|
use super::arm7tdmi::{Addr, Bus, CpuError};
|
||||||
use super::arm7tdmi::{Addr, Bus};
|
use super::GameBoyAdvance;
|
||||||
use super::sysbus::SysBus;
|
|
||||||
|
|
||||||
mod parser;
|
mod parser;
|
||||||
use parser::{parse_expr, DerefType, Expr, Value};
|
use parser::{parse_expr, DerefType, Expr, Value};
|
||||||
|
@ -16,14 +15,14 @@ use command::Command;
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum DebuggerError {
|
pub enum DebuggerError {
|
||||||
ParsingError(String),
|
ParsingError(String),
|
||||||
CpuError(arm7tdmi::CpuError),
|
CpuError(CpuError),
|
||||||
InvalidCommand(String),
|
InvalidCommand(String),
|
||||||
InvalidArgument(String),
|
InvalidArgument(String),
|
||||||
InvalidCommandFormat(String),
|
InvalidCommandFormat(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<arm7tdmi::CpuError> for DebuggerError {
|
impl From<CpuError> for DebuggerError {
|
||||||
fn from(e: arm7tdmi::CpuError) -> DebuggerError {
|
fn from(e: CpuError) -> DebuggerError {
|
||||||
DebuggerError::CpuError(e)
|
DebuggerError::CpuError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,18 +31,16 @@ type DebuggerResult<T> = Result<T, DebuggerError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Debugger {
|
pub struct Debugger {
|
||||||
pub cpu: arm7tdmi::cpu::Core,
|
pub gba: GameBoyAdvance,
|
||||||
pub sysbus: SysBus,
|
|
||||||
running: bool,
|
running: bool,
|
||||||
breakpoints: Vec<u32>,
|
breakpoints: Vec<u32>,
|
||||||
pub previous_command: Option<Command>,
|
pub previous_command: Option<Command>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debugger {
|
impl Debugger {
|
||||||
pub fn new(cpu: arm7tdmi::cpu::Core, sysbus: SysBus) -> Debugger {
|
pub fn new(gba: GameBoyAdvance) -> Debugger {
|
||||||
Debugger {
|
Debugger {
|
||||||
cpu: cpu,
|
gba: gba,
|
||||||
sysbus: sysbus,
|
|
||||||
breakpoints: Vec::new(),
|
breakpoints: Vec::new(),
|
||||||
running: false,
|
running: false,
|
||||||
previous_command: None,
|
previous_command: None,
|
||||||
|
@ -51,7 +48,7 @@ impl Debugger {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_breakpoint(&self) -> Option<u32> {
|
pub fn check_breakpoint(&self) -> Option<u32> {
|
||||||
let next_pc = self.cpu.get_next_pc();
|
let next_pc = self.gba.cpu.get_next_pc();
|
||||||
for bp in &self.breakpoints {
|
for bp in &self.breakpoints {
|
||||||
if *bp == next_pc {
|
if *bp == next_pc {
|
||||||
return Some(next_pc);
|
return Some(next_pc);
|
||||||
|
@ -105,7 +102,7 @@ impl Debugger {
|
||||||
Value::Num(n) => Ok(*n),
|
Value::Num(n) => Ok(*n),
|
||||||
Value::Identifier(reg) => {
|
Value::Identifier(reg) => {
|
||||||
let reg = self.decode_reg(®)?;
|
let reg = self.decode_reg(®)?;
|
||||||
Ok(self.cpu.get_reg(reg))
|
Ok(self.gba.cpu.get_reg(reg))
|
||||||
}
|
}
|
||||||
v => Err(DebuggerError::InvalidArgument(format!(
|
v => Err(DebuggerError::InvalidArgument(format!(
|
||||||
"addr: expected a number or register, got {:?}",
|
"addr: expected a number or register, got {:?}",
|
||||||
|
@ -120,14 +117,14 @@ impl Debugger {
|
||||||
Value::Deref(addr_value, deref_type) => {
|
Value::Deref(addr_value, deref_type) => {
|
||||||
let addr = self.val_address(&addr_value)?;
|
let addr = self.val_address(&addr_value)?;
|
||||||
match deref_type {
|
match deref_type {
|
||||||
DerefType::Word => self.sysbus.read_32(addr),
|
DerefType::Word => self.gba.sysbus.read_32(addr),
|
||||||
DerefType::HalfWord => self.sysbus.read_16(addr) as u32,
|
DerefType::HalfWord => self.gba.sysbus.read_16(addr) as u32,
|
||||||
DerefType::Byte => self.sysbus.read_8(addr) as u32,
|
DerefType::Byte => self.gba.sysbus.read_8(addr) as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => self.val_address(&rvalue)?,
|
_ => self.val_address(&rvalue)?,
|
||||||
};
|
};
|
||||||
self.cpu.set_reg(lvalue, rvalue);
|
self.gba.cpu.set_reg(lvalue, rvalue);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
src/gba.rs
Normal file
43
src/gba.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use super::arm7tdmi::{Core, DecodedInstruction};
|
||||||
|
use super::cartridge::Cartridge;
|
||||||
|
use super::lcd::Lcd;
|
||||||
|
use super::sysbus::SysBus;
|
||||||
|
/// Struct containing everything
|
||||||
|
///
|
||||||
|
use super::{EmuIoDev, GBAResult};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GameBoyAdvance {
|
||||||
|
pub cpu: Core,
|
||||||
|
pub sysbus: SysBus,
|
||||||
|
|
||||||
|
// io devices
|
||||||
|
lcd: Lcd,
|
||||||
|
|
||||||
|
post_bool_flags: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameBoyAdvance {
|
||||||
|
pub fn new(cpu: Core, bios_rom: Vec<u8>, gamepak: Cartridge) -> GameBoyAdvance {
|
||||||
|
let sysbus = SysBus::new(bios_rom, gamepak);
|
||||||
|
|
||||||
|
GameBoyAdvance {
|
||||||
|
cpu: cpu,
|
||||||
|
sysbus: sysbus,
|
||||||
|
|
||||||
|
lcd: Lcd::new(),
|
||||||
|
|
||||||
|
post_bool_flags: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self) -> GBAResult<DecodedInstruction> {
|
||||||
|
let previous_cycles = self.cpu.cycles;
|
||||||
|
let decoded = self.cpu.step_one(&mut self.sysbus)?;
|
||||||
|
let cycles = self.cpu.cycles - previous_cycles;
|
||||||
|
|
||||||
|
self.lcd.step(cycles, &mut self.sysbus); // drop interrupts at the moment
|
||||||
|
|
||||||
|
Ok(decoded) // return the decoded instruction for the debugger
|
||||||
|
}
|
||||||
|
}
|
20
src/interrupt.rs
Normal file
20
src/interrupt.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum Interrupt {
|
||||||
|
LCD_VBlank = 0,
|
||||||
|
LCD_HBlank = 1,
|
||||||
|
LCD_VCounterMatch = 2,
|
||||||
|
Timer0_Overflow = 3,
|
||||||
|
Timer1_Overflow = 4,
|
||||||
|
Timer2_Overflow = 5,
|
||||||
|
Timer3_Overflow = 6,
|
||||||
|
SerialCommunication = 7,
|
||||||
|
DMA0 = 8,
|
||||||
|
DMA1 = 9,
|
||||||
|
DMA2 = 10,
|
||||||
|
DMA3 = 11,
|
||||||
|
Keypad = 12,
|
||||||
|
GamePak = 13,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct InterruptController;
|
200
src/ioregs.rs
Normal file
200
src/ioregs.rs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::arm7tdmi::{Addr, Bus, MemoryAccess};
|
||||||
|
|
||||||
|
pub mod consts {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub const IO_BASE: Addr = 0x0400_0000;
|
||||||
|
|
||||||
|
// LCD I/O Registers
|
||||||
|
pub const REG_DISPCNT: Addr = IO_BASE + 0x_0000; // 2 R/W LCD Control
|
||||||
|
pub const REG_DISPSTAT: Addr = IO_BASE + 0x_0004; // 2 R/W General LCD Status (STAT,LYC)
|
||||||
|
pub const REG_VCOUNT: Addr = IO_BASE + 0x_0006; // 2 R Vertical Counter (LY)
|
||||||
|
pub const REG_BG0CNT: Addr = IO_BASE + 0x_0008; // 2 R/W BG0 Control
|
||||||
|
pub const REG_BG1CNT: Addr = IO_BASE + 0x_000A; // 2 R/W BG1 Control
|
||||||
|
pub const REG_BG2CNT: Addr = IO_BASE + 0x_000C; // 2 R/W BG2 Control
|
||||||
|
pub const REG_BG3CNT: Addr = IO_BASE + 0x_000E; // 2 R/W BG3 Control
|
||||||
|
pub const REG_BG0HOFS: Addr = IO_BASE + 0x_0010; // 2 W BG0 X-Offset
|
||||||
|
pub const REG_BG0VOFS: Addr = IO_BASE + 0x_0012; // 2 W BG0 Y-Offset
|
||||||
|
pub const REG_BG1HOFS: Addr = IO_BASE + 0x_0014; // 2 W BG1 X-Offset
|
||||||
|
pub const REG_BG1VOFS: Addr = IO_BASE + 0x_0016; // 2 W BG1 Y-Offset
|
||||||
|
pub const REG_BG2HOFS: Addr = IO_BASE + 0x_0018; // 2 W BG2 X-Offset
|
||||||
|
pub const REG_BG2VOFS: Addr = IO_BASE + 0x_001A; // 2 W BG2 Y-Offset
|
||||||
|
pub const REG_BG3HOFS: Addr = IO_BASE + 0x_001C; // 2 W BG3 X-Offset
|
||||||
|
pub const REG_BG3VOFS: Addr = IO_BASE + 0x_001E; // 2 W BG3 Y-Offset
|
||||||
|
pub const REG_BG2PA: Addr = IO_BASE + 0x_0020; // 2 W BG2 Rotation/Scaling Parameter A (dx)
|
||||||
|
pub const REG_BG2PB: Addr = IO_BASE + 0x_0022; // 2 W BG2 Rotation/Scaling Parameter B (dmx)
|
||||||
|
pub const REG_BG2PC: Addr = IO_BASE + 0x_0024; // 2 W BG2 Rotation/Scaling Parameter C (dy)
|
||||||
|
pub const REG_BG2PD: Addr = IO_BASE + 0x_0026; // 2 W BG2 Rotation/Scaling Parameter D (dmy)
|
||||||
|
pub const REG_BG2X: Addr = IO_BASE + 0x_0028; // 4 W BG2 Reference Point X-Coordinate
|
||||||
|
pub const REG_BG2Y: Addr = IO_BASE + 0x_002C; // 4 W BG2 Reference Point Y-Coordinate
|
||||||
|
pub const REG_BG3PA: Addr = IO_BASE + 0x_0030; // 2 W BG3 Rotation/Scaling Parameter A (dx)
|
||||||
|
pub const REG_BG3PB: Addr = IO_BASE + 0x_0032; // 2 W BG3 Rotation/Scaling Parameter B (dmx)
|
||||||
|
pub const REG_BG3PC: Addr = IO_BASE + 0x_0034; // 2 W BG3 Rotation/Scaling Parameter C (dy)
|
||||||
|
pub const REG_BG3PD: Addr = IO_BASE + 0x_0036; // 2 W BG3 Rotation/Scaling Parameter D (dmy)
|
||||||
|
pub const REG_BG3X: Addr = IO_BASE + 0x_0038; // 4 W BG3 Reference Point X-Coordinate
|
||||||
|
pub const REG_BG3Y: Addr = IO_BASE + 0x_003C; // 4 W BG3 Reference Point Y-Coordinate
|
||||||
|
pub const REG_WIN0H: Addr = IO_BASE + 0x_0040; // 2 W Window 0 Horizontal Dimensions
|
||||||
|
pub const REG_WIN1H: Addr = IO_BASE + 0x_0042; // 2 W Window 1 Horizontal Dimensions
|
||||||
|
pub const REG_WIN0V: Addr = IO_BASE + 0x_0044; // 2 W Window 0 Vertical Dimensions
|
||||||
|
pub const REG_WIN1V: Addr = IO_BASE + 0x_0046; // 2 W Window 1 Vertical Dimensions
|
||||||
|
pub const REG_WININ: Addr = IO_BASE + 0x_0048; // 2 R/W Inside of Window 0 and 1
|
||||||
|
pub const REG_WINOUT: Addr = IO_BASE + 0x_004A; // 2 R/W Inside of OBJ Window & Outside of Windows
|
||||||
|
pub const REG_MOSAIC: Addr = IO_BASE + 0x_004C; // 2 W Mosaic Size
|
||||||
|
pub const REG_BLDCNT: Addr = IO_BASE + 0x_0050; // 2 R/W Color Special Effects Selection
|
||||||
|
pub const REG_BLDALPHA: Addr = IO_BASE + 0x_0052; // 2 R/W Alpha Blending Coefficients
|
||||||
|
pub const REG_BLDY: Addr = IO_BASE + 0x_0054; // 2 W Brightness (Fade-In/Out) Coefficient
|
||||||
|
// Sound Registers
|
||||||
|
pub const REG_SOUND1CNT_L: Addr = IO_BASE + 0x_0060; // 2 R/W Channel 1 Sweep register (NR10)
|
||||||
|
pub const REG_SOUND1CNT_H: Addr = IO_BASE + 0x_0062; // 2 R/W Channel 1 Duty/Length/Envelope (NR11, NR12)
|
||||||
|
pub const REG_SOUND1CNT_X: Addr = IO_BASE + 0x_0064; // 2 R/W Channel 1 Frequency/Control (NR13, NR14)
|
||||||
|
pub const REG_SOUND2CNT_L: Addr = IO_BASE + 0x_0068; // 2 R/W Channel 2 Duty/Length/Envelope (NR21, NR22)
|
||||||
|
pub const REG_SOUND2CNT_H: Addr = IO_BASE + 0x_006C; // 2 R/W Channel 2 Frequency/Control (NR23, NR24)
|
||||||
|
pub const REG_SOUND3CNT_L: Addr = IO_BASE + 0x_0070; // 2 R/W Channel 3 Stop/Wave RAM select (NR30)
|
||||||
|
pub const REG_SOUND3CNT_H: Addr = IO_BASE + 0x_0072; // 2 R/W Channel 3 Length/Volume (NR31, NR32)
|
||||||
|
pub const REG_SOUND3CNT_X: Addr = IO_BASE + 0x_0074; // 2 R/W Channel 3 Frequency/Control (NR33, NR34)
|
||||||
|
pub const REG_SOUND4CNT_L: Addr = IO_BASE + 0x_0078; // 2 R/W Channel 4 Length/Envelope (NR41, NR42)
|
||||||
|
pub const REG_SOUND4CNT_H: Addr = IO_BASE + 0x_007C; // 2 R/W Channel 4 Frequency/Control (NR43, NR44)
|
||||||
|
pub const REG_SOUNDCNT_L: Addr = IO_BASE + 0x_0080; // 2 R/W Control Stereo/Volume/Enable (NR50, NR51)
|
||||||
|
pub const REG_SOUNDCNT_H: Addr = IO_BASE + 0x_0082; // 2 R/W Control Mixing/DMA Control
|
||||||
|
pub const REG_SOUNDCNT_X: Addr = IO_BASE + 0x_0084; // 2 R/W Control Sound on/off (NR52)
|
||||||
|
pub const REG_SOUNDBIAS: Addr = IO_BASE + 0x_0088; // 2 BIOS Sound PWM Control
|
||||||
|
pub const REG_WAVE_RAM: Addr = IO_BASE + 0x_0090; // Channel 3 Wave Pattern RAM (2 banks!!)
|
||||||
|
pub const REG_FIFO_A: Addr = IO_BASE + 0x_00A0; // 4 W Channel A FIFO, Data 0-3
|
||||||
|
pub const REG_FIFO_B: Addr = IO_BASE + 0x_00A4; // 4 W Channel B FIFO, Data 0-3
|
||||||
|
// DMA Transfer Channels
|
||||||
|
pub const REG_DMA0SAD: Addr = IO_BASE + 0x_00B0; // 4 W DMA 0 Source Address
|
||||||
|
pub const REG_DMA0DAD: Addr = IO_BASE + 0x_00B4; // 4 W DMA 0 Destination Address
|
||||||
|
pub const REG_DMA0CNT_L: Addr = IO_BASE + 0x_00B8; // 2 W DMA 0 Word Count
|
||||||
|
pub const REG_DMA0CNT_H: Addr = IO_BASE + 0x_00BA; // 2 R/W DMA 0 Control
|
||||||
|
pub const REG_DMA1SAD: Addr = IO_BASE + 0x_00BC; // 4 W DMA 1 Source Address
|
||||||
|
pub const REG_DMA1DAD: Addr = IO_BASE + 0x_00C0; // 4 W DMA 1 Destination Address
|
||||||
|
pub const REG_DMA1CNT_L: Addr = IO_BASE + 0x_00C4; // 2 W DMA 1 Word Count
|
||||||
|
pub const REG_DMA1CNT_H: Addr = IO_BASE + 0x_00C6; // 2 R/W DMA 1 Control
|
||||||
|
pub const REG_DMA2SAD: Addr = IO_BASE + 0x_00C8; // 4 W DMA 2 Source Address
|
||||||
|
pub const REG_DMA2DAD: Addr = IO_BASE + 0x_00CC; // 4 W DMA 2 Destination Address
|
||||||
|
pub const REG_DMA2CNT_L: Addr = IO_BASE + 0x_00D0; // 2 W DMA 2 Word Count
|
||||||
|
pub const REG_DMA2CNT_H: Addr = IO_BASE + 0x_00D2; // 2 R/W DMA 2 Control
|
||||||
|
pub const REG_DMA3SAD: Addr = IO_BASE + 0x_00D4; // 4 W DMA 3 Source Address
|
||||||
|
pub const REG_DMA3DAD: Addr = IO_BASE + 0x_00D8; // 4 W DMA 3 Destination Address
|
||||||
|
pub const REG_DMA3CNT_L: Addr = IO_BASE + 0x_00DC; // 2 W DMA 3 Word Count
|
||||||
|
pub const REG_DMA3CNT_H: Addr = IO_BASE + 0x_00DE; // 2 R/W DMA 3 Control
|
||||||
|
// Timer Registers
|
||||||
|
pub const REG_TM0CNT_L: Addr = IO_BASE + 0x_0100; // 2 R/W Timer 0 Counter/Reload
|
||||||
|
pub const REG_TM0CNT_H: Addr = IO_BASE + 0x_0102; // 2 R/W Timer 0 Control
|
||||||
|
pub const REG_TM1CNT_L: Addr = IO_BASE + 0x_0104; // 2 R/W Timer 1 Counter/Reload
|
||||||
|
pub const REG_TM1CNT_H: Addr = IO_BASE + 0x_0106; // 2 R/W Timer 1 Control
|
||||||
|
pub const REG_TM2CNT_L: Addr = IO_BASE + 0x_0108; // 2 R/W Timer 2 Counter/Reload
|
||||||
|
pub const REG_TM2CNT_H: Addr = IO_BASE + 0x_010A; // 2 R/W Timer 2 Control
|
||||||
|
pub const REG_TM3CNT_L: Addr = IO_BASE + 0x_010C; // 2 R/W Timer 3 Counter/Reload
|
||||||
|
pub const REG_TM3CNT_H: Addr = IO_BASE + 0x_010E; // 2 R/W Timer 3 Control
|
||||||
|
// Serial Communication (1)
|
||||||
|
pub const REG_SIODATA32: Addr = IO_BASE + 0x_0120; // 4 R/W SIO Data (Normal-32bit Mode; shared with below)
|
||||||
|
pub const REG_SIOMULTI0: Addr = IO_BASE + 0x_0120; // 2 R/W SIO Data 0 (Parent) (Multi-Player Mode)
|
||||||
|
pub const REG_SIOMULTI1: Addr = IO_BASE + 0x_0122; // 2 R/W SIO Data 1 (1st Child) (Multi-Player Mode)
|
||||||
|
pub const REG_SIOMULTI2: Addr = IO_BASE + 0x_0124; // 2 R/W SIO Data 2 (2nd Child) (Multi-Player Mode)
|
||||||
|
pub const REG_SIOMULTI3: Addr = IO_BASE + 0x_0126; // 2 R/W SIO Data 3 (3rd Child) (Multi-Player Mode)
|
||||||
|
pub const REG_SIOCNT: Addr = IO_BASE + 0x_0128; // 2 R/W SIO Control Register
|
||||||
|
pub const REG_SIOMLT_SEND: Addr = IO_BASE + 0x_012A; // 2 R/W SIO Data (Local of MultiPlayer; shared below)
|
||||||
|
pub const REG_SIODATA8: Addr = IO_BASE + 0x_012A; // 2 R/W SIO Data (Normal-8bit and UART Mode)
|
||||||
|
// Keypad Input
|
||||||
|
pub const REG_KEYINPUT: Addr = IO_BASE + 0x_0130; // 2 R Key Status
|
||||||
|
pub const REG_KEYCNT: Addr = IO_BASE + 0x_0132; // 2 R/W Key Interrupt Control
|
||||||
|
// Serial Communication (2)
|
||||||
|
pub const REG_RCNT: Addr = IO_BASE + 0x_0134; // 2 R/W SIO Mode Select/General Purpose Data
|
||||||
|
pub const REG_IR: Addr = IO_BASE + 0x_0136; // - - Ancient - Infrared Register (Prototypes only)
|
||||||
|
pub const REG_JOYCNT: Addr = IO_BASE + 0x_0140; // 2 R/W SIO JOY Bus Control
|
||||||
|
pub const REG_JOY_RECV: Addr = IO_BASE + 0x_0150; // 4 R/W SIO JOY Bus Receive Data
|
||||||
|
pub const REG_JOY_TRANS: Addr = IO_BASE + 0x_0154; // 4 R/W SIO JOY Bus Transmit Data
|
||||||
|
pub const REG_JOYSTAT: Addr = IO_BASE + 0x_0158; // 2 R/? SIO JOY Bus Receive Status
|
||||||
|
// Interrupt, Waitstate, and Power-Down Control
|
||||||
|
pub const REG_IE: Addr = IO_BASE + 0x_0200; // 2 R/W Interrupt Enable Register
|
||||||
|
pub const REG_IF: Addr = IO_BASE + 0x_0202; // 2 R/W Interrupt Request Flags / IRQ Acknowledge
|
||||||
|
pub const REG_WAITCNT: Addr = IO_BASE + 0x_0204; // 2 R/W Game Pak Waitstate Control
|
||||||
|
pub const REG_IME: Addr = IO_BASE + 0x_0208; // 2 R/W Interrupt Master Enable Register
|
||||||
|
pub const REG_POSTFLG: Addr = IO_BASE + 0x_0300; // 1 R/W Undocumented - Post Boot Flag
|
||||||
|
pub const REG_HALTCNT: Addr = IO_BASE + 0x_0301; // 1 W Undocumented - Power Down Control
|
||||||
|
}
|
||||||
|
|
||||||
|
use consts::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IoRegs {
|
||||||
|
bytes: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IoRegs {
|
||||||
|
fn default() -> IoRegs {
|
||||||
|
let mut ioregs = IoRegs {
|
||||||
|
bytes: vec![0; 4096].into_boxed_slice(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// init default values
|
||||||
|
ioregs.write_reg(REG_DISPCNT, 0x0080);
|
||||||
|
|
||||||
|
ioregs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoRegs {
|
||||||
|
pub fn read_reg(&self, addr: Addr) -> u16 {
|
||||||
|
let result = self
|
||||||
|
.get_bytes(addr - IO_BASE)
|
||||||
|
.read_u16::<LittleEndian>()
|
||||||
|
.unwrap();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_reg(&mut self, addr: Addr, value: u16) {
|
||||||
|
self.get_bytes_mut(addr - IO_BASE)
|
||||||
|
.write_u16::<LittleEndian>(value)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bus for IoRegs {
|
||||||
|
fn read_32(&self, addr: Addr) -> u32 {
|
||||||
|
self.get_bytes(addr).read_u32::<LittleEndian>().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_16(&self, addr: Addr) -> u16 {
|
||||||
|
self.read_reg(IO_BASE + addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_8(&self, addr: Addr) -> u8 {
|
||||||
|
self.read_reg(IO_BASE + addr) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_32(&mut self, addr: Addr, value: u32) -> Result<(), io::Error> {
|
||||||
|
self.get_bytes_mut(addr).write_u32::<LittleEndian>(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_16(&mut self, addr: Addr, value: u16) -> Result<(), io::Error> {
|
||||||
|
self.write_reg(IO_BASE + addr, value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error> {
|
||||||
|
let new_value = self.read_reg(IO_BASE + addr) & 0xff00 | (value as u16);
|
||||||
|
self.write_reg(IO_BASE + addr, new_value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a slice of bytes
|
||||||
|
fn get_bytes(&self, addr: Addr) -> &[u8] {
|
||||||
|
&self.bytes[addr as usize..]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a mutable slice of bytes
|
||||||
|
fn get_bytes_mut(&mut self, addr: Addr) -> &mut [u8] {
|
||||||
|
&mut self.bytes[addr as usize..]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns the number of cycles needed for this memory access
|
||||||
|
fn get_cycles(&self, _addr: Addr, _access: MemoryAccess) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
110
src/lcd.rs
Normal file
110
src/lcd.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use super::ioregs::consts::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::bit::BitIndex;
|
||||||
|
use crate::num::FromPrimitive;
|
||||||
|
|
||||||
|
#[derive(Debug, Primitive)]
|
||||||
|
enum BGMode {
|
||||||
|
Mode0 = 0,
|
||||||
|
Mode1 = 1,
|
||||||
|
Mode2 = 2,
|
||||||
|
Mode3 = 3,
|
||||||
|
Mode4 = 4,
|
||||||
|
Mode5 = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DisplayControl {
|
||||||
|
bg_mode: BGMode,
|
||||||
|
display_frame: usize,
|
||||||
|
hblank_interval_free: bool,
|
||||||
|
obj_character_vram_mapping: bool, // true - 1 dimentional, false - 2 dimentional
|
||||||
|
forced_blank: bool,
|
||||||
|
disp_bg0: bool,
|
||||||
|
disp_bg1: bool,
|
||||||
|
disp_bg2: bool,
|
||||||
|
disp_bg3: bool,
|
||||||
|
disp_obj: bool,
|
||||||
|
disp_window0: bool,
|
||||||
|
disp_window1: bool,
|
||||||
|
disp_obj_window: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for DisplayControl {
|
||||||
|
fn from(v: u16) -> Self {
|
||||||
|
DisplayControl {
|
||||||
|
bg_mode: BGMode::from_u8(v.bit_range(0..2) as u8).unwrap(),
|
||||||
|
// bit 3 is unused
|
||||||
|
display_frame: v.bit(4) as usize,
|
||||||
|
hblank_interval_free: v.bit(5),
|
||||||
|
obj_character_vram_mapping: v.bit(6),
|
||||||
|
forced_blank: v.bit(7),
|
||||||
|
disp_bg0: v.bit(8),
|
||||||
|
disp_bg1: v.bit(9),
|
||||||
|
disp_bg2: v.bit(10),
|
||||||
|
disp_bg3: v.bit(11),
|
||||||
|
disp_obj: v.bit(12),
|
||||||
|
disp_window0: v.bit(13),
|
||||||
|
disp_window1: v.bit(14),
|
||||||
|
disp_obj_window: v.bit(15),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DisplayStatus {
|
||||||
|
vblank_flag: bool,
|
||||||
|
hblank_flag: bool,
|
||||||
|
vcount_flag: bool,
|
||||||
|
vblank_irq_enable: bool,
|
||||||
|
hblank_irq_enable: bool,
|
||||||
|
vcount_irq_enable: bool,
|
||||||
|
vcount_setting: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for DisplayStatus {
|
||||||
|
fn from(v: u16) -> Self {
|
||||||
|
DisplayStatus {
|
||||||
|
vblank_flag: v.bit(0),
|
||||||
|
hblank_flag: v.bit(1),
|
||||||
|
vcount_flag: v.bit(2),
|
||||||
|
vblank_irq_enable: v.bit(3),
|
||||||
|
hblank_irq_enable: v.bit(4),
|
||||||
|
vcount_irq_enable: v.bit(5),
|
||||||
|
// bits 6-7 are unused in GBA
|
||||||
|
vcount_setting: v.bit_range(8..15) as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Lcd {
|
||||||
|
cycles: usize,
|
||||||
|
current_scanline: usize, // VCOUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lcd {
|
||||||
|
const DISPLAY_WIDTH: usize = 240;
|
||||||
|
const DISPLAY_HEIGHT: usize = 160;
|
||||||
|
|
||||||
|
pub fn new() -> Lcd {
|
||||||
|
Lcd {
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmuIoDev for Lcd {
|
||||||
|
fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> Option<Interrupt> {
|
||||||
|
self.cycles += cycles;
|
||||||
|
|
||||||
|
let mut result = None;
|
||||||
|
let mut dispcnt = DisplayControl::from(sysbus.ioregs.read_reg(REG_DISPCNT));
|
||||||
|
let mut dispstat = DisplayStatus::from(sysbus.ioregs.read_reg(REG_DISPSTAT));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
39
src/lib.rs
39
src/lib.rs
|
@ -19,4 +19,43 @@ pub mod cartridge;
|
||||||
pub mod debugger;
|
pub mod debugger;
|
||||||
pub mod disass;
|
pub mod disass;
|
||||||
pub mod sysbus;
|
pub mod sysbus;
|
||||||
|
pub use sysbus::SysBus;
|
||||||
|
pub mod interrupt;
|
||||||
|
pub mod ioregs;
|
||||||
|
pub use interrupt::Interrupt;
|
||||||
|
pub mod gba;
|
||||||
|
pub use gba::GameBoyAdvance;
|
||||||
|
pub mod lcd;
|
||||||
|
pub mod palette;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
pub trait EmuIoDev {
|
||||||
|
fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> Option<Interrupt>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GBAError {
|
||||||
|
IO(::std::io::Error),
|
||||||
|
CpuError(arm7tdmi::CpuError),
|
||||||
|
DebuggerError(debugger::DebuggerError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type GBAResult<T> = Result<T, GBAError>;
|
||||||
|
|
||||||
|
impl From<::std::io::Error> for GBAError {
|
||||||
|
fn from(err: ::std::io::Error) -> GBAError {
|
||||||
|
GBAError::IO(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<arm7tdmi::CpuError> for GBAError {
|
||||||
|
fn from(err: arm7tdmi::CpuError) -> GBAError {
|
||||||
|
GBAError::CpuError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<debugger::DebuggerError> for GBAError {
|
||||||
|
fn from(err: debugger::DebuggerError) -> GBAError {
|
||||||
|
GBAError::DebuggerError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
59
src/palette.rs
Normal file
59
src/palette.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Rgb15 {
|
||||||
|
pub r: u8,
|
||||||
|
pub g: u8,
|
||||||
|
pub b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for Rgb15 {
|
||||||
|
fn from(v: u16) -> Rgb15 {
|
||||||
|
use bit::BitIndex;
|
||||||
|
Rgb15 {
|
||||||
|
r: v.bit_range(0..5) as u8,
|
||||||
|
g: v.bit_range(5..10) as u8,
|
||||||
|
b: v.bit_range(10..15) as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Rgb15 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Rgb15({:#x},{:#x},{:#x})", self.r, self.g, self.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rgb15 {
|
||||||
|
/// Convert 15-bit high color to a 24-bit true color.
|
||||||
|
pub fn get_rgb24(&self) -> (u8, u8, u8) {
|
||||||
|
(self.r << 3, self.g << 3, self.b << 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Palette {
|
||||||
|
pub bg_colors: [Rgb15; 256],
|
||||||
|
pub fg_colors: [Rgb15; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u8]> for Palette {
|
||||||
|
fn from(bytes: &[u8]) -> Palette {
|
||||||
|
let mut rdr = Cursor::new(bytes);
|
||||||
|
|
||||||
|
let mut bg_colors: [Rgb15; 256] = [0.into(); 256];
|
||||||
|
for i in 0..256 {
|
||||||
|
bg_colors[i] = rdr.read_u16::<LittleEndian>().unwrap().into();
|
||||||
|
}
|
||||||
|
let mut fg_colors: [Rgb15; 256] = [0.into(); 256];
|
||||||
|
for i in 0..256 {
|
||||||
|
fg_colors[i] = rdr.read_u16::<LittleEndian>().unwrap().into();
|
||||||
|
}
|
||||||
|
Palette {
|
||||||
|
bg_colors,
|
||||||
|
fg_colors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use crate::cartridge::Cartridge;
|
use super::{cartridge::Cartridge, ioregs::IoRegs};
|
||||||
|
|
||||||
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
||||||
use super::arm7tdmi::Addr;
|
use super::arm7tdmi::Addr;
|
||||||
|
@ -111,7 +111,7 @@ pub struct SysBus {
|
||||||
onboard_work_ram: BoxedMemory,
|
onboard_work_ram: BoxedMemory,
|
||||||
internal_work_ram: BoxedMemory,
|
internal_work_ram: BoxedMemory,
|
||||||
/// Currently model the IOMem as regular buffer, later make it into something more sophisticated.
|
/// Currently model the IOMem as regular buffer, later make it into something more sophisticated.
|
||||||
ioregs: BoxedMemory,
|
pub ioregs: IoRegs,
|
||||||
palette_ram: BoxedMemory,
|
palette_ram: BoxedMemory,
|
||||||
vram: BoxedMemory,
|
vram: BoxedMemory,
|
||||||
oam: BoxedMemory,
|
oam: BoxedMemory,
|
||||||
|
@ -128,7 +128,7 @@ impl SysBus {
|
||||||
WaitState::new(3, 3, 6),
|
WaitState::new(3, 3, 6),
|
||||||
),
|
),
|
||||||
internal_work_ram: BoxedMemory::new(vec![0; INTERNAL_RAM].into_boxed_slice()),
|
internal_work_ram: BoxedMemory::new(vec![0; INTERNAL_RAM].into_boxed_slice()),
|
||||||
ioregs: BoxedMemory::new(vec![0; 1024].into_boxed_slice()),
|
ioregs: IoRegs::default(),
|
||||||
palette_ram: BoxedMemory::new_with_waitstate(
|
palette_ram: BoxedMemory::new_with_waitstate(
|
||||||
vec![0; PALETTE_RAM_SIZE].into_boxed_slice(),
|
vec![0; PALETTE_RAM_SIZE].into_boxed_slice(),
|
||||||
WaitState::new(1, 1, 2),
|
WaitState::new(1, 1, 2),
|
||||||
|
|
Reference in a new issue