Implement DMA, WIP

I have fought very hard against the rust ownership model,
In the end for DMA to play nice with my code, I had to resort to use
unsafe code for now..

The DMA implementation itself is not accurate to say the least, but will
have to do for now.

Tonc's dma_demo.gba plays but with a visual glitch.


Former-commit-id: 3b9cdcb2d09c78701290f2c48b77f9f3487e85c9
This commit is contained in:
Michel Heily 2019-11-09 01:43:43 +02:00
parent 3a1d5c10ce
commit c78a111ad4
8 changed files with 427 additions and 289 deletions

View file

@ -1,74 +1,210 @@
// use super::arm7tdmi::{Addr, Bus};
// use super::ioregs::consts::*;
// use super::sysbus::SysBus;
// use super::{EmuIoDev, Interrupt};
use std::collections::VecDeque;
// #[allow(non_camel_case_types)]
// #[derive(Debug)]
// pub struct DmaChannel {
// src_ioreg: Addr, /* Source Address register */
// dst_ioreg: Addr, /* Destination Address register */
// wc_ioreg: Addr, /* Word Count 14bit */
// }
use super::arm7tdmi::{Addr, Bus};
use super::sysbus::SysBus;
use super::{Interrupt, IrqBitmask, SyncedIoDevice};
// #[derive(Debug, Primitive)]
// enum DmaAddrControl {
// Increment = 0,
// Decrement = 1,
// Fixed = 2,
// IncrementReloadProhibited = 3,
// }
use num::FromPrimitive;
// #[derive(Debug)]
// enum DmaTransferType {
// Xfer16bit,
// Xfer32bit,
// }
#[derive(Debug)]
enum DmaTransferType {
Xfer16bit,
Xfer32bit,
}
// #[derive(Debug, Primitive)]
// enum DmaStartTiming {
// Immediately = 0,
// VBlank = 1,
// HBlank = 2,
// Special = 3,
// }
#[derive(Debug)]
pub struct DmaChannel {
id: usize,
// #[derive(Debug)]
// struct DmaControl {
// dst_addr_ctl: DmaAddrControl,
// src_addr_ctl: DmaAddrControl,
// repeat: bool,
// xfer: DmaTransferType,
// start_timing: DmaStartTiming,
// irq_upon_end_of_wc: bool,
// enable: bool,
// }
pub src: u32,
pub dst: u32,
pub wc: u32,
pub ctrl: DmaChannelCtrl,
// impl DmaChannel {
// pub fn new(src_ioreg: Addr, dst_ioreg: Addr, wc_ioreg: Addr) -> DmaChannel {
// DmaChannel {
// src_ioreg,
// dst_ioreg,
// wc_ioreg,
// }
// }
running: bool,
cycles: usize,
start_cycles: usize,
irq: Interrupt,
}
// // fn src_addr(&self, sysbus: &SysBus) -> Addr {
// // sysbus.ioregs.read_32(self.src_ioreg - IO_BASE) as Addr
// // }
impl DmaChannel {
pub fn new(id: usize) -> DmaChannel {
if id > 3 {
panic!("invalid dma id {}", id);
}
DmaChannel {
id: id,
irq: Interrupt::from_usize(id + 8).unwrap(),
running: false,
src: 0,
dst: 0,
wc: 0,
ctrl: DmaChannelCtrl(0),
cycles: 0,
start_cycles: 0,
}
}
// // fn dst_addr(&self, sysbus: &SysBus) -> Addr {
// // sysbus.ioregs.read_32(self.dst_ioreg - IO_BASE) as Addr
// // }
pub fn is_running(&self) -> bool {
self.running
}
// // fn word_count(&self, sysbus: &SysBus) -> usize {
// // sysbus.ioregs.read_reg(self.wc_ioreg) as usize
// // }
// }
pub fn write_src_low(&mut self, low: u16) {
let src = self.src;
self.src = (src & 0xffff0000) | (low as u32);
}
// impl EmuIoDev for DmaChannel {
// fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> (usize, Option<Interrupt>) {
// // TODO
// (0, None)
// }
// }
pub fn write_src_high(&mut self, high: u16) {
let src = self.src;
let high = high as u32;
self.src = (src & 0xffff) | (high << 16);
}
pub fn write_dst_low(&mut self, low: u16) {
let dst = self.dst;
self.dst = (dst & 0xffff0000) | (low as u32);
}
pub fn write_dst_high(&mut self, high: u16) {
let dst = self.dst;
let high = high as u32;
self.dst = (dst & 0xffff) | (high << 16);
}
pub fn write_word_count(&mut self, value: u16) {
self.wc = value as u32;
}
pub fn write_dma_ctrl(&mut self, value: u16) -> bool {
let ctrl = DmaChannelCtrl(value);
let mut start_immediately = false;
if ctrl.is_enabled() {
self.start_cycles = self.cycles;
self.running = true;
if ctrl.timing() == 0 {
start_immediately = true;
}
}
self.ctrl = ctrl;
return start_immediately;
}
fn xfer(&mut self, sb: &mut SysBus, irqs: &mut IrqBitmask) {
let word_size = if self.ctrl.is_32bit() { 4 } else { 2 };
let dst_rld = self.dst;
for word in 0..self.wc {
if word_size == 4 {
let w = sb.read_32(self.src);
sb.write_32(self.dst, w)
} else {
let hw = sb.read_16(self.src);
// println!("src {:x} dst {:x}", self.src, self.dst);
sb.write_16(self.dst, hw)
}
match self.ctrl.src_adj() {
/* Increment */ 0 => self.src += word_size,
/* Decrement */ 1 => self.src -= word_size,
/* Fixed */ 2 => {}
_ => panic!("forbidden DMA source address adjustment"),
}
match self.ctrl.dst_adj() {
/* Increment[+Reload] */ 0 | 3 => self.dst += word_size,
/* Decrement */ 1 => self.dst -= word_size,
/* Fixed */ 2 => {}
_ => panic!("forbidden DMA dest address adjustment"),
}
}
if self.ctrl.is_triggering_irq() {
irqs.add_irq(self.irq);
}
if self.ctrl.repeat() {
self.start_cycles = self.cycles;
/* reload */
if 3 == self.ctrl.dst_adj() {
self.dst = dst_rld;
}
} else {
self.running = false;
self.ctrl.set_enabled(false);
}
}
}
#[derive(Debug)]
pub struct DmaController {
pub channels: [DmaChannel; 4],
xfers_queue: VecDeque<usize>,
cycles: usize,
}
impl DmaController {
pub fn new() -> DmaController {
DmaController {
channels: [
DmaChannel::new(0),
DmaChannel::new(1),
DmaChannel::new(2),
DmaChannel::new(3),
],
xfers_queue: VecDeque::new(),
cycles: 0,
}
}
pub fn perform_work(&mut self, sb: &mut SysBus, irqs: &mut IrqBitmask) -> bool {
if self.xfers_queue.is_empty() {
false
} else {
while let Some(id) = self.xfers_queue.pop_front() {
self.channels[id].xfer(sb, irqs)
}
true
}
}
pub fn write_16(&mut self, channel_id: usize, ofs: u32, value: u16) {
match ofs {
0 => self.channels[channel_id].write_src_low(value),
2 => self.channels[channel_id].write_src_high(value),
4 => self.channels[channel_id].write_dst_low(value),
6 => self.channels[channel_id].write_dst_high(value),
8 => self.channels[channel_id].write_word_count(value),
10 => {
if self.channels[channel_id].write_dma_ctrl(value) {
self.xfers_queue.push_back(channel_id)
}
}
_ => panic!("Invalid dma offset"),
}
}
pub fn notify_vblank(&mut self) {
for i in 0..4 {
if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 1 {
self.xfers_queue.push_back(i);
}
}
}
pub fn notify_hblank(&mut self) {
for i in 0..4 {
if self.channels[i].ctrl.is_enabled() && self.channels[i].ctrl.timing() == 2 {
self.xfers_queue.push_back(i);
}
}
}
}
bitfield! {
#[derive(Default)]
pub struct DmaChannelCtrl(u16);
impl Debug;
u16;
dst_adj, _ : 6, 5;
src_adj, _ : 8, 7;
repeat, _ : 9;
is_32bit, _: 10;
timing, _: 13, 12;
is_triggering_irq, _: 14;
is_enabled, set_enabled: 15;
}

