changeset 2685:da2e06c42d16

Add a compile-time flag to use RGB565 instead of ABGR/ARGB
author Michael Pavone <pavone@retrodev.com>
date Sun, 30 Mar 2025 00:06:53 -0700
parents c649bcc18487
children 05915f01046d
files Makefile cd_graphics.c oscilloscope.c pixel.h png.c png.h ppm.c ppm.h render.h render_sdl.c vdp.c vdp.h
diffstat 12 files changed, 241 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Sat Mar 29 23:54:45 2025 -0700
+++ b/Makefile	Sun Mar 30 00:06:53 2025 -0700
@@ -67,6 +67,10 @@
 USE_GLES:=1
 endif
 
+ifdef USE_RGB565
+CFLAGS+= -DUSE_RGB565
+endif
+
 ifdef USE_FBDEV
 LIBS=alsa
 ifndef NOGL
--- a/cd_graphics.c	Sat Mar 29 23:54:45 2025 -0700
+++ b/cd_graphics.c	Sun Mar 30 00:06:53 2025 -0700
@@ -263,7 +263,7 @@
 static void render_graphics_debug(segacd_context *cd)
 {
 	int pitch;
-	uint32_t *fb = render_get_framebuffer(cd->graphics_debug_window, &pitch);
+	pixel_t *fb = render_get_framebuffer(cd->graphics_debug_window, &pitch);
 	uint32_t pixels = (cd->gate_array[GA_STAMP_SIZE] & BIT_SMS) ? 4096 : 256;
 	uint32_t stamp_size = (cd->gate_array[GA_STAMP_SIZE] & BIT_STS) ? 32 : 16;
 	uint32_t num_stamps = pixels / stamp_size;
@@ -284,7 +284,7 @@
 				{
 					for (uint32_t y = start_y; y < start_y + stamp_size; y++)
 					{
-						uint32_t *line = fb + y * pitch / sizeof(uint32_t);
+						pixel_t *line = fb + y * pitch / sizeof(uint32_t);
 						for (uint32_t x = tile_x; x < tile_x + 8; x += 4)
 						{
 						
--- a/oscilloscope.c	Sat Mar 29 23:54:45 2025 -0700
+++ b/oscilloscope.c	Sun Mar 30 00:06:53 2025 -0700
@@ -56,15 +56,16 @@
 void scope_render(oscilloscope *scope)
 {
 	int pitch;
-	uint32_t *fb = render_get_framebuffer(scope->window, &pitch);
+	pixel_t *fb = render_get_framebuffer(scope->window, &pitch);
 	memset(fb, 0, HEIGHT * pitch);
-	pitch /= sizeof(uint32_t);
+	pitch /= sizeof(pixel_t);
 	int offset = 0;
 	int column_width = WIDTH/3;
 	int width = column_width * 3;
 	int row_height = HEIGHT / ((scope->num_channels + 2) / 3);
 	float value_scale = (float)row_height / 20000.0f;
-	uint32_t *cur_line = fb;
+	pixel_t *cur_line = fb;
+	pixel_t white = render_map_color(255, 255, 255);
 	for (uint8_t i = 0; i < scope->num_channels; i++)
 	{
 		float samples_per_pixel = (float)scope->channels[i].period / (float)(2*column_width);
@@ -96,13 +97,13 @@
 				int delta = last_y > y ? -1 : 1;
 				while (last_y != y)
 				{
-					cur_line[last_y * pitch + x ] = 0xFFFFFFFF;
+					cur_line[last_y * pitch + x ] = white;
 					last_y += delta;
 				}
 			} else {
 				last_y = y;
 			}
-			cur_line[y * pitch + x ] = 0xFFFFFFFF;
+			cur_line[y * pitch + x ] = white;
 			cur_sample += samples_per_pixel;
 			if (cur_sample + 0.5f >= scope->channels[i].period) {
 				cur_sample -= scope->channels[i].period;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pixel.h	Sun Mar 30 00:06:53 2025 -0700
@@ -0,0 +1,14 @@
+#ifndef PIXEL_H_
+#define PIXEL_H_
+
+#include <stdint.h>
+#ifdef USE_RGB565
+typedef uint16_t pixel_t;
+#else
+typedef uint32_t pixel_t;
+#endif
+
+#define PITCH_BYTES(width) (sizeof(uint32_t) * ((width * sizeof(pixel_t) + sizeof(uint32_t) - 1) / sizeof(uint32_t)))
+#define PITCH_PIXEL_T(width) ((width * sizeof(pixel_t) + sizeof(uint32_t) - 1) / sizeof(uint32_t)) * (sizeof(uint32_t) / sizeof(pixel_t))
+
+#endif //PIXEL_H_
--- a/png.c	Sat Mar 29 23:54:45 2025 -0700
+++ b/png.c	Sun Mar 30 00:06:53 2025 -0700
@@ -60,25 +60,37 @@
 	write_chunk(f, ihdr, chunk, sizeof(chunk));
 }
 
-void save_png24_frame(FILE *f, uint32_t *buffer, apng_state *apng, uint32_t width, uint32_t height, uint32_t pitch)
+void save_png24_frame(FILE *f, pixel_t *buffer, apng_state *apng, uint32_t width, uint32_t height, uint32_t pitch)
 {
 	uint32_t idat_size = (1 + width*3) * height;
 	uint8_t *idat_buffer = malloc(idat_size);
-	uint32_t *pixel = buffer;
+	pixel_t *pixel = buffer;
 	uint8_t *cur = idat_buffer;
 	for (uint32_t y = 0; y < height; y++)
 	{
 		//save filter type
 		*(cur++) = 0;
-		uint32_t *start = pixel;
+		pixel_t *start = pixel;
 		for (uint32_t x = 0; x < width; x++, pixel++)
 		{
-			uint32_t value = *pixel;
+			pixel_t value = *pixel;
+#ifdef USE_RGB565
+			*(cur++) = (value >> 8 & 0xF8) | (value >> (8+5));
+			*(cur++) = (value >> 3 & 0xFC) | (value >> (3+5) & 0x3);
+			*(cur++) = value << 3 | (value >> (5-3) & 0x3);
+#else
+#ifdef USE_GLES
+			*(cur++) = value;
+			*(cur++) = value >> 8;
+			*(cur++) = value >> 16;
+#else
 			*(cur++) = value >> 16;
 			*(cur++) = value >> 8;
 			*(cur++) = value;
+#endif
+#endif
 		}
-		pixel = start + pitch / sizeof(uint32_t);
+		pixel = start + pitch / sizeof(pixel_t);
 	}
 	
 	uLongf compress_buffer_size = idat_size + 5 * (idat_size/16383 + 1) + 3;
@@ -147,27 +159,27 @@
 	free(apng);
 }
 
-void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
+void save_png24(FILE *f, pixel_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
 {
 	write_header(f, width, height, COLOR_TRUE);
 	save_png24_frame(f, buffer, NULL, width, height, pitch);
 	write_chunk(f, iend, NULL, 0);
 }
 
-void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
+void save_png(FILE *f, pixel_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
 {
-	uint32_t palette[256];
+	pixel_t palette[256];
 	uint8_t pal_buffer[256*3];
 	uint32_t num_pal = 0;
 	uint32_t index_size = (1 + width) * height;
 	uint8_t *index_buffer = malloc(index_size);
 	uint8_t *cur = index_buffer;
-	uint32_t *pixel = buffer;
+	pixel_t *pixel = buffer;
 	for (uint32_t y = 0; y < height; y++)
 	{
 		//save filter type
 		*(cur++) = 0;
-		uint32_t *start = pixel;
+		pixel_t *start = pixel;
 		for (uint32_t x = 0; x < width; x++, pixel++, cur++)
 		{
 			uint32_t value = (*pixel) & 0xFFFFFF;
@@ -189,15 +201,27 @@
 			}
 			*cur = i;
 		}
-		pixel = start + pitch / sizeof(uint32_t);
+		pixel = start + pitch / sizeof(pixel_t);
 	}
 	write_header(f, width, height, COLOR_INDEXED);
 	cur = pal_buffer;
 	for (uint32_t i = 0; i < num_pal; i++)
 	{
+#ifdef USE_RGB565
+		*(cur++) = (palette[i] >> 8 & 0xF8) | (palette[i] >> (8+5));
+		*(cur++) = (palette[i] >> 3 & 0xFC) | (palette[i] >> (3+5) & 0x3);
+		*(cur++) = palette[i] << 3 | (palette[i] >> (5-3) & 0x3);
+#else
+#ifdef USE_GLES
+		*(cur++) = palette[i];
+		*(cur++) = palette[i] >> 8;
+		*(cur++) = palette[i] >> 16;
+#else
 		*(cur++) = palette[i] >> 16;
 		*(cur++) = palette[i] >> 8;
 		*(cur++) = palette[i];
+#endif
+#endif
 	}
 	write_chunk(f, plte, pal_buffer, num_pal * 3);
 	uLongf compress_buffer_size = index_size + 5 * (index_size/16383 + 1) + 3;
--- a/png.h	Sat Mar 29 23:54:45 2025 -0700
+++ b/png.h	Sun Mar 30 00:06:53 2025 -0700
@@ -1,6 +1,8 @@
 #ifndef PNG_H_
 #define PNG_H_
 
+#include "pixel.h"
+
 typedef struct {
 	uint32_t sequence_number;
 	uint32_t num_frames;
@@ -9,9 +11,9 @@
 	uint16_t delay_den;
 } apng_state;
 
-void save_png24_frame(FILE *f, uint32_t *buffer, apng_state *apng, uint32_t width, uint32_t height, uint32_t pitch);
-void save_png24(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
-void save_png(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
+void save_png24_frame(FILE *f, pixel_t *buffer, apng_state *apng, uint32_t width, uint32_t height, uint32_t pitch);
+void save_png24(FILE *f, pixel_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
+void save_png(FILE *f, pixel_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
 apng_state* start_apng(FILE *f, uint32_t width, uint32_t height, float frame_rate);
 void end_apng(FILE *f, apng_state *apng);
 uint32_t *load_png(uint8_t *buffer, uint32_t buf_size, uint32_t *width, uint32_t *height);
--- a/ppm.c	Sat Mar 29 23:54:45 2025 -0700
+++ b/ppm.c	Sun Mar 30 00:06:53 2025 -0700
@@ -1,21 +1,34 @@
 #include <stdint.h>
 #include <stdio.h>
+#include "pixel.h"
 
-void save_ppm(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
+void save_ppm(FILE *f, pixel_t *buffer, uint32_t width, uint32_t height, uint32_t pitch)
 {
 	fprintf(f, "P6\n%d %d\n255\n", width, height);
 	for(uint32_t y = 0; y < height; y++)
 	{
-		uint32_t *line = buffer;
+		pixel_t *line = buffer;
 		for (uint32_t x = 0; x < width; x++, line++)
 		{
 			uint8_t buf[3] = {
+#ifdef USE_RGB565
+				(*line >> 8 & 0xF8) | (*line >> (8+5)),       //red
+				(*line >> 3 & 0xFC) | (*line >> (3+5) & 0x3), //green
+				*line << 3 | (*line >> (5-3) & 0x3)           //blue
+#else
+#ifdef USE_GLES
+				*line,      //red
+				*line >> 8, //green
+				*line >> 16 //blue
+#else
 				*line >> 16, //red
 				*line >> 8,  //green
 				*line        //blue
+#endif
+#endif
 			};
 			fwrite(buf, 1, sizeof(buf), f);
 		}
-		buffer = buffer + pitch / sizeof(uint32_t);
+		buffer = buffer + pitch / sizeof(pixel_t);
 	}
 }
--- a/ppm.h	Sat Mar 29 23:54:45 2025 -0700
+++ b/ppm.h	Sun Mar 30 00:06:53 2025 -0700
@@ -1,6 +1,7 @@
 #ifndef PPM_H_
 #define PPM_H_
+#include "pixel.h"
 
-void save_ppm(FILE *f, uint32_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
+void save_ppm(FILE *f, pixel_t *buffer, uint32_t width, uint32_t height, uint32_t pitch);
 
 #endif //PPM_H_
--- a/render.h	Sat Mar 29 23:54:45 2025 -0700
+++ b/render.h	Sun Mar 30 00:06:53 2025 -0700
@@ -99,14 +99,16 @@
 typedef void (*ui_render_fun)(void);
 typedef int (*render_thread_fun)(void*);
 
-uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
+#include "pixel.h"
+
+pixel_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
 void render_save_screenshot(char *path);
 uint8_t render_saving_video(void);
 void render_end_video(void);
 void render_save_video(char *path);
 uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler);
 void render_destroy_window(uint8_t which);
-uint32_t *render_get_framebuffer(uint8_t which, int *pitch);
+pixel_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);
--- a/render_sdl.c	Sat Mar 29 23:54:45 2025 -0700
+++ b/render_sdl.c	Sun Mar 30 00:06:53 2025 -0700
@@ -44,7 +44,7 @@
 	uint8_t              num_static;
 #ifndef DISABLE_OPENGL
 	SDL_GLContext        *gl_context;
-	uint32_t             *texture_buf;
+	pixel_t              *texture_buf;
 	uint32_t             tex_width;
 	uint32_t             tex_height;
 	GLuint               gl_texture[2];
@@ -94,7 +94,7 @@
 static uint8_t sync_src;
 static uint32_t min_buffered;
 
-uint32_t **frame_buffers;
+pixel_t **frame_buffers;
 uint32_t num_buffers;
 uint32_t buffer_storage;
 
@@ -339,13 +339,17 @@
 	return is_fullscreen;
 }
 
-uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b)
+pixel_t render_map_color(uint8_t r, uint8_t g, uint8_t b)
 {
+#ifdef USE_RGB565
+	return r << 8 & 0xF800 | g << 3 & 0x07E0 | b >> 3;
+#else
 #ifdef USE_GLES
 	return 255UL << 24 | b << 16 | g << 8 | r;
 #else
 	return 255UL << 24 | r << 16 | g << 8 | b;
 #endif
+#endif
 }
 
 static uint8_t external_sync;
@@ -457,19 +461,38 @@
 }
 #endif
 
-static uint32_t texture_buf[512 * 513];
+static pixel_t texture_buf[512 * 513];
 #ifdef DISABLE_OPENGL
+#ifdef USE_RGB565
+#define RENDER_FORMAT SDL_PIXELFORMAT_RGB565
+#else
 #define RENDER_FORMAT SDL_PIXELFORMAT_ARGB8888
-#else
+#endif
+#else //DISABLE_OPENGL
+#ifdef USE_RGB565
+#define INTERNAL_FORMAT GL_RGB
+#define SRC_FORMAT GL_RGB
+#define SRC_TYPE GL_UNSIGNED_SHORT_5_6_5
+#define RENDER_FORMAT SDL_PIXELFORMAT_RGB565
+#else //USE_RGB565
+#define SRC_TYPE GL_UNSIGNED_BYTE
 #ifdef USE_GLES
 #define INTERNAL_FORMAT GL_RGBA
 #define SRC_FORMAT GL_RGBA
 #define RENDER_FORMAT SDL_PIXELFORMAT_ABGR8888
-#else
+#else //USE_GLES
 #define INTERNAL_FORMAT GL_RGBA8
 #define SRC_FORMAT GL_BGRA
 #define RENDER_FORMAT SDL_PIXELFORMAT_ARGB8888
+#endif //USE_GLES
+#endif //USE_RGB565
+
+#ifdef USE_GLES
+#define SRC_FORMAT32 GL_RGBA
+#else
+#define SRC_FORMAT32 GL_BGRA
 #endif
+
 static void gl_setup()
 {
 	tern_val def = {.ptrval = "linear"};
@@ -494,10 +517,10 @@
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 		if (i < 2) {
 			//TODO: Fixme for PAL + invalid display mode
-			glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, tex_width, tex_height, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, texture_buf);
+			glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, tex_width, tex_height, 0, SRC_FORMAT, SRC_TYPE, texture_buf);
 		} else {
-			uint32_t blank = 255UL << 24;
-			glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, &blank);
+			pixel_t blank = render_map_color(0, 0, 0);
+			glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, SRC_TYPE, &blank);
 		}
 	}
 	glGenBuffers(2, buffers);
@@ -538,7 +561,7 @@
 	glDeleteBuffers(2, buffers);
 	glDeleteTextures(3, textures);
 }
-#endif
+#endif //DISABLE_OPENGL
 
 static uint8_t texture_init;
 static void render_alloc_surfaces()
@@ -1190,7 +1213,7 @@
 		free_buffer_mutex = SDL_CreateMutex();
 		frame_ready = SDL_CreateCond();
 		buffer_storage = 4;
-		frame_buffers = calloc(buffer_storage, sizeof(uint32_t*));
+		frame_buffers = calloc(buffer_storage, sizeof(pixel_t*));
 		frame_buffers[0] = texture_buf;
 		num_buffers = 1;
 	}
@@ -1688,7 +1711,7 @@
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 			if (i) {
-				glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, extras[win_idx].color);
+				glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, 1, 1, 0, SRC_FORMAT, SRC_TYPE, extras[win_idx].color);
 			} else {
 				extras[win_idx].tex_width = width;
 				extras[win_idx].tex_height = height;
@@ -1697,8 +1720,8 @@
 					extras[win_idx].tex_width = nearest_pow2(width);
 					extras[win_idx].tex_height = nearest_pow2(height);
 				}
