Skip to content

Commit 36cb58f

Browse files
committed
add display
1 parent f4a7ebc commit 36cb58f

File tree

4 files changed

+235
-1
lines changed

4 files changed

+235
-1
lines changed

cores/gba/sim/include/gpu.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#pragma once
2+
3+
#include "gba.hpp"
4+
5+
void render_frame(GameboyAdvanceHarness& gba, u32 framebuffer[160][240]);
6+
7+
void run_with_display(GameboyAdvanceHarness& gba);

cores/gba/sim/src/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
target_sources(GameboyAdvanceHarness
22
PRIVATE
3-
${CMAKE_CURRENT_SOURCE_DIR}/gba.cpp)
3+
${CMAKE_CURRENT_SOURCE_DIR}/gba.cpp
4+
${CMAKE_CURRENT_SOURCE_DIR}/gpu.cpp)

cores/gba/sim/src/gpu.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Code taken w/ modifications from https://github.com/RidgeX/ygba
2+
// Copyright (c) 2021 Ridge Shrubsall
3+
// SPDX-License-Identifier: BSD-3-Clause
4+
5+
#include "gpu.hpp"
6+
#include "gba.hpp"
7+
#include <iostream>
8+
9+
#define BIT(x, i) (((x) >> (i)) & 1)
10+
#define BITS(x, i, j) (((x) >> (i)) & ((1 << ((j) - (i) + 1)) - 1))
11+
12+
#define SCREEN_WIDTH 240
13+
#define SCREEN_HEIGHT 160
14+
15+
static u32 rgb555_to_rgb888(u16 pixel) {
16+
int red = BITS(pixel, 0, 4);
17+
int green = BITS(pixel, 5, 9);
18+
int blue = BITS(pixel, 10, 14);
19+
20+
red = (red << 3) | (red >> 2);
21+
green = (green << 3) | (green >> 2);
22+
blue = (blue << 3) | (blue >> 2);
23+
24+
return 0xff << 24 | blue << 16 | green << 8 | red;
25+
}
26+
27+
static bool read_bitmap_pixel(GameboyAdvanceHarness& gba, int x, int y, int mode, u16& out) {
28+
auto vram = gba.get_top().rootp->GameboyAdvance__DOT__VRAM__DOT__mem;
29+
auto io = gba.get_top().rootp->GameboyAdvance__DOT__IO__DOT__mem;
30+
31+
int w = (mode == 5 ? 160 : 240);
32+
int h = (mode == 5 ? 128 : 160);
33+
34+
if (x >= w || y >= h)
35+
return false;
36+
37+
if (mode == 3) {
38+
u32 addr = (y * 240 + x) * 2;
39+
out = *(u16*)&vram[addr];
40+
return true;
41+
}
42+
43+
if (mode == 4) {
44+
bool page = (io[0x000] & 0x10); // DISPCNT bit 4
45+
u32 base = page ? 0xA000 : 0x0000;
46+
47+
uint8_t index = vram[base + y * 240 + x];
48+
if (index == 0)
49+
return false;
50+
51+
auto palette = gba.get_top().rootp->GameboyAdvance__DOT__Palette__DOT__mem;
52+
out = *(u16*)&palette[index * 2];
53+
return true;
54+
}
55+
56+
if (mode == 5) {
57+
bool page = (io[0x000] & 0x10);
58+
u32 base = page ? 0xA000 : 0x0000;
59+
60+
u32 addr = base + (y * 160 + x) * 2;
61+
out = *(u16*)&vram[addr];
62+
return true;
63+
}
64+
65+
return false;
66+
}
67+
68+
void render_frame(GameboyAdvanceHarness& gba, u32 framebuffer[160][240]) {
69+
70+
auto io = gba.get_top().rootp->GameboyAdvance__DOT__IO__DOT__mem;
71+
int mode = io[0] & 0x7; // DISPCNT
72+
73+
for (int y = 0; y < 160; y++) {
74+
for (int x = 0; x < 240; x++) {
75+
u16 pixel;
76+
77+
if (read_bitmap_pixel(gba, x, y, mode, pixel)) {
78+
framebuffer[y][x] = rgb555_to_rgb888(pixel);
79+
} else {
80+
framebuffer[y][x] = 0xFF000000; // black
81+
}
82+
}
83+
}
84+
}
85+
86+
#include <SDL2/SDL.h>
87+
#include <cstdint>
88+
89+
class SDLDisplay {
90+
public:
91+
static constexpr int WIDTH = 240;
92+
static constexpr int HEIGHT = 160;
93+
94+
bool init(int scale = 3) {
95+
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
96+
return false;
97+
}
98+
99+
window = SDL_CreateWindow(
100+
"GBA",
101+
SDL_WINDOWPOS_CENTERED,
102+
SDL_WINDOWPOS_CENTERED,
103+
WIDTH * scale,
104+
HEIGHT * scale,
105+
0);
106+
107+
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
108+
109+
texture = SDL_CreateTexture(
110+
renderer,
111+
SDL_PIXELFORMAT_ARGB8888,
112+
SDL_TEXTUREACCESS_STREAMING,
113+
WIDTH,
114+
HEIGHT);
115+
116+
return window && renderer && texture;
117+
}
118+
119+
void draw(uint32_t framebuffer[HEIGHT][WIDTH]) {
120+
SDL_UpdateTexture(texture, nullptr, framebuffer, WIDTH * sizeof(uint32_t));
121+
122+
SDL_RenderClear(renderer);
123+
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
124+
SDL_RenderPresent(renderer);
125+
}
126+
127+
bool process_events() {
128+
SDL_Event e;
129+
while (SDL_PollEvent(&e)) {
130+
if (e.type == SDL_QUIT)
131+
return false;
132+
}
133+
return true;
134+
}
135+
136+
~SDLDisplay() {
137+
if (texture)
138+
SDL_DestroyTexture(texture);
139+
if (renderer)
140+
SDL_DestroyRenderer(renderer);
141+
if (window)
142+
SDL_DestroyWindow(window);
143+
SDL_Quit();
144+
}
145+
146+
private:
147+
SDL_Window* window = nullptr;
148+
SDL_Renderer* renderer = nullptr;
149+
SDL_Texture* texture = nullptr;
150+
};
151+
152+
void run_with_display(GameboyAdvanceHarness& gba) {
153+
const int WIDTH = 240;
154+
const int HEIGHT = 160;
155+
const int SCALE = 3;
156+
const int CYCLES_PER_FRAME = 280896;
157+
158+
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
159+
std::cerr << "SDL init failed\n";
160+
return;
161+
}
162+
163+
SDL_Window* window = SDL_CreateWindow(
164+
"GBA",
165+
SDL_WINDOWPOS_CENTERED,
166+
SDL_WINDOWPOS_CENTERED,
167+
WIDTH * SCALE,
168+
HEIGHT * SCALE,
169+
0);
170+
171+
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
172+
SDL_Texture* texture = SDL_CreateTexture(
173+
renderer,
174+
SDL_PIXELFORMAT_ARGB8888,
175+
SDL_TEXTUREACCESS_STREAMING,
176+
WIDTH,
177+
HEIGHT);
178+
179+
if (!window || !renderer || !texture) {
180+
std::cerr << "SDL setup failed\n";
181+
return;
182+
}
183+
184+
uint32_t framebuffer[HEIGHT][WIDTH];
185+
int cycle_counter = 0;
186+
bool running = true;
187+
188+
while (running) {
189+
// --- Handle events ---
190+
SDL_Event e;
191+
while (SDL_PollEvent(&e)) {
192+
if (e.type == SDL_QUIT) {
193+
running = false;
194+
}
195+
}
196+
197+
// --- Run emulation ---
198+
gba.tick();
199+
cycle_counter++;
200+
201+
// --- Render once per frame ---
202+
if (cycle_counter >= CYCLES_PER_FRAME) {
203+
cycle_counter = 0;
204+
205+
render_frame(gba, framebuffer);
206+
207+
SDL_UpdateTexture(texture, nullptr, framebuffer, WIDTH * sizeof(uint32_t));
208+
209+
SDL_RenderClear(renderer);
210+
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
211+
SDL_RenderPresent(renderer);
212+
213+
// Optional: cap to ~60 FPS
214+
SDL_Delay(16);
215+
}
216+
}
217+
218+
SDL_DestroyTexture(texture);
219+
SDL_DestroyRenderer(renderer);
220+
SDL_DestroyWindow(window);
221+
SDL_Quit();
222+
}

cores/gba/tests/arm.gba/test_arm.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <gtest/gtest.h>
99

1010
#include "gba.hpp"
11+
#include "gpu.hpp"
12+
1113
#include "util/decode.hpp"
1214
#include "util/test_config.hpp"
1315
#include "util/util.hpp"
@@ -409,6 +411,8 @@ TEST(ARM_GBA_Tests, CPUInstrsAll) {
409411
harness.get_top().rootp->GameboyAdvance__DOT__cpu_inst__DOT__regs.__PVT__common.__PVT__r2 = 436207618;
410412

411413
if (step_index == 1331) {
414+
run_with_display(harness);
415+
412416
// TODO: Implement display
413417
break;
414418
}

0 commit comments

Comments
 (0)