mirror of https://github.com/inolen/redream.git
initial g2 dma support
This commit is contained in:
parent
fdc4f3ae45
commit
1a761877c4
|
@ -6,7 +6,157 @@
|
|||
|
||||
struct reg_cb holly_cb[NUM_HOLLY_REGS];
|
||||
|
||||
static void holly_update_sh4_interrupts(struct holly *hl) {
|
||||
static void holly_ch2_dma(struct holly *hl) {
|
||||
// FIXME what are SB_LMMODE0 / SB_LMMODE1
|
||||
struct sh4_dtr dtr = {0};
|
||||
dtr.channel = 2;
|
||||
dtr.rw = false;
|
||||
dtr.addr = *hl->SB_C2DSTAT;
|
||||
sh4_ddt(hl->sh4, &dtr);
|
||||
|
||||
*hl->SB_C2DLEN = 0;
|
||||
*hl->SB_C2DST = 0;
|
||||
holly_raise_interrupt(hl, HOLLY_INTC_DTDE2INT);
|
||||
}
|
||||
|
||||
static void holly_gdrom_dma(struct holly *hl) {
|
||||
if (!*hl->SB_GDEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct gdrom *gd = hl->gdrom;
|
||||
struct sh4 *sh4 = hl->sh4;
|
||||
|
||||
CHECK_EQ(*hl->SB_GDDIR, 1); // gdrom -> sh4
|
||||
|
||||
int transfer_size = *hl->SB_GDLEN;
|
||||
int remaining = transfer_size;
|
||||
uint32_t addr = *hl->SB_GDSTAR;
|
||||
|
||||
gdrom_dma_begin(gd);
|
||||
|
||||
while (remaining) {
|
||||
// read a single sector at a time from the gdrom
|
||||
uint8_t sector_data[SECTOR_SIZE];
|
||||
int n = gdrom_dma_read(gd, sector_data, sizeof(sector_data));
|
||||
|
||||
struct sh4_dtr dtr = {0};
|
||||
dtr.channel = 0;
|
||||
dtr.rw = true;
|
||||
dtr.data = sector_data;
|
||||
dtr.addr = addr;
|
||||
dtr.size = n;
|
||||
sh4_ddt(sh4, &dtr);
|
||||
|
||||
remaining -= n;
|
||||
addr += n;
|
||||
}
|
||||
|
||||
gdrom_dma_end(gd);
|
||||
|
||||
*hl->SB_GDSTARD = addr;
|
||||
*hl->SB_GDLEND = transfer_size;
|
||||
*hl->SB_GDST = 0;
|
||||
holly_raise_interrupt(hl, HOLLY_INTC_G1DEINT);
|
||||
}
|
||||
|
||||
static void holly_g2_dma(struct holly *hl, int channel) {
|
||||
// clang-format off
|
||||
struct g2_channel_desc {
|
||||
int STAG, STAR, LEN, DIR, TSEL, EN, ST, SUSP;
|
||||
holly_interrupt_t INTR;
|
||||
};
|
||||
static struct g2_channel_desc channel_descs[] = {
|
||||
{SB_ADSTAG, SB_ADSTAR, SB_ADLEN, SB_ADDIR, SB_ADTSEL, SB_ADEN, SB_ADST, SB_ADSUSP, HOLLY_INTC_G2DEAINT},
|
||||
{SB_E1STAG, SB_E1STAR, SB_E1LEN, SB_E1DIR, SB_E1TSEL, SB_E1EN, SB_E1ST, SB_E1SUSP, HOLLY_INTC_G2DE1INT},
|
||||
{SB_E2STAG, SB_E2STAR, SB_E2LEN, SB_E2DIR, SB_E2TSEL, SB_E2EN, SB_E2ST, SB_E2SUSP, HOLLY_INTC_G2DE2INT},
|
||||
{SB_DDSTAG, SB_DDSTAR, SB_DDLEN, SB_DDDIR, SB_DDTSEL, SB_DDEN, SB_DDST, SB_DDSUSP, HOLLY_INTC_G2DEDINT},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
struct g2_channel_desc *desc = &channel_descs[channel];
|
||||
uint32_t *STAG = &hl->reg[desc->STAG];
|
||||
uint32_t *STAR = &hl->reg[desc->STAR];
|
||||
uint32_t *LEN = &hl->reg[desc->LEN];
|
||||
uint32_t *DIR = &hl->reg[desc->DIR];
|
||||
uint32_t *TSEL = &hl->reg[desc->TSEL];
|
||||
uint32_t *EN = &hl->reg[desc->EN];
|
||||
uint32_t *ST = &hl->reg[desc->ST];
|
||||
uint32_t *SUSP = &hl->reg[desc->SUSP];
|
||||
holly_interrupt_t INTR = desc->INTR;
|
||||
|
||||
if (!*EN) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct address_space *space = hl->sh4->memory_if->space;
|
||||
int transfer_size = *LEN & 0x7fffffff;
|
||||
int enable = *LEN >> 31;
|
||||
int remaining = transfer_size;
|
||||
uint32_t src = *STAR;
|
||||
uint32_t dst = *STAG;
|
||||
|
||||
// only sh4 -> g2 supported for now
|
||||
CHECK_EQ(*DIR, 0);
|
||||
|
||||
while (remaining) {
|
||||
as_write32(space, dst, as_read32(space, src));
|
||||
remaining -= 4;
|
||||
src += 4;
|
||||
dst += 4;
|
||||
}
|
||||
|
||||
*STAR = src;
|
||||
*STAG = dst;
|
||||
*LEN = 0;
|
||||
*EN = enable ? 1 : *EN;
|
||||
*ST = 0;
|
||||
holly_raise_interrupt(hl, INTR);
|
||||
}
|
||||
|
||||
static void holly_maple_dma(struct holly *hl) {
|
||||
if (!*hl->SB_MDEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct maple *mp = hl->maple;
|
||||
struct address_space *space = hl->sh4->memory_if->space;
|
||||
uint32_t addr = *hl->SB_MDSTAR;
|
||||
union maple_transfer desc;
|
||||
struct maple_frame frame, res;
|
||||
|
||||
do {
|
||||
desc.full = as_read64(space, addr);
|
||||
addr += 8;
|
||||
|
||||
// read input
|
||||
frame.header.full = as_read32(space, addr);
|
||||
addr += 4;
|
||||
|
||||
for (uint32_t i = 0; i < frame.header.num_words; i++) {
|
||||
frame.params[i] = as_read32(space, addr);
|
||||
addr += 4;
|
||||
}
|
||||
|
||||
// handle command and write response
|
||||
if (maple_handle_command(mp, desc.port, &frame, &res)) {
|
||||
as_write32(space, desc.result_addr, res.header.full);
|
||||
desc.result_addr += 4;
|
||||
|
||||
for (uint32_t i = 0; i < res.header.num_words; i++) {
|
||||
as_write32(space, desc.result_addr, res.params[i]);
|
||||
desc.result_addr += 4;
|
||||
}
|
||||
} else {
|
||||
as_write32(space, desc.result_addr, 0xffffffff);
|
||||
}
|
||||
} while (!desc.last);
|
||||
|
||||
*hl->SB_MDST = 0;
|
||||
holly_raise_interrupt(hl, HOLLY_INTC_MDEINT);
|
||||
}
|
||||
|
||||
static void holly_update_interrupts(struct holly *hl) {
|
||||
// trigger the respective level-encoded interrupt on the sh4 interrupt
|
||||
// controller
|
||||
{
|
||||
|
@ -38,6 +188,8 @@ static void holly_update_sh4_interrupts(struct holly *hl) {
|
|||
sh4_clear_interrupt(hl->sh4, SH4_INTC_IRL_13);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO check for hardware DMA initiation
|
||||
}
|
||||
|
||||
#define define_reg_read(name, type) \
|
||||
|
@ -92,14 +244,15 @@ 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);
|
||||
|
||||
if (intr == HOLLY_INTC_PCVOINT) {
|
||||
maple_vblank(hl->maple);
|
||||
}
|
||||
|
||||
uint32_t *status = holly_interrupt_status(hl, type);
|
||||
*status |= irq;
|
||||
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
|
||||
// check for hardware dma initiation
|
||||
if (intr == HOLLY_INTC_PCVOINT && *hl->SB_MDTSEL && *hl->SB_MDEN) {
|
||||
holly_maple_dma(hl);
|
||||
}
|
||||
}
|
||||
|
||||
void holly_clear_interrupt(struct holly *hl, holly_interrupt_t intr) {
|
||||
|
@ -109,7 +262,7 @@ void holly_clear_interrupt(struct holly *hl, holly_interrupt_t intr) {
|
|||
uint32_t *status = holly_interrupt_status(hl, type);
|
||||
*status &= ~irq;
|
||||
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
void holly_toggle_interrupt(struct holly *hl, holly_interrupt_t intr) {
|
||||
|
@ -161,240 +314,136 @@ REG_W32(holly_cb, SB_ISTNRM) {
|
|||
struct holly *hl = dc->holly;
|
||||
// writing a 1 clears the interrupt
|
||||
*hl->SB_ISTNRM &= ~value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_ISTEXT) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_ISTEXT &= ~value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_ISTERR) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_ISTERR &= ~value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML2NRM) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML2NRM = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML2EXT) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML2EXT = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML2ERR) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML2ERR = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML4NRM) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML4NRM = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML4EXT) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML4EXT = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML4ERR) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML4ERR = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML6NRM) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML6NRM = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML6EXT) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML6EXT = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_IML6ERR) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_IML6ERR = value;
|
||||
holly_update_sh4_interrupts(hl);
|
||||
holly_update_interrupts(hl);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_C2DST) {
|
||||
struct holly *hl = dc->holly;
|
||||
|
||||
*hl->SB_C2DST = value;
|
||||
|
||||
if (!*hl->SB_C2DST) {
|
||||
return;
|
||||
if ((*hl->SB_C2DST = value)) {
|
||||
holly_ch2_dma(hl);
|
||||
}
|
||||
|
||||
// FIXME what are SB_LMMODE0 / SB_LMMODE1
|
||||
struct sh4_dtr dtr = {0};
|
||||
dtr.channel = 2;
|
||||
dtr.rw = false;
|
||||
dtr.addr = *hl->SB_C2DSTAT;
|
||||
sh4_ddt(hl->sh4, &dtr);
|
||||
|
||||
*hl->SB_C2DLEN = 0;
|
||||
*hl->SB_C2DST = 0;
|
||||
holly_raise_interrupt(hl, HOLLY_INTC_DTDE2INT);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_SDST) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_SDST = value;
|
||||
if (!*hl->SB_SDST) {
|
||||
return;
|
||||
if ((*hl->SB_SDST = value)) {
|
||||
LOG_FATAL("Sort DMA not supported");
|
||||
}
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_MDST) {
|
||||
struct holly *hl = dc->holly;
|
||||
if ((*hl->SB_MDST = value)) {
|
||||
holly_maple_dma(hl);
|
||||
}
|
||||
LOG_FATAL("Sort DMA not supported");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_GDST) {
|
||||
struct gdrom *gd = dc->gdrom;
|
||||
struct holly *hl = dc->holly;
|
||||
|
||||
// if a "0" is written to this register, it is ignored
|
||||
*hl->SB_GDST |= value;
|
||||
|
||||
if (!*hl->SB_GDST) {
|
||||
return;
|
||||
if ((*hl->SB_GDST = value)) {
|
||||
holly_gdrom_dma(hl);
|
||||
}
|
||||
|
||||
CHECK_EQ(*hl->SB_GDEN, 1); // dma enabled
|
||||
CHECK_EQ(*hl->SB_GDDIR, 1); // gd-rom -> system memory
|
||||
|
||||
int transfer_size = *hl->SB_GDLEN;
|
||||
uint32_t start = *hl->SB_GDSTAR;
|
||||
|
||||
int remaining = transfer_size;
|
||||
uint32_t addr = start;
|
||||
|
||||
gdrom_dma_begin(gd);
|
||||
|
||||
while (remaining) {
|
||||
// read a single sector at a time from the gdrom
|
||||
uint8_t sector_data[SECTOR_SIZE];
|
||||
int n = gdrom_dma_read(gd, sector_data, sizeof(sector_data));
|
||||
|
||||
struct sh4_dtr dtr = {0};
|
||||
dtr.channel = 0;
|
||||
dtr.rw = true;
|
||||
dtr.data = sector_data;
|
||||
dtr.addr = addr;
|
||||
dtr.size = n;
|
||||
sh4_ddt(dc->sh4, &dtr);
|
||||
|
||||
remaining -= n;
|
||||
addr += n;
|
||||
}
|
||||
|
||||
gdrom_dma_end(gd);
|
||||
|
||||
*hl->SB_GDSTARD = start + transfer_size;
|
||||
*hl->SB_GDLEND = transfer_size;
|
||||
*hl->SB_GDST = 0;
|
||||
holly_raise_interrupt(hl, HOLLY_INTC_G1DEINT);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_ADEN) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_ADEN = value;
|
||||
if (!*hl->SB_ADEN) {
|
||||
return;
|
||||
}
|
||||
LOG_WARNING("Ignored aica DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_ADST) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_ADST = value;
|
||||
if (!*hl->SB_ADST) {
|
||||
return;
|
||||
if ((*hl->SB_ADST = value)) {
|
||||
holly_g2_dma(hl, 0);
|
||||
}
|
||||
LOG_WARNING("Ignored aica DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_E1EN) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_E1EN = value;
|
||||
if (!*hl->SB_E1EN) {
|
||||
return;
|
||||
}
|
||||
LOG_WARNING("Ignored ext1 DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_E1ST) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_E1ST = value;
|
||||
if (!*hl->SB_E1ST) {
|
||||
return;
|
||||
if ((*hl->SB_E1ST = value)) {
|
||||
holly_g2_dma(hl, 1);
|
||||
}
|
||||
LOG_WARNING("Ignored ext1 DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_E2EN) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_E2EN = value;
|
||||
if (!*hl->SB_E2EN) {
|
||||
return;
|
||||
}
|
||||
LOG_WARNING("Ignored ext2 DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_E2ST) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_E2ST = value;
|
||||
if (!*hl->SB_E2ST) {
|
||||
return;
|
||||
if ((*hl->SB_E2ST = value)) {
|
||||
holly_g2_dma(hl, 2);
|
||||
}
|
||||
LOG_WARNING("Ignored ext2 DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_DDEN) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_DDEN = value;
|
||||
if (!*hl->SB_DDEN) {
|
||||
return;
|
||||
}
|
||||
LOG_WARNING("Ignored dev DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_DDST) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_DDST = value;
|
||||
if (!*hl->SB_DDST) {
|
||||
return;
|
||||
if ((*hl->SB_DDST = value)) {
|
||||
holly_g2_dma(hl, 3);
|
||||
}
|
||||
LOG_WARNING("Ignored dev DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_PDEN) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_PDEN = value;
|
||||
if (!*hl->SB_PDEN) {
|
||||
return;
|
||||
}
|
||||
LOG_WARNING("Ignored pvr DMA request");
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_PDST) {
|
||||
struct holly *hl = dc->holly;
|
||||
*hl->SB_PDST = value;
|
||||
if (!*hl->SB_PDST) {
|
||||
return;
|
||||
if ((*hl->SB_PDST = value)) {
|
||||
LOG_WARNING("Ignored pvr DMA request");
|
||||
}
|
||||
LOG_WARNING("Ignored pvr DMA request");
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
|
|
@ -8,45 +8,6 @@ struct maple {
|
|||
struct maple_device *devices[4];
|
||||
};
|
||||
|
||||
static void maple_dma(struct maple *mp) {
|
||||
struct address_space *space = mp->sh4->memory_if->space;
|
||||
uint32_t start_addr = mp->holly->reg[SB_MDSTAR];
|
||||
union maple_transfer desc;
|
||||
struct maple_frame frame, res;
|
||||
|
||||
do {
|
||||
desc.full = as_read64(space, start_addr);
|
||||
start_addr += 8;
|
||||
|
||||
// read input
|
||||
frame.header.full = as_read32(space, start_addr);
|
||||
start_addr += 4;
|
||||
|
||||
for (uint32_t i = 0; i < frame.header.num_words; i++) {
|
||||
frame.params[i] = as_read32(space, start_addr);
|
||||
start_addr += 4;
|
||||
}
|
||||
|
||||
// handle frame and write response
|
||||
struct maple_device *dev = mp->devices[desc.port];
|
||||
|
||||
if (dev && dev->frame(dev, &frame, &res)) {
|
||||
as_write32(space, desc.result_addr, res.header.full);
|
||||
desc.result_addr += 4;
|
||||
|
||||
for (uint32_t i = 0; i < res.header.num_words; i++) {
|
||||
as_write32(space, desc.result_addr, res.params[i]);
|
||||
desc.result_addr += 4;
|
||||
}
|
||||
} else {
|
||||
as_write32(space, desc.result_addr, 0xffffffff);
|
||||
}
|
||||
} while (!desc.last);
|
||||
|
||||
mp->holly->reg[SB_MDST] = 0;
|
||||
holly_raise_interrupt(mp->holly, HOLLY_INTC_MDEINT);
|
||||
}
|
||||
|
||||
static bool maple_init(struct device *dev) {
|
||||
// struct maple *mp = (struct maple *)dev;
|
||||
return true;
|
||||
|
@ -61,19 +22,15 @@ static void maple_keydown(struct device *dev, enum keycode key, int16_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
void maple_vblank(struct maple *mp) {
|
||||
uint32_t enabled = mp->holly->reg[SB_MDEN];
|
||||
uint32_t vblank_initiate = mp->holly->reg[SB_MDTSEL];
|
||||
int maple_handle_command(struct maple *mp, int port, struct maple_frame *frame,
|
||||
struct maple_frame *res) {
|
||||
struct maple_device *dev = mp->devices[port];
|
||||
|
||||
// The controller can be started up by two methods: by software, or by
|
||||
// hardware
|
||||
// in synchronization with the V-BLANK signal. These methods are selected
|
||||
// through the trigger selection register (SB_MDTSEL).
|
||||
if (enabled && vblank_initiate) {
|
||||
maple_dma(mp);
|
||||
if (!dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO maple vblank interrupt?
|
||||
return dev->frame(dev, frame, res);
|
||||
}
|
||||
|
||||
struct maple *maple_create(struct dreamcast *dc) {
|
||||
|
@ -98,18 +55,3 @@ void maple_destroy(struct maple *mp) {
|
|||
dc_destroy_window_interface(mp->window_if);
|
||||
dc_destroy_device((struct device *)mp);
|
||||
}
|
||||
|
||||
REG_W32(holly_cb, SB_MDST) {
|
||||
struct maple *mp = dc->maple;
|
||||
struct holly *hl = dc->holly;
|
||||
|
||||
*hl->SB_MDST = value;
|
||||
|
||||
if (*hl->SB_MDEN) {
|
||||
if (*hl->SB_MDST) {
|
||||
maple_dma(mp);
|
||||
}
|
||||
} else {
|
||||
*hl->SB_MDST = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ struct maple_device {
|
|||
|
||||
struct maple_device *controller_create();
|
||||
|
||||
void maple_vblank(struct maple *mp);
|
||||
int maple_handle_command(struct maple *mp, int port, struct maple_frame *frame,
|
||||
struct maple_frame *res);
|
||||
|
||||
struct maple *maple_create(struct dreamcast *dc);
|
||||
void maple_destroy(struct maple *mp);
|
||||
|
||||
|
|
Loading…
Reference in New Issue