mirror of https://github.com/mgba-emu/mgba.git
GB: Add basic I/O, interrupts and video
This commit is contained in:
parent
64676529ba
commit
8622ba7ed0
68
src/gb/gb.c
68
src/gb/gb.c
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gb.h"
|
||||
|
||||
#include "gb/io.h"
|
||||
|
||||
#include "util/crc32.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/math.h"
|
||||
|
@ -30,17 +32,18 @@ void GBCreate(struct GB* gb) {
|
|||
}
|
||||
|
||||
static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component) {
|
||||
struct GB* gba = (struct GB*) component;
|
||||
gba->cpu = cpu;
|
||||
struct GB* gb = (struct GB*) component;
|
||||
gb->cpu = cpu;
|
||||
|
||||
GBInterruptHandlerInit(&cpu->irqh);
|
||||
GBMemoryInit(gba);
|
||||
GBMemoryInit(gb);
|
||||
GBVideoInit(&gb->video);
|
||||
|
||||
gba->romVf = 0;
|
||||
gb->romVf = 0;
|
||||
|
||||
gba->pristineRom = 0;
|
||||
gba->pristineRomSize = 0;
|
||||
gba->yankedRomSize = 0;
|
||||
gb->pristineRom = 0;
|
||||
gb->pristineRomSize = 0;
|
||||
gb->yankedRomSize = 0;
|
||||
}
|
||||
|
||||
bool GBLoadROM(struct GB* gb, struct VFile* vf, struct VFile* sav, const char* fname) {
|
||||
|
@ -120,17 +123,62 @@ void GBReset(struct LR35902Core* cpu) {
|
|||
gb->yankedRomSize = 0;
|
||||
}
|
||||
GBMemoryReset(gb);
|
||||
|
||||
gb->video.p = gb;
|
||||
GBVideoReset(&gb->video);
|
||||
}
|
||||
|
||||
void GBUpdateIRQs(struct GB* gb) {
|
||||
if (!gb->memory.ime) {
|
||||
return;
|
||||
}
|
||||
int irqs = gb->memory.ie & gb->memory.io[REG_IF];
|
||||
if (!irqs) {
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_VBLANK)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_VBLANK);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_LCDSTAT)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_LCDSTAT);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_TIMER)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_TIMER);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_SIO)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_SIO);
|
||||
return;
|
||||
}
|
||||
if (irqs & (1 << GB_IRQ_KEYPAD)) {
|
||||
LR35902RaiseIRQ(gb->cpu, GB_VECTOR_KEYPAD);
|
||||
}
|
||||
}
|
||||
|
||||
void GBProcessEvents(struct LR35902Core* cpu) {
|
||||
// TODO
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
int32_t cycles = cpu->nextEvent;
|
||||
int32_t nextEvent = INT_MAX;
|
||||
int32_t testEvent;
|
||||
|
||||
testEvent = GBVideoProcessEvents(&gb->video, cycles);
|
||||
if (testEvent < nextEvent) {
|
||||
nextEvent = testEvent;
|
||||
}
|
||||
|
||||
cpu->cycles -= cycles;
|
||||
cpu->nextEvent = nextEvent;
|
||||
}
|
||||
|
||||
void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
|
||||
// TODO
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
gb->memory.ime = enable;
|
||||
GBUpdateIRQs(gb);
|
||||
}
|
||||
|
||||
void GBHitStub(struct LR35902Core* cpu) {
|
||||
// TODO
|
||||
printf("Hit stub at address %04X\n", cpu->pc);
|
||||
//printf("Hit stub at address %04X\n", cpu->pc);
|
||||
}
|
||||
|
|
14
src/gb/gb.h
14
src/gb/gb.h
|
@ -11,6 +11,7 @@
|
|||
#include "lr35902/lr35902.h"
|
||||
|
||||
#include "gb/memory.h"
|
||||
#include "gb/video.h"
|
||||
|
||||
extern const uint32_t DMG_LR35902_FREQUENCY;
|
||||
extern const uint32_t CGB_LR35902_FREQUENCY;
|
||||
|
@ -25,11 +26,20 @@ enum GBIRQ {
|
|||
GB_IRQ_KEYPAD = 0x4,
|
||||
};
|
||||
|
||||
enum GBIRQVector {
|
||||
GB_VECTOR_VBLANK = 0x40,
|
||||
GB_VECTOR_LCDSTAT = 0x48,
|
||||
GB_VECTOR_TIMER = 0x50,
|
||||
GB_VECTOR_SIO = 0x58,
|
||||
GB_VECTOR_KEYPAD = 0x60,
|
||||
};
|
||||
|
||||
struct GB {
|
||||
struct LR35902Component d;
|
||||
|
||||
struct LR35902Core* cpu;
|
||||
struct GBMemory memory;
|
||||
struct GBVideo video;
|
||||
|
||||
int* keySource;
|
||||
|
||||
|
@ -47,9 +57,7 @@ void GBDestroy(struct GB* gb);
|
|||
|
||||
void GBReset(struct LR35902Core* cpu);
|
||||
|
||||
void GBWriteIE(struct GB* gb, uint8_t value);
|
||||
void GBRaiseIRQ(struct GB* gb, enum GBIRQ irq);
|
||||
void GBTestIRQ(struct LR35902Core* cpu);
|
||||
void GBUpdateIRQs(struct GB* gb);
|
||||
void GBHalt(struct GB* gb);
|
||||
void GBStop(struct GB* gb);
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "io.h"
|
||||
|
||||
#include "gb/gb.h"
|
||||
|
||||
void GBIOInit(struct GB* gb) {
|
||||
memset(gb->memory.io, 0, sizeof(gb->memory.io));
|
||||
}
|
||||
|
||||
void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
||||
switch (address) {
|
||||
case REG_IF:
|
||||
gb->memory.io[REG_IF] = value;
|
||||
GBUpdateIRQs(gb);
|
||||
return;
|
||||
case REG_LCDC:
|
||||
// TODO: handle GBC differences
|
||||
GBVideoWriteLCDC(&gb->video, value);
|
||||
break;
|
||||
case REG_IE:
|
||||
gb->memory.ie = value;
|
||||
GBUpdateIRQs(gb);
|
||||
break;
|
||||
default:
|
||||
// TODO: Log
|
||||
if (address >= GB_SIZE_IO) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
gb->memory.io[address] = value;
|
||||
}
|
||||
|
||||
uint8_t GBIORead(struct GB* gb, unsigned address) {
|
||||
switch (address) {
|
||||
case REG_IF:
|
||||
break;
|
||||
case REG_IE:
|
||||
return gb->memory.ie;
|
||||
default:
|
||||
// TODO: Log
|
||||
if (address >= GB_SIZE_IO) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return gb->memory.io[address];
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef GB_IO_H
|
||||
#define GB_IO_H
|
||||
|
||||
#include "util/common.h"
|
||||
|
||||
enum GBIORegisters {
|
||||
REG_JOYP = 0x00,
|
||||
REG_SB = 0x01,
|
||||
REG_SC = 0x02,
|
||||
|
||||
// Timing
|
||||
REG_DIV = 0x04,
|
||||
REG_TIMA = 0x05,
|
||||
REG_TMA = 0x06,
|
||||
REG_TAC = 0x07,
|
||||
|
||||
// Interrupts
|
||||
REG_IF = 0x0F,
|
||||
REG_IE = 0xFF,
|
||||
|
||||
// Audio
|
||||
REG_NR10 = 0x10,
|
||||
REG_NR11 = 0x11,
|
||||
REG_NR12 = 0x12,
|
||||
REG_NR13 = 0x13,
|
||||
REG_NR14 = 0x14,
|
||||
REG_NR21 = 0x16,
|
||||
REG_NR22 = 0x17,
|
||||
REG_NR23 = 0x18,
|
||||
REG_NR24 = 0x19,
|
||||
REG_NR30 = 0x1A,
|
||||
REG_NR31 = 0x1B,
|
||||
REG_NR32 = 0x1C,
|
||||
REG_NR33 = 0x1D,
|
||||
REG_NR34 = 0x1E,
|
||||
REG_NR41 = 0x20,
|
||||
REG_NR42 = 0x21,
|
||||
REG_NR43 = 0x22,
|
||||
REG_NR44 = 0x23,
|
||||
REG_NR50 = 0x24,
|
||||
REG_NR51 = 0x25,
|
||||
REG_NR52 = 0x26,
|
||||
|
||||
REG_WAVE_0 = 0x30,
|
||||
REG_WAVE_1 = 0x31,
|
||||
REG_WAVE_2 = 0x32,
|
||||
REG_WAVE_3 = 0x33,
|
||||
REG_WAVE_4 = 0x34,
|
||||
REG_WAVE_5 = 0x35,
|
||||
REG_WAVE_6 = 0x36,
|
||||
REG_WAVE_7 = 0x37,
|
||||
REG_WAVE_8 = 0x38,
|
||||
REG_WAVE_9 = 0x39,
|
||||
REG_WAVE_A = 0x3A,
|
||||
REG_WAVE_B = 0x3B,
|
||||
REG_WAVE_C = 0x3C,
|
||||
REG_WAVE_D = 0x3D,
|
||||
REG_WAVE_E = 0x3E,
|
||||
REG_WAVE_F = 0x3F,
|
||||
|
||||
// Video
|
||||
REG_LCDC = 0x40,
|
||||
REG_STAT = 0x41,
|
||||
REG_SCY = 0x42,
|
||||
REG_SCX = 0x43,
|
||||
REG_LY = 0x44,
|
||||
REG_LYC = 0x45,
|
||||
REG_DMA = 0x46,
|
||||
REG_BGP = 0x47,
|
||||
REG_OBP0 = 0x48,
|
||||
REG_OBP1 = 0x49,
|
||||
REG_WY = 0x4A,
|
||||
REG_WX = 0x4B,
|
||||
};
|
||||
|
||||
struct GB;
|
||||
void GBIOInit(struct GB* gb);
|
||||
void GBIOWrite(struct GB* gb, unsigned address, uint8_t value);
|
||||
uint8_t GBIORead(struct GB* gb, unsigned address);
|
||||
|
||||
#endif
|
|
@ -6,6 +6,7 @@
|
|||
#include "memory.h"
|
||||
|
||||
#include "gb/gb.h"
|
||||
#include "gb/io.h"
|
||||
|
||||
#include "util/memory.h"
|
||||
|
||||
|
@ -26,6 +27,10 @@ void GBMemoryInit(struct GB* gb) {
|
|||
gb->memory.rom = 0;
|
||||
gb->memory.romBank = 0;
|
||||
gb->memory.romSize = 0;
|
||||
|
||||
memset(gb->memory.hram, 0, sizeof(gb->memory.hram));
|
||||
|
||||
GBIOInit(gb);
|
||||
}
|
||||
|
||||
void GBMemoryDeinit(struct GB* gb) {
|
||||
|
@ -80,8 +85,20 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
|
|||
case GB_REGION_WORKING_RAM_BANK1:
|
||||
return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
|
||||
default:
|
||||
// TODO
|
||||
return 0;
|
||||
if (address < GB_BASE_OAM) {
|
||||
return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
|
||||
}
|
||||
if (address < GB_BASE_IO) {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
if (address < GB_BASE_HRAM) {
|
||||
return GBIORead(gb, address & (GB_SIZE_IO - 1));
|
||||
}
|
||||
if (address < GB_BASE_IE) {
|
||||
return memory->hram[address & GB_SIZE_HRAM];
|
||||
}
|
||||
return GBIORead(gb, REG_IE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,8 +138,17 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
|
|||
memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
|
||||
return;
|
||||
default:
|
||||
// TODO
|
||||
return;
|
||||
if (address < GB_BASE_OAM) {
|
||||
memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
|
||||
} else if (address < GB_BASE_IO) {
|
||||
// TODO
|
||||
} else if (address < GB_BASE_HRAM) {
|
||||
GBIOWrite(gb, address & (GB_SIZE_IO - 1), value);
|
||||
} else if (address < GB_BASE_IE) {
|
||||
memory->hram[address & GB_SIZE_HRAM] = value;
|
||||
} else {
|
||||
GBIOWrite(gb, REG_IE, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,12 @@ struct GBMemory {
|
|||
uint8_t* wram;
|
||||
uint8_t* wramBank;
|
||||
|
||||
uint8_t io[GB_SIZE_IO];
|
||||
bool ime;
|
||||
uint8_t ie;
|
||||
|
||||
uint8_t hram[GB_SIZE_HRAM];
|
||||
|
||||
size_t romSize;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "video.h"
|
||||
|
||||
#include "gb/gb.h"
|
||||
#include "gb/io.h"
|
||||
|
||||
#include "util/memory.h"
|
||||
|
||||
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer);
|
||||
static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer);
|
||||
static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
|
||||
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
static void GBVideoDummyRendererDrawScanline(struct GBVideoRenderer* renderer, int y);
|
||||
static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);
|
||||
static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
|
||||
|
||||
static struct GBVideoRenderer dummyRenderer = {
|
||||
.init = GBVideoDummyRendererInit,
|
||||
.reset = GBVideoDummyRendererReset,
|
||||
.deinit = GBVideoDummyRendererDeinit,
|
||||
.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
|
||||
.writeVRAM = GBVideoDummyRendererWriteVRAM,
|
||||
.drawScanline = GBVideoDummyRendererDrawScanline,
|
||||
.finishFrame = GBVideoDummyRendererFinishFrame,
|
||||
.getPixels = GBVideoDummyRendererGetPixels
|
||||
};
|
||||
|
||||
void GBVideoInit(struct GBVideo* video) {
|
||||
video->renderer = &dummyRenderer;
|
||||
video->vram = 0;
|
||||
video->frameskip = 0;
|
||||
}
|
||||
|
||||
void GBVideoReset(struct GBVideo* video) {
|
||||
video->ly = 0;
|
||||
video->mode = 1;
|
||||
|
||||
video->nextEvent = INT_MAX;
|
||||
video->eventDiff = 0;
|
||||
|
||||
video->nextMode = INT_MAX;
|
||||
|
||||
video->frameCounter = 0;
|
||||
video->frameskipCounter = 0;
|
||||
|
||||
if (video->vram) {
|
||||
mappedMemoryFree(video->vram, GB_SIZE_VRAM);
|
||||
}
|
||||
video->vram = anonymousMemoryMap(GB_SIZE_VRAM);
|
||||
video->renderer->vram = video->vram;
|
||||
|
||||
video->renderer->deinit(video->renderer);
|
||||
video->renderer->init(video->renderer);
|
||||
}
|
||||
|
||||
void GBVideoDeinit(struct GBVideo* video) {
|
||||
GBVideoAssociateRenderer(video, &dummyRenderer);
|
||||
mappedMemoryFree(video->vram, GB_SIZE_VRAM);
|
||||
}
|
||||
|
||||
void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
|
||||
video->eventDiff += cycles;
|
||||
if (video->nextEvent != INT_MAX) {
|
||||
video->nextEvent -= cycles;
|
||||
}
|
||||
if (video->nextEvent <= 0) {
|
||||
if (video->nextEvent != INT_MAX) {
|
||||
video->nextMode -= video->eventDiff;
|
||||
}
|
||||
if (video->nextMode <= 0) {
|
||||
video->mode = (video->mode + 1) & 3;
|
||||
switch (video->mode) {
|
||||
case 0:
|
||||
video->nextMode = GB_VIDEO_MODE_0_LENGTH;
|
||||
break;
|
||||
case 1:
|
||||
video->nextMode = GB_VIDEO_MODE_1_LENGTH;
|
||||
break;
|
||||
case 2:
|
||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
||||
++video->ly;
|
||||
if (video->ly >= GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
|
||||
video->ly = 0;
|
||||
++video->frameCounter;
|
||||
}
|
||||
video->p->memory.io[REG_LY] = video->ly;
|
||||
break;
|
||||
case 3:
|
||||
video->nextMode = GB_VIDEO_MODE_3_LENGTH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
video->nextEvent = video->nextMode;
|
||||
video->eventDiff = 0;
|
||||
}
|
||||
return video->nextEvent;
|
||||
}
|
||||
|
||||
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && GBRegisterLCDCIsEnable(value)) {
|
||||
// TODO: Does enabling the LCD start in vblank?
|
||||
video->mode = 2;
|
||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
||||
video->nextEvent = video->nextMode;
|
||||
video->eventDiff = 0;
|
||||
if (video->nextEvent < video->p->cpu->nextEvent) {
|
||||
video->p->cpu->nextEvent = video->nextEvent;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer) {
|
||||
UNUSED(renderer);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer) {
|
||||
UNUSED(renderer);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer) {
|
||||
UNUSED(renderer);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(address);
|
||||
return value;
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(address);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererDrawScanline(struct GBVideoRenderer* renderer, int y) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(y);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer) {
|
||||
UNUSED(renderer);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(stride);
|
||||
UNUSED(pixels);
|
||||
// Nothing to do
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef GB_VIDEO_H
|
||||
#define GB_VIDEO_H
|
||||
|
||||
#include "util/common.h"
|
||||
|
||||
#include "gb/memory.h"
|
||||
|
||||
enum {
|
||||
GB_VIDEO_HORIZONTAL_PIXELS = 160,
|
||||
GB_VIDEO_VERTICAL_PIXELS = 144,
|
||||
GB_VIDEO_VBLANK_PIXELS = 10,
|
||||
GB_VIDEO_VERTICAL_TOTAL_PIXELS = GB_VIDEO_VERTICAL_PIXELS + GB_VIDEO_VBLANK_PIXELS,
|
||||
|
||||
GB_VIDEO_MODE_0_LENGTH = 203, // Estimates, figure out with more precision
|
||||
GB_VIDEO_MODE_2_LENGTH = 81,
|
||||
GB_VIDEO_MODE_3_LENGTH = 172,
|
||||
|
||||
GB_VIDEO_HORIZONTAL_LENGTH = GB_VIDEO_MODE_0_LENGTH + GB_VIDEO_MODE_2_LENGTH + GB_VIDEO_MODE_3_LENGTH,
|
||||
|
||||
GB_VIDEO_MODE_1_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VBLANK_PIXELS,
|
||||
GB_VIDEO_TOTAL_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VERTICAL_TOTAL_PIXELS,
|
||||
};
|
||||
|
||||
struct GBVideoRenderer {
|
||||
void (*init)(struct GBVideoRenderer* renderer);
|
||||
void (*reset)(struct GBVideoRenderer* renderer);
|
||||
void (*deinit)(struct GBVideoRenderer* renderer);
|
||||
|
||||
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
void (*drawScanline)(struct GBVideoRenderer* renderer, int y);
|
||||
void (*finishFrame)(struct GBVideoRenderer* renderer);
|
||||
|
||||
void (*getPixels)(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
|
||||
void (*putPixels)(struct GBVideoRenderer* renderer, unsigned stride, void* pixels);
|
||||
|
||||
uint8_t* vram;
|
||||
};
|
||||
|
||||
DECL_BITFIELD(GBRegisterLCDC, uint8_t);
|
||||
DECL_BIT(GBRegisterLCDC, Enable, 7);
|
||||
|
||||
DECL_BITFIELD(GBRegisterSTAT, uint8_t);
|
||||
|
||||
struct GBVideo {
|
||||
struct GB* p;
|
||||
struct GBVideoRenderer* renderer;
|
||||
|
||||
int ly;
|
||||
int mode;
|
||||
|
||||
int32_t nextEvent;
|
||||
int32_t eventDiff;
|
||||
|
||||
int32_t nextMode;
|
||||
|
||||
uint8_t* vram;
|
||||
|
||||
int32_t frameCounter;
|
||||
int frameskip;
|
||||
int frameskipCounter;
|
||||
};
|
||||
|
||||
void GBVideoInit(struct GBVideo* video);
|
||||
void GBVideoReset(struct GBVideo* video);
|
||||
void GBVideoDeinit(struct GBVideo* video);
|
||||
void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer);
|
||||
int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles);
|
||||
|
||||
void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value);
|
||||
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value);
|
||||
|
||||
#endif
|
|
@ -69,8 +69,9 @@ void LR35902Reset(struct LR35902Core* cpu) {
|
|||
cpu->irqh.reset(cpu);
|
||||
}
|
||||
|
||||
void LR35902RaiseIRQ(struct LR35902Core* cpu) {
|
||||
// TODO
|
||||
void LR35902RaiseIRQ(struct LR35902Core* cpu, uint8_t vector) {
|
||||
cpu->irqPending = true;
|
||||
cpu->irqVector = vector;
|
||||
}
|
||||
|
||||
void LR35902Tick(struct LR35902Core* cpu) {
|
||||
|
@ -80,6 +81,12 @@ void LR35902Tick(struct LR35902Core* cpu) {
|
|||
cpu->executionState &= 3;
|
||||
switch (state) {
|
||||
case LR35902_CORE_FETCH:
|
||||
if (cpu->irqPending) {
|
||||
cpu->pc = cpu->irqVector;
|
||||
cpu->irqPending = false;
|
||||
cpu->irqh.setInterrupts(cpu, false);
|
||||
// TODO: stall
|
||||
}
|
||||
cpu->bus = cpu->memory.load8(cpu, cpu->pc);
|
||||
break;
|
||||
case LR35902_CORE_DECODE:
|
||||
|
|
|
@ -120,6 +120,9 @@ struct LR35902Core {
|
|||
bool condition;
|
||||
LR35902Instruction instruction;
|
||||
|
||||
bool irqPending;
|
||||
uint16_t irqVector;
|
||||
|
||||
struct LR35902Memory memory;
|
||||
struct LR35902InterruptHandler irqh;
|
||||
|
||||
|
@ -142,7 +145,7 @@ void LR35902HotplugAttach(struct LR35902Core* cpu, size_t slot);
|
|||
void LR35902HotplugDetach(struct LR35902Core* cpu, size_t slot);
|
||||
|
||||
void LR35902Reset(struct LR35902Core* cpu);
|
||||
void LR35902RaiseIRQ(struct LR35902Core*);
|
||||
void LR35902RaiseIRQ(struct LR35902Core* cpu, uint8_t vector);
|
||||
|
||||
void LR35902Tick(struct LR35902Core* cpu);
|
||||
|
||||
|
|
Loading…
Reference in New Issue