386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / gdevsvga.c
CommitLineData
c47ab29c
WJ
1/* Copyright (C) 1991 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* gdevsvga.c */
21/* SuperVGA display drivers for Ghostscript */
22#include "dos_.h"
23typedef union REGS registers;
24#include "memory_.h"
25#include "gs.h"
26#include "gxdevice.h"
27
28#ifndef USE_ASM
29# define USE_ASM 0 /* don't use assembly language */
30#endif
31
32/* Define the short (integer) version of "transparent" color. */
33/* ****** Depends on gx_no_color_index being all 1's. ******/
34#define no_color ((int)gx_no_color_index)
35
36/* Procedures */
37
38 /* See gxdevice.h for the definitions of the procedures. */
39
40private dev_proc_close_device(svga_close);
41private dev_proc_map_rgb_color(svga_map_rgb_color);
42private dev_proc_map_color_rgb(svga_map_color_rgb);
43private dev_proc_fill_rectangle(svga_fill_rectangle);
44private dev_proc_copy_mono(svga_copy_mono);
45private dev_proc_copy_color(svga_copy_color);
46private dev_proc_get_bits(svga_get_bits);
47
48/* Type for frame buffer pointers. */
49/*** Intimately tied to the 80x86 (x<2) addressing architecture. ***/
50typedef byte far *fb_ptr;
51
52/* The device descriptor */
53typedef struct gx_device_svga_s gx_device_svga;
54struct gx_device_svga_s {
55 gx_device_common;
56 int (*get_mode)(P0());
57 void (*set_mode)(P1(int));
58 void (*set_page)(P3(gx_device_svga *, int, int));
59 uint raster; /* frame buffer bytes per line */
60 int page; /* current page */
61 int wnum_read, wnum_write; /* window #s for read vs. write */
62 /* Following are device-specific. */
63 union {
64 struct {
65 void (*bios_set_page)(P2(int, int)); /* set-page function */
66 } vesa;
67 struct {
68 int select_reg; /* page-select register */
69 } atiw;
70 struct {
71 int et_model; /* 4 for ET4000, */
72 /* 3 for ET3000 */
73 } tseng;
74 } info;
75};
76
77/* The color map for dynamically assignable colors. */
78#define first_color_index 64
79private int next_color_index;
80private ushort dynamic_colors[256 - first_color_index];
81
82/* Macro for casting gx_device argument */
83#define fb_dev ((gx_device_svga *)dev)
84
85/* Procedure records */
86#define svga_procs(open) {\
87 open, gx_default_get_initial_matrix,\
88 gx_default_sync_output, gx_default_output_page, svga_close,\
89 svga_map_rgb_color, svga_map_color_rgb,\
90 svga_fill_rectangle, gx_default_tile_rectangle,\
91 svga_copy_mono, svga_copy_color, gx_default_draw_line,\
92 svga_get_bits, gx_default_get_props, gx_default_put_props\
93}
94
95/* The initial parameters map an appropriate fraction of */
96/* the screen to an 8.5" x 11" coordinate space. */
97/* This may or may not be what is desired! */
98#define svga_device(procs, name, get_mode, set_mode, set_page) {\
99 sizeof(gx_device_svga),\
100 &procs,\
101 name,\
102 640, 480, /* screen size */\
103 480 / 11.0, 480 / 11.0, /* resolution */\
104 no_margins,\
105 dci_color(8, 31, 4),\
106 0, /* not opened yet */\
107 get_mode, set_mode, set_page\
108 }
109
110/* Save the controller mode */
111private int svga_save_mode = -1;
112
113/* Macro for validating rectangle parameters x and w. */
114/* set_pixel_ptr implicitly validates y and h. */
115#define validate_rect()\
116 if ( w <= 0 ) return 0;\
117 if ( x < 0 || x + w > dev->width ) return -1
118
119/* ------ Internal routines ------ */
120
121#define regen 0xa000
122
123/* Construct a pointer for writing a pixel. */
124/* Assume 64K pages, 64K granularity. */
125#define set_pixel_ptr(ptr, fbdev, x, y, wnum)\
126{ ulong index = (ulong)(y) * fbdev->raster + (uint)(x);\
127 if ( (uint)(index >> 16) != fbdev->page )\
128 { if ( y < 0 || y >= fbdev->height ) return -1;\
129 (*fbdev->set_page)(fbdev, (fbdev->page = index >> 16), wnum);\
130 }\
131 ptr = (fb_ptr)MK_PTR(regen, (ushort)index);\
132}
133#define set_pixel_write_ptr(ptr, fbdev, x, y)\
134 set_pixel_ptr(ptr, fbdev, x, y, fbdev->wnum_write)
135#define set_pixel_read_ptr(ptr, fbdev, x, y)\
136 set_pixel_ptr(ptr, fbdev, x, y, fbdev->wnum_read)
137
138/* Table structure for looking up graphics modes. */
139typedef struct {
140 int width, height; /* "key" */
141 int mode; /* "value" */
142} mode_info;
143
144/* Find the graphics mode for a desired width and height. */
145/* Return the graphics mode or -1. */
146private int
147svga_find_mode(gx_device *dev, mode_info _ds *mip)
148{ for ( ; mip->mode >= 0; mip++ )
149 { if ( mip->width == fb_dev->width &&
150 mip->height == fb_dev->height
151 )
152 return mip->mode;
153 }
154 return -1;
155}
156
157/* Set the index for writing into the color DAC. */
158#define svga_dac_set_write_index(i) outportb(0x3c8, i)
159
160/* Write 6-bit R,G,B values into the color DAC. */
161#define svga_dac_write(r, g, b)\
162 (outportb(0x3c9, r), outportb(0x3c9, g), outportb(0x3c9, b))
163
164/* ------ Common procedures ------ */
165
166/* Initialize the device structure and the DACs. */
167private int
168svga_open(gx_device *dev, int mode)
169{ fb_dev->raster = fb_dev->width;
170 fb_dev->x_pixels_per_inch =
171 fb_dev->y_pixels_per_inch =
172 fb_dev->height / 11.0;
173 /* Set the display mode. */
174 if ( svga_save_mode < 0 )
175 svga_save_mode = (*fb_dev->get_mode)();
176 (*fb_dev->set_mode)(mode);
177 /* Load the color DAC. */
178 svga_dac_set_write_index(0);
179 { int c;
180 for ( c = 0; c < 64; c++ )
181 { static byte c2[10] =
182 { 0, 42, 0, 0, 0, 0, 0, 0, 21, 63 };
183 svga_dac_write(c2[(c >> 2) & 9], c2[(c >> 1) & 9],
184 c2[c & 9]);
185 }
186 }
187 /* Initialize the dynamic color table. */
188 next_color_index = first_color_index;
189 fb_dev->page = -1;
190 return 0;
191}
192
193/* Close the device; reinitialize the display for text mode. */
194private int
195svga_close(gx_device *dev)
196{ if ( svga_save_mode >= 0 )
197 (*fb_dev->set_mode)(svga_save_mode);
198 svga_save_mode = -1;
199 return 0;
200}
201
202/* Map a r-g-b color to a palette index. */
203/* The first 64 entries of the color map are set */
204/* for compatibility with the older display modes: */
205/* these are indexed as 0.0.R0.G0.B0.R1.G1.B1. */
206private gx_color_index
207svga_map_rgb_color(gx_device *dev, ushort r, ushort g, ushort b)
208{
209#define cv_bits(v,n) (v >> (gx_color_value_bits - n))
210 ushort r5 = cv_bits(r, 5), g5 = cv_bits(g, 5), b5 = cv_bits(b, 5);
211 static byte cube_bits[32] =
212 { 0, 128, 128, 128, 128, 128, 128, 128, 128, 128,
213 8, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
214 1, 128, 128, 128, 128, 128, 128, 128, 128, 128,
215 9
216 };
217 uint cx = ((uint)cube_bits[r5] << 2) + ((uint)cube_bits[g5] << 1) +
218 (uint)cube_bits[b5];
219 ushort rgb;
220 /* Check for a color on the cube. */
221 if ( cx < 64 ) return (gx_color_index)cx;
222 /* Not on the cube, check the dynamic color table. */
223 rgb = (r5 << 10) + (g5 << 5) + b5;
224 { int i = next_color_index - first_color_index;
225 register ushort _ds *pdc = dynamic_colors + i;
226 while ( --i >= 0 )
227 if ( *--pdc == rgb )
228 return (gx_color_index)(i + first_color_index);
229 }
230 /* Not on the cube, and not in the dynamic table. */
231 /* Put in the dynamic table if space available. */
232 if ( next_color_index < 255 )
233 { int i = next_color_index++;
234 dynamic_colors[i - first_color_index] = rgb;
235 svga_dac_set_write_index(i);
236 svga_dac_write(cv_bits(r, 6), cv_bits(g, 6), cv_bits(b, 6));
237 return (gx_color_index)i;
238 }
239 /* No space left, report failure. */
240 return gx_no_color_index;
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. */
246private int
247svga_map_color_rgb(gx_device *dev, gx_color_index color, ushort prgb[3])
248{ uint cval;
249 outportb(0x3c7, (byte)color);
250#define dacin() (cval = inportb(0x3c9) >> 1,\
251 ((cval << 11) + (cval << 6) + (cval << 1) + (cval >> 4)) >>\
252 (16 - gx_color_value_bits))
253 prgb[0] = dacin();
254 prgb[1] = dacin();
255 prgb[2] = dacin();
256#undef dacin
257 return 0;
258}
259
260/* Copy a monochrome bitmap. The colors are given explicitly. */
261/* Color = gx_no_color_index means transparent (no effect on the image). */
262private int
263svga_copy_mono(gx_device *dev,
264 byte *base, int sourcex, int raster, gx_bitmap_id id,
265 int x, int y, int w, int h, gx_color_index czero, gx_color_index cone)
266{ register int xi;
267 uint skip = fb_dev->raster - w;
268 int yi;
269 fb_ptr ptr = (fb_ptr)0;
270 byte *srow = base + (sourcex >> 3);
271 uint imask = 0x80 >> (sourcex & 7);
272 validate_rect();
273#define izero (int)czero
274#define ione (int)cone
275 for ( yi = 0; yi < h; yi++ )
276 { byte *sptr = srow;
277 uint bits = *sptr;
278 register uint mask = imask;
279 if ( PTR_OFF(ptr) <= skip )
280 set_pixel_write_ptr(ptr, fb_dev, x, y + yi);
281 for ( xi = 0; xi < w; xi++ )
282 { if ( PTR_OFF(ptr) == 0 )
283 set_pixel_write_ptr(ptr, fb_dev, x + xi, y + yi);
284 if ( bits & mask )
285 { if ( ione != no_color ) *ptr = (byte)ione;
286 }
287 else
288 { if ( izero != no_color ) *ptr = (byte)izero;
289 }
290 if ( !(mask >>= 1) ) mask = 0x80, bits = *++sptr;
291 ptr++;
292 }
293 ptr += skip;
294 srow += raster;
295 }
296#undef izero
297#undef ione
298 return 0;
299}
300
301/* Copy a color pixelmap. This is just like a bitmap, */
302/* except that each pixel takes 8 bits instead of 1. */
303private int
304svga_copy_color(gx_device *dev,
305 byte *base, int sourcex, int raster, gx_bitmap_id id,
306 int x, int y, int w, int h)
307{ int xi, yi;
308 int skip = raster - w;
309 byte *sptr = base + sourcex;
310 fb_ptr ptr;
311 validate_rect();
312 for ( yi = y; yi - y < h; yi++ )
313 { ptr = 0;
314 for ( xi = x; xi - x < w; xi++ )
315 { if ( PTR_OFF(ptr) == 0 )
316 set_pixel_write_ptr(ptr, fb_dev, xi, yi);
317 *ptr++ = *sptr++;
318 }
319 sptr += skip;
320 }
321 return 0;
322}
323
324/* Fill a rectangle. */
325private int
326svga_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
327 gx_color_index color)
328{ uint raster = fb_dev->raster;
329 ushort limit = (ushort)-raster;
330 int yi;
331 fb_ptr ptr;
332 if ( x < 0 || x + w > dev->width ) return -1; /* skip w */
333 set_pixel_write_ptr(ptr, fb_dev, x, y);
334 /* Most fills are very small and don't cross a page boundary. */
335 yi = h;
336 switch ( w )
337 {
338 case 0: return 0; /* no-op */
339 case 1:
340 while ( --yi >= 0 && PTR_OFF(ptr) < limit )
341 ptr[0] = (byte)color,
342 ptr += raster;
343 if ( !++yi ) return 0;
344 break;
345 case 2:
346 while ( --yi >= 0 && PTR_OFF(ptr) < limit )
347 ptr[0] = ptr[1] = (byte)color,
348 ptr += raster;
349 if ( !++yi ) return 0;
350 break;
351 case 3:
352 while ( --yi >= 0 && PTR_OFF(ptr) < limit )
353 ptr[0] = ptr[1] = ptr[2] = (byte)color,
354 ptr += raster;
355 if ( !++yi ) return 0;
356 break;
357 case 4:
358 while ( --yi >= 0 && PTR_OFF(ptr) < limit )
359 ptr[0] = ptr[1] = ptr[2] = ptr[3] = (byte)color,
360 ptr += raster;
361 if ( !++yi ) return 0;
362 break;
363 default:
364 if ( w < 0 ) return 0;
365 }
366 while ( --yi >= 0 )
367 { if ( PTR_OFF(ptr) < limit )
368 { memset(ptr, (byte)color, w);
369 ptr += raster;
370 }
371 else if ( PTR_OFF(ptr) <= (ushort)(-w) )
372 { memset(ptr, (byte)color, w);
373 if ( yi > 0 )
374 set_pixel_write_ptr(ptr, fb_dev, x, y + h - yi);
375 }
376 else
377 { uint left = (uint)0x10000 - PTR_OFF(ptr);
378 memset(ptr, (byte)color, left);
379 set_pixel_write_ptr(ptr, fb_dev, x + left, y + h - 1 - yi);
380 memset(ptr, (byte)color, w - left);
381 ptr += raster - left;
382 }
383 }
384 return 0;
385}
386
387/* Read scan lines back from the frame buffer. */
388int
389svga_get_bits(gx_device *dev, int y, byte *data, uint size, int pad_to_word)
390{ /* We don't have to worry about padding, because we read back */
391 /* a byte per pixel and the frame buffer width is always */
392 /* a multiple of 8 pixels. */
393 uint bytes_per_row = dev->width;
394 uint count = min(dev->height - y, size / bytes_per_row);
395 byte *dest = data;
396 ushort limit = (ushort)-bytes_per_row;
397 int j;
398 for ( j = count; j--; dest += bytes_per_row, y++ )
399 { fb_ptr src;
400 set_pixel_read_ptr(src, fb_dev, 0, y);
401 /* The logic here is similar to fill_rectangle. */
402 if ( PTR_OFF(src) <= limit )
403 memcpy(dest, src, bytes_per_row);
404 else
405 { uint left = (uint)0x10000 - PTR_OFF(src);
406 memcpy(dest, src, left);
407 set_pixel_read_ptr(src, fb_dev, left, y);
408 memcpy(dest + left, src, bytes_per_row - left);
409 }
410 }
411 return count;
412}
413
414/* ------ The VESA device ------ */
415
416private dev_proc_open_device(vesa_open);
417private gx_device_procs vesa_procs = svga_procs(vesa_open);
418private int vesa_get_mode(P0());
419private void vesa_set_mode(P1(int));
420private void vesa_set_page(P3(gx_device_svga *, int, int));
421gx_device_svga gs_vesa_device =
422 svga_device(vesa_procs, "vesa", vesa_get_mode, vesa_set_mode, vesa_set_page);
423
424/* Define the structure for information returned by the BIOS. */
425#define bits_include(a, m) !(~(a) & (m))
426typedef enum {
427 m_supported = 1,
428 m_graphics = 0x10
429} mode_attribute;
430typedef enum {
431 w_supported = 1,
432 w_readable = 2,
433 w_writable = 4
434} win_attribute;
435typedef struct {
436 ushort mode_attributes;
437 byte win_a_attributes;
438 byte win_b_attributes;
439 ushort win_granularity;
440 ushort win_size;
441 ushort win_a_segment;
442 ushort win_b_segment;
443 void (*win_func_ptr)(P2(int, int));
444 ushort bytes_per_line;
445 /* Optional information */
446 ushort x_resolution;
447 ushort y_resolution;
448 byte x_char_size;
449 byte y_char_size;
450 byte number_of_planes;
451 byte bits_per_pixel;
452 byte number_of_banks;
453 byte memory_model;
454 byte bank_size;
455} vesa_info;
456
457/* Read the device mode */
458private int
459vesa_get_mode()
460{ registers regs;
461 regs.h.ah = 0x4f;
462 regs.h.al = 0x03;
463 int86(0x10, &regs, &regs);
464 return regs.rshort.bx;
465}
466
467/* Set the device mode */
468private void
469vesa_set_mode(int mode)
470{ registers regs;
471 regs.h.ah = 0x4f;
472 regs.h.al = 0x02;
473 regs.rshort.bx = mode;
474 int86(0x10, &regs, &regs);
475}
476
477/* Read information about a device mode */
478private int
479vesa_get_info(int mode, vesa_info _ss *info)
480{ registers regs;
481 struct SREGS sregs;
482 regs.h.ah = 0x4f;
483 regs.h.al = 0x01;
484 regs.rshort.cx = mode;
485 regs.rshort.di = PTR_OFF(info);
486 segread(&sregs);
487 sregs.es = sregs.ss;
488 int86x(0x10, &regs, &regs, &sregs);
489#ifdef DEBUG
490 if ( regs.h.ah == 0 && regs.h.al == 0x4f )
491 dprintf8("vesa_get_info(%x): ma=%x wa=%x/%x wg=%x ws=%x wseg=%x/%x\n",
492 mode, info->mode_attributes,
493 info->win_a_attributes, info->win_b_attributes,
494 info->win_granularity, info->win_size,
495 info->win_a_segment, info->win_b_segment);
496 else
497 dprintf3("vesa_get_info(%x) failed: ah=%x al=%x\n",
498 mode, regs.h.ah, regs.h.al);
499#endif
500 return (regs.h.ah == 0 && regs.h.al == 0x4f ? 0 : -1);
501}
502
503/* Initialize the graphics mode. */
504private int
505vesa_open(gx_device *dev)
506{ /* Select the proper video mode */
507 { vesa_info info;
508 static mode_info mode_table[6] = {
509 { 640, 400, 0x100 },
510 { 640, 480, 0x101 },
511 { 800, 600, 0x103 },
512 { 1024, 768, 0x105 },
513 { 1280, 1024, 0x107 },
514 { -1, -1, -1 }
515 };
516 mode_info _ds *mip;
517 for ( mip = mode_table; mip->mode >= 0; mip++ )
518 { if ( mip->width == fb_dev->width &&
519 mip->height == fb_dev->height &&
520 vesa_get_info(mip->mode, &info) >= 0 &&
521 bits_include(info.mode_attributes,
522 m_supported | m_graphics) &&
523 info.win_granularity == 64 &&
524 info.win_size == 64 &&
525 bits_include(info.win_a_attributes,
526 w_supported) &&
527 info.win_a_segment == regen
528 )
529 { /* Make sure we can both read & write. */
530 /* Initialize for the default case. */
531 fb_dev->wnum_read = 0;
532 fb_dev->wnum_write = 0;
533 if ( bits_include(info.win_a_attributes,
534 w_readable | w_writable)
535 )
536 break;
537 else if ( info.win_b_segment == regen &&
538 bits_include(info.win_b_attributes,
539 w_supported) &&
540 bits_include(info.win_a_attributes |
541 info.win_b_attributes,
542 w_readable | w_writable)
543 )
544 { /* Two superimposed windows. */
545 if ( !bits_include(info.win_a_attributes,
546 w_writable)
547 )
548 fb_dev->wnum_write = 1;
549 else
550 fb_dev->wnum_read = 1;
551 }
552 break;
553 }
554 }
555 if ( mip->mode < 0 ) return -1; /* mode not available */
556 fb_dev->info.vesa.bios_set_page = info.win_func_ptr;
557 return svga_open(dev, mip->mode);
558 }
559}
560
561/* Set the current display page. */
562private void
563vesa_set_page(gx_device_svga *dev, int pn, int wnum)
564{
565#if USE_ASM
566extern void vesa_call_set_page(P3(void (*)(P2(int, int)), int, int));
567 if ( dev->info.vesa.bios_set_page != NULL )
568 vesa_call_set_page(dev->info.vesa.bios_set_page, pn, wnum);
569 else
570#endif
571 { registers regs;
572 regs.rshort.dx = pn;
573 regs.h.ah = 0x4f;
574 regs.h.al = 5;
575 regs.rshort.bx = wnum;
576 int86(0x10, &regs, &regs);
577 }
578}
579
580/* ------ The ATI Wonder device ------ */
581
582private dev_proc_open_device(atiw_open);
583private gx_device_procs atiw_procs = svga_procs(atiw_open);
584private int atiw_get_mode(P0());
585private void atiw_set_mode(P1(int));
586private void atiw_set_page(P3(gx_device_svga *, int, int));
587gx_device_svga gs_atiw_device =
588 svga_device(atiw_procs, "atiw", atiw_get_mode, atiw_set_mode, atiw_set_page);
589
590/* Read the device mode */
591private int
592atiw_get_mode()
593{ registers regs;
594 regs.h.ah = 0xf;
595 int86(0x10, &regs, &regs);
596 return regs.h.al;
597}
598
599/* Set the device mode */
600private void
601atiw_set_mode(int mode)
602{ registers regs;
603 regs.h.ah = 0;
604 regs.h.al = mode;
605 int86(0x10, &regs, &regs);
606}
607
608/* Initialize the graphics mode. */
609private int
610atiw_open(gx_device *dev)
611{ /* Select the proper video mode */
612 { static mode_info mode_table[4] = {
613 { 640, 400, 0x61 },
614 { 640, 480, 0x62 },
615 { 800, 600, 0x63 },
616 { -1, -1, -1 }
617 };
618 int mode = svga_find_mode(dev, mode_table);
619 if ( mode < 0 ) return -1; /* mode not available */
620 fb_dev->info.atiw.select_reg = *(int *)MK_PTR(0xc000, 0x10);
621 return svga_open(dev, mode);
622 }
623}
624
625/* Set the current display page. */
626private void
627atiw_set_page(gx_device_svga *dev, int pn, int wnum)
628{ int select_reg = dev->info.atiw.select_reg;
629 byte reg;
630 disable();
631 outportb(select_reg, 0xb2);
632 reg = inportb(select_reg + 1);
633 outportb(select_reg, 0xb2);
634 outportb(select_reg + 1, (reg & 0xe1) + (pn << 1));
635 enable();
636}
637
638/* ------ The Tseng Labs ET3000/4000 device ------ */
639
640private dev_proc_open_device(tseng_open);
641private gx_device_procs tseng_procs = svga_procs(tseng_open);
642/* We can use the tseng_get/set_mode procedures. */
643private void tseng_set_page(P3(gx_device_svga *, int, int));
644gx_device_svga gs_tseng_device =
645 svga_device(tseng_procs, "tseng", atiw_get_mode, atiw_set_mode, tseng_set_page);
646
647/* Initialize the graphics mode. */
648private int
649tseng_open(gx_device *dev)
650{ fb_dev->wnum_read = 1;
651 fb_dev->wnum_write = 0;
652 /* Select the proper video mode */
653 { static mode_info mode_table[5] = {
654 { 640, 350, 0x2d },
655 { 640, 480, 0x2e },
656 { 800, 600, 0x30 },
657 { 1024, 768, 0x38 },
658 { -1, -1, -1 }
659 };
660 int mode = svga_find_mode(dev, mode_table);
661 fb_ptr p0 = (fb_ptr)MK_PTR(regen, 0);
662 if ( mode < 0 ) return -1; /* mode not available */
663 /* Figure out whether we have an ET3000 or an ET4000 */
664 /* by playing with the segment register. */
665 outportb(0x3cd, 0x44);
666 *p0 = 4; /* byte 0, page 4 */
667 outportb(0x3cd, 0x40);
668 *p0 = 3; /* byte 0, page 0 */
669 fb_dev->info.tseng.et_model = *p0;
670 /* read page 0 if ET3000, */
671 /* page 4 if ET4000 */
672 return svga_open(dev, mode);
673 }
674}
675
676/* Set the current display page. */
677private void
678tseng_set_page(gx_device_svga *dev, int pn, int wnum)
679{ /* The ET3000 has read page = 5:3, write page = 2:0; */
680 /* the ET4000 has read page = 7:4, write page = 3:0. */
681 int shift = dev->info.tseng.et_model;
682 int mask = (1 << shift) - 1;
683 if ( wnum ) pn <<= shift, mask <<= shift;
684 outportb(0x3cd, (inportb(0x3cd) & ~mask) + pn);
685}