inital aica timer support

This commit is contained in:
Anthony Pesch 2016-10-01 12:09:09 -07:00
parent 70a4ddfd26
commit a2ea84baa9
7 changed files with 202 additions and 60 deletions

View File

@ -3,24 +3,115 @@
#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;
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 */) { \
return *(type *)&aica->aica_regs[addr]; \
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]; \
}
@ -36,6 +127,18 @@ define_reg_read(r32, uint32_t);
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); \
@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
//

View File

@ -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;

View File

@ -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);