1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 #include "jove.h"
9 
10 #ifdef IBMPCDOS	/* the body is the rest of this file */
11 
12 #include "fp.h"	/* scr_putchar */
13 #include "chars.h"
14 #include "screen.h"
15 #include "term.h"
16 
17 /* here come the actual emulation routines */
18 
19 #include <dos.h>
20 #include <conio.h>
21 
22 typedef unsigned char	BYTE;
23 typedef unsigned int	WORD;
24 
25 #define VBS_set_cursor_position	0x02
26 #define VBS_get_cursor_position_and_size	0x03
27 #define VBS_select_active_display_page	0x05
28 #define VBS_scroll_window_up	0x06
29 #define VBS_scroll_window_down	0x07
30 #define VBS_read_character_and_attribute	0x08
31 #define VBS_write_character_and_attribute	0x09
32 #define VBS_TTY_character_output	0x0e
33 #define VBS_get_current_video_state	0x0f
34 
35 #define VBSF_get_font_information	0x1130
36 
37 #define VideoBIOS(r)	int86(0x10, (r), (r));
38 
39 private BYTE
40 	c_attr = 0x07,	/* current attribute white on black */
41 	c_row = 0,	/* current row */
42 	c_col = 0;	/* current column */
43 
44 int
45 	Txattr = 0x07,	/* VAR: text-attribute (white on black) */
46 	Mlattr = 0x70,	/* VAR: mode-line-attribute (black on white) */
47 	Hlattr = 0x10;	/* VAR: highlight-attribute */
48 
49 void
getTERM()50 getTERM()
51 {
52 	/* Check if 101- or 102-key keyboard is installed.
53 	 * This test is apparently unreliable, so we allow override.
54 	 * Courtesy of Ralph Brown's interrupt list.
55 	 */
56 	char _far *kbd_stat_byte3 = (char _far *)0x00400096UL;
57 	enhanced_keybrd = (0x10 & *kbd_stat_byte3) != 0;
58 	pcSetTerm();
59 }
60 
61 private void
setcolor(attr)62 setcolor(attr)
63 BYTE attr;
64 {
65 	c_attr = attr;
66 }
67 
68 private void
set_cur()69 set_cur()
70 {
71 	union REGS vr;
72 
73 	vr.h.ah = VBS_set_cursor_position;
74 	vr.h.bh = 0;	/* video page 0 */
75 	vr.h.dl = c_col;
76 	vr.h.dh = c_row;
77 	VideoBIOS(&vr);
78 }
79 
80 private void
get_cur()81 get_cur()
82 {
83 	union REGS vr;
84 
85 	vr.h.ah = VBS_get_cursor_position_and_size;
86 	vr.h.bh = 0;	/* video page 0 */
87 	VideoBIOS(&vr);
88 	c_col = vr.h.dl;
89 	c_row = vr.h.dh;
90 }
91 
92 private BYTE
chpl()93 chpl()
94 {
95 	union REGS vr;
96 
97 	vr.h.ah = VBS_get_current_video_state;
98 	VideoBIOS(&vr);
99 	return vr.h.ah;
100 }
101 
102 #define cur_mov(r, c)	{ c_row = (r); c_col = (c); set_cur(); }
103 
104 private void
scr_win(op,no,ur,lr)105 scr_win(op, no, ur, lr)
106 int op, no, ur, lr;
107 {
108 	union REGS vr;
109 
110 	vr.h.ah = op;	/* scroll window up or down */
111 	vr.h.al = no;	/* number of rows to scroll */
112 
113 	vr.h.ch = ur;	/* upper row */
114 	vr.h.cl = 0;	/* left column */
115 	vr.h.dh = lr;	/* lower row */
116 	vr.h.dl = CO-1;	/* right column */
117 
118 	vr.h.bh = c_attr;
119 	VideoBIOS(&vr);
120 }
121 
122 void
i_lines(top,bottom,num)123 i_lines(top, bottom, num)
124 int top, bottom, num;
125 {
126 	scr_win(VBS_scroll_window_down, num, top, bottom);
127 }
128 
129 void
d_lines(top,bottom,num)130 d_lines(top, bottom, num)
131 int top, bottom, num;
132 {
133 	scr_win(VBS_scroll_window_up, num, top, bottom);
134 }
135 
136 void
clr_page()137 clr_page()
138 {
139 	SO_off();
140 	/* Note: VBS_scroll_window_up with a count of 0 clears the screen! */
141 	scr_win(VBS_scroll_window_up, 0, 0, ILI);
142 	cur_mov(0, 0);
143 }
144 
145 private void
ch_out(c,n)146 ch_out(c, n)
147 BYTE c, n;
148 {
149 	union REGS vr;
150 
151 	vr.h.ah = VBS_write_character_and_attribute;
152 	vr.h.al = c;
153 	vr.h.bl = c_attr;
154 	vr.h.bh = 0;	/* video page 0 */
155 	vr.x.cx = n;
156 	VideoBIOS(&vr);
157 }
158 
159 void
clr_eoln()160 clr_eoln()
161 {
162 	ch_out(' ', CO-c_col);
163 }
164 
165 /* Video mode setting derived from code posted to comp.os.msdos.programmer
166  * by Joe Huffman 1990 August 15 (found on SIMTEL in msdos/screen/vidmode.zip)
167  */
168 
169 private BYTE
lpp()170 lpp()
171 {
172 	union REGS vr;
173 	int	lines;
174 
175 	vr.x.ax = VBSF_get_font_information;
176 	vr.h.bh = 0;	/* we don't care which pointer we get back */
177 	vr.h.dl = 0;	/* default, if BIOS doesn't know how to this */
178 	VideoBIOS(&vr);
179 	lines = vr.h.dl;	/* number of last line on screen */
180 	switch (lines) {
181 	default:
182 		return lines + 1;
183 	case 25:
184 	case 28:
185 	case 43:
186 	case 50:
187 		return lines;	/* IBM EGA BUG!*/
188 	case 0:
189 		return 25;	/* Who knows?  Just a guess. */
190 	}
191 }
192 
193 /* discover current video attribute */
194 
195 private void
get_c_attr()196 get_c_attr()
197 {
198 	union REGS vr;
199 
200 	vr.h.dl = ' ';	/* write out a SPace, using DOS */
201 	vr.h.ah = 0x02;
202 	int86(0x21, &vr, &vr);
203 
204 	vr.h.ah = VBS_TTY_character_output;	/* backspace over it, using BIOS */
205 	vr.h.al = BS;
206 	vr.h.bh = 0;	/* page number 0 */
207 	VideoBIOS(&vr);
208 
209 	vr.h.ah = VBS_read_character_and_attribute;	/* find out attribute */
210 	VideoBIOS(&vr);
211 	c_attr = vr.h.ah;
212 }
213 
214 /* codes for selecting scan lines for alpha mode (service 0x12, function 0x30) */
215 
216 #define EGA200	0
217 #define EGA350	1
218 #define EGA400	2
219 
220 /* codes for selecting ROM font (function code for service 0x11)
221  * Notes from Ralf Brown's Interrupt List:
222  *	  The routines called with AL=1xh are designed to be called only
223  *	  immediately after a mode set and are similar to the routines called
224  *	  with AL=0xh, except that:
225  *	      Page 0 must be active.
226  *	      Bytes/character is recalculated.
227  *	      Max character rows is recalculated.
228  *	      CRT buffer length is recalculated.
229  *	      CRTC registers are reprogrammed as follows:
230  *		     R09 = bytes/char-1 ; max scan line (mode 7 only)
231  *		     R0A = bytes/char-2 ; cursor start
232  *		     R0B = 0		; cursor end
233  *		     R12 = ((rows+1)*(bytes/char))-1 ; vertical display end
234  *		     R14 = bytes/char	; underline loc
235  *			   (*** BUG: should be 1 less ***)
236  *	the current block specifiers may be determined with INT 10/AH=1Bh,
237  *	  looking at offsets 2Bh and 2Ch of the returned data (VGA only)
238  */
239 
240 #define	EGA8x8	0x12	/* not 0x02 or 0x23 */
241 #define	EGA8x14	0x11	/* not 0x01 or 0x22 */
242 #define	EGA8x16	0x14	/* not 0x04 or 0x24 */
243 
244 private void
EGAsetup(scanlines,font)245 EGAsetup(scanlines, font)
246 BYTE	scanlines;
247 BYTE	font;
248 {
249 	union REGS vr;
250 	vr.h.ah = VBS_select_active_display_page;
251 	vr.h.al = 0;	/* page 0 */
252 	VideoBIOS(&vr);
253 
254 	vr.h.ah = 0x12;	/* request EGA information */
255 	vr.h.bl = 0x10;	/* function number */
256 	vr.x.cx = 0x0000;
257 	VideoBIOS(&vr);
258 
259 	if (vr.x.cx != 0) {
260 		/* the display seems to be EGA or better */
261 		vr.h.ah = 0x12;	/* select scan lines for alpha mode */
262 		vr.h.al = scanlines;
263 		vr.h.bl = 0x30;
264 		VideoBIOS(&vr);
265 
266 		/* Note: bh is left over from VideoBIOS call *before* last! */
267 		if (vr.h.bh == 0)
268 			vr.x.ax = 0x0003;	/* monochrome */
269 		else
270 			vr.x.ax = 0x0007;	/* 80x25 color text */
271 		VideoBIOS(&vr);
272 
273 		vr.h.ah = 0x11;	/* load ROM font */
274 		vr.h.al = font;
275 		vr.h.bl = 0;	/* into block 0 */
276 		VideoBIOS(&vr);
277 	}
278 	get_c_attr();
279 }
280 
281 private bool
set_lines(lines)282 set_lines(lines)
283 int	lines;
284 {
285 	switch (lines) {
286 	case 25:
287 		EGAsetup(EGA400, EGA8x16);
288 		break;
289 	case 28:
290 		EGAsetup(EGA400, EGA8x14);
291 		break;
292 	case 43:
293 		EGAsetup(EGA350, EGA8x8);
294 		break;
295 	case 50:
296 		EGAsetup(EGA400, EGA8x8);
297 		break;
298 	default:
299 		return NO;
300 	}
301 	return YES;
302 }
303 
304 private bool	pc_set = NO;
305 private int	unsetLI;
306 
307 void
pcSetTerm()308 pcSetTerm()
309 {
310 	char	*t = getenv("TERM");
311 
312 	if (!pc_set) {
313 		int	lines = lpp();
314 
315 		unsetLI = lines;
316 		if (t != NULL) {
317 			if (stricmp(t, "ega25") == 0)
318 				lines = 25;
319 			else if (stricmp(t, "ega28") == 0)
320 				lines = 28;
321 			else if (stricmp(t, "ega43") == 0)
322 				lines = 43;
323 			else if (stricmp(t, "ega50") == 0)
324 				lines = 50;
325 		}
326 		if (lines != unsetLI && set_lines(lines))
327 			pc_set = YES;
328 	}
329 	CO = chpl();
330 	if (CO > MAXCOLS)
331 		CO = MAXCOLS;
332 	LI = lpp();
333 	ILI = LI - 1;
334 	get_cur();
335 }
336 
337 void
ttsize()338 ttsize()
339 {
340 }
341 
342 void
pcUnsetTerm()343 pcUnsetTerm()
344 {
345 	if (pc_set) {
346 		pc_set = NO;
347 		(void) set_lines(unsetLI);
348 	}
349 }
350 
351 private void
line_feed()352 line_feed()
353 {
354 	if (++c_row > ILI) {
355 		c_row = ILI;
356 		scr_win(VBS_scroll_window_up, 1, 0, ILI);
357 	}
358 	set_cur();
359 }
360 
361 #define BELL_P 0x61			/* speaker */
362 #define BELL_D 0x2dc			/* 550 hz  */
363 #define TIME_P 0x40			/* timer   */
364 #define TINI   182			/* 10110110b timer initialization */
365 
366 void
dobell(n)367 dobell(n)	/* declared in term.h */
368 int	n;
369 {
370 	unsigned char	spkr_state = inp(BELL_P);
371 
372 	/* ??? Should accesses to timer be run with interrupts disabled?
373 	 * It looks as if the timer would be in a bad state if a sequence
374 	 * of accesses is interrupted. -- DHR 1995 Sept.
375 	 */
376 	outp(TIME_P+3, TINI);
377 	outp(TIME_P+2, BELL_D&0xff);
378 	outp(TIME_P+2, BELL_D>>8);
379 	outp(BELL_P, spkr_state|3);		/* turn speaker on  */
380 	while (n-- > 0) {
381 #ifdef NEVER
382 		/* Control duration by counting half cycles of the audio tone.
383 		 * We detect a half-cycle boundary by noting when the high-order
384 		 * byte of the counter changes between zero and non-zero.
385 		 * This should be unaffected by CPU speed.
386 		 * We can't use the BIOS interval timer facility because
387 		 * I think it would use the timer channel we're already using.
388 		 */
389 		int	half_cycs = 200;	/* number of half-cycles to play */
390 		bool	hi_is_0 = NO;	/* detect zero transitions; initial lie unimportant */
391 
392 		for (;;) {
393 			(void) inp(TIME_P+2);	/* low-order counter 2 byte */
394 			if ((inp(TIME_P+2) == 0) != hi_is_0) {
395 				hi_is_0 = !hi_is_0;
396 				if (--half_cycs <= 0)
397 					break;
398 			}
399 		}
400 #else
401 		unsigned int i = 0x8888;	/* a CPU-speed dependent duration */
402 
403 		do ; while (--i > 0);
404 #endif
405 	}
406 	outp(BELL_P, spkr_state);
407 }
408 
409 /* scr_putchar: put char on screen.  Declared in fp.h */
410 
411 #ifdef USE_PROTOTYPES
412 /* char is subject to default argument promotions */
413 void
scr_putchar(char c)414 scr_putchar(char c)
415 #else
416 void
417 scr_putchar(c)
418 char c;
419 #endif
420 {
421 	switch (c) {
422 	case LF:
423 		line_feed();
424 		break;
425 	case CR:
426 		c_col = 0;
427 		set_cur();
428 		break;
429 	case BS:
430 		if (c_col > 0)
431 			c_col--;
432 		set_cur();
433 		break;
434 	case CTL('G'):	/* ??? is this ever used? */
435 		dobell(1);
436 		break;
437 	default:
438 		ch_out((c), 1);
439 		if (++c_col > CO-1) {
440 			c_col = 0;
441 			line_feed();
442 		}
443 		set_cur();
444 		break;
445 	}
446 }
447 
448 /* No cursor optimization on an IBMPCDOS, this simplifies things a lot.
449  * Think about it: it would be silly!
450  */
451 
452 void
Placur(line,col)453 Placur(line, col)
454 int line,
455     col;
456 {
457 	cur_mov(line, col);
458 	CapCol = col;
459 	CapLine = line;
460 }
461 
462 private bool
463 	doing_so = NO,
464 	doing_us = NO;
465 
466 private void
doattr()467 doattr()
468 {
469 	setcolor((doing_so? Mlattr : Txattr) ^ (doing_us? Hlattr : 0));
470 }
471 
472 void
SO_effect(f)473 SO_effect(f)
474 bool f;
475 {
476 	doing_so = f;
477 	doattr();
478 }
479 
480 void
US_effect(f)481 US_effect(f)
482 bool	f;
483 {
484 	doing_us = f;
485 	doattr();
486 }
487 
488 #endif /* IBMPCDOS */
489