This is raster.c in view mode; [Download] [Up]
/* * raster.c - Common routines for video chip emulation. * * Written by * Ettore Perazzoli (ettore@comm2000.it) * * This file is part of VICE, the Versatile Commodore Emulator. * See README for copyright notice. * * 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. * */ /* This file is ugly, and needs better organization. In particular, it would be nice to remove all the machine-specific hacks. And it would be nice to put it into a standalone module, although this would probably affect performance. */ #define _RASTER_C #include <signal.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <stdarg.h> #include <string.h> #include "vice.h" #include "vmachine.h" #include "resources.h" #ifndef _RASTER_H #include "raster.h" #endif #include "vsync.h" #undef MIN #undef MAX #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) /* XShmPutImage is a bit more picky about its parameters than XPutImage. This is useful to debug XLib failures caused by wrong calls. */ /* #define RASTER_DEBUG_PUTIMAGE_CALLS */ inline static void refresh_all(void); /* ------------------------------------------------------------------------- */ inline static void apply_change(struct changes *c, int idx) { switch (c->actions[idx].type) { case CHANGE_BYTE: *(c->actions[idx].value.integer.oldp) = c->actions[idx].value.integer.new; break; case CHANGE_PTR: default: *(c->actions[idx].value.ptr.oldp) = c->actions[idx].value.ptr.new; } } inline static void apply_all_changes(struct changes *c) { int i; for (i = 0; i < c->count; i++) apply_change(c, i); c->count = 0; } inline static void add_int_change(struct changes *c, int where, int *ptr, int new_value) { int count = c->count++; c->actions[count].where = where; c->actions[count].type = CHANGE_BYTE; c->actions[count].value.integer.oldp = ptr; c->actions[count].value.integer.new = new_value; } inline static void add_ptr_change(struct changes *c, int where, void **ptr, void *new_value) { int count = c->count++; c->actions[count].where = where; c->actions[count].type = CHANGE_PTR; c->actions[count].value.ptr.oldp = ptr; c->actions[count].value.ptr.new = new_value; } inline static void add_int_change_next_line(int *ptr, int new_value) { if (skip_next_frame) *ptr = new_value; else add_int_change(&next_line_changes, 0, ptr, new_value); } inline static void add_ptr_change_next_line(void **ptr, void *new_value) { if (skip_next_frame) *ptr = new_value; else add_ptr_change(&next_line_changes, 0, ptr, new_value); } inline static void add_int_change_foreground(int char_x, int *ptr, int new_value) { if (skip_next_frame || char_x <= 0) { *ptr = new_value; } else if (char_x < SCREEN_TEXTCOLS) { add_int_change(&foreground_changes, char_x, ptr, new_value); have_changes_on_this_line = 1; } else { add_int_change_next_line(ptr, new_value); } } inline static void add_ptr_change_foreground(int char_x, void **ptr, void *new_value) { if (skip_next_frame || char_x <= 0) { *ptr = new_value; } else if (char_x < SCREEN_TEXTCOLS) { add_ptr_change(&foreground_changes, char_x, ptr, new_value); have_changes_on_this_line = 1; } else { add_ptr_change_next_line(ptr, new_value); } } inline static void add_int_change_background(int raster_x, int *ptr, int new_value) { if (skip_next_frame || raster_x <= 0) { *ptr = new_value; } else if (raster_x < SCREEN_WIDTH) { add_int_change(&background_changes, raster_x, ptr, new_value); have_changes_on_this_line = 1; } else { add_int_change_next_line(ptr, new_value); } } inline static void add_ptr_change_background(int raster_x, void **ptr, void *new_value) { if (skip_next_frame || raster_x <= 0) { *ptr = new_value; } else if (raster_x < SCREEN_WIDTH) { add_ptr_change(&background_changes, raster_x, ptr, new_value); have_changes_on_this_line = 1; } else { add_ptr_change_next_line(ptr, new_value); } } inline static void add_int_change_border(int raster_x, int *ptr, int new_value) { if (skip_next_frame || raster_x <= 0) { *ptr = new_value; } else if (raster_x < SCREEN_WIDTH) { add_int_change(&border_changes, raster_x, ptr, new_value); have_changes_on_this_line = 1; } else { add_int_change_next_line(ptr, new_value); } } /* ------------------------------------------------------------------------- */ #define vid_memcpy(dst, src, cnt) memcpy(dst, src, cnt * sizeof(PIXEL)) #if X_DISPLAY_DEPTH > 8 inline static void vid_memset(PIXEL *dst, PIXEL c, int cnt) { int i; for (i = 0; i < cnt; i++) dst[i] = c; } /* This is used to paint borders and blank lines. */ #define DRAW_BLANK(p, start, end, pixel_width) \ do { \ PIXEL *dst; \ int i; \ \ dst = (PIXEL *)p + start * pixel_width; \ for(i = 0;i < ((end) - (start) + 1) * pixel_width; i++) \ *(dst++) = PIXEL((int)border_color); \ } while(0) #else /* 8 bit depth */ #define vid_memset(dst, c, cnt) memset(dst,c,cnt) /* This is used to paint borders and blank lines. */ #define DRAW_BLANK(p, start, end, pixel_width) \ vid_memset(((BYTE *)(p) + (start) * pixel_width), \ PIXEL(border_color), \ ((end) - (start) + 1) * pixel_width) #endif /* ------------------------------------------------------------------------- */ /* Initialization. */ static void reset_raster(void) { int i; frame_buffer_ptr = (FRAME_BUFFER_START(frame_buffer) + 2 * SCREEN_MAX_SPRITE_WIDTH); rasterline = 0; oldclk = 0; xsmooth = ysmooth = 0; blank = 0; mem_counter = memptr = 0; #ifdef __VIC_II__ mem_counter_inc = SCREEN_TEXTCOLS; #endif ycounter = 0; asleep = 0; changed_area.is_null = 1; for (i = 0; i < SCREEN_HEIGHT; i++) { #if SCREEN_NUM_SPRITES > 0 int j; for (j = 0; j < SCREEN_NUM_SPRITES; j++) memset(&(cache[i].sprites[j]), 0, sizeof(struct sprite_line_cache)); #endif cache[i].is_dirty = 1; } #if SCREEN_NUM_SPRITES > 0 #if 0 for (i = 0; i < SCREEN_NUM_SPRITES; i++) memset(&(sprites[i]), 0, sizeof(struct sprite)); #endif sprite_data = sprite_data_1; new_sprite_data = sprite_data_2; #endif } static int init_raster(int active, int max_pixel_width, int max_pixel_height) { /* Keep enough space on the left and the right for one sprite of the maximum size. This way we can clip sprites more easily. */ if (frame_buffer_alloc(&frame_buffer, ((SCREEN_WIDTH + 2 * 2 * SCREEN_MAX_SPRITE_WIDTH) * max_pixel_width), (SCREEN_HEIGHT + 1) * max_pixel_height)) return -1; reset_raster(); return 0; } /* Resize the canvas with the specified values and center the screen image on it. The actual size can be different if the parameters are not suitable. */ static void resize(unsigned int width, unsigned int height) { if (width >= SCREEN_WIDTH * pixel_width) { window_x_offset = (width - SCREEN_WIDTH * pixel_width) / 2; window_first_x = 0; } else { window_x_offset = 0; #ifndef SCREEN_BORDERWIDTH_VARIES if (width > SCREEN_XPIX * pixel_width) window_first_x = (SCREEN_BORDERWIDTH - (width / pixel_width - SCREEN_XPIX) / 2); else window_first_x = SCREEN_BORDERWIDTH; #else window_first_x = (SCREEN_WIDTH - width / pixel_width) / 2; #endif } if (height >= SCREEN_HEIGHT * pixel_height) { window_y_offset = (height - SCREEN_HEIGHT * pixel_height) / 2; window_first_line = 0; window_last_line = window_first_line + SCREEN_HEIGHT - 1; } else { window_y_offset = 0; #ifndef SCREEN_BORDERHEIGHT_VARIES if (height > SCREEN_YPIX * pixel_height) window_first_line = (SCREEN_BORDERHEIGHT - (height / pixel_height - SCREEN_YPIX) / 2); else window_first_line = SCREEN_BORDERHEIGHT; #else window_first_line = (SCREEN_HEIGHT - height / pixel_height) / 2; #endif window_last_line = window_first_line + height / pixel_height; } canvas_resize(canvas, width, height); /* Make sure we don't waste space showing unused lines. */ if ((window_first_line < SCREEN_FIRST_DISPLAYED_LINE && window_last_line < SCREEN_LAST_DISPLAYED_LINE) || (window_first_line > SCREEN_FIRST_DISPLAYED_LINE && window_last_line > SCREEN_LAST_DISPLAYED_LINE)) { window_first_line = SCREEN_FIRST_DISPLAYED_LINE; window_last_line = window_first_line + height / pixel_height; } window_width = width; window_height = height; } /* Open the emulation window. */ static int open_output_window(char *win_name, unsigned int width, unsigned int height, palette_t *palette, canvas_redraw_t exposure_handler) { int i; window_width = width * pixel_width; window_height = height * pixel_height; canvas = canvas_create(win_name, &window_width, &window_height, !asleep, exposure_handler, palette, pixel_table); /* Prepare the double and quad pixel tables. */ for (i = 0; i < 0x100; i++) *((PIXEL *)(double_pixel_table + i)) = *((PIXEL *)(double_pixel_table + i) + 1) = pixel_table[i]; for (i = 0; i < 0x100; i++) *((PIXEL2 *)(quad_pixel_table + i)) = *((PIXEL2 *)(quad_pixel_table + i) + 1) = double_pixel_table[i]; if (!canvas) return 1; resize(window_width, window_height); frame_buffer_clear(&frame_buffer, PIXEL(0)); refresh_all(); return 0; } /* ------------------------------------------------------------------------- */ /* set a new palette */ static int set_palette_file_name(resource_value_t v) { int i; /* If called before initialization, just set the resource value. The palette file will be loaded afterwards. */ if (palette == NULL) { string_set(&palette_file_name, (char *) v); return 0; } if (palette_load((char *) v, palette) < 0) { fprintf(stderr, "Couldn't load palette `%s'\n", (char *) v); return -1; } canvas_set_palette(canvas, palette, pixel_table); /* Prepare the double and quad pixel tables. */ for (i = 0; i < 0x100; i++) *((PIXEL *)(double_pixel_table + i)) = *((PIXEL *)(double_pixel_table + i) + 1) = pixel_table[i]; for (i = 0; i < 0x100; i++) *((PIXEL2 *)(quad_pixel_table + i)) = *((PIXEL2 *)(quad_pixel_table + i) + 1) = double_pixel_table[i]; init_drawing_tables(); /* Make sure the pixel tables are recalculated properly. */ video_resize(); string_set(&palette_file_name, (char *) v); return 0; } /* ------------------------------------------------------------------------- */ /* Read length bytes from src and store them in dest, checking for differences between the two arrays. The smallest interval that contains different bytes is returned as [*xs; *xe]. */ inline static int _fill_cache(BYTE *dest, BYTE *src, int length, int srcstep, int *xs, int *xe, int no_check) { if (no_check) { int i; *xs = 0; *xe = length - 1; if (srcstep == 1) memcpy(dest, src, length); else for (i = 0; i < length; i++, src += srcstep) dest[i] = src[0]; return 1; } else { int x = 0, i; for (i = 0; dest[i] == src[0] && i < length; i++, src += srcstep) /* do nothing */ ; if (i < length) { if (*xs > i) *xs = i; for (; i < length; i++, src += srcstep) if (dest[i] != src[0]) { dest[i] = src[0]; x = i; } if (*xe < x) *xe = x; return 1; } else return 0; } } /* Do as _fill_cache(), but split each byte into low and high nibble. These are stored into different destinations. */ inline static int _fill_cache_nibbles(BYTE * desthi, BYTE * destlo, BYTE * src, int length, int srcstep, int *xs, int *xe, int no_check) { if (no_check) { int i; *xs = 0; *xe = length - 1; for (i = 0; i < length; i++, src += srcstep) { desthi[i] = HI_NIBBLE(src[0]); destlo[i] = LO_NIBBLE(src[0]); } return 1; } else { int i, x = 0; BYTE b; for (i = 0; desthi[i] == HI_NIBBLE(src[0]) && destlo[i] == LO_NIBBLE(src[0]) && i < length; i++, src += srcstep) /* do nothing */ ; if (i < length) { if (*xs > i) *xs = i; for (; i < length; i++, src += srcstep) if (desthi[i] != (b = HI_NIBBLE(src[0]))) { desthi[i] = b; x = i; } else if (destlo[i] != (b = LO_NIBBLE(src[0]))) { destlo[i] = b; x = i; } if (*xe < x) *xe = x; return 1; } else return 0; } } /* This function is used for text modes. It checks for differences in the character memory too. */ inline static int _fill_cache_text(BYTE * dest, BYTE * src, BYTE * charmem, int length, int l, int *xs, int *xe, int no_check) { if (no_check) { int i; *xs = 0; *xe = length - 1; for (i = 0; i < length; i++, src++) dest[i] = GET_CHAR_DATA(charmem, src[0], l); return 1; } else { BYTE b; int i; for (i = 0; dest[i] == GET_CHAR_DATA(charmem, src[0], l) && i < length; i++, src++) /* do nothing */ ; if (i < length) { *xs = *xe = i; for (; i < length; i++, src++) if (dest[i] != (b = GET_CHAR_DATA(charmem, src[0], l))) { dest[i] = b; *xe = i; } return 1; } else return 0; } } #if SCREEN_NUM_SPRITES > 0 /* Fill the sprite cache with the new sprite data. [*xs; *xe] is the changed interval (in pixels). */ inline static int _fill_sprite_cache(struct line_cache *ll, int *xs, int *xe) { int rr = 0, i, r, sxe, sxs, sxe1, sxs1, n = 0, msk; struct sprite spr; struct sprite_line_cache *l = ll->sprites; struct sprite_line_cache *sprl; int xs_return = SCREEN_WIDTH, xe_return = 0; ll->numsprites = SCREEN_NUM_SPRITES; ll->sprmask = 0; for (msk = 1, i = 0; i < SCREEN_NUM_SPRITES; i++, msk <<= 1) { spr = sprites[i]; sprl = l + i; r = 0; if (dma_msk & msk) { DWORD data = sprite_data[i]; ll->sprmask |= msk; sxe = spr.x + (spr.x_expanded ? 48 : 24); sxs = spr.x; if (spr.x != sprl->x) { if (sprl->visible) { sxe1 = sprl->x + (sprl->x_expanded ? 48 : 24); sxs1 = sprl->x; n++; if (sxs1 < sxs) sxs = sxs1; if (sxe1 > sxe) sxe = sxe1; } sprl->x = spr.x; r = 1; } if (!sprl->visible) { sprl->visible = 1; r = 1; } if (spr.x_expanded != sprl->x_expanded) { sprl->x_expanded = spr.x_expanded; r = 1; } if (spr.multicolor != sprl->multicolor) { sprl->multicolor = spr.multicolor; r = 1; } if (mc_sprite_color_1 != sprl->c1) { sprl->c1 = mc_sprite_color_1; r = 1; } if (mc_sprite_color_2 != sprl->c2) { sprl->c2 = mc_sprite_color_2; r = 1; } if (spr.color != sprl->c3) { sprl->c3 = spr.color; r = 1; } if (spr.in_background != sprl->in_background) { sprl->in_background = spr.in_background; r = 1; } if (sprl->data != data) { sprl->data = data; r = 1; } if (r) { xs_return = MIN(xs_return, sxs); xe_return = MAX(xe_return, sxe); rr = 1; } } else if (sprl->visible) { sprl->visible = 0; sxe = sprl->x + (sprl->x_expanded ? 24 : 48); xs_return = MIN(xs_return, sprl->x); xe_return = MAX(xe_return, sxe); rr = 1; } } if (xe_return >= SCREEN_WIDTH) *xe = SCREEN_WIDTH - 1; else *xe = xe_return; *xs = xs_return; return rr; } #endif /* SCREEN_NUM_SPRITES > 0 */ /* ------------------------------------------------------------------------- */ /* Increase the size of the rectangle to be refreshed so that it also includes the interval [xs; xe] of line y. The lines must be added in order: from top to bottom. */ inline static void add_line(int y, int xs, int xe) { #ifdef RASTER_DEBUG_PUTIMAGE_CALLS printf("add_line(): y = %d, xs = %d, xe = %d\n", y, xs, xe); #endif if (changed_area.is_null) { changed_area.ys = changed_area.ye = y; changed_area.xs = xs; changed_area.xe = xe; changed_area.is_null = 0; } else { changed_area.xs = MIN(xs, changed_area.xs); changed_area.xe = MAX(xe, changed_area.xe); changed_area.ye = y; } #ifdef RASTER_DEBUG_PUTIMAGE_CALLS printf("add_line(): current changed_area has " "ys = %d, ye = %d, xs = %d, xe = %d\n", changed_area.ys, changed_area.ye, changed_area.xs, changed_area.xe); #endif } /* Refresh the changed rectangle. */ inline static void refresh_changed(void) { int x, y, xx, yy; int w, h; if (changed_area.is_null) return; x = changed_area.xs; y = changed_area.ys; xx = changed_area.xs - window_first_x; yy = changed_area.ys - window_first_line; w = changed_area.xe - changed_area.xs + 1; h = changed_area.ye - changed_area.ys + 1; if (xx < 0) { x -= xx; w += xx; xx = 0; } if (yy < 0) { y -= yy; h += yy; yy = 0; } x *= pixel_width; xx *= pixel_width; w *= pixel_width; y *= pixel_height; yy *= pixel_height; h *= pixel_height; #if defined (RASTER_DEBUG_PUTIMAGE_CALLS) printf("Refresh %d %d %d %d %d %d \n", x, y, xx, yy, w, h); #endif x += 2 * SCREEN_MAX_SPRITE_WIDTH; canvas_refresh(canvas, frame_buffer, x, y, xx + window_x_offset, yy + window_y_offset, w, h); changed_area.is_null = 1; } /* Unconditionally refresh the whole screen. */ inline static void refresh_all(void) { #if defined (RASTER_DEBUG_PUTIMAGE_CALLS) printf("Global refresh %d %d %d %d %d %d\n", window_first_x * pixel_width + 2 * SCREEN_MAX_SPRITE_WIDTH, window_first_line * pixel_height, window_x_offset, window_y_offset, MIN(window_width, SCREEN_WIDTH * pixel_width), MIN(window_height, SCREEN_HEIGHT * pixel_height)); #endif canvas_refresh(canvas, frame_buffer, window_first_x * pixel_width + 2 * SCREEN_MAX_SPRITE_WIDTH, window_first_line * pixel_height, window_x_offset, window_y_offset, MIN(window_width, SCREEN_WIDTH * pixel_width), MIN(window_height, SCREEN_HEIGHT * pixel_height)); } /* ------------------------------------------------------------------------- */ inline static void handle_blank_line(void) { /* Changes... Should/could be handled better. */ if (have_changes_on_this_line) { int i, xs; apply_all_changes(&background_changes); apply_all_changes(&foreground_changes); for (xs = i = 0; i < border_changes.count; i++) { int xe = border_changes.actions[i].where; if (xs < xe) { DRAW_BLANK(frame_buffer_ptr, xs, xe, pixel_width); xs = xe; } apply_change(&border_changes, i); } if (xs <= SCREEN_WIDTH - 1) DRAW_BLANK(frame_buffer_ptr, xs, SCREEN_WIDTH - 1, pixel_width); border_changes.count = 0; cache[rasterline].border_color = -1; cache[rasterline].blank = 1; have_changes_on_this_line = 0; add_line(rasterline, 0, SCREEN_WIDTH - 1); if (pixel_height == 2 && double_scan_enabled) vid_memcpy((FRAME_BUFFER_LINE_START(frame_buffer, 2*rasterline + 1) + 2 * SCREEN_MAX_SPRITE_WIDTH), frame_buffer_ptr, pixel_width * SCREEN_WIDTH); } else if (CANVAS_USES_TRIPLE_BUFFERING(canvas) || dont_cache || cache[rasterline].is_dirty || border_color != cache[rasterline].border_color || !cache[rasterline].blank) { /* Even when the actual caching is disabled, redraw blank lines only if it is really necessary to do so. */ cache[rasterline].border_color = border_color; cache[rasterline].blank = 1; cache[rasterline].is_dirty = 0; DRAW_BLANK(frame_buffer_ptr, 0, SCREEN_WIDTH - 1, pixel_width); if (pixel_height == 2 && double_scan_enabled) DRAW_BLANK((FRAME_BUFFER_LINE_START(frame_buffer, 2*rasterline + 1) + 2 * SCREEN_MAX_SPRITE_WIDTH), 0, SCREEN_WIDTH - 1, pixel_width); add_line(rasterline, 0, SCREEN_WIDTH - 1); } #ifdef __VIC_II__ update_sprite_collisions(); #endif } /* Draw the borders. */ inline static void draw_borders(void) { if (!open_left_border) DRAW_BLANK(frame_buffer_ptr, 0, display_xstart - 1, pixel_width); if (!open_right_border) DRAW_BLANK(frame_buffer_ptr, display_xstop, SCREEN_WIDTH - 1, pixel_width); } inline static void handle_visible_line_with_cache(void) { int needs_update = 0; int line; int vm = draw_idle_state ? SCREEN_IDLE_MODE : video_mode; int changed_start = SCREEN_TEXTCOLS, changed_end = -1; struct line_cache *l = &cache[rasterline]; line = rasterline - SCREEN_BORDERHEIGHT - ysmooth - 1; /* Check for "major" changes first. If there is any, just write straight to the cache without any comparisons and redraw the whole line. */ if (l->is_dirty || dont_cache || l->n != line || l->xsmooth != xsmooth || l->video_mode != vm || l->blank || l->ycounter != ycounter || l->border_color != border_color || l->display_xstart != display_xstart || l->display_xstop != display_xstop || (l->open_right_border && !open_right_border) || (l->open_left_border && !open_left_border)) { needs_update = 1; l->n = line; l->video_mode = vm; l->blank = 0; l->xsmooth = xsmooth; l->ycounter = ycounter; l->border_color = border_color; l->display_xstart = display_xstart; l->display_xstop = display_xstop; l->open_right_border = open_right_border; l->open_left_border = open_left_border; #if defined(__VIC_II__) l->overscan_background_color = overscan_background_color; #endif /* defined(__VIC_II__) */ /* Fill the space between the border and the graphics with the background color (necessary if `xsmooth' is != 0). */ vid_memset(frame_buffer_ptr + SCREEN_BORDERWIDTH * pixel_width, PIXEL(overscan_background_color), xsmooth * pixel_width); if (open_left_border) vid_memset(frame_buffer_ptr, PIXEL(overscan_background_color), (SCREEN_BORDERWIDTH + xsmooth) * pixel_width); if (open_right_border) vid_memset((frame_buffer_ptr + ((SCREEN_BORDERWIDTH + SCREEN_XPIX + xsmooth) * pixel_width)), PIXEL(overscan_background_color), (SCREEN_WIDTH - SCREEN_BORDERWIDTH - SCREEN_XPIX - xsmooth) * pixel_width); #if SCREEN_NUM_SPRITES > 0 _fill_sprite_cache(l, &changed_start, &changed_end); #endif video_modes[vm].fill_cache(l, &changed_start, &changed_end, 1); /* [ `changed_start' ; `changed_end' ] now covers the whole line, as we have called fill_cache() with `1' as the last parameter (no check). */ video_modes[vm].draw_line_cached(l, changed_start, changed_end); changed_start = 0; changed_end = SCREEN_WIDTH - 1; draw_borders(); } else { /* There are no `major' changes: try to do some optimization. */ #if SCREEN_NUM_SPRITES > 0 int sprites_need_update; int sprite_changed_start, sprite_changed_end; sprites_need_update = _fill_sprite_cache(l, &sprite_changed_start, &sprite_changed_end); /* If sprites have changed, do not bother trying to reduce the amount of recalculated data, but simply redraw everything. */ needs_update = video_modes[vm].fill_cache(l, &changed_start, &changed_end, sprites_need_update); #if defined(__VIC_II__) /* If the background color changes, we might get the wrong color in the left part of the screen, between the border and the start of the graphics. */ if (ysmooth > 0 && l->overscan_background_color != overscan_background_color) needs_update = 1; #endif /* defined(__VIC_II__) */ if (needs_update) { video_modes[vm].draw_line_cached(l, changed_start, changed_end); /* Fill the space between the border and the graphics with the background color (necessary if xsmooth is > 0). */ vid_memset(frame_buffer_ptr + SCREEN_BORDERWIDTH * pixel_width, PIXEL(overscan_background_color), xsmooth * pixel_width); /* If xsmooth > 0, drawing the graphics might have corrupted part of the border... fix it here. */ if (!open_right_border) DRAW_BLANK(frame_buffer_ptr, SCREEN_BORDERWIDTH + SCREEN_XPIX, SCREEN_BORDERWIDTH + SCREEN_XPIX + 8, pixel_width); /* Calculate the interval in pixel coordinates. */ #if defined(__VIC_II__) if (l->overscan_background_color != overscan_background_color) { if (ysmooth > 0) changed_start = SCREEN_BORDERWIDTH; else changed_start = (SCREEN_BORDERWIDTH + xsmooth + 8 * changed_start); l->overscan_background_color = overscan_background_color; } else { changed_start = (SCREEN_BORDERWIDTH + xsmooth + 8 * changed_start); } #else /* defined(__VIC_II__) */ changed_start = SCREEN_BORDERWIDTH + xsmooth + 8 * changed_start; #endif /* defined(__VIC_II__) */ changed_end = (SCREEN_BORDERWIDTH + xsmooth + 8 * (changed_end + 1) - 1); if (sprites_need_update) { /* FIXME: wrong. */ if (open_left_border) changed_start = 0; if (open_right_border) changed_end = SCREEN_WIDTH - 1; /* Even if we have recalculated the whole line, we will refresh only the part that has actually changed when writing to the window. */ changed_start = MIN(changed_start, sprite_changed_start); changed_end = MAX(changed_end, sprite_changed_end); /* The borders have not changed, so do not repaint them even if there are sprites under them. */ changed_start = MAX(changed_start, display_xstart); changed_end = MIN(changed_end, display_xstop); } } #if SCREEN_NUM_SPRITES > 0 else { /* if (needs_update) */ cl_ss_collmask = l->ss_collmask; cl_sb_collmask = l->sb_collmask; } #endif #else /* no sprites */ needs_update = video_modes[vm].fill_cache(l, &changed_start, &changed_end, 0); if (needs_update) { video_modes[vm].draw_line_cached(l, changed_start, changed_end); /* Convert from character to pixel coordinates. */ changed_start = SCREEN_BORDERWIDTH + xsmooth + 8 * changed_start; changed_end = (SCREEN_BORDERWIDTH + xsmooth + 8 * (changed_end + 1) - 1); } #endif /* no sprites */ draw_borders(); } if (needs_update) { add_line(rasterline, changed_start, changed_end); if (pixel_height == 2 && double_scan_enabled) { vid_memcpy((FRAME_BUFFER_LINE_START(frame_buffer, 2*rasterline + 1) + 2*SCREEN_MAX_SPRITE_WIDTH + changed_start * pixel_width), frame_buffer_ptr + changed_start * pixel_width, (changed_end - changed_start + 1) * pixel_width); } } l->is_dirty = 0; } inline static void handle_visible_line_without_cache() { /* If screen is scrolled to the right, we need to fill with the background color the blank part on the left. */ vid_memset(frame_buffer_ptr + SCREEN_BORDERWIDTH * pixel_width, PIXEL(overscan_background_color), xsmooth * pixel_width); if (open_left_border) vid_memset(frame_buffer_ptr, PIXEL(overscan_background_color), (SCREEN_BORDERWIDTH + xsmooth) * pixel_width); if (open_right_border) vid_memset((frame_buffer_ptr + ((SCREEN_BORDERWIDTH + SCREEN_XPIX + xsmooth) * pixel_width)), PIXEL(overscan_background_color), (SCREEN_WIDTH - SCREEN_BORDERWIDTH - SCREEN_XPIX - xsmooth) * pixel_width); /* Draw the graphics and sprites. */ if (draw_idle_state) video_modes[SCREEN_IDLE_MODE].draw_line(); else video_modes[video_mode].draw_line(); draw_borders(); if (CANVAS_USES_TRIPLE_BUFFERING(canvas) || dont_cache || dma_msk || cache[rasterline].is_dirty || cache[rasterline].blank || cache[rasterline].border_color != border_color || (cache[rasterline].open_right_border != open_right_border) || (cache[rasterline].open_left_border != open_left_border) #if defined(__VIC_II__) || (cache[rasterline].overscan_background_color != overscan_background_color) #endif ) { cache[rasterline].blank = 0; cache[rasterline].is_dirty = 0; cache[rasterline].border_color = border_color; cache[rasterline].open_right_border = open_right_border; cache[rasterline].open_left_border = open_left_border; #if defined(__VIC_II__) cache[rasterline].overscan_background_color = overscan_background_color; #endif add_line(rasterline, 0, SCREEN_WIDTH - 1); } else { /* Still do some minimal caching anyway. */ /* Only update the part between the borders. */ add_line(rasterline, SCREEN_BORDERWIDTH, SCREEN_BORDERWIDTH + SCREEN_XPIX - 1); } if (pixel_height == 2 && double_scan_enabled) vid_memcpy((FRAME_BUFFER_LINE_START(frame_buffer, rasterline*2 + 1) + 2 * SCREEN_MAX_SPRITE_WIDTH), frame_buffer_ptr, SCREEN_WIDTH * pixel_width); } inline static void handle_visible_line_with_changes(void) { int xs, xstop, i; for (xs = i = 0; i < background_changes.count; i++) { int xe = background_changes.actions[i].where; if (xs < xe) { int vm = draw_idle_state ? SCREEN_IDLE_MODE : video_mode; video_modes[vm].draw_background(xs, xe - 1); xs = xe; } apply_change(&background_changes, i); } if (xs <= SCREEN_WIDTH - 1) { int vm = draw_idle_state ? SCREEN_IDLE_MODE : video_mode; video_modes[vm].draw_background(xs, SCREEN_WIDTH - 1); } for (xs = i = 0; i < foreground_changes.count; i++) { int xe = foreground_changes.actions[i].where; if (xs < xe) { int vm = draw_idle_state ? SCREEN_IDLE_MODE : video_mode; video_modes[vm].draw_foreground(xs, xe - 1); xs = xe; } apply_change(&foreground_changes, i); } if (xs <= SCREEN_TEXTCOLS - 1) { int vm = draw_idle_state ? SCREEN_IDLE_MODE : video_mode; video_modes[vm].draw_foreground(xs, SCREEN_TEXTCOLS - 1); } #if SCREEN_NUM_SPRITES > 0 draw_sprites(); #endif /* Draw left border. */ xstop = display_xstart - 1; if (!open_left_border) { for (xs = i = 0; (i < border_changes.count && border_changes.actions[i].where <= xstop); i++) { int xe = border_changes.actions[i].where; if (xs < xe) { DRAW_BLANK(frame_buffer_ptr, xs, xe - 1, pixel_width); xs = xe; } apply_change(&border_changes, i); } if (xs <= xstop) DRAW_BLANK(frame_buffer_ptr, xs, xstop, pixel_width); } else { for (i = 0; (i < border_changes.count && border_changes.actions[i].where <= xstop); i++) apply_change(&border_changes, i); } /* Draw right border. */ if (!open_right_border) { for (; (i < border_changes.count && border_changes.actions[i].where <= display_xstop); i++) apply_change(&border_changes, i); for (xs = display_xstop; i < border_changes.count; i++) { int xe = border_changes.actions[i].where; if (xs < xe) { DRAW_BLANK(frame_buffer_ptr, xs, xe - 1, pixel_width); xs = xe; } apply_change(&border_changes, i); } if (xs <= SCREEN_WIDTH - 1) DRAW_BLANK(frame_buffer_ptr, xs, SCREEN_WIDTH - 1, pixel_width); } else { for (i = 0; i < border_changes.count; i++) apply_change(&border_changes, i); } foreground_changes.count = 0; background_changes.count = 0; border_changes.count = 0; /* Do not cache this line. */ cache[rasterline].is_dirty = 1; add_line(rasterline, 0, SCREEN_WIDTH - 1); if (pixel_height == 2 && double_scan_enabled) vid_memcpy(FRAME_BUFFER_LINE_START(frame_buffer, 2*rasterline + 1) + 2 * SCREEN_MAX_SPRITE_WIDTH, frame_buffer_ptr, pixel_width * SCREEN_WIDTH); have_changes_on_this_line = 0; } inline static void handle_visible_line(void) { if (have_changes_on_this_line) handle_visible_line_with_changes(); else if (!CANVAS_USES_TRIPLE_BUFFERING(canvas) && video_cache_enabled && !open_left_border && !open_right_border) /* FIXME: shortcut! */ handle_visible_line_with_cache(); else handle_visible_line_without_cache(); #if !defined (__VIC_II__) && !defined (__VDC__) if (++ycounter >= SCREEN_CHARHEIGHT) { ycounter = 0; memptr += memptr_inc; #ifdef __CRTC__ memptr &= addr_mask; #endif } #endif } inline static void handle_end_of_frame(void) { frame_buffer_ptr = (FRAME_BUFFER_START(frame_buffer) + 2 * SCREEN_MAX_SPRITE_WIDTH); mem_counter = memptr = rasterline = 0; #ifndef __VIC_II__ ycounter = 0; #endif #ifdef __CRTC__ memptr = scraddr & addr_mask; #endif if (!skip_next_frame) { if (dont_cache) refresh_all(); else refresh_changed(); } skip_next_frame = do_vsync(skip_next_frame); #if defined(__MSDOS__) && !defined(VIC20) if (window_width == SCREEN_XPIX && window_height == SCREEN_YPIX) canvas_set_border_color(canvas, border_color); #endif } /* Emulate one raster line. */ inline static void emulate_line(void) { oldclk += CYCLES_PER_LINE; /* Emulate the vertical blank flip-flops. (Well, sort of.) */ if (rasterline == display_ystart && !blank) blank_enabled = 0; else if (rasterline == display_ystop) blank_enabled = 1; if (rasterline >= SCREEN_FIRST_DISPLAYED_LINE && rasterline <= SCREEN_LAST_DISPLAYED_LINE) { if (!skip_next_frame && !asleep && (rasterline >= window_first_line && rasterline <= window_last_line)) { if ((blank_this_line || blank_enabled) && !open_left_border) handle_blank_line(); else handle_visible_line(); if (++num_cached_lines == (window_last_line - window_first_line)) { dont_cache = 0; num_cached_lines = 0; } } else { #ifdef __VIC_II__ if (!skip_next_frame) update_sprite_collisions(); #endif if (have_changes_on_this_line) { apply_all_changes(&background_changes); apply_all_changes(&foreground_changes); apply_all_changes(&border_changes); have_changes_on_this_line = 0; } } #ifdef __VIC_II__ if (!idle_state) mem_counter = (mem_counter + mem_counter_inc) & 0x3ff; mem_counter_inc = SCREEN_TEXTCOLS; /* `ycounter' makes the chip go to idle state when it reaches the maximum value. */ if (ycounter == 7) { idle_state = 1; memptr = mem_counter; } if (!idle_state || bad_line) { ycounter = (ycounter + 1) & 0x7; idle_state = 0; } if (force_display_state) { idle_state = 0; force_display_state = 0; } draw_idle_state = idle_state; bad_line = 0; #endif rasterline++; frame_buffer_ptr = FRAME_BUFFER_LINE_START(frame_buffer, rasterline * pixel_height); frame_buffer_ptr += 2 * SCREEN_MAX_SPRITE_WIDTH; #ifdef __VIC_II__ if (rasterline == 0x30) allow_bad_lines = !blank; #endif } else { #ifdef __VIC_II__ if (!skip_next_frame) update_sprite_collisions(); #endif if (have_changes_on_this_line) { apply_all_changes(&background_changes); apply_all_changes(&foreground_changes); apply_all_changes(&border_changes); have_changes_on_this_line = 0; } rasterline++; if (rasterline == SCREEN_HEIGHT) { handle_end_of_frame(); } else { frame_buffer_ptr = FRAME_BUFFER_LINE_START(frame_buffer, rasterline * pixel_height); frame_buffer_ptr += 2 * SCREEN_MAX_SPRITE_WIDTH; } } apply_all_changes(&next_line_changes); #ifdef __VIC_II__ /* Handle open borders. */ open_left_border = open_right_border; open_right_border = 0; #endif #if SCREEN_NUM_SPRITES > 0 dma_msk = new_dma_msk; #endif memory_fetch_done = 0; blank_this_line = 0; } /* Disable all the caching for the next frame. */ inline static void force_repaint(void) { dont_cache = 1; num_cached_lines = 0; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.