upload of the files for pgb
This commit is contained in:
commit
335839205a
10 changed files with 2905 additions and 0 deletions
10
.clangd
Normal file
10
.clangd
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
CompileFlags:
|
||||||
|
Add:
|
||||||
|
- -x
|
||||||
|
- c
|
||||||
|
- -Wall
|
||||||
|
- -Wextra
|
||||||
|
- -Werror
|
||||||
|
- -Wpedantic
|
||||||
|
- -std=c99
|
||||||
|
- -Wno-empty-translation-unit
|
||||||
31
Makefile
Normal file
31
Makefile
Normal 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
0
README.md
Normal file
BIN
pgb
Executable file
BIN
pgb
Executable file
Binary file not shown.
45
src/main.c
Normal file
45
src/main.c
Normal 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
47
src/pgb.h
Normal 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
67
src/ppu.c
Normal 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
37
src/timer.c
Normal 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
53
src/window.c
Normal 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);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue