mirror of https://github.com/inolen/redream.git
inital aica timer support
This commit is contained in:
parent
70a4ddfd26
commit
a2ea84baa9
|
@ -3,48 +3,151 @@
|
|||
#include "hw/aica/aica_types.h"
|
||||
#include "hw/arm/arm.h"
|
||||
#include "hw/dreamcast.h"
|
||||
#include "hw/holly/holly.h"
|
||||
#include "hw/memory.h"
|
||||
#include "hw/scheduler.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
|
||||
#define AICA_CLOCK_FREQ INT64_C(22579200)
|
||||
#define AICA_SAMPLE_FREQ INT64_C(44100)
|
||||
#define AICA_TIMER_PERIOD 0xff
|
||||
|
||||
#define TCNT(n) \
|
||||
(n == 0 ? aica->common->TIMA : n == 1 ? aica->common->TIMB \
|
||||
: aica->common->TIMC)
|
||||
|
||||
#define TCTL(n) \
|
||||
(n == 0 ? aica->common->TACTL : n == 1 ? aica->common->TBCTL \
|
||||
: aica->common->TCCTL)
|
||||
|
||||
struct aica {
|
||||
struct device;
|
||||
uint8_t *aica_regs;
|
||||
uint8_t *wave_ram;
|
||||
struct common_data *common_data;
|
||||
struct common_data *common;
|
||||
struct timer *timers[3];
|
||||
};
|
||||
|
||||
static void aica_raise_interrupt(struct aica *aica, int intr) {
|
||||
aica->common->SCIPD |= intr;
|
||||
}
|
||||
|
||||
static void aica_clear_interrupt(struct aica *aica, int intr) {
|
||||
aica->common->SCIPD &= ~intr;
|
||||
}
|
||||
|
||||
static void aica_update_arm(struct aica *aica) {}
|
||||
|
||||
static void aica_update_sh(struct aica *aica) {}
|
||||
static void aica_update_sh(struct aica *aica) {
|
||||
uint32_t enabled_intr = aica->common->MCIEB;
|
||||
uint32_t pending_intr = aica->common->MCIPD & enabled_intr;
|
||||
|
||||
#define define_reg_read(name, type) \
|
||||
type aica_reg_##name(struct aica *aica, uint32_t addr) { \
|
||||
if (addr >= 2800 /* common */) { \
|
||||
return *(type *)&aica->aica_regs[addr]; \
|
||||
} \
|
||||
return *(type *)&aica->aica_regs[addr]; \
|
||||
if (pending_intr) {
|
||||
holly_toggle_interrupt(aica->holly, HOLLY_INTC_G2AICINT);
|
||||
}
|
||||
}
|
||||
|
||||
static void aica_timer_reschedule(struct aica *aica, int n, uint32_t period);
|
||||
|
||||
static void aica_timer_expire(struct aica *aica, int n) {
|
||||
aica->timers[n] = NULL;
|
||||
aica_timer_reschedule(aica, n, AICA_TIMER_PERIOD);
|
||||
}
|
||||
|
||||
static void aica_timer_expire_0(void *data) {
|
||||
aica_timer_expire(data, 0);
|
||||
}
|
||||
|
||||
static void aica_timer_expire_1(void *data) {
|
||||
aica_timer_expire(data, 1);
|
||||
}
|
||||
|
||||
static void aica_timer_expire_2(void *data) {
|
||||
aica_timer_expire(data, 2);
|
||||
}
|
||||
|
||||
static uint32_t aica_timer_tctl(struct aica *aica, int n) {
|
||||
return TCTL(n);
|
||||
}
|
||||
|
||||
static uint32_t aica_timer_tcnt(struct aica *aica, int n) {
|
||||
struct timer *timer = aica->timers[n];
|
||||
if (!timer) {
|
||||
return TCNT(n);
|
||||
}
|
||||
|
||||
int tctl = TCTL(n);
|
||||
int64_t freq = AICA_SAMPLE_FREQ >> tctl;
|
||||
int64_t remaining = scheduler_remaining_time(aica->scheduler, timer);
|
||||
int64_t cycles = NANO_TO_CYCLES(remaining, freq);
|
||||
return (uint32_t)cycles;
|
||||
}
|
||||
|
||||
static void aica_timer_reschedule(struct aica *aica, int n, uint32_t period) {
|
||||
struct timer **timer = &aica->timers[n];
|
||||
|
||||
int64_t freq = AICA_SAMPLE_FREQ >> TCTL(n);
|
||||
int64_t cycles = (int64_t)period;
|
||||
int64_t remaining = CYCLES_TO_NANO(cycles, freq);
|
||||
|
||||
if (*timer) {
|
||||
scheduler_cancel_timer(aica->scheduler, *timer);
|
||||
*timer = NULL;
|
||||
}
|
||||
|
||||
timer_cb cb = (n == 0 ? &aica_timer_expire_0 : n == 1 ? &aica_timer_expire_1
|
||||
: &aica_timer_expire_2);
|
||||
*timer = scheduler_start_timer(aica->scheduler, cb, aica, remaining);
|
||||
}
|
||||
|
||||
#define define_reg_read(name, type) \
|
||||
type aica_reg_##name(struct aica *aica, uint32_t addr) { \
|
||||
if (addr >= 2800 /* common */) { \
|
||||
switch (addr) { \
|
||||
case 0x90: /* TIMA */ \
|
||||
return (aica_timer_tctl(aica, 0) << 8) | aica_timer_tcnt(aica, 0); \
|
||||
case 0x94: /* TIMB */ \
|
||||
return (aica_timer_tctl(aica, 1) << 8) | aica_timer_tcnt(aica, 1); \
|
||||
case 0x98: /* TIMC */ \
|
||||
return (aica_timer_tctl(aica, 2) << 8) | aica_timer_tcnt(aica, 2); \
|
||||
} \
|
||||
return *(type *)&aica->aica_regs[0x2800 + addr]; \
|
||||
} \
|
||||
return *(type *)&aica->aica_regs[addr]; \
|
||||
}
|
||||
|
||||
define_reg_read(r8, uint8_t);
|
||||
define_reg_read(r16, uint16_t);
|
||||
define_reg_read(r32, uint32_t);
|
||||
|
||||
#define define_reg_write(name, type) \
|
||||
void aica_reg_##name(struct aica *aica, uint32_t addr, type value) { \
|
||||
*(type *)&aica->aica_regs[addr] = value; \
|
||||
if (addr >= 2800 /* common */) { \
|
||||
addr -= 2800; \
|
||||
\
|
||||
switch (addr) { \
|
||||
case 0x400: { /* ARMRST */ \
|
||||
if (value) { \
|
||||
arm_suspend(aica->arm); \
|
||||
} else { \
|
||||
arm_resume(aica->arm); \
|
||||
} \
|
||||
} break; \
|
||||
} \
|
||||
} \
|
||||
#define define_reg_write(name, type) \
|
||||
void aica_reg_##name(struct aica *aica, uint32_t addr, type value) { \
|
||||
*(type *)&aica->aica_regs[addr] = value; \
|
||||
if (addr >= 2800 /* common */) { \
|
||||
addr -= 2800; \
|
||||
\
|
||||
switch (addr) { \
|
||||
case 0x90: { /* TIMA */ \
|
||||
aica_timer_reschedule(aica, 0, \
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 0)); \
|
||||
} break; \
|
||||
case 0x94: { /* TIMB */ \
|
||||
aica_timer_reschedule(aica, 1, \
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 1)); \
|
||||
} break; \
|
||||
case 0x98: { /* TIMC */ \
|
||||
aica_timer_reschedule(aica, 2, \
|
||||
AICA_TIMER_PERIOD - aica_timer_tcnt(aica, 2)); \
|
||||
} break; \
|
||||
case 0x400: { /* ARMRST */ \
|
||||
if (value) { \
|
||||
arm_suspend(aica->arm); \
|
||||
} else { \
|
||||
arm_resume(aica->arm); \
|
||||
} \
|
||||
} break; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
define_reg_write(w8, uint8_t);
|
||||
|
@ -91,14 +194,33 @@ define_write_wave(w8, uint8_t);
|
|||
define_write_wave(w16, uint16_t);
|
||||
define_write_wave(w32, uint32_t);
|
||||
|
||||
static void aica_run(struct device *dev, int64_t ns) {
|
||||
struct aica *aica = (struct aica *)dev;
|
||||
|
||||
int64_t cycles = MAX(NANO_TO_CYCLES(ns, AICA_CLOCK_FREQ), INT64_C(1));
|
||||
|
||||
while (cycles > 0) {
|
||||
cycles--;
|
||||
}
|
||||
|
||||
aica_raise_interrupt(aica, AICA_INT_SAMPLE);
|
||||
|
||||
aica_update_arm(aica);
|
||||
aica_update_sh(aica);
|
||||
}
|
||||
|
||||
static bool aica_init(struct device *dev) {
|
||||
struct aica *aica = (struct aica *)dev;
|
||||
struct dreamcast *dc = aica->dc;
|
||||
|
||||
aica->aica_regs = memory_translate(dc->memory, "aica reg ram", 0x00000000);
|
||||
aica->wave_ram = memory_translate(dc->memory, "aica wave ram", 0x00000000);
|
||||
aica->common_data = (struct common_data *)(aica->aica_regs + 0x2800);
|
||||
aica->aica_regs = memory_translate(aica->memory, "aica reg ram", 0x00000000);
|
||||
aica->wave_ram = memory_translate(aica->memory, "aica wave ram", 0x00000000);
|
||||
aica->common = (struct common_data *)(aica->aica_regs + 0x2800);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
aica_timer_reschedule(aica, i, AICA_TIMER_PERIOD);
|
||||
}
|
||||
|
||||
// arm cpu is initially suspended
|
||||
arm_suspend(aica->arm);
|
||||
|
||||
return true;
|
||||
|
@ -107,10 +229,14 @@ static bool aica_init(struct device *dev) {
|
|||
struct aica *aica_create(struct dreamcast *dc) {
|
||||
struct aica *aica =
|
||||
dc_create_device(dc, sizeof(struct aica), "aica", &aica_init);
|
||||
|
||||
aica->execute_if = dc_create_execute_interface(&aica_run);
|
||||
|
||||
return aica;
|
||||
}
|
||||
|
||||
void aica_destroy(struct aica *aica) {
|
||||
dc_destroy_execute_interface(aica->execute_if);
|
||||
dc_destroy_device((struct device *)aica);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,19 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
// interrupts
|
||||
#define AICA_INT_INTON 0x1
|
||||
#define AICA_INT_RES1 0x2
|
||||
#define AICA_INT_RES2 0x4
|
||||
#define AICA_INT_MIDI_IN 0x8
|
||||
#define AICA_INT_DMA_END 0x10
|
||||
#define AICA_INT_DATA 0x20
|
||||
#define AICA_INT_TIMER_A 0x40
|
||||
#define AICA_INT_TIMER_B 0x80
|
||||
#define AICA_INT_TIMER_C 0x100
|
||||
#define AICA_INT_MIDI_OUT 0x200
|
||||
#define AICA_INT_SAMPLE 0x400
|
||||
|
||||
struct channel_data {
|
||||
uint32_t SA_hi : 7;
|
||||
uint32_t PCMS : 2;
|
||||
|
|
|
@ -74,6 +74,20 @@ static bool holly_init(struct device *dev) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static uint32_t *holly_interrupt_status(struct holly *hl,
|
||||
enum holly_interrupt_type type) {
|
||||
switch (type) {
|
||||
case HOLLY_INTC_NRM:
|
||||
return hl->SB_ISTNRM;
|
||||
case HOLLY_INTC_EXT:
|
||||
return hl->SB_ISTEXT;
|
||||
case HOLLY_INTC_ERR:
|
||||
return hl->SB_ISTERR;
|
||||
default:
|
||||
LOG_FATAL("Invalid interrupt type");
|
||||
}
|
||||
}
|
||||
|
||||
void holly_raise_interrupt(struct holly *hl, holly_interrupt_t intr) {
|
||||
enum holly_interrupt_type type = HOLLY_INTERRUPT_TYPE(intr);
|
||||
uint32_t irq = HOLLY_INTERRUPT_IRQ(intr);
|
||||
|
@ -82,19 +96,8 @@ void holly_raise_interrupt(struct holly *hl, holly_interrupt_t intr) {
|
|||
maple_vblank(hl->maple);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case HOLLY_INTC_NRM:
|
||||
*hl->SB_ISTNRM |= irq;
|
||||
break;
|
||||
|
||||
case HOLLY_INTC_EXT:
|
||||
*hl->SB_ISTEXT |= irq;
|
||||
break;
|
||||
|
||||
case HOLLY_INTC_ERR:
|
||||
*hl->SB_ISTERR |= irq;
|
||||
break;
|
||||
}
|
||||
uint32_t *status = holly_interrupt_status(hl, type);
|
||||
*status |= irq;
|
||||
|
||||
holly_update_sh4_interrupts(hl);
|
||||
}
|
||||
|
@ -103,23 +106,24 @@ void holly_clear_interrupt(struct holly *hl, holly_interrupt_t intr) {
|
|||
enum holly_interrupt_type type = HOLLY_INTERRUPT_TYPE(intr);
|
||||
uint32_t irq = HOLLY_INTERRUPT_IRQ(intr);
|
||||
|
||||
switch (type) {
|
||||
case HOLLY_INTC_NRM:
|
||||
*hl->SB_ISTNRM &= ~irq;
|
||||
break;
|
||||
|
||||
case HOLLY_INTC_EXT:
|
||||
*hl->SB_ISTEXT &= ~irq;
|
||||
break;
|
||||
|
||||
case HOLLY_INTC_ERR:
|
||||
*hl->SB_ISTERR &= ~irq;
|
||||
break;
|
||||
}
|
||||
uint32_t *status = holly_interrupt_status(hl, type);
|
||||
*status &= ~irq;
|
||||
|
||||
holly_update_sh4_interrupts(hl);
|
||||
}
|
||||
|
||||
void holly_toggle_interrupt(struct holly *hl, holly_interrupt_t intr) {
|
||||
enum holly_interrupt_type type = HOLLY_INTERRUPT_TYPE(intr);
|
||||
uint32_t irq = HOLLY_INTERRUPT_IRQ(intr);
|
||||
|
||||
uint32_t *status = holly_interrupt_status(hl, type);
|
||||
if (*status & irq) {
|
||||
holly_clear_interrupt(hl, intr);
|
||||
} else {
|
||||
holly_raise_interrupt(hl, intr);
|
||||
}
|
||||
}
|
||||
|
||||
struct holly *holly_create(struct dreamcast *dc) {
|
||||
struct holly *hl =
|
||||
dc_create_device(dc, sizeof(struct holly), "holly", &holly_init);
|
||||
|
|
|
@ -24,6 +24,7 @@ extern struct reg_cb holly_cb[NUM_HOLLY_REGS];
|
|||
|
||||
void holly_raise_interrupt(struct holly *hl, holly_interrupt_t intr);
|
||||
void holly_clear_interrupt(struct holly *hl, holly_interrupt_t intr);
|
||||
void holly_toggle_interrupt(struct holly *hl, holly_interrupt_t intr);
|
||||
|
||||
struct holly *holly_create(struct dreamcast *dc);
|
||||
void holly_destroy(struct holly *hl);
|
||||
|
|
|
@ -12,7 +12,6 @@ enum {
|
|||
};
|
||||
|
||||
// interrupts
|
||||
|
||||
#define HOLLY_INTERRUPT(type, irq) (((uint64_t)type << 32) | irq)
|
||||
#define HOLLY_INTERRUPT_TYPE(intr) (intr >> 32)
|
||||
#define HOLLY_INTERRUPT_IRQ(intr) ((uint32_t)intr)
|
||||
|
@ -23,8 +22,6 @@ enum holly_interrupt_type {
|
|||
HOLLY_INTC_ERR = 0x3
|
||||
};
|
||||
|
||||
// using a typedef and defines here as msvc (as of visual studio 2015) doesn't
|
||||
// support 64-bit enums
|
||||
typedef uint64_t holly_interrupt_t;
|
||||
|
||||
//
|
||||
|
|
|
@ -39,8 +39,8 @@ void scheduler_tick(struct scheduler *sch, int64_t ns) {
|
|||
next_time = next_timer->expire;
|
||||
}
|
||||
|
||||
// go ahead and update base time before running devices and expiring timers
|
||||
// in case one of them schedules a new timer
|
||||
// update base time before running devices and expiring timers in case one
|
||||
// of them schedules a new timer
|
||||
int64_t slice = next_time - sch->base_time;
|
||||
sch->base_time += slice;
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ static void sh4_tmu_expire(struct sh4 *sh4, int n) {
|
|||
*tcnt = *tcor;
|
||||
|
||||
// reschedule the timer with the new count
|
||||
sh4->tmu_timers[n] = NULL;
|
||||
sh4_tmu_reschedule(sh4, n, *tcnt, *tcr);
|
||||
}
|
||||
|
||||
|
@ -647,7 +648,7 @@ static void sh4_run_inner(struct device *dev, int64_t ns) {
|
|||
}
|
||||
}
|
||||
|
||||
void sh4_run(struct device *dev, int64_t ns) {
|
||||
static void sh4_run(struct device *dev, int64_t ns) {
|
||||
prof_enter("sh4_run");
|
||||
|
||||
sh4_run_inner(dev, ns);
|
||||
|
|
Loading…
Reference in New Issue