View file

@ -1,42 +1,19 @@
/// Struct containing everything
///
use std::cell::RefCell;
use std::rc::Rc;
use super::arm7tdmi::{exception::Exception, Core, DecodedInstruction};
use super::cartridge::Cartridge;
use super::gpu::*;
use super::interrupt::*;
use super::ioregs::IoRegs;
use super::iodev::*;
use super::sysbus::SysBus;
use super::timer::Timers;
use super::GBAResult;
use super::SyncedIoDevice;
use crate::backend::*;
#[derive(Debug)]
pub struct IoDevices {
pub intc: InterruptController,
pub gpu: Gpu,
pub timers: Timers,
}
impl IoDevices {
pub fn new() -> IoDevices {
IoDevices {
intc: InterruptController::new(),
gpu: Gpu::new(),
timers: Timers::new(),
}
}
}
pub struct GameBoyAdvance {
backend: Box<EmulatorBackend>,
backend: Box<dyn EmulatorBackend>,
pub cpu: Core,
pub sysbus: SysBus,
pub io: Rc<RefCell<IoDevices>>,
pub sysbus: Box<SysBus>,
}
impl GameBoyAdvance {
@ -44,51 +21,63 @@ impl GameBoyAdvance {
cpu: Core,
bios_rom: Vec<u8>,
gamepak: Cartridge,
backend: Box<EmulatorBackend>,
backend: Box<dyn EmulatorBackend>,
) -> GameBoyAdvance {
let io = Rc::new(RefCell::new(IoDevices::new()));
let ioregs = IoRegs::new(io.clone());
let sysbus = SysBus::new(io.clone(), bios_rom, gamepak, ioregs);
let io = IoDevices::new();
GameBoyAdvance {
backend: backend,
cpu: cpu,
sysbus: sysbus,
io: io.clone(),
sysbus: Box::new(SysBus::new(io, bios_rom, gamepak)),
}
}
pub fn frame(&mut self) {
self.update_key_state();
while self.io.borrow().gpu.state != GpuState::VBlank {
let cycles = self.emulate_cpu();
self.emulate_peripherals(cycles);
while self.sysbus.io.gpu.state != GpuState::VBlank {
self.step_new();
}
self.backend.render(self.io.borrow().gpu.get_framebuffer());
while self.io.borrow().gpu.state == GpuState::VBlank {
let cycles = self.emulate_cpu();
self.emulate_peripherals(cycles);
self.backend.render(self.sysbus.io.gpu.get_framebuffer());
while self.sysbus.io.gpu.state == GpuState::VBlank {
self.step_new();
}
}
fn update_key_state(&mut self) {
self.sysbus.ioregs.keyinput = self.backend.get_key_state();
self.sysbus.io.keyinput = self.backend.get_key_state();
}
pub fn emulate_cpu(&mut self) -> usize {
// TODO deprecate
pub fn step(&mut self) -> GBAResult<DecodedInstruction> {
let previous_cycles = self.cpu.cycles;
self.cpu.step(&mut self.sysbus).unwrap();
self.cpu.cycles - previous_cycles
let executed_insn = self.cpu.step_one(&mut self.sysbus)?;
let cycles = self.cpu.cycles - previous_cycles;
Ok(executed_insn)
}
pub fn emulate_peripherals(&mut self, cycles: usize) {
pub fn step_new(&mut self) {
let mut irqs = IrqBitmask(0);
let mut io = self.io.borrow_mut();
let previous_cycles = self.cpu.cycles;
// // I hate myself for doing this, but rust left me no choice.
let io = unsafe {
let ptr = &mut *self.sysbus as *mut SysBus;
&mut (*ptr).io as &mut IoDevices
};
if !io.dmac.perform_work(&mut self.sysbus, &mut irqs) {
self.cpu.step(&mut self.sysbus).unwrap();
}
let cycles = self.cpu.cycles - previous_cycles;
io.timers.step(cycles, &mut self.sysbus, &mut irqs);
io.gpu.step(cycles, &mut self.sysbus, &mut irqs);
if let Some(new_gpu_state) = io.gpu.step(cycles, &mut self.sysbus, &mut irqs) {
match new_gpu_state {
GpuState::VBlank => io.dmac.notify_vblank(),
GpuState::HBlank => io.dmac.notify_hblank(),
_ => {}
}
}
if !self.cpu.cpsr.irq_disabled() {
io.intc.request_irqs(irqs);
@ -97,18 +86,4 @@ impl GameBoyAdvance {
}
}
}
pub fn step(&mut self) -> GBAResult<DecodedInstruction> {
let previous_cycles = self.cpu.cycles;
let executed_insn = self.cpu.step_one(&mut self.sysbus)?;
let cycles = self.cpu.cycles - previous_cycles;
self.emulate_peripherals(cycles);
if self.io.borrow().gpu.state == GpuState::VBlank {
self.backend.render(self.io.borrow().gpu.get_framebuffer());
}
Ok(executed_insn)
}
}

View file

@ -444,10 +444,14 @@ impl Gpu {
pub fn get_framebuffer(&self) -> &[u32] {
&self.frame_buffer.0
}
}
impl SyncedIoDevice for Gpu {
fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
// Returns the new gpu state
pub fn step(
&mut self,
cycles: usize,
sb: &mut SysBus,
irqs: &mut IrqBitmask,
) -> Option<GpuState> {
self.cycles += cycles;
if self.dispstat.vcount_setting() != 0 {
@ -472,12 +476,14 @@ impl SyncedIoDevice for Gpu {
irqs.set_LCD_HBlank(true);
};
self.state = HBlank;
return Some(HBlank);
} else {
self.dispstat.set_vblank(true);
if self.dispstat.vblank_irq_enable() {
irqs.set_LCD_VBlank(true);
};
self.state = VBlank;
return Some(VBlank);
};
}
}
@ -487,6 +493,7 @@ impl SyncedIoDevice for Gpu {
self.state = HDraw;
self.dispstat.set_hblank(false);
self.dispstat.set_vblank(false);
return Some(HDraw);
}
}
VBlank => {
@ -497,9 +504,12 @@ impl SyncedIoDevice for Gpu {
self.dispstat.set_vblank(false);
self.current_scanline = 0;
self.render_scanline(sb);
return Some(HDraw);
}
}
}
return None;
}
}

