/*
**  linux/atari/atacon.c
**
**  Copyright (C) 1993 Bjoern Brauel and Roman Hodek
**
**
** This file is subject to the terms and conditions of the GNU General Public
** License.  See the file README.legal in the main directory of this archive
** for more details.
*/

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/tty.h>
#include <linux/console.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/bootinfo.h>
#include <linux/delay.h>

#include <linux/malloc.h>
#include <linux/mm.h>

#include <linux/atarihw.h>
#include <linux/atariints.h>

#include <asm/system.h>

/* The following symbols select what modes are supported. They should
 * be settable by the user ("make config") later.
 * True color (= 16 plane) mode isn't implemented yet!
 */
#define	CONFIG_ATARI_2PLANE
#define	CONFIG_ATARI_4PLANE
#define	CONFIG_ATARI_8PLANE
#undef	CONFIG_ATARI_16PLANE

/* To do:
 *
 *  - Do proper Falcon video mode setting, analogous to the TT (but
 *    much more and better modes possible!)
 *  - Implement 16 plane mode.
 *  - Do a backing store of characters and attributes on the screen
 *    for conole switching (or will this be done in the device
 *    independent part (console.c) ?)
 *
 */


/*
** Macros
*/


/* Define if the font contains all 256 characters (undefined: only
 * 32..127 and 160..255, this is the font in font.c of the Amiga
 * distribution)
 */

#define	WHOLE_FONT

static unsigned long mem_req;

/* There is no support for fontwidth != 8 now!! (And I guess, it will
 * never come; who needs such stuff??)
 */

extern unsigned char
    fontdata_8x16[], fontdata_8x8[];

extern int
    fontheight_8x16, fontheight_8x8,
    fontwidth_8x16, fontwidth_8x8;

extern char
    fontname_8x16[], fontname_8x8[];





/* FONT_CNV does the character conversion necessary if the font
 * doesn't contain all characters. It returns 0 if the characters is
 * not in the font (WHOLE_FONT undefined).
 * 
 * If we have a full font, FONT_CNV has to do the jobs of translating
 * the character to be printed according to the console's current
 * translation table. This is just a quick hack to enable the
 * different translations (normal, graphic, ...) that VT102 allows. It
 * should really be done in console.c, where it is commented out
 * currently, and I didn't want to interfere with the people
 * maintaining that code.
 *                                                             Roman
 */

#ifndef WHOLE_FONT

#define	FONT_CNV(c) ({
   int __rv = 1;
   if ((c >= 32) && (c < 127))
	   /* lower half of charset -- normal ASCII */
	   c -=  32;
    else if ((c >= 160) && (c < 255))
		/* upper half of charset -- special characters */
		c -= 64;
    else
		/* not defined in charset -- non-printable character */
		__rv = 0;
   __rv;
})

#else

#define	FONT_CNV(c) ((c = conp->vc_translate[c]) != 0)

#endif


/*
** Local Variables
*/
struct tt_vid_type
{
	const char	*name;
	ushort		xres;
	ushort		yres;
	ushort		depth;
};

struct falcon_vid_type
{
	const char	*name;
};

struct display;

struct display_switch {
	void (*bmove)( register struct display *p, int sy, int sx, int dy,
				   int dx, int height, int width);
	void (*clear)( struct condata *conp, register struct display *p,
				   int sy, int sx, int height, int width);
	void (*putc)( struct condata *conp, struct display *p, int c,
				  int y, int x, int mode );
	void (*putcs)( struct condata *conp, struct display *p,
				   const char *s, int count, int y, int x, int mode);
	void (*rev_char)( struct display *display, int x, int y );
};	

struct display
{
    ushort scr_height;			/* screens dimensions */
    ushort scr_width;
    ushort scr_depth;			/* # planes */
    ushort bytes_per_row;		/* offset to one line below; if (scr_depth
								 * != 1), this is (scr_width / 8 *
								 * scr_depth)! */
    ushort cursor_x;            /* current cursor position */
    ushort cursor_y;

    ushort shiftmode;           /* display mode */

    ushort fgcol;               /* text colors */
    ushort bgcol;

    u_char *bitplane;	        /* pointer to display bitmap; planes
								 * are interleaved!
								 */
    u_long *color_map;          /* XXX Not yet implemented XXX */

    u_char *fontdata;           /* Font associated to this display */
    ushort fontheight;
    ushort fontwidth;

	struct display_switch *dispsw;	/* pointers to depth specific
									 * functions
									 */
};

static struct display disp[NR_CONSOLES];

static struct tt_vid_type tt_video_modes[TT_SHIFTER_NUMMODE] = {
	{ "stlow",   320 , 200 , 1 } ,			/* ST-Low */
	{ "stmid",   640 , 200 , 2 } ,			/* ST-Mid */
	{ "sthigh",  640 , 400 , 1 } ,			/* ST-High */
	{ "",        000 , 000 , 0 } ,			/* unused */
	{ "ttmid",   640 , 480 , 4 } ,			/* TT-Mid */
	{ "",        000 , 000 , 0 } ,			/* unused */
	{ "tthigh", 1280 , 960 , 1 } ,			/* TT-High */
	{ "ttlow",   320 , 480 , 8 } };			/* TT-Low */
	
static struct tt_vid_type falcon_video_modes[] = {
	{ "sthigh" }
};

/* Default color maps for 4 and 2 plane modes. These use the PC-ish
 * assignment (the low 3 bits turn each RBG channel on/off, the 4th
 * bit is intensity). Currently, 8 plane mode uses the 4 plane color
 * table, too.
 */

u_short default_16_colors[16] = {
	0x000,	/* black */
	0x00c,	/* blue */
	0x0c0,	/* green */
	0x0cc,	/* cyan */
	0xc00,	/* red */
	0xc0c,	/* magenta */
	0xcc0,	/* brown */
	0xccc,	/* light gray */
	0x888,	/* gray */
	0x00f,	/* light blue */
	0x0f0,	/* light green */
	0x0ff,	/* light cyan */
	0xf00,	/* light red */
	0xf0f,	/* light magenta */
	0xff0,	/* light brown */
	0xfff	/* white */
};

/* The 4 colors of 2 plane mode are used as a grey scale to implement
 * the vc_intensity == 0 or == 2 modes.
 */

u_short default_4_colors[4] = {
	0x000,	/* black */
	0xccc,	/* light gray */
	0x888,	/* gray */
	0xfff	/* white */
};

		
static int cursor_drawn = 0;

static int atacon_default_font = -1;
static int atacon_default_shiftmode = -1;


#undef CURSOR_DELAY_TIMER
#define	CURSOR_DELAY_VBL

#ifdef CURSOR_DELAY_TIMER

/* Time to elapse before the cursor is really drawn, in jiffies */
#define	CURSOR_DRAW_DELAY	3

#define	CURSOR_UNDRAWN()						\
    do {										\
		del_timer( &atacon_cursor_timer );		\
		cursor_drawn = 0;						\
	} while(0)

#endif

#ifdef CURSOR_DELAY_VBL

/* The VBL cursor looks much better than the timer cursor, because the
 * inverting is done only in the VBlank time, when nothing is
 * displayed. This avoids flickering when moving the cursor fastly. I
 * guess the timer cursor will go away in futuere.
 */

/* Time to elapse before the cursor is really drawn, in VBL units
 * (60 Hz: 17 ms, 72 Hz: 14 ms)
 */
#define	CURSOR_DRAW_DELAY	2

/* # VBL ints between cursor state changes */
#define	DEFAULT_CURSOR_BLINK_RATE	42

static int		vbl_cursor_cnt = 0;
static int		cursor_on = 0;
static int		cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;

#define	CURSOR_UNDRAWN()						\
    do {										\
		vbl_cursor_cnt = 0;						\
		cursor_drawn = 0;						\
	} while(0)

#endif


/***************************** Prototypes *****************************/

static void detect_video( ushort *yres, ushort *xres, ushort *depth, ushort
                          *shiftmode );
static int shifter_init( u_long adr);
static int atacon_init( struct condata *conp, long mem_start);
static int atacon_deinit( struct condata *conp);
static __inline__ void *mymemclear_small( void * s, size_t count);
static __inline__ void *mymemclear( void * s, size_t count);
static __inline__ void *mymemmove( void * d, void * s, size_t count);
static __inline__ void memclear_4p_col( void *d, size_t h, u_long val,
                                        u_short bpr );
static __inline__ void memset_even_4p( void *d, size_t count, u_long val1,
                                       u_long val2 );
static __inline__ void memmove_4p_col( void *d, void *s, u_short h, u_short
                                       bpr );
static __inline__ unsigned long expand4l( unsigned char c );
static __inline__ void expand4dl( unsigned char c, unsigned long *ret1,
                                  unsigned long *ret2 );
static __inline__ unsigned long dup4l( unsigned char c );
static __inline__ void memclear_8p_col( void *d, size_t h, u_long val1,
                                        u_long val2, u_short bpr );
static __inline__ void memset_even_8p( void *d, size_t count, u_long val1,
                                       u_long val2, u_long val3, u_long
                                       val4 );
static __inline__ void memmove_8p_col( void *d, void *s, u_short h, u_short
                                       bpr );
static __inline__ void expand8dl( unsigned char c, u_long *ret1, u_long
                                  *ret2 );
static __inline__ void expand8ql( unsigned char c, unsigned long *ret1,
                                  unsigned long *ret2, unsigned long *ret3,
                                  unsigned long *ret4 );
static __inline__ void memclear_2p_col( void *d, size_t h, u_short val,
                                        u_short bpr );
static __inline__ void memset_even_2p( void *d, size_t count, u_long val );
static __inline__ void memmove_2p_col( void *d, void *s, u_short h, u_short
                                       bpr );
static __inline__ unsigned short expand2w( unsigned char c );
static __inline__ unsigned short dup2w( unsigned char c );
static int atacon_bmove( struct condata *conp, int sy, int sx, int dy, int
                         dx, int height, int width);
static int atacon_clear( struct condata *conp, int sy, int sx, int height,
                         int width);
static int atacon_putc( struct condata *conp, int c, int y, int x, int
                        mode);
static int atacon_putcs( struct condata *conp, const char *s, int count,
                         int y, int x, int mode);
static int atacon_scroll( struct condata *conp, int t, int b, int dir, int
                          count);
static int atacon_switch( struct condata *conp);
static void atacon_bmove_1_plane( register struct display *p, int sy, int
                                  sx, int dy, int dx, int height, int
                                  width);
static void atacon_clear_1_plane( struct condata *conp, register struct
                                  display *p, int sy, int sx, int height,
                                  int width );
static void atacon_putc_1_plane( struct condata *conp, struct display *p,
                                 int c, int y, int x, int mode);
static void atacon_putcs_1_plane( struct condata *conp, struct display *p,
                                  const char *s, int count, int y, int x,
                                  int mode);
static void atacon_rev_char_1_plane( struct display *display, int x, int y
                                     );
#ifdef CONFIG_ATARI_2PLANE
static void atacon_bmove_2_plane( register struct display *p, int sy, int
                                  sx, int dy, int dx, int height, int
                                  width);
static void atacon_clear_2_plane( struct condata *conp, register struct
                                  display *p, int sy, int sx, int height,
                                  int width );
static void atacon_putc_2_plane( struct condata *conp, struct display *p,
                                 int c, int y, int x, int mode);
static void atacon_putcs_2_plane( struct condata *conp, struct display *p,
                                  const char *s, int count, int y, int x,
                                  int mode);
static void atacon_rev_char_2_plane( struct display *display, int x, int y
                                     );
#endif
#ifdef CONFIG_ATARI_4PLANE
static void atacon_bmove_4_plane( register struct display *p, int sy, int
                                  sx, int dy, int dx, int height, int
                                  width);
static void atacon_clear_4_plane( struct condata *conp, register struct
                                  display *p, int sy, int sx, int height,
                                  int width );
static void atacon_putc_4_plane( struct condata *conp, struct display *p,
                                 int c, int y, int x, int mode);
static void atacon_putcs_4_plane( struct condata *conp, struct display *p,
                                  const char *s, int count, int y, int x,
                                  int mode);
static void atacon_rev_char_4_plane( struct display *display, int x, int y
                                     );
#endif
#ifdef CONFIG_ATARI_8PLANE
static void atacon_bmove_8_plane( register struct display *p, int sy, int
                                  sx, int dy, int dx, int height, int
                                  width);
static void atacon_clear_8_plane( struct condata *conp, register struct
                                  display *p, int sy, int sx, int height,
                                  int width );
static void atacon_putc_8_plane( struct condata *conp, struct display *p,
                                 int c, int y, int x, int mode);
static void atacon_putcs_8_plane( struct condata *conp, struct display *p,
                                  const char *s, int count, int y, int x,
                                  int mode);
static void atacon_rev_char_8_plane( struct display *display, int x, int y
                                     );
#endif
#ifdef CURSOR_DELAY_TIMER
static void atacon_curtimfunc( unsigned long _disp );
#endif
#ifdef CURSOR_DELAY_VBL
static void atacon_vbl_handler( struct intframe *fp, void *dummy );
#endif
static int atacon_cursor( struct condata *conp, int mode);

/************************* End of Prototypes **************************/


struct display_switch
	dispsw_1_plane = {
		atacon_bmove_1_plane, atacon_clear_1_plane, atacon_putc_1_plane,
		atacon_putcs_1_plane, atacon_rev_char_1_plane
	}
#ifdef CONFIG_ATARI_2PLANE
	, dispsw_2_plane = {
		atacon_bmove_2_plane, atacon_clear_2_plane, atacon_putc_2_plane,
		atacon_putcs_2_plane, atacon_rev_char_2_plane
	}
#endif
#ifdef CONFIG_ATARI_4PLANE
    , dispsw_4_plane = {
		atacon_bmove_4_plane, atacon_clear_4_plane, atacon_putc_4_plane,
		atacon_putcs_4_plane, atacon_rev_char_4_plane
	}
#endif
#ifdef CONFIG_ATARI_8PLANE
	, dispsw_8_plane = {
		atacon_bmove_8_plane, atacon_clear_8_plane, atacon_putc_8_plane,
		atacon_putcs_8_plane, atacon_rev_char_8_plane
	}
#endif
#ifdef CONFIG_ATARI_16PLANE
	, dispsw_16_plane = {
		atacon_bmove_16_plane, atacon_clear_16_plane, atacon_putc_16_plane,
		atacon_putcs_16_plane, atacon_rev_char_16_plane
	}
#endif
;
	



#ifdef CURSOR_DELAY_TIMER

static struct timer_list atacon_cursor_timer = { NULL,0,0,atacon_curtimfunc };

#endif


/* ================================================================= */
/*                        Initialization Functions                   */
/* ================================================================= */


/* atari_video_setup() processes command line options and stores them
 * in atacon_default_font and atacon_default_shiftmode. Their validity
 * is checked later.
 */

void atari_video_setup( char *options, int *ints )

{	char	*this_opt;
	int		i, max;

    if (!options || !*options)
        return;
    
    for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {

		if (!*this_opt) continue;
		
		if (!strcmp (this_opt, "f8x8")) {
            atacon_default_font = 8;
	    }
        else if (!strcmp (this_opt, "f8x16")) {
            atacon_default_font = 16;
	    }
        else {

			if (boot_info.bi_atari.model == ATARI_TT) {
				max = sizeof(tt_video_modes)/sizeof(tt_video_modes[0]);
				for (i = 0; i < max; i++) {
					if (!strcmp( this_opt, tt_video_modes[i].name )) {
						atacon_default_shiftmode = i;
						break;
					}
				}
			}
			else {
				max = sizeof(falcon_video_modes)/sizeof(falcon_video_modes[0]);
				for (i = 0; i < max; i++) {
					if (!strcmp( this_opt, falcon_video_modes[i].name )) {
						atacon_default_shiftmode = i;
						break;
					}
				}
			}

			if (i == max)
				printk( "atari_video_setup: Unknown option %s\n", this_opt );
		}
	}
}


/* detect_video() selects a screen size/depth pair and programs the
 * hardware for this mode. It chooses the shift mode given on the
 * command line, if this is possible with the connected monitor type.
 * Else is falls back to a default mode for the monitor.
 * Note that this isn't implemented for the Falcon yet, because there
 * are many, many possibilities. The Falcon uses always St High mode
 * for now. To be re-worked.
 */

static void detect_video( ushort *yres, ushort *xres, ushort *depth,
						  ushort *shiftmode )

{	static int detected = 0;
	
	if (boot_info.bi_atari.model == ATARI_TT) {

		int		mono_moni, i;

		if (!detected) {
			/* Determine the connected monitor: The DMA sound must be
			 * disabled before reading the MFP GPIP, because the Sound
			 * Done Signal and the Monochrome Detect are XORed together!
			 */

			tt_dmasnd.ctrl = DMASND_CTRL_OFF;
			udelay( 20 );	/* wait a while for things to settle down */
			mono_moni = (mfp.par_dt_reg & 0x80) == 0;

			/* If the command line shift mode isn't compatible with the
			 * monitor type, reset it to -1. A default mode will be
			 * selected then below.
			 */

			if (atacon_default_shiftmode != -1)
				if ((mono_moni && atacon_default_shiftmode != TT_HIGH) ||
					(!mono_moni && atacon_default_shiftmode == TT_HIGH))
					atacon_default_shiftmode = -1;
		
			/* Set a default shift mode, if none was given on the command
			 * line or that isn't possible.
			 */
			if (atacon_default_shiftmode == -1)
				atacon_default_shiftmode = mono_moni ? TT_HIGH :
#ifdef CONFIG_ATARI_4PLANE
					ST_HIGH;
#else
					TT_MID;
#endif

			/* Initialize the shift mode, palette bank = 0 */
			shifter_tt.tt_shiftmode = atacon_default_shiftmode << 8;

			/* Set up the colormap registers */
			if (tt_video_modes[atacon_default_shiftmode].depth >= 4) {
				for( i = 0; i < 16; ++i )
					tt_palette[i] = default_16_colors[i];
			}
			else if (tt_video_modes[atacon_default_shiftmode].depth == 2) {
				for( i = 0; i < 4; ++i )
					tt_palette[i] = default_4_colors[i];
			}
			else /* depth == 1 */ {
				tt_palette[254] = 0x000; /* black */
				tt_palette[255] = 0xfff; /* white */
			}
			
			detected = 1;
		}
		
		*yres  = tt_video_modes[atacon_default_shiftmode].yres;
		*xres  = tt_video_modes[atacon_default_shiftmode].xres;
		*depth = tt_video_modes[atacon_default_shiftmode].depth;
		*shiftmode = atacon_default_shiftmode;
	}
	else if (boot_info.bi_atari.model == ATARI_FALCON) {

		*yres  = 400;
		*xres  = 640;
		*depth = 1;

	}
 }

static int shifter_init(u_long adr)
 {
/* Setup Screen Memory */
  shifter.bas_hi=(u_char)((adr & 0xff0000) >> 16);
  shifter.bas_md=(u_char)((adr & 0x00ff00) >> 8);
  shifter.bas_lo=(u_char)(adr & 0x0000ff);
  return 0;
 }


