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:
parent
3a1d5c10ce
commit
c78a111ad4
264
src/core/dma.rs
264
src/core/dma.rs
|
@ -1,74 +1,210 @@
|
||||||
// use super::arm7tdmi::{Addr, Bus};
|
use std::collections::VecDeque;
|
||||||
// use super::ioregs::consts::*;
|
|
||||||
// use super::sysbus::SysBus;
|
|
||||||
// use super::{EmuIoDev, Interrupt};
|
|
||||||
|
|
||||||
// #[allow(non_camel_case_types)]
|
use super::arm7tdmi::{Addr, Bus};
|
||||||
// #[derive(Debug)]
|
use super::sysbus::SysBus;
|
||||||
// pub struct DmaChannel {
|
use super::{Interrupt, IrqBitmask, SyncedIoDevice};
|
||||||
// src_ioreg: Addr, /* Source Address register */
|
|
||||||
// dst_ioreg: Addr, /* Destination Address register */
|
|
||||||
// wc_ioreg: Addr, /* Word Count 14bit */
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Debug, Primitive)]
|
use num::FromPrimitive;
|
||||||
// enum DmaAddrControl {
|
|
||||||
// Increment = 0,
|
|
||||||
// Decrement = 1,
|
|
||||||
// Fixed = 2,
|
|
||||||
// IncrementReloadProhibited = 3,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Debug)]
|
#[derive(Debug)]
|
||||||
// enum DmaTransferType {
|
enum DmaTransferType {
|
||||||
// Xfer16bit,
|
Xfer16bit,
|
||||||
// Xfer32bit,
|
Xfer32bit,
|
||||||
// }
|
}
|
||||||
|
|
||||||
// #[derive(Debug, Primitive)]
|
#[derive(Debug)]
|
||||||
// enum DmaStartTiming {
|
pub struct DmaChannel {
|
||||||
// Immediately = 0,
|
id: usize,
|
||||||
// VBlank = 1,
|
|
||||||
// HBlank = 2,
|
|
||||||
// Special = 3,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[derive(Debug)]
|
pub src: u32,
|
||||||
// struct DmaControl {
|
pub dst: u32,
|
||||||
// dst_addr_ctl: DmaAddrControl,
|
pub wc: u32,
|
||||||
// src_addr_ctl: DmaAddrControl,
|
pub ctrl: DmaChannelCtrl,
|
||||||
// repeat: bool,
|
|
||||||
// xfer: DmaTransferType,
|
|
||||||
// start_timing: DmaStartTiming,
|
|
||||||
// irq_upon_end_of_wc: bool,
|
|
||||||
// enable: bool,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl DmaChannel {
|
running: bool,
|
||||||
// pub fn new(src_ioreg: Addr, dst_ioreg: Addr, wc_ioreg: Addr) -> DmaChannel {
|
cycles: usize,
|
||||||
// DmaChannel {
|
start_cycles: usize,
|
||||||
// src_ioreg,
|
irq: Interrupt,
|
||||||
// dst_ioreg,
|
}
|
||||||
// wc_ioreg,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // fn src_addr(&self, sysbus: &SysBus) -> Addr {
|
impl DmaChannel {
|
||||||
// // sysbus.ioregs.read_32(self.src_ioreg - IO_BASE) as Addr
|
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 {
|
pub fn is_running(&self) -> bool {
|
||||||
// // sysbus.ioregs.read_32(self.dst_ioreg - IO_BASE) as Addr
|
self.running
|
||||||
// // }
|
}
|
||||||
|
|
||||||
// // fn word_count(&self, sysbus: &SysBus) -> usize {
|
pub fn write_src_low(&mut self, low: u16) {
|
||||||
// // sysbus.ioregs.read_reg(self.wc_ioreg) as usize
|
let src = self.src;
|
||||||
// // }
|
self.src = (src & 0xffff0000) | (low as u32);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// impl EmuIoDev for DmaChannel {
|
pub fn write_src_high(&mut self, high: u16) {
|
||||||
// fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> (usize, Option<Interrupt>) {
|
let src = self.src;
|
||||||
// // TODO
|
let high = high as u32;
|
||||||
// (0, None)
|
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;
|
||||||
|
}
|
||||||
|
|
103
src/core/gba.rs
103
src/core/gba.rs
|
@ -1,42 +1,19 @@
|
||||||
/// Struct containing everything
|
/// Struct containing everything
|
||||||
///
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use super::arm7tdmi::{exception::Exception, Core, DecodedInstruction};
|
use super::arm7tdmi::{exception::Exception, Core, DecodedInstruction};
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::gpu::*;
|
use super::gpu::*;
|
||||||
use super::interrupt::*;
|
use super::interrupt::*;
|
||||||
use super::ioregs::IoRegs;
|
use super::iodev::*;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
use super::timer::Timers;
|
|
||||||
use super::GBAResult;
|
use super::GBAResult;
|
||||||
use super::SyncedIoDevice;
|
use super::SyncedIoDevice;
|
||||||
use crate::backend::*;
|
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 {
|
pub struct GameBoyAdvance {
|
||||||
backend: Box<EmulatorBackend>,
|
backend: Box<dyn EmulatorBackend>,
|
||||||
pub cpu: Core,
|
pub cpu: Core,
|
||||||
pub sysbus: SysBus,
|
pub sysbus: Box<SysBus>,
|
||||||
|
|
||||||
pub io: Rc<RefCell<IoDevices>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameBoyAdvance {
|
impl GameBoyAdvance {
|
||||||
|
@ -44,51 +21,63 @@ impl GameBoyAdvance {
|
||||||
cpu: Core,
|
cpu: Core,
|
||||||
bios_rom: Vec<u8>,
|
bios_rom: Vec<u8>,
|
||||||
gamepak: Cartridge,
|
gamepak: Cartridge,
|
||||||
backend: Box<EmulatorBackend>,
|
backend: Box<dyn EmulatorBackend>,
|
||||||
) -> GameBoyAdvance {
|
) -> GameBoyAdvance {
|
||||||
let io = Rc::new(RefCell::new(IoDevices::new()));
|
let io = IoDevices::new();
|
||||||
|
|
||||||
let ioregs = IoRegs::new(io.clone());
|
|
||||||
let sysbus = SysBus::new(io.clone(), bios_rom, gamepak, ioregs);
|
|
||||||
|
|
||||||
GameBoyAdvance {
|
GameBoyAdvance {
|
||||||
backend: backend,
|
backend: backend,
|
||||||
cpu: cpu,
|
cpu: cpu,
|
||||||
sysbus: sysbus,
|
sysbus: Box::new(SysBus::new(io, bios_rom, gamepak)),
|
||||||
|
|
||||||
io: io.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame(&mut self) {
|
pub fn frame(&mut self) {
|
||||||
self.update_key_state();
|
self.update_key_state();
|
||||||
while self.io.borrow().gpu.state != GpuState::VBlank {
|
while self.sysbus.io.gpu.state != GpuState::VBlank {
|
||||||
let cycles = self.emulate_cpu();
|
self.step_new();
|
||||||
self.emulate_peripherals(cycles);
|
|
||||||
}
|
}
|
||||||
self.backend.render(self.io.borrow().gpu.get_framebuffer());
|
self.backend.render(self.sysbus.io.gpu.get_framebuffer());
|
||||||
while self.io.borrow().gpu.state == GpuState::VBlank {
|
while self.sysbus.io.gpu.state == GpuState::VBlank {
|
||||||
let cycles = self.emulate_cpu();
|
self.step_new();
|
||||||
self.emulate_peripherals(cycles);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_key_state(&mut self) {
|
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;
|
let previous_cycles = self.cpu.cycles;
|
||||||
self.cpu.step(&mut self.sysbus).unwrap();
|
let executed_insn = self.cpu.step_one(&mut self.sysbus)?;
|
||||||
self.cpu.cycles - previous_cycles
|
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 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.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() {
|
if !self.cpu.cpsr.irq_disabled() {
|
||||||
io.intc.request_irqs(irqs);
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -444,10 +444,14 @@ impl Gpu {
|
||||||
pub fn get_framebuffer(&self) -> &[u32] {
|
pub fn get_framebuffer(&self) -> &[u32] {
|
||||||
&self.frame_buffer.0
|
&self.frame_buffer.0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl SyncedIoDevice for Gpu {
|
// Returns the new gpu state
|
||||||
fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
pub fn step(
|
||||||
|
&mut self,
|
||||||
|
cycles: usize,
|
||||||
|
sb: &mut SysBus,
|
||||||
|
irqs: &mut IrqBitmask,
|
||||||
|
) -> Option<GpuState> {
|
||||||
self.cycles += cycles;
|
self.cycles += cycles;
|
||||||
|
|
||||||
if self.dispstat.vcount_setting() != 0 {
|
if self.dispstat.vcount_setting() != 0 {
|
||||||
|
@ -472,12 +476,14 @@ impl SyncedIoDevice for Gpu {
|
||||||
irqs.set_LCD_HBlank(true);
|
irqs.set_LCD_HBlank(true);
|
||||||
};
|
};
|
||||||
self.state = HBlank;
|
self.state = HBlank;
|
||||||
|
return Some(HBlank);
|
||||||
} else {
|
} else {
|
||||||
self.dispstat.set_vblank(true);
|
self.dispstat.set_vblank(true);
|
||||||
if self.dispstat.vblank_irq_enable() {
|
if self.dispstat.vblank_irq_enable() {
|
||||||
irqs.set_LCD_VBlank(true);
|
irqs.set_LCD_VBlank(true);
|
||||||
};
|
};
|
||||||
self.state = VBlank;
|
self.state = VBlank;
|
||||||
|
return Some(VBlank);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,6 +493,7 @@ impl SyncedIoDevice for Gpu {
|
||||||
self.state = HDraw;
|
self.state = HDraw;
|
||||||
self.dispstat.set_hblank(false);
|
self.dispstat.set_hblank(false);
|
||||||
self.dispstat.set_vblank(false);
|
self.dispstat.set_vblank(false);
|
||||||
|
return Some(HDraw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VBlank => {
|
VBlank => {
|
||||||
|
@ -497,9 +504,12 @@ impl SyncedIoDevice for Gpu {
|
||||||
self.dispstat.set_vblank(false);
|
self.dispstat.set_vblank(false);
|
||||||
self.current_scanline = 0;
|
self.current_scanline = 0;
|
||||||
self.render_scanline(sb);
|
self.render_scanline(sb);
|
||||||
|
return Some(HDraw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,35 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use super::arm7tdmi::{Addr, Bus};
|
use super::arm7tdmi::{Addr, Bus};
|
||||||
use super::gba::IoDevices;
|
use super::dma::DmaController;
|
||||||
use super::gpu::regs::WindowFlags;
|
use super::gpu::regs::WindowFlags;
|
||||||
|
use super::gpu::*;
|
||||||
|
use super::interrupt::InterruptController;
|
||||||
use super::keypad;
|
use super::keypad;
|
||||||
use super::sysbus::BoxedMemory;
|
use super::sysbus::BoxedMemory;
|
||||||
|
use super::timer::Timers;
|
||||||
|
|
||||||
use consts::*;
|
use consts::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IoRegs {
|
pub struct IoDevices {
|
||||||
mem: BoxedMemory,
|
pub intc: InterruptController,
|
||||||
pub io: Rc<RefCell<IoDevices>>,
|
pub gpu: Gpu,
|
||||||
|
pub timers: Timers,
|
||||||
|
pub dmac: DmaController,
|
||||||
pub keyinput: u16,
|
pub keyinput: u16,
|
||||||
pub post_boot_flag: bool,
|
pub post_boot_flag: bool,
|
||||||
pub waitcnt: WaitControl, // TODO also implement 4000800
|
pub waitcnt: WaitControl, // TODO also implement 4000800
|
||||||
|
|
||||||
|
mem: BoxedMemory,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoRegs {
|
impl IoDevices {
|
||||||
pub fn new(io: Rc<RefCell<IoDevices>>) -> IoRegs {
|
pub fn new() -> IoDevices {
|
||||||
IoRegs {
|
IoDevices {
|
||||||
|
gpu: Gpu::new(),
|
||||||
|
timers: Timers::new(),
|
||||||
|
dmac: DmaController::new(),
|
||||||
|
intc: InterruptController::new(),
|
||||||
mem: BoxedMemory::new(vec![0; 0x800].into_boxed_slice()),
|
mem: BoxedMemory::new(vec![0; 0x800].into_boxed_slice()),
|
||||||
io: io,
|
|
||||||
post_boot_flag: false,
|
post_boot_flag: false,
|
||||||
keyinput: keypad::KEYINPUT_ALL_RELEASED,
|
keyinput: keypad::KEYINPUT_ALL_RELEASED,
|
||||||
waitcnt: WaitControl(0),
|
waitcnt: WaitControl(0),
|
||||||
|
@ -30,14 +37,15 @@ impl IoRegs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus for IoRegs {
|
impl Bus for IoDevices {
|
||||||
fn read_32(&self, addr: Addr) -> u32 {
|
fn read_32(&self, addr: Addr) -> u32 {
|
||||||
(self.read_16(addr + 2) as u32) << 16 | (self.read_16(addr) as u32)
|
(self.read_16(addr + 2) as u32) << 16 | (self.read_16(addr) as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_16(&self, addr: Addr) -> u16 {
|
fn read_16(&self, addr: Addr) -> u16 {
|
||||||
let io = self.io.borrow();
|
let io = self;
|
||||||
match addr + IO_BASE {
|
let io_addr = addr + IO_BASE;
|
||||||
|
match io_addr {
|
||||||
REG_DISPCNT => io.gpu.dispcnt.0,
|
REG_DISPCNT => io.gpu.dispcnt.0,
|
||||||
REG_DISPSTAT => io.gpu.dispstat.0,
|
REG_DISPSTAT => io.gpu.dispstat.0,
|
||||||
REG_VCOUNT => io.gpu.current_scanline as u16,
|
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_L => io.timers[3].timer_data,
|
||||||
REG_TM3CNT_H => io.timers[3].timer_ctl.0,
|
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_HALTCNT => 0,
|
||||||
REG_KEYINPUT => self.keyinput as u16,
|
REG_KEYINPUT => io.keyinput as u16,
|
||||||
_ => self.mem.read_16(addr),
|
_ => {
|
||||||
|
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) {
|
fn write_16(&mut self, addr: Addr, value: u16) {
|
||||||
let mut io = self.io.borrow_mut();
|
let mut io = self;
|
||||||
match addr + IO_BASE {
|
io.mem.write_16(addr, value);
|
||||||
|
let io_addr = addr + IO_BASE;
|
||||||
|
match io_addr {
|
||||||
REG_DISPCNT => io.gpu.dispcnt.0 = value,
|
REG_DISPCNT => io.gpu.dispcnt.0 = value,
|
||||||
REG_DISPSTAT => io.gpu.dispstat.0 |= value & !3,
|
REG_DISPSTAT => io.gpu.dispstat.0 |= value & !3,
|
||||||
REG_BG0CNT => io.gpu.bg[0].bgcnt.0 = value,
|
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_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 => {}
|
REG_HALTCNT => {}
|
||||||
_ => {
|
_ => {
|
||||||
let ioreg_addr = IO_BASE + addr;
|
|
||||||
println!(
|
println!(
|
||||||
"Unimplemented write to {:x} {}",
|
"Unimplemented write to {:x} {}",
|
||||||
ioreg_addr,
|
io_addr,
|
||||||
io_reg_string(ioreg_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_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_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_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_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_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_L: Addr = 0x0400_00B8; // 2 W DMA 0 Word Count
|
|
@ -4,7 +4,7 @@ pub mod gpu;
|
||||||
pub mod sysbus;
|
pub mod sysbus;
|
||||||
pub use sysbus::SysBus;
|
pub use sysbus::SysBus;
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
pub mod ioregs;
|
pub mod iodev;
|
||||||
pub use interrupt::Interrupt;
|
pub use interrupt::Interrupt;
|
||||||
pub use interrupt::IrqBitmask;
|
pub use interrupt::IrqBitmask;
|
||||||
pub mod gba;
|
pub mod gba;
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use super::arm7tdmi::bus::Bus;
|
use super::arm7tdmi::bus::Bus;
|
||||||
use super::arm7tdmi::Addr;
|
use super::arm7tdmi::Addr;
|
||||||
use super::gba::IoDevices;
|
use super::cartridge::Cartridge;
|
||||||
use super::gpu::GpuState;
|
use super::gpu::GpuState;
|
||||||
use super::{cartridge::Cartridge, ioregs::IoRegs};
|
use super::iodev::IoDevices;
|
||||||
|
|
||||||
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
||||||
const WORK_RAM_SIZE: usize = 256 * 1024;
|
const WORK_RAM_SIZE: usize = 256 * 1024;
|
||||||
|
@ -141,13 +139,11 @@ impl Bus for DummyBus {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SysBus {
|
pub struct SysBus {
|
||||||
pub io: Rc<RefCell<IoDevices>>,
|
pub io: IoDevices,
|
||||||
|
|
||||||
bios: BoxedMemory,
|
bios: BoxedMemory,
|
||||||
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.
|
|
||||||
pub ioregs: IoRegs,
|
|
||||||
pub palette_ram: BoxedMemory,
|
pub palette_ram: BoxedMemory,
|
||||||
pub vram: BoxedMemory,
|
pub vram: BoxedMemory,
|
||||||
pub oam: BoxedMemory,
|
pub oam: BoxedMemory,
|
||||||
|
@ -156,19 +152,13 @@ pub struct SysBus {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysBus {
|
impl SysBus {
|
||||||
pub fn new(
|
pub fn new(io: IoDevices, bios_rom: Vec<u8>, gamepak: Cartridge) -> SysBus {
|
||||||
io: Rc<RefCell<IoDevices>>,
|
|
||||||
bios_rom: Vec<u8>,
|
|
||||||
gamepak: Cartridge,
|
|
||||||
ioregs: IoRegs,
|
|
||||||
) -> SysBus {
|
|
||||||
SysBus {
|
SysBus {
|
||||||
io: io,
|
io: io,
|
||||||
|
|
||||||
bios: BoxedMemory::new(bios_rom.into_boxed_slice()),
|
bios: BoxedMemory::new(bios_rom.into_boxed_slice()),
|
||||||
onboard_work_ram: BoxedMemory::new(vec![0; WORK_RAM_SIZE].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()),
|
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: BoxedMemory::new(vec![0; PALETTE_RAM_SIZE].into_boxed_slice()),
|
||||||
vram: BoxedMemory::new(vec![0; VIDEO_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()),
|
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;
|
let ofs = addr & 0x00ff_ffff;
|
||||||
match addr & 0xff000000 {
|
match addr & 0xff000000 {
|
||||||
BIOS_ADDR => (&self.bios, ofs),
|
BIOS_ADDR => (&self.bios, ofs),
|
||||||
EWRAM_ADDR => (&self.onboard_work_ram, ofs & 0x3_ffff),
|
EWRAM_ADDR => (&self.onboard_work_ram, ofs & 0x3_ffff),
|
||||||
IWRAM_ADDR => (&self.internal_work_ram, ofs & 0x7fff),
|
IWRAM_ADDR => (&self.internal_work_ram, ofs & 0x7fff),
|
||||||
IOMEM_ADDR => (&self.ioregs, {
|
IOMEM_ADDR => (&self.io, {
|
||||||
if ofs & 0xffff == 0x8000 {
|
if ofs & 0xffff == 0x8000 {
|
||||||
0x800
|
0x800
|
||||||
} else {
|
} else {
|
||||||
|
@ -205,13 +195,13 @@ impl SysBus {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO proc-macro for generating this function
|
/// 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;
|
let ofs = addr & 0x00ff_ffff;
|
||||||
match addr & 0xff000000 {
|
match addr & 0xff000000 {
|
||||||
BIOS_ADDR => (&mut self.bios, ofs),
|
BIOS_ADDR => (&mut self.bios, ofs),
|
||||||
EWRAM_ADDR => (&mut self.onboard_work_ram, ofs & 0x3_ffff),
|
EWRAM_ADDR => (&mut self.onboard_work_ram, ofs & 0x3_ffff),
|
||||||
IWRAM_ADDR => (&mut self.internal_work_ram, ofs & 0x7fff),
|
IWRAM_ADDR => (&mut self.internal_work_ram, ofs & 0x7fff),
|
||||||
IOMEM_ADDR => (&mut self.ioregs, {
|
IOMEM_ADDR => (&mut self.io, {
|
||||||
if ofs & 0xffff == 0x8000 {
|
if ofs & 0xffff == 0x8000 {
|
||||||
0x800
|
0x800
|
||||||
} else {
|
} else {
|
||||||
|
@ -249,24 +239,24 @@ impl SysBus {
|
||||||
MemoryAccessWidth::MemoryAccess32 => cycles += 2,
|
MemoryAccessWidth::MemoryAccess32 => cycles += 2,
|
||||||
_ => cycles += 1,
|
_ => cycles += 1,
|
||||||
}
|
}
|
||||||
if self.io.borrow().gpu.state == GpuState::HDraw {
|
if self.io.gpu.state == GpuState::HDraw {
|
||||||
cycles += 1;
|
cycles += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GAMEPAK_WS0_ADDR => match access.0 {
|
GAMEPAK_WS0_ADDR => match access.0 {
|
||||||
MemoryAccessType::NonSeq => match access.1 {
|
MemoryAccessType::NonSeq => match access.1 {
|
||||||
MemoryAccessWidth::MemoryAccess32 => {
|
MemoryAccessWidth::MemoryAccess32 => {
|
||||||
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
|
cycles += nonseq_cycles[self.io.waitcnt.ws0_first_access() as usize];
|
||||||
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_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 => {
|
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 {
|
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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::core::GBAError;
|
||||||
use crate::disass::Disassembler;
|
use crate::disass::Disassembler;
|
||||||
|
|
||||||
use super::palette_view::create_palette_view;
|
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 super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
|
||||||
|
|
||||||
use ansi_term::Colour;
|
use ansi_term::Colour;
|
||||||
|
@ -32,7 +32,7 @@ pub enum Command {
|
||||||
AddBreakpoint(Addr),
|
AddBreakpoint(Addr),
|
||||||
DelBreakpoint(Addr),
|
DelBreakpoint(Addr),
|
||||||
PaletteView,
|
PaletteView,
|
||||||
TileView(u32),
|
// TileView(u32),
|
||||||
ClearBreakpoints,
|
ClearBreakpoints,
|
||||||
ListBreakpoints,
|
ListBreakpoints,
|
||||||
Reset,
|
Reset,
|
||||||
|
@ -44,7 +44,7 @@ impl Command {
|
||||||
use Command::*;
|
use Command::*;
|
||||||
match *self {
|
match *self {
|
||||||
Info => println!("{}", debugger.gba.cpu),
|
Info => println!("{}", debugger.gba.cpu),
|
||||||
DisplayInfo => println!("GPU: {:#?}", debugger.gba.io.borrow().gpu),
|
DisplayInfo => { /*println!("GPU: {:#?}", debugger.gba.sysbus.io.gpu)*/ }
|
||||||
Step(count) => {
|
Step(count) => {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
match debugger.gba.step() {
|
match debugger.gba.step() {
|
||||||
|
@ -154,7 +154,7 @@ impl Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaletteView => create_palette_view(&debugger.gba.sysbus.palette_ram.mem),
|
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 => {
|
Reset => {
|
||||||
println!("resetting cpu...");
|
println!("resetting cpu...");
|
||||||
debugger.gba.cpu.reset(&mut debugger.gba.sysbus);
|
debugger.gba.cpu.reset(&mut debugger.gba.sysbus);
|
||||||
|
@ -297,13 +297,13 @@ impl Debugger {
|
||||||
))),
|
))),
|
||||||
},
|
},
|
||||||
"palette-view" => Ok(Command::PaletteView),
|
"palette-view" => Ok(Command::PaletteView),
|
||||||
"tiles" => {
|
// "tiles" => {
|
||||||
if args.len() != 1 {
|
// if args.len() != 1 {
|
||||||
return Err(DebuggerError::InvalidCommandFormat("tile <bg>".to_string()));
|
// return Err(DebuggerError::InvalidCommandFormat("tile <bg>".to_string()));
|
||||||
}
|
// }
|
||||||
let bg = self.val_number(&args[0])?;
|
// let bg = self.val_number(&args[0])?;
|
||||||
Ok(Command::TileView(bg))
|
// Ok(Command::TileView(bg))
|
||||||
}
|
// }
|
||||||
"bl" => Ok(Command::ListBreakpoints),
|
"bl" => Ok(Command::ListBreakpoints),
|
||||||
"q" | "quit" => Ok(Command::Quit),
|
"q" | "quit" => Ok(Command::Quit),
|
||||||
"r" | "reset" => Ok(Command::Reset),
|
"r" | "reset" => Ok(Command::Reset),
|
||||||
|
|
|
@ -1,107 +1,107 @@
|
||||||
use std::time::Duration;
|
// use std::time::Duration;
|
||||||
|
|
||||||
use sdl2::event::Event;
|
// use sdl2::event::Event;
|
||||||
use sdl2::pixels::Color;
|
// use sdl2::pixels::Color;
|
||||||
use sdl2::rect::{Point, Rect};
|
// use sdl2::rect::{Point, Rect};
|
||||||
use sdl2::render::Canvas;
|
// use sdl2::render::Canvas;
|
||||||
|
|
||||||
use crate::core::gba::GameBoyAdvance;
|
// use crate::core::gba::GameBoyAdvance;
|
||||||
use crate::core::gpu::PixelFormat;
|
// use crate::core::gpu::PixelFormat;
|
||||||
|
|
||||||
fn draw_tile(
|
// fn draw_tile(
|
||||||
gba: &GameBoyAdvance,
|
// gba: &GameBoyAdvance,
|
||||||
tile_addr: u32,
|
// tile_addr: u32,
|
||||||
pixel_format: PixelFormat,
|
// pixel_format: PixelFormat,
|
||||||
p: Point,
|
// p: Point,
|
||||||
canvas: &mut Canvas<sdl2::video::Window>,
|
// canvas: &mut Canvas<sdl2::video::Window>,
|
||||||
) {
|
// ) {
|
||||||
let io = gba.io.borrow();
|
// let io = &mut gba.sysbus.io;
|
||||||
for y in 0..8 {
|
// for y in 0..8 {
|
||||||
for x in 0..8 {
|
// for x in 0..8 {
|
||||||
let index = io
|
// let index = io
|
||||||
.gpu
|
// .gpu
|
||||||
.read_pixel_index(&gba.sysbus, tile_addr, x, y, pixel_format);
|
// .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);
|
// let color = io.gpu.get_palette_color(&gba.sysbus, index as u32, 0, 0);
|
||||||
canvas.set_draw_color(Color::RGB(
|
// canvas.set_draw_color(Color::RGB(
|
||||||
(color.r() as u8) << 3,
|
// (color.r() as u8) << 3,
|
||||||
(color.g() as u8) << 3,
|
// (color.g() as u8) << 3,
|
||||||
(color.b() as u8) << 3,
|
// (color.b() as u8) << 3,
|
||||||
));
|
// ));
|
||||||
canvas.draw_point(p.offset(x as i32, y as i32)).unwrap();
|
// canvas.draw_point(p.offset(x as i32, y as i32)).unwrap();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
const TILESET_INITIAL_X: i32 = 0x20;
|
// const TILESET_INITIAL_X: i32 = 0x20;
|
||||||
const TILESET_INITIAL_Y: i32 = 0x20;
|
// const TILESET_INITIAL_Y: i32 = 0x20;
|
||||||
|
|
||||||
pub fn create_tile_view(bg: u32, gba: &GameBoyAdvance) {
|
// pub fn create_tile_view(bg: u32, gba: &GameBoyAdvance) {
|
||||||
let sdl_context = sdl2::init().unwrap();
|
// let sdl_context = sdl2::init().unwrap();
|
||||||
let video_subsystem = sdl_context.video().unwrap();
|
// let video_subsystem = sdl_context.video().unwrap();
|
||||||
|
|
||||||
let window = video_subsystem
|
// let window = video_subsystem
|
||||||
.window("PaletteView", 512, 512)
|
// .window("PaletteView", 512, 512)
|
||||||
.position_centered()
|
// .position_centered()
|
||||||
.build()
|
// .build()
|
||||||
.unwrap();
|
// .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 (tile_size, pixel_format) = bgcnt.tile_format();
|
||||||
let tileset_addr = bgcnt.char_block();
|
// let tileset_addr = bgcnt.char_block();
|
||||||
let tilemap_addr = bgcnt.screen_block();
|
// let tilemap_addr = bgcnt.screen_block();
|
||||||
let tiles_per_row = 32;
|
// let tiles_per_row = 32;
|
||||||
let num_tiles = 0x4000 / tile_size;
|
// let num_tiles = 0x4000 / tile_size;
|
||||||
println!("tileset: {:#x}, tilemap: {:#x}", tileset_addr, tilemap_addr);
|
// println!("tileset: {:#x}, tilemap: {:#x}", tileset_addr, tilemap_addr);
|
||||||
|
|
||||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
// let mut event_pump = sdl_context.event_pump().unwrap();
|
||||||
'running: loop {
|
// 'running: loop {
|
||||||
for event in event_pump.poll_iter() {
|
// for event in event_pump.poll_iter() {
|
||||||
match event {
|
// match event {
|
||||||
Event::Quit { .. } => break 'running,
|
// Event::Quit { .. } => break 'running,
|
||||||
Event::MouseButtonDown { x, y, .. } => {
|
// Event::MouseButtonDown { x, y, .. } => {
|
||||||
let click_point = Point::new(x, y);
|
// let click_point = Point::new(x, y);
|
||||||
let mut tile_x = TILESET_INITIAL_X;
|
// let mut tile_x = TILESET_INITIAL_X;
|
||||||
let mut tile_y = TILESET_INITIAL_Y;
|
// let mut tile_y = TILESET_INITIAL_Y;
|
||||||
for t in 0..num_tiles {
|
// for t in 0..num_tiles {
|
||||||
let tile_addr = tileset_addr + t * tile_size;
|
// let tile_addr = tileset_addr + t * tile_size;
|
||||||
if t != 0 && t % tiles_per_row == 0 {
|
// if t != 0 && t % tiles_per_row == 0 {
|
||||||
tile_y += 10;
|
// tile_y += 10;
|
||||||
tile_x = TILESET_INITIAL_Y;
|
// tile_x = TILESET_INITIAL_Y;
|
||||||
}
|
// }
|
||||||
tile_x += 10;
|
// tile_x += 10;
|
||||||
if Rect::new(tile_x, tile_y, 8, 8).contains_point(click_point) {
|
// if Rect::new(tile_x, tile_y, 8, 8).contains_point(click_point) {
|
||||||
println!("tile #{:#x}, addr={:#x}", t, tile_addr);
|
// println!("tile #{:#x}, addr={:#x}", t, tile_addr);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
_ => {}
|
// _ => {}
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(00, 00, 00));
|
// canvas.set_draw_color(Color::RGB(00, 00, 00));
|
||||||
canvas.clear();
|
// canvas.clear();
|
||||||
|
|
||||||
let mut tile_x = TILESET_INITIAL_X;
|
// let mut tile_x = TILESET_INITIAL_X;
|
||||||
let mut tile_y = TILESET_INITIAL_Y;
|
// let mut tile_y = TILESET_INITIAL_Y;
|
||||||
for t in 0..num_tiles {
|
// for t in 0..num_tiles {
|
||||||
let tile_addr = tileset_addr + t * tile_size;
|
// let tile_addr = tileset_addr + t * tile_size;
|
||||||
if t != 0 && t % tiles_per_row == 0 {
|
// if t != 0 && t % tiles_per_row == 0 {
|
||||||
tile_y += 10;
|
// tile_y += 10;
|
||||||
tile_x = TILESET_INITIAL_Y;
|
// tile_x = TILESET_INITIAL_Y;
|
||||||
}
|
// }
|
||||||
tile_x += 10;
|
// tile_x += 10;
|
||||||
draw_tile(
|
// draw_tile(
|
||||||
gba,
|
// gba,
|
||||||
tile_addr,
|
// tile_addr,
|
||||||
pixel_format,
|
// pixel_format,
|
||||||
Point::from((tile_x, tile_y)),
|
// Point::from((tile_x, tile_y)),
|
||||||
&mut canvas,
|
// &mut canvas,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
canvas.present();
|
// canvas.present();
|
||||||
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
// ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
Reference in a new issue