1 /* Copyright (c) NetHack PC Development Team 1995 */
2 /* VESA BIOS functions copyright (c) Ray Chason 2016 */
3 /* NetHack may be freely redistributed. See license for details. */
5 * vidvesa.c - VGA Hardware video support with VESA BIOS Extensions
10 #ifdef SCREEN_VESA /* this file is for SCREEN_VESA only */
20 #define BACKGROUND_VESA_COLOR 1
21 #define FIRST_TEXT_COLOR 240
23 static unsigned long FDECL(vesa_SetWindow
, (int window
, unsigned long offset
));
24 static unsigned long FDECL(vesa_ReadPixel32
, (unsigned x
, unsigned y
));
25 static void FDECL(vesa_WritePixel32
, (unsigned x
, unsigned y
,
26 unsigned long color
));
27 static void FDECL(vesa_WritePixel
, (unsigned x
, unsigned y
, unsigned color
));
28 static unsigned long FDECL(vesa_MakeColor
, (unsigned r
, unsigned g
, unsigned b
));
29 static void FDECL(vesa_GetRGB
, (
31 unsigned char *rp
, unsigned char *gp
, unsigned char *bp
));
32 static void FDECL(vesa_FillRect
, (
33 unsigned left
, unsigned top
,
34 unsigned width
, unsigned height
,
37 static void NDECL(vesa_redrawmap
);
38 static void FDECL(vesa_cliparound
, (int, int));
39 static void FDECL(decal_packed
, (const struct TileImage
*tile
, unsigned special
));
40 static void FDECL(vesa_SwitchMode
, (unsigned mode
));
41 static boolean
FDECL(vesa_SetPalette
, (const struct Pixel
*));
42 static boolean
FDECL(vesa_SetHardPalette
, (const struct Pixel
*));
43 static boolean
FDECL(vesa_SetSoftPalette
, (const struct Pixel
*));
44 static void FDECL(vesa_DisplayCell
, (const struct TileImage
*tile
, int, int));
45 static void FDECL(vesa_DisplayCellInMemory
, (const struct TileImage
*tile
,
46 int, char buf
[TILE_Y
][640*2]));
47 static unsigned FDECL(vesa_FindMode
, (unsigned long mode_addr
, unsigned bits
));
48 static void FDECL(vesa_WriteChar
, (int, int, int, int, BOOLEAN_P
));
49 static void FDECL(vesa_WriteCharInMemory
, (int, int, char buf
[TILE_Y
][640*2],
51 static void FDECL(vesa_WriteStr
, (const char *, int, int, int, int));
52 static char __far
*NDECL(vesa_FontPtrs
);
55 static void NDECL(positionbar
);
58 extern int clipx
, clipxmax
; /* current clipping column from wintty.c */
59 extern int curcol
, currow
; /* current column and row */
60 extern int g_attribute
;
61 extern int attrib_text_normal
; /* text mode normal attribute */
62 extern int attrib_gr_normal
; /* graphics mode normal attribute */
63 extern int attrib_gr_intense
; /* graphics mode intense attribute */
64 extern boolean inmap
; /* in the map window */
65 extern boolean restoring
;
71 static unsigned char __far
*font
;
73 static struct map_struct
{
78 } map
[ROWNO
][COLNO
]; /* track the glyphs */
80 #define vesa_clearmap() \
83 for (y = 0; y < ROWNO; ++y) \
84 for (x = 0; x < COLNO; ++x) { \
85 map[y][x].glyph = cmap_to_glyph(S_stone); \
86 map[y][x].ch = S_stone; \
88 map[y][x].special = 0; \
93 static int viewport_size
= 40;
95 static const struct Pixel defpalette
[] = { /* Colors for text and the position bar */
96 { 0x18, 0x18, 0x18, 0xff }, /* CLR_BLACK */
97 { 0xaa, 0x00, 0x00, 0xff }, /* CLR_RED */
98 { 0x00, 0xaa, 0x00, 0xff }, /* CLR_GREEN */
99 { 0x99, 0x40, 0x00, 0xff }, /* CLR_BROWN */
100 { 0x00, 0x00, 0xaa, 0xff }, /* CLR_BLUE */
101 { 0xaa, 0x00, 0xaa, 0xff }, /* CLR_MAGENTA */
102 { 0x00, 0xaa, 0xaa, 0xff }, /* CLR_CYAN */
103 { 0xaa, 0xaa, 0xaa, 0xff }, /* CLR_GRAY */
104 { 0x55, 0x55, 0x55, 0xff }, /* NO_COLOR */
105 { 0xff, 0x90, 0x00, 0xff }, /* CLR_ORANGE */
106 { 0x00, 0xff, 0x00, 0xff }, /* CLR_BRIGHT_GREEN */
107 { 0xff, 0xff, 0x00, 0xff }, /* CLR_YELLOW */
108 { 0x00, 0x00, 0xff, 0xff }, /* CLR_BRIGHT_BLUE */
109 { 0xff, 0x00, 0xff, 0xff }, /* CLR_BRIGHT_MAGENTA */
110 { 0x00, 0xff, 0xff, 0xff }, /* CLR_BRIGHT_CYAN */
111 { 0xff, 0xff, 0xff, 0xff } /* CLR_WHITE */
114 /* Information about the selected VESA mode */
115 static unsigned short vesa_mode
= 0xFFFF; /* Mode number */
116 static unsigned short vesa_x_res
; /* X resolution */
117 static unsigned short vesa_y_res
; /* Y resolution */
118 static unsigned short vesa_x_center
; /* X centering offset */
119 static unsigned short vesa_y_center
; /* Y centering offset */
120 static unsigned short vesa_scan_line
; /* Bytes per scan line */
121 static int vesa_read_win
; /* Select the read window */
122 static int vesa_write_win
; /* Select the write window */
123 static unsigned long vesa_win_pos
[2]; /* Window position */
124 static unsigned long vesa_win_addr
[2]; /* Window physical address */
125 static unsigned long vesa_win_size
; /* Window size */
126 static unsigned long vesa_win_gran
; /* Window granularity */
127 static unsigned char vesa_pixel_size
;
128 static unsigned char vesa_pixel_bytes
;
129 static unsigned char vesa_red_pos
;
130 static unsigned char vesa_red_size
;
131 static unsigned char vesa_green_pos
;
132 static unsigned char vesa_green_size
;
133 static unsigned char vesa_blue_pos
;
134 static unsigned char vesa_blue_size
;
135 static unsigned long vesa_palette
[256];
140 unsigned short XResolution
; /* horizontal resolution in pixels or characters */
141 unsigned short YResolution
; /* vertical resolution in pixels or characters */
142 unsigned char BitsPerPixel
; /* bits per pixel */
143 unsigned char MemoryModel
; /* memory model type */
146 static const struct OldModeInfo old_mode_table
[] = {
147 { 0x0101, 640, 480, 8, 4 },
148 { 0x0103, 800, 600, 8, 4 },
149 { 0x0105, 1024, 768, 8, 4 },
150 { 0x0107, 1280, 1024, 8, 4 },
151 { 0x0110, 640, 480, 15, 6 },
152 { 0x0111, 640, 480, 16, 6 },
153 { 0x0112, 640, 480, 24, 6 },
154 { 0x0113, 800, 600, 15, 6 },
155 { 0x0114, 800, 600, 16, 6 },
156 { 0x0115, 800, 600, 24, 6 },
157 { 0x0116, 1024, 768, 15, 6 },
158 { 0x0117, 1024, 768, 16, 6 },
159 { 0x0118, 1024, 768, 24, 6 },
160 { 0x0119, 1280, 1024, 15, 6 },
161 { 0x011A, 1280, 1024, 16, 6 },
162 { 0x011B, 1280, 1024, 24, 6 },
165 /* Retrieve the mode info block */
167 vesa_GetModeInfo(mode
, info
)
169 struct ModeInfoBlock
*info
;
171 int mode_info_sel
= -1; /* custodial */
175 mode_info_seg
= __dpmi_allocate_dos_memory(
176 (sizeof(*info
) + 15) / 16,
178 if (mode_info_seg
< 0) goto error
;
180 memset(info
, 0, sizeof(*info
));
181 dosmemput(info
, sizeof(*info
), mode_info_seg
* 16L);
183 memset(®s
, 0, sizeof(regs
));
187 regs
.x
.es
= mode_info_seg
;
188 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
190 if (regs
.x
.ax
!= 0x004F) goto error
;
191 dosmemget(mode_info_seg
* 16L, sizeof(*info
), info
);
192 if (!(info
->ModeAttributes
& 0x0001)) goto error
;
194 if (!(info
->ModeAttributes
& 0x0002)) {
195 /* Older VESA BIOS that did not return certain mode properties, but
196 that has fixed mode numbers; search the table to find the right
201 for (i
= 0; i
< SIZE(old_mode_table
); ++i
) {
202 if (mode
== old_mode_table
[i
].mode
) {
206 if (i
>= SIZE(old_mode_table
)) goto error
;
208 info
->XResolution
= old_mode_table
[i
].XResolution
;
209 info
->YResolution
= old_mode_table
[i
].YResolution
;
210 info
->NumberOfPlanes
= 1;
211 info
->BitsPerPixel
= old_mode_table
[i
].BitsPerPixel
;
212 info
->NumberOfBanks
= 1;
213 info
->MemoryModel
= old_mode_table
[i
].MemoryModel
;
216 __dpmi_free_dos_memory(mode_info_sel
);
220 if (mode_info_sel
!= -1) __dpmi_free_dos_memory(mode_info_sel
);
224 /* Set the memory window and return the offset */
226 vesa_SetWindow(window
, offset
)
228 unsigned long offset
;
230 /* If the desired offset is already within the window, leave the window
231 as it is and return the address based on the current window position.
232 This minimizes the use of the window switch function.
234 On the first call to the function, vesa_win_pos[window] == 0xFFFFFFFF,
235 the offset will always be less than this, and the BIOS will always be
238 unsigned long pos
= vesa_win_pos
[window
];
239 if (offset
< pos
|| pos
+ vesa_win_size
<= offset
) {
242 memset(®s
, 0, sizeof(regs
));
246 regs
.x
.dx
= offset
/ vesa_win_gran
;
247 pos
= regs
.x
.dx
* vesa_win_gran
;
248 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
249 vesa_win_pos
[window
] = pos
;
252 offset
= offset
- vesa_win_pos
[window
] + vesa_win_addr
[window
];
253 /* Keep from crashing the system if some malfunction gives us a bad
255 if (offset
< 0xA0000 || offset
> 0xBFFFF) {
256 vesa_SwitchMode(MODETEXT
);
257 fprintf(stderr
, "Abort: offset=%08lX\n", offset
);
264 vesa_ReadPixel32(x
, y
)
267 unsigned long offset
= y
* vesa_scan_line
+ x
* vesa_pixel_bytes
;
268 unsigned long addr
, color
;
271 switch (vesa_pixel_size
) {
273 addr
= vesa_SetWindow(vesa_read_win
, offset
);
274 color
= _farpeekb(_dos_ds
, addr
);
279 addr
= vesa_SetWindow(vesa_read_win
, offset
);
280 color
= _farpeekw(_dos_ds
, addr
);
284 /* Pixel may cross a window boundary */
286 for (i
= 0; i
< 3; ++i
) {
287 addr
= vesa_SetWindow(vesa_read_win
, offset
+ i
);
288 color
|= (unsigned long) _farpeekb(_dos_ds
, addr
) << (i
* 8);
293 addr
= vesa_SetWindow(vesa_read_win
, offset
);
294 color
= _farpeekl(_dos_ds
, addr
);
301 vesa_WritePixel32(x
, y
, color
)
305 unsigned long offset
= y
* vesa_scan_line
+ x
* vesa_pixel_bytes
;
309 switch (vesa_pixel_size
) {
311 addr
= vesa_SetWindow(vesa_write_win
, offset
);
312 _farpokeb(_dos_ds
, addr
, color
);
317 addr
= vesa_SetWindow(vesa_write_win
, offset
);
318 _farpokew(_dos_ds
, addr
, color
);
322 /* Pixel may cross a window boundary */
323 for (i
= 0; i
< 3; ++i
) {
324 addr
= vesa_SetWindow(vesa_read_win
, offset
+ i
);
325 _farpokeb(_dos_ds
, addr
, (unsigned char) (color
>> (i
* 8)));
330 addr
= vesa_SetWindow(vesa_write_win
, offset
);
331 _farpokel(_dos_ds
, addr
, color
);
337 vesa_WritePixel(x
, y
, color
)
341 if (vesa_pixel_size
== 8) {
342 vesa_WritePixel32(x
, y
, color
);
344 vesa_WritePixel32(x
, y
, vesa_palette
[color
& 0xFF]);
349 vesa_MakeColor(r
, g
, b
)
352 r
= (r
& 0xFF) >> (8 - vesa_red_size
);
353 g
= (g
& 0xFF) >> (8 - vesa_green_size
);
354 b
= (b
& 0xFF) >> (8 - vesa_blue_size
);
355 return ((unsigned long) r
<< vesa_red_pos
)
356 | ((unsigned long) g
<< vesa_green_pos
)
357 | ((unsigned long) b
<< vesa_blue_pos
);
361 vesa_GetRGB(color
, rp
, gp
, bp
)
363 unsigned char *rp
, *gp
, *bp
;
367 r
= color
>> vesa_red_pos
;
368 g
= color
>> vesa_green_pos
;
369 b
= color
>> vesa_blue_pos
;
370 r
<<= 8 - vesa_red_size
;
371 g
<<= 8 - vesa_green_size
;
372 b
<<= 8 - vesa_blue_size
;
373 *rp
= (unsigned char) r
;
374 *gp
= (unsigned char) g
;
375 *bp
= (unsigned char) b
;
379 vesa_FillRect(left
, top
, width
, height
, color
)
380 unsigned left
, top
, width
, height
, color
;
384 for (y
= 0; y
< height
; ++y
) {
385 for (x
= 0; x
< width
; ++x
) {
386 vesa_WritePixel(left
+ x
, top
+ y
, color
);
403 col
= curcol
; /* Character cell row and column */
408 vesa_gotoloc(col
, row
);
412 vesa_clear_screen(colour
)
415 vesa_FillRect(0, 0, vesa_x_res
, vesa_y_res
, colour
);
416 if (iflags
.tile_view
)
418 vesa_gotoloc(0, 0); /* is this needed? */
421 /* clear to end of line */
423 vesa_cl_end(col
, row
)
426 unsigned left
= vesa_x_center
+ col
* 8;
427 unsigned top
= vesa_y_center
+ row
* 16;
428 unsigned width
= (CO
- 1 - col
) * 8;
429 unsigned height
= 16;
431 vesa_FillRect(left
, top
, width
, height
, BACKGROUND_VESA_COLOR
);
434 /* clear to end of screen */
443 unsigned left
= vesa_x_center
;
444 unsigned top
= vesa_y_center
+ cy
* 16;
445 unsigned width
= 640;
446 unsigned height
= (LI
- 1 - cy
) * 16;
448 vesa_FillRect(left
, top
, width
, height
, BACKGROUND_VESA_COLOR
);
453 vesa_tty_end_screen()
455 vesa_clear_screen(BACKGROUND_VESA_COLOR
);
456 vesa_SwitchMode(MODETEXT
);
460 vesa_tty_startup(wid
, hgt
)
463 /* code to sense display adapter is required here - MJA */
471 attrib_gr_normal
= ATTRIB_VGA_NORMAL
;
472 attrib_gr_intense
= ATTRIB_VGA_INTENSE
;
473 g_attribute
= attrib_gr_normal
; /* Give it a starting value */
477 * Screen output routines (these are heavily used).
479 * These are the 3 routines used to place information on the screen
480 * in the VGA PC tty port of NetHack. These are the routines
481 * that get called by the general interface routines in video.c.
483 * vesa_xputs -Writes a c null terminated string at the current location.
485 * vesa_xputc -Writes a single character at the current location. Since
486 * various places in the code assume that control characters
487 * can be used to control, we are forced to interpret some of
488 * the more common ones, in order to keep things looking correct.
490 * vesa_xputg -This routine is used to display a graphical representation of a
491 * NetHack glyph (a tile) at the current location. For more
492 * information on NetHack glyphs refer to the comments in
498 vesa_xputs(s
, col
, row
)
503 vesa_WriteStr(s
, strlen(s
), col
, row
, g_attribute
);
507 /* write out character (and attribute) */
524 vesa_WriteChar((unsigned char) ch
, col
, row
, attr
, FALSE
);
529 vesa_gotoloc(col
, row
);
532 #if defined(USE_TILES)
533 /* Place tile represent. a glyph at current location */
535 vesa_xputg(glyphnum
, ch
,
539 unsigned special
; /* special feature: corpse, invis, detected, pet, ridden -
545 const struct TileImage
*packcell
;
549 if ((col
< 0 || col
>= COLNO
)
550 || (row
< TOP_MAP_ROW
|| row
>= (ROWNO
+ TOP_MAP_ROW
)))
552 ry
= row
- TOP_MAP_ROW
;
553 map
[ry
][col
].glyph
= glyphnum
;
554 map
[ry
][col
].ch
= ch
;
555 map
[ry
][col
].special
= special
;
556 attr
= (g_attribute
== 0) ? attrib_gr_normal
: g_attribute
;
557 map
[ry
][col
].attr
= attr
;
558 if (iflags
.traditional_view
) {
559 vesa_WriteChar((unsigned char) ch
, col
, row
, attr
, FALSE
);
561 if ((col
>= clipx
) && (col
<= clipxmax
)) {
562 packcell
= get_tile(glyph2tile
[glyphnum
]);
563 if (!iflags
.over_view
&& map
[ry
][col
].special
)
564 decal_packed(packcell
, special
);
565 vesa_DisplayCell(packcell
, col
- clipx
, row
);
570 vesa_gotoloc(col
, row
);
572 #endif /* USE_TILES */
575 * Cursor location manipulation, and location information fetching
579 * vesa_gotoloc(x,y) - Moves the "cursor" on screen to the specified x
580 * and y character cell location. This routine
581 * determines the location where screen writes
582 * will occur next, it does not change the location
583 * of the player on the NetHack level.
587 vesa_gotoloc(col
, row
)
590 curcol
= min(col
, CO
- 1); /* protection from callers */
591 currow
= min(row
, LI
- 1);
594 #if defined(USE_TILES) && defined(CLIPPING)
596 vesa_cliparound(x
, y
)
601 if (!iflags
.tile_view
|| iflags
.over_view
|| iflags
.traditional_view
)
605 clipx
= max(0, x
- (viewport_size
/ 2));
606 clipxmax
= clipx
+ (viewport_size
- 1);
607 } else if (x
> clipxmax
- 5) {
608 clipxmax
= min(COLNO
- 1, x
+ (viewport_size
/ 2));
609 clipx
= clipxmax
- (viewport_size
- 1);
612 if (on_level(&u
.uz0
, &u
.uz
) && !restoring
)
613 /* (void) doredraw(); */
622 const struct TileImage
*packcell
;
624 /* y here is in screen rows*/
625 /* Build each row in local memory, then write, to minimize use of the
626 window switch function */
627 for (y
= 0; y
< ROWNO
; ++y
) {
628 char buf
[TILE_Y
][640*2];
630 for (x
= clipx
; x
<= clipxmax
; ++x
) {
631 if (iflags
.traditional_view
) {
632 vesa_WriteCharInMemory((unsigned char) map
[y
][x
].ch
, x
,
633 buf
, map
[y
][x
].attr
);
636 packcell
= get_tile(glyph2tile
[t
]);
637 if (!iflags
.over_view
&& map
[y
][x
].special
)
638 decal_packed(packcell
, map
[y
][x
].special
);
639 vesa_DisplayCellInMemory(packcell
, x
- clipx
, buf
);
642 if (iflags
.over_view
&& vesa_pixel_size
!= 8) {
643 for (t
= 0; t
< TILE_Y
; ++t
) {
644 for (x
= 0; x
< 640; ++x
) {
645 unsigned long c1
= vesa_palette
[buf
[t
][x
* 2 + 0]];
646 unsigned long c2
= vesa_palette
[buf
[t
][x
* 2 + 1]];
647 unsigned char r1
, r2
, g1
, g2
, b1
, b2
;
649 vesa_GetRGB(c1
, &r1
, &g1
, &b1
);
650 vesa_GetRGB(c2
, &r2
, &g2
, &b2
);
654 vesa_WritePixel32(x
, (y
+ TOP_MAP_ROW
) * TILE_Y
+ t
,
655 vesa_MakeColor(r1
, g1
, b1
));
659 for (t
= 0; t
< TILE_Y
; ++t
) {
660 for (x
= 0; x
< 640; ++x
) {
661 vesa_WritePixel(x
, (y
+ TOP_MAP_ROW
) * TILE_Y
+ t
, buf
[t
][x
]);
667 #endif /* USE_TILES && CLIPPING */
675 /* pline("Into userpan"); */
676 if (iflags
.over_view
|| iflags
.traditional_view
)
679 x
= min(COLNO
- 1, clipxmax
+ 10);
681 x
= max(0, clipx
- 10);
682 vesa_cliparound(x
, 10); /* y value is irrelevant on VGA clipping */
691 /* vesa_HideCursor(); */
693 iflags
.over_view
= TRUE
;
697 iflags
.over_view
= FALSE
;
698 clipx
= max(0, (curcol
- viewport_size
/ 2));
699 if (clipx
> ((CO
- 1) - viewport_size
))
700 clipx
= (CO
- 1) - viewport_size
;
701 clipxmax
= clipx
+ (viewport_size
- 1);
709 /* vesa_HideCursor(); */
711 /* switch_symbols(FALSE); */
712 iflags
.traditional_view
= TRUE
;
716 iflags
.traditional_view
= FALSE
;
717 if (!iflags
.over_view
) {
718 clipx
= max(0, (curcol
- viewport_size
/ 2));
719 if (clipx
> ((CO
- 1) - viewport_size
))
720 clipx
= (CO
- 1) - viewport_size
;
721 clipxmax
= clipx
+ (viewport_size
- 1);
735 decal_packed(gp
, special
)
736 const struct TileImage
*gp
;
739 /* FIXME: the tile array is fixed in memory and should not be changed;
740 if we ever implement this, we'll have to copy the pixels */
741 if (special
& MG_CORPSE
) {
742 } else if (special
& MG_INVIS
) {
743 } else if (special
& MG_DETECT
) {
744 } else if (special
& MG_PET
) {
745 } else if (special
& MG_RIDDEN
) {
751 * initialize the SCREEN, switch it to graphics mode,
752 * initialize the pointers to the fonts, clear
759 const struct Pixel
*paletteptr
;
761 const char *tile_file
;
764 * Attempt to open the required tile files. If we can't
765 * don't perform the video mode switch, use TTY code instead.
768 tile_file
= iflags
.wc_tile_file
;
769 if (tile_file
== NULL
|| tile_file
== '\0') {
770 tile_file
= "nhtiles.bmp";
772 if (!read_tiles(tile_file
, FALSE
))
774 if (get_palette() == NULL
)
778 raw_printf("Reverting to TTY mode, tile initialization failure (%d).",
782 iflags
.tile_view
= FALSE
;
783 iflags
.over_view
= FALSE
;
786 /* clear_screen() */ /* not vesa_clear_screen() */
791 if (vesa_mode
== 0xFFFF) {
794 vesa_SwitchMode(vesa_mode
);
795 windowprocs
.win_cliparound
= vesa_cliparound
;
797 paletteptr
= get_palette();
798 iflags
.tile_view
= TRUE
;
799 iflags
.over_view
= FALSE
;
801 paletteptr
= defpalette
;
803 vesa_SetPalette(paletteptr
);
804 g_attribute
= attrib_gr_normal
;
805 font
= vesa_FontPtrs();
808 clipxmax
= clipx
+ (viewport_size
- 1);
812 * Switches modes of the video card.
814 * If mode == MODETEXT (0x03), then the card is placed into text
815 * mode. Otherwise, the card is placed in the mode selected by
816 * vesa_detect. Supported modes are those with packed 8 bit pixels.
820 vesa_SwitchMode(mode
)
825 if (mode
== MODETEXT
) {
828 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
829 } else if (mode
>= 0x100) {
832 regs
.x
.bx
= mode
& 0x81FF;
833 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
834 /* Record that the window position is unknown */
835 vesa_win_pos
[0] = 0xFFFFFFFF;
836 vesa_win_pos
[1] = 0xFFFFFFFF;
838 iflags
.grmode
= 0; /* force text mode for error msg */
839 regs
.x
.ax
= MODETEXT
;
840 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
841 g_attribute
= attrib_text_normal
;
842 impossible("vesa_SwitchMode: Bad video mode requested 0x%X", mode
);
847 * This allows grouping of several tasks to be done when
848 * switching back to text mode. This is a public (extern) function.
855 vesa_SwitchMode(MODETEXT
);
856 windowprocs
.win_cliparound
= tty_cliparound
;
857 g_attribute
= attrib_text_normal
;
858 iflags
.tile_view
= FALSE
;
863 * Returns a far pointer (or flat 32 bit pointer under djgpp) to the
864 * location of the appropriate ROM font for the _current_ video mode
865 * (so you must place the card into the desired video mode before
866 * calling this function).
868 * This function takes advantage of the video BIOS loading the
869 * address of the appropriate character definition table for
870 * the current graphics mode into interrupt vector 0x43 (0000:010C).
878 tmp
= (USHORT __far
*) MK_PTR(((USHORT
) FONT_PTR_SEGMENT
),
879 ((USHORT
) FONT_PTR_OFFSET
));
880 foff
= READ_ABSOLUTE_WORD(tmp
);
882 fseg
= READ_ABSOLUTE_WORD(tmp
);
883 retval
= (char __far
*) MK_PTR(fseg
, foff
);
888 * This will verify the existance of a VGA adapter on the machine.
889 * Video function call 0x4F00 returns 0x004F in AX if successful, and
890 * returns a VbeInfoBlock describing the features of the VESA BIOS.
895 int vbe_info_sel
= -1; /* custodial */
897 struct VbeInfoBlock vbe_info
;
899 unsigned long mode_addr
;
900 struct ModeInfoBlock mode_info
;
902 vbe_info_seg
= __dpmi_allocate_dos_memory(
903 (sizeof(vbe_info
) + 15) / 16,
905 if (vbe_info_seg
< 0) goto error
;
907 /* Request VBE 2.0 information if it is available */
908 memset(&vbe_info
, 0, sizeof(vbe_info
));
909 memcpy(vbe_info
.VbeSignature
, "VBE2", 4);
910 dosmemput(&vbe_info
, sizeof(vbe_info
), vbe_info_seg
* 16L);
912 /* Request VESA BIOS information */
915 regs
.x
.es
= vbe_info_seg
;
916 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
918 /* Check for successful completion of function: is VESA BIOS present? */
919 if (regs
.x
.ax
!= 0x004F) goto error
;
920 dosmemget(vbe_info_seg
* 16L, sizeof(vbe_info
), &vbe_info
);
921 if (memcmp(vbe_info
.VbeSignature
, "VESA", 4) != 0) goto error
;
923 /* Get the address of the mode list */
924 /* The mode list may be within the DOS memory area allocated above.
925 That area must remain allocated and must not be rewritten until
927 mode_addr
= (vbe_info
.VideoModePtr
>> 16) * 16L
928 + (vbe_info
.VideoModePtr
& 0xFFFF);
930 /* Scan the mode list for an acceptable mode */
931 vesa_mode
= vesa_FindMode(mode_addr
, 32);
932 if (vesa_mode
== 0xFFFF)
933 vesa_mode
= vesa_FindMode(mode_addr
, 24);
934 if (vesa_mode
== 0xFFFF)
935 vesa_mode
= vesa_FindMode(mode_addr
, 16);
936 if (vesa_mode
== 0xFFFF)
937 vesa_mode
= vesa_FindMode(mode_addr
, 15);
938 if (vesa_mode
== 0xFFFF)
939 vesa_mode
= vesa_FindMode(mode_addr
, 8);
940 if (vesa_mode
== 0xFFFF)
943 /* Set up the variables for the pixel functions */
944 vesa_GetModeInfo(vesa_mode
, &mode_info
);
945 vesa_x_res
= mode_info
.XResolution
;
946 vesa_y_res
= mode_info
.YResolution
;
947 vesa_x_center
= (vesa_x_res
- 640) / 2;
948 vesa_y_center
= (vesa_y_res
- 480) / 2;
949 vesa_scan_line
= mode_info
.BytesPerScanLine
;
950 vesa_win_size
= mode_info
.WinSize
* 1024L;
951 vesa_win_gran
= mode_info
.WinGranularity
* 1024L;
952 vesa_pixel_size
= mode_info
.BitsPerPixel
;
953 vesa_pixel_bytes
= (vesa_pixel_size
+ 7) / 8;
954 if (vbe_info
.VbeVersion
>= 0x0300) {
955 vesa_red_pos
= mode_info
.RedFieldPosition
;
956 vesa_red_size
= mode_info
.RedMaskSize
;
957 vesa_green_pos
= mode_info
.GreenFieldPosition
;
958 vesa_green_size
= mode_info
.GreenMaskSize
;
959 vesa_blue_pos
= mode_info
.BlueFieldPosition
;
960 vesa_blue_size
= mode_info
.BlueMaskSize
;
962 switch (vesa_pixel_size
) {
992 vesa_win_addr
[0] = mode_info
.WinASegment
* 16L;
993 vesa_win_addr
[1] = mode_info
.WinBSegment
* 16L;
994 vesa_win_pos
[0] = 0xFFFFFFFF; /* position unknown */
995 vesa_win_pos
[1] = 0xFFFFFFFF;
997 if (mode_info
.WinAAttributes
& 0x2) {
999 } else if (mode_info
.WinBAttributes
& 0x2) {
1002 goto error
; /* Shouldn't happen */
1005 if (mode_info
.WinAAttributes
& 0x4) {
1007 } else if (mode_info
.WinBAttributes
& 0x4) {
1010 goto error
; /* Shouldn't happen */
1013 __dpmi_free_dos_memory(vbe_info_sel
);
1017 if (vbe_info_sel
!= -1) __dpmi_free_dos_memory(vbe_info_sel
);
1022 vesa_FindMode(mode_addr
, bits
)
1023 unsigned long mode_addr
;
1026 unsigned selected_mode
;
1027 struct ModeInfoBlock mode_info0
, mode_info
;
1028 unsigned model
= (bits
== 8) ? 4 : 6;
1030 memset(&mode_info
, 0, sizeof(mode_info
));
1031 selected_mode
= 0xFFFF;
1033 unsigned mode
= _farpeekw(_dos_ds
, mode_addr
);
1034 if (mode
== 0xFFFF) break;
1037 /* Query the mode info; skip to next if not in fact supported */
1038 if (!vesa_GetModeInfo(mode
, &mode_info0
)) continue;
1040 /* Check that the mode is acceptable */
1041 if (mode_info0
.XResolution
< 640) continue;
1042 if (mode_info0
.YResolution
< 480) continue;
1043 if (mode_info0
.NumberOfPlanes
!= 1) continue;
1044 if (mode_info0
.BitsPerPixel
!= bits
) continue;
1045 if (mode_info0
.NumberOfBanks
!= 1) continue;
1046 if (mode_info0
.MemoryModel
!= model
) continue;
1047 if (mode_info0
.ModeAttributes
& 0x40) continue;
1049 /* The mode is OK. Accept it if it is smaller than any previous mode
1050 or if no previous mode is accepted. */
1051 if (selected_mode
== 0xFFFF
1052 || mode_info0
.XResolution
* mode_info0
.YResolution
1053 < mode_info
.XResolution
* mode_info
.YResolution
) {
1054 selected_mode
= mode
;
1055 mode_info
= mode_info0
;
1059 return selected_mode
;
1063 * Write character 'ch', at (x,y) and
1064 * do it using the colour 'colour'.
1068 vesa_WriteChar(chr
, col
, row
, colour
, transparent
)
1069 int chr
, col
, row
, colour
;
1070 boolean transparent
;
1075 unsigned char __far
*fp
= font
;
1078 pixx
= min(col
, (CO
- 1)) * 8; /* min() protects from callers */
1079 pixy
= min(row
, (LI
- 1)) * 16; /* assumes 8 x 16 char set */
1080 pixx
+= vesa_x_center
;
1081 pixy
+= vesa_y_center
;
1083 for (i
= 0; i
< MAX_ROWS_PER_CELL
; ++i
) {
1084 fnt
= READ_ABSOLUTE((fp
+ chr
* 16 + i
));
1085 for (j
= 0; j
< 8; ++j
) {
1086 if (fnt
& (0x80 >> j
)) {
1087 vesa_WritePixel(pixx
+ j
, pixy
+ i
, colour
+ FIRST_TEXT_COLOR
);
1088 } else if (!transparent
) {
1089 vesa_WritePixel(pixx
+ j
, pixy
+ i
, BACKGROUND_VESA_COLOR
);
1096 * Like vesa_WriteChar, but draw the character in local memory rather than in
1097 * the VGA frame buffer.
1099 * vesa_redrawmap uses this to gather a row of cells in local memory and then
1100 * draw them in strict row-major order, minimizing the use of the VESA
1101 * windowing function.
1105 vesa_WriteCharInMemory(chr
, col
, buf
, colour
)
1107 char buf
[TILE_Y
][640*2];
1113 unsigned char __far
*fp
= font
;
1116 pixx
= min(col
, (CO
- 1)) * 8; /* min() protects from callers */
1118 for (i
= 0; i
< MAX_ROWS_PER_CELL
; ++i
) {
1119 fnt
= READ_ABSOLUTE((fp
+ chr
* 16 + i
));
1120 for (j
= 0; j
< 8; ++j
) {
1121 if (fnt
& (0x80 >> j
)) {
1122 buf
[i
][pixx
+ j
] = colour
+ FIRST_TEXT_COLOR
;
1124 buf
[i
][pixx
+ j
] = BACKGROUND_VESA_COLOR
;
1131 * This is the routine that displays a high-res "cell" pointed to by 'gp'
1132 * at the desired location (col,row).
1134 * Note: (col,row) in this case refer to the coordinate location in
1135 * NetHack character grid terms, (ie. the 40 x 25 character grid),
1136 * not the x,y pixel location.
1140 vesa_DisplayCell(tile
, col
, row
)
1141 const struct TileImage
*tile
;
1144 int i
, j
, pixx
, pixy
;
1146 pixx
= col
* TILE_X
;
1147 pixy
= row
* TILE_Y
;
1148 if (iflags
.over_view
) {
1150 pixx
+= vesa_x_center
;
1151 pixy
+= vesa_y_center
;
1152 if (vesa_pixel_size
!= 8) {
1153 for (i
= 0; i
< TILE_Y
; ++i
) {
1154 for (j
= 0; j
< TILE_X
; j
+= 2) {
1155 unsigned index
= i
* tile
->width
+ j
;
1156 unsigned long c1
= vesa_palette
[tile
->indexes
[index
+ 0]];
1157 unsigned long c2
= vesa_palette
[tile
->indexes
[index
+ 1]];
1158 unsigned char r1
, r2
, g1
, g2
, b1
, b2
;
1160 vesa_GetRGB(c1
, &r1
, &g1
, &b1
);
1161 vesa_GetRGB(c2
, &r2
, &g2
, &b2
);
1165 vesa_WritePixel32(pixx
+ j
/ 2, pixy
+ i
,
1166 vesa_MakeColor(r1
, g1
, b1
));
1170 for (i
= 0; i
< TILE_Y
; ++i
) {
1171 for (j
= 0; j
< TILE_X
; j
+= 2) {
1172 unsigned index
= i
* tile
->width
+ j
;
1173 vesa_WritePixel(pixx
+ j
/ 2, pixy
+ i
, tile
->indexes
[index
]);
1178 pixx
+= vesa_x_center
;
1179 pixy
+= vesa_y_center
;
1180 for (i
= 0; i
< TILE_Y
; ++i
) {
1181 for (j
= 0; j
< TILE_X
; ++j
) {
1182 unsigned index
= i
* tile
->width
+ j
;
1183 vesa_WritePixel(pixx
+ j
, pixy
+ i
, tile
->indexes
[index
]);
1190 * Like vesa_DisplayCell, but draw the tile in local memory rather than in
1191 * the VGA frame buffer.
1193 * vesa_redrawmap uses this to gather a row of cells in local memory and then
1194 * draw them in strict row-major order, minimizing the use of the VESA
1195 * windowing function.
1199 vesa_DisplayCellInMemory(tile
, col
, buf
)
1200 const struct TileImage
*tile
;
1202 char buf
[TILE_Y
][640*2];
1206 pixx
= col
* TILE_X
;
1207 if (iflags
.over_view
&& vesa_pixel_size
== 8) {
1209 for (i
= 0; i
< TILE_Y
; ++i
) {
1210 for (j
= 0; j
< TILE_X
; j
+= 2) {
1211 unsigned index
= i
* tile
->width
+ j
;
1212 buf
[i
][pixx
+ j
/ 2] = tile
->indexes
[index
];
1216 for (i
= 0; i
< TILE_Y
; ++i
) {
1217 for (j
= 0; j
< TILE_X
; ++j
) {
1218 unsigned index
= i
* tile
->width
+ j
;
1219 buf
[i
][pixx
+ j
] = tile
->indexes
[index
];
1226 * Write the character string pointed to by 's', whose maximum length
1227 * is 'len' at location (x,y) using the 'colour' colour.
1231 vesa_WriteStr(s
, len
, col
, row
, colour
)
1233 int len
, col
, row
, colour
;
1235 const unsigned char *us
;
1238 /* protection from callers */
1243 us
= (const unsigned char *) s
;
1244 while ((*us
!= 0) && (i
< len
) && (col
< (CO
- 1))) {
1245 vesa_WriteChar(*us
, col
, row
, colour
, FALSE
);
1253 * Initialize the VGA palette with the desired colours. This
1254 * must be a series of 720 bytes for use with a card in 256
1255 * colour mode at 640 x 480. The first 240 palette entries are
1256 * used by the tile set; the last 16 are reserved for text.
1260 vesa_SetPalette(palette
)
1261 const struct Pixel
*palette
;
1263 if (vesa_pixel_size
== 8) {
1264 vesa_SetHardPalette(palette
);
1266 vesa_SetSoftPalette(palette
);
1271 vesa_SetHardPalette(palette
)
1272 const struct Pixel
*palette
;
1274 const struct Pixel
*p
= palette
;
1275 int palette_sel
= -1; /* custodial */
1277 unsigned long palette_ptr
;
1279 unsigned char r
, g
, b
;
1280 unsigned long color
;
1283 palette_seg
= __dpmi_allocate_dos_memory( 1024 / 16, &palette_sel
);
1284 if (palette_seg
< 0) goto error
;
1286 /* Use 8 bit DACs if we have them */
1287 memset(®s
, 0, sizeof(regs
));
1291 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
1292 if (regs
.x
.ax
!= 0x004F) {
1294 } else if (regs
.h
.bh
> 8) {
1297 shift
= 8 - regs
.h
.bh
;
1300 /* Set the tile set and text colors */
1301 palette_ptr
= palette_seg
* 16L;
1303 for (i
= 0; i
< FIRST_TEXT_COLOR
; ++i
) {
1307 color
= ((unsigned long) r
<< 16)
1308 | ((unsigned long) g
<< 8)
1309 | ((unsigned long) b
<< 0);
1310 _farpokel(_dos_ds
, palette_ptr
, color
);
1315 palette_ptr
+= FIRST_TEXT_COLOR
* 4;
1318 for (i
= FIRST_TEXT_COLOR
; i
< 256; ++i
) {
1322 color
= ((unsigned long) r
<< 16)
1323 | ((unsigned long) g
<< 8)
1324 | ((unsigned long) b
<< 0);
1325 _farpokel(_dos_ds
, palette_ptr
, color
);
1330 memset(®s
, 0, sizeof(regs
));
1336 regs
.x
.es
= palette_seg
;
1337 (void) __dpmi_int(VIDEO_BIOS
, ®s
);
1339 __dpmi_free_dos_memory(palette_sel
);
1343 if (palette_sel
!= -1) __dpmi_free_dos_memory(palette_sel
);
1348 vesa_SetSoftPalette(palette
)
1349 const struct Pixel
*palette
;
1351 const struct Pixel
*p
;
1353 unsigned char r
, g
, b
;
1355 /* Set the tile set and text colors */
1358 for (i
= 0; i
< FIRST_TEXT_COLOR
; ++i
) {
1362 vesa_palette
[i
] = vesa_MakeColor(r
, g
, b
);
1367 for (i
= FIRST_TEXT_COLOR
; i
< 256; ++i
) {
1371 vesa_palette
[i
] = vesa_MakeColor(r
, g
, b
);
1378 #define PBAR_ROW (LI - 4)
1379 #define PBAR_COLOR_ON 16 /* slate grey background colour of tiles */
1380 #define PBAR_COLOR_OFF 0 /* bluish grey, used in old style only */
1381 #define PBAR_COLOR_STAIRS CLR_BROWN /* brown */
1382 #define PBAR_COLOR_HERO CLR_WHITE /* creamy white */
1384 static unsigned char pbar
[COLNO
];
1387 vesa_update_positionbar(posbar
)
1400 char *posbar
= pbar
;
1402 int k
, x
, y
, colour
, row
;
1405 boolean nowhere
= FALSE
;
1406 int pixy
= (PBAR_ROW
* MAX_ROWS_PER_CELL
);
1409 if (!iflags
.grmode
|| !iflags
.tile_view
)
1411 if ((clipx
< 0) || (clipxmax
<= 0) || (clipx
>= clipxmax
))
1415 pline("Would have put bar using %d - %d.", clipx
, clipxmax
);
1420 for (y
= pixy
; y
< (pixy
+ MAX_ROWS_PER_CELL
); ++y
) {
1421 for (x
= 0; x
< 640; ++x
) {
1423 if ((k
< clipx
) || (k
> clipxmax
)) {
1424 colour
= PBAR_COLOR_OFF
;
1426 colour
= PBAR_COLOR_ON
;
1427 vesa_WritePixel(x
+ vesa_x_center
, y
+ vesa_y_center
, colour
);
1431 for (y
= pixy
, row
= 0; y
< (pixy
+ MAX_ROWS_PER_CELL
); ++y
, ++row
) {
1432 if ((!row
) || (row
== (ROWS_PER_CELL
- 1))) {
1434 stopk
= SCREENBYTES
;
1439 for (x
= 0; x
< 640; ++x
) {
1441 if ((k
< startk
) || (k
> stopk
))
1442 colour
= BACKGROUND_VGA_COLOR
;
1444 colour
= PBAR_COLOR_ON
;
1445 vesa_WritePixel(x
+ vesa_x_center
, y
+ vesa_y_center
, colour
);
1451 while (*posbar
!= 0) {
1452 feature
= *posbar
++;
1455 vesa_WriteChar(feature
, (int) *posbar
++, PBAR_ROW
, PBAR_COLOR_STAIRS
, TRUE
);
1458 vesa_WriteChar(feature
, (int) *posbar
++, PBAR_ROW
, PBAR_COLOR_STAIRS
, TRUE
);
1461 ucol
= (int) *posbar
++;
1462 vesa_WriteChar(feature
, ucol
, PBAR_ROW
, PBAR_COLOR_HERO
, TRUE
);
1464 default: /* unanticipated symbols */
1465 vesa_WriteChar(feature
, (int) *posbar
++, PBAR_ROW
, PBAR_COLOR_STAIRS
, TRUE
);
1470 #ifdef SIMULATE_CURSOR
1473 if ((tmp
!= ucol
) && (curcol
>= 0))
1474 vesa_WriteChar('_', tmp
, PBAR_ROW
, PBAR_COLOR_HERO
, TRUE
);
1479 #endif /*POSITIONBAR*/
1481 #ifdef SIMULATE_CURSOR
1483 static unsigned long undercursor
[TILE_Y
][TILE_X
];
1488 unsigned x
, y
, left
, top
, right
, bottom
, width
;
1489 boolean isrogue
= Is_rogue_level(&u
.uz
);
1491 (isrogue
|| iflags
.over_view
|| iflags
.traditional_view
|| !inmap
);
1494 if (!cursor_type
&& inmap
)
1495 return; /* CURSOR_INVIS - nothing to do */
1497 x
= min(curcol
, (CO
- 1)); /* protection from callers */
1498 y
= min(currow
, (LI
- 1)); /* protection from callers */
1499 if (!halfwidth
&& ((x
< clipx
) || (x
> clipxmax
)))
1503 left
= x
* TILE_X
; /* convert to pixels */
1511 left
+= vesa_x_center
;
1512 top
+= vesa_y_center
;
1513 right
= left
+ width
- 1;
1514 bottom
= top
+ TILE_Y
- 1;
1516 for (y
= 0; y
< ROWS_PER_CELL
; ++y
) {
1517 for (x
= 0; x
< width
; ++x
) {
1518 undercursor
[y
][x
] = vesa_ReadPixel32(left
+ x
, top
+ y
);
1523 * Now we have a snapshot of the current cell.
1524 * Write the cursor on top of the display.
1528 curtyp
= cursor_type
;
1530 curtyp
= CURSOR_UNDERLINE
;
1534 vesa_WritePixel(left
, top
, FIRST_TEXT_COLOR
+ 15);
1535 vesa_WritePixel(left
+ 1, top
, FIRST_TEXT_COLOR
+ 15);
1536 vesa_WritePixel(right
- 1, top
, FIRST_TEXT_COLOR
+ 15);
1537 vesa_WritePixel(right
, top
, FIRST_TEXT_COLOR
+ 15);
1538 vesa_WritePixel(left
, top
+ 1, FIRST_TEXT_COLOR
+ 15);
1539 vesa_WritePixel(right
, top
+ 1, FIRST_TEXT_COLOR
+ 15);
1540 vesa_WritePixel(left
, bottom
- 1, FIRST_TEXT_COLOR
+ 15);
1541 vesa_WritePixel(right
, bottom
- 1, FIRST_TEXT_COLOR
+ 15);
1542 vesa_WritePixel(left
, bottom
, FIRST_TEXT_COLOR
+ 15);
1543 vesa_WritePixel(left
+ 1, bottom
, FIRST_TEXT_COLOR
+ 15);
1544 vesa_WritePixel(right
- 1, bottom
, FIRST_TEXT_COLOR
+ 15);
1545 vesa_WritePixel(right
, bottom
, FIRST_TEXT_COLOR
+ 15);
1548 case CURSOR_UNDERLINE
:
1549 for (x
= left
; x
<= right
; ++x
) {
1550 vesa_WritePixel(x
, bottom
, FIRST_TEXT_COLOR
+ 15);
1559 for (x
= left
; x
<= right
; ++x
) {
1560 vesa_WritePixel(x
, top
, FIRST_TEXT_COLOR
+ 15);
1562 for (y
= top
+ 1; y
<= bottom
- 1; ++y
) {
1563 vesa_WritePixel(left
, y
, FIRST_TEXT_COLOR
+ 15);
1564 vesa_WritePixel(right
, y
, FIRST_TEXT_COLOR
+ 15);
1566 for (x
= left
; x
<= right
; ++x
) {
1567 vesa_WritePixel(x
, bottom
, FIRST_TEXT_COLOR
+ 15);
1580 unsigned x
, y
, left
, top
, width
;
1581 boolean isrogue
= Is_rogue_level(&u
.uz
);
1583 (isrogue
|| iflags
.over_view
|| iflags
.traditional_view
|| !inmap
);
1586 if (!cursor_type
&& inmap
)
1587 return; /* CURSOR_INVIS - nothing to do */
1589 x
= min(curcol
, (CO
- 1)); /* protection from callers */
1590 y
= min(currow
, (LI
- 1)); /* protection from callers */
1591 if (!halfwidth
&& ((x
< clipx
) || (x
> clipxmax
)))
1595 left
= x
* TILE_X
; /* convert to pixels */
1603 left
+= vesa_x_center
;
1604 top
+= vesa_y_center
;
1606 for (y
= 0; y
< ROWS_PER_CELL
; ++y
) {
1607 for (x
= 0; x
< width
; ++x
) {
1608 vesa_WritePixel32(left
+ x
, top
+ y
, undercursor
[y
][x
]);
1612 #endif /* SIMULATE_CURSOR */
1613 #endif /* SCREEN_VESA */