2000-09-22 17:16:05 +00:00
|
|
|
/*
|
|
|
|
* video_vosf.h - Video/graphics emulation, video on SEGV signals support
|
|
|
|
*
|
2005-01-30 21:42:16 +00:00
|
|
|
* Basilisk II (C) 1997-2005 Christian Bauer
|
2000-09-22 17:16:05 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef VIDEO_VOSF_H
|
|
|
|
#define VIDEO_VOSF_H
|
|
|
|
|
2003-09-29 07:02:58 +00:00
|
|
|
// Note: this file must be #include'd only in video_x.cpp
|
2000-09-22 17:16:05 +00:00
|
|
|
#ifdef ENABLE_VOSF
|
|
|
|
|
2001-06-28 21:20:02 +00:00
|
|
|
#include "sigsegv.h"
|
|
|
|
#include "vm_alloc.h"
|
2004-12-11 10:20:32 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include "util_windows.h"
|
|
|
|
#endif
|
2001-06-28 21:20:02 +00:00
|
|
|
|
2004-06-24 15:19:56 +00:00
|
|
|
// Glue for SDL and X11 support
|
2004-06-23 14:30:48 +00:00
|
|
|
#ifdef USE_SDL_VIDEO
|
|
|
|
#define MONITOR_INIT SDL_monitor_desc &monitor
|
2005-05-12 11:09:31 +00:00
|
|
|
#define VIDEO_DRV_WIN_INIT driver_window *drv
|
2005-05-12 14:39:56 +00:00
|
|
|
#define VIDEO_DRV_DGA_INIT driver_fullscreen *drv
|
2005-11-21 23:38:46 +00:00
|
|
|
#define VIDEO_DRV_LOCK_PIXELS SDL_VIDEO_LOCK_SURFACE(drv->s)
|
|
|
|
#define VIDEO_DRV_UNLOCK_PIXELS SDL_VIDEO_UNLOCK_SURFACE(drv->s)
|
2005-05-12 11:09:31 +00:00
|
|
|
#define VIDEO_DRV_DEPTH drv->s->format->BitsPerPixel
|
|
|
|
#define VIDEO_DRV_WIDTH drv->s->w
|
|
|
|
#define VIDEO_DRV_HEIGHT drv->s->h
|
|
|
|
#define VIDEO_DRV_ROW_BYTES drv->s->pitch
|
2004-06-23 14:30:48 +00:00
|
|
|
#else
|
2004-06-24 15:19:56 +00:00
|
|
|
#ifdef SHEEPSHAVER
|
2004-06-23 14:30:48 +00:00
|
|
|
#define MONITOR_INIT /* nothing */
|
2005-05-12 11:09:31 +00:00
|
|
|
#define VIDEO_DRV_WIN_INIT /* nothing */
|
|
|
|
#define VIDEO_DRV_DGA_INIT /* nothing */
|
2003-05-22 22:13:56 +00:00
|
|
|
#define VIDEO_DRV_WINDOW the_win
|
|
|
|
#define VIDEO_DRV_GC the_gc
|
|
|
|
#define VIDEO_DRV_IMAGE img
|
|
|
|
#define VIDEO_DRV_HAVE_SHM have_shm
|
|
|
|
#else
|
2004-06-23 14:30:48 +00:00
|
|
|
#define MONITOR_INIT X11_monitor_desc &monitor
|
2005-05-12 11:09:31 +00:00
|
|
|
#define VIDEO_DRV_WIN_INIT driver_window *drv
|
|
|
|
#define VIDEO_DRV_DGA_INIT driver_dga *drv
|
2003-05-22 22:13:56 +00:00
|
|
|
#define VIDEO_DRV_WINDOW drv->w
|
|
|
|
#define VIDEO_DRV_GC drv->gc
|
|
|
|
#define VIDEO_DRV_IMAGE drv->img
|
|
|
|
#define VIDEO_DRV_HAVE_SHM drv->have_shm
|
2004-06-23 14:30:48 +00:00
|
|
|
#endif
|
|
|
|
#define VIDEO_DRV_LOCK_PIXELS /* nothing */
|
|
|
|
#define VIDEO_DRV_UNLOCK_PIXELS /* nothing */
|
2005-05-12 11:09:31 +00:00
|
|
|
#define VIDEO_DRV_DEPTH VIDEO_DRV_IMAGE->depth
|
|
|
|
#define VIDEO_DRV_WIDTH VIDEO_DRV_IMAGE->width
|
|
|
|
#define VIDEO_DRV_HEIGHT VIDEO_DRV_IMAGE->height
|
2004-06-23 14:30:48 +00:00
|
|
|
#define VIDEO_DRV_ROW_BYTES VIDEO_DRV_IMAGE->bytes_per_line
|
2003-05-22 22:13:56 +00:00
|
|
|
#endif
|
|
|
|
|
2001-06-28 21:20:02 +00:00
|
|
|
// Variables for Video on SEGV support
|
2001-06-28 22:06:18 +00:00
|
|
|
static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
|
2001-06-28 21:20:02 +00:00
|
|
|
|
|
|
|
struct ScreenPageInfo {
|
|
|
|
int top, bottom; // Mapping between this virtual page and Mac scanlines
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ScreenInfo {
|
|
|
|
uintptr memStart; // Start address aligned to page boundary
|
|
|
|
uint32 memLength; // Length of the memory addressed by the screen pages
|
|
|
|
|
2001-07-07 09:14:47 +00:00
|
|
|
uintptr pageSize; // Size of a page
|
2001-06-28 21:20:02 +00:00
|
|
|
int pageBits; // Shift count to get the page number
|
|
|
|
uint32 pageCount; // Number of pages allocated to the screen
|
|
|
|
|
|
|
|
bool dirty; // Flag: set if the frame buffer was touched
|
2005-04-02 09:50:17 +00:00
|
|
|
bool very_dirty; // Flag: set if the frame buffer was completely modified (e.g. colormap changes)
|
2001-06-28 21:20:02 +00:00
|
|
|
char * dirtyPages; // Table of flags set if page was altered
|
|
|
|
ScreenPageInfo * pageInfo; // Table of mappings page -> Mac scanlines
|
|
|
|
};
|
|
|
|
|
|
|
|
static ScreenInfo mainBuffer;
|
|
|
|
|
|
|
|
#define PFLAG_SET_VALUE 0x00
|
|
|
|
#define PFLAG_CLEAR_VALUE 0x01
|
|
|
|
#define PFLAG_SET_VALUE_4 0x00000000
|
|
|
|
#define PFLAG_CLEAR_VALUE_4 0x01010101
|
|
|
|
#define PFLAG_SET(page) mainBuffer.dirtyPages[page] = PFLAG_SET_VALUE
|
|
|
|
#define PFLAG_CLEAR(page) mainBuffer.dirtyPages[page] = PFLAG_CLEAR_VALUE
|
|
|
|
#define PFLAG_ISSET(page) (mainBuffer.dirtyPages[page] == PFLAG_SET_VALUE)
|
|
|
|
#define PFLAG_ISCLEAR(page) (mainBuffer.dirtyPages[page] != PFLAG_SET_VALUE)
|
|
|
|
|
|
|
|
#ifdef UNALIGNED_PROFITABLE
|
|
|
|
# define PFLAG_ISSET_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_SET_VALUE_4)
|
|
|
|
# define PFLAG_ISCLEAR_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_CLEAR_VALUE_4)
|
|
|
|
#else
|
|
|
|
# define PFLAG_ISSET_4(page) \
|
|
|
|
PFLAG_ISSET(page ) && PFLAG_ISSET(page+1) \
|
|
|
|
&& PFLAG_ISSET(page+2) && PFLAG_ISSET(page+3)
|
|
|
|
# define PFLAG_ISCLEAR_4(page) \
|
|
|
|
PFLAG_ISCLEAR(page ) && PFLAG_ISCLEAR(page+1) \
|
|
|
|
&& PFLAG_ISCLEAR(page+2) && PFLAG_ISCLEAR(page+3)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Set the selected page range [ first_page, last_page [ into the SET state
|
|
|
|
#define PFLAG_SET_RANGE(first_page, last_page) \
|
|
|
|
memset(mainBuffer.dirtyPages + (first_page), PFLAG_SET_VALUE, \
|
|
|
|
(last_page) - (first_page))
|
|
|
|
|
|
|
|
// Set the selected page range [ first_page, last_page [ into the CLEAR state
|
|
|
|
#define PFLAG_CLEAR_RANGE(first_page, last_page) \
|
|
|
|
memset(mainBuffer.dirtyPages + (first_page), PFLAG_CLEAR_VALUE, \
|
|
|
|
(last_page) - (first_page))
|
|
|
|
|
|
|
|
#define PFLAG_SET_ALL do { \
|
|
|
|
PFLAG_SET_RANGE(0, mainBuffer.pageCount); \
|
|
|
|
mainBuffer.dirty = true; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define PFLAG_CLEAR_ALL do { \
|
|
|
|
PFLAG_CLEAR_RANGE(0, mainBuffer.pageCount); \
|
|
|
|
mainBuffer.dirty = false; \
|
2005-04-02 09:50:17 +00:00
|
|
|
mainBuffer.very_dirty = false; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define PFLAG_SET_VERY_DIRTY do { \
|
|
|
|
mainBuffer.very_dirty = true; \
|
2001-06-28 21:20:02 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
// Set the following macro definition to 1 if your system
|
|
|
|
// provides a really fast strchr() implementation
|
|
|
|
//#define HAVE_FAST_STRCHR 0
|
|
|
|
|
|
|
|
static inline int find_next_page_set(int page)
|
|
|
|
{
|
|
|
|
#if HAVE_FAST_STRCHR
|
|
|
|
char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_SET_VALUE);
|
|
|
|
return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
|
|
|
|
#else
|
|
|
|
while (PFLAG_ISCLEAR_4(page))
|
|
|
|
page += 4;
|
|
|
|
while (PFLAG_ISCLEAR(page))
|
|
|
|
page++;
|
|
|
|
return page;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int find_next_page_clear(int page)
|
|
|
|
{
|
|
|
|
#if HAVE_FAST_STRCHR
|
|
|
|
char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_CLEAR_VALUE);
|
|
|
|
return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
|
|
|
|
#else
|
|
|
|
while (PFLAG_ISSET_4(page))
|
|
|
|
page += 4;
|
|
|
|
while (PFLAG_ISSET(page))
|
|
|
|
page++;
|
|
|
|
return page;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2005-06-25 11:40:29 +00:00
|
|
|
#if defined(HAVE_PTHREADS)
|
|
|
|
static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
|
|
|
|
#define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
|
|
|
|
#define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
|
2004-12-11 10:20:32 +00:00
|
|
|
#elif defined(_WIN32)
|
|
|
|
static mutex_t vosf_lock; // Mutex to protect frame buffer (dirtyPages in fact)
|
|
|
|
#define LOCK_VOSF vosf_lock.lock();
|
|
|
|
#define UNLOCK_VOSF vosf_lock.unlock();
|
2005-06-25 11:40:29 +00:00
|
|
|
#elif defined(HAVE_SPINLOCKS)
|
|
|
|
static spinlock_t vosf_lock = SPIN_LOCK_UNLOCKED; // Mutex to protect frame buffer (dirtyPages in fact)
|
|
|
|
#define LOCK_VOSF spin_lock(&vosf_lock)
|
|
|
|
#define UNLOCK_VOSF spin_unlock(&vosf_lock)
|
2001-06-28 21:20:02 +00:00
|
|
|
#else
|
|
|
|
#define LOCK_VOSF
|
|
|
|
#define UNLOCK_VOSF
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int log_base_2(uint32 x)
|
|
|
|
{
|
|
|
|
uint32 mask = 0x80000000;
|
|
|
|
int l = 31;
|
|
|
|
while (l >= 0 && (x & mask) == 0) {
|
|
|
|
mask >>= 1;
|
|
|
|
l--;
|
|
|
|
}
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
2001-06-28 22:06:18 +00:00
|
|
|
// Extend size to page boundary
|
|
|
|
static uint32 page_extend(uint32 size)
|
|
|
|
{
|
2004-12-18 22:20:27 +00:00
|
|
|
const uint32 page_size = vm_get_page_size();
|
2001-06-28 22:06:18 +00:00
|
|
|
const uint32 page_mask = page_size - 1;
|
|
|
|
return (size + page_mask) & ~page_mask;
|
|
|
|
}
|
|
|
|
|
2001-06-28 21:20:02 +00:00
|
|
|
|
2004-06-26 15:22:02 +00:00
|
|
|
/*
|
|
|
|
* Check if VOSF acceleration is profitable on this platform
|
|
|
|
*/
|
|
|
|
|
2004-06-26 16:26:07 +00:00
|
|
|
const int VOSF_PROFITABLE_TRIES = 3; // Make 3 attempts for full screen update
|
|
|
|
const int VOSF_PROFITABLE_THRESHOLD = 16667; // 60 Hz
|
2004-06-26 15:22:02 +00:00
|
|
|
|
|
|
|
static bool video_vosf_profitable(void)
|
|
|
|
{
|
2004-06-26 16:26:07 +00:00
|
|
|
int64 durations[VOSF_PROFITABLE_TRIES];
|
|
|
|
int mean_duration = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < VOSF_PROFITABLE_TRIES; i++) {
|
|
|
|
uint64 start = GetTicks_usec();
|
|
|
|
for (int p = 0; p < mainBuffer.pageCount; p++) {
|
|
|
|
uint8 *addr = (uint8 *)(mainBuffer.memStart + (p * mainBuffer.pageSize));
|
|
|
|
addr[0] = 0; // Trigger Screen_fault_handler()
|
|
|
|
}
|
|
|
|
int64 duration = GetTicks_usec() - start;
|
|
|
|
mean_duration += duration;
|
|
|
|
durations[i] = duration;
|
|
|
|
|
|
|
|
PFLAG_CLEAR_ALL;
|
|
|
|
mainBuffer.dirty = false;
|
|
|
|
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
|
|
|
|
return false;
|
2004-06-26 15:22:02 +00:00
|
|
|
}
|
|
|
|
|
2004-06-26 16:26:07 +00:00
|
|
|
mean_duration /= VOSF_PROFITABLE_TRIES;
|
|
|
|
D(bug("Triggered %d screen faults in %ld usec on average\n", mainBuffer.pageCount, mean_duration));
|
|
|
|
return (mean_duration < (VOSF_PROFITABLE_THRESHOLD * (frame_skip ? frame_skip : 1)));
|
2004-06-26 15:22:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-06-28 21:20:02 +00:00
|
|
|
/*
|
2001-07-07 09:14:47 +00:00
|
|
|
* Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
|
2001-06-28 21:20:02 +00:00
|
|
|
*/
|
|
|
|
|
2004-06-23 14:30:48 +00:00
|
|
|
static bool video_vosf_init(MONITOR_INIT)
|
2001-06-28 21:20:02 +00:00
|
|
|
{
|
2004-06-26 17:20:35 +00:00
|
|
|
VIDEO_MODE_INIT_MONITOR;
|
2002-04-25 11:00:31 +00:00
|
|
|
|
2004-12-18 22:20:27 +00:00
|
|
|
const uintptr page_size = vm_get_page_size();
|
2001-07-07 09:14:47 +00:00
|
|
|
const uintptr page_mask = page_size - 1;
|
|
|
|
|
|
|
|
// Round up frame buffer base to page boundary
|
|
|
|
mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
|
|
|
|
|
|
|
|
// The frame buffer size shall already be aligned to page boundary (use page_extend)
|
|
|
|
mainBuffer.memLength = the_buffer_size;
|
|
|
|
|
|
|
|
mainBuffer.pageSize = page_size;
|
|
|
|
mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
|
|
|
|
mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
|
|
|
|
|
|
|
|
// The "2" more bytes requested are a safety net to insure the
|
|
|
|
// loops in the update routines will terminate.
|
|
|
|
// See "How can we deal with array overrun conditions ?" hereunder for further details.
|
2001-07-14 18:41:05 +00:00
|
|
|
mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
|
|
|
|
if (mainBuffer.dirtyPages == NULL)
|
2001-07-07 09:14:47 +00:00
|
|
|
return false;
|
2001-06-28 21:20:02 +00:00
|
|
|
|
2001-07-07 09:14:47 +00:00
|
|
|
PFLAG_CLEAR_ALL;
|
|
|
|
PFLAG_CLEAR(mainBuffer.pageCount);
|
|
|
|
PFLAG_SET(mainBuffer.pageCount+1);
|
|
|
|
|
|
|
|
// Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
|
2001-07-14 18:41:05 +00:00
|
|
|
mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
|
|
|
|
if (mainBuffer.pageInfo == NULL)
|
2001-07-07 09:14:47 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
uint32 a = 0;
|
2001-07-11 19:26:14 +00:00
|
|
|
for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
|
2003-05-22 22:13:56 +00:00
|
|
|
unsigned y1 = a / VIDEO_MODE_ROW_BYTES;
|
|
|
|
if (y1 >= VIDEO_MODE_Y)
|
|
|
|
y1 = VIDEO_MODE_Y - 1;
|
2001-07-07 09:14:47 +00:00
|
|
|
|
2003-05-22 22:13:56 +00:00
|
|
|
unsigned y2 = (a + mainBuffer.pageSize) / VIDEO_MODE_ROW_BYTES;
|
|
|
|
if (y2 >= VIDEO_MODE_Y)
|
|
|
|
y2 = VIDEO_MODE_Y - 1;
|
2001-07-07 09:14:47 +00:00
|
|
|
|
|
|
|
mainBuffer.pageInfo[i].top = y1;
|
|
|
|
mainBuffer.pageInfo[i].bottom = y2;
|
|
|
|
|
|
|
|
a += mainBuffer.pageSize;
|
|
|
|
if (a > mainBuffer.memLength)
|
|
|
|
a = mainBuffer.memLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can now write-protect the frame buffer
|
|
|
|
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// The frame buffer is sane, i.e. there is no write to it yet
|
|
|
|
mainBuffer.dirty = false;
|
|
|
|
return true;
|
|
|
|
}
|
2001-06-28 21:20:02 +00:00
|
|
|
|
|
|
|
|
2001-07-07 09:14:47 +00:00
|
|
|
/*
|
|
|
|
* Deinitialize VOSF system
|
|
|
|
*/
|
2001-06-28 21:20:02 +00:00
|
|
|
|
2001-07-07 09:14:47 +00:00
|
|
|
static void video_vosf_exit(void)
|
|
|
|
{
|
2001-07-14 18:41:05 +00:00
|
|
|
if (mainBuffer.pageInfo) {
|
|
|
|
free(mainBuffer.pageInfo);
|
|
|
|
mainBuffer.pageInfo = NULL;
|
2001-07-07 09:14:47 +00:00
|
|
|
}
|
2001-07-14 18:41:05 +00:00
|
|
|
if (mainBuffer.dirtyPages) {
|
|
|
|
free(mainBuffer.dirtyPages);
|
|
|
|
mainBuffer.dirtyPages = NULL;
|
2001-06-28 21:20:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-09-22 17:16:05 +00:00
|
|
|
/*
|
2001-06-28 22:06:18 +00:00
|
|
|
* Screen fault handler
|
2000-09-22 17:16:05 +00:00
|
|
|
*/
|
|
|
|
|
2003-05-22 22:13:56 +00:00
|
|
|
bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
|
2000-09-22 17:16:05 +00:00
|
|
|
{
|
2001-05-20 20:31:50 +00:00
|
|
|
const uintptr addr = (uintptr)fault_address;
|
|
|
|
|
2001-01-11 16:39:08 +00:00
|
|
|
/* Someone attempted to write to the frame buffer. Make it writeable
|
2001-06-28 22:06:18 +00:00
|
|
|
* now so that the data could actually be written to. It will be made
|
2001-01-11 16:39:08 +00:00
|
|
|
* read-only back in one of the screen update_*() functions.
|
|
|
|
*/
|
2001-07-07 09:14:47 +00:00
|
|
|
if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
|
|
|
|
const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
|
2001-01-11 16:39:08 +00:00
|
|
|
LOCK_VOSF;
|
|
|
|
PFLAG_SET(page);
|
2001-07-07 09:14:47 +00:00
|
|
|
vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
|
2001-01-28 14:05:19 +00:00
|
|
|
mainBuffer.dirty = true;
|
2001-01-11 16:39:08 +00:00
|
|
|
UNLOCK_VOSF;
|
2001-05-20 20:31:50 +00:00
|
|
|
return true;
|
2000-09-22 17:16:05 +00:00
|
|
|
}
|
|
|
|
|
2001-01-11 16:39:08 +00:00
|
|
|
/* Otherwise, we don't know how to handle the fault, let it crash */
|
2001-05-20 20:31:50 +00:00
|
|
|
return false;
|
2000-09-22 17:16:05 +00:00
|
|
|
}
|
2000-10-11 17:55:06 +00:00
|
|
|
|
2001-06-28 22:06:18 +00:00
|
|
|
|
2000-09-22 17:16:05 +00:00
|
|
|
/*
|
|
|
|
* Update display for Windowed mode and VOSF
|
|
|
|
*/
|
|
|
|
|
2001-01-11 18:00:40 +00:00
|
|
|
/* How can we deal with array overrun conditions ?
|
|
|
|
|
|
|
|
The state of the framebuffer pages that have been touched are maintained
|
|
|
|
in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
|
|
|
|
|
|
|
|
Terminology
|
|
|
|
|
|
|
|
"Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
|
|
|
|
"CLEAR Page Guard" refers to the page following the Last Page but is always
|
|
|
|
in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
|
|
|
|
Page Guard but is always in the SET state.
|
|
|
|
|
|
|
|
Rough process
|
|
|
|
|
2001-01-28 14:05:19 +00:00
|
|
|
The update routines must determine which pages have to be blitted to the
|
2001-01-11 18:00:40 +00:00
|
|
|
screen. This job consists in finding the first_page that was touched.
|
|
|
|
i.e. find the next page that is SET. Then, finding how many pages were
|
|
|
|
touched starting from first_page. i.e. find the next page that is CLEAR.
|
|
|
|
|
2001-01-28 14:05:19 +00:00
|
|
|
There are two cases to check:
|
2001-01-11 18:00:40 +00:00
|
|
|
|
|
|
|
- Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
|
|
|
|
but it is beyond the valid pageCount value. Therefore, we exit from the
|
|
|
|
update routine.
|
|
|
|
|
|
|
|
- Last Page is SET: first_page equals (pageCount - 1) and
|
|
|
|
find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
|
|
|
|
page to the screen. On the next iteration, page equals pageCount and
|
|
|
|
find_next_page_set() will reach the SET Page Guard. We still safely exit
|
|
|
|
from the update routine because the SET Page Guard position is greater
|
|
|
|
than pageCount.
|
|
|
|
*/
|
|
|
|
|
2005-05-12 14:39:56 +00:00
|
|
|
static void update_display_window_vosf(VIDEO_DRV_WIN_INIT)
|
2000-09-22 17:16:05 +00:00
|
|
|
{
|
2003-05-22 22:13:56 +00:00
|
|
|
VIDEO_MODE_INIT;
|
2002-04-25 11:00:31 +00:00
|
|
|
|
2000-09-22 17:16:05 +00:00
|
|
|
int page = 0;
|
|
|
|
for (;;) {
|
2001-07-11 19:26:14 +00:00
|
|
|
const unsigned first_page = find_next_page_set(page);
|
2001-01-11 16:39:08 +00:00
|
|
|
if (first_page >= mainBuffer.pageCount)
|
2000-09-22 17:16:05 +00:00
|
|
|
break;
|
2001-01-11 16:39:08 +00:00
|
|
|
|
|
|
|
page = find_next_page_clear(first_page);
|
|
|
|
PFLAG_CLEAR_RANGE(first_page, page);
|
2000-10-13 16:47:52 +00:00
|
|
|
|
2000-09-22 17:16:05 +00:00
|
|
|
// Make the dirty pages read-only again
|
|
|
|
const int32 offset = first_page << mainBuffer.pageBits;
|
|
|
|
const uint32 length = (page - first_page) << mainBuffer.pageBits;
|
2001-06-26 22:35:42 +00:00
|
|
|
vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
|
2000-09-22 17:16:05 +00:00
|
|
|
|
|
|
|
// There is at least one line to update
|
|
|
|
const int y1 = mainBuffer.pageInfo[first_page].top;
|
|
|
|
const int y2 = mainBuffer.pageInfo[page - 1].bottom;
|
|
|
|
const int height = y2 - y1 + 1;
|
2004-06-23 14:30:48 +00:00
|
|
|
|
2005-03-28 16:14:25 +00:00
|
|
|
// Update the_host_buffer
|
2004-06-23 14:30:48 +00:00
|
|
|
VIDEO_DRV_LOCK_PIXELS;
|
2005-03-28 16:14:25 +00:00
|
|
|
const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
|
|
|
|
const int dst_bytes_per_row = VIDEO_DRV_ROW_BYTES;
|
|
|
|
int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
|
|
|
|
for (j = y1; j <= y2; j++) {
|
|
|
|
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
|
|
|
|
i1 += src_bytes_per_row;
|
|
|
|
i2 += dst_bytes_per_row;
|
2000-09-22 17:16:05 +00:00
|
|
|
}
|
2004-06-23 14:30:48 +00:00
|
|
|
VIDEO_DRV_UNLOCK_PIXELS;
|
|
|
|
|
|
|
|
#ifdef USE_SDL_VIDEO
|
|
|
|
SDL_UpdateRect(drv->s, 0, y1, VIDEO_MODE_X, height);
|
|
|
|
#else
|
2003-05-22 22:13:56 +00:00
|
|
|
if (VIDEO_DRV_HAVE_SHM)
|
|
|
|
XShmPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height, 0);
|
2000-09-22 17:16:05 +00:00
|
|
|
else
|
2003-05-22 22:13:56 +00:00
|
|
|
XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
|
2004-06-23 14:30:48 +00:00
|
|
|
#endif
|
2000-09-22 17:16:05 +00:00
|
|
|
}
|
2001-01-28 14:05:19 +00:00
|
|
|
mainBuffer.dirty = false;
|
2000-09-22 17:16:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update display for DGA mode and VOSF
|
2001-06-28 22:06:18 +00:00
|
|
|
* (only in Real or Direct Addressing mode)
|
2000-09-22 17:16:05 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#if REAL_ADDRESSING || DIRECT_ADDRESSING
|
2005-05-12 14:39:56 +00:00
|
|
|
static void update_display_dga_vosf(VIDEO_DRV_DGA_INIT)
|
2000-09-22 17:16:05 +00:00
|
|
|
{
|
2003-05-22 22:13:56 +00:00
|
|
|
VIDEO_MODE_INIT;
|
2002-04-25 11:00:31 +00:00
|
|
|
|
2005-05-12 11:09:31 +00:00
|
|
|
// Compute number of bytes per row, take care to virtual screens
|
|
|
|
const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
|
|
|
|
const int dst_bytes_per_row = TrivialBytesPerRow(VIDEO_MODE_X, DepthModeForPixelDepth(VIDEO_DRV_DEPTH));
|
|
|
|
const int scr_bytes_per_row = VIDEO_DRV_ROW_BYTES;
|
|
|
|
assert(dst_bytes_per_row <= scr_bytes_per_row);
|
|
|
|
const int scr_bytes_left = scr_bytes_per_row - dst_bytes_per_row;
|
|
|
|
|
|
|
|
// Full screen update requested?
|
2005-04-02 09:50:17 +00:00
|
|
|
if (mainBuffer.very_dirty) {
|
|
|
|
PFLAG_CLEAR_ALL;
|
|
|
|
vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ);
|
|
|
|
memcpy(the_buffer_copy, the_buffer, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
|
2005-05-12 11:09:31 +00:00
|
|
|
VIDEO_DRV_LOCK_PIXELS;
|
|
|
|
int i1 = 0, i2 = 0;
|
|
|
|
for (int j = 0; j < VIDEO_MODE_Y; j++) {
|
|
|
|
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
|
|
|
|
i1 += src_bytes_per_row;
|
|
|
|
i2 += scr_bytes_per_row;
|
|
|
|
}
|
|
|
|
#ifdef USE_SDL_VIDEO
|
|
|
|
SDL_UpdateRect(drv->s, 0, 0, VIDEO_MODE_X, VIDEO_MODE_Y);
|
|
|
|
#endif
|
2005-04-02 09:50:17 +00:00
|
|
|
VIDEO_DRV_UNLOCK_PIXELS;
|
|
|
|
return;
|
|
|
|
}
|
2005-03-28 16:14:25 +00:00
|
|
|
|
2005-05-12 11:09:31 +00:00
|
|
|
// Setup partial blitter (use 64-pixel wide chunks)
|
|
|
|
const int n_pixels = 64;
|
|
|
|
const int n_chunks = VIDEO_MODE_X / n_pixels;
|
2005-05-12 16:55:16 +00:00
|
|
|
const int n_pixels_left = VIDEO_MODE_X - (n_chunks * n_pixels);
|
2005-05-12 11:09:31 +00:00
|
|
|
const int src_chunk_size = src_bytes_per_row / n_chunks;
|
|
|
|
const int dst_chunk_size = dst_bytes_per_row / n_chunks;
|
|
|
|
const int src_chunk_size_left = src_bytes_per_row - (n_chunks * src_chunk_size);
|
|
|
|
const int dst_chunk_size_left = dst_bytes_per_row - (n_chunks * dst_chunk_size);
|
|
|
|
|
2005-05-12 14:39:56 +00:00
|
|
|
int page = 0, last_scanline = -1;
|
2000-09-22 17:16:05 +00:00
|
|
|
for (;;) {
|
2001-07-11 19:26:14 +00:00
|
|
|
const unsigned first_page = find_next_page_set(page);
|
2001-01-11 16:39:08 +00:00
|
|
|
if (first_page >= mainBuffer.pageCount)
|
2000-09-22 17:16:05 +00:00
|
|
|
break;
|
2001-01-11 16:39:08 +00:00
|
|
|
|
|
|
|
page = find_next_page_clear(first_page);
|
|
|
|
PFLAG_CLEAR_RANGE(first_page, page);
|
|
|
|
|
2000-09-22 17:16:05 +00:00
|
|
|
// Make the dirty pages read-only again
|
|
|
|
const int32 offset = first_page << mainBuffer.pageBits;
|
|
|
|
const uint32 length = (page - first_page) << mainBuffer.pageBits;
|
2001-06-26 22:35:42 +00:00
|
|
|
vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
|
2005-03-28 16:14:25 +00:00
|
|
|
|
2005-05-12 14:39:56 +00:00
|
|
|
// Optimized for scanlines, don't process overlapping lines again
|
|
|
|
int y1 = mainBuffer.pageInfo[first_page].top;
|
|
|
|
int y2 = mainBuffer.pageInfo[page - 1].bottom;
|
|
|
|
if (y1 <= last_scanline && ++y1 >= VIDEO_MODE_Y)
|
|
|
|
continue;
|
|
|
|
if (y2 <= last_scanline && ++y2 >= VIDEO_MODE_Y)
|
|
|
|
continue;
|
|
|
|
last_scanline = y2;
|
|
|
|
|
|
|
|
// Update the_host_buffer and copy of the_buffer, one line at a time
|
2005-04-02 09:50:17 +00:00
|
|
|
int i1 = y1 * src_bytes_per_row;
|
2005-05-12 11:09:31 +00:00
|
|
|
int i2 = y1 * scr_bytes_per_row;
|
2005-05-12 16:55:16 +00:00
|
|
|
#ifdef USE_SDL_VIDEO
|
|
|
|
int bbi = 0;
|
|
|
|
SDL_Rect bb[3] = {
|
|
|
|
{ VIDEO_MODE_X, y1, 0, 0 },
|
|
|
|
{ VIDEO_MODE_X, -1, 0, 0 },
|
|
|
|
{ VIDEO_MODE_X, -1, 0, 0 }
|
|
|
|
};
|
|
|
|
#endif
|
2005-04-02 09:50:17 +00:00
|
|
|
VIDEO_DRV_LOCK_PIXELS;
|
|
|
|
for (int j = y1; j <= y2; j++) {
|
|
|
|
for (int i = 0; i < n_chunks; i++) {
|
|
|
|
if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size) != 0) {
|
|
|
|
memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size);
|
|
|
|
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size);
|
2005-05-12 11:09:31 +00:00
|
|
|
#ifdef USE_SDL_VIDEO
|
2005-05-12 16:55:16 +00:00
|
|
|
const int x = i * n_pixels;
|
|
|
|
if (x < bb[bbi].x) {
|
|
|
|
if (bb[bbi].w)
|
|
|
|
bb[bbi].w += bb[bbi].x - x;
|
|
|
|
else
|
|
|
|
bb[bbi].w = n_pixels;
|
|
|
|
bb[bbi].x = x;
|
|
|
|
}
|
|
|
|
else if (x >= bb[bbi].x + bb[bbi].w)
|
|
|
|
bb[bbi].w = x + n_pixels - bb[bbi].x;
|
2005-05-12 11:09:31 +00:00
|
|
|
#endif
|
2005-04-02 09:50:17 +00:00
|
|
|
}
|
|
|
|
i1 += src_chunk_size;
|
|
|
|
i2 += dst_chunk_size;
|
|
|
|
}
|
|
|
|
if (src_chunk_size_left && dst_chunk_size_left) {
|
|
|
|
if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left) != 0) {
|
|
|
|
memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left);
|
|
|
|
Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size_left);
|
|
|
|
}
|
|
|
|
i1 += src_chunk_size_left;
|
|
|
|
i2 += dst_chunk_size_left;
|
2005-05-12 16:55:16 +00:00
|
|
|
#ifdef USE_SDL_VIDEO
|
|
|
|
const int x = n_chunks * n_pixels;
|
|
|
|
if (x < bb[bbi].x) {
|
|
|
|
if (bb[bbi].w)
|
|
|
|
bb[bbi].w += bb[bbi].x - x;
|
|
|
|
else
|
|
|
|
bb[bbi].w = n_pixels_left;
|
|
|
|
bb[bbi].x = x;
|
|
|
|
}
|
|
|
|
else if (x >= bb[bbi].x + bb[bbi].w)
|
|
|
|
bb[bbi].w = x + n_pixels_left - bb[bbi].x;
|
|
|
|
#endif
|
2005-04-02 09:50:17 +00:00
|
|
|
}
|
2005-05-12 11:09:31 +00:00
|
|
|
i2 += scr_bytes_left;
|
2005-05-12 16:55:16 +00:00
|
|
|
#ifdef USE_SDL_VIDEO
|
|
|
|
bb[bbi].h++;
|
|
|
|
if (bb[bbi].w && (j == y1 || j == y2 - 1 || j == y2)) {
|
|
|
|
bbi++;
|
|
|
|
assert(bbi <= 3);
|
|
|
|
if (j != y2)
|
|
|
|
bb[bbi].y = j + 1;
|
|
|
|
}
|
|
|
|
#endif
|
2005-04-02 09:50:17 +00:00
|
|
|
}
|
2005-05-12 16:55:16 +00:00
|
|
|
#ifdef USE_SDL_VIDEO
|
|
|
|
SDL_UpdateRects(drv->s, bbi, bb);
|
|
|
|
#endif
|
2005-04-02 09:50:17 +00:00
|
|
|
VIDEO_DRV_UNLOCK_PIXELS;
|
2000-09-22 17:16:05 +00:00
|
|
|
}
|
2001-01-28 14:05:19 +00:00
|
|
|
mainBuffer.dirty = false;
|
2000-09-22 17:16:05 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif /* ENABLE_VOSF */
|
|
|
|
|
|
|
|
#endif /* VIDEO_VOSF_H */
|