static int atacon_init(struct condata *conp, long mem_start)
{
    int unit = conp - vc_cons;	

	/* set up the display defaults */
	detect_video( &disp[unit].scr_height, &disp[unit].scr_width,
				  &disp[unit].scr_depth, &disp[unit].shiftmode );

	/* If no font was selected on the command line, choose a default
	 * font: This is 8x8 if the display has less than 400 lines, 8x16
	 * else.
	 */
	if (atacon_default_font == -1)
		atacon_default_font = (disp[unit].scr_height < 400) ? 8 : 16;

	if (atacon_default_font == 16) {
		disp[unit].fontdata   = fontdata_8x16;
		disp[unit].fontheight = fontheight_8x16;
		disp[unit].fontwidth  = fontwidth_8x16;
	}
	else if (atacon_default_font == 8) {
		disp[unit].fontdata   = fontdata_8x8;
		disp[unit].fontheight = fontheight_8x8;
		disp[unit].fontwidth  = fontwidth_8x8;
	}
	else
		panic( "atacon_init: No support for fontheight %d.\n",
			   atacon_default_font );

	disp[unit].bytes_per_row = (disp[unit].scr_width >> 3) *
		                       disp[unit].scr_depth;
	disp[unit].cursor_x = 0;
	disp[unit].cursor_y = 0;
	
	if (disp[unit].scr_depth == 1) {
		disp[unit].dispsw = &dispsw_1_plane;
		disp[unit].fgcol = 1;
		disp[unit].bgcol = 0;
	}
#ifdef CONFIG_ATARI_2PLANE
	else if (disp[unit].scr_depth == 2) {
		disp[unit].dispsw = &dispsw_2_plane;
		disp[unit].fgcol = 3;
		disp[unit].bgcol = 0;
	}
#endif
#ifdef CONFIG_ATARI_4PLANE
	else if (disp[unit].scr_depth == 4) {
		disp[unit].dispsw = &dispsw_4_plane;
		disp[unit].fgcol = 7;
		disp[unit].bgcol = 0;
	}
#endif
#ifdef CONFIG_ATARI_8PLANE
	else if (disp[unit].scr_depth == 8) {
		disp[unit].dispsw = &dispsw_8_plane;
		disp[unit].fgcol = 7;
		disp[unit].bgcol = 0;
	}
#endif
#ifdef CONFIG_ATARI_16PLANE
	else if (disp[unit].scr_depth == 16) {
		disp[unit].dispsw = &dispsw_16_plane;
		disp[unit].fgcol = 0xbbbbbb;
		disp[unit].bgcol = 0;
	}
#endif
	else
		panic( "atacon_init: %d planes not supported.\n",
			   disp[unit].scr_depth );
	
    conp->vc_cols = disp[unit].scr_width / disp[unit].fontwidth;
    conp->vc_rows = disp[unit].scr_height / disp[unit].fontheight;

	mem_req = (disp[unit].scr_width/8)*disp[unit].scr_height*
		      disp[unit].scr_depth;
    /* locate the bitplane */
	mem_start = (mem_start + 7) & ~7; /* round up */
	disp[unit].bitplane = (u_char *)mem_start;
	mem_start += mem_req;
	mymemclear( disp[unit].bitplane, mem_req );

	shifter_init( (u_long)(disp[unit].bitplane) );
	

#ifdef CURSOR_DELAY_VBL
	add_isr( IRQ4, atacon_vbl_handler, 0, NULL );
#endif
	
	return mem_start;
}

static int atacon_deinit (struct condata *conp)
{
    return 0;
}


/* ================================================================= */
/*                      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 thats 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
 * asynchronisity.
 *
 * 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).
 */


static __inline__ void *mymemclear_small(void * s, size_t count)
{
	long tmp1, tmp2, tmp3, tmp4;
	
	if (!count) return(0);
	
	__asm__ __volatile__
	   ("moveq  #0,%2 ; movel  %2,%3 ; movel  %2,%4 ; movel  %2,%5\n\t"
		"addl   %1,%0\n\t"
        "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" (tmp1), "=d" (tmp2), "=d" (tmp3), "=d" (tmp4)
		: "0" (s), "1" (count)
	   );

   return(0);              
}

