all repos — mgba @ 31b9100f381fb7ee75ba8ee006a86ab65e6e0b35

mGBA Game Boy Advance Emulator

GB MBC: Pocket Cam support
Vicki Pfau vi@endrift.com
Wed, 26 Jul 2017 10:57:57 -0700
commit

31b9100f381fb7ee75ba8ee006a86ab65e6e0b35

parent

55330698cb08493324a8be5e812e86f94e2ac1fe

M CHANGESCHANGES

@@ -1,6 +1,7 @@

0.7.0: (Future) Features: - ELF support + - Game Boy Camera support Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - Python: Fix importing .gb or .gba before .core
M include/mgba/core/interface.hinclude/mgba/core/interface.h

@@ -61,6 +61,7 @@

enum mPeripheral { mPERIPH_ROTATION = 1, mPERIPH_RUMBLE, + mPERIPH_IMAGE_SOURCE, mPERIPH_CUSTOM = 0x1000 };

@@ -80,6 +81,12 @@ time_t (*unixTime)(struct mRTCSource*);

void (*serialize)(struct mRTCSource*, struct mStateExtdataItem*); bool (*deserialize)(struct mRTCSource*, const struct mStateExtdataItem*); +}; + +struct mImageSource { + void (*startRequestImage)(struct mImageSource*); + void (*stopRequestImage)(struct mImageSource*); + void (*requestImage)(struct mImageSource*, unsigned w, unsigned h, const uint32_t** buffer, size_t* stride); }; enum mRTCGenericType {
M include/mgba/internal/gb/mbc.hinclude/mgba/internal/gb/mbc.h

@@ -21,6 +21,11 @@ void GBMBCSwitchBank(struct GB* gb, int bank);

void GBMBCSwitchBank0(struct GB* gb, int bank); void GBMBCSwitchSramBank(struct GB* gb, int bank); +enum GBCam { + GBCAM_WIDTH = 128, + GBCAM_HEIGHT = 112 +}; + struct GBMBCRTCSaveBuffer { uint32_t sec; uint32_t min;
M include/mgba/internal/gb/memory.hinclude/mgba/internal/gb/memory.h

@@ -117,6 +117,7 @@ };

struct GBPocketCamState { bool registersActive; + uint8_t registers[0x36]; }; struct GBTAMA5State {

@@ -179,6 +180,7 @@ time_t rtcLastLatch;

struct mRTCSource* rtc; struct mRotationSource* rotation; struct mRumble* rumble; + struct mImageSource* cam; }; struct LR35902Core;
M src/gb/core.csrc/gb/core.c

@@ -479,6 +479,9 @@ break;

case mPERIPH_RUMBLE: gb->memory.rumble = periph; break; + case mPERIPH_IMAGE_SOURCE: + gb->memory.cam = periph; + break; default: return; }
M src/gb/gb.csrc/gb/gb.c

@@ -294,6 +294,9 @@ gb->sramRealVf->close(gb->sramRealVf);

} gb->sramRealVf = NULL; gb->sramVf = NULL; + if (gb->memory.cam && gb->memory.cam->stopRequestImage) { + gb->memory.cam->stopRequestImage(gb->memory.cam); + } } void GBSynthesizeROM(struct VFile* vf) {
M src/gb/mbc.csrc/gb/mbc.c

@@ -37,6 +37,7 @@

static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address); static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); +static void _GBPocketCamCapture(struct GBMemory*); void GBMBCSwitchBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_CART_BANK0;

@@ -242,6 +243,9 @@ break;

case GB_POCKETCAM: gb->memory.mbcWrite = _GBPocketCam; gb->memory.mbcRead = _GBPocketCamRead; + if (gb->memory.cam && gb->memory.cam->startRequestImage) { + gb->memory.cam->startRequestImage(gb->memory.cam); + } break; }

@@ -757,6 +761,16 @@ } else {

memory->mbcState.pocketCam.registersActive = true; } break; + case 0x5: + address &= 0x7F; + if (address == 0 && value & 1) { + value &= 6; // TODO: Timing + _GBPocketCamCapture(memory); + } + if (address < sizeof(memory->mbcState.pocketCam.registers)) { + memory->mbcState.pocketCam.registers[address] = value; + } + break; default: mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value); break;

@@ -765,9 +779,51 @@ }

uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) { if (memory->mbcState.pocketCam.registersActive) { + if ((address & 0x7F) == 0) { + return memory->mbcState.pocketCam.registers[0]; + } return 0; } return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; +} + +void _GBPocketCamCapture(struct GBMemory* memory) { + if (!memory->cam) { + return; + } + const uint32_t* image = NULL; + size_t stride; + memory->cam->requestImage(memory->cam, GBCAM_WIDTH, GBCAM_HEIGHT, &image, &stride); + if (!image) { + return; + } + memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4); + struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam; + size_t x, y; + for (y = 0; y < GBCAM_HEIGHT; ++y) { + for (x = 0; x < GBCAM_WIDTH; ++x) { + uint32_t color = image[y * stride + x]; + uint32_t gray = ((color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF)); + uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]); + gray = (gray + 1) * exposure / 0x300; + // TODO: Additional processing + int matrixEntry = 3 * ((x & 3) + 4 * (y & 3)); + if (gray < pocketCam->registers[matrixEntry + 6]) { + gray = 0x101; + } else if (gray < pocketCam->registers[matrixEntry + 7]) { + gray = 0x100; + } else if (gray < pocketCam->registers[matrixEntry + 8]) { + gray = 0x001; + } else { + gray = 0; + } + int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20; + uint16_t existing; + LOAD_16LE(existing, coord + 0x100, memory->sram); + existing |= gray << (7 - (x & 7)); + STORE_16LE(existing, coord + 0x100, memory->sram); + } + } } void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
M src/gb/memory.csrc/gb/memory.c

@@ -105,6 +105,7 @@

gb->memory.rtc = NULL; gb->memory.rotation = NULL; gb->memory.rumble = NULL; + gb->memory.cam = NULL; GBIOInit(gb); }
M src/platform/qt/CoreController.cppsrc/platform/qt/CoreController.cpp

@@ -47,8 +47,6 @@ m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width());

m_threadContext.startCallback = [](mCoreThread* context) { CoreController* controller = static_cast<CoreController*>(context->userData); - context->core->setPeripheral(context->core, mPERIPH_ROTATION, controller->m_inputController->rotationSource()); - context->core->setPeripheral(context->core, mPERIPH_RUMBLE, controller->m_inputController->rumble()); switch (context->core->platform(context->core)) { #ifdef M_CORE_GBA

@@ -285,6 +283,9 @@ }

void CoreController::setInputController(InputController* inputController) { m_inputController = inputController; + m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_ROTATION, m_inputController->rotationSource()); + m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_RUMBLE, m_inputController->rumble()); + m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_IMAGE_SOURCE, m_inputController->imageSource()); } void CoreController::setLogger(LogController* logger) {
M src/platform/qt/InputController.cppsrc/platform/qt/InputController.cpp

@@ -78,6 +78,27 @@ return lux->value;

}; setLuminanceLevel(0); #endif + + m_image.startRequestImage = [](mImageSource* context) { + InputControllerImage* image = static_cast<InputControllerImage*>(context); + image->image.load(":/res/no-cam.png"); + }; + m_image.stopRequestImage = nullptr; + m_image.requestImage = [](mImageSource* context, unsigned w, unsigned h, const uint32_t** buffer, size_t* stride) { + InputControllerImage* image = static_cast<InputControllerImage*>(context); + image->resizedImage = image->image.scaled(w, h, Qt::KeepAspectRatioByExpanding); + image->resizedImage = image->resizedImage.convertToFormat(QImage::Format_RGB32); + const uint32_t* bits = reinterpret_cast<const uint32_t*>(image->resizedImage.constBits()); + QSize size = image->resizedImage.size(); + if (size.width() > w) { + bits += size.width() / 2; + } + if (size.height() > h) { + bits += (size.height() / 2) * size.width(); + } + *buffer = bits; + *stride = size.width(); + }; } InputController::~InputController() {
M src/platform/qt/InputController.hsrc/platform/qt/InputController.h

@@ -9,6 +9,7 @@

#include "GamepadAxisEvent.h" #include "GamepadHatEvent.h" +#include <QImage> #include <QObject> #include <QSet> #include <QTimer>

@@ -82,6 +83,7 @@ void releaseFocus(QWidget* focus);

mRumble* rumble(); mRotationSource* rotationSource(); + mImageSource* imageSource() { return &m_image; } GBALuminanceSource* luminance() { return &m_lux; } signals:

@@ -114,6 +116,12 @@ uint8_t value;

} m_lux; uint8_t m_luxValue; int m_luxLevel; + + struct InputControllerImage : mImageSource { + InputController* p; + QImage image; + QImage resizedImage; + } m_image; mInputMap m_inputMap; ConfigController* m_config = nullptr;
M src/platform/qt/resources.qrcsrc/platform/qt/resources.qrc

@@ -3,5 +3,6 @@ <qresource>

<file>../../../res/mgba-1024.png</file> <file>../../../res/keymap.qpic</file> <file>../../../res/patrons.txt</file> + <file>../../../res/no-cam.png</file> </qresource> </RCC>