mirror of https://github.com/mgba-emu/mgba.git
105 lines
2.6 KiB
C
105 lines
2.6 KiB
C
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#include <mgba/internal/gb/sio.h>
|
|
|
|
#include <mgba/internal/gb/gb.h>
|
|
#include <mgba/internal/gb/io.h>
|
|
#include <mgba/internal/gb/serialize.h>
|
|
|
|
mLOG_DEFINE_CATEGORY(GB_SIO, "GB Serial I/O", "gb.sio");
|
|
|
|
const int GBSIOCyclesPerTransfer[2] = {
|
|
512,
|
|
16
|
|
};
|
|
|
|
void _GBSIOProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
|
|
|
void GBSIOInit(struct GBSIO* sio) {
|
|
sio->pendingSB = 0xFF;
|
|
sio->event.context = sio;
|
|
sio->event.name = "GB SIO";
|
|
sio->event.callback = _GBSIOProcessEvents;
|
|
sio->event.priority = 0x30;
|
|
|
|
sio->driver = NULL;
|
|
}
|
|
|
|
void GBSIOReset(struct GBSIO* sio) {
|
|
sio->nextEvent = INT_MAX;
|
|
sio->remainingBits = 0;
|
|
GBSIOSetDriver(sio, sio->driver);
|
|
}
|
|
|
|
void GBSIODeinit(struct GBSIO* sio) {
|
|
UNUSED(sio);
|
|
// Nothing to do yet
|
|
}
|
|
|
|
void GBSIOSetDriver(struct GBSIO* sio, struct GBSIODriver* driver) {
|
|
if (sio->driver) {
|
|
if (sio->driver->deinit) {
|
|
sio->driver->deinit(sio->driver);
|
|
}
|
|
}
|
|
if (driver) {
|
|
driver->p = sio;
|
|
|
|
if (driver->init) {
|
|
if (!driver->init(driver)) {
|
|
driver->deinit(driver);
|
|
mLOG(GB_SIO, ERROR, "Could not initialize SIO driver");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
sio->driver = driver;
|
|
}
|
|
|
|
void _GBSIOProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|
UNUSED(cyclesLate);
|
|
struct GBSIO* sio = context;
|
|
bool doIRQ = false;
|
|
if (sio->remainingBits) {
|
|
doIRQ = true;
|
|
--sio->remainingBits;
|
|
sio->p->memory.io[REG_SB] &= ~(128 >> sio->remainingBits);
|
|
sio->p->memory.io[REG_SB] |= sio->pendingSB & (128 >> sio->remainingBits);
|
|
}
|
|
if (!sio->remainingBits) {
|
|
sio->p->memory.io[REG_SC] = GBRegisterSCClearEnable(sio->p->memory.io[REG_SC]);
|
|
if (doIRQ) {
|
|
sio->p->memory.io[REG_IF] |= (1 << GB_IRQ_SIO);
|
|
GBUpdateIRQs(sio->p);
|
|
sio->pendingSB = 0xFF;
|
|
}
|
|
} else {
|
|
mTimingSchedule(timing, &sio->event, sio->period);
|
|
}
|
|
}
|
|
|
|
void GBSIOWriteSB(struct GBSIO* sio, uint8_t sb) {
|
|
if (!sio->driver) {
|
|
return;
|
|
}
|
|
sio->driver->writeSB(sio->driver, sb);
|
|
}
|
|
|
|
void GBSIOWriteSC(struct GBSIO* sio, uint8_t sc) {
|
|
sio->period = GBSIOCyclesPerTransfer[GBRegisterSCGetClockSpeed(sc)]; // TODO Shift Clock
|
|
if (GBRegisterSCIsEnable(sc)) {
|
|
mTimingDeschedule(&sio->p->timing, &sio->event);
|
|
if (GBRegisterSCIsShiftClock(sc)) {
|
|
mTimingSchedule(&sio->p->timing, &sio->event, sio->period);
|
|
sio->remainingBits = 8;
|
|
}
|
|
}
|
|
if (sio->driver) {
|
|
sio->driver->writeSC(sio->driver, sc);
|
|
}
|
|
}
|
|
|