File: ibmpc.c

package info (click to toggle)
vile 9.2y-1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 5,700 kB
  • ctags: 7,341
  • sloc: ansic: 78,427; lex: 4,726; perl: 2,922; cpp: 2,710; sh: 2,612; makefile: 715; awk: 212
file content (1440 lines) | stat: -rw-r--r-- 35,245 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
/*
 * The routines in this file provide support for the IBM-PC family
 * of machines, and their typical display cards:  mono, cga, ega, vga.
 * It goes directly to the graphics RAM to do screen output.
 *
 * Modified by Pete Ruczynski (pjr) for auto-sensing and selection of
 * display type.
 *
 * $Header: /usr/build/vile/vile/RCS/ibmpc.c,v 1.96 2001/02/13 23:48:06 tom Exp $
 *
 */

#define	termdef	1			/* don't define "term" external */

#include	"estruct.h"
#include	"edef.h"

#if DISP_BORLAND || !DISP_IBMPC
#error misconfigured:  DISP_IBMPC should be defined if using ibmpc.c
#error (and DISP_BORLAND should not be defined)
#endif

#if CC_DJGPP
#include <pc.h>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define outp(p,v) outportb(p,v)
#define inp(p) inportb(p)
#define far
#include <io.h> /* for setmode() */
#include <fcntl.h> /* for O_BINARY */
#include <dpmi.h> /* for the register struct */
#include <go32.h>
#endif

#define NROW	50			/* Max Screen size.		*/
#define NCOL	80			/* Edit if you want to. 	*/
#define	NPAUSE	200			/* # times thru update to pause */
#define	SPACE	32			/* space character		*/

#if CC_WATCOM
#define	MONOADDR	(0xb000 << 4)	/* MONO screen memory */
#define	OTHERADDR	(0xb800 << 4)	/* CGA,EGA,VGA screen memory */
#endif

#if CC_DJGPP
#define FAR_POINTER(s,o) (0xe0000000 + s*16 + o)
#define FP_SEG(a)	((ULONG)(a) >> 4L)
#define FP_OFF(a)	((ULONG)(a) & 0x0fL)
#define	MONOADDR	0xb000		/* MONO screen memory */
#define	OTHERADDR	0xb800		/* CGA,EGA,VGA screen memory */
#endif

#ifndef OTHERADDR
#define	MONOADDR	0xb0000000L	/* MONO screen memory */
#define	OTHERADDR	0xb8000000L	/* CGA,EGA,VGA screen memory */
#endif

#ifndef FAR_POINTER
#define FAR_POINTER(s,o) (s)
#endif

#define	MONOSCR	0
#define	CGASCR	1
#define	EGASCR	2
#define	VGASCR	3

static	const	long	ScreenAddress[] = {
		MONOADDR,	/* MONOSCR: Monochrome adapter */
		OTHERADDR,	/* CGASCR: Color graphics adapter */
		OTHERADDR,	/* EGASCR: Enhanced graphics adapter */
		OTHERADDR	/* VGASCR: VGA adapter */
	};


#if CC_WATCOM
#ifdef __386__
#define	INTX86(a,b,c)		int386(a, b, c)
#define	INTX86X(a,b,c,d)	int386x(a, b, c, d)
#else
#define	INTX86(a,b,c)		int86(a, b, c)
#define	INTX86X(a,b,c,d)	int86x(a, b, c, d)
#endif
#define	_AX_		eax
#define	_BX_		ebx
#define	_CX_		ecx
#define	_DX_		edx
#define	_DI_		edi
#else
#define	INTX86(a,b,c)	int86(a, b, c)
#define	INTX86X(a,b,c,d)	int86x(a, b, c, d)
#define	_AX_		ax
#define	_BX_		bx
#define	_CX_		cx
#define	_DX_		dx
#define	_DI_		di
#endif

#define	ColorDisplay()	(dtype != MONOSCR && !monochrome)
#define	AttrColor(b,f)	(((ctrans[b] & 7) << 4) | (ctrans[f] & 15))
#define	Black(n)	((n) ? 0 : 7)
#define	White(n)	((n) ? 7 : 0)
#define	AttrMono(f)	(((Black(f) & 7) << 4) | (White(f) & 15))

#if OPT_MS_MOUSE
	static	int	ms_crsr2inx  ( int, int );
	static	void	ms_deinstall (void);
	static	void	ms_hidecrsr  (void);
	static	void	ms_install   (void);
	static	void	ms_setvrange ( int, int );
	static	void	ms_showcrsr  (void);
#else
# define ms_deinstall()
# define ms_install()
#endif

static	int	dtype = -1;	/* current display type		*/
#if CC_DJGPP
#define PACKED __attribute__ ((packed))
#else
#define PACKED
#endif

	/* mode-independent VGA-BIOS status information */
typedef	struct {
	UCHAR	video_modes[3] PACKED;	/* 00 Supported video-modes */
	UCHAR	reserved[4] PACKED;	/* 03 */
	UCHAR	text_scanlines PACKED;	/* 07 Number of pixel rows in text mode */
	UCHAR	num_charsets PACKED;	/* 08 Number of character sets that can be displayed */
	UCHAR	num_loadable PACKED;	/* 09 Number of character sets loadable into video RAM */
	UCHAR	capability PACKED;	/* 0A VGA-BIOS capability info */
	UCHAR	more_info PACKED;	/* 0B More VGA-BIOS capability info */
	UCHAR	reserved2[4] PACKED;	/* 0C */
	} static_VGA_info PACKED;

	/* mode-dependent VGA-BIOS status information */
typedef	struct {
	static_VGA_info *static_info PACKED; /* 00 Address of table containing static info */
	UCHAR	code_number PACKED;	/* 04 Code number of current video mode */
	USHORT	num_columns PACKED;	/* 05 Number of displayed screen or pixel cols */
	USHORT	page_length PACKED;	/* 07 Length of display page in video RAM */
	USHORT	curr_page PACKED;	/* 09 Starting address of current display-page in video RAM */
	UCHAR	crsr_pos[8][2] PACKED;	/* 0B Cursor positions in display-pages in col/row order */
	UCHAR	crsr_end PACKED;	/* 1B Ending row of cursor (pixel) row */
	UCHAR	crsr_start PACKED;	/* 1C Starting row of cursor (pixel) row */
	UCHAR	curr_page_num PACKED;	/* 1D Number of current display page */
	USHORT	port_crt PACKED;	/* 1E Port address of the CRT controller address register */
	UCHAR	curr_crtc PACKED;	/* 20 Current contents of CRTC control registers */
	UCHAR	curr_color_sel PACKED;	/* 21 Current color selection register contents */
	UCHAR	num_rows PACKED;	/* 22 Number of screen rows displayed */
	USHORT	char_height PACKED;	/* 23 Height of characters in pixel rows */
	UCHAR	code_active PACKED;	/* 25 Code number of active video adapter */
	UCHAR	code_inactive PACKED;	/* 26 Code number of inactive video adapter */
	USHORT	num_colors PACKED;	/* 27 Number of displayable colors (0=monochrome) */
	UCHAR	num_pages PACKED;	/* 29 Number of screen pages */
	UCHAR	num_pixel_rows PACKED;	/* 2A Number of displayed pixel rows (RES_200, etc.) */
	UCHAR	num_cset0 PACKED;	/* 2B Number of char-table used with chars whose 3rd attr bit is 0 */
	UCHAR	num_cset1 PACKED;	/* 2C Number of char-table used with chars whose 3rd attr bit is 1 */
	UCHAR	misc_info PACKED;	/* 2D Miscellaneous information */
	UCHAR	reserved[3] PACKED;	/* 2E */
	UCHAR	size_of_ram PACKED;	/* 31 Size of available video RAM (0=64k, 1=128k, 2=192k, 3=256k) */
	UCHAR	reserved2[14] PACKED;	/* 32 */
	} dynamic_VGA_info PACKED;	/* length == 64 bytes */

	/* scan-line resolution codes */
#define	RES_200	0
#define	RES_350	1
#define	RES_400	2
#define RES_480 3

	/* character-size codes */
#define	C8x8	0x12
#define	C8x14	0x11
#define	C8x16	0x14

	/* character-size in pixels, for mouse-positioning */
static	int	chr_wide = 8;
static	int	chr_high = 8;

typedef	struct	{
	char	*name;
	UCHAR	type;
	UCHAR	mode;
	UCHAR	vchr;	/* code for setting character-height */
	UCHAR	rows;
	UCHAR	cols;
	UCHAR	vres;	/* required scan-lines, RES_200, ... */
	} DRIVERS;

#define ORIGTYPE  0	/* store original info in this slot.
			   (these values should all (?) get replaced
			   at open time) */

/* order this table so that more higher resolution entries for the same
 *  name come first.  remember -- synonyms take only 10 bytes, plus the
 *  name itself.
 */
static	DRIVERS drivers[] = {
		{"default",ORIGTYPE,    3,      C8x8,   25,  80, RES_200},
		{"2",      VGASCR,	3,	C8x16,	25,  80, RES_400},
		{"25",     VGASCR,	3,	C8x16,	25,  80, RES_400},
		{"2",      CGASCR,	3,	C8x8,	25,  80, RES_200},
		{"25",     CGASCR,	3,	C8x8,	25,  80, RES_200},
		{"CGA",    CGASCR,	3,	C8x8,	25,  80, RES_200},
		{"MONO",   MONOSCR,	3,	C8x8,	25,  80, RES_200},
		{"80x25",  VGASCR,	3,	C8x16,	25,  80, RES_400},
		{"80x28",  VGASCR,	3,	C8x14,  28,  80, RES_400},
		{"EGA",    EGASCR,	3,	C8x8,	43,  80, RES_350},
		{"4",      EGASCR,	3,	C8x8,	43,  80, RES_350},
		{"43",     EGASCR,	3,	C8x8,	43,  80, RES_350},
		{"80x43",  VGASCR,	3,	C8x8,   43,  80, RES_350},
		{"5",      VGASCR,	3,	C8x8,	50,  80, RES_400},
		{"50",     VGASCR,	3,	C8x8,	50,  80, RES_400},
		{"VGA",    VGASCR,	3,	C8x8,	50,  80, RES_400},
		{"80x50",  VGASCR,	3,	C8x8,	50,  80, RES_400},
		{"80x14",  VGASCR,	3,	C8x14,  14,  80, RES_200},
		{"40x12",  VGASCR,	1,	C8x16,	12,  40, RES_200},
		{"40x21",  VGASCR,	1,	C8x16,	21,  40, RES_350},
		{"40x25",  VGASCR,	1,	C8x16,	25,  40, RES_400},
		{"40x28",  VGASCR,	1,	C8x14,  28,  40, RES_400},
		{"40x50",  VGASCR,	1,	C8x8,   50,  40, RES_400},

	};

/*  the following should be sized dynamically, but it may not be worth it,
	if we come up in the biggest size anyway, since we never shrink
	but only grow */
static USHORT *screen_lines[NROW];	/* pointer to screen lines	*/
static USHORT *screen2_lines[NROW];	/* pointer to page-1 lines	*/


static	int	ibm_opened,
		original_page,	/* display-page (we use 0)	*/
		allowed_vres,	/* possible scan-lines, 1 bit per value */
		original_curs,	/* start/stop scan lines	*/
		monochrome	= FALSE;

extern  void	ibmmove   (int,int);
extern  void	ibmeeol   (void);
extern  void	ibmeeop   (void);
extern  void	ibmbeep   (void);
extern  void    ibmopen   (void);
extern	void	ibmrev    (UINT);
extern	int	ibmcres   (const char *);
extern	void	ibmclose  (void);
extern	void	ibmputc   (int);
extern	void	ibmkopen  (void);
extern	void	ibmkclose (void);

#if	OPT_COLOR
extern	void	ibmfcol   (int);
extern	void	ibmbcol   (int);

int	cfcolor = -1;		/* current forground color */
int	cbcolor = -1;		/* current background color */
static	const char *initpalettestr = "0 4 2 6 1 5 3 7 8 12 10 14 9 13 11 15";
/* black, red, green, yellow, blue, magenta, cyan, white   */
#endif
extern	void	ibmscroll (int,int,int);

static	int	screen_init(int);
static	int	identify_adapter(void);
static	int	blank_attr(void);

static	const struct {
	char  *seq;
	int   code;
} keyseqs[] = {	/* use 'Z' in place of leading 0, so these can be C-strings */
	/* Arrow keys */
	{"Z\110",     KEY_Up},
	{"Z\120",     KEY_Down},
	{"Z\115",     KEY_Right},
	{"Z\113",     KEY_Left},
	/* page scroll */
	{"Z\121",     KEY_Next},
	{"Z\111",     KEY_Prior},
	{"Z\107",     KEY_Home},
	{"Z\117",     KEY_End},
	/* editing */
	{"ZR",	      KEY_Insert},
	{"Z\123",     KEY_Delete},
	/* function keys */
	{"Z;",	    KEY_F1},
	{"Z<",      KEY_F2},
	{"Z=",      KEY_F3},
	{"Z>",      KEY_F4},
	{"Z?",      KEY_F5},
	{"Z@",      KEY_F6},
	{"ZA",      KEY_F7},
	{"ZB",      KEY_F8},
	{"ZC",      KEY_F9},
	{"ZD",	    KEY_F10},
};

static int current_ibmtype;

/*
 * Standard terminal interface dispatch table. Most of the fields point into
 * "termio" code.
 */
TERM    term    = {
	NROW,
	NROW,
	NCOL,
	NCOL,
	NPAUSE,
	ibmopen,
	ibmclose,
	ibmkopen,
	ibmkclose,
	ttgetc,
	ibmputc,
	tttypahead,
	ttflush,
	ibmmove,
	ibmeeol,
	ibmeeop,
	ibmbeep,
	ibmrev,
	ibmcres,
#if	OPT_COLOR
	ibmfcol,
	ibmbcol,
	set_ctrans,
#else
	nullterm_setfore,
	nullterm_setback,
	nullterm_setpal,
#endif
	nullterm_setccol,
	ibmscroll,
	nullterm_pflush,
	nullterm_icursor,
	nullterm_settitle,
	nullterm_watchfd,
	nullterm_unwatchfd,
	nullterm_cursorvis,
};

#if CC_DJGPP
_go32_dpmi_seginfo vgainfo;
#endif

static int
get_vga_bios_info(dynamic_VGA_info *buffer)
{
# if CC_DJGPP
	_go32_dpmi_registers regs;
	vgainfo.size = (sizeof (dynamic_VGA_info)+15) / 16;
	if (_go32_dpmi_allocate_dos_memory(&vgainfo) != 0) {
		fprintf(stderr,"Couldn't allocate vgainfo memory\n");
		exit(BADEXIT);
	}

	regs.x.ax = 0x1b00;
	regs.x.bx = 0;
	regs.x.ss = regs.x.sp = 0;
	regs.x.es = vgainfo.rm_segment;
	regs.x.di = 0;
	_go32_dpmi_simulate_int(0x10, &regs);

	dosmemget( vgainfo.rm_segment*16, sizeof (dynamic_VGA_info), buffer);

	_go32_dpmi_free_dos_memory(&vgainfo);

	return (regs.h.al == 0x1b);
#else
	struct SREGS segs;
	union REGS regs;

#if CC_WATCOM
	segread(&segs);
#else
	segs.es   = FP_SEG(buffer);
#endif
	regs.x._DI_ = FP_OFF(buffer);
	regs.x._AX_ = 0x1b00;
	regs.x._BX_ = 0;
	INTX86X(0x10, &regs, &regs, &segs); /* Get VGA-BIOS status */

	return (regs.h.al == 0x1b);
#endif
}

static void
set_display (int mode)
{
	union REGS regs;
	regs.h.ah = 0;
	regs.h.al = mode;
	INTX86(0x10, &regs, &regs);
}

static void
set_page (int page)
{
	union REGS regs;
	regs.h.ah = 5;
	regs.h.al = page;
	INTX86(0x10, &regs, &regs);
}

#ifdef MUCK_WITH_KBD_RATE
/* this code turned off because there's currently no facility
 * to reset to the original rate when we quit.  i _hate_ programs
 * that don't put the world back they way they found it.
 */
/*  set the keyboard rate to max */
static void
maxkbdrate (void)
{
	union REGS regs;
	regs.h.ah = 0x3;
	regs.h.al = 0x5;
	regs.h.bh = 0x0;
	regs.h.bl = 0x0;
	INTX86(0x16, &regs, &regs);
}
#endif

static void
set_char_size(int code)
{
	union REGS regs;
	switch (code) {
	case C8x8:	chr_wide = 8;	chr_high = 8;	break;
	case C8x14:	chr_wide = 8;	chr_high = 14;	break;
	case C8x16:	chr_wide = 8;	chr_high = 16;	break;
	default:	return;		/* cannot set this one! */
	}
	regs.h.ah = 0x11;		/* set char. generator function code	*/
	regs.h.al = code;		/*  to specified ROM			*/
	regs.h.bl = 0;		/* Character table 0			*/
	INTX86(0x10, &regs, &regs);	/* VIDEO - TEXT-MODE CHARACTER GENERATOR FUNCTIONS */
}

static void
set_cursor(int start_stop)
{
	union REGS regs;
	regs.h.ah = 1;		/* set cursor size function code */
	regs.x._CX_ = (drivers[ORIGTYPE].mode <= 3) ?
		start_stop & 0x707 : start_stop;
	INTX86(0x10, &regs, &regs);	/* VIDEO - SET TEXT-MODE CURSOR SHAPE */
}

static int
get_cursor(void)
{
	union REGS regs;
	regs.h.ah = 3;
	regs.h.bh = 0;
	INTX86(0x10, &regs, &regs);	/* VIDEO - GET CURSOR POSITION */
	return regs.x._CX_;
}

static void
set_vertical_resolution(int code)
{
	union REGS regs;
	regs.h.ah = 0x12;
	regs.h.al = code;
	regs.h.bl = 0x30;
	INTX86(0x10, &regs, &regs);	/* VIDEO - SELECT VERTICAL RESOLUTION */
	if (code == RES_200)
		delay(50);	/* patch: timing problem? */
}

/*--------------------------------------------------------------------------*/

#if	OPT_COLOR
void
ibmfcol(		/* set the current output color */
int color)		/* color to set */
{
	if (color < 0)
		color = C_WHITE;
	cfcolor = ctrans[color];
}

void
ibmbcol(		/* set the current background color */
int color)		/* color to set */
{
	if (color < 0)
		color = C_BLACK;
	cbcolor = ctrans[color];
}
#endif

void
ibmmove(int row, int col)
{
	union REGS regs;
	regs.h.ah = 2;		/* set cursor position function code */
	regs.h.dl = col;
	regs.h.dh = row;
	regs.h.bh = 0;		/* set screen page number */
	INTX86(0x10, &regs, &regs);
}

/* erase to the end of the line */
void
ibmeeol(void)
{
	int ccol,crow;	/* current column,row for cursor */
	union REGS regs;

	/* find the current cursor position */
	regs.h.ah = 3;		/* read cursor position function code */
	regs.h.bh = 0;		/* current video page */
	INTX86(0x10, &regs, &regs);

	ccol = regs.h.dl;		/* record current column */
	crow = regs.h.dh;		/* and row */

	scwrite(crow, ccol, term.cols-ccol, NULL, NULL, gfcolor, gbcolor);

}

/* put a character at the current position in the current colors */
void
ibmputc(int ch)
{
	union REGS regs;
	regs.h.ah = 14;		/* write char to screen with current attrs */
	regs.h.al = ch;
#if	OPT_COLOR
	regs.h.bl = ColorDisplay() ? cfcolor : White(TRUE);
#else
	regs.h.bl = White(TRUE);
#endif
	regs.h.bh = 0;		/* current video page */
	INTX86(0x10, &regs, &regs);
}

void
ibmeeop(void)
{
	union REGS regs;
	regs.h.ah = 6;		/* scroll page up function code */
	regs.h.al = 0;		/* # lines to scroll (clear it) */
	regs.x._CX_ = 0;		/* upper left corner of scroll */
	regs.h.dh = term.rows - 1; /* lower right corner of scroll */
	regs.h.dl = term.cols - 1;
	regs.h.bh = blank_attr();
	INTX86(0x10, &regs, &regs);
}

void
ibmrev(		/* change reverse video state */
UINT state)	/* TRUE = reverse, FALSE = normal */
{
	/* This never gets used under the IBM-PC driver */
}

/* change screen properties */
int
ibmcres(
const char *res)	/* name for new "resolution" */
{
	int i;
	int status = FALSE;

	if (!res || !strcmp(res, "?"))	/* find the default configuration */
		res = "default";

	/* look up the driver by name, initialize screen */
	for (i = 0; i < TABLESIZE(drivers); i++) {
		if (strcmp(res, drivers[i].name) == 0) {
			if ((status = screen_init(i)) == TRUE) {
				strcpy(current_res_name, res);
				break;
			}
		}
	}
	return status;
}

#if OPT_MS_MOUSE || OPT_FLASH
extern VIDEO **vscreen;	/* patch: edef.h */

static	int	vp2attr ( VIDEO *, int );
static	void	move2nd ( int, int );
static	void	copy2nd ( int );

static int
vp2attr(VIDEO *vp, int inverted)
{
	int	attr;
#if	OPT_COLOR
	if (ColorDisplay())
		attr = AttrColor(
			inverted ? ReqFcolor(vp) : ReqBcolor(vp),
			inverted ? ReqBcolor(vp) : ReqFcolor(vp));
	else
#endif
	 attr = AttrMono(!inverted);
	return (attr << 8);
}

static void
move2nd(int row, int col)
{
	union REGS regs;
	regs.h.ah = 0x02;
	regs.h.bh = 1;
	regs.h.dh = row;
	regs.h.dl = col;
	INTX86(0x10, &regs, &regs);
}

static void
copy2nd(int inverted)
{
	WINDOW	*wp;
	VIDEO	*vp;
	USHORT *lp;
	char *tt;
	register int	row, col, attr;

	for_each_visible_window(wp) {
		for (row = wp->w_toprow; row <= mode_row(wp); row++) {
			vp = vscreen[row];
			lp = screen2_lines[row];
			tt = vp->v_text;
			attr = vp2attr(vp, inverted ^ (row == mode_row(wp)));
			for (col = 0; col < term.cols; col++) {
				lp[col] = (attr | tt[col]);
			}
		}
	}

	/* fill in the message line */
	lp = screen2_lines[term.rows-1];
	attr = blank_attr() << 8;
	for (col = 0; col < term.cols; col++)
		lp[col] = attr | SPACE;
	move2nd(ttrow, ttcol);
}
#endif

/*
 * Reading back from display memory does not work properly when using a mouse,
 * because the portion of the display line beginning with the mouse cursor is
 * altered (by the mouse driver, apparently).  Flash the display by building an
 * inverted copy of the screen in the second display-page and toggling
 * momentarily to that copy.
 *
 * Note: there's no window from which to copy the message line.
 */
