mirror of https://github.com/mgba-emu/mgba.git
GBA: Gigantic refactor and add preliminary Game Boy Player support
This commit is contained in:
parent
56208521d6
commit
43d9c8b754
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
10
src/gba/io.c
10
src/gba/io.c
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue