1 /* Copyright (C) 1991, 1995, 1996, 1997, 1998, 1999 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevsvga.c,v 1.2.6.1.2.1 2003/01/17 00:49:01 giles Exp $ */
20 /* SuperVGA display drivers */
21 #include "memory_.h"
22 #include "gconfigv.h"		/* for USE_ASM */
23 #include "gx.h"
24 #include "gserrors.h"
25 #include "gxarith.h"		/* for ...log2 */
26 #include "gxdevice.h"
27 #include "gdevpccm.h"
28 #include "gdevpcfb.h"
29 #include "gdevsvga.h"
30 #include "gsparam.h"
31 
32 /* The color map for dynamically assignable colors. */
33 #define first_dc_index 64
34 private int next_dc_index;
35 
36 #define dc_hash_size 293	/* prime, >num_dc */
37 typedef struct {
38     ushort rgb, index;
39 } dc_entry;
40 private dc_entry dynamic_colors[dc_hash_size + 1];
41 
42 #define num_colors 255
43 
44 /* Macro for casting gx_device argument */
45 #define fb_dev ((gx_device_svga *)dev)
46 
47 /* Procedure records */
48 #define svga_procs(open) {\
49 	open, NULL /*get_initial_matrix*/,\
50 	NULL /*sync_output*/, NULL /*output_page*/, svga_close,\
51 	svga_map_rgb_color, svga_map_color_rgb,\
52 	svga_fill_rectangle, NULL /*tile_rectangle*/,\
53 	svga_copy_mono, svga_copy_color, NULL /*draw_line*/,\
54 	svga_get_bits, NULL /*get_params*/, svga_put_params,\
55 	NULL /*map_cmyk_color*/, NULL /*get_xfont_procs*/,\
56 	NULL /*get_xfont_device*/, NULL /*map_rgb_alpha_color*/,\
57 	gx_page_device_get_page_device, NULL /*get_alpha_bits*/,\
58 	svga_copy_alpha\
59 }
60 
61 /* Save the controller mode */
62 private int svga_save_mode = -1;
63 
64 /* ------ Internal routines ------ */
65 
66 #define regen 0xa000
67 
68 /* Construct a pointer for writing a pixel. */
69 /* Assume 64K pages, 64K granularity. */
70 /* We know that y is within bounds. */
71 #define set_pixel_ptr(ptr, fbdev, x, y, wnum)\
72 {	ulong index = (ulong)(y) * fbdev->raster + (uint)(x);\
73 	if ( (uint)(index >> 16) != fbdev->current_page )\
74 	   {	(*fbdev->set_page)(fbdev, (fbdev->current_page = index >> 16), wnum);\
75 	   }\
76 	ptr = (fb_ptr)MK_PTR(regen, (ushort)index);\
77 }
78 #define set_pixel_write_ptr(ptr, fbdev, x, y)\
79   set_pixel_ptr(ptr, fbdev, x, y, fbdev->wnum_write)
80 #define set_pixel_read_ptr(ptr, fbdev, x, y)\
81   set_pixel_ptr(ptr, fbdev, x, y, fbdev->wnum_read)
82 
83 /* Find the graphics mode for a desired width and height. */
84 /* Set the mode in the device structure and return 0, */
85 /* or return an error code. */
86 int
svga_find_mode(gx_device * dev,const mode_info * mip)87 svga_find_mode(gx_device * dev, const mode_info * mip)
88 {
89     for (;; mip++) {
90 	if (mip->width >= fb_dev->width &&
91 	    mip->height >= fb_dev->height ||
92 	    mip[1].mode < 0
93 	    ) {
94 	    fb_dev->mode = mip;
95 	    gx_device_adjust_resolution(dev, mip->width, mip->height, 1);
96 	    fb_dev->raster = fb_dev->width;
97 	    return 0;
98 	}
99     }
100     return_error(gs_error_rangecheck);
101 }
102 
103 /* Set the index for writing into the color DAC. */
104 #define svga_dac_set_write_index(i) outportb(0x3c8, i)
105 
106 /* Write 6-bit R,G,B values into the color DAC. */
107 #define svga_dac_write(r, g, b)\
108   (outportb(0x3c9, r), outportb(0x3c9, g), outportb(0x3c9, b))
109 
110 /* ------ Common procedures ------ */
111 
112 #define cv_bits(v,n) (v >> (gx_color_value_bits - n))
113 
114 /* Initialize the dynamic color table, if any. */
115 void
svga_init_colors(gx_device * dev)116 svga_init_colors(gx_device * dev)
117 {
118     if (fb_dev->fixed_colors)
119 	next_dc_index = num_colors;
120     else {
121 	memset(dynamic_colors, 0,
122 	       (dc_hash_size + 1) * sizeof(dc_entry));
123 	next_dc_index = first_dc_index;
124     }
125 }
126 
127 /* Load the color DAC with the predefined colors. */
128 private void
svga_load_colors(gx_device * dev)129 svga_load_colors(gx_device * dev)
130 {
131     int ci;
132 
133     svga_dac_set_write_index(0);
134     if (fb_dev->fixed_colors)
135 	for (ci = 0; ci < num_colors; ci++) {
136 	    gx_color_value rgb[3];
137 
138 	    pc_8bit_map_color_rgb(dev, (gx_color_index) ci, rgb);
139 	    svga_dac_write(cv_bits(rgb[0], 6), cv_bits(rgb[1], 6),
140 			   cv_bits(rgb[2], 6));
141     } else
142 	for (ci = 0; ci < 64; ci++) {
143 	    static const byte c2[10] =
144 	    {0, 42, 0, 0, 0, 0, 0, 0, 21, 63};
145 
146 	    svga_dac_write(c2[(ci >> 2) & 9], c2[(ci >> 1) & 9],
147 			   c2[ci & 9]);
148 	}
149 }
150 
151 /* Initialize the device structure and the DACs. */
152 int
svga_open(gx_device * dev)153 svga_open(gx_device * dev)
154 {
155     fb_dev->x_pixels_per_inch =
156 	fb_dev->y_pixels_per_inch =
157 	fb_dev->height / PAGE_HEIGHT_INCHES;
158     /* Set the display mode. */
159     if (svga_save_mode < 0)
160 	svga_save_mode = (*fb_dev->get_mode) ();
161     (*fb_dev->set_mode) (fb_dev->mode->mode);
162     svga_init_colors(dev);
163     svga_load_colors(dev);
164     fb_dev->current_page = -1;
165     return 0;
166 }
167 
168 /* Close the device; reinitialize the display for text mode. */
169 int
svga_close(gx_device * dev)170 svga_close(gx_device * dev)
171 {
172     if (svga_save_mode >= 0)
173 	(*fb_dev->set_mode) (svga_save_mode);
174     svga_save_mode = -1;
175     return 0;
176 }
177 
178 /* Map a r-g-b color to a palette index. */
179 /* The first 64 entries of the color map are set */
180 /* for compatibility with the older display modes: */
181 /* these are indexed as 0.0.R0.G0.B0.R1.G1.B1. */
182 gx_color_index
svga_map_rgb_color(gx_device * dev,gx_color_value r,gx_color_value g,gx_color_value b)183 svga_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g,
184 		   gx_color_value b)
185 {
186     ushort rgb;
187 
188     if (fb_dev->fixed_colors) {
189 	gx_color_index ci = pc_8bit_map_rgb_color(dev, r, g, b);
190 
191 	/* Here is where we should permute the index to match */
192 	/* the old color map... but we don't yet. */
193 	return ci;
194     } {
195 	ushort r5 = cv_bits(r, 5), g5 = cv_bits(g, 5), b5 = cv_bits(b, 5);
196 	static const byte cube_bits[32] =
197 	{0, 128, 128, 128, 128, 128, 128, 128, 128, 128,
198 	 8, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
199 	 1, 128, 128, 128, 128, 128, 128, 128, 128, 128,
200 	 9
201 	};
202 	uint cx = ((uint) cube_bits[r5] << 2) +
203 	((uint) cube_bits[g5] << 1) +
204 	(uint) cube_bits[b5];
205 
206 	/* Check for a color on the cube. */
207 	if (cx < 64)
208 	    return (gx_color_index) cx;
209 	/* Not on the cube, check the dynamic color table. */
210 	rgb = (r5 << 10) + (g5 << 5) + b5;
211     }
212     {
213 	register dc_entry *pdc;
214 
215 	for (pdc = &dynamic_colors[rgb % dc_hash_size];
216 	     pdc->rgb != 0; pdc++
217 	    )
218 	    if (pdc->rgb == rgb)
219 		return (gx_color_index) (pdc->index);
220 	if (pdc == &dynamic_colors[dc_hash_size]) {	/* Wraparound */
221 	    for (pdc = &dynamic_colors[0]; pdc->rgb != 0; pdc++)
222 		if (pdc->rgb == rgb)
223 		    return (gx_color_index) (pdc->index);
224 	}
225 	if (next_dc_index == num_colors) {	/* No space left, report failure. */
226 	    return gx_no_color_index;
227 	}
228 	/* Not on the cube, and not in the dynamic table. */
229 	/* Put in the dynamic table if space available. */
230 	{
231 	    int i = next_dc_index++;
232 
233 	    pdc->rgb = rgb;
234 	    pdc->index = i;
235 	    svga_dac_set_write_index(i);
236 	    svga_dac_write(cv_bits(r, 6), cv_bits(g, 6),
237 			   cv_bits(b, 6));
238 	    return (gx_color_index) i;
239 	}
240     }
241 }
242 
243 /* Map a color code to r-g-b. */
244 /* This routine must invert the transformation of the one above. */
245 /* Since this is practically never used, we just read the DAC. */
246 int
svga_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])247 svga_map_color_rgb(gx_device * dev, gx_color_index color,
248 		   gx_color_value prgb[3])
249 {
250     uint cval;
251 
252     outportb(0x3c7, (byte) color);
253 #define dacin() (cval = inportb(0x3c9) >> 1,\
254   ((cval << 11) + (cval << 6) + (cval << 1) + (cval >> 4)) >>\
255    (16 - gx_color_value_bits))
256     prgb[0] = dacin();
257     prgb[1] = dacin();
258     prgb[2] = dacin();
259 #undef dacin
260     return 0;
261 }
262 
263 /* Fill a rectangle. */
264 int
svga_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)265 svga_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
266 		    gx_color_index color)
267 {
268     uint raster = fb_dev->raster;
269     ushort limit = (ushort) - raster;
270     int yi;
271     fb_ptr ptr;
272 
273     fit_fill(dev, x, y, w, h);
274     set_pixel_write_ptr(ptr, fb_dev, x, y);
275     /* Most fills are very small and don't cross a page boundary. */
276     yi = h;
277     switch (w) {
278 	case 0:
279 	    return 0;		/* no-op */
280 	case 1:
281 	    while (--yi >= 0 && PTR_OFF(ptr) < limit)
282 		ptr[0] = (byte) color,
283 		    ptr += raster;
284 	    if (!++yi)
285 		return 0;
286 	    break;
287 	case 2:
288 	    while (--yi >= 0 && PTR_OFF(ptr) < limit)
289 		ptr[0] = ptr[1] = (byte) color,
290 		    ptr += raster;
291 	    if (!++yi)
292 		return 0;
293 	    break;
294 	case 3:
295 	    while (--yi >= 0 && PTR_OFF(ptr) < limit)
296 		ptr[0] = ptr[1] = ptr[2] = (byte) color,
297 		    ptr += raster;
298 	    if (!++yi)
299 		return 0;
300 	    break;
301 	case 4:
302 	    while (--yi >= 0 && PTR_OFF(ptr) < limit)
303 		ptr[0] = ptr[1] = ptr[2] = ptr[3] = (byte) color,
304 		    ptr += raster;
305 	    if (!++yi)
306 		return 0;
307 	    break;
308 	default:
309 	    if (w < 0)
310 		return 0;
311 	    /* Check for erasepage. */
312 	    if (w == dev->width && h == dev->height &&
313 		color < first_dc_index
314 		)
315 		svga_init_colors(dev);
316     }
317     while (--yi >= 0) {
318 	if (PTR_OFF(ptr) < limit) {
319 	    memset(ptr, (byte) color, w);
320 	    ptr += raster;
321 	} else if (PTR_OFF(ptr) <= (ushort) (-w)) {
322 	    memset(ptr, (byte) color, w);
323 	    if (yi > 0)
324 		set_pixel_write_ptr(ptr, fb_dev, x, y + h - yi);
325 	} else {
326 	    uint left = (uint) 0x10000 - PTR_OFF(ptr);
327 
328 	    memset(ptr, (byte) color, left);
329 	    set_pixel_write_ptr(ptr, fb_dev, x + left, y + h - 1 - yi);
330 	    memset(ptr, (byte) color, w - left);
331 	    ptr += raster - left;
332 	}
333     }
334     return 0;
335 }
336 
337 /* Copy a monochrome bitmap.  The colors are given explicitly. */
338 /* Color = gx_no_color_index means transparent (no effect on the image). */
339 int
svga_copy_mono(gx_device * dev,const byte * base,int sourcex,int sraster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index czero,gx_color_index cone)340 svga_copy_mono(gx_device * dev,
341 	       const byte * base, int sourcex, int sraster, gx_bitmap_id id,
342       int x, int y, int w, int h, gx_color_index czero, gx_color_index cone)
343 {
344     uint raster = fb_dev->raster;
345     ushort limit;
346     register int wi;
347     uint skip;
348     int yi;
349     register fb_ptr ptr = (fb_ptr) 0;
350     const byte *srow;
351     uint invert;
352 
353     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
354     limit = (ushort) - w;
355     skip = raster - w + 1;
356     srow = base + (sourcex >> 3);
357 #define izero (int)czero
358 #define ione (int)cone
359     if (ione == no_color) {
360 	gx_color_index temp;
361 
362 	if (izero == no_color)
363 	    return 0;		/* no-op */
364 	temp = czero;
365 	czero = cone;
366 	cone = temp;
367 	invert = ~0;
368     } else
369 	invert = 0;
370     /* Pre-filling saves us a test in the loop, */
371     /* and since tiling is uncommon, we come out ahead. */
372     if (izero != no_color)
373 	svga_fill_rectangle(dev, x, y, w, h, czero);
374     for (yi = 0; yi < h; yi++) {
375 	const byte *sptr = srow;
376 	uint bits;
377 	int bitno = sourcex & 7;
378 
379 	wi = w;
380 	if (PTR_OFF(ptr) <= skip) {
381 	    set_pixel_write_ptr(ptr, fb_dev, x, y + yi);
382 	} else if (PTR_OFF(ptr) > limit) {	/* We're crossing a page boundary. */
383 	    /* This is extremely rare, so it doesn't matter */
384 	    /* how slow it is. */
385 	    int xi = (ushort) - PTR_OFF(ptr);
386 
387 	    svga_copy_mono(dev, srow, sourcex & 7, sraster,
388 			   gx_no_bitmap_id, x, y + yi, xi, 1,
389 			   gx_no_color_index, cone);
390 	    set_pixel_write_ptr(ptr, fb_dev, x + xi, y + yi);
391 	    sptr = srow - (sourcex >> 3) + ((sourcex + xi) >> 3);
392 	    bitno = (sourcex + xi) & 7;
393 	    wi -= xi;
394 	}
395 	bits = *sptr ^ invert;
396 	switch (bitno) {
397 #define ifbit(msk)\
398   if ( bits & msk ) *ptr = (byte)ione;\
399   if ( !--wi ) break; ptr++
400 	    case 0:
401 	      bit0:ifbit(0x80);
402 	    case 1:
403 		ifbit(0x40);
404 	    case 2:
405 		ifbit(0x20);
406 	    case 3:
407 		ifbit(0x10);
408 	    case 4:
409 		ifbit(0x08);
410 	    case 5:
411 		ifbit(0x04);
412 	    case 6:
413 		ifbit(0x02);
414 	    case 7:
415 		ifbit(0x01);
416 #undef ifbit
417 		bits = *++sptr ^ invert;
418 		goto bit0;
419 	}
420 	ptr += skip;
421 	srow += sraster;
422     }
423 #undef izero
424 #undef ione
425     return 0;
426 }
427 
428 /* Copy a color pixelmap.  This is just like a bitmap, */
429 /* except that each pixel takes 8 bits instead of 1. */
430 int
svga_copy_color(gx_device * dev,const byte * base,int sourcex,int sraster,gx_bitmap_id id,int x,int y,int w,int h)431 svga_copy_color(gx_device * dev,
432 		const byte * base, int sourcex, int sraster, gx_bitmap_id id,
433 		int x, int y, int w, int h)
434 {
435     int xi, yi;
436     int skip;
437     const byte *sptr;
438     fb_ptr ptr;
439 
440     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
441     skip = sraster - w;
442     sptr = base + sourcex;
443     for (yi = y; yi - y < h; yi++) {
444 	ptr = 0;
445 	for (xi = x; xi - x < w; xi++) {
446 	    if (PTR_OFF(ptr) == 0)
447 		set_pixel_write_ptr(ptr, fb_dev, xi, yi);
448 	    *ptr++ = *sptr++;
449 	}
450 	sptr += skip;
451     }
452     return 0;
453 }
454 
455 /* Put parameters. */
456 int
svga_put_params(gx_device * dev,gs_param_list * plist)457 svga_put_params(gx_device * dev, gs_param_list * plist)
458 {
459     int ecode = 0;
460     int code;
461     const char *param_name;
462 
463     if ((code = ecode) < 0 ||
464 	(code = gx_default_put_params(dev, plist)) < 0
465 	) {
466     }
467     return code;
468 }
469 
470 /* Read scan lines back from the frame buffer. */
471 int
svga_get_bits(gx_device * dev,int y,byte * data,byte ** actual_data)472 svga_get_bits(gx_device * dev, int y, byte * data, byte ** actual_data)
473 {
474     uint bytes_per_row = dev->width;
475     ushort limit = (ushort) - bytes_per_row;
476     fb_ptr src;
477 
478     if (y < 0 || y >= dev->height)
479 	return gs_error_rangecheck;
480     set_pixel_read_ptr(src, fb_dev, 0, y);
481     /* The logic here is similar to fill_rectangle. */
482     if (PTR_OFF(src) <= limit)
483 	memcpy(data, src, bytes_per_row);
484     else {
485 	uint left = (uint) 0x10000 - PTR_OFF(src);
486 
487 	memcpy(data, src, left);
488 	set_pixel_read_ptr(src, fb_dev, left, y);
489 	memcpy(data + left, src, bytes_per_row - left);
490     }
491     if (actual_data != 0)
492 	*actual_data = data;
493     return 0;
494 }
495 
496 /* Copy an alpha-map to the screen. */
497 /* Depth is 1, 2, or 4. */
498 private int
svga_copy_alpha(gx_device * dev,const byte * base,int sourcex,int sraster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color,int depth)499 svga_copy_alpha(gx_device * dev, const byte * base, int sourcex,
500 		int sraster, gx_bitmap_id id, int x, int y, int w, int h,
501 		gx_color_index color, int depth)
502 {
503     int xi, yi;
504     int skip;
505     const byte *sptr;
506     byte mask;
507     int ishift;
508 
509     /* We fake alpha by interpreting it as saturation, i.e., */
510     /* alpha = 0 is white, alpha = 1 is the full color. */
511     byte shades[16];
512     gx_color_value rgb[3];
513     int log2_depth = depth >> 1;	/* works for 1,2,4 */
514     int n1 = (1 << depth) - 1;
515 
516     fit_copy(dev, base, sourcex, sraster, id, x, y, w, h);
517     shades[0] = (byte) svga_map_rgb_color(dev, gx_max_color_value,
518 					  gx_max_color_value,
519 					  gx_max_color_value);
520     shades[n1] = (byte) color;
521     if (n1 > 1) {
522 	memset(shades + 1, 255, n1 - 1);
523 	svga_map_color_rgb(dev, color, rgb);
524     }
525     skip = sraster - ((w * depth) >> 3);
526     sptr = base + (sourcex >> (3 - log2_depth));
527     mask = n1;
528     ishift = (~sourcex & (7 >> log2_depth)) << log2_depth;
529     for (yi = y; yi - y < h; yi++) {
530 	fb_ptr ptr = 0;
531 	int shift = ishift;
532 
533 	for (xi = x; xi - x < w; xi++, ptr++) {
534 	    uint a = (*sptr >> shift) & mask;
535 
536 	    if (PTR_OFF(ptr) == 0)
537 		set_pixel_write_ptr(ptr, fb_dev, xi, yi);
538 	  map:if (a != 0) {
539 		byte ci = shades[a];
540 
541 		if (ci == 255) {	/* Map the color now. */
542 #define make_shade(v, alpha, n1)\
543   (gx_max_color_value -\
544    ((ulong)(gx_max_color_value - (v)) * (alpha) / (n1)))
545 		    gx_color_value r =
546 		    make_shade(rgb[0], a, n1);
547 		    gx_color_value g =
548 		    make_shade(rgb[1], a, n1);
549 		    gx_color_value b =
550 		    make_shade(rgb[2], a, n1);
551 		    gx_color_index sci =
552 		    svga_map_rgb_color(dev, r, g, b);
553 
554 		    if (sci == gx_no_color_index) {
555 			a += (n1 + 1 - a) >> 1;
556 			goto map;
557 		    }
558 		    shades[a] = ci = (byte) sci;
559 		}
560 		*ptr = ci;
561 	    }
562 	    if (shift == 0)
563 		shift = 8 - depth, sptr++;
564 	    else
565 		shift -= depth;
566 	}
567 	sptr += skip;
568     }
569     return 0;
570 }
571 
572 /* ------ The VESA device ------ */
573 
574 private dev_proc_open_device(vesa_open);
575 private const gx_device_procs vesa_procs = svga_procs(vesa_open);
576 int vesa_get_mode(P0());
577 void vesa_set_mode(P1(int));
578 private void vesa_set_page(P3(gx_device_svga *, int, int));
579 gx_device_svga far_data gs_vesa_device =
580 svga_device(vesa_procs, "vesa", vesa_get_mode, vesa_set_mode, vesa_set_page);
581 
582 /* Define the structures for information returned by the BIOS. */
583 #define bits_include(a, m) !(~(a) & (m))
584 /* Information about the BIOS capabilities. */
585 typedef struct {
586     byte vesa_signature[4];	/* "VESA" */
587     ushort vesa_version;
588     char *product_info;		/* product name string */
589     byte capabilities[4];	/* (undefined) */
590     ushort *mode_list;		/* supported video modes, -1 ends */
591 } vga_bios_info;
592 
593 /* Information about an individual VESA mode. */
594 typedef enum {
595     m_supported = 1,
596     m_graphics = 0x10
597 } mode_attribute;
598 typedef enum {
599     w_supported = 1,
600     w_readable = 2,
601     w_writable = 4
602 } win_attribute;
603 typedef struct {
604     ushort mode_attributes;
605     byte win_a_attributes;
606     byte win_b_attributes;
607     ushort win_granularity;
608     ushort win_size;
609     ushort win_a_segment;
610     ushort win_b_segment;
611     void (*win_func_ptr) (P2(int, int));
612     ushort bytes_per_line;
613     /* Optional information */
614     ushort x_resolution;
615     ushort y_resolution;
616     byte x_char_size;
617     byte y_char_size;
618     byte number_of_planes;
619     byte bits_per_pixel;
620     byte number_of_banks;
621     byte memory_model;
622     byte bank_size;
623     /* Padding to 256 bytes */
624     byte _padding[256 - 29];
625 } vesa_info;
626 
627 /* Read the device mode */
628 int
vesa_get_mode(void)629 vesa_get_mode(void)
630 {
631     registers regs;
632 
633     regs.h.ah = 0x4f;
634     regs.h.al = 0x03;
635     int86(0x10, &regs, &regs);
636     return regs.rshort.bx;
637 }
638 
639 /* Set the device mode */
640 void
vesa_set_mode(int mode)641 vesa_set_mode(int mode)
642 {
643     registers regs;
644 
645     regs.h.ah = 0x4f;
646     regs.h.al = 0x02;
647     regs.rshort.bx = mode;
648     int86(0x10, &regs, &regs);
649 }
650 
651 /* Read information about a device mode */
652 private int
vesa_get_info(int mode,vesa_info _ss * info)653 vesa_get_info(int mode, vesa_info _ss * info)
654 {
655     registers regs;
656     struct SREGS sregs;
657 
658     regs.h.ah = 0x4f;
659     regs.h.al = 0x01;
660     regs.rshort.cx = mode;
661     segread(&sregs);
662     sregs.es = sregs.ss;
663     regs.rshort.di = PTR_OFF(info);
664     int86x(0x10, &regs, &regs, &sregs);
665 #ifdef DEBUG
666     if (regs.h.ah == 0 && regs.h.al == 0x4f)
667 	dlprintf8("vesa_get_info(%x): ma=%x wa=%x/%x wg=%x ws=%x wseg=%x/%x\n",
668 		  mode, info->mode_attributes,
669 		  info->win_a_attributes, info->win_b_attributes,
670 		  info->win_granularity, info->win_size,
671 		  info->win_a_segment, info->win_b_segment);
672     else
673 	dlprintf3("vesa_get_info(%x) failed: ah=%x al=%x\n",
674 		  mode, regs.h.ah, regs.h.al);
675 #endif
676     return (regs.h.ah == 0 && regs.h.al == 0x4f ? 0 : -1);
677 }
678 
679 /* Initialize the graphics mode. */
680 /* Shared routine to look up a VESA-compatible BIOS mode. */
681 private int
vesa_find_mode(gx_device * dev,const mode_info * mode_table)682 vesa_find_mode(gx_device * dev, const mode_info * mode_table)
683 {				/* Select the proper video mode */
684     vesa_info info;
685     const mode_info *mip;
686 
687     for (mip = mode_table; mip->mode >= 0; mip++) {
688 	if (mip->width >= fb_dev->width &&
689 	    mip->height >= fb_dev->height &&
690 	    vesa_get_info(mip->mode, &info) >= 0 &&
691 	    bits_include(info.mode_attributes,
692 			 m_supported | m_graphics) &&
693 	    info.win_granularity <= 64 &&
694 	    (info.win_granularity & (info.win_granularity - 1)) == 0 &&
695 	    info.win_size == 64 &&
696 	    bits_include(info.win_a_attributes,
697 			 w_supported) &&
698 	    info.win_a_segment == regen
699 	    ) {			/* Make sure we can both read & write. */
700 	    /* Initialize for the default case. */
701 	    fb_dev->wnum_read = 0;
702 	    fb_dev->wnum_write = 0;
703 	    if (bits_include(info.win_a_attributes,
704 			     w_readable | w_writable)
705 		)
706 		break;
707 	    else if (info.win_b_segment == regen &&
708 		     bits_include(info.win_b_attributes,
709 				  w_supported) &&
710 		     bits_include(info.win_a_attributes |
711 				  info.win_b_attributes,
712 				  w_readable | w_writable)
713 		) {		/* Two superimposed windows. */
714 		if (!bits_include(info.win_a_attributes,
715 				  w_writable)
716 		    )
717 		    fb_dev->wnum_write = 1;
718 		else
719 		    fb_dev->wnum_read = 1;
720 	    }
721 	    break;
722 	}
723     }
724     if (mip->mode < 0)
725 	return_error(gs_error_rangecheck);	/* mode not available */
726     fb_dev->mode = mip;
727     gx_device_adjust_resolution(dev, mip->width, mip->height, 1);
728     fb_dev->info.vesa.bios_set_page = info.win_func_ptr;
729     fb_dev->info.vesa.pn_shift = ilog2(64 / info.win_granularity);
730     /* Reset the raster per the VESA info. */
731     fb_dev->raster = info.bytes_per_line;
732     return 0;
733 }
734 private int
vesa_open(gx_device * dev)735 vesa_open(gx_device * dev)
736 {
737     static const mode_info mode_table[] =
738     {
739 	{640, 400, 0x100},
740 	{640, 480, 0x101},
741 	{800, 600, 0x103},
742 	{1024, 768, 0x105},
743 	{1280, 1024, 0x107},
744 	{-1, -1, -1}
745     };
746     int code = vesa_find_mode(dev, mode_table);
747 
748     if (code < 0)
749 	return code;
750     return svga_open(dev);
751 }
752 
753 /* Set the current display page. */
754 private void
vesa_set_page(gx_device_svga * dev,int pn,int wnum)755 vesa_set_page(gx_device_svga * dev, int pn, int wnum)
756 {
757 #if USE_ASM
758     extern void vesa_call_set_page(P3(void (*)(P2(int, int)), int, int));
759 
760     if (dev->info.vesa.bios_set_page != NULL)
761 	vesa_call_set_page(dev->info.vesa.bios_set_page, pn << dev->info.vesa.pn_shift, wnum);
762     else
763 #endif
764     {
765 	registers regs;
766 
767 	regs.rshort.dx = pn << dev->info.vesa.pn_shift;
768 	regs.h.ah = 0x4f;
769 	regs.h.al = 5;
770 	regs.rshort.bx = wnum;
771 	int86(0x10, &regs, &regs);
772     }
773 }
774 
775 /* ------ The ATI Wonder device ------ */
776 
777 private dev_proc_open_device(atiw_open);
778 private const gx_device_procs atiw_procs = svga_procs(atiw_open);
779 private int atiw_get_mode(P0());
780 private void atiw_set_mode(P1(int));
781 private void atiw_set_page(P3(gx_device_svga *, int, int));
782 gx_device_svga far_data gs_atiw_device =
783 svga_device(atiw_procs, "atiw", atiw_get_mode, atiw_set_mode, atiw_set_page);
784 
785 /* Read the device mode */
786 private int
atiw_get_mode(void)787 atiw_get_mode(void)
788 {
789     registers regs;
790 
791     regs.h.ah = 0xf;
792     int86(0x10, &regs, &regs);
793     return regs.h.al;
794 }
795 
796 /* Set the device mode */
797 private void
atiw_set_mode(int mode)798 atiw_set_mode(int mode)
799 {
800     registers regs;
801 
802     regs.h.ah = 0;
803     regs.h.al = mode;
804     int86(0x10, &regs, &regs);
805 }
806 
807 /* Initialize the graphics mode. */
808 private int
atiw_open(gx_device * dev)809 atiw_open(gx_device * dev)
810 {				/* Select the proper video mode */
811     {
812 	static const mode_info mode_table[] =
813 	{
814 	    {640, 400, 0x61},
815 	    {640, 480, 0x62},
816 	    {800, 600, 0x63},
817 	    {1024, 768, 0x64},
818 	    {-1, -1, -1}
819 	};
820 	int code = svga_find_mode(dev, mode_table);
821 
822 	if (code < 0)
823 	    return code;	/* mode not available */
824 	fb_dev->info.atiw.select_reg = *(int *)MK_PTR(0xc000, 0x10);
825 	return svga_open(dev);
826     }
827 }
828 
829 /* Set the current display page. */
830 private void
atiw_set_page(gx_device_svga * dev,int pn,int wnum)831 atiw_set_page(gx_device_svga * dev, int pn, int wnum)
832 {
833     int select_reg = dev->info.atiw.select_reg;
834     byte reg;
835 
836     disable();
837     outportb(select_reg, 0xb2);
838     reg = inportb(select_reg + 1);
839     outportb(select_reg, 0xb2);
840     outportb(select_reg + 1, (reg & 0xe1) + (pn << 1));
841     enable();
842 }
843 
844 /* ------ The Trident device ------ */
845 
846 private dev_proc_open_device(tvga_open);
847 private const gx_device_procs tvga_procs = svga_procs(tvga_open);
848 
849 /* We can use the atiw_get/set_mode procedures. */
850 private void tvga_set_page(P3(gx_device_svga *, int, int));
851 gx_device_svga far_data gs_tvga_device =
852 svga_device(tvga_procs, "tvga", atiw_get_mode, atiw_set_mode, tvga_set_page);
853 
854 /* Initialize the graphics mode. */
855 private int
tvga_open(gx_device * dev)856 tvga_open(gx_device * dev)
857 {
858     fb_dev->wnum_read = 1;
859     fb_dev->wnum_write = 0;
860     /* Select the proper video mode */
861     {
862 	static const mode_info mode_table[] =
863 	{
864 	    {640, 400, 0x5c},
865 	    {640, 480, 0x5d},
866 	    {800, 600, 0x5e},
867 	    {1024, 768, 0x62},
868 	    {-1, -1, -1}
869 	};
870 	int code = svga_find_mode(dev, mode_table);
871 
872 	if (code < 0)
873 	    return code;	/* mode not available */
874 	return svga_open(dev);
875     }
876 }
877 
878 /* Set the current display page. */
879 private void
tvga_set_page(gx_device_svga * dev,int pn,int wnum)880 tvga_set_page(gx_device_svga * dev, int pn, int wnum)
881 {
882     /* new mode */
883     outportb(0x3c4, 0x0b);
884     inportb(0x3c4);
885 
886     outportb(0x3c4, 0x0e);
887     outportb(0x3c5, pn ^ 2);
888 }
889 
890 /* ------ The Tseng Labs ET3000/4000 devices ------ */
891 
892 private dev_proc_open_device(tseng_open);
893 private const gx_device_procs tseng_procs =
894 svga_procs(tseng_open);
895 
896 /* We can use the atiw_get/set_mode procedures. */
897 private void tseng_set_page(P3(gx_device_svga *, int, int));
898 
899 /* The 256-color Tseng device */
900 gx_device_svga far_data gs_tseng_device =
901 svga_device(tseng_procs, "tseng", atiw_get_mode, atiw_set_mode, tseng_set_page);
902 
903 /* Initialize the graphics mode. */
904 private int
tseng_open(gx_device * dev)905 tseng_open(gx_device * dev)
906 {
907     fb_dev->wnum_read = 1;
908     fb_dev->wnum_write = 0;
909     /* Select the proper video mode */
910     {
911 	static const mode_info mode_table[] =
912 	{
913 	    {640, 350, 0x2d},
914 	    {640, 480, 0x2e},
915 	    {800, 600, 0x30},
916 	    {1024, 768, 0x38},
917 	    {-1, -1, -1}
918 	};
919 	int code = svga_find_mode(dev, mode_table);
920 	volatile_fb_ptr p0 = (volatile_fb_ptr) MK_PTR(regen, 0);
921 
922 	if (code < 0)
923 	    return code;	/* mode not available */
924 	code = svga_open(dev);
925 	if (code < 0)
926 	    return 0;
927 	/* Figure out whether we have an ET3000 or an ET4000 */
928 	/* by playing with the segment register. */
929 	outportb(0x3cd, 0x44);
930 	*p0 = 4;		/* byte 0, page 4 */
931 	outportb(0x3cd, 0x40);
932 	*p0 = 3;		/* byte 0, page 0 */
933 	fb_dev->info.tseng.et_model = *p0;
934 	/* read page 0 if ET3000, */
935 	/* page 4 if ET4000 */
936 	return 0;
937     }
938 }
939 
940 /* Set the current display page. */
941 private void
tseng_set_page(gx_device_svga * dev,int pn,int wnum)942 tseng_set_page(gx_device_svga * dev, int pn, int wnum)
943 {				/* The ET3000 has read page = 5:3, write page = 2:0; */
944     /* the ET4000 has read page = 7:4, write page = 3:0. */
945     int shift = dev->info.tseng.et_model;
946     int mask = (1 << shift) - 1;
947 
948     if (wnum)
949 	pn <<= shift, mask <<= shift;
950     outportb(0x3cd, (inportb(0x3cd) & ~mask) + pn);
951 }
952 /* ------ The Cirrus device (CL-GD54XX) ------ */
953 /* Written by Piotr Strzelczyk, BOP s.c., Gda\'nsk, Poland, */
954 /* e-mail contact via B.Jackowski@GUST.org.pl */
955 
956 private dev_proc_open_device(cirr_open);
957 private gx_device_procs cirr_procs = svga_procs(cirr_open);
958 
959 /* We can use the atiw_get/set_mode procedures. */
960 private void cirr_set_page(P3(gx_device_svga *, int, int));
961 gx_device_svga gs_cirr_device =
962 svga_device(cirr_procs, "cirr", atiw_get_mode, atiw_set_mode, cirr_set_page);
963 
964 /* Initialize the graphics mode. */
965 private int
cirr_open(gx_device * dev)966 cirr_open(gx_device * dev)
967 {
968     fb_dev->wnum_read = 1;
969     fb_dev->wnum_write = 0;
970     /* Select the proper video mode */
971     {
972 	static const mode_info mode_table[] =
973 	{
974 	    {640, 400, 0x5e},
975 	    {640, 480, 0x5f},
976 	    {800, 600, 0x5c},
977 	    {1024, 768, 0x60},
978 	    {-1, -1, -1}
979 	};
980 	int code = svga_find_mode(dev, mode_table);
981 
982 	if (code < 0)
983 	    return code;	/* mode not available */
984 	outportb(0x3c4, 0x06);
985 	outportb(0x3c5, 0x12);
986 	outportb(0x3ce, 0x0b);
987 	outportb(0x3cf, (inportb(0x3cf) & 0xde));
988 	return svga_open(dev);
989     }
990 }
991 
992 /* Set the current display page. */
993 private void
cirr_set_page(gx_device_svga * dev,int pn,int wnum)994 cirr_set_page(gx_device_svga * dev, int pn, int wnum)
995 {
996     outportb(0x3ce, 0x09);
997     outportb(0x3cf, pn << 4);
998 }
999 
1000 /* ------ The Avance Logic device (mostly experimental) ------ */
1001 /* For questions about this device, please contact Stefan Freund */
1002 /* <freund@ikp.uni-koeln.de>. */
1003 
1004 private dev_proc_open_device(ali_open);
1005 private const gx_device_procs ali_procs = svga_procs(ali_open);
1006 
1007 /* We can use the atiw_get/set_mode procedures. */
1008 private void ali_set_page(P3(gx_device_svga *, int, int));
1009 
1010 /* The 256-color Avance Logic device */
1011 gx_device_svga gs_ali_device =
1012 svga_device(ali_procs, "ali", atiw_get_mode, atiw_set_mode,
1013 	    ali_set_page);
1014 
1015 /* Initialize the graphics mode. */
1016 private int
ali_open(gx_device * dev)1017 ali_open(gx_device * dev)
1018 {
1019     fb_dev->wnum_read = 1;
1020     fb_dev->wnum_write = 0;
1021     /* Select the proper video mode */
1022     {
1023 	static const mode_info mode_table[] =
1024 	{
1025 	    {640, 400, 0x29},
1026 	    {640, 480, 0x2a},
1027 	    {800, 600, 0x2c},
1028 	    {1024, 768, 0x31},
1029 	    {-1, -1, -1}
1030 	};
1031 	int code = svga_find_mode(dev, mode_table);
1032 
1033 	if (code < 0)
1034 	    return code;	/* mode not available */
1035 	return svga_open(dev);
1036     }
1037 
1038 }
1039 
1040 /* Set the current display page. */
1041 private void
ali_set_page(gx_device_svga * dev,int pn,int wnum)1042 ali_set_page(gx_device_svga * dev, int pn, int wnum)
1043 {
1044     outportb(0x3d6, pn);	/* read  */
1045     outportb(0x3d7, pn);	/* write */
1046 }
1047