#if OPT_FLASH
void
flash_display (void)
{
	copy2nd(TRUE);
	set_page(1);
	catnap(100,FALSE); /* if we don't wait, we cannot see the flash */
	set_page(0);
}
#endif	/* OPT_FLASH */

void
ibmbeep(void)
{
#if	OPT_FLASH
	if (global_g_val(GMDFLASH) && ibm_opened) {
		flash_display();
		return;
	}
#endif
	bdos(6, BEL, 0);	/* annoying!! */
}

void
ibmopen(void)
{
	register DRIVERS *driver = &drivers[ORIGTYPE];
	int i;
	union REGS regs;

	if (sizeof(dynamic_VGA_info) != 64) {
		printf("DOS vile build error -- dynamic_VGA_info struct"
			" must be packed (ibmpc.c)\n");
		exit(BADEXIT);
	}

	regs.h.ah = 0xf;
	INTX86(0x10,&regs, &regs);	/* VIDEO - GET DISPLAY MODE */

	driver->vchr  = C8x8;
	driver->vres  = RES_200;
	driver->rows  = 25;
	driver->cols  = regs.h.ah;
	driver->mode  = regs.h.al;
	original_page = regs.h.bh;
	driver->type  = identify_adapter();
	allowed_vres  = (1<<RES_200);	/* assume only 200 scan-lines */

	if (driver->type == VGASCR) {	/* we can determine original rows */
		dynamic_VGA_info buffer;

		if (get_vga_bios_info(&buffer)) {
#undef TESTIT
#ifdef TESTIT
			printf ("call succeeded\n");
			printf ("static_info is 0x%x\n", buffer.static_info);
			printf ("code_number is 0x%x\n", buffer.code_number);
			printf ("num_columns is 0x%x\n", buffer.num_columns);
			printf ("page_length is 0x%x\n", buffer.page_length);
			printf ("curr_page is 0x%x\n", buffer.curr_page);
			printf ("num_rows is 0x%x\n", buffer.num_rows);
#endif
			switch (buffer.char_height) {
			case 8:		driver->vchr = C8x8;	break;
			case 14:	driver->vchr = C8x14;	break;
			case 16:	driver->vchr = C8x16;	break;
			}
			driver->rows = buffer.num_rows;
#if CC_DJGPP
			{ ULONG staticinfop;
			static_VGA_info static_info;
			staticinfop = ((ULONG)buffer.static_info & 0xffffL);
			staticinfop += (((ULONG)buffer.static_info >> 12) &
						0xffff0L);
			dosmemget( staticinfop, sizeof(static_info),
					&static_info);
			allowed_vres = static_info.text_scanlines;
			}
#elif CC_WATCOM
			{
			static_VGA_info __far *staticinfop;
			staticinfop = MK_FP (
				((ULONG)(buffer.static_info) >> 16) & 0xffffL,
				((ULONG)(buffer.static_info)      ) & 0xffffL
				);
			allowed_vres = staticinfop->text_scanlines;
			}
#else
			allowed_vres = buffer.static_info->text_scanlines;
#endif
			driver->vres = buffer.num_pixel_rows;
		}
	} else if (driver->type == EGASCR) {
		allowed_vres |= (1<<RES_350);
	}

	original_curs = get_cursor();

#ifdef PVGA
	set_display(10);	/* set graphic 640x350 mode */
	regs.x._AX_ = 0x007F;
	regs.h.bh = 0x01;		/* set non-VGA mode */
	INTX86(0x10,&regs, &regs);
	set_display(7);		/* set Hercule mode */
	current_res_name = "25";
#endif

#if	OPT_COLOR
	set_palette(initpalettestr);
#endif
	if (!ibmcres(current_res_name))
		(void)screen_init(ORIGTYPE);
	revexist = TRUE;
	ttopen();

#ifdef MUCK_WITH_KBD_RATE
	maxkbdrate();   /* set the keyboard rate to max */
#endif
	for (i = TABLESIZE(keyseqs); i--; ) {
		int len = strlen(keyseqs[i].seq);
		if (keyseqs[i].seq[0] == 'Z') {
			keyseqs[i].seq[0] = '\0';
		}
		addtosysmap(keyseqs[i].seq, len, keyseqs[i].code);
	}

	ibm_opened = TRUE;	/* now safe to use 'flash', etc. */
}

void
ibmclose(void)
{
	int	ctype = current_ibmtype;

	if (current_ibmtype != ORIGTYPE)
		(void)screen_init(ORIGTYPE);
	if (original_page != 0)
		set_page(original_page);
	set_cursor(original_curs);

	current_ibmtype = ctype; /* ...so subsequent term.open restores us */

	dtype = MONOSCR;		/* ...force monochrome */
	kbd_erase_to_end(0);	/* ...force colors _off_ */
	kbd_flush();
}

/* "keyboard" open and close */
void
ibmkopen(void)
{
	ms_install();
#if CC_DJGPP
	setmode(0,O_BINARY);
#endif
}

void
ibmkclose(void)
{
#if CC_DJGPP
	setmode(0,O_TEXT);
#endif
	ms_deinstall();
}