-				extras[win_idx].texture_buf = calloc(extras[win_idx].tex_width * extras[win_idx].tex_height, sizeof(uint32_t));
-				glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, extras[win_idx].tex_width, extras[win_idx].tex_height, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, extras[win_idx].texture_buf);
+				extras[win_idx].texture_buf = calloc(PITCH_PIXEL_T(extras[win_idx].tex_width) * extras[win_idx].tex_height, sizeof(pixel_t));
+				glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, extras[win_idx].tex_width, extras[win_idx].tex_height, 0, SRC_FORMAT, SRC_TYPE, extras[win_idx].texture_buf);
 			}
 		}
 		glGenBuffers(3, extras[win_idx].gl_buffers);
@@ -1876,12 +1899,12 @@
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 		//TODO: maybe make this respect the npot texture setting?
-		glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, width, height, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, pixels);
+		glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, width, height, 0, SRC_FORMAT32, GL_UNSIGNED_BYTE, pixels);
 	} else
 #endif
 	{
 		extra->static_images[img_index] = SDL_CreateTexture(extra->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height);
-		SDL_UpdateTexture(extra->static_images[img_index], NULL, pixels, sizeof(uint32_t) * width);
+		SDL_UpdateTexture(extra->static_images[img_index], NULL, pixels, sizeof(pixel_t) * width);
 	}
 	free(pixels);
 	return img_index;
@@ -1963,7 +1986,7 @@
 		extra->color[1] = g;
 		extra->color[2] = r;
 		glBindTexture(GL_TEXTURE_2D, extra->gl_texture[1]);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, SRC_FORMAT, GL_UNSIGNED_BYTE, extra->color);
+		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, SRC_FORMAT, SRC_TYPE, extra->color);
 		extra_draw_quad(extra, extra->gl_texture[1], 1.0f, 1.0f);
 	}
 #endif
@@ -1982,18 +2005,18 @@
 #endif
 }
 
-uint32_t *locked_pixels;
+pixel_t *locked_pixels;
 uint32_t locked_pitch;
-uint32_t *render_get_framebuffer(uint8_t which, int *pitch)
+pixel_t *render_get_framebuffer(uint8_t which, int *pitch)
 {
 	if (sync_src == SYNC_AUDIO_THREAD || sync_src == SYNC_EXTERNAL) {
-		*pitch = LINEBUF_SIZE * sizeof(uint32_t);
-		uint32_t *buffer;
+		*pitch = PITCH_BYTES(LINEBUF_SIZE);
+		pixel_t *buffer;
 		SDL_LockMutex(free_buffer_mutex);
 			if (num_buffers) {
 				buffer = frame_buffers[--num_buffers];
 			} else {
-				buffer = calloc(tex_width*(tex_height + 1), sizeof(uint32_t));
+				buffer = calloc(tex_width*(tex_height + 1), sizeof(pixel_t));
 			}
 		SDL_UnlockMutex(free_buffer_mutex);
 		locked_pixels = buffer;
@@ -2001,11 +2024,11 @@
 	}
 #ifndef DISABLE_OPENGL
 	if (render_gl && which <= FRAMEBUFFER_EVEN) {
-		*pitch = LINEBUF_SIZE * sizeof(uint32_t);
+		*pitch = PITCH_BYTES(LINEBUF_SIZE);
 		return texture_buf;
 	} else if (render_gl && which >= FRAMEBUFFER_USER_START) {
 		uint8_t win_idx = which - FRAMEBUFFER_USER_START;
-		*pitch = extras[win_idx].width * sizeof(uint32_t);
+		*pitch = PITCH_BYTES(extras[win_idx].width);
 		return extras[win_idx].texture_buf;
 	} else {
 #endif
@@ -2030,7 +2053,7 @@
 		}
 		static uint8_t last;
 		if (which <= FRAMEBUFFER_EVEN) {
-			locked_pixels = (uint32_t *)pixels;
+			locked_pixels = (pixel_t *)pixels;
 			if (which == FRAMEBUFFER_EVEN) {
 				pixels += *pitch;
 			}
@@ -2040,18 +2063,18 @@
 			}
 			last = which;
 		}
-		return (uint32_t *)pixels;
+		return (pixel_t *)pixels;
 #ifndef DISABLE_OPENGL
 	}
 #endif
 }
 
-static void release_buffer(uint32_t *buffer)
+static void release_buffer(pixel_t *buffer)
 {
 	SDL_LockMutex(free_buffer_mutex);
 		if (num_buffers == buffer_storage) {
 			buffer_storage *= 2;
-			frame_buffers = realloc(frame_buffers, sizeof(uint32_t*)*buffer_storage);
+			frame_buffers = realloc(frame_buffers, sizeof(pixel_t*)*buffer_storage);
 		}
 		frame_buffers[num_buffers++] = buffer;
 	SDL_UnlockMutex(free_buffer_mutex);
@@ -2066,7 +2089,7 @@
 
 static uint32_t last_width, last_height;
 static uint8_t interlaced, last_field;
-static void process_framebuffer(uint32_t *buffer, uint8_t which, int width)
+static void process_framebuffer(pixel_t *buffer, uint8_t which, int width)
 {
 	if (sync_src == SYNC_VIDEO && which <= FRAMEBUFFER_EVEN && source_frame_count < 0) {
 		source_frame++;
@@ -2100,24 +2123,24 @@
 			screenshot_path = NULL;
 		}
 		interlaced = last_field != which;
-		buffer += overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard];
+		buffer += overscan_left[video_standard] + PITCH_PIXEL_T(LINEBUF_SIZE) * overscan_top[video_standard];
 	}
 #ifndef DISABLE_OPENGL
 	if (render_gl && which <= FRAMEBUFFER_EVEN) {
 		SDL_GL_MakeCurrent(main_window, main_context);
 		glBindTexture(GL_TEXTURE_2D, textures[which]);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, SRC_FORMAT, GL_UNSIGNED_BYTE, buffer);
+		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, LINEBUF_SIZE, height, SRC_FORMAT, SRC_TYPE, buffer);
 
 		if (screenshot_file) {
 			//properly supporting interlaced modes here is non-trivial, so only save the odd field for now
 #ifndef DISABLE_ZLIB
 			if (!strcasecmp(ext, "png")) {
 				free(ext);
-				save_png(screenshot_file, buffer, width, height, LINEBUF_SIZE*sizeof(uint32_t));
+				save_png(screenshot_file, buffer, width, height, PITCH_BYTES(LINEBUF_SIZE));
 			} else {
 				free(ext);
 #endif
-				save_ppm(screenshot_file, buffer, width, height, LINEBUF_SIZE*sizeof(uint32_t));
+				save_ppm(screenshot_file, buffer, width, height, PITCH_BYTES(LINEBUF_SIZE));
 #ifndef DISABLE_ZLIB
 			}
 #endif
@@ -2128,14 +2151,14 @@
 				//TODO: more precise frame rate
 				apng = start_apng(apng_file, width, height, video_standard == VID_PAL ? 50.0 : 60.0);
 			}
-			save_png24_frame(apng_file, buffer, apng, width, height, LINEBUF_SIZE*sizeof(uint32_t));
+			save_png24_frame(apng_file, buffer, apng, width, height, PITCH_BYTES(LINEBUF_SIZE));
 		}
 #endif
 	} else if (render_gl && which >= FRAMEBUFFER_USER_START) {
 		uint8_t win_idx = which - FRAMEBUFFER_USER_START;
 		SDL_GL_MakeCurrent(extras[win_idx].win, extras[win_idx].gl_context);
 		glBindTexture(GL_TEXTURE_2D, extras[win_idx].gl_texture[0]);
-		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extras[win_idx].width, extras[win_idx].height, SRC_FORMAT, GL_UNSIGNED_BYTE, buffer);
+		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extras[win_idx].width, extras[win_idx].height, SRC_FORMAT, SRC_TYPE, buffer);
 	} else {
 #endif
 		uint32_t shot_height = height;
@@ -2298,7 +2321,7 @@
 }
 
 typedef struct {
-	uint32_t *buffer;
+	pixel_t *buffer;
 	int      width;
 	uint8_t  which;
 } frame;
@@ -2337,6 +2360,20 @@
 			frame_queue_len++;
 			SDL_CondSignal(frame_ready);
 		SDL_UnlockMutex(frame_mutex);
