Skip to content

Commit b008079

Browse files
committed
Began PPU
1 parent 2969a57 commit b008079

File tree

12 files changed

+110
-12
lines changed

12 files changed

+110
-12
lines changed

.vscode/settings.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
"locale": "cpp",
5959
"optional": "cpp",
6060
"xlocbuf": "cpp",
61-
"xlocmes": "cpp"
61+
"xlocmes": "cpp",
62+
"chrono": "cpp",
63+
"ratio": "cpp"
6264
}
6365
}

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# A Gameboy emulator written in C++ (WIP)
1+
# A Game Boy emulator written in C++ (WIP)
22

33
I started this project to create a cycle accurate emulator which is capable to do emulation in **interpreter mode** and in **jit mode**.
44
The aim was to create a code base which is easy to follow for beginners.
@@ -55,4 +55,5 @@ struct Instruction
5555
- [Opcodes](http://www.devrs.com/gb/files/opcodes.html)
5656
- [Opcodes Table](http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html)
5757
- [Memory Map](http://gameboy.mongenel.com/dmg/asmmemmap.html)
58-
- [Game Boy - CPU Manual](https://realboyemulator.files.wordpress.com/2013/01/gbcpuman.pdf) (Backup of the Site in the *docs folder*)
58+
- [Game Boy - CPU Manual](https://realboyemulator.files.wordpress.com/2013/01/gbcpuman.pdf) (Backup of the Site in the *docs folder*)
59+
- [Ultimate Game Boy Talk](https://www.youtube.com/watch?v=HyzD8pNlpwI)

src/hardware/cpu.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Cpu::Cpu(Memory& memory) : memory(memory)
55
nextInstruction = parseNextInstruction();
66
}
77

8-
void Cpu::cycle()
8+
long Cpu::cycle()
99
{
1010
int cycles = clock.getCatchUpCycles();
1111
while(cycles > 0 && (state == RUNNING || state == STEP))
@@ -19,6 +19,7 @@ void Cpu::cycle()
1919
if(state == STEP) state = PAUSED;
2020
}
2121
clock.Reset();
22+
return cycles;
2223
}
2324

2425
bool Cpu::getFlag(Flag flag)

src/hardware/cpu.h

+1-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Cpu
3131
public:
3232
Cpu(Memory& memory);
3333

34-
void cycle();
34+
long cycle();
3535

3636
bool getFlag(Flag flag);
3737
void setFlag(Flag flag, bool value);
@@ -64,7 +64,6 @@ class Cpu
6464

6565
Memory& memory;
6666
Clock clock = Clock(4194304); // Hz
67-
bool interruptsEnabled = true;
6867

6968
private:
7069
ParsedInstruction parseNextInstruction();

src/hardware/gameboy.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
Gameboy::Gameboy(Cartridge& cartridge) :
44
cartridge(cartridge),
55
memory(Memory(cartridge)),
6-
cpu(Cpu(memory))
6+
cpu(Cpu(memory)),
7+
ppu(memory)
78
{ }
89

910
void Gameboy::process()
1011
{
11-
cpu.cycle();
12+
long cycles = cpu.cycle();
13+
ppu.cycle(cycles);
1214
}

src/hardware/gameboy.h

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#define GAMEBOY_H
44

55
#include "cpu.h"
6+
#include "ppu.h"
67
#include "memory.h"
78
#include "cartridge.h"
89

@@ -15,6 +16,7 @@ class Gameboy
1516
Cartridge& cartridge;
1617
Memory memory;
1718
Cpu cpu;
19+
Ppu ppu;
1820
};
1921

2022
#endif // GAMEBOY_H

src/hardware/helper/clock.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ void Clock::Reset() { lastCycle = getNowMs(); }
77

88
long Clock::getCatchUpCycles()
99
{
10+
if(lastCycle == -1) Reset();
1011
long long nowMs = getNowMs();
11-
long cycles = (int) (nowMs - lastCycle) * frequency / 1000;
12+
long cycles = (long) (nowMs - lastCycle) * frequency / 1000;
1213
return cycles;
1314
}
1415

src/hardware/memory.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ void Memory::write(const uint16_t address, const uint8_t value) { memory[address
1010
uint8_t Memory::read(const uint16_t address, const bool debugAccess) const
1111
{
1212
// Cartridge Mapping
13-
if(address >= 0x100 && address <= 0x3F00)
13+
if(address >= 0x000 && address <= 0x3F00)
1414
return cartridge.read(address);
1515
// VRAM Access through the PPU
1616
// CPU is not allowed to access it directly

src/hardware/ppu.cpp

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "ppu.h"
2+
3+
Ppu::Ppu(Memory& memory) : memory(memory) { }
4+
5+
void Ppu::cycle(long cycles)
6+
{
7+
if(!memory.readIORegisterBit(LCD_STATUS, LCD_ENABLE)) return;
8+
9+
cycleCount += cycles;
10+
uint8_t currentMode =
11+
memory.readIORegisterBit(LCD_CONTROL, LCD_MODE_HIGH) << 1
12+
& memory.readIORegisterBit(LCD_CONTROL, LCD_MODE_LOW);
13+
14+
switch(currentMode)
15+
{
16+
case MODE_OAM:
17+
if(!cycleCount >= CYCLES_PER_OAMSEARCH) return;
18+
19+
// Nothing to do in this stage besides switching into the next mode
20+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_HIGH, 1);
21+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_LOW, 1);
22+
cycleCount = cycleCount % CYCLES_PER_OAMSEARCH;
23+
break;
24+
case MODE_TRANSFER:
25+
if(!cycleCount >= CYCLES_PER_TRANSFER) return;
26+
27+
// LYC? HBLANK INTERRUPT?
28+
29+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_HIGH, 0);
30+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_LOW, 0);
31+
cycleCount = cycleCount % CYCLES_PER_TRANSFER;
32+
break;
33+
case MODE_HBLANK:
34+
if(!cycleCount >= CYCLES_PER_HBLANK) return;
35+
memory.write(LCD_Y, memory.read(LCD_Y) + 1);
36+
// HBLANK INTERRUPT?
37+
// DRAW Line on Screen
38+
// Enter V-BLANK
39+
if(memory.read(LCD_Y) < 144)
40+
{
41+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_HIGH, 0);
42+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_LOW, 1);
43+
}
44+
// Enter OAM again
45+
else
46+
{
47+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_HIGH, 1);
48+
memory.writeIORegisterBit(LCD_CONTROL, LCD_MODE_LOW, 0);
49+
}
50+
cycleCount = cycleCount % CYCLES_PER_HBLANK;
51+
break;
52+
case MODE_VBLANK:
53+
if(!cycleCount >= CYCLES_PER_VBLANK) return;
54+
// SET V INTERRUPT
55+
56+
cycleCount = cycleCount % CYCLES_PER_VBLANK;
57+
break;
58+
}
59+
}

