/* * screen.pc: * IBM PC screen handling * * Copyright (c) 1989 University of Toronto. All rights reserved. * Anyone may use or copy this software, except that it may not be * sold for profit, that this copyright notice remain intact, and that * credit is given where it is due. The University of Toronto and the * author make no warranty and accept no liability for this software. */ static char rcsid[] = "$Header: screen.pc,v 1.8 89/06/19 17:16:54 pkern Exp $"; #include #include "cterm.h" #include "pc.h" #ifdef BGI #include #include #include #endif extern uchar interlace; static union REGS r; static struct SREGS sr; static struct REGPACK rp; static int o_scrn[WIDE * ROWS]; /* screen memory storage */ static int o_xy=0; /* cursor position memory */ static int *scrn; /* screen position memory */ static unsigned int lbuf[WIDE+1]; static int ncols=0, vmode=-1, pgnum=0; /* see init_scr() */ static int o_ncols=0; static unsigned int vaddr=0xb000; /* cursor types */ static unsigned int crsr_blk=0x000c, crsr_ul=0x0b0c; #define sPOS(a, b) ((b)*ncols+(a)) static int *scp; /* general purpose screen pointer */ /* macros for frequently used code */ #define crtint int86(CRT_INT, &r, &r) #define BLANK 0x20 #define NORM 0x07 #define GO_HOME r.h.ah=2; r.h.bh=pgnum; r.x.dx=0; crtint; #define GO_XY(a,b) \ r.h.ah=2; r.h.bh=pgnum; r.h.dh=(b); r.h.dl=(a); crtint; #define CX_POSN *((char far *)(0x00450+(pgnum<<1))) #define CY_POSN *((char far *)(0x00451+(pgnum<<1))) #define CURS_POS *((int far *)(0x00450+(pgnum<<1))) #define CLEAN(a) \ r.x.ax=0x0920; r.x.bx=(pgnum<<8)|c_norm; r.x.cx=(a); crtint; static uchar xattr=NORM; /* attribute storage */ static uchar rattr=0, o_rattr=0; uchar c_norm=NORM, c_rev=NORM, c_bold=NORM, c_ul=1, g_type=0; /* * init_scr: * try to figure out where the display is * and set our internal variables to reflect it. */ init_scr() { #ifdef BGI if (gfx_driver == DETECT) { detectgraph(&gfx_driver, &gfx_mode); switch (gfx_driver) { case CGA: case MCGA: txt_mode = BW80; txt_addr = 0xb800; break; case EGA: case EGA64: case EGAMONO: txt_mode = BW80; txt_addr = 0xa000; break; default: txt_mode = MONO; txt_addr = 0xb000; c_ul = 1; } } textmode(BW80); #else if (vmode < 0) { r.h.al = txt_mode; vaddr = txt_addr; } else r.h.al = vmode; r.h.ah = 0; crtint; scrn = (int far *) MK_FP(vaddr, 0); #endif r.h.ah = 0x0f; /* get video mode */ crtint; ncols = r.h.ah; /* num of cols */ vmode = r.h.al; /* video mode */ pgnum = r.h.bh; /* page number */ r.h.ah = 0x03; /* get cursor mode */ r.h.bh = pgnum; crtint; crsr_blk = (r.h.cl < 7) ? 7 : r.h.cl; crsr_ul = ((r.h.cl-1) << 8) | r.h.cl; segread(&sr); attribs(rattr); return(vmode == txt_mode); } reset_scr() { #ifdef BGI if (gfx_driver > 0) { restorecrtmode(); closegraph(); } gfx_driver = DETECT; #endif } curs_xy(x, y) uchar x, y; { GO_XY(x-1, y-1) } xypos(xp, yp) uchar *xp, *yp; { /* r.x.dx = CURS_POS; */ r.h.ah = 3; r.h.bh = pgnum; crtint; *xp = r.h.dl+1; *yp = r.h.dh+1; } columns(n) int n; { if (!avm_mode) /* can't do 132 columns */ return(ncols); if (n > 80) { vmode = avm_mode; vaddr = avm_addr; } else { vmode = txt_mode; vaddr = txt_addr; } init_scr(); if (o_ncols) o_xy = o_ncols = 0; return(ncols); } bkgnd(sw) int sw; { /* nothing yet */ } /* scroll up n lines between min_y and max_y */ scrl_up(n, min_y, max_y, x, y) uchar n, min_y, max_y, x, y; { if (n > max_y-min_y+1) n = max_y-min_y+1; r.h.ah = 6; r.h.al = n; r.h.bh = c_norm; r.h.cl = 0; r.h.ch = min_y-1; r.h.dl = ncols-1; r.h.dh = max_y-1; crtint; } /* scroll down n lines between min_y and max_y */ scrl_down(n, min_y, max_y, x, y) uchar n, min_y, max_y, x, y; { if (n > max_y-min_y+1) n = max_y-min_y+1; r.h.ah = 7; r.h.al = n; r.h.bh = c_norm; r.h.cl = 0; r.h.ch = min_y-1; r.h.dl = ncols-1; r.h.dh = max_y-1; crtint; } /* eol : erase to end of line */ eol_erase(x, y) uchar x, y; { if (x > ncols) return; CLEAN(ncols-(x-1)) } /* * bol : erase from beginning of line to cursor pos. * ie. goto head of line, erase chars, return to original pos */ bol_erase(x, y) uchar x, y; { GO_XY(0, y-1) CLEAN(x) GO_XY(x-1, y-1) } /* eos : erase to end of line and to end of screen */ eos_erase(x, y) uchar x, y; { CLEAN((ROWS-y)*ncols + ncols-x+1) } /* * bos : erase from beginning of screen (home) to cursor pos. */ bos_erase(x, y) uchar x, y; { GO_HOME CLEAN(ncols*(y-1)+x) GO_XY(x-1, y-1) } /* goto head of line, erase line, return to original pos */ line_erase(x, y) uchar x, y; { GO_XY(0, y-1) CLEAN(ncols) GO_XY(x-1, y-1) } /* erase entire screen (not including status line) */ all_erase(x, y) uchar x, y; { if (interlace) return; GO_HOME CLEAN(ncols*ROWS) GO_XY(x-1, y-1) } /* * insert line(s) : * scroll down n lines, goto head of original line, erase empty space */ line_ins(n, x, y, max_y) uchar n, x, y, max_y; { scrl_down(n, y, max_y, x, y); GO_XY(0, y-1) } /* * delete line(s) : * scroll up n lines, goto new empty line, erase lines, * return to head of original line */ line_del(n, x, y, max_y) uchar n, x, y, max_y; { scrl_up(n, y, max_y, x, y); GO_XY(0, y-1) } /* * delete char(s) : * ie. copy rest of line to the left, goto line-end, erase n chars * and return to original cursor pos */ char_del(n, x, y) uchar n, x, y; { int *p; register uchar yn = y-1; if (x > ncols) return; if (x+n-1 > ncols) n = ncols-x+1; #ifdef BGI movetext(x+n,y, ncols,y, x,y); #else scp = &scrn[sPOS(x-1, yn)]; p = &scrn[sPOS(x+n-1, yn)]; movmem(p, scp, (ncols-(x+n-1)) * 2); /* scp = &scrn[sPOS(ncols-n, yn)]; memset(scp, '\0', n * 2); */ #endif GO_XY(ncols-n, yn) CLEAN(n) GO_XY(x-1, y-1) } /* * insert char(s) : * ie. copy rest of line to right and clean out new space(s) */ char_ins(n, x, y) uchar n, x, y; { int *p; register uchar yn = y-1; if (x > ncols) return; if (x+n-1 > ncols) n = ncols-x+1; #ifdef BGI movetext(x,y, ncols-n,y, x+n,y); #else scp = &scrn[sPOS(x-1, yn)]; p = &scrn[sPOS(x+n-1, yn)]; movmem(scp, p, (ncols-(x+n-1)) * 2); /* scp = &scrn[sPOS(x-1, yn)]; memset(scp, '\0', n * 2); */ #endif GO_XY(x-1, yn) CLEAN(n) GO_XY(x-1, y-1) } /* * set attributes */ attribs(attr) uchar attr; { rattr = attr; xattr = c_norm; if (attr & AT_UL) xattr = c_ul; if (attr & AT_BOLD) xattr |= 0x08; if (attr & AT_BLINK) xattr |= 0x80; if (attr & AT_REV) xattr = (xattr & 0x88) | (c_rev << 4); } /* * screen alignment test : * home cursor and write a screen of E's in normal attrib mode. * previous attribs are not forgotten. */ e_screen() { GO_HOME r.h.ah = 0x09; r.h.al = 'E'; r.h.bh = pgnum; r.h.bl = c_norm; r.x.cx = ncols * ROWS; crtint; /* screen of E's */ } /* basic single-char output */ burpc(c, x, y) int c, x, y; { r.h.ah = 0x09; r.h.al = c; r.h.bh = pgnum; r.h.bl = xattr; r.x.cx = 1; crtint; GO_XY(x, y-1) } /* sound bell/horn/whistle/whatever */ beep(x, y) int x, y; { /* r.x.ax = 0x0e07; r.x.bx = (pgnum << 8); crtint; */ /* honk(0x533, 64); /* normal 1000 Hz bell */ /* mildly fancy honk(0x333, 32); honk(0x533, 32); honk(0x733, 32); */ #ifdef PLAY /* getting fancier ... */ play("g3.24,d3.24,g3.24,d3.24,g3.24"); #else /* default */ honk(2915, 64); /* middle C (approx. 440 Hz) */ #endif } /* insert mode burpc() */ /* move line right, deposit char */ insurpc(c, x, y) int c, x, y; { char_ins(1, x, y); burpc(c, x, y); } /* output string at x,y with attr */ burps(s, x, y, at) char *s; uchar x, y, at; { int c; uchar xn = x, yn = y; #ifdef BGI GO_XY(x-1, y-1) yn = xattr; attribs(at); c = xattr << 8; xattr = yn; if (!s[1]) { r.h.ah = 0x09; r.h.al = *s; r.h.bh = pgnum; r.h.bl = c >> 8; r.x.cx = 1; crtint; return; } xn = 0; while (*s) lbuf[xn++] = c | *s++; puttext(x, y, x+xn-1, y, lbuf); #else c = xattr; attribs(at); GO_XY(xn-1, yn-1) while (*s) { r.h.ah = 0x09; r.h.al = *s++; r.h.bh = pgnum; r.h.bl = xattr; r.x.cx = 1; crtint; GO_XY(xn++, yn-1) if (xn == ncols) { xn = 1; if (yn < ROWS) yn++; } } GO_XY(x-1, y-1) xattr = c; #endif /* BGI */ } /* set cursor type */ curs_type(sw) uchar sw; { r.h.ah = 1; r.x.cx = (sw) ? crsr_blk : crsr_ul; if (sw & 0x80) r.h.ch = 0x20 | (r.h.ch & 0x1f); crtint; } /* save screen image & cursor position */ save_scr() { register int n; register int *os, *s; if (interlace) return; r.h.ah = 3; r.h.bh = pgnum; crtint; o_xy = r.x.dx; n = ncols * ROWS; #ifdef BGI gettext(1,1, ncols,ROWS, o_scrn); #else s = scrn; os = o_scrn; /* while (n--) *os++ = *s++; */ movmem(s, os, n * 2); #endif o_ncols = ncols; o_rattr = rattr; } /* restore screen image & cursor position */ restore_scr() { register int n; register int *os, *s; if (interlace) return; n = ncols * ROWS; GO_HOME CLEAN(n) if (o_ncols) { #ifdef BGI puttext(1,1, ncols,ROWS, o_scrn); #else s = scrn; os = o_scrn; /* while (n--) *s++ = *os++; */ movmem(os, s, n * 2); #endif } attribs(o_rattr); o_rattr = 0; r.h.ah = 2; r.h.bh = pgnum; r.x.dx = o_xy; crtint; o_ncols = 0; } /* save text in line lnum from o_scrn to buf */ scgets(buf, siz, lnum) char buf[]; int siz, lnum; { char c; int i, j, n; if (lnum > ROWS) return(-1); j = sPOS(0, lnum-1); /* first column */ n = sPOS(o_ncols-1, lnum-1); /* last column */ /* find last non-blank char in line */ for (; j < n; n--) if ((c = o_scrn[n] & 0x7f) != NUL && c != ' ') { n++; break; } /* save text to buf */ for (i=0; i < siz && j < n; i++, j++) buf[i] = o_scrn[j] & 0x7f; buf[i] = NUL; return(i); } /* write string to status line */ uchar c_stat = 3; sturp(s, x, y) char *s; int x, y; { int i=0, c; if (interlace) return; #ifndef VIDMEM if (!c_stat) { register char *p = s; while (*p) *p++ = ' '; burps(s, 1, ROWS+1, 0); } else { c = c_rev; c_rev = c_stat; i = rattr; burps(s, 1, ROWS+1, AT_REV); c_rev = c; rattr = i; } #else c = (c_stat) ? (c_stat << 4) : 0; /* reverse video or blank */ scp = &scrn[sPOS(0, ROWS)]; r.h.dh = c; while (*s && i++ < ncols) { r.h.dl = *s++; *scp++ = r.x.dx; } r.h.dl = ' '; while (i++ < ncols) *scp++ = r.x.dx; #endif GO_XY(x-1, y-1) } /* delay for n milliseconds */ dsleep(n) unsigned int n; { #ifdef notdef /* #ifdef PCAT */ unsigned long m; m = n * 1000; r.h.ah = 86; r.x.cx = m >> 16; r.x.dx = m & 0xffff; int86(0x15, &r, &r); #else unsigned int x; while(n--) for (x=0x400; x; x--) ; /* ... just a guess */ #endif } /* clear screen & home cursor */ clr_home() { GO_HOME if (interlace) return; CLEAN(ncols * (ROWS+1)) } /* dburp -- put debug message on status line */ dburp(s) char *s; { uchar x, y; xypos(&x, &y); sturp(s, x, y); } /* * honk: sound the horn */ #define TimerCtl 0x43 #define Timer2Cnt 0x42 #define BPort 0x61 honk(tdiv, n) unsigned int tdiv, n; { unsigned char bsav; register unsigned int i; register unsigned char c, x; c = tdiv >> 8; x = tdiv & 0xff; outportb(TimerCtl, 0xb6); /* select timer 2 */ outportb(Timer2Cnt, x); /* timer 2 counter lsb */ outportb(Timer2Cnt, c); /* timer 2 counter msb */ c = inportb(BPort); /* save 8255 port b setting */ bsav = c; c |= 3; outportb(BPort, c); /* turn speaker on */ while (n--) for(x=1; x; x++) ; outportb(BPort, bsav); /* restore 8255 port */ } #ifdef PLAY #define sqrt_2 1.059463994 /* 12th root of 2 */ /* * 12 notes : c d# d e# e f g# g a# a b# b * per octave : 0 1 2 3 4 5 6 7 8 9 10 11 */ static int notes[7] = { 9, 11, 0, 2, 4, 5, 7}; /* a b c d e f g */ play(p) char *p; { int note, octv, tlen; float freq; long clk; tlen = 10; /* expecting: n[#]o.l{,n[#]o.l} * n == note [a-g], * [#] == optional sharp, * o == octave [0-..], * l == duration [1-..] */ while (*p) { freq = 55.0; /* low "C" == 55 Hz */ clk = 1283000; /* PC/AT speaker clock speed */ octv = 0; note = notes[*p - 'a']; if (*++p == '#') { p++; if (note) note--; else { /* c# */ octv = 1; note = notes['g']; } } octv += atoi(p); while (*++p != '.') ; tlen = atoi(++p); while (*p && *p++ != ',') ; for (; octv; octv--) freq *= 2; for (; note; note--) freq *= sqrt_2; note = clk/freq; honk(note, tlen); } } #endif /* PLAY */ /* IBM PC graphics nitty-gritty */ #ifdef BGI #include "scr_gfx.bgi" #else /* for now, use bios calls until someone can * tell me how to handle graphics directly */ #ifndef BIOS #define BIOS 1 #endif #include "scr_gfx.h" #endif /* BGI */