aica rtc implementation

This commit is contained in:
Anthony Pesch 2016-10-11 23:25:59 -07:00
parent 3b1536a137
commit 49272b5de1
6 changed files with 247 additions and 75 deletions

View File

@ -1,5 +1,6 @@
#include "hw/aica/aica.h"
#include "core/log.h"
#include "core/option.h"
#include "hw/aica/aica_types.h"
#include "hw/arm/arm.h"
#include "hw/dreamcast.h"
@ -8,37 +9,59 @@
#include "hw/scheduler.h"
#include "hw/sh4/sh4.h"
DEFINE_OPTION_INT(rtc, 0, OPTION_HIDDEN);
#define AICA_CLOCK_FREQ INT64_C(22579200)
#define AICA_SAMPLE_FREQ INT64_C(44100)
#define AICA_NUM_CHANNELS 64
#define AICA_TIMER_PERIOD 0xff
struct aica_channel {
struct channel_data *data;
};
struct aica {
struct device;
uint8_t *aica_regs;
uint8_t *reg_ram;
uint8_t *wave_ram;
struct common_data *common;
// timers
struct timer *timers[3];
// real-time clock
struct timer *rtc_timer;
int rtc_write;
uint32_t rtc;
// channels
struct common_data *common_data;
struct aica_channel channels[AICA_NUM_CHANNELS];
};
/*
* interrupts
*/
static void aica_raise_interrupt(struct aica *aica, int intr) {
aica->common->SCIPD |= intr;
aica->common_data->MCIPD |= intr;
aica->common_data->SCIPD |= intr;
}
static void aica_clear_interrupt(struct aica *aica, int intr) {
aica->common->SCIPD &= ~intr;
aica->common_data->MCIPD &= ~intr;
aica->common_data->SCIPD &= ~intr;
}
static void aica_update_arm(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;
uint32_t enabled_intr = aica->common_data->MCIEB;
uint32_t pending_intr = aica->common_data->MCIPD & enabled_intr;
if (pending_intr) {
holly_toggle_interrupt(aica->holly, HOLLY_INTC_G2AICINT);
}
}
/*
* timers
*/
static void aica_timer_reschedule(struct aica *aica, int n, uint32_t period);
static void aica_timer_expire(struct aica *aica, int n) {
@ -65,16 +88,16 @@ static void aica_timer_expire_2(void *data) {
}
static uint32_t aica_timer_tctl(struct aica *aica, int n) {
return n == 0 ? aica->common->TACTL : n == 1 ? aica->common->TBCTL
: aica->common->TCCTL;
return n == 0 ? aica->common_data->TACTL : n == 1 ? aica->common_data->TBCTL
: aica->common_data->TCCTL;
}
static uint32_t aica_timer_tcnt(struct aica *aica, int n) {
struct timer *timer = aica->timers[n];
if (!timer) {
// if no timer has been created, return the raw value
return n == 0 ? aica->common->TIMA : n == 1 ? aica->common->TIMB
: aica->common->TIMC;
return n == 0 ? aica->common_data->TIMA : n == 1 ? aica->common_data->TIMB
: aica->common_data->TIMC;
}
// else, dynamically compute the value based on the timer's remaining time
@ -103,76 +126,196 @@ static void aica_timer_reschedule(struct aica *aica, int n, uint32_t period) {
scheduler_start_timer(aica->scheduler, timer_cbs[n], aica, remaining);
}
/*static uint32_t aica_channel_reg_read(struct aica *aica, uint32_t addr,
uint32_t data_mask) {
int ch = addr >> 7;
static void aica_timer_shutdown(struct aica *aica) {
for (int i = 0; i < 3; i++) {
scheduler_cancel_timer(aica->scheduler, aica->timers[i]);
}
}
static void aica_timer_init(struct aica *aica) {
for (int i = 0; i < 3; i++) {
aica_timer_reschedule(aica, i, AICA_TIMER_PERIOD);
}
}
/*
* rtc
*/
static uint32_t aica_rtc_reg_read(struct aica *aica, uint32_t addr,
uint32_t data_mask) {
switch (addr) {
case 0x0:
return aica->rtc >> 16;
case 0x4:
return aica->rtc & 0xffff;
case 0x8:
return 0;
default:
CHECK(false);
return 0;
}
}
static void aica_rtc_reg_write(struct aica *aica, uint32_t addr, uint32_t data,
uint32_t data_mask) {
switch (addr) {
case 0x0:
if (aica->rtc_write) {
aica->rtc = (data << 16) | (aica->rtc & 0xffff);
aica->rtc_write = 0;
}
break;
case 0x4:
if (aica->rtc_write) {
aica->rtc = (aica->rtc & 0xffff0000) | (data & 0xffff);
}
break;
case 0x8:
aica->rtc_write = data & 1;
break;
default:
CHECK(false);
break;
}
}
static void aica_rtc_timer(void *data) {
struct aica *aica = data;
aica->rtc++;
aica->rtc_timer =
scheduler_start_timer(aica->scheduler, &aica_rtc_timer, aica, NS_PER_SEC);
}
static void aica_rtc_shutdown(struct aica *aica) {
scheduler_cancel_timer(aica->scheduler, aica->rtc_timer);
// persist clock
OPTION_rtc = *(int *)&aica->rtc;
}
static void aica_rtc_init(struct aica *aica) {
// seed clock from persistant options
CHECK(sizeof(aica->rtc) <= sizeof(OPTION_rtc));
aica->rtc = *(uint32_t *)&OPTION_rtc;
// increment clock every second
aica->rtc_timer =
scheduler_start_timer(aica->scheduler, &aica_rtc_timer, aica, NS_PER_SEC);
}
/*
* channels
*/
static uint32_t aica_channel_reg_read(struct aica *aica, uint32_t addr,
uint32_t data_mask) {
int n = addr >> 7;
addr &= 0x7f;
return aica->aica_regs[(ch << 7) | addr];
struct aica_channel *ch = &aica->channels[n];
return READ_DATA(&ch->data[addr]);
}
static void aica_channel_reg_write(struct aica *aica, uint32_t addr,
uint32_t data, uint32_t data_mask) {
int ch = addr >> 7;
int n = addr >> 7;
addr &= 0x7f;
struct aica_channel *ch = &aica->channels[n];
aica->aica_regs[(ch << 7) | addr] = data;
WRITE_DATA(&ch->data[addr]);
}
static uint32_t aica_common_reg_read(struct aica *aica, uint32_t addr, uint32_t
data_mask) {
return 0;
static uint32_t aica_common_reg_read(struct aica *aica, uint32_t addr,
uint32_t data_mask) {
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 READ_DATA(&aica->reg_ram[0x2800 + addr]);
}
static void aica_common_reg_write(struct aica *aica, uint32_t addr,
uint32_t data, uint32_t data_mask) {}*/
uint32_t data, uint32_t data_mask) {
WRITE_DATA(&aica->reg_ram[addr]);
uint32_t aica_reg_read(struct aica *aica, uint32_t addr, uint32_t data_mask) {
if (addr < 0x2000) { /* channel */
} else if (addr >= 0x2800 && addr < 0x2d08 /* common */) {
addr -= 0x2800;
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 READ_DATA(&aica->aica_regs[0x2800 + addr]);
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 0x9c:
case 0x9d:
case 0xa0:
case 0xa1: { /* SCIEB, SCIPD */
aica_update_arm(aica);
} break;
case 0xa4:
case 0xa5: { /* SCIRE */
aica->common_data->SCIPD &= ~aica->common_data->SCIRE;
aica->common_data->SCIRE = 0;
aica_update_arm(aica);
} break;
case 0xb4:
case 0xb5:
case 0xb8:
case 0xb9: { /* MCIEB, MCIPD */
aica_update_sh(aica);
} break;
case 0xbc:
case 0xbd: { /* MCIRE */
aica->common_data->MCIPD &= ~aica->common_data->MCIRE;
aica->common_data->MCIRE = 0;
aica_update_sh(aica);
} break;
case 0x400: { /* ARMRST */
if (data) {
arm_suspend(aica->arm);
} else {
arm_resume(aica->arm);
}
} break;
}
}
return READ_DATA(&aica->aica_regs[addr]);
/*
* memory callbacks
*/
uint32_t aica_reg_read(struct aica *aica, uint32_t addr, uint32_t data_mask) {
if (addr < 0x2000) {
return aica_channel_reg_read(aica, addr, data_mask);
} else if (addr >= 0x2800 && addr < 0x2d08) {
return aica_common_reg_read(aica, addr - 0x2800, data_mask);
} else if (addr >= 0x10000 && addr < 0x1000c) {
return aica_rtc_reg_read(aica, addr - 0x10000, data_mask);
}
return READ_DATA(&aica->reg_ram[addr]);
}
void aica_reg_write(struct aica *aica, uint32_t addr, uint32_t data,
uint32_t data_mask) {
WRITE_DATA(&aica->aica_regs[addr]);
if (addr < 0x2000) { /* channel */
} else if (addr >= 0x2800 && addr < 0x2d08 /* common */) {
addr -= 0x2800;
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 (data) {
arm_suspend(aica->arm);
} else {
arm_resume(aica->arm);
}
} break;
}
if (addr < 0x2000) {
aica_channel_reg_write(aica, addr, data, data_mask);
return;
} else if (addr >= 0x2800 && addr < 0x2d08) {
aica_common_reg_write(aica, addr - 0x2800, data, data_mask);
return;
} else if (addr >= 0x10000 && addr < 0x1000c) {
aica_rtc_reg_write(aica, addr - 0x10000, data, data_mask);
return;
}
WRITE_DATA(&aica->reg_ram[addr]);
}
uint32_t aica_wave_read(struct aica *aica, uint32_t addr, uint32_t data_mask) {
@ -206,14 +349,14 @@ void aica_wave_write(struct aica *aica, uint32_t addr, uint32_t data,
WRITE_DATA(&aica->wave_ram[addr]);
}
/*
* device
*/
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--;
}
// int64_t cycles = MAX(NANO_TO_CYCLES(ns, AICA_CLOCK_FREQ), INT64_C(1));
// int64_t samples = NANO_TO_CYCLES(ns, AICA_SAMPLE_FREQ);
aica_raise_interrupt(aica, AICA_INT_SAMPLE);
@ -224,15 +367,20 @@ static void aica_run(struct device *dev, int64_t ns) {
static bool aica_init(struct device *dev) {
struct aica *aica = (struct aica *)dev;
aica->aica_regs = memory_translate(aica->memory, "aica reg ram", 0x00000000);
aica->reg_ram = 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);
// setup channel data aliases
for (int i = 0; i < AICA_NUM_CHANNELS; i++) {
struct aica_channel *ch = &aica->channels[i];
ch->data = (struct channel_data *)(aica->reg_ram +
sizeof(struct channel_data) * i);
}
aica->common_data = (struct common_data *)(aica->reg_ram + 0x2800);
aica_timer_init(aica);
aica_rtc_init(aica);
// arm cpu is initially suspended
arm_suspend(aica->arm);
return true;
@ -248,6 +396,9 @@ struct aica *aica_create(struct dreamcast *dc) {
}
void aica_destroy(struct aica *aica) {
aica_rtc_shutdown(aica);
aica_timer_shutdown(aica);
dc_destroy_execute_interface(aica->execute_if);
dc_destroy_device((struct device *)aica);
}

View File

@ -17,6 +17,7 @@
#define AICA_INT_SAMPLE 0x400
struct channel_data {
// 0x0
uint32_t SA_hi : 7;
uint32_t PCMS : 2;
uint32_t LPCTL : 1;
@ -26,21 +27,26 @@ struct channel_data {
uint32_t KYONEX : 1;
uint32_t : 16;
// 0x4
uint32_t SA_lo : 16;
uint32_t : 16;
// 0x8
uint32_t LSA : 16;
uint32_t : 16;
// 0xc
uint32_t LEA : 16;
uint32_t : 16;
// 0x10
uint32_t AR : 5;
uint32_t : 1;
uint32_t D1R : 5;
uint32_t D2R : 5;
uint32_t : 16;
// 0x14
uint32_t RR : 5;
uint32_t DL : 5;
uint32_t KRS : 4;
@ -48,12 +54,14 @@ struct channel_data {
uint32_t : 1;
uint32_t : 16;
// 0x18
uint32_t FNS : 10;
uint32_t : 1;
uint32_t OCT : 4;
uint32_t : 1;
uint32_t : 16;
// 0x1c
uint32_t ALFOS : 3;
uint32_t ALFOWS : 2;
uint32_t PLFOS : 3;
@ -62,48 +70,58 @@ struct channel_data {
uint32_t LFORE : 1;
uint32_t : 16;
// 0x20
uint32_t ISEL : 4;
uint32_t IMXL : 4;
uint32_t : 8;
uint32_t : 16;
// 0x24
uint32_t DIPAN : 5;
uint32_t : 3;
uint32_t DISDL : 4;
uint32_t : 4;
uint32_t : 16;
// 0x28
uint32_t Q : 5;
uint32_t : 3;
uint32_t TL : 8;
uint32_t : 16;
// 0x2c
uint32_t FLV0 : 13;
uint32_t : 3;
uint32_t : 16;
// 0x30
uint32_t FLV1 : 13;
uint32_t : 3;
uint32_t : 16;
// 0x34
uint32_t FLV2 : 13;
uint32_t : 3;
uint32_t : 16;
// 0x38
uint32_t FLV3 : 13;
uint32_t : 3;
uint32_t : 16;
// 0x3c
uint32_t FLV4 : 13;
uint32_t : 3;
uint32_t : 16;
// 0x40
uint32_t FD1R : 5;
uint32_t : 3;
uint32_t FAR : 5;
uint32_t : 3;
uint32_t : 16;
// 0x44
uint32_t FRR : 5;
uint32_t : 3;
uint32_t FD2R : 5;

View File

@ -199,7 +199,8 @@ static bool memory_create_shmem(struct memory *memory) {
}
static void memory_destroy_shmem(struct memory *memory) {
CHECK(unmap_shared_memory(memory->shmem, memory->shmem_base, memory->shmem_size));
CHECK(unmap_shared_memory(memory->shmem, memory->shmem_base,
memory->shmem_size));
destroy_shared_memory(memory->shmem);
}

View File

@ -59,7 +59,8 @@ static void pvr_reconfigure_spg(struct pvr *pvr) {
}
LOG_INFO(
"ReconfigureSPG: pixel_clock %d, line_clock %d, vcount %d, hcount %d, "
"pvr_reconfigure_spg: pixel_clock %d, line_clock %d, vcount %d, hcount "
"%d, "
"interlace %d, vbstart %d, vbend %d",
pixel_clock, pvr->line_clock, pvr->SPG_LOAD->vcount,
pvr->SPG_LOAD->hcount, pvr->SPG_CONTROL->interlace,

View File

@ -3,7 +3,7 @@
#include "core/option.h"
#include "hw/dreamcast.h"
DEFINE_OPTION_STRING(bios, "dc_boot.bin", "Path to BIOS");
DEFINE_OPTION_STRING(bios, "dc_boot.bin", "Path to boot rom");
#define BIOS_SIZE 0x00200000

View File

@ -4,7 +4,7 @@
#include "hw/dreamcast.h"
#include "sys/filesystem.h"
DEFINE_OPTION_STRING(flash, "dc_flash.bin", "Path to flash ROM");
DEFINE_OPTION_STRING(flash, "dc_flash.bin", "Path to flash rom");
// there doesn't seem to be any documentation on the flash rom used by thae
// dreamcast. however, several people have replaced it with the MX29LV160TMC-90
@ -67,7 +67,8 @@ static int flash_save_rom(struct flash *flash, const char *path) {
return 1;
}
static uint32_t flash_cmd_read(struct flash *flash, uint32_t addr, uint32_t data_mask) {
static uint32_t flash_cmd_read(struct flash *flash, uint32_t addr,
uint32_t data_mask) {
uint32_t *mem = (uint32_t *)&flash->rom[addr];
return *mem & data_mask;
}