changeset 1631:c4ba3177b72d

WIP new VDP plane debug view and support for detached VDP debug views generally
author Michael Pavone <pavone@retrodev.com>
date Sun, 04 Nov 2018 22:51:50 -0800
parents 5aa0c3c43b97
children cc699c4966b1
files bindings.c default.cfg genesis.c render.h render_sdl.c vdp.c vdp.h
diffstat 7 files changed, 239 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/bindings.c	Sun Nov 04 11:45:41 2018 -0800
+++ b/bindings.c	Sun Nov 04 22:51:50 2018 -0800
@@ -6,6 +6,7 @@
 #include "saves.h"
 #include "util.h"
 #include "genesis.h"
+#include "sms.h"
 #include "menu.h"
 #include "bindings.h"
 #include "controller_info.h"
@@ -35,7 +36,8 @@
 	UI_RELOAD,
 	UI_SMS_PAUSE,
 	UI_SCREENSHOT,
-	UI_EXIT
+	UI_EXIT,
+	UI_PLANE_DEBUG
 } ui_action;
 
 typedef struct {
@@ -372,6 +374,20 @@
 			}
 #endif
 			break;
+		case UI_PLANE_DEBUG: {
+			vdp_context *vdp = NULL;
+			if (current_system->type == SYSTEM_GENESIS) {
+				genesis_context *gen = (genesis_context *)current_system;
+				vdp = gen->vdp;
+			} else if (current_system->type == SYSTEM_SMS) {
+				sms_context *sms = (sms_context *)current_system;
+				vdp = sms->vdp;
+			}
+			if (vdp) {
+				vdp_toggle_debug_view(vdp, VDP_DEBUG_PLANE);
+			}
+			break;
+		}
 		}
 		break;
 	}
@@ -575,6 +591,8 @@
 			*subtype_a = UI_SCREENSHOT;
 		} else if(!strcmp(target + 3, "exit")) {
 			*subtype_a = UI_EXIT;
+		} else if (!strcmp(target + 3, "plane_debug")) {
+			*subtype_a = UI_PLANE_DEBUG;
 		} else {
 			warning("Unreconized UI binding type %s\n", target);
 			return 0;
--- a/default.cfg	Sun Nov 04 11:45:41 2018 -0800
+++ b/default.cfg	Sun Nov 04 22:51:50 2018 -0800
@@ -19,6 +19,7 @@
 		] ui.vdp_debug_pal
 		u ui.enter_debugger
 		p ui.screenshot