+#ifdef __ANDROID__
+		if (which <= FRAMEBUFFER_EVEN) {
+			static uint32_t frame_counter, start;
+			frame_counter++;
+			uint32_t last_frame= SDL_GetTicks();
+			if ((last_frame - start) > FPS_INTERVAL) {
+				if (start && (last_frame-start)) {
+					debug_message("%s - %.1f fps (emulated)", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0));
+				}
+				start = last_frame;
+				frame_counter = 0;
+			}
+		}
+#endif
 		return;
 	}
 	//TODO: Maybe fixme for render API
--- a/vdp.c	Sat Mar 29 23:54:45 2025 -0700
+++ b/vdp.c	Sun Mar 30 00:06:53 2025 -0700
@@ -160,8 +160,8 @@
 {
 	vdp_context *context = calloc(1, sizeof(vdp_context) + VRAM_SIZE);
 	if (headless) {
-		context->fb = malloc(512 * LINEBUF_SIZE * sizeof(uint32_t));
-		context->output_pitch = LINEBUF_SIZE * sizeof(uint32_t);
+		context->fb = malloc(512 * LINEBUF_SIZE * sizeof(pixel_t));
+		context->output_pitch = LINEBUF_SIZE * sizeof(pixel_t);
 	} else {
 		context->cur_buffer = FRAMEBUFFER_ODD;
 		context->fb = render_get_framebuffer(FRAMEBUFFER_ODD, &context->output_pitch);
@@ -339,7 +339,7 @@
 		context->flags2 |= FLAG2_REGION_PAL;
 	}
 	update_video_params(context);
-	context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
+	context->output = (pixel_t *)(((char *)context->fb) + context->output_pitch * context->border_top);
 	return context;
 }
 
@@ -999,7 +999,7 @@
 	)) {
 		uint8_t bg_end_slot = BG_START_SLOT + (context->regs[REG_MODE_4] & BIT_H40) ? LINEBUF_SIZE/2 : (256+HORIZ_BORDER)/2;
 		if (context->hslot < bg_end_slot) {
-			uint32_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET];
+			pixel_t color = (context->regs[REG_MODE_2] & BIT_MODE_5) ? context->colors[addr] : context->colors[addr + MODE4_OFFSET];
 			context->output[(context->hslot - BG_START_SLOT)*2 + 1] = color;
 		}
 	}
