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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
|
#include "gb/io.h"
|
||||||
|
|
||||||
#include "util/crc32.h"
|
#include "util/crc32.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/math.h"
|
#include "util/math.h"
|
||||||
|
@ -30,17 +32,18 @@ void GBCreate(struct GB* gb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component) {
|
static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component) {
|
||||||
struct GB* gba = (struct GB*) component;
|
struct GB* gb = (struct GB*) component;
|
||||||
gba->cpu = cpu;
|
gb->cpu = cpu;
|
||||||
|
|
||||||
GBInterruptHandlerInit(&cpu->irqh);
|
GBInterruptHandlerInit(&cpu->irqh);
|
||||||
GBMemoryInit(gba);
|
GBMemoryInit(gb);
|
||||||
|
GBVideoInit(&gb->video);
|
||||||
|
|
||||||
gba->romVf = 0;
|
gb->romVf = 0;
|
||||||
|
|
||||||
gba->pristineRom = 0;
|
gb->pristineRom = 0;
|
||||||
gba->pristineRomSize = 0;
|
gb->pristineRomSize = 0;
|
||||||
gba->yankedRomSize = 0;
|
gb->yankedRomSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBLoadROM(struct GB* gb, struct VFile* vf, struct VFile* sav, const char* fname) {
|
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;
|
gb->yankedRomSize = 0;
|
||||||
}
|
}
|
||||||
GBMemoryReset(gb);
|
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) {
|
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) {
|
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) {
|
void GBHitStub(struct LR35902Core* cpu) {
|
||||||
// TODO
|
// 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 "lr35902/lr35902.h"
|
||||||
|
|
||||||
#include "gb/memory.h"
|
#include "gb/memory.h"
|
||||||
|
#include "gb/video.h"
|
||||||
|
|
||||||
extern const uint32_t DMG_LR35902_FREQUENCY;
|
extern const uint32_t DMG_LR35902_FREQUENCY;
|
||||||
extern const uint32_t CGB_LR35902_FREQUENCY;
|
extern const uint32_t CGB_LR35902_FREQUENCY;
|
||||||
|
@ -25,11 +26,20 @@ enum GBIRQ {
|
||||||
GB_IRQ_KEYPAD = 0x4,
|
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 GB {
|
||||||
struct LR35902Component d;
|
struct LR35902Component d;
|
||||||
|
|
||||||
struct LR35902Core* cpu;
|
struct LR35902Core* cpu;
|
||||||
struct GBMemory memory;
|
struct GBMemory memory;
|
||||||
|
struct GBVideo video;
|
||||||
|
|
||||||
int* keySource;
|
int* keySource;
|
||||||
|
|
||||||
|
@ -47,9 +57,7 @@ void GBDestroy(struct GB* gb);
|
||||||
|
|
||||||
void GBReset(struct LR35902Core* cpu);
|
void GBReset(struct LR35902Core* cpu);
|
||||||
|
|
||||||
void GBWriteIE(struct GB* gb, uint8_t value);
|
void GBUpdateIRQs(struct GB* gb);
|
||||||
void GBRaiseIRQ(struct GB* gb, enum GBIRQ irq);
|
|
||||||
void GBTestIRQ(struct LR35902Core* cpu);
|
|
||||||
void GBHalt(struct GB* gb);
|
void GBHalt(struct GB* gb);
|
||||||
void GBStop(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 "memory.h"
|
||||||
|
|
||||||
#include "gb/gb.h"
|
#include "gb/gb.h"
|
||||||
|
#include "gb/io.h"
|
||||||
|
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
|
||||||
|
@ -26,6 +27,10 @@ void GBMemoryInit(struct GB* gb) {
|
||||||
gb->memory.rom = 0;
|
gb->memory.rom = 0;
|
||||||
gb->memory.romBank = 0;
|
gb->memory.romBank = 0;
|
||||||
gb->memory.romSize = 0;
|
gb->memory.romSize = 0;
|
||||||
|
|
||||||
|
memset(gb->memory.hram, 0, sizeof(gb->memory.hram));
|
||||||
|
|
||||||
|
GBIOInit(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBMemoryDeinit(struct GB* gb) {
|
void GBMemoryDeinit(struct GB* gb) {
|
||||||
|
@ -80,9 +85,21 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
|
||||||
case GB_REGION_WORKING_RAM_BANK1:
|
case GB_REGION_WORKING_RAM_BANK1:
|
||||||
return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
|
return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
|
||||||
default:
|
default:
|
||||||
|
if (address < GB_BASE_OAM) {
|
||||||
|
return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
|
||||||
|
}
|
||||||
|
if (address < GB_BASE_IO) {
|
||||||
// TODO
|
// TODO
|
||||||
return 0;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBStore16(struct LR35902Core* cpu, uint16_t address, int16_t value) {
|
void GBStore16(struct LR35902Core* cpu, uint16_t address, int16_t value) {
|
||||||
|
@ -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;
|
memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
if (address < GB_BASE_OAM) {
|
||||||
|
memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
|
||||||
|
} else if (address < GB_BASE_IO) {
|
||||||
// TODO
|
// TODO
|
||||||
return;
|
} 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* wram;
|
||||||
uint8_t* wramBank;
|
uint8_t* wramBank;
|
||||||
|
|
||||||
|
uint8_t io[GB_SIZE_IO];
|
||||||
|
bool ime;
|
||||||
|
uint8_t ie;
|
||||||
|
|
||||||
|
uint8_t hram[GB_SIZE_HRAM];
|
||||||
|
|
||||||
size_t romSize;
|
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);
|
cpu->irqh.reset(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LR35902RaiseIRQ(struct LR35902Core* cpu) {
|
void LR35902RaiseIRQ(struct LR35902Core* cpu, uint8_t vector) {
|
||||||
// TODO
|
cpu->irqPending = true;
|
||||||
|
cpu->irqVector = vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LR35902Tick(struct LR35902Core* cpu) {
|
void LR35902Tick(struct LR35902Core* cpu) {
|
||||||
|
@ -80,6 +81,12 @@ void LR35902Tick(struct LR35902Core* cpu) {
|
||||||
cpu->executionState &= 3;
|
cpu->executionState &= 3;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case LR35902_CORE_FETCH:
|
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);
|
cpu->bus = cpu->memory.load8(cpu, cpu->pc);
|
||||||
break;
|
break;
|
||||||
case LR35902_CORE_DECODE:
|
case LR35902_CORE_DECODE:
|
||||||
|
|
|
@ -120,6 +120,9 @@ struct LR35902Core {
|
||||||
bool condition;
|
bool condition;
|
||||||
LR35902Instruction instruction;
|
LR35902Instruction instruction;
|
||||||
|
|
||||||
|
bool irqPending;
|
||||||
|
uint16_t irqVector;
|
||||||
|
|
||||||
struct LR35902Memory memory;
|
struct LR35902Memory memory;
|
||||||
struct LR35902InterruptHandler irqh;
|
struct LR35902InterruptHandler irqh;
|
||||||
|
|
||||||
|
@ -142,7 +145,7 @@ void LR35902HotplugAttach(struct LR35902Core* cpu, size_t slot);
|
||||||
void LR35902HotplugDetach(struct LR35902Core* cpu, size_t slot);
|
void LR35902HotplugDetach(struct LR35902Core* cpu, size_t slot);
|
||||||
|
|
||||||
void LR35902Reset(struct LR35902Core* cpu);
|
void LR35902Reset(struct LR35902Core* cpu);
|
||||||
void LR35902RaiseIRQ(struct LR35902Core*);
|
void LR35902RaiseIRQ(struct LR35902Core* cpu, uint8_t vector);
|
||||||
|
|
||||||
void LR35902Tick(struct LR35902Core* cpu);
|
void LR35902Tick(struct LR35902Core* cpu);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue