Squashed commit of the following:

CPU speed animation touch-ups, and enable building for desktop
    Rename/shuffle animation declarations and code
    Refactor backend video system to be a dynamically initialized module
    Basic CPU speed texture animation works on desktop Linux and Android
    Use static pixel buffers to avoid malloc/free churn
    Improve CPU animations
    First cut at CPU speed message animation
    Refactor some of the classic interface functions to be potentially reusable elsewhere
This commit is contained in:
Aaron Culliney 2015-03-22 18:53:13 -07:00
parent 358541b8cc
commit af42dc36b2
20 changed files with 1137 additions and 429 deletions

View File

@ -9,11 +9,12 @@
*
*/
#include <jni.h>
#include "common.h"
#include "video/renderer.h"
#include "androidkeys.h"
#include <jni.h>
#include <math.h>
static bool nativePaused = false;
#if TESTING
@ -56,22 +57,22 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsChanged(JNIEnv *env, jobject obj, jint width, jint height) {
LOG("%s", "native graphicsChanged...");
video_driver_reshape(width, height);
video_backend->reshape(width, height);
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsInitialized(JNIEnv *env, jobject obj, jint width, jint height) {
LOG("%s", "native graphicsInitialized...");
video_driver_reshape(width, height);
video_backend->reshape(width, height);
#if TESTING
_run_tests();
#else
static bool graphicsPreviouslyInitialized = false;
if (graphicsPreviouslyInitialized) {
video_driver_shutdown();
video_backend->shutdown();
}
graphicsPreviouslyInitialized = true;
video_driver_init((void *)0);
video_backend->init((void *)0);
#endif
}
@ -118,7 +119,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeRender(JNIEnv *env, jobject
extern volatile bool _vid_dirty;
_vid_dirty = true;
video_driver_render();
video_backend->render();
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeReboot(JNIEnv *env, jobject obj) {
@ -160,17 +161,23 @@ jboolean Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnTouch(JNIEnv *env, jo
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeIncreaseCPUSpeed(JNIEnv *env, jobject obj) {
pthread_mutex_lock(&interface_mutex);
if (cpu_scale_factor > 1.0) {
cpu_scale_factor += 0.25;
int percent_scale = (int)round(cpu_scale_factor * 100.0);
if (percent_scale >= 100) {
percent_scale += 25;
} else {
cpu_scale_factor += 0.05;
percent_scale += 5;
}
cpu_scale_factor = percent_scale/100.0;
if (cpu_scale_factor > CPU_SCALE_FASTEST) {
cpu_scale_factor = CPU_SCALE_FASTEST;
}
//video_driver_animate_speedscale();
LOG("native set emulation percentage to %f", cpu_scale_factor);
if (video_animation_show_cpuspeed) {
video_animation_show_cpuspeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
@ -182,17 +189,28 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeIncreaseCPUSpeed(JNIEnv *en
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeDecreaseCPUSpeed(JNIEnv *env, jobject obj) {
pthread_mutex_lock(&interface_mutex);
if (cpu_scale_factor > 1.0) {
cpu_scale_factor -= 0.25;
int percent_scale = (int)round(cpu_scale_factor * 100.0);
if (cpu_scale_factor == CPU_SCALE_FASTEST) {
cpu_scale_factor = CPU_SCALE_FASTEST0;
percent_scale = (int)round(cpu_scale_factor * 100);
} else {
cpu_scale_factor -= 0.05;
if (percent_scale > 100) {
percent_scale -= 25;
} else {
percent_scale -= 5;
}
}
cpu_scale_factor = percent_scale/100.0;
if (cpu_scale_factor < CPU_SCALE_SLOWEST) {
cpu_scale_factor = CPU_SCALE_SLOWEST;
}
//video_driver_animate_speedscale();
LOG("native set emulation percentage to %f", cpu_scale_factor);
if (video_animation_show_cpuspeed) {
video_animation_show_cpuspeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();

View File

@ -11,6 +11,8 @@ APPLE2_ARM_SRC := \
APPLE2_VIDEO_SRC = \
$(APPLE2_SRC_PATH)/video/glvideo.c \
$(APPLE2_SRC_PATH)/video/glanimation.c \
$(APPLE2_SRC_PATH)/video/glcpuanim.c \
$(APPLE2_SRC_PATH)/video/gltouchjoy.c \
$(APPLE2_SRC_PATH)/video_util/matrixUtil.c \
$(APPLE2_SRC_PATH)/video_util/modelUtil.c \

View File

@ -37,6 +37,8 @@ INTERFACE_CLASSIC_SRC = \
VIDEO_SRC = \
src/video/xvideo.c \
src/video/glvideo.c \
src/video/glanimation.c \
src/video/glcpuanim.c \
src/video/glutinput.c \
src/video_util/matrixUtil.c \
src/video_util/modelUtil.c \

View File

@ -170,7 +170,7 @@ AC_ARG_ENABLE([opengl], AS_HELP_STRING([--disable-opengl], [Disable OpenGL video
opengl_supported='yes'
AC_DEFINE(VIDEO_OPENGL, 1, [Use OpenGL])
AC_DEFINE(USE_GLUT, 1, [Use GLUT library])
VIDEO_O="src/video/glvideo.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="src/video/glvideo.o src/video/glanimation.o src/video/glcpuanim.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"
AC_MSG_RESULT([Building emulator with OpenGL support, w00t!])
], [
AC_MSG_WARN([Did not find OpenGL GLEW library...])

View File

@ -15,7 +15,8 @@
*/
#include "common.h"
#include "video/renderer.h"
#define SCANSTEP SCANWIDTH-12
#define DYNAMIC_SZ 11 // 7 pixels (as bytes) + 2pre + 2post
@ -23,10 +24,12 @@
static uint8_t vga_mem_page_0[SCANWIDTH*SCANHEIGHT];
static uint8_t vga_mem_page_1[SCANWIDTH*SCANHEIGHT];
A2Color colormap[256] = { { 0 } };
A2Color_s colormap[256] = { { 0 } };
video_backend_s *video_backend = NULL;
uint8_t video__wider_font[0x8000];
uint8_t video__font[0x4000];
static uint8_t video__wider_font[0x8000];
static uint8_t video__font[0x4000];
static uint8_t video__int_font[3][0x4000]; // interface font
// Precalculated framebuffer offsets given VM addr
unsigned int video__screen_addresses[8192];
@ -41,11 +44,6 @@ uint8_t video__hires_odd[0x800];
uint8_t video__dhires1[256];
uint8_t video__dhires2[256];
#ifdef INTERFACE_CLASSIC
// Interface font
static uint8_t video__int_font[3][0x4000];
#endif
int video__current_page; // current visual page
int video__strictcolors = 1;// refactor : should be static
@ -493,8 +491,7 @@ void video_loadfont(int first, int quantity, const uint8_t *data, int mode) {
}
}
#ifdef INTERFACE_CLASSIC
void video_loadfont_int(int first, int quantity, const uint8_t *data) {
static void video_loadfont_int(int first, int quantity, const uint8_t *data) {
unsigned int i = quantity * 8;
while (i--) {
unsigned int j = 8;
@ -512,10 +509,9 @@ void video_loadfont_int(int first, int quantity, const uint8_t *data) {
}
}
}
#endif
// ----------------------------------------------------------------------------
// Plotting routines
// lores/char plotting routines
static inline void _plot_char40(uint8_t **d, uint8_t **s) {
*((uint32_t *)(*d)) = *((uint32_t *)(*s));
@ -536,19 +532,19 @@ static inline void _plot_char40(uint8_t **d, uint8_t **s) {
*d += SCANSTEP, *s += 4;
}
static inline void _plot_char80(uint8_t **d, uint8_t **s) {
static inline void _plot_char80(uint8_t **d, uint8_t **s, const unsigned int fb_width) {
*((uint32_t *)(*d)) = *((uint32_t *)(*s));
*d += 4, *s += 4;
*((uint16_t *)(*d)) = *((uint16_t *)(*s));
*d += 2, *s += 2;
*((uint8_t *)(*d)) = *((uint8_t *)(*s));
*d += SCANWIDTH-6, *s -= 6;
*d += fb_width-6, *s -= 6;
*((uint32_t *)(*d)) = *((uint32_t *)(*s));
*d += 4, *s += 4;
*((uint16_t *)(*d)) = *((uint16_t *)(*s));
*d += 2, *s += 2;
*((uint8_t *)(*d)) = *((uint8_t *)(*s));
*d += SCANWIDTH-6, *s += 2;
*d += fb_width-6, *s += 2;
}
static inline void _plot_lores(uint8_t **d, const uint32_t val) {
@ -569,27 +565,6 @@ static inline void _plot_lores(uint8_t **d, const uint32_t val) {
*((uint16_t *)(*d)) = (uint16_t)(val & 0xffff);
}
#ifdef INTERFACE_CLASSIC
void video_plotchar( int x, int y, int scheme, uint8_t c ) {
_vid_dirty = true;
uint8_t *d;
uint8_t *s;
unsigned int off = y * SCANWIDTH * 16 + x * 7 + 4;
s = video__int_font[scheme] + c * 64;
d = video__fb1 + off;
_plot_char80(&d,&s);
_plot_char80(&d,&s);
_plot_char80(&d,&s);
_plot_char80(&d,&s);
_plot_char80(&d,&s);
_plot_char80(&d,&s);
_plot_char80(&d,&s);
_plot_char80(&d,&s);
}
#endif
static inline void _plot_character(const unsigned int font_off, uint8_t *fb_ptr) {
_vid_dirty = true;
uint8_t *font_ptr = video__wider_font+font_off;
@ -616,14 +591,14 @@ static inline void _plot_character1(uint16_t ea, uint8_t b)
static inline void _plot_80character(const unsigned int font_off, uint8_t *fb_ptr) {
_vid_dirty = true;
uint8_t *font_ptr = video__font+font_off;
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
}
// FIXME TODO NOTE : dup'ing work here?
@ -743,6 +718,162 @@ GLUE_C_WRITE(video__write_2e_text1_mixed)
DRAW_MIXED(1, SS_RAMWRT);
}
// ----------------------------------------------------------------------------
// interface/messages plotting
void video_load_interface_fonts(void) {
video_loadfont_int(0x00,0x40,ucase_glyphs);
video_loadfont_int(0x40,0x20,ucase_glyphs);
video_loadfont_int(0x60,0x20,lcase_glyphs);
video_loadfont_int(0x80,0x40,ucase_glyphs);
video_loadfont_int(0xC0,0x20,ucase_glyphs);
video_loadfont_int(0xE0,0x20,lcase_glyphs);
video_loadfont_int(0x80,11,interface_glyphs);
video_loadfont_int(MOUSETEXT_BEGIN,0x20,mousetext_glyphs);
}
// Classic interface and messages
void video_plotchar_fb(uint8_t *fb, int fb_width, int x, int y, int scheme, uint8_t c) {
_vid_dirty = true;
unsigned int off = y * fb_width * 16 + x * 7 + 4;
uint8_t *dst = fb + off;
uint8_t *src = video__int_font[scheme] + c * 64;
_plot_char80(&dst, &src, fb_width);
_plot_char80(&dst, &src, fb_width);
_plot_char80(&dst, &src, fb_width);
_plot_char80(&dst, &src, fb_width);
_plot_char80(&dst, &src, fb_width);
_plot_char80(&dst, &src, fb_width);
_plot_char80(&dst, &src, fb_width);
_plot_char80(&dst, &src, fb_width);
}
void video_plotchar(int x, int y, int scheme, uint8_t c) {
video_plotchar_fb(video__fb1, SCANWIDTH, x, y, scheme, c);
}
void video_interface_print_fb(uint8_t *fb, int fb_width, int x, int y, int cs, const char *s) {
for (; *s; x++, s++) {
video_plotchar_fb(fb, fb_width, x, y, cs, *s);
}
}
void video_interface_print(int x, int y, int cs, const char *s) {
video_interface_print_fb(video__fb1, SCANWIDTH, x, y, cs, s);
}
#define IsGraphic(c) ((c) == '|' || (((unsigned char)c) >= 0x80 && ((unsigned char)c) <= 0x8A))
#define IsInside(x,y) ((x) >= 0 && (x) <= xlen-1 && (y) >= 0 && (y) <= ylen-1)
static void _convert_screen_graphics(char *screen, const int x, const int y, const int xlen, const int ylen) {
static char map[11][3][4] ={ { "...",
".||",
".|." },
{ "...",
"||.",
".|." },
{ ".|.",
".||",
"..." },
{ ".|.",
"||.",
"..." },
{ "~|~",
".|.",
"~|~" },
{ "~.~",
"|||",
"~.~" },
{ ".|.",
".||",
".|." },
{ ".|.",
"||.",
".|." },
{ "...",
"|||",
".|." },
{ ".|.",
"|||",
"..." },
{ ".|.",
"|||",
".|." } };
bool found_glyph = false;
int k = 10;
for (; k >= 0; k--) {
found_glyph = true;
for (int yy = y - 1; found_glyph && yy <= y + 1; yy++) {
int idx = yy*(xlen+1);
for (int xx = x - 1; xx <= x + 1; xx++) {
char map_ch = map[k][ yy - y + 1 ][ xx - x + 1 ];
if (IsInside(xx, yy)) {
char c = *(screen + idx + xx);
if (!IsGraphic( c ) && (map_ch == '|')) {
found_glyph = false;
break;
} else if (IsGraphic( c ) && (map_ch == '.')) {
found_glyph = false;
break;
}
} else if (map_ch == '|') {
found_glyph = false;
break;
}
}
idx += xlen+1;
}
if (found_glyph) {
break;
}
}
if (found_glyph) {
*(screen + y*(xlen+1) + x) = 0x80 + k;
}
}
void video_interface_translate_screen_x_y(char *screen, const int xlen, const int ylen) {
for (int idx=0, y=0; y < ylen; y++, idx+=xlen+1) {
for (int x = 0; x < xlen; x++) {
if (*(screen + idx + x) == '|') {
_convert_screen_graphics(screen, x, y, xlen, ylen);
}
}
}
}
void video_interface_print_submenu_centered_fb(uint8_t *fb, int submenu_width, int submenu_height, char *submenu, const int xlen, const int ylen) {
video_interface_translate_screen_x_y(submenu, xlen, ylen);
int x = (submenu_width - xlen) >> 1;
int y = (submenu_height - ylen) >> 1;
int fb_width = (submenu_width*7) + INTERPOLATED_PIXEL_ADJUSTMENT; // HACK NOTE : interpolated pixel adjustment still necessary ...
int ymax = y+ylen;
for (int idx=0; y < ymax; y++, idx+=xlen+1) {
video_interface_print_fb(fb, fb_width, x, y, 2, &submenu[ idx ]);
}
}
void video_interface_print_submenu_centered(char *submenu, const int xlen, const int ylen) {
video_interface_print_submenu_centered_fb(video__fb1, INTERFACE_SCREEN_X, TEXT_ROWS, submenu, xlen, ylen);
}
// ----------------------------------------------------------------------------
// Double-Hires GRaphics
@ -1052,7 +1183,7 @@ void video_init(void) {
#if !defined(__APPLE__)
#if !defined(ANDROID)
if (!is_headless) {
video_driver_init((void *)0);
video_backend->init((void*)0);
}
#endif
#endif
@ -1062,7 +1193,7 @@ void video_init(void) {
void video_shutdown(void) {
#if !HEADLESS
if (!is_headless) {
video_driver_shutdown();
video_backend->shutdown();
}
#if !defined(__APPLE__)
exit(0);
@ -1072,7 +1203,7 @@ void video_shutdown(void) {
void video_main_loop(void) {
#if !HEADLESS
video_driver_main_loop();
video_backend->main_loop();
#endif
}

View File

@ -16,27 +16,20 @@
#include "common.h"
static struct stat statbuf = { 0 };
static int altdrive = 0;
bool in_interface = false;
static struct stat statbuf;
static int altdrive;
/*#define undoc_supported 1*/
/*#else*/
/*#define undoc_supported 0*/
void copy_and_pad_string(char *dest, const char* src, const char c, const int len, const char cap)
{
const char* p;
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;
for (p = src; ((*p != '\0') && (p-src < len-1)); p++)
{
for (; ((*p != '\0') && (p-src < len-1)); p++) {
*d++ = *p;
}
while (d-dest < len-1)
{
while (d-dest < len-1) {
*d++ = c;
}
@ -44,199 +37,46 @@ void copy_and_pad_string(char *dest, const char* src, const char c, const int le
}
static void pad_string(char *s, const char c, const int len) {
char *p;
char *p = s;
for (p = s; ((*p != '\0') && (p-s < len-1)); p++)
{
for (; ((*p != '\0') && (p-s < len-1)); p++) {
// counting ...
}
while (p-s < len-1)
{
while (p-s < len-1) {
*p++ = c;
}
*p = '\0';
}
/* -------------------------------------------------------------------------
c_load_interface_font()
------------------------------------------------------------------------- */
void c_load_interface_font()
{
video_loadfont_int(0x00,0x40,ucase_glyphs);
video_loadfont_int(0x40,0x20,ucase_glyphs);
video_loadfont_int(0x60,0x20,lcase_glyphs);
video_loadfont_int(0x80,0x40,ucase_glyphs);
video_loadfont_int(0xC0,0x20,ucase_glyphs);
video_loadfont_int(0xE0,0x20,lcase_glyphs);
video_loadfont_int(0x80,11,interface_glyphs);
video_loadfont_int(MOUSETEXT_BEGIN,0x20,mousetext_glyphs);
}
/* -------------------------------------------------------------------------
c_interface_print()
------------------------------------------------------------------------- */
void c_interface_print( int x, int y, int cs, const char *s )
{
for (; *s; x++, s++)
{
video_plotchar( x, y, cs, *s );
}
void c_interface_print( int x, int y, int cs, const char *s ) {
video_interface_print(x, y, cs, s);
}
/* -------------------------------------------------------------------------
c_interface_print_screen()
------------------------------------------------------------------------- */
void c_interface_print_screen( char screen[24][INTERFACE_SCREEN_X+1] )
{
for (int y = 0; y < 24; y++)
{
void c_interface_print_screen( char screen[24][INTERFACE_SCREEN_X+1] ) {
for (int y = 0; y < 24; y++) {
c_interface_print( 0, y, 2, screen[ y ] );
}
}
/* -------------------------------------------------------------------------
c_interface_translate_screen()
------------------------------------------------------------------------- */
#define IsGraphic(c) ((c) == '|' || (((unsigned char)c) >= 0x80 && ((unsigned char)c) <= 0x8A))
#define IsInside(x,y) ((x) >= 0 && (x) <= xlen-1 && (y) >= 0 && (y) <= ylen-1)
static void _convert_screen_graphics( char *screen, const int x, const int y, const int xlen, const int ylen )
{
static char map[11][3][4] ={ { "...",
".||",
".|." },
{ "...",
"||.",
".|." },
{ ".|.",
".||",
"..." },
{ ".|.",
"||.",
"..." },
{ "~|~",
".|.",
"~|~" },
{ "~.~",
"|||",
"~.~" },
{ ".|.",
".||",
".|." },
{ ".|.",
"||.",
".|." },
{ "...",
"|||",
".|." },
{ ".|.",
"|||",
"..." },
{ ".|.",
"|||",
".|." } };
bool found_glyph = false;
int k = 10;
for (; k >= 0; k--)
{
found_glyph = true;
for (int yy = y - 1; found_glyph && yy <= y + 1; yy++)
{
int idx = yy*(xlen+1);
for (int xx = x - 1; xx <= x + 1; xx++)
{
char map_ch = map[k][ yy - y + 1 ][ xx - x + 1 ];
if (IsInside(xx, yy))
{
char c = *(screen + idx + xx);
if (!IsGraphic( c ) && (map_ch == '|'))
{
found_glyph = false;
break;
}
else if (IsGraphic( c ) && (map_ch == '.'))
{
found_glyph = false;
break;
}
}
else if (map_ch == '|')
{
found_glyph = false;
break;
}
}
idx += xlen+1;
}
if (found_glyph)
{
break;
}
}
if (found_glyph)
{
*(screen + y*(xlen+1) + x) = 0x80 + k;
}
static void c_interface_translate_screen_x_y(char *screen, const int xlen, const int ylen) {
video_interface_translate_screen_x_y(screen, xlen, ylen);
}
static void c_interface_translate_screen_x_y(char *screen, const int xlen, const int ylen)
{
for (int idx=0, y=0; y < ylen; y++, idx+=xlen+1)
{
for (int x = 0; x < xlen; x++)
{
if (*(screen + idx + x) == '|')
{
_convert_screen_graphics(screen, x, y, xlen, ylen);
}
}
}
}
void c_interface_translate_screen( char screen[24][INTERFACE_SCREEN_X+1] )
{
void c_interface_translate_screen( char screen[24][INTERFACE_SCREEN_X+1] ) {
c_interface_translate_screen_x_y(screen[0], INTERFACE_SCREEN_X, 24);
}
/* -------------------------------------------------------------------------
c_interface_print_submenu_centered()
------------------------------------------------------------------------- */
void c_interface_print_submenu_centered( char *submenu, const int xlen, const int ylen )
{
c_interface_translate_screen_x_y(submenu, xlen, ylen);
int x = (INTERFACE_SCREEN_X - xlen) >> 1;
int y = (24 - ylen) >> 1;
int ymax = y+ylen;
for (int idx=0; y < ymax; y++, idx+=xlen+1)
{
c_interface_print( x, y, 2, &submenu[ idx ] );
}
void c_interface_print_submenu_centered( char *submenu, const int xlen, const int ylen ) {
video_interface_print_submenu_centered(submenu, xlen, ylen);
}
/* ------------------------------------------------------------------------- */
static int c_interface_cut_name(char *name)
{
char *p = name + strlen(name) - 1;
@ -1280,8 +1120,10 @@ void c_interface_parameters()
#ifdef __linux__
LOG("Back to Linux, w00t!\n");
#endif
#ifdef AUDIO_ENABLED
speaker_destroy();
MB_Destroy();
#endif
video_shutdown();
exit( 0 );

View File

@ -17,16 +17,13 @@
#ifndef A2_INTERFACE_H
#define A2_INTERFACE_H
#define INTERFACE_SCREEN_X 80
#define MOUSETEXT_BEGIN 0x90
#include "video/video.h"
extern bool in_interface;
void c_interface_begin(int current_key);
void c_interface_print(int x, int y, const int cs, const char *s);
void c_interface_print_submenu_centered(char *submenu, const int xlen, const int ylen);
void c_load_interface_font();
void c_interface_keyboard_layout();
void c_interface_parameters();
void c_interface_credits();

View File

@ -254,6 +254,72 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked)
timing_toggle_cpu_speed();
break;
}
//#define CPUSCALE_ANIMATIONS_KEYS 1
#if CPUSCALE_ANIMATIONS_KEYS
if (current_key == kF3) {
double scale = (alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor);
int percent_scale = (int)round(scale * 100);
if (scale == CPU_SCALE_FASTEST) {
scale = CPU_SCALE_FASTEST0;
percent_scale = (int)round(scale * 100);
} else {
if (percent_scale > 100) {
percent_scale -= 25;
} else {
percent_scale -= 5;
}
}
scale = percent_scale/100.0;
if (scale < CPU_SCALE_SLOWEST) {
scale = CPU_SCALE_SLOWEST;
}
if (alt_speed_enabled) {
cpu_altscale_factor = scale;
} else {
cpu_scale_factor = scale;
}
if (video_animation_show_cpuspeed) {
video_animation_show_cpuspeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
break;
}
if (current_key == kF4) {
int percent_scale = (int)round((alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor) * 100);
double scale = 0.0;
if (percent_scale >= 100) {
percent_scale += 25;
} else {
percent_scale += 5;
}
scale = percent_scale/100.0;
if (scale > CPU_SCALE_FASTEST) {
scale = CPU_SCALE_FASTEST;
}
if (alt_speed_enabled) {
cpu_altscale_factor = scale;
} else {
cpu_scale_factor = scale;
}
if (video_animation_show_cpuspeed) {
video_animation_show_cpuspeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
break;
}
#endif
#endif
if (current_key == kEND)

View File

@ -593,12 +593,8 @@ void reinitialize(void) {
}
void c_initialize_firsttime(void) {
#ifdef INTERFACE_CLASSIC
/* read in system files and calculate system defaults */
c_load_interface_font();
#endif
video_load_interface_fonts();
/* initialize the video system */
video_init();
#ifdef DEBUGGER

View File

@ -64,7 +64,7 @@ static bool auto_adjust_speed = true;
double cpu_scale_factor = 1.0;
double cpu_altscale_factor = 1.0;
bool is_fullspeed = false;
static bool alt_speed_enabled = false;
bool alt_speed_enabled = false;
// misc
volatile uint8_t emul_reinitialize = 0;

View File

@ -37,6 +37,7 @@
#define CLK_6502_INT ((_M14_INT * 65) / 912)
#define CPU_SCALE_SLOWEST 0.25
#define CPU_SCALE_FASTEST0 4.0
#define CPU_SCALE_FASTEST 4.05
#ifdef INTERFACE_CLASSIC
# define CPU_SCALE_STEP_DIV 0.01
@ -50,6 +51,7 @@ extern int cycles_speaker_feedback; // current -/+ speaker requested fee
extern double cpu_scale_factor; // scale factor #1
extern double cpu_altscale_factor; // scale factor #2
extern bool is_fullspeed; // emulation in full native speed?
extern bool alt_speed_enabled;
extern pthread_t cpu_thread_id;
extern pthread_mutex_t interface_mutex;

View File

@ -12,19 +12,39 @@ precision highp float;
// above.
#if __VERSION__ >= 140
in vec2 varTexcoord;
out vec4 fragColor;
in vec2 varTexcoord;
out vec4 fragColor;
#else
varying vec2 varTexcoord;
#endif
uniform sampler2D diffuseTexture;
// global alpha value
uniform float aValue;
// texture switch
uniform int tex2Use;
// Framebuffer
uniform sampler2D framebufferTexture;
// Floating message
uniform sampler2D messageTexture;
#if __VERSION__ >= 140
#define OUTPUT_TEXTURE(TEX) \
vec4 tex = texture(TEX, varTexcoord.st, 0.0); \
fragColor = vec4(tex.r, tex.g, tex.b, 1.0*aValue)
#else
#define OUTPUT_TEXTURE(TEX) \
vec4 tex = texture2D(TEX, varTexcoord.st, 0.0); \
gl_FragColor = vec4(tex.r, tex.g, tex.b, 1.0*aValue)
#endif
void main(void)
{
#if __VERSION__ >= 140
fragColor = texture(diffuseTexture, varTexcoord.st, 0.0);
#else
gl_FragColor = texture2D(diffuseTexture, varTexcoord.st, 0.0);
#endif
if (tex2Use == 1) {
OUTPUT_TEXTURE(messageTexture);
} else {
OUTPUT_TEXTURE(framebufferTexture);
}
}

76
src/video/glanimation.c Normal file
View File

@ -0,0 +1,76 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#include "common.h"
#include "video/glanimation.h"
#include "video/glvideo.h"
void (*video_animation_show_cpuspeed)(void) = NULL;
void (*video_animation_show_track_sector)(int drive, int track, int sect) = NULL;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
typedef struct glanim_array_node_t {
struct glanim_array_node_t *next;
glanim_t *anim;
} glanim_array_node_t;
static glanim_array_node_t *animations = NULL;
// -----------------------------------------------------------------------------
void gldriver_register_animation(glanim_t *anim) {
pthread_mutex_lock(&mutex);
glanim_array_node_t *node = malloc(sizeof(glanim_array_node_t));
assert(node);
node->next = NULL;
node->anim = anim;
if (animations == NULL) {
animations = node;
} else {
glanim_array_node_t *p = animations;
while (p->next) {
p = p->next;
}
p->next = node;
}
pthread_mutex_unlock(&mutex);
}
void gldriver_animation_init(void) {
LOG("gldriver_animation_init ...");
glanim_array_node_t *p = animations;
while (p) {
p->anim->ctor();
p = p->next;
}
}
void gldriver_animation_destroy(void) {
LOG("gldriver_animation_destroy ...");
glanim_array_node_t *p = animations;
while (p) {
p->anim->dtor();
p = p->next;
}
}
void gldriver_animation_render(void) {
glanim_array_node_t *p = animations;
while (p) {
p->anim->render();
p = p->next;
}
}

40
src/video/glanimation.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#include "common.h"
#ifndef _GLANIMATION_H_
#define _GLANIMATION_H_
typedef void (*glanim_ctor_fn)(void);
typedef void (*glanim_dtor_fn)(void);
typedef void (*glanim_render_fn)(void);
typedef struct glanim_t {
glanim_ctor_fn ctor;
glanim_dtor_fn dtor;
glanim_render_fn render;
} glanim_t;
// register an animation
void gldriver_register_animation(glanim_t *anim);
// initialize animations module
void gldriver_animation_init(void);
// destroy animations module
void gldriver_animation_destroy(void);
// renders the animation
void gldriver_animation_render(void);
#endif

408
src/video/glcpuanim.c Normal file
View File

@ -0,0 +1,408 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#include "common.h"
#include "video/glanimation.h"
#include "video/glvideo.h"
#define CPUTIMING_TEMPLATE_COLS 8
#define CPUTIMING_TEMPLATE_ROWS 3
// HACK NOTE FIXME TODO : interpolated pixel adjustment still necessary ...
#define MESSAGE_FB_WIDTH ((CPUTIMING_TEMPLATE_COLS * FONT80_WIDTH_PIXELS) + INTERPOLATED_PIXEL_ADJUSTMENT)
#define MESSAGE_FB_HEIGHT (CPUTIMING_TEMPLATE_ROWS * FONT_HEIGHT_PIXELS)
static bool animation_subsystem_functional = false;
static char cputiming_template[CPUTIMING_TEMPLATE_ROWS][CPUTIMING_TEMPLATE_COLS+1] = {
"||||||||",
"| xxx% |",
"||||||||",
};
static struct timespec cputiming_begin = { 0 };
static bool cputiming_enabled = true;
static bool texture_dirty = true;
static demoModel *cpuMessageObjModel = NULL;
static GLuint cpuMessageObjVAOName = UNINITIALIZED_GL;
static GLenum cpuMessageObjElementType = UNINITIALIZED_GL;
static GLuint cpuMessageObjNumElements = UNINITIALIZED_GL;
static GLuint cpuMessageObjTextureName = UNINITIALIZED_GL;
static GLuint cpuMessageObjPosBufferName = UNINITIALIZED_GL;
static GLuint cpuMessageObjTexcoordBufferName = UNINITIALIZED_GL;
static GLuint cpuMessageObjElementBufferName = UNINITIALIZED_GL;
static uint8_t cpuMessageFB[MESSAGE_FB_WIDTH * MESSAGE_FB_HEIGHT] = { 0 };
static uint8_t cpuMessagePixels[MESSAGE_FB_WIDTH * MESSAGE_FB_HEIGHT * 4] = { 0 };// RGBA8888
static glanim_t cpuMessageAnimation = { 0 };
static void _create_message_model(void) {
const GLfloat messageObj_positions[] = {
-0.3, -0.3, -0.1, 1.0,
0.3, -0.3, -0.1, 1.0,
-0.3, 0.3, -0.1, 1.0,
0.3, 0.3, -0.1, 1.0,
};
const GLfloat messageObj_texcoords[] = {
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
};
const GLushort indices[] = {
0, 1, 2, 2, 1, 3
};
demoModel *messageObj = calloc(1, sizeof(demoModel));
messageObj->numVertices = 4;
messageObj->numElements = 6;
messageObj->positions = malloc(sizeof(messageObj_positions));
memcpy(messageObj->positions, &messageObj_positions[0], sizeof(messageObj_positions));
messageObj->positionType = GL_FLOAT;
messageObj->positionSize = 4; // x,y,z coordinates
messageObj->positionArraySize = sizeof(messageObj_positions);
messageObj->texcoords = malloc(sizeof(messageObj_texcoords));
memcpy(messageObj->texcoords, &messageObj_texcoords[0], sizeof(messageObj_texcoords));
messageObj->texcoordType = GL_FLOAT;
messageObj->texcoordSize = 2; // s,t coordinates
messageObj->texcoordArraySize = sizeof(messageObj_texcoords);
messageObj->normals = NULL;
messageObj->normalType = GL_NONE;
messageObj->normalSize = GL_NONE;
messageObj->normalArraySize = 0;
messageObj->elements = malloc(sizeof(indices));
memcpy(messageObj->elements, &indices[0], sizeof(indices));
messageObj->elementType = GL_UNSIGNED_SHORT;
messageObj->elementArraySize = sizeof(indices);
mdlDestroyModel(cpuMessageObjModel);
cpuMessageObjModel = messageObj;
}
static void _create_message_VAO_VBOs(const demoModel *messageModel, GLuint *messageVAOName, GLuint *posBufferName, GLuint *texcoordBufferName, GLuint *elementBufferName) {
// Create a vertex array object (VAO) to cache model parameters
#if USE_VAO
glGenVertexArrays(1, messageVAOName);
glBindVertexArray(*messageVAOName);
#endif
// Create a vertex buffer object (VBO) to store positions and load data
glGenBuffers(1, posBufferName);
glBindBuffer(GL_ARRAY_BUFFER, *posBufferName);
glBufferData(GL_ARRAY_BUFFER, messageModel->positionArraySize, messageModel->positions, GL_STATIC_DRAW);
#if USE_VAO
// Enable the position attribute for this VAO
glEnableVertexAttribArray(POS_ATTRIB_IDX);
// Get the size of the position type so we can set the stride properly
GLsizei posTypeSize = _get_gl_type_size(messageModel->positionType);
// Set up parmeters for position attribute in the VAO including,
// size, type, stride, and offset in the currenly bound VAO
// This also attaches the position VBO to the VAO
glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
messageModel->positionSize, // How many elements are there per position?
messageModel->positionType, // What is the type of this data?
GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types)
messageModel->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)?
0); // What is the offset in the VBO to the position data?
#endif
if (messageModel->texcoords) {
// Create a VBO to store texcoords
glGenBuffers(1, texcoordBufferName);
glBindBuffer(GL_ARRAY_BUFFER, *texcoordBufferName);
// Allocate and load texcoord data into the VBO
glBufferData(GL_ARRAY_BUFFER, messageModel->texcoordArraySize, messageModel->texcoords, GL_STATIC_DRAW);
#if USE_VAO
// Enable the texcoord attribute for this VAO
glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX);
// Get the size of the texcoord type so we can set the stride properly
GLsizei texcoordTypeSize = _get_gl_type_size(messageModel->texcoordType);
// Set up parmeters for texcoord attribute in the VAO including,
// size, type, stride, and offset in the currenly bound VAO
// This also attaches the texcoord VBO to VAO
glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
messageModel->texcoordSize, // How many elements are there per texture coord?
messageModel->texcoordType, // What is the type of this data in the array?
GL_TRUE, // Do we want to normalize this data (0-1 range for fixed-point types)
messageModel->texcoordSize*texcoordTypeSize, // What is the stride (i.e. bytes between texcoords)?
0); // What is the offset in the VBO to the texcoord data?
#endif
}
// Create a VBO to vertex array elements
// This also attaches the element array buffer to the VAO
glGenBuffers(1, elementBufferName);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *elementBufferName);
// Allocate and load vertex array element data into VBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, messageModel->elementArraySize, messageModel->elements, GL_STATIC_DRAW);
GL_ERRLOG("finished creating VAO/VBOs");
}
static void _destroy_VAO_VBOs(GLuint vaoName, GLuint posBufferName, GLuint texcoordBufferName, GLuint elementBufferName) {
// Bind the VAO so we can get data from it
#if USE_VAO
glBindVertexArray(vaoName);
// For every possible attribute set in the VAO
for (GLuint index = 0; index < 16; index++) {
// Get the VBO set for that attibute
GLuint bufName = 0;
glGetVertexAttribiv(index , GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint*)&bufName);
// If there was a VBO set...
if (bufName) {
//...delete the VBO
glDeleteBuffers(1, &bufName);
}
}
// Get any element array VBO set in the VAO
{
GLuint bufName = 0;
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint*)&bufName);
// If there was a element array VBO set in the VAO
if (bufName) {
//...delete the VBO
glDeleteBuffers(1, &bufName);
}
}
// Finally, delete the VAO
glDeleteVertexArrays(1, &vaoName);
#else
glDeleteBuffers(1, &posBufferName);
glDeleteBuffers(1, &texcoordBufferName);
glDeleteBuffers(1, &elementBufferName);
#endif
GL_ERRLOG("destroying VAO/VBOs");
}
static void _create_message_texture(void) {
cpuMessageObjTextureName = UNINITIALIZED_GL;
// Create a texture object to apply to model
glGenTextures(1, &cpuMessageObjTextureName);
glBindTexture(GL_TEXTURE_2D, cpuMessageObjTextureName);
// Set up filter and wrap modes for this texture object
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Indicate that pixel rows are tightly packed
// (defaults to stride of 4 which is kind of only good for
// RGBA or FLOAT data types)
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// register texture with OpenGL
glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, MESSAGE_FB_WIDTH, MESSAGE_FB_HEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)cpuMessagePixels);
GL_ERRLOG("finished creating message texture");
}
static void cpuanim_init(void) {
LOG("gldriver_animation_init ...");
_create_message_model();
cpuMessageObjVAOName = UNINITIALIZED_GL;
cpuMessageObjPosBufferName = UNINITIALIZED_GL;
cpuMessageObjTexcoordBufferName = UNINITIALIZED_GL;
cpuMessageObjElementBufferName = UNINITIALIZED_GL;
_create_message_VAO_VBOs(cpuMessageObjModel, &cpuMessageObjVAOName, &cpuMessageObjPosBufferName, &cpuMessageObjTexcoordBufferName, &cpuMessageObjElementBufferName);
if (cpuMessageObjPosBufferName == UNINITIALIZED_GL || cpuMessageObjTexcoordBufferName == UNINITIALIZED_GL || cpuMessageObjElementBufferName == UNINITIALIZED_GL)
{
LOG("not initializing CPU speed animations");
return;
}
cpuMessageObjNumElements = cpuMessageObjModel->numElements;
cpuMessageObjElementType = cpuMessageObjModel->elementType;
_create_message_texture();
animation_subsystem_functional = true;
}
static void cpuanim_destroy(void) {
LOG("gldriver_animation_destroy ...");
if (!animation_subsystem_functional) {
return;
}
animation_subsystem_functional = false;
// cleanup cputiming message objects
glDeleteTextures(1, &cpuMessageObjTextureName);
cpuMessageObjTextureName = UNINITIALIZED_GL;
_destroy_VAO_VBOs(cpuMessageObjVAOName, cpuMessageObjPosBufferName, cpuMessageObjTexcoordBufferName, cpuMessageObjElementBufferName);
cpuMessageObjVAOName = UNINITIALIZED_GL;
cpuMessageObjPosBufferName = UNINITIALIZED_GL;
cpuMessageObjTexcoordBufferName = UNINITIALIZED_GL;
cpuMessageObjElementBufferName = UNINITIALIZED_GL;
mdlDestroyModel(cpuMessageObjModel);
cpuMessageObjModel = NULL;
}
static void _render_message_object(GLfloat alpha, GLuint vaoName, GLuint posBufferName, GLuint texcoordBufferName, GLuint elementBufferName) {
// Bind our vertex array object
#if USE_VAO
glBindVertexArray(vaoName);
#else
glBindBuffer(GL_ARRAY_BUFFER, posBufferName);
GLsizei posTypeSize = _get_gl_type_size(cpuMessageObjModel->positionType);
GLsizei texcoordTypeSize = _get_gl_type_size(cpuMessageObjModel->texcoordType);
// Set up parmeters for position attribute in the VAO including, size, type, stride, and offset in the currenly
// bound VAO This also attaches the position VBO to the VAO
glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
cpuMessageObjModel->positionSize, // How many elements are there per position?
cpuMessageObjModel->positionType, // What is the type of this data?
GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types)
cpuMessageObjModel->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)?
0); // What is the offset in the VBO to the position data?
glEnableVertexAttribArray(POS_ATTRIB_IDX);
// Set up parmeters for texcoord attribute in the VAO including, size, type, stride, and offset in the currenly
// bound VAO This also attaches the texcoord VBO to VAO
glBindBuffer(GL_ARRAY_BUFFER, texcoordBufferName);
glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
cpuMessageObjModel->texcoordSize, // How many elements are there per texture coord?
cpuMessageObjModel->texcoordType, // What is the type of this data in the array?
GL_TRUE, // Do we want to normalize this data (0-1 range for fixed-point types)
cpuMessageObjModel->texcoordSize*texcoordTypeSize,// What is the stride (i.e. bytes between texcoords)?
0); // What is the offset in the VBO to the texcoord data?
glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferName);
#endif
glUniform1f(alphaValue, alpha);
// Draw the message object
glDrawElements(GL_TRIANGLES, cpuMessageObjNumElements, cpuMessageObjElementType, 0);
GL_ERRLOG("CPU message render");
}
static void cpuanim_render(void) {
if (!animation_subsystem_functional) {
return;
}
if (!cputiming_enabled) {
return;
}
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
float alpha = 0.95;
struct timespec deltat = timespec_diff(cputiming_begin, now, NULL);
if (deltat.tv_sec >= 1) {
cputiming_enabled = false;
return;
} else if (deltat.tv_nsec >= NANOSECONDS_PER_SECOND/2) {
alpha -= ((float)deltat.tv_nsec-(NANOSECONDS_PER_SECOND/2)) / (float)(NANOSECONDS_PER_SECOND/2);
if (alpha < 0.0) {
alpha = 0.0;
}
}
//LOG("alpha : %f", alpha);
glActiveTexture(TEXTURE_ACTIVE_MESSAGE);
glBindTexture(GL_TEXTURE_2D, cpuMessageObjTextureName);
if (texture_dirty) {
texture_dirty = false;
glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, MESSAGE_FB_WIDTH, MESSAGE_FB_HEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)cpuMessagePixels);
}
glUniform1i(uniformTex2Use, TEXTURE_ID_MESSAGE);
_render_message_object(alpha, cpuMessageObjVAOName, cpuMessageObjPosBufferName, cpuMessageObjTexcoordBufferName, cpuMessageObjElementBufferName);
}
static void cpuanim_show(void) {
if (!animation_subsystem_functional) {
return;
}
char buf[8];
double scale = (alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor);
int percentScale = scale * 100;
if (percentScale < 100.0) {
snprintf(buf, 3, "%d", percentScale);
cputiming_template[1][2] = ' ';
cputiming_template[1][3] = buf[0];
cputiming_template[1][4] = buf[1];
} else if (scale == CPU_SCALE_FASTEST) {
cputiming_template[1][2] = 'm';
cputiming_template[1][3] = 'a';
cputiming_template[1][4] = 'x';
} else {
snprintf(buf, 4, "%d", percentScale);
cputiming_template[1][2] = buf[0];
cputiming_template[1][3] = buf[1];
cputiming_template[1][4] = buf[2];
}
// render template into indexed fb
unsigned int submenu_width = CPUTIMING_TEMPLATE_COLS;
unsigned int submenu_height = CPUTIMING_TEMPLATE_ROWS;
char *submenu = cputiming_template[0];
video_interface_print_submenu_centered_fb(cpuMessageFB, submenu_width, submenu_height, submenu, submenu_width, submenu_height);
// generate RGBA_8888 from indexed color
unsigned int count = MESSAGE_FB_WIDTH * MESSAGE_FB_HEIGHT;
for (unsigned int i=0, j=0; i<count; i++, j+=4) {
uint8_t index = *(cpuMessageFB + i);
*( (uint32_t*)(cpuMessagePixels + j) ) = (uint32_t)(
((uint32_t)(colormap[index].red) << 0 ) |
((uint32_t)(colormap[index].green) << 8 ) |
((uint32_t)(colormap[index].blue) << 16) |
((uint32_t)0xff << 24)
);
}
cputiming_enabled = true;
texture_dirty = true;
clock_gettime(CLOCK_MONOTONIC, &cputiming_begin);
}
__attribute__((constructor))
static void _init_glcpuanim(void) {
LOG("Registering CPU speed animations");
video_animation_show_cpuspeed = &cpuanim_show;
cpuMessageAnimation.ctor = &cpuanim_init;
cpuMessageAnimation.dtor = &cpuanim_destroy;
cpuMessageAnimation.render = &cpuanim_render;
gldriver_register_animation(&cpuMessageAnimation);
}

View File

@ -12,17 +12,7 @@
// GL touch joystick -- Created by Aaron Culliney
#include "common.h"
#include "video_util/modelUtil.h"
#include "video_util/matrixUtil.h"
#include "video_util/sourceUtil.h"
#ifdef __APPLE__
#import <CoreFoundation/CoreFoundation.h>
#endif
// VAO optimization (may not be available on all platforms)
#define USE_VAO 0
#include "video/glvideo.h"
void gldriver_joystick_reset(void) {
}

View File

@ -12,34 +12,9 @@
// glvideo -- Created by Aaron Culliney
#include "common.h"
#include "video/glvideo.h"
#include "video/glinput.h"
#include "video/renderer.h"
#include "video_util/modelUtil.h"
#include "video_util/matrixUtil.h"
#include "video_util/sourceUtil.h"
#ifdef __APPLE__
#import <CoreFoundation/CoreFoundation.h>
#define USE_VAO 1
#endif
// TODO: implement 3D CRT object, possibly with perspective drawing?
#define PERSPECTIVE 0
// VAO optimization (may not be available on all platforms)
#ifdef ANDROID
#warning YAY Awesome! Various older Android and Android-ish devices (*cough* Kindle *cough*) have buggy OpenGL VAO support, so do like Nancy and just say no
#define USE_VAO 0
#elif !defined(USE_VAO)
#define USE_VAO 1
#endif
enum {
POS_ATTRIB_IDX,
TEXCOORD_ATTRIB_IDX,
NORMAL_ATTRIB_IDX,
};
#include "video/glanimation.h"
bool safe_to_do_opengl_logging = false;
@ -56,57 +31,42 @@ static int viewportHeight = SCANHEIGHT*1.5;
static int adjustedHeight = 0;
#endif
static GLint uniformMVPIdx;
static GLenum crtElementType;
static GLuint crtNumElements;
GLint uniformTex2Use = UNINITIALIZED_GL;
GLint alphaValue = UNINITIALIZED_GL;
static GLint uniformMVPIdx = UNINITIALIZED_GL;
static GLenum crtElementType = UNINITIALIZED_GL;
static GLuint crtNumElements = UNINITIALIZED_GL;
static GLuint a2TextureName = 0;
static GLuint defaultFBO = 0;
static GLuint program = 0;
static GLuint a2TextureName = UNINITIALIZED_GL;
static GLuint defaultFBO = UNINITIALIZED_GL;
static GLuint program = UNINITIALIZED_GL;
static GLuint crtVAOName = 0;
static GLuint posBufferName = 0;
static GLuint texcoordBufferName;
static GLuint elementBufferName = 0;
static GLuint crtVAOName = UNINITIALIZED_GL;
static GLuint posBufferName = UNINITIALIZED_GL;
static GLuint texcoordBufferName = UNINITIALIZED_GL;
static GLuint elementBufferName = UNINITIALIZED_GL;
static demoModel *crtModel = NULL;
static video_backend_s glvideo_backend = { 0 };
#if USE_GLUT
static int glutWindow = -1;
#endif
//----------------------------------------------------------------------------
//
// OpenGL helper routines
//
static inline GLsizei _get_gl_type_size(GLenum type) {
switch (type) {
case GL_BYTE:
return sizeof(GLbyte);
case GL_UNSIGNED_BYTE:
return sizeof(GLubyte);
case GL_SHORT:
return sizeof(GLshort);
case GL_UNSIGNED_SHORT:
return sizeof(GLushort);
case GL_INT:
return sizeof(GLint);
case GL_UNSIGNED_INT:
return sizeof(GLuint);
case GL_FLOAT:
return sizeof(GLfloat);
}
return 0;
}
static void _create_CRT_model(void) {
#define STRIDE 9*sizeof(GLfloat)
#define TEST_COLOR_OFF (GLvoid *)(3*sizeof(GLfloat))
#define TEX_COORD_OFF (GLvoid *)(7*sizeof(GLfloat))
// NOTE: vertices in Normalized Device Coordinates
const GLfloat crt_positions[] = {
// CRT screen quad
-1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 0.0, 1.0,
-1.0, 1.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
1.0, 1.0, 0.0, 1.0,
#if PERSPECTIVE
// CRT back side point
0.0, 0.0, -1.0, 1.0,
@ -289,8 +249,11 @@ static void _destroy_VAO(GLuint vaoName) {
glDeleteVertexArrays(1, &vaoName);
#else
glDeleteBuffers(1, &posBufferName);
posBufferName = UNINITIALIZED_GL;
glDeleteBuffers(1, &texcoordBufferName);
texcoordBufferName = UNINITIALIZED_GL;
glDeleteBuffers(1, &elementBufferName);
elementBufferName = UNINITIALIZED_GL;
#endif
GL_ERRLOG("destroying VAO/VBOs");
@ -301,6 +264,7 @@ static GLuint _create_CRT_texture(void) {
// Create a texture object to apply to model
glGenTextures(1, &texName);
glActiveTexture(TEXTURE_ACTIVE_FRAMEBUFFER);
glBindTexture(GL_TEXTURE_2D, texName);
// Set up filter and wrap modes for this texture object
@ -498,11 +462,34 @@ static GLuint _build_program(demoSource *vertexSource, demoSource *fragmentSourc
// Setup common program input points //
///////////////////////////////////////
GLint samplerLoc = glGetUniformLocation(prgName, "diffuseTexture");
GLint fbSamplerLoc = glGetUniformLocation(prgName, "framebufferTexture");
if (fbSamplerLoc < 0) {
LOG("OOPS, no framebufferTexture shader : %d", fbSamplerLoc);
} else {
glUniform1i(fbSamplerLoc, TEXTURE_ID_FRAMEBUFFER);
}
// Indicate that the diffuse texture will be bound to texture unit 0
GLint unit = 0;
glUniform1i(samplerLoc, unit);
GLint messageSamplerLoc = glGetUniformLocation(prgName, "messageTexture");
if (messageSamplerLoc < 0) {
LOG("OOPS, no messageSamplerLoc shader : %d", messageSamplerLoc);
} else {
glUniform1i(messageSamplerLoc, TEXTURE_ID_MESSAGE);
}
uniformMVPIdx = glGetUniformLocation(prgName, "modelViewProjectionMatrix");
if (uniformMVPIdx < 0) {
LOG("OOPS, no modelViewProjectionMatrix in shader : %d", uniformMVPIdx);
}
uniformTex2Use = glGetUniformLocation(prgName, "tex2Use");
if (uniformTex2Use < 0) {
LOG("OOPS, no texture selector in shader : %d", uniformTex2Use);
}
alphaValue = glGetUniformLocation(prgName, "aValue");
if (alphaValue < 0) {
LOG("OOPS, no texture selector in shader : %d", alphaValue);
}
GL_ERRLOG("build program");
@ -533,6 +520,8 @@ static demoSource *_create_shader_source(const char *fileName) {
return src;
}
static void gldriver_render(void);
static void gldriver_init_common(void) {
LOG("%s %s", glGetString(GL_RENDERER), glGetString(GL_VERSION));
@ -578,11 +567,6 @@ static void gldriver_init_common(void) {
srcDestroySource(vtxSource);
srcDestroySource(frgSource);
uniformMVPIdx = glGetUniformLocation(program, "modelViewProjectionMatrix");
if (uniformMVPIdx < 0) {
LOG("No modelViewProjectionMatrix in character shader");
}
// ----------------------------
// setup static OpenGL state
@ -593,13 +577,16 @@ static void gldriver_init_common(void) {
glEnable(GL_CULL_FACE);
// Always use this clear color
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Draw our scene once without presenting the rendered image.
// This is done in order to pre-warm OpenGL
// We don't need to present the buffer since we don't actually want the
// user to see this, we're only drawing as a pre-warm stage
video_driver_render();
gldriver_render();
// Check for errors to make sure all of our setup went ok
GL_ERRLOG("finished initialization");
@ -614,15 +601,19 @@ static void gldriver_init_common(void) {
}
static void gldriver_shutdown(void) {
#if USE_GLUT
glutDestroyWindow(glutWindow);
#endif
// Cleanup all OpenGL objects
glDeleteTextures(1, &a2TextureName);
a2TextureName = 0;
a2TextureName = UNINITIALIZED_GL;
_destroy_VAO(crtVAOName);
crtVAOName = 0;
crtVAOName = UNINITIALIZED_GL;
mdlDestroyModel(crtModel);
crtModel = NULL;
glDeleteProgram(program);
program = 0;
program = UNINITIALIZED_GL;
gldriver_animation_destroy();
}
//----------------------------------------------------------------------------
@ -705,7 +696,9 @@ static void gldriver_render(void) {
}
}
glActiveTexture(TEXTURE_ACTIVE_FRAMEBUFFER);
glBindTexture(GL_TEXTURE_2D, a2TextureName);
glUniform1i(uniformTex2Use, TEXTURE_ID_FRAMEBUFFER);
if (_vid_dirty) {
glTexImage2D(GL_TEXTURE_2D, /*level*/0, /*internal format*/GL_RGBA, SCANWIDTH, SCANHEIGHT, /*border*/0, /*format*/GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)&pixels[0]);
}
@ -728,10 +721,10 @@ static void gldriver_render(void) {
crtModel->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)?
0); // What is the offset in the VBO to the position data?
glEnableVertexAttribArray(POS_ATTRIB_IDX);
glBindBuffer(GL_ARRAY_BUFFER, texcoordBufferName);
// Set up parmeters for texcoord attribute in the VAO including, size, type, stride, and offset in the currenly
// bound VAO This also attaches the texcoord VBO to VAO
glBindBuffer(GL_ARRAY_BUFFER, texcoordBufferName);
glVertexAttribPointer(TEXCOORD_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
crtModel->texcoordSize, // How many elements are there per texture coord?
crtModel->texcoordType, // What is the type of this data in the array?
@ -739,21 +732,29 @@ static void gldriver_render(void) {
crtModel->texcoordSize*texcoordTypeSize, // What is the stride (i.e. bytes between texcoords)?
0); // What is the offset in the VBO to the texcoord data?
glEnableVertexAttribArray(TEXCOORD_ATTRIB_IDX);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferName);
#endif
glUniform1f(alphaValue, 1.0);
// Cull back faces now that we no longer render
// with an inverted matrix
glCullFace(GL_BACK);
//glCullFace(GL_BACK);
// Draw the CRT object
// Draw the CRT object and others
glDrawElements(GL_TRIANGLES, crtNumElements, crtElementType, 0);
// Prep any other objects/animations
gldriver_animation_render();
_vid_dirty = false;
#if USE_GLUT
glutSwapBuffers();
#endif
GL_ERRLOG("gldriver_render");
}
static void gldriver_reshape(int w, int h) {
@ -801,7 +802,6 @@ static void gldriver_reshape(int w, int h) {
}
#if USE_GLUT
static int glutWindow = -1;
static void gldriver_init_glut(GLuint fbo) {
glutInit(&argc, argv);
glutInitDisplayMode(/*GLUT_DOUBLE|*/GLUT_RGBA|GLUT_DEPTH);
@ -833,9 +833,9 @@ static void gldriver_init_glut(GLuint fbo) {
#endif
//----------------------------------------------------------------------------
// renderer API
// backend renderer API
void video_driver_init(void *fbo) {
static void gldriver_init(void *fbo) {
safe_to_do_opengl_logging = true;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
@ -850,26 +850,26 @@ void video_driver_init(void *fbo) {
#else
#error no working codepaths
#endif
gldriver_animation_init();
}
void video_driver_main_loop(void) {
static void gldriver_main_loop(void) {
#if USE_GLUT
glutMainLoop();
#endif
// fall through if not GLUT
}
void video_driver_render(void) {
gldriver_render();
}
void video_driver_reshape(int w, int h) {
gldriver_reshape(w, h);
}
void video_driver_shutdown(void) {
#if USE_GLUT
glutDestroyWindow(glutWindow);
#endif
gldriver_shutdown();
__attribute__((constructor))
static void _init_glvideo(void) {
LOG("Initializing OpenGL renderer");
glvideo_backend.init = &gldriver_init;
glvideo_backend.main_loop = &gldriver_main_loop;
glvideo_backend.reshape = &gldriver_reshape;
glvideo_backend.render = &gldriver_render;
glvideo_backend.shutdown = &gldriver_shutdown;
video_backend = &glvideo_backend;
}

78
src/video/glvideo.h Normal file
View File

@ -0,0 +1,78 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#ifndef _GLVIDEO_H_
#define _GLVIDEO_H_
#define UNINITIALIZED_GL 31337
#ifdef __APPLE__
#import <CoreFoundation/CoreFoundation.h>
#define USE_VAO 1
#endif
// TODO: implement 3D CRT object, possibly with perspective drawing?
#define PERSPECTIVE 0
// VAO optimization (may not be available on all platforms)
#ifdef ANDROID
#include "glanimation.h"
#warning Certain Android and Android-ish devices (*cough* Kindle *cough*) have buggy OpenGL VAO support ...
#define USE_VAO 0
#elif !defined(USE_VAO)
#define USE_VAO 1
#endif
#include "video_util/modelUtil.h"
#include "video_util/matrixUtil.h"
#include "video_util/sourceUtil.h"
enum {
POS_ATTRIB_IDX,
TEXCOORD_ATTRIB_IDX,
NORMAL_ATTRIB_IDX,
};
enum {
TEXTURE_ID_FRAMEBUFFER=0,
TEXTURE_ID_MESSAGE,
};
enum {
TEXTURE_ACTIVE_FRAMEBUFFER = GL_TEXTURE0,
TEXTURE_ACTIVE_MESSAGE = GL_TEXTURE1,
};
static inline GLsizei _get_gl_type_size(GLenum type) {
switch (type) {
case GL_BYTE:
return sizeof(GLbyte);
case GL_UNSIGNED_BYTE:
return sizeof(GLubyte);
case GL_SHORT:
return sizeof(GLshort);
case GL_UNSIGNED_SHORT:
return sizeof(GLushort);
case GL_INT:
return sizeof(GLint);
case GL_UNSIGNED_INT:
return sizeof(GLuint);
case GL_FLOAT:
return sizeof(GLfloat);
}
return 0;
}
extern GLint uniformTex2Use;
extern GLint alphaValue;
#endif

View File

@ -1,18 +0,0 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
// glvideo -- Created by Aaron Culliney
void video_driver_init(void *context);
void video_driver_main_loop(void);
void video_driver_reshape(int width, int height);
void video_driver_render(void);
void video_driver_shutdown(void);

View File

@ -17,37 +17,53 @@
#ifndef A2_VIDEO_H
#define A2_VIDEO_H
#ifndef __ASSEMBLER__
typedef struct video_backend_s {
void (*init)(void *context);
void (*main_loop)(void);
void (*reshape)(int width, int height);
void (*render)(void);
void (*shutdown)(void);
} video_backend_s;
typedef struct A2Color {
/*
* Color structure
*/
typedef struct A2Color_s {
uint8_t red;
uint8_t green;
uint8_t blue;
} A2Color;
} A2Color_s;
/*
* Reference to the internal 8bit-indexed color format
*/
extern A2Color colormap[];
extern A2Color_s colormap[];
#include "prefs.h"
/*
* The registered video backend (renderer).
*/
extern video_backend_s *video_backend;
/* Prepare the video system, converting console to graphics mode, or
/*
* 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
*/
void video_init(void);
/* Begin main video loop (does not return)
/*
* Begin main video loop (does not return)
*/
void video_main_loop(void);
/* Undo anything done by video_init() and video_start(). Called before exiting the
/*
* Undo anything done by video_init() and video_start(). Called before exiting the
* emulator.
*/
void video_shutdown(void);
/* Setup the display. This may be called multiple times in a run, and is
/*
* Setup the display. This may be called multiple times in a run, and is
* used when graphics parameters (II+ vs //e, hires color representation) may
* have changed.
*
@ -62,7 +78,8 @@ void video_shutdown(void);
*/
void video_set(int flags);
/* Set the font used by the display. QTY characters are loaded starting
/*
* Set the font used by the display. QTY characters are loaded starting
* with FIRST, from DATA. DATA contains 8 bytes for each character, each
* byte representing a row (top-to-bottom). The row byte contains 7
* pixels in little-endian format.
@ -80,37 +97,25 @@ void video_set(int flags);
*/
void video_loadfont(int first, int qty, const uint8_t *data, int mode);
/* Redraw the display. This is called after exiting an interface display,
/*
* Loads the interface/messages to a seperate character table for system menus/messages
*/
void video_load_interface_fonts(void);
/*
* Redraw the display. This is called after exiting an interface display,
* when changes have been made to the Apple's emulated framebuffer that
* bypass the driver's hooks, or when the video mode has changed.
*/
void video_redraw(void);
/* Change the displayed video page to PAGE
/*
* Change the displayed video page to PAGE
* 0 - Page 1: $400-$7ff/$2000-$3fff
* 1 - Page 2: $800-$bff/$4000-$5fff
*/
void video_setpage(int page);
#ifdef INTERFACE_CLASSIC
/* Like loadfont, but writes to a seperate 256 character table used by
* video_plotchar and not the apple text-mode display.
*/
void video_loadfont_int(int first, int qty, const uint8_t *data);
/* Plot a character to the text mode screen, *not* writing to apple
* memory. This is used by the interface screens.
*
* ROW, COL, and CODE are self-expanatory. COLOR gives the color scheme
* to use:
*
* 0 - Green text on Black background
* 1 - Green text on Blue background
* 2 - Red text on Black background
*/
void video_plotchar(int row, int col, int color, uint8_t code);
#endif
/*
* Get a reference to current internal framebuffer
*/
@ -121,6 +126,49 @@ const uint8_t * const video_current_framebuffer();
*/
bool video_dirty(void);
// ----------------------------------------------------------------------------
/*
* Plot a character to the text mode screen, *not* writing to apple
* memory. This is used by the interface screens.
*
* ROW, COL, and CODE are self-expanatory. COLOR gives the color scheme
* to use:
*
* 0 - Green text on Black background
* 1 - Green text on Blue background
* 2 - Red text on Black background
*/
void video_plotchar(int row, int col, int color, uint8_t code);
/*
* Same as video_plotchar(), but allows plotting to a separate buffer
*/
void video_plotchar_fb(uint8_t *fb, int fb_width, int row, int col, int color, uint8_t code);
void video_interface_print(int x, int y, const int cs, const char *s);
void video_interface_print_fb(uint8_t *fb, int fb_width, int x, int y, const int cs, const char *s);
void video_interface_print_submenu_centered(char *submenu, const int xlen, const int ylen);
void video_interface_print_submenu_centered_fb(uint8_t *fb, int screen_char_width, int screen_char_height, char *submenu, const int xlen, const int ylen);
// ----------------------------------------------------------------------------
/*
* Show CPU speed animation
*/
extern void (*video_animation_show_cpuspeed)(void);
/*
* Show track/sector animation
*/
extern void (*video_animation_show_track_sector)(int drive, int track, int sect);
// ----------------------------------------------------------------------------
/*
* VBL routines
*/
@ -128,18 +176,30 @@ uint16_t video_scanner_get_address(bool *vblBarOut);
uint8_t floating_bus(void);
uint8_t floating_bus_hibit(const bool hibit);
#endif /* !__ASSEMBLER__ */
/**** Private stuff follows *****/
/* 640x400 mode really isn't what it advertises. It's really 560x384 with 4
/*
* 640x400 mode really isn't what it advertises. It's really 560x384 with 4
* extra bytes on each side for color interpolation hack. This is yet another
* area where I've traded the optimization gain (especially on older slower
* machines) for a standard resolution.
*/
#define SCANWIDTH 568
#define _SCANWIDTH 560
#define INTERPOLATED_PIXEL_ADJUSTMENT (4+4)
#define SCANWIDTH (_SCANWIDTH+INTERPOLATED_PIXEL_ADJUSTMENT)
#define SCANHEIGHT 384
#define SCANSTEP SCANWIDTH-12
#define TEXT_ROWS 24
#define BEGIN_MIX 20
#define TEXT_COLS 40
#define TEXT80_COLS 80
#define INTERFACE_SCREEN_X TEXT80_COLS
#define FONT_HEIGHT_PIXELS 16
#define FONT_WIDTH_PIXELS 14
#define FONT80_WIDTH_PIXELS 7
#define MOUSETEXT_BEGIN 0x90
#define COLOR_BLACK 0
@ -177,14 +237,13 @@ uint8_t floating_bus_hibit(const bool hibit);
#define COLOR_FLASHING_GREEN 59
#ifndef __ASSEMBLER__
/* ----------------------------------
generic graphics globals
---------------------------------- */
/* Pointers to framebuffer (can be VGA memory or host buffer)
/*
* Pointers to framebuffer (can be VGA memory or host buffer)
*/
extern uint8_t *video__fb1,*video__fb2;
extern uint8_t video__hires_even[0x800];
@ -219,6 +278,5 @@ video__write_2e_even1(),
video__write_2e_odd1_mixed(),
video__write_2e_even1_mixed();
#endif /* !__ASSEMBLER__ */
#endif /* !A2_VIDEO_H */