mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-12-26 00:31:44 +00:00
Beginning to refactor display and backend video
- Futher disentangle display, interface, and video backends - Backend video owns the staging/intermediate framebuffer for now - Add the beginnings of display update callbacks
This commit is contained in:
parent
0801c9f010
commit
f8b4602fca
@ -31,12 +31,13 @@ ASM_SRC_x86 = \
|
||||
src/x86/glue.S src/x86/cpu.S
|
||||
|
||||
VIDEO_SRC = \
|
||||
src/video/xvideo.c \
|
||||
src/video/glvideo.c \
|
||||
src/video/glutinput.c \
|
||||
src/video/glnode.c \
|
||||
src/video/glhudmodel.c \
|
||||
src/video/glalert.c \
|
||||
src/video/video.c \
|
||||
src/video/xvideo.c \
|
||||
src/video_util/matrixUtil.c \
|
||||
src/video_util/modelUtil.c \
|
||||
src/video_util/sourceUtil.c \
|
||||
|
25
configure.ac
25
configure.ac
@ -170,6 +170,15 @@ dnl Video ...
|
||||
AC_PATH_XTRA
|
||||
|
||||
|
||||
VIDEO_O="src/video/video.o"
|
||||
testcpu_VIDEO_O="src/video/video.o"
|
||||
testdisk_VIDEO_O="src/video/video.o"
|
||||
testdisplay_VIDEO_O="src/video/video.o"
|
||||
testprefs_VIDEO_O="src/video/video.o"
|
||||
testtrace_VIDEO_O="src/video/video.o"
|
||||
testui_VIDEO_O="src/video/video.o"
|
||||
testvm_VIDEO_O="src/video/video.o"
|
||||
|
||||
AC_ARG_ENABLE([opengl], AS_HELP_STRING([--enable-opengl], [Enable OpenGL graphics output (autodetected)]))
|
||||
AC_CHECK_HEADER(GL/glew.h, [
|
||||
AC_CHECK_HEADER(GL/freeglut.h, [
|
||||
@ -181,15 +190,15 @@ AC_CHECK_HEADER(GL/glew.h, [
|
||||
|
||||
AC_DEFINE(VIDEO_OPENGL, 1, [Building with OpenGL support])
|
||||
AC_DEFINE(USE_GLUT, 1, [Use GLUT library])
|
||||
VIDEO_O="src/video/glvideo.o src/video/glnode.o src/video/glalert.o src/video/glhudmodel.o src/video/glutinput.o src/video_util/matrixUtil.o src/video_util/modelUtil.o src/video_util/sourceUtil.o src/video_util/vectorUtil.o"
|
||||
VIDEO_O="$VIDEO_O src/video/glvideo.o src/video/glnode.o src/video/glalert.o src/video/glhudmodel.o src/video/glutinput.o src/video_util/matrixUtil.o src/video_util/modelUtil.o src/video_util/sourceUtil.o src/video_util/vectorUtil.o"
|
||||
dnl HACK there's gotta be a better way ... without this verbosity, CFLAGS are not correct (lacking -DTESTING=1 , etc) if we don't specify specific obj files for test binaries
|
||||
testcpu_VIDEO_O="src/video/testcpu-glvideo.o src/video/testcpu-glnode.o src/video/testcpu-glalert.o src/video/testcpu-glhudmodel.o src/video/testcpu-glutinput.o src/video_util/testcpu-matrixUtil.o src/video_util/testcpu-modelUtil.o src/video_util/testcpu-sourceUtil.o src/video_util/testcpu-vectorUtil.o"
|
||||
testdisk_VIDEO_O="src/video/testdisk-glvideo.o src/video/testdisk-glnode.o src/video/testdisk-glalert.o src/video/testdisk-glhudmodel.o src/video/testdisk-glutinput.o src/video_util/testdisk-matrixUtil.o src/video_util/testdisk-modelUtil.o src/video_util/testdisk-sourceUtil.o src/video_util/testdisk-vectorUtil.o"
|
||||
testdisplay_VIDEO_O="src/video/testdisplay-glvideo.o src/video/testdisplay-glnode.o src/video/testdisplay-glalert.o src/video/testdisplay-glhudmodel.o src/video/testdisplay-glutinput.o src/video_util/testdisplay-matrixUtil.o src/video_util/testdisplay-modelUtil.o src/video_util/testdisplay-sourceUtil.o src/video_util/testdisplay-vectorUtil.o"
|
||||
testprefs_VIDEO_O="src/video/testprefs-glvideo.o src/video/testprefs-glnode.o src/video/testprefs-glalert.o src/video/testprefs-glhudmodel.o src/video/testprefs-glutinput.o src/video_util/testprefs-matrixUtil.o src/video_util/testprefs-modelUtil.o src/video_util/testprefs-sourceUtil.o src/video_util/testprefs-vectorUtil.o"
|
||||
testtrace_VIDEO_O="src/video/testtrace-glvideo.o src/video/testtrace-glnode.o src/video/testtrace-glalert.o src/video/testtrace-glhudmodel.o src/video/testtrace-glutinput.o src/video_util/testtrace-matrixUtil.o src/video_util/testtrace-modelUtil.o src/video_util/testtrace-sourceUtil.o src/video_util/testtrace-vectorUtil.o"
|
||||
testui_VIDEO_O="src/video/testui-glvideo.o src/video/testui-glnode.o src/video/testui-glalert.o src/video/testui-glhudmodel.o src/video/testui-glutinput.o src/video_util/testui-matrixUtil.o src/video_util/testui-modelUtil.o src/video_util/testui-sourceUtil.o src/video_util/testui-vectorUtil.o"
|
||||
testvm_VIDEO_O="src/video/testvm-glvideo.o src/video/testvm-glnode.o src/video/testvm-glalert.o src/video/testvm-glhudmodel.o src/video/testvm-glutinput.o src/video_util/testvm-matrixUtil.o src/video_util/testvm-modelUtil.o src/video_util/testvm-sourceUtil.o src/video_util/testvm-vectorUtil.o"
|
||||
testcpu_VIDEO_O="$testcpu_VIDEO_O src/video/testcpu-glvideo.o src/video/testcpu-glnode.o src/video/testcpu-glalert.o src/video/testcpu-glhudmodel.o src/video/testcpu-glutinput.o src/video_util/testcpu-matrixUtil.o src/video_util/testcpu-modelUtil.o src/video_util/testcpu-sourceUtil.o src/video_util/testcpu-vectorUtil.o"
|
||||
testdisk_VIDEO_O="$testdisk_VIDEO_O src/video/testdisk-glvideo.o src/video/testdisk-glnode.o src/video/testdisk-glalert.o src/video/testdisk-glhudmodel.o src/video/testdisk-glutinput.o src/video_util/testdisk-matrixUtil.o src/video_util/testdisk-modelUtil.o src/video_util/testdisk-sourceUtil.o src/video_util/testdisk-vectorUtil.o"
|
||||
testdisplay_VIDEO_O="$testdisplay_VIDEO_O src/video/testdisplay-glvideo.o src/video/testdisplay-glnode.o src/video/testdisplay-glalert.o src/video/testdisplay-glhudmodel.o src/video/testdisplay-glutinput.o src/video_util/testdisplay-matrixUtil.o src/video_util/testdisplay-modelUtil.o src/video_util/testdisplay-sourceUtil.o src/video_util/testdisplay-vectorUtil.o"
|
||||
testprefs_VIDEO_O="$testprefs_VIDEO_O src/video/testprefs-glvideo.o src/video/testprefs-glnode.o src/video/testprefs-glalert.o src/video/testprefs-glhudmodel.o src/video/testprefs-glutinput.o src/video_util/testprefs-matrixUtil.o src/video_util/testprefs-modelUtil.o src/video_util/testprefs-sourceUtil.o src/video_util/testprefs-vectorUtil.o"
|
||||
testtrace_VIDEO_O="$testtrace_VIDEO_O src/video/testtrace-glvideo.o src/video/testtrace-glnode.o src/video/testtrace-glalert.o src/video/testtrace-glhudmodel.o src/video/testtrace-glutinput.o src/video_util/testtrace-matrixUtil.o src/video_util/testtrace-modelUtil.o src/video_util/testtrace-sourceUtil.o src/video_util/testtrace-vectorUtil.o"
|
||||
testui_VIDEO_O="$testui_VIDEO_O src/video/testui-glvideo.o src/video/testui-glnode.o src/video/testui-glalert.o src/video/testui-glhudmodel.o src/video/testui-glutinput.o src/video_util/testui-matrixUtil.o src/video_util/testui-modelUtil.o src/video_util/testui-sourceUtil.o src/video_util/testui-vectorUtil.o"
|
||||
testvm_VIDEO_O="$testvm_VIDEO_O src/video/testvm-glvideo.o src/video/testvm-glnode.o src/video/testvm-glalert.o src/video/testvm-glhudmodel.o src/video/testvm-glutinput.o src/video_util/testvm-matrixUtil.o src/video_util/testvm-modelUtil.o src/video_util/testvm-sourceUtil.o src/video_util/testvm-vectorUtil.o"
|
||||
AC_MSG_RESULT([Building emulator with OpenGL support, w00t!])
|
||||
])
|
||||
], [
|
||||
|
@ -72,9 +72,10 @@
|
||||
#include "vm.h"
|
||||
#include "timing.h"
|
||||
#include "cpu.h"
|
||||
#include "display.h"
|
||||
#include "disk.h"
|
||||
#include "interface.h"
|
||||
#include "display.h"
|
||||
#include "video/video.h"
|
||||
#include "disk.h"
|
||||
#include "keys.h"
|
||||
#include "joystick.h"
|
||||
#include "glue.h"
|
||||
|
333
src/display.c
333
src/display.c
@ -21,25 +21,8 @@
|
||||
|
||||
#define DYNAMIC_SZ 11 // 7 pixels (as bytes) + 2pre + 2post
|
||||
|
||||
typedef enum drawpage_mode_t {
|
||||
DRAWPAGE_TEXT = 1,
|
||||
DRAWPAGE_HIRES,
|
||||
} drawpage_mode_t;
|
||||
|
||||
typedef struct backend_node_s {
|
||||
struct backend_node_s *next;
|
||||
long order;
|
||||
video_backend_s *backend;
|
||||
} backend_node_s;
|
||||
|
||||
static backend_node_s *head = NULL;
|
||||
|
||||
// framebuffers
|
||||
static uint8_t *video__fb = NULL;
|
||||
|
||||
A2Color_s colormap[256] = { { 0 } };
|
||||
static pthread_t render_thread_id = 0;
|
||||
static pthread_mutex_t video_scan_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t display_scan_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static uint8_t video__wider_font[0x8000] = { 0 };
|
||||
static uint8_t video__font[0x4000] = { 0 };
|
||||
@ -48,13 +31,13 @@ static uint8_t video__int_font[3][0x4000] = { { 0 } }; // interface font
|
||||
static color_mode_t color_mode = COLOR_NONE;
|
||||
|
||||
// Precalculated framebuffer offsets given VM addr
|
||||
unsigned int video__screen_addresses[8192] = { INT_MIN };
|
||||
uint8_t video__columns[8192] = { 0 };
|
||||
static unsigned int video__screen_addresses[8192] = { INT_MIN };
|
||||
static uint8_t video__columns[8192] = { 0 };
|
||||
|
||||
uint8_t video__hires_even[0x800] = { 0 };
|
||||
uint8_t video__hires_odd[0x800] = { 0 };
|
||||
static uint8_t video__hires_even[0x800] = { 0 };
|
||||
static uint8_t video__hires_odd[0x800] = { 0 };
|
||||
|
||||
volatile unsigned long _vid_dirty = 0;
|
||||
static volatile unsigned long _vid_dirty = 0;
|
||||
|
||||
// Video constants -- sourced from AppleWin
|
||||
static const bool bVideoScannerNTSC = true;
|
||||
@ -74,17 +57,20 @@ static const int kVLine0State = 0x100; // V[543210CBA] = 100000000
|
||||
static const int kVPresetLine = 256; // line when V state presets
|
||||
static const int kVSyncLines = 4; // lines per VSync duration
|
||||
|
||||
uint8_t video__odd_colors[2] = { COLOR_LIGHT_PURPLE, COLOR_LIGHT_BLUE };
|
||||
uint8_t video__even_colors[2] = { COLOR_LIGHT_GREEN, COLOR_LIGHT_RED };
|
||||
static uint8_t video__odd_colors[2] = { COLOR_LIGHT_PURPLE, COLOR_LIGHT_BLUE };
|
||||
static uint8_t video__even_colors[2] = { COLOR_LIGHT_GREEN, COLOR_LIGHT_RED };
|
||||
|
||||
static display_update_fn textCallbackFn = NULL;
|
||||
static display_update_fn hiresCallbackFn = NULL;
|
||||
|
||||
// 40col/80col/lores/hires/dhires line offsets
|
||||
unsigned short video__line_offset[TEXT_ROWS] = {
|
||||
static uint16_t video__line_offset[TEXT_ROWS] = {
|
||||
0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380,
|
||||
0x028, 0x0A8, 0x128, 0x1A8, 0x228, 0x2A8, 0x328, 0x3A8,
|
||||
0x050, 0x0D0, 0x150, 0x1D0, 0x250, 0x2D0, 0x350, 0x3D0
|
||||
};
|
||||
|
||||
uint8_t video__dhires1[256] = {
|
||||
static uint8_t video__dhires1[256] = {
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
@ -95,7 +81,7 @@ uint8_t video__dhires1[256] = {
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
};
|
||||
|
||||
uint8_t video__dhires2[256] = {
|
||||
static uint8_t video__dhires2[256] = {
|
||||
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,
|
||||
0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,
|
||||
0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,
|
||||
@ -492,16 +478,16 @@ static void video_prefsChanged(const char *domain) {
|
||||
val = COLOR_INTERP;
|
||||
}
|
||||
color_mode = (color_mode_t)val;
|
||||
video_reset();
|
||||
display_reset();
|
||||
}
|
||||
|
||||
void video_reset(void) {
|
||||
void display_reset(void) {
|
||||
_initialize_hires_values();
|
||||
_initialize_tables_video();
|
||||
video_setDirty(A2_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
void video_loadfont(int first, int quantity, const uint8_t *data, int mode) {
|
||||
void display_loadFont(int first, int quantity, const uint8_t *data, int mode) {
|
||||
uint8_t fg = 0;
|
||||
uint8_t bg = 0;
|
||||
switch (mode) {
|
||||
@ -646,11 +632,11 @@ static inline void __plot_character40(const unsigned int font_off, uint8_t *fb_p
|
||||
_plot_char40(/*dst*/&fb_ptr, /*src*/&font_ptr);
|
||||
}
|
||||
|
||||
static void _plot_character40(uint16_t off, int page, int bank) {
|
||||
static void _plot_character40(uint16_t off, int page, int bank, uint8_t *fb_ptr) {
|
||||
uint16_t base = page ? 0x0800 : 0x0400;
|
||||
uint16_t ea = base+off;
|
||||
uint8_t b = apple_ii_64k[bank][ea];
|
||||
__plot_character40(b<<7/* *128 */, video__fb+video__screen_addresses[off]);
|
||||
__plot_character40(b<<7/* *128 */, fb_ptr+video__screen_addresses[off]);
|
||||
}
|
||||
|
||||
static inline void __plot_character80(const unsigned int font_off, uint8_t *fb_ptr) {
|
||||
@ -665,16 +651,16 @@ static inline void __plot_character80(const unsigned int font_off, uint8_t *fb_p
|
||||
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
|
||||
}
|
||||
|
||||
static void _plot_character80(uint16_t off, int page, int bank) {
|
||||
static void _plot_character80(uint16_t off, int page, int bank, uint8_t *fb_ptr) {
|
||||
uint16_t base = page ? 0x0800 : 0x0400;
|
||||
uint16_t ea = base+off;
|
||||
{
|
||||
uint8_t b = apple_ii_64k[1][ea];
|
||||
__plot_character80(b<<6/* *64 */, video__fb+video__screen_addresses[off]);
|
||||
__plot_character80(b<<6/* *64 */, fb_ptr+video__screen_addresses[off]);
|
||||
}
|
||||
{
|
||||
uint8_t b = apple_ii_64k[0][ea];
|
||||
__plot_character80(b<<6/* *64 */, video__fb+video__screen_addresses[off]+7);
|
||||
__plot_character80(b<<6/* *64 */, fb_ptr+video__screen_addresses[off]+7);
|
||||
}
|
||||
}
|
||||
|
||||
@ -703,11 +689,11 @@ static inline void __plot_block40(const uint8_t val, uint8_t *fb_ptr) {
|
||||
_plot_lores40(/*dst*/&fb_ptr, val32);
|
||||
}
|
||||
|
||||
static void _plot_block40(uint16_t off, int page, int bank) {
|
||||
static void _plot_block40(uint16_t off, int page, int bank, uint8_t *fb_ptr) {
|
||||
uint16_t base = page ? 0x0800 : 0x0400;
|
||||
uint16_t ea = base+off;
|
||||
uint8_t b = apple_ii_64k[bank][ea];
|
||||
__plot_block40(b, video__fb+video__screen_addresses[off]);
|
||||
__plot_block40(b, fb_ptr+video__screen_addresses[off]);
|
||||
}
|
||||
|
||||
static inline void __plot_block80(const uint8_t val, uint8_t *fb_ptr) {
|
||||
@ -747,7 +733,7 @@ static inline uint8_t __shift_block80(uint8_t b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
static void _plot_block80(uint16_t off, int page, int bank) {
|
||||
static void _plot_block80(uint16_t off, int page, int bank, uint8_t *fb_ptr) {
|
||||
uint16_t base = page ? 0x0800 : 0x0400;
|
||||
uint16_t ea = base+off;
|
||||
|
||||
@ -757,20 +743,20 @@ static void _plot_block80(uint16_t off, int page, int bank) {
|
||||
{
|
||||
uint8_t b = apple_ii_64k[1][ea];
|
||||
b = __shift_block80(b);
|
||||
uint8_t *fb = video__fb+video__screen_addresses[off];
|
||||
uint8_t *fb = fb_ptr+video__screen_addresses[off];
|
||||
__plot_block80(b, fb);
|
||||
}
|
||||
|
||||
// plot odd half-block from main mem
|
||||
{
|
||||
uint8_t b = apple_ii_64k[0][ea];
|
||||
uint8_t *fb = video__fb+video__screen_addresses[off] + 7;
|
||||
uint8_t *fb = fb_ptr+video__screen_addresses[off] + 7;
|
||||
__plot_block80(b, fb);
|
||||
}
|
||||
}
|
||||
|
||||
static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(uint16_t, int, int) {
|
||||
void (*plotFn)(uint16_t, int, int) = NULL;
|
||||
static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(uint16_t, int, int, uint8_t*) {
|
||||
void (*plotFn)(uint16_t, int, int, uint8_t*) = NULL;
|
||||
|
||||
if (currswitches & txtflags) {
|
||||
plotFn = (currswitches & SS_80COL) ? _plot_character80 : _plot_character40;
|
||||
@ -874,16 +860,59 @@ GLUE_C_WRITE(video__write_2e_text1_mixed)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Classic interface and printing HUD messages
|
||||
|
||||
void interface_plotChar(uint8_t *fboff, int fb_pix_width, interface_colorscheme_t cs, uint8_t c) {
|
||||
static void _display_plotChar(uint8_t *fboff, const unsigned int fbPixWidth, const interface_colorscheme_t cs, const uint8_t c) {
|
||||
uint8_t *src = video__int_font[cs] + c * (FONT_GLYPH_X*FONT_GLYPH_Y);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fb_pix_width);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
_plot_char80(&fboff, &src, fbPixWidth);
|
||||
}
|
||||
|
||||
void display_plotChar(uint8_t *fb, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const uint8_t c) {
|
||||
assert(col < 80);
|
||||
assert(row < 24);
|
||||
|
||||
if (textCallbackFn) {
|
||||
textCallbackFn((pixel_delta_t){ .row = row, .col = col, .b = c, .cs = cs });
|
||||
}
|
||||
|
||||
if (fb) {
|
||||
unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _INTERPOLATED_PIXEL_ADJUSTMENT_PRE;
|
||||
_display_plotChar(fb+off, SCANWIDTH, cs, c);
|
||||
video_setDirty(FB_DIRTY_FLAG);
|
||||
}
|
||||
}
|
||||
|
||||
static void _display_plotLine(uint8_t *fb, const unsigned int fbPixWidth, const unsigned int xAdjust, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *line) {
|
||||
for (uint8_t x=col; *line; x++, line++) {
|
||||
char c = *line;
|
||||
|
||||
if (fb) {
|
||||
unsigned int off = row * fbPixWidth * FONT_HEIGHT_PIXELS + x * FONT80_WIDTH_PIXELS + xAdjust;
|
||||
_display_plotChar(fb+off, fbPixWidth, cs, c);
|
||||
} else if (textCallbackFn) {
|
||||
textCallbackFn((pixel_delta_t){ .row = row, .col = x, .b = c, .cs = cs });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void display_plotLine(uint8_t *fb, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *message) {
|
||||
_display_plotLine(fb, /*fbPixWidth:*/SCANWIDTH, /*xAdjust:*/_INTERPOLATED_PIXEL_ADJUSTMENT_PRE, col, row, cs, message);
|
||||
video_setDirty(FB_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
void display_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, const char *message, const uint8_t message_cols, const uint8_t message_rows) {
|
||||
assert(message_cols < 80);
|
||||
assert(message_rows < 24);
|
||||
|
||||
int fbPixWidth = (message_cols*FONT80_WIDTH_PIXELS);
|
||||
for (int row=0, idx=0; row<message_rows; row++, idx+=message_cols+1) {
|
||||
_display_plotLine(fb, fbPixWidth, /*xAdjust:*/0, /*col:*/0, row, cs, &message[ idx ]);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -897,11 +926,11 @@ static inline void __plot_hires80_pixels(uint8_t idx, uint8_t *fb_ptr) {
|
||||
*((uint32_t *)(fb_ptr+SCANWIDTH)) = b;
|
||||
}
|
||||
|
||||
static inline void __plot_hires80(uint16_t base, uint16_t ea) {
|
||||
static inline void __plot_hires80(uint16_t base, uint16_t ea, uint8_t *fb_ptr) {
|
||||
ea &= ~0x1;
|
||||
|
||||
uint16_t memoff = ea - base;
|
||||
uint8_t *fb_ptr = video__fb+video__screen_addresses[memoff];
|
||||
fb_ptr = fb_ptr+video__screen_addresses[memoff];
|
||||
uint8_t col = video__columns[memoff];
|
||||
|
||||
uint8_t b0 = 0x0;
|
||||
@ -966,10 +995,10 @@ static inline void __plot_hires80(uint16_t base, uint16_t ea) {
|
||||
__plot_hires80_pixels(b, fb_ptr);
|
||||
}
|
||||
|
||||
static void _plot_hires80(uint16_t off, int page, int bank, bool is_even) {
|
||||
static void _plot_hires80(uint16_t off, int page, int bank, bool is_even, uint8_t *fb_ptr) {
|
||||
uint16_t base = page ? 0x4000 : 0x2000;
|
||||
uint16_t ea = base+off;
|
||||
__plot_hires80(base, ea);
|
||||
__plot_hires80(base, ea, fb_ptr);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -1016,12 +1045,12 @@ static inline void _plot_hires_pixels(uint8_t *dst, const uint8_t *src) {
|
||||
}
|
||||
}
|
||||
|
||||
static void _plot_hires40(uint16_t off, int page, int bank, bool is_even) {
|
||||
static void _plot_hires40(uint16_t off, int page, int bank, bool is_even, uint8_t *fb_ptr) {
|
||||
uint16_t base = page ? 0x4000 : 0x2000;
|
||||
uint16_t ea = base+off;
|
||||
uint8_t b = apple_ii_64k[bank][ea];
|
||||
|
||||
uint8_t *fb_ptr = video__fb+video__screen_addresses[off];
|
||||
fb_ptr = fb_ptr+video__screen_addresses[off];
|
||||
|
||||
uint8_t _buf[DYNAMIC_SZ] = { 0 };
|
||||
uint8_t *color_buf = (uint8_t *)_buf; // <--- work around for -Wstrict-aliasing
|
||||
@ -1099,7 +1128,7 @@ static void _plot_hires40(uint16_t off, int page, int bank, bool is_even) {
|
||||
_plot_hires_pixels(fb_ptr-4, color_buf);
|
||||
}
|
||||
|
||||
static void (*_hirespage_plotter(uint32_t currswitches))(uint16_t, int, int, bool) {
|
||||
static void (*_hirespage_plotter(uint32_t currswitches))(uint16_t, int, int, bool, uint8_t*) {
|
||||
return ((currswitches & SS_80COL) && (currswitches & SS_DHIRES)) ? _plot_hires80 : _plot_hires40;
|
||||
}
|
||||
|
||||
@ -1201,93 +1230,6 @@ GLUE_C_WRITE(video__write_2e_odd1_mixed)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void video_init(void) {
|
||||
assert(pthread_self() != cpu_thread_id);
|
||||
LOG("(re)setting render_thread_id : %ld -> %ld", render_thread_id, pthread_self());
|
||||
render_thread_id = pthread_self();
|
||||
|
||||
assert(!video__fb);
|
||||
video__fb = MALLOC(SCANWIDTH*SCANHEIGHT*sizeof(uint8_t));
|
||||
video_clear();
|
||||
|
||||
video_getCurrentBackend()->init((void*)0);
|
||||
}
|
||||
|
||||
void _video_setRenderThread(pthread_t id) {
|
||||
LOG("setting render_thread_id : %ld -> %ld", render_thread_id, id);
|
||||
render_thread_id = id;
|
||||
}
|
||||
|
||||
bool video_isRenderThread(void) {
|
||||
return (pthread_self() == render_thread_id);
|
||||
}
|
||||
|
||||
void video_shutdown(void) {
|
||||
|
||||
#if MOBILE_DEVICE
|
||||
// WARNING : shutdown should occur on the render thread. Platform code (iOS, Android) should ensure this is called
|
||||
// from within a render pass...
|
||||
assert(!render_thread_id || pthread_self() == render_thread_id);
|
||||
#endif
|
||||
|
||||
video_getCurrentBackend()->shutdown();
|
||||
|
||||
if (pthread_self() == render_thread_id) {
|
||||
FREE(video__fb);
|
||||
}
|
||||
}
|
||||
|
||||
void video_render(void) {
|
||||
assert(pthread_self() == render_thread_id);
|
||||
video_getCurrentBackend()->render();
|
||||
}
|
||||
|
||||
void video_main_loop(void) {
|
||||
video_getCurrentBackend()->main_loop();
|
||||
}
|
||||
|
||||
void video_clear(void) {
|
||||
memset(video__fb, 0x0, sizeof(uint8_t)*SCANWIDTH*SCANHEIGHT);
|
||||
video_setDirty(A2_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
bool video_saveState(StateHelper_s *helper) {
|
||||
bool saved = false;
|
||||
int fd = helper->fd;
|
||||
|
||||
do {
|
||||
uint8_t state = 0x0;
|
||||
if (!helper->save(fd, &state, 1)) {
|
||||
break;
|
||||
}
|
||||
LOG("SAVE (no-op) video__current_page = %02x", state);
|
||||
|
||||
saved = true;
|
||||
} while (0);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool video_loadState(StateHelper_s *helper) {
|
||||
bool loaded = false;
|
||||
int fd = helper->fd;
|
||||
|
||||
do {
|
||||
uint8_t state = 0x0;
|
||||
|
||||
if (!helper->load(fd, &state, 1)) {
|
||||
break;
|
||||
}
|
||||
LOG("LOAD (no-op) video__current_page = %02x", state);
|
||||
|
||||
loaded = true;
|
||||
} while (0);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static inline void _currentPageAndBank(uint32_t currswitches, drawpage_mode_t mode, OUTPARM int *page, OUTPARM int *bank) {
|
||||
// UTAIIe : 5-25
|
||||
if (currswitches & SS_80STORE) {
|
||||
@ -1305,15 +1247,21 @@ static inline void _currentPageAndBank(uint32_t currswitches, drawpage_mode_t mo
|
||||
*bank = 0;
|
||||
}
|
||||
|
||||
uint8_t *video_currentFramebuffer(void) {
|
||||
return video__fb;
|
||||
void display_setUpdateCallback(drawpage_mode_t mode, display_update_fn updateFn) {
|
||||
if (mode == DRAWPAGE_TEXT) {
|
||||
textCallbackFn = updateFn;
|
||||
} else if (mode == DRAWPAGE_TEXT) {
|
||||
hiresCallbackFn = updateFn;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *video_scan(void) {
|
||||
void display_renderStagingFramebuffer(uint8_t *stagingFB) {
|
||||
|
||||
#warning FIXME TODO ... this needs to scan memory in the same way as the actually //e video scanner
|
||||
|
||||
pthread_mutex_lock(&video_scan_mutex);
|
||||
pthread_mutex_lock(&display_scan_mutex);
|
||||
|
||||
int page = 0;
|
||||
int bank = 0;
|
||||
@ -1325,22 +1273,22 @@ uint8_t *video_scan(void) {
|
||||
_currentPageAndBank(mainswitches, mainDrawPageMode, &page, &bank);
|
||||
|
||||
if (mainDrawPageMode == DRAWPAGE_TEXT) {
|
||||
void (*textMainPlotFn)(uint16_t, int, int) = _textpage_plotter(mainswitches, SS_TEXT);
|
||||
void (*textMainPlotFn)(uint16_t, int, int, uint8_t*) = _textpage_plotter(mainswitches, SS_TEXT);
|
||||
for (unsigned int y=0; y < TEXT_ROWS-4; y++) {
|
||||
for (unsigned int x=0; x < TEXT_COLS; x++) {
|
||||
uint16_t off = video__line_offset[y] + x;
|
||||
textMainPlotFn(off, page, bank);
|
||||
textMainPlotFn(off, page, bank, stagingFB);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(!(mainswitches & SS_TEXT) && "TEXT should not be set");
|
||||
assert((mainswitches & SS_HIRES) && "HIRES should be set");
|
||||
void (*hiresMainPlotFn)(uint16_t, int, int, bool) = _hirespage_plotter(mainswitches);
|
||||
void (*hiresMainPlotFn)(uint16_t, int, int, bool, uint8_t*) = _hirespage_plotter(mainswitches);
|
||||
for (unsigned int y=0; y < TEXT_ROWS-4; y++) {
|
||||
for (unsigned int x=0; x < TEXT_COLS; x++) {
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
uint16_t off = video__line_offset[y] + (0x400*i) + x;
|
||||
hiresMainPlotFn(off, page, bank, /*even*/!(x & 1));
|
||||
hiresMainPlotFn(off, page, bank, /*even*/!(x & 1), stagingFB);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1353,23 +1301,23 @@ uint8_t *video_scan(void) {
|
||||
_currentPageAndBank(mixedswitches, mixedDrawPageMode, &page, &bank);
|
||||
|
||||
if (mixedDrawPageMode == DRAWPAGE_TEXT) {
|
||||
void (*textMixedPlotFn)(uint16_t, int, int) = _textpage_plotter(mixedswitches, (SS_TEXT|SS_MIXED));
|
||||
void (*textMixedPlotFn)(uint16_t, int, int, uint8_t*) = _textpage_plotter(mixedswitches, (SS_TEXT|SS_MIXED));
|
||||
for (unsigned int y=TEXT_ROWS-4; y < TEXT_ROWS; y++) {
|
||||
for (unsigned int x=0; x < TEXT_COLS; x++) {
|
||||
uint16_t off = video__line_offset[y] + x;
|
||||
textMixedPlotFn(off, page, bank);
|
||||
textMixedPlotFn(off, page, bank, stagingFB);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//assert(!(mixedswitches & SS_TEXT) && "TEXT should not be set"); // TEXT may have been reset from last sample?
|
||||
assert(!(mixedswitches & SS_MIXED) && "MIXED should not be set");
|
||||
assert((mixedswitches & SS_HIRES) && "HIRES should be set");
|
||||
void (*hiresMixedPlotFn)(uint16_t, int, int, bool) = _hirespage_plotter(mixedswitches);
|
||||
void (*hiresMixedPlotFn)(uint16_t, int, int, bool, uint8_t*) = _hirespage_plotter(mixedswitches);
|
||||
for (unsigned int y=TEXT_ROWS-4; y < TEXT_ROWS; y++) {
|
||||
for (unsigned int x=0; x < TEXT_COLS; x++) {
|
||||
for (unsigned int i = 0; i < 8; i++) {
|
||||
uint16_t off = video__line_offset[y] + (0x400*i) + x;
|
||||
hiresMixedPlotFn(off, page, bank, /*even*/!(x & 1));
|
||||
hiresMixedPlotFn(off, page, bank, /*even*/!(x & 1), stagingFB);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1377,12 +1325,10 @@ uint8_t *video_scan(void) {
|
||||
|
||||
video_setDirty(FB_DIRTY_FLAG);
|
||||
|
||||
pthread_mutex_unlock(&video_scan_mutex);
|
||||
|
||||
return video__fb;
|
||||
pthread_mutex_unlock(&display_scan_mutex);
|
||||
}
|
||||
|
||||
void video_flashText(void) {
|
||||
void display_flashText(void) {
|
||||
static bool normal = false;
|
||||
|
||||
normal = !normal;
|
||||
@ -1555,60 +1501,6 @@ uint8_t floating_bus_hibit(const bool hibit) {
|
||||
return (b & ~0x80) | (hibit ? 0x80 : 0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static bool null_backend_running = true;
|
||||
|
||||
void video_registerBackend(video_backend_s *backend, long order) {
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
backend_node_s *node = MALLOC(sizeof(backend_node_s));
|
||||
assert(node);
|
||||
node->next = NULL;
|
||||
node->order = order;
|
||||
node->backend = backend;
|
||||
|
||||
backend_node_s *p0 = NULL;
|
||||
backend_node_s *p = head;
|
||||
while (p && (order > p->order)) {
|
||||
p0 = p;
|
||||
p = p->next;
|
||||
}
|
||||
if (p0) {
|
||||
p0->next = node;
|
||||
} else {
|
||||
head = node;
|
||||
}
|
||||
node->next = p;
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
video_backend_s *video_getCurrentBackend(void) {
|
||||
return head->backend;
|
||||
}
|
||||
|
||||
video_animation_s *video_getAnimationDriver(void) {
|
||||
return video_getCurrentBackend()->anim;
|
||||
}
|
||||
|
||||
static void _null_backend_init(void *context) {
|
||||
}
|
||||
|
||||
static void _null_backend_main_loop(void) {
|
||||
while (null_backend_running) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void _null_backend_render(void) {
|
||||
}
|
||||
|
||||
static void _null_backend_shutdown(void) {
|
||||
null_backend_running = false;
|
||||
}
|
||||
|
||||
static void _init_interface(void) {
|
||||
LOG("Initializing display subsystem");
|
||||
_initialize_interface_fonts();
|
||||
@ -1618,15 +1510,6 @@ static void _init_interface(void) {
|
||||
_initialize_color();
|
||||
|
||||
prefs_registerListener(PREF_DOMAIN_VIDEO, &video_prefsChanged);
|
||||
|
||||
static video_backend_s null_backend = { 0 };
|
||||
null_backend.init = &_null_backend_init;
|
||||
null_backend.main_loop = &_null_backend_main_loop;
|
||||
null_backend.render = &_null_backend_render;
|
||||
null_backend.shutdown = &_null_backend_shutdown;
|
||||
static video_animation_s _null_animations = { 0 };
|
||||
null_backend.anim = &_null_animations;
|
||||
video_registerBackend(&null_backend, VID_PRIO_NULL);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_interface(void) {
|
||||
|
147
src/display.h
147
src/display.h
@ -12,74 +12,53 @@
|
||||
#ifndef _DISPLAY_H_
|
||||
#define _DISPLAY_H_
|
||||
|
||||
/*
|
||||
* Color structure
|
||||
*/
|
||||
typedef struct A2Color_s {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
} A2Color_s;
|
||||
|
||||
/*
|
||||
* Reference to the internal 8bit-indexed color format
|
||||
*/
|
||||
extern A2Color_s colormap[];
|
||||
|
||||
/*
|
||||
* Color options
|
||||
*/
|
||||
typedef enum color_mode_t {
|
||||
COLOR_NONE = 0,
|
||||
/*LAZY_COLOR, deprecated*/
|
||||
COLOR,
|
||||
/*LAZY_INTERP, deprecated*/
|
||||
COLOR_INTERP,
|
||||
NUM_COLOROPTS
|
||||
} color_mode_t;
|
||||
|
||||
typedef struct video_animation_s {
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
// touch HUD functions
|
||||
void (*animation_showTouchKeyboard)(void);
|
||||
void (*animation_hideTouchKeyboard)(void);
|
||||
void (*animation_showTouchJoystick)(void);
|
||||
void (*animation_hideTouchJoystick)(void);
|
||||
void (*animation_showTouchMenu)(void);
|
||||
void (*animation_hideTouchMenu)(void);
|
||||
#endif
|
||||
|
||||
// misc animations
|
||||
void (*animation_showMessage)(char *message, unsigned int cols, unsigned int rows);
|
||||
void (*animation_showPaused)(void);
|
||||
void (*animation_showCPUSpeed)(void);
|
||||
void (*animation_showDiskChosen)(int drive);
|
||||
void (*animation_showTrackSector)(int drive, int track, int sect);
|
||||
|
||||
} video_animation_s;
|
||||
/*
|
||||
* Graphics mode
|
||||
*/
|
||||
typedef enum drawpage_mode_t {
|
||||
DRAWPAGE_TEXT = 1,
|
||||
DRAWPAGE_HIRES,
|
||||
NUM_DRAWPAGE_MODES,
|
||||
} drawpage_mode_t;
|
||||
|
||||
/*
|
||||
* Get current animation driver
|
||||
* Pixel deltas
|
||||
*/
|
||||
video_animation_s *video_getAnimationDriver(void);
|
||||
typedef struct pixel_delta_t {
|
||||
uint8_t row; // row
|
||||
uint8_t col; // col
|
||||
uint8_t b; // byte
|
||||
uint8_t cs; // colorscheme (interface updates only)
|
||||
} pixel_delta_t;
|
||||
|
||||
/*
|
||||
* Prepare the video system, converting console to graphics mode, or
|
||||
* opening X window, or whatever. This is called only once when the
|
||||
* emulator is run
|
||||
* Text/hires callback
|
||||
*/
|
||||
void video_init(void);
|
||||
|
||||
/*
|
||||
* Enters emulator-managed main video loop--if backend rendering system requires it. Currently only used by desktop X11
|
||||
* and desktop OpenGL/GLUT.
|
||||
*/
|
||||
void video_main_loop(void);
|
||||
|
||||
/*
|
||||
* Shutdown video system. Should only be called on the render thread (unless render thread is in emulator-managed main
|
||||
* video loop).
|
||||
*/
|
||||
void video_shutdown(void);
|
||||
|
||||
/*
|
||||
* Begin a render pass (only for non-emulator-managed main video). This should only be called on the render thread.
|
||||
*/
|
||||
void video_render(void);
|
||||
|
||||
/*
|
||||
* Set the render thread ID. Use with caution.
|
||||
*/
|
||||
void _video_setRenderThread(pthread_t id);
|
||||
|
||||
/*
|
||||
* Check if running on render thread.
|
||||
*/
|
||||
bool video_isRenderThread(void);
|
||||
typedef void (*display_update_fn)(pixel_delta_t);
|
||||
|
||||
/*
|
||||
* Setup the display. This may be called multiple times in a run, and is
|
||||
@ -90,7 +69,7 @@ bool video_isRenderThread(void);
|
||||
* soft-switches.
|
||||
*
|
||||
*/
|
||||
void video_reset(void);
|
||||
void display_reset(void);
|
||||
|
||||
/*
|
||||
* Set the font used by the display. QTY characters are loaded starting
|
||||
@ -109,49 +88,53 @@ void video_reset(void);
|
||||
* adaptors which color normal text and MouseText differently. I had one
|
||||
* once for a //c.
|
||||
*/
|
||||
void video_loadfont(int first, int qty, const uint8_t *data, int mode);
|
||||
|
||||
/*
|
||||
* Flushes currently set Apple //e video memory into staging framebuffer and returns pointer.
|
||||
* This should only really be called from render thread or testsuite.
|
||||
*/
|
||||
uint8_t *video_scan(void);
|
||||
|
||||
/*
|
||||
* Get a reference to current staging framebuffer
|
||||
*/
|
||||
uint8_t *video_currentFramebuffer(void);
|
||||
void display_loadFont(int first, int qty, const uint8_t *data, int mode);
|
||||
|
||||
/*
|
||||
* Toggles FLASHing text between NORMAL and INVERSE character sets.
|
||||
*/
|
||||
void video_flashText(void);
|
||||
void display_flashText(void);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Clear the current display.
|
||||
* Plot character into staging framebuffer.
|
||||
*
|
||||
* - Framebuffer may be NULL or should be exactly SCANWIDTH*SCANHEIGHT*sizeof(uint8_t) size
|
||||
* - Pixels in the framebuffer are 8bit indexed color into the colormap array
|
||||
* - Triggers text update callback
|
||||
* - This should only be called from video backends/interface
|
||||
*/
|
||||
void video_clear(void);
|
||||
|
||||
#define A2_DIRTY_FLAG 0x1 // Apple //e video is dirty
|
||||
#define FB_DIRTY_FLAG 0x2 // Internal framebuffer is dirty
|
||||
void display_plotChar(uint8_t *fb, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const uint8_t c);
|
||||
|
||||
/*
|
||||
* True if dirty bit(s) are set for flag(s)
|
||||
* Plot NULL_terminated string into staging framebuffer.
|
||||
* - See display_plotChar() ...
|
||||
*
|
||||
*/
|
||||
bool video_isDirty(unsigned long flags);
|
||||
void display_plotLine(uint8_t *fb, const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *message);
|
||||
|
||||
/*
|
||||
* Atomically set dirty bit(s), return previous bit(s) value
|
||||
* Plot multi-line message into staging framebuffer.
|
||||
* - See display_plotChar() ...
|
||||
*/
|
||||
unsigned long video_setDirty(unsigned long flags);
|
||||
void display_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, const char *message, const uint8_t message_cols, const uint8_t message_rows);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Atomically clear dirty bit(s), return previous bit(s) value
|
||||
* Set TEXT/HIRES update callback(s).
|
||||
*/
|
||||
unsigned long video_clearDirty(unsigned long flags);
|
||||
void display_setUpdateCallback(drawpage_mode_t mode, display_update_fn updateFn);
|
||||
|
||||
extern bool video_saveState(StateHelper_s *helper);
|
||||
extern bool video_loadState(StateHelper_s *helper);
|
||||
/*
|
||||
* Flushes currently set Apple //e video memory into staging framebuffer.
|
||||
*
|
||||
* - Framebuffer should be exactly SCANWIDTH*SCANHEIGHT*sizeof(uint8_t) size
|
||||
* - Pixels in the framebuffer are 8bit indexed color into the colormap array
|
||||
* - This should only be called from video backends or testsuite
|
||||
*/
|
||||
void display_renderStagingFramebuffer(uint8_t *stagingFB);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
int64_t (*interface_onTouchEvent)(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) = NULL;
|
||||
#endif
|
||||
|
||||
static uint8_t *stagingFB = NULL;
|
||||
|
||||
static char disk_path[PATH_MAX] = { 0 };
|
||||
|
||||
// 2015/04/12 : This was legacy code for rendering the menu interfaces on desktop Linux. Portions here are resurrected
|
||||
@ -120,29 +122,19 @@ static void _translate_screen_x_y(char *screen, const int xlen, const int ylen)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Menu/HUD message printing
|
||||
|
||||
static void _interface_plotLine(uint8_t *fb, int fb_pix_width, int fb_pix_x_adjust, int col, int row, interface_colorscheme_t cs, const char *message) {
|
||||
for (; *message; col++, message++) {
|
||||
char c = *message;
|
||||
unsigned int off = row * fb_pix_width * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + fb_pix_x_adjust;
|
||||
interface_plotChar(fb+off, fb_pix_width, cs, c);
|
||||
}
|
||||
}
|
||||
|
||||
void interface_plotMessage(uint8_t *fb, interface_colorscheme_t cs, char *message, int message_cols, int message_rows) {
|
||||
void interface_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, char *message, const uint8_t message_cols, const uint8_t message_rows) {
|
||||
_translate_screen_x_y(message, message_cols, message_rows);
|
||||
int fb_pix_width = (message_cols*FONT80_WIDTH_PIXELS);
|
||||
for (int row=0, idx=0; row<message_rows; row++, idx+=message_cols+1) {
|
||||
_interface_plotLine(fb, fb_pix_width, 0, 0, row, cs, &message[ idx ]);
|
||||
}
|
||||
display_plotMessage(fb, cs, message, message_cols, message_rows);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Desktop Legacy Menu Interface
|
||||
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
#if INTERFACE_CLASSIC
|
||||
|
||||
void interface_setStagingFramebuffer(uint8_t *fb) {
|
||||
stagingFB = fb;
|
||||
}
|
||||
|
||||
static void _interface_plotMessageCentered(uint8_t *fb, int fb_cols, int fb_rows, interface_colorscheme_t cs, char *message, const int message_cols, const int message_rows) {
|
||||
_translate_screen_x_y(message, message_cols, message_rows);
|
||||
@ -152,19 +144,13 @@ static void _interface_plotMessageCentered(uint8_t *fb, int fb_cols, int fb_rows
|
||||
assert(fb_pix_width == SCANWIDTH);
|
||||
int row_max = row + message_rows;
|
||||
for (int idx=0; row<row_max; row++, idx+=message_cols+1) {
|
||||
_interface_plotLine(fb, fb_pix_width, _INTERPOLATED_PIXEL_ADJUSTMENT_PRE, col, row, cs, &message[ idx ]);
|
||||
display_plotLine(fb, col, row, cs, &message[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
static struct stat statbuf = { 0 };
|
||||
static int altdrive = 0;
|
||||
|
||||
void video_plotchar(const int col, const int row, const interface_colorscheme_t cs, const uint8_t c) {
|
||||
unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _INTERPOLATED_PIXEL_ADJUSTMENT_PRE;
|
||||
interface_plotChar(video_currentFramebuffer()+off, SCANWIDTH, cs, c);
|
||||
video_setDirty(FB_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
void copy_and_pad_string(char *dest, const char* src, const char c, const int len, const char cap) {
|
||||
const char* p = src;
|
||||
char* d = dest;
|
||||
@ -195,7 +181,7 @@ static void pad_string(char *s, const char c, const int len) {
|
||||
}
|
||||
|
||||
void c_interface_print( int x, int y, const interface_colorscheme_t cs, const char *s ) {
|
||||
_interface_plotLine(video_currentFramebuffer(), SCANWIDTH, _INTERPOLATED_PIXEL_ADJUSTMENT_PRE, x, y, cs, s);
|
||||
display_plotLine(stagingFB, /*col:*/x, /*row:*/y, cs, s);
|
||||
video_setDirty(FB_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
@ -217,7 +203,7 @@ void c_interface_translate_screen( char screen[24][INTERFACE_SCREEN_X+1] ) {
|
||||
}
|
||||
|
||||
void c_interface_print_submenu_centered( char *submenu, const int message_cols, const int message_rows ) {
|
||||
_interface_plotMessageCentered(video_currentFramebuffer(), INTERFACE_SCREEN_X, TEXT_ROWS, RED_ON_BLACK, submenu, message_cols, message_rows);
|
||||
_interface_plotMessageCentered(stagingFB, INTERFACE_SCREEN_X, TEXT_ROWS, RED_ON_BLACK, submenu, message_cols, message_rows);
|
||||
video_setDirty(FB_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
@ -879,26 +865,26 @@ void c_interface_parameters()
|
||||
{
|
||||
if (temp[ j ] == '\0')
|
||||
{
|
||||
video_plotchar( INTERFACE_PATH_MIN + j, 5+i, 0, ' ' );
|
||||
display_plotChar(stagingFB, /*col:*/INTERFACE_PATH_MIN + j, /*row:*/5+i, GREEN_ON_BLACK, ' ' );
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
video_plotchar( INTERFACE_PATH_MIN + j, 5+i, 0, temp[ j ] );
|
||||
display_plotChar(stagingFB, /*col:*/INTERFACE_PATH_MIN + j, /*row:*/5+i, /*cs:*/GREEN_ON_BLACK, temp[j]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (temp[ j ] == '\0')
|
||||
{
|
||||
video_plotchar( INTERFACE_PATH_MIN + j, 5+i, option==OPT_PATH,' ' );
|
||||
display_plotChar(stagingFB, /*col:*/INTERFACE_PATH_MIN + j, /*row:*/5+i, (option == OPT_PATH ? GREEN_ON_BLUE : GREEN_ON_BLACK), ' ' );
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
video_plotchar( INTERFACE_PATH_MIN + j, 5+i, option==OPT_PATH, temp[ j ]);
|
||||
display_plotChar(stagingFB, /*col:*/INTERFACE_PATH_MIN + j, /*row:*/5+i, (option == OPT_PATH ? GREEN_ON_BLUE : GREEN_ON_BLACK), temp[j]);
|
||||
}
|
||||
|
||||
}
|
||||
@ -906,7 +892,7 @@ void c_interface_parameters()
|
||||
|
||||
for (; j < INTERFACE_PATH_MAX; j++)
|
||||
{
|
||||
video_plotchar( INTERFACE_PATH_MIN + j, 5+i, 0, ' ' );
|
||||
display_plotChar(stagingFB, /*col:*/INTERFACE_PATH_MIN + j, /*row:*/5+i, GREEN_ON_BLACK, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1103,7 +1089,7 @@ void c_interface_parameters()
|
||||
else if ((ch == kESC) || c_keys_is_interface_key(ch))
|
||||
{
|
||||
timing_initialize();
|
||||
video_reset();
|
||||
display_reset();
|
||||
vm_reinitializeAudio();
|
||||
c_joystick_reset();
|
||||
#if !TESTING
|
||||
@ -1540,7 +1526,7 @@ static void *interface_thread(void *current_key)
|
||||
break;
|
||||
|
||||
case kF7:
|
||||
c_interface_debugging();
|
||||
c_interface_debugging(stagingFB);
|
||||
break;
|
||||
|
||||
case kF8:
|
||||
|
@ -24,8 +24,7 @@ typedef enum interface_colorscheme_t {
|
||||
RED_ON_BLACK,
|
||||
} interface_colorscheme_t;
|
||||
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
void video_plotchar(int col, int row, interface_colorscheme_t cs, uint8_t c);
|
||||
#if INTERFACE_CLASSIC
|
||||
void c_interface_begin(int current_key);
|
||||
void c_interface_print(int x, int y, const interface_colorscheme_t cs, const char *s);
|
||||
void c_interface_print_submenu_centered(char *submenu, int xlen, int ylen);
|
||||
@ -35,13 +34,11 @@ void c_interface_credits();
|
||||
void c_interface_exit(int ch);
|
||||
void c_interface_translate_screen(char screen[24][INTERFACE_SCREEN_X+1]);
|
||||
void c_interface_select_diskette(int);
|
||||
void interface_setStagingFramebuffer(uint8_t *stagingFB);
|
||||
#endif
|
||||
|
||||
// Plot character at pixel buffer offset
|
||||
void interface_plotChar(uint8_t *fboff, int fb_pix_width, interface_colorscheme_t cs, uint8_t c);
|
||||
|
||||
// Plot message into pixel buffer
|
||||
void interface_plotMessage(uint8_t *fb, interface_colorscheme_t cs, char *message, int message_cols, int message_rows);
|
||||
void interface_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, char *message, const uint8_t message_cols, const uint8_t message_rows);
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
typedef enum interface_device_t {
|
||||
|
@ -80,7 +80,7 @@ struct opcode_struct
|
||||
extern const struct opcode_struct *opcodes;
|
||||
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
void c_interface_debugging();
|
||||
void c_interface_debugging(uint8_t *stagingFB);
|
||||
#endif
|
||||
|
||||
void debugger_setInputText(const char *text, const bool deterministically);
|
||||
|
@ -1091,8 +1091,10 @@ void fb_sha1() {
|
||||
uint8_t md[SHA_DIGEST_LENGTH];
|
||||
char buf[(SHA_DIGEST_LENGTH*2)+1];
|
||||
|
||||
const uint8_t * const fb = video_scan();
|
||||
uint8_t *fb = MALLOC(SCANWIDTH*SCANHEIGHT*sizeof(uint8_t));
|
||||
display_renderStagingFramebuffer(fb);
|
||||
SHA1(fb, SCANWIDTH*SCANHEIGHT, md);
|
||||
FREE(fb);
|
||||
|
||||
int i=0;
|
||||
for (int j=0; j<SHA_DIGEST_LENGTH; j++, i+=2) {
|
||||
@ -1409,7 +1411,7 @@ static void do_debug_command() {
|
||||
main debugging console
|
||||
------------------------------------------------------------------------- */
|
||||
|
||||
void c_interface_debugging() {
|
||||
void c_interface_debugging(uint8_t *stagingFB) {
|
||||
|
||||
static char lex_initted = 0;
|
||||
|
||||
@ -1453,7 +1455,7 @@ void c_interface_debugging() {
|
||||
c_interface_print(1, 1+PROMPT_Y, 0, command_line);
|
||||
|
||||
/* highlight cursor */
|
||||
video_plotchar(1+command_pos, 1+PROMPT_Y, 1, command_line[command_pos]);
|
||||
display_plotChar(stagingFB, /*col:*/1+command_pos, /*row:*/1+PROMPT_Y, GREEN_ON_BLUE, command_line[command_pos]);
|
||||
|
||||
while ((ch = c_mygetch(1)) == -1)
|
||||
{
|
||||
|
@ -45,8 +45,12 @@ void sha1_to_str(const uint8_t * const md, char *buf);
|
||||
|
||||
static inline int ASSERT_SHA(const char *SHA_STR) {
|
||||
uint8_t md[SHA_DIGEST_LENGTH];
|
||||
const uint8_t * const fb = video_scan();
|
||||
|
||||
uint8_t fb = MALLOC(SCANWIDTH*SCANHEIGHT);
|
||||
display_renderStagingFramebuffer(fb);
|
||||
SHA1(fb, SCANWIDTH*SCANHEIGHT, md);
|
||||
FREE(fb);
|
||||
|
||||
sha1_to_str(md, mdstr);
|
||||
ASSERT(strcasecmp(mdstr, SHA_STR) == 0);
|
||||
return 0;
|
||||
|
@ -1395,8 +1395,12 @@ static int _test_disk_image_with_gzip_header(int readonly) {
|
||||
|
||||
do {
|
||||
uint8_t md[SHA_DIGEST_LENGTH];
|
||||
const uint8_t * const fb = video_scan();
|
||||
|
||||
uint8_t fb = MALLOC(SCANWIDTH*SCANHEIGHT);
|
||||
display_renderStagingFramebuffer(fb);
|
||||
SHA1(fb, SCANWIDTH*SCANHEIGHT, md);
|
||||
FREE(fb);
|
||||
|
||||
sha1_to_str(md, mdstr);
|
||||
bool matches_sha1 = (strcasecmp(mdstr, GZBAD_NIB_LOAD_SHA1) == 0);
|
||||
bool matches_sha2 = (strcasecmp(mdstr, GZBAD_NIB_LOAD_SHA2) == 0);
|
||||
@ -1499,8 +1503,12 @@ TEST test_reinsert_edgecase() {
|
||||
c_debugger_clear_watchpoints();
|
||||
c_debugger_go();
|
||||
uint8_t md[SHA_DIGEST_LENGTH];
|
||||
const uint8_t * const fb = video_scan();
|
||||
|
||||
uint8_t fb = MALLOC(SCANWIDTH*SCANHEIGHT);
|
||||
display_renderStagingFramebuffer(fb);
|
||||
SHA1(fb, SCANWIDTH*SCANHEIGHT, md);
|
||||
FREE(fb);
|
||||
|
||||
sha1_to_str(md, mdstr);
|
||||
ASSERT(strcmp(mdstr, BLANK_SCREEN) != 0);
|
||||
|
||||
|
@ -452,7 +452,7 @@ cpu_runloop:
|
||||
dbg_ticks += EXECUTION_PERIOD_NSECS;
|
||||
if ((dbg_ticks % (NANOSECONDS_PER_SECOND>>1)) == 0)
|
||||
{
|
||||
video_flashText(); // TODO FIXME : proper FLASH timing ...
|
||||
display_flashText(); // TODO FIXME : proper FLASH timing ...
|
||||
}
|
||||
#if DEBUG_TIMING
|
||||
// collect timing statistics
|
||||
|
@ -280,11 +280,6 @@ static void glvideo_shutdown(void) {
|
||||
static void glvideo_render(void) {
|
||||
SCOPE_TRACE_VIDEO("glvideo render");
|
||||
|
||||
uint8_t *fb = video_currentFramebuffer();
|
||||
if (UNLIKELY(!fb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (UNLIKELY(prefsChanged)) {
|
||||
glvideo_applyPrefs();
|
||||
}
|
||||
@ -316,15 +311,20 @@ static void glvideo_render(void) {
|
||||
glUniformMatrix4fv(uniformMVPIdx, 1, GL_FALSE, mvpIdentity);
|
||||
#endif
|
||||
|
||||
static uint8_t fb[SCANWIDTH*SCANHEIGHT*sizeof(uint8_t)];
|
||||
#if INTERFACE_CLASSIC
|
||||
interface_setStagingFramebuffer(fb);
|
||||
#endif
|
||||
unsigned long wasDirty = 0UL;
|
||||
if (!cpu_isPaused()) {
|
||||
// check if a2 video memory is dirty
|
||||
unsigned long wasDirty = video_clearDirty(A2_DIRTY_FLAG);
|
||||
wasDirty = video_clearDirty(A2_DIRTY_FLAG);
|
||||
if (wasDirty) {
|
||||
fb = video_scan();
|
||||
display_renderStagingFramebuffer(fb);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long wasDirty = video_clearDirty(FB_DIRTY_FLAG);
|
||||
wasDirty = video_clearDirty(FB_DIRTY_FLAG);
|
||||
char *pixels = (char *)crtModel->texPixels;
|
||||
if (wasDirty) {
|
||||
SCOPE_TRACE_VIDEO("pixel convert");
|
||||
|
174
src/video/video.c
Normal file
174
src/video/video.c
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2017 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
typedef struct backend_node_s {
|
||||
struct backend_node_s *next;
|
||||
long order;
|
||||
video_backend_s *backend;
|
||||
} backend_node_s;
|
||||
|
||||
static bool video_initialized = false;
|
||||
static bool null_backend_running = true;
|
||||
static backend_node_s *head = NULL;
|
||||
static video_backend_s *currentBackend = NULL;
|
||||
static pthread_t render_thread_id = 0;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void video_init(void) {
|
||||
video_initialized = true;
|
||||
|
||||
assert(pthread_self() != cpu_thread_id);
|
||||
LOG("(re)setting render_thread_id : %ld -> %ld", render_thread_id, pthread_self());
|
||||
render_thread_id = pthread_self();
|
||||
|
||||
video_clear();
|
||||
|
||||
currentBackend->init((void*)0);
|
||||
}
|
||||
|
||||
void _video_setRenderThread(pthread_t id) {
|
||||
LOG("setting render_thread_id : %ld -> %ld", render_thread_id, id);
|
||||
render_thread_id = id;
|
||||
}
|
||||
|
||||
bool video_isRenderThread(void) {
|
||||
return (pthread_self() == render_thread_id);
|
||||
}
|
||||
|
||||
void video_shutdown(void) {
|
||||
|
||||
#if MOBILE_DEVICE
|
||||
// WARNING : shutdown should occur on the render thread. Platform code (iOS, Android) should ensure this is called
|
||||
// from within a render pass...
|
||||
assert(!render_thread_id || pthread_self() == render_thread_id);
|
||||
#endif
|
||||
|
||||
currentBackend->shutdown();
|
||||
}
|
||||
|
||||
void video_render(void) {
|
||||
assert(pthread_self() == render_thread_id);
|
||||
currentBackend->render();
|
||||
}
|
||||
|
||||
void video_main_loop(void) {
|
||||
currentBackend->main_loop();
|
||||
}
|
||||
|
||||
void video_clear(void) {
|
||||
video_setDirty(A2_DIRTY_FLAG);
|
||||
}
|
||||
|
||||
bool video_saveState(StateHelper_s *helper) {
|
||||
bool saved = false;
|
||||
int fd = helper->fd;
|
||||
|
||||
do {
|
||||
uint8_t state = 0x0;
|
||||
if (!helper->save(fd, &state, 1)) {
|
||||
break;
|
||||
}
|
||||
LOG("SAVE (no-op) video__current_page = %02x", state);
|
||||
|
||||
saved = true;
|
||||
} while (0);
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool video_loadState(StateHelper_s *helper) {
|
||||
bool loaded = false;
|
||||
int fd = helper->fd;
|
||||
|
||||
do {
|
||||
uint8_t state = 0x0;
|
||||
|
||||
if (!helper->load(fd, &state, 1)) {
|
||||
break;
|
||||
}
|
||||
LOG("LOAD (no-op) video__current_page = %02x", state);
|
||||
|
||||
loaded = true;
|
||||
} while (0);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Video backend registration and selection
|
||||
|
||||
void video_registerBackend(video_backend_s *backend, long order) {
|
||||
assert(!video_initialized); // backends cannot be registered after we've picked one to use
|
||||
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
backend_node_s *node = MALLOC(sizeof(backend_node_s));
|
||||
assert(node);
|
||||
node->next = NULL;
|
||||
node->order = order;
|
||||
node->backend = backend;
|
||||
|
||||
backend_node_s *p0 = NULL;
|
||||
backend_node_s *p = head;
|
||||
while (p && (order > p->order)) {
|
||||
p0 = p;
|
||||
p = p->next;
|
||||
}
|
||||
if (p0) {
|
||||
p0->next = node;
|
||||
} else {
|
||||
head = node;
|
||||
}
|
||||
node->next = p;
|
||||
|
||||
currentBackend = head->backend;
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
video_animation_s *video_getAnimationDriver(void) {
|
||||
return currentBackend->anim;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// NULL video backend ...
|
||||
|
||||
static void _null_backend_init(void *context) {
|
||||
}
|
||||
|
||||
static void _null_backend_main_loop(void) {
|
||||
while (null_backend_running) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void _null_backend_render(void) {
|
||||
}
|
||||
|
||||
static void _null_backend_shutdown(void) {
|
||||
null_backend_running = false;
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void _init_video(void) {
|
||||
static video_backend_s null_backend = { 0 };
|
||||
null_backend.init = &_null_backend_init;
|
||||
null_backend.main_loop = &_null_backend_main_loop;
|
||||
null_backend.render = &_null_backend_render;
|
||||
null_backend.shutdown = &_null_backend_shutdown;
|
||||
static video_animation_s _null_animations = { 0 };
|
||||
null_backend.anim = &_null_animations;
|
||||
video_registerBackend(&null_backend, VID_PRIO_NULL);
|
||||
}
|
||||
|
@ -16,6 +16,104 @@
|
||||
#ifndef A2_VIDEO_H
|
||||
#define A2_VIDEO_H
|
||||
|
||||
/*
|
||||
* Animation
|
||||
*/
|
||||
typedef struct video_animation_s {
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
// touch HUD functions
|
||||
void (*animation_showTouchKeyboard)(void);
|
||||
void (*animation_hideTouchKeyboard)(void);
|
||||
void (*animation_showTouchJoystick)(void);
|
||||
void (*animation_hideTouchJoystick)(void);
|
||||
void (*animation_showTouchMenu)(void);
|
||||
void (*animation_hideTouchMenu)(void);
|
||||
#endif
|
||||
|
||||
// misc animations
|
||||
void (*animation_showMessage)(char *message, unsigned int cols, unsigned int rows);
|
||||
void (*animation_showPaused)(void);
|
||||
void (*animation_showCPUSpeed)(void);
|
||||
void (*animation_showDiskChosen)(int drive);
|
||||
void (*animation_showTrackSector)(int drive, int track, int sect);
|
||||
|
||||
} video_animation_s;
|
||||
|
||||
/*
|
||||
* Prepare the video system, converting console to graphics mode, or opening X window, or whatever.
|
||||
*/
|
||||
void video_init(void);
|
||||
|
||||
/*
|
||||
* Enters emulator-managed main video loop (if backend rendering system requires it). Currently only used by desktop
|
||||
* X11 and desktop OpenGL/GLUT.
|
||||
*/
|
||||
void video_main_loop(void);
|
||||
|
||||
/*
|
||||
* Shutdown video system. Should only be called on the render thread (unless render thread is in emulator-managed main
|
||||
* video loop).
|
||||
*/
|
||||
void video_shutdown(void);
|
||||
|
||||
/*
|
||||
* Begin a render pass (only for non-emulator-managed main video). This should only be called on the render thread.
|
||||
*/
|
||||
void video_render(void);
|
||||
|
||||
/*
|
||||
* Set the render thread ID. Use with caution.
|
||||
*/
|
||||
void _video_setRenderThread(pthread_t id);
|
||||
|
||||
/*
|
||||
* Check if the current code is running on render thread.
|
||||
*/
|
||||
bool video_isRenderThread(void);
|
||||
|
||||
/*
|
||||
* Clear the current display.
|
||||
*/
|
||||
void video_clear(void);
|
||||
|
||||
#define A2_DIRTY_FLAG 0x1 // Apple //e video is dirty
|
||||
#define FB_DIRTY_FLAG 0x2 // Internal framebuffer is dirty
|
||||
|
||||
/*
|
||||
* True if dirty bit(s) are set for flag(s)
|
||||
*/
|
||||
bool video_isDirty(unsigned long flags);
|
||||
|
||||
/*
|
||||
* Atomically set dirty bit(s), return previous bit(s) value
|
||||
*/
|
||||
unsigned long video_setDirty(unsigned long flags);
|
||||
|
||||
/*
|
||||
* Atomically clear dirty bit(s), return previous bit(s) value
|
||||
*/
|
||||
unsigned long video_clearDirty(unsigned long flags);
|
||||
|
||||
/*
|
||||
* State save support for video subsystem.
|
||||
*/
|
||||
bool video_saveState(StateHelper_s *helper);
|
||||
|
||||
/*
|
||||
* State restore support for video subsystem.
|
||||
*/
|
||||
bool video_loadState(StateHelper_s *helper);
|
||||
|
||||
/*
|
||||
* Get current animation driver
|
||||
*/
|
||||
video_animation_s *video_getAnimationDriver(void);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Video Backend API
|
||||
|
||||
typedef struct video_backend_s {
|
||||
void (*init)(void *context);
|
||||
void (*main_loop)(void);
|
||||
@ -24,20 +122,6 @@ typedef struct video_backend_s {
|
||||
video_animation_s *anim;
|
||||
} video_backend_s;
|
||||
|
||||
/*
|
||||
* Color structure
|
||||
*/
|
||||
typedef struct A2Color_s {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
} A2Color_s;
|
||||
|
||||
/*
|
||||
* Reference to the internal 8bit-indexed color format
|
||||
*/
|
||||
extern A2Color_s colormap[];
|
||||
|
||||
#if VIDEO_X11
|
||||
// X11 scaling ...
|
||||
typedef enum a2_video_mode_t {
|
||||
@ -57,9 +141,10 @@ enum {
|
||||
VID_PRIO_NULL = 100,
|
||||
};
|
||||
|
||||
/*
|
||||
* Register a video backend at the specific prioritization, regardless of user choice.
|
||||
*/
|
||||
void video_registerBackend(video_backend_s *backend, long prio);
|
||||
|
||||
video_backend_s *video_getCurrentBackend(void);
|
||||
|
||||
#endif /* !A2_VIDEO_H */
|
||||
|
||||
|
@ -333,39 +333,55 @@ static int keysym_to_scancode(void) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// copy Apple //e video memory into XImage uint32_t buffer
|
||||
static void post_image() {
|
||||
// copy Apple //e video memory into XImage uint32_t buffer
|
||||
uint8_t *fb = video_scan();
|
||||
uint8_t index;
|
||||
|
||||
unsigned int count = SCANWIDTH * SCANHEIGHT;
|
||||
for (unsigned int i=0, j=0; i<count; i++, j+=4)
|
||||
{
|
||||
index = *(fb + i);
|
||||
*( (uint32_t*)(image->data + j) ) = (uint32_t)(
|
||||
((uint32_t)(colormap[index].red) << red_shift) |
|
||||
((uint32_t)(colormap[index].green) << green_shift) |
|
||||
((uint32_t)(colormap[index].blue) << blue_shift) |
|
||||
((uint32_t)0xff /* alpha */ << alpha_shift)
|
||||
);
|
||||
if (scale > 1)
|
||||
static uint8_t fb[SCANWIDTH*SCANHEIGHT*sizeof(uint8_t)];
|
||||
#if INTERFACE_CLASSIC
|
||||
interface_setStagingFramebuffer(fb);
|
||||
#endif
|
||||
unsigned long wasDirty = 0UL;
|
||||
|
||||
// check if a2 video memory is dirty
|
||||
wasDirty = video_clearDirty(A2_DIRTY_FLAG);
|
||||
if (wasDirty) {
|
||||
display_renderStagingFramebuffer(fb);
|
||||
}
|
||||
|
||||
// now check/clear if we should redraw
|
||||
wasDirty = video_clearDirty(FB_DIRTY_FLAG);
|
||||
if (wasDirty) {
|
||||
uint8_t index;
|
||||
|
||||
unsigned int count = SCANWIDTH * SCANHEIGHT;
|
||||
for (unsigned int i=0, j=0; i<count; i++, j+=4)
|
||||
{
|
||||
j+=4;
|
||||
|
||||
// duplicate pixel
|
||||
index = *(fb + i);
|
||||
*( (uint32_t*)(image->data + j) ) = (uint32_t)(
|
||||
((uint32_t)(colormap[index].red) << red_shift) |
|
||||
((uint32_t)(colormap[index].green) << green_shift) |
|
||||
((uint32_t)(colormap[index].blue) << blue_shift) |
|
||||
((uint32_t)0xff /* alpha */ << alpha_shift)
|
||||
);
|
||||
|
||||
if (((i+1) % SCANWIDTH) == 0)
|
||||
((uint32_t)(colormap[index].red) << red_shift) |
|
||||
((uint32_t)(colormap[index].green) << green_shift) |
|
||||
((uint32_t)(colormap[index].blue) << blue_shift) |
|
||||
((uint32_t)0xff /* alpha */ << alpha_shift)
|
||||
);
|
||||
if (scale > 1)
|
||||
{
|
||||
// duplicate entire row
|
||||
int stride8 = SCANWIDTH<<3;//*8
|
||||
memcpy(/* dest */image->data + j + 4, /* src */image->data + j + 4 - stride8, stride8);
|
||||
j += stride8;
|
||||
j+=4;
|
||||
|
||||
// duplicate pixel
|
||||
*( (uint32_t*)(image->data + j) ) = (uint32_t)(
|
||||
((uint32_t)(colormap[index].red) << red_shift) |
|
||||
((uint32_t)(colormap[index].green) << green_shift) |
|
||||
((uint32_t)(colormap[index].blue) << blue_shift) |
|
||||
((uint32_t)0xff /* alpha */ << alpha_shift)
|
||||
);
|
||||
|
||||
if (((i+1) % SCANWIDTH) == 0)
|
||||
{
|
||||
// duplicate entire row
|
||||
int stride8 = SCANWIDTH<<3;//*8
|
||||
memcpy(/* dest */image->data + j + 4, /* src */image->data + j + 4 - stride8, stride8);
|
||||
j += stride8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -859,7 +875,7 @@ static void xdriver_init(void *context) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void xdriver_shutdown(bool emulatorShuttingDown) {
|
||||
static void xdriver_shutdown(void) {
|
||||
_destroy_image();
|
||||
}
|
||||
|
||||
|
20
src/vm.c
20
src/vm.c
@ -804,7 +804,7 @@ GLUE_C_READ(iie_altchar_off)
|
||||
{
|
||||
if (softswitches & SS_ALTCHAR) {
|
||||
softswitches &= ~SS_ALTCHAR;
|
||||
video_loadfont(0x40,0x40,ucase_glyphs,3);
|
||||
display_loadFont(0x40,0x40,ucase_glyphs,3);
|
||||
video_setDirty(A2_DIRTY_FLAG);
|
||||
}
|
||||
return floating_bus();
|
||||
@ -814,8 +814,8 @@ GLUE_C_READ(iie_altchar_on)
|
||||
{
|
||||
if (!(softswitches & SS_ALTCHAR)) {
|
||||
softswitches |= SS_ALTCHAR;
|
||||
video_loadfont(0x40,0x20,mousetext_glyphs,1);
|
||||
video_loadfont(0x60,0x20,lcase_glyphs,2);
|
||||
display_loadFont(0x40,0x20,mousetext_glyphs,1);
|
||||
display_loadFont(0x60,0x20,lcase_glyphs,2);
|
||||
video_setDirty(A2_DIRTY_FLAG);
|
||||
}
|
||||
return floating_bus();
|
||||
@ -970,11 +970,11 @@ static void _initialize_iie_switches(void) {
|
||||
}
|
||||
|
||||
static void _initialize_font(void) {
|
||||
video_loadfont(0x00,0x40,ucase_glyphs,2);
|
||||
video_loadfont(0x40,0x40,ucase_glyphs,3);
|
||||
video_loadfont(0x80,0x40,ucase_glyphs,0);
|
||||
video_loadfont(0xC0,0x20,ucase_glyphs,0);
|
||||
video_loadfont(0xE0,0x20,lcase_glyphs,0);
|
||||
display_loadFont(0x00,0x40,ucase_glyphs,2);
|
||||
display_loadFont(0x40,0x40,ucase_glyphs,3);
|
||||
display_loadFont(0x80,0x40,ucase_glyphs,0);
|
||||
display_loadFont(0xC0,0x20,ucase_glyphs,0);
|
||||
display_loadFont(0xE0,0x20,lcase_glyphs,0);
|
||||
}
|
||||
|
||||
static void _initialize_apple_ii_memory(void) {
|
||||
@ -1049,7 +1049,7 @@ static void _initialize_tables(void) {
|
||||
|
||||
// initialize first text & hires page, which are specially bank switched
|
||||
//
|
||||
// video_reset() below substitutes it's own hooks for all visible write locations affect the display, leaving our
|
||||
// display_reset() below substitutes it's own hooks for all visible write locations affect the display, leaving our
|
||||
// write-functions in place only at the `screen holes', hence the name.
|
||||
for (unsigned int i = 0x400; i < 0x800; i++) {
|
||||
cpu65_vmem_r[i] = iie_read_ram_text_page0;
|
||||
@ -1215,7 +1215,7 @@ static void _initialize_tables(void) {
|
||||
cpu65_vmem_r[i] = iie_read_slot_expansion;
|
||||
}
|
||||
|
||||
video_reset();
|
||||
display_reset();
|
||||
|
||||
// Peripheral card slot initializations ...
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user