Home | History | Annotate | Line # | Download | only in rcons
rcons_subr.c revision 1.1
      1 /*	$NetBSD: rcons_subr.c,v 1.1 1995/09/17 19:56:41 pk Exp $ */
      2 
      3 /*
      4  * Copyright (c) 1991, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This software was developed by the Computer Systems Engineering group
      8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
      9  * contributed to Berkeley.
     10  *
     11  * All advertising materials mentioning features or use of this software
     12  * must display the following acknowledgement:
     13  *	This product includes software developed by the University of
     14  *	California, Lawrence Berkeley Laboratory.
     15  *
     16  * Redistribution and use in source and binary forms, with or without
     17  * modification, are permitted provided that the following conditions
     18  * are met:
     19  * 1. Redistributions of source code must retain the above copyright
     20  *    notice, this list of conditions and the following disclaimer.
     21  * 2. Redistributions in binary form must reproduce the above copyright
     22  *    notice, this list of conditions and the following disclaimer in the
     23  *    documentation and/or other materials provided with the distribution.
     24  * 3. All advertising materials mentioning features or use of this software
     25  *    must display the following acknowledgement:
     26  *	This product includes software developed by the University of
     27  *	California, Berkeley and its contributors.
     28  * 4. Neither the name of the University nor the names of its contributors
     29  *    may be used to endorse or promote products derived from this software
     30  *    without specific prior written permission.
     31  *
     32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     42  * SUCH DAMAGE.
     43  *
     44  *	@(#)rcons_subr.c	8.1 (Berkeley) 6/11/93
     45  */
     46 
     47 #ifdef _KERNEL
     48 #include <sys/param.h>
     49 #include <sys/device.h>
     50 #else
     51 #include <sys/types.h>
     52 #include "myfbdevice.h"
     53 #endif
     54 
     55 #include <dev/rcons/rcons.h>
     56 #include <dev/rcons/raster.h>
     57 
     58 void rcons_text(struct rconsole *, char *, int);
     59 void rcons_pctrl(struct rconsole *, int);
     60 void rcons_esc(struct rconsole *, int);
     61 void rcons_doesc(struct rconsole *, int);
     62 void rcons_cursor(struct rconsole *);
     63 void rcons_invert(struct rconsole *, int);
     64 void rcons_clear2eop(struct rconsole *);
     65 void rcons_clear2eol(struct rconsole *);
     66 void rcons_scroll(struct rconsole *, int);
     67 void rcons_delchar(struct rconsole *, int);
     68 void rcons_delline(struct rconsole *, int);
     69 void rcons_insertchar(struct rconsole *, int);
     70 void rcons_insertline(struct rconsole *, int);
     71 
     72 extern void rcons_bell(struct rconsole *);
     73 
     74 #define RCONS_ISPRINT(c) ((c) >= ' ' && (c) <= '~')
     75 #define RCONS_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
     76 
     77 /* Output (or at least handle) a string sent to the console */
     78 void
     79 rcons_puts(rc, str, n)
     80 	register struct rconsole *rc;
     81 	register char *str;
     82 	register int n;
     83 {
     84 	register int c, i, j;
     85 	register char *cp;
     86 
     87 	/* Jump scroll */
     88 	/* XXX maybe this should be an option? */
     89 	if ((rc->rc_bits & FB_INESC) == 0) {
     90 		/* Count newlines up to an escape sequence */
     91 		i = 0;
     92 		j = 0;
     93 		for (cp = str; j++ < n && *cp != '\033'; ++cp) {
     94 			if (*cp == '\n')
     95 				++i;
     96 			else if (*cp == '\013')
     97 				--i;
     98 		}
     99 
    100 		/* Only jump scroll two or more rows */
    101 		if (*rc->rc_row + i >= rc->rc_maxrow + 1) {
    102 			/* Erase the cursor (if necessary) */
    103 			if (rc->rc_bits & FB_CURSOR)
    104 				rcons_cursor(rc);
    105 
    106 			rcons_scroll(rc, i);
    107 		}
    108 	}
    109 
    110 	/* Process characters */
    111 	while (--n >= 0) {
    112 		c = *str;
    113 		if (c == '\033') {
    114 			/* Start an escape (perhaps aborting one in progress) */
    115 			rc->rc_bits |= FB_INESC | FB_P0_DEFAULT | FB_P1_DEFAULT;
    116 			rc->rc_bits &= ~(FB_P0 | FB_P1);
    117 
    118 			/* Most parameters default to 1 */
    119 			rc->rc_p0 = rc->rc_p1 = 1;
    120 		} else if (rc->rc_bits & FB_INESC) {
    121 			rcons_esc(rc, c);
    122 		} else {
    123 			/* Erase the cursor (if necessary) */
    124 			if (rc->rc_bits & FB_CURSOR)
    125 				rcons_cursor(rc);
    126 
    127 			/* Display the character */
    128 			if (RCONS_ISPRINT(c)) {
    129 				/* Try to output as much as possible */
    130 				j = rc->rc_maxcol - (*rc->rc_col + 1);
    131 				if (j > n)
    132 					j = n;
    133 				for (i = 1; i < j && RCONS_ISPRINT(str[i]); ++i)
    134 					continue;
    135 				rcons_text(rc, str, i);
    136 				--i;
    137 				str += i;
    138 				n -= i;
    139 			} else
    140 				rcons_pctrl(rc, c);
    141 		}
    142 		++str;
    143 	}
    144 	/* Redraw the cursor (if necessary) */
    145 	if ((rc->rc_bits & FB_CURSOR) == 0)
    146 		rcons_cursor(rc);
    147 }
    148 
    149 /* Actually write a string to the frame buffer */
    150 void
    151 rcons_text(rc, str, n)
    152 	register struct rconsole *rc;
    153 	register char *str;
    154 	register int n;
    155 {
    156 	register int x, y, op;
    157 
    158 	x = *rc->rc_col * rc->rc_font->width + rc->rc_xorigin;
    159 	y = *rc->rc_row * rc->rc_font->height +
    160 	    rc->rc_font_ascent + rc->rc_yorigin;
    161 	op = RAS_SRC;
    162 	if (((rc->rc_bits & FB_STANDOUT) != 0) ^
    163 	    ((rc->rc_bits & FB_INVERT) != 0))
    164 		op = RAS_NOT(op);
    165 	raster_textn(rc->rc_sp, x, y, op, rc->rc_font, str, n);
    166 	*rc->rc_col += n;
    167 	if (*rc->rc_col >= rc->rc_maxcol) {
    168 		*rc->rc_col = 0;
    169 		(*rc->rc_row)++;
    170 	}
    171 	if (*rc->rc_row >= rc->rc_maxrow)
    172 		rcons_scroll(rc, 1);
    173 }
    174 
    175 /* Handle a control character sent to the console */
    176 void
    177 rcons_pctrl(rc, c)
    178 	register struct rconsole *rc;
    179 	register int c;
    180 {
    181 
    182 	switch (c) {
    183 
    184 	case '\r':	/* Carriage return */
    185 		*rc->rc_col = 0;
    186 		break;
    187 
    188 	case '\b':	/* Backspace */
    189 		if (*rc->rc_col > 0)
    190 			(*rc->rc_col)--;
    191 		break;
    192 
    193 	case '\013':	/* Vertical tab */
    194 		if (*rc->rc_row > 0)
    195 			(*rc->rc_row)--;
    196 		break;
    197 
    198 	case '\f':	/* Formfeed */
    199 		*rc->rc_row = *rc->rc_col = 0;
    200 		rcons_clear2eop(rc);
    201 		break;
    202 
    203 	case '\n':	/* Linefeed */
    204 		(*rc->rc_row)++;
    205 		if (*rc->rc_row >= rc->rc_maxrow)
    206 			rcons_scroll(rc, 1);
    207 		break;
    208 
    209 	case '\007':	/* Bell */
    210 		rcons_bell(rc);
    211 		break;
    212 
    213 	case '\t':	/* Horizontal tab */
    214 		*rc->rc_col = (*rc->rc_col + 8) & ~7;
    215 		if (*rc->rc_col >= rc->rc_maxcol)
    216 			*rc->rc_col = rc->rc_maxcol - 1;
    217 		break;
    218 	}
    219 }
    220 
    221 /* Handle the next character in an escape sequence */
    222 void
    223 rcons_esc(rc, c)
    224 	register struct rconsole *rc;
    225 	register int c;
    226 {
    227 
    228 	if (c == '[') {
    229 		/* Parameter 0 */
    230 		rc->rc_bits &= ~FB_P1;
    231 		rc->rc_bits |= FB_P0;
    232 	} else if (c == ';') {
    233 		/* Parameter 1 */
    234 		rc->rc_bits &= ~FB_P0;
    235 		rc->rc_bits |= FB_P1;
    236 	} else if (RCONS_ISDIGIT(c)) {
    237 		/* Add a digit to a parameter */
    238 		if (rc->rc_bits & FB_P0) {
    239 			/* Parameter 0 */
    240 			if (rc->rc_bits & FB_P0_DEFAULT) {
    241 				rc->rc_bits &= ~FB_P0_DEFAULT;
    242 				rc->rc_p0 = 0;
    243 			}
    244 			rc->rc_p0 *= 10;
    245 			rc->rc_p0 += c - '0';
    246 		} else if (rc->rc_bits & FB_P1) {
    247 			/* Parameter 1 */
    248 			if (rc->rc_bits & FB_P1_DEFAULT) {
    249 				rc->rc_bits &= ~FB_P1_DEFAULT;
    250 				rc->rc_p1 = 0;
    251 			}
    252 			rc->rc_p1 *= 10;
    253 			rc->rc_p1 += c - '0';
    254 		}
    255 	} else {
    256 		/* Erase the cursor (if necessary) */
    257 		if (rc->rc_bits & FB_CURSOR)
    258 			rcons_cursor(rc);
    259 
    260 		/* Process the completed escape sequence */
    261 		rcons_doesc(rc, c);
    262 		rc->rc_bits &= ~FB_INESC;
    263 	}
    264 }
    265 
    266 /* Process a complete escape sequence */
    267 void
    268 rcons_doesc(rc, c)
    269 	register struct rconsole *rc;
    270 	register int c;
    271 {
    272 
    273 #ifdef notdef
    274 	/* XXX add escape sequence to enable visual (and audible) bell */
    275 	rc->rc_bits = FB_VISBELL;
    276 #endif
    277 
    278 	switch (c) {
    279 
    280 	case '@':
    281 		/* Insert Character (ICH) */
    282 		rcons_insertchar(rc, rc->rc_p0);
    283 		break;
    284 
    285 	case 'A':
    286 		/* Cursor Up (CUU) */
    287 		*rc->rc_row -= rc->rc_p0;
    288 		if (*rc->rc_row < 0)
    289 			*rc->rc_row = 0;
    290 		break;
    291 
    292 	case 'B':
    293 		/* Cursor Down (CUD) */
    294 		*rc->rc_row += rc->rc_p0;
    295 		if (*rc->rc_row >= rc->rc_maxrow)
    296 			*rc->rc_row = rc->rc_maxrow - 1;
    297 		break;
    298 
    299 	case 'C':
    300 		/* Cursor Forward (CUF) */
    301 		*rc->rc_col += rc->rc_p0;
    302 		if (*rc->rc_col >= rc->rc_maxcol)
    303 			*rc->rc_col = rc->rc_maxcol - 1;
    304 		break;
    305 
    306 	case 'D':
    307 		/* Cursor Backward (CUB) */
    308 		*rc->rc_col -= rc->rc_p0;
    309 		if (*rc->rc_col < 0)
    310 			*rc->rc_col = 0;
    311 		break;
    312 
    313 	case 'E':
    314 		/* Cursor Next Line (CNL) */
    315 		*rc->rc_col = 0;
    316 		*rc->rc_row += rc->rc_p0;
    317 		if (*rc->rc_row >= rc->rc_maxrow)
    318 			*rc->rc_row = rc->rc_maxrow - 1;
    319 		break;
    320 
    321 	case 'f':
    322 		/* Horizontal And Vertical Position (HVP) */
    323 	case 'H':
    324 		/* Cursor Position (CUP) */
    325 		*rc->rc_col = rc->rc_p1 - 1;
    326 		if (*rc->rc_col < 0)
    327 			*rc->rc_col = 0;
    328 		else if (*rc->rc_col >= rc->rc_maxcol)
    329 			*rc->rc_col = rc->rc_maxcol - 1;
    330 
    331 		*rc->rc_row = rc->rc_p0 - 1;
    332 		if (*rc->rc_row < 0)
    333 			*rc->rc_row = 0;
    334 		else if (*rc->rc_row >= rc->rc_maxrow)
    335 			*rc->rc_row = rc->rc_maxrow - 1;
    336 		break;
    337 
    338 	case 'J':
    339 		/* Erase in Display (ED) */
    340 		rcons_clear2eop(rc);
    341 		break;
    342 
    343 	case 'K':
    344 		/* Erase in Line (EL) */
    345 		rcons_clear2eol(rc);
    346 		break;
    347 
    348 	case 'L':
    349 		/* Insert Line (IL) */
    350 		rcons_insertline(rc, rc->rc_p0);
    351 		break;
    352 
    353 	case 'M':
    354 		/* Delete Line (DL) */
    355 		rcons_delline(rc, rc->rc_p0);
    356 		break;
    357 
    358 	case 'P':
    359 		/* Delete Character (DCH) */
    360 		rcons_delchar(rc, rc->rc_p0);
    361 		break;
    362 
    363 	case 'm':
    364 		/* Select Graphic Rendition (SGR); */
    365 		/* (defaults to zero) */
    366 		if (rc->rc_bits & FB_P0_DEFAULT)
    367 			rc->rc_p0 = 0;
    368 		if (rc->rc_p0)
    369 			rc->rc_bits |= FB_STANDOUT;
    370 		else
    371 			rc->rc_bits &= ~FB_STANDOUT;
    372 		break;
    373 
    374 	case 'p':
    375 		/* Black On White (SUNBOW) */
    376 		rcons_invert(rc, 0);
    377 		break;
    378 
    379 	case 'q':
    380 		/* White On Black (SUNWOB) */
    381 		rcons_invert(rc, 1);
    382 		break;
    383 
    384 	case 'r':
    385 		/* Set scrolling (SUNSCRL) */
    386 		/* (defaults to zero) */
    387 		if (rc->rc_bits & FB_P0_DEFAULT)
    388 			rc->rc_p0 = 0;
    389 		/* XXX not implemented yet */
    390 		rc->rc_scroll = rc->rc_p0;
    391 		break;
    392 
    393 	case 's':
    394 		/* Reset terminal emulator (SUNRESET) */
    395 		rc->rc_bits &= ~FB_STANDOUT;
    396 		rc->rc_scroll = 0;
    397 		if (rc->rc_bits & FB_INVERT)
    398 			rcons_invert(rc, 0);
    399 		break;
    400 	}
    401 }
    402 
    403 /* Paint (or unpaint) the cursor */
    404 void
    405 rcons_cursor(rc)
    406 	register struct rconsole *rc;
    407 {
    408 	register int x, y;
    409 
    410 	x = *rc->rc_col * rc->rc_font->width + rc->rc_xorigin;
    411 	y = *rc->rc_row * rc->rc_font->height + rc->rc_yorigin;
    412 	raster_op(rc->rc_sp, x, y,
    413 #ifdef notdef
    414 	    /* XXX This is the right way but too slow */
    415 	    rc->rc_font->chars[(int)' '].r->width,
    416 	    rc->rc_font->chars[(int)' '].r->height,
    417 #else
    418 	    rc->rc_font->width, rc->rc_font->height,
    419 #endif
    420 	    RAS_INVERT, (struct raster *) 0, 0, 0);
    421 	rc->rc_bits ^= FB_CURSOR;
    422 }
    423 
    424 /* Possibly change to SUNWOB or SUNBOW mode */
    425 void
    426 rcons_invert(rc, wob)
    427 	struct rconsole *rc;
    428 	int wob;
    429 {
    430 	if (((rc->rc_bits & FB_INVERT) != 0) ^ wob) {
    431 		/* Invert the display */
    432 		raster_op(rc->rc_sp, 0, 0, rc->rc_sp->width, rc->rc_sp->height,
    433 		    RAS_INVERT, (struct raster *) 0, 0, 0);
    434 
    435 		/* Swap things around */
    436 		rc->rc_ras_blank = RAS_NOT(rc->rc_ras_blank);
    437 		rc->rc_bits ^= FB_INVERT;
    438 	}
    439 }
    440 
    441 /* Clear to the end of the page */
    442 void
    443 rcons_clear2eop(rc)
    444 	register struct rconsole *rc;
    445 {
    446 	register int y;
    447 
    448 	if (*rc->rc_col == 0 && *rc->rc_row == 0) {
    449 		/* Clear the entire frame buffer */
    450 		raster_op(rc->rc_sp, 0, 0,
    451 		    rc->rc_sp->width, rc->rc_sp->height,
    452 		    rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    453 	} else {
    454 		/* Only clear what needs to be cleared */
    455 		rcons_clear2eol(rc);
    456 		y = (*rc->rc_row + 1) * rc->rc_font->height;
    457 
    458 		raster_op(rc->rc_sp, rc->rc_xorigin, rc->rc_yorigin + y,
    459 		    rc->rc_emuwidth, rc->rc_emuheight - y,
    460 		    rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    461 	}
    462 }
    463 
    464 /* Clear to the end of the line */
    465 void
    466 rcons_clear2eol(rc)
    467 	register struct rconsole *rc;
    468 {
    469 	register int x;
    470 
    471 	x = *rc->rc_col * rc->rc_font->width;
    472 
    473 	raster_op(rc->rc_sp,
    474 	    rc->rc_xorigin + x,
    475 	    *rc->rc_row * rc->rc_font->height + rc->rc_yorigin,
    476 	    rc->rc_emuwidth - x, rc->rc_font->height,
    477 	    rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    478 }
    479 
    480 /* Scroll up one line */
    481 void
    482 rcons_scroll(rc, n)
    483 	register struct rconsole *rc;
    484 	register int n;
    485 {
    486 	register int ydiv;
    487 
    488 	/* Can't scroll more than the whole screen */
    489 	if (n > rc->rc_maxrow)
    490 		n = rc->rc_maxrow;
    491 
    492 	/* Calculate new row */
    493 	*rc->rc_row -= n;
    494 	if (*rc->rc_row < 0)
    495 		*rc->rc_row  = 0;
    496 
    497 	/* Calculate number of pixels to scroll */
    498 	ydiv = rc->rc_font->height * n;
    499 
    500 	raster_op(rc->rc_sp, rc->rc_xorigin, rc->rc_yorigin,
    501 	    rc->rc_emuwidth, rc->rc_emuheight - ydiv,
    502 	    RAS_SRC, rc->rc_sp, rc->rc_xorigin, ydiv + rc->rc_yorigin);
    503 
    504 	raster_op(rc->rc_sp,
    505 	    rc->rc_xorigin, rc->rc_yorigin + rc->rc_emuheight - ydiv,
    506 	    rc->rc_emuwidth, ydiv, rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    507 }
    508 
    509 /* Delete characters */
    510 void
    511 rcons_delchar(rc, n)
    512 	register struct rconsole *rc;
    513 	register int n;
    514 {
    515 	register int tox, fromx, y, width;
    516 
    517 	/* Can't delete more chars than there are */
    518 	if (n > rc->rc_maxcol - *rc->rc_col)
    519 		n = rc->rc_maxcol - *rc->rc_col;
    520 
    521 	fromx = (*rc->rc_col + n) * rc->rc_font->width;
    522 	tox = *rc->rc_col * rc->rc_font->width;
    523 	y = *rc->rc_row * rc->rc_font->height;
    524 	width = n * rc->rc_font->width;
    525 
    526 	raster_op(rc->rc_sp, tox + rc->rc_xorigin, y + rc->rc_yorigin,
    527 	    rc->rc_emuwidth - fromx, rc->rc_font->height,
    528 	    RAS_SRC, rc->rc_sp, fromx + rc->rc_xorigin, y + rc->rc_yorigin);
    529 
    530 	raster_op(rc->rc_sp,
    531 	    rc->rc_emuwidth - width + rc->rc_xorigin, y + rc->rc_yorigin,
    532 	    width, rc->rc_font->height,
    533 	    rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    534 }
    535 
    536 /* Delete a number of lines */
    537 void
    538 rcons_delline(rc, n)
    539 	register struct rconsole *rc;
    540 	register int n;
    541 {
    542 	register int fromy, toy, height;
    543 
    544 	/* Can't delete more lines than there are */
    545 	if (n > rc->rc_maxrow - *rc->rc_row)
    546 		n = rc->rc_maxrow - *rc->rc_row;
    547 
    548 	fromy = (*rc->rc_row + n) * rc->rc_font->height;
    549 	toy = *rc->rc_row * rc->rc_font->height;
    550 	height = rc->rc_font->height * n;
    551 
    552 	raster_op(rc->rc_sp, rc->rc_xorigin, toy + rc->rc_yorigin,
    553 	    rc->rc_emuwidth, rc->rc_emuheight - fromy, RAS_SRC,
    554 	    rc->rc_sp, rc->rc_xorigin, fromy + rc->rc_yorigin);
    555 
    556 	raster_op(rc->rc_sp,
    557 	    rc->rc_xorigin, rc->rc_emuheight - height + rc->rc_yorigin,
    558 	    rc->rc_emuwidth, height,
    559 	    rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    560 }
    561 
    562 /* Insert some characters */
    563 void
    564 rcons_insertchar(rc, n)
    565 	register struct rconsole *rc;
    566 	register int n;
    567 {
    568 	register int tox, fromx, y;
    569 
    570 	/* Can't insert more chars than can fit */
    571 	if (n > rc->rc_maxcol - *rc->rc_col)
    572 		n = rc->rc_maxcol - *rc->rc_col;
    573 
    574 	tox = (*rc->rc_col + n) * rc->rc_font->width;
    575 	fromx = *rc->rc_col * rc->rc_font->width;
    576 	y = *rc->rc_row * rc->rc_font->height;
    577 
    578 	raster_op(rc->rc_sp, tox + rc->rc_xorigin, y + rc->rc_yorigin,
    579 	    rc->rc_emuwidth - tox, rc->rc_font->height,
    580 	    RAS_SRC, rc->rc_sp, fromx + rc->rc_xorigin, y + rc->rc_yorigin);
    581 
    582 	raster_op(rc->rc_sp, fromx + rc->rc_xorigin, y + rc->rc_yorigin,
    583 	    rc->rc_font->width * n, rc->rc_font->height,
    584 	    rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    585 }
    586 
    587 /* Insert some lines */
    588 void
    589 rcons_insertline(rc, n)
    590 	register struct rconsole *rc;
    591 	register int n;
    592 {
    593 	register int fromy, toy;
    594 
    595 	/* Can't insert more lines than can fit */
    596 	if (n > rc->rc_maxrow - *rc->rc_row)
    597 		n = rc->rc_maxrow - *rc->rc_row;
    598 
    599 	toy = (*rc->rc_row + n) * rc->rc_font->height;
    600 	fromy = *rc->rc_row * rc->rc_font->height;
    601 
    602 	raster_op(rc->rc_sp, rc->rc_xorigin, toy + rc->rc_yorigin,
    603 	    rc->rc_emuwidth, rc->rc_emuheight - toy,
    604 	    RAS_SRC, rc->rc_sp, rc->rc_xorigin, fromy + rc->rc_yorigin);
    605 
    606 	raster_op(rc->rc_sp, rc->rc_xorigin, fromy + rc->rc_yorigin,
    607 	    rc->rc_emuwidth, rc->rc_font->height * n,
    608 	    rc->rc_ras_blank, (struct raster *) 0, 0, 0);
    609 }
    610