This is vic.c in view mode; [Download] [Up]
/* * vic.c - A line-based VIC-I emulation (under construction). * * Written by * Ettore Perazzoli (ettore@comm2000.it) * * 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. * */ /* Warning: this emulation is very incomplete and buggy. */ #ifndef VIC20 #define VIC20 #endif #define _VIC_C /* 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 "vice.h" #include <stdlib.h> #include <stdio.h> #include "vic.h" #include "vmachine.h" #include "interrupt.h" #include "raster.h" #include "vic20sound.h" #include "mem.h" #include "resources.h" #include "cmdline.h" #include "utils.h" /* #define VIC_REGISTERS_DEBUG */ /* ------------------------------------------------------------------------- */ /* VIC resources. */ /* 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; /* Name of palette file. */ static char *palette_file_name; static int set_video_cache_enabled(resource_value_t v) { video_cache_enabled = (int) v; return 0; } /* prototype for resources - new function from raster.c */ static int set_palette_file_name(resource_value_t v); #if 0 static int set_palette_file(resource_value_t v) { if (palette_file_name != NULL) free(palette_file_name); palette_file_name = stralloc((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[] = { { "DoubleSize", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &double_size_enabled, set_double_size_enabled }, { "DoubleScan", RES_INTEGER, (resource_value_t) 0, (resource_value_t *) &double_scan_enabled, set_double_scan_enabled }, { "PaletteFile", RES_STRING, (resource_value_t) "default", (resource_value_t *) &palette_file_name, set_palette_file_name }, #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) 0, (resource_value_t *) &video_cache_enabled, set_video_cache_enabled }, #endif { NULL } }; int vic_init_resources(void) { return resources_register(resources); } /* ------------------------------------------------------------------------- */ /* VIC 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 vic_init_cmdline_options(void) { return cmdline_register_options(cmdline_options); } /* ------------------------------------------------------------------------- */ static void set_memory_ptrs(void); static void init_drawing_tables(void); static int fill_cache(struct line_cache *l, int *xs, int *xe, int r); static void draw_line(void); static void draw_line_2x(void); static void draw_line_cached(struct line_cache *l, int xs, int xe); static void draw_line_cached_2x(struct line_cache *l, int xs, int xe); static void draw_reverse_line(void); static void draw_reverse_line_2x(void); static void draw_reverse_line_cached(struct line_cache *l, int xs, int xe); static void draw_reverse_line_cached_2x(struct line_cache *l, int xs, int xe); /* Define the position of the raster beam precisely. */ #define RASTER_Y ((int)(clk / CYCLES_PER_LINE) % SCREEN_HEIGHT) #define RASTER_CYCLE ((int)(clk % CYCLES_PER_LINE)) static palette_t *palette; static BYTE vic[64]; static BYTE auxiliary_color; static BYTE *colormem; static BYTE *screenmem; static BYTE *chargen_ptr = chargen_rom + 0x400; /* On MS-DOS, do not duplicate pixels. Otherwise, we would always need at least 466 horizontal pixels to contain the whole screen. */ #ifndef __MSDOS__ #define DUPLICATE_PIXELS #endif #ifdef DUPLICATE_PIXELS typedef PIXEL2 VIC_PIXEL; #define VIC_PIXEL(n) PIXEL2(n) typedef PIXEL4 VIC_PIXEL2; #define VIC_PIXEL2(n) PIXEL4(n) #define VIC_PIXEL_WIDTH 2 #else typedef PIXEL VIC_PIXEL; #define VIC_PIXEL(n) PIXEL(n) typedef PIXEL2 VIC_PIXEL2; #define VIC_PIXEL2(n) PIXEL2(n) #define VIC_PIXEL_WIDTH 1 #endif #include "raster.c" /* -------------------------------------------------------------------------- */ /* Initialization. */ canvas_t vic_init(void) { static const char *color_names[] = { "Black", "White", "Red", "Cyan", "Purple", "Green", "Blue", "Yellow", "Orange", "Light Orange", "Pink", "Light Cyan", "Light Purple", "Light Green", "Light Blue", "Light Yellow" }; int width, height; /* FIXME: the maximum pixel width should be 4 instead of 6, but we need some extra space for clipping long lines... This should be done in a cleaner way. */ init_raster(1, 6, 2); width = VIC_SCREEN_WIDTH; height = (VIC_SCREEN_LAST_DISPLAYED_LINE - VIC_SCREEN_FIRST_DISPLAYED_LINE + 1); video_resize(); palette = palette_create(VIC_NUM_COLORS, color_names); if (palette == NULL) return NULL; if (palette_load(palette_file_name, palette) < 0) { printf("Cannot load default palette.\n"); return NULL; } if (open_output_window(VIC_WINDOW_TITLE, width, height, palette, (canvas_redraw_t)vic_exposure_handler)) { fprintf(stderr, "fatal error: cannot open window for the VIC emulation.\n"); return NULL; } video_mode = VIC_STANDARD_MODE; set_memory_ptrs(); refresh_all(); init_drawing_tables(); return canvas; } /* This hook is called whenever the screen parameters (eg. window size) are changed. */ void video_resize(void) { static int old_size = 0; if (double_size_enabled) { pixel_width = 2 * VIC_PIXEL_WIDTH; pixel_height = 2; video_modes[VIC_STANDARD_MODE].fill_cache = fill_cache; video_modes[VIC_STANDARD_MODE].draw_line_cached = draw_line_cached_2x; video_modes[VIC_STANDARD_MODE].draw_line = draw_line_2x; video_modes[VIC_REVERSE_MODE].fill_cache = fill_cache; video_modes[VIC_REVERSE_MODE].draw_line_cached = draw_reverse_line_cached_2x; video_modes[VIC_REVERSE_MODE].draw_line = draw_reverse_line_2x; if (old_size == 1) { window_width *= 2; window_height *= 2; } } else { pixel_width = VIC_PIXEL_WIDTH; pixel_height = 1; video_modes[VIC_STANDARD_MODE].fill_cache = fill_cache; video_modes[VIC_STANDARD_MODE].draw_line_cached = draw_line_cached; video_modes[VIC_STANDARD_MODE].draw_line = draw_line; video_modes[VIC_REVERSE_MODE].fill_cache = fill_cache; video_modes[VIC_REVERSE_MODE].draw_line_cached = draw_reverse_line_cached; video_modes[VIC_REVERSE_MODE].draw_line = draw_reverse_line; if (old_size == 2) { 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(0)); force_repaint(); } } void video_free(void) { frame_buffer_free(&frame_buffer); } /* -------------------------------------------------------------------------- */ /* VIC access functions. */ void REGPARM2 store_vic(ADDRESS addr, BYTE value) { addr &= 0xf; vic[addr] = value; #ifdef VIC_REGISTERS_DEBUG printf("VIC: write $90%02X, value = $%02X\n", addr, value); #endif switch (addr) { case 0: /* $9000 Screen X Location. */ value &= 0x7f; if (value > 8) value = 8; if (value < 1) value = 1; display_xstart = value * 4; display_xstop = display_xstart + text_cols * 8; if (display_xstop >= VIC_SCREEN_WIDTH) display_xstop = VIC_SCREEN_WIDTH - 1; #ifdef VIC_REGISTERS_DEBUG printf("\tscreen X location: $%02X\n", value); #endif return; case 1: /* $9001 Screen Y Location. */ display_ystart = value * 2; display_ystop = display_ystart + text_lines * char_height; #ifdef VIC_REGISTERS_DEBUG printf("\tscreen Y location: $%02X\n", value); return; #endif return; case 2: /* $9002 Columns Displayed. */ colormem = ram + ((value & 0x80) ? 0x9600 : 0x9400); text_cols = value & 0x7f; if (text_cols > VIC_SCREEN_MAX_TEXTCOLS) text_cols = VIC_SCREEN_MAX_TEXTCOLS; display_xstop = display_xstart + text_cols * 8; if (display_xstop >= VIC_SCREEN_WIDTH) display_xstop = VIC_SCREEN_WIDTH - 1; set_memory_ptrs(); memptr_inc = text_cols; #ifdef VIC_REGISTERS_DEBUG printf("\tcolor RAM at $%04X\n", colormem - ram); printf("\tcolumns displayed: %d\n", text_cols); #endif break; case 3: /* $9003 Rows Displayed, Character size . */ text_lines = (value & 0x7e) >> 1; if (text_lines > VIC_SCREEN_MAX_TEXTLINES) text_lines = VIC_SCREEN_MAX_TEXTLINES; char_height = (value & 0x1) ? 16 : 8; display_ystop = display_ystart + text_lines * char_height; #ifdef VIC_REGISTERS_DEBUG printf("\trows displayed: %d\n", text_lines); printf("\tcharacter height: %d\n", char_height); #endif set_memory_ptrs(); return; case 4: /* $9004 Raster line count -- read only. */ #ifdef VIC_REGISTERS_DEBUG printf("\t(raster line counter, read-only)\n"); #endif return; case 5: /* $9005 Video and char matrix base address. */ set_memory_ptrs(); return; case 6: /* $9006. */ case 7: /* $9007 Light Pen X,Y. */ #ifdef VIC_REGISTERS_DEBUG printf("\t(light pen register, read-only)\n"); #endif return; case 8: /* $9008. */ case 9: /* $9009 Paddle X,Y. */ #ifdef VIC_REGISTERS_DEBUG printf("\t(paddle, read-only)\n"); #endif return; case 10: /* $900A Bass Enable and Frequency. */ case 11: /* $900B Alto Enable and Frequency. */ case 12: /* $900C Soprano Enable and Frequency. */ case 13: /* $900D Noise Enable and Frequency. */ #ifdef VIC_REGISTERS_DEBUG printf("\t(sound register, not implemented)\n"); #endif store_sound(addr, value); return; case 14: /* $900E Auxiliary Colour, Master Volume. */ auxiliary_color = value >> 4; #ifdef VIC_REGISTERS_DEBUG printf("\tauxiliary color set to $%02X\n", auxiliary_color); printf("\t(master volume not implemented)\n"); #endif store_sound(addr, value); return; case 15: /* $900F Screen and Border Colors, Reverse Video. */ border_color = value & 0x7; background_color = value >> 4; video_mode = (value & 8) ? VIC_STANDARD_MODE : VIC_REVERSE_MODE; #ifdef VIC_REGISTERS_DEBUG printf("\tborder color: $%02X\n", border_color); printf("\tbackground color: $%02X\n", background_color); #endif return; } } BYTE REGPARM1 read_vic(ADDRESS addr) { addr &= 0xf; switch (addr) { case 3: return ((RASTER_Y & 1) << 7) | (vic[3] & ~0x80); case 4: return RASTER_Y >> 1; default: return vic[addr]; } } /* -------------------------------------------------------------------------- */ /* Set the memory pointers according to the values stored in the VIC registers. */ static void set_memory_ptrs(void) { int tmp; ADDRESS charaddr; tmp = vic[0x5] & 0xf; charaddr = (tmp & 0x8) ? 0x0000 : 0x8000; charaddr += (tmp & 0x7) * 0x400; if (charaddr >= 0x8000 && charaddr < 0x9000) { chargen_ptr = chargen_rom + 0x400 + (charaddr & 0xfff); #ifdef VIC_REGISTERS_DEBUG printf("\tcharacter memory at $%04X (character ROM + $%04X)\n", charaddr, charaddr & 0xfff); #endif } else { if(charaddr == 0x1c00) { chargen_ptr = chargen_rom; /* handle wraparound */ } else { chargen_ptr = ram + charaddr; } #ifdef VIC_REGISTERS_DEBUG printf("\tcharacter memory at $%04X\n", charaddr); #endif } colormem = ram + 0x9400 + (vic[0x2] & 0x80 ? 0x200 : 0x0); screenmem = ram + (((vic[0x2] & 0x80) << 2) | ((vic[0x5] & 0x70) << 6)); #ifdef VIC_REGISTERS_DEBUG printf("\tcolor memory at $%04X\n", colormem - ram); printf("\tscreen memory at $%04X\n", screenmem - ram); #endif } /* -------------------------------------------------------------------------- */ /* Here comes the part that actually repaints each raster line. This table is used to speed up the drawing. */ static WORD dwg_table[256][256][8]; /* [byte][color][position] */ static void init_drawing_tables(void) { int byte, color, pos; for (byte = 0; byte < 0x100; byte++) { for (color = 0; color < 0x100; color++) { if (color & 0x8) { /* Multicolor mode. */ for (pos = 0; pos < 8; pos += 2) { dwg_table[byte][color][pos] = dwg_table[byte][color][pos + 1] = (byte >> (6 - pos)) & 0x3; } } else { /* Standard mode. */ for (pos = 0; pos < 8; pos++) { dwg_table[byte][color][pos] = ((byte >> (7 - pos)) & 0x1) * 2; } } } } } /* ------------------------------------------------------------------------- */ /* Notice: The screen origin X register has a 4-pixel granularity, so our write accesses are always aligned. */ int int_rasterdraw(long offset) { maincpu_set_alarm(A_RASTERDRAW, CYCLES_PER_LINE - offset); emulate_line(); if (rasterline == 0) { /* Turn border on. */ blank_enabled = 1; } return 0; } static int fill_cache(struct line_cache *l, int *xs, int *xe, int r) { if (l->bgdata[0] != background_color || l->colordata2[0] != auxiliary_color || l->numcols != text_cols) { l->bgdata[0] = background_color; l->colordata2[0] = auxiliary_color; l->numcols = text_cols; *xs = 0; *xe = text_cols; r = 1; } r = _fill_cache(l->colordata1, colormem + memptr, VIC_SCREEN_TEXTCOLS, 1, xs, xe, r); r = _fill_cache_text(l->fgdata, screenmem + memptr, chargen_ptr, VIC_SCREEN_TEXTCOLS, ycounter, xs, xe, r); return r; } #define PUT_PIXEL(p, d, c, b, x) \ *((VIC_PIXEL *)(p) + (x)) = (c)[dwg_table[(d)][(b)][(x)]] #define DRAW_LINE(p, xs, xe, reverse) \ do { \ static VIC_PIXEL c[4]; \ int b, i; \ BYTE d; \ PIXEL *pp = (PIXEL *)(p) + (xs) * 8 * VIC_PIXEL_WIDTH; \ \ c[0] = VIC_PIXEL(background_color); \ c[1] = VIC_PIXEL(border_color); \ c[3] = VIC_PIXEL(auxiliary_color); \ for (i = (xs); i <= (xe); i++, pp += 8 * VIC_PIXEL_WIDTH) { \ b = (colormem + memptr)[i]; \ c[2] = VIC_PIXEL(b & 0x7); \ if (reverse) \ d = ~(GET_CHAR_DATA (chargen_ptr, (screenmem + memptr)[i], \ ycounter)); \ else \ d = GET_CHAR_DATA (chargen_ptr, (screenmem + memptr)[i], \ ycounter); \ PUT_PIXEL(pp, d, c, b, 0); PUT_PIXEL(pp, d, c, b, 1); \ PUT_PIXEL(pp, d, c, b, 2); PUT_PIXEL(pp, d, c, b, 3); \ PUT_PIXEL(pp, d, c, b, 4); PUT_PIXEL(pp, d, c, b, 5); \ PUT_PIXEL(pp, d, c, b, 6); PUT_PIXEL(pp, d, c, b, 7); \ } \ } while (0) static void draw_line(void) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH; DRAW_LINE(p, 0, text_cols - 1, 0); } static void draw_reverse_line(void) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH; DRAW_LINE(p, 0, text_cols - 1, 1); } static void draw_line_cached(struct line_cache *l, int xs, int xe) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH; DRAW_LINE(p, xs, xe, 0); } static void draw_reverse_line_cached(struct line_cache *l, int xs, int xe) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH; DRAW_LINE(p, xs, xe, 1); } #define PUT_PIXEL_2x(p, d, c, b, x) \ *((VIC_PIXEL2 *)(p) + (x)) = (c)[dwg_table[(d)][(b)][(x)]] #define DRAW_LINE_2x(p, xs, xe, reverse) \ do { \ static VIC_PIXEL2 c[4]; \ BYTE d, b; \ int i; \ PIXEL *pp = (PIXEL *)(p) + (xs) * 16 * VIC_PIXEL_WIDTH; \ \ c[0] = VIC_PIXEL2(background_color); \ c[1] = VIC_PIXEL2(border_color); \ c[3] = VIC_PIXEL2(auxiliary_color); \ for (i = (xs); i <= (xe); i++, pp += 16 * VIC_PIXEL_WIDTH) { \ b = (colormem + memptr)[i]; \ c[2] = VIC_PIXEL2(b & 0x7); \ if (reverse) \ d = ~(GET_CHAR_DATA (chargen_ptr, (screenmem + memptr)[i], \ ycounter)); \ else \ d = GET_CHAR_DATA (chargen_ptr, (screenmem + memptr)[i], \ ycounter); \ PUT_PIXEL_2x(pp, d, c, b, 0); PUT_PIXEL_2x(pp, d, c, b, 1); \ PUT_PIXEL_2x(pp, d, c, b, 2); PUT_PIXEL_2x(pp, d, c, b, 3); \ PUT_PIXEL_2x(pp, d, c, b, 4); PUT_PIXEL_2x(pp, d, c, b, 5); \ PUT_PIXEL_2x(pp, d, c, b, 6); PUT_PIXEL_2x(pp, d, c, b, 7); \ } \ } while (0) static void draw_line_2x(void) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH * 2; DRAW_LINE_2x(p, 0, text_cols - 1, 0); } static void draw_reverse_line_2x(void) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH * 2; DRAW_LINE_2x(p, 0, text_cols - 1, 1); } static void draw_line_cached_2x(struct line_cache *l, int xs, int xe) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH * 2; DRAW_LINE_2x(p, xs, xe, 0); } static void draw_reverse_line_cached_2x(struct line_cache *l, int xs, int xe) { PIXEL *p = frame_buffer_ptr + display_xstart * VIC_PIXEL_WIDTH * 2; DRAW_LINE_2x(p, xs, xe, 1); } /* ------------------------------------------------------------------------- */ void vic_exposure_handler(unsigned int width, unsigned int height) { resize(width, height); force_repaint(); } void vic_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.