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