/* $NetBSD: vga.c,v 1.10 2009/03/18 10:22:27 cegger Exp $ */ /*- * Copyright (C) 1995-1997 Gary Thomas (gdt@linuxppc.org) * All rights reserved. * * VGA 'glass TTY' emulator * * 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 Gary Thomas. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifdef CONS_VGA #include #include "boot.h" #define COL 80 #define ROW 25 #define CHR 2 #define MONO_BASE 0x3B4 #define MONO_BUF 0xB0000 #define CGA_BASE 0x3D4 #define CGA_BUF 0xB8000 static u_char background = 0; /* Black */ static u_char foreground = 7; /* White */ u_int addr_6845; u_short *Crtat; int lastpos; /* * The current state of virtual displays */ struct screen { u_short *cp; /* the current character address */ enum state { NORMAL, /* no pending escape */ ESC, /* saw ESC */ EBRAC, /* saw ESC[ */ EBRACEQ /* saw ESC[= */ } state; /* command parser state */ int cx; /* the first escape seq argument */ int cy; /* the second escap seq argument */ int *accp; /* pointer to the current processed argument */ int row; /* current column */ int so; /* standout mode */ u_short color; /* normal character color */ u_short color_so; /* standout color */ u_short save_color; /* saved normal color */ u_short save_color_so; /* saved standout color */ } screen; /* * Color and attributes for normal, standout and kernel output * are stored in the least-significant byte of a u_short * so they don't have to be shifted for use. * This is all byte-order dependent. */ #define CATTR(x) (x) /* store color/attributes un-shifted */ #define ATTR_ADDR(which) (((u_char *)&(which))+1) /* address of attributes */ u_short pccolor; /* color/attributes for tty output */ u_short pccolor_so; /* color/attributes, standout mode */ static void cursor(void); static void initscreen(void); void fillw(u_short, u_short *, int); void video_on(void); void video_off(void); /* * cursor() sets an offset (0-1999) into the 80x25 text area */ static void cursor(void) { int pos = screen.cp - Crtat; if (lastpos != pos) { outb(addr_6845, 14); outb(addr_6845+1, pos >> 8); outb(addr_6845, 15); outb(addr_6845+1, pos); lastpos = pos; } } static void initscreen(void) { struct screen *d = &screen; pccolor = CATTR((background<<4)|foreground); pccolor_so = CATTR((foreground<<4)|background); d->color = pccolor; d->save_color = pccolor; d->color_so = pccolor_so; d->save_color_so = pccolor_so; } #define wrtchar(c, d) { \ *(d->cp) = c; \ d->cp++; \ d->row++; \ } void fillw(u_short val, u_short *buf, int num) { /* Need to byte swap value */ u_short tmp; tmp = val; while (num-- > 0) *buf++ = tmp; } /* * vga_putc (nee sput) has support for emulation of the 'ibmpc' termcap entry. * This is a bare-bones implementation of a bare-bones entry * One modification: Change li#24 to li#25 to reflect 25 lines * "ca" is the color/attributes value (left-shifted by 8) * or 0 if the current regular color for that screen is to be used. */ void vga_putc(int c) { struct screen *d = &screen; u_short *base; int i, j; u_short *pp; base = Crtat; switch (d->state) { case NORMAL: switch (c) { case 0x0: /* Ignore pad characters */ return; case 0x1B: d->state = ESC; break; case '\t': do { wrtchar(d->color | ' ', d); } while (d->row % 8); break; case '\b': /* non-destructive backspace */ if (d->cp > base) { d->cp--; d->row--; if (d->row < 0) d->row += COL; /* prev column */ } break; case '\n': d->cp += COL; case '\r': d->cp -= d->row; d->row = 0; break; case '\007': break; default: if (d->so) { wrtchar(d->color_so|(c<<8), d); } else { wrtchar(d->color | (c<<8), d); } if (d->row >= COL) d->row = 0; break; } break; case EBRAC: /* * In this state, the action at the end of the switch * on the character type is to go to NORMAL state, * and intermediate states do a return rather than break. */ switch (c) { case 'm': d->so = d->cx; break; case 'A': /* back one row */ if (d->cp >= base + COL) d->cp -= COL; break; case 'B': /* down one row */ d->cp += COL; break; case 'C': /* right cursor */ d->cp++; d->row++; break; case 'D': /* left cursor */ if (d->cp > base) { d->cp--; d->row--; if (d->row < 0) d->row += COL; /* prev column ??? */ } break; case 'J': /* Clear to end of display */ fillw(d->color|(' '<<8), d->cp, base + COL * ROW - d->cp); break; case 'K': /* Clear to EOL */ fillw(d->color|(' '<<8), d->cp, COL - (d->cp - base) % COL); break; case 'H': /* Cursor move */ if (d->cx > ROW) d->cx = ROW; if (d->cy > COL) d->cy = COL; if (d->cx == 0 || d->cy == 0) { d->cp = base; d->row = 0; } else { d->cp = base + (d->cx - 1) * COL + d->cy - 1; d->row = d->cy - 1; } break; case '_': /* set cursor */ if (d->cx) d->cx = 1; /* block */ else d->cx = 12; /* underline */ outb(addr_6845, 10); outb(addr_6845+1, d->cx); outb(addr_6845, 11); outb(addr_6845+1, 13); break; case ';': /* Switch params in cursor def */ d->accp = &d->cy; return; case '=': /* ESC[= color change */ d->state = EBRACEQ; return; case 'L': /* Insert line */ i = (d->cp - base) / COL; /* avoid deficiency of bcopy implementation */ /* XXX: comment and hack relevant? */ pp = base + COL * (ROW-2); for (j = ROW - 1 - i; j--; pp -= COL) memmove(pp + COL, pp, COL * CHR); fillw(d->color|(' '<<8), base + i * COL, COL); break; case 'M': /* Delete line */ i = (d->cp - base) / COL; pp = base + i * COL; memmove(pp, pp + COL, (ROW-1 - i)*COL*CHR); fillw(d->color|(' '<<8), base + COL * (ROW - 1), COL); break; default: /* Only numbers valid here */ if ((c >= '0') && (c <= '9')) { *(d->accp) *= 10; *(d->accp) += c - '0'; return; } else break; } d->state = NORMAL; break; case EBRACEQ: { /* * In this state, the action at the end of the switch * on the character type is to go to NORMAL state, * and intermediate states do a return rather than break. */ u_char *colp; /* * Set foreground/background color * for normal mode, standout mode * or kernel output. * Based on code from kentp@svmp03. */ switch (c) { case 'F': colp = ATTR_ADDR(d->color); do_fg: *colp = (*colp & 0xf0) | (d->cx); break; case 'G': colp = ATTR_ADDR(d->color); do_bg: *colp = (*colp & 0xf) | (d->cx << 4); break; case 'H': colp = ATTR_ADDR(d->color_so); goto do_fg; case 'I': colp = ATTR_ADDR(d->color_so); goto do_bg; case 'S': d->save_color = d->color; d->save_color_so = d->color_so; break; case 'R': d->color = d->save_color; d->color_so = d->save_color_so; break; default: /* Only numbers valid here */ if ((c >= '0') && (c <= '9')) { d->cx *= 10; d->cx += c - '0'; return; } else break; } d->state = NORMAL; } break; case ESC: switch (c) { case 'c': /* Clear screen & home */ fillw(d->color|(' '<<8), base, COL * ROW); d->cp = base; d->row = 0; d->state = NORMAL; break; case '[': /* Start ESC [ sequence */ d->state = EBRAC; d->cx = 0; d->cy = 0; d->accp = &d->cx; break; default: /* Invalid, clear state */ d->state = NORMAL; break; } break; } if (d->cp >= base + (COL * ROW)) { /* scroll check */ memmove(base, base + COL, COL * (ROW - 1) * CHR); fillw(d->color|(' '<<8), base + COL * (ROW - 1), COL); d->cp -= COL; } cursor(); } void vga_puts(char *s) { char c; while ((c = *s++)) { vga_putc(c); } } void video_on(void) { /* Enable video */ outb(0x3C4, 0x01); outb(0x3C5, inb(0x3C5) & ~0x20); } void video_off(void) { /* Disable video */ outb(0x3C4, 0x01); outb(0x3C5, inb(0x3C5) | 0x20); } void vga_init(u_char *ISA_mem) { struct screen *d = &screen; memset(d, 0, sizeof (screen)); video_on(); d->cp = Crtat = (u_short *)&ISA_mem[0x0B8000]; addr_6845 = CGA_BASE; initscreen(); fillw(pccolor|(' '<<8), d->cp, COL * ROW); } #endif /* CONS_VGA */