Revision 17b0018b hw/vga.c
b/hw/vga.c | ||
---|---|---|
49 | 49 |
#include "thunk.h" |
50 | 50 |
|
51 | 51 |
//#define DEBUG_VGA |
52 |
//#define DEBUG_VGA_MEM |
|
52 | 53 |
|
53 | 54 |
#define MSR_COLOR_EMULATION 0x01 |
54 | 55 |
#define MSR_PAGE_SELECT 0x20 |
... | ... | |
85 | 86 |
DisplayState *ds; |
86 | 87 |
uint32_t font_offsets[2]; |
87 | 88 |
int graphic_mode; |
88 |
int shift_control; |
|
89 |
uint8_t shift_control; |
|
90 |
uint8_t double_scan; |
|
89 | 91 |
uint32_t line_offset; |
90 | 92 |
uint32_t line_compare; |
91 | 93 |
uint32_t start_addr; |
... | ... | |
93 | 95 |
uint32_t last_width, last_height; |
94 | 96 |
uint8_t cursor_start, cursor_end; |
95 | 97 |
uint32_t cursor_offset; |
98 |
unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned b); |
|
96 | 99 |
/* tell for each page if it has been updated since the last time */ |
97 | 100 |
uint8_t vram_updated[VGA_RAM_SIZE / 4096]; |
98 | 101 |
uint32_t last_palette[256]; |
99 |
#define CH_ATTR_SIZE (132 * 60)
|
|
102 |
#define CH_ATTR_SIZE (160 * 100)
|
|
100 | 103 |
uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */ |
101 | 104 |
} VGAState; |
102 | 105 |
|
... | ... | |
199 | 202 |
|
200 | 203 |
static uint32_t expand4[256]; |
201 | 204 |
static uint16_t expand2[256]; |
205 |
static uint8_t expand4to8[16]; |
|
202 | 206 |
|
203 | 207 |
VGAState vga_state; |
204 | 208 |
int vga_io_memory; |
... | ... | |
503 | 507 |
int memory_map_mode, plane, write_mode, b, func_select; |
504 | 508 |
uint32_t write_mask, bit_mask, set_mask; |
505 | 509 |
|
506 |
#ifdef DEBUG_VGA |
|
510 |
#ifdef DEBUG_VGA_MEM
|
|
507 | 511 |
printf("vga: [0x%x] = 0x%02x\n", addr, val); |
508 | 512 |
#endif |
509 | 513 |
/* convert to VGA memory offset */ |
... | ... | |
533 | 537 |
plane = addr & 3; |
534 | 538 |
if (s->sr[2] & (1 << plane)) { |
535 | 539 |
s->vram_ptr[addr] = val; |
536 |
#ifdef DEBUG_VGA |
|
540 |
#ifdef DEBUG_VGA_MEM
|
|
537 | 541 |
printf("vga: chain4: [0x%x]\n", addr); |
538 | 542 |
#endif |
539 | 543 |
s->vram_updated[addr >> 12] = 1; |
... | ... | |
544 | 548 |
if (s->sr[2] & (1 << plane)) { |
545 | 549 |
addr = ((addr & ~1) << 1) | plane; |
546 | 550 |
s->vram_ptr[addr] = val; |
547 |
#ifdef DEBUG_VGA |
|
551 |
#ifdef DEBUG_VGA_MEM
|
|
548 | 552 |
printf("vga: odd/even: [0x%x]\n", addr); |
549 | 553 |
#endif |
550 | 554 |
s->vram_updated[addr >> 12] = 1; |
... | ... | |
615 | 619 |
((uint32_t *)s->vram_ptr)[addr] = |
616 | 620 |
(((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | |
617 | 621 |
(val & write_mask); |
618 |
#ifdef DEBUG_VGA |
|
622 |
#ifdef DEBUG_VGA_MEM
|
|
619 | 623 |
printf("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n", |
620 | 624 |
addr * 4, write_mask, val); |
621 | 625 |
#endif |
... | ... | |
699 | 703 |
return (v << 2) | (b << 1) | b; |
700 | 704 |
} |
701 | 705 |
|
706 |
static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b) |
|
707 |
{ |
|
708 |
unsigned int col; |
|
709 |
col = rgb_to_pixel8(r, g, b); |
|
710 |
col |= col << 8; |
|
711 |
col |= col << 16; |
|
712 |
return col; |
|
713 |
} |
|
714 |
|
|
715 |
static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b) |
|
716 |
{ |
|
717 |
unsigned int col; |
|
718 |
col = rgb_to_pixel15(r, g, b); |
|
719 |
col |= col << 16; |
|
720 |
return col; |
|
721 |
} |
|
722 |
|
|
723 |
static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b) |
|
724 |
{ |
|
725 |
unsigned int col; |
|
726 |
col = rgb_to_pixel16(r, g, b); |
|
727 |
col |= col << 16; |
|
728 |
return col; |
|
729 |
} |
|
730 |
|
|
731 |
static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b) |
|
732 |
{ |
|
733 |
unsigned int col; |
|
734 |
col = rgb_to_pixel32(r, g, b); |
|
735 |
return col; |
|
736 |
} |
|
737 |
|
|
702 | 738 |
/* return true if the palette was modified */ |
703 | 739 |
static int update_palette16(VGAState *s) |
704 | 740 |
{ |
705 |
int full_update, i, depth;
|
|
741 |
int full_update, i; |
|
706 | 742 |
uint32_t v, col, *palette; |
707 |
unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned b); |
|
708 |
depth = s->ds->depth; |
|
709 |
switch(depth) { |
|
710 |
case 8: |
|
711 |
rgb_to_pixel = rgb_to_pixel8; |
|
712 |
break; |
|
713 |
case 15: |
|
714 |
rgb_to_pixel = rgb_to_pixel15; |
|
715 |
break; |
|
716 |
default: |
|
717 |
case 16: |
|
718 |
rgb_to_pixel = rgb_to_pixel16; |
|
719 |
break; |
|
720 |
case 32: |
|
721 |
rgb_to_pixel = rgb_to_pixel32; |
|
722 |
break; |
|
723 |
} |
|
724 | 743 |
|
725 | 744 |
full_update = 0; |
726 | 745 |
palette = s->last_palette; |
... | ... | |
731 | 750 |
else |
732 | 751 |
v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f); |
733 | 752 |
v = v * 3; |
734 |
col = rgb_to_pixel(c6_to_8(s->palette[v]), |
|
735 |
c6_to_8(s->palette[v + 1]), |
|
736 |
c6_to_8(s->palette[v + 2])); |
|
737 |
|
|
738 |
if (depth == 8) { |
|
739 |
col |= col << 8; |
|
740 |
col |= col << 16; |
|
741 |
} else if (depth <= 16) { |
|
742 |
col |= col << 16; |
|
753 |
col = s->rgb_to_pixel(c6_to_8(s->palette[v]), |
|
754 |
c6_to_8(s->palette[v + 1]), |
|
755 |
c6_to_8(s->palette[v + 2])); |
|
756 |
if (col != palette[i]) { |
|
757 |
full_update = 1; |
|
758 |
palette[i] = col; |
|
743 | 759 |
} |
744 |
// printf("%2d: %08x\n", i, col); |
|
760 |
} |
|
761 |
return full_update; |
|
762 |
} |
|
763 |
|
|
764 |
/* return true if the palette was modified */ |
|
765 |
static int update_palette256(VGAState *s) |
|
766 |
{ |
|
767 |
int full_update, i; |
|
768 |
uint32_t v, col, *palette; |
|
769 |
|
|
770 |
full_update = 0; |
|
771 |
palette = s->last_palette; |
|
772 |
v = 0; |
|
773 |
for(i = 0; i < 256; i++) { |
|
774 |
col = s->rgb_to_pixel(c6_to_8(s->palette[v]), |
|
775 |
c6_to_8(s->palette[v + 1]), |
|
776 |
c6_to_8(s->palette[v + 2])); |
|
745 | 777 |
if (col != palette[i]) { |
746 | 778 |
full_update = 1; |
747 | 779 |
palette[i] = col; |
748 | 780 |
} |
781 |
v += 3; |
|
749 | 782 |
} |
750 | 783 |
return full_update; |
751 | 784 |
} |
... | ... | |
806 | 839 |
vga_draw_glyph8_32, |
807 | 840 |
}; |
808 | 841 |
|
842 |
static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = { |
|
843 |
vga_draw_glyph16_8, |
|
844 |
vga_draw_glyph16_16, |
|
845 |
vga_draw_glyph16_16, |
|
846 |
vga_draw_glyph16_32, |
|
847 |
}; |
|
848 |
|
|
809 | 849 |
static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = { |
810 | 850 |
vga_draw_glyph9_8, |
811 | 851 |
vga_draw_glyph9_16, |
... | ... | |
882 | 922 |
cw = 8; |
883 | 923 |
if (s->sr[1] & 0x01) |
884 | 924 |
cw = 9; |
925 |
if (s->sr[1] & 0x08) |
|
926 |
cw = 16; /* NOTE: no 18 pixel wide */ |
|
885 | 927 |
x_incr = cw * ((s->ds->depth + 7) >> 3); |
886 | 928 |
width = (s->cr[0x01] + 1); |
887 |
height = s->cr[0x12] | |
|
888 |
((s->cr[0x07] & 0x02) << 7) | |
|
889 |
((s->cr[0x07] & 0x40) << 3); |
|
890 |
height = (height + 1) / cheight; |
|
929 |
if (s->cr[0x06] == 100) { |
|
930 |
/* ugly hack for CGA 160x100x16 - explain me the logic */ |
|
931 |
height = 100; |
|
932 |
} else { |
|
933 |
height = s->cr[0x12] | |
|
934 |
((s->cr[0x07] & 0x02) << 7) | |
|
935 |
((s->cr[0x07] & 0x40) << 3); |
|
936 |
height = (height + 1) / cheight; |
|
937 |
} |
|
891 | 938 |
if (width != s->last_width || height != s->last_height || |
892 | 939 |
cw != s->last_cw || cw != s->last_cw) { |
893 | 940 |
dpy_resize(s->ds, width * cw, height * cheight); |
... | ... | |
914 | 961 |
cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4; |
915 | 962 |
|
916 | 963 |
depth_index = get_depth_index(s->ds->depth); |
917 |
vga_draw_glyph8 = vga_draw_glyph8_table[depth_index]; |
|
964 |
if (cw == 16) |
|
965 |
vga_draw_glyph8 = vga_draw_glyph16_table[depth_index]; |
|
966 |
else |
|
967 |
vga_draw_glyph8 = vga_draw_glyph8_table[depth_index]; |
|
918 | 968 |
vga_draw_glyph9 = vga_draw_glyph9_table[depth_index]; |
919 | 969 |
|
920 | 970 |
dest = s->ds->data; |
... | ... | |
944 | 994 |
font_ptr += 32 * 4 * ch; |
945 | 995 |
bgcol = palette[cattr >> 4]; |
946 | 996 |
fgcol = palette[cattr & 0x0f]; |
947 |
if (cw == 8) {
|
|
997 |
if (cw != 9) {
|
|
948 | 998 |
vga_draw_glyph8(d1, linesize, |
949 | 999 |
font_ptr, cheight, fgcol, bgcol); |
950 | 1000 |
} else { |
... | ... | |
966 | 1016 |
if (line_last >= line_start && line_start < cheight) { |
967 | 1017 |
h = line_last - line_start + 1; |
968 | 1018 |
d = d1 + linesize * line_start; |
969 |
if (cw == 8) {
|
|
1019 |
if (cw != 9) {
|
|
970 | 1020 |
vga_draw_glyph8(d, linesize, |
971 | 1021 |
cursor_glyph, h, fgcol, bgcol); |
972 | 1022 |
} else { |
... | ... | |
989 | 1039 |
} |
990 | 1040 |
} |
991 | 1041 |
|
992 |
static vga_draw_line_func *vga_draw_line_table[4 * 6] = { |
|
1042 |
enum { |
|
1043 |
VGA_DRAW_LINE2, |
|
1044 |
VGA_DRAW_LINE2D2, |
|
1045 |
VGA_DRAW_LINE4, |
|
1046 |
VGA_DRAW_LINE4D2, |
|
1047 |
VGA_DRAW_LINE8D2, |
|
1048 |
VGA_DRAW_LINE8, |
|
1049 |
VGA_DRAW_LINE15, |
|
1050 |
VGA_DRAW_LINE16, |
|
1051 |
VGA_DRAW_LINE32, |
|
1052 |
VGA_DRAW_LINE_NB, |
|
1053 |
}; |
|
1054 |
|
|
1055 |
static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = { |
|
993 | 1056 |
vga_draw_line2_8, |
994 | 1057 |
vga_draw_line2_16, |
995 | 1058 |
vga_draw_line2_16, |
996 | 1059 |
vga_draw_line2_32, |
997 | 1060 |
|
1061 |
vga_draw_line2d2_8, |
|
1062 |
vga_draw_line2d2_16, |
|
1063 |
vga_draw_line2d2_16, |
|
1064 |
vga_draw_line2d2_32, |
|
1065 |
|
|
998 | 1066 |
vga_draw_line4_8, |
999 | 1067 |
vga_draw_line4_16, |
1000 | 1068 |
vga_draw_line4_16, |
1001 | 1069 |
vga_draw_line4_32, |
1002 | 1070 |
|
1071 |
vga_draw_line4d2_8, |
|
1072 |
vga_draw_line4d2_16, |
|
1073 |
vga_draw_line4d2_16, |
|
1074 |
vga_draw_line4d2_32, |
|
1075 |
|
|
1076 |
vga_draw_line8d2_8, |
|
1077 |
vga_draw_line8d2_16, |
|
1078 |
vga_draw_line8d2_16, |
|
1079 |
vga_draw_line8d2_32, |
|
1080 |
|
|
1003 | 1081 |
vga_draw_line8_8, |
1004 | 1082 |
vga_draw_line8_16, |
1005 | 1083 |
vga_draw_line8_16, |
... | ... | |
1029 | 1107 |
*/ |
1030 | 1108 |
static void vga_draw_graphic(VGAState *s, int full_update) |
1031 | 1109 |
{ |
1032 |
int y, update, page_min, page_max, linesize, y_start;
|
|
1110 |
int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask;
|
|
1033 | 1111 |
int width, height, shift_control, line_offset, page0, page1, bwidth; |
1112 |
int disp_width; |
|
1034 | 1113 |
uint8_t *d; |
1035 | 1114 |
uint32_t v, addr1, addr; |
1036 | 1115 |
vga_draw_line_func *vga_draw_line; |
1037 |
|
|
1038 |
full_update |= update_palette16(s); |
|
1039 |
|
|
1116 |
|
|
1040 | 1117 |
full_update |= update_basic_params(s); |
1041 | 1118 |
|
1042 | 1119 |
width = (s->cr[0x01] + 1) * 8; |
... | ... | |
1044 | 1121 |
((s->cr[0x07] & 0x02) << 7) | |
1045 | 1122 |
((s->cr[0x07] & 0x40) << 3); |
1046 | 1123 |
height = (height + 1); |
1047 |
|
|
1048 |
if (width != s->last_width || |
|
1049 |
height != s->last_height) { |
|
1050 |
dpy_resize(s->ds, width, height); |
|
1051 |
s->last_width = width; |
|
1052 |
s->last_height = height; |
|
1053 |
full_update = 1; |
|
1054 |
} |
|
1055 |
|
|
1124 |
disp_width = width; |
|
1125 |
|
|
1056 | 1126 |
shift_control = (s->gr[0x05] >> 5) & 3; |
1057 |
if (shift_control != s->shift_control) { |
|
1127 |
double_scan = (s->cr[0x09] & 0x80); |
|
1128 |
if (shift_control != s->shift_control || |
|
1129 |
double_scan != s->double_scan) { |
|
1058 | 1130 |
full_update = 1; |
1059 | 1131 |
s->shift_control = shift_control; |
1132 |
s->double_scan = double_scan; |
|
1060 | 1133 |
} |
1061 | 1134 |
|
1062 |
if (shift_control == 0) |
|
1063 |
v = 1; /* 4 bit/pixel */ |
|
1064 |
else if (shift_control == 1) |
|
1065 |
v = 0; /* 2 bit/pixel */ |
|
1066 |
else |
|
1067 |
v = 2; /* 8 bit/pixel */ |
|
1135 |
if (shift_control == 0) { |
|
1136 |
full_update |= update_palette16(s); |
|
1137 |
if (s->sr[0x01] & 8) { |
|
1138 |
v = VGA_DRAW_LINE4D2; |
|
1139 |
disp_width <<= 1; |
|
1140 |
} else { |
|
1141 |
v = VGA_DRAW_LINE4; |
|
1142 |
} |
|
1143 |
} else if (shift_control == 1) { |
|
1144 |
full_update |= update_palette16(s); |
|
1145 |
if (s->sr[0x01] & 8) { |
|
1146 |
v = VGA_DRAW_LINE2D2; |
|
1147 |
disp_width <<= 1; |
|
1148 |
} else { |
|
1149 |
v = VGA_DRAW_LINE2; |
|
1150 |
} |
|
1151 |
} else { |
|
1152 |
full_update |= update_palette256(s); |
|
1153 |
v = VGA_DRAW_LINE8D2; |
|
1154 |
double_scan = 1; /* XXX: explain me why it is always activated */ |
|
1155 |
} |
|
1068 | 1156 |
vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->ds->depth)]; |
1069 |
|
|
1157 |
|
|
1158 |
if (disp_width != s->last_width || |
|
1159 |
height != s->last_height) { |
|
1160 |
dpy_resize(s->ds, disp_width, height); |
|
1161 |
s->last_width = disp_width; |
|
1162 |
s->last_height = height; |
|
1163 |
full_update = 1; |
|
1164 |
} |
|
1165 |
|
|
1070 | 1166 |
line_offset = s->line_offset; |
1167 |
#if 0 |
|
1168 |
printf("w=%d h=%d v=%d line_offset=%d double_scan=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=%02x\n", |
|
1169 |
width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]); |
|
1170 |
#endif |
|
1071 | 1171 |
addr1 = (s->start_addr * 4); |
1072 | 1172 |
bwidth = width * 4; |
1073 | 1173 |
y_start = -1; |
... | ... | |
1075 | 1175 |
page_max = -1; |
1076 | 1176 |
d = s->ds->data; |
1077 | 1177 |
linesize = s->ds->linesize; |
1178 |
y1 = 0; |
|
1078 | 1179 |
for(y = 0; y < height; y++) { |
1079 | 1180 |
addr = addr1; |
1080 | 1181 |
if (!(s->cr[0x17] & 1)) { |
1182 |
int shift; |
|
1081 | 1183 |
/* CGA compatibility handling */ |
1082 |
addr = (addr & ~0x2000) | ((y & 1) << 13); |
|
1184 |
shift = 14 + ((s->cr[0x17] >> 6) & 1); |
|
1185 |
addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift); |
|
1083 | 1186 |
} |
1084 | 1187 |
if (!(s->cr[0x17] & 2)) { |
1085 |
addr = (addr & ~0x4000) | ((y & 2) << 13);
|
|
1188 |
addr = (addr & ~0x8000) | ((y1 & 2) << 14);
|
|
1086 | 1189 |
} |
1087 | 1190 |
page0 = addr >> 12; |
1088 | 1191 |
page1 = (addr + bwidth - 1) >> 12; |
... | ... | |
1103 | 1206 |
if (y_start >= 0) { |
1104 | 1207 |
/* flush to display */ |
1105 | 1208 |
dpy_update(s->ds, 0, y_start, |
1106 |
width, y - y_start); |
|
1209 |
disp_width, y - y_start);
|
|
1107 | 1210 |
y_start = -1; |
1108 | 1211 |
} |
1109 | 1212 |
} |
1110 |
if (y == s->line_compare) { |
|
1111 |
addr1 = 0; |
|
1112 |
} else { |
|
1113 |
addr1 += line_offset; |
|
1213 |
if (!double_scan || (y & 1) != 0) { |
|
1214 |
if (y1 == s->line_compare) { |
|
1215 |
addr1 = 0; |
|
1216 |
} else { |
|
1217 |
mask = (s->cr[0x17] & 3) ^ 3; |
|
1218 |
if ((y1 & mask) == mask) |
|
1219 |
addr1 += line_offset; |
|
1220 |
} |
|
1221 |
y1++; |
|
1114 | 1222 |
} |
1115 | 1223 |
d += linesize; |
1116 | 1224 |
} |
1117 | 1225 |
if (y_start >= 0) { |
1118 | 1226 |
/* flush to display */ |
1119 | 1227 |
dpy_update(s->ds, 0, y_start, |
1120 |
width, y - y_start); |
|
1228 |
disp_width, y - y_start);
|
|
1121 | 1229 |
} |
1122 | 1230 |
/* reset modified pages */ |
1123 | 1231 |
if (page_max != -1) { |
... | ... | |
1199 | 1307 |
unsigned long vga_ram_offset, int vga_ram_size) |
1200 | 1308 |
{ |
1201 | 1309 |
VGAState *s = &vga_state; |
1202 |
int i, j, v; |
|
1310 |
int i, j, v, b;
|
|
1203 | 1311 |
|
1204 | 1312 |
for(i = 0;i < 256; i++) { |
1205 | 1313 |
v = 0; |
... | ... | |
1214 | 1322 |
} |
1215 | 1323 |
expand2[i] = v; |
1216 | 1324 |
} |
1325 |
for(i = 0; i < 16; i++) { |
|
1326 |
v = 0; |
|
1327 |
for(j = 0; j < 4; j++) { |
|
1328 |
b = ((i >> j) & 1); |
|
1329 |
v |= b << (2 * j); |
|
1330 |
v |= b << (2 * j + 1); |
|
1331 |
} |
|
1332 |
expand4to8[i] = v; |
|
1333 |
} |
|
1217 | 1334 |
|
1218 | 1335 |
vga_reset(s); |
1219 | 1336 |
|
1337 |
switch(ds->depth) { |
|
1338 |
case 8: |
|
1339 |
s->rgb_to_pixel = rgb_to_pixel8_dup; |
|
1340 |
break; |
|
1341 |
case 15: |
|
1342 |
s->rgb_to_pixel = rgb_to_pixel15_dup; |
|
1343 |
break; |
|
1344 |
default: |
|
1345 |
case 16: |
|
1346 |
s->rgb_to_pixel = rgb_to_pixel16_dup; |
|
1347 |
break; |
|
1348 |
case 32: |
|
1349 |
s->rgb_to_pixel = rgb_to_pixel32_dup; |
|
1350 |
break; |
|
1351 |
} |
|
1352 |
|
|
1220 | 1353 |
s->vram_ptr = vga_ram_base; |
1221 | 1354 |
s->vram_offset = vga_ram_offset; |
1222 | 1355 |
s->vram_size = vga_ram_size; |
Also available in: Unified diff