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.