Home | History | Annotate | Line # | Download | only in dev
ite_rt.c revision 1.16
      1 /*	$NetBSD: ite_rt.c,v 1.16 1996/04/23 22:53:12 veego Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1993 Markus Wild
      5  * Copyright (c) 1993 Lutz Vieweg
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *      This product includes software developed by Lutz Vieweg.
     19  * 4. The name of the author may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 #include "grfrt.h"
     34 #if NGRFRT > 0
     35 
     36 #include <sys/param.h>
     37 #include <sys/conf.h>
     38 #include <sys/proc.h>
     39 #include <sys/device.h>
     40 #include <sys/ioctl.h>
     41 #include <sys/tty.h>
     42 #include <sys/systm.h>
     43 #include <dev/cons.h>
     44 #include <machine/cpu.h>
     45 #include <amiga/amiga/device.h>
     46 #include <amiga/dev/itevar.h>
     47 #include <amiga/dev/grfioctl.h>
     48 #include <amiga/dev/grfvar.h>
     49 #include <amiga/dev/grf_rtreg.h>
     50 
     51 int retina_console = 1;
     52 
     53 void retina_cursor __P((struct ite_softc *,int));
     54 void retina_scroll __P((struct ite_softc *,int,int,int,int));
     55 void retina_deinit __P((struct ite_softc *));
     56 void retina_clear __P((struct ite_softc *,int,int,int,int));
     57 void retina_putc __P((struct ite_softc *,int,int,int,int));
     58 void retina_init __P((struct ite_softc *));
     59 
     60 #ifdef RETINA_SPEED_HACK
     61 static void screen_up __P((struct ite_softc *, int, int, int));
     62 static void screen_down __P((struct ite_softc *, int, int, int));
     63 #endif
     64 
     65 /*
     66  * this function is called from grf_rt to init the grf_softc->g_conpri
     67  * field each time a retina is attached.
     68  */
     69 int
     70 grfrt_cnprobe()
     71 {
     72 	static int done;
     73 	int rv;
     74 
     75 	if (retina_console && done == 0)
     76 		rv = CN_INTERNAL;
     77 	else
     78 		rv = CN_NORMAL;
     79 	done = 1;
     80 	return(rv);
     81 }
     82 
     83 /*
     84  * init the required fields in the grf_softc struct for a
     85  * grf to function as an ite.
     86  */
     87 void
     88 grfrt_iteinit(gp)
     89 	struct grf_softc *gp;
     90 {
     91 	gp->g_iteinit = retina_init;
     92 	gp->g_itedeinit = retina_deinit;
     93 	gp->g_iteclear = retina_clear;
     94 	gp->g_iteputc = retina_putc;
     95 	gp->g_itescroll = retina_scroll;
     96 	gp->g_itecursor = retina_cursor;
     97 }
     98 
     99 
    100 void
    101 retina_init(ip)
    102 	struct ite_softc *ip;
    103 {
    104 	struct MonDef *md;
    105 
    106 	ip->priv = ip->grf->g_data;
    107 	md = (struct MonDef *) ip->priv;
    108 
    109 	ip->cols = md->TX;
    110 	ip->rows = md->TY;
    111 }
    112 
    113 
    114 void
    115 retina_cursor(ip, flag)
    116 	struct ite_softc *ip;
    117 	int flag;
    118 {
    119       volatile caddr_t ba = ip->grf->g_regkva;
    120 
    121       if (flag == ERASE_CURSOR)
    122         {
    123 	  /* disable cursor */
    124           WCrt (ba, CRT_ID_CURSOR_START, RCrt (ba, CRT_ID_CURSOR_START) | 0x20);
    125         }
    126       else
    127 	{
    128 	  int pos = ip->curx + ip->cury * ip->cols;
    129 
    130 	  /* make sure to enable cursor */
    131           WCrt (ba, CRT_ID_CURSOR_START, RCrt (ba, CRT_ID_CURSOR_START) & ~0x20);
    132 
    133 	  /* and position it */
    134 	  WCrt (ba, CRT_ID_CURSOR_LOC_HIGH, (u_char) (pos >> 8));
    135 	  WCrt (ba, CRT_ID_CURSOR_LOC_LOW,  (u_char) pos);
    136 
    137 	  ip->cursorx = ip->curx;
    138 	  ip->cursory = ip->cury;
    139 	}
    140 }
    141 
    142 
    143 
    144 #ifdef	RETINA_SPEED_HACK
    145 static void
    146 screen_up(ip, top, bottom, lines)
    147 	struct ite_softc *ip;
    148 	int top;
    149 	int bottom;
    150 	int lines;
    151 {
    152 	volatile caddr_t ba = ip->grf->g_regkva;
    153 	volatile caddr_t fb = ip->grf->g_fbkva;
    154 	const struct MonDef * md = (struct MonDef *) ip->priv;
    155 #ifdef BANKEDDEVPAGER
    156 	int bank;
    157 #endif
    158 
    159 	/* do some bounds-checking here.. */
    160 	if (top >= bottom)
    161 	  return;
    162 
    163 	if (top + lines >= bottom)
    164 	  {
    165 	    retina_clear (ip, top, 0, bottom - top, ip->cols);
    166 	    return;
    167 	  }
    168 
    169 
    170 #ifdef BANKEDDEVPAGER
    171 	/* make sure to save/restore active bank (and if it's only
    172 	   for tests of the feature in text-mode..) */
    173 	bank = (RSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO)
    174 		| (RSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI) << 8));
    175 #endif
    176 
    177 	/* the trick here is to use a feature of the NCR chip. It can
    178 	   optimize data access in various read/write modes. One of
    179 	   the modes is able to read/write from/to different zones.
    180 
    181 	   Thus, by setting the read-offset to lineN, and the write-offset
    182 	   to line0, we just cause read/write cycles for all characters
    183 	   up to the last line, and have the chip transfer the data. The
    184 	   `addqb' are the cheapest way to cause read/write cycles (DONT
    185 	   use `tas' on the Amiga!), their results are completely ignored
    186 	   by the NCR chip, it just replicates what it just read. */
    187 
    188 		/* write to primary, read from secondary */
    189 	WSeq (ba, SEQ_ID_EXTENDED_MEM_ENA,
    190 		(RSeq(ba, SEQ_ID_EXTENDED_MEM_ENA) & 0x1f) | 0 );
    191 		/* clear extended chain4 mode */
    192 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR, RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) & ~0x02);
    193 
    194 		/* set write mode 1, "[...] data in the read latches is written
    195 		   to memory during CPU memory write cycles. [...]" */
    196 	WGfx (ba, GCT_ID_GRAPHICS_MODE,
    197 		(RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 1);
    198 
    199 	{
    200 		/* write to line TOP */
    201 		long toploc = top * (md->TX / 16);
    202 		WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, ((unsigned char)toploc));
    203 		WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, ((unsigned char)(toploc >> 8)));
    204 	}
    205 	{
    206 		/* read from line TOP + LINES */
    207 		long fromloc = (top+lines) * (md->TX / 16);
    208 		WSeq (ba, SEQ_ID_SEC_HOST_OFF_LO, ((unsigned char)fromloc)) ;
    209 		WSeq (ba, SEQ_ID_SEC_HOST_OFF_HI, ((unsigned char)(fromloc >> 8))) ;
    210 	}
    211 	{
    212 		caddr_t p = (caddr_t)fb;
    213 		/* transfer all characters but LINES lines, unroll by 16 */
    214 		short x = (1 + bottom - (top + lines)) * (md->TX / 16) - 1;
    215 		do {
    216 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    217 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    218 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    219 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    220 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    221 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    222 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    223 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    224 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    225 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    226 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    227 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    228 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    229 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    230 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    231 			asm volatile("addqb #1,%0@+" : "=a" (p) : "0" (p));
    232 		} while (x--);
    233 	}
    234 
    235 		/* reset to default values */
    236 	WSeq (ba, SEQ_ID_SEC_HOST_OFF_HI, 0);
    237 	WSeq (ba, SEQ_ID_SEC_HOST_OFF_LO, 0);
    238 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, 0);
    239 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, 0);
    240 		/* write mode 0 */
    241 	WGfx (ba, GCT_ID_GRAPHICS_MODE,
    242 		(RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 0);
    243 		/* extended chain4 enable */
    244 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR,
    245 		RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) | 0x02);
    246 		/* read/write to primary on A0, secondary on B0 */
    247 	WSeq (ba, SEQ_ID_EXTENDED_MEM_ENA,
    248 		(RSeq(ba, SEQ_ID_EXTENDED_MEM_ENA) & 0x1f) | 0x40);
    249 
    250 
    251 	/* fill the free lines with spaces */
    252 
    253 	{  /* feed latches with value */
    254 		unsigned short * f = (unsigned short *) fb;
    255 
    256 		f += (1 + bottom - lines) * md->TX * 2;
    257 		*f = 0x2010;
    258 	}
    259 
    260 	   /* clear extended chain4 mode */
    261 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR, RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) & ~0x02);
    262 	   /* set write mode 1, "[...] data in the read latches is written
    263 	      to memory during CPU memory write cycles. [...]" */
    264 	WGfx (ba, GCT_ID_GRAPHICS_MODE, (RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 1);
    265 
    266 	{
    267 		unsigned long * p = (unsigned long *) fb;
    268 		short x = (lines * (md->TX/16)) - 1;
    269 		const unsigned long dummyval = 0;
    270 
    271 		p += (1 + bottom - lines) * (md->TX/4);
    272 
    273 		do {
    274 			*p++ = dummyval;
    275 			*p++ = dummyval;
    276 			*p++ = dummyval;
    277 			*p++ = dummyval;
    278 		} while (x--);
    279 	}
    280 
    281 	   /* write mode 0 */
    282 	WGfx (ba, GCT_ID_GRAPHICS_MODE, (RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 0);
    283 	   /* extended chain4 enable */
    284 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR , RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) | 0x02);
    285 
    286 #ifdef BANKEDDEVPAGER
    287 	/* restore former bank */
    288 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, (unsigned char) bank);
    289 	bank >>= 8;
    290 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, (unsigned char) bank);
    291 #endif
    292 };
    293 
    294 
    295 static void
    296 screen_down(ip, top, bottom, lines)
    297 	struct ite_softc *ip;
    298 	int top;
    299 	int bottom;
    300 	int lines;
    301 {
    302 	volatile caddr_t ba = ip->grf->g_regkva;
    303 	volatile caddr_t fb = ip->grf->g_fbkva;
    304 	const struct MonDef * md = (struct MonDef *) ip->priv;
    305 #ifdef BANKEDDEVPAGER
    306 	int bank;
    307 #endif
    308 
    309 	/* do some bounds-checking here.. */
    310 	if (top >= bottom)
    311 	  return;
    312 
    313 	if (top + lines >= bottom)
    314 	  {
    315 	    retina_clear (ip, top, 0, bottom - top, ip->cols);
    316 	    return;
    317 	  }
    318 
    319 #ifdef BANKEDDEVPAGER
    320 	/* make sure to save/restore active bank (and if it's only
    321 	   for tests of the feature in text-mode..) */
    322 	bank = (RSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO)
    323 		| (RSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI) << 8));
    324 #endif
    325 	/* see screen_up() for explanation of chip-tricks */
    326 
    327 		/* write to primary, read from secondary */
    328 	WSeq (ba, SEQ_ID_EXTENDED_MEM_ENA,
    329 		(RSeq(ba, SEQ_ID_EXTENDED_MEM_ENA) & 0x1f) | 0 );
    330 		/* clear extended chain4 mode */
    331 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR, RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) & ~0x02);
    332 
    333 		/* set write mode 1, "[...] data in the read latches is written
    334 		   to memory during CPU memory write cycles. [...]" */
    335 	WGfx (ba, GCT_ID_GRAPHICS_MODE, (RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 1);
    336 
    337 	{
    338 		/* write to line TOP + LINES */
    339 		long toloc = (top + lines) * (md->TX / 16);
    340 		WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, ((unsigned char)toloc));
    341 		WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, ((unsigned char)(toloc >> 8)));
    342 	}
    343 	{
    344 		/* read from line TOP */
    345 		long fromloc = top * (md->TX / 16);
    346 		WSeq (ba, SEQ_ID_SEC_HOST_OFF_LO, ((unsigned char)fromloc));
    347 		WSeq (ba, SEQ_ID_SEC_HOST_OFF_HI, ((unsigned char)(fromloc >> 8))) ;
    348 	}
    349 
    350 	{
    351 		caddr_t p = (caddr_t)fb;
    352 		short x = (1 + bottom - (top + lines)) * (md->TX / 16) - 1;
    353 		p += (1 + bottom - (top + lines)) * md->TX;
    354 		do {
    355 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    356 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    357 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    358 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    359 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    360 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    361 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    362 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    363 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    364 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    365 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    366 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    367 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    368 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    369 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    370 			asm volatile("addqb #1,%0@-" : "=a" (p) : "0" (p));
    371 		} while (x--);
    372 	}
    373 
    374 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, 0);
    375 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, 0);
    376 	WSeq (ba, SEQ_ID_SEC_HOST_OFF_HI, 0);
    377 	WSeq (ba, SEQ_ID_SEC_HOST_OFF_LO, 0);
    378 
    379 		/* write mode 0 */
    380 	WGfx (ba, GCT_ID_GRAPHICS_MODE,
    381 		(RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 0);
    382 		/* extended chain4 enable */
    383 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR , RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) | 0x02);
    384 		/* read/write to primary on A0, secondary on B0 */
    385 	WSeq (ba, SEQ_ID_EXTENDED_MEM_ENA,
    386 		(RSeq(ba, SEQ_ID_EXTENDED_MEM_ENA) & 0x1f) | 0x40 );
    387 
    388 	/* fill the free lines with spaces */
    389 
    390 	{  /* feed latches with value */
    391 		unsigned short * f = (unsigned short *) fb;
    392 
    393 		f += top * md->TX * 2;
    394 		*f = 0x2010;
    395 	}
    396 
    397 	   /* clear extended chain4 mode */
    398 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR, RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) & ~0x02);
    399 	   /* set write mode 1, "[...] data in the read latches is written
    400 	      to memory during CPU memory write cycles. [...]" */
    401 	WGfx (ba, GCT_ID_GRAPHICS_MODE, (RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 1);
    402 
    403 	{
    404 		unsigned long * p = (unsigned long *) fb;
    405 		short x = (lines * (md->TX/16)) - 1;
    406 		const unsigned long dummyval = 0;
    407 
    408 		p += top * (md->TX/4);
    409 
    410 		do {
    411 			*p++ = dummyval;
    412 			*p++ = dummyval;
    413 			*p++ = dummyval;
    414 			*p++ = dummyval;
    415 		} while (x--);
    416 	}
    417 
    418 	   /* write mode 0 */
    419 	WGfx (ba, GCT_ID_GRAPHICS_MODE, (RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 0);
    420 	   /* extended chain4 enable */
    421 	WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR , RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) | 0x02);
    422 
    423 #ifdef BANKEDDEVPAGER
    424 	/* restore former bank */
    425 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, (unsigned char) bank);
    426 	bank >>= 8;
    427 	WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, (unsigned char) bank);
    428 #endif
    429 };
    430 #endif	/* RETINA_SPEED_HACK */
    431 
    432 
    433 void
    434 retina_deinit(ip)
    435 	struct ite_softc *ip;
    436 {
    437 	ip->flags &= ~ITE_INITED;
    438 }
    439 
    440 
    441 void
    442 retina_putc(ip, c, dy, dx, mode)
    443 	struct ite_softc *ip;
    444 	int c;
    445 	int dy;
    446 	int dx;
    447 	int mode;
    448 {
    449 	volatile caddr_t fb = ip->grf->g_fbkva;
    450 	register u_char attr;
    451 
    452 	attr = (mode & ATTR_INV) ? 0x21 : 0x10;
    453 	if (mode & ATTR_UL)     attr  = 0x01;	/* ???????? */
    454 	if (mode & ATTR_BOLD)   attr |= 0x08;
    455 	if (mode & ATTR_BLINK)	attr |= 0x80;
    456 
    457 	fb += 4 * (dy * ip->cols + dx);
    458 	*fb++ = c; *fb = attr;
    459 }
    460 
    461 
    462 void
    463 retina_clear(ip, sy, sx, h, w)
    464 	struct ite_softc *ip;
    465 	int sy;
    466 	int sx;
    467 	int h;
    468 	int w;
    469 {
    470 	u_short * fb = (u_short *) ip->grf->g_fbkva;
    471 	short x;
    472 	const u_short fillval = 0x2010;
    473 
    474 	/* could probably be optimized just like the scrolling functions !! */
    475 	fb += 2 * (sy * ip->cols + sx);
    476 	while (h--)
    477 	  {
    478 	    for (x = 2 * (w - 1); x >= 0; x -= 2)
    479 	      fb[x] = fillval;
    480 	    fb += 2 * ip->cols;
    481 	  }
    482 }
    483 
    484 
    485 /*
    486  * RETINA_SPEED_HACK code seems to work on some boards and on others
    487  * it causes text to smear horizontally
    488  */
    489 void
    490 retina_scroll(ip, sy, sx, count, dir)
    491 	struct ite_softc *ip;
    492 	int sy;
    493 	int sx;
    494 	int count;
    495 	int dir;
    496 {
    497 	volatile caddr_t ba;
    498 	u_long *fb;
    499 
    500 	ba = ip->grf->g_regkva;
    501 	fb = (u_long *)ip->grf->g_fbkva;
    502 
    503 	retina_cursor(ip, ERASE_CURSOR);
    504 
    505 	if (dir == SCROLL_UP) {
    506 #ifdef	RETINA_SPEED_HACK
    507 		screen_up(ip, sy - count, ip->bottom_margin, count);
    508 #else
    509 		bcopy(fb + sy * ip->cols, fb + (sy - count) * ip->cols,
    510 		    4 * (ip->bottom_margin - sy + 1) * ip->cols);
    511 		retina_clear(ip, ip->bottom_margin + 1 - count, 0, count,
    512 		    ip->cols);
    513 #endif
    514 	} else if (dir == SCROLL_DOWN) {
    515 #ifdef	RETINA_SPEED_HACK
    516 		screen_down(ip, sy, ip->bottom_margin, count);
    517 #else
    518 		bcopy(fb + sy * ip->cols, fb + (sy + count) * ip->cols,
    519 		    4 * (ip->bottom_margin - sy - count + 1) * ip->cols);
    520 		retina_clear(ip, sy, 0, count, ip->cols);
    521 #endif
    522 	} else if (dir == SCROLL_RIGHT) {
    523 		bcopy(fb + sx + sy * ip->cols, fb + sx + sy * ip->cols + count,
    524 		    4 * (ip->cols - (sx + count)));
    525 		retina_clear(ip, sy, sx, 1, count);
    526 	} else {
    527 		bcopy(fb + sx + sy * ip->cols, fb + sx - count + sy * ip->cols,
    528 		    4 * (ip->cols - sx));
    529 		retina_clear(ip, sy, ip->cols - count, 1, count);
    530 	}
    531 #ifndef	RETINA_SPEED_HACK
    532 	retina_cursor(ip, !ERASE_CURSOR);
    533 #endif
    534 }
    535 
    536 #endif /* NGRFRT */
    537