Commit 17b0018b authored by bellard's avatar bellard
Browse files

Full VGA support, including old CGA modes, VGA planar and mode X


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@346 c046a42c-6fe2-441c-8c8c-71466251a162
parent 39cf7803
......@@ -49,6 +49,7 @@
#include "thunk.h"
//#define DEBUG_VGA
//#define DEBUG_VGA_MEM
#define MSR_COLOR_EMULATION 0x01
#define MSR_PAGE_SELECT 0x20
......@@ -85,7 +86,8 @@ typedef struct VGAState {
DisplayState *ds;
uint32_t font_offsets[2];
int graphic_mode;
int shift_control;
uint8_t shift_control;
uint8_t double_scan;
uint32_t line_offset;
uint32_t line_compare;
uint32_t start_addr;
......@@ -93,10 +95,11 @@ typedef struct VGAState {
uint32_t last_width, last_height;
uint8_t cursor_start, cursor_end;
uint32_t cursor_offset;
unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned b);
/* tell for each page if it has been updated since the last time */
uint8_t vram_updated[VGA_RAM_SIZE / 4096];
uint32_t last_palette[256];
#define CH_ATTR_SIZE (132 * 60)
#define CH_ATTR_SIZE (160 * 100)
uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
} VGAState;
......@@ -199,6 +202,7 @@ static const uint32_t dmask4[4] = {
static uint32_t expand4[256];
static uint16_t expand2[256];
static uint8_t expand4to8[16];
VGAState vga_state;
int vga_io_memory;
......@@ -503,7 +507,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
int memory_map_mode, plane, write_mode, b, func_select;
uint32_t write_mask, bit_mask, set_mask;
#ifdef DEBUG_VGA
#ifdef DEBUG_VGA_MEM
printf("vga: [0x%x] = 0x%02x\n", addr, val);
#endif
/* convert to VGA memory offset */
......@@ -533,7 +537,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
plane = addr & 3;
if (s->sr[2] & (1 << plane)) {
s->vram_ptr[addr] = val;
#ifdef DEBUG_VGA
#ifdef DEBUG_VGA_MEM
printf("vga: chain4: [0x%x]\n", addr);
#endif
s->vram_updated[addr >> 12] = 1;
......@@ -544,7 +548,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
if (s->sr[2] & (1 << plane)) {
addr = ((addr & ~1) << 1) | plane;
s->vram_ptr[addr] = val;
#ifdef DEBUG_VGA
#ifdef DEBUG_VGA_MEM
printf("vga: odd/even: [0x%x]\n", addr);
#endif
s->vram_updated[addr >> 12] = 1;
......@@ -615,7 +619,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
((uint32_t *)s->vram_ptr)[addr] =
(((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
(val & write_mask);
#ifdef DEBUG_VGA
#ifdef DEBUG_VGA_MEM
printf("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n",
addr * 4, write_mask, val);
#endif
......@@ -699,28 +703,43 @@ static inline int c6_to_8(int v)
return (v << 2) | (b << 1) | b;
}
static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
{
unsigned int col;
col = rgb_to_pixel8(r, g, b);
col |= col << 8;
col |= col << 16;
return col;
}
static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
{
unsigned int col;
col = rgb_to_pixel15(r, g, b);
col |= col << 16;
return col;
}
static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
{
unsigned int col;
col = rgb_to_pixel16(r, g, b);
col |= col << 16;
return col;
}
static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
{
unsigned int col;
col = rgb_to_pixel32(r, g, b);
return col;
}
/* return true if the palette was modified */
static int update_palette16(VGAState *s)
{
int full_update, i, depth;
int full_update, i;
uint32_t v, col, *palette;
unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned b);
depth = s->ds->depth;
switch(depth) {
case 8:
rgb_to_pixel = rgb_to_pixel8;
break;
case 15:
rgb_to_pixel = rgb_to_pixel15;
break;
default:
case 16:
rgb_to_pixel = rgb_to_pixel16;
break;
case 32:
rgb_to_pixel = rgb_to_pixel32;
break;
}
full_update = 0;
palette = s->last_palette;
......@@ -731,21 +750,35 @@ static int update_palette16(VGAState *s)
else
v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
v = v * 3;
col = rgb_to_pixel(c6_to_8(s->palette[v]),
c6_to_8(s->palette[v + 1]),
c6_to_8(s->palette[v + 2]));
if (depth == 8) {
col |= col << 8;
col |= col << 16;
} else if (depth <= 16) {
col |= col << 16;
col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
c6_to_8(s->palette[v + 1]),
c6_to_8(s->palette[v + 2]));
if (col != palette[i]) {
full_update = 1;
palette[i] = col;
}
// printf("%2d: %08x\n", i, col);
}
return full_update;
}
/* return true if the palette was modified */
static int update_palette256(VGAState *s)
{
int full_update, i;
uint32_t v, col, *palette;
full_update = 0;
palette = s->last_palette;
v = 0;
for(i = 0; i < 256; i++) {
col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
c6_to_8(s->palette[v + 1]),
c6_to_8(s->palette[v + 2]));
if (col != palette[i]) {
full_update = 1;
palette[i] = col;
}
v += 3;
}
return full_update;
}
......@@ -806,6 +839,13 @@ static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = {
vga_draw_glyph8_32,
};
static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = {
vga_draw_glyph16_8,
vga_draw_glyph16_16,
vga_draw_glyph16_16,
vga_draw_glyph16_32,
};
static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = {
vga_draw_glyph9_8,
vga_draw_glyph9_16,
......@@ -882,12 +922,19 @@ static void vga_draw_text(VGAState *s, int full_update)
cw = 8;
if (s->sr[1] & 0x01)
cw = 9;
if (s->sr[1] & 0x08)
cw = 16; /* NOTE: no 18 pixel wide */
x_incr = cw * ((s->ds->depth + 7) >> 3);
width = (s->cr[0x01] + 1);
height = s->cr[0x12] |
((s->cr[0x07] & 0x02) << 7) |
((s->cr[0x07] & 0x40) << 3);
height = (height + 1) / cheight;
if (s->cr[0x06] == 100) {
/* ugly hack for CGA 160x100x16 - explain me the logic */
height = 100;
} else {
height = s->cr[0x12] |
((s->cr[0x07] & 0x02) << 7) |
((s->cr[0x07] & 0x40) << 3);
height = (height + 1) / cheight;
}
if (width != s->last_width || height != s->last_height ||
cw != s->last_cw || cw != s->last_cw) {
dpy_resize(s->ds, width * cw, height * cheight);
......@@ -914,7 +961,10 @@ static void vga_draw_text(VGAState *s, int full_update)
cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
depth_index = get_depth_index(s->ds->depth);
vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
if (cw == 16)
vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
else
vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
dest = s->ds->data;
......@@ -944,7 +994,7 @@ static void vga_draw_text(VGAState *s, int full_update)
font_ptr += 32 * 4 * ch;
bgcol = palette[cattr >> 4];
fgcol = palette[cattr & 0x0f];
if (cw == 8) {
if (cw != 9) {
vga_draw_glyph8(d1, linesize,
font_ptr, cheight, fgcol, bgcol);
} else {
......@@ -966,7 +1016,7 @@ static void vga_draw_text(VGAState *s, int full_update)
if (line_last >= line_start && line_start < cheight) {
h = line_last - line_start + 1;
d = d1 + linesize * line_start;
if (cw == 8) {
if (cw != 9) {
vga_draw_glyph8(d, linesize,
cursor_glyph, h, fgcol, bgcol);
} else {
......@@ -989,17 +1039,45 @@ static void vga_draw_text(VGAState *s, int full_update)
}
}
static vga_draw_line_func *vga_draw_line_table[4 * 6] = {
enum {
VGA_DRAW_LINE2,
VGA_DRAW_LINE2D2,
VGA_DRAW_LINE4,
VGA_DRAW_LINE4D2,
VGA_DRAW_LINE8D2,
VGA_DRAW_LINE8,
VGA_DRAW_LINE15,
VGA_DRAW_LINE16,
VGA_DRAW_LINE32,
VGA_DRAW_LINE_NB,
};
static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
vga_draw_line2_8,
vga_draw_line2_16,
vga_draw_line2_16,
vga_draw_line2_32,
vga_draw_line2d2_8,
vga_draw_line2d2_16,
vga_draw_line2d2_16,
vga_draw_line2d2_32,
vga_draw_line4_8,
vga_draw_line4_16,
vga_draw_line4_16,
vga_draw_line4_32,
vga_draw_line4d2_8,
vga_draw_line4d2_16,
vga_draw_line4d2_16,
vga_draw_line4d2_32,
vga_draw_line8d2_8,
vga_draw_line8d2_16,
vga_draw_line8d2_16,
vga_draw_line8d2_32,
vga_draw_line8_8,
vga_draw_line8_16,
vga_draw_line8_16,
......@@ -1029,14 +1107,13 @@ static vga_draw_line_func *vga_draw_line_table[4 * 6] = {
*/
static void vga_draw_graphic(VGAState *s, int full_update)
{
int y, update, page_min, page_max, linesize, y_start;
int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask;
int width, height, shift_control, line_offset, page0, page1, bwidth;
int disp_width;
uint8_t *d;
uint32_t v, addr1, addr;
vga_draw_line_func *vga_draw_line;
full_update |= update_palette16(s);
full_update |= update_basic_params(s);
width = (s->cr[0x01] + 1) * 8;
......@@ -1044,30 +1121,53 @@ static void vga_draw_graphic(VGAState *s, int full_update)
((s->cr[0x07] & 0x02) << 7) |
((s->cr[0x07] & 0x40) << 3);
height = (height + 1);
if (width != s->last_width ||
height != s->last_height) {
dpy_resize(s->ds, width, height);
s->last_width = width;
s->last_height = height;
full_update = 1;
}
disp_width = width;
shift_control = (s->gr[0x05] >> 5) & 3;
if (shift_control != s->shift_control) {
double_scan = (s->cr[0x09] & 0x80);
if (shift_control != s->shift_control ||
double_scan != s->double_scan) {
full_update = 1;
s->shift_control = shift_control;
s->double_scan = double_scan;
}
if (shift_control == 0)
v = 1; /* 4 bit/pixel */
else if (shift_control == 1)
v = 0; /* 2 bit/pixel */
else
v = 2; /* 8 bit/pixel */
if (shift_control == 0) {
full_update |= update_palette16(s);
if (s->sr[0x01] & 8) {
v = VGA_DRAW_LINE4D2;
disp_width <<= 1;
} else {
v = VGA_DRAW_LINE4;
}
} else if (shift_control == 1) {
full_update |= update_palette16(s);
if (s->sr[0x01] & 8) {
v = VGA_DRAW_LINE2D2;
disp_width <<= 1;
} else {
v = VGA_DRAW_LINE2;
}
} else {
full_update |= update_palette256(s);
v = VGA_DRAW_LINE8D2;
double_scan = 1; /* XXX: explain me why it is always activated */
}
vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->ds->depth)];
if (disp_width != s->last_width ||
height != s->last_height) {
dpy_resize(s->ds, disp_width, height);
s->last_width = disp_width;
s->last_height = height;
full_update = 1;
}
line_offset = s->line_offset;
#if 0
printf("w=%d h=%d v=%d line_offset=%d double_scan=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=%02x\n",
width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]);
#endif
addr1 = (s->start_addr * 4);
bwidth = width * 4;
y_start = -1;
......@@ -1075,14 +1175,17 @@ static void vga_draw_graphic(VGAState *s, int full_update)
page_max = -1;
d = s->ds->data;
linesize = s->ds->linesize;
y1 = 0;
for(y = 0; y < height; y++) {
addr = addr1;
if (!(s->cr[0x17] & 1)) {
int shift;
/* CGA compatibility handling */
addr = (addr & ~0x2000) | ((y & 1) << 13);
shift = 14 + ((s->cr[0x17] >> 6) & 1);
addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift);
}
if (!(s->cr[0x17] & 2)) {
addr = (addr & ~0x4000) | ((y & 2) << 13);
addr = (addr & ~0x8000) | ((y1 & 2) << 14);
}
page0 = addr >> 12;
page1 = (addr + bwidth - 1) >> 12;
......@@ -1103,21 +1206,26 @@ static void vga_draw_graphic(VGAState *s, int full_update)
if (y_start >= 0) {
/* flush to display */
dpy_update(s->ds, 0, y_start,
width, y - y_start);
disp_width, y - y_start);
y_start = -1;
}
}
if (y == s->line_compare) {
addr1 = 0;
} else {
addr1 += line_offset;
if (!double_scan || (y & 1) != 0) {
if (y1 == s->line_compare) {
addr1 = 0;
} else {
mask = (s->cr[0x17] & 3) ^ 3;
if ((y1 & mask) == mask)
addr1 += line_offset;
}
y1++;
}
d += linesize;
}
if (y_start >= 0) {
/* flush to display */
dpy_update(s->ds, 0, y_start,
width, y - y_start);
disp_width, y - y_start);
}
/* reset modified pages */
if (page_max != -1) {
......@@ -1199,7 +1307,7 @@ int vga_init(DisplayState *ds, uint8_t *vga_ram_base,
unsigned long vga_ram_offset, int vga_ram_size)
{
VGAState *s = &vga_state;
int i, j, v;
int i, j, v, b;
for(i = 0;i < 256; i++) {
v = 0;
......@@ -1214,9 +1322,34 @@ int vga_init(DisplayState *ds, uint8_t *vga_ram_base,
}
expand2[i] = v;
}
for(i = 0; i < 16; i++) {
v = 0;
for(j = 0; j < 4; j++) {
b = ((i >> j) & 1);
v |= b << (2 * j);
v |= b << (2 * j + 1);
}
expand4to8[i] = v;
}
vga_reset(s);
switch(ds->depth) {
case 8:
s->rgb_to_pixel = rgb_to_pixel8_dup;
break;
case 15:
s->rgb_to_pixel = rgb_to_pixel15_dup;
break;
default:
case 16:
s->rgb_to_pixel = rgb_to_pixel16_dup;
break;
case 32:
s->rgb_to_pixel = rgb_to_pixel32_dup;
break;
}
s->vram_ptr = vga_ram_base;
s->vram_offset = vga_ram_offset;
s->vram_size = vga_ram_size;
......
......@@ -37,15 +37,11 @@
#if DEPTH != 15
static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol)
static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d,
uint32_t font_data,
uint32_t xorcol,
uint32_t bgcol)
{
uint32_t font_data, xorcol;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
#if BPP == 1
((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
((uint32_t *)d)[3] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
......@@ -64,6 +60,38 @@ static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
((uint32_t *)d)[6] = ((-(font_data >> 1) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[7] = ((-(font_data >> 0) & 1) & xorcol) ^ bgcol;
#endif
}
static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol)
{
uint32_t font_data, xorcol;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol);
font_ptr += 4;
d += linesize;
} while (--h);
}
static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol)
{
uint32_t font_data, xorcol;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
glue(vga_draw_glyph_line_, DEPTH)(d,
expand4to8[font_data >> 4],
xorcol, bgcol);
glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP,
expand4to8[font_data & 0x0f],
xorcol, bgcol);
font_ptr += 4;
d += linesize;
} while (--h);
......@@ -151,6 +179,48 @@ static void glue(vga_draw_line2_, DEPTH)(VGAState *s1, uint8_t *d,
}
}
#if BPP == 1
#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v)
#elif BPP == 2
#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v)
#else
#define PUT_PIXEL2(d, n, v) \
((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v)
#endif
/*
* 4 color mode, dup2 horizontal
*/
static void glue(vga_draw_line2d2_, DEPTH)(VGAState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t plane_mask, *palette, data, v;
int x;
palette = s1->last_palette;
plane_mask = mask16[s1->ar[0x12] & 0xf];
width >>= 3;
for(x = 0; x < width; x++) {
data = ((uint32_t *)s)[0];
data &= plane_mask;
v = expand2[GET_PLANE(data, 0)];
v |= expand2[GET_PLANE(data, 2)] << 2;
PUT_PIXEL2(d, 0, palette[v >> 12]);
PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]);
PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]);
PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]);
v = expand2[GET_PLANE(data, 1)];
v |= expand2[GET_PLANE(data, 3)] << 2;
PUT_PIXEL2(d, 4, palette[v >> 12]);
PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
d += BPP * 16;
s += 4;
}
}
/*
* 16 color mode
*/
......@@ -184,7 +254,62 @@ static void glue(vga_draw_line4_, DEPTH)(VGAState *s1, uint8_t *d,
}
/*
* 256 color mode
* 16 color mode, dup2 horizontal
*/
static void glue(vga_draw_line4d2_, DEPTH)(VGAState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t plane_mask, data, v, *palette;
int x;
palette = s1->last_palette;
plane_mask = mask16[s1->ar[0x12] & 0xf];
width >>= 3;
for(x = 0; x < width; x++) {
data = ((uint32_t *)s)[0];
data &= plane_mask;
v = expand4[GET_PLANE(data, 0)];
v |= expand4[GET_PLANE(data, 1)] << 1;
v |= expand4[GET_PLANE(data, 2)] << 2;
v |= expand4[GET_PLANE(data, 3)] << 3;
PUT_PIXEL2(d, 0, palette[v >> 28]);
PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]);
PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]);
PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]);
PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]);
PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
d += BPP * 16;
s += 4;
}
}
/*
* 256 color mode, double pixels
*
* XXX: add plane_mask support (never used in standard VGA modes)
*/
static void glue(vga_draw_line8d2_, DEPTH)(VGAState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t *palette;
int x;