static int
screen_init(			/* initialize the pointers to video memory */
int newtype)			/* type of graphics adapter to initialize */
{
	dynamic_VGA_info buffer;
	union REGS regs;
	union {
		long laddr;	/* long form of address */
		USHORT *paddr;	/* pointer form of address */
	} addr;
	long pagesize;
	register DRIVERS *driver;
	register int i;
	int	     type, rows, cols;

	driver = &drivers[newtype];

	/* check to see if we're allowed this many scan-lines */
	if ((allowed_vres & (1 << (driver->vres))) == 0)
		return FALSE;

	type = driver->type;
	rows = driver->rows;
	cols = driver->cols;

	/* and set up the various parameters as needed */
	set_vertical_resolution(driver->vres);

	set_display(driver->mode);
	set_vertical_resolution(driver->vres);
	set_char_size(driver->vchr);

	/* reset the original cursor -- it gets changed above somewhere */
	set_cursor(original_curs);

	/*
	 * Install an alternative hardcopy routine which prints as many lines
	 * as are displayed on the screen. The normal BIOS routine always
	 * prints 25 lines.
	 */
	if (rows > 25) {
		regs.h.ah = 0x12;	/* alternate select function code    */
		regs.h.bl = 0x20; 	/* alt. print screen routine	     */
		INTX86(0x10, &regs, &regs);	/* VIDEO - SELECT ALTERNATE PRTSCRN  */
	}

	if (driver->type == EGASCR) {
		outp(0x3d4, 10);	/* video bios bug workaround */
		outp(0x3d5, 6);
	}

	/* set the $sres environment var. to the name of the driver in use */
	(void)strcpy(screen_desc, driver->name);

	if ((type == MONOSCR) != (dtype == MONOSCR))
		sgarbf = TRUE;
	dtype = type;
	current_ibmtype = newtype;

	/* initialize the screen pointer array */
	if (monochrome)
		addr.laddr = FAR_POINTER(ScreenAddress[MONOSCR],0x0000);
	else if (type == MONOSCR)
		addr.laddr = FAR_POINTER(ScreenAddress[CGASCR],0x0000);
	else
		addr.laddr = FAR_POINTER(ScreenAddress[type],0x0000);

	for (i = 0; i < rows; i++)
		screen_lines[i] = addr.paddr + (cols * i);

#if OPT_FLASH || OPT_MS_MOUSE
	/* Build row-indices for display page #1, to use it in screen flashing
	 * or for mouse highlighting.
	 */
	if ((type == VGASCR) && get_vga_bios_info(&buffer)) {
		/*
		 * Setting page-1 seems to make Window 3.1 "aware" that we're
		 * going to write to that page.  Otherwise, the "flash" mode
		 * doesn't write to the correct address.
		 */
		set_page(1);
		get_vga_bios_info(&buffer);
		pagesize = (long)buffer.page_length; /* also, page-1 offset */
		set_page(0);
	} else {
		/*
		 * This was tested for all of the VGA combinations running
		 * with MSDOS, but isn't general -- dickey@software.org
		 */
		pagesize = (rows * cols);
		switch (cols) {
		case 40:	pagesize += ((4*cols) - 32);	break;
		case 80:	pagesize += ((2*cols) - 32);	break;
		}
		pagesize <<= 1;
	}
	addr.laddr += pagesize;
	for (i = 0; i < rows; i++)
		screen2_lines[i] = addr.paddr + (cols * i);
#endif
	/* changing the screen-size forces an update, so we do this last */
	newscreensize(rows, cols);
#if OPT_MS_MOUSE
	if (ms_exists()) {
		ms_deinstall();
		ms_install();
	}
#endif
	return TRUE;
}


int identify_adapter(void)
{
	union REGS regs;
	monochrome = FALSE;

	/* check for VGA or MCGA */
	regs.x._AX_ = 0x1a00;
	regs.h.bl = 0x00;
	INTX86(0x10,&regs, &regs); /* GET DISPLAY COMB. CODE (PS,VGA,MCGA) */

	if (regs.h.al == 0x1a) {	/* function succeeded */
		switch (regs.h.bl) {
		case 0x01:	monochrome = TRUE;
				return MONOSCR;

		case 0x02:	return CGASCR;

		case 0x05:	monochrome = TRUE;	/* FALLTHRU */
		case 0x04:	return EGASCR;

		case 0x07:	monochrome = TRUE;	/* FALLTHRU */
		case 0x08:	return VGASCR;

		case 0x0b:	monochrome = TRUE;	/* FALLTHRU */
		case 0x0a:				/* FALLTHRU */
		case 0x0c:	return CGASCR;	/* MCGA */
		}
	}

	/*
	 * Check for MONO board
	 */
	INTX86(0x11, &regs, &regs);	/* BIOS - GET EQUIPMENT LIST */

	/* Bits 4-5 in ax are:
	 *	00 EGA, VGA or PGA
	 *	01 40x25 color
	 *	10 80x25 color
	 *	11 80x25 monochrome
	 */
	if ((regs.x._AX_ & 0x30) == 0x30) {
		monochrome = TRUE;
		return MONOSCR;
	}

	/*
	 * Check for EGA board
	 */
	regs.h.ah = 0x12;
	regs.h.bl = 0x10;
	INTX86(0x10, &regs, &regs);	/* VIDEO - GET EGA INFO */

	if (regs.h.bl != 0x10)
		return EGASCR;

	return CGASCR;
}

/* write a line out*/
void
scwrite(int row, int col,	/* where */
	int nchar,		/* length of outstr and attrstr */
	const char *outstr,  	/* string to write */
	VIDEO_ATTR *attrstr, 	/* attributes to write out */
	int forg,		/* foreground color to write */
	int bacg)		/* background color to write */
{
	register USHORT *lnptr;
	register int i;

	if (row > term.rows-1)
		return;

	lnptr = screen_lines[row]+col;

	if (attrstr) {
		register USHORT attrnorm;
		register USHORT attrrev;
#if	OPT_COLOR
		if (ColorDisplay()) {
			attrnorm = AttrColor(bacg,forg) << 8;
			attrrev = AttrColor(forg,bacg) << 8;
		} else
#endif
		{
		attrnorm = AttrMono(bacg < forg) << 8;
		attrrev = AttrMono(forg < bacg) << 8;
		}
		for (i = 0; i < nchar; i++) {
			*lnptr++ = ((outstr != 0)
				? (outstr[i+col] & 0xff)
				: SPACE) | ((attrstr[i+col] & (VAREV|VASEL))
					? attrrev
					: attrnorm);
		}
	} else {
		register USHORT attr; /* temporary place to store attribute byte */
		/* build the attribute byte and setup the screen pointer */
#if	OPT_COLOR
		attr = ColorDisplay() ? AttrColor(bacg,forg) :
		    			AttrMono(bacg < forg);
#else
		attr = AttrMono(bacg < forg);
#endif
		attr <<= 8;
		for (i = 0; i < nchar; i++) {
			*lnptr++ = ((outstr != 0)
				? (outstr[i+col] & 0xff)
				: SPACE) | attr;
		}
	}

}