static __inline__ void *mymemclear(void * s, size_t count)
{
	if (!count)
		return(0);
	
    else if (count < 16) {
		long tmp;
		__asm__ __volatile__
		("moveq  #0,%2\n\t"
         "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:"
		 : "=a" (s), "=d" (count), "=d" (tmp)
		 : "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,sp@-; moveq #0,%2 ; lsrl #2,%1 ; jeq 6f\n\t"
         "lsrl   #1,%1 ; jcc 3f ; movel %2,%0@+\n\t"        
      "3: lsrl   #1,%1 ; jcc 4f ; movel %2,%0@+ ; movel %2,%0@+\n\t"
      "4: subql  #1,%1 ; jcs 6f\n\t"
      "5: movel %2,%0@+; movel %2,%0@+ ; movel %2,%0@+ ; movel %2,%0@+\n\t"
         "dbra %1,5b   ; subl #65536,%1; jcc 5b\n\t"
      "6: movew sp@+,%1; btst #1,%1 ; jeq 7f ; movew %2,%0@+\n\t"
      "7:              ; btst #0,%1 ; jeq 8f ; moveb %2,%0@+\n\t"
      "8:"
		 : "=a" (s), "=d" (count), "=d" (tmp)
		 : "0" (s), "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,sp@-; 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 ; subl #65536,%2; jcc 5b\n\t"
     "6: movew sp@+,%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__
       ("addal  %2,%0 ; addal %2,%1\n\t"
        "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__
       ("addal  %2,%0 ; addal %2,%1; 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,sp@-; 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 ; subl #65536,%2; jcc 5b\n\t"
     "6: movew sp@+,%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)
	   );
      }
   }
   return(0);              
}

/* Sets the bytes in the visible column at d, height h, to the value
 * val for a 4 plane screen. The the bis of the color in 'color' are
 * moved (8 times) to the respective bytes. This means:
 *
 * for( h times; d += bpr )
 *   *d     = (color & 1) ? 0xff : 0;
 *   *(d+2) = (color & 2) ? 0xff : 0;
 *   *(d+4) = (color & 4) ? 0xff : 0;
 *   *(d+6) = (color & 8) ? 0xff : 0;
 */

static __inline__ void memclear_4p_col( void *d, size_t h,
										u_long val, u_short bpr  )

{
	__asm__ __volatile__
		( "subql  #1,%1\n\t"
	   "1: movepl %4,%0@(0)\n\t"
		  "addaw  %5,%0\n\t"
		  "dbra	  %1,1b"
		  : "=a" (d), "=d" (h)
		  : "0" (d), "1" (h), "d" (val), "r" (bpr)
		);
}

/* Sets a 4 plane region from 'd', length 'count' bytes, to the color
 * in val1/val2. 'd' has to be an even address and count must be divisible
 * by 8, because only whole words and all planes are accessed. I.e.:
 *
 * for( count/8 times )
 *   *d     = *(d+1) = (color & 1) ? 0xff : 0;
 *   *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
 *   *(d+4) = *(d+5) = (color & 4) ? 0xff : 0;
 *   *(d+6) = *(d+7) = (color & 8) ? 0xff : 0;
 */

static __inline__ void memset_even_4p( void *d, size_t count,
									   u_long val1, u_long val2 )

{	
	__asm__ __volatile__
		( "lsrl   #3,%1\n\t"
		  "subql  #1,%1\n\t"
	   "1: movel  %4,%0@+\n\t"
		  "movel  %5,%0@+\n\t"
		  "dbra	  %1,1b\n\t"
		  "subl	  #65536,%1\n\t"
		  "jcc	  1b"
		  : "=a" (d), "=d" (count)
		  : "0" (d), "1" (count), "d" (val1), "d" (val2)
		);
}

/* Copies a 4 plane column from 's', height 'h', to 's'. */

static __inline__ void memmove_4p_col( void *d, void *s, u_short h,
									   u_short bpr )

{	unsigned long	tmp;

	__asm__ __volatile__
		( "subqw  #1,%2\n\t"
	   "1: movepl %0@(0),%3\n\t"
		  "movepl %3,%1@(0)\n\t"
		  "addaw  %7,%0\n\t"
		  "addaw  %7,%1\n\t"
		  "dbra   %2,1b"
		  : "=a" (s), "=a" (d), "=d" (h), "=&d" (tmp)
		  : "0" (s), "1" (d), "2" (h), "d" (bpr)
		);
}


/* This expands a 4 bit color into a long for movepl (4 plane) operations. */

static __inline__ unsigned long expand4l( unsigned char c )

{	unsigned long	rv;
	
	__asm__ __volatile__
		( "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  "lsll	 #8,%0\n\t"
		  "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  "lsll	 #8,%0\n\t"
		  "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  "lsll	 #8,%0\n\t"
		  "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  : "=&d" (rv), "=d" (c)
		  : "1" (c)
		);
	return( rv );
}

/* This expands a 4 bit color into two longs for two movel operations
 * (4 planes).
 */

static __inline__ void expand4dl( unsigned char c, unsigned long *ret1,
							   unsigned long *ret2 )

{	unsigned long	rv1, rv2;
	
	__asm__ __volatile__
		( "lsrb	 #1,%3\n\t"
		  "scs	 %0\n\t"
		  "extw	 %0\n\t"
		  "swap	 %0\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %0\n\t"
		  "extw	 %0\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %1\n\t"
		  "extw	 %1\n\t"
		  "swap	 %1\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs   %1\n\t"
		  "extw	 %1"
		  : "=&d" (rv1), "=&d" (rv2), "=d" (c)
		  : "2" (c)
		);
	*ret1 = rv1;
	*ret2 = rv2;
}


/* This duplicates a byte 4 times into a long. */

static __inline__ unsigned long dup4l( unsigned char c )

{	ushort	tmp;
	ulong	rv;

	__asm__ __volatile__
		( "moveb  %2,%0\n\t"
		  "lslw   #8,%0\n\t"
		  "moveb  %2,%0\n\t"
		  "movew  %0,%1\n\t"
		  "swap   %0\n\t"
		  "movew  %1,%0"
		  : "=&d" (rv), "=d" (tmp)
		  : "d" (c)
		);

	return( rv );
}


/* Sets the bytes in the visible column at d, height h, to the value
 * val1,val2 for a 8 plane screen. The the bis of the color in 'color' are
 * moved (8 times) to the respective bytes. This means:
 *
 * for( h times; d += bpr )
 *   *d      = (color & 1) ? 0xff : 0;
 *   *(d+2)  = (color & 2) ? 0xff : 0;
 *   *(d+4)  = (color & 4) ? 0xff : 0;
 *   *(d+6)  = (color & 8) ? 0xff : 0;
 *   *(d+8)  = (color & 16) ? 0xff : 0;
 *   *(d+10) = (color & 32) ? 0xff : 0;
 *   *(d+12) = (color & 64) ? 0xff : 0;
 *   *(d+14) = (color & 128) ? 0xff : 0;
 */

static __inline__ void memclear_8p_col( void *d, size_t h, u_long val1,
										u_long val2, u_short bpr  )

{
	__asm__ __volatile__
		( "subql  #1,%1\n\t"
	   "1: movepl %4,%0@(0)\n\t"
	      "movepl %5,%0@(8)\n\t"
		  "addaw  %6,%0\n\t"
		  "dbra	  %1,1b"
		  : "=a" (d), "=d" (h)
		  : "0" (d), "1" (h), "d" (val1), "d" (val2), "r" (bpr)
		);
}

/* Sets a 8 plane region from 'd', length 'count' bytes, to the color
 * val1..val4. 'd' has to be an even address and count must be divisible
 * by 16, because only whole words and all planes are accessed. I.e.:
 *
 * for( count/16 times )
 *   *d      = *(d+1)  = (color & 1) ? 0xff : 0;
 *   *(d+2)  = *(d+3)  = (color & 2) ? 0xff : 0;
 *   *(d+4)  = *(d+5)  = (color & 4) ? 0xff : 0;
 *   *(d+6)  = *(d+7)  = (color & 8) ? 0xff : 0;
 *   *(d+8)  = *(d+9)  = (color & 16) ? 0xff : 0;
 *   *(d+10) = *(d+11) = (color & 32) ? 0xff : 0;
 *   *(d+12) = *(d+13) = (color & 64) ? 0xff : 0;
 *   *(d+14) = *(d+15) = (color & 128) ? 0xff : 0;
 */

static __inline__ void memset_even_8p( void *d, size_t count,
									   u_long val1, u_long val2,
									   u_long val3, u_long val4 )

{	
	__asm__ __volatile__
		( "lsrl   #4,%1\n\t"
		  "subql  #1,%1\n\t"
	   "1: movel  %4,%0@+\n\t"
		  "movel  %5,%0@+\n\t"
		  "movel  %6,%0@+\n\t"
		  "movel  %7,%0@+\n\t"
		  "dbra	  %1,1b\n\t"
		  "subl	  #65536,%1\n\t"
		  "jcc	  1b"
		  : "=a" (d), "=d" (count)
		  : "0" (d), "1" (count), "d" (val1), "d" (val2),
		    "d" (val3), "d" (val4)
		);
}

/* Copies a 8 plane column from 's', height 'h', to 's'. */

static __inline__ void memmove_8p_col( void *d, void *s, u_short h,
									   u_short bpr )

{	unsigned long	tmp;

	__asm__ __volatile__
		( "subqw  #1,%2\n\t"
	   "1: movepl %0@(0),%3\n\t"
		  "movepl %3,%1@(0)\n\t"
	      "movepl %0@(8),%3\n\t"
		  "movepl %3,%1@(8)\n\t"
		  "addaw  %7,%0\n\t"
		  "addaw  %7,%1\n\t"
		  "dbra   %2,1b"
		  : "=a" (s), "=a" (d), "=d" (h), "=&d" (tmp)
		  : "0" (s), "1" (d), "2" (h), "d" (bpr)
		);
}


/* This expands a 8 bit color into two longs for two movepl (8 plane)
 * operations.
 */

static __inline__ void expand8dl( unsigned char c, u_long *ret1,
								  u_long *ret2 )

{	unsigned long	rv1, rv2;
	
	__asm__ __volatile__
		( "lsrb	 #1,%3\n\t"
		  "scs	 %0\n\t"
		  "lsll	 #8,%0\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %0\n\t"
		  "lsll	 #8,%0\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %0\n\t"
		  "lsll	 #8,%0\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %0\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %1\n\t"
		  "lsll	 #8,%1\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %1\n\t"
		  "lsll	 #8,%1\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %1\n\t"
		  "lsll	 #8,%1\n\t"
		  "lsrb	 #1,%3\n\t"
		  "scs	 %1"
		  : "=&d" (rv1), "=&d" (rv2),"=d" (c)
		  : "2" (c)
		);

	*ret1 = rv1;
	*ret2 = rv2;
}

/* This expands a 8 bit color into four longs for four movel operations
 * (8 planes).
 */

static __inline__ void expand8ql( unsigned char c, unsigned long *ret1,
								  unsigned long *ret2, unsigned long
								  *ret3, unsigned long *ret4 )

{	unsigned long	rv1, rv2, rv3, rv4;
	
	__asm__ __volatile__
		( "lsrb	 #1,%5\n\t"
		  "scs	 %0\n\t"
		  "extw	 %0\n\t"
		  "swap	 %0\n\t"
		  "lsrb	 #1,%5\n\t"
		  "scs	 %0\n\t"
		  "extw	 %0\n\t"
		  "lsrb	 #1,%5\n\t"
		  "scs	 %1\n\t"
		  "extw	 %1\n\t"
		  "swap	 %1\n\t"
		  "lsrb	 #1,%5\n\t"
		  "scs   %1\n\t"
		  "extw	 %1\n\t"
		  "lsrb	 #1,%5\n\t"
		  "scs	 %2\n\t"
		  "extw	 %2\n\t"
		  "swap	 %2\n\t"
		  "lsrb	 #1,%5\n\t"
		  "scs	 %2\n\t"
		  "extw	 %2\n\t"
		  "lsrb	 #1,%5\n\t"
		  "scs	 %3\n\t"
		  "extw	 %3\n\t"
		  "swap	 %3\n\t"
		  "lsrb	 #1,%5\n\t"
		  "scs   %3\n\t"
		  "extw	 %3"
		  : "=&d" (rv1), "=&d" (rv2), "=&d" (rv3), "=&d" (rv4), "=d" (c)
		  : "4" (c)
		);

	*ret1 = rv1;
	*ret2 = rv2;
	*ret3 = rv3;
	*ret4 = rv4;
}



/* Sets the bytes in the visible column at d, height h, to the value
 * val for a 2 plane screen. The the bis of the color in 'color' are
 * moved (8 times) to the respective bytes. This means:
 *
 * for( h times; d += bpr )
 *   *d     = (color & 1) ? 0xff : 0;
 *   *(d+2) = (color & 2) ? 0xff : 0;
 */

static __inline__ void memclear_2p_col( void *d, size_t h,
										u_short val, u_short bpr  )

{
	__asm__ __volatile__
		( "subql  #1,%1\n\t"
	   "1: movepw %4,%0@(0)\n\t"
		  "addaw  %5,%0\n\t"
		  "dbra	  %1,1b"
		  : "=a" (d), "=d" (h)
		  : "0" (d), "1" (h), "d" (val), "r" (bpr)
		);
}

/* Sets a 2 plane region from 'd', length 'count' bytes, to the color
 * in val1. 'd' has to be an even address and count must be divisible
 * by 8, because only whole words and all planes are accessed. I.e.:
 *
 * for( count/4 times )
 *   *d     = *(d+1) = (color & 1) ? 0xff : 0;
 *   *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
 */

static __inline__ void memset_even_2p( void *d, size_t count, u_long val )

{	
	__asm__ __volatile__
		( "lsrl   #2,%1\n\t"
		  "subql  #1,%1\n\t"
	   "1: movel  %4,%0@+\n\t"
		  "dbra	  %1,1b\n\t"
		  "subl	  #65536,%1\n\t"
		  "jcc	  1b"
		  : "=a" (d), "=d" (count)
		  : "0" (d), "1" (count), "d" (val)
		);
}

/* Copies a 2 plane column from 's', height 'h', to 's'. */

static __inline__ void memmove_2p_col( void *d, void *s, u_short h,
									   u_short bpr )

{	unsigned short	tmp;

	__asm__ __volatile__
		( "subqw  #1,%2\n\t"
	   "1: movepw %0@(0),%3\n\t"
		  "movepw %3,%1@(0)\n\t"
		  "addaw  %7,%0\n\t"
		  "addaw  %7,%1\n\t"
		  "dbra   %2,1b"
		  : "=a" (s), "=a" (d), "=d" (h), "=&d" (tmp)
		  : "0" (s), "1" (d), "2" (h), "d" (bpr)
		);
}


/* This expands a 2 bit color into a short for movepw (2 plane) operations. */

static __inline__ unsigned short expand2w( unsigned char c )

{	unsigned short	rv;
	
	__asm__ __volatile__
		( "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  "lsll	 #8,%0\n\t"
		  "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  : "=&d" (rv), "=d" (c)
		  : "1" (c)
		);
	return( rv );
}

/* This expands a 2 bit color into one long for a movel operation
 * (2 planes).
 */

static __inline__ void expand2l( unsigned char c, unsigned long *ret )

{	unsigned long	rv;
	
	__asm__ __volatile__
		( "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  "extw	 %0\n\t"
		  "swap	 %0\n\t"
		  "lsrb	 #1,%2\n\t"
		  "scs	 %0\n\t"
		  "extw	 %0\n\t"
		  : "=&d" (rv), "=d" (c)
		  : "1" (c)
		);

	*ret = rv;
}


/* This duplicates a byte 2 times into a short. */

static __inline__ unsigned short dup2w( unsigned char c )

{	ushort	rv;

	__asm__ __volatile__
		( "moveb  %1,%0\n\t"
		  "lslw   #8,%0\n\t"
		  "moveb  %1,%0\n\t"
		  : "=&d" (rv)
		  : "d" (c)
		);

	return( rv );
}



/* ====================================================================== */

/* atacon_XXX routines - interface used by the world
 *
 * This system is now divided into two levels because of complications
 * caused by hardware scrolling. Top level functions:
 *
 *    atacon_bmove(), atacon_clear(), atacon_putc()
 *
 * handles y values in range [0, scr_height-1] that correspond to real
 * screen positions.
 *
 *    atacon_bmove_physical_8()   -- These functions fast implementations
 *    atacon_clear_physical_8()   -- of original atacon_XXX fns.
 *    atacon_putc_physical_8()    -- (fontwidth  != 8) may be added later
 *
 */


static int atacon_bmove (struct condata *conp,
          int sy, int sx, int dy, int dx,
          int height, int width)
{
    int unit = conp - vc_cons;
    struct display *p = &disp[unit];
	
	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))
		atacon_cursor( conp, CM_ERASE );
	
	p->dispsw->bmove( p, sy, sx, dy, dx, height, width );
    return(0);
}

static int atacon_clear (struct condata *conp,
                         int sy, int sx, int height, int width)
{
    int unit = conp - vc_cons;
    register struct display *p = &disp[unit];

	if (sy <= p->cursor_y && p->cursor_y < sy+height &&
		sx <= p->cursor_x && p->cursor_x < sx+width)
		CURSOR_UNDRAWN();

	p->dispsw->clear( conp, p, sy, sx, height, width );

    return(0);
}

static int atacon_putc (struct condata *conp,
                        int c, int y, int x, int mode)
{
    int unit = conp - vc_cons;
    register struct display *p = &disp[unit];

	if (p->cursor_x == x && p->cursor_y == y)
		CURSOR_UNDRAWN();
	
	p->dispsw->putc( conp, p, c, y, x, mode );

    return(0);
}

static int atacon_putcs (struct condata *conp, const char *s,
                         int count, int y, int x, int mode)
{
    int unit = conp - vc_cons;
    register struct display *p = &disp[unit];

	if (p->cursor_y == y &&
		x <= p->cursor_x && p->cursor_x < x+count)
		CURSOR_UNDRAWN();
	
	p->dispsw->putcs( conp, p, s, count, y, x, mode );

    return(0);
}


static int atacon_scroll (struct condata *conp,
                          int t, int b, int dir, int count)
{
    switch (dir) {

	  case SM_UP:
		atacon_bmove( conp, t + count, 0, t, 0,
					  (b-t-count), conp->vc_cols );
		atacon_clear( conp, b-count, 0, count, conp->vc_cols );
        break;

	  case SM_DOWN:
		atacon_bmove( conp, t, 0, t+count, 0,
					  (b-t-count), conp->vc_cols );

        /* Fixed bmove() should end Arno's frustration with copying?
         * Confusius say:
         *    Man who copies in wrong direction, end up with trashed data
		 */

		atacon_clear( conp, t, 0, count, conp->vc_cols );
        break;

	  case SM_LEFT:
		atacon_bmove( conp, 0, t + count, 0, t,
					  conp->vc_rows, (b-t-count) );
		atacon_clear( conp, 0, b-count, conp->vc_rows, count );
        break;

	  case SM_RIGHT:
		atacon_bmove( conp, 0, t, 0, t+count,
					  conp->vc_rows, (b-t-count) );
		atacon_clear( conp, 0, t, conp->vc_rows, count );
        break;
    }

    return 0;
}

static int atacon_switch (struct condata *conp)
{
    return 0;
}



/* ================================================================= */
/*                           1 Plane Functions                       */
/* ================================================================= */


static void atacon_bmove_1_plane( register struct display *p,
								  int sy, int sx, int dy, int dx,
								  int height, int width)
{

    if (sx == 0 && dx == 0 && width == p->bytes_per_row) {
		mymemmove(p->bitplane + dy * width * p->fontheight,
				  p->bitplane + sy * width * p->fontheight,
				  width * height * p->fontheight );
    } else {
        register ushort rows;
        register u_char *src;
        register u_char *dst;
        register ushort bytes      = p->bytes_per_row;
        register ushort linesize   = bytes * p->fontheight;
        
        if (dy < sy || (dy == sy && dx < sx)) {
			src  = p->bitplane + sy * linesize + sx;
			dst  = p->bitplane + dy * linesize + dx;
			for (rows = height*p->fontheight ; rows-- ;) {
				mymemmove(dst, src, width);
				src += bytes;
				dst += bytes;
			}
        } else {
			src  = p->bitplane + (sy+height) * linesize + sx - bytes;
			dst  = p->bitplane + (dy+height) * linesize + dx - bytes;

			for (rows = height*p->fontheight ; rows-- ;) {
				mymemmove(dst, src, width);
				src -= bytes;
				dst -= bytes;
			}
        }
    }
}


static void atacon_clear_1_plane( struct condata *conp, register struct
								  display *p, int sy, int sx, int height,
								  int width )
{
    ulong offset;
    register u_char *start;
    register ushort rows;
    register ushort bytes = p->bytes_per_row;
    register ulong size;

    if (sx == 0 && width == bytes) {
        offset = sy     * bytes * p->fontheight;
        size   = height * bytes * p->fontheight;
		mymemclear( p->bitplane+offset, size );
    } else {
        offset = (sy * bytes * p->fontheight) + sx;
		start = p->bitplane+offset;
		
		for (rows = height*p->fontheight; rows-- ; start += bytes)
			mymemclear_small(start, width);
    }
}


static void atacon_putc_1_plane( struct condata *conp, struct display *p,
								 int c, int y, int x, int mode)
{
    register u_char   *dest;
    register u_char   *cdat;
    register ushort   rows;
    u_short           bytes;
    ulong             offset;

	if (!FONT_CNV(c)) return;

    bytes  = p->bytes_per_row;
    offset = y * p->fontheight * bytes + x;
    dest  = p->bitplane + offset;
    cdat  = p->fontdata + (c * p->fontheight);

	if (conp->vc_intensity != 1 || conp->vc_reverse ^ conp->vc_decscnm) {
		register u_char	andmask, xormask, c;
		register int	bold;
		
		andmask = (conp->vc_intensity == 0) ? 0x55 : 0xff;
		xormask = (conp->vc_reverse ^ conp->vc_decscnm) ? 0xff : 0;
		bold = (conp->vc_intensity == 2);
		
		for (rows = p->fontheight ; rows-- ; dest += bytes) {
			c = *cdat++;
			if (rows == 0 && conp->vc_underline) c = 0xff;
			c = (c & andmask) ^ xormask;
			if (bold) c |= c >> 1;
			*dest = c;
		}
	}
	else {
		
		for (rows = p->fontheight ; rows-- ; dest += bytes)
			*dest = *cdat++;

		if (conp->vc_underline)
			dest[-bytes] ^= ~0;
	}
}


static void atacon_putcs_1_plane( struct condata *conp, struct
								  display *p, const char *s,
								  int count, int y, int x, int mode)
{
    register u_char   *dest, *cdat;
    register ushort   rows, bytes;
    register u_char   c;
    u_char   *dest0;
    ulong    offset;
	int		 effects = (conp->vc_underline || conp->vc_intensity != 1 ||
						(conp->vc_reverse ^ conp->vc_decscnm));
	
	if (!count) return;
	
    bytes  = p->bytes_per_row;
    offset = y * p->fontheight * bytes + x;
    dest0  = p->bitplane + offset;

#if defined (__GNUC__) && defined (__mc68000__)
    if (!effects && p->fontheight == 8) {
		void *tmpa;
        /* Ultra special support for 8x8 fonts, as its nice and easy
         * and the console driver seems to spend most of its time in
         * here. Would take another 4 instructions to support,
         * (fontheight != 8) but I have an inbuilt suspicion of mulu's.
         * A hangover from my 68000 days, I guess.
         *
         * Both code and register use mirrors the C code directly below:
         *
         * a0 -- dest & dest0  [uses stack] 
         * a1 -- cdat     ; a2 -- s      ; a3 -- fontdata
         * d0 -- scratch  ; d1 -- count  ; d2 -- bytes
         * 
         */
        __asm__ __volatile__
			   ("subqw  #1,%1\n\t"
                "moveq  #0,d0\n\t"
			 "1: moveb  %2@+,d0\n\t"
#ifndef WHOLE_FONT
                "cmpb   #32,d0  ; jlt 2f ; cmpb #127,d0 ; jge 2f\n\t"
				"subb   #32,d0  ; jra 3f\n\t"
             "2: cmpb   #160,d0 ; jlt 1b ; cmpb #255,d0 ; jeq 1b\n\t"
				"subb   #64,d0\n\t3: "
#else
				"moveb %9@(d0:w),d0 ; beq 1b\n\t"
#endif
               "lea %7@(d0:w:8),%3 ; movel %0,sp@-\n\t"
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 0 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 1 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 2 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 3 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 4 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 5 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 6 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 7 */
                "movel  sp@+,%0 ; addql #1,%0 ; dbra %1,1b\n\t"
                : "=a" (dest0), "=d" (count), "=a" (s), "=&a" (tmpa)
                : "0" (dest0), "1" (count), "2" (s), "a" (p->fontdata),
				  "d" (bytes)
#ifdef WHOLE_FONT
				  , "a" (conp->vc_translate)
#endif
				: "d0" /* Sorry, it isn't possible to let gcc choose
					    * this register, too, because this would
					    * require another output operand and thus give
					    * more than 10 parameters total. But gcc can
					    * only handle up to 10 parameters at present :-(
					    */
			   );
        return;
    }
    if (!effects && p->fontheight == 16) {
		/* ++roman: the same for 8x16 */
		void *tmpa;
        __asm__ __volatile__
			   ("subqw  #1,%1\n\t"
             "1: moveq  #0,d0   ; moveb %2@+,d0\n\t"
#ifndef WHOLE_FONT
                "cmpb   #32,d0  ; jlt 2f ; cmpb #127,d0 ; jge 2f\n\t"
				"subb   #32,d0  ; jra 3f\n\t"
             "2: cmpb   #160,d0 ; jlt 1b ; cmpb #255,d0 ; jeq 1b\n\t"
				"subb   #64,d0\n\t3: "
#else
				"moveb %9@(d0:w),d0 ; beq 1b\n\t"
#endif
               "lslw #4,d0     ; lea %7@(d0:w),%3 ; movel %0,sp@-\n\t"
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 0 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 1 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 2 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 3 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 4 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 5 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 6 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 7 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 8 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 9 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 10 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 11 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 12 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 13 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 14 */
               " moveb  %3@+,%0@; addaw %8,%0\n\t" /* 15 */
                "movel  sp@+,%0 ; addql #1,%0 ; dbra %1,1b\n\t"
                : "=a" (dest0), "=d" (count), "=a" (s), "=&a" (tmpa)
                : "0" (dest0), "1" (count), "2" (s), "a" (p->fontdata),
				  "d" (bytes)
#ifdef WHOLE_FONT
				  , "a" (conp->vc_translate)
#endif
				: "d0" /* Sorry, it isn't possible to let gcc choose
					    * this register, too, because this would
					    * require another output operand and thus give
					    * more than 10 parameters total. But gcc can
					    * only handle up to 10 parameters at present :-(
					    */
			   );
        return;
    }
#endif

	if (effects) {
		register u_char	andmask, xormask, ch;
		register int	bold;
		
		andmask = (conp->vc_intensity == 0) ? 0x55 : 0xff;
		xormask = (conp->vc_reverse ^ conp->vc_decscnm) ? 0xff : 0;
		bold = (conp->vc_intensity == 2);

		while (count--) {
			c = *(s++);
			if (!FONT_CNV(c)) continue;

			dest = dest0++;
			cdat  = p->fontdata + (c * p->fontheight);

			for (rows = p->fontheight ; rows-- ; dest += bytes) {
				ch = *cdat++;
				if (rows == 0 && conp->vc_underline) ch = 0xff;
				ch = (ch & andmask) ^ xormask;
				if (bold) ch |= ch >> 1;
				*dest = ch;
			}
		}
	}
	else {

		while (count--) {
			c = *(s++);
			if (!FONT_CNV(c)) continue;

			dest = dest0++;
			cdat  = p->fontdata + (c * p->fontheight);

			for (rows = p->fontheight ; rows-- ; dest += bytes)
				*dest = *cdat++;
		}
	}
}


static void atacon_rev_char_1_plane( struct display *display, int x, int y )

{	u_char	*p;
	int		offs;
	u_short j;
	
	offs = y * display->fontheight * display->bytes_per_row + x;

	p = display->bitplane + offs;
	j = display->fontheight - 1;
	__asm__ __volatile__
		("1: notb  %0@ ; addaw %4,%0\n\t"
		 "dbra  %1,1b"
		 : "=a" (p), "=d" (j)
		 : "0" (p), "1" (j),
		 "d" (display->bytes_per_row)
		 );
}



