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. */
4 /*
5  * vidvesa.c - VGA Hardware video support with VESA BIOS Extensions
6  */
7 
8 #include "hack.h"
9 
10 #ifdef SCREEN_VESA /* this file is for SCREEN_VESA only    */
11 #include <dpmi.h>
12 
13 #include "pcvideo.h"
14 #include "tile.h"
15 #include "pctiles.h"
16 #include "vesa.h"
17 #include "wintty.h"
18 #include "tileset.h"
19 
20 #define BACKGROUND_VESA_COLOR 1
21 #define FIRST_TEXT_COLOR 240
22 
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, (
30         unsigned long color,
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,
35         unsigned color));
36 
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],
50         int));
51 static void FDECL(vesa_WriteStr, (const char *, int, int, int, int));
52 static char __far *NDECL(vesa_FontPtrs);
53 
54 #ifdef POSITIONBAR
55 static void NDECL(positionbar);
56 #endif
57 
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;
66 
67 /*
68  * Global Variables
69  */
70 
71 static unsigned char __far *font;
72 
73 static struct map_struct {
74     int glyph;
75     int ch;
76     int attr;
77     unsigned special;
78 } map[ROWNO][COLNO]; /* track the glyphs */
79 
80 #define vesa_clearmap()                                   \
81     {                                                     \
82         int x, y;                                         \
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;                   \
87                 map[y][x].attr = 0;                       \
88                 map[y][x].special = 0;                    \
89             }                                             \
90     }
91 #define TOP_MAP_ROW 1
92 
93 static int viewport_size = 40;
94 
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 */
112 };
113 
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];
136 
137 struct OldModeInfo {
138     unsigned mode;
139 
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 */
144 };
145 
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 },
163 };
164 
165 /* Retrieve the mode info block */
166 static boolean
vesa_GetModeInfo(mode,info)167 vesa_GetModeInfo(mode, info)
168 unsigned mode;
169 struct ModeInfoBlock *info;
170 {
171     int mode_info_sel = -1; /* custodial */
172     int mode_info_seg;
173     __dpmi_regs regs;
174 
175     mode_info_seg = __dpmi_allocate_dos_memory(
176             (sizeof(*info) + 15) / 16,
177             &mode_info_sel);
178     if (mode_info_seg < 0) goto error;
179 
180     memset(info, 0, sizeof(*info));
181     dosmemput(info, sizeof(*info), mode_info_seg * 16L);
182 
183     memset(&regs, 0, sizeof(regs));
184     regs.x.ax = 0x4F01;
185     regs.x.cx = mode;
186     regs.x.di = 0;
187     regs.x.es = mode_info_seg;
188     (void) __dpmi_int(VIDEO_BIOS, &regs);
189 
190     if (regs.x.ax != 0x004F) goto error;
191     dosmemget(mode_info_seg * 16L, sizeof(*info), info);
192     if (!(info->ModeAttributes & 0x0001)) goto error;
193 
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
197            mode properties */
198 
199         unsigned i;
200 
201         for (i = 0; i < SIZE(old_mode_table); ++i) {
202             if (mode == old_mode_table[i].mode) {
203                 break;
204             }
205         }
206         if (i >= SIZE(old_mode_table)) goto error;
207 
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;
214     }
215 
216     __dpmi_free_dos_memory(mode_info_sel);
217     return TRUE;
218 
219 error:
220     if (mode_info_sel != -1) __dpmi_free_dos_memory(mode_info_sel);
221     return FALSE;
222 }
223 
224 /* Set the memory window and return the offset */
225 static unsigned long
vesa_SetWindow(window,offset)226 vesa_SetWindow(window, offset)
227 int window;
228 unsigned long offset;
229 {
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.
233 
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
236        called. */
237 
238     unsigned long pos = vesa_win_pos[window];
239     if (offset < pos || pos + vesa_win_size <= offset) {
240         __dpmi_regs regs;
241 
242         memset(&regs, 0, sizeof(regs));
243         regs.x.ax = 0x4F05;
244         regs.h.bh = 0x00;
245         regs.h.bl = window;
246         regs.x.dx = offset / vesa_win_gran;
247         pos = regs.x.dx * vesa_win_gran;
248         (void) __dpmi_int(VIDEO_BIOS, &regs);
249         vesa_win_pos[window] = pos;
250     }
251 
252     offset = offset - vesa_win_pos[window] + vesa_win_addr[window];
253     /* Keep from crashing the system if some malfunction gives us a bad
254        offset */
255     if (offset < 0xA0000 || offset > 0xBFFFF) {
256         vesa_SwitchMode(MODETEXT);
257         fprintf(stderr, "Abort: offset=%08lX\n", offset);
258         exit(1);
259     }
260     return offset;
261 }
262 
263 static unsigned long
vesa_ReadPixel32(x,y)264 vesa_ReadPixel32(x, y)
265 unsigned x, y;
266 {
267     unsigned long offset = y * vesa_scan_line + x * vesa_pixel_bytes;
268     unsigned long addr, color;
269     unsigned i;
270 
271     switch (vesa_pixel_size) {
272     case 8:
273         addr = vesa_SetWindow(vesa_read_win, offset);
274         color = _farpeekb(_dos_ds, addr);
275         break;
276 
277     case 15:
278     case 16:
279         addr = vesa_SetWindow(vesa_read_win, offset);
280         color = _farpeekw(_dos_ds, addr);
281         break;
282 
283     case 24:
284         /* Pixel may cross a window boundary */
285         color = 0;
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);
289         }
290         break;
291 
292     case 32:
293         addr = vesa_SetWindow(vesa_read_win, offset);
294         color = _farpeekl(_dos_ds, addr);
295         break;
296     }
297     return color;
298 }
299 
300 static void
vesa_WritePixel32(x,y,color)301 vesa_WritePixel32(x, y, color)
302 unsigned x, y;
303 unsigned long color;
304 {
305     unsigned long offset = y * vesa_scan_line + x * vesa_pixel_bytes;
306     unsigned long addr;
307     unsigned i;
308 
309     switch (vesa_pixel_size) {
310     case 8:
311         addr = vesa_SetWindow(vesa_write_win, offset);
312         _farpokeb(_dos_ds, addr, color);
313         break;
314 
315     case 15:
316     case 16:
317         addr = vesa_SetWindow(vesa_write_win, offset);
318         _farpokew(_dos_ds, addr, color);
319         break;
320 
321     case 24:
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)));
326         }
327         break;
328 
329     case 32:
330         addr = vesa_SetWindow(vesa_write_win, offset);
331         _farpokel(_dos_ds, addr, color);
332         break;
333     }
334 }
335 
336 static void
vesa_WritePixel(x,y,color)337 vesa_WritePixel(x, y, color)
338 unsigned x, y;
339 unsigned color;
340 {
341     if (vesa_pixel_size == 8) {
342         vesa_WritePixel32(x, y, color);
343     } else {
344         vesa_WritePixel32(x, y, vesa_palette[color & 0xFF]);
345     }
346 }
347 
348 static unsigned long
vesa_MakeColor(r,g,b)349 vesa_MakeColor(r, g, b)
350 unsigned r, g, b;
351 {
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);
358 }
359 
360 static void
vesa_GetRGB(color,rp,gp,bp)361 vesa_GetRGB(color, rp, gp, bp)
362 unsigned long color;
363 unsigned char *rp, *gp, *bp;
364 {
365     unsigned r, g, b;
366 
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;
376 }
377 
378 static void
vesa_FillRect(left,top,width,height,color)379 vesa_FillRect(left, top, width, height, color)
380 unsigned left, top, width, height, color;
381 {
382     unsigned x, y;
383 
384     for (y = 0; y < height; ++y) {
385         for (x = 0; x < width; ++x) {
386             vesa_WritePixel(left + x, top + y, color);
387         }
388     }
389 }
390 
391 void
vesa_get_scr_size()392 vesa_get_scr_size()
393 {
394     CO = 80;
395     LI = 29;
396 }
397 
398 void
vesa_backsp()399 vesa_backsp()
400 {
401     int col, row;
402 
403     col = curcol; /* Character cell row and column */
404     row = currow;
405 
406     if (col > 0)
407         col = col - 1;
408     vesa_gotoloc(col, row);
409 }
410 
411 void
vesa_clear_screen(colour)412 vesa_clear_screen(colour)
413 int colour;
414 {
415     vesa_FillRect(0, 0, vesa_x_res, vesa_y_res, colour);
416     if (iflags.tile_view)
417         vesa_clearmap();
418     vesa_gotoloc(0, 0); /* is this needed? */
419 }
420 
421 /* clear to end of line */
422 void
vesa_cl_end(col,row)423 vesa_cl_end(col, row)
424 int col, row;
425 {
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;
430 
431     vesa_FillRect(left, top, width, height, BACKGROUND_VESA_COLOR);
432 }
433 
434 /* clear to end of screen */
435 void
vesa_cl_eos(cy)436 vesa_cl_eos(cy)
437 int cy;
438 {
439     int count;
440 
441     cl_end();
442     if (cy < LI - 1) {
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;
447 
448         vesa_FillRect(left, top, width, height, BACKGROUND_VESA_COLOR);
449     }
450 }
451 
452 void
vesa_tty_end_screen()453 vesa_tty_end_screen()
454 {
455     vesa_clear_screen(BACKGROUND_VESA_COLOR);
456     vesa_SwitchMode(MODETEXT);
457 }
458 
459 void
vesa_tty_startup(wid,hgt)460 vesa_tty_startup(wid, hgt)
461 int *wid, *hgt;
462 {
463     /* code to sense display adapter is required here - MJA */
464 
465     vesa_get_scr_size();
466     if (CO && LI) {
467         *wid = CO;
468         *hgt = LI;
469     }
470 
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 */
474 }
475 
476 /*
477  * Screen output routines (these are heavily used).
478  *
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.
482  *
483  * vesa_xputs -Writes a c null terminated string at the current location.
484  *
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.
489  *
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
493  *             include/display.h.
494  *
495  */
496 
497 void
vesa_xputs(s,col,row)498 vesa_xputs(s, col, row)
499 const char *s;
500 int col, row;
501 {
502     if (s != NULL) {
503         vesa_WriteStr(s, strlen(s), col, row, g_attribute);
504     }
505 }
506 
507 /* write out character (and attribute) */
508 void
vesa_xputc(ch,attr)509 vesa_xputc(ch, attr)
510 char ch;
511 int attr;
512 {
513     int col, row;
514 
515     col = curcol;
516     row = currow;
517 
518     switch (ch) {
519     case '\n':
520         col = 0;
521         ++row;
522         break;
523     default:
524         vesa_WriteChar((unsigned char) ch, col, row, attr, FALSE);
525         if (col < (CO - 1))
526             ++col;
527         break;
528     } /* end switch */
529     vesa_gotoloc(col, row);
530 }
531 
532 #if defined(USE_TILES)
533 /* Place tile represent. a glyph at current location */
534 void
vesa_xputg(glyphnum,ch,special)535 vesa_xputg(glyphnum, ch,
536           special)
537 int glyphnum;
538 int ch;
539 unsigned special; /* special feature: corpse, invis, detected, pet, ridden -
540                      hack.h */
541 {
542     int col, row;
543     int attr;
544     int ry;
545     const struct TileImage *packcell;
546 
547     row = currow;
548     col = curcol;
549     if ((col < 0 || col >= COLNO)
550         || (row < TOP_MAP_ROW || row >= (ROWNO + TOP_MAP_ROW)))
551         return;
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);
560     } else {
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);
566         }
567     }
568     if (col < (CO - 1))
569         ++col;
570     vesa_gotoloc(col, row);
571 }
572 #endif /* USE_TILES */
573 
574 /*
575  * Cursor location manipulation, and location information fetching
576  * routines.
577  * These include:
578  *
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.
584  */
585 
586 void
vesa_gotoloc(col,row)587 vesa_gotoloc(col, row)
588 int col, row;
589 {
590     curcol = min(col, CO - 1); /* protection from callers */
591     currow = min(row, LI - 1);
592 }
593 
594 #if defined(USE_TILES) && defined(CLIPPING)
595 static void
vesa_cliparound(x,y)596 vesa_cliparound(x, y)
597 int x, y;
598 {
599     int oldx = clipx;
600 
601     if (!iflags.tile_view || iflags.over_view || iflags.traditional_view)
602         return;
603 
604     if (x < clipx + 5) {
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);
610     }
611     if (clipx != oldx) {
612         if (on_level(&u.uz0, &u.uz) && !restoring)
613             /* (void) doredraw(); */
614             vesa_redrawmap();
615     }
616 }
617 
618 static void
vesa_redrawmap()619 vesa_redrawmap()
620 {
621     int x, y, t;
622     const struct TileImage *packcell;
623 
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];
629 
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);
634             } else {
635                 t = map[y][x].glyph;
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);
640             }
641         }
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;
648 
649                     vesa_GetRGB(c1, &r1, &g1, &b1);
650                     vesa_GetRGB(c2, &r2, &g2, &b2);
651                     r1 = (r1 + r2) / 2;
652                     g1 = (g1 + g2) / 2;
653                     b1 = (b1 + b2) / 2;
654                     vesa_WritePixel32(x, (y + TOP_MAP_ROW) * TILE_Y + t,
655                             vesa_MakeColor(r1, g1, b1));
656                 }
657             }
658         } else {
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]);
662                 }
663             }
664         }
665     }
666 }
667 #endif /* USE_TILES && CLIPPING */
668 
669 void
vesa_userpan(left)670 vesa_userpan(left)
671 boolean left;
672 {
673     int x;
674 
675     /*	pline("Into userpan"); */
676     if (iflags.over_view || iflags.traditional_view)
677         return;
678     if (left)
679         x = min(COLNO - 1, clipxmax + 10);
680     else
681         x = max(0, clipx - 10);
682     vesa_cliparound(x, 10); /* y value is irrelevant on VGA clipping */
683     positionbar();
684     vesa_DrawCursor();
685 }
686 
687 void
vesa_overview(on)688 vesa_overview(on)
689 boolean on;
690 {
691     /*	vesa_HideCursor(); */
692     if (on) {
693         iflags.over_view = TRUE;
694         clipx = 0;
695         clipxmax = CO - 1;
696     } else {
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);
702     }
703 }
704 
705 void
vesa_traditional(on)706 vesa_traditional(on)
707 boolean on;
708 {
709     /*	vesa_HideCursor(); */
710     if (on) {
711         /*		switch_symbols(FALSE); */
712         iflags.traditional_view = TRUE;
713         clipx = 0;
714         clipxmax = CO - 1;
715     } else {
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);
722         }
723     }
724 }
725 
726 void
vesa_refresh()727 vesa_refresh()
728 {
729     positionbar();
730     vesa_redrawmap();
731     vesa_DrawCursor();
732 }
733 
734 static void
decal_packed(gp,special)735 decal_packed(gp, special)
736 const struct TileImage *gp;
737 unsigned special;
738 {
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) {
746     }
747 }
748 
749 /*
750  * Open tile files,
751  * initialize the SCREEN, switch it to graphics mode,
752  * initialize the pointers to the fonts, clear
753  * the screen.
754  *
755  */
756 void
vesa_Init(void)757 vesa_Init(void)
758 {
759     const struct Pixel *paletteptr;
760 #ifdef USE_TILES
761     const char *tile_file;
762     int tilefailure = 0;
763     /*
764      * Attempt to open the required tile files. If we can't
765      * don't perform the video mode switch, use TTY code instead.
766      *
767      */
768     tile_file = iflags.wc_tile_file;
769     if (tile_file == NULL || *tile_file == '\0') {
770         tile_file = "nhtiles.bmp";
771     }
772     if (!read_tiles(tile_file, FALSE))
773         tilefailure |= 1;
774     if (get_palette() == NULL)
775         tilefailure |= 4;
776 
777     if (tilefailure) {
778         raw_printf("Reverting to TTY mode, tile initialization failure (%d).",
779                    tilefailure);
780         wait_synch();
781         iflags.usevga = 0;
782         iflags.tile_view = FALSE;
783         iflags.over_view = FALSE;
784         CO = 80;
785         LI = 25;
786         /*	clear_screen()	*/ /* not vesa_clear_screen() */
787         return;
788     }
789 #endif
790 
791     if (vesa_mode == 0xFFFF) {
792         vesa_detect();
793     }
794     vesa_SwitchMode(vesa_mode);
795     windowprocs.win_cliparound = vesa_cliparound;
796 #ifdef USE_TILES
797     paletteptr = get_palette();
798     iflags.tile_view = TRUE;
799     iflags.over_view = FALSE;
800 #else
801     paletteptr = defpalette;
802 #endif
803     vesa_SetPalette(paletteptr);
804     g_attribute = attrib_gr_normal;
805     font = vesa_FontPtrs();
806     clear_screen();
807     clipx = 0;
808     clipxmax = clipx + (viewport_size - 1);
809 }
810 
811 /*
812  * Switches modes of the video card.
813  *
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.
817  *
818  */
819 static void
vesa_SwitchMode(mode)820 vesa_SwitchMode(mode)
821 unsigned mode;
822 {
823     __dpmi_regs regs;
824 
825     if (mode == MODETEXT) {
826         iflags.grmode = 0;
827         regs.x.ax = mode;
828         (void) __dpmi_int(VIDEO_BIOS, &regs);
829     } else if (mode >= 0x100) {
830         iflags.grmode = 1;
831         regs.x.ax = 0x4F02;
832         regs.x.bx = mode & 0x81FF;
833         (void) __dpmi_int(VIDEO_BIOS, &regs);
834         /* Record that the window position is unknown */
835         vesa_win_pos[0] = 0xFFFFFFFF;
836         vesa_win_pos[1] = 0xFFFFFFFF;
837     } else {
838         iflags.grmode = 0; /* force text mode for error msg */
839         regs.x.ax = MODETEXT;
840         (void) __dpmi_int(VIDEO_BIOS, &regs);
841         g_attribute = attrib_text_normal;
842         impossible("vesa_SwitchMode: Bad video mode requested 0x%X", mode);
843     }
844 }
845 
846 /*
847  * This allows grouping of several tasks to be done when
848  * switching back to text mode. This is a public (extern) function.
849  *
850  */
851 void
vesa_Finish(void)852 vesa_Finish(void)
853 {
854     free_tiles();
855     vesa_SwitchMode(MODETEXT);
856     windowprocs.win_cliparound = tty_cliparound;
857     g_attribute = attrib_text_normal;
858     iflags.tile_view = FALSE;
859 }
860 
861 /*
862  *
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).
867  *
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).
871  */
872 static char __far *
vesa_FontPtrs(void)873 vesa_FontPtrs(void)
874 {
875     USHORT __far *tmp;
876     char __far *retval;
877     USHORT fseg, foff;
878     tmp = (USHORT __far *) MK_PTR(((USHORT) FONT_PTR_SEGMENT),
879                                   ((USHORT) FONT_PTR_OFFSET));
880     foff = READ_ABSOLUTE_WORD(tmp);
881     ++tmp;
882     fseg = READ_ABSOLUTE_WORD(tmp);
883     retval = (char __far *) MK_PTR(fseg, foff);
884     return retval;
885 }
886 
887 /*
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.
891  */
892 int
vesa_detect()893 vesa_detect()
894 {
895     int vbe_info_sel = -1; /* custodial */
896     int vbe_info_seg;
897     struct VbeInfoBlock vbe_info;
898     __dpmi_regs regs;
899     unsigned long mode_addr;
900     struct ModeInfoBlock mode_info;
901 
902     vbe_info_seg = __dpmi_allocate_dos_memory(
903             (sizeof(vbe_info) + 15) / 16,
904             &vbe_info_sel);
905     if (vbe_info_seg < 0) goto error;
906 
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);
911 
912     /* Request VESA BIOS information */
913     regs.x.ax = 0x4F00;
914     regs.x.di = 0;
915     regs.x.es = vbe_info_seg;
916     (void) __dpmi_int(VIDEO_BIOS, &regs);
917 
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;
922 
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
926        we're done here. */
927     mode_addr = (vbe_info.VideoModePtr >> 16) * 16L
928               + (vbe_info.VideoModePtr & 0xFFFF);
929 
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)
941         goto error;
942 
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;
961     } else {
962         switch (vesa_pixel_size) {
963         case 15:
964             vesa_blue_pos = 0;
965             vesa_blue_size = 5;
966             vesa_green_pos = 5;
967             vesa_green_size = 5;
968             vesa_red_pos = 10;
969             vesa_red_size = 5;
970             break;
971 
972         case 16:
973             vesa_blue_pos = 0;
974             vesa_blue_size = 5;
975             vesa_green_pos = 5;
976             vesa_green_size = 6;
977             vesa_red_pos = 11;
978             vesa_red_size = 5;
979             break;
980 
981         case 24:
982         case 32:
983             vesa_blue_pos = 0;
984             vesa_blue_size = 8;
985             vesa_green_pos = 8;
986             vesa_green_size = 8;
987             vesa_red_pos = 16;
988             vesa_red_size = 8;
989             break;
990         }
991     }
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;
996     /* Read window */
997     if (mode_info.WinAAttributes & 0x2) {
998         vesa_read_win = 0;
999     } else if (mode_info.WinBAttributes & 0x2) {
1000         vesa_read_win = 1;
1001     } else {
1002         goto error; /* Shouldn't happen */
1003     }
1004     /* Write window */
1005     if (mode_info.WinAAttributes & 0x4) {
1006         vesa_write_win = 0;
1007     } else if (mode_info.WinBAttributes & 0x4) {
1008         vesa_write_win = 1;
1009     } else {
1010         goto error; /* Shouldn't happen */
1011     }
1012 
1013     __dpmi_free_dos_memory(vbe_info_sel);
1014     return TRUE;
1015 
1016 error:
1017     if (vbe_info_sel != -1) __dpmi_free_dos_memory(vbe_info_sel);
1018     return FALSE;
1019 }
1020 
1021 static unsigned
vesa_FindMode(mode_addr,bits)1022 vesa_FindMode(mode_addr, bits)
1023 unsigned long mode_addr;
1024 unsigned bits;
1025 {
1026     unsigned selected_mode;
1027     struct ModeInfoBlock mode_info0, mode_info;
1028     unsigned model = (bits == 8) ? 4 : 6;
1029 
1030     memset(&mode_info, 0, sizeof(mode_info));
1031     selected_mode = 0xFFFF;
1032     while (1) {
1033         unsigned mode = _farpeekw(_dos_ds, mode_addr);
1034         if (mode == 0xFFFF) break;
1035         mode_addr += 2;
1036 
1037         /* Query the mode info; skip to next if not in fact supported */
1038         if (!vesa_GetModeInfo(mode, &mode_info0)) continue;
1039 
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;
1048 
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;
1056         }
1057     }
1058 
1059     return selected_mode;
1060 }
1061 
1062 /*
1063  * Write character 'ch', at (x,y) and
1064  * do it using the colour 'colour'.
1065  *
1066  */
1067 static void
vesa_WriteChar(chr,col,row,colour,transparent)1068 vesa_WriteChar(chr, col, row, colour, transparent)
1069 int chr, col, row, colour;
1070 boolean transparent;
1071 {
1072     int i, j;
1073     int pixx, pixy;
1074 
1075     unsigned char __far *fp = font;
1076     unsigned char fnt;
1077 
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;
1082 
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);
1090             }
1091         }
1092     }
1093 }
1094 
1095 /*
1096  * Like vesa_WriteChar, but draw the character in local memory rather than in
1097  * the VGA frame buffer.
1098  *
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.
1102  *
1103  */
1104 static void
vesa_WriteCharInMemory(chr,col,buf,colour)1105 vesa_WriteCharInMemory(chr, col, buf, colour)
1106 int chr, col;
1107 char buf[TILE_Y][640*2];
1108 int colour;
1109 {
1110     int i, j;
1111     int pixx;
1112 
1113     unsigned char __far *fp = font;
1114     unsigned char fnt;
1115 
1116     pixx = min(col, (CO - 1)) * 8;  /* min() protects from callers */
1117 
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;
1123             } else {
1124                 buf[i][pixx + j] = BACKGROUND_VESA_COLOR;
1125             }
1126         }
1127     }
1128 }
1129 
1130 /*
1131  * This is the routine that displays a high-res "cell" pointed to by 'gp'
1132  * at the desired location (col,row).
1133  *
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.
1137  *
1138  */
1139 static void
vesa_DisplayCell(tile,col,row)1140 vesa_DisplayCell(tile, col, row)
1141 const struct TileImage *tile;
1142 int col, row;
1143 {
1144     int i, j, pixx, pixy;
1145 
1146     pixx = col * TILE_X;
1147     pixy = row * TILE_Y;
1148     if (iflags.over_view) {
1149         pixx /= 2;
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;
1159 
1160                     vesa_GetRGB(c1, &r1, &g1, &b1);
1161                     vesa_GetRGB(c2, &r2, &g2, &b2);
1162                     r1 = (r1 + r2) / 2;
1163                     g1 = (g1 + g2) / 2;
1164                     b1 = (b1 + b2) / 2;
1165                     vesa_WritePixel32(pixx + j / 2, pixy + i,
1166                             vesa_MakeColor(r1, g1, b1));
1167                 }
1168             }
1169         } else {
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]);
1174                 }
1175             }
1176         }
1177     } else {
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]);
1184             }
1185         }
1186     }
1187 }
1188 
1189 /*
1190  * Like vesa_DisplayCell, but draw the tile in local memory rather than in
1191  * the VGA frame buffer.
1192  *
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.
1196  *
1197  */
1198 static void
vesa_DisplayCellInMemory(tile,col,buf)1199 vesa_DisplayCellInMemory(tile, col, buf)
1200 const struct TileImage *tile;
1201 int col;
1202 char buf[TILE_Y][640*2];
1203 {
1204     int i, j, pixx;
1205 
1206     pixx = col * TILE_X;
1207     if (iflags.over_view && vesa_pixel_size == 8) {
1208         pixx /= 2;
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];
1213             }
1214         }
1215     } else {
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];
1220             }
1221         }
1222     }
1223 }
1224 
1225 /*
1226  * Write the character string pointed to by 's', whose maximum length
1227  * is 'len' at location (x,y) using the 'colour' colour.
1228  *
1229  */
1230 static void
vesa_WriteStr(s,len,col,row,colour)1231 vesa_WriteStr(s, len, col, row, colour)
1232 const char *s;
1233 int len, col, row, colour;
1234 {
1235     const unsigned char *us;
1236     int i = 0;
1237 
1238     /* protection from callers */
1239     if (row > (LI - 1))
1240         return;
1241 
1242     i = 0;
1243     us = (const unsigned char *) s;
1244     while ((*us != 0) && (i < len) && (col < (CO - 1))) {
1245         vesa_WriteChar(*us, col, row, colour, FALSE);
1246         ++us;
1247         ++i;
1248         ++col;
1249     }
1250 }
1251 
1252 /*
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.
1257  *
1258  */
1259 static boolean
vesa_SetPalette(palette)1260 vesa_SetPalette(palette)
1261 const struct Pixel *palette;
1262 {
1263     if (vesa_pixel_size == 8) {
1264         vesa_SetHardPalette(palette);
1265     } else {
1266         vesa_SetSoftPalette(palette);
1267     }
1268 }
1269 
1270 static boolean
vesa_SetHardPalette(palette)1271 vesa_SetHardPalette(palette)
1272 const struct Pixel *palette;
1273 {
1274     const struct Pixel *p = palette;
1275     int palette_sel = -1; /* custodial */
1276     int palette_seg;
1277     unsigned long palette_ptr;
1278     unsigned i, shift;
1279     unsigned char r, g, b;
1280     unsigned long color;
1281     __dpmi_regs regs;
1282 
1283     palette_seg = __dpmi_allocate_dos_memory( 1024 / 16, &palette_sel);
1284     if (palette_seg < 0) goto error;
1285 
1286     /* Use 8 bit DACs if we have them */
1287     memset(&regs, 0, sizeof(regs));
1288     regs.x.ax = 0x4F08;
1289     regs.h.bl = 0;
1290     regs.h.bh = 8;
1291     (void) __dpmi_int(VIDEO_BIOS, &regs);
1292     if (regs.x.ax != 0x004F) {
1293         shift = 2;
1294     } else if (regs.h.bh > 8) {
1295         shift = 0;
1296     } else {
1297         shift = 8 - regs.h.bh;
1298     }
1299 
1300     /* Set the tile set and text colors */
1301     palette_ptr = palette_seg * 16L;
1302 #ifdef USE_TILES
1303     for (i = 0; i < FIRST_TEXT_COLOR; ++i) {
1304         r = p->r >> shift;
1305         g = p->g >> shift;
1306         b = p->b >> shift;
1307         color =   ((unsigned long) r << 16)
1308                 | ((unsigned long) g <<  8)
1309                 | ((unsigned long) b <<  0);
1310         _farpokel(_dos_ds, palette_ptr, color);
1311         palette_ptr += 4;
1312         ++p;
1313     }
1314 #else
1315     palette_ptr += FIRST_TEXT_COLOR * 4;
1316 #endif
1317     p = defpalette;
1318     for (i = FIRST_TEXT_COLOR; i < 256; ++i) {
1319         r = p->r >> shift;
1320         g = p->g >> shift;
1321         b = p->b >> shift;
1322         color =   ((unsigned long) r << 16)
1323                 | ((unsigned long) g <<  8)
1324                 | ((unsigned long) b <<  0);
1325         _farpokel(_dos_ds, palette_ptr, color);
1326         palette_ptr += 4;
1327         ++p;
1328     }
1329 
1330     memset(&regs, 0, sizeof(regs));
1331     regs.x.ax = 0x4F09;
1332     regs.h.bl = 0;
1333     regs.x.cx = 256;
1334     regs.x.dx = 0;
1335     regs.x.di = 0;
1336     regs.x.es = palette_seg;
1337     (void) __dpmi_int(VIDEO_BIOS, &regs);
1338 
1339     __dpmi_free_dos_memory(palette_sel);
1340     return TRUE;
1341 
1342 error:
1343     if (palette_sel != -1) __dpmi_free_dos_memory(palette_sel);
1344     return FALSE;
1345 }
1346 
1347 static boolean
vesa_SetSoftPalette(palette)1348 vesa_SetSoftPalette(palette)
1349 const struct Pixel *palette;
1350 {
1351     const struct Pixel *p;
1352     unsigned i;
1353     unsigned char r, g, b;
1354 
1355     /* Set the tile set and text colors */
1356 #ifdef USE_TILES
1357     p = palette;
1358     for (i = 0; i < FIRST_TEXT_COLOR; ++i) {
1359         r = p->r;
1360         g = p->g;
1361         b = p->b;
1362         vesa_palette[i] = vesa_MakeColor(r, g, b);
1363         ++p;
1364     }
1365 #endif
1366     p = defpalette;
1367     for (i = FIRST_TEXT_COLOR; i < 256; ++i) {
1368         r = p->r;
1369         g = p->g;
1370         b = p->b;
1371         vesa_palette[i] = vesa_MakeColor(r, g, b);
1372         ++p;
1373     }
1374 }
1375 
1376 #ifdef POSITIONBAR
1377 
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 */
1383 
1384 static unsigned char pbar[COLNO];
1385 
1386 void
vesa_update_positionbar(posbar)1387 vesa_update_positionbar(posbar)
1388 char *posbar;
1389 {
1390     char *p = pbar;
1391     if (posbar)
1392         while (*posbar)
1393             *p++ = *posbar++;
1394     *p = 0;
1395 }
1396 
1397 static void
positionbar()1398 positionbar()
1399 {
1400     char *posbar = pbar;
1401     int feature, ucol;
1402     int k, x, y, colour, row;
1403 
1404     int startk, stopk;
1405     boolean nowhere = FALSE;
1406     int pixy = (PBAR_ROW * MAX_ROWS_PER_CELL);
1407     int tmp;
1408 
1409     if (!iflags.grmode || !iflags.tile_view)
1410         return;
1411     if ((clipx < 0) || (clipxmax <= 0) || (clipx >= clipxmax))
1412         nowhere = TRUE;
1413     if (nowhere) {
1414 #ifdef DEBUG
1415         pline("Would have put bar using %d - %d.", clipx, clipxmax);
1416 #endif
1417         return;
1418     }
1419 #ifdef OLD_STYLE
1420     for (y = pixy; y < (pixy + MAX_ROWS_PER_CELL); ++y) {
1421         for (x = 0; x < 640; ++x) {
1422             k = x / 8;
1423             if ((k < clipx) || (k > clipxmax)) {
1424                 colour = PBAR_COLOR_OFF;
1425             } else
1426                 colour = PBAR_COLOR_ON;
1427             vesa_WritePixel(x + vesa_x_center, y + vesa_y_center, colour);
1428         }
1429     }
1430 #else
1431     for (y = pixy, row = 0; y < (pixy + MAX_ROWS_PER_CELL); ++y, ++row) {
1432         if ((!row) || (row == (ROWS_PER_CELL - 1))) {
1433             startk = 0;
1434             stopk = SCREENBYTES;
1435         } else {
1436             startk = clipx;
1437             stopk = clipxmax;
1438         }
1439         for (x = 0; x < 640; ++x) {
1440             k = x / 8;
1441             if ((k < startk) || (k > stopk))
1442                 colour = BACKGROUND_VGA_COLOR;
1443             else
1444                 colour = PBAR_COLOR_ON;
1445             vesa_WritePixel(x + vesa_x_center, y + vesa_y_center, colour);
1446         }
1447     }
1448 #endif
1449     ucol = 0;
1450     if (posbar) {
1451         while (*posbar != 0) {
1452             feature = *posbar++;
1453             switch (feature) {
1454             case '>':
1455                 vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE);
1456                 break;
1457             case '<':
1458                 vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE);
1459                 break;
1460             case '@':
1461                 ucol = (int) *posbar++;
1462                 vesa_WriteChar(feature, ucol, PBAR_ROW, PBAR_COLOR_HERO, TRUE);
1463                 break;
1464             default: /* unanticipated symbols */
1465                 vesa_WriteChar(feature, (int) *posbar++, PBAR_ROW, PBAR_COLOR_STAIRS, TRUE);
1466                 break;
1467             }
1468         }
1469     }
1470 #ifdef SIMULATE_CURSOR
1471     if (inmap) {
1472         tmp = curcol + 1;
1473         if ((tmp != ucol) && (curcol >= 0))
1474             vesa_WriteChar('_', tmp, PBAR_ROW, PBAR_COLOR_HERO, TRUE);
1475     }
1476 #endif
1477 }
1478 
1479 #endif /*POSITIONBAR*/
1480 
1481 #ifdef SIMULATE_CURSOR
1482 
1483 static unsigned long undercursor[TILE_Y][TILE_X];
1484 
1485 void
vesa_DrawCursor()1486 vesa_DrawCursor()
1487 {
1488     unsigned x, y, left, top, right, bottom, width;
1489     boolean isrogue = Is_rogue_level(&u.uz);
1490     boolean halfwidth =
1491         (isrogue || iflags.over_view || iflags.traditional_view || !inmap);
1492     int curtyp;
1493 
1494     if (!cursor_type && inmap)
1495         return; /* CURSOR_INVIS - nothing to do */
1496 
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)))
1500         return;
1501     if (inmap)
1502         x -= clipx;
1503     left = x * TILE_X; /* convert to pixels */
1504     top  = y * TILE_Y;
1505     if (halfwidth) {
1506         left /= 2;
1507         width = TILE_X / 2;
1508     } else {
1509         width = TILE_X;
1510     }
1511     left += vesa_x_center;
1512     top  += vesa_y_center;
1513     right = left + width - 1;
1514     bottom = top + TILE_Y - 1;
1515 
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);
1519         }
1520     }
1521 
1522     /*
1523      * Now we have a snapshot of the current cell.
1524      * Write the cursor on top of the display.
1525      */
1526 
1527     if (inmap)
1528         curtyp = cursor_type;
1529     else
1530         curtyp = CURSOR_UNDERLINE;
1531 
1532     switch (curtyp) {
1533     case CURSOR_CORNER:
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);
1546         break;
1547 
1548     case CURSOR_UNDERLINE:
1549         for (x = left; x <= right; ++x) {
1550             vesa_WritePixel(x, bottom, FIRST_TEXT_COLOR + 15);
1551         }
1552         break;
1553 
1554     case CURSOR_FRAME:
1555 
1556     /* fall through */
1557 
1558     default:
1559         for (x = left; x <= right; ++x) {
1560             vesa_WritePixel(x, top, FIRST_TEXT_COLOR + 15);
1561         }
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);
1565         }
1566         for (x = left; x <= right; ++x) {
1567             vesa_WritePixel(x, bottom, FIRST_TEXT_COLOR + 15);
1568         }
1569         break;
1570     }
1571 #ifdef POSITIONBAR
1572     if (inmap)
1573         positionbar();
1574 #endif
1575 }
1576 
1577 void
vesa_HideCursor()1578 vesa_HideCursor()
1579 {
1580     unsigned x, y, left, top, width;
1581     boolean isrogue = Is_rogue_level(&u.uz);
1582     boolean halfwidth =
1583         (isrogue || iflags.over_view || iflags.traditional_view || !inmap);
1584     int curtyp;
1585 
1586     if (!cursor_type && inmap)
1587         return; /* CURSOR_INVIS - nothing to do */
1588 
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)))
1592         return;
1593     if (inmap)
1594         x -= clipx;
1595     left = x * TILE_X; /* convert to pixels */
1596     top  = y * TILE_Y;
1597     if (halfwidth) {
1598         left /= 2;
1599         width = TILE_X / 2;
1600     } else {
1601         width = TILE_X;
1602     }
1603     left += vesa_x_center;
1604     top  += vesa_y_center;
1605 
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]);
1609         }
1610     }
1611 }
1612 #endif /* SIMULATE_CURSOR */
1613 #endif /* SCREEN_VESA */
1614