ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.38
Committed: 2004-06-23T14:30:48Z (20 years, 2 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.37: +52 -28 lines
Log Message:
Initial SDL/video support. Fix VOSF code could lead to a crash on run-time
resolution/depth switching. Rearrange blitter lookup code, aka make it cleaner.

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3     *
4 cebix 1.37 * Basilisk II (C) 1997-2004 Christian Bauer
5 gbeauche 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #ifndef VIDEO_VOSF_H
22     #define VIDEO_VOSF_H
23    
24 gbeauche 1.34 // Note: this file must be #include'd only in video_x.cpp
25 gbeauche 1.1 #ifdef ENABLE_VOSF
26    
27 cebix 1.19 #include <fcntl.h>
28     #include <sys/mman.h>
29     #include "sigsegv.h"
30     #include "vm_alloc.h"
31    
32 gbeauche 1.33 // Glue for SheepShaver and BasiliskII
33     #if POWERPC_ROM
34 gbeauche 1.38 enum {
35     VIDEO_DEPTH_1BIT = APPLE_1_BIT,
36     VIDEO_DEPTH_2BIT = APPLE_2_BIT,
37     VIDEO_DEPTH_4BIT = APPLE_4_BIT,
38     VIDEO_DEPTH_8BIT = APPLE_8_BIT,
39     VIDEO_DEPTH_16BIT = APPLE_16_BIT,
40     VIDEO_DEPTH_32BIT = APPLE_32_BIT
41     };
42     #else
43     enum {
44     VIDEO_DEPTH_1BIT = VDEPTH_1BIT,
45     VIDEO_DEPTH_2BIT = VDEPTH_2BIT,
46     VIDEO_DEPTH_4BIT = VDEPTH_4BIT,
47     VIDEO_DEPTH_8BIT = VDEPTH_8BIT,
48     VIDEO_DEPTH_16BIT = VDEPTH_16BIT,
49     VIDEO_DEPTH_32BIT = VDEPTH_32BIT
50     };
51     #endif
52     #ifdef USE_SDL_VIDEO
53     #define MONITOR_INIT SDL_monitor_desc &monitor
54     #define VIDEO_DRV_INIT driver_window *drv
55     #define VIDEO_DRV_ROW_BYTES drv->s->pitch
56     #define VIDEO_DRV_LOCK_PIXELS if (SDL_MUSTLOCK(drv->s)) SDL_LockSurface(drv->s)
57     #define VIDEO_DRV_UNLOCK_PIXELS if (SDL_MUSTLOCK(drv->s)) SDL_UnlockSurface(drv->s)
58     #define VIDEO_MODE_INIT video_mode const & mode = drv->mode
59     #define VIDEO_MODE_ROW_BYTES mode.bytes_per_row
60     #define VIDEO_MODE_X mode.x
61     #define VIDEO_MODE_Y mode.y
62     #define VIDEO_MODE_DEPTH (int)mode.depth
63     #else
64     #if POWERPC_ROM
65     #define MONITOR_INIT /* nothing */
66 gbeauche 1.33 #define VIDEO_DRV_INIT /* nothing */
67     #define VIDEO_DRV_WINDOW the_win
68     #define VIDEO_DRV_GC the_gc
69     #define VIDEO_DRV_IMAGE img
70     #define VIDEO_DRV_HAVE_SHM have_shm
71     #define VIDEO_MODE_INIT VideoInfo const & mode = VModes[cur_mode]
72     #define VIDEO_MODE_ROW_BYTES mode.viRowBytes
73     #define VIDEO_MODE_X mode.viXsize
74     #define VIDEO_MODE_Y mode.viYsize
75     #define VIDEO_MODE_DEPTH mode.viAppleMode
76     #else
77 gbeauche 1.38 #define MONITOR_INIT X11_monitor_desc &monitor
78 gbeauche 1.33 #define VIDEO_DRV_INIT driver_window *drv
79     #define VIDEO_DRV_WINDOW drv->w
80     #define VIDEO_DRV_GC drv->gc
81     #define VIDEO_DRV_IMAGE drv->img
82     #define VIDEO_DRV_HAVE_SHM drv->have_shm
83 gbeauche 1.38 #define VIDEO_MODE_INIT video_mode const & mode = drv->mode
84 gbeauche 1.33 #define VIDEO_MODE_ROW_BYTES mode.bytes_per_row
85     #define VIDEO_MODE_X mode.x
86     #define VIDEO_MODE_Y mode.y
87     #define VIDEO_MODE_DEPTH (int)mode.depth
88 gbeauche 1.38 #endif
89     #define VIDEO_DRV_LOCK_PIXELS /* nothing */
90     #define VIDEO_DRV_UNLOCK_PIXELS /* nothing */
91     #define VIDEO_DRV_ROW_BYTES VIDEO_DRV_IMAGE->bytes_per_line
92 gbeauche 1.33 #endif
93    
94 cebix 1.19 // Variables for Video on SEGV support
95 gbeauche 1.20 static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
96 cebix 1.19
97     struct ScreenPageInfo {
98     int top, bottom; // Mapping between this virtual page and Mac scanlines
99     };
100    
101     struct ScreenInfo {
102     uintptr memStart; // Start address aligned to page boundary
103     uint32 memLength; // Length of the memory addressed by the screen pages
104    
105 gbeauche 1.27 uintptr pageSize; // Size of a page
106 cebix 1.19 int pageBits; // Shift count to get the page number
107     uint32 pageCount; // Number of pages allocated to the screen
108    
109     bool dirty; // Flag: set if the frame buffer was touched
110     char * dirtyPages; // Table of flags set if page was altered
111     ScreenPageInfo * pageInfo; // Table of mappings page -> Mac scanlines
112     };
113    
114     static ScreenInfo mainBuffer;
115    
116     #define PFLAG_SET_VALUE 0x00
117     #define PFLAG_CLEAR_VALUE 0x01
118     #define PFLAG_SET_VALUE_4 0x00000000
119     #define PFLAG_CLEAR_VALUE_4 0x01010101
120     #define PFLAG_SET(page) mainBuffer.dirtyPages[page] = PFLAG_SET_VALUE
121     #define PFLAG_CLEAR(page) mainBuffer.dirtyPages[page] = PFLAG_CLEAR_VALUE
122     #define PFLAG_ISSET(page) (mainBuffer.dirtyPages[page] == PFLAG_SET_VALUE)
123     #define PFLAG_ISCLEAR(page) (mainBuffer.dirtyPages[page] != PFLAG_SET_VALUE)
124    
125     #ifdef UNALIGNED_PROFITABLE
126     # define PFLAG_ISSET_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_SET_VALUE_4)
127     # define PFLAG_ISCLEAR_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_CLEAR_VALUE_4)
128     #else
129     # define PFLAG_ISSET_4(page) \
130     PFLAG_ISSET(page ) && PFLAG_ISSET(page+1) \
131     && PFLAG_ISSET(page+2) && PFLAG_ISSET(page+3)
132     # define PFLAG_ISCLEAR_4(page) \
133     PFLAG_ISCLEAR(page ) && PFLAG_ISCLEAR(page+1) \
134     && PFLAG_ISCLEAR(page+2) && PFLAG_ISCLEAR(page+3)
135     #endif
136    
137     // Set the selected page range [ first_page, last_page [ into the SET state
138     #define PFLAG_SET_RANGE(first_page, last_page) \
139     memset(mainBuffer.dirtyPages + (first_page), PFLAG_SET_VALUE, \
140     (last_page) - (first_page))
141    
142     // Set the selected page range [ first_page, last_page [ into the CLEAR state
143     #define PFLAG_CLEAR_RANGE(first_page, last_page) \
144     memset(mainBuffer.dirtyPages + (first_page), PFLAG_CLEAR_VALUE, \
145     (last_page) - (first_page))
146    
147     #define PFLAG_SET_ALL do { \
148     PFLAG_SET_RANGE(0, mainBuffer.pageCount); \
149     mainBuffer.dirty = true; \
150     } while (0)
151    
152     #define PFLAG_CLEAR_ALL do { \
153     PFLAG_CLEAR_RANGE(0, mainBuffer.pageCount); \
154     mainBuffer.dirty = false; \
155     } while (0)
156    
157     // Set the following macro definition to 1 if your system
158     // provides a really fast strchr() implementation
159     //#define HAVE_FAST_STRCHR 0
160    
161     static inline int find_next_page_set(int page)
162     {
163     #if HAVE_FAST_STRCHR
164     char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_SET_VALUE);
165     return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
166     #else
167     while (PFLAG_ISCLEAR_4(page))
168     page += 4;
169     while (PFLAG_ISCLEAR(page))
170     page++;
171     return page;
172     #endif
173     }
174    
175     static inline int find_next_page_clear(int page)
176     {
177     #if HAVE_FAST_STRCHR
178     char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_CLEAR_VALUE);
179     return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
180     #else
181     while (PFLAG_ISSET_4(page))
182     page += 4;
183     while (PFLAG_ISSET(page))
184     page++;
185     return page;
186     #endif
187     }
188    
189 gbeauche 1.36 #ifdef HAVE_SPINLOCKS
190     static spinlock_t vosf_lock = SPIN_LOCK_UNLOCKED; // Mutex to protect frame buffer (dirtyPages in fact)
191     #define LOCK_VOSF spin_lock(&vosf_lock)
192     #define UNLOCK_VOSF spin_unlock(&vosf_lock)
193     #elif defined(HAVE_PTHREADS)
194 cebix 1.19 static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
195     #define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
196     #define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
197     #else
198     #define LOCK_VOSF
199     #define UNLOCK_VOSF
200     #endif
201    
202     static int log_base_2(uint32 x)
203     {
204     uint32 mask = 0x80000000;
205     int l = 31;
206     while (l >= 0 && (x & mask) == 0) {
207     mask >>= 1;
208     l--;
209     }
210     return l;
211     }
212    
213 gbeauche 1.20 // Extend size to page boundary
214     static uint32 page_extend(uint32 size)
215     {
216     const uint32 page_size = getpagesize();
217     const uint32 page_mask = page_size - 1;
218     return (size + page_mask) & ~page_mask;
219     }
220    
221 cebix 1.19
222     /*
223 gbeauche 1.27 * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
224 cebix 1.19 */
225    
226 gbeauche 1.38 static bool video_vosf_init(MONITOR_INIT)
227 cebix 1.19 {
228 gbeauche 1.33 VIDEO_MODE_INIT;
229 cebix 1.31
230 gbeauche 1.27 const uintptr page_size = getpagesize();
231     const uintptr page_mask = page_size - 1;
232    
233     // Round up frame buffer base to page boundary
234     mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
235    
236     // The frame buffer size shall already be aligned to page boundary (use page_extend)
237     mainBuffer.memLength = the_buffer_size;
238    
239     mainBuffer.pageSize = page_size;
240     mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
241     mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
242    
243     // The "2" more bytes requested are a safety net to insure the
244     // loops in the update routines will terminate.
245     // See "How can we deal with array overrun conditions ?" hereunder for further details.
246 gbeauche 1.29 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
247     if (mainBuffer.dirtyPages == NULL)
248 gbeauche 1.27 return false;
249 cebix 1.19
250 gbeauche 1.27 PFLAG_CLEAR_ALL;
251     PFLAG_CLEAR(mainBuffer.pageCount);
252     PFLAG_SET(mainBuffer.pageCount+1);
253    
254     // Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
255 gbeauche 1.29 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
256     if (mainBuffer.pageInfo == NULL)
257 gbeauche 1.27 return false;
258    
259     uint32 a = 0;
260 cebix 1.28 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
261 gbeauche 1.33 unsigned y1 = a / VIDEO_MODE_ROW_BYTES;
262     if (y1 >= VIDEO_MODE_Y)
263     y1 = VIDEO_MODE_Y - 1;
264    
265     unsigned y2 = (a + mainBuffer.pageSize) / VIDEO_MODE_ROW_BYTES;
266     if (y2 >= VIDEO_MODE_Y)
267     y2 = VIDEO_MODE_Y - 1;
268 gbeauche 1.27
269     mainBuffer.pageInfo[i].top = y1;
270     mainBuffer.pageInfo[i].bottom = y2;
271    
272     a += mainBuffer.pageSize;
273     if (a > mainBuffer.memLength)
274     a = mainBuffer.memLength;
275     }
276    
277     // We can now write-protect the frame buffer
278     if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
279     return false;
280    
281     // The frame buffer is sane, i.e. there is no write to it yet
282     mainBuffer.dirty = false;
283     return true;
284     }
285 cebix 1.19
286    
287 gbeauche 1.27 /*
288     * Deinitialize VOSF system
289     */
290 cebix 1.19
291 gbeauche 1.27 static void video_vosf_exit(void)
292     {
293 gbeauche 1.29 if (mainBuffer.pageInfo) {
294     free(mainBuffer.pageInfo);
295     mainBuffer.pageInfo = NULL;
296 gbeauche 1.27 }
297 gbeauche 1.29 if (mainBuffer.dirtyPages) {
298     free(mainBuffer.dirtyPages);
299     mainBuffer.dirtyPages = NULL;
300 cebix 1.19 }
301     }
302    
303    
304 gbeauche 1.1 /*
305 gbeauche 1.20 * Screen fault handler
306 gbeauche 1.1 */
307    
308 gbeauche 1.33 bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
309 gbeauche 1.1 {
310 gbeauche 1.16 const uintptr addr = (uintptr)fault_address;
311    
312 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
313 gbeauche 1.20 * now so that the data could actually be written to. It will be made
314 gbeauche 1.11 * read-only back in one of the screen update_*() functions.
315     */
316 gbeauche 1.27 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
317     const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
318 gbeauche 1.11 LOCK_VOSF;
319     PFLAG_SET(page);
320 gbeauche 1.27 vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
321 gbeauche 1.13 mainBuffer.dirty = true;
322 gbeauche 1.11 UNLOCK_VOSF;
323 gbeauche 1.16 return true;
324 gbeauche 1.1 }
325    
326 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
327 gbeauche 1.16 return false;
328 gbeauche 1.1 }
329    
330 gbeauche 1.20
331 gbeauche 1.1 /*
332     * Update display for Windowed mode and VOSF
333     */
334    
335 gbeauche 1.12 /* How can we deal with array overrun conditions ?
336    
337     The state of the framebuffer pages that have been touched are maintained
338     in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
339    
340     Terminology
341    
342     "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
343     "CLEAR Page Guard" refers to the page following the Last Page but is always
344     in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
345     Page Guard but is always in the SET state.
346    
347     Rough process
348    
349 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
350 gbeauche 1.12 screen. This job consists in finding the first_page that was touched.
351     i.e. find the next page that is SET. Then, finding how many pages were
352     touched starting from first_page. i.e. find the next page that is CLEAR.
353    
354 gbeauche 1.13 There are two cases to check:
355 gbeauche 1.12
356     - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
357     but it is beyond the valid pageCount value. Therefore, we exit from the
358     update routine.
359    
360     - Last Page is SET: first_page equals (pageCount - 1) and
361     find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
362     page to the screen. On the next iteration, page equals pageCount and
363     find_next_page_set() will reach the SET Page Guard. We still safely exit
364     from the update routine because the SET Page Guard position is greater
365     than pageCount.
366     */
367    
368 gbeauche 1.33 static inline void update_display_window_vosf(VIDEO_DRV_INIT)
369 gbeauche 1.1 {
370 gbeauche 1.33 VIDEO_MODE_INIT;
371 cebix 1.31
372 gbeauche 1.1 int page = 0;
373     for (;;) {
374 cebix 1.28 const unsigned first_page = find_next_page_set(page);
375 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
376 gbeauche 1.1 break;
377 gbeauche 1.11
378     page = find_next_page_clear(first_page);
379     PFLAG_CLEAR_RANGE(first_page, page);
380 cebix 1.7
381 gbeauche 1.1 // Make the dirty pages read-only again
382     const int32 offset = first_page << mainBuffer.pageBits;
383     const uint32 length = (page - first_page) << mainBuffer.pageBits;
384 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
385 gbeauche 1.1
386     // There is at least one line to update
387     const int y1 = mainBuffer.pageInfo[first_page].top;
388     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
389     const int height = y2 - y1 + 1;
390 gbeauche 1.38
391     VIDEO_DRV_LOCK_PIXELS;
392    
393 gbeauche 1.33 if (VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) {
394 cebix 1.6
395     // Update the_host_buffer and copy of the_buffer
396 gbeauche 1.33 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
397 gbeauche 1.38 const int dst_bytes_per_row = VIDEO_DRV_ROW_BYTES;
398 gbeauche 1.33 const int pixels_per_byte = VIDEO_MODE_X / src_bytes_per_row;
399 cebix 1.24 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
400 cebix 1.6 for (j = y1; j <= y2; j++) {
401 gbeauche 1.33 Screen_blit(the_host_buffer + i2, the_buffer + i1, VIDEO_MODE_X / pixels_per_byte);
402 cebix 1.24 i1 += src_bytes_per_row;
403     i2 += dst_bytes_per_row;
404 cebix 1.6 }
405    
406     } else {
407    
408     // Update the_host_buffer and copy of the_buffer
409 gbeauche 1.33 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
410 gbeauche 1.38 const int dst_bytes_per_row = VIDEO_DRV_ROW_BYTES;
411 gbeauche 1.33 const int bytes_per_pixel = src_bytes_per_row / VIDEO_MODE_X;
412 cebix 1.23 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
413 cebix 1.6 for (j = y1; j <= y2; j++) {
414 gbeauche 1.33 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VIDEO_MODE_X);
415 cebix 1.23 i1 += src_bytes_per_row;
416     i2 += dst_bytes_per_row;
417 cebix 1.6 }
418 gbeauche 1.1 }
419 cebix 1.15
420 gbeauche 1.38 VIDEO_DRV_UNLOCK_PIXELS;
421    
422     #ifdef USE_SDL_VIDEO
423     SDL_UpdateRect(drv->s, 0, y1, VIDEO_MODE_X, height);
424     #else
425 gbeauche 1.33 if (VIDEO_DRV_HAVE_SHM)
426     XShmPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height, 0);
427 gbeauche 1.1 else
428 gbeauche 1.33 XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
429 gbeauche 1.38 #endif
430 gbeauche 1.1 }
431 gbeauche 1.13 mainBuffer.dirty = false;
432 gbeauche 1.1 }
433    
434    
435     /*
436     * Update display for DGA mode and VOSF
437 gbeauche 1.20 * (only in Real or Direct Addressing mode)
438 gbeauche 1.1 */
439    
440     #if REAL_ADDRESSING || DIRECT_ADDRESSING
441     static inline void update_display_dga_vosf(void)
442     {
443 gbeauche 1.33 VIDEO_MODE_INIT;
444 cebix 1.31
445 gbeauche 1.1 int page = 0;
446     for (;;) {
447 cebix 1.28 const unsigned first_page = find_next_page_set(page);
448 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
449 gbeauche 1.1 break;
450 gbeauche 1.11
451     page = find_next_page_clear(first_page);
452     PFLAG_CLEAR_RANGE(first_page, page);
453    
454 gbeauche 1.1 // Make the dirty pages read-only again
455     const int32 offset = first_page << mainBuffer.pageBits;
456     const uint32 length = (page - first_page) << mainBuffer.pageBits;
457 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
458 gbeauche 1.1
459     // I am sure that y2 >= y1 and depth != 1
460     const int y1 = mainBuffer.pageInfo[first_page].top;
461     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
462    
463 gbeauche 1.33 const int bytes_per_row = VIDEO_MODE_ROW_BYTES;
464     const int bytes_per_pixel = VIDEO_MODE_ROW_BYTES / VIDEO_MODE_X;
465 gbeauche 1.1 int i, j;
466    
467     // Check for first column from left and first column
468     // from right that have changed
469 gbeauche 1.33 int x1 = VIDEO_MODE_X * bytes_per_pixel - 1;
470 gbeauche 1.1 for (j = y1; j <= y2; j++) {
471     uint8 * const p1 = &the_buffer[j * bytes_per_row];
472     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
473     for (i = 0; i < x1; i++) {
474     if (p1[i] != p2[i]) {
475     x1 = i;
476     break;
477     }
478     }
479     }
480     x1 /= bytes_per_pixel;
481    
482     int x2 = x1 * bytes_per_pixel;
483     for (j = y2; j >= y1; j--) {
484     uint8 * const p1 = &the_buffer[j * bytes_per_row];
485     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
486 gbeauche 1.33 for (i = VIDEO_MODE_X * bytes_per_pixel - 1; i > x2; i--) {
487 gbeauche 1.1 if (p1[i] != p2[i]) {
488     x2 = i;
489     break;
490     }
491     }
492     }
493     x2 /= bytes_per_pixel;
494    
495     // Update the_host_buffer and copy of the_buffer
496     // There should be at least one pixel to copy
497 gbeauche 1.38 VIDEO_DRV_LOCK_PIXELS;
498 gbeauche 1.1 const int width = x2 - x1 + 1;
499     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
500     for (j = y1; j <= y2; j++) {
501 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
502 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
503     i += bytes_per_row;
504     }
505 gbeauche 1.38 VIDEO_DRV_UNLOCK_PIXELS;
506 gbeauche 1.1 }
507 gbeauche 1.13 mainBuffer.dirty = false;
508 gbeauche 1.1 }
509     #endif
510    
511     #endif /* ENABLE_VOSF */
512    
513     #endif /* VIDEO_VOSF_H */