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.