From 3a1d5c10ce111a69591ed745fcbffd193c3b78c3 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Sat, 9 Nov 2019 00:55:09 +0200 Subject: [PATCH] Fix many bugs, refactor many things.. Passing: Armwrestler, cpu_test by Dead_Body Former-commit-id: 80d815d110c5341515dd01c476a0d7e25ecb66a8 --- Cargo.toml | 2 +- README.md | 19 +- img/tonc_bigmap_demo.gif.REMOVED.git-id | 1 + src/bin/main.rs | 24 +- src/core/arm7tdmi/alu.rs | 77 ++--- src/core/arm7tdmi/arm/exec.rs | 190 ++++++++---- src/core/arm7tdmi/cpu.rs | 15 +- src/core/arm7tdmi/thumb/display.rs | 45 +-- src/core/arm7tdmi/thumb/exec.rs | 279 ++++++++++------- src/core/arm7tdmi/thumb/mod.rs | 86 +++--- src/core/gpu/mod.rs | 2 +- src/core/ioregs.rs | 386 +++++++++++++++--------- src/core/mod.rs | 2 +- src/core/sysbus.rs | 130 ++++---- src/core/timer.rs | 16 +- src/debugger/command.rs | 10 +- src/debugger/mod.rs | 6 +- src/debugger/tile_view.rs | 2 +- 18 files changed, 751 insertions(+), 541 deletions(-) create mode 100644 img/tonc_bigmap_demo.gif.REMOVED.git-id diff --git a/Cargo.toml b/Cargo.toml index ae40fcc..a9d5eb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,5 +24,5 @@ bitflags = "1.1.0" zip = "0.5.3" [profile.dev] -opt-level = 1 +opt-level = 0 debug = true \ No newline at end of file diff --git a/README.md b/README.md index 39e6473..0a57c88 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,15 @@ [![Build Status](https://travis-ci.com/michelhe/rustboyadvance-ng.svg?branch=master)](https://travis-ci.com/michelhe/rustboyadvance-ng) -RustBoyAdvance-NG aims to be a Nintendo GameBoy Advance emulator and debugger, written in the rust programming language. +RustBoyAdvance-NG Nintendo GameBoy Advance emulator and debugger, written in the rust programming language. -RustBoyAdvance-NG currently has implemented - - Dumbed-down ARM/THUMB mode disassembling - - Some ARM/THUMB instruction are implemented, but not all of them. - - A neat debugger REPL +Currently passing armwrestler tests, and displays some of TONC's Demos. -But the way for full emulation is way far ahead, because most of the ARM/THUMB instructions are not yet implemented. +![TONC bigmap.gba demo ](img/tonc_bigmap_demo.gif) # Using the REPL You need to have rust installed, and somehow legally obtain a gba bios binary. -Currently to test the debugger, any binary file containing arm mode instructions will do. ```bash $ cargo run -- debug @@ -41,6 +37,11 @@ You know what they say, *third time's a charm*. - [GBATEK](http://problemkaputt.de/gbatek.htm) A single webpage written by *no$gba* developer Martin Korth. This page has pretty much everything. Seriously, it's the best. -- [NanoboyAdvance](https://github.com/fleroviux/NanoboyAdvance)] +- [TONC](https://www.coranac.com/tonc/text/) + A comprehensive GBA dev guide that I used a-lot in order to understand the GBA system. + Comes with neat demo roms that really helped me during development and debugging. +- [NanoboyAdvance](https://github.com/fleroviux/NanoboyAdvance) A GameBoy Advance emulator written in C++17 by a nice person called fleroviux. - I've used this emulator to search for a tough bug in mine. \ No newline at end of file + I've used this emulator to search for a tough bug in mine. +- [Eggvance](https://github.com/jsmolka/eggvance/tree/master/tests) + A GameBoy Advance emulator written in C++, with really useful CPU test roms. \ No newline at end of file diff --git a/img/tonc_bigmap_demo.gif.REMOVED.git-id b/img/tonc_bigmap_demo.gif.REMOVED.git-id new file mode 100644 index 0000000..ef9f1af --- /dev/null +++ b/img/tonc_bigmap_demo.gif.REMOVED.git-id @@ -0,0 +1 @@ +de93ded308a68b66c3fe94fe4933737d60a6aba9 \ No newline at end of file diff --git a/src/bin/main.rs b/src/bin/main.rs index 08a3c14..2eedfdb 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,8 +1,8 @@ -use std::time; -use std::fs::File; -use std::path::Path; use std::ffi::OsStr; +use std::fs::File; use std::io::prelude::*; +use std::path::Path; +use std::time; #[macro_use] extern crate clap; @@ -20,13 +20,11 @@ use rustboyadvance_ng::core::{GBAError, GBAResult, GameBoyAdvance}; use rustboyadvance_ng::debugger::Debugger; use rustboyadvance_ng::util::read_bin_file; - fn load_rom(path: &str) -> GBAResult> { if path.ends_with(".zip") { let zipfile = File::open(path)?; let mut archive = zip::ZipArchive::new(zipfile)?; - for i in 0..archive.len() - { + for i in 0..archive.len() { let mut file = archive.by_index(i)?; if file.name().ends_with(".gba") { let mut buf = Vec::new(); @@ -88,11 +86,15 @@ fn run_emulator(matches: &ArgMatches) -> GBAResult<()> { loop { let start_time = time::Instant::now(); gba.frame(); - let time_passed = start_time.elapsed(); - if time_passed <= frame_time { - if !no_framerate_limit { - ::std::thread::sleep(frame_time - time_passed); - } + if !no_framerate_limit { + let time_passed = start_time.elapsed(); + let delay = frame_time.checked_sub(time_passed); + match delay { + None => {} + Some(delay) => { + ::std::thread::sleep(delay); + } + }; } } } diff --git a/src/core/arm7tdmi/alu.rs b/src/core/arm7tdmi/alu.rs index 25e54b2..280f5aa 100644 --- a/src/core/arm7tdmi/alu.rs +++ b/src/core/arm7tdmi/alu.rs @@ -84,6 +84,20 @@ impl BarrelShifterValue { } None } + pub fn shifted_register( + reg: usize, + shift_by: ShiftRegisterBy, + bs_op: BarrelShiftOpCode, + added: Option, + ) -> BarrelShifterValue { + let shft_reg = ShiftedRegister { + reg, + shift_by, + bs_op, + added, + }; + BarrelShifterValue::ShiftedRegister(shft_reg) + } } impl Core { @@ -272,7 +286,7 @@ impl Core { } } - fn alu_sub_flags(a: i32, b: i32, carry: &mut bool, overflow: &mut bool) -> i32 { + pub fn alu_sub_flags(a: i32, b: i32, carry: &mut bool, overflow: &mut bool) -> i32 { let res = a.wrapping_sub(b); *carry = (b as u32) <= (a as u32); let (_, would_overflow) = a.overflowing_sub(b); @@ -280,7 +294,7 @@ impl Core { res } - fn alu_add_flags(a: i32, b: i32, carry: &mut bool, overflow: &mut bool) -> i32 { + pub fn alu_add_flags(a: i32, b: i32, carry: &mut bool, overflow: &mut bool) -> i32 { let res = a.wrapping_add(b) as u32; *carry = res < a as u32 || res < b as u32; let (_, would_overflow) = a.overflowing_add(b); @@ -288,63 +302,10 @@ impl Core { res as i32 } - #[allow(non_snake_case)] - pub fn alu(&mut self, opcode: AluOpCode, op1: i32, op2: i32) -> i32 { - use AluOpCode::*; - let C = self.cpsr.C() as i32; - - match opcode { - AND => op1 & op2, - EOR => op1 ^ op2, - SUB => op1.wrapping_sub(op2), - RSB => op2.wrapping_sub(op1), - ADD => op1.wrapping_add(op2), - ADC => op1.wrapping_add(op2).wrapping_add(C), - SBC => op1.wrapping_sub(op2.wrapping_add(1 - C)), - RSC => op2.wrapping_sub(op1.wrapping_add(1 - C)), - ORR => op1 | op2, - MOV => op2, - BIC => op1 & (!op2), - MVN => !op2, - _ => panic!("{} should be a PSR transfer", opcode), - } - } - - #[allow(non_snake_case)] - pub fn alu_flags(&mut self, opcode: AluOpCode, op1: i32, op2: i32) -> Option { - use AluOpCode::*; - let mut carry = self.bs_carry_out; - let C = self.cpsr.C() as i32; - let mut overflow = self.cpsr.V(); - - let result = match opcode { - AND | TST => op1 & op2, - EOR | TEQ => op1 ^ op2, - SUB | CMP => Self::alu_sub_flags(op1, op2, &mut carry, &mut overflow), - RSB => Self::alu_sub_flags(op2, op1, &mut carry, &mut overflow), - ADD | CMN => Self::alu_add_flags(op1, op2, &mut carry, &mut overflow), - ADC => Self::alu_add_flags(op1, op2.wrapping_add(C), &mut carry, &mut overflow), - SBC => Self::alu_sub_flags(op1, op2.wrapping_add(1 - C), &mut carry, &mut overflow), - RSC => Self::alu_sub_flags(op2, op1.wrapping_add(1 - C), &mut carry, &mut overflow), - ORR => op1 | op2, - MOV => op2, - BIC => op1 & (!op2), - MVN => !op2, - }; - + pub fn alu_update_flags(&mut self, result: i32, is_arithmetic: bool, c: bool, v: bool) { self.cpsr.set_N(result < 0); self.cpsr.set_Z(result == 0); - if opcode.is_arithmetic() { - self.cpsr.set_C(carry); - self.cpsr.set_V(overflow); - } else { - self.cpsr.set_C(self.bs_carry_out) - } - - if opcode.is_setting_flags() { - None - } else { - Some(result) - } + self.cpsr.set_C(c); + self.cpsr.set_V(v); } } diff --git a/src/core/arm7tdmi/arm/exec.rs b/src/core/arm7tdmi/arm/exec.rs index 4c101b0..2379b7f 100644 --- a/src/core/arm7tdmi/arm/exec.rs +++ b/src/core/arm7tdmi/arm/exec.rs @@ -161,8 +161,8 @@ impl Core { /// Add x=1I cycles if Op2 shifted-by-register. Add y=1S+1N cycles if Rd=R15. fn exec_data_processing(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult { self.S_cycle32(sb, self.pc); - let op1 = if insn.rn() == REG_PC { - self.pc as i32 + let mut op1 = if insn.rn() == REG_PC { + (insn.pc + 8) as i32 } else { self.get_reg(insn.rn()) as i32 }; @@ -171,6 +171,14 @@ impl Core { let opcode = insn.opcode().unwrap(); let op2 = insn.operand2()?; + match op2 { + BarrelShifterValue::ShiftedRegister(_) => { + if insn.rn() == REG_PC { + op1 += 4; + } + } + _ => {} + } let op2 = self.decode_operand2(op2, s_flag)? as i32; if !s_flag { @@ -185,10 +193,49 @@ impl Core { let rd = insn.rd(); + use AluOpCode::*; + let C = self.cpsr.C() as i32; let alu_res = if s_flag { - self.alu_flags(opcode, op1, op2) + let mut carry = self.bs_carry_out; + let mut overflow = self.cpsr.V(); + let result = match opcode { + AND | TST => op1 & op2, + EOR | TEQ => op1 ^ op2, + SUB | CMP => Self::alu_sub_flags(op1, op2, &mut carry, &mut overflow), + RSB => Self::alu_sub_flags(op2, op1, &mut carry, &mut overflow), + ADD | CMN => Self::alu_add_flags(op1, op2, &mut carry, &mut overflow), + ADC => Self::alu_add_flags(op1, op2.wrapping_add(C), &mut carry, &mut overflow), + SBC => Self::alu_sub_flags(op1, op2.wrapping_add(1 - C), &mut carry, &mut overflow), + RSC => Self::alu_sub_flags(op2, op1.wrapping_add(1 - C), &mut carry, &mut overflow), + ORR => op1 | op2, + MOV => op2, + BIC => op1 & (!op2), + MVN => !op2, + }; + + self.alu_update_flags(result, opcode.is_arithmetic(), carry, overflow); + + if opcode.is_setting_flags() { + None + } else { + Some(result) + } } else { - Some(self.alu(opcode, op1, op2)) + Some(match opcode { + AND => op1 & op2, + EOR => op1 ^ op2, + SUB => op1.wrapping_sub(op2), + RSB => op2.wrapping_sub(op1), + ADD => op1.wrapping_add(op2), + ADC => op1.wrapping_add(op2).wrapping_add(C), + SBC => op1.wrapping_sub(op2.wrapping_add(1 - C)), + RSC => op2.wrapping_sub(op1.wrapping_add(1 - C)), + ORR => op1 | op2, + MOV => op2, + BIC => op1 & (!op2), + MVN => !op2, + _ => panic!("{} should be a PSR transfer", opcode), + }) }; if let Some(result) = alu_res { @@ -252,10 +299,10 @@ impl Core { }; if insn.transfer_size() == 1 { self.N_cycle8(sb, addr); - sb.write_8(addr, value as u8); + self.write_8(addr, value as u8, sb); } else { self.N_cycle32(sb, addr); - sb.write_32(addr & !0x3, value); + self.write_32(addr & !0x3, value, sb); }; self.N_cycle32(sb, self.pc); } @@ -322,7 +369,7 @@ impl Core { match insn.halfword_data_transfer_type().unwrap() { ArmHalfwordTransferType::UnsignedHalfwords => { self.N_cycle32(sb, addr); - sb.write_16(addr, value as u16); + self.write_16(addr, value as u16, sb); self.N_cycle32(sb, self.pc); } _ => panic!("invalid HS flags for L=0"), @@ -369,71 +416,82 @@ impl Core { let psr_transfer = psr_user_flag & is_load & rlist.bit(REG_PC); - if is_load { - self.add_cycle(); - self.N_cycle32(sb, self.pc); - for r in 0..16 { - let r = if ascending { r } else { 15 - r }; - if rlist.bit(r) { - if r == rn { - writeback = false; - } - if full { - addr = addr.wrapping_add(step); - } - - let val = sb.read_32(addr as Addr); - self.S_cycle32(sb, self.pc); - if user_bank_transfer { - self.set_reg_user(r, val); - } else { - self.set_reg(r, val); - } - - if r == REG_PC { - if psr_transfer { - self.transfer_spsr_mode(); + if rlist != 0 { + if is_load { + self.add_cycle(); + self.N_cycle32(sb, self.pc); + for r in 0..16 { + let r = if ascending { r } else { 15 - r }; + if rlist.bit(r) { + if r == rn { + writeback = false; + } + if full { + addr = addr.wrapping_add(step); } - self.flush_pipeline(sb); - } - if !full { - addr = addr.wrapping_add(step); + let val = sb.read_32(addr as Addr); + self.S_cycle32(sb, self.pc); + if user_bank_transfer { + self.set_reg_user(r, val); + } else { + self.set_reg(r, val); + } + + if r == REG_PC { + if psr_transfer { + self.transfer_spsr_mode(); + } + self.flush_pipeline(sb); + } + + if !full { + addr = addr.wrapping_add(step); + } } } + } else { + let mut first = true; + for r in 0..16 { + let r = if ascending { r } else { 15 - r }; + if rlist.bit(r) { + if full { + addr = addr.wrapping_add(step); + } + + let val = if r == REG_PC { + insn.pc + 12 + } else { + if user_bank_transfer { + self.get_reg_user(r) + } else { + self.get_reg(r) + } + }; + if first { + self.N_cycle32(sb, addr as u32); + first = false; + } else { + self.S_cycle32(sb, addr as u32); + } + self.write_32(addr as Addr, val, sb); + + if !full { + addr = addr.wrapping_add(step); + } + } + } + self.N_cycle32(sb, self.pc); } } else { - let mut first = true; - for r in 0..16 { - let r = if ascending { r } else { 15 - r }; - if rlist.bit(r) { - if full { - addr = addr.wrapping_add(step); - } - - let val = if r == REG_PC { - insn.pc + 12 - } else { - if user_bank_transfer { - self.get_reg_user(r) - } else { - self.get_reg(r) - } - }; - if first { - self.N_cycle32(sb, addr as u32); - first = false; - } else { - self.S_cycle32(sb, addr as u32); - } - sb.write_32(addr as Addr, val); - - if !full { - addr = addr.wrapping_add(step); - } - } + if is_load { + let val = self.ldr_word(addr as u32, sb); + self.set_reg(REG_PC, val & !3); + self.flush_pipeline(sb); + } else { + self.write_32(addr as u32, self.pc + 4, sb); } - self.N_cycle32(sb, self.pc); + addr = addr.wrapping_add(step * 0x10); } if writeback { @@ -456,7 +514,7 @@ impl Core { let op1 = self.get_reg(rm) as i32; let op2 = self.get_reg(rs) as i32; - let mut result = (op1 * op2) as u32; + let mut result = op1.wrapping_mul(op2) as u32; if insn.accumulate_flag() { result = result.wrapping_add(self.get_reg(rn)); diff --git a/src/core/arm7tdmi/cpu.rs b/src/core/arm7tdmi/cpu.rs index 7f8072b..4b91e73 100644 --- a/src/core/arm7tdmi/cpu.rs +++ b/src/core/arm7tdmi/cpu.rs @@ -54,6 +54,7 @@ pub struct Core { gpr_previous: [u32; 15], memreq: Addr, + pub breakpoints: Vec, pub verbose: bool, } @@ -74,7 +75,7 @@ impl Core { pub fn get_reg(&self, r: usize) -> u32 { match r { - 0...14 => self.gpr[r], + 0..=14 => self.gpr[r], 15 => self.pc, _ => panic!("invalid register {}", r), } @@ -124,6 +125,18 @@ impl Core { } } + pub fn write_32(&mut self, addr: Addr, value: u32, bus: &mut SysBus) { + bus.write_32(addr & !0x3, value); + } + + pub fn write_16(&mut self, addr: Addr, value: u16, bus: &mut SysBus) { + bus.write_16(addr & !0x1, value); + } + + pub fn write_8(&mut self, addr: Addr, value: u8, bus: &mut SysBus) { + bus.write_8(addr, value); + } + /// Helper function for "ldr" instruction that handles misaligned addresses pub fn ldr_word(&mut self, addr: Addr, bus: &SysBus) -> u32 { if addr & 0x3 != 0 { diff --git a/src/core/arm7tdmi/thumb/display.rs b/src/core/arm7tdmi/thumb/display.rs index 1a20011..5c6c08c 100644 --- a/src/core/arm7tdmi/thumb/display.rs +++ b/src/core/arm7tdmi/thumb/display.rs @@ -27,27 +27,11 @@ impl ThumbInstruction { ) } - fn fmt_thumb_mul(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "mul\t{Rd}, {Rs}", - Rd = reg_string(self.rd()), - Rs = reg_string(self.rs()) - ) - } - fn fmt_thumb_alu_ops(&self, f: &mut fmt::Formatter) -> fmt::Result { - let (op, shft) = self.alu_opcode(); - if let Some(BarrelShifterValue::ShiftedRegister(x)) = shft { - write!(f, "{}", x.bs_op)?; - } else if op == AluOpCode::RSB { - write!(f, "neg")?; - } else { - write!(f, "{}", op)?; - } write!( f, - "\t{Rd}, {Rs}", + "{op}\t{Rd}, {Rs}", + op = self.format4_alu_op(), Rd = reg_string(self.rd()), Rs = reg_string(self.rs()) ) @@ -290,7 +274,6 @@ impl fmt::Display for ThumbInstruction { ThumbFormat::MoveShiftedReg => self.fmt_thumb_move_shifted_reg(f), ThumbFormat::AddSub => self.fmt_thumb_add_sub(f), ThumbFormat::DataProcessImm => self.fmt_thumb_data_process_imm(f), - ThumbFormat::Mul => self.fmt_thumb_mul(f), ThumbFormat::AluOps => self.fmt_thumb_alu_ops(f), ThumbFormat::HiRegOpOrBranchExchange => self.fmt_thumb_high_reg_op_or_bx(f), ThumbFormat::LdrPc => self.fmt_thumb_ldr_pc(f), @@ -332,3 +315,27 @@ impl fmt::Display for OpFormat5 { } } } + +impl fmt::Display for ThumbAluOps { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ThumbAluOps::*; + match self { + AND => write!(f, "and"), + EOR => write!(f, "eor"), + LSL => write!(f, "lsl"), + LSR => write!(f, "lsr"), + ASR => write!(f, "asr"), + ADC => write!(f, "adc"), + SBC => write!(f, "sbc"), + ROR => write!(f, "ror"), + TST => write!(f, "tst"), + NEG => write!(f, "neg"), + CMP => write!(f, "cmp"), + CMN => write!(f, "cmn"), + ORR => write!(f, "orr"), + MUL => write!(f, "mul"), + BIC => write!(f, "bic"), + MVN => write!(f, "mvn"), + } + } +} diff --git a/src/core/arm7tdmi/thumb/exec.rs b/src/core/arm7tdmi/thumb/exec.rs index 20f33b5..69862b4 100644 --- a/src/core/arm7tdmi/thumb/exec.rs +++ b/src/core/arm7tdmi/thumb/exec.rs @@ -1,18 +1,20 @@ +use crate::core::arm7tdmi::alu::AluOpCode; use crate::core::arm7tdmi::bus::Bus; use crate::core::arm7tdmi::cpu::{Core, CpuExecResult}; -use crate::core::arm7tdmi::alu::AluOpCode; use crate::core::arm7tdmi::*; use crate::core::sysbus::SysBus; +use crate::bit::BitIndex; + use super::*; fn push(cpu: &mut Core, bus: &mut SysBus, r: usize) { cpu.gpr[REG_SP] -= 4; - let stack_addr = cpu.gpr[REG_SP]; + let stack_addr = cpu.gpr[REG_SP] & !3; bus.write_32(stack_addr, cpu.get_reg(r)) } fn pop(cpu: &mut Core, bus: &mut SysBus, r: usize) { - let stack_addr = cpu.gpr[REG_SP]; - let val = bus.read_32(stack_addr); + let stack_addr = cpu.gpr[REG_SP] & !3; + let val = cpu.ldr_word(stack_addr, bus); cpu.set_reg(r, val); cpu.gpr[REG_SP] = stack_addr + 4; } @@ -31,14 +33,10 @@ impl Core { added: None, }) .unwrap() as i32; - self.cpsr.set_C(self.bs_carry_out); - let rd = insn.rd(); - let op1 = self.get_reg(rd) as i32; - let result = self.alu_flags(AluOpCode::MOV, op1, op2); - if let Some(result) = result { - self.set_reg(rd, result as u32); - } + self.set_reg(insn.rd(), op2 as u32); + self.alu_update_flags(op2, false, self.bs_carry_out, self.cpsr.V()); + self.S_cycle16(sb, self.pc + 2); Ok(()) } @@ -50,16 +48,17 @@ impl Core { } else { self.get_reg(insn.rn()) as i32 }; - let arm_alu_op = if insn.is_subtract() { - AluOpCode::SUB - } else { - AluOpCode::ADD - }; - let result = self.alu_flags(arm_alu_op, op1, op2); - if let Some(result) = result { - self.set_reg(insn.rd(), result as u32); - } + let mut carry = self.cpsr.C(); + let mut overflow = self.cpsr.V(); + let result = if insn.is_subtract() { + Self::alu_sub_flags(op1, op2, &mut carry, &mut overflow) + } else { + Self::alu_add_flags(op1, op2, &mut carry, &mut overflow) + }; + self.alu_update_flags(result, true, carry, overflow); + self.set_reg(insn.rd(), result as u32); + self.S_cycle16(sb, self.pc + 2); Ok(()) } @@ -69,49 +68,82 @@ impl Core { sb: &mut SysBus, insn: ThumbInstruction, ) -> CpuExecResult { - let arm_alu_op: AluOpCode = insn.format3_op().into(); + use OpFormat3::*; + let op = insn.format3_op(); let op1 = self.get_reg(insn.rd()) as i32; - let op2 = ((insn.raw & 0xff) as i8) as u8 as i32; - let result = self.alu_flags(arm_alu_op, op1, op2); - if let Some(result) = result { + let op2_imm = (insn.raw & 0xff) as i32; + let mut carry = self.cpsr.C(); + let mut overflow = self.cpsr.V(); + let result = match op { + MOV => op2_imm, + CMP | SUB => Self::alu_sub_flags(op1, op2_imm, &mut carry, &mut overflow), + ADD => Self::alu_add_flags(op1, op2_imm, &mut carry, &mut overflow), + }; + let arithmetic = op == ADD || op == SUB; + self.alu_update_flags(result, arithmetic, carry, overflow); + if op != CMP { self.set_reg(insn.rd(), result as u32); } self.S_cycle16(sb, self.pc + 2); Ok(()) } - fn exec_thumb_mul(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult { - let op1 = self.get_reg(insn.rd()) as i32; - let op2 = self.get_reg(insn.rs()) as i32; - let m = self.get_required_multipiler_array_cycles(op2); - for _ in 0..m { - self.add_cycle(); - } - let result = op1.wrapping_mul(op2) as u32; - self.cpsr.set_N((result as i32) < 0); - self.cpsr.set_Z(result == 0); - self.gpr[insn.rd()] = result; - self.S_cycle16(sb, self.pc + 2); - Ok(()) - } - fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult { let rd = insn.rd(); + let rs = insn.rs(); + let dst = self.get_reg(rd) as i32; + let src = self.get_reg(rs) as i32; - let (arm_alu_op, shft) = insn.alu_opcode(); - let op1 = if arm_alu_op == AluOpCode::RSB { - self.get_reg(insn.rs()) as i32 - } else { - self.get_reg(rd) as i32 - }; - let op2 = if let Some(shft) = shft { - self.get_barrel_shifted_value(shft) - } else { - self.get_reg(insn.rs()) as i32 - }; + let mut carry = self.cpsr.C(); + let c = self.cpsr.C() as i32; + let mut overflow = self.cpsr.V(); - let result = self.alu_flags(arm_alu_op, op1, op2); - if let Some(result) = result { + use ThumbAluOps::*; + let op = insn.format4_alu_op(); + let result = match op { + AND | TST => dst & src, + EOR => dst ^ src, + LSL | LSR | ASR | ROR => { + // TODO optimize this second match, keeping it here for code clearity + let bs_op = match op { + LSL => BarrelShiftOpCode::LSL, + LSR => BarrelShiftOpCode::LSR, + ASR => BarrelShiftOpCode::ASR, + ROR => BarrelShiftOpCode::ROR, + _ => unreachable!(), + }; + let shft = BarrelShifterValue::shifted_register( + rd, + ShiftRegisterBy::ByRegister(rs), + bs_op, + Some(true), + ); + let result = self.get_barrel_shifted_value(shft) as i32; + carry = self.bs_carry_out; + result + } + ADC => Self::alu_add_flags(dst, src, &mut carry, &mut overflow).wrapping_add(c), + SBC => Self::alu_sub_flags(dst, src, &mut carry, &mut overflow).wrapping_sub(1 - c), + NEG => Self::alu_sub_flags(0, src, &mut carry, &mut overflow), + CMP => Self::alu_sub_flags(dst, src, &mut carry, &mut overflow), + CMN => Self::alu_add_flags(dst, src, &mut carry, &mut overflow), + ORR => dst | src, + MUL => { + let m = self.get_required_multipiler_array_cycles(src); + for _ in 0..m { + self.add_cycle(); + } + // TODO - meaningless values? + carry = false; + overflow = false; + dst.wrapping_mul(src) + } + BIC => dst & (!src), + MVN => !src, + }; + self.alu_update_flags(result, op.is_arithmetic(), carry, overflow); + + if !op.is_setting_flags() { self.set_reg(rd, result as u32); } self.S_cycle16(sb, self.pc + 2); @@ -133,43 +165,49 @@ impl Core { sb: &mut SysBus, insn: ThumbInstruction, ) -> CpuExecResult { - if OpFormat5::BX == insn.format5_op() { - self.exec_thumb_bx(sb, insn) + let op = insn.format5_op(); + let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) { + insn.rd() + 8 } else { - let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) { - insn.rd() + 8 - } else { - insn.rd() - }; - let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) { - insn.rs() + 8 - } else { - insn.rs() - }; - let arm_alu_op: AluOpCode = insn.format5_op().into(); - let set_flags = arm_alu_op.is_setting_flags(); - let op1 = self.get_reg(dst_reg) as i32; - let op2 = self.get_reg(src_reg) as i32; - let alu_res = if set_flags { - self.alu_flags(arm_alu_op, op1, op2) - } else { - Some(self.alu(arm_alu_op, op1, op2)) - }; - if let Some(result) = alu_res { - self.set_reg(dst_reg, result as u32); + insn.rd() + }; + let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) { + insn.rs() + 8 + } else { + insn.rs() + }; + let op1 = self.get_reg(dst_reg) as i32; + let op2 = self.get_reg(src_reg) as i32; + + match op { + OpFormat5::BX => return self.exec_thumb_bx(sb, insn), + OpFormat5::ADD => { + self.set_reg(dst_reg, op1.wrapping_add(op2) as u32); + if dst_reg == REG_PC { + self.flush_pipeline(sb); + } + } + OpFormat5::CMP => { + let mut carry = self.cpsr.C(); + let mut overflow = self.cpsr.V(); + let result = Self::alu_sub_flags(op1, op2, &mut carry, &mut overflow); + self.alu_update_flags(result, true, carry, overflow); + } + OpFormat5::MOV => { + self.set_reg(dst_reg, op2 as u32); if dst_reg == REG_PC { self.flush_pipeline(sb); } } - self.S_cycle16(sb, self.pc + 2); - Ok(()) } + self.S_cycle16(sb, self.pc + 2); + Ok(()) } fn exec_thumb_ldr_pc(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult { let addr = (insn.pc & !0b10) + 4 + (insn.word8() as Addr); self.S_cycle16(sb, self.pc + 2); - let data = sb.read_32(addr); + let data = self.ldr_word(addr, sb); self.N_cycle16(sb, addr); self.set_reg(insn.rd(), data); @@ -202,10 +240,10 @@ impl Core { let value = self.get_reg(insn.rd()); if insn.is_transferring_bytes() { self.N_cycle8(sb, addr); - sb.write_8(addr, value as u8); + self.write_8(addr, value as u8, sb); } else { self.N_cycle32(sb, addr); - sb.write_32(addr, value); + self.write_32(addr, value, sb); }; } @@ -236,7 +274,7 @@ impl Core { (false, false) => /* strh */ { - sb.write_16(addr, self.gpr[rd] as u16); + self.write_16(addr, self.gpr[rd] as u16, sb); self.N_cycle16(sb, addr); } (false, true) => @@ -273,7 +311,7 @@ impl Core { sb: &mut SysBus, insn: ThumbInstruction, ) -> CpuExecResult { - let offset = if insn.is_transferring_bytes() { + let offset = if insn.raw.bit(12) { insn.offset5() } else { (insn.offset5() << 3) >> 1 @@ -295,7 +333,7 @@ impl Core { self.add_cycle(); self.gpr[insn.rd()] = data as u32; } else { - sb.write_16(addr, self.gpr[insn.rd()] as u16); + self.write_16(addr, self.gpr[insn.rd()] as u16, sb); self.N_cycle16(sb, addr); } self.N_cycle16(sb, self.pc + 2); @@ -329,12 +367,12 @@ impl Core { addr: Addr, ) -> CpuExecResult { if insn.is_load() { - let data = sb.read_32(addr); + let data = self.ldr_word(addr, sb); self.S_cycle16(sb, addr); self.add_cycle(); self.gpr[insn.rd()] = data; } else { - sb.write_32(addr, self.gpr[insn.rd()]); + self.write_32(addr, self.gpr[insn.rd()], sb); self.N_cycle16(sb, addr); } self.N_cycle16(sb, self.pc + 2); @@ -344,9 +382,8 @@ impl Core { fn exec_thumb_add_sp(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult { let op1 = self.gpr[REG_SP] as i32; let op2 = insn.sword7(); - let arm_alu_op = AluOpCode::ADD; - self.gpr[REG_SP] = self.alu(arm_alu_op, op1, op2) as u32; + self.gpr[REG_SP] = op1.wrapping_add(op2) as u32; self.S_cycle16(sb, self.pc + 2); Ok(()) } @@ -401,41 +438,60 @@ impl Core { let is_load = insn.is_load(); let rb = insn.rb(); - let mut addr = self.gpr[rb]; + let mut addr = self.gpr[rb] & !3; let rlist = insn.register_list(); self.N_cycle16(sb, self.pc); let mut first = true; - if is_load { - for r in 0..8 { - if rlist.bit(r) { - let val = sb.read_32(addr); - if first { - first = false; + let mut writeback = true; + + if rlist != 0 { + if is_load { + for r in 0..8 { + if rlist.bit(r) { + if r == rb { + writeback = false; + } + let val = self.ldr_word(addr, sb); + if first { + first = false; + self.add_cycle(); + } else { + self.S_cycle16(sb, addr); + } + addr += 4; self.add_cycle(); - } else { - self.S_cycle16(sb, addr); + self.set_reg(r, val); + } + } + self.S_cycle16(sb, self.pc + 2); + } else { + for r in 0..8 { + if rlist.bit(r) { + if first { + first = false; + } else { + self.S_cycle16(sb, addr); + } + self.write_32(addr, self.gpr[r], sb); + addr += 4; } - addr += 4; - self.add_cycle(); - self.set_reg(r, val); } } - self.S_cycle16(sb, self.pc + 2); } else { - for r in 0..8 { - if rlist.bit(r) { - if first { - first = false; - } else { - self.S_cycle16(sb, addr); - } - sb.write_32(addr, self.gpr[r]); - addr += 4; - } + // From gbatek.htm: Empty Rlist: R15 loaded/stored (ARMv4 only), and Rb=Rb+40h (ARMv4-v5). + if is_load { + let val = self.ldr_word(addr, sb); + self.set_reg(REG_PC, val & !1); + self.flush_pipeline(sb); + } else { + self.write_32(addr, self.pc + 2, sb); } + addr += 0x40; } - self.gpr[rb] = addr as u32; + if writeback { + self.gpr[rb] = addr; + } Ok(()) } @@ -493,7 +549,6 @@ impl Core { ThumbFormat::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn), ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn), ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(bus, insn), - ThumbFormat::Mul => self.exec_thumb_mul(bus, insn), ThumbFormat::AluOps => self.exec_thumb_alu_ops(bus, insn), ThumbFormat::HiRegOpOrBranchExchange => self.exec_thumb_hi_reg_op_or_bx(bus, insn), ThumbFormat::LdrPc => self.exec_thumb_ldr_pc(bus, insn), diff --git a/src/core/arm7tdmi/thumb/mod.rs b/src/core/arm7tdmi/thumb/mod.rs index 698057f..bd9ef72 100644 --- a/src/core/arm7tdmi/thumb/mod.rs +++ b/src/core/arm7tdmi/thumb/mod.rs @@ -43,8 +43,6 @@ pub enum ThumbFormat { AddSub, /// Format 3 DataProcessImm, - /// Belongs to Format 4, but decoded seperatly because AluOpCode doesn't have MUL - Mul, /// Format 4 AluOps, /// Format 5 @@ -98,8 +96,6 @@ impl InstructionDecoder for ThumbInstruction { Ok(MoveShiftedReg) } else if raw & 0xe000 == 0x2000 { Ok(DataProcessImm) - } else if raw & 0xffc0 == 0x4340 { - Ok(Mul) } else if raw & 0xfc00 == 0x4000 { Ok(AluOps) } else if raw & 0xfc00 == 0x4400 { @@ -183,6 +179,43 @@ pub enum OpFormat5 { BX = 3, } +#[derive(Debug, Primitive, PartialEq)] +pub enum ThumbAluOps { + AND = 0b0000, + EOR = 0b0001, + LSL = 0b0010, + LSR = 0b0011, + ASR = 0b0100, + ADC = 0b0101, + SBC = 0b0110, + ROR = 0b0111, + TST = 0b1000, + NEG = 0b1001, + CMP = 0b1010, + CMN = 0b1011, + ORR = 0b1100, + MUL = 0b1101, + BIC = 0b1110, + MVN = 0b1111, +} + +impl ThumbAluOps { + pub fn is_setting_flags(&self) -> bool { + use ThumbAluOps::*; + match self { + TST | CMP | CMN => true, + _ => false, + } + } + pub fn is_arithmetic(&self) -> bool { + use ThumbAluOps::*; + match self { + ADC | SBC | NEG | CMP | CMN => true, + _ => false, + } + } +} + impl From for AluOpCode { fn from(op: OpFormat5) -> AluOpCode { match op { @@ -245,49 +278,8 @@ impl ThumbInstruction { OpFormat5::from_u8(self.raw.bit_range(8..10) as u8).unwrap() } - pub fn alu_opcode(&self) -> (AluOpCode, Option) { - use ShiftRegisterBy::*; - match self.raw.bit_range(6..10) { - 0b0010 => ( - AluOpCode::MOV, - Some(BarrelShifterValue::ShiftedRegister(ShiftedRegister { - reg: self.rd(), - shift_by: ByRegister(self.rs()), - bs_op: BarrelShiftOpCode::LSL, - added: Some(true), - })), - ), - 0b0011 => ( - AluOpCode::MOV, - Some(BarrelShifterValue::ShiftedRegister(ShiftedRegister { - reg: self.rd(), - shift_by: ByRegister(self.rs()), - bs_op: BarrelShiftOpCode::LSR, - added: Some(true), - })), - ), - 0b0100 => ( - AluOpCode::MOV, - Some(BarrelShifterValue::ShiftedRegister(ShiftedRegister { - reg: self.rd(), - shift_by: ByRegister(self.rs()), - bs_op: BarrelShiftOpCode::ASR, - added: Some(true), - })), - ), - 0b0111 => ( - AluOpCode::MOV, - Some(BarrelShifterValue::ShiftedRegister(ShiftedRegister { - reg: self.rd(), - shift_by: ByRegister(self.rs()), - bs_op: BarrelShiftOpCode::ROR, - added: Some(true), - })), - ), - 0b1001 => (AluOpCode::RSB, Some(BarrelShifterValue::ImmediateValue(0))), - 0b1101 => panic!("tried to decode MUL"), - op => (AluOpCode::from_u16(op).unwrap(), None), - } + pub fn format4_alu_op(&self) -> ThumbAluOps { + ThumbAluOps::from_u16(self.raw.bit_range(6..10)).unwrap() } pub fn offset5(&self) -> i8 { diff --git a/src/core/gpu/mod.rs b/src/core/gpu/mod.rs index c0a94da..a69f378 100644 --- a/src/core/gpu/mod.rs +++ b/src/core/gpu/mod.rs @@ -455,7 +455,7 @@ impl SyncedIoDevice for Gpu { .set_vcount(self.dispstat.vcount_setting() == self.current_scanline as u16); } if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount() { - irqs.set_LCD_VCounterMatch(true);; + irqs.set_LCD_VCounterMatch(true); } match self.state { diff --git a/src/core/ioregs.rs b/src/core/ioregs.rs index 7fcfb47..6b49ede 100644 --- a/src/core/ioregs.rs +++ b/src/core/ioregs.rs @@ -7,125 +7,6 @@ use super::gpu::regs::WindowFlags; use super::keypad; use super::sysbus::BoxedMemory; -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_L: Addr = IO_BASE + 0x_0028; // 4 W BG2 Reference Point X-Coordinate, lower 16 bit - pub const REG_BG2X_H: Addr = IO_BASE + 0x_002A; // 4 W BG2 Reference Point X-Coordinate, upper 16 bit - pub const REG_BG2Y_L: Addr = IO_BASE + 0x_002C; // 4 W BG2 Reference Point Y-Coordinate, lower 16 bit - pub const REG_BG2Y_H: Addr = IO_BASE + 0x_002E; // 4 W BG2 Reference Point Y-Coordinate, upper 16 bit - 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_L: Addr = IO_BASE + 0x_0038; // 4 W BG3 Reference Point X-Coordinate, lower 16 bit - pub const REG_BG3X_H: Addr = IO_BASE + 0x_003A; // 4 W BG3 Reference Point X-Coordinate, upper 16 bit - pub const REG_BG3Y_L: Addr = IO_BASE + 0x_003C; // 4 W BG3 Reference Point Y-Coordinate, lower 16 bit - pub const REG_BG3Y_H: Addr = IO_BASE + 0x_003E; // 4 W BG3 Reference Point Y-Coordinate, upper 16 bit - 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)] @@ -140,7 +21,7 @@ pub struct IoRegs { impl IoRegs { pub fn new(io: Rc>) -> IoRegs { IoRegs { - mem: BoxedMemory::new(vec![0; 0x400].into_boxed_slice(), 0x3ff), + mem: BoxedMemory::new(vec![0; 0x800].into_boxed_slice()), io: io, post_boot_flag: false, keyinput: keypad::KEYINPUT_ALL_RELEASED, @@ -200,7 +81,12 @@ impl Bus for IoRegs { } fn read_8(&self, addr: Addr) -> u8 { - self.read_16(addr) as u8 + let t = self.read_16(addr & !1); + if addr & 1 != 0 { + (t >> 8) as u8 + } else { + t as u8 + } } fn write_32(&mut self, addr: Addr, value: u32) { @@ -211,12 +97,12 @@ impl Bus for IoRegs { fn write_16(&mut self, addr: Addr, value: u16) { let mut io = self.io.borrow_mut(); match addr + IO_BASE { - REG_DISPCNT => io.gpu.dispcnt.0 |= value, - REG_DISPSTAT => io.gpu.dispstat.0 |= value, - REG_BG0CNT => io.gpu.bg[0].bgcnt.0 |= value, - REG_BG1CNT => io.gpu.bg[1].bgcnt.0 |= value, - REG_BG2CNT => io.gpu.bg[2].bgcnt.0 |= value, - REG_BG3CNT => io.gpu.bg[3].bgcnt.0 |= value, + REG_DISPCNT => io.gpu.dispcnt.0 = value, + REG_DISPSTAT => io.gpu.dispstat.0 |= value & !3, + REG_BG0CNT => io.gpu.bg[0].bgcnt.0 = value, + REG_BG1CNT => io.gpu.bg[1].bgcnt.0 = value, + REG_BG2CNT => io.gpu.bg[2].bgcnt.0 = value, + REG_BG3CNT => io.gpu.bg[3].bgcnt.0 = value, REG_BG0HOFS => io.gpu.bg[0].bghofs = value & 0x1ff, REG_BG0VOFS => io.gpu.bg[0].bgvofs = value & 0x1ff, REG_BG1HOFS => io.gpu.bg[1].bghofs = value & 0x1ff, @@ -311,24 +197,25 @@ impl Bus for IoRegs { REG_POSTFLG => self.post_boot_flag = value != 0, REG_HALTCNT => {} _ => { + let ioreg_addr = IO_BASE + addr; + println!( + "Unimplemented write to {:x} {}", + ioreg_addr, + io_reg_string(ioreg_addr) + ); self.mem.write_16(addr, value); } } } fn write_8(&mut self, addr: Addr, value: u8) { - if addr & 1 != 0 { - let addr = addr & !1; - let t = self.read_16(addr); - let upper = (value as u16); - let lower = t & 0xff; - self.write_16(addr, (upper << 8) | lower); + let t = self.read_16(addr & !1); + let t = if addr & 1 != 0 { + (t & 0xff) | (value as u16) << 8 } else { - let t = self.read_16(addr); - let upper = t << 8; - let lower = (value as u16); - self.write_16(addr, (upper << 8) | lower); - } + (t & 0xff00) | (value as u16) + }; + self.write_16(addr, t); } } @@ -348,3 +235,226 @@ bitfield! { PHI_terminal_output, _: 12, 11; prefetch, _: 14; } + +#[rustfmt::skip] +pub mod consts { + use super::*; + + pub const IO_BASE: Addr = 0x0400_0000; + + pub const REG_DISPCNT: Addr = 0x0400_0000; // 2 R/W LCD Control + pub const REG_GREENSWAP: Addr = 0x0400_0002; // 2 R/W Undocumented - Green Swap + pub const REG_DISPSTAT: Addr = 0x0400_0004; // 2 R/W General LCD Status (STAT,LYC) + pub const REG_VCOUNT: Addr = 0x0400_0006; // 2 R Vertical Counter (LY) + pub const REG_BG0CNT: Addr = 0x0400_0008; // 2 R/W BG0 Control + pub const REG_BG1CNT: Addr = 0x0400_000A; // 2 R/W BG1 Control + pub const REG_BG2CNT: Addr = 0x0400_000C; // 2 R/W BG2 Control + pub const REG_BG3CNT: Addr = 0x0400_000E; // 2 R/W BG3 Control + pub const REG_BG0HOFS: Addr = 0x0400_0010; // 2 W BG0 X-Offset + pub const REG_BG0VOFS: Addr = 0x0400_0012; // 2 W BG0 Y-Offset + pub const REG_BG1HOFS: Addr = 0x0400_0014; // 2 W BG1 X-Offset + pub const REG_BG1VOFS: Addr = 0x0400_0016; // 2 W BG1 Y-Offset + pub const REG_BG2HOFS: Addr = 0x0400_0018; // 2 W BG2 X-Offset + pub const REG_BG2VOFS: Addr = 0x0400_001A; // 2 W BG2 Y-Offset + pub const REG_BG3HOFS: Addr = 0x0400_001C; // 2 W BG3 X-Offset + pub const REG_BG3VOFS: Addr = 0x0400_001E; // 2 W BG3 Y-Offset + pub const REG_BG2PA: Addr = 0x0400_0020; // 2 W BG2 Rotation/Scaling Parameter A (dx) + pub const REG_BG2PB: Addr = 0x0400_0022; // 2 W BG2 Rotation/Scaling Parameter B (dmx) + pub const REG_BG2PC: Addr = 0x0400_0024; // 2 W BG2 Rotation/Scaling Parameter C (dy) + pub const REG_BG2PD: Addr = 0x0400_0026; // 2 W BG2 Rotation/Scaling Parameter D (dmy) + pub const REG_BG2X_L: Addr = 0x0400_0028; // 4 W BG2 Reference Point X-Coordinate, lower 16 bit + pub const REG_BG2X_H: Addr = 0x0400_002A; // 4 W BG2 Reference Point X-Coordinate, upper 16 bit + pub const REG_BG2Y_L: Addr = 0x0400_002C; // 4 W BG2 Reference Point Y-Coordinate, lower 16 bit + pub const REG_BG2Y_H: Addr = 0x0400_002E; // 4 W BG2 Reference Point Y-Coordinate, upper 16 bit + pub const REG_BG3PA: Addr = 0x0400_0030; // 2 W BG3 Rotation/Scaling Parameter A (dx) + pub const REG_BG3PB: Addr = 0x0400_0032; // 2 W BG3 Rotation/Scaling Parameter B (dmx) + pub const REG_BG3PC: Addr = 0x0400_0034; // 2 W BG3 Rotation/Scaling Parameter C (dy) + pub const REG_BG3PD: Addr = 0x0400_0036; // 2 W BG3 Rotation/Scaling Parameter D (dmy) + pub const REG_BG3X_L: Addr = 0x0400_0038; // 4 W BG3 Reference Point X-Coordinate, lower 16 bit + pub const REG_BG3X_H: Addr = 0x0400_003A; // 4 W BG3 Reference Point X-Coordinate, upper 16 bit + pub const REG_BG3Y_L: Addr = 0x0400_003C; // 4 W BG3 Reference Point Y-Coordinate, lower 16 bit + pub const REG_BG3Y_H: Addr = 0x0400_003E; // 4 W BG3 Reference Point Y-Coordinate, upper 16 bit + pub const REG_WIN0H: Addr = 0x0400_0040; // 2 W Window 0 Horizontal Dimensions + pub const REG_WIN1H: Addr = 0x0400_0042; // 2 W Window 1 Horizontal Dimensions + pub const REG_WIN0V: Addr = 0x0400_0044; // 2 W Window 0 Vertical Dimensions + pub const REG_WIN1V: Addr = 0x0400_0046; // 2 W Window 1 Vertical Dimensions + pub const REG_WININ: Addr = 0x0400_0048; // 2 R/W Inside of Window 0 and 1 + pub const REG_WINOUT: Addr = 0x0400_004A; // 2 R/W Inside of OBJ Window & Outside of Windows + pub const REG_MOSAIC: Addr = 0x0400_004C; // 2 W Mosaic Size + pub const REG_BLDCNT: Addr = 0x0400_0050; // 2 R/W Color Special Effects Selection + pub const REG_BLDALPHA: Addr = 0x0400_0052; // 2 R/W Alpha Blending Coefficients + pub const REG_BLDY: Addr = 0x0400_0054; // 2 W Brightness (Fade-In/Out) Coefficient + pub const REG_SOUND1CNT_L: Addr = 0x0400_0060; // 2 R/W Channel 1 Sweep register (NR10) + pub const REG_SOUND1CNT_H: Addr = 0x0400_0062; // 2 R/W Channel 1 Duty/Length/Envelope (NR11, NR12) + pub const REG_SOUND1CNT_X: Addr = 0x0400_0064; // 2 R/W Channel 1 Frequency/Control (NR13, NR14) + pub const REG_SOUND2CNT_L: Addr = 0x0400_0068; // 2 R/W Channel 2 Duty/Length/Envelope (NR21, NR22) + pub const REG_SOUND2CNT_H: Addr = 0x0400_006C; // 2 R/W Channel 2 Frequency/Control (NR23, NR24) + pub const REG_SOUND3CNT_L: Addr = 0x0400_0070; // 2 R/W Channel 3 Stop/Wave RAM select (NR30) + pub const REG_SOUND3CNT_H: Addr = 0x0400_0072; // 2 R/W Channel 3 Length/Volume (NR31, NR32) + pub const REG_SOUND3CNT_X: Addr = 0x0400_0074; // 2 R/W Channel 3 Frequency/Control (NR33, NR34) + pub const REG_SOUND4CNT_L: Addr = 0x0400_0078; // 2 R/W Channel 4 Length/Envelope (NR41, NR42) + pub const REG_SOUND4CNT_H: Addr = 0x0400_007C; // 2 R/W Channel 4 Frequency/Control (NR43, NR44) + pub const REG_SOUNDCNT_L: Addr = 0x0400_0080; // 2 R/W Control Stereo/Volume/Enable (NR50, NR51) + pub const REG_SOUNDCNT_H: Addr = 0x0400_0082; // 2 R/W Control Mixing/DMA Control + pub const REG_SOUNDCNT_X: Addr = 0x0400_0084; // 2 R/W Control Sound on/off (NR52) + pub const REG_SOUNDBIAS: Addr = 0x0400_0088; // 2 BIOS Sound PWM Control + pub const REG_WAVE_RAM: Addr = 0x0400_0090; // Channel 3 Wave Pattern RAM (2 banks!!) + pub const REG_FIFO_A: Addr = 0x0400_00A0; // 4 W Channel A FIFO, Data 0-3 + pub const REG_FIFO_B: Addr = 0x0400_00A4; // 4 W Channel B FIFO, Data 0-3 + pub const REG_DMA0SAD: Addr = 0x0400_00B0; // 4 W DMA 0 Source Address + pub const REG_DMA0DAD: Addr = 0x0400_00B4; // 4 W DMA 0 Destination Address + pub const REG_DMA0CNT_L: Addr = 0x0400_00B8; // 2 W DMA 0 Word Count + pub const REG_DMA0CNT_H: Addr = 0x0400_00BA; // 2 R/W DMA 0 Control + pub const REG_DMA1SAD: Addr = 0x0400_00BC; // 4 W DMA 1 Source Address + pub const REG_DMA1DAD: Addr = 0x0400_00C0; // 4 W DMA 1 Destination Address + pub const REG_DMA1CNT_L: Addr = 0x0400_00C4; // 2 W DMA 1 Word Count + pub const REG_DMA1CNT_H: Addr = 0x0400_00C6; // 2 R/W DMA 1 Control + pub const REG_DMA2SAD: Addr = 0x0400_00C8; // 4 W DMA 2 Source Address + pub const REG_DMA2DAD: Addr = 0x0400_00CC; // 4 W DMA 2 Destination Address + pub const REG_DMA2CNT_L: Addr = 0x0400_00D0; // 2 W DMA 2 Word Count + pub const REG_DMA2CNT_H: Addr = 0x0400_00D2; // 2 R/W DMA 2 Control + pub const REG_DMA3SAD: Addr = 0x0400_00D4; // 4 W DMA 3 Source Address + pub const REG_DMA3DAD: Addr = 0x0400_00D8; // 4 W DMA 3 Destination Address + pub const REG_DMA3CNT_L: Addr = 0x0400_00DC; // 2 W DMA 3 Word Count + pub const REG_DMA3CNT_H: Addr = 0x0400_00DE; // 2 R/W DMA 3 Control + pub const REG_TM0CNT_L: Addr = 0x0400_0100; // 2 R/W Timer 0 Counter/Reload + pub const REG_TM0CNT_H: Addr = 0x0400_0102; // 2 R/W Timer 0 Control + pub const REG_TM1CNT_L: Addr = 0x0400_0104; // 2 R/W Timer 1 Counter/Reload + pub const REG_TM1CNT_H: Addr = 0x0400_0106; // 2 R/W Timer 1 Control + pub const REG_TM2CNT_L: Addr = 0x0400_0108; // 2 R/W Timer 2 Counter/Reload + pub const REG_TM2CNT_H: Addr = 0x0400_010A; // 2 R/W Timer 2 Control + pub const REG_TM3CNT_L: Addr = 0x0400_010C; // 2 R/W Timer 3 Counter/Reload + pub const REG_TM3CNT_H: Addr = 0x0400_010E; // 2 R/W Timer 3 Control + pub const REG_SIODATA32: Addr = 0x0400_0120; // 4 R/W SIO Data (Normal-32bit Mode; shared with below) + pub const REG_SIOMULTI0: Addr = 0x0400_0120; // 2 R/W SIO Data 0 (Parent) (Multi-Player Mode) + pub const REG_SIOMULTI1: Addr = 0x0400_0122; // 2 R/W SIO Data 1 (1st Child) (Multi-Player Mode) + pub const REG_SIOMULTI2: Addr = 0x0400_0124; // 2 R/W SIO Data 2 (2nd Child) (Multi-Player Mode) + pub const REG_SIOMULTI3: Addr = 0x0400_0126; // 2 R/W SIO Data 3 (3rd Child) (Multi-Player Mode) + pub const REG_SIOCNT: Addr = 0x0400_0128; // 2 R/W SIO Control Register + pub const REG_SIOMLT_SEND: Addr = 0x0400_012A; // 2 R/W SIO Data (Local of MultiPlayer; shared below) + pub const REG_SIODATA8: Addr = 0x0400_012A; // 2 R/W SIO Data (Normal-8bit and UART Mode) + pub const REG_KEYINPUT: Addr = 0x0400_0130; // 2 R Key Status + pub const REG_KEYCNT: Addr = 0x0400_0132; // 2 R/W Key Interrupt Control + pub const REG_RCNT: Addr = 0x0400_0134; // 2 R/W SIO Mode Select/General Purpose Data + pub const REG_IR: Addr = 0x0400_0136; // - - Ancient - Infrared Register (Prototypes only) + pub const REG_JOYCNT: Addr = 0x0400_0140; // 2 R/W SIO JOY Bus Control + pub const REG_JOY_RECV: Addr = 0x0400_0150; // 4 R/W SIO JOY Bus Receive Data + pub const REG_JOY_TRANS: Addr = 0x0400_0154; // 4 R/W SIO JOY Bus Transmit Data + pub const REG_JOYSTAT: Addr = 0x0400_0158; // 2 R/? SIO JOY Bus Receive Status + pub const REG_IE: Addr = 0x0400_0200; // 2 R/W Interrupt Enable Register + pub const REG_IF: Addr = 0x0400_0202; // 2 R/W Interrupt Request Flags / IRQ Acknowledge + pub const REG_WAITCNT: Addr = 0x0400_0204; // 2 R/W Game Pak Waitstate Control + pub const REG_IME: Addr = 0x0400_0208; // 2 R/W Interrupt Master Enable Register + pub const REG_POSTFLG: Addr = 0x0400_0300; // 1 R/W Undocumented - Post Boot Flag + pub const REG_HALTCNT: Addr = 0x0400_0301; // 1 W Undocumented - Power Down Control +} + +fn io_reg_string(addr: u32) -> &'static str { + match addr { + REG_DISPCNT => "REG_DISPCNT", + REG_DISPSTAT => "REG_DISPSTAT", + REG_VCOUNT => "REG_VCOUNT", + REG_BG0CNT => "REG_BG0CNT", + REG_BG1CNT => "REG_BG1CNT", + REG_BG2CNT => "REG_BG2CNT", + REG_BG3CNT => "REG_BG3CNT", + REG_BG0HOFS => "REG_BG0HOFS", + REG_BG0VOFS => "REG_BG0VOFS", + REG_BG1HOFS => "REG_BG1HOFS", + REG_BG1VOFS => "REG_BG1VOFS", + REG_BG2HOFS => "REG_BG2HOFS", + REG_BG2VOFS => "REG_BG2VOFS", + REG_BG3HOFS => "REG_BG3HOFS", + REG_BG3VOFS => "REG_BG3VOFS", + REG_BG2PA => "REG_BG2PA", + REG_BG2PB => "REG_BG2PB", + REG_BG2PC => "REG_BG2PC", + REG_BG2PD => "REG_BG2PD", + REG_BG2X_L => "REG_BG2X_L", + REG_BG2X_H => "REG_BG2X_H", + REG_BG2Y_L => "REG_BG2Y_L", + REG_BG2Y_H => "REG_BG2Y_H", + REG_BG3PA => "REG_BG3PA", + REG_BG3PB => "REG_BG3PB", + REG_BG3PC => "REG_BG3PC", + REG_BG3PD => "REG_BG3PD", + REG_BG3X_L => "REG_BG3X_L", + REG_BG3X_H => "REG_BG3X_H", + REG_BG3Y_L => "REG_BG3Y_L", + REG_BG3Y_H => "REG_BG3Y_H", + REG_WIN0H => "REG_WIN0H", + REG_WIN1H => "REG_WIN1H", + REG_WIN0V => "REG_WIN0V", + REG_WIN1V => "REG_WIN1V", + REG_WININ => "REG_WININ", + REG_WINOUT => "REG_WINOUT", + REG_MOSAIC => "REG_MOSAIC", + REG_BLDCNT => "REG_BLDCNT", + REG_BLDALPHA => "REG_BLDALPHA", + REG_BLDY => "REG_BLDY", + REG_SOUND1CNT_L => "REG_SOUND1CNT_L", + REG_SOUND1CNT_H => "REG_SOUND1CNT_H", + REG_SOUND1CNT_X => "REG_SOUND1CNT_X", + REG_SOUND2CNT_L => "REG_SOUND2CNT_L", + REG_SOUND2CNT_H => "REG_SOUND2CNT_H", + REG_SOUND3CNT_L => "REG_SOUND3CNT_L", + REG_SOUND3CNT_H => "REG_SOUND3CNT_H", + REG_SOUND3CNT_X => "REG_SOUND3CNT_X", + REG_SOUND4CNT_L => "REG_SOUND4CNT_L", + REG_SOUND4CNT_H => "REG_SOUND4CNT_H", + REG_SOUNDCNT_L => "REG_SOUNDCNT_L", + REG_SOUNDCNT_H => "REG_SOUNDCNT_H", + REG_SOUNDCNT_X => "REG_SOUNDCNT_X", + REG_SOUNDBIAS => "REG_SOUNDBIAS", + REG_WAVE_RAM => "REG_WAVE_RAM", + REG_FIFO_A => "REG_FIFO_A", + REG_FIFO_B => "REG_FIFO_B", + REG_DMA0SAD => "REG_DMA0SAD", + REG_DMA0DAD => "REG_DMA0DAD", + REG_DMA0CNT_L => "REG_DMA0CNT_L", + REG_DMA0CNT_H => "REG_DMA0CNT_H", + REG_DMA1SAD => "REG_DMA1SAD", + REG_DMA1DAD => "REG_DMA1DAD", + REG_DMA1CNT_L => "REG_DMA1CNT_L", + REG_DMA1CNT_H => "REG_DMA1CNT_H", + REG_DMA2SAD => "REG_DMA2SAD", + REG_DMA2DAD => "REG_DMA2DAD", + REG_DMA2CNT_L => "REG_DMA2CNT_L", + REG_DMA2CNT_H => "REG_DMA2CNT_H", + REG_DMA3SAD => "REG_DMA3SAD", + REG_DMA3DAD => "REG_DMA3DAD", + REG_DMA3CNT_L => "REG_DMA3CNT_L", + REG_DMA3CNT_H => "REG_DMA3CNT_H", + REG_TM0CNT_L => "REG_TM0CNT_L", + REG_TM0CNT_H => "REG_TM0CNT_H", + REG_TM1CNT_L => "REG_TM1CNT_L", + REG_TM1CNT_H => "REG_TM1CNT_H", + REG_TM2CNT_L => "REG_TM2CNT_L", + REG_TM2CNT_H => "REG_TM2CNT_H", + REG_TM3CNT_L => "REG_TM3CNT_L", + REG_TM3CNT_H => "REG_TM3CNT_H", + REG_SIODATA32 => "REG_SIODATA32", + REG_SIOMULTI0 => "REG_SIOMULTI0", + REG_SIOMULTI1 => "REG_SIOMULTI1", + REG_SIOMULTI2 => "REG_SIOMULTI2", + REG_SIOMULTI3 => "REG_SIOMULTI3", + REG_SIOCNT => "REG_SIOCNT", + REG_SIOMLT_SEND => "REG_SIOMLT_SEND", + REG_SIODATA8 => "REG_SIODATA8", + REG_KEYINPUT => "REG_KEYINPUT", + REG_KEYCNT => "REG_KEYCNT", + REG_RCNT => "REG_RCNT", + REG_IR => "REG_IR", + REG_JOYCNT => "REG_JOYCNT", + REG_JOY_RECV => "REG_JOY_RECV", + REG_JOY_TRANS => "REG_JOY_TRANS", + REG_JOYSTAT => "REG_JOYSTAT", + REG_IE => "REG_IE", + REG_IF => "REG_IF", + REG_WAITCNT => "REG_WAITCNT", + REG_IME => "REG_IME", + REG_POSTFLG => "REG_POSTFLG", + REG_HALTCNT => "REG_HALTCNT", + _ => "UNKNOWN", + } +} diff --git a/src/core/mod.rs b/src/core/mod.rs index 26852ee..7ee97da 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -53,4 +53,4 @@ impl From for GBAError { fn from(err: zip::result::ZipError) -> GBAError { GBAError::IO(::std::io::Error::from(::std::io::ErrorKind::InvalidInput)) } -} \ No newline at end of file +} diff --git a/src/core/sysbus.rs b/src/core/sysbus.rs index e96f82c..f1cf964 100644 --- a/src/core/sysbus.rs +++ b/src/core/sysbus.rs @@ -74,51 +74,45 @@ impl fmt::Display for MemoryAccess { #[derive(Debug)] pub struct BoxedMemory { pub mem: Box<[u8]>, - mask: u32, } impl BoxedMemory { - pub fn new(boxed_slice: Box<[u8]>, mask: u32) -> BoxedMemory { - BoxedMemory { - mem: boxed_slice, - mask: mask, - } + pub fn new(boxed_slice: Box<[u8]>) -> BoxedMemory { + BoxedMemory { mem: boxed_slice } } } impl Bus for BoxedMemory { fn read_32(&self, addr: Addr) -> u32 { - (&self.mem[(addr & self.mask) as usize..]) + (&self.mem[addr as usize..]) .read_u32::() .unwrap() } fn read_16(&self, addr: Addr) -> u16 { - (&self.mem[(addr & self.mask) as usize..]) + (&self.mem[addr as usize..]) .read_u16::() .unwrap() } fn read_8(&self, addr: Addr) -> u8 { - (&self.mem[(addr & self.mask) as usize..])[0] + (&self.mem[addr as usize..])[0] } fn write_32(&mut self, addr: Addr, value: u32) { - (&mut self.mem[(addr & self.mask) as usize..]) + (&mut self.mem[addr as usize..]) .write_u32::(value) .unwrap() } fn write_16(&mut self, addr: Addr, value: u16) { - (&mut self.mem[(addr & self.mask) as usize..]) + (&mut self.mem[addr as usize..]) .write_u16::(value) .unwrap() } fn write_8(&mut self, addr: Addr, value: u8) { - (&mut self.mem[(addr & self.mask) as usize..]) - .write_u8(value) - .unwrap() + (&mut self.mem[addr as usize..]).write_u8(value).unwrap() } } @@ -171,56 +165,70 @@ impl SysBus { SysBus { io: io, - bios: BoxedMemory::new(bios_rom.into_boxed_slice(), 0xff_ffff), - onboard_work_ram: BoxedMemory::new( - vec![0; WORK_RAM_SIZE].into_boxed_slice(), - (WORK_RAM_SIZE as u32) - 1, - ), - internal_work_ram: BoxedMemory::new( - vec![0; INTERNAL_RAM_SIZE].into_boxed_slice(), - 0x7fff, - ), + bios: BoxedMemory::new(bios_rom.into_boxed_slice()), + onboard_work_ram: BoxedMemory::new(vec![0; WORK_RAM_SIZE].into_boxed_slice()), + internal_work_ram: BoxedMemory::new(vec![0; INTERNAL_RAM_SIZE].into_boxed_slice()), ioregs: ioregs, - palette_ram: BoxedMemory::new( - vec![0; PALETTE_RAM_SIZE].into_boxed_slice(), - (PALETTE_RAM_SIZE as u32) - 1, - ), - vram: BoxedMemory::new( - vec![0; VIDEO_RAM_SIZE].into_boxed_slice(), - (VIDEO_RAM_SIZE as u32) - 1, - ), - oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice(), (OAM_SIZE as u32) - 1), + palette_ram: BoxedMemory::new(vec![0; PALETTE_RAM_SIZE].into_boxed_slice()), + vram: BoxedMemory::new(vec![0; VIDEO_RAM_SIZE].into_boxed_slice()), + oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice()), gamepak: gamepak, dummy: DummyBus([0; 4]), } } - fn map(&self, addr: Addr) -> &Bus { + fn map(&self, addr: Addr) -> (&Bus, Addr) { + let ofs = addr & 0x00ff_ffff; match addr & 0xff000000 { - BIOS_ADDR => &self.bios, - EWRAM_ADDR => &self.onboard_work_ram, - IWRAM_ADDR => &self.internal_work_ram, - IOMEM_ADDR => &self.ioregs, - PALRAM_ADDR => &self.palette_ram, - VRAM_ADDR => &self.vram, - OAM_ADDR => &self.oam, - GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => &self.gamepak, - _ => &self.dummy, + BIOS_ADDR => (&self.bios, ofs), + EWRAM_ADDR => (&self.onboard_work_ram, ofs & 0x3_ffff), + IWRAM_ADDR => (&self.internal_work_ram, ofs & 0x7fff), + IOMEM_ADDR => (&self.ioregs, { + if ofs & 0xffff == 0x8000 { + 0x800 + } else { + ofs & 0x7ff + } + }), + PALRAM_ADDR => (&self.palette_ram, ofs & 0x3ff), + VRAM_ADDR => (&self.vram, { + let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1); + if ofs > 0x18000 { + ofs -= 0x8000; + } + ofs + }), + OAM_ADDR => (&self.oam, ofs & 0x3ff), + GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => (&self.gamepak, ofs), + _ => (&self.dummy, ofs), } } /// TODO proc-macro for generating this function - fn map_mut(&mut self, addr: Addr) -> &mut Bus { + fn map_mut(&mut self, addr: Addr) -> (&mut Bus, Addr) { + let ofs = addr & 0x00ff_ffff; match addr & 0xff000000 { - BIOS_ADDR => &mut self.bios, - EWRAM_ADDR => &mut self.onboard_work_ram, - IWRAM_ADDR => &mut self.internal_work_ram, - IOMEM_ADDR => &mut self.ioregs, - PALRAM_ADDR => &mut self.palette_ram, - VRAM_ADDR => &mut self.vram, - OAM_ADDR => &mut self.oam, - GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => &mut self.gamepak, - _ => &mut self.dummy, + BIOS_ADDR => (&mut self.bios, ofs), + EWRAM_ADDR => (&mut self.onboard_work_ram, ofs & 0x3_ffff), + IWRAM_ADDR => (&mut self.internal_work_ram, ofs & 0x7fff), + IOMEM_ADDR => (&mut self.ioregs, { + if ofs & 0xffff == 0x8000 { + 0x800 + } else { + ofs & 0x7ff + } + }), + PALRAM_ADDR => (&mut self.palette_ram, ofs & 0x3ff), + VRAM_ADDR => (&mut self.vram, { + let mut ofs = ofs & ((VIDEO_RAM_SIZE as u32) - 1); + if ofs > 0x18000 { + ofs -= 0x8000; + } + ofs + }), + OAM_ADDR => (&mut self.oam, ofs & 0x3ff), + GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => (&mut self.gamepak, ofs), + _ => (&mut self.dummy, ofs), } } @@ -274,26 +282,32 @@ impl SysBus { impl Bus for SysBus { fn read_32(&self, addr: Addr) -> u32 { - self.map(addr).read_32(addr & 0xff_ffff) + let (dev, addr) = self.map(addr); + dev.read_32(addr & 0xff_fffc) } fn read_16(&self, addr: Addr) -> u16 { - self.map(addr).read_16(addr & 0xff_ffff) + let (dev, addr) = self.map(addr); + dev.read_16(addr & 0xff_fffe) } fn read_8(&self, addr: Addr) -> u8 { - self.map(addr).read_8(addr & 0xff_ffff) + let (dev, addr) = self.map(addr); + dev.read_8(addr & 0xff_ffff) } fn write_32(&mut self, addr: Addr, value: u32) { - self.map_mut(addr).write_32(addr & 0xff_ffff, value) + let (dev, addr) = self.map_mut(addr); + dev.write_32(addr & 0xff_fffc, value); } fn write_16(&mut self, addr: Addr, value: u16) { - self.map_mut(addr).write_16(addr & 0xff_ffff, value) + let (dev, addr) = self.map_mut(addr); + dev.write_16(addr & 0xff_fffe, value); } fn write_8(&mut self, addr: Addr, value: u8) { - self.map_mut(addr).write_8(addr & 0xff_ffff, value) + let (dev, addr) = self.map_mut(addr); + dev.write_8(addr & 0xff_ffff, value); } } diff --git a/src/core/timer.rs b/src/core/timer.rs index 4bbb891..0a8cd42 100644 --- a/src/core/timer.rs +++ b/src/core/timer.rs @@ -100,17 +100,15 @@ impl SyncedIoDevice for Timers { for i in 0..4 { if self[i].timer_ctl.enabled() && !self[i].timer_ctl.cascade() { match self[i].add_cycles(cycles, irqs) { - TimerAction::Overflow(num_overflows) => { - match i { - 3 => {} - _ => { - let next_i = i + 1; - if self[next_i].timer_ctl.cascade() { - self[next_i].add_cycles(num_overflows, irqs); - } + TimerAction::Overflow(num_overflows) => match i { + 3 => {} + _ => { + let next_i = i + 1; + if self[next_i].timer_ctl.cascade() { + self[next_i].add_cycles(num_overflows, irqs); } } - } + }, TimerAction::Increment => {} } } diff --git a/src/debugger/command.rs b/src/debugger/command.rs index a6f537b..efd6266 100644 --- a/src/debugger/command.rs +++ b/src/debugger/command.rs @@ -137,19 +137,19 @@ impl Command { debugger.stop(); } AddBreakpoint(addr) => { - if !debugger.breakpoints.contains(&addr) { - let new_index = debugger.breakpoints.len(); - debugger.breakpoints.push(addr); + if !debugger.gba.cpu.breakpoints.contains(&addr) { + let new_index = debugger.gba.cpu.breakpoints.len(); + debugger.gba.cpu.breakpoints.push(addr); println!("added breakpoint [{}] 0x{:08x}", new_index, addr); } else { println!("breakpoint already exists!") } } DelBreakpoint(addr) => debugger.delete_breakpoint(addr), - ClearBreakpoints => debugger.breakpoints.clear(), + ClearBreakpoints => debugger.gba.cpu.breakpoints.clear(), ListBreakpoints => { println!("breakpoint list:"); - for (i, b) in debugger.breakpoints.iter().enumerate() { + for (i, b) in debugger.gba.cpu.breakpoints.iter().enumerate() { println!("[{}] 0x{:08x}", i, b) } } diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs index 66a4694..d073a87 100644 --- a/src/debugger/mod.rs +++ b/src/debugger/mod.rs @@ -36,7 +36,6 @@ type DebuggerResult = Result; pub struct Debugger { pub gba: GameBoyAdvance, running: bool, - breakpoints: Vec, pub previous_command: Option, } @@ -44,7 +43,6 @@ impl Debugger { pub fn new(gba: GameBoyAdvance) -> Debugger { Debugger { gba: gba, - breakpoints: Vec::new(), running: false, previous_command: None, } @@ -52,7 +50,7 @@ impl Debugger { pub fn check_breakpoint(&self) -> Option { let next_pc = self.gba.cpu.get_next_pc(); - for bp in &self.breakpoints { + for bp in &self.gba.cpu.breakpoints { if *bp == next_pc { return Some(next_pc); } @@ -62,7 +60,7 @@ impl Debugger { } pub fn delete_breakpoint(&mut self, addr: u32) { - self.breakpoints.retain(|&a| a != addr); + self.gba.cpu.breakpoints.retain(|&a| a != addr); } fn decode_reg(&self, s: &str) -> DebuggerResult { diff --git a/src/debugger/tile_view.rs b/src/debugger/tile_view.rs index 127ca9a..0060d24 100644 --- a/src/debugger/tile_view.rs +++ b/src/debugger/tile_view.rs @@ -21,7 +21,7 @@ fn draw_tile( let index = io .gpu .read_pixel_index(&gba.sysbus, tile_addr, x, y, pixel_format); - let color = io.gpu.get_palette_color(&gba.sysbus, index as u32, 0); + let color = io.gpu.get_palette_color(&gba.sysbus, index as u32, 0, 0); canvas.set_draw_color(Color::RGB( (color.r() as u8) << 3, (color.g() as u8) << 3,