mirror of https://github.com/mgba-emu/mgba.git
GBA: Refactor gba-sensors and gba-gpio into gba-hardware
This commit is contained in:
parent
0de46a7867
commit
5499ec8113
|
@ -1,459 +0,0 @@
|
||||||
/* Copyright (c) 2013-2014 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 "gba.h"
|
|
||||||
|
|
||||||
#include "gba-gpio.h"
|
|
||||||
#include "gba-sensors.h"
|
|
||||||
#include "gba-serialize.h"
|
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
static void _readPins(struct GBACartridgeGPIO* gpio);
|
|
||||||
static void _outputPins(struct GBACartridgeGPIO* gpio, unsigned pins);
|
|
||||||
|
|
||||||
static void _rtcReadPins(struct GBACartridgeGPIO* gpio);
|
|
||||||
static unsigned _rtcOutput(struct GBACartridgeGPIO* gpio);
|
|
||||||
static void _rtcProcessByte(struct GBACartridgeGPIO* gpio);
|
|
||||||
static void _rtcUpdateClock(struct GBACartridgeGPIO* gpio);
|
|
||||||
static unsigned _rtcBCD(unsigned value);
|
|
||||||
|
|
||||||
static void _gyroReadPins(struct GBACartridgeGPIO* gpio);
|
|
||||||
|
|
||||||
static void _rumbleReadPins(struct GBACartridgeGPIO* gpio);
|
|
||||||
|
|
||||||
static void _lightReadPins(struct GBACartridgeGPIO* gpio);
|
|
||||||
|
|
||||||
static const int RTC_BYTES[8] = {
|
|
||||||
0, // Force reset
|
|
||||||
0, // Empty
|
|
||||||
7, // Date/Time
|
|
||||||
0, // Force IRQ
|
|
||||||
1, // Control register
|
|
||||||
0, // Empty
|
|
||||||
3, // Time
|
|
||||||
0 // Empty
|
|
||||||
};
|
|
||||||
|
|
||||||
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* base) {
|
|
||||||
gpio->gpioBase = base;
|
|
||||||
GBAGPIOClear(gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAGPIOClear(struct GBACartridgeGPIO* gpio) {
|
|
||||||
gpio->gpioDevices = GPIO_NONE;
|
|
||||||
gpio->direction = GPIO_WRITE_ONLY;
|
|
||||||
gpio->pinState = 0;
|
|
||||||
gpio->direction = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value) {
|
|
||||||
switch (address) {
|
|
||||||
case GPIO_REG_DATA:
|
|
||||||
gpio->pinState &= ~gpio->direction;
|
|
||||||
gpio->pinState |= value;
|
|
||||||
_readPins(gpio);
|
|
||||||
break;
|
|
||||||
case GPIO_REG_DIRECTION:
|
|
||||||
gpio->direction = value;
|
|
||||||
break;
|
|
||||||
case GPIO_REG_CONTROL:
|
|
||||||
gpio->readWrite = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
GBALog(gpio->p, GBA_LOG_WARN, "Invalid GPIO address");
|
|
||||||
}
|
|
||||||
if (gpio->readWrite) {
|
|
||||||
uint16_t old = gpio->gpioBase[0];
|
|
||||||
old &= ~gpio->direction;
|
|
||||||
gpio->gpioBase[0] = old | gpio->pinState;
|
|
||||||
} else {
|
|
||||||
gpio->gpioBase[0] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio) {
|
|
||||||
gpio->gpioDevices |= GPIO_RTC;
|
|
||||||
gpio->rtc.bytesRemaining = 0;
|
|
||||||
|
|
||||||
gpio->rtc.transferStep = 0;
|
|
||||||
|
|
||||||
gpio->rtc.bitsRead = 0;
|
|
||||||
gpio->rtc.bits = 0;
|
|
||||||
gpio->rtc.commandActive = 0;
|
|
||||||
gpio->rtc.command.packed = 0;
|
|
||||||
gpio->rtc.control.packed = 0x40;
|
|
||||||
memset(gpio->rtc.time, 0, sizeof(gpio->rtc.time));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _readPins(struct GBACartridgeGPIO* gpio) {
|
|
||||||
if (gpio->gpioDevices & GPIO_RTC) {
|
|
||||||
_rtcReadPins(gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpio->gpioDevices & GPIO_GYRO) {
|
|
||||||
_gyroReadPins(gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpio->gpioDevices & GPIO_RUMBLE) {
|
|
||||||
_rumbleReadPins(gpio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpio->gpioDevices & GPIO_LIGHT_SENSOR) {
|
|
||||||
_lightReadPins(gpio);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _outputPins(struct GBACartridgeGPIO* gpio, unsigned pins) {
|
|
||||||
if (gpio->readWrite) {
|
|
||||||
uint16_t old = gpio->gpioBase[0];
|
|
||||||
old &= gpio->direction;
|
|
||||||
gpio->pinState = old | (pins & ~gpio->direction & 0xF);
|
|
||||||
gpio->gpioBase[0] = gpio->pinState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// == RTC
|
|
||||||
|
|
||||||
void _rtcReadPins(struct GBACartridgeGPIO* gpio) {
|
|
||||||
// Transfer sequence:
|
|
||||||
// P: 0 | 1 | 2 | 3
|
|
||||||
// == Initiate
|
|
||||||
// > HI | - | LO | -
|
|
||||||
// > HI | - | HI | -
|
|
||||||
// == Transfer bit (x8)
|
|
||||||
// > LO | x | HI | -
|
|
||||||
// > HI | - | HI | -
|
|
||||||
// < ?? | x | ?? | -
|
|
||||||
// == Terminate
|
|
||||||
// > - | - | LO | -
|
|
||||||
switch (gpio->rtc.transferStep) {
|
|
||||||
case 0:
|
|
||||||
if ((gpio->pinState & 5) == 1) {
|
|
||||||
gpio->rtc.transferStep = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if ((gpio->pinState & 5) == 5) {
|
|
||||||
gpio->rtc.transferStep = 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
if (!gpio->p0) {
|
|
||||||
gpio->rtc.bits &= ~(1 << gpio->rtc.bitsRead);
|
|
||||||
gpio->rtc.bits |= gpio->p1 << gpio->rtc.bitsRead;
|
|
||||||
} else {
|
|
||||||
if (gpio->p2) {
|
|
||||||
// GPIO direction should always != reading
|
|
||||||
if (gpio->dir1) {
|
|
||||||
if (gpio->rtc.command.reading) {
|
|
||||||
GBALog(gpio->p, GBA_LOG_GAME_ERROR, "Attempting to write to RTC while in read mode");
|
|
||||||
}
|
|
||||||
++gpio->rtc.bitsRead;
|
|
||||||
if (gpio->rtc.bitsRead == 8) {
|
|
||||||
_rtcProcessByte(gpio);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_outputPins(gpio, 5 | (_rtcOutput(gpio) << 1));
|
|
||||||
++gpio->rtc.bitsRead;
|
|
||||||
if (gpio->rtc.bitsRead == 8) {
|
|
||||||
--gpio->rtc.bytesRemaining;
|
|
||||||
if (gpio->rtc.bytesRemaining <= 0) {
|
|
||||||
gpio->rtc.commandActive = 0;
|
|
||||||
gpio->rtc.command.reading = 0;
|
|
||||||
}
|
|
||||||
gpio->rtc.bitsRead = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gpio->rtc.bitsRead = 0;
|
|
||||||
gpio->rtc.bytesRemaining = 0;
|
|
||||||
gpio->rtc.commandActive = 0;
|
|
||||||
gpio->rtc.command.reading = 0;
|
|
||||||
gpio->rtc.transferStep = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rtcProcessByte(struct GBACartridgeGPIO* gpio) {
|
|
||||||
--gpio->rtc.bytesRemaining;
|
|
||||||
if (!gpio->rtc.commandActive) {
|
|
||||||
union RTCCommandData command;
|
|
||||||
command.packed = gpio->rtc.bits;
|
|
||||||
if (command.magic == 0x06) {
|
|
||||||
gpio->rtc.command = command;
|
|
||||||
|
|
||||||
gpio->rtc.bytesRemaining = RTC_BYTES[gpio->rtc.command.command];
|
|
||||||
gpio->rtc.commandActive = gpio->rtc.bytesRemaining > 0;
|
|
||||||
switch (command.command) {
|
|
||||||
case RTC_RESET:
|
|
||||||
gpio->rtc.control.packed = 0;
|
|
||||||
break;
|
|
||||||
case RTC_DATETIME:
|
|
||||||
case RTC_TIME:
|
|
||||||
_rtcUpdateClock(gpio);
|
|
||||||
break;
|
|
||||||
case RTC_FORCE_IRQ:
|
|
||||||
case RTC_CONTROL:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GBALog(gpio->p, GBA_LOG_WARN, "Invalid RTC command byte: %02X", gpio->rtc.bits);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (gpio->rtc.command.command) {
|
|
||||||
case RTC_CONTROL:
|
|
||||||
gpio->rtc.control.packed = gpio->rtc.bits;
|
|
||||||
break;
|
|
||||||
case RTC_FORCE_IRQ:
|
|
||||||
GBALog(gpio->p, GBA_LOG_STUB, "Unimplemented RTC command %u", gpio->rtc.command.command);
|
|
||||||
break;
|
|
||||||
case RTC_RESET:
|
|
||||||
case RTC_DATETIME:
|
|
||||||
case RTC_TIME:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gpio->rtc.bits = 0;
|
|
||||||
gpio->rtc.bitsRead = 0;
|
|
||||||
if (!gpio->rtc.bytesRemaining) {
|
|
||||||
gpio->rtc.commandActive = 0;
|
|
||||||
gpio->rtc.command.reading = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned _rtcOutput(struct GBACartridgeGPIO* gpio) {
|
|
||||||
uint8_t outputByte = 0;
|
|
||||||
switch (gpio->rtc.command.command) {
|
|
||||||
case RTC_CONTROL:
|
|
||||||
outputByte = gpio->rtc.control.packed;
|
|
||||||
break;
|
|
||||||
case RTC_DATETIME:
|
|
||||||
case RTC_TIME:
|
|
||||||
outputByte = gpio->rtc.time[7 - gpio->rtc.bytesRemaining];
|
|
||||||
break;
|
|
||||||
case RTC_FORCE_IRQ:
|
|
||||||
case RTC_RESET:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
unsigned output = (outputByte >> gpio->rtc.bitsRead) & 1;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rtcUpdateClock(struct GBACartridgeGPIO* gpio) {
|
|
||||||
time_t t;
|
|
||||||
struct GBARTCSource* rtc = gpio->p->rtcSource;
|
|
||||||
if (rtc) {
|
|
||||||
rtc->sample(rtc);
|
|
||||||
t = rtc->unixTime(rtc);
|
|
||||||
} else {
|
|
||||||
t = time(0);
|
|
||||||
}
|
|
||||||
struct tm date;
|
|
||||||
#ifdef _WIN32
|
|
||||||
date = *localtime(&t);
|
|
||||||
#else
|
|
||||||
localtime_r(&t, &date);
|
|
||||||
#endif
|
|
||||||
gpio->rtc.time[0] = _rtcBCD(date.tm_year - 100);
|
|
||||||
gpio->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
|
|
||||||
gpio->rtc.time[2] = _rtcBCD(date.tm_mday);
|
|
||||||
gpio->rtc.time[3] = _rtcBCD(date.tm_wday);
|
|
||||||
if (gpio->rtc.control.hour24) {
|
|
||||||
gpio->rtc.time[4] = _rtcBCD(date.tm_hour);
|
|
||||||
} else {
|
|
||||||
gpio->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
|
|
||||||
}
|
|
||||||
gpio->rtc.time[5] = _rtcBCD(date.tm_min);
|
|
||||||
gpio->rtc.time[6] = _rtcBCD(date.tm_sec);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned _rtcBCD(unsigned value) {
|
|
||||||
int counter = value % 10;
|
|
||||||
value /= 10;
|
|
||||||
counter += (value % 10) << 4;
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Gyro
|
|
||||||
|
|
||||||
void GBAGPIOInitGyro(struct GBACartridgeGPIO* gpio) {
|
|
||||||
gpio->gpioDevices |= GPIO_GYRO;
|
|
||||||
gpio->gyroSample = 0;
|
|
||||||
gpio->gyroEdge = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _gyroReadPins(struct GBACartridgeGPIO* gpio) {
|
|
||||||
struct GBARotationSource* gyro = gpio->p->rotationSource;
|
|
||||||
if (!gyro) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpio->p0) {
|
|
||||||
if (gyro->sample) {
|
|
||||||
gyro->sample(gyro);
|
|
||||||
}
|
|
||||||
int32_t sample = gyro->readGyroZ(gyro);
|
|
||||||
|
|
||||||
// Normalize to ~12 bits, focused on 0x6C0
|
|
||||||
gpio->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpio->gyroEdge && !gpio->p1) {
|
|
||||||
// Write bit on falling edge
|
|
||||||
unsigned bit = gpio->gyroSample >> 15;
|
|
||||||
gpio->gyroSample <<= 1;
|
|
||||||
_outputPins(gpio, bit << 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
gpio->gyroEdge = gpio->p1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Rumble
|
|
||||||
|
|
||||||
void GBAGPIOInitRumble(struct GBACartridgeGPIO* gpio) {
|
|
||||||
gpio->gpioDevices |= GPIO_RUMBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _rumbleReadPins(struct GBACartridgeGPIO* gpio) {
|
|
||||||
struct GBARumble* rumble = gpio->p->rumble;
|
|
||||||
if (!rumble) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rumble->setRumble(rumble, gpio->p3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Light sensor
|
|
||||||
|
|
||||||
void GBAGPIOInitLightSensor(struct GBACartridgeGPIO* gpio) {
|
|
||||||
gpio->gpioDevices |= GPIO_LIGHT_SENSOR;
|
|
||||||
gpio->lightCounter = 0;
|
|
||||||
gpio->lightEdge = false;
|
|
||||||
gpio->lightSample = 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _lightReadPins(struct GBACartridgeGPIO* gpio) {
|
|
||||||
if (gpio->p2) {
|
|
||||||
// Boktai chip select
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (gpio->p1) {
|
|
||||||
struct GBALuminanceSource* lux = gpio->p->luminanceSource;
|
|
||||||
GBALog(gpio->p, GBA_LOG_DEBUG, "[SOLAR] Got reset");
|
|
||||||
gpio->lightCounter = 0;
|
|
||||||
if (lux) {
|
|
||||||
lux->sample(lux);
|
|
||||||
gpio->lightSample = lux->readLuminance(lux);
|
|
||||||
} else {
|
|
||||||
gpio->lightSample = 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gpio->p0 && gpio->lightEdge) {
|
|
||||||
++gpio->lightCounter;
|
|
||||||
}
|
|
||||||
gpio->lightEdge = !gpio->p0;
|
|
||||||
|
|
||||||
bool sendBit = gpio->lightCounter >= gpio->lightSample;
|
|
||||||
_outputPins(gpio, sendBit << 3);
|
|
||||||
GBALog(gpio->p, GBA_LOG_DEBUG, "[SOLAR] Output %u with pins %u", gpio->lightCounter, gpio->pinState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Tilt (not technically GPIO)
|
|
||||||
|
|
||||||
void GBAGPIOInitTilt(struct GBACartridgeGPIO* gpio) {
|
|
||||||
gpio->gpioDevices |= GPIO_TILT;
|
|
||||||
gpio->tiltX = 0xFFF;
|
|
||||||
gpio->tiltY = 0xFFF;
|
|
||||||
gpio->tiltState = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAGPIOTiltWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint8_t value) {
|
|
||||||
switch (address) {
|
|
||||||
case 0x8000:
|
|
||||||
if (value == 0x55) {
|
|
||||||
gpio->tiltState = 1;
|
|
||||||
} else {
|
|
||||||
GBALog(gpio->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x8100:
|
|
||||||
if (value == 0xAA && gpio->tiltState == 1) {
|
|
||||||
gpio->tiltState = 0;
|
|
||||||
struct GBARotationSource* rotationSource = gpio->p->rotationSource;
|
|
||||||
if (!rotationSource || !rotationSource->readTiltX || !rotationSource->readTiltY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (rotationSource->sample) {
|
|
||||||
rotationSource->sample(rotationSource);
|
|
||||||
}
|
|
||||||
int32_t x = rotationSource->readTiltX(rotationSource);
|
|
||||||
int32_t y = rotationSource->readTiltY(rotationSource);
|
|
||||||
// Normalize to ~12 bits, focused on 0x3A0
|
|
||||||
gpio->tiltX = (x >> 21) + 0x3A0; // Crop off an extra bit so that we can't go negative
|
|
||||||
gpio->tiltY = (y >> 21) + 0x3A0;
|
|
||||||
} else {
|
|
||||||
GBALog(gpio->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
GBALog(gpio->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor write to %04x: %02x", address, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t GBAGPIOTiltRead(struct GBACartridgeGPIO* gpio, uint32_t address) {
|
|
||||||
switch (address) {
|
|
||||||
case 0x8200:
|
|
||||||
return gpio->tiltX & 0xFF;
|
|
||||||
case 0x8300:
|
|
||||||
return ((gpio->tiltX >> 8) & 0xF) | 0x80;
|
|
||||||
case 0x8400:
|
|
||||||
return gpio->tiltY & 0xFF;
|
|
||||||
case 0x8500:
|
|
||||||
return (gpio->tiltY >> 8) & 0xF;
|
|
||||||
default:
|
|
||||||
GBALog(gpio->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor read from %04x", address);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Serialization
|
|
||||||
|
|
||||||
void GBAGPIOSerialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state) {
|
|
||||||
state->gpio.readWrite = gpio->readWrite;
|
|
||||||
state->gpio.pinState = gpio->pinState;
|
|
||||||
state->gpio.pinDirection = gpio->direction;
|
|
||||||
state->gpio.devices = gpio->gpioDevices;
|
|
||||||
state->gpio.rtc = gpio->rtc;
|
|
||||||
state->gpio.gyroSample = gpio->gyroSample;
|
|
||||||
state->gpio.gyroEdge = gpio->gyroEdge;
|
|
||||||
state->gpio.tiltSampleX = gpio->tiltX;
|
|
||||||
state->gpio.tiltSampleY = gpio->tiltY;
|
|
||||||
state->gpio.tiltState = gpio->tiltState;
|
|
||||||
state->gpio.lightCounter = gpio->lightCounter;
|
|
||||||
state->gpio.lightSample = gpio->lightSample;
|
|
||||||
state->gpio.lightEdge = gpio->lightEdge;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAGPIODeserialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state) {
|
|
||||||
gpio->readWrite = state->gpio.readWrite;
|
|
||||||
gpio->pinState = state->gpio.pinState;
|
|
||||||
gpio->direction = state->gpio.pinDirection;
|
|
||||||
// TODO: Deterministic RTC
|
|
||||||
gpio->rtc = state->gpio.rtc;
|
|
||||||
gpio->gyroSample = state->gpio.gyroSample;
|
|
||||||
gpio->gyroEdge = state->gpio.gyroEdge;
|
|
||||||
gpio->tiltX = state->gpio.tiltSampleX;
|
|
||||||
gpio->tiltY = state->gpio.tiltSampleY;
|
|
||||||
gpio->tiltState = state->gpio.tiltState;
|
|
||||||
gpio->lightCounter = state->gpio.lightCounter;
|
|
||||||
gpio->lightSample = state->gpio.lightSample;
|
|
||||||
gpio->lightEdge = state->gpio.lightEdge;
|
|
||||||
}
|
|
|
@ -0,0 +1,458 @@
|
||||||
|
/* Copyright (c) 2013-2015 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 "gba.h"
|
||||||
|
|
||||||
|
#include "gba-hardware.h"
|
||||||
|
#include "gba-serialize.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static void _readPins(struct GBACartridgeHardware* hw);
|
||||||
|
static void _outputPins(struct GBACartridgeHardware* hw, unsigned pins);
|
||||||
|
|
||||||
|
static void _rtcReadPins(struct GBACartridgeHardware* hw);
|
||||||
|
static unsigned _rtcOutput(struct GBACartridgeHardware* hw);
|
||||||
|
static void _rtcProcessByte(struct GBACartridgeHardware* hw);
|
||||||
|
static void _rtcUpdateClock(struct GBACartridgeHardware* hw);
|
||||||
|
static unsigned _rtcBCD(unsigned value);
|
||||||
|
|
||||||
|
static void _gyroReadPins(struct GBACartridgeHardware* hw);
|
||||||
|
|
||||||
|
static void _rumbleReadPins(struct GBACartridgeHardware* hw);
|
||||||
|
|
||||||
|
static void _lightReadPins(struct GBACartridgeHardware* hw);
|
||||||
|
|
||||||
|
static const int RTC_BYTES[8] = {
|
||||||
|
0, // Force reset
|
||||||
|
0, // Empty
|
||||||
|
7, // Date/Time
|
||||||
|
0, // Force IRQ
|
||||||
|
1, // Control register
|
||||||
|
0, // Empty
|
||||||
|
3, // Time
|
||||||
|
0 // Empty
|
||||||
|
};
|
||||||
|
|
||||||
|
void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) {
|
||||||
|
hw->gpioBase = base;
|
||||||
|
GBAHardwareClear(hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAHardwareClear(struct GBACartridgeHardware* hw) {
|
||||||
|
hw->devices = HW_NONE;
|
||||||
|
hw->direction = GPIO_WRITE_ONLY;
|
||||||
|
hw->pinState = 0;
|
||||||
|
hw->direction = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
|
||||||
|
switch (address) {
|
||||||
|
case GPIO_REG_DATA:
|
||||||
|
hw->pinState &= ~hw->direction;
|
||||||
|
hw->pinState |= value;
|
||||||
|
_readPins(hw);
|
||||||
|
break;
|
||||||
|
case GPIO_REG_DIRECTION:
|
||||||
|
hw->direction = value;
|
||||||
|
break;
|
||||||
|
case GPIO_REG_CONTROL:
|
||||||
|
hw->readWrite = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GBALog(hw->p, GBA_LOG_WARN, "Invalid GPIO address");
|
||||||
|
}
|
||||||
|
if (hw->readWrite) {
|
||||||
|
uint16_t old = hw->gpioBase[0];
|
||||||
|
old &= ~hw->direction;
|
||||||
|
hw->gpioBase[0] = old | hw->pinState;
|
||||||
|
} else {
|
||||||
|
hw->gpioBase[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAHardwareInitRTC(struct GBACartridgeHardware* hw) {
|
||||||
|
hw->devices |= HW_RTC;
|
||||||
|
hw->rtc.bytesRemaining = 0;
|
||||||
|
|
||||||
|
hw->rtc.transferStep = 0;
|
||||||
|
|
||||||
|
hw->rtc.bitsRead = 0;
|
||||||
|
hw->rtc.bits = 0;
|
||||||
|
hw->rtc.commandActive = 0;
|
||||||
|
hw->rtc.command.packed = 0;
|
||||||
|
hw->rtc.control.packed = 0x40;
|
||||||
|
memset(hw->rtc.time, 0, sizeof(hw->rtc.time));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _readPins(struct GBACartridgeHardware* hw) {
|
||||||
|
if (hw->devices & HW_RTC) {
|
||||||
|
_rtcReadPins(hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hw->devices & HW_GYRO) {
|
||||||
|
_gyroReadPins(hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hw->devices & HW_RUMBLE) {
|
||||||
|
_rumbleReadPins(hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hw->devices & HW_LIGHT_SENSOR) {
|
||||||
|
_lightReadPins(hw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _outputPins(struct GBACartridgeHardware* hw, unsigned pins) {
|
||||||
|
if (hw->readWrite) {
|
||||||
|
uint16_t old = hw->gpioBase[0];
|
||||||
|
old &= hw->direction;
|
||||||
|
hw->pinState = old | (pins & ~hw->direction & 0xF);
|
||||||
|
hw->gpioBase[0] = hw->pinState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// == RTC
|
||||||
|
|
||||||
|
void _rtcReadPins(struct GBACartridgeHardware* hw) {
|
||||||
|
// Transfer sequence:
|
||||||
|
// P: 0 | 1 | 2 | 3
|
||||||
|
// == Initiate
|
||||||
|
// > HI | - | LO | -
|
||||||
|
// > HI | - | HI | -
|
||||||
|
// == Transfer bit (x8)
|
||||||
|
// > LO | x | HI | -
|
||||||
|
// > HI | - | HI | -
|
||||||
|
// < ?? | x | ?? | -
|
||||||
|
// == Terminate
|
||||||
|
// > - | - | LO | -
|
||||||
|
switch (hw->rtc.transferStep) {
|
||||||
|
case 0:
|
||||||
|
if ((hw->pinState & 5) == 1) {
|
||||||
|
hw->rtc.transferStep = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if ((hw->pinState & 5) == 5) {
|
||||||
|
hw->rtc.transferStep = 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (!hw->p0) {
|
||||||
|
hw->rtc.bits &= ~(1 << hw->rtc.bitsRead);
|
||||||
|
hw->rtc.bits |= hw->p1 << hw->rtc.bitsRead;
|
||||||
|
} else {
|
||||||
|
if (hw->p2) {
|
||||||
|
// GPIO direction should always != reading
|
||||||
|
if (hw->dir1) {
|
||||||
|
if (hw->rtc.command.reading) {
|
||||||
|
GBALog(hw->p, GBA_LOG_GAME_ERROR, "Attempting to write to RTC while in read mode");
|
||||||
|
}
|
||||||
|
++hw->rtc.bitsRead;
|
||||||
|
if (hw->rtc.bitsRead == 8) {
|
||||||
|
_rtcProcessByte(hw);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_outputPins(hw, 5 | (_rtcOutput(hw) << 1));
|
||||||
|
++hw->rtc.bitsRead;
|
||||||
|
if (hw->rtc.bitsRead == 8) {
|
||||||
|
--hw->rtc.bytesRemaining;
|
||||||
|
if (hw->rtc.bytesRemaining <= 0) {
|
||||||
|
hw->rtc.commandActive = 0;
|
||||||
|
hw->rtc.command.reading = 0;
|
||||||
|
}
|
||||||
|
hw->rtc.bitsRead = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hw->rtc.bitsRead = 0;
|
||||||
|
hw->rtc.bytesRemaining = 0;
|
||||||
|
hw->rtc.commandActive = 0;
|
||||||
|
hw->rtc.command.reading = 0;
|
||||||
|
hw->rtc.transferStep = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rtcProcessByte(struct GBACartridgeHardware* hw) {
|
||||||
|
--hw->rtc.bytesRemaining;
|
||||||
|
if (!hw->rtc.commandActive) {
|
||||||
|
union RTCCommandData command;
|
||||||
|
command.packed = hw->rtc.bits;
|
||||||
|
if (command.magic == 0x06) {
|
||||||
|
hw->rtc.command = command;
|
||||||
|
|
||||||
|
hw->rtc.bytesRemaining = RTC_BYTES[hw->rtc.command.command];
|
||||||
|
hw->rtc.commandActive = hw->rtc.bytesRemaining > 0;
|
||||||
|
switch (command.command) {
|
||||||
|
case RTC_RESET:
|
||||||
|
hw->rtc.control.packed = 0;
|
||||||
|
break;
|
||||||
|
case RTC_DATETIME:
|
||||||
|
case RTC_TIME:
|
||||||
|
_rtcUpdateClock(hw);
|
||||||
|
break;
|
||||||
|
case RTC_FORCE_IRQ:
|
||||||
|
case RTC_CONTROL:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GBALog(hw->p, GBA_LOG_WARN, "Invalid RTC command byte: %02X", hw->rtc.bits);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (hw->rtc.command.command) {
|
||||||
|
case RTC_CONTROL:
|
||||||
|
hw->rtc.control.packed = hw->rtc.bits;
|
||||||
|
break;
|
||||||
|
case RTC_FORCE_IRQ:
|
||||||
|
GBALog(hw->p, GBA_LOG_STUB, "Unimplemented RTC command %u", hw->rtc.command.command);
|
||||||
|
break;
|
||||||
|
case RTC_RESET:
|
||||||
|
case RTC_DATETIME:
|
||||||
|
case RTC_TIME:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hw->rtc.bits = 0;
|
||||||
|
hw->rtc.bitsRead = 0;
|
||||||
|
if (!hw->rtc.bytesRemaining) {
|
||||||
|
hw->rtc.commandActive = 0;
|
||||||
|
hw->rtc.command.reading = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _rtcOutput(struct GBACartridgeHardware* hw) {
|
||||||
|
uint8_t outputByte = 0;
|
||||||
|
switch (hw->rtc.command.command) {
|
||||||
|
case RTC_CONTROL:
|
||||||
|
outputByte = hw->rtc.control.packed;
|
||||||
|
break;
|
||||||
|
case RTC_DATETIME:
|
||||||
|
case RTC_TIME:
|
||||||
|
outputByte = hw->rtc.time[7 - hw->rtc.bytesRemaining];
|
||||||
|
break;
|
||||||
|
case RTC_FORCE_IRQ:
|
||||||
|
case RTC_RESET:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
unsigned output = (outputByte >> hw->rtc.bitsRead) & 1;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rtcUpdateClock(struct GBACartridgeHardware* hw) {
|
||||||
|
time_t t;
|
||||||
|
struct GBARTCSource* rtc = hw->p->rtcSource;
|
||||||
|
if (rtc) {
|
||||||
|
rtc->sample(rtc);
|
||||||
|
t = rtc->unixTime(rtc);
|
||||||
|
} else {
|
||||||
|
t = time(0);
|
||||||
|
}
|
||||||
|
struct tm date;
|
||||||
|
#ifdef _WIN32
|
||||||
|
date = *localtime(&t);
|
||||||
|
#else
|
||||||
|
localtime_r(&t, &date);
|
||||||
|
#endif
|
||||||
|
hw->rtc.time[0] = _rtcBCD(date.tm_year - 100);
|
||||||
|
hw->rtc.time[1] = _rtcBCD(date.tm_mon + 1);
|
||||||
|
hw->rtc.time[2] = _rtcBCD(date.tm_mday);
|
||||||
|
hw->rtc.time[3] = _rtcBCD(date.tm_wday);
|
||||||
|
if (hw->rtc.control.hour24) {
|
||||||
|
hw->rtc.time[4] = _rtcBCD(date.tm_hour);
|
||||||
|
} else {
|
||||||
|
hw->rtc.time[4] = _rtcBCD(date.tm_hour % 12);
|
||||||
|
}
|
||||||
|
hw->rtc.time[5] = _rtcBCD(date.tm_min);
|
||||||
|
hw->rtc.time[6] = _rtcBCD(date.tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned _rtcBCD(unsigned value) {
|
||||||
|
int counter = value % 10;
|
||||||
|
value /= 10;
|
||||||
|
counter += (value % 10) << 4;
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// == Gyro
|
||||||
|
|
||||||
|
void GBAHardwareInitGyro(struct GBACartridgeHardware* hw) {
|
||||||
|
hw->devices |= HW_GYRO;
|
||||||
|
hw->gyroSample = 0;
|
||||||
|
hw->gyroEdge = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _gyroReadPins(struct GBACartridgeHardware* hw) {
|
||||||
|
struct GBARotationSource* gyro = hw->p->rotationSource;
|
||||||
|
if (!gyro) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hw->p0) {
|
||||||
|
if (gyro->sample) {
|
||||||
|
gyro->sample(gyro);
|
||||||
|
}
|
||||||
|
int32_t sample = gyro->readGyroZ(gyro);
|
||||||
|
|
||||||
|
// Normalize to ~12 bits, focused on 0x6C0
|
||||||
|
hw->gyroSample = (sample >> 21) + 0x6C0; // Crop off an extra bit so that we can't go negative
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hw->gyroEdge && !hw->p1) {
|
||||||
|
// Write bit on falling edge
|
||||||
|
unsigned bit = hw->gyroSample >> 15;
|
||||||
|
hw->gyroSample <<= 1;
|
||||||
|
_outputPins(hw, bit << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
hw->gyroEdge = hw->p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// == Rumble
|
||||||
|
|
||||||
|
void GBAHardwareInitRumble(struct GBACartridgeHardware* hw) {
|
||||||
|
hw->devices |= HW_RUMBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rumbleReadPins(struct GBACartridgeHardware* hw) {
|
||||||
|
struct GBARumble* rumble = hw->p->rumble;
|
||||||
|
if (!rumble) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rumble->setRumble(rumble, hw->p3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// == Light sensor
|
||||||
|
|
||||||
|
void GBAHardwareInitLight(struct GBACartridgeHardware* hw) {
|
||||||
|
hw->devices |= HW_LIGHT_SENSOR;
|
||||||
|
hw->lightCounter = 0;
|
||||||
|
hw->lightEdge = false;
|
||||||
|
hw->lightSample = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _lightReadPins(struct GBACartridgeHardware* hw) {
|
||||||
|
if (hw->p2) {
|
||||||
|
// Boktai chip select
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hw->p1) {
|
||||||
|
struct GBALuminanceSource* lux = hw->p->luminanceSource;
|
||||||
|
GBALog(hw->p, GBA_LOG_DEBUG, "[SOLAR] Got reset");
|
||||||
|
hw->lightCounter = 0;
|
||||||
|
if (lux) {
|
||||||
|
lux->sample(lux);
|
||||||
|
hw->lightSample = lux->readLuminance(lux);
|
||||||
|
} else {
|
||||||
|
hw->lightSample = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hw->p0 && hw->lightEdge) {
|
||||||
|
++hw->lightCounter;
|
||||||
|
}
|
||||||
|
hw->lightEdge = !hw->p0;
|
||||||
|
|
||||||
|
bool sendBit = hw->lightCounter >= hw->lightSample;
|
||||||
|
_outputPins(hw, sendBit << 3);
|
||||||
|
GBALog(hw->p, GBA_LOG_DEBUG, "[SOLAR] Output %u with pins %u", hw->lightCounter, hw->pinState);
|
||||||
|
}
|
||||||
|
|
||||||
|
// == Tilt
|
||||||
|
|
||||||
|
void GBAHardwareInitTilt(struct GBACartridgeHardware* hw) {
|
||||||
|
hw->devices |= HW_TILT;
|
||||||
|
hw->tiltX = 0xFFF;
|
||||||
|
hw->tiltY = 0xFFF;
|
||||||
|
hw->tiltState = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAHardwareTiltWrite(struct GBACartridgeHardware* hw, uint32_t address, uint8_t value) {
|
||||||
|
switch (address) {
|
||||||
|
case 0x8000:
|
||||||
|
if (value == 0x55) {
|
||||||
|
hw->tiltState = 1;
|
||||||
|
} else {
|
||||||
|
GBALog(hw->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x8100:
|
||||||
|
if (value == 0xAA && hw->tiltState == 1) {
|
||||||
|
hw->tiltState = 0;
|
||||||
|
struct GBARotationSource* rotationSource = hw->p->rotationSource;
|
||||||
|
if (!rotationSource || !rotationSource->readTiltX || !rotationSource->readTiltY) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (rotationSource->sample) {
|
||||||
|
rotationSource->sample(rotationSource);
|
||||||
|
}
|
||||||
|
int32_t x = rotationSource->readTiltX(rotationSource);
|
||||||
|
int32_t y = rotationSource->readTiltY(rotationSource);
|
||||||
|
// Normalize to ~12 bits, focused on 0x3A0
|
||||||
|
hw->tiltX = (x >> 21) + 0x3A0; // Crop off an extra bit so that we can't go negative
|
||||||
|
hw->tiltY = (y >> 21) + 0x3A0;
|
||||||
|
} else {
|
||||||
|
GBALog(hw->p, GBA_LOG_GAME_ERROR, "Tilt sensor wrote wrong byte to %04x: %02x", address, value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GBALog(hw->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor write to %04x: %02x", address, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
|
||||||
|
switch (address) {
|
||||||
|
case 0x8200:
|
||||||
|
return hw->tiltX & 0xFF;
|
||||||
|
case 0x8300:
|
||||||
|
return ((hw->tiltX >> 8) & 0xF) | 0x80;
|
||||||
|
case 0x8400:
|
||||||
|
return hw->tiltY & 0xFF;
|
||||||
|
case 0x8500:
|
||||||
|
return (hw->tiltY >> 8) & 0xF;
|
||||||
|
default:
|
||||||
|
GBALog(hw->p, GBA_LOG_GAME_ERROR, "Invalid tilt sensor read from %04x", address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// == Serialization
|
||||||
|
|
||||||
|
void GBAHardwareSerialize(struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
|
||||||
|
state->hw.readWrite = hw->readWrite;
|
||||||
|
state->hw.pinState = hw->pinState;
|
||||||
|
state->hw.pinDirection = hw->direction;
|
||||||
|
state->hw.devices = hw->devices;
|
||||||
|
state->hw.rtc = hw->rtc;
|
||||||
|
state->hw.gyroSample = hw->gyroSample;
|
||||||
|
state->hw.gyroEdge = hw->gyroEdge;
|
||||||
|
state->hw.tiltSampleX = hw->tiltX;
|
||||||
|
state->hw.tiltSampleY = hw->tiltY;
|
||||||
|
state->hw.tiltState = hw->tiltState;
|
||||||
|
state->hw.lightCounter = hw->lightCounter;
|
||||||
|
state->hw.lightSample = hw->lightSample;
|
||||||
|
state->hw.lightEdge = hw->lightEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, struct GBASerializedState* state) {
|
||||||
|
hw->readWrite = state->hw.readWrite;
|
||||||
|
hw->pinState = state->hw.pinState;
|
||||||
|
hw->direction = state->hw.pinDirection;
|
||||||
|
// TODO: Deterministic RTC
|
||||||
|
hw->rtc = state->hw.rtc;
|
||||||
|
hw->gyroSample = state->hw.gyroSample;
|
||||||
|
hw->gyroEdge = state->hw.gyroEdge;
|
||||||
|
hw->tiltX = state->hw.tiltSampleX;
|
||||||
|
hw->tiltY = state->hw.tiltSampleY;
|
||||||
|
hw->tiltState = state->hw.tiltState;
|
||||||
|
hw->lightCounter = state->hw.lightCounter;
|
||||||
|
hw->lightSample = state->hw.lightSample;
|
||||||
|
hw->lightEdge = state->hw.lightEdge;
|
||||||
|
}
|
|
@ -1,23 +1,44 @@
|
||||||
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#ifndef GBA_GPIO_H
|
#ifndef GBA_HARDWARE_H
|
||||||
#define GBA_GPIO_H
|
#define GBA_HARDWARE_H
|
||||||
|
|
||||||
#include "util/common.h"
|
#include "util/common.h"
|
||||||
|
|
||||||
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
|
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
|
||||||
|
|
||||||
enum GPIODevice {
|
struct GBARotationSource {
|
||||||
GPIO_NO_OVERRIDE = 0x8000,
|
void (*sample)(struct GBARotationSource*);
|
||||||
GPIO_NONE = 0,
|
|
||||||
GPIO_RTC = 1,
|
int32_t (*readTiltX)(struct GBARotationSource*);
|
||||||
GPIO_RUMBLE = 2,
|
int32_t (*readTiltY)(struct GBARotationSource*);
|
||||||
GPIO_LIGHT_SENSOR = 4,
|
|
||||||
GPIO_GYRO = 8,
|
int32_t (*readGyroZ)(struct GBARotationSource*);
|
||||||
GPIO_TILT = 16
|
};
|
||||||
|
|
||||||
|
struct GBALuminanceSource {
|
||||||
|
void (*sample)(struct GBALuminanceSource*);
|
||||||
|
|
||||||
|
uint8_t (*readLuminance)(struct GBALuminanceSource*);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GBARTCSource {
|
||||||
|
void (*sample)(struct GBARTCSource*);
|
||||||
|
|
||||||
|
time_t (*unixTime)(struct GBARTCSource*);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GBAHardwareDevice {
|
||||||
|
HW_NO_OVERRIDE = 0x8000,
|
||||||
|
HW_NONE = 0,
|
||||||
|
HW_RTC = 1,
|
||||||
|
HW_RUMBLE = 2,
|
||||||
|
HW_LIGHT_SENSOR = 4,
|
||||||
|
HW_GYRO = 8,
|
||||||
|
HW_TILT = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
enum GPIORegister {
|
enum GPIORegister {
|
||||||
|
@ -74,9 +95,9 @@ struct GBARumble {
|
||||||
void (*setRumble)(struct GBARumble*, int enable);
|
void (*setRumble)(struct GBARumble*, int enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBACartridgeGPIO {
|
struct GBACartridgeHardware {
|
||||||
struct GBA* p;
|
struct GBA* p;
|
||||||
int gpioDevices;
|
int devices;
|
||||||
enum GPIODirection readWrite;
|
enum GPIODirection readWrite;
|
||||||
uint16_t* gpioBase;
|
uint16_t* gpioBase;
|
||||||
|
|
||||||
|
@ -114,21 +135,21 @@ struct GBACartridgeGPIO {
|
||||||
int tiltState;
|
int tiltState;
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBAGPIOInit(struct GBACartridgeGPIO* gpio, uint16_t* gpioBase);
|
void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase);
|
||||||
void GBAGPIOClear(struct GBACartridgeGPIO* gpio);
|
void GBAHardwareClear(struct GBACartridgeHardware* gpio);
|
||||||
void GBAGPIOWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint16_t value);
|
|
||||||
|
|
||||||
void GBAGPIOInitRTC(struct GBACartridgeGPIO* gpio);
|
void GBAHardwareInitRTC(struct GBACartridgeHardware* gpio);
|
||||||
void GBAGPIOInitGyro(struct GBACartridgeGPIO* gpio);
|
void GBAHardwareInitGyro(struct GBACartridgeHardware* gpio);
|
||||||
void GBAGPIOInitRumble(struct GBACartridgeGPIO* gpio);
|
void GBAHardwareInitRumble(struct GBACartridgeHardware* gpio);
|
||||||
void GBAGPIOInitLightSensor(struct GBACartridgeGPIO* gpio);
|
void GBAHardwareInitLight(struct GBACartridgeHardware* gpio);
|
||||||
void GBAGPIOInitTilt(struct GBACartridgeGPIO* gpio);
|
void GBAHardwareInitTilt(struct GBACartridgeHardware* gpio);
|
||||||
|
|
||||||
void GBAGPIOTiltWrite(struct GBACartridgeGPIO* gpio, uint32_t address, uint8_t value);
|
void GBAHardwareGPIOWrite(struct GBACartridgeHardware* gpio, uint32_t address, uint16_t value);
|
||||||
uint8_t GBAGPIOTiltRead(struct GBACartridgeGPIO* gpio, uint32_t address);
|
void GBAHardwareTiltWrite(struct GBACartridgeHardware* gpio, uint32_t address, uint8_t value);
|
||||||
|
uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* gpio, uint32_t address);
|
||||||
|
|
||||||
struct GBASerializedState;
|
struct GBASerializedState;
|
||||||
void GBAGPIOSerialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state);
|
void GBAHardwareSerialize(struct GBACartridgeHardware* gpio, struct GBASerializedState* state);
|
||||||
void GBAGPIODeserialize(struct GBACartridgeGPIO* gpio, struct GBASerializedState* state);
|
void GBAHardwareDeserialize(struct GBACartridgeHardware* gpio, struct GBASerializedState* state);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -672,7 +672,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(state->timers, gba->timers, sizeof(state->timers));
|
memcpy(state->timers, gba->timers, sizeof(state->timers));
|
||||||
GBAGPIOSerialize(&gba->memory.gpio, state);
|
GBAHardwareSerialize(&gba->memory.hw, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state) {
|
void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state) {
|
||||||
|
@ -701,5 +701,5 @@ void GBAIODeserialize(struct GBA* gba, struct GBASerializedState* state) {
|
||||||
gba->timersEnabled |= 1 << i;
|
gba->timersEnabled |= 1 << i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GBAGPIODeserialize(&gba->memory.gpio, state);
|
GBAHardwareDeserialize(&gba->memory.hw, state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "gba-gpio.h"
|
#include "gba-hardware.h"
|
||||||
#include "gba-io.h"
|
#include "gba-io.h"
|
||||||
#include "gba-serialize.h"
|
#include "gba-serialize.h"
|
||||||
#include "hle-bios.h"
|
#include "hle-bios.h"
|
||||||
|
@ -46,7 +46,7 @@ void GBAMemoryInit(struct GBA* gba) {
|
||||||
gba->memory.wram = 0;
|
gba->memory.wram = 0;
|
||||||
gba->memory.iwram = 0;
|
gba->memory.iwram = 0;
|
||||||
gba->memory.rom = 0;
|
gba->memory.rom = 0;
|
||||||
gba->memory.gpio.p = gba;
|
gba->memory.hw.p = gba;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < 16; ++i) {
|
for (i = 0; i < 16; ++i) {
|
||||||
|
@ -566,8 +566,8 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
value = memory->savedata.data[address & (SIZE_CART_SRAM - 1)];
|
value = memory->savedata.data[address & (SIZE_CART_SRAM - 1)];
|
||||||
} else if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) {
|
} else if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) {
|
||||||
value = GBASavedataReadFlash(&memory->savedata, address);
|
value = GBASavedataReadFlash(&memory->savedata, address);
|
||||||
} else if (memory->gpio.gpioDevices & GPIO_TILT) {
|
} else if (memory->hw.devices & HW_TILT) {
|
||||||
value = GBAGPIOTiltRead(&memory->gpio, address & OFFSET_MASK);
|
value = GBAHardwareTiltRead(&memory->hw, address & OFFSET_MASK);
|
||||||
} else {
|
} else {
|
||||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Reading from non-existent SRAM: 0x%08X", address);
|
GBALog(gba, GBA_LOG_GAME_ERROR, "Reading from non-existent SRAM: 0x%08X", address);
|
||||||
value = 0xFF;
|
value = 0xFF;
|
||||||
|
@ -710,9 +710,9 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
||||||
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 1)) >> 1);
|
gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 1)) >> 1);
|
||||||
break;
|
break;
|
||||||
case REGION_CART0:
|
case REGION_CART0:
|
||||||
if (memory->gpio.gpioDevices != GPIO_NONE && IS_GPIO_REGISTER(address & 0xFFFFFF)) {
|
if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFF)) {
|
||||||
uint32_t reg = address & 0xFFFFFF;
|
uint32_t reg = address & 0xFFFFFF;
|
||||||
GBAGPIOWrite(&memory->gpio, reg, value);
|
GBAHardwareGPIOWrite(&memory->hw, reg, value);
|
||||||
} else {
|
} else {
|
||||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad cartridge Store16: 0x%08X", address);
|
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad cartridge Store16: 0x%08X", address);
|
||||||
}
|
}
|
||||||
|
@ -787,8 +787,8 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
||||||
GBASavedataWriteFlash(&memory->savedata, address, value);
|
GBASavedataWriteFlash(&memory->savedata, address, value);
|
||||||
} else if (memory->savedata.type == SAVEDATA_SRAM) {
|
} else if (memory->savedata.type == SAVEDATA_SRAM) {
|
||||||
memory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value;
|
memory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value;
|
||||||
} else if (memory->gpio.gpioDevices & GPIO_TILT) {
|
} else if (memory->hw.devices & HW_TILT) {
|
||||||
GBAGPIOTiltWrite(&memory->gpio, address & OFFSET_MASK, value);
|
GBAHardwareTiltWrite(&memory->hw, address & OFFSET_MASK, value);
|
||||||
} else {
|
} else {
|
||||||
GBALog(gba, GBA_LOG_GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address);
|
GBALog(gba, GBA_LOG_GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include "arm.h"
|
#include "arm.h"
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
|
|
||||||
#include "gba-gpio.h"
|
#include "gba-hardware.h"
|
||||||
#include "gba-savedata.h"
|
#include "gba-savedata.h"
|
||||||
|
|
||||||
enum GBAMemoryRegion {
|
enum GBAMemoryRegion {
|
||||||
|
@ -116,7 +116,7 @@ struct GBAMemory {
|
||||||
uint32_t* rom;
|
uint32_t* rom;
|
||||||
uint16_t io[SIZE_IO >> 1];
|
uint16_t io[SIZE_IO >> 1];
|
||||||
|
|
||||||
struct GBACartridgeGPIO gpio;
|
struct GBACartridgeHardware hw;
|
||||||
struct GBASavedata savedata;
|
struct GBASavedata savedata;
|
||||||
size_t romSize;
|
size_t romSize;
|
||||||
uint16_t romID;
|
uint16_t romID;
|
||||||
|
|
|
@ -6,107 +6,107 @@
|
||||||
#include "gba-overrides.h"
|
#include "gba-overrides.h"
|
||||||
|
|
||||||
#include "gba.h"
|
#include "gba.h"
|
||||||
#include "gba-gpio.h"
|
#include "gba-hardware.h"
|
||||||
|
|
||||||
#include "util/configuration.h"
|
#include "util/configuration.h"
|
||||||
|
|
||||||
static const struct GBACartridgeOverride _overrides[] = {
|
static const struct GBACartridgeOverride _overrides[] = {
|
||||||
// Boktai: The Sun is in Your Hand
|
// Boktai: The Sun is in Your Hand
|
||||||
{ "U3IJ", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
{ "U3IJ", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
||||||
{ "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
{ "U3IE", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
||||||
{ "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
{ "U3IP", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Boktai 2: Solar Boy Django
|
// Boktai 2: Solar Boy Django
|
||||||
{ "U32J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
{ "U32J", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
||||||
{ "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
{ "U32E", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
||||||
{ "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
{ "U32P", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Drill Dozer
|
// Drill Dozer
|
||||||
{ "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, IDLE_LOOP_NONE },
|
{ "V49J", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE },
|
||||||
{ "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, IDLE_LOOP_NONE },
|
{ "V49E", SAVEDATA_SRAM, HW_RUMBLE, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Final Fantasy Tactics Advance
|
// Final Fantasy Tactics Advance
|
||||||
{ "AFXE", SAVEDATA_FLASH512, GPIO_NONE, 0x8000428 },
|
{ "AFXE", SAVEDATA_FLASH512, HW_NONE, 0x8000428 },
|
||||||
|
|
||||||
// Koro Koro Puzzle - Happy Panechu!
|
// Koro Koro Puzzle - Happy Panechu!
|
||||||
{ "KHPJ", SAVEDATA_EEPROM, GPIO_TILT, IDLE_LOOP_NONE },
|
{ "KHPJ", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Mega Man Battle Network
|
// Mega Man Battle Network
|
||||||
{ "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x800032E },
|
{ "AREE", SAVEDATA_SRAM, HW_NONE, 0x800032E },
|
||||||
|
|
||||||
// Pokemon Ruby
|
// Pokemon Ruby
|
||||||
{ "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXVJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXVE", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXVP", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXVI", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXVS", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXVD", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXVF", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Pokemon Sapphire
|
// Pokemon Sapphire
|
||||||
{ "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXPJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXPE", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXPP", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXPI", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXPS", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXPD", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "AXPF", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Pokemon Emerald
|
// Pokemon Emerald
|
||||||
{ "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BPEJ", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BPEE", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BPEP", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BPEI", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "BPES", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BPES", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "BPED", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BPED", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
{ "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BPEF", SAVEDATA_FLASH1M, HW_RTC, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Pokemon Mystery Dungeon
|
// Pokemon Mystery Dungeon
|
||||||
{ "B24J", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "B24J", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "B24E", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "B24E", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "B24P", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "B24P", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "B24U", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "B24U", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Pokemon FireRed
|
// Pokemon FireRed
|
||||||
{ "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "BPRJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "BPRE", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "BPRP", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Pokemon LeafGreen
|
// Pokemon LeafGreen
|
||||||
{ "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "BPGJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "BPGE", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "BPGP", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// RockMan EXE 4.5 - Real Operation
|
// RockMan EXE 4.5 - Real Operation
|
||||||
{ "BR4J", SAVEDATA_FLASH512, GPIO_RTC, IDLE_LOOP_NONE },
|
{ "BR4J", SAVEDATA_FLASH512, HW_RTC, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Shin Bokura no Taiyou: Gyakushuu no Sabata
|
// Shin Bokura no Taiyou: Gyakushuu no Sabata
|
||||||
{ "U33J", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
{ "U33J", SAVEDATA_EEPROM, HW_RTC | HW_LIGHT_SENSOR, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Super Mario Advance 4
|
// Super Mario Advance 4
|
||||||
{ "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "AX4J", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "AX4E", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
{ "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "AX4P", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Top Gun - Combat Zones
|
// Top Gun - Combat Zones
|
||||||
{ "A2YE", SAVEDATA_FORCE_NONE, GPIO_NONE, IDLE_LOOP_NONE },
|
{ "A2YE", SAVEDATA_FORCE_NONE, HW_NONE, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Wario Ware Twisted
|
// Wario Ware Twisted
|
||||||
{ "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, IDLE_LOOP_NONE },
|
{ "RZWJ", SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, IDLE_LOOP_NONE },
|
||||||
{ "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, IDLE_LOOP_NONE },
|
{ "RZWE", SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, IDLE_LOOP_NONE },
|
||||||
{ "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, IDLE_LOOP_NONE },
|
{ "RZWP", SAVEDATA_SRAM, HW_RUMBLE | HW_GYRO, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
// Yoshi's Universal Gravitation
|
// Yoshi's Universal Gravitation
|
||||||
{ "KYGJ", SAVEDATA_EEPROM, GPIO_TILT, IDLE_LOOP_NONE },
|
{ "KYGJ", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE },
|
||||||
{ "KYGE", SAVEDATA_EEPROM, GPIO_TILT, IDLE_LOOP_NONE },
|
{ "KYGE", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE },
|
||||||
{ "KYGP", SAVEDATA_EEPROM, GPIO_TILT, IDLE_LOOP_NONE },
|
{ "KYGP", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE },
|
||||||
|
|
||||||
{ { 0, 0, 0, 0 }, 0, 0, IDLE_LOOP_NONE }
|
{ { 0, 0, 0, 0 }, 0, 0, IDLE_LOOP_NONE }
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOverride* override) {
|
bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOverride* override) {
|
||||||
override->savetype = SAVEDATA_AUTODETECT;
|
override->savetype = SAVEDATA_AUTODETECT;
|
||||||
override->hardware = GPIO_NONE;
|
override->hardware = HW_NONE;
|
||||||
override->idleLoop = IDLE_LOOP_NONE;
|
override->idleLoop = IDLE_LOOP_NONE;
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ void GBAOverrideSave(struct Configuration* config, const struct GBACartridgeOver
|
||||||
}
|
}
|
||||||
ConfigurationSetValue(config, sectionName, "savetype", savetype);
|
ConfigurationSetValue(config, sectionName, "savetype", savetype);
|
||||||
|
|
||||||
if (override->hardware != GPIO_NO_OVERRIDE) {
|
if (override->hardware != HW_NO_OVERRIDE) {
|
||||||
ConfigurationSetIntValue(config, sectionName, "hardware", override->hardware);
|
ConfigurationSetIntValue(config, sectionName, "hardware", override->hardware);
|
||||||
} else {
|
} else {
|
||||||
ConfigurationClearValue(config, sectionName, "hardware");
|
ConfigurationClearValue(config, sectionName, "hardware");
|
||||||
|
@ -215,27 +215,27 @@ void GBAOverrideApply(struct GBA* gba, const struct GBACartridgeOverride* overri
|
||||||
GBASavedataForceType(&gba->memory.savedata, override->savetype);
|
GBASavedataForceType(&gba->memory.savedata, override->savetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (override->hardware != GPIO_NO_OVERRIDE) {
|
if (override->hardware != HW_NO_OVERRIDE) {
|
||||||
GBAGPIOClear(&gba->memory.gpio);
|
GBAHardwareClear(&gba->memory.hw);
|
||||||
|
|
||||||
if (override->hardware & GPIO_RTC) {
|
if (override->hardware & HW_RTC) {
|
||||||
GBAGPIOInitRTC(&gba->memory.gpio);
|
GBAHardwareInitRTC(&gba->memory.hw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (override->hardware & GPIO_GYRO) {
|
if (override->hardware & HW_GYRO) {
|
||||||
GBAGPIOInitGyro(&gba->memory.gpio);
|
GBAHardwareInitGyro(&gba->memory.hw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (override->hardware & GPIO_RUMBLE) {
|
if (override->hardware & HW_RUMBLE) {
|
||||||
GBAGPIOInitRumble(&gba->memory.gpio);
|
GBAHardwareInitRumble(&gba->memory.hw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (override->hardware & GPIO_LIGHT_SENSOR) {
|
if (override->hardware & HW_LIGHT_SENSOR) {
|
||||||
GBAGPIOInitLightSensor(&gba->memory.gpio);
|
GBAHardwareInitLight(&gba->memory.hw);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (override->hardware & GPIO_TILT) {
|
if (override->hardware & HW_TILT) {
|
||||||
GBAGPIOInitTilt(&gba->memory.gpio);
|
GBAHardwareInitTilt(&gba->memory.hw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
/* Copyright (c) 2013-2014 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/. */
|
|
||||||
#ifndef GBA_SENSORS_H
|
|
||||||
#define GBA_SENSORS_H
|
|
||||||
|
|
||||||
#include "util/common.h"
|
|
||||||
|
|
||||||
struct GBARotationSource {
|
|
||||||
void (*sample)(struct GBARotationSource*);
|
|
||||||
|
|
||||||
int32_t (*readTiltX)(struct GBARotationSource*);
|
|
||||||
int32_t (*readTiltY)(struct GBARotationSource*);
|
|
||||||
|
|
||||||
int32_t (*readGyroZ)(struct GBARotationSource*);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GBALuminanceSource {
|
|
||||||
void (*sample)(struct GBALuminanceSource*);
|
|
||||||
|
|
||||||
uint8_t (*readLuminance)(struct GBALuminanceSource*);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GBARTCSource {
|
|
||||||
void (*sample)(struct GBARTCSource*);
|
|
||||||
|
|
||||||
time_t (*unixTime)(struct GBARTCSource*);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -129,7 +129,7 @@ extern const uint32_t GBA_SAVESTATE_MAGIC;
|
||||||
* 0x00290 - 0x002C3: GPIO state
|
* 0x00290 - 0x002C3: GPIO state
|
||||||
* | 0x00290 - 0x00291: Pin state
|
* | 0x00290 - 0x00291: Pin state
|
||||||
* | 0x00292 - 0x00293: Direction state
|
* | 0x00292 - 0x00293: Direction state
|
||||||
* | 0x00294 - 0x002B6: RTC state (see gba-gpio.h for format)
|
* | 0x00294 - 0x002B6: RTC state (see gba-hardware.h for format)
|
||||||
* | 0x002B7 - 0x002B7: GPIO devices
|
* | 0x002B7 - 0x002B7: GPIO devices
|
||||||
* | bit 0: Has RTC values
|
* | bit 0: Has RTC values
|
||||||
* | bit 1: Has rumble value (reserved)
|
* | bit 1: Has rumble value (reserved)
|
||||||
|
@ -269,9 +269,9 @@ struct GBASerializedState {
|
||||||
unsigned lightSample : 8;
|
unsigned lightSample : 8;
|
||||||
unsigned tiltState : 2;
|
unsigned tiltState : 2;
|
||||||
unsigned : 22;
|
unsigned : 22;
|
||||||
} gpio;
|
} hw;
|
||||||
|
|
||||||
uint32_t reservedGpio[12];
|
uint32_t reservedHardware[12];
|
||||||
|
|
||||||
uint32_t biosPrefetch;
|
uint32_t biosPrefetch;
|
||||||
uint32_t cpuPrefetch[2];
|
uint32_t cpuPrefetch[2];
|
||||||
|
|
|
@ -364,7 +364,7 @@ void GBALoadROM(struct GBA* gba, struct VFile* vf, struct VFile* sav, const char
|
||||||
gba->memory.romSize = gba->pristineRomSize;
|
gba->memory.romSize = gba->pristineRomSize;
|
||||||
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
|
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
|
||||||
GBASavedataInit(&gba->memory.savedata, sav);
|
GBASavedataInit(&gba->memory.savedata, sav);
|
||||||
GBAGPIOInit(&gba->memory.gpio, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
|
GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
|
||||||
// TODO: error check
|
// TODO: error check
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "gba-sensors.h"
|
#include "gba-hardware.h"
|
||||||
#include "gba-thread.h"
|
#include "gba-thread.h"
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
#include "sdl-events.h"
|
#include "sdl-events.h"
|
||||||
|
|
|
@ -58,26 +58,26 @@ void OverrideView::updateOverrides() {
|
||||||
m_override = (GBACartridgeOverride) {
|
m_override = (GBACartridgeOverride) {
|
||||||
"",
|
"",
|
||||||
static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1),
|
static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1),
|
||||||
GPIO_NO_OVERRIDE,
|
HW_NO_OVERRIDE,
|
||||||
IDLE_LOOP_NONE
|
IDLE_LOOP_NONE
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!m_ui.hwAutodetect->isChecked()) {
|
if (!m_ui.hwAutodetect->isChecked()) {
|
||||||
m_override.hardware = GPIO_NONE;
|
m_override.hardware = HW_NONE;
|
||||||
if (m_ui.hwRTC->isChecked()) {
|
if (m_ui.hwRTC->isChecked()) {
|
||||||
m_override.hardware |= GPIO_RTC;
|
m_override.hardware |= HW_RTC;
|
||||||
}
|
}
|
||||||
if (m_ui.hwGyro->isChecked()) {
|
if (m_ui.hwGyro->isChecked()) {
|
||||||
m_override.hardware |= GPIO_GYRO;
|
m_override.hardware |= HW_GYRO;
|
||||||
}
|
}
|
||||||
if (m_ui.hwLight->isChecked()) {
|
if (m_ui.hwLight->isChecked()) {
|
||||||
m_override.hardware |= GPIO_LIGHT_SENSOR;
|
m_override.hardware |= HW_LIGHT_SENSOR;
|
||||||
}
|
}
|
||||||
if (m_ui.hwTilt->isChecked()) {
|
if (m_ui.hwTilt->isChecked()) {
|
||||||
m_override.hardware |= GPIO_TILT;
|
m_override.hardware |= HW_TILT;
|
||||||
}
|
}
|
||||||
if (m_ui.hwRumble->isChecked()) {
|
if (m_ui.hwRumble->isChecked()) {
|
||||||
m_override.hardware |= GPIO_RUMBLE;
|
m_override.hardware |= HW_RUMBLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ void OverrideView::updateOverrides() {
|
||||||
m_override.idleLoop = parsedIdleLoop;
|
m_override.idleLoop = parsedIdleLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_override.savetype != SAVEDATA_AUTODETECT || m_override.hardware != GPIO_NO_OVERRIDE || m_override.idleLoop != IDLE_LOOP_NONE) {
|
if (m_override.savetype != SAVEDATA_AUTODETECT || m_override.hardware != HW_NO_OVERRIDE || m_override.idleLoop != IDLE_LOOP_NONE) {
|
||||||
m_controller->setOverride(m_override);
|
m_controller->setOverride(m_override);
|
||||||
} else {
|
} else {
|
||||||
m_controller->clearOverride();
|
m_controller->clearOverride();
|
||||||
|
@ -109,11 +109,11 @@ void OverrideView::gameStarted(GBAThread* thread) {
|
||||||
m_ui.hwTilt->setEnabled(false);
|
m_ui.hwTilt->setEnabled(false);
|
||||||
m_ui.hwRumble->setEnabled(false);
|
m_ui.hwRumble->setEnabled(false);
|
||||||
|
|
||||||
m_ui.hwRTC->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RTC);
|
m_ui.hwRTC->setChecked(thread->gba->memory.hw.devices & HW_RTC);
|
||||||
m_ui.hwGyro->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_GYRO);
|
m_ui.hwGyro->setChecked(thread->gba->memory.hw.devices & HW_GYRO);
|
||||||
m_ui.hwLight->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_LIGHT_SENSOR);
|
m_ui.hwLight->setChecked(thread->gba->memory.hw.devices & HW_LIGHT_SENSOR);
|
||||||
m_ui.hwTilt->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_TILT);
|
m_ui.hwTilt->setChecked(thread->gba->memory.hw.devices & HW_TILT);
|
||||||
m_ui.hwRumble->setChecked(thread->gba->memory.gpio.gpioDevices & GPIO_RUMBLE);
|
m_ui.hwRumble->setChecked(thread->gba->memory.hw.devices & HW_RUMBLE);
|
||||||
|
|
||||||
if (thread->gba->idleLoop != IDLE_LOOP_NONE) {
|
if (thread->gba->idleLoop != IDLE_LOOP_NONE) {
|
||||||
m_ui.idleLoop->setText(QString::number(thread->gba->idleLoop, 16));
|
m_ui.idleLoop->setText(QString::number(thread->gba->idleLoop, 16));
|
||||||
|
@ -123,7 +123,7 @@ void OverrideView::gameStarted(GBAThread* thread) {
|
||||||
}
|
}
|
||||||
|
|
||||||
GBAGetGameCode(thread->gba, m_override.id);
|
GBAGetGameCode(thread->gba, m_override.id);
|
||||||
m_override.hardware = thread->gba->memory.gpio.gpioDevices;
|
m_override.hardware = thread->gba->memory.hw.devices;
|
||||||
m_override.savetype = thread->gba->memory.savedata.type;
|
m_override.savetype = thread->gba->memory.savedata.type;
|
||||||
m_override.idleLoop = thread->gba->idleLoop;
|
m_override.idleLoop = thread->gba->idleLoop;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue