xref: /netbsd-src/sys/arch/amiga/dev/ite_cc.c (revision 3b2546cf2e22b1b294320ae90b74e52b6f4052c5)
1 /*	$Id: ite_cc.c,v 1.3 1993/09/02 18:08:03 mw Exp $ */
2 
3 #include "ite.h"
4 #if NITE > 0
5 
6 #include "param.h"
7 #include "conf.h"
8 #include "proc.h"
9 #include "ioctl.h"
10 #include "tty.h"
11 #include "systm.h"
12 
13 #include "ite.h"
14 #include "itevar.h"
15 
16 #include "machine/cpu.h"
17 
18 /* XXX */
19 #include "grfioctl.h"
20 #include "grfvar.h"
21 #include "grf_ccreg.h"
22 
23 #include "../amiga/custom.h"
24 
25 extern caddr_t CHIPMEMADDR;
26 
27 extern unsigned char kernel_font_width, kernel_font_height;
28 extern unsigned char kernel_font_lo, kernel_font_hi;
29 extern unsigned char kernel_font[], kernel_cursor[];
30 
31 /*
32  * This holds the instructions to retarget the plane 0 pointer
33  * at each split point.
34  */
35 typedef struct {
36   u_short wait[2];		/* wait instruction */
37   u_short plane[4];		/* move + hi word, move + lo word */
38 } COP_ROW;
39 
40 typedef struct {
41   u_char *buf;			/* pointer to row within frame buffer */
42   int polarity;			/* polarity for loading planes in copper list */
43 } BUF_ROW;
44 
45 /*
46  * This is what ip->priv points to;
47  * it contains local variables for custom-chip ites.
48  */
49 struct ccite {
50   struct ccfb *fb;
51   BUF_ROW *buf_rows;		/* array of pointers into the frame buffer */
52   COP_ROW *cop_rows[2];		/* extension to grf_cc's copper lists */
53 };
54 
55 static struct ccite ccite[NITE];
56 
57 static BUF_ROW ccite_buf_rows[NITE][100]; /* XXX see below */
58 
59 extern struct itesw itesw[];
60 
61 /* 8-by-N routines */
62 static void cc_8n_cursor(struct ite_softc *ip, int flag);
63 static void cc_8n_putc(struct ite_softc *ip, int c, int dy, int dx, int mode);
64 static void cc_8n_clear(struct ite_softc *ip, int sy, int sx, int h, int w);
65 static void cc_8n_scroll(struct ite_softc *ip, int sy, int sx, int count, int dir);
66 
67 /* (M<=8)-by-N routines */
68 static void cc_le32n_cursor(struct ite_softc *ip, int flag);
69 static void cc_le8n_putc(struct ite_softc *ip, int c, int dy, int dx, int mode);
70 static void cc_le8n_clear(struct ite_softc *ip, int sy, int sx, int h, int w);
71 static void cc_le8n_scroll(struct ite_softc *ip, int sy, int sx, int count, int dir);
72 
73 /* Mykes: Insert your whiz-bang 8-by-8 routines here... ;-) */
74 
75 
76 
77 customc_init(ip)
78 	register struct ite_softc *ip;
79 {
80   struct ccite *cci;
81   struct ccfb *fb;
82   struct itesw *sp = &itesw[ip->type];
83 
84   if (ip->grf == 0)
85     ip->grf = &grf_softc[ip - ite_softc];
86 
87   cci = &ccite[ip - ite_softc];
88   ip->priv = cci;
89   fb = (struct ccfb *) ip->grf->g_display.gd_regaddr;
90   cci->fb = fb;
91 
92   ip->font     = kernel_font;
93   ip->font_lo  = kernel_font_lo;
94   ip->font_hi  = kernel_font_hi;
95   ip->ftwidth  = kernel_font_width;
96   ip->ftheight = kernel_font_height;
97 #if 0
98   ip->cursor   = kernel_cursor;
99 #endif
100 
101   ip->rows     = fb->disp_height / ip->ftheight;
102   ip->cols     = fb->disp_width  / ip->ftwidth;
103 
104   /* Find the correct set of rendering routines for this font.  */
105 #if 0
106   /* The new unspecialized routines are faster than the old specialized ones
107      for the same font!!! (and without even unrolling them...)
108      Therefore I'm leaving them out for now.  */
109   if (ip->ftwidth == 8)
110     {
111       sp->ite_cursor = cc_8n_cursor;
112       sp->ite_putc = cc_8n_putc;
113       sp->ite_clear = cc_8n_clear;
114       sp->ite_scroll = cc_8n_scroll;
115     }
116   else
117 #endif
118   if (ip->ftwidth <= 8)
119     {
120       sp->ite_cursor = (void*)cc_le32n_cursor;
121       sp->ite_putc = (void*)cc_le8n_putc;
122       sp->ite_clear = (void*)cc_le8n_clear;
123       sp->ite_scroll = (void*)cc_le8n_scroll;
124     }
125   else
126     panic("kernel font size not supported");
127 
128   /* XXX It may be better if this was dynamic based on ip->rows,
129      but is dynamic memory allocation available at this point?  */
130   cci->buf_rows = ccite_buf_rows[ip - ite_softc];
131 
132   /* Now allocate memory for the special screen-split copper lists.
133      We will need a COP_ROW structure for each text row,
134      plus an extra row to terminate the list.  */
135   /* testing for the result is really redundant because chipmem_steal
136      panics if it runs out of memory.. */
137   if (! (cci->cop_rows[0] = (COP_ROW *)
138 	 chipmem_steal (sizeof(COP_ROW) * (ip->rows + 1)))
139       || !(cci->cop_rows[1] = (COP_ROW *)
140 	   chipmem_steal (sizeof(COP_ROW) * (ip->rows + 1))))
141     return 0;
142 
143   /* Initialize the screen-split row arrays.  */
144   {
145     int i, ypos = 0;
146     u_long rowbytes = fb->fb_width >> 3;
147     u_long fbp, fbp2;
148 
149     fbp = ((u_long)fb->fb + (fb->fb_x >> 3) + fb->fb_y * rowbytes);
150     for (i = 0; i < ip->rows; i++)
151       {
152 	cci->buf_rows[i].buf = (u_char*)fbp;
153 	cci->buf_rows[i].polarity = (fb->disp_y + ypos) & 1;
154 
155 	COP_WAIT(cci->cop_rows[0][i].wait, (fb->disp_y + ypos + 1) >> 1);
156 	fbp2 = (fbp - (u_long)CHIPMEMADDR
157 		+ (cci->buf_rows[i].polarity ? rowbytes : 0));
158 	COP_MOVE(cci->cop_rows[0][i].plane, bplpth(0), HIADDR(fbp2));
159 	COP_MOVE(cci->cop_rows[0][i].plane+2, bplptl(0), LOADDR(fbp2));
160 
161 	COP_WAIT(cci->cop_rows[1][i].wait, (fb->disp_y + ypos) >> 1);
162 	fbp2 = (fbp - (u_long)CHIPMEMADDR +
163 		(cci->buf_rows[i].polarity ? 0 : rowbytes));
164 	COP_MOVE(cci->cop_rows[1][i].plane, bplpth(0), HIADDR(fbp2));
165 	COP_MOVE(cci->cop_rows[1][i].plane+2, bplptl(0), LOADDR(fbp2));
166 
167 	ypos += ip->ftheight;
168 	fbp += ip->ftheight * rowbytes;
169       }
170 
171     /* Turn the display off after the last row;
172        otherwise we'll get funny text at the bottom of the screen
173        because of reordered rows.  */
174     COP_WAIT(cci->cop_rows[0][i].wait+0, (fb->disp_y + ypos + 1) >> 1);
175     COP_MOVE(cci->cop_rows[0][i].wait+2, bplcon0, 0x8204);
176     COP_END (cci->cop_rows[0][i].wait+4);
177     COP_WAIT(cci->cop_rows[1][i].wait+0, (fb->disp_y + ypos) >> 1);
178     COP_MOVE(cci->cop_rows[1][i].wait+2, bplcon0, 0x8204);
179     COP_END (cci->cop_rows[1][i].wait+4);
180   }
181 
182   /* Install the new copper list extensions.  */
183   cc_install_cop_ext(ip->grf, cci->cop_rows[0], cci->cop_rows[1]);
184 
185 #if 0
186   printf ("font@%x, cursor@%x\n", ip->font, ip->cursor);
187   dump_copperlist (fb->cop1);
188   dump_copperlist (fb->cop2);
189 #endif
190 }
191 
192 customc_deinit(ip)
193 	struct ite_softc *ip;
194 {
195   ip->flags &= ~ITE_INITED;
196 
197   /* Take our grubby little fingers out of the grf's copper list.  */
198   cc_uninstall_cop_ext(ip->grf);
199 }
200 
201 /*
202  * Swap two text rows in the display by modifying the copper list.
203  */
204 static __inline int
205 swap_rows(struct ite_softc *ip, int row1, int row2)
206 {
207 	struct ccite *cci = (struct ccite *) ip->priv;
208 	int rowbytes = cci->fb->fb_width >> 3;
209 	u_char *tmp, *fbp2;
210 
211 	/* Swap the plane pointers */
212 	tmp = cci->buf_rows[row1].buf;
213 	cci->buf_rows[row1].buf = cci->buf_rows[row2].buf;
214 	cci->buf_rows[row2].buf = tmp;
215 
216 	/* Update the copper lists */
217 	fbp2 = (cci->buf_rows[row1].buf - (u_long)CHIPMEMADDR
218 		+ (cci->buf_rows[row1].polarity ? rowbytes : 0));
219 	cci->cop_rows[0][row1].plane[1] = HIADDR(fbp2);
220 	cci->cop_rows[0][row1].plane[3] = LOADDR(fbp2);
221 
222 	fbp2 = (cci->buf_rows[row1].buf - (u_long)CHIPMEMADDR
223 		+ (cci->buf_rows[row1].polarity ? 0 : rowbytes));
224 	cci->cop_rows[1][row1].plane[1] = HIADDR(fbp2);
225 	cci->cop_rows[1][row1].plane[3] = LOADDR(fbp2);
226 
227 	fbp2 = (cci->buf_rows[row2].buf - (u_long)CHIPMEMADDR
228 		+ (cci->buf_rows[row2].polarity ? rowbytes : 0));
229 	cci->cop_rows[0][row2].plane[1] = HIADDR(fbp2);
230 	cci->cop_rows[0][row2].plane[3] = LOADDR(fbp2);
231 
232 	fbp2 = (cci->buf_rows[row2].buf - (u_long)CHIPMEMADDR
233 		+ (cci->buf_rows[row2].polarity ? 0 : rowbytes));
234 	cci->cop_rows[1][row2].plane[1] = HIADDR(fbp2);
235 	cci->cop_rows[1][row2].plane[3] = LOADDR(fbp2);
236 
237 	/* If the drawn cursor was on either row, swap it too.  */
238 	if (ip->cursory == row1)
239 		ip->cursory = row2;
240 	else if (ip->cursory == row2)
241 		ip->cursory = row1;
242 }
243 
244 
245 
246 /*** 8-by-N routines ***/
247 
248 static inline void
249 cc_8n_windowmove (src, srcx, srcy, srcmod,
250 		    dst, dstx, dsty, dstmod, h, w, op)
251     unsigned char *src, *dst;
252     unsigned short srcx, srcy, srcmod;
253     unsigned short dstx, dsty, dstmod;
254     unsigned short h, w;
255     unsigned char op;
256 {
257   short i;	/* NOT unsigned! */
258   unsigned char h1;
259 
260   src += srcmod * srcy + (srcx >> 3);
261   dst += dstmod * dsty + (dstx >> 3);
262 
263 #if 0
264 printf("ccwm: %x-%x-%x-%x-%c\n", src, dst, h, w,
265 	op == RR_XOR ? '^' : op == RR_COPY ? '|' : op == RR_CLEAR ? 'C' : 'I');
266 #endif
267 
268   /* currently, only drawing to byte slots is supported... */
269   if ((srcx & 07) || (dstx & 07) || (w & 07))
270     panic ("customc_windowmove: odd offset");
271 
272   w >>= 3;
273 
274   /* Ok, this is nastier than it could be to help the optimizer unroll
275      loops for the most common case of 8x8 characters.
276 
277      Note that bzero() does some clever optimizations for large range
278      clears, so it should pay the subroutine call. */
279 
280 /* perform OP on one bit row of data. */
281 #define ONEOP(dst, src, op) \
282 	do { if ((src) > (dst))					\
283 	  for (i = 0; i < w; i++) (dst)[i] op (src)[i];		\
284 	else							\
285 	  for (i = w - 1; i >= 0; i--) (dst)[i] op (src)[i]; } while (0)
286 
287 /* perform a block of eight ONEOPs. This enables the optimizer to unroll
288    the for-statements, as they have a loop counter known at compiletime */
289 #define EIGHTOP(dst, src, op) \
290 	for (h1 = 0; h1 < 8; h1++, src += srcmod, dst += dstmod) \
291 	    ONEOP (dst, src, op);
292 
293   switch (op)
294     {
295     case RR_COPY:
296       for (; h >= 8; h -= 8)
297 	EIGHTOP (dst, src, =);
298       for (; h > 0; h--, src += srcmod, dst += dstmod)
299         ONEOP (dst, src, =);
300       break;
301 
302     case RR_CLEAR:
303       for (; h >= 8; h -= 8)
304 	for (h1 = 0; h1 < 8; h1++, dst += dstmod)
305 	  bzero (dst, w);
306       for (; h > 0; h--, dst += dstmod)
307         bzero (dst, w);
308       break;
309 
310     case RR_XOR:
311       for (; h >= 8; h -= 8)
312 	EIGHTOP (dst, src, ^=);
313       for (; h > 0; h--, src += srcmod, dst += dstmod)
314         ONEOP (dst, src, ^=);
315       break;
316 
317     case RR_COPYINVERTED:
318       for (; h >= 8; h -= 8)
319 	EIGHTOP (dst, src, =~);
320       for (; h > 0; h--, src += srcmod, dst += dstmod)
321         ONEOP (dst, src, =~);
322       break;
323     }
324 }
325 
326 
327 static void
328 cc_8n_cursor(ip, flag)
329 	register struct ite_softc *ip;
330         register int flag;
331 {
332   struct ccite *cci = (struct ccite *) ip->priv;
333   struct ccfb *fb = cci->fb;
334   /* the cursor is always drawn in the last plane */
335   unsigned char *ovplane, opclr, opset;
336 
337   ovplane = fb->fb + (fb->fb_z - 1) * (fb->fb_planesize);
338 
339   if (flag == START_CURSOROPT || flag == END_CURSOROPT)
340     return;
341 
342   /* if drawing into an overlay plane, don't xor, clr and set */
343   if (fb->fb_z > fb->disp_z)
344     {
345       opclr = RR_CLEAR; opset = RR_COPY;
346     }
347   else
348     {
349       opclr = opset = RR_XOR;
350     }
351 
352   if (flag != DRAW_CURSOR)
353     {
354       /* erase it */
355       cc_8n_windowmove (ip->cursor, 0, 0, 1,
356 	  		  ovplane, fb->fb_x + ip->cursorx * ip->ftwidth,
357     			  fb->fb_y + ip->cursory * ip->ftheight,
358     			  fb->fb_width >> 3,
359     			  ip->ftheight, ip->ftwidth, opclr);
360     }
361   if (flag == DRAW_CURSOR || flag == MOVE_CURSOR)
362     {
363       /* draw it */
364       int newx = MIN(ip->curx, ip->cols - 1);
365       cc_8n_windowmove (ip->cursor, 0, 0, 1,
366 	    		  ovplane, fb->fb_x + newx * ip->ftwidth,
367     			  fb->fb_y + ip->cury * ip->ftheight,
368     			  fb->fb_width >> 3,
369     			  ip->ftheight, ip->ftwidth, opset);
370       ip->cursorx = newx;
371       ip->cursory = ip->cury;
372     }
373 }
374 
375 static void
376 cc_8n_putc(ip, c, dy, dx, mode)
377 	register struct ite_softc *ip;
378 	register int dy, dx;
379 	int c, mode;
380 {
381   register int wrr = ((mode == ATTR_INV) ? RR_COPYINVERTED : RR_COPY);
382   struct ccite *cci = (struct ccite *) ip->priv;
383   struct ccfb *fb = cci->fb;
384 
385   if (c >= ip->font_lo && c <= ip->font_hi)
386     {
387       c -= ip->font_lo;
388 
389       cc_8n_windowmove (ip->font, 0, c * ip->ftheight, 1,
390     			  cci->buf_rows[dy].buf,
391 			  dx * ip->ftwidth, 0, fb->fb_width >> 3,
392     			  ip->ftheight, ip->ftwidth, wrr);
393     }
394 }
395 
396 static void
397 cc_8n_clear(ip, sy, sx, h, w)
398 	struct ite_softc *ip;
399 	register int sy, sx, h, w;
400 {
401   struct ccite *cci = (struct ccite *) ip->priv;
402   struct ccfb *fb = cci->fb;
403   int y;
404 
405   for (y = sy; y < sy + h; y++)
406     cc_8n_windowmove (0, 0, 0, 0,
407     			cci->buf_rows[y].buf, sx * ip->ftwidth, 0,
408     		        fb->fb_width >> 3,
409     		        ip->ftheight, w * ip->ftwidth, RR_CLEAR);
410 }
411 
412 /* Note: sx is only relevant for SCROLL_LEFT or SCROLL_RIGHT.  */
413 static void
414 cc_8n_scroll(ip, sy, sx, count, dir)
415         register struct ite_softc *ip;
416         register int sy;
417         int dir, sx, count;
418 {
419   struct ccite *cci = (struct ccite *) ip->priv;
420 
421   if (dir == SCROLL_UP)
422     {
423       int dy = sy - count;
424       int bot = ip->inside_margins ? ip->bottom_margin : ip->rows - 1;
425       int height = bot - sy + 1;
426       int i;
427 
428       for (i = 0; i < height; i++)
429 	swap_rows(ip, sy + i, dy + i);
430     }
431   else if (dir == SCROLL_DOWN)
432     {
433       int dy = sy + count;
434       int bot = ip->inside_margins ? ip->bottom_margin : ip->rows - 1;
435       int height = bot - dy + 1;
436       int i;
437 
438       for (i = (height - 1); i >= 0; i--)
439 	swap_rows(ip, sy + i, dy + i);
440     }
441   else if (dir == SCROLL_RIGHT)
442     {
443       struct ccfb *fb = cci->fb;
444 
445       cc_8n_cursor(ip, ERASE_CURSOR);
446       cc_8n_windowmove(cci->buf_rows[sy].buf,
447 			 sx * ip->ftwidth, 0, fb->fb_width >> 3,
448 			 cci->buf_rows[sy].buf,
449 			 (sx + count) * ip->ftwidth, 0, fb->fb_width >> 3,
450 			 ip->ftheight, (ip->cols - (sx + count)) * ip->ftwidth, RR_COPY);
451     }
452   else
453     {
454       struct ccfb *fb = cci->fb;
455 
456       cc_8n_cursor(ip, ERASE_CURSOR);
457       cc_8n_windowmove(cci->buf_rows[sy].buf,
458 			 sx * ip->ftwidth, 0, fb->fb_width >> 3,
459 			 cci->buf_rows[sy].buf,
460 			 (sx - count) * ip->ftwidth, 0, fb->fb_width >> 3,
461 			 ip->ftheight, (ip->cols - sx) * ip->ftwidth, RR_COPY);
462     }
463 }
464 
465 
466 
467 /*** (M<8)-by-N routines ***/
468 
469 /* NOTE: This routine assumes a cursor overlay plane,
470    but it does allow cursors up to 32 pixels wide.  */
471 static void
472 cc_le32n_cursor(struct ite_softc *ip, int flag)
473 {
474   struct ccite *cci = (struct ccite *) ip->priv;
475   struct ccfb *fb = cci->fb;
476   /* the cursor is always drawn in the last plane */
477   unsigned char *ovplane, opclr, opset;
478 
479   if (flag == START_CURSOROPT || flag == END_CURSOROPT)
480     return;
481 
482   ovplane = fb->fb + (fb->fb_z - 1) * (fb->fb_planesize);
483 
484   if (flag != DRAW_CURSOR)
485     {
486       /* erase the cursor */
487       u_char *pl = ovplane + ((fb->fb_y + ip->cursory * ip->ftheight) * (fb->fb_width >> 3));
488       int ofs = fb->fb_x + ip->cursorx * ip->ftwidth;
489       int h;
490 
491       for (h = ip->ftheight-1; h >= 0; h--)
492 	{
493 	  asm("bfclr %0@{%1:%2}"
494 	      : : "a" (pl), "d" (ofs), "d" (ip->ftwidth));
495 	  pl += fb->fb_width >> 3;
496 	}
497     }
498   if (flag == DRAW_CURSOR || flag == MOVE_CURSOR)
499     {
500       u_char *pl;
501       int ofs, h;
502 
503       /* store the position */
504       ip->cursorx = MIN(ip->curx, ip->cols-1);
505       ip->cursory = ip->cury;
506 
507       /* draw the cursor */
508       pl = ovplane + ((fb->fb_y + ip->cursory * ip->ftheight) * (fb->fb_width >> 3));
509       ofs = fb->fb_x + ip->cursorx * ip->ftwidth;
510 
511       for (h = ip->ftheight-1; h >= 0; h--)
512 	{
513 	  asm("bfset %0@{%1:%2}"
514 	      : : "a" (pl), "d" (ofs), "d" (ip->ftwidth));
515 	  pl += fb->fb_width >> 3;
516 	}
517     }
518 }
519 
520 static void
521 cc_le8n_putc(struct ite_softc *ip, int c, int dy, int dx, int mode)
522 {
523   if (c >= ip->font_lo && c <= ip->font_hi)
524     {
525       struct ccite *cci = (struct ccite *) ip->priv;
526       struct ccfb *fb = cci->fb;
527       u_char *pl = cci->buf_rows[dy].buf;
528       int ofs = dx * ip->ftwidth;
529       u_char *fontp = ip->font + (c - ip->font_lo) * ip->ftheight;
530       int h;
531 
532       if (mode != ATTR_INV)
533 	{
534           for (h = ip->ftheight-1; h >= 0; h--)
535 	    {
536 	      asm("bfins %3,%0@{%1:%2}"
537 	          : : "a" (pl), "d" (ofs), "d" (ip->ftwidth), "d" (*fontp++));
538 	      pl += fb->fb_width >> 3;
539 	    }
540 	}
541       else
542 	{
543           for (h = ip->ftheight-1; h >= 0; h--)
544 	    {
545 	      asm("bfins %3,%0@{%1:%2}"
546 	          : : "a" (pl), "d" (ofs), "d" (ip->ftwidth), "d" (~(*fontp++)));
547 	      pl += fb->fb_width >> 3;
548 	    }
549 	}
550     }
551 }
552 
553 static void
554 cc_le8n_clear(struct ite_softc *ip, int sy, int sx, int h, int w)
555 {
556   struct ccite *cci = (struct ccite *) ip->priv;
557   struct ccfb *fb = cci->fb;
558 
559   if ((sx == 0) && (w == ip->cols))
560     {
561       /* common case: clearing whole lines */
562       while (h--)
563 	{
564           bzero(cci->buf_rows[sy].buf, (fb->fb_width >> 3) * ip->ftheight);
565 	  sy++;
566 	}
567     }
568   else
569     {
570       /* clearing only part of a line */
571       /* XXX could be optimized MUCH better, but is it worth the trouble? */
572       while (h--)
573 	{
574 	  u_char *pl = cci->buf_rows[sy].buf;
575           int ofs = sx * ip->ftwidth;
576 	  int i, j;
577 	  for (i = w-1; i >= 0; i--)
578 	    {
579 	      u_char *ppl = pl;
580               for (j = ip->ftheight-1; j >= 0; j--)
581 	        {
582 	          asm("bfclr %0@{%1:%2}"
583 	              : : "a" (ppl), "d" (ofs), "d" (ip->ftwidth));
584 	          ppl += fb->fb_width >> 3;
585 	        }
586 	      ofs += ip->ftwidth;
587 	    }
588 	  sy++;
589 	}
590     }
591 }
592 
593 /* Note: sx is only relevant for SCROLL_LEFT or SCROLL_RIGHT.  */
594 static void
595 cc_le8n_scroll(ip, sy, sx, count, dir)
596         register struct ite_softc *ip;
597         register int sy;
598         int dir, sx, count;
599 {
600   if (dir == SCROLL_UP)
601     {
602       int dy = sy - count;
603       int bot = ip->inside_margins ? ip->bottom_margin : ip->rows - 1;
604       int height = bot - sy + 1;
605       int i;
606 
607       for (i = 0; i < height; i++)
608 	swap_rows(ip, sy + i, dy + i);
609     }
610   else if (dir == SCROLL_DOWN)
611     {
612       int dy = sy + count;
613       int bot = ip->inside_margins ? ip->bottom_margin : ip->rows - 1;
614       int height = bot - dy + 1;
615       int i;
616 
617       for (i = (height - 1); i >= 0; i--)
618 	swap_rows(ip, sy + i, dy + i);
619     }
620   else if (dir == SCROLL_RIGHT)
621     {
622       struct ccite *cci = (struct ccite *) ip->priv;
623       struct ccfb *fb = cci->fb;
624       u_char *pl = cci->buf_rows[sy].buf;
625       int sofs = (ip->cols - count) * ip->ftwidth;
626       int dofs = (ip->cols) * ip->ftwidth;
627       int i, j;
628 
629       cc_le32n_cursor(ip, ERASE_CURSOR);
630       for (j = ip->ftheight-1; j >= 0; j--)
631 	{
632 	  int sofs2 = sofs, dofs2 = dofs;
633 	  for (i = (ip->cols - (sx + count))-1; i >= 0; i--)
634 	    {
635 	      int t;
636 	      sofs2 -= ip->ftwidth;
637 	      dofs2 -= ip->ftwidth;
638 	      asm("bfextu %1@{%2:%3},%0"
639 	          : "=d" (t)
640 		  : "a" (pl), "d" (sofs2), "d" (ip->ftwidth));
641 	      asm("bfins %3,%0@{%1:%2}"
642 	          : : "a" (pl), "d" (dofs2), "d" (ip->ftwidth), "d" (t));
643 	    }
644 	  pl += fb->fb_width >> 3;
645 	}
646     }
647   else /* SCROLL_LEFT */
648     {
649       struct ccite *cci = (struct ccite *) ip->priv;
650       struct ccfb *fb = cci->fb;
651       u_char *pl = cci->buf_rows[sy].buf;
652       int sofs = (sx) * ip->ftwidth;
653       int dofs = (sx - count) * ip->ftwidth;
654       int i, j;
655 
656       cc_le32n_cursor(ip, ERASE_CURSOR);
657       for (j = ip->ftheight-1; j >= 0; j--)
658 	{
659 	  int sofs2 = sofs, dofs2 = dofs;
660 	  for (i = (ip->cols - sx)-1; i >= 0; i--)
661 	    {
662 	      int t;
663 	      asm("bfextu %1@{%2:%3},%0"
664 	          : "=d" (t)
665 		  : "a" (pl), "d" (sofs2), "d" (ip->ftwidth));
666 	      asm("bfins %3,%0@{%1:%2}"
667 	          : : "a" (pl), "d" (dofs2), "d" (ip->ftwidth), "d" (t));
668 	      sofs2 += ip->ftwidth;
669 	      dofs2 += ip->ftwidth;
670 	    }
671 	  pl += fb->fb_width >> 3;
672 	}
673     }
674 }
675 
676 #endif
677