beginning ppu

This commit is contained in:
Samuel Walker 2025-02-01 00:48:49 -07:00
parent 5206c3871e
commit f4cbfd09c8
Signed by: piwalker
GPG Key ID: 616B1928705EA4C9
25 changed files with 683 additions and 43 deletions

9
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
"files.associations": {
"lcd.h": "c",
"ppu_sm.h": "c",
"string.h": "c",
"common.h": "c",
"ppu.h": "c"
}
}

View File

@ -15,5 +15,6 @@ typedef uint64_t u64;
#define BETWEEN(a, b, c) ((a >= b) && (a <= c))
void delay(u32 ms);
u32 get_ticks();
#define NO_IMPL { fprintf(stderr, "NOT YET IMPLEMENTED\n"); exit(-5); }

8
include/dma.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <common.h>
void dma_start(u8 start);
void dma_tick();
bool dma_transferring();

60
include/lcd.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <common.h>
typedef struct {
//registers
u8 lcdc;
u8 lcds;
u8 scroll_y;
u8 scroll_x;
u8 ly;
u8 ly_compare;
u8 dma;
u8 bg_palette;
u8 obj_palette[2];
u8 win_y;
u8 win_x;
//other data...
u32 bg_colors[4];
u32 sp1_colors[4];
u32 sp2_colors[4];
} lcd_context;
typedef enum {
MODE_HBLANK,
MODE_VBLANK,
MODE_OAM,
MODE_XFER
} lcd_mode;
lcd_context *lcd_get_context();
#define LCDC_BGW_ENABLE (BIT(lcd_get_context()->lcdc, 0))
#define LCDC_OBJ_ENABLE (BIT(lcd_get_context()->lcdc, 1))
#define LCDC_OBJ_HEIGHT (BIT(lcd_get_context()->lcdc, 2) ? 16 : 8)
#define LCDC_BG_MAP_AREA (BIT(lcd_get_context()->lcdc, 3) ? 0x9C00 : 0x9800)
#define LCDC_BGW_DATA_AREA (BIT(lcd_get_context()->lcdc, 4) ? 0x8000 : 0x8800)
#define LCDC_WIN_ENABLE (BIT(lcd_get_context()->lcdc, 5))
#define LCDC_WIN_MAP_AREA (BIT(lcd_get_context()->lcdc, 6) ? 0x9C00 : 0x9800)
#define LCDC_LCD_ENABLE (BIT(lcd_get_context()->lcdc, 7))
#define LCDS_MODE ((lcd_mode)(lcd_get_context()->lcds & 0b11))
#define LCDS_MODE_SET(mode) { lcd_get_context()->lcds &= ~0b11; lcd_get_context()->lcdc |= mode; }
#define LCDS_LYC (BIT(lcd_get_context()->lcds, 2))
#define LCDS_LYC_SET(b) BIT_SET(lcd_get_context()->lcds, 2, b)
typedef enum {
SS_HBLANK = (1 << 3),
SS_VBLANK = (1 << 4),
SS_OAM = (1 << 5),
SS_LYC = (1 << 6)
} stat_src;
#define LCDS_STAT_INT(src) (lcd_get_context()->lcds & src)
void lcd_init();
u8 lcd_read(u16 address);
void lcd_write(u16 address, u8 value);

View File

@ -2,5 +2,40 @@
#include <common.h>
static const LINES_PER_FRAME = 154;
static const TICKS_PER_LINE = 456;
static const YRES = 144;
static const XRES = 160;
typedef struct {
u8 y;
u8 x;
u8 tile;
unsigned f_cgb_pn : 3;
unsigned f_cgb_vram_bank : 1;
unsigned f_pn : 1;
unsigned f_x_flip : 1;
unsigned f_y_flip : 1;
unsigned f_bgp : 1;
} oam_entry;
typedef struct {
oam_entry oam_ram[40];
u8 vram[0x2000];
u32 current_frame;
u32 line_ticks;
u32 *video_buffer;
} ppu_context;
void ppu_init();
void ppu_tick();
void ppu_tick();
void ppu_oam_write(u16 address, u8 value);
u8 ppu_oam_read(u16 address);
void ppu_vram_write(u16 address, u8 value);
u8 ppu_vram_read(u16 address);
ppu_context *ppu_get_context();

