OSDN Git Service

Initial revision
[hengband/hengband.git] / src / main-ibm.c
1 /* File: main-ibm.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison, and others
5  *
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.
9  */
10
11 /* Purpose: Visual Display Support for "term.c", for the IBM */
12
13
14 /*
15  * Original code by "Billy Tanksley (wtanksle@ucsd.edu)"
16  * Use "Makefile.ibm" to compile Angband using this file.
17  *
18  * Support for DJGPP v2 by "Scott Egashira (egashira@u.washington.edu)"
19  *
20  * Extensive modifications by "Ben Harrison (benh@phial.com)",
21  * including "collation" of the Watcom C/C++ and DOS-286 patches.
22  *
23  * Watcom C/C++ changes by "David Boeren (akemi@netcom.com)"
24  * Use "Makefile.wat" to compile this file with Watcom C/C++, and
25  * be sure to define "USE_IBM" and "USE_WAT".
26  *
27  * DOS-286 (conio.h) changes by (Roland Jay Roberts (jay@map.com)
28  * Use "Makefile.286" (not ready) to compile this file for DOS-286,
29  * and be sure to define "USE_IBM", "USE_WAT", and "USE_286".  Also,
30  * depending on your compiler, you may need to define "USE_CONIO".
31  *
32  * True color palette support by "Mike Marcelais (michmarc@microsoft.com)",
33  * with interface to the "color_table" array by Ben Harrison.
34  *
35  * Both "shift" keys are treated as "identical", and all the modifier keys
36  * (control, shift, alt) are ignored when used with "normal" keys, unless
37  * they modify the underlying "ascii" value of the key.  You must use the
38  * new "user pref files" to be able to interact with the keypad and such.
39  *
40  * The "lib/user/pref-ibm.prf" file contains macro definitions and possible
41  * alternative color set definitions.  The "lib/user/font-ibm.prf" contains
42  * attr/char mappings for walls and floors and such.
43  *
44  * Note the "Term_user_ibm()" function hook, which could allow the user
45  * to interact with the "main-ibm.c" visual system.  Currently this hook
46  * is unused, but, for example, it could allow the user to toggle "sound"
47  * or "graphics" modes, or to select the number of screen rows, with the
48  * extra screen rows being used for the mirror window.
49  */
50
51
52 #include "angband.h"
53
54
55 #ifdef USE_IBM
56
57 #define USE_CONIO
58
59 /*
60  * Use a "virtual" screen to "buffer" screen writes.
61  */
62 #define USE_VIRTUAL
63
64
65 #include <bios.h>
66 #include <dos.h>
67
68 #ifdef USE_WAT
69
70 # include <conio.h>
71
72 # ifdef USE_CONIO
73 # else /* USE_CONIO */
74
75 #  include <graph.h>
76
77 #  define bioskey(C)    _bios_keybrd(C)
78
79 # endif /* USE_CONIO */
80
81 # ifndef USE_286
82 #  define int86(a,b,c)  int386(a,b,c)
83 # endif
84
85 # define inportb(x)     inp(x)
86 # define outportb(x,y)  outp(x,y)
87
88 #else /* USE_WAT */
89
90 # if __DJGPP__ > 1
91
92 # include <pc.h>
93 # include <osfcn.h>
94
95 # else /* __DJGPP__ > 1 */
96 #  ifdef __DJGPP__
97 #   error "Upgrade to version 2.0 of DJGPP"
98 #  endif /* __DJGPP__ */
99 # endif /* __DJGPP__ > 1 */
100
101 #endif /* USE_WAT */
102
103
104 #ifdef USE_CONIO
105
106 # include <conio.h>
107
108 /*
109  * Hack -- write directly to video card
110  */
111 /* extern int directvideo = 1; */
112
113 /*
114  * Hack -- no virtual screen
115  */
116 # undef USE_VIRTUAL
117
118 #endif /* USE_CONIO */
119
120
121 /*
122  * Keypress input modifier flags (hard-coded by DOS)
123  */
124 #define K_RSHIFT        0       /* Right shift key down */
125 #define K_LSHIFT        1       /* Left shift key down */
126 #define K_CTRL          2       /* Ctrl key down */
127 #define K_ALT           3       /* Alt key down */
128 #define K_SCROLL        4       /* Scroll lock on */
129 #define K_NUM           5       /* Num lock on */
130 #define K_CAPS          6       /* Caps lock on */
131 #define K_INSERT        7       /* Insert on */
132
133
134 /*
135  * Foreground color bits (hard-coded by DOS)
136  */
137 #define VID_BLACK       0x00
138 #define VID_BLUE        0x01
139 #define VID_GREEN       0x02
140 #define VID_CYAN        0x03
141 #define VID_RED         0x04
142 #define VID_MAGENTA     0x05
143 #define VID_YELLOW      0x06
144 #define VID_WHITE       0x07
145
146 /*
147  * Bright text (hard-coded by DOS)
148  */
149 #define VID_BRIGHT      0x08
150
151 /*
152  * Background color bits (hard-coded by DOS)
153  */
154 #define VUD_BLACK       0x00
155 #define VUD_BLUE        0x10
156 #define VUD_GREEN       0x20
157 #define VUD_CYAN        0x30
158 #define VUD_RED         0x40
159 #define VUD_MAGENTA     0x50
160 #define VUD_YELLOW      0x60
161 #define VUD_WHITE       0x70
162
163 /*
164  * Blinking text (hard-coded by DOS)
165  */
166 #define VUD_BRIGHT      0x80
167
168
169 /*
170  * Screen Size
171  */
172 static int rows = 25;
173 static int cols = 80;
174
175
176 /*
177  * Physical Screen
178  */
179 #ifdef USE_286
180 # define PhysicalScreen ((byte *)MK_FP(0xB800,0x0000))
181 #else
182 # define PhysicalScreen ((byte *)(0xB800 << 4))
183 #endif
184
185
186 #ifdef USE_VIRTUAL
187
188 /*
189  * Virtual Screen Contents
190  */
191 static byte *VirtualScreen;
192
193 #else
194
195 /*
196  * Physical screen access
197  */
198 #define VirtualScreen PhysicalScreen
199
200 #endif
201
202
203 /*
204  * Hack -- the cursor "visibility"
205  */
206 static int saved_cur_v;
207 static int saved_cur_high;
208 static int saved_cur_low;
209
210
211 #ifdef USE_CONIO
212 #else /* USE_CONIO */
213
214 /*
215  * This array is used for "wiping" the screen
216  */
217 static byte wiper[160];
218
219 #endif /* USE_CONIO */
220
221
222 /*
223  * The main screen (currently the only screen)
224  */
225 static term term_screen_body;
226
227
228 /*
229  * Choose between the "complex" and "simple" color methods
230  */
231 static byte use_color_complex = FALSE;
232
233
234 /*
235  * The "complex" color set
236  */
237 static long ibm_color_complex[16];
238
239
240 /*
241  * The "simple" color set
242  *
243  * This table is used by the "color" code to instantiate the "approximate"
244  * Angband colors using the only colors available on crappy monitors.
245  *
246  * The entries below are taken from the "color bits" defined above.
247  *
248  * Note that values from 16 to 255 are extremely ugly.
249  *
250  * The values below came from various sources, if you do not like them,
251  * get a better monitor, or edit "pref-ibm.prf" to use different codes.
252  *
253  * Note that many of the choices below suck, but so do crappy monitors.
254  */
255 static byte ibm_color_simple[16] =
256 {
257         VID_BLACK,                      /* Dark */
258         VID_WHITE,                      /* White */
259         VID_CYAN,                       /* Slate XXX */
260         VID_RED | VID_BRIGHT,   /* Orange XXX */
261         VID_RED,                        /* Red */
262         VID_GREEN,                      /* Green */
263         VID_BLUE,                       /* Blue */
264         VID_YELLOW,                     /* Umber XXX */
265         VID_BLACK | VID_BRIGHT, /* Light Dark */
266         VID_CYAN | VID_BRIGHT,  /* Light Slate XXX */
267         VID_MAGENTA,            /* Violet */
268         VID_YELLOW | VID_BRIGHT,        /* Yellow */
269         VID_MAGENTA | VID_BRIGHT,       /* Light Red XXX */
270         VID_GREEN | VID_BRIGHT, /* Light Green */
271         VID_BLUE | VID_BRIGHT,  /* Light Blue */
272         VID_YELLOW                      /* Light Umber XXX */
273 };
274
275
276
277 /*
278  * Activate the "ibm_color_complex" palette information.
279  *
280  * Code by Mike Marcelais, with help from "The programmer's guide
281  * to the EGA and VGA video cards" [Farraro].
282  *
283  * On VGA cards, colors go through a double-indirection when looking
284  * up the `real' color when in 16 color mode.  The color value in the
285  * attribute is looked up in the EGA color registers.  Then that value
286  * is looked up in the VGA color registers.  Then the color is displayed.
287  * This is done for compatability.  However, the EGA registers are
288  * initialized by default to 0..5, 14, 7, 38..3F and not 0..F which means
289  * that unless these are reset, the VGA setpalette function will not
290  * update the correct palette register!
291  *
292  * DJGPP's GrSetColor() does _not_ set the EGA palette list, only the
293  * VGA color list.
294  *
295  * Note that the "traditional" method, using "int86(0x10)", is very slow
296  * when called in protected mode, so we use a faster method using video
297  * ports instead.
298  *
299  * On Watcom machines, we could simply use the special "_remapallpalette()"
300  * function, which not only sets both palette lists (see below) but also
301  * checks for legality of the monitor mode, but, if we are doing bitmapped
302  * graphics, that function forgets to set the EGA registers for some reason.
303  */
304 static void activate_color_complex(void)
305 {
306         int i;
307         printf("%c%c%c%c",8,8,8,8);
308
309 #if 1
310
311         /* Edit the EGA palette */
312         inportb(0x3da);
313
314         /* Edit the colors */
315         for (i = 0; i < 16; i++)
316         {
317                 /* Set color "i" */
318                 outportb(0x3c0, i);
319
320                 /* To value "i" */
321                 outportb(0x3c0, i);
322         };
323
324         /* Use that EGA palette */
325         outportb(0x3c0, 0x20);
326
327         /* Edit VGA palette, starting at color zero */
328         outportb(0x3c8, 0);
329
330         /* Send the colors */
331         for (i = 0; i < 16; i++)
332         {
333                 /* Send the red, green, blue components */
334                 outportb(0x3c9, ((ibm_color_complex[i]) & 0xFF));
335                 outportb(0x3c9, ((ibm_color_complex[i] >> 8) & 0xFF));
336                 outportb(0x3c9, ((ibm_color_complex[i] >> 16) & 0xFF));
337         }
338
339 #else /* 1 */
340
341         /* Set the colors */
342         for (i = 0; i < 16; i++)
343         {
344                 union REGS r;
345
346                 /* Set EGA color */
347                 r.h.ah = 0x10;
348                 r.h.al = 0x00;
349
350                 /* Set color "i" */
351                 r.h.bl = i;
352
353                 /* To value "i" */
354                 r.h.bh = i;
355
356                 /* Do it */
357                 int86(0x10, &r, &r);
358
359                 /* Set VGA color */
360                 r.h.ah = 0x10;
361                 r.h.al = 0x10;
362
363                 /* Set color "i" */
364                 r.h.bh = 0x00;
365                 r.h.bl = i;
366
367                 /* Use this "green" value */
368                 r.h.ch = ((ibm_color_complex[i] >> 8) & 0xFF);
369
370                 /* Use this "blue" value */
371                 r.h.cl = ((ibm_color_complex[i] >> 16) & 0xFF);
372
373                 /* Use this "red" value */
374                 r.h.dh = ((ibm_color_complex[i]) & 0xFF);
375
376                 /* Do it */
377                 int86(0x10, &r, &r);
378         }
379
380 #endif /* 1 */
381
382 }
383
384
385 /*
386  * Note the use of "(x >> 2)" to convert an 8 bit value to a 6 bit value
387  * without losing much precision.
388  */
389 static int Term_xtra_ibm_react(void)
390 {
391         int i;
392
393         /* Complex method */
394         if (use_color_complex)
395         {
396                 long rv, gv, bv, code;
397
398                 bool change = FALSE;
399
400                 /* Save the default colors */
401                 for (i = 0; i < 16; i++)
402                 {
403                         /* Extract desired values */
404                         rv = angband_color_table[i][1] >> 2;
405                         gv = angband_color_table[i][2] >> 2;
406                         bv = angband_color_table[i][3] >> 2;
407
408                         /* Extract a full color code */
409                         code = ((rv) | (gv << 8) | (bv << 16));
410
411                         /* Activate changes */
412                         if (ibm_color_complex[i] != code)
413                         {
414                                 /* Note the change */
415                                 change = TRUE;
416
417                                 /* Apply the desired color */
418                                 ibm_color_complex[i] = code;
419                         }
420                 }
421
422                 /* Activate the palette if needed */
423                 if (change) activate_color_complex();
424         }
425
426         /* Simple method */
427         else
428         {
429                 /* Save the default colors */
430                 for (i = 0; i < 16; i++)
431                 {
432                         /* Simply accept the desired colors */
433                         ibm_color_simple[i] = angband_color_table[i][0];
434                 }
435         }
436
437         /* Success */
438         return (0);
439 }
440
441
442
443 /*
444  * Hack -- set the cursor "visibility"
445  */
446 static void curs_set(int v)
447 {
448         /* If needed */
449         if (saved_cur_v != v)
450         {
451                 union REGS r;
452
453                 /* Set cursor */
454                 r.h.ah = 1;
455
456                 /* Visible */
457                 if (v)
458                 {
459                         /* Use the saved values */
460                         r.h.ch = saved_cur_high;
461                         r.h.cl = saved_cur_low;
462                 }
463
464                 /* Invisible */
465                 else
466                 {
467                         /* Make it invisible */
468                         r.h.ch = 0x20;
469                         r.h.cl = 0x00;
470                 }
471
472                 /* Make the call */
473                 int86(0x10, &r, &r);
474
475                 /* Save the cursor state */
476                 saved_cur_v = v;
477         }
478 }
479
480
481
482 /*
483  * Process an event (check for a keypress)
484  *
485  * The keypress processing code is often the most system dependant part
486  * of Angband, since sometimes even the choice of compiler is important.
487  *
488  * For the IBM, we divide all keypresses into two catagories, first, the
489  * "normal" keys, including all keys required to play Angband, and second,
490  * the "special" keys, such as keypad keys, function keys, and various keys
491  * used in combination with various modifier keys.
492  *
493  * To simplify this file, we use Angband's "macro processing" ability, in
494  * combination with a specialized "pref-ibm.prf" file, to handle most of the
495  * "special" keys, instead of attempting to fully analyze them here.  This
496  * file only has to determine when a "special" key has been pressed, and
497  * translate it into a simple string which signals the use of a "special"
498  * key, the set of modifiers used, if any, and the hardware scan code of
499  * the actual key which was pressed.  To simplify life for the user, we
500  * treat both "shift" keys as identical modifiers.
501  *
502  * The final encoding is "^_MMMxSS\r", where "MMM" encodes the modifiers
503  * ("C" for control, "S" for shift, "A" for alt, or any ordered combination),
504  * and "SS" encodes the keypress (as the two "digit" hexidecimal encoding of
505  * the scan code of the key that was pressed), and the "^_" and "x" and "\r"
506  * delimit the encoding for recognition by the macro processing code.
507  *
508  * Some important facts about scan codes follow.  All "normal" keys use
509  * scan codes from 1-58.  The "function" keys use 59-68 (and 133-134).
510  * The "keypad" keys use 69-83.  Escape uses 1.  Enter uses 28.  Control
511  * uses 29.  Left Shift uses 42.  Right Shift uses 54.  PrtScrn uses 55.
512  * Alt uses 56.  Space uses 57.  CapsLock uses 58.  NumLock uses 69.
513  * ScrollLock uses 70.  The "keypad" keys which use scan codes 71-83
514  * are ordered KP7,KP8,KP9,KP-,KP4,KP5,KP6,KP+,KP1,KP2,KP3,INS,DEL.
515  *
516  * Using "bioskey(0x10)" instead of "bioskey(0)" apparently provides more
517  * information, including better access to the keypad keys in combination
518  * with various modifiers, but only works on "PC's after 6/1/86", and there
519  * is no way to determine if the function is provided on a machine.  I have
520  * been told that without it you cannot detect, for example, control-left.
521  * The basic scan code + ascii value pairs returned by the keypad follow,
522  * with values in parentheses only available to "bioskey(0x10)".
523  *
524  *         /      *      -      +      1      2      3      4
525  * Norm:  352f   372a   4a2d   4e2b   4f00   5000   5100   4b00
526  * Shft:  352f   372a   4a2d   4e2b   4f31   5032   5133   4b34
527  * Ctrl: (9500) (9600) (8e00) (9000)  7500  (9100)  7600   7300
528  *
529  *         5      6      7      8      9      0      .     Enter
530  * Norm: (4c00)  4d00   4700   4800   4900   5200   5300  (e00d)
531  * Shft:  4c35   4d36   4737   4838   4939   5230   532e  (e00d)
532  * Ctrl: (8f00)  7400   7700  (8d00)  8400  (9200) (9300) (e00a)
533  *
534  * See "pref-ibm.prf" for the "standard" macros for various keys.
535  *
536  * Certain "bizarre" keypad keys (such as "enter") return a "scan code"
537  * of "0xE0", and a "usable" ascii value.  These keys should be treated
538  * like the normal keys, see below.  XXX XXX XXX Note that these "special"
539  * keys could be prefixed with an optional "ctrl-^" which would allow them
540  * to be used in macros without hurting their use in normal situations.
541  */
542 static errr Term_xtra_ibm_event(int v)
543 {
544         int i, k, s;
545
546         bool mc = FALSE;
547         bool ms = FALSE;
548         bool ma = FALSE;
549
550
551         /* Hack -- Check for a keypress */
552         if (!v && !bioskey(1)) return (1);
553
554         /* Wait for a keypress */
555         k = bioskey(0x10);
556
557         /* Access the "modifiers" */
558         i = bioskey(2);
559
560         /* Extract the "scan code" */
561         s = ((k >> 8) & 0xFF);
562
563         /* Extract the "ascii value" */
564         k = (k & 0xFF);
565
566         /* Process "normal" keys */
567         if ((s <= 58) || (s == 0xE0))
568         {
569                 /* Enqueue it */
570                 if (k) Term_keypress(k);
571
572                 /* Success */
573                 return (0);
574         }
575
576         /* Extract the modifier flags */
577         if (i & (1 << K_CTRL)) mc = TRUE;
578         if (i & (1 << K_LSHIFT)) ms = TRUE;
579         if (i & (1 << K_RSHIFT)) ms = TRUE;
580         if (i & (1 << K_ALT)) ma = TRUE;
581
582
583         /* Begin a "macro trigger" */
584         Term_keypress(31);
585
586         /* Hack -- Send the modifiers */
587         if (mc) Term_keypress('C');
588         if (ms) Term_keypress('S');
589         if (ma) Term_keypress('A');
590
591         /* Introduce the hexidecimal scan code */
592         Term_keypress('x');
593
594         /* Encode the hexidecimal scan code */
595         Term_keypress(hexsym[s/16]);
596         Term_keypress(hexsym[s%16]);
597
598         /* End the "macro trigger" */
599         Term_keypress(13);
600
601         /* Success */
602         return (0);
603 }
604
605
606 /*
607  * Handle a "special request"
608  *
609  * The given parameters are "valid".
610  */
611 static errr Term_xtra_ibm(int n, int v)
612 {
613         int i;
614
615         /* Analyze the request */
616         switch (n)
617         {
618                 /* Make a "bell" noise */
619                 case TERM_XTRA_NOISE:
620                 {
621                         /* Make a bell noise */
622                         (void)write(1, "\007", 1);
623
624                         /* Success */
625                         return (0);
626                 }
627
628                 /* Set the cursor shape */
629                 case TERM_XTRA_SHAPE:
630                 {
631                         /* Set cursor shape */
632                         curs_set(v);
633
634                         /* Success */
635                         return (0);
636                 }
637
638                 /* Flush one line of output */
639                 case TERM_XTRA_FROSH:
640                 {
641
642 #ifdef USE_VIRTUAL
643
644 # ifdef USE_WAT
645
646                         /* Copy the virtual screen to the physical screen */
647                         memcpy(PhysicalScreen + (v*160), VirtualScreen + (v*160), 160);
648
649 # else /* USE_WAT */
650
651                         /* Apply the virtual screen to the physical screen */
652                         ScreenUpdateLine(VirtualScreen + ((v*cols) << 1), v);
653
654 # endif /* USE_WAT */
655
656 #endif /* USE_VIRTUAL */
657
658                         /* Success */
659                         return (0);
660                 }
661
662                 /* Clear the screen */
663                 case TERM_XTRA_CLEAR:
664                 {
665
666 #ifdef USE_CONIO
667
668                         /* Clear the screen */
669                         clrscr();
670
671 #else /* USE_CONIO */
672
673                         /* Clear each line (virtual or physical) */
674                         for (i = 0; i < rows; i++)
675                         {
676                                 /* Clear the line */
677                                 memcpy((VirtualScreen + ((i*cols) << 1)), wiper, (cols << 1));
678                         }
679
680 # ifdef USE_VIRTUAL
681
682 #  ifdef USE_WAT
683
684                         /* Copy the virtual screen to the physical screen */
685                         memcpy(PhysicalScreen, VirtualScreen, 25*80*2);
686
687 #  else /* USE_WAT */
688
689                         /* Erase the physical screen */
690                         ScreenClear();
691
692 #  endif /* USE_WAT */
693
694 # endif /* USE_VIRTUAL */
695
696 #endif /* USE_CONIO */
697
698                         /* Success */
699                         return (0);
700                 }
701
702                 /* Process events */
703                 case TERM_XTRA_EVENT:
704                 {
705                         /* Process one event */
706                         return (Term_xtra_ibm_event(v));
707                 }
708
709                 /* Flush events */
710                 case TERM_XTRA_FLUSH:
711                 {
712                         /* Strip events */
713                         while (!Term_xtra_ibm_event(FALSE)) /* loop */;
714
715                         /* Success */
716                         return (0);
717                 }
718
719                 /* React to global changes */
720                 case TERM_XTRA_REACT:
721                 {
722                         /* React to "color_table" changes */
723                         return (Term_xtra_ibm_react());
724                 }
725
726                 /* Delay for some milliseconds */
727                 case TERM_XTRA_DELAY:
728                 {
729                         /* Delay if needed */
730                         if (v > 0) delay(v);
731
732                         /* Success */
733                         return (0);
734                 }
735         }
736
737         /* Unknown request */
738         return (1);
739 }
740
741
742
743 /*
744  * Move the cursor
745  *
746  * The given parameters are "valid".
747  */
748 static errr Term_curs_ibm(int x, int y)
749 {
750
751 #ifdef USE_WAT
752
753 # ifdef USE_CONIO
754
755         /* Place the cursor */
756         gotoxy(x+1, y+1);
757
758 # else /* USE_CONIO */
759
760         union REGS r;
761
762         r.h.ah = 2;
763         r.h.bh = 0;
764         r.h.dl = x;
765         r.h.dh = y;
766
767         /* Place the cursor */
768         int86(0x10, &r, &r);
769
770 # endif /* USE_CONIO */
771
772 #else /* USE_WAT */
773
774         /* Move the cursor */
775         ScreenSetCursor(y, x);
776
777 #endif /* USE_WAT */
778
779         /* Success */
780         return (0);
781 }
782
783
784 /*
785  * Erase a block of the screen
786  *
787  * The given parameters are "valid".
788  */
789 static errr Term_wipe_ibm(int x, int y, int n)
790 {
791
792 #ifdef USE_CONIO
793
794         /* Wipe the region */
795         window(x+1, y+1, x+n, y+1);
796         clrscr();
797         window(1, 1, cols, rows);
798
799 #else /* USE_CONIO */
800
801         /* Wipe part of the virtual (or physical) screen */
802         memcpy(VirtualScreen + ((cols*y + x)<<1), wiper, n<<1);
803
804 #endif /* USE_CONIO */
805
806         /* Success */
807         return (0);
808 }
809
810
811 /*
812  * Place some text on the screen using an attribute
813  *
814  * The given parameters are "valid".  Be careful with "a".
815  *
816  * The string "cp" has length "n" and is NOT null-terminated.
817  */
818 static errr Term_text_ibm(int x, int y, int n, byte a, const char *cp)
819 {
820         register int i;
821         register byte attr;
822         register byte *dest;
823
824
825         /* Handle "complex" color */
826         if (use_color_complex)
827         {
828                 /* Extract a color index */
829                 attr = (a & 0x0F);
830         }
831
832         /* Handle "simple" color */
833         else
834         {
835                 /* Extract a color value */
836                 attr = ibm_color_simple[a & 0x0F];
837         }
838
839 #ifdef USE_CONIO
840
841         /* Place the cursor */
842         gotoxy(x+1, y+1);
843
844         /* Set the attribute */
845         textattr(attr);
846
847 #ifdef JP
848       /* Dump the text */
849       for (i = 0; i < n; i++) {
850               if (iskanji(cp[i])) {
851                       char jbuf[3];
852                       jbuf[0] = cp[i++];
853                       jbuf[1] = cp[i];
854                       jbuf[2] = '\0';
855                       cputs(jbuf);
856               } else {
857                       putch(cp[i]);
858               }
859       }
860 #else
861         /* Dump the text */
862         for (i = 0; i < n; i++) putch(cp[i]);
863 #endif
864
865 #else /* USE_CONIO */
866
867         /* Access the virtual (or physical) screen */
868         dest = VirtualScreen + (((cols * y) + x) << 1);
869
870         /* Save the data */
871         for (i = 0; i < n; i++)
872         {
873                 /* Apply */
874                 *dest++ = cp[i];
875                 *dest++ = attr;
876         }
877
878 #endif /* USE_CONIO */
879
880         /* Success */
881         return (0);
882 }
883
884
885 /*
886  * Place some attr/char pairs on the screen
887  *
888  * The given parameters are "valid".
889  */
890 #ifdef USE_TRANSPARENCY
891 static errr Term_pict_ibm(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp)
892 #else /* USE_TRANSPARENCY */
893 static errr Term_pict_ibm(int x, int y, int n, const byte *ap, const char *cp)
894 #endif /* USE_TRANSPARENCY */
895 {
896         register int i;
897         register byte attr;
898         register byte *dest;
899
900
901 #ifdef USE_CONIO
902
903         /* Place the cursor */
904         gotoxy(x+1, y+1);
905
906         /* Dump the text */
907         for (i = 0; i < n; i++)
908         {
909                 /* Handle "complex" color */
910                 if (use_color_complex)
911                 {
912                         /* Extract a color index */
913                         attr = (ap[i] & 0x0F);
914                 }
915
916                 /* Handle "simple" color */
917                 else
918                 {
919                         /* Extract a color value */
920                         attr = ibm_color_simple[ap[i] & 0x0F];
921                 }
922
923                 /* Set the attribute */
924                 textattr(attr);
925
926                 /* Dump the char */
927                 putch(cp[i]);
928         }
929
930 #else /* USE_CONIO */
931
932         /* Access the virtual (or physical) screen */
933         dest = VirtualScreen + (((cols * y) + x) << 1);
934
935         /* Save the data */
936         for (i = 0; i < n; i++)
937         {
938                 /* Handle "complex" color */
939                 if (use_color_complex)
940                 {
941                         /* Extract a color index */
942                         attr = (ap[i] & 0x0F);
943                 }
944
945                 /* Handle "simple" color */
946                 else
947                 {
948                         /* Extract a color value */
949                         attr = ibm_color_simple[ap[i] & 0x0F];
950                 }
951
952                 /* Apply */
953                 *dest++ = cp[i];
954                 *dest++ = attr;
955         }
956
957 #endif /* USE_CONIO */
958
959         /* Success */
960         return (0);
961 }
962
963
964 /*
965  * Init a Term
966  */
967 static void Term_init_ibm(term *t)
968 {
969         /* XXX Nothing */
970 }
971
972
973 /*
974  * Nuke a Term
975  */
976 static void Term_nuke_ibm(term *t)
977 {
978
979 #ifdef USE_WAT
980
981         /* Nothing */
982
983 #else /* USE_WAT */
984
985         union REGS r;
986
987 #endif /* USE_WAT */
988
989         /* Move the cursor to the bottom of the screen */
990         Term_curs_ibm(0, rows-1);
991
992 #ifdef USE_WAT
993
994         /* Restore the original video mode */
995         _setvideomode(_DEFAULTMODE);
996
997 #else /* USE_WAT */
998
999         /* Restore the original video mode */
1000         r.h.ah = 0x00;
1001         r.h.al = 0x03;
1002         int86(0x10, &r, &r);
1003
1004 #endif /* USE_WAT */
1005
1006         /* Make the cursor visible */
1007         curs_set(1);
1008 }
1009
1010
1011
1012 #ifdef USE_GRAPHICS
1013
1014 #ifdef USE_286
1015
1016 /*
1017  * In 286 mode we don't need to worry about translating from a 32bit
1018  * pointer to a 16 bit pointer so we just call the interrupt function
1019  *
1020  * Note the use of "intr()" instead of "int86()" so we can pass
1021  * segment registers.
1022  */
1023 void enable_graphic_font(void *font)
1024 {
1025         union REGPACK regs =
1026         {0};
1027
1028         regs.h.ah = 0x11;           /* Text font function */
1029         regs.h.bh = 0x10;           /* Size of a character -- 16 bytes */
1030         regs.h.cl = 0xFF;           /* Last character in font */
1031         regs.x.es = FP_SEG(font);   /* Pointer to font */
1032         regs.x.bp = FP_OFF(font);
1033         intr(0x10, &regs);
1034 };
1035
1036 #else /* USE_286 */
1037
1038 #ifdef USE_WAT
1039
1040 /*
1041  * This structure is used by the DMPI function to hold registers when
1042  * doing a real mode interrupt call.  (Stolen from the DJGPP <dpmi.h>
1043  * header file).
1044  */
1045
1046 typedef union
1047 {
1048         struct
1049         {
1050                 unsigned long edi;
1051                 unsigned long esi;
1052                 unsigned long ebp;
1053                 unsigned long res;
1054                 unsigned long ebx;
1055                 unsigned long edx;
1056                 unsigned long ecx;
1057                 unsigned long eax;
1058         } d;
1059         struct
1060         {
1061                 unsigned short di, di_hi;
1062                 unsigned short si, si_hi;
1063                 unsigned short bp, bp_hi;
1064                 unsigned short res, res_hi;
1065                 unsigned short bx, bx_hi;
1066                 unsigned short dx, dx_hi;
1067                 unsigned short cx, cx_hi;
1068                 unsigned short ax, ax_hi;
1069                 unsigned short flags;
1070                 unsigned short es;
1071                 unsigned short ds;
1072                 unsigned short fs;
1073                 unsigned short gs;
1074                 unsigned short ip;
1075                 unsigned short cs;
1076                 unsigned short sp;
1077                 unsigned short ss;
1078         } x;
1079         struct
1080         {
1081                 unsigned char edi[4];
1082                 unsigned char esi[4];
1083                 unsigned char ebp[4];
1084                 unsigned char res[4];
1085                 unsigned char bl, bh, ebx_b2, ebx_b3;
1086                 unsigned char dl, dh, edx_b2, edx_b3;
1087                 unsigned char cl, ch, ecx_b2, ecx_b3;
1088                 unsigned char al, ah, eax_b2, eax_b3;
1089         } h;
1090 } __dpmi_regs;
1091
1092 unsigned  __dpmi_allocate_dos_memory(int size, unsigned *selector)
1093 {
1094         union REGPACK regs =
1095         {0};
1096
1097         regs.w.ax  = 0x100;   /* DPMI function -- allocate low memory */
1098         regs.w.bx  = size;    /* Number of Paragraphs to allocate */
1099         intr(0x31, &regs);    /* DPMI interface */
1100
1101         *selector = regs.w.dx;
1102         return (regs.w.ax);
1103 };
1104
1105 void __dpmi_free_dos_memory(unsigned sel)
1106 {
1107         union REGPACK regs =
1108         {0};
1109
1110         regs.w.ax  = 0x101;      /* DPMI function -- free low memory */
1111         regs.x.edx = sel;        /* PM selector for memory block */
1112         intr(0x31, &regs);       /* DPMI interface */
1113 };
1114
1115 void __dpmi_int(int intno, __dpmi_regs *dblock)
1116 {
1117         union REGPACK regs =
1118         {0};
1119
1120         regs.w.ax  = 0x300;           /* DPMI function -- real mode interrupt */
1121         regs.h.bl  = intno;           /* interrupt 0x10 */
1122         regs.x.edi = FP_OFF(dblock);  /* Pointer to dblock (offset and segment) */
1123         regs.x.es  = FP_SEG(dblock);
1124         intr(0x31, &regs);            /* DPMI interface */
1125 };
1126
1127 unsigned short __dpmi_sel = 0x0000;
1128 #define _farsetsel(x) __dpmi_sel=(x)
1129 extern void _farnspokeb(unsigned long offset, unsigned char value);
1130 #pragma aux _farnspokeb =        \
1131           "push   fs"            \
1132           "mov    fs,__dpmi_sel" \
1133           "mov    fs:[eax],bl"   \
1134           "pop    fs"            \
1135           parm [eax] [bl];
1136
1137 #else /* USE_WAT */
1138
1139 #include <dpmi.h>
1140 #include <go32.h>
1141 #include <sys/farptr.h>
1142
1143 #endif /* USE_WAT */
1144
1145
1146 /*
1147  * Since you cannot send 32bit pointers to a 16bit interrupt handler
1148  * and the video BIOS wants a (16bit) pointer to the font, we have
1149  * to allocate a block of dos memory, copy the font into it, then
1150  * translate a 32bit pointer into a 16bit pointer to that block.
1151  *
1152  * DPMI - Dos Protected Mode Interface provides functions that let
1153  *        us do that.
1154  */
1155 void enable_graphic_font(const char *font)
1156 {
1157         __dpmi_regs dblock = {{0}};
1158
1159         unsigned int seg, i;
1160         int sel;
1161
1162         /*
1163          * Allocate a block of memory 4096 bytes big in `low memory' so a real
1164          * mode interrupt can access it.  Real mode pointer is returned as seg:0
1165          * Protected mode pointer is sel:0.
1166          */
1167         seg = __dpmi_allocate_dos_memory(256, &sel);
1168
1169         /* Copy the information into low memory buffer, by copying one byte at
1170          * a time.  According to the info in <sys/farptr.h>, the functions
1171          * _farsetsel() and _farnspokeb() will optimise away completely
1172          */
1173         _farsetsel(sel);               /* Set the selector to write to */
1174         for (i = 0; i<4096; i++)
1175         {
1176                 _farnspokeb(i, *font++);      /* Copy 1 byte into low (far) memory */
1177         }
1178
1179         /*
1180          * Now we use DPMI as a jumper to call the real mode interrupt.  This
1181          * is needed because loading `es' while in protected mode with a real
1182          * mode pointer will cause an Protection Fault and calling the interrupt
1183          * directly using the protected mode pointer will result in garbage
1184          * being received by the interrupt routine
1185          */
1186         dblock.d.eax = 0x1100;         /* BIOS function -- set font */
1187         dblock.d.ebx = 0x1000;         /* bh = size of a letter; bl = 0 (reserved) */
1188         dblock.d.ecx = 0x00FF;         /* Last character in font */
1189         dblock.x.es  = seg;            /* Pointer to font segment */
1190         dblock.d.ebp = 0x0000;         /* Pointer to font offset */
1191
1192         __dpmi_int(0x10, &dblock);
1193
1194         /* We're done with the low memory, free it */
1195         __dpmi_free_dos_memory(sel);
1196 }
1197
1198 #endif /* USE_286 */
1199
1200 #endif /* ALLOW_GRAPH */
1201
1202
1203
1204 /*
1205  * Initialize the IBM "visual module"
1206  *
1207  * Hack -- we assume that "blank space" should be "white space"
1208  * (and not "black space" which might make more sense).
1209  *
1210  * Note the use of "((x << 2) | (x >> 4))" to "expand" a 6 bit value
1211  * into an 8 bit value, without losing much precision, by using the 2
1212  * most significant bits as the least significant bits in the new value.
1213  */
1214 errr init_ibm(void)
1215 {
1216         int i;
1217         int mode;
1218
1219         term *t = &term_screen_body;
1220
1221         union REGS r;
1222
1223         /* Check for "Windows" */
1224         if (getenv("windir"))
1225         {
1226                 r.h.ah = 0x16;           /* Windows API Call -- Set device focus */
1227                 r.h.al = 0x8B;           /* Causes Dos boxes to become fullscreen */
1228                 r.h.bh = r.h.bl = 0x00;  /* 0x0000 = current Dos box */
1229                 int86(0x2F, &r, &r);       /* Call the Windows API */
1230         };
1231
1232         /* Initialize "color_table" */
1233         for (i = 0; i < 16; i++)
1234         {
1235                 long rv, gv, bv;
1236
1237                 /* Extract desired values */
1238                 rv = angband_color_table[i][1] >> 2;
1239                 gv = angband_color_table[i][2] >> 2;
1240                 bv = angband_color_table[i][3] >> 2;
1241
1242                 /* Extract the "complex" codes */
1243                 ibm_color_complex[i] = ((rv) | (gv << 8) | (bv << 16));
1244
1245                 /* Save the "simple" codes */
1246                 angband_color_table[i][0] = ibm_color_simple[i];
1247         }
1248
1249 #ifdef USE_WAT
1250
1251         /* Set the video mode */
1252         if (_setvideomode(_VRES16COLOR))
1253         {
1254                 mode = 0x13;
1255         }
1256
1257         /* Wimpy monitor */
1258         else
1259         {
1260                 mode = 0x03;
1261         }
1262
1263         /* Force 25 line mode */
1264         _setvideomode(_TEXTC80);
1265         _settextrows(25);
1266
1267 #else /* USE_WAT */
1268
1269         /* Set video mode */
1270         r.h.ah = 0x00;
1271         r.h.al = 0x13; /* VGA only mode */
1272         int86(0x10, &r, &r);
1273
1274         /* Get video mode */
1275         r.h.ah = 0x0F;
1276         int86(0x10, &r, &r);
1277         mode = r.h.al;
1278
1279         /* Set video mode */
1280         r.h.ah = 0x00;
1281         r.h.al = 0x03; /* Color text mode */
1282         int86(0x10, &r, &r);
1283
1284 #endif /* USE_WAT */
1285
1286         /* Check video mode */
1287         if (mode == 0x13)
1288         {
1289                 /* Remember the mode */
1290                 use_color_complex = TRUE;
1291
1292                 /* Instantiate the color set */
1293                 activate_color_complex();
1294         }
1295
1296 #ifdef USE_GRAPHICS
1297
1298         /* Try to activate bitmap graphics */
1299         if (arg_graphics && use_color_complex)
1300         {
1301                 FILE *f;
1302
1303                 char buf[4096];
1304
1305                 /* Build the filename */
1306                 path_build(buf, 1024, ANGBAND_DIR_XTRA, "angband.fnt");
1307
1308                 /* Open the file */
1309                 f = fopen(buf, "rb");
1310
1311                 /* Okay */
1312                 if (f)
1313                 {
1314                         /* Load the bitmap data */
1315                         if (fread(buf, 1, 4096, f) != 4096)
1316                         {
1317                                 quit("Corrupt 'angband.fnt' file");
1318                         }
1319
1320                         /* Close the file */
1321                         fclose(f);
1322
1323                         /* Enable graphics */
1324                         enable_graphic_font(buf);
1325
1326                         /* Enable colors (again) */
1327                         activate_color_complex();
1328
1329                         /* Use graphics */
1330                         use_graphics = TRUE;
1331                 }
1332         }
1333
1334 #endif
1335
1336 #ifdef USE_CONIO
1337 #else /* USE_CONIO */
1338
1339         /* Build a "wiper line" */
1340         for (i = 0; i < 80; i++)
1341         {
1342                 /* Space */
1343                 wiper[2*i] = ' ';
1344
1345                 /* Black */
1346                 wiper[2*i+1] = TERM_WHITE;
1347         }
1348
1349 #endif /* USE_CONIO */
1350
1351
1352 #ifdef USE_VIRTUAL
1353
1354         /* Make the virtual screen */
1355         C_MAKE(VirtualScreen, rows * cols * 2, byte);
1356
1357 #endif /* USE_VIRTUAL */
1358
1359
1360         /* Erase the screen */
1361         Term_xtra_ibm(TERM_XTRA_CLEAR, 0);
1362
1363
1364         /* Place the cursor */
1365         Term_curs_ibm(0, 0);
1366
1367
1368         /* Access the "default" cursor info */
1369         r.h.ah = 3;
1370         r.h.bh = 0;
1371
1372         /* Make the call */
1373         int86(0x10, &r, &r);
1374
1375         /* Extract the standard cursor info */
1376         saved_cur_v = 1;
1377         saved_cur_high = r.h.ch;
1378         saved_cur_low = r.h.cl;
1379
1380
1381         /* Initialize the term */
1382         term_init(t, 80, 24, 256);
1383
1384 #ifdef USE_CONIO
1385 #else /* USE_CONIO */
1386
1387         /* Always use "Term_pict()" */
1388         t->always_pict = TRUE;
1389
1390 #endif /* USE_CONIO */
1391
1392         /* Use "white space" to erase */
1393         t->attr_blank = TERM_WHITE;
1394         t->char_blank = ' ';
1395
1396         /* Prepare the init/nuke hooks */
1397         t->init_hook = Term_init_ibm;
1398         t->nuke_hook = Term_nuke_ibm;
1399
1400         /* Connect the hooks */
1401         t->xtra_hook = Term_xtra_ibm;
1402         t->curs_hook = Term_curs_ibm;
1403         t->wipe_hook = Term_wipe_ibm;
1404         t->text_hook = Term_text_ibm;
1405         t->pict_hook = Term_pict_ibm;
1406
1407         /* Save it */
1408         term_screen = t;
1409
1410         /* Activate it */
1411         Term_activate(term_screen);
1412
1413         /* Success */
1414         return 0;
1415 }
1416
1417
1418 #endif /* USE_IBM */
1419