xref: /plan9-contrib/sys/src/cmd/gs/src/gdevsvga.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 /* Copyright (C) 1991, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gdevsvga.c,v 1.2 2000/09/19 19:00:23 lpd 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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