@@ -1820,7 +1820,7 @@
 			dst = context->compositebuf + BORDER_LEFT + col * 8;
 		} else {
 			dst = context->compositebuf;
-			uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
+			pixel_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
 			memset(dst, 0, BORDER_LEFT);
 			context->done_composite = dst + BORDER_LEFT;
 			return;
@@ -2059,7 +2059,7 @@
 			line += context->border_top;
 		}
 		if (context->enabled_debuggers & (1 << DEBUG_CRAM)) {
-			uint32_t *fb = context->debug_fbs[DEBUG_CRAM] + context->debug_fb_pitch[DEBUG_CRAM] * line / sizeof(uint32_t);
+			pixel_t *fb = context->debug_fbs[DEBUG_CRAM] + context->debug_fb_pitch[DEBUG_CRAM] * line / sizeof(pixel_t);
 			if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 				for (int i = 0; i < 64; i++)
 				{
@@ -2082,7 +2082,7 @@
 				{
 					uint8_t entry = context->vdpmem[mode4_address_map[address] ^ 1];
 					uint8_t fg = entry >> 4, bg = entry & 0xF;
-					uint32_t fg_full, bg_full;
+					pixel_t fg_full, bg_full;
 					if (context->type == VDP_GAMEGEAR) {
 						//Game Gear uses CRAM entries 16-31 for TMS9918A modes
 						fg_full = context->colors[fg + 16 + MODE4_OFFSET];
@@ -2110,7 +2110,7 @@
 			context->enabled_debuggers & (1 << DEBUG_COMPOSITE)
 			&& line < (context->inactive_start + context->border_bot + context->border_top)
 		) {
-			uint32_t *fb = context->debug_fbs[DEBUG_COMPOSITE] + context->debug_fb_pitch[DEBUG_COMPOSITE] * line / sizeof(uint32_t);
+			pixel_t *fb = context->debug_fbs[DEBUG_COMPOSITE] + context->debug_fb_pitch[DEBUG_COMPOSITE] * line / sizeof(pixel_t);
 			if (is_mode_5) {
 				uint32_t left, right;
 				uint16_t top_line, bottom_line;
@@ -2175,7 +2175,7 @@
 	}
 }
 
-static void vram_debug_mode5(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void vram_debug_mode5(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
 	uint8_t pal = (context->debug_modes[DEBUG_VRAM] % 4) << 4;
 	int yshift, ymask, tilesize;
@@ -2190,7 +2190,7 @@
 	}
 	for (int y = 0; y < 512; y++)
 	{
-		uint32_t *line = fb + y * pitch / sizeof(uint32_t);
+		pixel_t *line = fb + y * pitch / sizeof(pixel_t);
 		int row = y >> yshift;
 		int yoff = y >> 1 & ymask;
 		for (int col = 0; col < 64; col++)
@@ -2210,11 +2210,11 @@
 	}
 }
 
-static void vram_debug_mode4(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void vram_debug_mode4(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
 	for (int y = 0; y < 256; y++)
 	{
-		uint32_t *line = fb + y * pitch / sizeof(uint32_t);
+		pixel_t *line = fb + y * pitch / sizeof(pixel_t);
 		int row = y >> 4;
 		int yoff = y >> 1 & 7;
 		for (int col = 0; col < 64; col++)
@@ -2237,13 +2237,13 @@
 	}
 }
 
-static void vram_debug_tms(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void vram_debug_tms(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
 	uint8_t pal = ((context->debug_modes[DEBUG_VRAM] % 14) + 2) << 1;
 	pal = (pal & 0xE) | (pal << 1 & 0x20);
 	for (int y = 0; y < 512; y++)
 	{
-		uint32_t *line = fb + y * pitch / sizeof(uint32_t);
+		pixel_t *line = fb + y * pitch / sizeof(pixel_t);
 		int row = y >> 4;
 		int yoff = y >> 1 & 7;
 		for (int col = 0; col < 64; col++)
@@ -2261,7 +2261,7 @@
 	}
 }
 
-static void plane_debug_mode5(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void plane_debug_mode5(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
 	uint16_t hscroll_mask;
 	uint16_t v_mul;
@@ -2309,7 +2309,7 @@
 		vscroll_mask = 0x1F;
 		break;
 	}
-	uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
+	pixel_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
 	uint16_t num_rows;
 	int num_lines;
 	if (context->double_res) {
@@ -2330,7 +2330,7 @@
 			uint16_t entry = context->vdpmem[address] << 8 | context->vdpmem[address + 1];
 			uint8_t pal = entry >> 9 & 0x30;
 
-			uint32_t *dst = fb + (row * pitch * num_lines / sizeof(uint32_t)) + col * 8;
+			pixel_t *dst = fb + (row * pitch * num_lines / sizeof(pixel_t)) + col * 8;
 			if (context->double_res) {
 				address = (entry & 0x3FF) * 64;
 			} else {
@@ -2349,7 +2349,7 @@
 			for (int y = 0; y < num_lines; y++)
 			{
 				uint16_t trow_address = address;
-				uint32_t *row_dst = dst;
+				pixel_t *row_dst = dst;
 				for (int x = 0; x < 4; x++)
 				{
 					uint8_t byte = context->vdpmem[trow_address];
@@ -2366,25 +2366,25 @@
 					*(row_dst++) = right ? context->colors[right|pal] : bg_color;
 				}
 				address += y_diff;
-				dst += pitch / sizeof(uint32_t);
+				dst += pitch / sizeof(pixel_t);
 			}
 		}
 	}
 }
 
-static void sprite_debug_mode5(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void sprite_debug_mode5(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
-	uint32_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
+	pixel_t bg_color = context->colors[context->regs[REG_BG_COLOR] & 0x3F];
 	//clear a single alpha channel bit so we can distinguish between actual bg color and sprite
 	//pixels that just happen to be the same color
 	bg_color &= 0xFEFFFFFF;
-	uint32_t *line = fb;
-	uint32_t border_line = render_map_color(0, 0, 255);
-	uint32_t sprite_outline = render_map_color(255, 0, 255);
+	pixel_t *line = fb;
+	pixel_t border_line = render_map_color(0, 0, 255);
+	pixel_t sprite_outline = render_map_color(255, 0, 255);
 	int right_border = 256 + ((context->h40_lines > context->output_lines / 2) ? 640 : 512);
 	for (int y = 0; y < 1024; y++)
 	{
-		uint32_t *cur = line;
+		pixel_t *cur = line;
 		if (y != 256 && y != 256+context->inactive_start*2) {
 			for (int x = 0; x < 255; x++)
 			{
@@ -2406,7 +2406,7 @@
 				*(cur++) = border_line;
 			}
 		}
-		line += pitch / sizeof(uint32_t);
+		line += pitch / sizeof(pixel_t);
 	}
 	for (int i = 0, index = 0; i < context->max_sprites_frame; i++)
 	{
@@ -2431,8 +2431,8 @@
 		uint16_t hflip = tileinfo & MAP_BIT_H_FLIP;
 		uint16_t vflip = tileinfo & MAP_BIT_V_FLIP;
 		uint32_t x = (((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF) * 2;
-		uint32_t *line = fb + y * pitch / sizeof(uint32_t) + x;
-		uint32_t *cur = line;
+		pixel_t *line = fb + y * pitch / sizeof(pixel_t) + x;
+		pixel_t *cur = line;
 		for (uint32_t cx = x, x2 = x + pixel_width; cx < x2; cx++)
 		{
 			*(cur++) = sprite_outline;
@@ -2453,7 +2453,7 @@
 		}
 		for (; y < y2; y++)
 		{
-			line += pitch / sizeof(uint32_t);
+			line += pitch / sizeof(pixel_t);
 			cur = line;
 			*(cur++) = sprite_outline;
 			uint16_t line_addr = tile_addr;
@@ -2463,7 +2463,7 @@
 				for (uint8_t cx = 0; cx < 4; cx++)
 				{
 					uint8_t pair = context->vdpmem[cur_addr];
-					uint32_t left, right;
+					pixel_t left, right;
 					if (hflip) {
 						right = pair >> 4;
 						left = pair & 0xF;
@@ -2506,7 +2506,7 @@
 			advance_source = !advance_source;
 		}
 		if (y2 != 1024) {
-			line += pitch / sizeof(uint32_t);
+			line += pitch / sizeof(pixel_t);
 			cur = line;
 			for (uint32_t cx = x, x2 = x + pixel_width; cx < x2; cx++)
 			{
@@ -2520,13 +2520,13 @@
 	}
 }
 
-static void plane_debug_mode4(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void plane_debug_mode4(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
-	uint32_t bg_color = context->colors[(context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
+	pixel_t bg_color = context->colors[(context->regs[REG_BG_COLOR] & 0xF) + MODE4_OFFSET];
 	uint32_t address = (context->regs[REG_SCROLL_A] & 0xE) << 10;
 	for (uint32_t row_address = address, end = address + 32*32*2; row_address < end; row_address += 2 * 32)
 	{
-		uint32_t *col = fb;
+		pixel_t *col = fb;
 		for(uint32_t cur = row_address, row_end = row_address + 2 * 32; cur < row_end; cur += 2)
 		{
 			uint32_t mapped = mode4_address_map[cur];
@@ -2551,7 +2551,7 @@
 			} else {
 				tile_inc = 4;
 			}
-			uint32_t *line = col;
+			pixel_t *line = col;
 			for (int y = 0; y < 16; y++)
 			{
 				uint32_t first = mode4_address_map[tile_address];
@@ -2560,10 +2560,10 @@
 				pixels |= planar_to_chunky[context->vdpmem[first+1]];
 				pixels |= planar_to_chunky[context->vdpmem[last]] << 3;
 				pixels |= planar_to_chunky[context->vdpmem[last+1]] << 2;
-				uint32_t *out = line;
+				pixel_t *out = line;
 				for (uint32_t i = i_init; i != i_limit; i += i_inc)
 				{
-					uint32_t pixel = context->colors[((pixels >> i & 0xF) | pal) + MODE4_OFFSET];
+					pixel_t pixel = context->colors[((pixels >> i & 0xF) | pal) + MODE4_OFFSET];
 					*(out++) = pixel;
 					*(out++) = pixel;
 				}
@@ -2572,22 +2572,22 @@
 				if (y & 1) {
 					tile_address += tile_inc;
 				}
-				line += pitch / sizeof(uint32_t);
+				line += pitch / sizeof(pixel_t);
 			}
 			
 			
 			
 			col += 16;
 		}
-		fb += 16 * pitch / sizeof(uint32_t);
+		fb += 16 * pitch / sizeof(pixel_t);
 	}
 }
 
-static void sprite_debug_mode4(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void sprite_debug_mode4(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
 }
 
-uint32_t tms_map_color(vdp_context *context, uint8_t color)
+pixel_t tms_map_color(vdp_context *context, uint8_t color)
 {
 	if (context->type == VDP_GAMEGEAR) {
 		//Game Gear uses CRAM entries 16-31 for TMS9918A modes
@@ -2599,7 +2599,7 @@
 	}
 }
 
-static void plane_debug_tms(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void plane_debug_tms(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
 	uint16_t table_address = context->regs[REG_SCROLL_A] << 10 & 0x3C00;
 	uint16_t color_address = context->regs[REG_COLOR_TABLE] << 6;
@@ -2636,10 +2636,10 @@
 	}
 	for (uint32_t row = 0; row < 24; row++)
 	{
-		uint32_t *colfb = fb;
+		pixel_t *colfb = fb;
 		for (uint32_t col = 0; col < cols; col++)
 		{
-			uint32_t *linefb = colfb;
+			pixel_t *linefb = colfb;
 			uint8_t pattern = context->vdpmem[mode4_address_map[table_address] ^ 1];
 			uint16_t caddress = color_address;
 			uint16_t paddress = pattern_address;
@@ -2665,7 +2665,7 @@
 						bg = tms_map_color(context, colors & 0xF);
 					}
 					
-					uint32_t *curfb = linefb;
+					pixel_t *curfb = linefb;
 					for (uint32_t x = 0; x < pixels; x++)
 					{
 						*(curfb++) = (bits & 0x80) ? fg : bg;
@@ -2673,7 +2673,7 @@
 							bits <<= 1;
 						}
 					}
-					linefb += pitch / sizeof(uint32_t);
+					linefb += pitch / sizeof(pixel_t);
 					if (y & 1) {
 						if (context->regs[REG_MODE_1] & BIT_M3) {
 							caddress++;
@@ -2685,11 +2685,11 @@
 			table_address++;
 			colfb += pixels;
 		}
-		fb += 16 * pitch / sizeof(uint32_t);
+		fb += 16 * pitch / sizeof(pixel_t);
 	}
 }
 
-static void sprite_debug_tms(uint32_t *fb, uint32_t pitch, vdp_context *context)
+static void sprite_debug_tms(pixel_t *fb, uint32_t pitch, vdp_context *context)
 {
 }
 
@@ -2698,7 +2698,7 @@
 	if (context->enabled_debuggers & (1 << DEBUG_PLANE)) {
 		
 		uint32_t pitch;
-		uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[DEBUG_PLANE], &pitch);
+		pixel_t *fb = render_get_framebuffer(context->debug_fb_indices[DEBUG_PLANE], &pitch);
 		if (context->type == VDP_GENESIS && (context->regs[REG_MODE_2] & BIT_MODE_5)) {
 			if ((context->debug_modes[DEBUG_PLANE] & 3) == 3) {
 				sprite_debug_mode5(fb, pitch, context);
@@ -2723,7 +2723,7 @@
 
 	if (context->enabled_debuggers & (1 << DEBUG_VRAM)) {
 		uint32_t pitch;
-		uint32_t *fb = render_get_framebuffer(context->debug_fb_indices[DEBUG_VRAM], &pitch);
+		pixel_t *fb = render_get_framebuffer(context->debug_fb_indices[DEBUG_VRAM], &pitch);
 		if (context->type == VDP_GENESIS && (context->regs[REG_MODE_2] & BIT_MODE_5)) {
 			vram_debug_mode5(fb, pitch, context);
 		} else if (context->type != VDP_TMS9918A && (context->regs[REG_MODE_1] & BIT_MODE_4)) {
@@ -2736,12 +2736,13 @@
 
 	if (context->enabled_debuggers & (1 << DEBUG_CRAM)) {
 		uint32_t starting_line = 512 - 32*4;
-		uint32_t *line = context->debug_fbs[DEBUG_CRAM]
-			+ context->debug_fb_pitch[DEBUG_CRAM]  * starting_line / sizeof(uint32_t);
+		pixel_t *line = context->debug_fbs[DEBUG_CRAM]
+			+ context->debug_fb_pitch[DEBUG_CRAM]  * starting_line / sizeof(pixel_t);
+		pixel_t black = render_map_color(0, 0, 0);
 		if (context->regs[REG_MODE_2] & BIT_MODE_5) {
 			for (int pal = 0; pal < 4; pal ++)
 			{
-				uint32_t *cur;
+				pixel_t *cur;
 				for (int y = 0; y < 31; y++)
 				{
 					cur = line;
@@ -2751,21 +2752,21 @@
 						{
 							*(cur++) = context->colors[pal * 16 + offset];
 						}
-						*(cur++) = 0xFF000000;
+						*(cur++) = black;
 					}
-					line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t);
+					line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(pixel_t);
 				}
 				cur = line;
 				for (int x = 0; x < 512; x++)
 				{
-					*(cur++) = 0xFF000000;
+					*(cur++) = black;
 				}
-				line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t);
+				line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(pixel_t);
 			}
 		} else {
 			for (int pal = 0; pal < 2; pal ++)
 			{
-				uint32_t *cur;
+				pixel_t *cur;
 				for (int y = 0; y < 31; y++)
 				{
 					cur = line;
@@ -2775,16 +2776,16 @@
 						{
 							*(cur++) = context->colors[pal * 16 + offset];
 						}
-						*(cur++) = 0xFF000000;
+						*(cur++) = black;
 					}
-					line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t);
+					line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(pixel_t);
 				}
 				cur = line;
 				for (int x = 0; x < 512; x++)
 				{
-					*(cur++) = 0xFF000000;
+					*(cur++) = black;
 				}
-				line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(uint32_t);
+				line += context->debug_fb_pitch[DEBUG_CRAM] / sizeof(pixel_t);
 			}
 		}
 		render_framebuffer_updated(context->debug_fb_indices[DEBUG_CRAM], 512);
@@ -2869,7 +2870,7 @@
 		context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
 	}
 	output_line += context->top_offset;
-	context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * output_line);
+	context->output = (pixel_t *)(((char *)context->fb) + context->output_pitch * output_line);
 #ifdef DEBUG_FB_FILL
 	for (int i = 0; i < LINEBUF_SIZE; i++)
 	{
@@ -2894,7 +2895,7 @@
 	uint16_t lines_max = context->inactive_start + context->border_bot + context->border_top;
 	if (context->output_lines <= lines_max && context->output_lines > 0) {
 		context->fb = render_get_framebuffer(context->cur_buffer, &context->output_pitch);
-		context->output = (uint32_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1 + context->top_offset));
+		context->output = (pixel_t *)(((char *)context->fb) + context->output_pitch * (context->output_lines - 1 + context->top_offset));
 	} else {
 		context->output = NULL;
 	}
@@ -2965,7 +2966,7 @@
 #define CHECK_LIMIT if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } context->hslot++; context->cycles += slot_cycles; CHECK_ONLY
 #define OUTPUT_PIXEL(slot) if ((slot) >= BG_START_SLOT) {\
 		uint8_t *src = context->compositebuf + ((slot) - BG_START_SLOT) *2;\
-		uint32_t *dst = context->output + ((slot) - BG_START_SLOT) *2;\
+		pixel_t *dst = context->output + ((slot) - BG_START_SLOT) *2;\
 		if ((*src & 0x3F) | test_layer) {\
 			*(dst++) = context->colors[*(src++)];\
 		} else {\
@@ -2980,7 +2981,7 @@
 
 #define OUTPUT_PIXEL_H40(slot) if (slot <= (BG_START_SLOT + LINEBUF_SIZE/2)) {\
 		uint8_t *src = context->compositebuf + (slot - BG_START_SLOT) *2;\
-		uint32_t *dst = context->output + (slot - BG_START_SLOT) *2;\
+		pixel_t *dst = context->output + (slot - BG_START_SLOT) *2;\
 		if ((*src & 0x3F) | test_layer) {\
 			*(dst++) = context->colors[*(src++)];\
 		} else {\
@@ -2999,7 +3000,7 @@
 
 #define OUTPUT_PIXEL_H32(slot) if (slot <= (BG_START_SLOT + (256+HORIZ_BORDER)/2)) {\
 		uint8_t *src = context->compositebuf + (slot - BG_START_SLOT) *2;\
-		uint32_t *dst = context->output + (slot - BG_START_SLOT) *2;\
+		pixel_t *dst = context->output + (slot - BG_START_SLOT) *2;\
 		if ((*src & 0x3F) | test_layer) {\
 			*(dst++) = context->colors[*(src++)];\
 		} else {\
@@ -3020,7 +3021,7 @@
 //BG_START_SLOT + 13/2=6, dst = 6, src = border + comp + 13
 #define OUTPUT_PIXEL_MODE4(slot) if ((slot) >= BG_START_SLOT) {\
 		uint8_t *src = context->compositebuf + ((slot) - BG_START_SLOT) *2;\
-		uint32_t *dst = context->output + ((slot) - BG_START_SLOT) *2;\
+		pixel_t *dst = context->output + ((slot) - BG_START_SLOT) *2;\
 		if ((slot) - BG_START_SLOT < BORDER_LEFT/2) {\
 			*(dst++) = context->colors[bgindex];\
 			*(dst++) = context->colors[bgindex];\
@@ -3269,7 +3270,7 @@
 		render_sprite_cells_mode4(context);\
 		MODE4_CHECK_SLOT_LINE(CALC_SLOT(slot, 5))
 
-static uint32_t dummy_buffer[LINEBUF_SIZE];
+static pixel_t dummy_buffer[LINEBUF_SIZE];
 static void vdp_h40_line(vdp_context * context)
 {
 	uint16_t address;
@@ -3314,7 +3315,7 @@
 
 	//Do palette lookup for end of previous line
 	uint8_t *src = context->compositebuf + (LINE_CHANGE_H40 - BG_START_SLOT) *2;
-	uint32_t *dst = context->output + (LINE_CHANGE_H40 - BG_START_SLOT) *2;
+	pixel_t *dst = context->output + (LINE_CHANGE_H40 - BG_START_SLOT) *2;
 	if (context->output) {
 		if (test_layer) {
 			for (int i = 0; i < LINEBUF_SIZE - (LINE_CHANGE_H40 - BG_START_SLOT) * 2; i++)
@@ -4292,7 +4293,7 @@
 			return;
 		}
 	}
-	uint32_t color;
+	pixel_t color;
 	if (context->type == VDP_GAMEGEAR) {
 		//Game Gear uses CRAM entries 16-31 for TMS9918A modes
 		color = context->colors[(context->regs[REG_BG_COLOR] & 0xF) + 16 + MODE4_OFFSET];
@@ -4801,7 +4802,7 @@
 			active_line = 0x200;
 		}
 	}
-	uint32_t *dst;
+	pixel_t *dst;
 	uint8_t *debug_dst;
 	if (context->output && context->hslot >= BG_START_SLOT && context->hslot <= bg_end_slot) {
 		dst = context->output + 2 * (context->hslot - BG_START_SLOT);
@@ -4873,7 +4874,7 @@
 
 		if (dst) {
 			uint8_t bg_index;
-			uint32_t bg_color;
+			pixel_t bg_color;
 			if (mode_5) {
 				bg_index = context->regs[REG_BG_COLOR] & 0x3F;
 				bg_color = context->colors[bg_index];
--- a/vdp.h	Sat Mar 29 23:54:45 2025 -0700
+++ b/vdp.h	Sun Mar 30 00:06:53 2025 -0700
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include "system.h"
 #include "serialize.h"
+#include "pixel.h"
 
 #define VDP_REGS 24
 #define CRAM_SIZE 64
@@ -183,11 +184,11 @@
 struct vdp_context {
 	system_header  *system;
 	//pointer to current line in framebuffer
-	uint32_t       *output;
+	pixel_t        *output;
 	//pointer to current framebuffer
-	uint32_t       *fb;
+	pixel_t       *fb;
 	uint8_t        *done_composite;
-	uint32_t       *debug_fbs[NUM_DEBUG_TYPES];
+	pixel_t        *debug_fbs[NUM_DEBUG_TYPES];
 	char           *kmod_msg_buffer;
 	vdp_hook       dma_hook;
 	vdp_reg_hook   reg_hook;
@@ -203,8 +204,8 @@
 	uint32_t       address;
 	uint32_t       address_latch;
 	uint32_t       serial_address;
-	uint32_t       colors[CRAM_SIZE*4];
-	uint32_t       debugcolors[1 << (3 + 1 + 1 + 1)];//3 bits for source, 1 bit for priority, 1 bit for shadow, 1 bit for hilight
+	pixel_t        colors[CRAM_SIZE*4];
+	pixel_t        debugcolors[1 << (3 + 1 + 1 + 1)];//3 bits for source, 1 bit for priority, 1 bit for shadow, 1 bit for hilight
 	uint16_t       cram[CRAM_SIZE];
 	uint32_t       frame;
 	uint32_t       vsram_size;