The display API provides access to the emulator’s screen output, LCD power state, and backlight level.
emu_framebuffer
Get a pointer to the framebuffer data.
const uint32_t* emu_framebuffer(const SyncEmu* emu, int32_t* w, int32_t* h);
Parameters
- emu: Pointer to emulator instance
- w: Output pointer for width (may be NULL)
- h: Output pointer for height (may be NULL)
Returns
- Non-null pointer: Pointer to framebuffer data (ARGB8888 format)
- NULL: Invalid emulator pointer
Description
Returns a pointer to the emulator’s framebuffer containing the current screen contents. The framebuffer is in ARGB8888 format (32 bits per pixel) with dimensions typically 320x240.
The width and height are written to the provided pointers if they are non-null.
Important: The returned pointer is only valid during the function call. The internal mutex protects the data, but you must copy the framebuffer data immediately. Do not store the pointer for later use.
- Format: ARGB8888 (32-bit)
- Byte order:
[AA BB GG RR] in memory
- Size:
width * height * 4 bytes
- Layout: Row-major, top-to-bottom
// Pixel format
typedef union {
uint32_t value;
struct {
uint8_t r; // Red
uint8_t g; // Green
uint8_t b; // Blue
uint8_t a; // Alpha (always 0xFF)
};
} Pixel;
Example: Copy Framebuffer
#include <string.h>
#include <stdint.h>
void copy_framebuffer(SyncEmu* emu, uint32_t* dest_buffer) {
int32_t width, height;
const uint32_t* fb = emu_framebuffer(emu, &width, &height);
if (!fb) {
fprintf(stderr, "Failed to get framebuffer\n");
return;
}
size_t size = width * height * sizeof(uint32_t);
memcpy(dest_buffer, fb, size);
printf("Copied %dx%d framebuffer (%zu bytes)\n",
width, height, size);
}
Example: Render to SDL2
#include <SDL2/SDL.h>
void render_to_sdl(SyncEmu* emu, SDL_Renderer* renderer, SDL_Texture* texture) {
int32_t width, height;
const uint32_t* fb = emu_framebuffer(emu, &width, &height);
if (!fb) return;
// Update SDL texture with framebuffer data
SDL_UpdateTexture(texture, NULL, fb, width * sizeof(uint32_t));
// Render to screen
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
// Setup
SDL_Texture* setup_sdl_texture(SDL_Renderer* renderer) {
return SDL_CreateTexture(
renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
320, 240
);
}
Example: Save to PNG
// Using stb_image_write.h
#include "stb_image_write.h"
void save_screenshot(SyncEmu* emu, const char* filename) {
int32_t width, height;
const uint32_t* fb = emu_framebuffer(emu, &width, &height);
if (!fb) {
fprintf(stderr, "Failed to get framebuffer\n");
return;
}
// Copy framebuffer (can't use directly due to mutex)
size_t size = width * height * sizeof(uint32_t);
uint32_t* buffer = malloc(size);
memcpy(buffer, fb, size);
// Convert ARGB to RGBA for PNG
for (int i = 0; i < width * height; i++) {
uint32_t pixel = buffer[i];
uint8_t* p = (uint8_t*)&buffer[i];
uint8_t r = (pixel >> 0) & 0xFF;
uint8_t g = (pixel >> 8) & 0xFF;
uint8_t b = (pixel >> 16) & 0xFF;
uint8_t a = (pixel >> 24) & 0xFF;
p[0] = r; p[1] = g; p[2] = b; p[3] = a;
}
stbi_write_png(filename, width, height, 4, buffer, width * 4);
free(buffer);
printf("Saved screenshot to %s\n", filename);
}
emu_is_lcd_on
Check if the LCD is powered on and should display content.
int emu_is_lcd_on(const SyncEmu* emu);
Parameters
- emu: Pointer to emulator instance
Returns
- 1: LCD is on (display should show content)
- 0: LCD is off (display should be blank)
Description
Returns whether the LCD is currently powered on. The LCD is considered off when either:
- Control port 0x05 bit 4 is clear (master LCD disable), OR
- LCD control register bit 11 is clear (LCD controller disable)
When the LCD is off, the screen should be rendered as black or blank, regardless of framebuffer contents.
Example: Conditional Rendering
void render_display(SyncEmu* emu, SDL_Renderer* renderer, SDL_Texture* texture) {
if (emu_is_lcd_on(emu)) {
// LCD is on - render framebuffer
int32_t width, height;
const uint32_t* fb = emu_framebuffer(emu, &width, &height);
SDL_UpdateTexture(texture, NULL, fb, width * sizeof(uint32_t));
SDL_RenderCopy(renderer, texture, NULL, NULL);
} else {
// LCD is off - render blank screen
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
}
SDL_RenderPresent(renderer);
}
Example: Power Indicator
void draw_lcd_indicator(SyncEmu* emu) {
int is_on = emu_is_lcd_on(emu);
printf("LCD: %s\n", is_on ? "ON" : "OFF");
// Draw power indicator LED
draw_led(is_on ? GREEN : RED);
}
emu_get_backlight
Get the current backlight brightness level.
uint8_t emu_get_backlight(const SyncEmu* emu);
Parameters
- emu: Pointer to emulator instance
Returns
- 0-255: Backlight brightness level (0 = off, 255 = maximum)
- 0: If emulator pointer is null
Description
Returns the current backlight brightness level from the LCD controller. The value ranges from 0 (backlight off) to 255 (maximum brightness).
The actual TI-84 Plus CE typically uses levels 0-100, but the hardware register is 8-bit.
Example: Apply Backlight
void apply_backlight(SyncEmu* emu, uint32_t* display_buffer,
int width, int height) {
uint8_t backlight = emu_get_backlight(emu);
float brightness = backlight / 255.0f;
// Darken display based on backlight level
for (int i = 0; i < width * height; i++) {
uint32_t pixel = display_buffer[i];
uint8_t* p = (uint8_t*)&display_buffer[i];
p[0] = (uint8_t)(p[0] * brightness); // R
p[1] = (uint8_t)(p[1] * brightness); // G
p[2] = (uint8_t)(p[2] * brightness); // B
}
}
Example: Backlight Overlay
// Show backlight level as overlay
void draw_backlight_indicator(SyncEmu* emu) {
uint8_t level = emu_get_backlight(emu);
printf("Backlight: %d/255 (%.1f%%)\n",
level, (level / 255.0f) * 100.0f);
// Draw brightness bar
int bar_width = (level * 200) / 255;
draw_bar(10, 10, bar_width, 20, GREEN);
}
Example: Realistic Display Simulation
void render_realistic_display(SyncEmu* emu, uint32_t* output) {
int32_t width, height;
const uint32_t* fb = emu_framebuffer(emu, &width, &height);
// Check LCD power
if (!emu_is_lcd_on(emu)) {
// LCD off - show black screen with faint reflection
memset(output, 0x00101010, width * height * 4);
return;
}
// Copy framebuffer
memcpy(output, fb, width * height * 4);
// Apply backlight brightness
uint8_t backlight = emu_get_backlight(emu);
float brightness = backlight / 255.0f;
// Add ambient light (screen is visible even with backlight off)
const float ambient = 0.05f; // 5% ambient visibility
float total_brightness = ambient + (brightness * (1.0f - ambient));
for (int i = 0; i < width * height; i++) {
uint8_t* p = (uint8_t*)&output[i];
p[0] = (uint8_t)(p[0] * total_brightness);
p[1] = (uint8_t)(p[1] * total_brightness);
p[2] = (uint8_t)(p[2] * total_brightness);
}
}
Complete Display Example
#include <SDL2/SDL.h>
#include <stdio.h>
#include <stdint.h>
typedef struct {
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Texture* texture;
uint32_t* buffer;
int width;
int height;
} Display;
Display* display_init(void) {
Display* d = malloc(sizeof(Display));
d->width = 320;
d->height = 240;
SDL_Init(SDL_INIT_VIDEO);
d->window = SDL_CreateWindow(
"TI-84 Plus CE",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
d->width * 2, d->height * 2, // 2x scale
SDL_WINDOW_SHOWN
);
d->renderer = SDL_CreateRenderer(d->window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
d->texture = SDL_CreateTexture(
d->renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
d->width, d->height
);
d->buffer = malloc(d->width * d->height * sizeof(uint32_t));
return d;
}
void display_render(Display* d, SyncEmu* emu) {
// Check if LCD is on
if (!emu_is_lcd_on(emu)) {
// Render black screen
memset(d->buffer, 0, d->width * d->height * 4);
} else {
// Get framebuffer
int32_t fb_width, fb_height;
const uint32_t* fb = emu_framebuffer(emu, &fb_width, &fb_height);
memcpy(d->buffer, fb, d->width * d->height * 4);
// Apply backlight
uint8_t backlight = emu_get_backlight(emu);
float brightness = (backlight / 255.0f) * 0.95f + 0.05f;
for (int i = 0; i < d->width * d->height; i++) {
uint8_t* p = (uint8_t*)&d->buffer[i];
p[0] = (uint8_t)(p[0] * brightness);
p[1] = (uint8_t)(p[1] * brightness);
p[2] = (uint8_t)(p[2] * brightness);
}
}
// Render to screen
SDL_UpdateTexture(d->texture, NULL, d->buffer,
d->width * sizeof(uint32_t));
SDL_RenderClear(d->renderer);
SDL_RenderCopy(d->renderer, d->texture, NULL, NULL);
SDL_RenderPresent(d->renderer);
}
void display_destroy(Display* d) {
free(d->buffer);
SDL_DestroyTexture(d->texture);
SDL_DestroyRenderer(d->renderer);
SDL_DestroyWindow(d->window);
free(d);
SDL_Quit();
}
See Also