src/hardware/ppu.h

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
#ifndef PPU_H
3+
#define PPU_H
4+
5+
#include "memory.h"
6+
7+
enum Mode {
8+
MODE_HBLANK = 0,
9+
MODE_VBLANK = 1,
10+
MODE_OAM = 2,
11+
MODE_TRANSFER = 3,
12+
};
13+
14+
class Ppu
15+
{
16+
public:
17+
Ppu(Memory& memory);
18+
19+
void cycle(long cycles);
20+
21+
private:
22+
Memory& memory;
23+
long cycleCount = 0;
24+
25+
const uint16_t CYCLES_PER_HBLANK = 207; // Mode 0 (H-Blank) 207 cycles
26+
const uint16_t CYCLES_PER_VBLANK = 4560; // Mode 1 (V-Blank) 4560 cycles
27+
const uint16_t CYCLES_PER_OAMSEARCH = 83; // Mode 2 (OAM Search) 83 cycles
28+
const uint16_t CYCLES_PER_TRANSFER = 175; // Mode 3 (Transfer LCD) 175 cycles
29+
};
30+
31+
#endif // PPU_H

src/main.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ int main(int argc, char** args) {
2828

2929
CpuScreen cpuScreen(gameboy.cpu);
3030
GameboyScreen gameboyScreen(gameboy);
31-
ComponentsScreen componentsScreen(gameboy.memory);
31+
ComponentsScreen componentsScreen(gameboy.memory, cartridge);
3232
InstructionScreen instructionScreen(gameboy.cpu);
3333

3434
window.addScreen(&componentsScreen);

src/screens/components_screen.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ComponentsScreen : public Screen
2626
std::function<uint8_t (uint32_t)> memoryReadFunc);
2727

2828
std::map<uint16_t, std::string> memoryToolTips = {
29-
{ 0x0100, "Cartridge Bank 0 Start" },
29+
{ 0x0000, "Cartridge Bank 0 Start" },
3030
{ 0x4000, "Cartridge Bank Switchable" },
3131
{ 0x8000, "Video RAM Start" },
3232
{ 0xA000, "External RAM Start" },

0 commit comments

Comments
 (0)