+		b ui.plane_debug
 		esc ui.exit
 		` ui.save_state
 		0 ui.set_speed.0
--- a/genesis.c	Sun Nov 04 11:45:41 2018 -0800
+++ b/genesis.c	Sun Nov 04 22:51:50 2018 -0800
@@ -1126,10 +1126,7 @@
 static void inc_debug_mode(system_header *system)
 {
 	genesis_context *gen = (genesis_context *)system;
-	gen->vdp->debug++;
-	if (gen->vdp->debug == 7) {
-		gen->vdp->debug = 0;
-	}
+	vdp_inc_debug_mode(gen->vdp);
 }
 
 static void inc_debug_pal(system_header *system)
--- a/render.h	Sun Nov 04 11:45:41 2018 -0800
+++ b/render.h	Sun Nov 04 22:51:50 2018 -0800
@@ -68,6 +68,7 @@
 
 #define FRAMEBUFFER_ODD 0
 #define FRAMEBUFFER_EVEN 1
+#define FRAMEBUFFER_USER_START 2
 
 #include "vdp.h"
 
@@ -88,8 +89,11 @@
 
 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
 void render_save_screenshot(char *path);
+uint8_t render_create_window(char *caption, uint32_t width, uint32_t height);
 uint32_t *render_get_framebuffer(uint8_t which, int *pitch);
 void render_framebuffer_updated(uint8_t which, int width);
+//returns the framebuffer index associated with the Window that has focus
+uint8_t render_get_active_framebuffer(void);
 void render_init(int width, int height, char * title, uint8_t fullscreen);
 void render_set_video_standard(vid_std std);
 void render_toggle_fullscreen();
--- a/render_sdl.c	Sun Nov 04 11:45:41 2018 -0800
+++ b/render_sdl.c	Sun Nov 04 22:51:50 2018 -0800
@@ -25,7 +25,9 @@
 #define MAX_EVENT_POLL_PER_FRAME 2
 
 static SDL_Window *main_window;
+static SDL_Window **extra_windows;
 static SDL_Renderer *main_renderer;
+static SDL_Renderer **extra_renderers;
 static SDL_Texture  **sdl_textures;
 static uint8_t num_textures;
 static SDL_Rect      main_clip;
@@ -1035,6 +1037,7 @@
 			}
 		}
 	}
+	render_gl = 0;
 	
 #ifndef DISABLE_OPENGL
 	char *gl_enabled_str = tern_find_path_default(config, "video\0gl\0", def, TVAL_PTR).ptrval;
@@ -1323,6 +1326,37 @@
 	screenshot_path = path;
 }
 
+uint8_t render_create_window(char *caption, uint32_t width, uint32_t height)
+{
+	num_textures++;
+	sdl_textures = realloc(sdl_textures, num_textures * sizeof(*sdl_textures));
+	extra_windows = realloc(extra_windows, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_windows));
+	extra_renderers = realloc(extra_renderers, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_renderers));
+	uint8_t win_idx = num_textures - FRAMEBUFFER_USER_START - 1;
+	extra_windows[win_idx] = SDL_CreateWindow(caption, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0);
+	if (!extra_windows[win_idx]) {
+		goto fail_window;
+	}
+	extra_renderers[win_idx] = SDL_CreateRenderer(extra_windows[win_idx], -1, SDL_RENDERER_ACCELERATED);
+	if (!extra_renderers[win_idx]) {
+		goto fail_renderer;
+	}
+	uint8_t texture_idx = num_textures-1;
+	sdl_textures[texture_idx] = SDL_CreateTexture(extra_renderers[win_idx], SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
+	if (!sdl_textures[texture_idx]) {
+		goto fail_texture;
+	}
+	return texture_idx;
+	
+fail_texture:
+	SDL_DestroyRenderer(extra_renderers[win_idx]);
+fail_renderer:
+	SDL_DestroyWindow(extra_windows[win_idx]);
+fail_window:
+	num_textures--;
+	return 0;
+}
+
 uint32_t *locked_pixels;
 uint32_t locked_pitch;
 uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
@@ -1467,7 +1501,12 @@
 	}
 #endif
 	last_height = height;
-	render_update_display();
+	if (which <= FRAMEBUFFER_EVEN) {
+		render_update_display();
+	} else {
+		SDL_RenderCopy(extra_renderers[which - FRAMEBUFFER_USER_START], sdl_textures[which], NULL, NULL);
+		SDL_RenderPresent(extra_renderers[which - FRAMEBUFFER_USER_START]);
+	}
 	if (screenshot_file) {
 		fclose(screenshot_file);
 	}
@@ -1835,3 +1874,17 @@
 {
 	return render_gl;
 }
+
+uint8_t render_get_active_framebuffer(void)
+{
+	if (SDL_GetWindowFlags(main_window) & SDL_WINDOW_INPUT_FOCUS) {
+		return FRAMEBUFFER_ODD;
+	}
+	for (int i = 0; i < num_textures - 2; i++)
+	{
+		if (extra_windows[i] && (SDL_GetWindowFlags(extra_windows[i]) & SDL_WINDOW_INPUT_FOCUS)) {
+			return FRAMEBUFFER_USER_START + i; 
+		}
+	}
+	return 0xFF;
+}
--- a/vdp.c	Sun Nov 04 11:45:41 2018 -0800
+++ b/vdp.c	Sun Nov 04 22:51:50 2018 -0800
@@ -1720,6 +1720,109 @@
 	}
 }
 
+static void vdp_update_per_frame_debug(vdp_context *context)
+{
+	if (context->enabled_debuggers & (1 << VDP_DEBUG_PLANE)) {
+		uint32_t pitch;
+		uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[VDP_DEBUG_PLANE], &pitch);
+		uint16_t hscroll_mask;
+		uint16_t v_mul;
+		uint16_t vscroll_mask = 0x1F | (context->regs[REG_SCROLL] & 0x30) << 1;
+		switch(context->regs[REG_SCROLL] & 0x3)
+		{
+		case 0:
+			hscroll_mask = 0x1F;
+			v_mul = 64;
+			break;
+		case 0x1:
+			hscroll_mask = 0x3F;
+			v_mul = 128;
+			break;
+		case 0x2:
+			//TODO: Verify this behavior
+			hscroll_mask = 0x1F;
+			v_mul = 0;
+			break;
+		case 0x3:
+			hscroll_mask = 0x7F;
+			v_mul = 256;
+			break;
+		}
+		uint16_t table_address;
+		switch(context->debug_modes[VDP_DEBUG_PLANE] % 3)
+		{
+		case 0:
+			table_address = context->regs[REG_SCROLL_A] << 10 & 0xE000;
+			break;
+		case 1:
+			table_address = context->regs[REG_SCROLL_B] << 13 & 0xE000;
+			break;
+		case 2:
+			table_address = context->regs[REG_WINDOW] << 10;
+			if (context->regs[REG_MODE_4] & BIT_H40) {
+				table_address &= 0xF000;
+				v_mul = 128;
+				hscroll_mask = 0x3F;
+			} else {
+				table_address &= 0xF800;
+				v_mul = 64;
+				hscroll_mask = 0x1F;
+			}
+			vscroll_mask = 0x1F;
+			break;
+		}
+		uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR & 0x3F]];
+		for (uint16_t row = 0; row < 128; row++)
+		{
+			uint16_t row_address = table_address + (row & vscroll_mask) * v_mul;
+			for (uint16_t col = 0; col < 128; col++)
+			{
+				uint16_t address = row_address + (col & hscroll_mask) * 2;
+				//pccv hnnn nnnn nnnn
+				//
+				uint16_t entry = context->vdpmem[address] << 8 | context->vdpmem[address + 1];
+				uint8_t pal = entry >> 9 & 0x30;
+				
+				uint32_t *dst = fb + (row * pitch * 8 / sizeof(uint32_t)) + col * 8;
+				address = (entry & 0x7FF) * 32;
+				int y_diff = 4;
+				if (entry & 0x1000) {
+					y_diff = -4;
+					address += 7 * 4;
+				}
+				int x_diff = 1;
+				if (entry & 0x800) {
+					x_diff = -1;
+					address += 3;
+				}
+				for (int y = 0; y < 8; y++)
+				{
+					uint16_t trow_address = address;
+					uint32_t *row_dst = dst;
+					for (int x = 0; x < 4; x++)
+					{
+						uint8_t byte = context->vdpmem[trow_address];
+						trow_address += x_diff;
+						uint8_t left, right;
+						if (x_diff > 0) {
+							left = byte >> 4;
+							right = byte & 0xF;
+						} else {
+							left = byte & 0xF;
+							right = byte >> 4;
+						}
+						*(row_dst++) = left ? context->colors[left|pal] : bg_color;
+						*(row_dst++) = right ? context->colors[right|pal] : bg_color;
+					}
+					address += y_diff;
+					dst += pitch / sizeof(uint32_t);
+				}
+			}
+		}
+		render_framebuffer_updated(context->debug_fb_indices[VDP_DEBUG_PLANE], 1024);
+	}
+}
+
 void vdp_force_update_framebuffer(vdp_context *context)
 {
 	uint16_t lines_max = (context->flags2 & FLAG2_REGION_PAL) 
@@ -1734,6 +1837,7 @@
 	);
 	render_framebuffer_updated(context->cur_buffer, context->h40_lines > context->output_lines / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
 	context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
+	vdp_update_per_frame_debug(context);
 }
 
 static void advance_output_line(vdp_context *context)
@@ -1752,6 +1856,7 @@
 			render_framebuffer_updated(context->cur_buffer, context->h40_lines > (context->inactive_start + context->border_top) / 2 ? LINEBUF_SIZE : (256+HORIZ_BORDER));
 			context->cur_buffer = context->flags2 & FLAG2_EVEN_FIELD ? FRAMEBUFFER_EVEN : FRAMEBUFFER_ODD;
 			context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
+			vdp_update_per_frame_debug(context);
 			context->h40_lines = 0;
 			context->frame++;
 			context->output_lines = 0;
@@ -3740,3 +3845,43 @@
 	context->pending_hint_start = load_int32(buf);
 	update_video_params(context);
 }
+
+void vdp_toggle_debug_view(vdp_context *context, uint8_t debug_type)
+{
+	if (context->enabled_debuggers & 1 << debug_type) {
+		//TODO: implement me
+	} else {
+		char *caption;
+		switch(debug_type)
+		{
+		case VDP_DEBUG_PLANE:
+			caption = "BlastEm - VDP Plane Debugger";
+			break;
+		default:
+			return;
+		}
+		context->debug_fb_indices[debug_type] = render_create_window(caption, 1024, 1024);
+		if (context->debug_fb_indices[debug_type]) {
+			context->enabled_debuggers |= 1 << debug_type;
+		}
+	}
+}
+
+void vdp_inc_debug_mode(vdp_context *context)
+{
+	uint8_t active = render_get_active_framebuffer();
+	if (active < FRAMEBUFFER_USER_START) {
+		context->debug++;
+		if (context->debug == 7) {
+			context->debug = 0;
+		}
+		return;
+	}
+	for (int i = 0; i < VDP_NUM_DEBUG_TYPES; i++)
+	{
+		if (context->enabled_debuggers & (1 << i) && context->debug_fb_indices[i] == active) {
+			context->debug_modes[i]++;
+			return;
+		}
+	}
+}
--- a/vdp.h	Sun Nov 04 11:45:41 2018 -0800
+++ b/vdp.h	Sun Nov 04 22:51:50 2018 -0800
@@ -147,6 +147,14 @@
 	uint8_t  partial;
 } fifo_entry;
 
+enum {
+	VDP_DEBUG_PLANE,
+	VDP_DEBUG_VRAM,
+	VDP_DEBUG_CRAM,
+	VDP_DEBUG_COMPOSITE,
+	VDP_NUM_DEBUG_TYPES
+};
+
 typedef struct {
 	fifo_entry  fifo[FIFO_SIZE];
 	int32_t     fifo_write;
@@ -212,8 +220,13 @@
 	uint8_t     cur_buffer;
 	uint8_t     *tmp_buf_a;
 	uint8_t     *tmp_buf_b;
+	uint8_t     enabled_debuggers;
+	uint8_t     debug_fb_indices[VDP_NUM_DEBUG_TYPES];
+	uint8_t     debug_modes[VDP_NUM_DEBUG_TYPES];
 } vdp_context;
 
+
+
 void init_vdp_context(vdp_context * context, uint8_t region_pal);
 void vdp_free(vdp_context *context);
 void vdp_run_context_full(vdp_context * context, uint32_t target_cycles);
@@ -253,5 +266,7 @@
 void vdp_serialize(vdp_context *context, serialize_buffer *buf);
 void vdp_deserialize(deserialize_buffer *buf, void *vcontext);
 void vdp_force_update_framebuffer(vdp_context *context);
+void vdp_toggle_debug_view(vdp_context *context, uint8_t debug_type);
+void vdp_inc_debug_mode(vdp_context *context);
 
 #endif //VDP_H_