/* reads back a line into a VIDEO struct, used in line-update computation */
VIDEO *
scread(VIDEO *vp, int row)
{
	register int	i;
	USHORT *slp;		/* screen line image		*/

	if (row > term.rows-1)
		return 0;

	if (vp == 0) {
		static	VIDEO	*mine;
		if (video_alloc(&mine))
			vp = mine;
		else
			tidy_exit(BADEXIT);
	}

	slp = &screen_lines[row];
	for (i = 0; i < term.cols; i++)
		vp->v_text[i] = slp[i];

	return vp;
}

/* returns attribute for blank/empty space */
static int
blank_attr(void)
{
	register int attr;
#if	OPT_COLOR
	attr = ColorDisplay() ? AttrColor(gbcolor,gfcolor) : AttrMono(TRUE);
#else
	attr = AttrMono(TRUE);
#endif
	return attr;
}

/*
 * Move 'n' lines starting at 'from' to 'to'
 *
 * OPT_PRETTIER_SCROLL is prettier but slower -- it scrolls a line at a time
 *	instead of all at once.
 */
void
ibmscroll(int from, int to, int n)
{
	union REGS regs;
#if OPT_PRETTIER_SCROLL
	if (absol(from-to) > 1) {
		ibmscroll(from, (from < to) ? to-1 : to+1, n);
		if (from < to)
			from = to-1;
		else
			from = to+1;
	}
#endif
	if (from > to) {
		regs.h.ah = 0x06;	/* scroll window up */
		regs.h.al = from - to;	/* number of lines to scroll */
		regs.h.ch = to;		/* upper window row */
		regs.h.dh = from + n - 1;	/* lower window row */
	} else {
		regs.h.ah = 0x07;	/* scroll window down */
		regs.h.al = to - from;	/* number of lines to scroll */
		regs.h.ch = from;	/* upper window row */
		regs.h.dh = to + n - 1;	/* lower window row */
	}
	regs.h.bh = blank_attr();	/* attribute to use for line-fill */
	regs.h.cl = 0;			/* left window column */
	regs.h.dl = term.cols - 1;	/* lower window column */
	INTX86(0x10, &regs, &regs);
}


/*--------------------------------------------------------------------------*/

#if OPT_MS_MOUSE

/* Define translations between mouse (pixels) and chars (row/col).
 * patch: why does 8 by 8 work, not chr_high by chr_wide?
 */
#define MS_CHAR_WIDE	((term.cols == 80) ? 8 : 16)
#define MS_CHAR_HIGH    8

#define	pixels2col(x)   ((x)/MS_CHAR_WIDE)
#define pixels2row(y)   ((y)/MS_CHAR_HIGH)

#define col2pixels(x)   ((x)*MS_CHAR_WIDE)
#define row2pixels(y)   ((y)*MS_CHAR_HIGH)

/* Define a macro for calling mouse services */
#define MouseCall INTX86(0x33, &regs, &regs)

#define MS_MOVEMENT	iBIT(0)	/* mouse cursor movement */
#define	MS_BTN1_PRESS   iBIT(1)	/* left button */
#define MS_BTN1_RELEASE iBIT(2)
#define MS_BTN2_PRESS   iBIT(3)	/* right button */
#define MS_BTN2_RELEASE iBIT(4)
#define MS_BTN3_PRESS   iBIT(5)	/* center button */
#define MS_BTN3_RELEASE iBIT(6)

#define BLINK 0x8000

	/* These have to be "far", otherwise TurboC doesn't force the
	 * segment register to be specified from 'ms_event_handler()'
	 */
int	far	button_pending;	/* 0=none, 1=pressed, 2=released */
int	far	button_number;	/* 1=left, 2=right, 3=center */
int	far	button_press_x;
int	far	button_press_y;
int	far	button_relsd_x;
int	far	button_relsd_y;
int		rodent_exists;
int		rodent_cursor_display;

#if CC_TURBO
#define	MsButtonPending() button_pending
#endif

/*
 * If we're using a DPMI configuration, we'll not be able to use the mouse
 * event handler, since it relies on a call rather than an interrupt.  We can
 * do polling instead.
 */
#if !CC_TURBO
static int
MsButtonPending(void)
{
	union REGS regs;
	regs.x._AX_ = 3;	/* query the mouse */
	MouseCall;

	if (regs.x._BX_ & 7) {
		if (regs.x._BX_ & 1)
			button_number = 1;
		else if (regs.x._BX_ & 2)
			button_number = 2;
		else if (regs.x._BX_ & 4)
			button_number = 3;

		button_press_x = button_relsd_x = regs.x._CX_;
		button_press_y = button_relsd_y = regs.x._DX_;
		button_pending = 1;

	} else {
		if (button_pending)
			button_pending = 2;
		button_relsd_x = regs.x._CX_;
		button_relsd_y = regs.x._DX_;
	}
	return button_pending;
}
#endif

int
ms_exists (void)
{
	return rodent_exists;
}

