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