/* $NetBSD: ite.c,v 1.16 1995/07/17 01:24:34 briggs Exp $ */ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah $Hdr: ite.c 1.28 92/12/20$ * * @(#)ite.c 8.2 (Berkeley) 1/12/94 */ /* * ite.c * * The ite module handles the system console; that is, stuff printed * by the kernel and by user programs while "desktop" and X aren't * running. Some (very small) parts are based on hp300's 4.4 ite.c, * hence the above copyright. * * -- Brad and Lawrence, June 26th, 1994 * */ #include #include #include #include #include #include #include #include #include #include "../mac68k/via.h" #include #include #define KEYBOARD_ARRAY #include #include #include #include "6x10.h" #define CHARWIDTH 6 #define CHARHEIGHT 10 #define dprintf if (0) printf #define ATTR_NONE 0 #define ATTR_BOLD 1 #define ATTR_UNDER 2 #define ATTR_REVERSE 4 enum vt100state_e { ESnormal, /* Nothing yet */ ESesc, /* Got ESC */ ESsquare, /* Got ESC [ */ ESgetpars, /* About to get or getting the parameters */ ESgotpars, /* Finished getting the parameters */ ESfunckey, /* Function key */ EShash, /* DEC-specific stuff (screen align, etc.) */ ESsetG0, /* Specify the G0 character set */ ESsetG1, /* Specify the G1 character set */ ESignore /* Ignore this sequence */ } vt100state = ESnormal; /* Received from MacBSDBoot, stored by Locore: */ long videoaddr; long videorowbytes; long videobitdepth; unsigned long videosize; /* Calculated in itecninit(): */ static int width, height, scrrows, scrcols; static void (*putpixel) (int x, int y, int *c, int num); static void (*reversepixel) (int x, int y, int num); /* VT100 state: */ #define MAXPARS 16 static int x = 0, y = 0, savex, savey; static int par[MAXPARS], numpars, hanging_cursor, attr; /* Our tty: */ struct tty *ite_tty; /* For polled ADB mode: */ static int polledkey; extern int adb_polling; /* Misc */ void itestart(); static void ite_putchar(char ch); /* VT100 tab stops & scroll region */ static char tab_stops[255]; static int scrreg_top, scrreg_bottom; /* * Bitmap handling functions */ static inline void putpixel1(int xx, int yy, int *c, int num) { unsigned int i, mask; unsigned char *sc; sc = (unsigned char *) videoaddr; i = 7 - (xx & 7); mask = ~(1 << i); sc += yy * videorowbytes + (xx >> 3); while (num--) { *sc &= mask; *sc |= (*c++ & 1) << i; sc += videorowbytes; } } static void putpixel2(int xx, int yy, int *c, int num) { unsigned int i, mask; unsigned char *sc; sc = (unsigned char *) videoaddr; i = 6 - ((xx & 3) << 1); mask = ~(3 << i); sc += yy * videorowbytes + (xx >> 2); while (num--) { *sc &= mask; *sc |= (*c++ & 3) << i; sc += videorowbytes; } } static void putpixel4(int xx, int yy, int *c, int num) { unsigned int i, mask; unsigned char *sc; sc = (unsigned char *) videoaddr; i = 4 - ((xx & 1) << 2); mask = ~(15 << i); sc += yy * videorowbytes + (xx >> 1); while (num--) { *sc &= mask; *sc |= (*c++ & 15) << i; sc += videorowbytes; } } static void putpixel8(int xx, int yy, int *c, int num) { unsigned char *sc; sc = (unsigned char *) videoaddr; sc += yy * videorowbytes + xx; while (num--) { *sc = *c++ & 0xff; sc += videorowbytes; } } static void reversepixel1(int xx, int yy, int num) { unsigned int mask; unsigned char *sc; sc = (unsigned char *) videoaddr; mask = 0; /* Get rid of warning from compiler */ switch (videobitdepth) { case 1: mask = 1 << (7 - (xx & 7)); sc += yy * videorowbytes + (xx >> 3); break; case 2: mask = 3 << (6 - ((xx & 3) << 1)); sc += yy * videorowbytes + (xx >> 2); break; case 4: mask = 15 << (4 - ((xx & 1) << 2)); sc += yy * videorowbytes + (xx >> 1); break; case 8: mask = 255; sc += yy * videorowbytes + xx; break; default: panic("reversepixel(): unsupported bit depth"); } while (num--) { *sc ^= mask; sc += videorowbytes; } } static void writechar(char ch, int x, int y, int attr) { int i, j, mask, rev, col[CHARHEIGHT]; unsigned char *c; ch &= 0x7F; x *= CHARWIDTH; y *= CHARHEIGHT; rev = (attr & ATTR_REVERSE) ? 255 : 0; c = &Font6x10[ch * CHARHEIGHT]; switch (videobitdepth) { case 1: for (j = 0; j < CHARWIDTH; j++) { mask = 1 << (CHARWIDTH - 1 - j); for (i = 0; i < CHARHEIGHT; i++) { col[i] = ((c[i] & mask) ? 255 : 0) ^ rev; } putpixel1(x + j, y, col, CHARHEIGHT); } if (attr & ATTR_UNDER) { col[0] = 255; for (j = 0; j < CHARWIDTH; j++) { putpixel1(x + j, y + CHARHEIGHT - 1, col, 1); } } break; case 2: case 4: case 8: for (j = 0; j < CHARWIDTH; j++) { mask = 1 << (CHARWIDTH - 1 - j); for (i = 0; i < CHARHEIGHT; i++) { col[i] = ((c[i] & mask) ? 255 : 0) ^ rev; } putpixel(x + j, y, col, CHARHEIGHT); } if (attr & ATTR_UNDER) { col[0] = 255; for (j = 0; j < CHARWIDTH; j++) { putpixel(x + j, y + CHARHEIGHT - 1, col, 1); } } break; } } static void drawcursor(void) { int j, X, Y; X = x * CHARWIDTH; Y = y * CHARHEIGHT; for (j = 0; j < CHARWIDTH; j++) { reversepixel(X + j, Y, CHARHEIGHT); } } static void erasecursor(void) { int j, X, Y; X = x * CHARWIDTH; Y = y * CHARHEIGHT; for (j = 0; j < CHARWIDTH; j++) { reversepixel(X + j, Y, CHARHEIGHT); } } static void scrollup(void) { unsigned long *from, *to; int i, linelongs, tocopy, copying; linelongs = videorowbytes * CHARHEIGHT / 4; to = (unsigned long *) videoaddr + ((scrreg_top-1) * linelongs); from = to + linelongs; tocopy = (scrreg_bottom - scrreg_top) * linelongs; while (tocopy > 0) { copying = (tocopy > 16383) ? 16383 : tocopy; bcopy(from, to, copying * 4); from += copying; to += copying; tocopy -= copying; } to = (unsigned long *) videoaddr; bzero(to + (scrreg_bottom - 1) * linelongs, linelongs * sizeof(long)); } static void scrolldown(void) { unsigned long *from, *to; int i, linelongs; linelongs = videorowbytes * CHARHEIGHT / 4; to = (unsigned long *) videoaddr + linelongs * (scrreg_bottom); from = to - linelongs; for (i = (scrreg_bottom - scrreg_top) * linelongs; i > 0; i--) { *--to = *--from; } for (i = linelongs; i > 0; i--) { *--to = 0; } } static void clear_screen(int which) { unsigned long *p; int i, linelongs; p = (unsigned long *) videoaddr; linelongs = videorowbytes * CHARHEIGHT / 4; switch (which) { case 0: /* To end of screen */ p += y * linelongs; i = (scrrows - y) * linelongs; break; case 1: /* To start of screen */ i = y * linelongs; break; case 2: /* Whole screen */ i = scrrows * linelongs; break; } bzero(p, i * sizeof(long)); } static void clear_line(int which) { int start, end, i; /* * This routine runs extremely slowly. I don't think it's * used all that often, except for To end of line. I'll go * back and speed this up when I speed up the whole ite * module. --LK */ switch (which) { case 0: /* To end of line */ start = x; end = scrcols; break; case 1: /* To start of line */ start = 0; end = x; break; case 2: /* Whole line */ start = 0; end = scrcols; break; } for (i = start; i < end; i++) { writechar(' ', i, y, ATTR_NONE); } } static void reset_tabs(void) { int i; for (i = 0; i<= scrcols; i++) { tab_stops[i] = ((i % 8) == 0); } } static void vt100_reset(void) { reset_tabs; scrreg_top = 1; scrreg_bottom = scrrows; attr = ATTR_NONE; } static void putc_normal(char ch) { switch (ch) { case '\a': /* Beep */ asc_ringbell(); break; case 127: /* Delete */ case '\b': /* Backspace */ if (hanging_cursor) { hanging_cursor = 0; } else if (x > 0) { x--; } break; case '\t': /* Tab */ do { ite_putchar(' '); } while (tab_stops[x] = 0); break; case '\n': /* Line feed */ if (y == scrreg_bottom - 1) { scrollup(); } else { y++; } break; case '\r': /* Carriage return */ x = 0; hanging_cursor = 0; break; case '\e': /* Escape */ vt100state = ESesc; hanging_cursor = 0; break; default: if (ch >= ' ') { if (hanging_cursor) { x = 0; if (y == scrreg_bottom - 1) { scrollup(); } else { y++; } hanging_cursor = 0; } writechar(ch, x, y, attr); if (x == scrcols - 1) { hanging_cursor = 1; } else { x++; } if (x >= scrcols) { /* can we ever get here? */ x = 0; y++; } } break; } } static void putc_esc(char ch) { vt100state = ESnormal; switch (ch) { case '[': vt100state = ESsquare; break; case 'D': /* Line feed */ if (y == scrreg_bottom - 1) { scrollup(); } else { y++; } break; case 'H': /* Set tab stop */ tab_stops[x] = 1; break; case 'M': /* Cursor up */ if (y == scrreg_top - 1) { scrolldown(); } else { y--; } break; case '>': vt100_reset(); break; case '7': /* Save cursor */ savex = x; savey = y; break; case '8': /* Restore cursor */ x = savex; y = savey; break; default: /* Rest not supported */ break; } } static void putc_gotpars(char ch) { int i; vt100state = ESnormal; switch (ch) { case 'A': /* Up */ i = par[0]; do { if (y == scrreg_top - 1) { scrolldown(); } else { y--; }; i--; } while (i > 0); break; case 'B': /* Down */ i = par[0]; do { if (y == scrreg_bottom - 1) { scrollup(); } else { y++; }; i--; } while (i > 0); break; case 'C': /* Right */ x+= par[0] ? par[0] : 1; break; case 'D': /* Left */ x-= par[0] ? par[0] : 1; break; case 'H': /* Set cursor position */ x = par[1] - 1; y = par[0] - 1; hanging_cursor = 0; break; case 'J': /* Clear part of screen */ clear_screen(par[0]); break; case 'K': /* Clear part of line */ clear_line(par[0]); break; case 'g': /* Clear tab stops */ if (numpars >= 1 && par[0] == 3) { reset_tabs(); } break; case 'm': /* Set attribute */ for (i = 0; i < numpars; i++) { switch (par[i]) { case 0: attr = ATTR_NONE; break; case 1: attr |= ATTR_BOLD; break; case 4: attr |= ATTR_UNDER; break; case 7: attr |= ATTR_REVERSE; break; } } break; case 'r': /* Set scroll region */ /* ensure top < bottom, and both within limits */ if ((numpars > 0) && (par[0] < scrrows)) { scrreg_top = par[0]; } else { scrreg_top = 1; } if ((numpars > 1) && (par[1] <= scrrows) && (par[1] > par[0])) { scrreg_bottom = par[1]; } else { scrreg_bottom = scrrows; } break; } } static void putc_getpars(char ch) { if (ch == '?') { /* Not supported */ return; } if (ch == '[') { vt100state = ESnormal; /* Not supported */ return; } if (ch == ';' && numpars < MAXPARS - 1) { numpars++; } else if (ch >= '0' && ch <= '9') { par[numpars] *= 10; par[numpars] += ch - '0'; } else { numpars++; vt100state = ESgotpars; putc_gotpars(ch); } } static void putc_square(char ch) { int i; for (i = 0; i < MAXPARS; i++) { par[i] = 0; } numpars = 0; vt100state = ESgetpars; putc_getpars(ch); } static void ite_putchar(char ch) { switch (vt100state) { default:vt100state = ESnormal; /* FALLTHROUGH */ case ESnormal: putc_normal(ch); break; case ESesc: putc_esc(ch); break; case ESsquare: putc_square(ch); break; case ESgetpars: putc_getpars(ch); break; case ESgotpars: putc_gotpars(ch); break; } if (x >= scrcols) { x = scrcols - 1; } if (x < 0) { x = 0; } if (y >= scrrows) { y = scrrows - 1; } if (y < 0) { y = 0; } } /* * Keyboard support functions */ static int ite_dopollkey(int key) { polledkey = key; return 0; } static int ite_pollforchar(void) { int s; register int intbits; s = splhigh(); polledkey = -1; adb_polling = 1; /* pretend we're VIA interrupt dispatcher */ while (polledkey == -1) { intbits = via_reg(VIA1, vIFR); if (intbits & V1IF_ADBRDY) { mrg_adbintr(); via_reg(VIA1, vIFR) = V1IF_ADBRDY; } if (intbits & 0x10) { mrg_pmintr(); via_reg(VIA1, vIFR) = 0x10; } } adb_polling = 0; splx(s); return polledkey; } /* * Tty handling functions */ static void iteattach(struct device * parent, struct device * dev, void *aux) { printf(" (minimal console)\n"); } extern int matchbyname(); struct cfdriver itecd = { NULL, "ite", matchbyname, iteattach, DV_TTY, sizeof(struct device) }; int iteopen(dev_t dev, int mode, int devtype, struct proc * p) { register struct tty *tp; register int error; int first = 0; dprintf("iteopen(): enter(0x%x)\n", (int) dev); if (ite_tty == NULL) tp = ite_tty = ttymalloc(); else tp = ite_tty; if ((tp->t_state & (TS_ISOPEN | TS_XCLUDE)) == (TS_ISOPEN | TS_XCLUDE) && p->p_ucred->cr_uid != 0) return (EBUSY); tp->t_oproc = itestart; tp->t_param = NULL; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = CS8 | CREAD; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; tp->t_state = TS_ISOPEN | TS_CARR_ON; ttsetwater(tp); } error = (*linesw[tp->t_line].l_open) (dev, tp); tp->t_winsize.ws_row = scrrows; tp->t_winsize.ws_col = scrcols; dprintf("iteopen(): exit(%d)\n", error); return (error); } int iteclose(dev_t dev, int flag, int mode, struct proc * p) { dprintf("iteclose: enter (%d)\n", (int) dev); (*linesw[ite_tty->t_line].l_close) (ite_tty, flag); ttyclose(ite_tty); #if 0 ttyfree(ite_tty); ite_tty = (struct tty *) 0; #endif dprintf("iteclose: exit\n"); return 0; } int iteread(dev_t dev, struct uio * uio, int flag) { dprintf("iteread: enter\n"); return (*linesw[ite_tty->t_line].l_read) (ite_tty, uio, flag); dprintf("iteread: exit\n"); } int itewrite(dev_t dev, struct uio * uio, int flag) { dprintf("itewrite: enter\n"); return (*linesw[ite_tty->t_line].l_write) (ite_tty, uio, flag); dprintf("itewrite: exit\n"); } struct tty * itetty(dev) dev_t dev; { return (ite_tty); } int iteioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc * p) { register struct tty *tp = ite_tty; int error; dprintf("iteioctl: enter(%d, 0x%x)\n", cmd, cmd); error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, addr, flag, p); if (error >= 0) { dprintf("iteioctl: exit(%d)\n", error); return (error); } error = ttioctl(tp, cmd, addr, flag, p); if (error >= 0) { dprintf("iteioctl: exit(%d)\n", error); return (error); } switch (cmd) { case ITEIOC_RINGBELL:{ asc_ringbell(); return (0); } case ITEIOC_SETBELL:{ struct bellparams *bp = (void *) addr; asc_setbellparams(bp->freq, bp->len, bp->vol); return (0); } case ITEIOC_GETBELL:{ struct bellparams *bp = (void *) addr; asc_getbellparams(&bp->freq, &bp->len, &bp->vol); return (0); } } dprintf("iteioctl: exit(ENOTTY)\n"); return (ENOTTY); } void itestart(register struct tty * tp) { register int cc, s; int hiwat = 0, hadcursor = 0; s = spltty(); if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { splx(s); return; } tp->t_state |= TS_BUSY; cc = tp->t_outq.c_cc; splx(s); erasecursor(); while (cc-- > 0) { ite_putchar(getc(&tp->t_outq)); } drawcursor(); s = spltty(); tp->t_state &= ~TS_BUSY; splx(s); } void itestop(struct tty * tp, int flag) { int s; s = spltty(); if (tp->t_state & TS_BUSY) { if ((tp->t_state & TS_TTSTOP) == 0) { tp->t_state |= TS_FLUSH; } } splx(s); } int ite_intr(adb_event_t * event) { static int shift = 0, control = 0; int key, press, val, state; char str[10], *s; key = event->u.k.key; press = ADBK_PRESS(key); val = ADBK_KEYVAL(key); if (val == ADBK_SHIFT) { shift = press; } else if (val == ADBK_CONTROL) { control = press; } else if (press) { switch (val) { case ADBK_UP: str[0] = '\e'; str[1] = 'O'; str[2] = 'A'; str[3] = '\0'; break; case ADBK_DOWN: str[0] = '\e'; str[1] = 'O'; str[2] = 'B'; str[3] = '\0'; break; case ADBK_RIGHT: str[0] = '\e'; str[1] = 'O'; str[2] = 'C'; str[3] = '\0'; break; case ADBK_LEFT: str[0] = '\e'; str[1] = 'O'; str[2] = 'D'; str[3] = '\0'; break; default: state = 0; if (shift) { state = 1; } if (control) { state = 2; } str[0] = keyboard[val][state]; str[1] = '\0'; break; } if (adb_polling) { polledkey = str[0]; } else { for (s = str; *s; s++) { (*linesw[ite_tty->t_line].l_rint) (*s, ite_tty); } } } } /* * Console functions */ int itecnprobe(struct consdev * cp) { int maj, unit; /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) { if (cdevsw[maj].d_open == iteopen) { break; } } if (maj == nchrdev) { panic("itecnprobe(): did not find iteopen()."); } unit = 0; /* hardcode first device as console. */ /* initialize required fields */ cp->cn_dev = makedev(maj, unit); cp->cn_pri = CN_INTERNAL; } int itecninit(struct consdev * cp) { width = videosize & 0xffff; height = (videosize >> 16) & 0xffff; scrrows = height / CHARHEIGHT; scrcols = width / CHARWIDTH; vt100_reset(); switch (videobitdepth) { default: case 1: putpixel = putpixel2; reversepixel = reversepixel1; break; case 2: putpixel = putpixel2; reversepixel = reversepixel1; break; case 4: putpixel = putpixel4; reversepixel = reversepixel1; break; case 8: putpixel = putpixel8; reversepixel = reversepixel1; break; } iteon(cp->cn_dev, 0); } int iteon(dev_t dev, int flags) { erasecursor(); clear_screen(2); drawcursor(); } int iteoff(dev_t dev, int flags) { erasecursor(); clear_screen(2); } int itecngetc(dev_t dev) { /* Oh, man... */ return ite_pollforchar(); } int itecnputc(dev_t dev, int c) { extern dev_t mac68k_serdev; int s; /* s = splhigh (); */ erasecursor(); ite_putchar(c); drawcursor(); if (mac68k_machine.serial_boot_echo) sercnputc(mac68k_serdev, c); /* splx (s); */ }