#include "mint.h" #include "fasttext.h" #ifdef FASTTEXT #ifdef __GNUC__ #define INLINE inline #define ITYPE long /* gcc's optimizer likes 32 bit integers */ #else #define INLINE #define ITYPE int #endif #define CONDEV (2) static SCREEN *current; static void paint P_((SCREEN *, int, char *)), paint8c P_((SCREEN *, int, char *)), paint16m P_((SCREEN *, int, char *)); INLINE static void curs_off P_((SCREEN *)), curs_on P_((SCREEN *)); INLINE static void flash P_((SCREEN *)); static void normal_putch P_((SCREEN *, int)); static void escy_putch P_((SCREEN *, int)); static char *chartab[256]; static long scrnsize; short hardscroll; static char *hardbase, *oldbase; typedef void (*Vfunc) P_((SCREEN *, int)); #define base *((char **)0x44eL) #define escy1 *((short *)0x4acL) static Vfunc state; static short hardline; static void (*vpaint) P_((SCREEN *, int, char *)); void init P_((void)); void hardware_scroll P_((SCREEN *)); INLINE static char *PLACE P_((SCREEN *, int, int)); INLINE static void gotoxy P_((SCREEN *, int, int)); INLINE static void clrline P_((SCREEN *, int)); INLINE static void clear P_((SCREEN *)); INLINE static void clrchar P_((SCREEN *, int, int)); INLINE static void clrfrom P_((SCREEN *, int, int, int, int)); INLINE static void delete_line P_((SCREEN *, int)); INLINE static void insert_line P_((SCREEN *, int)); static void setbgcol P_((SCREEN *, int)); static void setfgcol P_((SCREEN *, int)); static void putesc P_((SCREEN *, int)); static void escy1_putch P_((SCREEN *, int)); INLINE static void put_ch P_((SCREEN *, int)); /* routines for flashing the cursor for screen v */ /* flash(v): invert the character currently under the cursor */ INLINE static void flash(v) SCREEN *v; { char *place, d; ITYPE i, j, vplanes; vplanes = v->planes + v->planes; place = v->cursaddr; for (j = v->cheight; j > 0; --j) { d = (*place) ^ 0xff; for (i = 0; i < vplanes; i+=2) place[i] = d; place += v->planesiz; } } /* make sure the cursor is off */ INLINE static void curs_off(v) SCREEN *v; { if (v->flags & CURS_ON) { if (v->flags & CURS_FSTATE) { flash(v); v->flags &= ~CURS_FSTATE; } } } /* OK, show the cursor again (if appropriate) */ INLINE static void curs_on(v) SCREEN *v; { if (v->hidecnt) return; if (v->flags & CURS_ON) { /* if the cursor is flashing, we cheat a little and leave it off * to be turned on again (if necessary) by the VBL routine */ if (v->flags & CURS_FLASH) { v->curstimer = 2; return; } if (!(v->flags & CURS_FSTATE)) { v->flags |= CURS_FSTATE; flash(v); } } } void init() { SCREEN *v; int i, j; char *data, *foo; static char chardata[256*16]; foo = lineA0(); v = (SCREEN *)(foo - 346); /* Ehem... The screen might be bigger than 32767 bytes. Let's do some casting... Erling */ scrnsize = (v->maxy+1)*(long)v->linelen; if (hardscroll > 0) { if (!hardbase) hardbase = (char *)(((long)kcore(SCNSIZE(v)+256L)+255L) & 0xffffff00L); if (hardbase == 0) { ALERT("Insufficient memory for hardware scrolling!"); } else { quickmove(hardbase, base, scrnsize); v->cursaddr = v->cursaddr + (hardbase - base); oldbase = base; base = hardbase; Setscreen(hardbase, hardbase, -1); } } hardline = 0; if (v->cheight == 8 && v->planes == 2) { foo = &chardata[0]; vpaint = paint8c; for (i = 0; i < 256; i++) { chartab[i] = foo; data = v->fontdata + i; for (j = 0; j < 8; j++) { *foo++ = *data; data += v->form_width; } } } else if (v->cheight == 16 && v->planes == 1) { foo = &chardata[0]; vpaint = paint16m; for (i = 0; i < 256; i++) { chartab[i] = foo; data = v->fontdata + i; for (j = 0; j < 16; j++) { *foo++ = *data; data += v->form_width; } } } else vpaint = paint; if (v->hidecnt == 0) { /* * make sure the cursor is set up correctly and turned on */ (void)Cursconf(0,0); /* turn cursor off */ v->flags &= ~(CURS_FLASH|CURS_FSTATE); /* now turn the cursor on the way we like it */ v->hidecnt = 0; curs_on(v); } else { (void)Cursconf(0,0); v->flags &= ~CURS_ON; v->hidecnt = 1; } current = v; state = normal_putch; } /* * PLACE(v, x, y): the address corresponding to the upper left hand corner of * the character at position (x,y) on screen v */ INLINE static char *PLACE(v, x, y) SCREEN *v; int x, y; { char *place; int i, j; place = base + x; if (y == v->maxy) place += scrnsize - v->linelen; else if (y) /* Yo, the screen might be bigger than 32767 bytes... Do a cast to long. Erling. */ place += (long)y * v->linelen; if ((j = v->planes) > 1) { i = (x & 0xfffe); while (--j > 0) place += i; } return place; } /* * paint(v, c, place): put character 'c' at position 'place' on screen * v. It is assumed that x, y are proper coordinates! * Specialized versions (paint8c and paint16m) of this routine follow; * they assume 8 line high characters, medium res. and 16 line/mono, * respectively. */ static void paint(v, c, place) SCREEN *v; int c; char *place; { char *data, d, doinverse; ITYPE j, planecount; int vplanes; long vform_width, vplanesiz; vplanes = v->planes; data = v->fontdata + c; doinverse = (v->flags & FINVERSE) ? 0xff : 0; vform_width = v->form_width; vplanesiz = v->planesiz; for (j = v->cheight; j > 0; --j) { d = *data ^ doinverse; *place = d; if (vplanes > 1) { /* This should work for an arbitrary number of planes. Erling */ for(planecount=1;planecount 2) { place[4] = place[6] = d; } } #endif } place += vplanesiz; data += vform_width; } } static void paint8c(v, c, place) SCREEN *v; int c; char *place; { char *data; char d, doinverse; long vplanesiz; data = chartab[c]; doinverse = (v->flags & FINVERSE) ? 0xff : 0; vplanesiz = v->planesiz; if (!doinverse) { /* line 1 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; /* line 2 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; /* line 3 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; /* line 4 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; /* line 5 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; /* line 6 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; /* line 7 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; /* line 8 */ d = *data++; *place = d; place[2] = d; place += vplanesiz; } else { /* line 1 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; /* line 2 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; /* line 3 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; /* line 4 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; /* line 5 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; /* line 6 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; /* line 7 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; /* line 8 */ d = *data++ ^ doinverse; *place = d; place[2] = d; place += vplanesiz; } } static void paint16m(v, c, place) SCREEN *v; int c; char *place; { char *data; char d, doinverse; long vplanesiz; data = chartab[c]; doinverse = (v->flags & FINVERSE) ? 0xff : 0; vplanesiz = v->planesiz; if (!doinverse) { /* line 1 */ d = *data++; *place = d; place += vplanesiz; /* line 2 */ d = *data++; *place = d; place += vplanesiz; /* line 3 */ d = *data++; *place = d; place += vplanesiz; /* line 4 */ d = *data++; *place = d; place += vplanesiz; /* line 5 */ d = *data++; *place = d; place += vplanesiz; /* line 6 */ d = *data++; *place = d; place += vplanesiz; /* line 7 */ d = *data++; *place = d; place += vplanesiz; /* line 8 */ d = *data++; *place = d; place += vplanesiz; /* line 9 */ d = *data++; *place = d; place += vplanesiz; /* line 10 */ d = *data++; *place = d; place += vplanesiz; /* line 11 */ d = *data++; *place = d; place += vplanesiz; /* line 12 */ d = *data++; *place = d; place += vplanesiz; /* line 13 */ d = *data++; *place = d; place += vplanesiz; /* line 14 */ d = *data++; *place = d; place += vplanesiz; /* line 15 */ d = *data++; *place = d; place += vplanesiz; /* line 16 */ d = *data++; *place = d; place += vplanesiz; } else { /* line 1 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 2 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 3 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 4 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 5 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 6 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 7 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 8 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 9 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 10 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 11 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 12 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 13 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 14 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 15 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; /* line 16 */ d = *data++ ^ doinverse; *place = d; place += vplanesiz; } } /* * gotoxy (v, x, y): move current cursor address of screen v to (x, y) * makes sure that (x, y) will be legal */ INLINE static void gotoxy(v, x, y) SCREEN *v; int x, y; { if (x > v->maxx) x = v->maxx; else if (x < 0) x = 0; if (y > v->maxy) y = v->maxy; else if (y < 0) y = 0; curs_off(v); v->cx = x; v->cy = y; v->cursaddr = PLACE(v, x, y); } /* * clrline(v, r): clear line r of screen v */ INLINE static void clrline(v, r) SCREEN *v; int r; { long *dst; long nbytes; nbytes = v->linelen; /* Hey, again the screen might be bigger than 32767 bytes. Do another cast... */ dst = (long *)(base + ((long)r * v->linelen)); zero((char *)dst, nbytes); } /* * clear(v): clear the whole screen v */ INLINE static void clear(v) SCREEN *v; { zero(base, scrnsize); } /* * clrchar(v, x, y): clear the (x,y) position on screen v */ INLINE static void clrchar(v, x, y) SCREEN *v; int x, y; { int i, j, vplanes; char *place; vplanes = v->planes + v->planes; place = PLACE(v, x, y); for (j = v->cheight; j > 0; --j) { for (i = 0; i < vplanes; i+=2) place[i] = 0; place += v->planesiz; } } /* * clrfrom(v, x1, y1, x2, y2): clear screen v from position (x1,y1) to * position (x2, y2) inclusive. It is assumed that y2 >= y1. */ INLINE static void clrfrom(v, x1, y1, x2, y2) SCREEN *v; int x1,y1,x2,y2; { int i; for (i = x1; i <= v->maxx; i++) clrchar(v, i, y1); if (y2 > y1) { for (i = 0; i <= x2; i++) clrchar(v, i, y2); for (i = y1+1; i < y2; i++) clrline(v, i); } } /* * scroll a screen in hardware; if we still have hardware scrolling lines left, * just move the physical screen base, otherwise copy the screen back to the * hardware base and start over */ void hardware_scroll(v) SCREEN *v; { ++hardline; if (hardline < hardscroll) { /* just move the screen */ base += v->linelen; v->cursaddr = PLACE(v, v->cx, v->cy); Setscreen(base, base, -1); } else { hardline = 0; quickmove(hardbase, base + v->linelen, scrnsize - v->linelen); base = hardbase; v->cursaddr = PLACE(v, v->cx, v->cy); Setscreen(hardbase, hardbase, -1); } } /* * delete_line(v, r): delete line r of screen v. The screen below this * line is scrolled up, and the bottom line is cleared. */ #define scroll(v) delete_line(v, 0) INLINE static void delete_line(v, r) SCREEN *v; int r; { long *src, *dst, nbytes; if (r == 0) { if (hardbase) { hardware_scroll(v); clrline(v, v->maxy); return; } nbytes = scrnsize - v->linelen; } else nbytes = (long)v->linelen * (v->maxy - r); /* Sheeze, how many times do we really have to cast... Erling. */ dst = (long *)(base + ((long)r * v->linelen)); src = (long *)( ((long)dst) + v->linelen); quickmove(dst, src, nbytes); /* clear the last line */ clrline(v, v->maxy); } /* * insert_line(v, r): scroll all of the screen starting at line r down, * and then clear line r. */ INLINE static void insert_line(v, r) SCREEN *v; int r; { long *src, *dst; int i, limit; limit = v->maxy; for (i = limit-1; i >= r ; --i) { /* move line i to line i+1 */ /* AND do some casting to support big screens. Erling */ src = (long *)(base + ((long)i * v->linelen)); dst = (long *)(base + ((i+1)*(long)v->linelen)); quickmove(dst, src, v->linelen); } /* clear line r */ clrline(v, r); } /* * special states for handling ESC b x and ESC c x. Note that for now, * color is ignored. */ static void setbgcol(v, c) SCREEN *v; int c; { v->bgcol = c & ((1 << v->planes)-1); state = normal_putch; } static void setfgcol(v, c) SCREEN *v; int c; { v->fgcol = c & ((1 << v->planes)-1); state = normal_putch; } /* * putesc(v, c): handle the control sequence ESC c */ static void putesc(v, c) SCREEN *v; int c; { int cx, cy; cx = v->cx; cy = v->cy; switch (c) { case 'A': /* cursor up */ gotoxy(v, cx, cy-1); break; case 'B': /* cursor down */ gotoxy(v, cx, cy+1); break; case 'C': /* cursor right */ gotoxy(v, cx+1, cy); break; case 'D': /* cursor left */ gotoxy(v, cx-1, cy); break; case 'E': /* clear home */ curs_off(v); clear(v); /* fall through... */ case 'H': /* cursor home */ gotoxy(v, 0, 0); break; case 'I': /* cursor up, insert line */ if (cy == 0) { curs_off(v); insert_line(v, 0); } else gotoxy(v, cx, cy-1); break; case 'J': /* clear below cursor */ curs_off(v); clrfrom(v, cx, cy, v->maxx, v->maxy); break; case 'K': /* clear remainder of line */ curs_off(v); clrfrom(v, cx, cy, v->maxx, cy); break; case 'L': /* insert a line */ gotoxy(v, 0, cy); insert_line(v, cy); break; case 'M': /* delete line */ gotoxy(v, 0, cy); delete_line(v, cy); break; case 'Y': state = escy_putch; return; /* YES, this should be 'return' */ case 'b': state = setfgcol; return; case 'c': state = setbgcol; return; case 'd': /* clear to cursor position */ curs_off(v); clrfrom(v, 0, 0, cx, cy); break; case 'e': /* enable cursor */ curs_off(v); v->flags |= CURS_ON; v->hidecnt = 1; /* so --v->hidecnt shows the cursor */ break; case 'f': /* cursor off */ curs_off(v); v->hidecnt++; v->flags &= ~CURS_ON; break; case 'j': /* save cursor position */ v->savex = v->cx; v->savey = v->cy; break; case 'k': /* restore saved position */ gotoxy(v, v->savex, v->savey); break; case 'l': /* clear line */ gotoxy(v, 0, cy); clrline(v, cy); break; case 'o': /* clear from start of line to cursor */ curs_off(v); clrfrom(v, 0, cy, cx, cy); break; case 'p': /* reverse video on */ v->flags |= FINVERSE; break; case 'q': /* reverse video off */ v->flags &= ~FINVERSE; break; case 'v': /* wrap on */ v->flags |= FWRAP; break; case 'w': v->flags &= ~FWRAP; break; } state = normal_putch; } /* * escy1_putch(v, c): for when an ESC Y + char has been seen */ static void escy1_putch(v, c) SCREEN *v; int c; { gotoxy(v, c - ' ', escy1 - ' '); state = normal_putch; } /* * escy_putch(v, c): for when an ESC Y has been seen */ static void escy_putch(v, c) SCREEN *v; int c; { escy1 = c; state = escy1_putch; } /* * normal_putch(v, c): put character 'c' on screen 'v'. This is the default * for when no escape, etc. is active */ static void normal_putch(v, c) SCREEN *v; int c; { /* control characters */ if (c < ' ') { switch (c) { case '\r': gotoxy(v, 0, v->cy); return; case '\n': if (v->cy == v->maxy) { curs_off(v); scroll(v); } else gotoxy(v, v->cx, v->cy+1); return; case '\b': gotoxy(v, v->cx-1, v->cy); return; case '\007': /* BELL */ (void)bconout(CONDEV, 7); return; case '\033': /* ESC */ state = putesc; return; case '\t': gotoxy(v, (v->cx + 8) & ~7, v->cy); return; default: return; } } (*vpaint)(v, c, v->cursaddr); v->flags &= ~CURS_FSTATE; /* we just erased the cursor */ v->cx++; if (v->cx > v->maxx) { if (v->flags & FWRAP) { v->cx = 0; normal_putch(v, '\n'); v->cursaddr = PLACE(v, v->cx, v->cy); } else { v->cx = v->maxx; } } else { #if 0 v->cursaddr = PLACE(v, v->cx, v->cy); #else v->cursaddr++; if ( (v->cx & 1) == 0 && v->planes > 1) { /* new word */ short skipwords = v->planes - 1; v->cursaddr += skipwords+skipwords; } #endif } } INLINE static void put_ch(v, c) SCREEN *v; int c; { (*state)(v, c & 0x00ff); } static long screen_open P_((FILEPTR *f)); static long screen_read P_((FILEPTR *f, char *buf, long nbytes)); static long screen_write P_((FILEPTR *f, const char *buf, long nbytes)); static long screen_lseek P_((FILEPTR *f, long where, int whence)); static long screen_ioctl P_((FILEPTR *f, int mode, void *buf)); static long screen_close P_((FILEPTR *f, int pid)); static long screen_select P_((FILEPTR *f, long p, int mode)); static void screen_unselect P_((FILEPTR *f, long p, int mode)); extern long null_datime P_((FILEPTR *f, short *time, int rwflag)); DEVDRV screen_device = { screen_open, screen_write, screen_read, screen_lseek, screen_ioctl, null_datime, screen_close, screen_select, screen_unselect }; static long screen_open(f) FILEPTR *f; { if (!current) { init(); } else return EACCDN; /* screen in use */ f->flags |= O_TTY; return 0; } static long screen_close(f, pid) FILEPTR *f; int pid; { if (f->links <= 0) { if (hardbase) { quickmove(oldbase, base, scrnsize); base = oldbase; Setscreen(oldbase, oldbase, -1); } current = 0; } return 0; } static long screen_write(f, buf, bytes) FILEPTR *f; const char *buf; long bytes; { SCREEN *v = current; long *r; long ret = 0; int c; (void)checkkeys(); v->hidecnt++; v->flags |= CURS_UPD; /* for TOS 1.0 */ r = (long *)buf; while (bytes > 0) { c = *r++; put_ch(v, c); bytes -= 4; ret+= 4; } --v->hidecnt; v->flags &= ~CURS_UPD; curs_on(v); return ret; } static long screen_read(f, buf, bytes) FILEPTR *f; char *buf; long bytes; { long *r, ret = 0; r = (long *)buf; while (bytes > 0) { if ( (f->flags & O_NDELAY) && !bconstat(CONDEV) ) break; *r++ = bconin(CONDEV) & 0x7fffffff; bytes -= 4; ret += 4; } return ret; } static long screen_lseek(f, where, whence) FILEPTR *f; long where; int whence; { /* terminals always are at position 0 */ return 0; } static long screen_ioctl(f, mode, buf) FILEPTR *f; int mode; void *buf; { long *r = (long *)buf; struct winsize *w; if (mode == FIONREAD) { if (bconstat(CONDEV)) *r = 1; else *r = 0; } else if (mode == FIONWRITE) { *r = 1; } else if (mode == TIOCFLUSH) { /* BUG: this should flush the input/output buffers */ return 0; } else if (mode == TIOCGWINSZ) { w = (struct winsize *)buf; w->ws_row = current->maxy+1; w->ws_col = current->maxx+1; } else return tty_ioctl(f, mode, buf); return 0; } static long screen_select(f, p, mode) FILEPTR *f; long p; int mode; { struct tty *tty = (struct tty *)f->devinfo; int dev = CONDEV; if (mode == O_RDONLY) { if (bconstat(dev)) { return 1; } if (tty) { /* avoid collisions with other processes */ if (!tty->rsel) tty->rsel = p; } return 0; } else if (mode == O_WRONLY) { return 1; } /* default -- we don't know this mode, return 0 */ return 0; } static void screen_unselect(f, p, mode) FILEPTR *f; long p; int mode; { struct tty *tty = (struct tty *)f->devinfo; if (tty) { if (mode == O_RDONLY && tty->rsel == p) tty->rsel = 0; else if (mode == O_WRONLY && tty->wsel == p) tty->wsel = 0; } } #endif /* FASTTEXT */