#ifdef CONFIG_ATARI_2PLANE

/* ================================================================= */
/*                           2 Plane Functions                       */
/* ================================================================= */


/* Increment/decrement 2 plane addresses */

#define	INC_2P(p)	do { if (!((long)(++(p)) & 1)) (p) += 2; } while(0)
#define	DEC_2P(p)	do { if ((long)(--(p)) & 1) (p) -= 2; } while(0)

/* Convert a standard 4 bit color to our 2 bit color assignment:
 * If at least two RGB channels are active, the low bit is turned on;
 * The intensity bit (b3) is shifted into b1.
 */

#define	COLOR_2P(c)	(((c & 7) >= 3 && (c & 7) != 4) | (c & 8) >> 2)


static void atacon_bmove_2_plane( register struct display *p,
								  int sy, int sx, int dy, int dx,
								  int height, int width)
{
	/* bmove() has to distinguish two major cases: If both, source and
	 * destination, start at even addresses or both are at odd
	 * addresses, just the first odd and last even column (if present)
	 * require special treatment (memmove_col()). The rest between
	 * then can be copied by normal operations, because all adjancent
	 * bytes are affected and are to be stored in the same order.
	 *   The pathological case is when the move should go from an odd
	 * address to an even or vice versa. Since the bytes in the plane
	 * words must be assembled in new order, it seems wisest to make
	 * all movements by memmove_col().
	 */

    if (sx == 0 && dx == 0 && width == p->bytes_per_row/2) {
		/* Special (but often used) case: Moving whole lines can be
		 * done with memmove()
		 */
		mymemmove( p->bitplane + dy * p->bytes_per_row * p->fontheight,
				   p->bitplane + sy * p->bytes_per_row * p->fontheight,
				   p->bytes_per_row * height * p->fontheight );
    } else {
        register ushort rows, cols;
        register u_char *src;
        register u_char *dst;
        register ushort bytes    = p->bytes_per_row;
        register ushort linesize = bytes * p->fontheight;
		       unsigned colsize  = height * p->fontheight;
		       unsigned upwards  = (dy < sy) || (dy == sy && dx < sx);

		if ((sx & 1) == (dx & 1)) {
			/* odd->odd or even->even */

			if (upwards) {

				src = p->bitplane + sy * linesize + (sx>>1)*4 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*4 + (dx & 1);

				if (sx & 1) {
					memmove_2p_col( dst, src, colsize, bytes );
					src += 3;
					dst += 3;
					--width;
				}

				if (width > 1) {
					for( rows = colsize; rows > 0; --rows ) {
						mymemmove( dst, src, (width>>1)*4 );
						src += bytes;
						dst += bytes;
					}
				}
				
				if (width & 1) {
					src -= colsize * bytes;
					dst -= colsize * bytes;
					memmove_2p_col( dst + (width>>1)*4, src + (width>>1)*4,
									colsize, bytes );
				}
			}
			else {

				if (!((sx+width-1) & 1)) {
					src = p->bitplane + sy * linesize + ((sx+width-1)>>1)*4;
					dst = p->bitplane + dy * linesize + ((dx+width-1)>>1)*4;
					memmove_2p_col( dst, src, colsize, bytes );
					--width;
				}

				src = p->bitplane + sy * linesize + (sx>>1)*4 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*4 + (dx & 1);

				if (width > 1) {
					src += colsize * bytes + (sx & 1)*3;
					dst += colsize * bytes + (sx & 1)*3;
					for( rows = colsize; rows > 0; --rows ) {
						src -= bytes;
						dst -= bytes;
						mymemmove( dst, src, (width>>1)*4 );
					}
				}

				if (width & 1) {
					memmove_2p_col( dst-3, src-3, colsize, bytes );
				}

			}
		}
		else {
			/* odd->even or even->odd */

			if (upwards) {
				src = p->bitplane + sy * linesize + (sx>>1)*4 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*4 + (dx & 1);
				for( cols = width; cols > 0; --cols ) {
					memmove_2p_col( dst, src, colsize, bytes );
					INC_2P( src );
					INC_2P( dst );
				}
			}
			else {
				sx += width-1;
				dx += width-1;
				src = p->bitplane + sy * linesize + (sx>>1)*4 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*4 + (dx & 1);
				for( cols = width; cols > 0; --cols ) {
					memmove_2p_col( dst, src, colsize, bytes );
					DEC_2P( src );
					DEC_2P( dst );
				}
			}
		}
			
		
    }
}


static void atacon_clear_2_plane( struct condata *conp, register struct
								  display *p, int sy, int sx, int height,
								  int width )
{
    ulong offset;
    register u_char *start;
    register ushort rows;
    register ushort bytes = p->bytes_per_row;
	ushort          lines = height * p->fontheight;
    register ulong  size;
	u_long          cval;
	u_short			pcval;
	
	expand2l( COLOR_2P(conp->vc_video_erase_char >> 12), &cval );
	
    if (sx == 0 && width == bytes/2) {

        offset = sy * bytes * p->fontheight;
        size   = lines * bytes;
		memset_even_2p( p->bitplane+offset, size, cval );

    } else {

        offset = (sy * bytes * p->fontheight) + (sx>>1)*4 + (sx & 1);
		start = p->bitplane + offset;
		pcval = expand2w( COLOR_2P(conp->vc_video_erase_char >> 12) );
		
		/* Clears are split if the region starts at an odd column or
		 * end at an even column. These extra columns are spread
		 * across the interleaved planes. All in between can be
		 * cleared by normal mymemclear_small(), because both bytes of
		 * the single plane words are affected.
		 */

		if (sx & 1) {
			memclear_2p_col( start, lines, pcval, bytes );
			start += 3;
			width--;
		}

		if (width & 1) {
			memclear_2p_col( start + (width>>1)*4, lines, pcval, bytes );
			width--;
		}

		if (width) {
			for( rows = lines; rows-- ; start += bytes )
				memset_even_2p( start, width*2, cval );
		}
    }
}


static void atacon_putc_2_plane( struct condata *conp, struct display *p,
								 int c, int y, int x, int mode)

{	register u_char   *dest;
    register u_char   *cdat;
    register ushort   rows;
    register u_short  bytes = p->bytes_per_row;
	ulong			  eorx, fgx, bgx, fdx;

	if (!FONT_CNV(c)) return;

    dest  = p->bitplane + y * p->fontheight * bytes + (x>>1)*4 + (x & 1);
    cdat  = p->fontdata + (c * p->fontheight);

	fgx   = expand2w( COLOR_2P(conp->vc_attr) );
	bgx   = expand2w( COLOR_2P(conp->vc_attr >> 4) );
	eorx  = fgx ^ bgx;
		
    for( rows = p->fontheight ; rows-- ; dest += bytes ) {
		fdx = dup2w( *cdat++ );
		__asm__ __volatile__ ( "movepw %1,%0@(0)" : /* no outputs */
							   : "a" (dest), "d" ((fdx & eorx) ^ bgx) );
	}

	if (conp->vc_underline) {
		__asm__ __volatile__ ( "movepw %1,%0@(0)" : /* no outputs */
							   : "a" (dest-bytes), "d" (fgx) );
	}
}


static void atacon_putcs_2_plane( struct condata *conp, struct
								  display *p, const char *s,
								  int count, int y, int x, int mode)

{	register u_char   *dest, *dest0;
    register u_char   *cdat, c;
    register ushort   rows;
    register u_short  bytes;
	ulong			  eorx, fgx, bgx, fdx;

    bytes = p->bytes_per_row;
    dest0 = p->bitplane + y * p->fontheight * bytes + (x>>1)*4 + (x & 1);
	fgx   = expand2w( COLOR_2P(conp->vc_attr) );
	bgx   = expand2w( COLOR_2P(conp->vc_attr >> 4) );
	eorx  = fgx ^ bgx;

	while( count-- > 0 ) {

		c = *s++;
		if (!FONT_CNV(c)) continue;
		cdat  = p->fontdata + (c * p->fontheight);
	
		for( rows = p->fontheight, dest = dest0; rows-- ; dest += bytes ) {
			fdx = dup2w( *cdat++ );
			__asm__ __volatile__ ( "movepw %1,%0@(0)" : /* no outputs */
								   : "a" (dest), "d" ((fdx & eorx) ^ bgx) );
		}

		if (conp->vc_underline) {
			__asm__ __volatile__ ( "movepw %1,%0@(0)" : /* no outputs */
								   : "a" (dest-bytes), "d" (fgx) );
		}

		INC_2P(dest0);
	}
}


static void atacon_rev_char_2_plane( struct display *display, int x, int y )

{	u_char	*p;
	u_short j;
	
	p = display->bitplane +
		y * display->fontheight * display->bytes_per_row +
		(x>>1)*4 + (x & 1);
	j = display->fontheight - 1;

	__asm__ __volatile__
		/* This should really obey the individual character's
		 * background and foreground colors instead of simply
		 * inverting.
		 */
		("1: notb  %0@ ; notb %0@(2)\n\t"
         "addaw %4,%0\n\t"
		 "dbra  %1,1b"
		 : "=a" (p), "=d" (j)
		 : "0" (p), "1" (j), "d" (display->bytes_per_row)
		 );
}


#endif /* CONFIG_ATARI_2PLANE */



#ifdef CONFIG_ATARI_4PLANE

/* ================================================================= */
/*                           4 Plane Functions                       */
/* ================================================================= */


/* Increment/decrement 4 plane addresses */

#define	INC_4P(p)	do { if (!((long)(++(p)) & 1)) (p) += 6; } while(0)
#define	DEC_4P(p)	do { if ((long)(--(p)) & 1) (p) -= 6; } while(0)


