This is crtc.c in view mode; [Download] [Up]
/* * crtc.c - A line-based CRTC emulation (under construction). * * Written by * Ettore Perazzoli (ettore@comm2000.it) * André Fachat (fachat@physik.tu-chemnitz.de) * * 16/24bpp support added by * Steven Tieu (stieu@physics.ubc.ca) * Teemu Rantanen (tvr@cs.hut.fi) * * 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. * */ #ifndef PET #define PET #endif #define _CRTC_C #include "vice.h" /* On MS-DOS, we do not need 2x drawing functions. This is mainly to save memory and (little) speed. */ #ifndef __MSDOS__ #define NEED_2x #endif /* __MSDOS__ */ #include <stdlib.h> #include <stdio.h> #include "crtc.h" #include "raster.h" #include "vmachine.h" #include "interrupt.h" #include "mem.h" #include "pia.h" #include "resources.h" #include "cmdline.h" #include "utils.h" /* ------------------------------------------------------------------------- */ static void crtc_init_dwg_tables(void); static void crtc_update_memory_ptrs(void); static void crtc_arrange_window(int width, int height); static int fill_cache(struct line_cache *l, int *xs, int *xe, int r); static void draw_standard_line(void); static void draw_reverse_line(void); static void draw_standard_line_2x(void); static void draw_reverse_line_2x(void); static void draw_standard_line_cached(struct line_cache *l, int xs, int xe); static void draw_reverse_line_cached(struct line_cache *l, int xs, int xe); static void draw_standard_line_cached_2x(struct line_cache *l, int xs, int xe); static void draw_reverse_line_cached_2x(struct line_cache *l, int xs, int xe); static palette_t *palette; static BYTE crtc[19]; static BYTE *chargen_ptr = NULL; static BYTE *screenmem = NULL; static ADDRESS addr_mask = 0; static ADDRESS scraddr = 0; PIXEL4 dwg_table_0[256], dwg_table_1[256]; PIXEL4 dwg_table2x_0[256], dwg_table2x_1[256]; PIXEL4 dwg_table2x_2[256], dwg_table2x_3[256]; /* ------------------------------------------------------------------------- */ /* CRTC resources. */ /* Name of palette file. */ static char *palette_file_name; /* Flag: Do we use double size? */ static int double_size_enabled; /* Flag: Do we enable the video cache? */ static int video_cache_enabled; /* Flag: Do we copy lines in double size mode? */ static int double_scan_enabled; static int set_video_cache_enabled(resource_value_t v) { video_cache_enabled = (int) v; return 0; } /* prototype for resources - moved to raster.c */ static int set_palette_file_name(resource_value_t v); #if 0 static int set_palette_file_name(resource_value_t v) { /* 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) return -1; canvas_set_palette(canvas, palette, pixel_table); /* Make sure the pixel tables are recalculated properly. */ video_resize(); string_set(&palette_file_name, (char *) v); return 0; } #endif static int set_double_size_enabled(resource_value_t v) { double_size_enabled = (int) v; video_resize(); return 0; } static int set_double_scan_enabled(resource_value_t v) { double_scan_enabled = (int) v; video_resize(); return 0; } static resource_t resources[] = { { "PaletteFile", RES_STRING, (resource_value_t) "default", (resource_value_t *) &palette_file_name, set_palette_file_name }, #ifdef NEED_2x { "DoubleSize", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &double_size_enabled, set_double_size_enabled }, #endif #if defined NEED_2x || defined __MSDOS__ { "DoubleScan", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &double_scan_enabled, set_double_scan_enabled }, #endif #ifndef __MSDOS__ { "VideoCache", RES_INTEGER, (resource_value_t) 1, (resource_value_t *) &video_cache_enabled, set_video_cache_enabled }, #else { "VideoCache", RES_INTEGER, (resource_value_t) 1, (resource_value_t *) &video_cache_enabled, set_video_cache_enabled }, #endif { NULL } }; int crtc_init_resources(void) { return resources_register(resources); } /* ------------------------------------------------------------------------- */ /* CRTC command-line options. */ static cmdline_option_t cmdline_options[] = { { "-vcache", SET_RESOURCE, 0, NULL, NULL, "VideoCache", (resource_value_t) 1, NULL, "Enable the video cache" }, { "+vcache", SET_RESOURCE, 0, NULL, NULL, "VideoCache", (resource_value_t) 0, NULL, "Disable the video cache" }, { "-palette", SET_RESOURCE, 1, NULL, NULL, "PaletteFile", NULL, "<name>", "Specify palette file name" }, #ifdef NEED_2x { "-dsize", SET_RESOURCE, 0, NULL, NULL, "DoubleSize", (resource_value_t) 1, NULL, "Enable double size" }, { "+dsize", SET_RESOURCE, 0, NULL, NULL, "DoubleSize", (resource_value_t) 0, NULL, "Disable double size" }, { "-dscan", SET_RESOURCE, 0, NULL, NULL, "DoubleScan", (resource_value_t) 1, NULL, "Enable double scan" }, { "+dscan", SET_RESOURCE, 0, NULL, NULL, "DoubleScan", (resource_value_t) 0, NULL, "Disable double scan" }, #endif { NULL } }; int crtc_init_cmdline_options(void) { return cmdline_register_options(cmdline_options); } void init_drawing_tables(void) { crtc_init_dwg_tables(); } /* ------------------------------------------------------------------------- */ #include "raster.c" /* -------------------------------------------------------------------------- */ canvas_t crtc_init(void) { static const char *color_names[CRTC_NUM_COLORS] = { "Background", "Foreground" }; #ifdef __MSDOS__ /* FIXME: Should set VGA mode. */ if (SCREEN_XPIX > 320) double_size_enabled = 1; else double_size_enabled = 0; #endif init_raster(1, 2, 2); video_resize(); palette = palette_create(CRTC_NUM_COLORS, color_names); if (palette == NULL) return NULL; if (palette_load(palette_file_name, palette) < 0) { printf("Cannot load palette file `%s'.\n", palette_file_name); return NULL; } if (open_output_window(CRTC_WINDOW_TITLE, CRTC_SCREEN_XPIX + 10, CRTC_SCREEN_YPIX + 10, palette, (canvas_redraw_t) crtc_arrange_window)) { fprintf(stderr, "fatal error: can't open window for CRTC emulation.\n"); return NULL; } video_mode = CRTC_STANDARD_MODE; memptr_inc = crtc_cols; refresh_all(); chargen_ptr = chargen_rom; border_color = 0; background_color = 0; display_ystart = CRTC_SCREEN_BORDERHEIGHT; display_ystop = CRTC_SCREEN_BORDERHEIGHT + CRTC_SCREEN_MAX_YPIX; crtc_init_dwg_tables(); return canvas; } static void crtc_init_dwg_tables(void) { int byte, p; BYTE msk; for (byte = 0; byte < 0x0100; byte++) { *((PIXEL *) (dwg_table2x_0 + byte)) = *((PIXEL *) (dwg_table2x_0 + byte) + 1) = PIXEL(byte & 0x80 ? 1 : 0); *((PIXEL *) (dwg_table2x_0 + byte) + 2) = *((PIXEL *) (dwg_table2x_0 + byte) + 3) = PIXEL(byte & 0x40 ? 1 : 0); *((PIXEL *) (dwg_table2x_1 + byte)) = *((PIXEL *) (dwg_table2x_1 + byte) + 1) = PIXEL(byte & 0x20 ? 1 : 0); *((PIXEL *) (dwg_table2x_1 + byte) + 2) = *((PIXEL *) (dwg_table2x_1 + byte) + 3) = PIXEL(byte & 0x10 ? 1 : 0); *((PIXEL *) (dwg_table2x_2 + byte)) = *((PIXEL *) (dwg_table2x_2 + byte) + 1) = PIXEL(byte & 0x08 ? 1 : 0); *((PIXEL *) (dwg_table2x_2 + byte) + 2) = *((PIXEL *) (dwg_table2x_2 + byte) + 3) = PIXEL(byte & 0x04 ? 1 : 0); *((PIXEL *) (dwg_table2x_3 + byte)) = *((PIXEL *) (dwg_table2x_3 + byte) + 1) = PIXEL(byte & 0x02 ? 1 : 0); *((PIXEL *) (dwg_table2x_3 + byte) + 2) = *((PIXEL *) (dwg_table2x_3 + byte) + 3) = PIXEL(byte & 0x01 ? 1 : 0); } for (byte = 0; byte < 0x0100; byte++) { for (msk = 0x80, p = 0; p < 4; msk >>= 1, p++) *((PIXEL *)(dwg_table_0 + byte) + p) = PIXEL(byte & msk ? 1 : 0); for (p = 0; p < 4; msk >>= 1, p++) *((PIXEL *)(dwg_table_1 + byte) + p) = PIXEL(byte & msk ? 1 : 0); } } /* Set proper functions and constants for the current video settings. */ void video_resize(void) { static int old_size = 0; if (double_size_enabled) { pixel_height = 2; if (crtc_cols == 40) { pixel_width = 2; video_modes[CRTC_STANDARD_MODE].fill_cache = fill_cache; video_modes[CRTC_STANDARD_MODE].draw_line_cached = draw_standard_line_cached_2x; video_modes[CRTC_STANDARD_MODE].draw_line = draw_standard_line_2x; video_modes[CRTC_REVERSE_MODE].fill_cache = fill_cache; video_modes[CRTC_REVERSE_MODE].draw_line_cached = draw_reverse_line_cached_2x; video_modes[CRTC_REVERSE_MODE].draw_line = draw_reverse_line_2x; if (old_size == 1) { window_width *= 2; window_height *= 2; if (canvas) canvas_resize(canvas, window_width, window_height); } } else { /* When in 80 column mode, only the height is doubled. */ pixel_width = 1; video_modes[CRTC_STANDARD_MODE].fill_cache = fill_cache; video_modes[CRTC_STANDARD_MODE].draw_line_cached = draw_standard_line_cached; video_modes[CRTC_STANDARD_MODE].draw_line = draw_standard_line; video_modes[CRTC_REVERSE_MODE].fill_cache = fill_cache; video_modes[CRTC_REVERSE_MODE].draw_line_cached = draw_reverse_line_cached; video_modes[CRTC_REVERSE_MODE].draw_line = draw_reverse_line; if (old_size == 1) window_height *= 2; } } else { pixel_width = 1; pixel_height = 1; video_modes[CRTC_STANDARD_MODE].fill_cache = fill_cache; video_modes[CRTC_STANDARD_MODE].draw_line_cached = draw_standard_line_cached; video_modes[CRTC_STANDARD_MODE].draw_line = draw_standard_line; video_modes[CRTC_REVERSE_MODE].fill_cache = fill_cache; video_modes[CRTC_REVERSE_MODE].draw_line_cached = draw_reverse_line_cached; video_modes[CRTC_REVERSE_MODE].draw_line = draw_reverse_line; if (old_size == 2) { if (crtc_cols == 40) window_width /= 2; window_height /= 2; } } old_size = double_size_enabled ? 2 : 1; if (canvas) { resize(window_width, window_height); frame_buffer_clear(&frame_buffer, pixel_table[0]); force_repaint(); refresh_all(); } } void video_free(void) { frame_buffer_free(&frame_buffer); } static void crtc_arrange_window(int width, int height) { resize(width, height); refresh_all(); } /* -------------------------------------------------------------------------- */ /* CRTC interface functions. FIXME: Several registers are not implemented. */ void REGPARM2 store_crtc(ADDRESS addr, BYTE value) { crtc[addr] = value; switch (addr) { case 0: /* R00 Horizontal total (characters + 1) */ case 1: /* R01 Horizontal characters displayed */ memptr_inc = crtc[1]; if (crtc_cols == 80) memptr_inc *= 2; if (memptr_inc > crtc_cols) memptr_inc = crtc_cols; break; case 2: /* R02 Columns Displayed (+1) */ case 3: /* R03 Horizontal/Vertical Sync widths */ break; case 4: /* R04 Raster line count */ break; case 5: /* R05 Vertical Screen position */ break; case 6: /* R06 Number of display lines on screen */ case 7: /* R07 Lines displayed */ break; case 8: /* R08 unused: Interlace and Skew */ break; case 9: /* R09 Rasters between two display lines */ break; case 10: /* R10 Cursor (not implemented on the PET) */ case 11: /* R11 Cursor (not implemented on the PET) */ break; case 12: /* R12 Control register */ /* This is actually the upper 6 video RAM address bits. * But CBM decided that the two uppermost bits should be used * for control (Can anyone kill the guy who got that idea?) * The usage here is from the 8032 schematics on funet. * * Bit 0: 1=add 256 to screen start address ( 512 for 80-columns) * Bit 1: 1=add 512 to screen start address (1024 for 80-columns) * Bit 2: no connection * Bit 3: no connection * Bit 4: invert video signal * Bit 5: use top half of 4K character generator * Bit 6: (no pin on the CRTC, video address is 14 bit only) * Bit 7: (no pin on the CRTC, video address is 14 bit only) */ crtc_update_memory_ptrs(); break; case 13: /* R13 Address of first character */ /* Value + 32786 (8000) */ crtc_update_memory_ptrs(); break; case 14: case 15: /* R14-5 Cursor location HI/LO -- unused */ break; case 16: case 17: /* R16-7 Light Pen HI/LO -- read only */ break; case 18: case 19: /* R18-9 Update address HI/LO (only 6545) */ break; } } BYTE REGPARM1 read_crtc(ADDRESS addr) { switch (addr) { case 14: case 15: /* Cursor location HI/LO */ return crtc[addr]; case 16: case 17: /* Light Pen X,Y */ return 0xff; default: return 0; /* All the rest are write-only registers */ } } BYTE REGPARM1 peek_crtc(ADDRESS addr) { return read_crtc(addr); } void store_colorram(ADDRESS addr, BYTE value) { /* No color RAM. */ } BYTE read_colorram(ADDRESS addr) { /* Bogus. */ return 0; } void crtc_set_char(int crom) { chargen_ptr = chargen_rom + (crom ? 0x800 : 0); } void reset_crtc(void) { /* spec says to initialize "all internal scan counter circuits. * When /RES is low, all internal counters stop and clear and all * scan and video output go low; control registers are unaffected. * All scan timing initiates when /RES goes high. " * "In this way, /RES can synchronize display frame timing with * line frequency." * * Well, we just emulate... */ maincpu_set_alarm_clk(A_RASTERDRAW, CYCLES_PER_LINE); return; } int crtc_offscreen(void) { return rasterline >= CRTC_SCREEN_YPIX; } void crtc_set_screen_mode(BYTE *screen, int vmask, int num_cols) { int w; if (double_size_enabled) w = window_width; else w = (float)window_width * ((float)num_cols / (float)crtc_cols); memptr_inc = crtc_cols = num_cols; addr_mask = vmask; screenmem = screen; display_xstart = SCREEN_BORDERWIDTH; display_xstop = SCREEN_BORDERWIDTH + SCREEN_XPIX; display_ystart = SCREEN_BORDERHEIGHT; display_ystop = SCREEN_BORDERHEIGHT + SCREEN_YPIX; #ifdef __MSDOS__ /* FIXME: This does not have any effect until there is a gfx -> text -> gfx mode transition. Moreover, no resources should be changed behind user's back... So this is definitely a Bad Thing (tm). For now, it's fine with us, though. */ resources_set_value("VGAMode", (resource_value_t) (crtc_cols > 40 ? VGA_640x480 : VGA_320x200)); if (SCREEN_XPIX > 320) double_size_enabled = 1; else double_size_enabled = 0; #endif if (canvas) { canvas_resize(canvas, w, window_height); crtc_arrange_window(w, window_height); video_resize(); } } void crtc_screen_enable(int en) { en = en ? 0 : 1; blank = en; blank_enabled = en; } static void crtc_update_memory_ptrs(void) { scraddr = crtc[13] + ((crtc[12] & 0x3f) << 8); if ((addr_mask & 0x1000) || (scraddr & 0x1000)) video_mode = CRTC_STANDARD_MODE; else video_mode = CRTC_REVERSE_MODE; if (crtc_cols == 80) scraddr *= 2; scraddr &= addr_mask; /* printf("ram=%p, screenmem=%p, scraddr = %04x, addr_mask=%04x\n", ram, screenmem, scraddr, addr_mask); */ } /* -------------------------------------------------------------------------- */ int int_rasterdraw(long offset) { maincpu_set_alarm(A_RASTERDRAW, CYCLES_PER_LINE - offset); emulate_line(); /* This generates one raster interrupt per frame. */ if (rasterline == 0) { /* we assume this to start the screen */ signal_pia1(PIA_SIG_CB1, PIA_SIG_RISE); } else if (rasterline == CRTC_SCREEN_YPIX) { /* and this to end the screen */ signal_pia1(PIA_SIG_CB1, PIA_SIG_FALL); } return 0; } static int fill_cache(struct line_cache *l, int *xs, int *xe, int r) { static BYTE fake_char_data = 0; int retval, n; retval = _fill_cache_text(l->fgdata, screenmem + memptr, chargen_ptr, memptr_inc, ycounter, xs, xe, r); n = crtc_cols - memptr_inc; /* All the characters beyond the `memptr_inc'th one are blank. */ if (n > 0) { int xs1 = SCREEN_TEXTCOLS, xe1 = -1; if (_fill_cache(l->fgdata + memptr_inc, &fake_char_data, n, 0, &xs1, &xe1, r)) { xs1 += memptr_inc, *xs = MIN(xs1, *xs); xe1 += memptr_inc, *xe = MAX(xe1, *xe); retval = 1; } } return retval; } #define DRAW(reverse_flag) \ do { \ PIXEL *p = frame_buffer_ptr + SCREEN_BORDERWIDTH; \ register int i, d; \ \ for (i = 0; i < memptr_inc; i++, p += 8) { \ d = GET_CHAR_DATA(chargen_ptr, (screenmem + memptr)[i], \ ycounter); \ if ((reverse_flag)) \ d = ~d; \ *((PIXEL4 *) p) = dwg_table_0[d]; \ *((PIXEL4 *) p + 1) = dwg_table_1[d]; \ } \ \ d = (reverse_flag) ? 0xff: 0; \ for (; i < crtc_cols; i++, p += 8) { \ *((PIXEL4 *) p) = dwg_table_0[d]; \ *((PIXEL4 *) p + 1) = dwg_table_1[d]; \ } \ } while (0) static void draw_standard_line(void) { DRAW(0); } static void draw_reverse_line(void) { DRAW(1); } #define DRAW_2x(reverse_flag) \ do { \ PIXEL *p = (frame_buffer_ptr \ + SCREEN_BORDERWIDTH * pixel_width); \ register int i, d; \ \ for (i = 0; i < memptr_inc; i++, p += 16) { \ d = GET_CHAR_DATA(chargen_ptr, (screenmem + memptr)[i], \ ycounter); \ if ((reverse_flag)) \ d = ~d; \ *((PIXEL4 *) p) = dwg_table2x_0[d]; \ *((PIXEL4 *) p + 1) = dwg_table2x_1[d]; \ *((PIXEL4 *) p + 2) = dwg_table2x_2[d]; \ *((PIXEL4 *) p + 3) = dwg_table2x_3[d]; \ } \ \ d = (reverse_flag) ? 0xff : 0; \ for (; i < crtc_cols; i++, p += 16) { \ *((PIXEL4 *) p) = dwg_table2x_0[d]; \ *((PIXEL4 *) p + 1) = dwg_table2x_1[d]; \ *((PIXEL4 *) p + 2) = dwg_table2x_2[d]; \ *((PIXEL4 *) p + 3) = dwg_table2x_3[d]; \ } \ } while (0) static void draw_standard_line_2x(void) { DRAW_2x(0); } static void draw_reverse_line_2x(void) { DRAW_2x(1); } #define DRAW_CACHED(l, xs, xe, reverse_flag) \ do { \ PIXEL *p = frame_buffer_ptr + SCREEN_BORDERWIDTH + (xs) * 8; \ register int i; \ \ for (i = (xs); i <= (xe); i++, p += 8) { \ BYTE d = (l)->fgdata[i]; \ \ if ((reverse_flag)) \ d = ~d; \ *((PIXEL4 *) p) = dwg_table_0[d]; \ *((PIXEL4 *) p + 1) = dwg_table_1[d]; \ } \ } while (0) static void draw_standard_line_cached(struct line_cache *l, int xs, int xe) { DRAW_CACHED(l, xs, xe, 0); } static void draw_reverse_line_cached(struct line_cache *l, int xs, int xe) { DRAW_CACHED(l, xs, xe, 1); } #define DRAW_CACHED_2x(l, xs, xe, reverse_flag) \ do { \ PIXEL *p = (frame_buffer_ptr \ + 2 * (SCREEN_BORDERWIDTH + (xs) * 8)); \ register int i; \ \ for (i = (xs); i <= (xe); i++, p += 16) { \ BYTE d = (l)->fgdata[i]; \ \ if ((reverse_flag)) \ d = ~d; \ *((PIXEL4 *) p) = dwg_table2x_0[d]; \ *((PIXEL4 *) p + 1) = dwg_table2x_1[d]; \ *((PIXEL4 *) p + 2) = dwg_table2x_2[d]; \ *((PIXEL4 *) p + 3) = dwg_table2x_3[d]; \ } \ } while (0) static void draw_standard_line_cached_2x(struct line_cache *l, int xs, int xe) { DRAW_CACHED_2x(l, xs, xe, 0); } static void draw_reverse_line_cached_2x(struct line_cache *l, int xs, int xe) { DRAW_CACHED_2x(l, xs, xe, 1); } /* ------------------------------------------------------------------------- */ void crtc_prevent_clk_overflow(CLOCK sub) { oldclk -= sub; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.