GBA: Gigantic refactor and add preliminary Game Boy Player support

This commit is contained in:
Jeffrey Pfau 2015-07-13 20:46:41 -07:00
parent 56208521d6
commit 43d9c8b754
7 changed files with 257 additions and 86 deletions

View File

@ -81,6 +81,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) {
gba->logHandler = 0;
gba->logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL;
gba->stream = 0;
gba->keyCallback = 0;
gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
@ -777,6 +778,8 @@ void GBAFrameEnded(struct GBA* gba) {
gba->stream->postVideoFrame(gba->stream, gba->video.renderer);
}
GBAHardwarePlayerUpdate(gba);
struct GBAThread* thread = GBAThreadGetContext();
if (!thread) {
return;

View File

@ -11,6 +11,7 @@
#include "arm.h"
#include "debugger/debugger.h"
#include "gba/interface.h"
#include "gba/memory.h"
#include "gba/video.h"
#include "gba/audio.h"
@ -35,37 +36,6 @@ enum GBAIRQ {
IRQ_GAMEPAK = 0xD
};
enum GBALogLevel {
GBA_LOG_FATAL = 0x01,
GBA_LOG_ERROR = 0x02,
GBA_LOG_WARN = 0x04,
GBA_LOG_INFO = 0x08,
GBA_LOG_DEBUG = 0x10,
GBA_LOG_STUB = 0x20,
GBA_LOG_GAME_ERROR = 0x100,
GBA_LOG_SWI = 0x200,
GBA_LOG_STATUS = 0x400,
GBA_LOG_SIO = 0x800,
GBA_LOG_ALL = 0xF3F,
};
enum GBAKey {
GBA_KEY_A = 0,
GBA_KEY_B = 1,
GBA_KEY_SELECT = 2,
GBA_KEY_START = 3,
GBA_KEY_RIGHT = 4,
GBA_KEY_LEFT = 5,
GBA_KEY_UP = 6,
GBA_KEY_DOWN = 7,
GBA_KEY_R = 8,
GBA_KEY_L = 9,
GBA_KEY_MAX,
GBA_KEY_NONE = -1
};
enum GBAComponent {
GBA_COMPONENT_DEBUGGER,
GBA_COMPONENT_CHEAT_DEVICE,
@ -85,19 +55,10 @@ enum {
};
struct GBA;
struct GBARotationSource;
struct GBAThread;
struct Patch;
struct VFile;
typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args);
struct GBAAVStream {
void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
void (*postAudioFrame)(struct GBAAVStream*, int16_t left, int16_t right);
void (*postAudioBuffer)(struct GBAAVStream*, struct GBAAudio*);
};
struct GBATimer {
uint16_t reload;
uint16_t oldReload;
@ -150,6 +111,7 @@ struct GBA {
GBALogHandler logHandler;
enum GBALogLevel logLevel;
struct GBAAVStream* stream;
struct GBAKeyCallback* keyCallback;
enum GBAIdleLoopOptimization idleOptimization;
uint32_t idleLoop;

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "hardware.h"
#include "gba/io.h"
#include "gba/serialize.h"
#include "util/hash.h"
@ -25,6 +26,11 @@ static void _rumbleReadPins(struct GBACartridgeHardware* hw);
static void _lightReadPins(struct GBACartridgeHardware* hw);
static uint16_t _gbpRead(struct GBAKeyCallback*);
static bool _gbpSioLoad(struct GBASIODriver* driver);
static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
static int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles);
static const int RTC_BYTES[8] = {
0, // Force reset
0, // Empty
@ -46,6 +52,7 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) {
hw->direction = GPIO_WRITE_ONLY;
hw->pinState = 0;
hw->direction = 0;
hw->gbpRunning = false;
}
void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
@ -461,6 +468,30 @@ static const uint16_t _logoPalette[] = {
static const uint32_t _logoHash = 0xEEDA6963;
static const uint32_t _gbpTxData[] = {
0x0000494E, 0x0000494E,
0xB6B1494E, 0xB6B1544E,
0xABB1544E, 0xABB14E45,
0xB1BA4E45, 0xB1BA4F44,
0xB0BB4F44, 0xB0BB8002,
0x10000010, 0x20000013,
0x30000003, 0x30000003,
0x30000003, 0x30000003,
0x30000003, 0x00000000,
};
static const uint32_t _gbpRxData[] = {
0x00000000, 0x494EB6B1,
0x494EB6B1, 0x544EB6B1,
0x544EABB1, 0x4E45ABB1,
0x4E45B1BA, 0x4F44B1BA,
0x4F44B0BB, 0x8000B0BB,
0x10000010, 0x20000013,
0x40000004, 0x40000004,
0x40000004, 0x40000004,
0x40000004, 0x40000004
};
bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) {
if (memcmp(video->palette, _logoPalette, sizeof(_logoPalette)) != 0) {
return false;
@ -469,6 +500,98 @@ bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video) {
return hash == _logoHash;
}
void GBAHardwarePlayerUpdate(struct GBA* gba) {
if (gba->memory.hw.gbpRunning) {
if (GBAHardwarePlayerCheckScreen(&gba->video)) {
++gba->memory.hw.gbpInputsPosted;
gba->memory.hw.gbpInputsPosted %= 3;
gba->keyCallback = &gba->memory.hw.gbpCallback.d;
} else {
// TODO: Save and restore
gba->keyCallback = 0;
}
gba->memory.hw.gbpTxPosition = 0;
return;
}
if (gba->keyCallback || gba->sio.drivers.normal) {
return;
}
if (GBAHardwarePlayerCheckScreen(&gba->video)) {
gba->memory.hw.gbpRunning = true;
gba->memory.hw.gbpCallback.d.readKeys = _gbpRead;
gba->memory.hw.gbpCallback.p = &gba->memory.hw;
gba->memory.hw.gbpDriver.d.init = 0;
gba->memory.hw.gbpDriver.d.deinit = 0;
gba->memory.hw.gbpDriver.d.load = _gbpSioLoad;
gba->memory.hw.gbpDriver.d.unload = 0;
gba->memory.hw.gbpDriver.d.writeRegister = _gbpSioWriteRegister;
gba->memory.hw.gbpDriver.d.processEvents = _gbpSioProcessEvents;
gba->memory.hw.gbpDriver.p = &gba->memory.hw;
gba->memory.hw.gbpInputsPosted = 0;
gba->keyCallback = &gba->memory.hw.gbpCallback.d;
GBASIOSetDriver(&gba->sio, &gba->memory.hw.gbpDriver.d, SIO_NORMAL_32);
}
}
uint16_t _gbpRead(struct GBAKeyCallback* callback) {
struct GBAGBPKeyCallback* gbpCallback = (struct GBAGBPKeyCallback*) callback;
if (gbpCallback->p->gbpInputsPosted == 2) {
return 0x30F;
}
return 0x3FF;
}
bool _gbpSioLoad(struct GBASIODriver* driver) {
struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver;
gbp->p->gbpTxPosition = 0;
gbp->p->gbpNextEvent = INT_MAX;
return true;
}
uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver;
if (address == REG_SIOCNT) {
if (value & 0x0080) {
if (gbp->p->gbpTxPosition <= 16 && gbp->p->gbpTxPosition > 0) {
uint32_t rx = gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] | (gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] << 16);
uint32_t expected = _gbpRxData[gbp->p->gbpTxPosition];
// TODO: Check expected
uint32_t mask = 0;
if (gbp->p->gbpTxPosition == 15) {
mask = 0x22;
if (gbp->p->p->rumble) {
gbp->p->p->rumble->setRumble(gbp->p->p->rumble, (rx & mask) == mask);
}
}
}
gbp->p->gbpNextEvent = 2048;
}
value &= 0x78FB;
}
return value;
}
int32_t _gbpSioProcessEvents(struct GBASIODriver* driver, int32_t cycles) {
struct GBAGBPSIODriver* gbp = (struct GBAGBPSIODriver*) driver;
gbp->p->gbpNextEvent -= cycles;
if (gbp->p->gbpNextEvent <= 0) {
uint32_t tx = 0;
if (gbp->p->gbpTxPosition <= 16) {
tx = _gbpTxData[gbp->p->gbpTxPosition];
++gbp->p->gbpTxPosition;
}
gbp->p->p->memory.io[REG_SIODATA32_LO >> 1] = tx;
gbp->p->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16;
if (gbp->d.p->normalControl.irq) {
GBARaiseIRQ(gbp->p->p, IRQ_SIO);
}
gbp->d.p->normalControl.start = 0;
gbp->p->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt;
gbp->p->gbpNextEvent = INT_MAX;
}
return gbp->p->gbpNextEvent;
}
// == Serialization
void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASerializedState* state) {

View File

@ -7,6 +7,7 @@
#define GBA_HARDWARE_H
#include "util/common.h"
#include "gba/interface.h"
#include "macros.h"
@ -14,27 +15,6 @@
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)
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*);
};
struct GBARTCGenericSource {
struct GBARTCSource d;
struct GBA* p;
@ -102,6 +82,16 @@ struct GBARumble {
void (*setRumble)(struct GBARumble*, int enable);
};
struct GBAGBPKeyCallback {
struct GBAKeyCallback d;
struct GBACartridgeHardware* p;
};
struct GBAGBPSIODriver {
struct GBASIODriver d;
struct GBACartridgeHardware* p;
};
DECL_BITFIELD(GPIOPin, uint16_t);
struct GBACartridgeHardware {
@ -125,6 +115,13 @@ struct GBACartridgeHardware {
uint16_t tiltX;
uint16_t tiltY;
int tiltState;
bool gbpRunning;
int gbpInputsPosted;
int gbpTxPosition;
struct GBAGBPKeyCallback gbpCallback;
struct GBAGBPSIODriver gbpDriver;
int32_t gbpNextEvent;
};
void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase);
@ -141,6 +138,7 @@ void GBAHardwareTiltWrite(struct GBACartridgeHardware* gpio, uint32_t address, u
uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* gpio, uint32_t address);
struct GBAVideo;
void GBAHardwarePlayerUpdate(struct GBA* gba);
bool GBAHardwarePlayerCheckScreen(const struct GBAVideo* video);
void GBARTCGenericSourceInit(struct GBARTCGenericSource* rtc, struct GBA* gba);

101
src/gba/interface.h Normal file
View File

@ -0,0 +1,101 @@
/* 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/. */
#ifndef INTERFACE_H
#define INTERFACE_H
#include "util/common.h"
enum GBALogLevel {
GBA_LOG_FATAL = 0x01,
GBA_LOG_ERROR = 0x02,
GBA_LOG_WARN = 0x04,
GBA_LOG_INFO = 0x08,
GBA_LOG_DEBUG = 0x10,
GBA_LOG_STUB = 0x20,
GBA_LOG_GAME_ERROR = 0x100,
GBA_LOG_SWI = 0x200,
GBA_LOG_STATUS = 0x400,
GBA_LOG_SIO = 0x800,
GBA_LOG_ALL = 0xF3F,
};
enum GBAKey {
GBA_KEY_A = 0,
GBA_KEY_B = 1,
GBA_KEY_SELECT = 2,
GBA_KEY_START = 3,
GBA_KEY_RIGHT = 4,
GBA_KEY_LEFT = 5,
GBA_KEY_UP = 6,
GBA_KEY_DOWN = 7,
GBA_KEY_R = 8,
GBA_KEY_L = 9,
GBA_KEY_MAX,
GBA_KEY_NONE = -1
};
enum GBASIOMode {
SIO_NORMAL_8 = 0,
SIO_NORMAL_32 = 1,
SIO_MULTI = 2,
SIO_UART = 3,
SIO_GPIO = 8,
SIO_JOYBUS = 12
};
struct GBA;
struct GBAAudio;
struct GBASIO;
struct GBAThread;
struct GBAVideoRenderer;
typedef void (*GBALogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args);
struct GBAAVStream {
void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
void (*postAudioFrame)(struct GBAAVStream*, int16_t left, int16_t right);
void (*postAudioBuffer)(struct GBAAVStream*, struct GBAAudio*);
};
struct GBAKeyCallback {
uint16_t (*readKeys)(struct GBAKeyCallback*);
};
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*);
};
struct GBASIODriver {
struct GBASIO* p;
bool (*init)(struct GBASIODriver* driver);
void (*deinit)(struct GBASIODriver* driver);
bool (*load)(struct GBASIODriver* driver);
bool (*unload)(struct GBASIODriver* driver);
uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value);
int32_t (*processEvents)(struct GBASIODriver* driver, int32_t cycles);
};
#endif

