| @@ -1,6 +1,6 @@ | |||||
| CC = gcc | CC = gcc | ||||
| LD = $(CC) | LD = $(CC) | ||||
| CFLAGS = -Wall -Werror -Wshadow -I.. -g -DE6502_DEBUG | |||||
| CFLAGS = -Wall -Werror -Wshadow -I.. -g #-DE6502_DEBUG | |||||
| LDFLAGS = | LDFLAGS = | ||||
| OBJDIR = obj | OBJDIR = obj | ||||
| @@ -11,9 +11,9 @@ BINDIR = bin | |||||
| # nese | # nese | ||||
| TARGET_1 = nese | TARGET_1 = nese | ||||
| LDLIBS_1 = | |||||
| LDLIBS_1 = -lSDL | |||||
| SRC_SRCS_1 = nese.c ines.c nes.c ppu.c | |||||
| SRC_SRCS_1 = nese.c ines.c nes.c ppu.c sdl_render.c | |||||
| EXT_SRCS_1 = e6502/e6502.c | EXT_SRCS_1 = e6502/e6502.c | ||||
| @@ -2,7 +2,7 @@ | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include "ines.h" | #include "ines.h" | ||||
| #include "render.h" | |||||
| void e6502_print_registers(const e6502_Registers* regs, | void e6502_print_registers(const e6502_Registers* regs, | ||||
| @@ -33,6 +33,7 @@ void e6502_dump_stack(e6502_Core* core, FILE* file) { | |||||
| } | } | ||||
| extern nes_Renderer sdl_renderer; | |||||
| static nes sys = {0}; | static nes sys = {0}; | ||||
| @@ -44,11 +45,17 @@ int main(int argc, char* argv[]) { | |||||
| status = ines_load(&sys.cart.rom, stdin); | status = ines_load(&sys.cart.rom, stdin); | ||||
| nes_Renderer* rend = &sdl_renderer; | |||||
| if (status == 0) { | |||||
| status = nes_render_init(rend); | |||||
| } | |||||
| if (status == 0) { | if (status == 0) { | ||||
| nes_init(&sys); | nes_init(&sys); | ||||
| nes_reset(&sys); | nes_reset(&sys); | ||||
| int total_cycles = 0; | int total_cycles = 0; | ||||
| int last_frame_rendered = -1; | |||||
| for (int i = 0; i < n_loops && status == 0; ++i) { | for (int i = 0; i < n_loops && status == 0; ++i) { | ||||
| int run = 0; | int run = 0; | ||||
| status = nes_run(&sys, 1000, &run); | status = nes_run(&sys, 1000, &run); | ||||
| @@ -61,6 +68,11 @@ int main(int argc, char* argv[]) { | |||||
| us_run, run, | us_run, run, | ||||
| status == 0 ? "OK" : "Halted"); | status == 0 ? "OK" : "Halted"); | ||||
| */ | */ | ||||
| if ( status == 0 && | |||||
| sys.ppu.frame != last_frame_rendered) { | |||||
| status = nes_render(rend, &sys.ppu); | |||||
| last_frame_rendered = sys.ppu.frame; | |||||
| } | |||||
| } | } | ||||
| float ms_run = ( total_cycles * 1000. * | float ms_run = ( total_cycles * 1000. * | ||||
| @@ -49,13 +49,13 @@ uint8_t nes_ppu_read(nes_ppu* ppu, uint16_t addr) { | |||||
| 32 : 1; | 32 : 1; | ||||
| } | } | ||||
| fprintf(stderr, "PPU: <-R $%04x %02x\n", addr, val); | |||||
| // fprintf(stdout, "PPU: <-R $%04x %02x\n", addr, val); | |||||
| return val; | return val; | ||||
| } | } | ||||
| void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { | void nes_ppu_write(nes_ppu* ppu, uint16_t addr, uint8_t val) { | ||||
| fprintf(stderr, "PPU: W-> $%04x %02x\n", addr, val); | |||||
| // fprintf(stdout, "PPU: W-> $%04x %02x\n", addr, val); | |||||
| if (ppu_reg_ctrl == addr) { | if (ppu_reg_ctrl == addr) { | ||||
| ppu->control = val; | ppu->control = val; | ||||
| @@ -10,6 +10,9 @@ | |||||
| #define nes_ppu_postrender (1U) | #define nes_ppu_postrender (1U) | ||||
| #define nes_ppu_vblank (20U) | #define nes_ppu_vblank (20U) | ||||
| #define nes_ppu_render_w (320U) | |||||
| #define nes_ppu_render_h nes_ppu_height | |||||
| #define nes_ppu_mem_size (0x4000U) | #define nes_ppu_mem_size (0x4000U) | ||||
| #define nes_ppu_mem_pal_start (0x3F00U) | #define nes_ppu_mem_pal_start (0x3F00U) | ||||
| #define nes_ppu_mem_pal_size (0x0020U) | #define nes_ppu_mem_pal_size (0x0020U) | ||||
| @@ -0,0 +1,27 @@ | |||||
| #ifndef NES_RENDER_H_ | |||||
| #define NES_RENDER_H_ | |||||
| #include "ppu.h" | |||||
| typedef struct nes_Renderer_t { | |||||
| int (*init)(struct nes_Renderer_t*); | |||||
| void (*done)(struct nes_Renderer_t*); | |||||
| int (*render)(struct nes_Renderer_t*, nes_ppu*); | |||||
| void* data; | |||||
| } nes_Renderer; | |||||
| static inline int nes_render_init(nes_Renderer* rend) { | |||||
| return rend->init(rend); | |||||
| } | |||||
| static inline void nes_render_done(nes_Renderer* rend) { | |||||
| rend->done(rend); | |||||
| } | |||||
| static inline int nes_render(nes_Renderer* rend, nes_ppu* ppu) { | |||||
| return rend->render(rend, ppu); | |||||
| } | |||||
| #endif // NES_RENDER_H_ | |||||
| @@ -0,0 +1,109 @@ | |||||
| #include <SDL/SDL.h> | |||||
| #include "render.h" | |||||
| #include "ppu.h" | |||||
| typedef struct { | |||||
| uint8_t r; | |||||
| uint8_t g; | |||||
| uint8_t b; | |||||
| } __attribute__ (( packed )) sPal; | |||||
| static struct sPal nes_palette[64] = { | |||||
| {0x80,0x80,0x80}, {0x00,0x00,0xBB}, {0x37,0x00,0xBF}, {0x84,0x00,0xA6}, | |||||
| {0xBB,0x00,0x6A}, {0xB7,0x00,0x1E}, {0xB3,0x00,0x00}, {0x91,0x26,0x00}, | |||||
| {0x7B,0x2B,0x00}, {0x00,0x3E,0x00}, {0x00,0x48,0x0D}, {0x00,0x3C,0x22}, | |||||
| {0x00,0x2F,0x66}, {0x00,0x00,0x00}, {0x05,0x05,0x05}, {0x05,0x05,0x05}, | |||||
| {0xC8,0xC8,0xC8}, {0x00,0x59,0xFF}, {0x44,0x3C,0xFF}, {0xB7,0x33,0xCC}, | |||||
| {0xFF,0x33,0xAA}, {0xFF,0x37,0x5E}, {0xFF,0x37,0x1A}, {0xD5,0x4B,0x00}, | |||||
| {0xC4,0x62,0x00}, {0x3C,0x7B,0x00}, {0x1E,0x84,0x15}, {0x00,0x95,0x66}, | |||||
| {0x00,0x84,0xC4}, {0x11,0x11,0x11}, {0x09,0x09,0x09}, {0x09,0x09,0x09}, | |||||
| {0xFF,0xFF,0xFF}, {0x00,0x95,0xFF}, {0x6F,0x84,0xFF}, {0xD5,0x6F,0xFF}, | |||||
| {0xFF,0x77,0xCC}, {0xFF,0x6F,0x99}, {0xFF,0x7B,0x59}, {0xFF,0x91,0x5F}, | |||||
| {0xFF,0xA2,0x33}, {0xA6,0xBF,0x00}, {0x51,0xD9,0x6A}, {0x4D,0xD5,0xAE}, | |||||
| {0x00,0xD9,0xFF}, {0x66,0x66,0x66}, {0x0D,0x0D,0x0D}, {0x0D,0x0D,0x0D}, | |||||
| {0xFF,0xFF,0xFF}, {0x84,0xBF,0xFF}, {0xBB,0xBB,0xFF}, {0xD0,0xBB,0xFF}, | |||||
| {0xFF,0xBF,0xEA}, {0xFF,0xBF,0xCC}, {0xFF,0xC4,0xB7}, {0xFF,0xCC,0xAE}, | |||||
| {0xFF,0xD9,0xA2}, {0xCC,0xE1,0x99}, {0xAE,0xEE,0xB7}, {0xAA,0xF7,0xEE}, | |||||
| {0xB3,0xEE,0xFF}, {0xDD,0xDD,0xDD}, {0x11,0x11,0x11}, {0x11,0x11,0x11} | |||||
| }; | |||||
| typedef struct { | |||||
| SDL_Surface* surface; | |||||
| uint8_t buffer[3 * nes_ppu_render_w * nes_ppu_render_h]; | |||||
| } sdl_render_data; | |||||
| static sdl_render_data the_render_data = {0}; | |||||
| static int filter(const SDL_Event* event) { | |||||
| return event->type == SDL_QUIT; | |||||
| } | |||||
| static int sdl_render_init(nes_Renderer* rend) { | |||||
| int status = SDL_Init(SDL_INIT_VIDEO); | |||||
| if (0 != status) { | |||||
| fprintf(stderr, "SDL: Failed to initialize\n"); | |||||
| } else { | |||||
| SDL_WM_SetCaption("NESe", "NESe"); | |||||
| if (NULL == SDL_SetVideoMode(nes_ppu_render_w, | |||||
| nes_ppu_render_h, | |||||
| 24, SDL_HWSURFACE)) { | |||||
| fprintf(stderr, "SDL: Failed to set video mode\n"); | |||||
| status = -1; | |||||
| } | |||||
| } | |||||
| if (0 == status) { | |||||
| the_render_data.surface = SDL_CreateRGBSurfaceFrom( | |||||
| the_render_data.buffer, nes_ppu_render_w, | |||||
| nes_ppu_render_h, 24, nes_ppu_render_w * 3, | |||||
| 0xFFU, 0xFF00U, 0xFF0000U, 0 | |||||
| ); | |||||
| SDL_SetEventFilter(filter); | |||||
| rend->data = &the_render_data; | |||||
| } | |||||
| return status; | |||||
| } | |||||
| static void sdl_render_done(nes_Renderer* rend) { | |||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | |||||
| if (NULL != data->surface) { | |||||
| SDL_FreeSurface(data->surface); | |||||
| data->surface = NULL; | |||||
| } | |||||
| SDL_Quit(); | |||||
| } | |||||
| static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { | |||||
| sdl_render_data* data = (sdl_render_data*)rend->data; | |||||
| // TODO | |||||
| SDL_Surface* screen = SDL_GetVideoSurface(); | |||||
| if (0 == SDL_BlitSurface(data->surface, NULL, screen, NULL)) { | |||||
| SDL_UpdateRect(screen, 0, 0, 0, 0); | |||||
| } | |||||
| SDL_Event event = {0}; | |||||
| return (1 == SDL_PollEvent(&event) && event.type == SDL_QUIT) ? | |||||
| -1 : 0; | |||||
| } | |||||
| nes_Renderer sdl_renderer = { | |||||
| .init = sdl_render_init, | |||||
| .done = sdl_render_done, | |||||
| .render = sdl_render, | |||||
| }; | |||||