1 /* This file is properly part of the excellent DJGPP compiler/tools package
2 put out by DJ Delorie for use under DOS. Unfortunately, the module
3 as distributed in many versions of the DJGPP is buggy -- this copy
4 of gppconio.c fixes many of those bugs. I understand that these
5 changes have been sent back to the DJGPP maintainers -- hopefully
6 vile will not need to include this file in the future. -pgf 9/95
7
8 $Id: gppconio.c,v 1.5 2020/01/19 16:04:58 tom Exp $
9 */
10
11 #if __DJGPP__ < 2
12
13 /**********************************************************************
14 *
15 * NAME: gppconio.c
16 *
17 * DESCRIPTION: simulate Borland text video funcs for GNU C++
18 *
19 * copyright (c) 1991 J. Alan Eldridge
20 *
21 * M O D I F I C A T I O N H I S T O R Y
22 *
23 * when who what
24 * -------------------------------------------------------------------
25 * 10/27/91 J. Alan Eldridge created
26 * 01/06/92 D. Buerssner make it work with extended characters
27 * (buers@dg1.chemie.uni-konstanz.de) speed-up of cputs
28 * some missing brackets in VIDADDR
29 * don't need scrollwindow anymore
30 * 07/15/93 D. Buerssner take care of cursor tracking
31 * txinfo.curx and txinfo.cury
32 * fill in missing functionality
33 * - textmode
34 * - cscanf
35 * - cgets
36 * - getch and ungetch
37 * - _setcursortype
38 * - kbhit
39 * (hpefully) proper initialization of
40 * txinfo.normattrib and txinfo.attribute
41 * gotoxy(1,1) in clrscr (bug introduced
42 * by my previous patches)
43 * gotoxy(1,1) in window
44 * take care of BELL and BACKSPACE
45 * in putch and cputs
46 * take care of blinking bit in textcolor
47 * and textbackground
48 * declare (and ignore) directvideo
49 * 10/09/93 DJ Delorie Switch to dosmem*() for DPMI
50 * 05/01/94 DJ Delorie Add _wscroll
51 *********************************************************************/
52
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <stdarg.h>
56 #include <dos.h>
57 #include <pc.h>
58 #include <go32.h>
59 #include "gppconio.h"
60
61 int _wscroll = 1;
62
63 int directvideo = 1; /* We ignore this */
64
65 static void setcursor(unsigned int shape);
66 static int getvideomode(void);
67 static void bell(void);
68 static int get_screenattrib(void);
69 static int isEGA(void);
70 static int _scan_getche(FILE *fp);
71 static int _scan_ungetch(int c, FILE *fp);
72
73 #define DBGGTINFO 0
74
75 static unsigned ScreenAddress = 0xb8000UL; /* initialize just in case */
76 static struct text_info txinfo;
77 static int ungot_char;
78 static int char_avail = 0;
79
80 #define VIDADDR(r,c) (ScreenAddress + 2*(((r) * txinfo.screenwidth) + (c)))
81
82 int
puttext(int c,int r,int c2,int r2,void * buf)83 puttext(int c, int r, int c2, int r2, void *buf)
84 {
85 short *cbuf = (short *) buf;
86 /* we should check for valid parameters, and maybe return 0 */
87 r--, r2--, c--, c2--;
88 for (; r <= r2; r++) {
89 dosmemput(cbuf, (c2 - c + 1) * 2, VIDADDR(r, c));
90 cbuf += c2 - c + 1;
91 }
92 return 1;
93 }
94
95 int
gettext(int c,int r,int c2,int r2,void * buf)96 gettext(int c, int r, int c2, int r2, void *buf)
97 {
98 short *cbuf = (short *) buf;
99 /* we should check for valid parameters, and maybe return 0 */
100 r--, r2--, c--, c2--;
101 for (; r <= r2; r++) {
102 dosmemget(VIDADDR(r, c), (c2 - c + 1) * 2, cbuf);
103 cbuf += c2 - c + 1;
104 }
105 return 1;
106 }
107
108 void
gotoxy(int col,int row)109 gotoxy(int col, int row)
110 {
111 ScreenSetCursor(row + txinfo.wintop - 2, col + txinfo.winleft - 2);
112 txinfo.curx = col;
113 txinfo.cury = row;
114 }
115
116 int
wherex(void)117 wherex(void)
118 {
119 int row, col;
120
121 ScreenGetCursor(&row, &col);
122
123 return col - txinfo.winleft + 2;
124 }
125
126 int
wherey(void)127 wherey(void)
128 {
129 int row, col;
130
131 ScreenGetCursor(&row, &col);
132
133 return row - txinfo.wintop + 2;
134 }
135
136 void
textmode(int mode)137 textmode(int mode)
138 {
139 union REGS regs;
140 int mode_to_set = mode;
141 if (mode == LASTMODE)
142 mode = mode_to_set = txinfo.currmode;
143 if (mode == C4350)
144 /*
145 * just set mode 3 and load 8x8 font, idea taken
146 * (and code translated from Assembler to C)
147 * form Csaba Biegels stdvga.asm
148 */
149 mode_to_set = 0x03;
150 regs.h.ah = 0x00; /* set mode */
151 regs.h.al = mode_to_set;
152 int86(0x10, ®s, ®s);
153 if (mode == C80 || mode == BW80 || mode == C4350) {
154 if (isEGA()) {
155 /*
156 * enable cursor size emulation, see Ralf Browns
157 * interrupt list
158 */
159 regs.h.ah = 0x12;
160 regs.h.bl = 0x34;
161 regs.h.al = 0x00; /* 0: enable (1: disable) */
162 int86(0x10, ®s, ®s);
163 }
164 }
165 if (mode == C4350) {
166 if (!isEGA())
167 return;
168 /* load 8x8 font */
169 regs.x.ax = 0x1112;
170 regs.x.bx = 0;
171 int86(0x10, ®s, ®s);
172 }
173 /* _setcursortype(_NORMALCURSOR); */
174 /* reinitialize txinfo structure to take into account new mode */
175 gppconio_init();
176 #if 0
177 /*
178 * For mode C4350 the screen is not cleared on my OAK-VGA.
179 * Should we clear it here? TURBOC doesn't so we don't bother either.
180 */
181 clrscr();
182 #endif
183 }
184
185 void
textattr(int attr)186 textattr(int attr)
187 {
188 txinfo.attribute = ScreenAttrib = (unsigned char) attr;
189 }
190
191 void
textcolor(int color)192 textcolor(int color)
193 {
194 /* strip blinking (highest) bit and textcolor */
195 ScreenAttrib &= 0x70; /* strip blinking (highest) bit and textcolor */
196 txinfo.attribute = (ScreenAttrib |= (color & 0x8f));
197 }
198
199 void
textbackground(int color)200 textbackground(int color)
201 {
202 /* strip background color, keep blinking bit */
203 ScreenAttrib &= 0x8f;
204 /* high intensity background colors (>7) are not allowed
205 so we strip 0x08 bit (and higher bits) of color */
206 txinfo.attribute = (ScreenAttrib |= ((color & 0x07) << 4));
207 }
208
209 void
highvideo(void)210 highvideo(void)
211 {
212 txinfo.attribute = (ScreenAttrib |= 0x08);
213 }
214
215 void
lowvideo(void)216 lowvideo(void)
217 {
218 txinfo.attribute = (ScreenAttrib &= 0x07);
219 }
220
221 void
normvideo(void)222 normvideo(void)
223 {
224 txinfo.attribute = ScreenAttrib = txinfo.normattr;
225 }
226
227 void
_setcursortype(int type)228 _setcursortype(int type)
229 {
230 unsigned cursor_shape;
231 switch (type) {
232 case _NOCURSOR:
233 cursor_shape = 0x0700;
234 break;
235 case _SOLIDCURSOR:
236 cursor_shape = 0x0007;
237 break;
238 /* case _NORMALCURSOR: */
239 default:
240 cursor_shape = 0x0607;
241 break;
242 }
243 setcursor(cursor_shape);
244 }
245
246 static void
setcursor(unsigned int cursor_shape)247 setcursor(unsigned int cursor_shape)
248 /* Sets the shape of the cursor */
249 {
250 union REGS reg;
251
252 reg.h.ah = 1;
253 reg.x.cx = cursor_shape;
254 int86(0x10, ®, ®);
255 } /* setcursor */
256
257 static void
getwincursor(int * row,int * col)258 getwincursor(int *row, int *col)
259 {
260 ScreenGetCursor(row, col);
261 }
262
263 void
clreol(void)264 clreol(void)
265 {
266 short image[256];
267 short val = ' ' | (ScreenAttrib << 8);
268 int c, row, col, ncols;
269
270 getwincursor(&row, &col);
271 ncols = txinfo.winright - col;
272
273 for (c = 0; c < ncols; c++)
274 image[c] = val;
275
276 puttext(col + 1, row + 1, txinfo.winright, row + 1, image);
277 }
278
279 static void
fillrow(int row,int left,int right,int fill)280 fillrow(int row, int left, int right, int fill)
281 {
282 int col;
283 short filler[right - left + 1];
284
285 for (col = left; col <= right; col++)
286 filler[col - left] = fill;
287 dosmemput(filler, (right - left + 1) * 2, VIDADDR(row, left));
288 }
289
290 void
clrscr(void)291 clrscr(void)
292 {
293 short filler[txinfo.winright - txinfo.winleft + 1];
294 int row, col;
295 for (col = 0; col < txinfo.winright - txinfo.winleft + 1; col++)
296 filler[col] = ' ' | (ScreenAttrib << 8);
297 for (row = txinfo.wintop - 1; row < txinfo.winbottom; row++)
298 dosmemput(filler, (txinfo.winright - txinfo.winleft + 1) * 2,
299 VIDADDR(row, txinfo.winleft - 1));
300 gotoxy(1, 1);
301 }
302
303 int
putch(int c)304 putch(int c)
305 {
306 int row, col;
307
308 ScreenGetCursor(&row, &col);
309
310 /* first, handle the character */
311 if (c == '\n') {
312 row++;
313 } else if (c == '\r') {
314 col = txinfo.winleft - 1;
315 } else if (c == '\b') {
316 if (col > txinfo.winleft - 1)
317 col--;
318 else if (row > txinfo.wintop - 1) {
319 /*
320 * Turbo-C ignores this case; we are smarter.
321 */
322 row--;
323 col = txinfo.winright - 1;
324 }
325 } else if (c == 0x07)
326 bell();
327 else {
328 /* short val = c | (ScreenAttrib << 8); */
329 /* puttext(col + 1, row + 1, col + 1, row + 1, &val); */
330 ScreenPutChar(c, ScreenAttrib, col, row);
331 col++;
332 }
333
334 /* now, readjust the window */
335
336 if (col >= txinfo.winright) {
337 col = txinfo.winleft - 1;
338 row++;
339 }
340
341 if (row >= txinfo.winbottom) {
342 /* scrollwin(0, txinfo.winbottom - txinfo.wintop, 1); */
343 if (_wscroll) {
344 ScreenSetCursor(txinfo.wintop - 1, 0);
345 delline();
346 }
347 row--;
348 }
349
350 ScreenSetCursor(row, col);
351 txinfo.cury = row - txinfo.wintop + 2;
352 txinfo.curx = col - txinfo.winleft + 2;
353 return c;
354 }
355
356 int
getche(void)357 getche(void)
358 {
359 if (char_avail)
360 /*
361 * We don't know, whether the ungot char was already echoed
362 * we assume yes (for example in cscanf, probably the only
363 * place where ungetch is ever called.
364 * There is no way to check for this really, because
365 * ungetch could have been called with a character that
366 * hasn't been got by a conio function.
367 * We don't echo again.
368 */
369 return (getch());
370 return (putch(getch()));
371 }
372
373 int
getch(void)374 getch(void)
375 {
376 union REGS regs;
377 int c;
378 if (char_avail) {
379 c = ungot_char;
380 char_avail = 0;
381 } else {
382 regs.x.ax = 0x0700;
383 int86(0x21, ®s, ®s);
384 c = regs.h.al;
385 }
386 return (c);
387 }
388
389 int
ungetch(int c)390 ungetch(int c)
391 {
392 if (char_avail)
393 return (EOF);
394 ungot_char = c;
395 char_avail = 1;
396 return (c);
397 }
398
399 /*
400 * kbhit from libc in libsrc/c/dos/kbhit.s doesn't check
401 * for ungotten chars, so we have to provide a new one
402 * Don't call it kbhit, rather use a new name (_conio_kbhit)
403 * and do a #define kbhit _conio_kbhit in gppconio.h.
404 * The old kbhit still can be used if gppconio.h
405 * is not included of after #undef kbhit
406 * If you don't use ungetch (directly or indirectly by cscanf)
407 * both kbhit and _conio_kbhit are the same.
408 * So this shouldn't cause any trouble with previously written
409 * source, because ungetch wasn't available.
410 * The only problem might be, if anybody just included gppconio.h
411 * and has not linked with libpc, (I can't think of a good reason
412 * for this). This will result a link error (undefined symbol _conio_kbhit).
413 */
414
415 #undef kbhit /* want to be able to call kbhit from libc */
416
417 /* The kbhit in libc doesn't work for the second byte of extended chars. */
418 int
kbhit(void)419 kbhit(void)
420 {
421 union REGS regs;
422 regs.h.ah = 0x0b;
423 int86(0x21, ®s, ®s);
424 return regs.h.al ? -1 : 0;
425 }
426
427 int
_conio_kbhit(void)428 _conio_kbhit(void)
429 {
430 if (char_avail)
431 return (1);
432 else
433 return (kbhit());
434 }
435
436 /*
437 * The next two functions are needed by cscanf
438 */
439 static int
_scan_getche(FILE * fp)440 _scan_getche(FILE *fp)
441 {
442 return (getche());
443 }
444
445 static int
_scan_ungetch(int c,FILE * fp)446 _scan_ungetch(int c, FILE *fp)
447 {
448 return (ungetch(c));
449 }
450
451 void
insline(void)452 insline(void)
453 {
454 int row, col, left, right, nbytes, bot, fill;
455 ScreenGetCursor(&row, &col);
456 left = txinfo.winleft - 1;
457 right = txinfo.winright - 1;
458 nbytes = (right - left + 1) * 2;
459 bot = txinfo.winbottom - 1;
460 fill = ' ' | (ScreenAttrib << 8);
461 while (bot > row) {
462 movedata(_go32_conventional_mem_selector(), VIDADDR(bot - 1, left),
463 _go32_conventional_mem_selector(), VIDADDR(bot, left),
464 nbytes);
465 bot--;
466 }
467 if (row <= bot) {
468 fillrow(row, left, right, fill);
469 }
470 }
471
472 void
delline(void)473 delline(void)
474 {
475 int row, col, left, right, nbytes, bot, fill;
476 ScreenGetCursor(&row, &col);
477 left = txinfo.winleft - 1;
478 right = txinfo.winright - 1;
479 nbytes = (right - left + 1) * 2;
480 bot = txinfo.winbottom - 1;
481 fill = ' ' | (ScreenAttrib << 8);
482 while (row < bot) {
483 movedata(_go32_conventional_mem_selector(), VIDADDR(row + 1, left),
484 _go32_conventional_mem_selector(), VIDADDR(row, left),
485 nbytes);
486 row++;
487 }
488 fillrow(bot, left, right, fill);
489 }
490
491 void
window(int left,int top,int right,int bottom)492 window(int left, int top, int right, int bottom)
493 {
494 if (top < 1 || left < 1 || right > txinfo.screenwidth ||
495 bottom > txinfo.screenheight)
496 return;
497
498 txinfo.wintop = top;
499 txinfo.winleft = left;
500 txinfo.winright = right;
501 txinfo.winbottom = bottom;
502 gotoxy(1, 1);
503 }
504
505 int
cputs(const char * s)506 cputs(const char *s)
507 {
508 int row, col, c;
509 const unsigned char *ss = (const unsigned char *) s;
510 short *viaddr;
511 short sa = ScreenAttrib << 8;
512 ScreenGetCursor(&row, &col);
513 viaddr = (short *) VIDADDR(row, col);
514 /*
515 * Instead of just calling putch; we do everything by hand here,
516 * This is much faster. We don't move the cursor after each character,
517 * only after the whole string is written, because ScreenSetCursor
518 * needs to long because of switching to real mode needed with djgpp.
519 * You won't recognize the difference.
520 */
521 while ((c = *ss++)) {
522 /* first, handle the character */
523 if (c == '\n') {
524 row++;
525 viaddr += txinfo.screenwidth;
526 } else if (c == '\r') {
527 col = txinfo.winleft - 1;
528 viaddr = (short *) VIDADDR(row, col);
529 } else if (c == '\b') {
530 if (col > txinfo.winleft - 1) {
531 col--;
532 viaddr--;
533 } else if (row > txinfo.wintop - 1) {
534 /*
535 * Turbo-C ignores this case. We want to be able to
536 * edit strings with backspace in gets after
537 * a linefeed, so we are smarter
538 */
539 row--;
540 col = txinfo.winright - 1;
541 viaddr = (short *) VIDADDR(row, col);
542 }
543 } else if (c == 0x07)
544 bell();
545 else {
546 short q = c | sa;
547 dosmemput(&q, 2, (int) viaddr);
548 viaddr++;
549 col++;
550 }
551
552 /* now, readjust the window */
553
554 if (col >= txinfo.winright) {
555 col = txinfo.winleft - 1;
556 row++;
557 viaddr = (short *) VIDADDR(row, col);
558 }
559
560 if (row >= txinfo.winbottom) {
561 ScreenSetCursor(txinfo.wintop - 1, 0); /* goto first line in window */
562 delline(); /* and delete it */
563 row--;
564 viaddr -= txinfo.screenwidth;
565 }
566 }
567
568 ScreenSetCursor(row, col);
569 txinfo.cury = row - txinfo.wintop + 2;
570 txinfo.curx = col - txinfo.winleft + 2;
571 return (*(--ss));
572 }
573
574 int
cprintf(const char * fmt,...)575 cprintf(const char *fmt,...)
576 {
577 int cnt;
578 char buf[2048]; /* this is buggy, because buffer might be too small. */
579 va_list ap;
580
581 va_start(ap, fmt);
582 cnt = vsprintf(buf, fmt, ap);
583 va_end(ap);
584
585 cputs(buf);
586 return cnt;
587 }
588
589 char *
cgets(char * string)590 cgets(char *string)
591 {
592 unsigned len = 0;
593 unsigned int maxlen_wanted;
594 char *sp;
595 int c;
596 /*
597 * Be smart and check for NULL pointer.
598 * Don't know whether TURBOC does this.
599 */
600 if (!string)
601 return (NULL);
602 maxlen_wanted = (unsigned int) ((unsigned char) string[0]);
603 sp = &(string[2]);
604 /*
605 * Should the string be shorter maxlen_wanted including or excluding
606 * the trailing '\0' ? We don't take any risk.
607 */
608 while (len < maxlen_wanted - 1) {
609 c = getch();
610 /*
611 * should we check for backspace here?
612 * TURBOC does (just checked) but doesn't in cscanf (that's harder
613 * or even impossible). We do the same.
614 */
615 if (c == '\b') {
616 if (len > 0) {
617 cputs("\b \b"); /* go back, clear char on screen with space
618 and go back again */
619 len--;
620 sp[len] = '\0'; /* clear the character in the string */
621 }
622 } else if (c == '\r') {
623 sp[len] = '\0';
624 break;
625 } else if (c == 0) {
626 /* special character ends input */
627 sp[len] = '\0';
628 ungetch(c); /* keep the char for later processing */
629 break;
630 } else {
631 sp[len] = putch(c);
632 len++;
633 }
634 }
635 sp[maxlen_wanted - 1] = '\0';
636 string[1] = (char) ((unsigned char) len);
637 return (sp);
638 }
639
640 int
cscanf(const char * fmt,...)641 cscanf(const char *fmt,...)
642 {
643 return (_doscan_low(NULL, _scan_getche, _scan_ungetch,
644 fmt, (void **) ((&fmt) + 1)));
645 }
646
647 int
movetext(int left,int top,int right,int bottom,int dleft,int dtop)648 movetext(int left, int top, int right, int bottom, int dleft, int dtop)
649 {
650 char *buf = malloc((right - left + 1) * (bottom - top + 1) * 2);
651
652 if (!buf)
653 return 0;
654
655 gettext(left, top, right, bottom, buf);
656 puttext(dleft, dtop, dleft + right - left, dtop + bottom - top, buf);
657 free(buf);
658 return 1;
659 }
660
661 static void
_gettextinfo(struct text_info * t)662 _gettextinfo(struct text_info *t)
663 {
664 int row, col;
665
666 t->winleft = t->wintop = 1;
667 t->winright = t->screenwidth = ScreenCols();
668 t->winbottom = t->screenheight = ScreenRows();
669 ScreenAttrib = t->attribute = t->normattr = get_screenattrib();
670 t->currmode = getvideomode();
671 ScreenGetCursor(&row, &col);
672 t->curx = col + 1;
673 t->cury = row + 1;
674 #if DBGGTINFO
675 printf("left=%2d,right=%2d,top=%2d,bottom=%2d\n", t->winleft,
676 t->winright, t->wintop, t->winbottom);
677 printf("scrht=%2d,scrwid=%2d,norm=%2x,mode=%2d,x=%2d,y=%2d\n",
678 t->screenheight, t->screenwidth, t->normattr, t->currmode,
679 t->curx, t->cury);
680 #endif
681 }
682
683 void
gettextinfo(struct text_info * t)684 gettextinfo(struct text_info *t)
685 {
686 *t = txinfo;
687 #if DBGGTINFO
688 printf("left=%2d,right=%2d,top=%2d,bottom=%2d\n", t->winleft,
689 t->winright, t->wintop, t->winbottom);
690 printf("scrht=%2d,scrwid=%2d,norm=%2x,mode=%2d,x=%2d,y=%2d\n",
691 t->screenheight, t->screenwidth, t->normattr, t->currmode,
692 t->curx, t->cury);
693 #endif
694 }
695
696 static int
getvideomode(void)697 getvideomode(void)
698 {
699 int mode = ScreenMode();
700 /*
701 * in mode C80 we might have loaded a different font
702 */
703 if (mode == C80)
704 if (ScreenRows() > 25)
705 mode = C4350;
706 return (mode);
707 }
708
709 static void
bell(void)710 bell(void)
711 {
712 union REGS regs;
713 #if 0
714 /* use BIOS */
715 regs.h.ah = 0x0e; /* write */
716 regs.h.al = 0x07; /* bell */
717 int86(0x10, ®s, ®s);
718 #else
719 /* use DOS */
720 regs.h.ah = 0x06; /* write */
721 regs.h.dl = 0x07; /* bell */
722 int86(0x21, ®s, ®s);
723 #endif
724 }
725
726 static int
get_screenattrib(void)727 get_screenattrib(void)
728 {
729 union REGS regs;
730 regs.h.ah = 0x08; /* read character and attribute */
731 regs.h.bh = 0; /* video page 0 */
732 int86(0x10, ®s, ®s);
733 return (regs.h.ah & 0x7f); /* strip highest (BLINK) bit */
734 }
735
736 /* check if we have at least EGA (idea form Ralf Browns interrupt list) */
737 static int
isEGA(void)738 isEGA(void)
739 {
740 union REGS regs;
741 regs.h.ah = 0x12;
742 regs.h.bl = 0x10;
743 regs.h.bh = 0xff;
744 int86(0x10, ®s, ®s);
745 return (regs.h.bh != 0xff);
746 }
747
748 extern int _gppconio_init;
749
750 void
gppconio_init(void)751 gppconio_init(void)
752 {
753 static int oldattrib = -1;
754 if (oldattrib == -1)
755 oldattrib = get_screenattrib();
756 _gettextinfo(&txinfo);
757 if (txinfo.currmode == 7) /* MONO */
758 ScreenAddress = 0xb0000UL;
759 else
760 ScreenAddress = 0xb8000UL;
761 ScreenAttrib = txinfo.normattr = txinfo.attribute = oldattrib;
762 _gppconio_init = 1;
763 }
764
765 #endif /* __DJGPP__ < 2 */
766