[WIP] Timers
Seems to work, but the cycle's are not accurate so they run slowly Former-commit-id: a0b80acb4b68ed64caa535a0ec9f75a081d3aed4
This commit is contained in:
parent
c7dd713605
commit
c72bbb96fd
|
@ -3,20 +3,22 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::arm7tdmi::{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::ioregs::IoRegs;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
use super::EmuIoDev;
|
use super::timer::Timers;
|
||||||
use super::GBAResult;
|
use super::GBAResult;
|
||||||
|
use super::SyncedIoDevice;
|
||||||
use crate::backend::*;
|
use crate::backend::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IoDevices {
|
pub struct IoDevices {
|
||||||
pub intc: InterruptController,
|
pub intc: InterruptController,
|
||||||
pub gpu: Gpu,
|
pub gpu: Gpu,
|
||||||
|
pub timers: Timers,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoDevices {
|
impl IoDevices {
|
||||||
|
@ -24,6 +26,7 @@ impl IoDevices {
|
||||||
IoDevices {
|
IoDevices {
|
||||||
intc: InterruptController::new(),
|
intc: InterruptController::new(),
|
||||||
gpu: Gpu::new(),
|
gpu: Gpu::new(),
|
||||||
|
timers: Timers::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +49,7 @@ impl GameBoyAdvance {
|
||||||
let io = Rc::new(RefCell::new(IoDevices::new()));
|
let io = Rc::new(RefCell::new(IoDevices::new()));
|
||||||
|
|
||||||
let ioregs = IoRegs::new(io.clone());
|
let ioregs = IoRegs::new(io.clone());
|
||||||
let sysbus = SysBus::new(bios_rom, gamepak, ioregs);
|
let sysbus = SysBus::new(io.clone(), bios_rom, gamepak, ioregs);
|
||||||
|
|
||||||
GameBoyAdvance {
|
GameBoyAdvance {
|
||||||
backend: backend,
|
backend: backend,
|
||||||
|
@ -81,10 +84,17 @@ impl GameBoyAdvance {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emulate_peripherals(&mut self, cycles: usize) {
|
pub fn emulate_peripherals(&mut self, cycles: usize) {
|
||||||
|
let mut irqs = IrqBitmask(0);
|
||||||
let mut io = self.io.borrow_mut();
|
let mut io = self.io.borrow_mut();
|
||||||
let (_, irq) = io.gpu.step(cycles, &mut self.sysbus);
|
|
||||||
if let Some(irq) = irq {
|
io.timers.step(cycles, &mut self.sysbus, &mut irqs);
|
||||||
io.intc.request_irq(&mut self.cpu, irq);
|
io.gpu.step(cycles, &mut self.sysbus, &mut irqs);
|
||||||
|
|
||||||
|
if !self.cpu.cpsr.irq_disabled() {
|
||||||
|
io.intc.request_irqs(irqs);
|
||||||
|
if io.intc.irq_pending() {
|
||||||
|
self.cpu.exception(Exception::Irq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -376,8 +376,8 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmuIoDev for Gpu {
|
impl SyncedIoDevice for Gpu {
|
||||||
fn step(&mut self, cycles: usize, sb: &mut SysBus) -> (usize, Option<Interrupt>) {
|
fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
||||||
self.cycles += cycles;
|
self.cycles += cycles;
|
||||||
|
|
||||||
if self.dispstat.vcount_setting() != 0 {
|
if self.dispstat.vcount_setting() != 0 {
|
||||||
|
@ -385,7 +385,7 @@ impl EmuIoDev for Gpu {
|
||||||
.set_vcount(self.dispstat.vcount_setting() == self.current_scanline as u16);
|
.set_vcount(self.dispstat.vcount_setting() == self.current_scanline as u16);
|
||||||
}
|
}
|
||||||
if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount() {
|
if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount() {
|
||||||
return (0, Some(Interrupt::LCD_VCounterMatch));
|
irqs.set_LCD_VCounterMatch(true);;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.state {
|
match self.state {
|
||||||
|
@ -394,28 +394,22 @@ impl EmuIoDev for Gpu {
|
||||||
self.current_scanline += 1;
|
self.current_scanline += 1;
|
||||||
self.cycles -= Gpu::CYCLES_HDRAW;
|
self.cycles -= Gpu::CYCLES_HDRAW;
|
||||||
|
|
||||||
let (new_state, irq) = if self.current_scanline < Gpu::DISPLAY_HEIGHT {
|
if self.current_scanline < Gpu::DISPLAY_HEIGHT {
|
||||||
self.scanline(sb);
|
self.scanline(sb);
|
||||||
// HBlank
|
// HBlank
|
||||||
self.dispstat.set_hblank(true);
|
self.dispstat.set_hblank(true);
|
||||||
let irq = if self.dispstat.hblank_irq_enable() {
|
if self.dispstat.hblank_irq_enable() {
|
||||||
Some(Interrupt::LCD_HBlank)
|
irqs.set_LCD_HBlank(true);
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
(HBlank, irq)
|
self.state = HBlank;
|
||||||
} else {
|
} else {
|
||||||
self.scanline(sb);
|
self.scanline(sb);
|
||||||
self.dispstat.set_vblank(true);
|
self.dispstat.set_vblank(true);
|
||||||
let irq = if self.dispstat.vblank_irq_enable() {
|
if self.dispstat.vblank_irq_enable() {
|
||||||
Some(Interrupt::LCD_VBlank)
|
irqs.set_LCD_VBlank(true);
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
(VBlank, irq)
|
self.state = VBlank;
|
||||||
};
|
};
|
||||||
self.state = new_state;
|
|
||||||
return (0, irq);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HBlank => {
|
HBlank => {
|
||||||
|
@ -424,7 +418,6 @@ impl EmuIoDev 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 (0, None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VBlank => {
|
VBlank => {
|
||||||
|
@ -435,12 +428,9 @@ impl EmuIoDev for Gpu {
|
||||||
self.dispstat.set_vblank(false);
|
self.dispstat.set_vblank(false);
|
||||||
self.current_scanline = 0;
|
self.current_scanline = 0;
|
||||||
self.scanline(sb);
|
self.scanline(sb);
|
||||||
return (0, None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(0, None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use super::arm7tdmi::{exception::Exception, Core};
|
use super::arm7tdmi::Core;
|
||||||
|
|
||||||
use crate::bit::BitIndex;
|
|
||||||
|
|
||||||
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
@ -21,34 +19,57 @@ pub enum Interrupt {
|
||||||
GamePak = 13,
|
GamePak = 13,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct InterruptController {
|
pub struct InterruptController {
|
||||||
pub interrupt_master_enable: bool,
|
pub interrupt_master_enable: bool,
|
||||||
pub interrupt_enable: u16,
|
pub interrupt_enable: IrqBitmask,
|
||||||
pub interrupt_flags: u16,
|
pub interrupt_flags: IrqBitmask,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InterruptController {
|
impl InterruptController {
|
||||||
pub fn new() -> InterruptController {
|
pub fn new() -> InterruptController {
|
||||||
InterruptController {
|
InterruptController {
|
||||||
interrupt_master_enable: false,
|
interrupt_master_enable: false,
|
||||||
interrupt_enable: 0,
|
..Default::default()
|
||||||
interrupt_flags: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interrupts_disabled(&self, cpu: &Core) -> bool {
|
pub fn request_irqs(&mut self, flags: IrqBitmask) {
|
||||||
cpu.cpsr.irq_disabled() | (self.interrupt_master_enable)
|
if !self.interrupt_master_enable {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn request_irq(&mut self, cpu: &mut Core, irq: Interrupt) {
|
|
||||||
if self.interrupts_disabled(cpu) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let irq_bit_index = irq as usize;
|
self.interrupt_flags.0 |= flags.0 & self.interrupt_enable.0;
|
||||||
if self.interrupt_enable.bit(irq_bit_index) {
|
|
||||||
self.interrupt_flags = 1 << irq_bit_index;
|
|
||||||
cpu.exception(Exception::Irq);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn irq_pending(&self) -> bool {
|
||||||
|
self.interrupt_master_enable & (self.interrupt_flags.0 != 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IrqBitmask {
|
||||||
|
pub fn add_irq(&mut self, i: Interrupt) {
|
||||||
|
self.0 |= 1 << (i as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Default, Copy, Clone, PartialEq)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub struct IrqBitmask(u16);
|
||||||
|
impl Debug;
|
||||||
|
u16;
|
||||||
|
pub LCD_VBlank, set_LCD_VBlank: 0;
|
||||||
|
pub LCD_HBlank, set_LCD_HBlank: 1;
|
||||||
|
pub LCD_VCounterMatch, set_LCD_VCounterMatch: 2;
|
||||||
|
pub Timer0_Overflow, set_Timer0_Overflow: 3;
|
||||||
|
pub Timer1_Overflow, set_Timer1_Overflow: 4;
|
||||||
|
pub Timer2_Overflow, set_Timer2_Overflow: 5;
|
||||||
|
pub Timer3_Overflow, set_Timer3_Overflow: 6;
|
||||||
|
pub SerialCommunication, set_SerialCommunication: 7;
|
||||||
|
pub DMA0, set_DMA0: 8;
|
||||||
|
pub DMA1, set_DMA1: 9;
|
||||||
|
pub DMA2, set_DMA2: 10;
|
||||||
|
pub DMA3, set_DMA3: 11;
|
||||||
|
pub Keypad, set_Keypad: 12;
|
||||||
|
pub GamePak, set_GamePak: 13;
|
||||||
|
}
|
||||||
|
|
|
@ -166,8 +166,17 @@ impl Bus for IoRegs {
|
||||||
REG_BLDY => io.gpu.bldy,
|
REG_BLDY => io.gpu.bldy,
|
||||||
|
|
||||||
REG_IME => io.intc.interrupt_master_enable as u16,
|
REG_IME => io.intc.interrupt_master_enable as u16,
|
||||||
REG_IE => io.intc.interrupt_enable as u16,
|
REG_IE => io.intc.interrupt_enable.0 as u16,
|
||||||
REG_IF => io.intc.interrupt_flags as u16,
|
REG_IF => io.intc.interrupt_flags.0 as u16,
|
||||||
|
|
||||||
|
REG_TM0CNT_L => io.timers[0].timer_data,
|
||||||
|
REG_TM0CNT_H => io.timers[0].timer_ctl.0,
|
||||||
|
REG_TM1CNT_L => io.timers[1].timer_data,
|
||||||
|
REG_TM1CNT_H => io.timers[1].timer_ctl.0,
|
||||||
|
REG_TM2CNT_L => io.timers[2].timer_data,
|
||||||
|
REG_TM2CNT_H => io.timers[2].timer_ctl.0,
|
||||||
|
REG_TM3CNT_L => io.timers[3].timer_data,
|
||||||
|
REG_TM3CNT_H => io.timers[3].timer_ctl.0,
|
||||||
|
|
||||||
REG_POSTFLG => self.post_boot_flag as u16,
|
REG_POSTFLG => self.post_boot_flag as u16,
|
||||||
REG_HALTCNT => 0,
|
REG_HALTCNT => 0,
|
||||||
|
@ -216,9 +225,33 @@ impl Bus for IoRegs {
|
||||||
REG_BLDALPHA => io.gpu.bldalpha = value,
|
REG_BLDALPHA => io.gpu.bldalpha = value,
|
||||||
REG_BLDY => io.gpu.bldy = value,
|
REG_BLDY => io.gpu.bldy = value,
|
||||||
|
|
||||||
REG_IME => io.intc.interrupt_master_enable = value == 1,
|
REG_IME => io.intc.interrupt_master_enable = value != 0,
|
||||||
REG_IE => io.intc.interrupt_enable = value,
|
REG_IE => io.intc.interrupt_enable.0 = value,
|
||||||
REG_IF => io.intc.interrupt_flags &= !value,
|
REG_IF => io.intc.interrupt_flags.0 &= !value,
|
||||||
|
|
||||||
|
REG_TM0CNT_L => {
|
||||||
|
io.timers[0].timer_data = value;
|
||||||
|
io.timers[0].initial_data = value;
|
||||||
|
}
|
||||||
|
REG_TM0CNT_H => io.timers[0].timer_ctl.0 = value,
|
||||||
|
|
||||||
|
REG_TM1CNT_L => {
|
||||||
|
io.timers[1].timer_data = value;
|
||||||
|
io.timers[0].initial_data = value;
|
||||||
|
}
|
||||||
|
REG_TM1CNT_H => io.timers[1].timer_ctl.0 = value,
|
||||||
|
|
||||||
|
REG_TM2CNT_L => {
|
||||||
|
io.timers[2].timer_data = value;
|
||||||
|
io.timers[0].initial_data = value;
|
||||||
|
}
|
||||||
|
REG_TM2CNT_H => io.timers[2].timer_ctl.0 = value,
|
||||||
|
|
||||||
|
REG_TM3CNT_L => {
|
||||||
|
io.timers[3].timer_data = value;
|
||||||
|
io.timers[0].initial_data = value;
|
||||||
|
}
|
||||||
|
REG_TM3CNT_H => io.timers[3].timer_ctl.0 = value,
|
||||||
|
|
||||||
REG_POSTFLG => self.post_boot_flag = value != 0,
|
REG_POSTFLG => self.post_boot_flag = value != 0,
|
||||||
REG_HALTCNT => {}
|
REG_HALTCNT => {}
|
||||||
|
|
|
@ -6,16 +6,18 @@ pub use sysbus::SysBus;
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
pub mod ioregs;
|
pub mod ioregs;
|
||||||
pub use interrupt::Interrupt;
|
pub use interrupt::Interrupt;
|
||||||
|
pub use interrupt::IrqBitmask;
|
||||||
pub mod gba;
|
pub mod gba;
|
||||||
pub use gba::GameBoyAdvance;
|
pub use gba::GameBoyAdvance;
|
||||||
pub mod dma;
|
pub mod dma;
|
||||||
pub mod keypad;
|
pub mod keypad;
|
||||||
pub mod palette;
|
pub mod palette;
|
||||||
|
pub mod timer;
|
||||||
|
|
||||||
use crate::debugger;
|
use crate::debugger;
|
||||||
|
|
||||||
pub trait EmuIoDev {
|
pub trait SyncedIoDevice {
|
||||||
fn step(&mut self, cycles: usize, sysbus: &mut SysBus) -> (usize, Option<Interrupt>);
|
fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{cartridge::Cartridge, ioregs::IoRegs};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
||||||
use super::arm7tdmi::Addr;
|
use super::arm7tdmi::Addr;
|
||||||
|
use super::gba::IoDevices;
|
||||||
|
use super::{cartridge::Cartridge, ioregs::IoRegs};
|
||||||
|
|
||||||
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;
|
||||||
|
@ -132,6 +135,8 @@ impl Bus for DummyBus {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SysBus {
|
pub struct SysBus {
|
||||||
|
pub io: Rc<RefCell<IoDevices>>,
|
||||||
|
|
||||||
bios: BoxedMemory,
|
bios: BoxedMemory,
|
||||||
onboard_work_ram: BoxedMemory,
|
onboard_work_ram: BoxedMemory,
|
||||||
internal_work_ram: BoxedMemory,
|
internal_work_ram: BoxedMemory,
|
||||||
|
@ -145,8 +150,15 @@ pub struct SysBus {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysBus {
|
impl SysBus {
|
||||||
pub fn new(bios_rom: Vec<u8>, gamepak: Cartridge, ioregs: IoRegs) -> SysBus {
|
pub fn new(
|
||||||
|
io: Rc<RefCell<IoDevices>>,
|
||||||
|
bios_rom: Vec<u8>,
|
||||||
|
gamepak: Cartridge,
|
||||||
|
ioregs: IoRegs,
|
||||||
|
) -> SysBus {
|
||||||
SysBus {
|
SysBus {
|
||||||
|
io: io,
|
||||||
|
|
||||||
bios: BoxedMemory::new(bios_rom.into_boxed_slice(), 0xff_ffff),
|
bios: BoxedMemory::new(bios_rom.into_boxed_slice(), 0xff_ffff),
|
||||||
onboard_work_ram: BoxedMemory::new_with_waitstate(
|
onboard_work_ram: BoxedMemory::new_with_waitstate(
|
||||||
vec![0; WORK_RAM_SIZE].into_boxed_slice(),
|
vec![0; WORK_RAM_SIZE].into_boxed_slice(),
|
||||||
|
|
125
src/core/timer.rs
Normal file
125
src/core/timer.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use super::interrupt::{Interrupt, IrqBitmask};
|
||||||
|
use super::sysbus::SysBus;
|
||||||
|
use super::SyncedIoDevice;
|
||||||
|
|
||||||
|
use num::FromPrimitive;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Timer {
|
||||||
|
// registers
|
||||||
|
pub timer_ctl: TimerCtl,
|
||||||
|
pub timer_data: u16,
|
||||||
|
|
||||||
|
timer_id: usize,
|
||||||
|
reg: u32,
|
||||||
|
target: u32,
|
||||||
|
fired: bool,
|
||||||
|
pub initial_data: u16,
|
||||||
|
|
||||||
|
pub cycles: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TimerAction {
|
||||||
|
Overflow,
|
||||||
|
Increment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
pub fn new(timer_id: usize) -> Timer {
|
||||||
|
if timer_id > 3 {
|
||||||
|
panic!("invalid timer id {}", timer_id);
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
timer_id: timer_id,
|
||||||
|
..Timer::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_irq(&self) -> Interrupt {
|
||||||
|
Interrupt::from_usize(self.timer_id + 8).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frequency(&self) -> usize {
|
||||||
|
match self.timer_ctl.prescalar() {
|
||||||
|
0 => 1,
|
||||||
|
1 => 64,
|
||||||
|
2 => 256,
|
||||||
|
3 => 1024,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_cycles(&mut self, cycles: usize, irqs: &mut IrqBitmask) -> TimerAction {
|
||||||
|
self.cycles += cycles;
|
||||||
|
|
||||||
|
let frequency = self.frequency();
|
||||||
|
while self.cycles >= frequency {
|
||||||
|
self.cycles -= frequency;
|
||||||
|
self.timer_data = self.timer_data.wrapping_add(1);
|
||||||
|
if self.timer_data == 0 {
|
||||||
|
if self.timer_ctl.irq_enabled() {
|
||||||
|
irqs.add_irq(self.get_irq());
|
||||||
|
}
|
||||||
|
println!("timer{} overflow", self.timer_id);
|
||||||
|
return TimerAction::Overflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TimerAction::Increment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Timers([Timer; 4]);
|
||||||
|
|
||||||
|
impl std::ops::Index<usize> for Timers {
|
||||||
|
type Output = Timer;
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::IndexMut<usize> for Timers {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
&mut self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timers {
|
||||||
|
pub fn new() -> Timers {
|
||||||
|
Timers([Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyncedIoDevice for Timers {
|
||||||
|
fn step(&mut self, cycles: usize, _sb: &mut SysBus, irqs: &mut IrqBitmask) {
|
||||||
|
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 => match i {
|
||||||
|
3 => {}
|
||||||
|
_ => {
|
||||||
|
let next_i = i +1;
|
||||||
|
if self[next_i].timer_ctl.cascade() {
|
||||||
|
println!("{:?} is cascade!", self[next_i]);
|
||||||
|
self[next_i].add_cycles(1, irqs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TimerCtl(u16);
|
||||||
|
impl Debug;
|
||||||
|
u16;
|
||||||
|
prescalar, _ : 1, 0;
|
||||||
|
cascade, _ : 2;
|
||||||
|
irq_enabled, _ : 6;
|
||||||
|
enabled, set_enabled : 7;
|
||||||
|
}
|
Reference in a new issue