Add tile debugger to main view
This commit is contained in:
parent
77b37fc3b6
commit
1add13dd20
|
@ -11,6 +11,7 @@ only tested on Linux. Here is how to run the project:
|
||||||
## Controls
|
## Controls
|
||||||
- `p`: Pauses the emulation
|
- `p`: Pauses the emulation
|
||||||
- `o`: Go to the next palette in the pattern viewer
|
- `o`: Go to the next palette in the pattern viewer
|
||||||
|
- `t`: Show tile IDs
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
- GCC compiler
|
- GCC compiler
|
||||||
|
|
|
@ -89,7 +89,12 @@ int gui_input() {
|
||||||
system_toggle_pause();
|
system_toggle_pause();
|
||||||
} else {
|
} else {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
if (event.key.keysym.sym == SDLK_t) {
|
||||||
|
PPUDebugFlags *ppu_debug = &ppu_get_state()->debug;
|
||||||
|
ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger;
|
||||||
|
} else {
|
||||||
pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym);
|
pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,20 +34,8 @@ void main_window_render_delay(SDL_Renderer *renderer) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void main_window_render(NesMainWindow *window, PPUPixel *pixels) {
|
void main_window_render(NesMainWindow *window, pixel *pixels) {
|
||||||
SDL_RenderClear(window->sdl_context.renderer);
|
SDL_RenderClear(window->sdl_context.renderer);
|
||||||
|
|
||||||
// unsigned int frame_buffer[240 * 256];
|
|
||||||
// for (int i = 0; i < 240 * 256; i++) {
|
|
||||||
// PPUPixel pixel = pixels[i];
|
|
||||||
//
|
|
||||||
// unsigned int *data = &frame_buffer[i];
|
|
||||||
// *data = 0xff000000;
|
|
||||||
// *data |= pixel.r << 16;
|
|
||||||
// *data |= pixel.g << 8;
|
|
||||||
// *data |= pixel.b;
|
|
||||||
// }
|
|
||||||
|
|
||||||
SDL_UpdateTexture(window->texture, NULL, pixels, 240 * sizeof(unsigned int));
|
SDL_UpdateTexture(window->texture, NULL, pixels, 240 * sizeof(unsigned int));
|
||||||
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
|
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#define MAIN_WINDOW_WIDTH 256
|
#define MAIN_WINDOW_WIDTH 256
|
||||||
#define MAIN_WINDOW_HEIGHT 240
|
#define MAIN_WINDOW_HEIGHT 240
|
||||||
#define MAIN_WINDOW_SCALE 2
|
#define MAIN_WINDOW_SCALE 3
|
||||||
|
|
||||||
typedef struct nes_main_window {
|
typedef struct nes_main_window {
|
||||||
NesSdlContext sdl_context;
|
NesSdlContext sdl_context;
|
||||||
|
@ -22,7 +22,7 @@ typedef struct nes_main_window {
|
||||||
void main_window_init(NesMainWindow *window);
|
void main_window_init(NesMainWindow *window);
|
||||||
void main_window_uninit(NesMainWindow *window);
|
void main_window_uninit(NesMainWindow *window);
|
||||||
|
|
||||||
void main_window_render(NesMainWindow *window, PPUPixel* pixels);
|
void main_window_render(NesMainWindow *window, pixel* pixels);
|
||||||
void main_window_present(NesMainWindow *window);
|
void main_window_present(NesMainWindow *window);
|
||||||
|
|
||||||
#endif //NES_EMULATOR_MAIN_WINDOW_H
|
#endif //NES_EMULATOR_MAIN_WINDOW_H
|
||||||
|
|
|
@ -69,46 +69,42 @@ typedef struct ppu_tile_fetch {
|
||||||
byte pattern_table_tile_high;
|
byte pattern_table_tile_high;
|
||||||
} PPUTileFetch;
|
} PPUTileFetch;
|
||||||
|
|
||||||
//typedef struct ppu_pixel {
|
#if DEBUG
|
||||||
// byte r;
|
typedef union {
|
||||||
// byte g;
|
struct {
|
||||||
// byte b;
|
byte tile_debugger: 1;
|
||||||
//} PPUPixel;
|
} flags;
|
||||||
typedef unsigned int PPUPixel;
|
byte flags_byte;
|
||||||
|
} PPUDebugFlags;
|
||||||
typedef struct ppu_tile_queue {
|
#endif
|
||||||
PPUTileFetch first_fetch;
|
|
||||||
PPUTileFetch second_fetch;
|
|
||||||
PPUTileFetch displayed_fetch;
|
|
||||||
} PPUTileQueue;
|
|
||||||
|
|
||||||
typedef struct ppu {
|
typedef struct ppu {
|
||||||
PPUMemory memory;
|
PPUMemory memory;
|
||||||
PPUPixel pixels[256 * 240];
|
pixel pixels[256 * 240];
|
||||||
|
|
||||||
byte registers[8];
|
byte registers[8];
|
||||||
byte oam_dma_register;
|
byte oam_dma_register;
|
||||||
byte oam[PPU_OAM_SIZE];
|
byte oam[PPU_OAM_SIZE];
|
||||||
bool odd_frame;
|
bool odd_frame;
|
||||||
address v;
|
|
||||||
address t;
|
|
||||||
byte x;
|
|
||||||
bool w;
|
|
||||||
|
|
||||||
byte x_scroll;
|
byte x_scroll;
|
||||||
byte fine_x_scroll;
|
byte fine_x_scroll;
|
||||||
byte y_scroll;
|
byte y_scroll;
|
||||||
byte ppu_addr_increment;
|
byte x;
|
||||||
|
bool w;
|
||||||
|
|
||||||
|
byte ppu_addr_increment;
|
||||||
address ppu_address;
|
address ppu_address;
|
||||||
address temp_ppu_addr;
|
address temp_ppu_addr;
|
||||||
address bg_pattern_table_addr;
|
address bg_pattern_table_addr;
|
||||||
|
|
||||||
PPUTileFetch fetch;
|
PPUTileFetch fetch;
|
||||||
PPUTileQueue tile_queue;
|
|
||||||
unsigned long frame;
|
unsigned long frame;
|
||||||
unsigned int scanline;
|
unsigned int scanline;
|
||||||
unsigned int cycle;
|
unsigned int cycle;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
PPUDebugFlags debug;
|
||||||
|
#endif
|
||||||
} PPU;
|
} PPU;
|
||||||
|
|
||||||
PPU *ppu_get_state();
|
PPU *ppu_get_state();
|
||||||
|
@ -137,17 +133,6 @@ void ppu_cycle();
|
||||||
*/
|
*/
|
||||||
bool ppu_read_flag(size_t reg, byte mask);
|
bool ppu_read_flag(size_t reg, byte mask);
|
||||||
|
|
||||||
/**
|
|
||||||
* Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false).
|
|
||||||
* Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false).
|
|
||||||
*
|
|
||||||
* @param reg The register index
|
|
||||||
* @param mask The value mask
|
|
||||||
*/
|
|
||||||
//void ppu_sig_read_register(byte reg);
|
|
||||||
//
|
|
||||||
//void ppu_sig_write_register(byte reg);
|
|
||||||
|
|
||||||
byte ppu_read_reg(byte reg);
|
byte ppu_read_reg(byte reg);
|
||||||
|
|
||||||
void ppu_write_reg(byte reg, byte data);
|
void ppu_write_reg(byte reg, byte data);
|
||||||
|
|
|
@ -5,14 +5,9 @@
|
||||||
#ifndef NESEMULATOR_TYPES_H
|
#ifndef NESEMULATOR_TYPES_H
|
||||||
#define NESEMULATOR_TYPES_H
|
#define NESEMULATOR_TYPES_H
|
||||||
|
|
||||||
//#define RAM_SIZE 0xffff
|
|
||||||
//#define VRAM_SIZE 0x4000
|
|
||||||
|
|
||||||
typedef unsigned char byte;
|
typedef unsigned char byte;
|
||||||
typedef unsigned short address;
|
typedef unsigned short address;
|
||||||
typedef unsigned short word;
|
typedef unsigned short word;
|
||||||
|
typedef unsigned int pixel;
|
||||||
//typedef byte ram[RAM_SIZE];
|
|
||||||
//typedef byte vram[VRAM_SIZE];
|
|
||||||
|
|
||||||
#endif //NESEMULATOR_TYPES_H
|
#endif //NESEMULATOR_TYPES_H
|
||||||
|
|
23
ppu/ppu.c
23
ppu/ppu.c
|
@ -20,6 +20,7 @@
|
||||||
#include "../cpu/cpu.h"
|
#include "../cpu/cpu.h"
|
||||||
#include "../include/rom.h"
|
#include "../include/rom.h"
|
||||||
#include "colors.h"
|
#include "colors.h"
|
||||||
|
#include "tile_debugger.h"
|
||||||
|
|
||||||
#define PPU_VISIBLE_FRAME_END 240
|
#define PPU_VISIBLE_FRAME_END 240
|
||||||
#define PPU_POST_RENDER_LINE_START PPU_VISIBLE_FRAME_END
|
#define PPU_POST_RENDER_LINE_START PPU_VISIBLE_FRAME_END
|
||||||
|
@ -31,7 +32,7 @@
|
||||||
#define NAMETABLE_TILE_SIZE 8
|
#define NAMETABLE_TILE_SIZE 8
|
||||||
|
|
||||||
PPU ppu_state;
|
PPU ppu_state;
|
||||||
PPUPixel color_list[0x40] = COLOR_LIST;
|
pixel color_list[0x40] = COLOR_LIST;
|
||||||
|
|
||||||
void ppu_init() {
|
void ppu_init() {
|
||||||
memset(&ppu_state, 0, sizeof(PPU));
|
memset(&ppu_state, 0, sizeof(PPU));
|
||||||
|
@ -95,7 +96,7 @@ static inline byte ppu_pixel_get_palette(byte attribute) {
|
||||||
return palette & 0b11;
|
return palette & 0b11;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ppu_pixel_set_color(PPUPixel *pixel, byte pt_low, byte pt_high, byte attribute) {
|
static inline void ppu_pixel_set_color(pixel *pixel, byte pt_low, byte pt_high, byte attribute) {
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
byte pixel_offset = 8 - i - 1;
|
byte pixel_offset = 8 - i - 1;
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ static inline void ppu_pixel_set_color(PPUPixel *pixel, byte pt_low, byte pt_hig
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppu_draw_tile() {
|
void ppu_draw_tile() {
|
||||||
PPUTileFetch fetch = ppu_state.tile_queue.displayed_fetch;
|
PPUTileFetch fetch = ppu_state.fetch;
|
||||||
|
|
||||||
unsigned int y = ppu_state.scanline;
|
unsigned int y = ppu_state.scanline;
|
||||||
unsigned int x = ppu_state.cycle;
|
unsigned int x = ppu_state.cycle;
|
||||||
|
@ -129,15 +130,20 @@ void ppu_draw_tile() {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
unsigned int pixel_index = y * PPU_VISIBLE_FRAME_END + x;
|
unsigned int pixel_index = y * PPU_VISIBLE_FRAME_END + x;
|
||||||
PPUPixel *pixel = &ppu_state.pixels[pixel_index];
|
pixel *pixel = &ppu_state.pixels[pixel_index];
|
||||||
ppu_pixel_set_color(pixel, fetch.pattern_table_tile_low, fetch.pattern_table_tile_high, fetch.attribute_table);
|
ppu_pixel_set_color(pixel, fetch.pattern_table_tile_low, fetch.pattern_table_tile_high, fetch.attribute_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte ppu_get_pattern(byte tile_index, byte high) {
|
byte ppu_get_pattern(byte tile_index, byte high) {
|
||||||
return tile_index;
|
#if DEBUG
|
||||||
// byte tile_row_index = (ppu_state.scanline + ppu_state.y_scroll) % 8;
|
if (ppu_state.debug.flags.tile_debugger) {
|
||||||
// address pattern_addr = ppu_state.bg_pattern_table_addr | tile_index << 4 | high << 3 | tile_row_index;
|
return tile_debugger_encode_number_as_pattern(tile_index, ppu_state.scanline % 8);
|
||||||
// return ppu_read(pattern_addr);
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
byte tile_row_index = (ppu_state.scanline + ppu_state.y_scroll) % 8;
|
||||||
|
address pattern_addr = ppu_state.bg_pattern_table_addr | tile_index << 4 | high << 3 | tile_row_index;
|
||||||
|
return ppu_read(pattern_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppu_fetch_tile(bool render) {
|
void ppu_fetch_tile(bool render) {
|
||||||
|
@ -169,7 +175,6 @@ void ppu_fetch_tile(bool render) {
|
||||||
ppu_state.fetch.pattern_table_tile_low = ppu_get_pattern(ppu_state.fetch.nametable, 0);
|
ppu_state.fetch.pattern_table_tile_low = ppu_get_pattern(ppu_state.fetch.nametable, 0);
|
||||||
} else if (fetch_cycle == 7) {
|
} else if (fetch_cycle == 7) {
|
||||||
ppu_state.fetch.pattern_table_tile_high = ppu_get_pattern(ppu_state.fetch.nametable, 1);
|
ppu_state.fetch.pattern_table_tile_high = ppu_get_pattern(ppu_state.fetch.nametable, 1);
|
||||||
ppu_state.tile_queue.displayed_fetch = ppu_state.fetch;
|
|
||||||
|
|
||||||
if (render) {
|
if (render) {
|
||||||
ppu_draw_tile();
|
ppu_draw_tile();
|
||||||
|
|
|
@ -4,7 +4,34 @@
|
||||||
|
|
||||||
#include "tile_debugger.h"
|
#include "tile_debugger.h"
|
||||||
|
|
||||||
byte tile_encode_number(byte num) {
|
// Contains the patterns of every hexadecimal digit encoded as pattern data.
|
||||||
// 8 segment display
|
// The first dimension of the table represents a row in a tile.
|
||||||
|
byte hex_pattern_table[5][0x10] = {
|
||||||
|
{0b111, 0b001, 0b111, 0b111, 0b101, 0b111, 0b111, 0b111, 0b111, 0b111, 0b010, 0b111, 0b111, 0b110, 0b111, 0b111},
|
||||||
|
{0b101, 0b001, 0b001, 0b001, 0b101, 0b100, 0b100, 0b001, 0b101, 0b101, 0b101, 0b101, 0b100, 0b101, 0b100, 0b100},
|
||||||
|
{0b101, 0b001, 0b111, 0b111, 0b111, 0b111, 0b111, 0b010, 0b111, 0b111, 0b111, 0b110, 0b100, 0b101, 0b111, 0b110},
|
||||||
|
{0b101, 0b001, 0b100, 0b001, 0b001, 0b001, 0b101, 0b010, 0b101, 0b001, 0b101, 0b101, 0b100, 0b101, 0b100, 0b100},
|
||||||
|
{0b111, 0b001, 0b111, 0b111, 0b001, 0b111, 0b111, 0b010, 0b111, 0b001, 0b101, 0b111, 0b111, 0b110, 0b111, 0b100},
|
||||||
|
};
|
||||||
|
|
||||||
|
byte tile_debugger_encode_number_as_pattern(byte num, byte tile_fine_y) {
|
||||||
|
if (tile_fine_y == 6) {
|
||||||
|
return 0x7f; // On row 6, a full line is drawn to make it easier to separate tiles
|
||||||
|
} else if (tile_fine_y == 5 || tile_fine_y == 7) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first digit of the hex is encoded
|
||||||
|
byte remaining = num % 0x10;
|
||||||
|
byte encoded = hex_pattern_table[tile_fine_y][remaining];
|
||||||
|
|
||||||
|
if (num > 0xf) {
|
||||||
|
// If the number is greater than 0xF, we need a second digit
|
||||||
|
// We encode it, then add it 4 pixels to the left of the already encoded digit
|
||||||
|
byte tenths = num / 0x10;
|
||||||
|
byte tenths_encoded = hex_pattern_table[tile_fine_y][tenths];
|
||||||
|
encoded = (tenths_encoded << 4) | encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encoded;
|
||||||
}
|
}
|
|
@ -7,4 +7,13 @@
|
||||||
|
|
||||||
#include "../include/types.h"
|
#include "../include/types.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a number as pattern data.
|
||||||
|
*
|
||||||
|
* @param num The number to encode
|
||||||
|
* @param tile_fine_y The row of the tile
|
||||||
|
* @return Pattern data representing the row of the encoded number
|
||||||
|
*/
|
||||||
|
byte tile_debugger_encode_number_as_pattern(byte num, byte tile_fine_y);
|
||||||
|
|
||||||
#endif //NES_EMULATOR_TILE_DEBUGGER_H
|
#endif //NES_EMULATOR_TILE_DEBUGGER_H
|
||||||
|
|
Loading…
Reference in New Issue