View File

@ -584,14 +584,18 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
case REG_KEYINPUT:
if (gba->rr && gba->rr->isPlaying(gba->rr)) {
return 0x3FF ^ gba->rr->queryInput(gba->rr);
} else if (gba->keySource) {
uint16_t input = *gba->keySource;
} else {
uint16_t input = 0x3FF;
if (gba->keyCallback) {
input = gba->keyCallback->readKeys(gba->keyCallback);
} else if (gba->keySource) {
input = *gba->keySource;
}
if (gba->rr && gba->rr->isRecording(gba->rr)) {
gba->rr->logInput(gba->rr, input);
}
return 0x3FF ^ input;
}
break;
case REG_SIOCNT:
return gba->sio.siocnt;

View File

@ -8,36 +8,16 @@
#include "util/common.h"
#include "gba/interface.h"
#define MAX_GBAS 4
extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS];
enum GBASIOMode {
SIO_NORMAL_8 = 0,
SIO_NORMAL_32 = 1,
SIO_MULTI = 2,
SIO_UART = 3,
SIO_GPIO = 8,
SIO_JOYBUS = 12
};
enum {
RCNT_INITIAL = 0x8000
};
struct GBASIO;
struct GBASIODriver {
struct GBASIO* p;
bool (*init)(struct GBASIODriver* driver);
void (*deinit)(struct GBASIODriver* driver);
bool (*load)(struct GBASIODriver* driver);
bool (*unload)(struct GBASIODriver* driver);
uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value);
int32_t (*processEvents)(struct GBASIODriver* driver, int32_t cycles);
};
struct GBASIODriverSet {
struct GBASIODriver* normal;
struct GBASIODriver* multiplayer;