View file

@ -1,28 +1,35 @@
use std::cell::RefCell;
use std::rc::Rc;
use super::arm7tdmi::{Addr, Bus};
use super::gba::IoDevices;
use super::dma::DmaController;
use super::gpu::regs::WindowFlags;
use super::gpu::*;
use super::interrupt::InterruptController;
use super::keypad;
use super::sysbus::BoxedMemory;
use super::timer::Timers;
use consts::*;
#[derive(Debug)]
pub struct IoRegs {
mem: BoxedMemory,
pub io: Rc<RefCell<IoDevices>>,
pub struct IoDevices {
pub intc: InterruptController,
pub gpu: Gpu,
pub timers: Timers,
pub dmac: DmaController,
pub keyinput: u16,
pub post_boot_flag: bool,
pub waitcnt: WaitControl, // TODO also implement 4000800
mem: BoxedMemory,
}
impl IoRegs {
pub fn new(io: Rc<RefCell<IoDevices>>) -> IoRegs {
IoRegs {
impl IoDevices {
pub fn new() -> IoDevices {
IoDevices {
gpu: Gpu::new(),
timers: Timers::new(),
dmac: DmaController::new(),
intc: InterruptController::new(),
mem: BoxedMemory::new(vec![0; 0x800].into_boxed_slice()),
io: io,
post_boot_flag: false,
keyinput: keypad::KEYINPUT_ALL_RELEASED,
waitcnt: WaitControl(0),
@ -30,14 +37,15 @@ impl IoRegs {
}
}
impl Bus for IoRegs {
impl Bus for IoDevices {
fn read_32(&self, addr: Addr) -> u32 {
(self.read_16(addr + 2) as u32) << 16 | (self.read_16(addr) as u32)
}
fn read_16(&self, addr: Addr) -> u16 {
let io = self.io.borrow();
match addr + IO_BASE {
let io = self;
let io_addr = addr + IO_BASE;
match io_addr {
REG_DISPCNT => io.gpu.dispcnt.0,
REG_DISPSTAT => io.gpu.dispstat.0,
REG_VCOUNT => io.gpu.current_scanline as u16,
@ -71,12 +79,24 @@ impl Bus for IoRegs {
REG_TM3CNT_L => io.timers[3].timer_data,
REG_TM3CNT_H => io.timers[3].timer_ctl.0,
REG_WAITCNT => self.waitcnt.0,
REG_DMA0CNT_H => io.dmac.channels[0].ctrl.0,
REG_DMA1CNT_H => io.dmac.channels[1].ctrl.0,
REG_DMA2CNT_H => io.dmac.channels[2].ctrl.0,
REG_DMA3CNT_H => io.dmac.channels[3].ctrl.0,
REG_POSTFLG => self.post_boot_flag as u16,
REG_WAITCNT => io.waitcnt.0,
REG_POSTFLG => io.post_boot_flag as u16,
REG_HALTCNT => 0,
REG_KEYINPUT => self.keyinput as u16,
_ => self.mem.read_16(addr),
REG_KEYINPUT => io.keyinput as u16,
_ => {
println!(
"Unimplemented read from {:x} {}",
io_addr,
io_reg_string(io_addr)
);
io.mem.read_16(addr)
}
}
}
@ -95,8 +115,10 @@ impl Bus for IoRegs {
}
fn write_16(&mut self, addr: Addr, value: u16) {
let mut io = self.io.borrow_mut();
match addr + IO_BASE {
let mut io = self;
io.mem.write_16(addr, value);
let io_addr = addr + IO_BASE;
match io_addr {
REG_DISPCNT => io.gpu.dispcnt.0 = value,
REG_DISPSTAT => io.gpu.dispstat.0 |= value & !3,
REG_BG0CNT => io.gpu.bg[0].bgcnt.0 = value,
@ -192,18 +214,22 @@ impl Bus for IoRegs {
}
REG_TM3CNT_H => io.timers[3].timer_ctl.0 = value,
REG_WAITCNT => self.waitcnt.0 = value,
DMA_BASE..=REG_DMA3CNT_H => {
let ofs = io_addr - DMA_BASE;
let channel_id = (ofs / 12) as usize;
io.dmac.write_16(channel_id, ofs % 12, value)
}
REG_POSTFLG => self.post_boot_flag = value != 0,
REG_WAITCNT => io.waitcnt.0 = value,
REG_POSTFLG => io.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)
io_addr,
io_reg_string(io_addr)
);
self.mem.write_16(addr, value);
}
}
}
@ -301,6 +327,7 @@ pub mod consts {
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 DMA_BASE: Addr = REG_DMA0SAD;
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

View file

@ -4,7 +4,7 @@ pub mod gpu;
pub mod sysbus;
pub use sysbus::SysBus;
pub mod interrupt;
pub mod ioregs;
pub mod iodev;
pub use interrupt::Interrupt;
pub use interrupt::IrqBitmask;
pub mod gba;

View file

@ -1,15 +1,13 @@
use std::cell::RefCell;
use std::fmt;
use std::ops::Add;
use std::rc::Rc;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use super::arm7tdmi::bus::Bus;
use super::arm7tdmi::Addr;
use super::gba::IoDevices;
use super::cartridge::Cartridge;
use super::gpu::GpuState;
use super::{cartridge::Cartridge, ioregs::IoRegs};
use super::iodev::IoDevices;
const VIDEO_RAM_SIZE: usize = 128 * 1024;
const WORK_RAM_SIZE: usize = 256 * 1024;
@ -141,13 +139,11 @@ impl Bus for DummyBus {
#[derive(Debug)]
pub struct SysBus {
pub io: Rc<RefCell<IoDevices>>,
pub io: IoDevices,
bios: BoxedMemory,
onboard_work_ram: BoxedMemory,
internal_work_ram: BoxedMemory,
/// Currently model the IOMem as regular buffer, later make it into something more sophisticated.
pub ioregs: IoRegs,
pub palette_ram: BoxedMemory,
pub vram: BoxedMemory,
pub oam: BoxedMemory,
@ -156,19 +152,13 @@ pub struct SysBus {
}
impl SysBus {
pub fn new(
io: Rc<RefCell<IoDevices>>,
bios_rom: Vec<u8>,
gamepak: Cartridge,
ioregs: IoRegs,
) -> SysBus {
pub fn new(io: IoDevices, bios_rom: Vec<u8>, gamepak: Cartridge) -> SysBus {
SysBus {
io: io,
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()),
vram: BoxedMemory::new(vec![0; VIDEO_RAM_SIZE].into_boxed_slice()),
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice()),
@ -177,13 +167,13 @@ impl SysBus {
}
}
fn map(&self, addr: Addr) -> (&Bus, Addr) {
fn map(&self, addr: Addr) -> (&dyn Bus, Addr) {
let ofs = addr & 0x00ff_ffff;
match addr & 0xff000000 {
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, {
IOMEM_ADDR => (&self.io, {
if ofs & 0xffff == 0x8000 {
0x800
} else {
@ -205,13 +195,13 @@ impl SysBus {
}
/// TODO proc-macro for generating this function
fn map_mut(&mut self, addr: Addr) -> (&mut Bus, Addr) {
fn map_mut(&mut self, addr: Addr) -> (&mut dyn Bus, Addr) {
let ofs = addr & 0x00ff_ffff;
match addr & 0xff000000 {
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, {
IOMEM_ADDR => (&mut self.io, {
if ofs & 0xffff == 0x8000 {
0x800
} else {
@ -249,24 +239,24 @@ impl SysBus {
MemoryAccessWidth::MemoryAccess32 => cycles += 2,
_ => cycles += 1,
}
if self.io.borrow().gpu.state == GpuState::HDraw {
if self.io.gpu.state == GpuState::HDraw {
cycles += 1;
}
}
GAMEPAK_WS0_ADDR => match access.0 {
MemoryAccessType::NonSeq => match access.1 {
MemoryAccessWidth::MemoryAccess32 => {
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
cycles += nonseq_cycles[self.io.waitcnt.ws0_first_access() as usize];
cycles += seq_cycles[self.io.waitcnt.ws0_second_access() as usize];
}
_ => {
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
cycles += nonseq_cycles[self.io.waitcnt.ws0_first_access() as usize];
}
},
MemoryAccessType::Seq => {
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
cycles += seq_cycles[self.io.waitcnt.ws0_second_access() as usize];
if access.1 == MemoryAccessWidth::MemoryAccess32 {
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
cycles += seq_cycles[self.io.waitcnt.ws0_second_access() as usize];
}
}
},

View file

@ -6,7 +6,7 @@ use crate::core::GBAError;
use crate::disass::Disassembler;
use super::palette_view::create_palette_view;
use super::tile_view::create_tile_view;
// use super::tile_view::create_tile_view;
use super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
use ansi_term::Colour;
@ -32,7 +32,7 @@ pub enum Command {
AddBreakpoint(Addr),
DelBreakpoint(Addr),
PaletteView,
TileView(u32),
// TileView(u32),
ClearBreakpoints,
ListBreakpoints,
Reset,
@ -44,7 +44,7 @@ impl Command {
use Command::*;
match *self {
Info => println!("{}", debugger.gba.cpu),
DisplayInfo => println!("GPU: {:#?}", debugger.gba.io.borrow().gpu),
DisplayInfo => { /*println!("GPU: {:#?}", debugger.gba.sysbus.io.gpu)*/ }
Step(count) => {
for _ in 0..count {
match debugger.gba.step() {
@ -154,7 +154,7 @@ impl Command {
}
}
PaletteView => create_palette_view(&debugger.gba.sysbus.palette_ram.mem),
TileView(bg) => create_tile_view(bg, &debugger.gba),
// TileView(bg) => create_tile_view(bg, &debugger.gba),
Reset => {
println!("resetting cpu...");
debugger.gba.cpu.reset(&mut debugger.gba.sysbus);
@ -297,13 +297,13 @@ impl Debugger {
))),
},
"palette-view" => Ok(Command::PaletteView),
"tiles" => {
if args.len() != 1 {
return Err(DebuggerError::InvalidCommandFormat("tile <bg>".to_string()));
}
let bg = self.val_number(&args[0])?;
Ok(Command::TileView(bg))
}
// "tiles" => {
// if args.len() != 1 {
// return Err(DebuggerError::InvalidCommandFormat("tile <bg>".to_string()));
// }
// let bg = self.val_number(&args[0])?;
// Ok(Command::TileView(bg))
// }
"bl" => Ok(Command::ListBreakpoints),
"q" | "quit" => Ok(Command::Quit),
"r" | "reset" => Ok(Command::Reset),

View file

@ -1,107 +1,107 @@
use std::time::Duration;
// use std::time::Duration;
use sdl2::event::Event;
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
use sdl2::render::Canvas;
// use sdl2::event::Event;
// use sdl2::pixels::Color;
// use sdl2::rect::{Point, Rect};
// use sdl2::render::Canvas;
use crate::core::gba::GameBoyAdvance;
use crate::core::gpu::PixelFormat;
// use crate::core::gba::GameBoyAdvance;
// use crate::core::gpu::PixelFormat;
fn draw_tile(
gba: &GameBoyAdvance,
tile_addr: u32,
pixel_format: PixelFormat,
p: Point,
canvas: &mut Canvas<sdl2::video::Window>,
) {
let io = gba.io.borrow();
for y in 0..8 {
for x in 0..8 {
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, 0);
canvas.set_draw_color(Color::RGB(
(color.r() as u8) << 3,
(color.g() as u8) << 3,
(color.b() as u8) << 3,
));
canvas.draw_point(p.offset(x as i32, y as i32)).unwrap();
}
}
}
// fn draw_tile(
// gba: &GameBoyAdvance,
// tile_addr: u32,
// pixel_format: PixelFormat,
// p: Point,
// canvas: &mut Canvas<sdl2::video::Window>,
// ) {
// let io = &mut gba.sysbus.io;
// for y in 0..8 {
// for x in 0..8 {
// 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, 0);
// canvas.set_draw_color(Color::RGB(
// (color.r() as u8) << 3,
// (color.g() as u8) << 3,
// (color.b() as u8) << 3,
// ));
// canvas.draw_point(p.offset(x as i32, y as i32)).unwrap();
// }
// }
// }
const TILESET_INITIAL_X: i32 = 0x20;
const TILESET_INITIAL_Y: i32 = 0x20;
// const TILESET_INITIAL_X: i32 = 0x20;
// const TILESET_INITIAL_Y: i32 = 0x20;
pub fn create_tile_view(bg: u32, gba: &GameBoyAdvance) {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
// pub fn create_tile_view(bg: u32, gba: &GameBoyAdvance) {
// let sdl_context = sdl2::init().unwrap();
// let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window("PaletteView", 512, 512)
.position_centered()
.build()
.unwrap();
// let window = video_subsystem
// .window("PaletteView", 512, 512)
// .position_centered()
// .build()
// .unwrap();
let mut canvas = window.into_canvas().build().unwrap();
// let mut canvas = window.into_canvas().build().unwrap();
let bgcnt = gba.io.borrow().gpu.bg[bg as usize].bgcnt.clone();
// let bgcnt = gba.sysbus.io.gpu.bg[bg as usize].bgcnt.clone();
let (tile_size, pixel_format) = bgcnt.tile_format();
let tileset_addr = bgcnt.char_block();
let tilemap_addr = bgcnt.screen_block();
let tiles_per_row = 32;
let num_tiles = 0x4000 / tile_size;
println!("tileset: {:#x}, tilemap: {:#x}", tileset_addr, tilemap_addr);
// let (tile_size, pixel_format) = bgcnt.tile_format();
// let tileset_addr = bgcnt.char_block();
// let tilemap_addr = bgcnt.screen_block();
// let tiles_per_row = 32;
// let num_tiles = 0x4000 / tile_size;
// println!("tileset: {:#x}, tilemap: {:#x}", tileset_addr, tilemap_addr);
let mut event_pump = sdl_context.event_pump().unwrap();
'running: loop {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. } => break 'running,
Event::MouseButtonDown { x, y, .. } => {
let click_point = Point::new(x, y);
let mut tile_x = TILESET_INITIAL_X;
let mut tile_y = TILESET_INITIAL_Y;
for t in 0..num_tiles {
let tile_addr = tileset_addr + t * tile_size;
if t != 0 && t % tiles_per_row == 0 {
tile_y += 10;
tile_x = TILESET_INITIAL_Y;
}
tile_x += 10;
if Rect::new(tile_x, tile_y, 8, 8).contains_point(click_point) {
println!("tile #{:#x}, addr={:#x}", t, tile_addr);
}
}
}
_ => {}
}
}
// let mut event_pump = sdl_context.event_pump().unwrap();
// 'running: loop {
// for event in event_pump.poll_iter() {
// match event {
// Event::Quit { .. } => break 'running,
// Event::MouseButtonDown { x, y, .. } => {
// let click_point = Point::new(x, y);
// let mut tile_x = TILESET_INITIAL_X;
// let mut tile_y = TILESET_INITIAL_Y;
// for t in 0..num_tiles {
// let tile_addr = tileset_addr + t * tile_size;
// if t != 0 && t % tiles_per_row == 0 {
// tile_y += 10;
// tile_x = TILESET_INITIAL_Y;
// }
// tile_x += 10;
// if Rect::new(tile_x, tile_y, 8, 8).contains_point(click_point) {
// println!("tile #{:#x}, addr={:#x}", t, tile_addr);
// }
// }
// }
// _ => {}
// }
// }
canvas.set_draw_color(Color::RGB(00, 00, 00));
canvas.clear();
// canvas.set_draw_color(Color::RGB(00, 00, 00));
// canvas.clear();
let mut tile_x = TILESET_INITIAL_X;
let mut tile_y = TILESET_INITIAL_Y;
for t in 0..num_tiles {
let tile_addr = tileset_addr + t * tile_size;
if t != 0 && t % tiles_per_row == 0 {
tile_y += 10;
tile_x = TILESET_INITIAL_Y;
}
tile_x += 10;
draw_tile(
gba,
tile_addr,
pixel_format,
Point::from((tile_x, tile_y)),
&mut canvas,
);
}
canvas.present();
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
}
}
// let mut tile_x = TILESET_INITIAL_X;
// let mut tile_y = TILESET_INITIAL_Y;
// for t in 0..num_tiles {
// let tile_addr = tileset_addr + t * tile_size;
// if t != 0 && t % tiles_per_row == 0 {
// tile_y += 10;
// tile_x = TILESET_INITIAL_Y;
// }
// tile_x += 10;
// draw_tile(
// gba,
// tile_addr,
// pixel_format,
// Point::from((tile_x, tile_y)),
// &mut canvas,
// );
// }
// canvas.present();
// ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
// }
// }