upload of the files for pgb

This commit is contained in:
Patrick 2025-11-25 19:01:03 +01:00
commit 335839205a
10 changed files with 2905 additions and 0 deletions

10
.clangd Normal file
View file

@ -0,0 +1,10 @@
CompileFlags:
Add:
- -x
- c
- -Wall
- -Wextra
- -Werror
- -Wpedantic
- -std=c99
- -Wno-empty-translation-unit

31
Makefile Normal file
View file

@ -0,0 +1,31 @@
CC = cc
CFLAGS = -Wall -Wextra -Wpedantic -Werror -std=c99 -lX11
SRCDIR = src
OBJDIR = obj
SRC = $(wildcard $(SRCDIR)/*.c)
OBJ = $(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
TARGET = pgb
debug: CFLAGS += -g3 -fsanitize=address
debug: all
release: CFLAGS += -O3 -s
release: all
all: $(OBJDIR) $(TARGET)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) -o $@ $^
$(OBJDIR):
mkdir -p $(OBJDIR)
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(OBJDIR) $(TARGET)
.PHONY: clean all debug release

0
README.md Normal file
View file

BIN
pgb Executable file

Binary file not shown.

2615
src/cpu.c Normal file

File diff suppressed because it is too large Load diff

45
src/main.c Normal file
View file

@ -0,0 +1,45 @@
#include "pgb.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define GB_RATE 59.73
#define CLOCKS_PER_FRAME 70224
#define TIME_TO_SLEEP (CLOCKS_PER_SEC / GB_RATE)
int main(const int argc, const char **argv) {
create_window();
if (argc > 1) {
const char *gamerom = NULL;
const char *logfile = NULL;
int i;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "--file") == 0) {
gamerom = argv[++i];
} else if (strcmp(argv[i], "--log") == 0) {
logfile = argv[++i];
}
}
if (gamerom != NULL) {
cpu_init(gamerom, logfile);
int timer = time(NULL);
while (1) {
int i;
int start = clock();
for (i = 0; i < CLOCKS_PER_FRAME; i++) {
cpu_cycle();
ppu_dot();
timer_cycle();
}
while ((clock() - start) < TIME_TO_SLEEP) {
}
if (time(NULL) - timer >= 1) {
timer = time(NULL);
}
}
}
}
close_window();
return EXIT_SUCCESS;
}

47
src/pgb.h Normal file
View file

@ -0,0 +1,47 @@
typedef unsigned char u8;
typedef signed char s8;
typedef unsigned short u16;
typedef signed char s16;
typedef enum R8 { R8_A, R8_F, R8_B, R8_C, R8_D, R8_E, R8_H, R8_L } R8;
typedef enum R16 { R16_AF, R16_BC, R16_DE, R16_HL, R16_SP, R16_PC } R16;
typedef enum RFLAG {
NOFLAG0,
NOFLAG1,
NOFLAG2,
NOFLAG3,
FLAG_C,
FLAG_H,
FLAG_N,
FLAG_Z
} RFLAG;
typedef enum INTERRUPT {
INT_VBLANK,
INT_LCD,
INT_TIMER,
INT_SERIAL,
INT_JOYPAD
} INTERRUPT;
void lock_oam(void);
void unlock_oam(void);
void lock_vram(void);
void unlock_vram(void);
u8 mread8_external(const u16 addr);
u16 mread16_external(const u16 addr);
void mwrite8_external(const u16 addr, const u8 val);
void mwrite16_external(const u16 addr, const u16 val);
void cpu_init(const char *gamerom, const char *logfile);
void cpu_cycle(void);
void create_interrupt(const INTERRUPT interrupt);
void ppu_dot(void);
void timer_cycle(void);
void create_window(void);
void draw_pixel(u8 x, u8 y, u8 color);
void close_window(void);

67
src/ppu.c Normal file
View file

@ -0,0 +1,67 @@
#include "pgb.h"
#include <stdio.h>
#include <stdlib.h>
static u16 dot;
static u8 scanline;
static u8 x;
static u8 delay;
#define get_lcdc(bit) ((mread8_external(0xFF40) >> bit) & 1)
void fetch_bg_pixel(u8 display_x, u8 display_y) {
u8 internal_x = mread8_external(0xFF43) + display_x;
u8 internal_y = mread8_external(0xFF42) + display_y;
u16 tile_addr_offset =
mread8_external((get_lcdc(3) ? 0x9C00 : 0x9800) + (u16)(internal_x / 8) +
((u16)(internal_y / 8)) * 32) *
16;
u16 tile_addr = get_lcdc(4) ? (0x8000 + tile_addr_offset)
: (0x9000 + (s16)tile_addr_offset);
u8 tile_x = internal_x % 8;
u8 tile_y = internal_y % 8;
u16 tile_data = mread16_external(tile_addr + tile_y * 2);
if (tile_y < 3) {
u16 addr = 0x8000 + (mread8_external((get_lcdc(3) ? 0x9C00 : 0x9800) +
(u16)internal_x / 8 +
(u16)((internal_y + 3) / 8) * 32) *
16);
if (mread16_external(addr + (tile_y + 3) * 2) != tile_data && get_lcdc(4)) {
printf("(u16)(%d) (u16)(%d)\n", (internal_y) / 8, (internal_y + 3) / 8);
}
}
u8 color = (tile_data >> abs(tile_x - 7)) & 1;
color += ((tile_data >> abs(tile_x - 15)) & 1) * 2;
draw_pixel(display_x, display_y, color);
}
void ppu_dot(void) {
if (dot == 0) {
lock_oam();
x = 0;
delay = 0;
} else if (dot == 456) {
dot = -1;
scanline++;
if (scanline == 144) {
create_interrupt(INT_VBLANK);
}
if (scanline == 154) {
scanline = 0;
}
}
if (dot >= 80 && x < 160) {
if (delay == 0) {
unlock_vram();
fetch_bg_pixel(x, scanline);
x++;
lock_vram();
} else {
delay--;
}
} else if (x == 160) {
unlock_vram();
unlock_oam();
}
dot++;
}

37
src/timer.c Normal file
View file

@ -0,0 +1,37 @@
#include "pgb.h"
static u16 counter = 0;
static u16 get_cycles_to_count(void) {
switch (mread8_external(0xFF07) & 3) {
case 0:
return 1024;
break;
case 1:
return 16;
break;
case 2:
return 64;
break;
case 3:
return 256;
break;
}
return 1024;
}
void timer_cycle(void) {
if (mread8_external(0xFF07) & 4) {
if (counter < get_cycles_to_count()) {
counter++;
} else {
counter = 0;
if (mread8_external(0xFF05) == 0xFF) {
mwrite8_external(0xFF05, mread8_external(0xFF06));
create_interrupt(INT_TIMER);
} else {
mwrite8_external(0xFF05, mread8_external(0xFF05) + 1);
}
}
}
}

53
src/window.c Normal file
View file

@ -0,0 +1,53 @@
#include "pgb.h"
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
static Display *display;
static Window window;
static int screen;
void create_window(void) {
display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
screen = DefaultScreen(display);
window = XCreateSimpleWindow(display, RootWindow(display, screen), 10, 10,
320, 288, 1, BlackPixel(display, screen),
WhitePixel(display, screen));
XStoreName(display, window, "PGB");
XSelectInput(display, window, ExposureMask | KeyPressMask);
XMapWindow(display, window);
}
void draw_pixel(u8 x, u8 y, u8 color) {
unsigned long real_color = 0xFFFFFFFF;
switch (color) {
case 0:
real_color = 0xFFFFFFFF;
break;
case 1:
real_color = 0xAAAAAAAA;
break;
case 2:
real_color = 0x55555555;
break;
case 3:
real_color = 0x00000000;
break;
}
XSetForeground(display, DefaultGC(display, screen), real_color);
XFillRectangle(display, window, DefaultGC(display, screen), x * 2, y * 2, 2,
2);
}
void close_window(void) {
XDestroyWindow(display, window);
XCloseDisplay(display);
}