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