void
ms_processing (void)
{
	WINDOW	*wp;
	int	copied = FALSE,
		invert, normal,
		first, first_x = ttcol, first_y = ttrow,
		last,  last_x  = ttcol,  last_y = ttrow,
		that,  that_x,  that_y,
		attr, delta;
	USHORT *s2page = screen2_lines[0];

	ms_showcrsr();
	while (MsButtonPending()) {
		if (button_pending == 2) {	/* released? */
			button_pending = 0;
			break;
		} else {	/* selection */

#if CC_TURBO
			disable();
			that_x = button_relsd_x;
			that_y = button_relsd_y;
			enable();
#else
			that_x = button_relsd_x;
			that_y = button_relsd_y;
#endif

			if (!copied) {
				int x = pixels2col(button_press_x);
				int y = pixels2row(button_press_y);
				wp = row2window(y);
				/* Set the dot-location if button 1 was pressed
				 * in a window.
				 */
				if (wp != 0
				 && ttrow != term.rows - 1
				 && setcursor(y, x)) {
					(void)update(TRUE);
					ms_setvrange(wp->w_toprow,
						     mode_row(wp) - 1);
				} else {	/* cannot reposition */
					kbd_alarm();
					while (MsButtonPending() != 2)
						;
					continue;
				}
				first_x = last_x = col2pixels(ttcol);
				first_y = last_y = row2pixels(ttrow);
				first = ms_crsr2inx(first_x, first_y);

				copy2nd(FALSE);
				set_page(1);
				copied = TRUE;

				invert = vp2attr(vscreen[ttrow],TRUE);
				normal = vp2attr(vscreen[ttrow],FALSE);
			}

			that  = ms_crsr2inx(that_x, that_y);
			last  = ms_crsr2inx(last_x, last_y);
			delta = (last < that) ? 1 : -1;

			if (that != last) {
				register int j;
				if (((last < that) && (last <= first))
				 || ((last > that) && (last >= first))) {
					attr = normal;
				} else {
					attr = invert;
				}
#define HiLite(n,attr) s2page[n] = attr | (s2page[n] & 0xff)

				for (j = last; j != that; j += delta) {
					if (j == first) {
						if (attr == normal) {
							attr = invert;
						} else {
							attr = normal;
						}
					}
					HiLite(j,attr);
				}
				HiLite(that,invert|BLINK);
			}
			last_x = that_x;
			last_y = that_y;
		}
	}

	/*
	 * If we've been highlighting the selection, finish it off.
	 */
	if (copied) {
		set_page(0);
		ms_setvrange(0, term.rows-1);
		mlerase();
		setwmark(pixels2row(last_y), pixels2col(last_x));
		if (DOT.l != MK.l
		 || DOT.o != MK.o) {
			regionshape = EXACT;
			(void)yankregion();
			(void)update(TRUE);
		}
		movecursor(pixels2row(first_y), pixels2col(first_x));
	}
}

/* translate cursor position (pixels) to array-index of the text-position */
static int
ms_crsr2inx(int x, int y)
{
	return pixels2col(x) + (pixels2row(y) * term.cols);
}

static void
ms_deinstall(void)
{
	union REGS regs;
	ms_hidecrsr();
	regs.x._AX_ = 0;	/* reset the mouse */
	MouseCall;
	rodent_exists = FALSE;
}

	/* This event-handler cannot do I/O; tracing it can be tricky...
	 */
#if CC_TURBO
void far
ms_event_handler (void)
{
	UINT	ms_event  = _AX;
/*	UINT	ms_button = _BX;*/
	UINT	ms_horz   = _CX;
	UINT	ms_vert   = _DX;

	if (ms_event & MS_BTN1_PRESS) {
		button_pending = 1;
		button_number  = 1;
		button_press_x = button_relsd_x = ms_horz;
		button_press_y = button_relsd_y = ms_vert;
	} else {	/* movement or release */
		if (ms_event & MS_BTN1_RELEASE)
			button_pending = 2;
		button_relsd_x = ms_horz;
		button_relsd_y = ms_vert;
	}
	return;
}
#endif

static void
ms_hidecrsr(void)
{
	union REGS regs;
	/* Hides the mouse cursor if it is displayed */
	if (rodent_cursor_display) {
		rodent_cursor_display = FALSE;
		regs.x._AX_ = 0x02;
		MouseCall;
	}
} /* End of ms_hidecrsr() */

static void
ms_install(void)
{
	union REGS regs;
	if (rodent_exists)
		return;

	/* If a mouse is installed, initializes the mouse and
	 * sets rodent_exists to 1. If no mouse is installed,
	 * sets rodent_exists to 0.
	 */
	regs.x._AX_ = 0;
	MouseCall;
	rodent_exists = regs.x._AX_;
	rodent_cursor_display = FALSE; /* safest assumption */
	if (ms_exists()) {
		struct SREGS segs;

		memset(&regs, 0, sizeof(regs));
		memset(&segs, 0, sizeof(segs));

		regs.x._AX_ = 0x4;	/* set mouse position */
		regs.x._CX_ = 0;
		regs.x._DX_ = 0;
		MouseCall;

#if CC_TURBO
		regs.x._AX_ = 0xc;	/* define event handler */
		regs.x._CX_ = MS_MOVEMENT | MS_BTN1_PRESS | MS_BTN1_RELEASE;
		regs.x._DX_ = FP_OFF(ms_event_handler);
		segs.es = FP_SEG(ms_event_handler);
		INTX86X(0x33, &regs, &regs, &segs);
#endif
	}
}

static void
ms_setvrange(int upperrow, int lowerrow)
{
	union REGS regs;
	/* Restricts vertical cursor movement to the screen region
	 * between upperrow and lowerrow.  If the cursor is outside the range,
	 * it is moved inside.
	 */
	regs.x._AX_ = 0x08;
	regs.x._CX_ = row2pixels(upperrow);
	regs.x._DX_ = row2pixels(lowerrow);
	MouseCall;
} /* End of ms_setvrange() */


static void
ms_showcrsr(void)
{
	union REGS regs;
	/* Displays the mouse cursor */
	int counter;

	/* Call Int 33H Function 2AH to get the value of the display counter */
	regs.x._AX_ = 0x2A;
	MouseCall;
	counter = regs.x._AX_;

	/* Call Int 33H Function 01H as many times as needed to display */
	/* the mouse cursor */
	while (counter-- > 0) {
		regs.x._AX_ = 0x01;
		MouseCall;
	}
	rodent_cursor_display = TRUE;
} /* End of ms_showcrsr() */
#endif /* OPT_MS_MOUSE */