static void atacon_bmove_4_plane( register struct display *p,
								  int sy, int sx, int dy, int dx,
								  int height, int width)
{
	/* bmove() has to distinguish two major cases: If both, source and
	 * destination, start at even addresses or both are at odd
	 * addresses, just the first odd and last even column (if present)
	 * require special treatment (memmove_col()). The rest between
	 * then can be copied by normal operations, because all adjancent
	 * bytes are affected and are to be stored in the same order.
	 *   The pathological case is when the move should go from an odd
	 * address to an even or vice versa. Since the bytes in the plane
	 * words must be assembled in new order, it seems wisest to make
	 * all movements by memmove_col().
	 */

    if (sx == 0 && dx == 0 && width == p->bytes_per_row/4) {
		/* Special (but often used) case: Moving whole lines can be
		 * done with memmove()
		 */
		mymemmove( p->bitplane + dy * p->bytes_per_row * p->fontheight,
				   p->bitplane + sy * p->bytes_per_row * p->fontheight,
				   p->bytes_per_row * height * p->fontheight );
    } else {
        register ushort rows, cols;
        register u_char *src;
        register u_char *dst;
        register ushort bytes    = p->bytes_per_row;
        register ushort linesize = bytes * p->fontheight;
		       unsigned colsize  = height * p->fontheight;
		       unsigned upwards  = (dy < sy) || (dy == sy && dx < sx);

		if ((sx & 1) == (dx & 1)) {
			/* odd->odd or even->even */

			if (upwards) {

				src = p->bitplane + sy * linesize + (sx>>1)*8 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*8 + (dx & 1);

				if (sx & 1) {
					memmove_4p_col( dst, src, colsize, bytes );
					src += 7;
					dst += 7;
					--width;
				}

				if (width > 1) {
					for( rows = colsize; rows > 0; --rows ) {
						mymemmove( dst, src, (width>>1)*8 );
						src += bytes;
						dst += bytes;
					}
				}
				
				if (width & 1) {
					src -= colsize * bytes;
					dst -= colsize * bytes;
					memmove_4p_col( dst + (width>>1)*8, src + (width>>1)*8,
									colsize, bytes );
				}
			}
			else {

				if (!((sx+width-1) & 1)) {
					src = p->bitplane + sy * linesize + ((sx+width-1)>>1)*8;
					dst = p->bitplane + dy * linesize + ((dx+width-1)>>1)*8;
					memmove_4p_col( dst, src, colsize, bytes );
					--width;
				}

				src = p->bitplane + sy * linesize + (sx>>1)*8 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*8 + (dx & 1);

				if (width > 1) {
					src += colsize * bytes + (sx & 1)*7;
					dst += colsize * bytes + (sx & 1)*7;
					for( rows = colsize; rows > 0; --rows ) {
						src -= bytes;
						dst -= bytes;
						mymemmove( dst, src, (width>>1)*8 );
					}
				}

				if (width & 1) {
					memmove_4p_col( dst-7, src-7, colsize, bytes );
				}

			}
		}
		else {
			/* odd->even or even->odd */

			if (upwards) {
				src = p->bitplane + sy * linesize + (sx>>1)*8 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*8 + (dx & 1);
				for( cols = width; cols > 0; --cols ) {
					memmove_4p_col( dst, src, colsize, bytes );
					INC_4P( src );
					INC_4P( dst );
				}
			}
			else {
				sx += width-1;
				dx += width-1;
				src = p->bitplane + sy * linesize + (sx>>1)*8 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*8 + (dx & 1);
				for( cols = width; cols > 0; --cols ) {
					memmove_4p_col( dst, src, colsize, bytes );
					DEC_4P( src );
					DEC_4P( dst );
				}
			}
		}
			
		
    }
}


static void atacon_clear_4_plane( struct condata *conp, register struct
								  display *p, int sy, int sx, int height,
								  int width )
{
    ulong offset;
    register u_char *start;
    register ushort rows;
    register ushort bytes = p->bytes_per_row;
	ushort          lines = height * p->fontheight;
    register ulong  size;
	u_long          cval1, cval2, pcval;

	expand4dl( conp->vc_video_erase_char >> 12, &cval1, &cval2 );
	
    if (sx == 0 && width == bytes/4) {

        offset = sy * bytes * p->fontheight;
        size   = lines * bytes;
		memset_even_4p( p->bitplane+offset, size, cval1, cval2 );

    } else {

        offset = (sy * bytes * p->fontheight) + (sx>>1)*8 + (sx & 1);
		start = p->bitplane + offset;
		pcval = expand4l( conp->vc_video_erase_char >> 12 );
		
		/* Clears are split if the region starts at an odd column or
		 * end at an even column. These extra columns are spread
		 * across the interleaved planes. All in between can be
		 * cleared by normal mymemclear_small(), because both bytes of
		 * the single plane words are affected.
		 */

		if (sx & 1) {
			memclear_4p_col( start, lines, pcval, bytes );
			start += 7;
			width--;
		}

		if (width & 1) {
			memclear_4p_col( start + (width>>1)*8, lines, pcval, bytes );
			width--;
		}

		if (width) {
			for( rows = lines; rows-- ; start += bytes )
				memset_even_4p( start, width*4, cval1, cval2 );
		}
    }
}


static void atacon_putc_4_plane( struct condata *conp, struct display *p,
								 int c, int y, int x, int mode)
{	register u_char   *dest;
    register u_char   *cdat;
    register ushort   rows;
    register u_short  bytes = p->bytes_per_row;
	ulong			  eorx, fgx, bgx, fdx;

	if (!FONT_CNV(c)) return;

    dest  = p->bitplane + y * p->fontheight * bytes + (x>>1)*8 + (x & 1);
    cdat  = p->fontdata + (c * p->fontheight);

	fgx   = expand4l( conp->vc_attr );
	bgx   = expand4l( conp->vc_attr >> 4 );
	eorx  = fgx ^ bgx;
		
    for( rows = p->fontheight ; rows-- ; dest += bytes ) {
		fdx = dup4l( *cdat++ );
		__asm__ __volatile__ ( "movepl %1,%0@(0)" : /* no outputs */
							   : "a" (dest), "d" ((fdx & eorx) ^ bgx) );
	}

	if (conp->vc_underline) {
		__asm__ __volatile__ ( "movepl %1,%0@(0)" : /* no outputs */
							   : "a" (dest-bytes), "d" (fgx) );
	}
}


static void atacon_putcs_4_plane( struct condata *conp, struct
								  display *p, const char *s,
								  int count, int y, int x, int mode)
{	register u_char   *dest, *dest0;
    register u_char   *cdat, c;
    register ushort   rows;
    register u_short  bytes;
	ulong			  eorx, fgx, bgx, fdx;

    bytes = p->bytes_per_row;
    dest0 = p->bitplane + y * p->fontheight * bytes + (x>>1)*8 + (x & 1);
	fgx   = expand4l( conp->vc_attr );
	bgx   = expand4l( conp->vc_attr >> 4 );
	eorx  = fgx ^ bgx;

	while( count-- > 0 ) {

		/* I think, unrolling the loops like in the 1 plane case isn't
		 * practicable here, because the body is much longer for 4
		 * planes (mostly the dup4l()). I guess, unrolling this would
		 * need more than 256 bytes and so exceed the instruction
		 * cache :-(
		 */
		
		c = *s++;
		if (!FONT_CNV(c)) continue;
		cdat  = p->fontdata + (c * p->fontheight);
	
		for( rows = p->fontheight, dest = dest0; rows-- ; dest += bytes ) {
			fdx = dup4l( *cdat++ );
			__asm__ __volatile__ ( "movepl %1,%0@(0)" : /* no outputs */
								   : "a" (dest), "d" ((fdx & eorx) ^ bgx) );
		}

		if (conp->vc_underline) {
			__asm__ __volatile__ ( "movepl %1,%0@(0)" : /* no outputs */
								   : "a" (dest-bytes), "d" (fgx) );
		}

		INC_4P(dest0);
	}
}


static void atacon_rev_char_4_plane( struct display *display, int x, int y )

{	u_char	*p;
	u_short j;
	
	p = display->bitplane +
		y * display->fontheight * display->bytes_per_row +
		(x>>1)*8 + (x & 1);
	j = display->fontheight - 1;

	__asm__ __volatile__
		/* This should really obey the individual character's
		 * background and foreground colors instead of simply
		 * inverting.
		 */
		("1: notb  %0@ ; notb %0@(2) ; notb %0@(4) ; notb %0@(6)\n\t"
         "addaw %4,%0\n\t"
		 "dbra  %1,1b"
		 : "=a" (p), "=d" (j)
		 : "0" (p), "1" (j), "d" (display->bytes_per_row)
		 );
}


#endif /* CONFIG_ATARI_4PLANE */



#ifdef CONFIG_ATARI_8PLANE

/* ================================================================= */
/*                           8 Plane Functions                       */
/* ================================================================= */


/* In 8 plane mode, 256 colors would be possible, but only the first
 * 16 are used by the console code (the upper 4 bits are
 * background/unused). For that, the following functions mask off the
 * higher 4 bits of each color.
 */

/* Increment/decrement 8 plane addresses */

#define	INC_8P(p)	do { if (!((long)(++(p)) & 1)) (p) += 14; } while(0)
#define	DEC_8P(p)	do { if ((long)(--(p)) & 1) (p) -= 14; } while(0)


static void atacon_bmove_8_plane( register struct display *p,
								  int sy, int sx, int dy, int dx,
								  int height, int width)
{
	/* bmove() has to distinguish two major cases: If both, source and
	 * destination, start at even addresses or both are at odd
	 * addresses, just the first odd and last even column (if present)
	 * require special treatment (memmove_col()). The rest between
	 * then can be copied by normal operations, because all adjancent
	 * bytes are affected and are to be stored in the same order.
	 *   The pathological case is when the move should go from an odd
	 * address to an even or vice versa. Since the bytes in the plane
	 * words must be assembled in new order, it seems wisest to make
	 * all movements by memmove_col().
	 */

    if (sx == 0 && dx == 0 && width == p->bytes_per_row/8) {
		/* Special (but often used) case: Moving whole lines can be
		 * done with memmove()
		 */
		mymemmove( p->bitplane + dy * p->bytes_per_row * p->fontheight,
				   p->bitplane + sy * p->bytes_per_row * p->fontheight,
				   p->bytes_per_row * height * p->fontheight );
    } else {
        register ushort rows, cols;
        register u_char *src;
        register u_char *dst;
        register ushort bytes    = p->bytes_per_row;
        register ushort linesize = bytes * p->fontheight;
		       unsigned colsize  = height * p->fontheight;
		       unsigned upwards  = (dy < sy) || (dy == sy && dx < sx);

		if ((sx & 1) == (dx & 1)) {
			/* odd->odd or even->even */

			if (upwards) {

				src = p->bitplane + sy * linesize + (sx>>1)*16 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*16 + (dx & 1);

				if (sx & 1) {
					memmove_8p_col( dst, src, colsize, bytes );
					src += 15;
					dst += 15;
					--width;
				}

				if (width > 1) {
					for( rows = colsize; rows > 0; --rows ) {
						mymemmove( dst, src, (width>>1)*16 );
						src += bytes;
						dst += bytes;
					}
				}
				
				if (width & 1) {
					src -= colsize * bytes;
					dst -= colsize * bytes;
					memmove_8p_col( dst + (width>>1)*16, src + (width>>1)*16,
									colsize, bytes );
				}
			}
			else {

				if (!((sx+width-1) & 1)) {
					src = p->bitplane + sy * linesize + ((sx+width-1)>>1)*16;
					dst = p->bitplane + dy * linesize + ((dx+width-1)>>1)*16;
					memmove_8p_col( dst, src, colsize, bytes );
					--width;
				}

				src = p->bitplane + sy * linesize + (sx>>1)*16 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*16 + (dx & 1);

				if (width > 1) {
					src += colsize * bytes + (sx & 1)*15;
					dst += colsize * bytes + (sx & 1)*15;
					for( rows = colsize; rows > 0; --rows ) {
						src -= bytes;
						dst -= bytes;
						mymemmove( dst, src, (width>>1)*16 );
					}
				}

				if (width & 1) {
					memmove_8p_col( dst-15, src-15, colsize, bytes );
				}

			}
		}
		else {
			/* odd->even or even->odd */

			if (upwards) {
				src = p->bitplane + sy * linesize + (sx>>1)*16 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*16 + (dx & 1);
				for( cols = width; cols > 0; --cols ) {
					memmove_8p_col( dst, src, colsize, bytes );
					INC_8P( src );
					INC_8P( dst );
				}
			}
			else {
				sx += width-1;
				dx += width-1;
				src = p->bitplane + sy * linesize + (sx>>1)*16 + (sx & 1);
				dst = p->bitplane + dy * linesize + (dx>>1)*16 + (dx & 1);
				for( cols = width; cols > 0; --cols ) {
					memmove_8p_col( dst, src, colsize, bytes );
					DEC_8P( src );
					DEC_8P( dst );
				}
			}
		}
			
		
    }
}


static void atacon_clear_8_plane( struct condata *conp, register struct
								  display *p, int sy, int sx, int height,
								  int width )
{
    ulong offset;
    register u_char *start;
    register ushort rows;
    register ushort bytes = p->bytes_per_row;
	ushort          lines = height * p->fontheight;
    register ulong  size;
	u_long          cval1, cval2, cval3, cval4, pcval1, pcval2;

	expand8ql( (conp->vc_video_erase_char >> 12) & 0xf,
			   &cval1, &cval2, &cval3, &cval4 );
	
    if (sx == 0 && width == bytes/8) {

        offset = sy * bytes * p->fontheight;
        size   = lines * bytes;
		memset_even_8p( p->bitplane+offset, size, cval1, cval2, cval3, cval4 );

    } else {

        offset = (sy * bytes * p->fontheight) + (sx>>1)*16 + (sx & 1);
		start = p->bitplane + offset;
		expand8dl( (conp->vc_video_erase_char >> 12) & 0xf, &pcval1, &pcval2 );
		
		/* Clears are split if the region starts at an odd column or
		 * end at an even column. These extra columns are spread
		 * across the interleaved planes. All in between can be
		 * cleared by normal mymemclear_small(), because both bytes of
		 * the single plane words are affected.
		 */

		if (sx & 1) {
			memclear_8p_col( start, lines, pcval1, pcval2, bytes );
			start += 7;
			width--;
		}

		if (width & 1) {
			memclear_8p_col( start + (width>>1)*16, lines, pcval1,
							 pcval2, bytes );
			width--;
		}

		if (width) {
			for( rows = lines; rows-- ; start += bytes )
				memset_even_8p( start, width*8, cval1, cval2, cval3, cval4 );
		}
    }
}


static void atacon_putc_8_plane( struct condata *conp, struct display *p,
								 int c, int y, int x, int mode)
{	register u_char   *dest;
    register u_char   *cdat;
    register ushort   rows;
    register u_short  bytes = p->bytes_per_row;
	ulong			  eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;

	if (!FONT_CNV(c)) return;

    dest  = p->bitplane + y * p->fontheight * bytes + (x>>1)*16 + (x & 1);
    cdat  = p->fontdata + (c * p->fontheight);

	expand8dl( conp->vc_attr & 0x0f, &fgx1, &fgx2 );
	expand8dl( (conp->vc_attr >> 4) & 0x0f, &bgx1, &bgx2 );
	eorx1  = fgx1 ^ bgx1; eorx2  = fgx2 ^ bgx2;
		
    for( rows = p->fontheight ; rows-- ; dest += bytes ) {
		fdx = dup4l( *cdat++ );
		__asm__ __volatile__
			( "movepl %1,%0@(0)\n\t"
			  "movepl %2,%0@(8)"
			  : /* no outputs */
			  : "a" (dest), "d" ((fdx & eorx1) ^ bgx1),
			    "d" ((fdx & eorx2) ^ bgx2)
			);
	}

	if (conp->vc_underline) {
		__asm__ __volatile__
			( "movepl %1,%0@(0)\n\t"
			  "movepl %2,%0@(8)"
			  : /* no outputs */
			  : "a" (dest-bytes), "d" (fgx1), "d" (fgx2)
			);
	}
}


static void atacon_putcs_8_plane( struct condata *conp, struct
								  display *p, const char *s,
								  int count, int y, int x, int mode)

{	register u_char   *dest, *dest0;
    register u_char   *cdat, c;
    register ushort   rows;
    register u_short  bytes;
	ulong			  eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;

    bytes = p->bytes_per_row;
    dest0 = p->bitplane + y * p->fontheight * bytes + (x>>1)*16 + (x & 1);

	expand8dl( conp->vc_attr & 0x0f, &fgx1, &fgx2 );
	expand8dl( (conp->vc_attr >> 4) & 0x0f, &bgx1, &bgx2 );
	eorx1  = fgx1 ^ bgx1; eorx2  = fgx2 ^ bgx2;

	while( count-- > 0 ) {

		/* I think, unrolling the loops like in the 1 plane case isn't
		 * practicable here, because the body is much longer for 4
		 * planes (mostly the dup4l()). I guess, unrolling this would
		 * need more than 256 bytes and so exceed the instruction
		 * cache :-(
		 */
		
		c = *s++;
		if (!FONT_CNV(c)) continue;
		cdat  = p->fontdata + (c * p->fontheight);
	
		for( rows = p->fontheight, dest = dest0; rows-- ; dest += bytes ) {
			fdx = dup4l( *cdat++ );
			__asm__ __volatile__
				( "movepl %1,%0@(0)\n\t"
				  "movepl %2,%0@(8)"
				  : /* no outputs */
				  : "a" (dest), "d" ((fdx & eorx1) ^ bgx1),
				  "d" ((fdx & eorx2) ^ bgx2)
				);
		}

		if (conp->vc_underline) {
			__asm__ __volatile__
				( "movepl %1,%0@(0)\n\t"
				  "movepl %2,%0@(8)"
				  : /* no outputs */
				  : "a" (dest-bytes), "d" (fgx1), "d" (fgx2)
				);
		}

		INC_8P(dest0);
	}
}


static void atacon_rev_char_8_plane( struct display *display, int x, int y )

{	u_char	*p;
	u_short j;
	
	p = display->bitplane +
		y * display->fontheight * display->bytes_per_row +
		(x>>1)*16 + (x & 1);
	j = display->fontheight - 1;

	__asm__ __volatile__
		/* This should really obey the individual character's
		 * background and foreground colors instead of simply
		 * inverting. For 8 plane mode, only the lower 4 bits of the
		 * color are inverted, because only that color registers have
		 * been set up.
		 */
		("1: notb  %0@ ; notb %0@(2) ; notb %0@(4) ; notb %0@(6)\n\t"
         "addaw %4,%0\n\t"
		 "dbra  %1,1b"
		 : "=a" (p), "=d" (j)
		 : "0" (p), "1" (j), "d" (display->bytes_per_row)
		 );
}


#endif /* CONFIG_ATARI_8PLANE */


/* ================================================================= */
/*                            Cursor Functions                       */
/* ================================================================= */


#ifdef CURSOR_DELAY_TIMER

static void atacon_curtimfunc( unsigned long _disp )

{	struct display *display = (struct display *)_disp;

	/* Do nothing if cursor is already drawn (how?) or the active
	 * console has been changed.
	 */
	if (cursor_drawn || display != &disp[fg_console])
		return;

	atacon_rev_char( display, display->cursor_x, display->cursor_y );
	cursor_drawn = 1;

}

#endif

#ifdef CURSOR_DELAY_VBL

static void atacon_vbl_handler( struct intframe *fp, void *dummy )

{	struct display *display;

	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.
		 */
		display = &disp[fg_console];
		display->dispsw->rev_char( display, display->cursor_x,
								   display->cursor_y );
		cursor_drawn ^= 1;
		vbl_cursor_cnt = cursor_blink_rate;
	}
}

#endif


static int atacon_cursor (struct condata *conp, int mode)

{	struct display *display = &disp[conp - vc_cons];

	switch( mode ) {

	  case CM_ERASE:
		if (cursor_drawn)
			display->dispsw->rev_char( display, display->cursor_x,
									   display->cursor_y );
		display->cursor_x = conp->vc_x;
		display->cursor_y = conp->vc_y;
		CURSOR_UNDRAWN();
#ifdef CURSOR_DELAY_VBL
		cursor_on = 0;
#endif
		break;

	  case CM_MOVE:
	  case CM_DRAW:
		if (cursor_drawn)
			display->dispsw->rev_char( display, display->cursor_x,
									   display->cursor_y);
		CURSOR_UNDRAWN();

		display->cursor_x = conp->vc_x;
		display->cursor_y = conp->vc_y;

#ifdef CURSOR_DELAY_TIMER		
		del_timer( &atacon_cursor_timer );
		atacon_cursor_timer.expires = CURSOR_DRAW_DELAY;
		atacon_cursor_timer.data = (unsigned long)display;
		add_timer( &atacon_cursor_timer );
#endif
#ifdef CURSOR_DELAY_VBL
		vbl_cursor_cnt = CURSOR_DRAW_DELAY;
		cursor_on = 1;
#endif
		break;
	}

	return( 0 );
}

/*
 * The console "switch" structure for the Atari native console
 */


struct consw ata_con =
{
    atacon_init, atacon_deinit, atacon_clear, atacon_putc, atacon_putcs,
    atacon_cursor, atacon_scroll, atacon_bmove, atacon_switch
};