8
include/ppu_sm.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <common.h>
void ppu_mode_oam();
void ppu_mode_xfer();
void ppu_mode_vblank();
void ppu_mode_hblank();

View File

@ -2,5 +2,17 @@
#include <common.h>
typedef struct {
u16 div;
u8 tima;
u8 tma;
u8 tac;
} timer_context;
void timer_init();
void timer_tick();
void timer_tick();
void timer_write(u16 address, u8 value);
u8 timer_read(u16 address);
timer_context *timer_get_context();

View File

@ -6,4 +6,5 @@ static const int SCREEN_WIDTH = 1024;
static const int SCREEN_HEIGHT = 768;
void ui_init();
void ui_handle_events();
void ui_handle_events();
void ui_update();

View File

@ -4,6 +4,8 @@
#include <cpu.h>
#include <ram.h>
#include <io.h>
#include <ppu.h>
#include <dma.h>
// 0x0000 - 0x3FFF : ROM Bank 0
// 0x4000 - 0x7FFF : ROM Bank 1 - Switchable
@ -24,10 +26,7 @@ u8 bus_read(u16 address) {
return cart_read(address);
} else if (address < 0xA000) {
//Char/Map Data
//TODO
printf("UNSUPPORTED bus_read(%04X)\n", address);
//NO_IMPL
return 0;
return ppu_vram_read(address);
} else if (address < 0xC000) {
//Cartridge RAM
return cart_read(address);
@ -39,10 +38,9 @@ u8 bus_read(u16 address) {
return 0;
} else if (address < 0xFEA0) {
//OAM
//TODO
printf("UNSUPPORTED bus_read(%04X)\n", address);
//NO_IMPL
return 0;
if(dma_transferring())
return 0xFF;
return ppu_oam_read(address);
} else if (address < 0xFF00) {
//reserved unusable
return 0;
@ -66,10 +64,8 @@ void bus_write(u16 address, u8 value) {
return;
} else if (address < 0xA000) {
//Char/Map Data
//TODO
printf("UNSUPPORTED bus_write(%04X)\n", address);
ppu_vram_write(address, value);
return;
//NO_IMPL
} else if (address < 0xC000) {
//Cartridge RAM
cart_write(address, value);
@ -83,10 +79,9 @@ void bus_write(u16 address, u8 value) {
return;
} else if (address < 0xFEA0) {
//OAM
//TODO
printf("UNSUPPORTED bus_write(%04X)\n", address);
if(!dma_transferring())
ppu_oam_write(address, value);
return;
//NO_IMPL
} else if (address < 0xFF00) {
//reserved unusable
return;

View File

@ -4,11 +4,36 @@
#include <emu.h>
#include <interrupts.h>
#include <dbg.h>
#include <timer.h>
cpu_context ctx = {0};
#define CPU_DEBUG 0
#define FILE_LOG 0
#if FILE_LOG == 1
FILE *log;
#endif
void cpu_init() {
ctx.regs.a = 0x01;
ctx.regs.f = 0xB0;
ctx.regs.b = 0x00;
ctx.regs.c = 0x13;
ctx.regs.d = 0x00;
ctx.regs.e = 0xD8;
ctx.regs.h = 0x01;
ctx.regs.l = 0x4D;
ctx.regs.sp = 0xFFFE;
ctx.regs.pc = 0x100;
ctx.ie_register = 0;
ctx.int_flags = 0;
ctx.int_master_enabled = false;
ctx.enabling_ime = false;
timer_get_context()->div = 0xABCC;
#if FILE_LOG == 1
log = fopen("log.txt", "w");
#endif
}
static void fetch_instruction() {
@ -32,7 +57,15 @@ bool cpu_step() {
if(!ctx.halted) {
u16 pc = ctx.regs.pc;
fetch_instruction();
emu_cycles(1);
#if FILE_LOG == 1
fprintf(log, "A:%02X F:%02X B:%02X C:%02X D:%02X E:%02X H:%02X L:%02X SP:%04X PC:%04X PCMEM:%02X,%02X,%02X,%02X\n",
ctx.regs.a, ctx.regs.f, ctx.regs.b, ctx.regs.c, ctx.regs.d, ctx.regs.e, ctx.regs.h, ctx.regs.l, ctx.regs.sp, pc,
bus_read(pc), bus_read(pc+1), bus_read(pc+2), bus_read(pc+3)
);
#endif
fetch_data();
#if CPU_DEBUG == 1
char flags[16];
sprintf(flags, "%c%c%c%c",
ctx.regs.f & (1 << 7) ? 'Z' : '-',
@ -52,6 +85,7 @@ bool cpu_step() {
dbg_update();
dbg_print();
#endif
execute();
} else {
@ -81,4 +115,8 @@ u8 cpu_get_ie_register(){
void cpu_set_ie_register(u8 ie){
ctx.ie_register = ie;
}
void cpu_request_interrupt(interrupt_type t) {
ctx.int_flags |= t;
}

View File

@ -44,7 +44,7 @@ void fetch_data() {
return;
case AM_R_MR: {
u16 addr = cpu_read_reg(ctx.cur_inst->reg_2);
if(ctx.cur_inst->reg_1 == RT_C) {
if(ctx.cur_inst->reg_2 == RT_C) {
addr |= 0xFF00;
}
ctx.fetched_data = bus_read(addr);
@ -74,7 +74,7 @@ void fetch_data() {
cpu_set_reg(RT_HL, cpu_read_reg(RT_HL)-1);
return;
case AM_R_A8:
ctx.fetched_data = bus_read(bus_read(ctx.regs.pc));
ctx.fetched_data = bus_read(ctx.regs.pc);
emu_cycles(2);
ctx.regs.pc++;
return;

View File

@ -5,7 +5,7 @@
//process CPU instructions...
void cpu_set_flags(cpu_context *ctx, u8 z, u8 n, u8 h, u8 c){
void cpu_set_flags(cpu_context *ctx, int8_t z, int8_t n, int8_t h, int8_t c){
if (z != -1){
BIT_SET(ctx->regs.f, 7, z)
}
@ -52,7 +52,7 @@ static void proc_daa(cpu_context *ctx) {
}
static void proc_cpl(cpu_context *ctx) {
ctx->regs.a = -ctx->regs.a;
ctx->regs.a = ~ctx->regs.a;
cpu_set_flags(ctx, -1, 1, 1, -1);
}
@ -112,7 +112,7 @@ static void proc_rra(cpu_context *ctx) {
static void proc_cb(cpu_context *ctx) {
u8 op = ctx->fetched_data;
reg_type reg = decode_reg(op & 0b111);
u8 bit = (op >> 3) & 0xb111;
u8 bit = (op >> 3) & 0b111;
u8 bit_op = (op >> 6) & 0b11;
u8 reg_val = cpu_read_reg8(reg);
emu_cycles(1);
@ -138,6 +138,7 @@ static void proc_cb(cpu_context *ctx) {
reg_val |= (1 << bit);
cpu_set_reg8(reg, reg_val);
return;
default: break;
}
bool flagC = CPU_FLAG_C;
@ -148,7 +149,7 @@ static void proc_cb(cpu_context *ctx) {
bool setC = false;
u8 result = (reg_val << 1) & 0xFF;
if(reg_val & (1 << 7) != 0) {
if((reg_val & (1 << 7)) != 0) {
result |= 1;
setC = true;
}
@ -322,7 +323,7 @@ static void proc_add(cpu_context *ctx) {
if(ctx->cur_inst->reg_1 == RT_SP) {
z = 0;
h = (cpu_read_reg(ctx->cur_inst->reg_1) & 0xF) + (ctx->fetched_data & 0xF) >= 0x10;
c = (int)(cpu_read_reg(ctx->cur_inst->reg_1) & 0xFF) + (int)(ctx->fetched_data & 0xFF) > 0x100;
c = (int)(cpu_read_reg(ctx->cur_inst->reg_1) & 0xFF) + (int)(ctx->fetched_data & 0xFF) >= 0x100;
}
cpu_set_reg(ctx->cur_inst->reg_1, val & 0xFFFF);
@ -349,7 +350,7 @@ static void proc_inc(cpu_context *ctx) {
return;
}
cpu_set_flags(ctx, val == 0, 0, (val & 0x0F), -1);
cpu_set_flags(ctx, val == 0, 0, (val & 0x0F) == 0, -1);
}
static void proc_dec(cpu_context *ctx) {
@ -432,8 +433,8 @@ static void proc_ld(cpu_context *ctx) {
u8 cflag = (cpu_read_reg(ctx->cur_inst->reg_2) & 0xFF) + (ctx->fetched_data & 0xFF) >= 0x100;
cpu_set_flags(ctx, 0, 0, hflag, cflag);
cpu_set_reg(ctx->cur_inst->reg_1, cpu_read_reg(ctx->cur_inst->reg_2) + (char)ctx->fetched_data);
return;
}
cpu_set_reg(ctx->cur_inst->reg_1, ctx->fetched_data);
}

View File

@ -73,14 +73,14 @@ u8 cpu_read_reg8(reg_type rt) {
void cpu_set_reg8(reg_type rt, u8 val) {
switch(rt) {
case RT_A: ctx.regs.a = val & 0XFF; break;
case RT_F: ctx.regs.f = val & 0XFF; break;
case RT_B: ctx.regs.b = val & 0XFF; break;
case RT_C: ctx.regs.c = val & 0XFF; break;
case RT_D: ctx.regs.d = val & 0XFF; break;
case RT_E: ctx.regs.e = val & 0XFF; break;
case RT_H: ctx.regs.h = val & 0XFF; break;
case RT_L: ctx.regs.l = val & 0XFF; break;
case RT_A: ctx.regs.a = val & 0xFF; break;
case RT_F: ctx.regs.f = val & 0xFF; break;
case RT_B: ctx.regs.b = val & 0xFF; break;
case RT_C: ctx.regs.c = val & 0xFF; break;
case RT_D: ctx.regs.d = val & 0xFF; break;
case RT_E: ctx.regs.e = val & 0xFF; break;
case RT_H: ctx.regs.h = val & 0xFF; break;
case RT_L: ctx.regs.l = val & 0xFF; break;
case RT_HL: bus_write(cpu_read_reg(RT_HL), val); break;

40
lib/dma.c Normal file
View File

@ -0,0 +1,40 @@
#include <dma.h>
#include <ppu.h>
#include <bus.h>
typedef struct {
bool active;
u8 byte;
u8 value;
u8 start_delay;
} dma_context;
static dma_context ctx;
void dma_start(u8 start) {
ctx.active = true;
ctx.byte = 0;
ctx.start_delay = 2;
ctx.value = start;
}
void dma_tick() {
if (!ctx.active) {
return;
}
if (ctx.start_delay) {
ctx.start_delay--;
return;
}
ppu_oam_write(ctx.byte, bus_read((ctx.value * 0x100) + ctx.byte));
ctx.byte++;
ctx.active = ctx.byte < 0xA0;
}
bool dma_transferring() {
return ctx.active;
}

View File

@ -3,6 +3,9 @@
#include <cart.h>
#include <cpu.h>
#include <ui.h>
#include <timer.h>
#include <dma.h>
#include <ppu.h>
#ifdef _WIN32
#include <windows.h>
@ -19,6 +22,8 @@ emu_context *emu_get_context() {
}
DWORD WINAPI cpu_run(void *p) {
ppu_init();
timer_init();
cpu_init();
ctx.running = true;
@ -36,7 +41,6 @@ DWORD WINAPI cpu_run(void *p) {
return -3;
}
ctx.ticks++;
}
return 0;
@ -80,9 +84,15 @@ int emu_run(int argc, char **argv) {
}
#endif
u32 prev_frame = 0;
while(!ctx.die) {
sleep_ms(1);
ui_handle_events();
if (prev_frame != ppu_get_context()->current_frame) {
ui_update();
}
prev_frame = ppu_get_context()->current_frame;
}
@ -90,5 +100,12 @@ int emu_run(int argc, char **argv) {
}
void emu_cycles(int cpu_cycles) {
//TODO
for (int i = 0; i < cpu_cycles; i++){
for(int n = 0; n < 4; n++){
ctx.ticks++;
timer_tick();
ppu_tick();
}
dma_tick();
}
}

View File

@ -18,7 +18,7 @@ instruction instructions[0x100] = {
[0x0C] = {IN_INC, AM_R, RT_C},
[0x0D] = {IN_DEC, AM_R, RT_C},
[0x0E] = {IN_LD, AM_R_D8, RT_C},
[0x0F] = {IN_RRA},
[0x0F] = {IN_RRCA},
//0x1X
[0x10] = {IN_STOP},
[0x11] = {IN_LD, AM_R_D16, RT_DE},
@ -258,7 +258,7 @@ instruction instructions[0x100] = {
[0xF6] = {IN_OR, AM_R_D8, RT_A},
[0xF7] = {IN_RST, AM_IMP, RT_NONE, RT_NONE, CT_NONE, 0x30},
[0xF8] = {IN_LD, AM_HL_SPR, RT_HL, RT_SP},
[0xF9] = {IN_LD, AM_R_R, RT_HL, RT_SP},
[0xF9] = {IN_LD, AM_R_R, RT_SP, RT_HL},
[0xFA] = {IN_LD, AM_R_A16, RT_A},
[0xFB] = {IN_EI},
[0xFE] = {IN_CP, AM_R_D8, RT_A},

View File

@ -9,7 +9,7 @@ void int_handle(cpu_context *ctx, u16 address) {
bool int_check(cpu_context *ctx, u16 address, interrupt_type t){
if(ctx->int_flags & t && ctx->ie_register & t) {
int_handle(ctx, address);
ctx->int_flags &= -t;
ctx->int_flags &= ~t;
ctx->halted = false;
ctx->int_master_enabled = false;
return true;

View File

@ -1,4 +1,8 @@
#include <io.h>
#include <timer.h>
#include <cpu.h>
#include <dma.h>
#include <lcd.h>
static char serial_data[2];
@ -11,6 +15,18 @@ u8 io_read(u16 address){
return serial_data[1];
}
if(BETWEEN(address, 0xFF04, 0xFF07)){
return timer_read(address);
}
if(BETWEEN(address, 0xFF40, 0xFF4B)){
return lcd_read(address);
}
if(address == 0xFF0F) {
return cpu_get_int_flags();
}
printf("UNSUPPORTED io_read(%04X)\n", address);
return 0;
}
@ -27,6 +43,21 @@ void io_write(u16 address, u8 value){
return;
}
if(BETWEEN(address, 0xFF04, 0xFF07)){
timer_write(address, value);
return;
}
if(address == 0xFF0F) {
cpu_set_int_flags(value);
return;
}
if(BETWEEN(address, 0xFF40, 0xFF4B)){
lcd_write(address, value);
return;
}
printf("UNSUPPORTED io_write(%04X)\n", address);
}

73
lib/lcd.c Normal file
View File

@ -0,0 +1,73 @@
#include <lcd.h>
#include <ppu.h>
#include <dma.h>
static lcd_context ctx;
static unsigned long colors_default[4] = {0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000};
lcd_context *lcd_get_context() {
return &ctx;
}
void lcd_init() {
ctx.lcdc = 0x91;
ctx.scroll_x = 0;
ctx.scroll_y = 0;
ctx.ly = 0;
ctx.ly_compare = 0;
ctx.bg_palette = 0xFC;
ctx.obj_palette[0] = 0xFF;
ctx.obj_palette[1] = 0xFF;
ctx.win_x = 0;
ctx.win_y = 0;
for(int i = 0; i < 4; i++) {
ctx.bg_colors[i] = colors_default[i];
ctx.sp1_colors[i] = colors_default[i];
ctx.sp2_colors[i] = colors_default[i];
}
}
u8 lcd_read(u16 address) {
u8 offset = (address - 0xFF40);
u8 *p = (u8 *)&ctx;
return p[offset];
}
void update_palette(u8 palette_data, u8 pal) {
u32 *p_colors = ctx.bg_colors;
switch(pal) {
case 1:
p_colors = ctx.sp1_colors;
break;
case 2:
p_colors = ctx.sp2_colors;
break;
}
p_colors[0] = colors_default[palette_data & 0b11];
p_colors[1] = colors_default[(palette_data >> 2) & 0b11];
p_colors[2] = colors_default[(palette_data >> 4) & 0b11];
p_colors[3] = colors_default[(palette_data >> 6) & 0b11];
}
void lcd_write(u16 address, u8 value) {
u8 offset = (address - 0xFF40);
u8 *p = (u8 *)&ctx;
p[offset] = value;
if(offset == 6) {
//offset FF64
dma_start(value);
}
if(offset == 7) {
update_palette(value, 0);
} else if(offset == 8) {
update_palette(value & 0b11111100, 1);
} else if(offset == 9) {
update_palette(value & 0b11111100, 2);
}
}

View File

@ -1,9 +1,67 @@
#include <ppu.h>
#include <lcd.h>
#include <string.h>
#include <ppu_sm.h>
static ppu_context ctx;
ppu_context *ppu_get_context() {
return &ctx;
}
void ppu_init() {
ctx.current_frame = 0;
ctx.line_ticks = 0;
ctx.video_buffer = malloc(YRES * XRES * sizeof(u32));
lcd_init();
LCDS_MODE_SET(MODE_OAM);
memset(ctx.oam_ram, 0, sizeof(ctx.oam_ram));
memset(ctx.video_buffer, 0, YRES*XRES*sizeof(u32));
}
void ppu_tick() {
ctx.line_ticks++;
switch(LCDS_MODE) {
case MODE_OAM:
ppu_mode_oam();
break;
case MODE_XFER:
ppu_mode_xfer();
break;
case MODE_VBLANK:
ppu_mode_vblank();
break;
case MODE_HBLANK:
ppu_mode_hblank();
break;
}
}
void ppu_oam_write(u16 address, u8 value) {
if (address >= 0xFE00) {
address -= 0xFE00;
}
u8 *p = (u8 *)ctx.oam_ram;
p[address] = value;
}
u8 ppu_oam_read(u16 address){
if (address > 0xFE00) {
address -= 0xFE00;
}
u8 *p = (u8 *)ctx.oam_ram;
return p[address];
}
void ppu_vram_write(u16 address, u8 value){
ctx.vram[address - 0x8000] = value;
}
u8 ppu_vram_read(u16 address){
return ctx.vram[address - 0x8000];
}

90
lib/ppu_sm.c Normal file
View File

@ -0,0 +1,90 @@
#include <ppu.h>
#include <lcd.h>
#include <cpu.h>
#include <interrupts.h>
void increment_ly() {
lcd_get_context()->ly++;
if(lcd_get_context()->ly == lcd_get_context()->ly_compare) {
LCDS_LYC_SET(1);
if(LCDS_STAT_INT(SS_LYC)) {
cpu_request_interrupt(IT_LCD_STAT);
}
} else {
LCDS_LYC_SET(0);
}
}
void ppu_mode_oam() {
if(ppu_get_context()->line_ticks >= 80) {
LCDS_MODE_SET(MODE_XFER);
}
}
void ppu_mode_xfer() {
if(ppu_get_context()->line_ticks >= 80 + 172) {
LCDS_MODE_SET(MODE_HBLANK);
}
}
void ppu_mode_vblank() {
if(ppu_get_context()->line_ticks >= TICKS_PER_LINE) {
increment_ly();
if(lcd_get_context()->ly >= LINES_PER_FRAME) {
LCDS_MODE_SET(MODE_OAM);
lcd_get_context()->ly = 0;
}
ppu_get_context()->line_ticks = 0;
}
}
static u32 target_frame_time = 1000/60;
static long prev_frame_time = 0;
static long start_timer = 0;
static long frame_count = 0;
void ppu_mode_hblank() {
if (ppu_get_context()->line_ticks >= TICKS_PER_LINE) {
increment_ly();
if(lcd_get_context()->ly >= YRES) {
LCDS_MODE_SET(MODE_VBLANK);
cpu_request_interrupt(IT_VBLANK);
if(LCDS_STAT_INT(SS_VBLANK)) {
cpu_request_interrupt(IT_LCD_STAT);
}
ppu_get_context()->current_frame++;
//calc FPS...
u32 end = get_ticks();
u32 frame_time = end - prev_frame_time;
if(frame_time < target_frame_time) {
delay((target_frame_time - frame_time));
}
if (end - start_timer >= 1000) {
u32 fps = frame_count;
start_timer = end;
frame_count = 0;
printf("FPS: %ld\n", fps);
}
frame_count++;
prev_frame_time = get_ticks();
} else {
LCDS_MODE_SET(MODE_OAM);
}
ppu_get_context()->line_ticks = 0;
}
}

View File

@ -1,10 +1,88 @@
#include <timer.h>
#include <interrupts.h>
static timer_context ctx = {0};
void timer_init() {
ctx.div = 0XAC00;
}
void timer_tick() {
u16 prev_div = ctx.div;
ctx.div++;
bool timer_update = false;
switch(ctx.tac & 0b11) {
case 0b00:
timer_update = (prev_div & (1 << 9)) && (!(ctx.div & (1 << 9)));
break;
case 0b01:
timer_update = (prev_div & (1 << 3)) && (!(ctx.div & (1 << 3)));
break;
case 0b10:
timer_update = (prev_div & (1 << 5)) && (!(ctx.div & (1 << 5)));
break;
case 0b11:
timer_update = (prev_div & (1 << 7)) && (!(ctx.div & (1 << 7)));
break;
}
if(timer_update && ctx.tac & (1 << 2)) {
ctx.tima++;
if(ctx.tima == 0xFF){
ctx.tima = ctx.tma;
cpu_request_interrupt(IT_TIMER);
}
}
}
void timer_write(u16 address, u8 value){
switch(address) {
case 0xFF04:
//DIV
ctx.div = 0;
break;
case 0xFF05:
//TIMA
ctx.tima = value;
break;
case 0xFF06:
//TMA
ctx.tma = value;
break;
case 0xFF07:
//TAC
ctx.tac = value;
break;
}
}
u8 timer_read(u16 address) {
switch(address) {
case 0xFF04:
//DIV
return ctx.div >> 8;
case 0xFF05:
//TIMA
return ctx.tima;
case 0xFF06:
//TMA
return ctx.tma;
case 0xFF07:
//TAC
return ctx.tac;
}
}
timer_context *timer_get_context() {
return &ctx;
}

View File

@ -9,6 +9,13 @@ SDL_Renderer *sdlRenderer;
SDL_Texture *sdlTexture;
SDL_Surface *screen;
SDL_Window *sdlDebugWindow;
SDL_Renderer *sdlDebugRenderer;
SDL_Texture *sdlDebugTexture;
SDL_Surface *debugScreen;
static int scale = 4;
void ui_init(){
SDL_Init(SDL_INIT_VIDEO);
printf("SDL INIT\n");
@ -16,6 +23,79 @@ void ui_init(){
printf("TTF INIT\n");
SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, 0, &sdlWindow, &sdlRenderer);
SDL_CreateWindowAndRenderer(16 * 8 * scale, 32 * 8 * scale, 0, &sdlDebugWindow, &sdlDebugRenderer);
debugScreen = SDL_CreateRGBSurface(0, (16 * 8 * scale) + (16 * scale),
(32 * 8 * scale) + (64 * scale), 32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
sdlDebugTexture = SDL_CreateTexture(sdlDebugRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
(16 * 8 * scale) + (16 * scale),
(32 * 8 * scale) + (64 * scale));
int x, y;
SDL_GetWindowPosition(sdlWindow, &x, &y);
SDL_SetWindowPosition(sdlDebugWindow, x+SCREEN_WIDTH, y);
}
static unsigned long tile_colors[4] = {0xFFFFFFFF, 0xFFAAAAAA, 0xFF555555, 0xFF000000};
void display_tile(SDL_Surface *surface, u16 startLocation, u16 tileNum, int x, int y) {
SDL_Rect rc;
for(int tileY = 0; tileY < 16; tileY += 2) {
u8 b1 = bus_read(startLocation+(tileNum * 16) + tileY);
u8 b2 = bus_read(startLocation+(tileNum * 16) + tileY + 1);
for (int bit=7; bit >= 0; bit--) {
u8 hi = !!(b1 & (1 << bit)) << 1;
u8 lo = !!(b2 & (1 << bit));
u8 color = hi | lo;
rc.x = x + ((7 - bit) * scale);
rc.y = y + (tileY/2 * scale);
rc.w = scale;
rc.h = scale;
SDL_FillRect(surface, &rc, tile_colors[color]);
}
}
}
void update_debug_window() {
int xDraw = 0;
int yDraw = 0;
int tileNum = 0;
SDL_Rect rc;
rc.x = 0;
rc.y = 0;
rc.w = debugScreen->w;
rc.h = debugScreen->h;
SDL_FillRect(debugScreen, &rc, 0xFF111111);
u16 addr = 0x8000;
//384 tiles, 24 x 16
for(int y = 0; y < 24; y++) {
for(int x = 0; x < 16; x++) {
display_tile(debugScreen, addr, tileNum, xDraw + (x * scale), yDraw + (y * scale));
xDraw += (8 * scale);
tileNum++;
}
yDraw += (8 * scale);
xDraw = 0;
}
SDL_UpdateTexture(sdlDebugTexture, NULL, debugScreen->pixels, debugScreen->pitch);
SDL_RenderClear(sdlDebugRenderer);
SDL_RenderCopy(sdlDebugRenderer, sdlDebugTexture, NULL, NULL);
SDL_RenderPresent(sdlDebugRenderer);
}
void ui_update() {
update_debug_window();
}
void ui_handle_events(){
@ -29,4 +109,8 @@ void ui_handle_events(){
void delay(u32 ms) {
SDL_Delay(ms);
}
u32 get_ticks() {
return SDL_GetTicks();
}

View File

@ -7,5 +7,6 @@ db "My Test ROM ", 0 ; title
ds $150 - @, 0
EntryPoint:
xor a
ld a,0x80
rlc a
halt

Binary file not shown.