Date: Wed, 16 Apr 1997 08:09:30 +0200 (MET DST)
From: Jesper Skov <jskov@cs.auc.dk>
To: linux-m68k@phil.uni-sb.de
Subject: L68K: CopCon update
Sender: owner-linux-m68k@phil.uni-sb.de
Reply-To: linux-m68k@phil.uni-sb.de

Hey

This copcon diff is relative to 2.1.33, and fixes the problem with
lace and hopefully also its stability problem on some machines.

-- Jesper

diff -u --recursive -B --exclude-from=/pack/kernel/tools/lib/diff-excludes -P /pack/kernel/dist/linux-2.1.33/arch/m68k/amiga/amifb.c ./arch/m68k/amiga/amifb.c
--- /pack/kernel/dist/linux-2.1.33/arch/m68k/amiga/amifb.c	Fri Mar 14 19:09:53 1997
+++ ./arch/m68k/amiga/amifb.c	Tue Apr 15 17:56:47 1997
@@ -57,6 +57,12 @@
 #include <asm/amigaints.h>
 #include <linux/fb.h>
 
+#ifdef CONFIG_COPCON
+#define MAXCOPCONROWS 128
+extern u_char *line_ptrs[MAXCOPCONROWS];
+extern short line_ptrs_used;
+#endif
+
 #define DEBUG
 
 #if !defined(CONFIG_AMIFB_OCS) && !defined(CONFIG_AMIFB_ECS) && !defined(CONFIG_AMIFB_AGA)
@@ -1127,7 +1133,11 @@
 	 * Don't change the order, build_copper()/rebuild_copper() rely on this
 	 */
 
+#if !defined(CONFIG_COPCON)
 #define COPLISTSIZE (sizeof(copins)*64)
+#else
+#define COPLISTSIZE (sizeof(copins)*MAXCOPCONROWS*(8+1)*2)
+#endif
 
 enum {
 	cop_wait, cop_bplcon0,
@@ -3552,8 +3562,8 @@
 	struct amiga_fb_par *par = &currentpar;
 	copins *copl, *cops;
 	u_short line, h_end1, h_end2;
-	short i;
-	u_long p;
+	int i, j;
+	u_long p, offset;
 
 	if (IS_AGA && maxfmode + par->clk_shift == 0)
 		h_end1 = par->diwstrt_h-64;
@@ -3565,10 +3575,11 @@
 
 	copl = copdisplay.rebuild[1];
 	p = par->bplpt0;
+#if !defined(CONFIG_COPCON)     
 	if (par->vmode & FB_VMODE_YWRAP) {
 		if ((par->vyres-par->yoffset) != 1 || !mod2(par->diwstrt_v)) {
 			if (par->yoffset > par->vyres-par->yres) {
-				for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+				for (i = 0; i < par->bpp; i++, p += par->next_plane) {
 					(copl++)->l = CMOVE(highw(p), bplpt[i]);
 					(copl++)->l = CMOVE2(loww(p), bplpt[i]);
 				}
@@ -3585,12 +3596,63 @@
 			}
 		} else p = par->bplpt0wrap;
 	}
-	for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+	for (i = 0; i < par->bpp; i++, p += par->next_plane) {
 		(copl++)->l = CMOVE(highw(p), bplpt[i]);
 		(copl++)->l = CMOVE2(loww(p), bplpt[i]);
 	}
-	copl->l = CEND;
 
+#else  /* CONFIG_COPCON */
+	if(do_blank <= 0){
+
+		line = par->diwstrt_v - 2;
+		p = (u_long) line_ptrs[0] + (mod2(line) ? par->next_line : 0);
+		for (i = 0; i < par->bpp; i++, p += par->next_plane) {
+			(copl++)->l = CMOVE(highw(p), bplpt[i]);
+			(copl++)->l = CMOVE2(loww(p), bplpt[i]);
+		}
+		
+		for(j = 1; j < line_ptrs_used; j++){
+			line += par->yoffset << par->line_shift;
+			p = (u_long) line_ptrs[j] + (mod2(line) ? par->next_line : 0);
+			
+			if (line >= 512) {
+				if((line - (par->yoffset << par->line_shift)) >= 510)
+					line -= 512;
+				else {
+					(copl++)->l = CWAIT(h_end1, 510);
+					line -= 512;
+				}
+			}
+			
+			if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0){
+				(copl++)->l = CWAIT(h_end1, line);
+			} else {
+				(copl++)->l = CWAIT(h_end2, line);
+			}
+			
+			for (i = 0; i < par->bpp; i++, p += par->next_plane) {
+				(copl++)->l = CMOVE(highw(p), bplpt[i]);
+				(copl++)->l = CMOVE2(loww(p), bplpt[i]);
+			}
+			
+		}
+		line += (par->yoffset + 1) << par->line_shift;
+		if (line >= 512) {
+			if((line - (par->yoffset << par->line_shift)) >= 510)
+				line -= 512;
+			else {
+				(copl++)->l = CWAIT(h_end1, 510);
+				line -= 512;
+			}
+		}
+		if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
+			(copl++)->l = CWAIT(h_end1, line);
+		else
+			(copl++)->l = CWAIT(h_end2, line);
+	}
+#endif /* CONFIG_COPCON */
+	copl->l = CEND;
+		
 	if (par->bplcon0 & BPC0_LACE) {
 		cops = copdisplay.rebuild[0];
 		p = par->bplpt0;
@@ -3598,10 +3660,11 @@
 			p -= par->next_line;
 		else
 			p += par->next_line;
+#if !defined(CONFIG_COPCON)     
 		if (par->vmode & FB_VMODE_YWRAP) {
 			if ((par->vyres-par->yoffset) != 1 || mod2(par->diwstrt_v)) {
 				if (par->yoffset > par->vyres-par->yres+1) {
-					for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+					for (i = 0; i < par->bpp; i++, p += par->next_plane) {
 						(cops++)->l = CMOVE(highw(p), bplpt[i]);
 						(cops++)->l = CMOVE2(loww(p), bplpt[i]);
 					}
@@ -3622,10 +3685,60 @@
 				}
 			} else p = par->bplpt0wrap - par->next_line;
 		}
-		for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
+		for (i = 0; i < par->bpp; i++, p += par->next_plane) {
 			(cops++)->l = CMOVE(highw(p), bplpt[i]);
 			(cops++)->l = CMOVE2(loww(p), bplpt[i]);
 		}
+#else  /* CONFIG_COPCON */
+		if(do_blank <= 0){
+
+			offset = par->next_line;
+
+			line = par->diwstrt_v - 1;
+
+			p = (u_long) line_ptrs[0]  + (mod2(line) ? offset : 0);
+			for (i = 0; i < par->bpp; i++, p += par->next_plane) {
+				(cops++)->l = CMOVE(highw(p), bplpt[i]);
+				(cops++)->l = CMOVE2(loww(p), bplpt[i]);
+			}
+
+			for(j = 1; j < line_ptrs_used; j++){
+				line += par->yoffset << par->line_shift;
+				
+				p = ((u_long) line_ptrs[j]) + (mod2(line) ? offset : 0);			
+				if (line >= 512) {
+					if((line - (par->yoffset << par->line_shift)) >= 510)
+						line -= 512;
+					else {
+						(cops++)->l = CWAIT(h_end1, 510);
+						line -= 512;
+					}
+				}
+				if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
+					(cops++)->l = CWAIT(h_end1, line);
+				else
+					(cops++)->l = CWAIT(h_end2, line);
+				
+				for (i = 0; i < par->bpp; i++, p += par->next_plane) {
+					(cops++)->l = CMOVE(highw(p), bplpt[i]);
+					(cops++)->l = CMOVE2(loww(p), bplpt[i]);
+				}
+			}
+			line += (par->yoffset + 1) << par->line_shift;
+			if (line >= 512) {
+				if((line - (par->yoffset << par->line_shift)) >= 510)
+					line -= 512;
+				else {
+					(cops++)->l = CWAIT(h_end1, 510);
+					line -= 512;
+				}
+			}
+			if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
+				(cops++)->l = CWAIT(h_end1, line);
+			else
+				(cops++)->l = CWAIT(h_end2, line);
+		}
+#endif /* CONFIG_COPCON */
 		cops->l = CEND;
 	}
 }
diff -u --recursive -B --exclude-from=/pack/kernel/tools/lib/diff-excludes -P /pack/kernel/dist/linux-2.1.33/arch/m68k/amiga/config.c ./arch/m68k/amiga/config.c
--- /pack/kernel/dist/linux-2.1.33/arch/m68k/amiga/config.c	Sun Apr  6 16:38:04 1997
+++ ./arch/m68k/amiga/config.c	Mon Apr 14 18:47:10 1997
@@ -76,6 +76,9 @@
 static void amiga_reset (void);
 static void amiga_waitbut(void);
 extern struct consw fb_con;
+#ifdef CONFIG_COPCON
+extern struct consw cop_con;
+#endif
 extern struct fb_info *amiga_fb_init(long *);
 extern void zorro_init(void);
 static void amiga_savekmsg_init(void);
@@ -336,7 +339,11 @@
 #endif
   mach_reset           = amiga_reset;
   waitbut              = amiga_waitbut;
+#ifdef CONFIG_COPCON
+  conswitchp           = &cop_con;
+#else
   conswitchp           = &fb_con;
+#endif
   mach_fb_init         = amiga_fb_init;
   mach_debug_init      = amiga_debug_init;
   mach_video_setup     = amiga_video_setup;
diff -u --recursive -B --exclude-from=/pack/kernel/tools/lib/diff-excludes -P /pack/kernel/dist/linux-2.1.33/arch/m68k/config.in ./arch/m68k/config.in
--- /pack/kernel/dist/linux-2.1.33/arch/m68k/config.in	Mon Apr 14 11:29:27 1997
+++ ./arch/m68k/config.in	Mon Apr 14 18:47:10 1997
@@ -217,6 +217,7 @@
 if [ "$CONFIG_AMIGA" = "y" ]; then
   dep_tristate 'Multiface Card III parallel support' CONFIG_MULTIFACE_III_LP $CONFIG_PRINTER
   tristate 'Amiga mouse support' CONFIG_AMIGAMOUSE
+  bool 'Amiga Copper Console' CONFIG_COPCON
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
   tristate 'Atari mouse support' CONFIG_ATARIMOUSE
diff -u --recursive -B --exclude-from=/pack/kernel/tools/lib/diff-excludes -P /pack/kernel/dist/linux-2.1.33/arch/m68k/console/Makefile ./arch/m68k/console/Makefile
--- /pack/kernel/dist/linux-2.1.33/arch/m68k/console/Makefile	Sat Sep  7 22:47:28 1996
+++ ./arch/m68k/console/Makefile	Tue Apr 15 18:10:18 1997
@@ -10,12 +10,17 @@
 GSPH2C = gspahextoc
 
 L_TARGET = console.a
-L_OBJS = fbcon.o fonts.o font_8x16.o font_8x8.o pearl_8x8.o
+L_OBJS = fbcon.o fonts.o font_8x16.o font_8x8.o pearl_8x8.o
 M_OBJS =
 
 ifdef CONFIG_AMIGA_GSP
 L_OBJS := $(L_OBJS) gspcon.o gspcore.o
 endif
+
+ifdef CONFIG_COPCON
+L_OBJS := $(L_OBJS) copcon.o
+endif
+
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive -B --exclude-from=/pack/kernel/tools/lib/diff-excludes -P /pack/kernel/dist/linux-2.1.33/arch/m68k/console/copcon.c ./arch/m68k/console/copcon.c
--- /pack/kernel/dist/linux-2.1.33/arch/m68k/console/copcon.c	Thu Jan  1 00:00:00 1970
+++ ./arch/m68k/console/copcon.c	Tue Apr 15 16:30:44 1997
@@ -0,0 +1,1282 @@
+/*
+ * linux/arch/m68k/console/copcon.c -- Low level copper based console driver
+ *
+ *    Copyright (C) 1996-1997 Jesper Skov (jskov@cs.auc.dk)
+ *
+ * This driver is based on fbcon.c by Geert Uytterhoeven et al.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * This driver is using the Amiga Copper to display and control the rows of
+ * the console individually. The result of this is the ability to scroll
+ * (row wise) the whole (or parts) of the display without burdening the CPU
+ * or the Amiga Blitter. Still the display is scrolled at a much higher speed
+ * than can be obtained by using either the CPU or the Blitter.
+ * 
+ * The theory of operation is simply to keep an array with pointers to the
+ * start of each row's bitmap memory. Whenever the display is changed, a
+ * copper list is build based on this array; in steps of the fontheight, the
+ * Copper is instructed to update the bitplane pointers to the bitmap memory
+ * of the row assigned to this vertical position.
+ * 
+ * Scrolling can now be implemented by exchanging the pointers in the array,
+ * and subsequently updating the copper list. A process probably taking less
+ * than one raster line of precious CPU time (even with my sloppy code).
+ *
+ */
+
+/* 
+ * TO DO:
+ *
+ * Presently the lines_lock variable is used to prevent scrolling while
+ * switching virtual consoles. This should be handled in some other way as
+ * the display is sure to be fucked up by such a cancelled scroll operation.
+ * A solution would be to cli/sti the routine that cleans up the display when
+ * changing consoles, but it is a pretty slow thing... Ideas are welcome.
+ *
+ * If scrolling out more than half the number of lines, the top and bottom
+ * line ptrs can simply be exchanged. Don't know if the gain is worth the
+ * hassle though.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/kd.h>
+#include <linux/malloc.h>
+
+#include <asm/setup.h>
+#include <asm/irq.h>
+#ifdef CONFIG_AMIGA
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#endif /* CONFIG_AMIGA */
+#ifdef CONFIG_ATARI
+#include <asm/atariints.h>
+#endif
+#ifdef CONFIG_FB_CYBER
+#include "../amiga/s3blit.h"
+#endif
+#include <linux/fb.h>
+#include <asm/font.h>
+#include <asm/machdep.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "../../../drivers/char/vt_kern.h"   /* vt_cons and vc_resize_con() */
+#include "../../../drivers/char/console_struct.h"
+
+/* Import console_blanked from console.c */
+
+extern int console_blanked;
+
+struct fb_info *fb_info;
+struct display *disp;
+
+#define MAXCOPCONROWS 128
+u_char *line_ptrs[MAXCOPCONROWS];
+u_char *line_ptrs_roll[MAXCOPCONROWS];
+short line_ptrs_used;
+u_char lines_lock;		/* Lock array from access during update */
+
+static int cursor_drawn = 0;
+
+#define CURSOR_DRAW_DELAY           (2)
+
+/* # VBL ints between cursor state changes */
+#define AMIGA_CURSOR_BLINK_RATE   (20)
+
+static int vbl_cursor_cnt = 0;
+static int cursor_on = 0;
+static int cursor_blink_rate;
+
+static __inline__ int CURSOR_UNDRAWN(void)
+{
+	int cursor_was_drawn;
+	vbl_cursor_cnt = 0;
+	cursor_was_drawn = cursor_drawn;
+	cursor_drawn = 0;
+	return(cursor_was_drawn);
+}
+
+   /*
+    *    Attribute Decoding
+    */
+
+/* Color */
+#define attr_fgcol(p,conp)    \
+	(((conp)->vc_attr >> ((p)->inverse ? 4 : 0)) & 0x0f)
+#define attr_bgcol(p,conp)    \
+	(((conp)->vc_attr >> ((p)->inverse ? 0 : 4)) & 0x0f)
+#define	attr_bgcol_ec(p,conp) \
+	(((conp)->vc_video_erase_char >> ((p)->inverse ? 8 : 12)) & 0x0f)
+
+
+   /*
+    *    Scroll Method
+    */
+
+#define SCROLL_YWRAP          (0)
+#define SCROLL_YPAN           (1)
+#define SCROLL_YMOVE          (2)
+
+#define divides(a, b)         ((!(a) || (b)%(a)) ? 0 : 1)
+
+   /*
+    *    Interface used by the world
+    */
+
+static u_long copcon_startup(u_long kmem_start, const char **display_desc);
+static void copcon_init(struct vc_data *conp);
+static int copcon_deinit(struct vc_data *conp);
+static int copcon_changevar(int con);
+static int copcon_clear(struct vc_data *conp, int sy, int sx, int height,
+                        int width);
+static int copcon_putc(struct vc_data *conp, int c, int yy, int xx);
+static int copcon_putcs(struct vc_data *conp, const char *s, int count, int yy,
+                        int xx);
+static int copcon_cursor(struct vc_data *conp, int mode);
+static int copcon_scroll(struct vc_data *conp, int t, int b, int dir, int count);
+static int copcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+                        int height, int width);
+static int copcon_switch(struct vc_data *conp);
+static int copcon_blank(int blank);
+static int copcon_get_font(struct vc_data *conp, int *w, int *h, char *data);
+static int copcon_set_font(struct vc_data *conp, int w, int h, char *data);
+static int copcon_set_palette(struct vc_data *conp, unsigned char *table);
+
+   /*
+    *    Internal routines
+    */
+
+static void copcon_update_lines(void);
+static void copcon_setup(int con, int setcol, int cls);
+static __inline__ void *mymemclear_small(void *s, size_t count);
+static __inline__ void *mymemclear(void *s, size_t count);
+static __inline__ void *mymemset(void *s, size_t count);
+static __inline__ void *mymemmove(void *d, void *s, size_t count);
+static __inline__ void *mymemexg(void *d, void *s, size_t count);
+static __inline__ void fast_memmove(char *dst, char *src, size_t size);
+static void copcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp);
+static __inline__ void updatescrollmode(struct display *p);
+
+   /*
+    *    Color Planes
+    */
+
+static void bmove_plan(struct display *p, int sy, int sx, int dy, int dx,
+                       int height, int width);
+static void clear_plan(struct vc_data *conp, struct display *p, int sy, int sx,
+                       int height, int width);
+static void putc_plan(struct vc_data *conp, struct display *p, int c, int yy,
+                      int xx);
+static void putcs_plan(struct vc_data *conp, struct display *p, const char *s,
+                       int count, int yy, int xx);
+static void rev_char_plan(struct display *p, int xx, int yy);
+
+   /*
+    *    `switch' for the Low Level Operations
+    */
+
+struct display_switch {
+	void (*bmove)(struct display *p, int sy, int sx, int dy, int dx,
+		      int height, int width);
+	void (*clear)(struct vc_data *conp, struct display *p, int sy, int sx,
+		      int height, int width);
+	void (*putc)(struct vc_data *conp, struct display *p, int c, int yy,
+		     int xx);
+	void (*putcs)(struct vc_data *conp, struct display *p, const char *s,
+		      int count, int yy, int xx);
+	void (*rev_char)(struct display *p, int xx, int yy);
+};
+
+struct display_switch dispsw_plan = {
+   bmove_plan, clear_plan, putc_plan, putcs_plan, rev_char_plan
+};
+
+static u_long copcon_startup(u_long kmem_start, const char **display_desc)
+{
+	int irqres = 0;
+
+	if (!MACH_IS_AMIGA) {
+		*display_desc = "copcon_startup: Only runs on Amiga hardware!";
+		return(kmem_start);
+	}
+
+	fb_info = mach_fb_init(&kmem_start);
+	disp = fb_info->disp;
+	*display_desc = fb_info->modename;
+	fb_info->changevar = &copcon_changevar;
+	
+	cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE;
+	irqres = request_irq(IRQ_AMIGA_VERTB, copcon_vbl_handler, 0,
+			     "console/cursor", copcon_vbl_handler);
+	
+	if (irqres)
+		panic("copcon_startup: Couldn't add vblank interrupt");
+	
+	return(kmem_start);
+}
+
+static void copcon_init(struct vc_data *conp)
+{
+	int unit = conp->vc_num;
+
+	if (unit)
+		disp[unit] = disp[0];
+	disp[unit].conp = conp;
+	copcon_setup(unit, 1, 0);
+}
+
+
+static int copcon_deinit(struct vc_data *conp)
+{
+	disp[conp->vc_num].conp = 0;
+	return(0);
+}
+
+static int copcon_changevar(int con)
+{
+	copcon_setup(con, 1, 1);
+	return(0);
+}
+
+static void copcon_setup(int con, int setcol, int cls)
+{
+	struct display *p = &disp[con];
+	struct vc_data *conp = p->conp;
+	short i;
+	u_char *bpl_ptr;
+
+	static int once_in = 0;
+
+	p->var.xoffset = 0;
+
+	if (!fb_info->fontname[0] ||
+	    !findsoftfont(fb_info->fontname, &p->fontwidth, &p->fontheight,
+			  &p->fontdata) || p->fontwidth != 8)
+		getdefaultfont(p->var.xres, p->var.yres, NULL, &p->fontwidth,
+			       &p->fontheight, &p->fontdata);
+	if (p->fontwidth != 8)
+		panic("copcon_setup: No support for fontwidth != 8");
+
+	conp->vc_cols = p->var.xres / p->fontwidth;
+ 	conp->vc_rows = p->var.yres / p->fontheight;
+	
+	/* fbcon needs to know fontheight for copper update */
+	p->var.yoffset = p->fontheight;
+
+	p->vrows = p->var.yres_virtual/p->fontheight;
+	conp->vc_can_do_color = p->var.bits_per_pixel != 1;
+	
+	if (p->type == FB_TYPE_PLANES) {
+		/* PLANES */
+		if (p->line_length)
+			p->next_line = p->line_length;
+		else
+			p->next_line = p->var.xres_virtual>>3;
+		p->next_plane = p->var.yres_virtual*p->next_line;
+		p->dispsw = &dispsw_plan;
+	} else	{
+		printk("copcon_setup: Only PLANES supported!\n");
+	}
+
+
+	if(!once_in){
+		/* We won't init this array more than once */
+		once_in++;
+
+
+		line_ptrs_used = conp->vc_rows;
+		if(line_ptrs_used > MAXCOPCONROWS)
+			panic("copcon_setup: Too many rows for current line_ptrs[]");
+	
+
+		/* Initialize line_ptrs array */
+		bpl_ptr = p->screen_base;
+		for(i = 0; i < (conp->vc_rows); i++){
+			line_ptrs[i] = bpl_ptr;
+			bpl_ptr += p->next_line * p->fontheight;
+		}
+
+		lines_lock = 0;
+	}
+
+	if (setcol) {
+		p->fgcol = (p->var.bits_per_pixel > 2) ?
+			7 : (1<<p->var.bits_per_pixel)-1;
+		p->bgcol = 0;
+	}
+
+	if (cls)
+		vc_resize_con(conp->vc_rows, conp->vc_cols, con);
+}
+
+static __inline__ void updatescrollmode(struct display *p)
+{
+   if (divides(p->ywrapstep, p->fontheight) &&
+       divides(p->fontheight, p->var.yres_virtual))
+      p->scrollmode = SCROLL_YWRAP;
+   else if (divides(p->ypanstep, p->fontheight) &&
+            p->var.yres_virtual >= p->var.yres+p->fontheight)
+      p->scrollmode = SCROLL_YPAN;
+   else
+      p->scrollmode = SCROLL_YMOVE;
+}
+
+static void copcon_vbl_handler(int irq, void *dummy, struct pt_regs *fp)
+{
+	struct display *p;
+
+	if (!cursor_on)
+		return;
+
+	if (vbl_cursor_cnt && --vbl_cursor_cnt == 0) {
+		/* Here no check is possible for console changing. The console
+		 * switching code should set vbl_cursor_cnt to an appropriate
+		 * value.
+		 */
+		p = &disp[fg_console];
+		p->dispsw->rev_char(p, p->cursor_x, p->cursor_y);
+		cursor_drawn ^= 1;
+		vbl_cursor_cnt = cursor_blink_rate;
+	}
+}
+
+/* ================================================================= */
+/*                      Utility Assembler Functions                  */
+/* ================================================================= */
+
+
+/* ====================================================================== */
+
+/* Those of a delicate disposition might like to skip the next couple of
+ * pages.
+ *
+ * These functions are drop in replacements for memmove and
+ * memset(_, 0, _). However their five instances add at least a kilobyte
+ * to the object file. You have been warned.
+ *
+ * Not a great fan of assembler for the sake of it, but I think
+ * that these routines are at least 10 times faster than their C
+ * equivalents for large blits, and that's important to the lowest level of
+ * a graphics driver. Question is whether some scheme with the blitter
+ * would be faster. I suspect not for simple text system - not much
+ * asynchrony.
+ *
+ * Code is very simple, just gruesome expansion. Basic strategy is to
+ * increase data moved/cleared at each step to 16 bytes to reduce
+ * instruction per data move overhead. movem might be faster still
+ * For more than 15 bytes, we try to align the write direction on a
+ * longword boundary to get maximum speed. This is even more gruesome.
+ * Unaligned read/write used requires 68020+ - think this is a problem?
+ *
+ * Sorry!
+ */
+
+
+/* ++roman: I've optimized Robert's original versions in some minor
+ * aspects, e.g. moveq instead of movel, let gcc choose the registers,
+ * use movem in some places...
+ * For other modes than 1 plane, lots of more such assembler functions
+ * were needed (e.g. the ones using movep or expanding color values).
+ */
+
+/* ++andreas: more optimizations:
+   subl #65536,d0 replaced by clrw d0; subql #1,d0 for dbcc
+   addal is faster than addaw
+   movep is rather expensive compared to ordinary move's
+   some functions rewritten in C for clarity, no speed loss */
+
+static __inline__ void *mymemclear_small(void *s, size_t count)
+{
+   if (!count)
+      return(0);
+
+   __asm__ __volatile__(
+         "lsrl   #1,%1 ; jcc 1f ; moveb %2,%0@-\n\t"
+      "1: lsrl   #1,%1 ; jcc 1f ; movew %2,%0@-\n\t"
+      "1: lsrl   #1,%1 ; jcc 1f ; movel %2,%0@-\n\t"
+      "1: lsrl   #1,%1 ; jcc 1f ; movel %2,%0@- ; movel %2,%0@-\n\t"
+      "1: subql  #1,%1 ; jcs 3f\n\t"
+      "2: moveml %2/%3/%4/%5,%0@-\n\t"
+         "dbra %1,2b\n\t"
+      "3:"
+         : "=a" (s), "=d" (count)
+         :  "d" (0), "d" (0), "d" (0), "d" (0),
+            "0" ((char *)s+count), "1" (count)
+  );
+
+   return(0);
+}
+
+
+static __inline__ void *mymemclear(void *s, size_t count)
+{
+   if (!count)
+      return(0);
+
+   if (count < 16) {
+      __asm__ __volatile__(
+            "lsrl   #1,%1 ; jcc 1f ; clrb %0@+\n\t"
+         "1: lsrl   #1,%1 ; jcc 1f ; clrw %0@+\n\t"
+         "1: lsrl   #1,%1 ; jcc 1f ; clrl %0@+\n\t"
+         "1: lsrl   #1,%1 ; jcc 1f ; clrl %0@+ ; clrl %0@+\n\t"
+         "1:"
+            : "=a" (s), "=d" (count)
+            : "0" (s), "1" (count)
+     );
+   } else {
+      long tmp;
+      __asm__ __volatile__(
+            "movel %1,%2\n\t"
+            "lsrl   #1,%2 ; jcc 1f ; clrb %0@+ ; subqw #1,%1\n\t"
+            "lsrl   #1,%2 ; jcs 2f\n\t"  /* %0 increased=>bit 2 switched*/
+            "clrw   %0@+  ; subqw  #2,%1 ; jra 2f\n\t"
+         "1: lsrl   #1,%2 ; jcc 2f\n\t"
+            "clrw   %0@+  ; subqw  #2,%1\n\t"
+         "2: movew %1,%2; lsrl #2,%1 ; jeq 6f\n\t"
+            "lsrl   #1,%1 ; jcc 3f ; clrl %0@+\n\t"
+         "3: lsrl   #1,%1 ; jcc 4f ; clrl %0@+ ; clrl %0@+\n\t"
+         "4: subql  #1,%1 ; jcs 6f\n\t"
+         "5: clrl %0@+; clrl %0@+ ; clrl %0@+ ; clrl %0@+\n\t"
+            "dbra %1,5b   ; clrw %1; subql #1,%1; jcc 5b\n\t"
+         "6: movew %2,%1; btst #1,%1 ; jeq 7f ; clrw %0@+\n\t"
+         "7:            ; btst #0,%1 ; jeq 8f ; clrb %0@+\n\t"
+         "8:"
+            : "=a" (s), "=d" (count), "=d" (tmp)
+            : "0" (s), "1" (count)
+     );
+   }
+
+   return(0);
+}
+
+
+static __inline__ void *mymemset(void *s, size_t count)
+{
+   if (!count)
+      return(0);
+
+   __asm__ __volatile__(
+         "lsrl   #1,%1 ; jcc 1f ; moveb %2,%0@-\n\t"
+      "1: lsrl   #1,%1 ; jcc 1f ; movew %2,%0@-\n\t"
+      "1: lsrl   #1,%1 ; jcc 1f ; movel %2,%0@-\n\t"
+      "1: lsrl   #1,%1 ; jcc 1f ; movel %2,%0@- ; movel %2,%0@-\n\t"
+      "1: subql  #1,%1 ; jcs 3f\n\t"
+      "2: moveml %2/%3/%4/%5,%0@-\n\t"
+         "dbra %1,2b\n\t"
+      "3:"
+         : "=a" (s), "=d" (count)
+         :  "d" (-1), "d" (-1), "d" (-1), "d" (-1),
+            "0" ((char *) s + count), "1" (count)
+  );
+
+   return(0);
+}
+
+
+static __inline__ void *mymemmove(void *d, void *s, size_t count)
+{
+   if (d < s) {
+      if (count < 16) {
+         __asm__ __volatile__(
+               "lsrl   #1,%2 ; jcc 1f ; moveb %1@+,%0@+\n\t"
+            "1: lsrl   #1,%2 ; jcc 1f ; movew %1@+,%0@+\n\t"
+            "1: lsrl   #1,%2 ; jcc 1f ; movel %1@+,%0@+\n\t"
+            "1: lsrl   #1,%2 ; jcc 1f ; movel %1@+,%0@+ ; movel %1@+,%0@+\n\t"
+            "1:"
+               : "=a" (d), "=a" (s), "=d" (count)
+               : "0" (d), "1" (s), "2" (count)
+        );
+      } else {
+         long tmp;
+         __asm__ __volatile__(
+               "movel  %0,%3\n\t"
+               "lsrl   #1,%3 ; jcc 1f ; moveb %1@+,%0@+ ; subqw #1,%2\n\t"
+               "lsrl   #1,%3 ; jcs 2f\n\t"  /* %0 increased=>bit 2 switched*/
+               "movew  %1@+,%0@+  ; subqw  #2,%2 ; jra 2f\n\t"
+            "1: lsrl   #1,%3 ; jcc 2f\n\t"
+               "movew  %1@+,%0@+  ; subqw  #2,%2\n\t"
+            "2: movew  %2,%-; lsrl #2,%2 ; jeq 6f\n\t"
+               "lsrl   #1,%2 ; jcc 3f ; movel %1@+,%0@+\n\t"
+            "3: lsrl   #1,%2 ; jcc 4f ; movel %1@+,%0@+ ; movel %1@+,%0@+\n\t"
+            "4: subql  #1,%2 ; jcs 6f\n\t"
+            "5: movel  %1@+,%0@+;movel %1@+,%0@+\n\t"
+               "movel  %1@+,%0@+;movel %1@+,%0@+\n\t"
+               "dbra   %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t"
+            "6: movew  %+,%2; btst #1,%2 ; jeq 7f ; movew %1@+,%0@+\n\t"
+            "7:              ; btst #0,%2 ; jeq 8f ; moveb %1@+,%0@+\n\t"
+            "8:"
+               : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp)
+               : "0" (d), "1" (s), "2" (count)
+        );
+      }
+   } else {
+      if (count < 16) {
+         __asm__ __volatile__(
+               "lsrl   #1,%2 ; jcc 1f ; moveb %1@-,%0@-\n\t"
+            "1: lsrl   #1,%2 ; jcc 1f ; movew %1@-,%0@-\n\t"
+            "1: lsrl   #1,%2 ; jcc 1f ; movel %1@-,%0@-\n\t"
+            "1: lsrl   #1,%2 ; jcc 1f ; movel %1@-,%0@- ; movel %1@-,%0@-\n\t"
+            "1:"
+               : "=a" (d), "=a" (s), "=d" (count)
+               : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count)
+        );
+      } else {
+         long tmp;
+         __asm__ __volatile__(
+               "movel %0,%3\n\t"
+               "lsrl   #1,%3 ; jcc 1f ; moveb %1@-,%0@- ; subqw #1,%2\n\t"
+               "lsrl   #1,%3 ; jcs 2f\n\t"  /* %0 increased=>bit 2 switched*/
+               "movew  %1@-,%0@-  ; subqw  #2,%2 ; jra 2f\n\t"
+            "1: lsrl   #1,%3 ; jcc 2f\n\t"
+               "movew  %1@-,%0@-  ; subqw  #2,%2\n\t"
+            "2: movew %2,%-; lsrl #2,%2 ; jeq 6f\n\t"
+               "lsrl   #1,%2 ; jcc 3f ; movel %1@-,%0@-\n\t"
+            "3: lsrl   #1,%2 ; jcc 4f ; movel %1@-,%0@- ; movel %1@-,%0@-\n\t"
+            "4: subql  #1,%2 ; jcs 6f\n\t"
+            "5: movel %1@-,%0@-;movel %1@-,%0@-\n\t"
+               "movel %1@-,%0@-;movel %1@-,%0@-\n\t"
+               "dbra %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t"
+            "6: movew %+,%2; btst #1,%2 ; jeq 7f ; movew %1@-,%0@-\n\t"
+            "7:              ; btst #0,%2 ; jeq 8f ; moveb %1@-,%0@-\n\t"
+            "8:"
+               : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp)
+               : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count)
+        );
+      }
+   }
+
+   return(0);
+}
+
+static __inline__ void *mymemexg(void *d, void *s, size_t count)
+{
+	long tmp, tmp2;
+	__asm__ __volatile__(
+	       "movel  %0,%3\n\t"
+               "lsrl   #1,%3 ; jcc 1f ; moveb %1@,%4 ; moveb %0@,%1@+ ; moveb %4,%0@+ ; subqw #1,%2\n\t"
+               "lsrl   #1,%3 ; jcs 2f\n\t"  /* %0 increased=>bit 2 switched*/
+               "movew  %1@,%4 ; movew %0@,%1@+ ; movew %4,%0@+ ; subqw  #2,%2 ; jra 2f\n\t"
+            "1: lsrl   #1,%3 ; jcc 2f\n\t"
+               "movew  %1@,%4 ; movew %0@,%1@+ ; movew %4,%0@+ ; subqw  #2,%2\n\t"
+            "2: movew  %2,%-; lsrl #2,%2 ; jeq 6f\n\t"
+               "lsrl   #1,%2 ; jcc 3f ; movel %1@,%4 ; movel %0@,%1@+ ; movel %4,%0@+\n\t"
+            "3: lsrl   #1,%2 ; jcc 4f ; movel %1@,%4 ; movel %0@,%1@+ ; movel %4,%0@+ ; movel %1@,%4 ; movel %0@,%1@+ ; movel %4,%0@+\n\t"
+            "4: subql  #1,%2 ; jcs 6f\n\t"
+            "5: movel %1@,%4 ; movel %0@,%1@+ ; movel %4,%0@+;movel %1@,%4 ; movel %0@,%1@+ ; movel %4,%0@+\n\t"
+               "movel %1@,%4 ; movel %0@,%1@+ ; movel %4,%0@+;movel %1@,%4 ; movel %0@,%1@+ ; movel %4,%0@+\n\t"
+               "dbra   %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t"
+            "6: movew  %+,%2; btst #1,%2 ; jeq 7f ; movew %1@,%4 ; movew %0@,%1@+ ; movew %4,%0@+\n\t"
+            "7:              ; btst #0,%2 ; jeq 8f ; moveb %1@,%4 ; moveb %0@,%1@+ ; moveb %4,%0@+\n\t"
+            "8:"
+               : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp), "=d" (tmp2)
+               : "0" (d), "1" (s), "2" (count)
+        );
+	return(0);
+}
+
+
+/* ++andreas: Simple and fast version of memmove, assumes size is
+   divisible by 16, suitable for moving the whole screen bitplane */
+static __inline__ void fast_memmove(char *dst, char *src, size_t size)
+{
+  if (!size)
+    return;
+  if (dst < src)
+    __asm__ __volatile__
+      ("1:"
+       "  moveml %0@+,%/d0/%/d1/%/a0/%/a1\n"
+       "  moveml %/d0/%/d1/%/a0/%/a1,%1@\n"
+       "  addql #8,%1; addql #8,%1\n"
+       "  dbra %2,1b\n"
+       "  clrw %2; subql #1,%2\n"
+       "  jcc 1b"
+       : "=a" (src), "=a" (dst), "=d" (size)
+       : "0" (src), "1" (dst), "2" (size / 16 - 1)
+       : "d0", "d1", "a0", "a1", "memory");
+  else
+    __asm__ __volatile__
+      ("1:"
+       "  subql #8,%0; subql #8,%0\n"
+       "  moveml %0@,%/d0/%/d1/%/a0/%/a1\n"
+       "  moveml %/d0/%/d1/%/a0/%/a1,%1@-\n"
+       "  dbra %2,1b\n"
+       "  clrw %2; subql #1,%2\n"
+       "  jcc 1b"
+       : "=a" (src), "=a" (dst), "=d" (size)
+       : "0" (src + size), "1" (dst + size), "2" (size / 16 - 1)
+       : "d0", "d1", "a0", "a1", "memory");
+}
+
+/* ====================================================================== */
+
+/* copcon_XXX routines - interface used by the world */
+
+static int copcon_clear(struct vc_data *conp, int sy, int sx, int height,
+                       int width)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+
+	if (!p->can_soft_blank && console_blanked)
+		return(0);
+
+	if ((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
+	    (sx <= p->cursor_x) && (p->cursor_x < sx+width))
+		CURSOR_UNDRAWN();
+
+	/* Clear display line wise */
+	do{
+		p->dispsw->clear(conp, p, sy++, sx, 1, width);
+	} while (--height);
+	
+	return(0);
+}
+
+static int copcon_putc(struct vc_data *conp, int c, int yy, int xx)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+
+	if (!p->can_soft_blank && console_blanked)
+		return(0);
+
+	if ((p->cursor_x == xx) && (p->cursor_y == yy))
+		CURSOR_UNDRAWN();
+
+	p->dispsw->putc(conp, p, c, yy, xx);
+
+	return(0);
+}
+
+
+static int copcon_putcs(struct vc_data *conp, const char *s, int count, int yy,
+			int xx)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+	
+	if (!p->can_soft_blank && console_blanked)
+		return(0);
+
+	if ((p->cursor_y == yy) && (xx <= p->cursor_x) &&
+	    (p->cursor_x < xx+count))
+		CURSOR_UNDRAWN();
+
+	p->dispsw->putcs(conp, p, s, count, yy, xx);
+	
+	return(0);
+}
+
+static int copcon_cursor(struct vc_data *conp, int mode)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+
+	if (CURSOR_UNDRAWN ())
+		p->dispsw->rev_char(p, p->cursor_x, p->cursor_y);
+	p->cursor_x = conp->vc_x;
+	p->cursor_y = conp->vc_y;
+	
+	switch (mode) {
+	case CM_ERASE:
+		cursor_on = 0;
+		break;
+
+	case CM_MOVE:
+	case CM_DRAW:
+		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+		cursor_on = 1;
+		break;
+	}
+	return(0);
+}
+
+static int copcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+	int i;
+	int bi = b, bj = b;
+	int ti = t, tj = t;
+	unsigned long flags;
+
+
+	if (!p->can_soft_blank && console_blanked)
+		return(0);
+	
+	copcon_cursor(conp, CM_ERASE);
+
+	switch (dir) {
+	case SM_UP:
+
+		if (count > conp->vc_rows) /* Maximum realistic size */
+			count = conp->vc_rows;
+		
+		/* XXX Only scroll if we're not changing console */
+		if(!lines_lock){
+
+			save_flags(flags);
+			cli();
+
+			for(i = 0; i < count; i++)
+				line_ptrs_roll[i] = line_ptrs[ti++];
+			
+			while(ti < b)
+				line_ptrs[tj++] = line_ptrs[ti++];
+
+			while(i)
+				line_ptrs[tj++] = line_ptrs_roll[--i];
+
+			restore_flags(flags);
+
+			fb_info->updatevar(unit);
+		
+			copcon_clear(conp, b-count, 0, count, conp->vc_cols);
+		}
+		break;
+
+	case SM_DOWN:
+		if (count > conp->vc_rows) /* Maximum realistic size */
+			count = conp->vc_rows;
+		
+		/* XXX Only scroll if we're not changing console */
+		if(!lines_lock){
+
+			save_flags(flags);
+			cli();
+
+			for(i = 0; i < count; i++)
+				line_ptrs_roll[i] = line_ptrs[--bi];
+			
+			while(bi > t)
+				line_ptrs[--bj] = line_ptrs[--bi];
+
+			while(i)
+				line_ptrs[--bj] = line_ptrs_roll[--i];
+		
+			restore_flags(flags);
+
+			fb_info->updatevar(unit);
+		
+			copcon_clear(conp, t, 0, count, conp->vc_cols);
+		}
+		break;
+		
+	case SM_LEFT:
+		copcon_bmove(conp, 0, t+count, 0, t, conp->vc_rows, b-t-count);
+		copcon_clear(conp, 0, b-count, conp->vc_rows, count);
+		break;
+		
+	case SM_RIGHT:
+		copcon_bmove(conp, 0, t, 0, t+count, conp->vc_rows, b-t-count);
+		copcon_clear(conp, 0, t, conp->vc_rows, count);
+		break;
+	}
+	return(0);
+}
+
+/* copcon_bmove only handles horizontal scrolling */
+static int copcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
+                       int height, int width)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+
+	if (!p->can_soft_blank && console_blanked)
+		return(0);
+
+	if (((sy <= p->cursor_y) && (p->cursor_y < sy+height) &&
+	     (sx <= p->cursor_x) && (p->cursor_x < sx+width)) ||
+	    ((dy <= p->cursor_y) && (p->cursor_y < dy+height) &&
+	     (dx <= p->cursor_x) && (p->cursor_x < dx+width)))
+		copcon_cursor(conp, CM_ERASE);
+
+	/* Do scrolling one line at a time */
+	do {
+		p->dispsw->bmove(p, sy++, sx, dy++, dx, 1, width);
+	} while (--height);
+	
+	return(0);
+}
+
+
+static int copcon_switch(struct vc_data *conp)
+{
+	copcon_update_lines();
+
+	if (fb_info && fb_info->switch_con)
+		(*fb_info->switch_con)(conp->vc_num);
+	return(0);
+}
+
+static int copcon_blank(int blank)
+{
+	struct display *p = &disp[fg_console];
+
+	copcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
+
+	if (!p->can_soft_blank)
+		if (blank) {
+			mymemclear(p->screen_base, p->var.xres_virtual*
+				   p->var.yres_virtual*
+				   p->var.bits_per_pixel>>3);
+			return(0);
+		} else {
+			/* Tell console.c that it has to restore the screen 
+			 * itself
+			 */
+			return(1);
+		}
+	(*fb_info->blank)(blank);
+	return(0);
+}
+
+
+static void copcon_update_lines(void)
+{
+	struct display *p = &disp[fg_console];
+
+	int i, line, size;
+	u_char *src, *dest, *bpl_ptr;
+
+	lines_lock++;
+
+	size = (p->var.xres/p->fontwidth) * p->fontheight;
+
+	/* Reshuffle lines to correct bitmap order */
+	bpl_ptr = p->screen_base;
+	for(line = 0; line < line_ptrs_used; line++){
+		src = line_ptrs[line];
+		dest = bpl_ptr;
+		bpl_ptr += p->fontheight*p->next_line;
+		if(dest == src)
+			continue;
+		
+		for (i = p->var.bits_per_pixel; i--;) {
+			mymemexg(src, dest, size);
+			dest += p->next_plane;
+			src += p->next_plane;
+		}
+		line_ptrs[line] = bpl_ptr;
+	}
+
+	lines_lock--;
+}
+
+
+static int copcon_get_font(struct vc_data *conp, int *w, int *h, char *data)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+	int i, j, size, alloc;
+
+	size = (p->fontwidth+7)/8 * p->fontheight * 256;
+	alloc = (*w+7)/8 * *h * 256;
+	*w = p->fontwidth;
+	*h = p->fontheight;
+   
+	if (alloc < size)
+		/* allocation length not sufficient */
+		return( -ENAMETOOLONG );
+
+	for (i = 0; i < 256; i++)
+		for (j = 0; j < p->fontheight; j++)
+			data[i*32+j] = p->fontdata[i*p->fontheight+j];
+	return( 0 );
+}
+
+
+#define REFCOUNT(fd)	(((int *)(fd))[-1])
+
+static int copcon_set_font(struct vc_data *conp, int w, int h, char *data)
+{
+	int unit = conp->vc_num;
+	struct display *p = &disp[unit];
+	int i, j, size, userspace = 1, resize;
+	char *old_data = NULL, *new_data;
+
+	if (w < 0)
+		w = p->fontwidth;
+	if (h < 0)
+		h = p->fontheight;
+	
+	if (w == 0) {
+		/* engage predefined font, name in 'data' */
+		char name[MAX_FONT_NAME+1];
+	   
+		if ((i = verify_area( VERIFY_READ, (void *)data, MAX_FONT_NAME )))
+			return i;
+		copy_from_user( name, data, MAX_FONT_NAME );
+		name[sizeof(name)-1] = 0;
+		
+		if (!findsoftfont( name, &w, &h, (u_char **)&data ))
+			return( -ENOENT );
+		userspace = 0;
+	}
+	else if (w == 1) {
+		/* copy font from some other console in 'h'*/
+		struct display *op;
+
+		if (h < 0 || !vc_cons_allocated( h ))
+			return( -ENOTTY );
+		if (h == unit)
+			return( 0 ); /* nothing to do */
+		op = &disp[h];
+		if (op->fontdata == p->fontdata)
+			return( 0 ); /* already the same font... */
+
+		resize = (op->fontwidth != p->fontwidth) ||
+			     (op->fontheight != p->fontheight);
+		if (p->userfont)
+			old_data = p->fontdata;
+		p->fontdata = op->fontdata;
+		w = p->fontwidth = op->fontwidth;
+		h = p->fontheight = op->fontheight;
+		if ((p->userfont = op->userfont))
+			REFCOUNT(p->fontdata)++; /* increment usage counter */
+		goto activate;
+	}
+
+	if (w != 8)
+		/* Currently only fontwidth == 8 supported */
+		return( -ENXIO );
+	
+	resize = (w != p->fontwidth) || (h != p->fontheight);
+	size = (w+7)/8 * h * 256;
+	
+	if (p->userfont)
+		old_data = p->fontdata;
+	
+	if (userspace) {
+		if (!(new_data = kmalloc( sizeof(int)+size, GFP_USER )))
+			return( -ENOMEM );
+		new_data += sizeof(int);
+		REFCOUNT(new_data) = 1; /* usage counter */
+
+		for (i = 0; i < 256; i++)
+			for (j = 0; j < h; j++)
+				new_data[i*h+j] = data[i*32+j];
+
+		p->fontdata = new_data;
+		p->userfont = 1;
+	}
+	else {
+		p->fontdata = data;
+		p->userfont = 0;
+	}
+	p->fontwidth = w;
+	p->fontheight = h;
+
+  activate:
+	if (resize) {
+		p->var.xoffset = p->var.yoffset = p->yscroll = 0;  /* reset wrap/pan */
+		/* Adjust the virtual screen-size to fontheight*rows */
+		p->var.yres_virtual = (p->var.yres/h)*h;
+		p->vrows = p->var.yres_virtual/h;
+		updatescrollmode(p);
+		vc_resize_con( p->var.yres/h, p->var.xres/w, unit );
+	}
+	else if (unit == fg_console)
+		update_screen( unit );
+	
+	if (old_data) {
+		if (--REFCOUNT(old_data) == 0) {
+			kfree( old_data - sizeof(int) );
+		}
+	}
+	
+	return( 0 );
+}
+
+static unsigned short palette_red[16];
+static unsigned short palette_green[16];
+static unsigned short palette_blue[16];
+
+static struct fb_cmap palette_cmap  = {
+    0, 16, palette_red, palette_green, palette_blue, NULL
+};
+
+static int copcon_set_palette(struct vc_data *conp, unsigned char *table)
+{
+    int unit = conp->vc_num;
+    struct display *p = &disp[unit];
+    int i, j, k;
+    u_char val;
+
+    if (!conp->vc_can_do_color || (!p->can_soft_blank && console_blanked))
+	return(-EINVAL);
+    for (i = j = 0; i < 16; i++) {
+	k = table[i];
+	val = conp->vc_palette[j++];
+	palette_red[k] = (val<<8)|val;
+	val = conp->vc_palette[j++];
+	palette_green[k] = (val<<8)|val;
+	val = conp->vc_palette[j++];
+	palette_blue[k] = (val<<8)|val;
+    }
+    palette_cmap.len = 1<<p->var.bits_per_pixel;
+    if (palette_cmap.len > 16)
+	palette_cmap.len = 16;
+    return(fb_info->setcmap(&palette_cmap, unit));
+}
+
+/* ====================================================================== */
+
+/*
+ *    Low Level Operations for the various display memory organizations.
+ *
+ *    Currently only the following organizations are supported here:
+ *
+ *      - Color Normal Planes
+ */
+
+/* ====================================================================== */
+
+/* ====================================================================== */
+
+   /*
+    *    Color Planes
+    */
+
+/* Only required to handle horizontal scrolling, one line at a time */
+static void bmove_plan(struct display *p, int sy, int sx, int dy, int dx,
+                       int height, int width)
+{
+	u_char *src, *dest, *src0, *dest0;
+	u_int i, rows;
+
+
+	if(sy != dy)
+		panic("copcon: bmove_plan - moving across lines!");
+
+	if(height != 1)
+		panic("copcon: bmove_plan - moving 1++ lines!");
+
+	src0 = line_ptrs[sy]+sx;
+	dest0 = line_ptrs[dy]+dx;
+	for (i = p->var.bits_per_pixel; i--;) {
+ 		src = src0;
+		dest = dest0;
+		for (rows = height*p->fontheight; rows--;) {
+			mymemmove(dest, src, width);
+			src += p->next_line;
+			dest += p->next_line;
+		}
+		src0 += p->next_plane;
+		dest0 += p->next_plane;
+	}
+}
+
+
+static void clear_plan(struct vc_data *conp, struct display *p, int sy, int sx,
+                       int height, int width)
+{
+	u_char *dest, *dest0;
+	u_int i, rows;
+	int bg;
+
+	if(height != 1)
+		panic("copcon: clear_plan - moving 1++ lines!");
+
+	dest0 = line_ptrs[sy]+sx;
+
+	bg = attr_bgcol_ec(p,conp);
+	for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+		dest = dest0;
+		for (rows = height*p->fontheight; rows--; dest += p->next_line)
+			if (bg & 1)
+				mymemset(dest, width);
+			else
+				mymemclear(dest, width);
+		bg >>= 1;
+	}
+}
+
+
+static void putc_plan(struct vc_data *conp, struct display *p, int c, int yy,
+                      int xx)
+{
+	u_char *dest, *dest0, *cdat, *cdat0;
+	u_int rows, i;
+	u_char d;
+	int fg, bg;
+
+	c &= 0xff;
+
+	dest0 = line_ptrs[yy]+xx;
+	cdat0 = p->fontdata+c*p->fontheight;
+	fg = attr_fgcol(p,conp);
+	bg = attr_bgcol(p,conp);
+	
+	for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+		dest = dest0;
+		cdat = cdat0;
+		for (rows = p->fontheight; rows--; dest += p->next_line) {
+			d = *cdat++;
+			if (bg & 1)
+				if (fg & 1)
+					*dest = 0xff;
+				else
+					*dest = ~d;
+			else
+				if (fg & 1)
+					*dest = d;
+				else
+					*dest = 0x00;
+		}
+		bg >>= 1;
+		fg >>= 1;
+	}
+}
+
+
+/*
+ *    I split the console character loop in two parts
+ *    (cfr. fbcon_putcs_ilbm())
+ */
+
+static void putcs_plan(struct vc_data *conp, struct display *p, const char *s,
+                       int count, int yy, int xx)
+{
+	u_char *dest, *dest0, *dest1;
+	u_char *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20;
+	u_char *cdat30, *cdat40;
+	u_int rows, i;
+	u_char c1, c2, c3, c4;
+	u_long d;
+	int fg0, bg0, fg, bg;
+
+	dest0 = line_ptrs[yy]+xx;
+	fg0 = attr_fgcol(p,conp);
+	bg0 = attr_bgcol(p,conp);
+
+	while (count--){
+		if (xx & 3 || count < 3) {	/* Slow version */
+			c1 = *s++;
+			dest1 = dest0++;
+			xx++;
+			
+			cdat10 = p->fontdata+c1*p->fontheight;
+			fg = fg0;
+			bg = bg0;
+			
+			for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) {
+				dest = dest1;
+				cdat1 = cdat10;
+
+				for (rows = p->fontheight; rows--; dest += p->next_line) {
+					d = *cdat1++;
+					if (bg & 1)
+						if (fg & 1)
+							*dest = 0xff;
+						else
+							*dest = ~d;
+					else
+						if (fg & 1)
+							*dest = d;
+						else
+							*dest = 0x00;
+				}
+				bg >>= 1;
+				fg >>= 1;
+			}
+		} else {	/* Fast version */
+			c1 = s[0];
+			c2 = s[1];
+			c3 = s[2];
+			c4 = s[3];
+			
+			dest1 = dest0;
+			cdat10 = p->fontdata+c1*p->fontheight;
+			cdat20 = p->fontdata+c2*p->fontheight;
+			cdat30 = p->fontdata+c3*p->fontheight;
+			cdat40 = p->fontdata+c4*p->fontheight;
+			fg = fg0;
+			bg = bg0;
+
+			for (i = p->var.bits_per_pixel; i--; dest1 += p->next_plane) {
+				dest = dest1;
+				cdat1 = cdat10;
+				cdat2 = cdat20;
+				cdat3 = cdat30;
+				cdat4 = cdat40;
+				for (rows = p->fontheight; rows--; dest += p->next_line) {
+					d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++;
+					if (bg & 1)
+						if (fg & 1)
+							*(u_long *)dest = 0xffffffff;
+						else
+							*(u_long *)dest = ~d;
+					else
+						if (fg & 1)
+							*(u_long *)dest = d;
+						else
+							*(u_long *)dest = 0x00000000;
+				}
+				bg >>= 1;
+				fg >>= 1;
+			}
+			s += 4;
+			dest0 += 4;
+			xx += 4;
+			count -= 3;
+		}
+	}
+}
+
+
+static void rev_char_plan(struct display *p, int xx, int yy)
+{
+	u_char *dest, *dest0;
+	u_int rows, i;
+	int mask;
+
+	dest0 = line_ptrs[yy]+xx;
+	mask = p->fgcol ^ p->bgcol;
+
+	/*
+	 *    This should really obey the individual character's
+	 *    background and foreground colors instead of simply
+	 *    inverting.
+	 */
+
+	for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
+		if (mask & 1) {
+			dest = dest0;
+			for (rows = p->fontheight; rows--; dest += p->next_line){
+				*dest = ~*dest;
+			}
+		}
+		mask >>= 1;
+	}
+}
+
+/* ====================================================================== */
+
+
+/* ====================================================================== */
+
+   /*
+    *    The console `switch' structure for the text mode based console
+    */
+
+struct consw cop_con = {
+	copcon_startup, copcon_init, copcon_deinit, copcon_clear, copcon_putc,
+	copcon_putcs, copcon_cursor, copcon_scroll, copcon_bmove,
+	copcon_switch, copcon_blank, copcon_get_font, copcon_set_font,
+	copcon_set_palette
+};
