|
|
|
@ -16,22 +16,12 @@
|
|
|
|
|
#include "common.h"
|
|
|
|
|
#include "video/video.h"
|
|
|
|
|
|
|
|
|
|
#if __clang__
|
|
|
|
|
# pragma clang diagnostic push
|
|
|
|
|
# pragma clang diagnostic ignored "-Wunused-variable"
|
|
|
|
|
#elif __GNUC__
|
|
|
|
|
# pragma GCC diagnostic push
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wunused-variable"
|
|
|
|
|
# pragma GCC diagnostic ignored "-Wunused-const-variable"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define SCANSTEP (SCANWIDTH-12)
|
|
|
|
|
#define SCANDSTEP (SCANWIDTH-6)
|
|
|
|
|
|
|
|
|
|
#define DYNAMIC_SZ 11 // 7 pixels (as bytes) + 2pre + 2post
|
|
|
|
|
|
|
|
|
|
A2Color_s colormap[256] = { { 0 } };
|
|
|
|
|
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 };
|
|
|
|
@ -46,8 +36,17 @@ static uint8_t video__columns[8192] = { 0 };
|
|
|
|
|
static uint8_t video__hires_even[0x800] = { 0 };
|
|
|
|
|
static uint8_t video__hires_odd[0x800] = { 0 };
|
|
|
|
|
|
|
|
|
|
static volatile unsigned long _vid_dirty = 0;
|
|
|
|
|
#define FB_SIZ (SCANWIDTH*SCANHEIGHT*sizeof(uint8_t))
|
|
|
|
|
|
|
|
|
|
#if INTERFACE_CLASSIC
|
|
|
|
|
static uint8_t fbInterface[FB_SIZ] = { 0 };
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static uint8_t fbStaging[FB_SIZ] = { 0 };
|
|
|
|
|
#define FB_BASE (&fbStaging[0])
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
// FIXME TODO REMOVE THESE ...
|
|
|
|
|
// Video constants -- sourced from AppleWin
|
|
|
|
|
static const bool bVideoScannerNTSC = true;
|
|
|
|
|
static const int kHBurstClock = 53; // clock when Color Burst starts
|
|
|
|
@ -65,20 +64,18 @@ static const int kPALVSyncLine = 264; // line when VSync starts (PAL)
|
|
|
|
|
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
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
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 };
|
|
|
|
|
|
|
|
|
|
// TODO FIXME : support linked list of callbacks here ...
|
|
|
|
|
static display_update_fn textCallbackFn = NULL;
|
|
|
|
|
static display_update_fn hiresCallbackFn = NULL;
|
|
|
|
|
static display_update_fn modeCallbackFn = NULL;
|
|
|
|
|
|
|
|
|
|
// 40col/80col/lores/hires/dhires line offsets
|
|
|
|
|
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
|
|
|
|
|
// video line offsets
|
|
|
|
|
static uint16_t video__line_offset[TEXT_ROWS + /*VBL:*/ 8 + /*extra:*/1] = {
|
|
|
|
|
0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380,
|
|
|
|
|
0x028, 0x0A8, 0x128, 0x1A8, 0x228, 0x2A8, 0x328, 0x3A8,
|
|
|
|
|
0x050, 0x0D0, 0x150, 0x1D0, 0x250, 0x2D0, 0x350, 0x3D0,
|
|
|
|
|
0x078, 0x0F8, 0x178, 0x1F8, 0x278, 0x2F8, 0x378, 0x3F8,
|
|
|
|
|
0x3F8
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static uint8_t video__dhires1[256] = {
|
|
|
|
@ -332,6 +329,7 @@ static void _initialize_row_col_tables(void) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#warning FIXME TODO : move _initialize_tables_video() to vm.c ...
|
|
|
|
|
static void _initialize_tables_video(void) {
|
|
|
|
|
// initialize text/lores & hires graphics routines
|
|
|
|
|
for (unsigned int y = 0; y < TEXT_ROWS; y++) {
|
|
|
|
@ -449,7 +447,7 @@ static void _initialize_color() {
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void video_prefsChanged(const char *domain) {
|
|
|
|
|
static void display_prefsChanged(const char *domain) {
|
|
|
|
|
long val = COLOR_INTERP;
|
|
|
|
|
prefs_parseLongValue(domain, PREF_COLOR_MODE, &val, /*base:*/10);
|
|
|
|
|
if (val < 0) {
|
|
|
|
@ -465,7 +463,6 @@ static void video_prefsChanged(const char *domain) {
|
|
|
|
|
void display_reset(void) {
|
|
|
|
|
_initialize_hires_values();
|
|
|
|
|
_initialize_tables_video();
|
|
|
|
|
video_setDirty(A2_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void display_loadFont(const uint8_t first, const uint8_t quantity, const uint8_t *data, font_mode_t mode) {
|
|
|
|
@ -605,6 +602,7 @@ static inline void _plot_lores80(uint8_t **d, const uint32_t val) {
|
|
|
|
|
*((uint8_t *)(*d)) = (uint8_t)val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static inline void __plot_character40(const unsigned int font_off, uint8_t *fb_ptr) {
|
|
|
|
|
uint8_t *font_ptr = video__wider_font+font_off;
|
|
|
|
|
_plot_char40(/*dst*/&fb_ptr, /*src*/&font_ptr);
|
|
|
|
@ -628,7 +626,32 @@ static void _plot_character40(uint8_t col, uint8_t row, int page, int bank, uint
|
|
|
|
|
textCallbackFn((pixel_delta_t){ .row = row, .col = col, .b = b, .cs = INVALID_COLORSCHEME });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void _plot_char40_scanline(scan_data_t *scandata) {
|
|
|
|
|
uint8_t *scanline = scandata->scanline;
|
|
|
|
|
unsigned int scanrow = scandata->scanrow;
|
|
|
|
|
unsigned int scancol = scandata->scancol;
|
|
|
|
|
unsigned int scanend = scandata->scanend;
|
|
|
|
|
|
|
|
|
|
uint16_t fb_base = video__line_offset[scanrow>>3];
|
|
|
|
|
uint16_t glyph_off = (scanrow&0x7);
|
|
|
|
|
|
|
|
|
|
for (unsigned int col = scancol; col < scanend; col++)
|
|
|
|
|
{
|
|
|
|
|
uint16_t fb_off = fb_base + col;
|
|
|
|
|
uint8_t b = scanline[(col<<1)+1]; // MBD data only
|
|
|
|
|
|
|
|
|
|
unsigned int glyph_base = (b<<7); // *128
|
|
|
|
|
|
|
|
|
|
uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + ((glyph_off<<1) * SCANWIDTH);
|
|
|
|
|
uint8_t *font_ptr = video__wider_font + glyph_base + (glyph_off<<4);
|
|
|
|
|
|
|
|
|
|
_plot_char40(/*dst:*/&fb_ptr, /*src:*/&font_ptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static inline void __plot_character80(const unsigned int font_off, uint8_t *fb_ptr) {
|
|
|
|
|
uint8_t *font_ptr = video__font+font_off;
|
|
|
|
|
_plot_char80(/*dst*/&fb_ptr, /*src*/&font_ptr, SCANWIDTH);
|
|
|
|
@ -661,7 +684,33 @@ static void _plot_character80(uint8_t col, uint8_t row, int page, int bank, uint
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void _plot_char80_scanline(scan_data_t *scandata) {
|
|
|
|
|
uint8_t *scanline = scandata->scanline;
|
|
|
|
|
unsigned int scanrow = scandata->scanrow;
|
|
|
|
|
unsigned int scancol = scandata->scancol;
|
|
|
|
|
unsigned int scanend = scandata->scanend;
|
|
|
|
|
|
|
|
|
|
uint16_t fb_base = video__line_offset[scanrow>>3];
|
|
|
|
|
uint16_t glyph_off = (scanrow&0x7);
|
|
|
|
|
|
|
|
|
|
for (unsigned int col = scancol; col < scanend; col++)
|
|
|
|
|
{
|
|
|
|
|
uint16_t fb_off = fb_base + col;
|
|
|
|
|
for (unsigned int x=0; x<2; x++) {
|
|
|
|
|
uint8_t b = scanline[(col<<1)+x]; // AUX, MBD
|
|
|
|
|
unsigned int glyph_base = (b<<6); // *64
|
|
|
|
|
|
|
|
|
|
uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + (7*x) + ((glyph_off<<1) * SCANWIDTH);
|
|
|
|
|
uint8_t *font_ptr = video__font + glyph_base + (glyph_off<<3);
|
|
|
|
|
|
|
|
|
|
_plot_char80(/*dst:*/&fb_ptr, /*src:*/&font_ptr, SCANWIDTH);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static inline void __plot_block40(const uint8_t val, uint8_t col, uint8_t *fb_ptr) {
|
|
|
|
|
|
|
|
|
|
uint8_t color = (val & 0x0F) << 4;
|
|
|
|
@ -724,7 +773,48 @@ static void _plot_block40(uint8_t col, uint8_t row, int page, int bank, uint8_t
|
|
|
|
|
textCallbackFn((pixel_delta_t){ .row = row, .col = col, .b = b, .cs = COLOR16 });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void _plot_lores40_scanline(scan_data_t *scandata) {
|
|
|
|
|
uint8_t *scanline = scandata->scanline;
|
|
|
|
|
unsigned int scanrow = scandata->scanrow;
|
|
|
|
|
unsigned int scancol = scandata->scancol;
|
|
|
|
|
unsigned int scanend = scandata->scanend;
|
|
|
|
|
|
|
|
|
|
uint16_t fb_base = video__line_offset[scanrow>>3];
|
|
|
|
|
uint16_t block_off = (scanrow&0x7);
|
|
|
|
|
|
|
|
|
|
uint8_t hi_nyb = !!(block_off>>2); // 0,1,2,3 => 0 4,5,6,7 => 1
|
|
|
|
|
uint8_t lores_mask = (0x0f << (hi_nyb<<2) ); // 0x0f --or-- 0xf0
|
|
|
|
|
uint8_t lores_shift = ((1-hi_nyb)<<2); // -> 0xi0
|
|
|
|
|
|
|
|
|
|
for (unsigned int col = scancol; col < scanend; col++)
|
|
|
|
|
{
|
|
|
|
|
uint16_t fb_off = fb_base + col;
|
|
|
|
|
uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + ((block_off<<1) * SCANWIDTH);
|
|
|
|
|
|
|
|
|
|
uint8_t b = scanline[(col<<1)+1]; // MBD data only
|
|
|
|
|
uint8_t val = (b & lores_mask) << lores_shift;
|
|
|
|
|
|
|
|
|
|
uint32_t val32;
|
|
|
|
|
if (color_mode == COLOR_NONE) {
|
|
|
|
|
if (val != 0x0 && val != 0xf) {
|
|
|
|
|
uint8_t rot2 = ((col % 2) << 1); // 2 phases at double rotation
|
|
|
|
|
val = (val << rot2) | ((val & 0xC0) >> rot2);
|
|
|
|
|
}
|
|
|
|
|
val32 = ((val & 0x10) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 0;
|
|
|
|
|
val32 |= ((val & 0x20) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 8;
|
|
|
|
|
val32 |= ((val & 0x40) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 16;
|
|
|
|
|
val32 |= ((val & 0x80) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 24;
|
|
|
|
|
} else {
|
|
|
|
|
val32 = (val << 24) | (val << 16) | (val << 8) | val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_plot_lores40(/*dst:*/&fb_ptr, val32);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static inline void __plot_block80(const uint8_t val, uint8_t *fb_ptr) {
|
|
|
|
|
uint8_t color = (val & 0x0F) << 4;
|
|
|
|
|
uint32_t val32 = (color << 24) | (color << 16) | (color << 8) | color;
|
|
|
|
@ -749,7 +839,9 @@ static inline void __plot_block80(const uint8_t val, uint8_t *fb_ptr) {
|
|
|
|
|
fb_ptr += SCANDSTEP;
|
|
|
|
|
_plot_lores80(/*dst*/&fb_ptr, val32);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// HACK FIXME TODO ... is this correct? MONOCOLOR output appears wrong...
|
|
|
|
|
static inline uint8_t __shift_block80(uint8_t b) {
|
|
|
|
|
// plot even half-block from auxmem, rotate nybbles to match color (according to UTAIIe: 8-29)
|
|
|
|
|
uint8_t b0 = (b & 0x0F);
|
|
|
|
@ -762,6 +854,7 @@ static inline uint8_t __shift_block80(uint8_t b) {
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static void _plot_block80(uint8_t col, uint8_t row, int page, int bank, uint8_t *fb_ptr) {
|
|
|
|
|
assert(bank == 0); // FIXME TODO ...
|
|
|
|
|
uint16_t base = page ? 0x0800 : 0x0400;
|
|
|
|
@ -791,16 +884,58 @@ static void _plot_block80(uint8_t col, uint8_t row, int page, int bank, uint8_t
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(uint8_t, uint8_t, int, int, uint8_t*) {
|
|
|
|
|
void (*plotFn)(uint8_t, uint8_t, int, int, uint8_t*) = NULL;
|
|
|
|
|
static void _plot_lores80_scanline(scan_data_t *scandata) {
|
|
|
|
|
uint8_t *scanline = scandata->scanline;
|
|
|
|
|
unsigned int scanrow = scandata->scanrow;
|
|
|
|
|
unsigned int scancol = scandata->scancol;
|
|
|
|
|
unsigned int scanend = scandata->scanend;
|
|
|
|
|
|
|
|
|
|
uint16_t fb_base = video__line_offset[scanrow>>3];
|
|
|
|
|
uint16_t block_off = (scanrow&0x7);
|
|
|
|
|
|
|
|
|
|
uint8_t hi_nyb = !!(block_off>>2); // 0,1,2,3 => 0 4,5,6,7 => 1
|
|
|
|
|
uint8_t lores_mask = (0x0f << (hi_nyb<<2) ); // 0x0f --or-- 0xf0
|
|
|
|
|
uint8_t lores_shift = ((1-hi_nyb)<<2); // -> 0xi0
|
|
|
|
|
|
|
|
|
|
for (unsigned int col = scancol; col < scanend; col++)
|
|
|
|
|
{
|
|
|
|
|
uint16_t fb_off = fb_base + col;
|
|
|
|
|
{
|
|
|
|
|
uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + ((block_off<<1) * SCANWIDTH);
|
|
|
|
|
|
|
|
|
|
uint8_t b = scanline[(col<<1)+0]; // AUX
|
|
|
|
|
b = __shift_block80(b);
|
|
|
|
|
uint8_t val = (b & lores_mask) << lores_shift;
|
|
|
|
|
// TODO FIXME : implement monocolor ...
|
|
|
|
|
uint32_t val32 = (val << 24) | (val << 16) | (val << 8) | val;
|
|
|
|
|
|
|
|
|
|
_plot_lores80(/*dst:*/&fb_ptr, val32);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off] + 7 + ((block_off<<1) * SCANWIDTH);
|
|
|
|
|
|
|
|
|
|
uint8_t b = scanline[(col<<1)+1]; // MBD
|
|
|
|
|
uint8_t val = (b & lores_mask) << lores_shift;
|
|
|
|
|
// TODO FIXME : implement monocolor ...
|
|
|
|
|
uint32_t val32 = (val << 24) | (val << 16) | (val << 8) | val;
|
|
|
|
|
|
|
|
|
|
_plot_lores80(/*dst:*/&fb_ptr, val32);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(scan_data_t*) {
|
|
|
|
|
void (*plotFn)(scan_data_t*) = NULL;
|
|
|
|
|
|
|
|
|
|
if (currswitches & txtflags) {
|
|
|
|
|
plotFn = (currswitches & SS_80COL) ? _plot_character80 : _plot_character40;
|
|
|
|
|
plotFn = (currswitches & SS_80COL) ? _plot_char80_scanline : _plot_char40_scanline;
|
|
|
|
|
} else {
|
|
|
|
|
assert(!(currswitches & SS_HIRES) && "must be lores graphics or programmer error");
|
|
|
|
|
if (!(currswitches & SS_80COL)) {
|
|
|
|
|
plotFn = _plot_block40;
|
|
|
|
|
plotFn = _plot_lores40_scanline;
|
|
|
|
|
if (!(currswitches & SS_DHIRES)) {
|
|
|
|
|
// LORES40 ...
|
|
|
|
|
} else {
|
|
|
|
@ -809,11 +944,11 @@ static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(uint8
|
|
|
|
|
} else {
|
|
|
|
|
if (currswitches & SS_DHIRES) {
|
|
|
|
|
// LORES80 ...
|
|
|
|
|
plotFn = _plot_block80;
|
|
|
|
|
plotFn = _plot_lores80_scanline;
|
|
|
|
|
} else {
|
|
|
|
|
/* ??? */
|
|
|
|
|
LOG("!!!!!!!!!!!! what mode is this? !!!!!!!!!!!!");
|
|
|
|
|
plotFn = _plot_block40;
|
|
|
|
|
//LOG("!!!!!!!!!!!! what mode is this? !!!!!!!!!!!!");
|
|
|
|
|
plotFn = _plot_lores40_scanline;
|
|
|
|
|
#warning FIXME TODO ... verify this lores40/lores80 mode ...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -822,82 +957,6 @@ static void (*_textpage_plotter(uint32_t currswitches, uint32_t txtflags))(uint8
|
|
|
|
|
return plotFn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawpage_mode_t display_currentDrawpageMode(uint32_t currswitches) {
|
|
|
|
|
if (currswitches & SS_TEXT) {
|
|
|
|
|
return DRAWPAGE_TEXT;
|
|
|
|
|
} else {
|
|
|
|
|
if (currswitches & SS_HIRES) {
|
|
|
|
|
return DRAWPAGE_HIRES;
|
|
|
|
|
} else {
|
|
|
|
|
return DRAWPAGE_TEXT; // (LORES)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline drawpage_mode_t _currentMainMode(uint32_t currswitches) {
|
|
|
|
|
return display_currentDrawpageMode(currswitches);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline drawpage_mode_t _currentMixedMode(uint32_t currswitches) {
|
|
|
|
|
if (currswitches & (SS_TEXT|SS_MIXED)) {
|
|
|
|
|
return DRAWPAGE_TEXT;
|
|
|
|
|
} else {
|
|
|
|
|
if (currswitches & SS_HIRES) {
|
|
|
|
|
return DRAWPAGE_HIRES;
|
|
|
|
|
} else {
|
|
|
|
|
return DRAWPAGE_TEXT; // (LORES)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GLUE_C_WRITE(video__write_2e_text0)
|
|
|
|
|
{
|
|
|
|
|
run_args.base_textwrt[ea] = b;
|
|
|
|
|
drawpage_mode_t mode = _currentMainMode(run_args.softswitches);
|
|
|
|
|
if (mode == DRAWPAGE_HIRES) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!(run_args.softswitches & SS_PAGE2)) {
|
|
|
|
|
video_setDirty(A2_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GLUE_C_WRITE(video__write_2e_text0_mixed)
|
|
|
|
|
{
|
|
|
|
|
run_args.base_textwrt[ea] = b;
|
|
|
|
|
drawpage_mode_t mode = _currentMixedMode(run_args.softswitches);
|
|
|
|
|
if (mode == DRAWPAGE_HIRES) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!(run_args.softswitches & SS_PAGE2)) {
|
|
|
|
|
video_setDirty(A2_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GLUE_C_WRITE(video__write_2e_text1)
|
|
|
|
|
{
|
|
|
|
|
run_args.base_ramwrt[ea] = b;
|
|
|
|
|
drawpage_mode_t mode = _currentMainMode(run_args.softswitches);
|
|
|
|
|
if (mode == DRAWPAGE_HIRES) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (run_args.softswitches & SS_PAGE2) {
|
|
|
|
|
video_setDirty(A2_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GLUE_C_WRITE(video__write_2e_text1_mixed)
|
|
|
|
|
{
|
|
|
|
|
run_args.base_ramwrt[ea] = b;
|
|
|
|
|
drawpage_mode_t mode = _currentMixedMode(run_args.softswitches);
|
|
|
|
|
if (mode == DRAWPAGE_HIRES) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (run_args.softswitches & SS_PAGE2) {
|
|
|
|
|
video_setDirty(A2_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Classic interface and printing HUD messages
|
|
|
|
|
|
|
|
|
@ -913,40 +972,30 @@ static void _display_plotChar(uint8_t *fboff, const unsigned int fbPixWidth, con
|
|
|
|
|
_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) {
|
|
|
|
|
#if INTERFACE_CLASSIC
|
|
|
|
|
void display_plotChar(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);
|
|
|
|
|
}
|
|
|
|
|
unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _INTERPOLATED_PIXEL_ADJUSTMENT_PRE;
|
|
|
|
|
_display_plotChar(fbInterface+off, SCANWIDTH, cs, c);
|
|
|
|
|
video_setDirty(FB_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
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 (textCallbackFn) {
|
|
|
|
|
textCallbackFn((pixel_delta_t){ .row = row, .col = x, .b = c, .cs = cs });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fb) {
|
|
|
|
|
unsigned int off = row * fbPixWidth * FONT_HEIGHT_PIXELS + x * FONT80_WIDTH_PIXELS + xAdjust;
|
|
|
|
|
_display_plotChar(fb+off, fbPixWidth, cs, c);
|
|
|
|
|
}
|
|
|
|
|
unsigned int off = row * fbPixWidth * FONT_HEIGHT_PIXELS + x * FONT80_WIDTH_PIXELS + xAdjust;
|
|
|
|
|
_display_plotChar(fb+off, fbPixWidth, cs, c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
#if INTERFACE_CLASSIC
|
|
|
|
|
void display_plotLine(const uint8_t col, const uint8_t row, const interface_colorscheme_t cs, const char *message) {
|
|
|
|
|
_display_plotLine(fbInterface, /*fbPixWidth:*/SCANWIDTH, /*xAdjust:*/_INTERPOLATED_PIXEL_ADJUSTMENT_PRE, col, row, cs, message);
|
|
|
|
|
video_setDirty(FB_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
@ -962,24 +1011,29 @@ void display_plotMessage(uint8_t *fb, const interface_colorscheme_t cs, const ch
|
|
|
|
|
// Double-HIRES (HIRES80) graphics
|
|
|
|
|
|
|
|
|
|
static inline void __plot_hires80_pixels(uint8_t idx, uint8_t *fb_ptr) {
|
|
|
|
|
if (color_mode == COLOR_NONE) {
|
|
|
|
|
uint32_t b;
|
|
|
|
|
b = (idx & 0x1) ? COLOR_LIGHT_WHITE : COLOR_BLACK;
|
|
|
|
|
b |= ((idx & 0x2) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 8;
|
|
|
|
|
b |= ((idx & 0x4) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 16;
|
|
|
|
|
b |= ((idx & 0x8) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 24;
|
|
|
|
|
*((uint32_t *)fb_ptr) = b;
|
|
|
|
|
*((uint32_t *)(fb_ptr+SCANWIDTH)) = b;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uint8_t bCurr = idx;
|
|
|
|
|
|
|
|
|
|
uint8_t b1 = video__dhires1[idx];
|
|
|
|
|
uint8_t b2 = video__dhires2[idx];
|
|
|
|
|
uint32_t b = (b2<<24) | (b1<<8);
|
|
|
|
|
*((uint32_t *)fb_ptr) = b;
|
|
|
|
|
*((uint32_t *)(fb_ptr+SCANWIDTH)) = b;
|
|
|
|
|
if (color_mode == COLOR_NONE) {
|
|
|
|
|
uint32_t b32;
|
|
|
|
|
b32 = (bCurr & 0x1) ? COLOR_LIGHT_WHITE : COLOR_BLACK;
|
|
|
|
|
b32 |= ((bCurr & 0x2) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 8;
|
|
|
|
|
b32 |= ((bCurr & 0x4) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 16;
|
|
|
|
|
b32 |= ((bCurr & 0x8) ? COLOR_LIGHT_WHITE : COLOR_BLACK) << 24;
|
|
|
|
|
*((uint32_t *)fb_ptr) = b32;
|
|
|
|
|
*((uint32_t *)(fb_ptr+SCANWIDTH)) = b32;
|
|
|
|
|
} else {
|
|
|
|
|
// TODO FIXME : handle interpolated here ...
|
|
|
|
|
uint32_t b32;
|
|
|
|
|
b32 = (bCurr & 0x1) ? bCurr : COLOR_BLACK;
|
|
|
|
|
b32 |= ((bCurr & 0x2) ? bCurr : COLOR_BLACK) << 8;
|
|
|
|
|
b32 |= ((bCurr & 0x4) ? bCurr : COLOR_BLACK) << 16;
|
|
|
|
|
b32 |= ((bCurr & 0x8) ? bCurr : COLOR_BLACK) << 24;
|
|
|
|
|
*((uint32_t *)fb_ptr) = b32;
|
|
|
|
|
*((uint32_t *)(fb_ptr+SCANWIDTH)) = b32;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static inline void __plot_hires80(uint16_t base, uint16_t ea, uint8_t *fb_ptr) {
|
|
|
|
|
ea &= ~0x1;
|
|
|
|
|
|
|
|
|
@ -1055,11 +1109,87 @@ static void _plot_hires80(uint16_t off, int page, int bank, bool is_even, uint8_
|
|
|
|
|
uint16_t ea = base+off;
|
|
|
|
|
__plot_hires80(base, ea, fb_ptr);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void _plot_hires80_scanline(scan_data_t *scandata) {
|
|
|
|
|
uint8_t *scanline = scandata->scanline;
|
|
|
|
|
unsigned int scanrow = scandata->scanrow;
|
|
|
|
|
unsigned int scancol = scandata->scancol;
|
|
|
|
|
unsigned int scanend = scandata->scanend;
|
|
|
|
|
|
|
|
|
|
uint16_t fb_base = video__line_offset[scanrow>>3] + (0x400 * (scanrow & 0x7));
|
|
|
|
|
|
|
|
|
|
// |AUX 0a|MBD 0b |AUX 1c |MBD 1d |AUX 2e |MBD 2f |AUX 3g |MBD 3h ...
|
|
|
|
|
// aaaaaaab bbbbbbcc cccccddd ddddeeee eeefffff ffgggggg ghhhhhhh ...
|
|
|
|
|
// 01234560 12345601 23456012 34560123 45601234 56012345 60123456
|
|
|
|
|
|
|
|
|
|
for (unsigned int col = scancol; col < scanend; col++)
|
|
|
|
|
{
|
|
|
|
|
uint16_t fb_off = fb_base + col;
|
|
|
|
|
|
|
|
|
|
uint8_t idx = (col<<1);
|
|
|
|
|
uint8_t is_odd = (col & 0x1);
|
|
|
|
|
|
|
|
|
|
uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off];
|
|
|
|
|
|
|
|
|
|
uint8_t aux = scanline[idx]; // AUX
|
|
|
|
|
uint8_t mbd = scanline[idx+1]; // MBD
|
|
|
|
|
|
|
|
|
|
if (!is_odd) {
|
|
|
|
|
// idx = 0 -> aaaa aaab bbbb 0000
|
|
|
|
|
// idx = 2 -> eeee eeef ffff 0000
|
|
|
|
|
|
|
|
|
|
uint8_t auxLO = (aux & (0x0F<<0)) >> 0; // 0000aaaa -> 0000aaaa
|
|
|
|
|
__plot_hires80_pixels(auxLO, fb_ptr);
|
|
|
|
|
fb_ptr += 4;
|
|
|
|
|
|
|
|
|
|
uint8_t auxHI = (aux & (0x07<<4)) >> 4; // 0aaa0000 -> 00000aaa
|
|
|
|
|
uint8_t mbdLO = (mbd & (0x01<<0)) << 3; // 0000000b -> 0000b000
|
|
|
|
|
__plot_hires80_pixels((mbdLO | auxHI), fb_ptr);
|
|
|
|
|
fb_ptr += 4;
|
|
|
|
|
|
|
|
|
|
uint8_t mbdXX = (mbd & (0x0F<<1)) >> 1; // 000bbbb0 -> 0000bbbb
|
|
|
|
|
__plot_hires80_pixels(mbdXX, fb_ptr);
|
|
|
|
|
fb_ptr += 4;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
// partial ... overwritten by next even scan ...
|
|
|
|
|
uint8_t mbdHI = (mbd & (0x03<<5)) >> 5; // 0bb00000 -> 0000XXbb
|
|
|
|
|
__plot_hires80_pixels(mbdHI, fb_ptr);
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// idx = 1 -> bbcc cccc cddd dddd
|
|
|
|
|
// idx = 3 -> ffgg gggg ghhh hhhh
|
|
|
|
|
|
|
|
|
|
fb_ptr -= 2;
|
|
|
|
|
|
|
|
|
|
uint8_t mb0 = scanline[idx-1]; // MBD-1
|
|
|
|
|
|
|
|
|
|
uint8_t mbdHI = (mb0 & (0x03<<5)) >> 5; // 0bb00000 -> 000000bb
|
|
|
|
|
uint8_t auxLO = (aux & (0x03<<0)) << 2; // 000000cc -> 0000cc00
|
|
|
|
|
__plot_hires80_pixels((auxLO | mbdHI), fb_ptr);
|
|
|
|
|
fb_ptr += 4;
|
|
|
|
|
|
|
|
|
|
uint8_t auxXX = (aux & (0x0F<<2)) >> 2; // 00cccc00 -> 0000cccc
|
|
|
|
|
__plot_hires80_pixels(auxXX, fb_ptr);
|
|
|
|
|
fb_ptr += 4;
|
|
|
|
|
|
|
|
|
|
uint8_t auxHI = (aux & (0x01<<6)) >> 6; // 0c000000 -> 0000000c
|
|
|
|
|
uint8_t mbdLO = (mbd & (0x07<<0)) << 1; // 00000ddd -> 0000ddd0
|
|
|
|
|
__plot_hires80_pixels((mbdLO | auxHI), fb_ptr);
|
|
|
|
|
fb_ptr += 4;
|
|
|
|
|
|
|
|
|
|
uint8_t mbdXX = (mbd & (0x0F<<3)) >> 3; // 0dddd000 -> 0000dddd
|
|
|
|
|
__plot_hires80_pixels(mbdXX, fb_ptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
// Hires GRaphics
|
|
|
|
|
// Hires GRaphics (HIRES40)
|
|
|
|
|
|
|
|
|
|
static inline void _calculate_interp_color(uint8_t *color_buf, const unsigned int idx, const uint8_t *interp_base, const uint16_t ea) {
|
|
|
|
|
static inline void _calculate_interp_color(uint8_t *color_buf, const unsigned int idx, const uint8_t *interp_base, uint8_t b) {
|
|
|
|
|
if (color_buf[idx] != 0x0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -1075,7 +1205,7 @@ static inline void _calculate_interp_color(uint8_t *color_buf, const unsigned in
|
|
|
|
|
// Calculates the color at the edge of interpolated bytes: called 4 times in little endian order (...7 0...7 0...)
|
|
|
|
|
if (pixL == COLOR_LIGHT_WHITE) {
|
|
|
|
|
if (pixR == COLOR_LIGHT_WHITE) {
|
|
|
|
|
pixL = apple_ii_64k[0][ea];
|
|
|
|
|
pixL = b;
|
|
|
|
|
color_buf[idx] = interp_base[pixL>>7];
|
|
|
|
|
} else {
|
|
|
|
|
color_buf[idx] = pixR;
|
|
|
|
@ -1100,6 +1230,7 @@ static inline void _plot_hires_pixels(uint8_t *dst, const uint8_t *src) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static void _plot_hires40(uint16_t off, int page, int bank, bool is_even, uint8_t *fb_ptr) {
|
|
|
|
|
assert(bank == 0); // FIXME TODO ...
|
|
|
|
|
uint16_t base = page ? 0x4000 : 0x2000;
|
|
|
|
@ -1174,11 +1305,107 @@ static void _plot_hires40(uint16_t off, int page, int bank, bool is_even, uint8_
|
|
|
|
|
|
|
|
|
|
_plot_hires_pixels(/*dst:*/fb_ptr-4, /*src:*/color_buf);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
static void _plot_hires40_scanline(scan_data_t *scandata) {
|
|
|
|
|
|
|
|
|
|
// FIXME TODO ... this can be further streamlined to keep track of previous data
|
|
|
|
|
|
|
|
|
|
uint8_t *scanline = scandata->scanline;
|
|
|
|
|
unsigned int scanrow = scandata->scanrow;
|
|
|
|
|
unsigned int scancol = scandata->scancol;
|
|
|
|
|
unsigned int scanend = scandata->scanend;
|
|
|
|
|
assert(scancol < scanend);
|
|
|
|
|
assert(scanend > 0);
|
|
|
|
|
|
|
|
|
|
uint16_t fb_base = video__line_offset[scanrow>>3] + (0x400 * (scanrow & 0x7));
|
|
|
|
|
for (unsigned int col = scancol; col < scanend; col++)
|
|
|
|
|
{
|
|
|
|
|
uint16_t fb_off = fb_base + col;
|
|
|
|
|
uint8_t *fb_ptr = FB_BASE + video__screen_addresses[fb_off];
|
|
|
|
|
|
|
|
|
|
bool is_even = !(col & 0x1);
|
|
|
|
|
uint8_t idx = (col<<1)+1; // MBD data only
|
|
|
|
|
uint8_t b = scanline[idx];
|
|
|
|
|
|
|
|
|
|
uint8_t _buf[DYNAMIC_SZ] = { 0 };
|
|
|
|
|
uint8_t *color_buf = (uint8_t *)_buf; // <--- work around for -Wstrict-aliasing
|
|
|
|
|
|
|
|
|
|
uint8_t *hires_ptr = NULL;
|
|
|
|
|
if (is_even) {
|
|
|
|
|
hires_ptr = (uint8_t *)&video__hires_even[b<<3];
|
|
|
|
|
} else {
|
|
|
|
|
hires_ptr = (uint8_t *)&video__hires_odd[b<<3];
|
|
|
|
|
}
|
|
|
|
|
*((uint32_t *)&color_buf[2]) = *((uint32_t *)(hires_ptr+0));
|
|
|
|
|
*((uint16_t *)&color_buf[6]) = *((uint16_t *)(hires_ptr+4));
|
|
|
|
|
*((uint8_t *)&color_buf[8]) = *((uint8_t *)(hires_ptr+6));
|
|
|
|
|
hires_ptr = NULL;
|
|
|
|
|
|
|
|
|
|
// copy adjacent pixel bytes
|
|
|
|
|
*((uint16_t *)&color_buf[0]) = *((uint16_t *)(fb_ptr-3));
|
|
|
|
|
*((uint16_t *)&color_buf[DYNAMIC_SZ-2]) = *((uint16_t *)(fb_ptr+15));
|
|
|
|
|
|
|
|
|
|
if (color_mode != COLOR_NONE) {
|
|
|
|
|
uint8_t *hires_altbase = NULL;
|
|
|
|
|
if (is_even) {
|
|
|
|
|
hires_altbase = (uint8_t *)&video__hires_odd[0];
|
|
|
|
|
} else {
|
|
|
|
|
hires_altbase = (uint8_t *)&video__hires_even[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if right-side color is not black, re-calculate edge values
|
|
|
|
|
if (color_buf[DYNAMIC_SZ-2] & 0xff) {
|
|
|
|
|
if ((col < CYCLES_VIS-1) && (col < scanend - 1)) {
|
|
|
|
|
uint8_t bNext = scanline[idx+2];
|
|
|
|
|
if ((b & 0x40) && (bNext & 0x1)) {
|
|
|
|
|
*((uint16_t *)&color_buf[DYNAMIC_SZ-3]) = (uint16_t)0x3737;// COLOR_LIGHT_WHITE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if left-side color is not black, re-calculate edge values
|
|
|
|
|
if (color_buf[1] & 0xff) {
|
|
|
|
|
if (col > 0) {
|
|
|
|
|
uint8_t bPrev = scanline[idx-2];
|
|
|
|
|
if ((bPrev & 0x40) && (b & 0x1)) {
|
|
|
|
|
*((uint16_t *)&color_buf[1]) = (uint16_t)0x3737;// COLOR_LIGHT_WHITE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (color_mode == COLOR_INTERP) {
|
|
|
|
|
uint8_t *interp_base = NULL;
|
|
|
|
|
uint8_t *interp_altbase = NULL;
|
|
|
|
|
if (is_even) {
|
|
|
|
|
interp_base = (uint8_t *)&video__even_colors[0];
|
|
|
|
|
interp_altbase = (uint8_t *)&video__odd_colors[0];
|
|
|
|
|
} else {
|
|
|
|
|
interp_base = (uint8_t *)&video__odd_colors[0];
|
|
|
|
|
interp_altbase = (uint8_t *)&video__even_colors[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// calculate interpolated/bleed colors
|
|
|
|
|
uint8_t bl = 0x0;
|
|
|
|
|
if (col > 0) {
|
|
|
|
|
bl = scanline[idx-2];
|
|
|
|
|
}
|
|
|
|
|
_calculate_interp_color(color_buf, 1, interp_altbase, bl);
|
|
|
|
|
_calculate_interp_color(color_buf, 2, interp_base, b);
|
|
|
|
|
_calculate_interp_color(color_buf, 8, interp_base, b);
|
|
|
|
|
if (col < CYCLES_VIS-1) {
|
|
|
|
|
bl = scanline[idx+2];
|
|
|
|
|
}
|
|
|
|
|
_calculate_interp_color(color_buf, 9, interp_altbase, bl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_plot_hires_pixels(/*dst:*/fb_ptr-4, /*src:*/color_buf);
|
|
|
|
|
////fb_ptr += 7;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
GLUE_C_WRITE(video__write_2e_hgr0)
|
|
|
|
|
{
|
|
|
|
|
run_args.base_hgrwrt[ea] = b;
|
|
|
|
@ -1257,105 +1484,145 @@ void display_setUpdateCallback(drawpage_mode_t mode, display_update_fn updateFn)
|
|
|
|
|
assert(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void display_renderStagingFramebuffer(uint8_t *stagingFB) {
|
|
|
|
|
static void (*_hirespage_plotter(uint32_t currswitches))(scan_data_t*) {
|
|
|
|
|
return ((currswitches & SS_80COL) && (currswitches & SS_DHIRES)) ? _plot_hires80_scanline : _plot_hires40_scanline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#warning FIXME TODO ... this needs to scan memory in the same way as the actually //e video scanner
|
|
|
|
|
uint8_t *display_renderStagingFramebuffer(void) {
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&display_scan_mutex);
|
|
|
|
|
|
|
|
|
|
int page = 0;
|
|
|
|
|
int bank = 0;
|
|
|
|
|
const uint32_t mainswitches = run_args.softswitches;
|
|
|
|
|
|
|
|
|
|
// render main portion of screen ...
|
|
|
|
|
|
|
|
|
|
drawpage_mode_t mainDrawPageMode = _currentMainMode(mainswitches);
|
|
|
|
|
_currentPageAndBank(mainswitches, mainDrawPageMode, &page, &bank);
|
|
|
|
|
drawpage_mode_t mainDrawPageMode = video_currentMainMode(mainswitches);
|
|
|
|
|
int page = video_currentPage(mainswitches);
|
|
|
|
|
|
|
|
|
|
static uint8_t scanline[CYCLES_VIS<<1] = { 0 }; // 80 columns of data ...
|
|
|
|
|
|
|
|
|
|
if (mainDrawPageMode == DRAWPAGE_TEXT) {
|
|
|
|
|
void (*textMainPlotFn)(uint8_t, uint8_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++) {
|
|
|
|
|
textMainPlotFn(x, y, page, bank, stagingFB);
|
|
|
|
|
uint16_t base = page ? 0x0800 : 0x0400;
|
|
|
|
|
for (unsigned int row=0; row < TEXT_ROWS-4; row++) {
|
|
|
|
|
for (unsigned int col=0; col < TEXT_COLS; col++) {
|
|
|
|
|
uint16_t off = video__line_offset[row] + col;
|
|
|
|
|
uint16_t ea = base+off;
|
|
|
|
|
scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX
|
|
|
|
|
scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD
|
|
|
|
|
}
|
|
|
|
|
for (unsigned int i = 0; i < 8; i++) {
|
|
|
|
|
display_flushScanline(&((scan_data_t){
|
|
|
|
|
.scanline = &scanline[0],
|
|
|
|
|
.softswitches = mainswitches,
|
|
|
|
|
.scanrow = (row<<3) + i,
|
|
|
|
|
.scancol = 0,
|
|
|
|
|
.scanend = CYCLES_VIS,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} 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, uint8_t*) = _hirespage_plotter(mainswitches);
|
|
|
|
|
for (unsigned int y=0; y < TEXT_ROWS-4; y++) {
|
|
|
|
|
for (unsigned int x=0; x < TEXT_COLS; x++) {
|
|
|
|
|
uint16_t base = page ? 0x4000 : 0x2000;
|
|
|
|
|
for (unsigned int row=0; row < TEXT_ROWS-4; row++) {
|
|
|
|
|
for (unsigned int col=0; col < TEXT_COLS; col++) {
|
|
|
|
|
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), stagingFB);
|
|
|
|
|
uint16_t off = video__line_offset[row] + (0x400*i) + col;
|
|
|
|
|
uint16_t ea = base+off;
|
|
|
|
|
scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX
|
|
|
|
|
scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD
|
|
|
|
|
display_flushScanline(&((scan_data_t){
|
|
|
|
|
.scanline = &scanline[0],
|
|
|
|
|
.softswitches = mainswitches,
|
|
|
|
|
.scanrow = (row<<3) + i,
|
|
|
|
|
.scancol = 0,
|
|
|
|
|
.scanend = CYCLES_VIS,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(void)mainswitches;
|
|
|
|
|
|
|
|
|
|
// resample current switches ... and render mixed portion of screen
|
|
|
|
|
const uint32_t mixedswitches = run_args.softswitches;
|
|
|
|
|
|
|
|
|
|
drawpage_mode_t mixedDrawPageMode = _currentMixedMode(mixedswitches);
|
|
|
|
|
_currentPageAndBank(mixedswitches, mixedDrawPageMode, &page, &bank);
|
|
|
|
|
drawpage_mode_t mixedDrawPageMode = video_currentMixedMode(mixedswitches);
|
|
|
|
|
page = video_currentPage(mixedswitches);
|
|
|
|
|
|
|
|
|
|
if (mixedDrawPageMode == DRAWPAGE_TEXT) {
|
|
|
|
|
void (*textMixedPlotFn)(uint8_t, uint8_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++) {
|
|
|
|
|
textMixedPlotFn(x, y, page, bank, stagingFB);
|
|
|
|
|
uint16_t base = page ? 0x0800 : 0x0400;
|
|
|
|
|
for (unsigned int row=TEXT_ROWS-4; row < TEXT_ROWS; row++) {
|
|
|
|
|
for (unsigned int col=0; col < TEXT_COLS; col++) {
|
|
|
|
|
uint16_t off = video__line_offset[row] + col;
|
|
|
|
|
uint16_t ea = base+off;
|
|
|
|
|
scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX
|
|
|
|
|
scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD
|
|
|
|
|
}
|
|
|
|
|
for (unsigned int i = 0; i < 8; i++) {
|
|
|
|
|
display_flushScanline(&((scan_data_t){
|
|
|
|
|
.scanline = &scanline[0],
|
|
|
|
|
.softswitches = mixedswitches,
|
|
|
|
|
.scanrow = (row<<3) + i,
|
|
|
|
|
.scancol = 0,
|
|
|
|
|
.scanend = CYCLES_VIS,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} 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, 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++) {
|
|
|
|
|
uint16_t base = page ? 0x4000 : 0x2000;
|
|
|
|
|
for (unsigned int row=TEXT_ROWS-4; row < TEXT_ROWS; row++) {
|
|
|
|
|
for (unsigned int col=0; col < TEXT_COLS; col++) {
|
|
|
|
|
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), stagingFB);
|
|
|
|
|
uint16_t off = video__line_offset[row] + (0x400*i) + col;
|
|
|
|
|
uint16_t ea = base+off;
|
|
|
|
|
scanline[(col<<1)+0] = apple_ii_64k[1][ea]; // AUX
|
|
|
|
|
scanline[(col<<1)+1] = apple_ii_64k[0][ea]; // MBD
|
|
|
|
|
display_flushScanline(&((scan_data_t){
|
|
|
|
|
.scanline = &scanline[0],
|
|
|
|
|
.softswitches = mixedswitches,
|
|
|
|
|
.scanrow = (row<<3) + i,
|
|
|
|
|
.scancol = 0,
|
|
|
|
|
.scanend = CYCLES_VIS,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
video_setDirty(FB_DIRTY_FLAG);
|
|
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&display_scan_mutex);
|
|
|
|
|
return display_getCurrentFramebuffer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void display_flashText(void) {
|
|
|
|
|
static bool normal = false;
|
|
|
|
|
|
|
|
|
|
normal = !normal;
|
|
|
|
|
|
|
|
|
|
// flash only if it's text or mixed modes.
|
|
|
|
|
if (run_args.softswitches & (SS_TEXT|SS_MIXED)) {
|
|
|
|
|
if (normal) {
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].red = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].green = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].blue = 0;
|
|
|
|
|
if (normal) {
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].red = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].green = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].blue = 0;
|
|
|
|
|
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].red = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].green = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].blue = 0xff;
|
|
|
|
|
} else {
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].red = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].green = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].blue = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].red = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].green = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].blue = 0xff;
|
|
|
|
|
} else {
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].red = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].green = 0xff;
|
|
|
|
|
colormap[ COLOR_FLASHING_BLACK].blue = 0xff;
|
|
|
|
|
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].red = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].green = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].blue = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
video_setDirty(FB_DIRTY_FLAG);
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].red = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].green = 0;
|
|
|
|
|
colormap[ COLOR_FLASHING_WHITE].blue = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
video_setDirty(FB_DIRTY_FLAG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
bool video_isDirty(unsigned long flags) {
|
|
|
|
|
return (_vid_dirty & flags);
|
|
|
|
|
}
|
|
|
|
@ -1501,6 +1768,82 @@ uint8_t floating_bus_hibit(const bool hibit) {
|
|
|
|
|
uint8_t b = apple_ii_64k[0][scanner_addr];
|
|
|
|
|
return (b & ~0x80) | (hibit ? 0x80 : 0);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
uint8_t *display_getCurrentFramebuffer(void) {
|
|
|
|
|
#if INTERFACE_CLASSIC
|
|
|
|
|
if (interface_isShowing()) {
|
|
|
|
|
return fbInterface;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return fbStaging;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if TESTING
|
|
|
|
|
// HACK FIXME TODO ... should consolidate this into debugger ...
|
|
|
|
|
extern pthread_mutex_t interface_mutex;
|
|
|
|
|
extern pthread_cond_t cpu_thread_cond;
|
|
|
|
|
extern pthread_cond_t dbg_thread_cond;
|
|
|
|
|
uint8_t *display_waitForNextCompleteFramebuffer(void) {
|
|
|
|
|
int err = 0;
|
|
|
|
|
if ((err = pthread_cond_signal(&cpu_thread_cond))) {
|
|
|
|
|
LOG("pthread_cond_signal : %d", err);
|
|
|
|
|
}
|
|
|
|
|
if ((err = pthread_cond_wait(&dbg_thread_cond, &interface_mutex))) {
|
|
|
|
|
LOG("pthread_cond_wait : %d", err);
|
|
|
|
|
}
|
|
|
|
|
return display_getCurrentFramebuffer ();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void display_flushScanline(scan_data_t *scandata) {
|
|
|
|
|
#if TESTING
|
|
|
|
|
// FIXME TODO ... remove this bracing when video refactoring is done
|
|
|
|
|
#else
|
|
|
|
|
ASSERT_ON_CPU_THREAD();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
unsigned int scanrow = scandata->scanrow;
|
|
|
|
|
unsigned int scancol = scandata->scancol;
|
|
|
|
|
unsigned int scanend = scandata->scanend;
|
|
|
|
|
|
|
|
|
|
assert((scanend > 0) && (scanend <= CYCLES_VIS));
|
|
|
|
|
assert(scancol < CYCLES_VIS);
|
|
|
|
|
assert(scanrow < SCANLINES_VBL_BEGIN);
|
|
|
|
|
|
|
|
|
|
const uint32_t currswitches = scandata->softswitches;
|
|
|
|
|
const drawpage_mode_t mode = (scanrow < SCANLINES_MIX) ? video_currentMainMode(currswitches) : video_currentMixedMode(currswitches);
|
|
|
|
|
|
|
|
|
|
void (*plotFn)(scan_data_t *) = NULL;
|
|
|
|
|
|
|
|
|
|
if (mode == DRAWPAGE_TEXT) {
|
|
|
|
|
const uint32_t textmask = (scanrow < SCANLINES_MIX) ? SS_TEXT : (SS_TEXT|SS_MIXED);
|
|
|
|
|
plotFn = _textpage_plotter(currswitches, textmask);
|
|
|
|
|
} else {
|
|
|
|
|
assert(!(currswitches & SS_TEXT) && "TEXT should not be set");
|
|
|
|
|
assert((currswitches & SS_HIRES) && "HIRES should be set");
|
|
|
|
|
plotFn = _hirespage_plotter(currswitches);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
plotFn(scandata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void display_frameComplete(void) {
|
|
|
|
|
ASSERT_ON_CPU_THREAD();
|
|
|
|
|
|
|
|
|
|
video_setDirty(FB_DIRTY_FLAG);
|
|
|
|
|
|
|
|
|
|
#if TESTING
|
|
|
|
|
// HACK FIXME TODO ... should consolidate this into debugger ...
|
|
|
|
|
int err = 0;
|
|
|
|
|
if ((err = pthread_cond_signal(&dbg_thread_cond))) {
|
|
|
|
|
LOG("pthread_cond_signal : %d", err);
|
|
|
|
|
}
|
|
|
|
|
if ((err = pthread_cond_wait(&cpu_thread_cond, &interface_mutex))) {
|
|
|
|
|
LOG("pthread_cond_wait : %d", err);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void _init_interface(void) {
|
|
|
|
|
LOG("Initializing display subsystem");
|
|
|
|
@ -1510,16 +1853,10 @@ static void _init_interface(void) {
|
|
|
|
|
_initialize_dhires_values();
|
|
|
|
|
_initialize_color();
|
|
|
|
|
|
|
|
|
|
prefs_registerListener(PREF_DOMAIN_VIDEO, &video_prefsChanged);
|
|
|
|
|
prefs_registerListener(PREF_DOMAIN_VIDEO, &display_prefsChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static __attribute__((constructor)) void __init_interface(void) {
|
|
|
|
|
emulator_registerStartupCallback(CTOR_PRIORITY_LATE, &_init_interface);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if __clang__
|
|
|
|
|
# pragma clang diagnostic pop
|
|
|
|
|
#elif __GNUC__
|
|
|
|
|
# pragma GCC diagnostic pop
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|