 /*
  * UAE - The Un*x Amiga Emulator
  *
  * Screen drawing functions
  *
  * Copyright 1995-1998 Bernd Schmidt
  * Copyright 1995 Alessandro Bissacco
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include <ctype.h>
#include <assert.h>

#include "config.h"
#include "options.h"
#include "threaddep/penguin.h"
#include "uae.h"
#include "memory.h"
#include "custom.h"
#include "readcpu.h"
#include "newcpu.h"
#include "xwin.h"
#include "autoconf.h"
#include "gui.h"
#include "picasso96.h"
#include "drawing.h"
#include "savestate.h"

#ifdef UNALIGNED_PROFITABLE
#include "p2c.h"
#endif

/* NOTE: Remove this once Toni finishes the AGA support in the asm-code - LORES_HACK is broken now */
/*
#if defined X86_ASSEMBLY
#define LORES_HACK
#endif
*/

/* Chipset type:
 * OCS_CHIPSET
 * ECS_AGNUS
 * ECS_DENISE
 * ECS_AGNUS | ECS_DENISE
 * AGA_CHIPSET
*/

int lores_factor, lores_shift, sprite_width, borderblank;
uae_u32 fmode;

static int sprx_inc, sprx_shift;
static int interlace_seen = 0;

static int dblpf_ind1[256], dblpf_ind2[256], dblpf_2nd1[256], dblpf_2nd2[256];
static int dblpf_aga1[256], dblpf_aga2[256];

static int dblpfofs[] = { 0, 2, 4, 8, 16, 32, 64, 128 };

/* Big lookup tables for planar to chunky conversion. */
uae_u32 superhirestab_h[256][1];
uae_u32 hirestab_h[256][2];
uae_u32 lorestab_h[256][4];

/* superhires ? */
uae_u32 hirestab_l[256][1];
uae_u32 lorestab_l[256][2];

static uae_u32 clxtab[256];

/* Video buffer description structure. Filled in by the graphics system
 * dependent code. */

struct vidbuf_description gfxvidinfo;

/* OCS/ECS mode color lookup table. */
xcolnr xcolors[4096];

/* AGA mode color lookup tables */
unsigned int xredcolors[256], xgreencolors[256], xbluecolors[256];

struct color_entry colors_for_drawing;

/* 880 isn't a magic number, it's a safe number with some padding at the end.
 * This used to be 1000, but that's excessive. (840 is too low). I'm too lazy
 * to figure out the exact space needed. UPDATE: superhires need bigger buffer */
union {
    /* Let's try to align this thing. */
    double uupzuq;
    long int cruxmedo;
    unsigned char apixels[1760];
} pixdata;

uae_u32 ham_linebuf[1760];
unsigned char spriteagadpfpixels[1760]; /* AGA dualplayfield sprite */

char *xlinebuffer;

static int *amiga2aspect_line_map, *native2amiga_line_map;
static char *row_map[2049];
static int max_drawn_amiga_line;

/* line_draw_funcs: pfield_do_linetoscr, pfield_do_fill_line, decode_ham6 */
typedef void (*line_draw_func)(int, int);

/* AGA mode or OCS/ECS mode functions */
typedef void (*pfield_doline_def)(int);
static pfield_doline_def *pfield_doline;
typedef void (*pfield_linetoscr_def)(int,int,int,int);
typedef void (*fill_line_def)(char*,int,int);
static fill_line_def fill_line_all;
static pfield_linetoscr_def pfield_linetoscr_full_all, pfield_linetoscr_full_double_all, pfield_linetoscr_all;

#define LINE_UNDECIDED 1
#define LINE_DECIDED 2
#define LINE_DECIDED_DOUBLE 3
#define LINE_AS_PREVIOUS 4
#define LINE_BLACK 5
#define LINE_REMEMBERED_AS_BLACK 6
#define LINE_DONE 7
#define LINE_DONE_AS_PREVIOUS 8
#define LINE_REMEMBERED_AS_PREVIOUS 9

static char *line_drawn;
static char linestate[(MAXVPOS + 1)*2 + 1];

uae_u8 line_data[(MAXVPOS+1) * 2][MAX_PLANES * MAX_WORDS_PER_LINE * 2];

static int min_diwstart, max_diwstop, prev_x_adjust, linetoscr_x_adjust, linetoscr_right_x;
static int linetoscr_x_adjust_bytes;
static int thisframe_y_adjust, prev_y_adjust;
static int thisframe_y_adjust_real, max_ypos_thisframe, min_ypos_for_screen;
static int extra_y_adjust;
int thisframe_first_drawn_line, thisframe_last_drawn_line;

static int last_redraw_point;

static int first_drawn_line, last_drawn_line;
static int first_block_line, last_block_line;

/* These are generated by the drawing code from the line_decisions array for
 * each line that needs to be drawn. */
static int bplehb, bplham, bpldualpf, bpldualpfpri, bpldualpf2of, bplplanecnt, bplres;
static int bpldelay1, bpldelay2, aga_active;
static int plfpri[3];
static int scoloff[2];

int picasso_requested_on;
int picasso_on;

uae_sem_t gui_sem;
int inhibit_frame;

int framecnt = 0;
static int frame_redraw_necessary;
static int picasso_redraw_necessary;

static __inline__ void count_frame (void)
{
    framecnt++;
    if (framecnt >= currprefs.framerate)
	framecnt = 0;
}

int coord_native_to_amiga_x (int x)
{
    x += linetoscr_x_adjust;
    x <<= (1 - lores_shift);
    return x + 2*DISPLAY_LEFT_SHIFT;
}

int coord_native_to_amiga_y (int y)
{
    return native2amiga_line_map[y] + thisframe_y_adjust - minfirstline;
}

void notice_screen_contents_lost (void)
{
    picasso_redraw_necessary = 1;
    frame_redraw_necessary = 2;
}

static struct decision *dp_for_drawing;
static struct draw_info *dip_for_drawing;

void record_diw_line (int first, int last)
{
    if (last > max_diwstop)
	max_diwstop = last;
    if (first < min_diwstart)
	min_diwstart = first;
}

/*
 * Screen update macros/functions
 */

static unsigned int ham_lastcolor;

static void ham_decoder(int pix, int stoppos, uae_u32 *buf)
{
    if(bplplanecnt==6) {
	if(currprefs.chipset & AGA_CHIPSET) {
	/* AGA mode HAM6 */
	    while (pix < stoppos) {
		int pv = pixdata.apixels[pix];
		switch(pv & 0x30) {
		    case 0x00: ham_lastcolor = colors_for_drawing.color_regs[pv]; break;
		    case 0x10: ham_lastcolor &= 0xFFFF00; ham_lastcolor |= (pv & 0xF) << 4; break;
		    case 0x20: ham_lastcolor &= 0x00FFFF; ham_lastcolor |= (pv & 0xF) << 20; break;
		    case 0x30: ham_lastcolor &= 0xFF00FF; ham_lastcolor |= (pv & 0xF) << 12; break;
		}
		if(buf) buf[pix] = ham_lastcolor;
		pix++;
	    }
	} else {
	/* OCS/ECS mode HAM6 */
	    while (pix < stoppos) {
		int pv = pixdata.apixels[pix];
		switch(pv & 0x30) {
		    case 0x00: ham_lastcolor = colors_for_drawing.color_regs[pv]; break;
		    case 0x10: ham_lastcolor &= 0xFF0; ham_lastcolor |= (pv & 0xF); break;
		    case 0x20: ham_lastcolor &= 0x0FF; ham_lastcolor |= (pv & 0xF) << 8; break;
		    case 0x30: ham_lastcolor &= 0xF0F; ham_lastcolor |= (pv & 0xF) << 4; break;
		}
		if(buf) buf[pix] = ham_lastcolor;
		pix++;
	    }
	}
    } else { /* AGA mode HAM8 */
	while (pix < stoppos) {
	    int pv = pixdata.apixels[pix];
	    switch(pv & 0x3) {
		case 0x0: ham_lastcolor = colors_for_drawing.color_regs[pv >> 2]; break;
		case 0x1: ham_lastcolor &= 0xFFFF03; ham_lastcolor |= (pv & 0xFC); break;
		case 0x2: ham_lastcolor &= 0x03FFFF; ham_lastcolor |= (pv & 0xFC) << 16; break;
		case 0x3: ham_lastcolor &= 0xFF03FF; ham_lastcolor |= (pv & 0xFC) << 8; break;
	    }
	    if(buf) buf[pix] = ham_lastcolor;
	    pix++;
	}
    }
}

static void init_ham_decoding(int first)
{
    int pix = dp_for_drawing->diwfirstword;
    ham_lastcolor = colors_for_drawing.color_regs[0];
    ham_decoder(pix,first,0);
}

static void decode_ham(int pix, int stoppos)
{
    uae_u32 *buf = ham_linebuf;

    if (stoppos > dp_for_drawing->diwlastword)
	stoppos = dp_for_drawing->diwlastword;
    if (pix < dp_for_drawing->diwfirstword) {
	ham_lastcolor = colors_for_drawing.color_regs[0];
	pix = dp_for_drawing->diwfirstword;
    }
#ifdef LORES_HACK
    if (currprefs.gfx_lores == 2)
	pix <<= 1, stoppos <<= 1;
#endif
    ham_decoder(pix, stoppos, buf);
}

static int linetoscr_double_offset;

/* convert AGA R8G8B8 to destination screen mode */

/*
#define CONVERT_RGB(color) ((xbluecolors[(color)&255]|xgreencolors[((color)>>8)&255]|xredcolors[((color)>>16)&255]))
*/
#define CONVERT_RGB(c) \
    (xbluecolors[((uae_u8*)(c))[0]]|xgreencolors[((uae_u8*)(c))[1]]|xredcolors[((uae_u8*)(c))[2]])

/* combined FILL_LINE for ECS and AGA */
#define FILL_LINE(NAME, TYPE, CONVERT, AGA) \
static void NAME (char *buf, int start, int stop) \
{ \
    TYPE *b = (TYPE *)buf; \
    int i;\
    unsigned long col = borderblank ? 0 : AGA ? colors_for_drawing.color_regs[0] : colors_for_drawing.acolors[0]; \
    for (i = start; i < stop; i++) \
	b[i] = (TYPE)CONVERT(AGA ? CONVERT_RGB(&col) : (xcolnr)col); \
}

/* combined LINE_TO_SCR for ECS and AGA */
#define LINE_TO_SCR(NAME, TYPE, DO_DOUBLE, CONVERT, AGA) \
static void NAME(int pix, int lframe_end, int diw_end, int stoppos) \
{ \
    TYPE *buf = ((TYPE *)xlinebuffer); \
    TYPE d1, d2; \
    uae_u8 xor = (uae_u8)(dp_for_drawing->bplcon4 >> 8); \
    int offset; \
\
    if (DO_DOUBLE) offset = linetoscr_double_offset / sizeof(TYPE); \
    /* Left border area */ \
    d1 = (TYPE)CONVERT(borderblank ? 0 : AGA ? CONVERT_RGB (&colors_for_drawing.color_regs[xor]) : colors_for_drawing.acolors[0]); \
    while (pix < lframe_end) { \
	buf[pix] = d1; if (DO_DOUBLE) buf[pix+offset] = d1; \
	pix++; \
    } \
    if (bplham) { \
	/* HAM playfield */ \
    while (pix < diw_end) { \
	    TYPE d = (TYPE)CONVERT(AGA ? CONVERT_RGB (&ham_linebuf[pix]) : &ham_linebuf[pix]); \
	    buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \
	    pix++; \
	} \
    } else if (bpldualpf) { \
	if (AGA) { \
	    /* AGA mode Dual Playfield */ \
	    int *lookup = bpldualpfpri ? dblpf_aga2 : dblpf_aga1; \
	    int *lookup_no = bpldualpfpri ? dblpf_2nd2 : dblpf_2nd1; \
	    while (pix < diw_end) { \
		int pixcol = pixdata.apixels[pix]; \
		int val = lookup[pixcol]; \
		TYPE d; \
		if (spriteagadpfpixels[pix]) { \
		    d = (TYPE)CONVERT(CONVERT_RGB (&colors_for_drawing.color_regs[spriteagadpfpixels[pix]])); \
		    spriteagadpfpixels[pix]=0; \
		} else { \
		    if (lookup_no[pixcol] == 2)  val += dblpfofs[bpldualpf2of]; \
		    d = (TYPE)CONVERT(CONVERT_RGB (&colors_for_drawing.color_regs[val])); \
		} \
		buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \
		pix++; \
	    } \
	} else { \
	    /* OCS/ECS mode Dual Playfield */ \
	    int *lookup = bpldualpfpri ? dblpf_ind2 : dblpf_ind1; \
	    while (pix < diw_end) { \
		int pixcol = pixdata.apixels[pix]; \
		TYPE d = CONVERT (colors_for_drawing.acolors[lookup[pixcol]]); \
		buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \
		pix++; \
	    } \
	} \
    } else if (bplehb) { \
	while (pix < diw_end) { \
	    int p = pixdata.apixels[pix]; \
	    TYPE d; \
	    if (AGA) { \
		/* AGA mode EHB playfield */ \
		int ehbtmp = p >= 32 ? (colors_for_drawing.color_regs[(p-32)^xor] >> 1) & 0x7F7F7F : colors_for_drawing.color_regs[p]; \
		d = (TYPE)CONVERT(CONVERT_RGB (&ehbtmp)); \
	    } else { \
		/* OCS/ECS mode EHB playfield */ \
		d = CONVERT (colors_for_drawing.acolors[p]); \
		if (p >= 32) d = CONVERT (xcolors[(colors_for_drawing.color_regs[p-32] >> 1) & 0x777]); \
	    } \
        buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \
	    pix++; \
	} \
    } else { \
    /* Playfield */ \
	while (pix < diw_end) { \
	    TYPE d = (TYPE)CONVERT(AGA ? CONVERT_RGB (&colors_for_drawing.color_regs[pixdata.apixels[pix]^xor]) : colors_for_drawing.acolors[pixdata.apixels[pix]]); \
	    buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \
	    pix++; \
	} \
    } \
    /* Right border area */ \
    d2 = (TYPE)CONVERT(borderblank ? 0 : AGA ? CONVERT_RGB (&colors_for_drawing.color_regs[xor]) : colors_for_drawing.acolors[0]); \
    while (pix < stoppos) { \
	buf[pix] = d2; if (DO_DOUBLE) buf[pix+offset] = d2; \
	pix++; \
    } \
}

#define pfield_linetoscr_full8_aga pfield_linetoscr_8_aga
#define pfield_linetoscr_full16_aga pfield_linetoscr_16_aga
#define pfield_linetoscr_full24_aga pfield_linetoscr_24_aga
#define pfield_linetoscr_full32_aga pfield_linetoscr_32_aga

#define pfield_linetoscr_full8_double_aga pfield_linetoscr_8_double_slow_aga
#define pfield_linetoscr_full16_double_aga pfield_linetoscr_16_double_slow_aga
#define pfield_linetoscr_full24_double_aga pfield_linetoscr_24_double_slow_aga
#define pfield_linetoscr_full32_double_aga pfield_linetoscr_32_double_slow_aga

LINE_TO_SCR(pfield_linetoscr_8_ecs, uae_u8, FALSE, (uae_u8), FALSE)
LINE_TO_SCR(pfield_linetoscr_16_ecs, uae_u16, FALSE, (uae_u16), FALSE)
LINE_TO_SCR(pfield_linetoscr_32_ecs, uae_u32, FALSE, (uae_u32), FALSE)
LINE_TO_SCR(pfield_linetoscr_8_double_slow_ecs, uae_u8, TRUE, (uae_u8), FALSE)
LINE_TO_SCR(pfield_linetoscr_16_double_slow_ecs, uae_u16, TRUE, (uae_u16), FALSE)
LINE_TO_SCR(pfield_linetoscr_32_double_slow_ecs, uae_u32, TRUE, (uae_u32), FALSE)

LINE_TO_SCR(pfield_linetoscr_8_aga, uae_u8, FALSE, (uae_u8), TRUE)
LINE_TO_SCR(pfield_linetoscr_16_aga, uae_u16, FALSE, (uae_u16), TRUE)
LINE_TO_SCR(pfield_linetoscr_32_aga, uae_u32, FALSE, (uae_u32), TRUE)
LINE_TO_SCR(pfield_linetoscr_8_double_slow_aga, uae_u8, TRUE, (uae_u8), TRUE)
LINE_TO_SCR(pfield_linetoscr_16_double_slow_aga, uae_u16, TRUE, (uae_u16), TRUE)
LINE_TO_SCR(pfield_linetoscr_32_double_slow_aga, uae_u32, TRUE, (uae_u32), TRUE)

FILL_LINE(fill_line_8_ecs, uae_u8, (uae_u8), FALSE)
FILL_LINE(fill_line_16_ecs, uae_u16, (uae_u16), FALSE)
FILL_LINE(fill_line_32_ecs, uae_u32, (uae_u32), FALSE)

FILL_LINE(fill_line_8_aga, uae_u8, (uae_u8) , TRUE)
FILL_LINE(fill_line_16_aga, uae_u16, (uae_u16), TRUE)
FILL_LINE(fill_line_32_aga, uae_u32, (uae_u32), TRUE)

#ifdef HAVE_UAE_U24
LINE_TO_SCR(pfield_linetoscr_24_ecs, uae_u24, FALSE, uae24_convert, FALSE)
LINE_TO_SCR(pfield_linetoscr_24_double_slow_ecs, uae_u24, TRUE, uae24_convert, FALSE)
LINE_TO_SCR(pfield_linetoscr_24_aga, uae_u24, FALSE, uae24_convert, TRUE)
LINE_TO_SCR(pfield_linetoscr_24_double_slow_aga, uae_u24, TRUE, uae24_convert, TRUE)
FILL_LINE(fill_line_24_ecs, uae_u24, uae24_convert, FALSE)
FILL_LINE(fill_line_24_aga, uae_u24, uae24_convert, TRUE)
#else
/* Dummy versions */
static void pfield_linetoscr_24_ecs (int pix, int lframe_end, int diw_end, int stoppos)
{
}
static void pfield_linetoscr_24_double_slow_ecs (int pix, int lframe_end, int diw_end, int stoppos)
{
}
static void fill_line_24_ecs (char *buf, int start, int stop)
{
}
static void pfield_linetoscr_24_aga (int pix, int lframe_end, int diw_end, int stoppos)
{
}
static void pfield_linetoscr_24_double_slow_aga (int pix, int lframe_end, int diw_end, int stoppos)
{
}
static void fill_line_24_aga (char *buf, int start, int stop)
{
}
#endif

#define pfield_linetoscr_full8_ecs pfield_linetoscr_8_ecs
#define pfield_linetoscr_full16_ecs pfield_linetoscr_16_ecs
#define pfield_linetoscr_full24_ecs pfield_linetoscr_24_ecs
#define pfield_linetoscr_full32_ecs pfield_linetoscr_32_ecs

#define pfield_linetoscr_full8_double_ecs pfield_linetoscr_8_double_slow_ecs
#define pfield_linetoscr_full16_double_ecs pfield_linetoscr_16_double_slow_ecs
#define pfield_linetoscr_full24_double_ecs pfield_linetoscr_24_double_slow_ecs
#define pfield_linetoscr_full32_double_ecs pfield_linetoscr_32_double_slow_ecs

static __inline__ void fill_line_full (void)
{
    fill_line_all(xlinebuffer, linetoscr_x_adjust, linetoscr_x_adjust + gfxvidinfo.width);
}

#define fill_line fill_line_full

#if 1
#undef fill_line
static __inline__ void fill_line_2 (void)
{
    int shift;
    int nints, nrem;
    int *start;
    xcolnr val;

#ifdef HAVE_UAE_U24
    if (gfxvidinfo.pixbytes == 3) {
	fill_line_all (xlinebuffer, linetoscr_x_adjust, linetoscr_x_adjust + gfxvidinfo.width);
	return;
    }
#endif
    
    shift = 0;
    if (gfxvidinfo.pixbytes == 2)
	shift = 1;
    if (gfxvidinfo.pixbytes == 4)
	shift = 2;

    nints = gfxvidinfo.width >> (2-shift);
    nrem = nints & 7;
    nints &= ~7;
    start = (int *)(((char *)xlinebuffer) + (linetoscr_x_adjust << shift));
    if(borderblank)
	val=0;
    else if(currprefs.chipset & AGA_CHIPSET) {
	val = CONVERT_RGB (&colors_for_drawing.color_regs[0]);
	if(gfxvidinfo.pixbytes == 2) val |= val << 16; /*  correct ??? (TW) */
    } else
	val = colors_for_drawing.acolors[0];
    for (; nints > 0; nints -= 8, start += 8) {
	*start = val;
	*(start+1) = val;
	*(start+2) = val;
	*(start+3) = val;
	*(start+4) = val;
	*(start+5) = val;
	*(start+6) = val;
	*(start+7) = val;
    }

    switch (nrem) {
     case 7:
	*start++ = val;
     case 6:
	*start++ = val;
     case 5:
	*start++ = val;
     case 4:
	*start++ = val;
     case 3:
	*start++ = val;
     case 2:
	*start++ = val;
     case 1:
	*start = val;
    }
}
#define fill_line fill_line_2

#ifdef X86_ASSEMBLY

/* Not AGA fixed (TW) */

#if 0 /* TW_X86 */
#undef pfield_linetoscr_full8
#else
#undef pfield_linetoscr_full8_ecs
#endif
/* The types are lies, of course. */
extern void pfield_linetoscr_normal_asm8 (void) __asm__("pfield_linetoscr_normal_asm8");
extern void pfield_linetoscr_ehb_asm8 (void) __asm__("pfield_linetoscr_ehb_asm8");
extern void pfield_linetoscr_ham6_asm8 (void) __asm__("pfield_linetoscr_ham6_asm8");
extern void pfield_linetoscr_dualpf_asm8 (void) __asm__("pfield_linetoscr_dualpf_asm8");
extern void pfield_linetoscr_hdouble_asm8 (void) __asm__("pfield_linetoscr_hdouble_asm8");
extern void pfield_linetoscr_hdouble_dpf_asm8 (void) __asm__("pfield_linetoscr_hdouble_dpf_asm8");
extern void pfield_linetoscr_hdouble_ehb_asm8 (void) __asm__("pfield_linetoscr_hdouble_ehb_asm8");
extern void pfield_linetoscr_asm8 (void (*) (void), int, int, int, int, ...) __asm__("pfield_linetoscr_asm8");

#if 0 /* TW_X86 */
static void pfield_linetoscr_full8 (int pix, int lframe_end, int diw_end, int stoppos)
#else
static void pfield_linetoscr_full8_ecs (int pix, int lframe_end, int diw_end, int stoppos)
#endif
{
    int lframe_end_1, diw_end_1;

    lframe_end_1 = pix + ((lframe_end - pix) & ~3);
    diw_end_1 = stoppos - ((stoppos - diw_end) & ~3);

    if (bplham && bplplanecnt == 6) {
	pfield_linetoscr_asm8 (pfield_linetoscr_ham6_asm8, pix, lframe_end_1, diw_end_1, stoppos);
    } else if (bpldualpf) {
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    pfield_linetoscr_asm8 (pfield_linetoscr_hdouble_dpf_asm8, pix, lframe_end_1, diw_end_1, stoppos,
				   bpldualpfpri ? dblpf_ind2 : dblpf_ind1);
	else
#endif
	    pfield_linetoscr_asm8 (pfield_linetoscr_dualpf_asm8, pix, lframe_end_1, diw_end_1, stoppos,
				  bpldualpfpri ? dblpf_ind2 : dblpf_ind1);
    } else if (bplehb) {
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    pfield_linetoscr_asm8 (pfield_linetoscr_hdouble_ehb_asm8, pix, lframe_end_1, diw_end_1, stoppos);
	else
#endif
	    pfield_linetoscr_asm8 (pfield_linetoscr_ehb_asm8, pix, lframe_end_1, diw_end_1, stoppos);
    } else {
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    pfield_linetoscr_asm8 (pfield_linetoscr_hdouble_asm8, pix, lframe_end_1, diw_end_1, stoppos);
	else
#endif
	    pfield_linetoscr_asm8 (pfield_linetoscr_normal_asm8, pix, lframe_end_1, diw_end_1, stoppos);
    }

    /* The assembly functions work on aligned data, so we may have to do some
     * additional work at the edges. */
    if (lframe_end != lframe_end_1) {
	int i, c = colors_for_drawing.acolors[0];
	for (i = lframe_end_1; i < lframe_end; i++)
	    xlinebuffer[i] = c;
    }
    if (diw_end != diw_end_1) {
	int i, c = colors_for_drawing.acolors[0];
	for (i = diw_end; i < diw_end_1; i++)
	    xlinebuffer[i] = c;
    }
}

#if 0 /* TW_X86 */
#undef pfield_linetoscr_full16
#else
#undef pfield_linetoscr_full16_ecs
#endif
extern void pfield_linetoscr_normal_asm16 (void) __asm__("pfield_linetoscr_normal_asm16");
extern void pfield_linetoscr_ehb_asm16 (void) __asm__("pfield_linetoscr_ehb_asm16");
extern void pfield_linetoscr_ham6_asm16 (void) __asm__("pfield_linetoscr_ham6_asm16");
extern void pfield_linetoscr_dualpf_asm16 (void) __asm__("pfield_linetoscr_dualpf_asm16");
extern void pfield_linetoscr_hdouble_asm16 (void) __asm__("pfield_linetoscr_hdouble_asm16");
extern void pfield_linetoscr_hdouble_dpf_asm16 (void) __asm__("pfield_linetoscr_hdouble_dpf_asm16");
extern void pfield_linetoscr_hdouble_ehb_asm16 (void) __asm__("pfield_linetoscr_hdouble_ehb_asm16");
extern void pfield_linetoscr_asm16 (void (*) (void), int, int, int, int, ...) __asm__("pfield_linetoscr_asm16");
#if 0 /* TW_X86 */
static void pfield_linetoscr_full16 (int pix, int lframe_end, int diw_end, int stoppos)
#else
static void pfield_linetoscr_full16_ecs (int pix, int lframe_end, int diw_end, int stoppos)
#endif
{
    int lframe_end_1, diw_end_1;

    lframe_end_1 = pix + ((lframe_end - pix) & ~3);
    diw_end_1 = stoppos - ((stoppos - diw_end) & ~3);

    if (bplham && bplplanecnt == 6) {
	pfield_linetoscr_asm16 (pfield_linetoscr_ham6_asm16, pix, lframe_end_1, diw_end_1, stoppos);
    } else if (bpldualpf) {
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    pfield_linetoscr_asm16 (pfield_linetoscr_hdouble_dpf_asm16, pix, lframe_end_1, diw_end_1, stoppos,
				   bpldualpfpri ? dblpf_ind2 : dblpf_ind1);
	else
#endif
	    pfield_linetoscr_asm16 (pfield_linetoscr_dualpf_asm16, pix, lframe_end_1, diw_end_1, stoppos,
				   bpldualpfpri ? dblpf_ind2 : dblpf_ind1);
    } else if (bplehb) {
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    pfield_linetoscr_asm16 (pfield_linetoscr_hdouble_ehb_asm16, pix, lframe_end_1, diw_end_1, stoppos);
	else
#endif
	    pfield_linetoscr_asm16 (pfield_linetoscr_ehb_asm16, pix, lframe_end_1, diw_end_1, stoppos);
    } else {
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    pfield_linetoscr_asm16 (pfield_linetoscr_hdouble_asm16, pix, lframe_end_1, diw_end_1, stoppos);
	else
#endif
	    pfield_linetoscr_asm16 (pfield_linetoscr_normal_asm16, pix, lframe_end_1, diw_end_1, stoppos);
    }

    /* The assembly functions work on aligned data, so we may have to do some
     * additional work at the edges. */
    if (lframe_end != lframe_end_1) {
	int i, c = colors_for_drawing.acolors[0];
	for (i = lframe_end_1; i < lframe_end; i++)
	    ((uae_u16 *)xlinebuffer)[i] = c;
    }
    if (diw_end != diw_end_1) {
	int i, c = colors_for_drawing.acolors[0];
	for (i = diw_end; i < diw_end_1; i++)
	    ((uae_u16 *)xlinebuffer)[i] = c;
    }
}
#endif

#ifndef NO_DOUBLING_LINETOSCR
#define NO_DOUBLING_LINETOSCR
#endif

#endif

#ifdef NO_DOUBLING_LINETOSCR

#undef pfield_linetoscr_full8_double_ecs
#undef pfield_linetoscr_full16_double_ecs
#undef pfield_linetoscr_full8_double_aga
#undef pfield_linetoscr_full16_double_aga
#define pfield_linetoscr_full8_double_ecs pfield_linetoscr_full_double
#define pfield_linetoscr_full8_double_aga pfield_linetoscr_full_double
#define pfield_linetoscr_full16_double_ecs pfield_linetoscr_full_double
#define pfield_linetoscr_full16_double_aga pfield_linetoscr_full_double

static void pfield_linetoscr_full_double (int start, int lframe_end, int diw_end, int stop)
{
    char *oldxlb = (char *)xlinebuffer;
    pfield_linetoscr_full_all (start, lframe_end, diw_end, stop);
    xlinebuffer = oldxlb + linetoscr_double_offset;
    pfield_linetoscr_full_all (start, lframe_end, diw_end, stop);
}
#endif

static int linetoscr_diw_end, linetoscr_diw_start;

/* Initialize the variables necessary for drawing a line.
 * This involves setting up start/stop positions and display window
 * borders. Also, since the drawing code may not fully overwrite all
 * releveant areas in the pixdata.apixels array for speed reasons, parts of
 * it must possibly be cleared here. */
static void pfield_init_linetoscr (void)
{
    int ddf_left, ddf_right, maxmaxdelay;
    int mindelay = bpldelay1, maxdelay = bpldelay2;
    if (bpldelay1 > bpldelay2)
	maxdelay = bpldelay1, mindelay = bpldelay2;

    linetoscr_diw_start = dp_for_drawing->diwfirstword;
    linetoscr_diw_end = dp_for_drawing->diwlastword;

    /* We should really look at DDF also when calculating max_diwstop/min_diwstrt,
     * so that centering works better, but I'm afraid that might cost too many
     * cycles. Plus it's dangerous, see the code below that handles the case
     * with sprites. */
    if (dip_for_drawing->nr_sprites == 0) {
	int hiresadjust = RES_SHIFT(bplres);
	ddf_left = ((dp_for_drawing->plfstrt + hiresadjust)*2 + mindelay - DISPLAY_LEFT_SHIFT) << lores_shift;
	ddf_right = ((dp_for_drawing->plfstrt + dp_for_drawing->plflinelen + hiresadjust)*2 + maxdelay - DISPLAY_LEFT_SHIFT) << lores_shift;

	if (linetoscr_diw_start < ddf_left)
	    linetoscr_diw_start = ddf_left;
	if (linetoscr_diw_end > ddf_right)
	    linetoscr_diw_end = ddf_right;

	if (mindelay != maxdelay) {
	    /* Raahh...
	     * We just clear the maximum amount of space that may need to be
	     * cleared. We could do this exactly, but it would come out slower
	     * because of the overhead. */
	    maxmaxdelay = (fmode&3) == 3 ? 64 : (fmode&3) == 0 ? 16 : 32;
	    if (currprefs.gfx_lores == 2) {
		fuzzy_memset_le32 (pixdata.apixels, 0, (ddf_left>>1), maxmaxdelay);
		fuzzy_memset_le32 (pixdata.apixels, 0, (ddf_right>>1) - maxmaxdelay, maxmaxdelay);
	    } else if (currprefs.gfx_lores) {
		fuzzy_memset_le32 (pixdata.apixels, 0, ddf_left, maxmaxdelay);
		fuzzy_memset_le32 (pixdata.apixels, 0, ddf_right - maxmaxdelay, maxmaxdelay);
	    } else {
		fuzzy_memset_le32 (pixdata.apixels, 0, ddf_left, maxmaxdelay*2);
		fuzzy_memset_le32 (pixdata.apixels, 0, ddf_right - maxmaxdelay*2, maxmaxdelay*2);
	    }
	}
    } else {
	int hiresadjust = RES_SHIFT(bplres);
	/* We swap mindelay and maxdelay here to get rid of the mindelay != maxdelay check.
	 * Since we have to do a memset anyway (because there may be sprites),
	 * we might as well clear all at once. */
	ddf_left = ((dp_for_drawing->plfstrt + hiresadjust)*2 + maxdelay - DISPLAY_LEFT_SHIFT) << lores_shift;
	ddf_right = ((dp_for_drawing->plfstrt + dp_for_drawing->plflinelen + hiresadjust)*2 + mindelay - DISPLAY_LEFT_SHIFT) << lores_shift;
	if (linetoscr_diw_start < ddf_left) {
	    int strt = linetoscr_diw_start;
	    int stop = ddf_left;
#ifdef LORES_HACK
	    if (currprefs.gfx_lores == 2)
		strt >>= 1, stop >>= 1;
#endif
	    if (strt < stop)
		fuzzy_memset (pixdata.apixels, 0, strt, stop - strt);
	}

	if (linetoscr_diw_end > ddf_right) {
	    int strt = ddf_right;
	    int stop = linetoscr_diw_end;
#ifdef LORES_HACK
	    if (currprefs.gfx_lores == 2)
		strt >>= 1, stop >>= 1;
#endif
	    if (strt < stop)
		fuzzy_memset (pixdata.apixels, 0, strt, stop - strt);
	}
    }
    /* Perverse cases happen. */
    if (linetoscr_diw_end < linetoscr_diw_start)
	linetoscr_diw_end = linetoscr_diw_start;
}

static void pfield_do_linetoscr(int start, int stop)
{
    int lframe_end = linetoscr_diw_start, diw_end = linetoscr_diw_end;

    if (stop > linetoscr_right_x)
	stop = linetoscr_right_x;
    if (start < linetoscr_x_adjust)
	start = linetoscr_x_adjust;

    if (lframe_end < start)
	lframe_end = start;
    if (diw_end > stop)
	diw_end = stop;

    if (start >= stop)
	return;
    if (start == linetoscr_x_adjust && stop == linetoscr_right_x)
	    pfield_linetoscr_full_all(start, lframe_end, diw_end, stop);
	else
    	pfield_linetoscr_all(start, lframe_end, diw_end, stop);
}

static void pfield_do_fill_line (int start, int stop)
{
    if (stop > linetoscr_right_x)
	stop = linetoscr_right_x;
    if (start < linetoscr_x_adjust)
	start = linetoscr_x_adjust;

    if (start >= stop)
	return;

    fill_line_all(xlinebuffer, start, stop);
}

static void pfield_do_linetoscr_full (int double_line)
{
    int start = linetoscr_x_adjust, stop = start + gfxvidinfo.width;
    int lframe_end = linetoscr_diw_start, diw_end = linetoscr_diw_end;
    if (lframe_end < start)
	lframe_end = start;
    if (diw_end > stop)
	diw_end = stop;

    if (double_line)
        pfield_linetoscr_full_double_all (start, lframe_end, diw_end, stop);
	else
	    pfield_linetoscr_full_all (start, lframe_end, diw_end, stop);
}

static void gen_pfield_tables (void)
{
    int i;
    union {
	struct {
	    uae_u8 a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p;
	} foo;
	struct {
	    uae_u32 a, b, c, d;
	} bar;
    } baz;

    /* For now, the AGA stuff is broken in the dual playfield case. We encode
     * sprites in dpf mode by ORing the pixel value with 0x80. To make dual
     * playfield rendering easy, the lookup tables contain are made linear for
     * values >= 128. That only works for OCS/ECS, though. */

    for (i = 0; i < 256; i++) {
	int plane1 = (i & 1) | ((i >> 1) & 2) | ((i >> 2) & 4) | ((i >> 3) & 8);
	int plane2 = ((i >> 1) & 1) | ((i >> 2) & 2) | ((i >> 3) & 4) | ((i >> 4) & 8);
	dblpf_2nd1[i] = plane1 == 0 ? (plane2 == 0 ? 0 : 2) : 1;
	dblpf_2nd2[i] = plane2 == 0 ? (plane1 == 0 ? 0 : 1) : 2;
	dblpf_aga1[i] = plane1 == 0 ? plane2 : plane1;
	dblpf_aga2[i] = plane2 == 0 ? plane1 : plane2;
    if (plane2 > 0)
	    plane2 += 8;
	dblpf_ind1[i] = i >= 128 ? i & 0x7F : (plane1 == 0 ? plane2 : plane1);
	dblpf_ind2[i] = i >= 128 ? i & 0x7F : (plane2 == 0 ? plane1 : plane2);
    }

    for (i = 0; i < 256; i++) {
	/* We lose every second pixel in HiRes if UAE runs in a 320x200 screen. */
	baz.foo.a = i & 64 ? 1 : 0;
	baz.foo.b = i & 16 ? 1 : 0;
	baz.foo.c = i & 4 ? 1 : 0;
	baz.foo.d = i & 1 ? 1 : 0;
	hirestab_l[i][0] = baz.bar.a;

	baz.foo.a = i & 128 ? 1 : 0;
	baz.foo.b = i & 64 ? 1 : 0;
	baz.foo.c = i & 32 ? 1 : 0;
	baz.foo.d = i & 16 ? 1 : 0;
	baz.foo.e = i & 8 ? 1 : 0;
	baz.foo.f = i & 4 ? 1 : 0;
	baz.foo.g = i & 2 ? 1 : 0;
	baz.foo.h = i & 1 ? 1 : 0;
	lorestab_l[i][0] = baz.bar.a;
	lorestab_l[i][1] = baz.bar.b;
	clxtab[i] = ((((i & 3) && (i & 12)) << 9)
		     | (((i & 3) && (i & 48)) << 10)
		     | (((i & 3) && (i & 192)) << 11)
		     | (((i & 12) && (i & 48)) << 12)
		     | (((i & 12) && (i & 192)) << 13)
		     | (((i & 48) && (i & 192)) << 14));
    }

    for (i = 0; i < 256; i++) {
	/* We lose every second pixel in SuperHiRes in 640*400 screen */
	baz.foo.a = i & 64 ? 1 : 0;
    baz.foo.b = i & 16 ? 1 : 0;
	baz.foo.c = i & 4 ? 1 : 0;
	baz.foo.d = i & 1 ? 1 : 0;
	superhirestab_h[i][0] = baz.bar.a;

	baz.foo.a = i & 128 ? 1 : 0;
	baz.foo.b = i & 64 ? 1 : 0;
	baz.foo.c = i & 32 ? 1 : 0;
	baz.foo.d = i & 16 ? 1 : 0;
	baz.foo.e = i & 8 ? 1 : 0;
	baz.foo.f = i & 4 ? 1 : 0;
	baz.foo.g = i & 2 ? 1 : 0;
	baz.foo.h = i & 1 ? 1 : 0;
	hirestab_h[i][0] = baz.bar.a;
	hirestab_h[i][1] = baz.bar.b;

	baz.foo.a = i & 128 ? 1 : 0;
	baz.foo.b = i & 128 ? 1 : 0;
	baz.foo.c = i & 64 ? 1 : 0;
	baz.foo.d = i & 64 ? 1 : 0;
	baz.foo.e = i & 32 ? 1 : 0;
	baz.foo.f = i & 32 ? 1 : 0;
	baz.foo.g = i & 16 ? 1 : 0;
	baz.foo.h = i & 16 ? 1 : 0;
	baz.foo.i = i & 8 ? 1 : 0;
	baz.foo.j = i & 8 ? 1 : 0;
	baz.foo.k = i & 4 ? 1 : 0;
	baz.foo.l = i & 4 ? 1 : 0;
	baz.foo.m = i & 2 ? 1 : 0;
	baz.foo.n = i & 2 ? 1 : 0;
	baz.foo.o = i & 1 ? 1 : 0;
	baz.foo.p = i & 1 ? 1 : 0;
	lorestab_h[i][0] = baz.bar.a;
	lorestab_h[i][1] = baz.bar.b;
	lorestab_h[i][2] = baz.bar.c;
	lorestab_h[i][3] = baz.bar.d;
    }
}

static uae_u32 attach_2nd;

static uae_u32 do_sprite_collisions (struct sprite_draw *spd, int prev_overlap, int i, int nr_spr)
{
    int j;
    int sprxp = spd[i].linepos;
    uae_u32 datab = spd[i].datab;
    uae_u32 mask2 = 0;
    int sbit = 1 << (spd[i].num&7);
    int attach_compare = -1;

    attach_2nd = 0;
    if ((spd[i].num & 1) && (spd[i].ctl & 0x80))
        attach_compare = spd[i].num - 1;

    for (j = prev_overlap; j < i; j++) {
	uae_u32 mask1;
	int off;

	if (spd[i].num < spd[j].num)
	    continue;

	off = sprxp - spd[j].linepos;
	off <<= sprx_shift;
	mask1 = spd[j].datab >> off;

	/* If j is an attachment for i, then j doesn't block i */
	if (spd[j].num == attach_compare) {
	    attach_2nd |= mask1;
	} else {
	    mask1 |= (mask1 & 0xAAAAAAAA) >> 1;
	    mask1 |= (mask1 & 0x55555555) << 1;
	    if (datab & mask1)
		clxdat |= clxtab[(sbit | (1 << (spd[j].num&7))) & clx_sprmask];
	    mask2 |= mask1;
	}
    }
    for (j = i+1; j < nr_spr; j++) {
	uae_u32 mask1;
	int off = spd[j].linepos - sprxp;

	if (off >= sprite_width || spd[i].num < spd[j].num)
	    break;

	off <<= sprx_shift;
	mask1 = spd[j].datab << off;

	/* If j is an attachment for i, then j doesn't block i */
	if (spd[j].num == attach_compare) {
	    attach_2nd |= mask1;
	} else {
	    mask1 |= (mask1 & 0xAAAAAAAA) >> 1;
	    mask1 |= (mask1 & 0x55555555) << 1;
	    if (datab & mask1)
		clxdat |= clxtab[(sbit | (1 << (spd[j].num&7))) & clx_sprmask];
	    mask2 |= mask1;
	}
    }
    datab &= ~mask2;
    return datab;
}

static __inline__ void render_sprite (int spr, int sprxp, uae_u32 datab, int ham, int attch)
{
    uae_u32 datcd = attach_2nd;
    uae_u8 *dst;
    int basecol = scoloff[spr&1];

    if (!attch)
	basecol += ((spr & 7) & ~1)*2;

    sprxp -= sprx_shift ? (spr&~7)<<1 : 0; /* AGA hires (70ns) sprite width hack */
    if ((currprefs.chipset & AGA_CHIPSET) && bpldualpf) {
	dst = spriteagadpfpixels;
    } else {
	dst = pixdata.apixels;
	if (bpldualpf) basecol |= 128;
    }

    for(; attch ? datab | datcd : datab; sprxp += sprx_inc) {
	int col;

	if (attch) {
	    col = ((datab & 3) << 2) | (datcd & 3);
	    datcd >>= 2;
	} else
	    col = datab & 3;
	datab >>= 2;
	if (col) {
	    col |= basecol;
	    if (ham) {
		col = colors_for_drawing.color_regs[col];
		ham_linebuf[sprxp] = col;
		if (sprx_inc == 2)
		    ham_linebuf[sprxp+1] = col;
	    } else {
		dst[sprxp] = col;
		if (sprx_inc == 2)
		    dst[sprxp+1] = col;
	    }
	}
    }
}

static void walk_sprites (struct sprite_draw *spd, int nr_spr)
{
    int i, prev_overlap;
    int last_sprite_pos = -64;
    uae_u32 plane1 = 0, plane2 = 0;

    prev_overlap = 0;
    for (i = 0; i < nr_spr; i++) {
	int sprxp = spd[i].linepos;
	int m = 1 << (spd[i].num & 7);
	uae_u32 datab;
	while (prev_overlap < i && spd[prev_overlap].linepos + sprite_width <= sprxp)
	    prev_overlap++;

	datab = do_sprite_collisions (spd, prev_overlap, i, nr_spr);
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    sprxp >>= 1;
#endif
	if ((bpldualpf && plfpri[1] < 256) || (plfpri[2] < 256)) {
	    if (sprxp != last_sprite_pos) {
		int ok = last_sprite_pos-sprxp+16;
		int ok2;
		if (ok <= 0) {
		    ok = ok2 = 0;
		    plane1 = 0;
		    plane2 = 0;
		} else {
		    ok2 = ok << sprx_shift;
		    plane1 >>= 32 - ok2;
		    plane2 >>= 32 - ok2;
		}
		last_sprite_pos = sprxp;

		if (bpldualpf) {
		    uae_u32 mask = 3 << ok2;
		    int p = sprxp+ok;

		    for (; mask; mask <<= 2, p += sprx_inc) {
			int data = pixdata.apixels[p];
			if (dblpf_2nd2[data] == 2)
			    plane2 |= mask;
			if (dblpf_2nd1[data] == 1)
			    plane1 |= mask;
		    }
		} else {
		    uae_u32 mask = 3 << ok2;
		    int p = sprxp+ok;

		    for (; mask; mask <<= 2, p += sprx_inc) {
			if (pixdata.apixels[p])
			    plane2 |= mask;
		    }
		}
	    }
	    if (bpldualpf && m >= plfpri[1]) {
		datab &= ~plane1;
		attach_2nd &= ~plane1;
	    }
	    if (m >= plfpri[2]) {
		datab &= ~plane2;
		attach_2nd &= ~plane2;
	    }
	}
	if ((spd[i].num & 1) && (spd[i].ctl & 0x80)) {
	    /* Attached sprite */
	    render_sprite (spd[i].num, sprxp, datab, bplham ? 1 : 0, 1);
	    /* This still leaves one attached sprite bug, but at the moment I'm too lazy */
	    if (i + 1 < nr_spr && spd[i+1].num == spd[i].num - 1 && spd[i+1].linepos == spd[i].linepos)
		i++;
	} else {
	    render_sprite (spd[i].num, sprxp, datab, bplham ? 1 : 0, 0);
	}
    }
}

#ifndef SMART_UPDATE
#undef UNALIGNED_PROFITABLE
#endif

#ifdef UNALIGNED_PROFITABLE

static void pfield_doline_unaligned_h (int lineno)
{
    int xpos = dp_for_drawing->plfstrt * 4 - DISPLAY_LEFT_SHIFT * 2;

    if (bplres == RES_HIRES) {
	int xpos1 = xpos + 16 + bpldelay1*2;
	int xpos2;
	uae_u8 *dataptr = line_data[lineno];
	int len = dp_for_drawing->plflinelen >> 1;

	if (len <= 0)
	    return;

	if (bpldelay1 == bpldelay2) {
	    switch (bplplanecnt) {
	     case 1: set_hires_h_0_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 2: set_hires_h_1_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: set_hires_h_2_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 4: set_hires_h_3_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: set_hires_h_4_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 6: set_hires_h_5_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: set_hires_h_6_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 8: set_hires_h_7_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }
	} else {
	    switch (bplplanecnt) {
	     case 1: case 2: set_hires_h_0_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: case 4: set_hires_h_1_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: case 6: set_hires_h_2_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: case 8: set_hires_h_3_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }

	    xpos2 = xpos + 16 + bpldelay2*2;
	    switch (bplplanecnt) {
	     case 2: case 3: set_hires_h_0_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 4: case 5: set_hires_h_1_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 6: case 7: set_hires_h_2_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 8:         set_hires_h_3_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	    }
	}
    } else {
	int xpos1 = xpos + 32 + bpldelay1*2;
	int xpos2;
	uae_u8 *dataptr = line_data[lineno];
	int len = dp_for_drawing->plflinelen >> 2;

	if (len <= 0)
	    return;

	if (bpldelay1 == bpldelay2) {
	    switch (bplplanecnt) {
	     case 1: set_lores_h_0_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 2: set_lores_h_1_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: set_lores_h_2_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 4: set_lores_h_3_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: set_lores_h_4_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 6: set_lores_h_5_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: set_lores_h_6_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 8: set_lores_h_7_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }
	} else {
	    switch (bplplanecnt) {
	     case 1: case 2: set_lores_h_0_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: case 4: set_lores_h_1_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: case 6: set_lores_h_2_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: case 8: set_lores_h_3_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }

	    xpos2 = xpos + 32 + bpldelay2*2;
	    switch (bplplanecnt) {
	     case 2: case 3: set_lores_h_0_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 4: case 5: set_lores_h_1_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 6: case 7: set_lores_h_2_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 8:         set_lores_h_3_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	    }
	}
    }
}

static void pfield_doline_unaligned_l (int lineno)
{
    int xpos = dp_for_drawing->plfstrt * 2 - DISPLAY_LEFT_SHIFT;

    if (bplres == RES_HIRES) {
	int xpos1 = xpos + 8 + bpldelay1;
	int xpos2;
	uae_u8 *dataptr = line_data[lineno];
	int len = dp_for_drawing->plflinelen >> 1;

	if (len <= 0)
	    return;

	if (bpldelay1 == bpldelay2) {
	    switch (bplplanecnt) {
	     case 1: set_hires_l_0_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 2: set_hires_l_1_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: set_hires_l_2_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 4: set_hires_l_3_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: set_hires_l_4_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 6: set_hires_l_5_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: set_hires_l_6_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 8: set_hires_l_7_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }
	} else {
	    switch (bplplanecnt) {
	     case 1: case 2: set_hires_l_0_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: case 4: set_hires_l_1_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: case 6: set_hires_l_2_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: case 8: set_hires_l_3_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }

	    xpos2 = xpos + 8 + bpldelay2;
	    switch (bplplanecnt) {
	     case 2: case 3: set_hires_l_0_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 4: case 5: set_hires_l_1_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 6: case 7: set_hires_l_2_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 8:         set_hires_l_3_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	    }
	}
    } else {
	int xpos1 = xpos + 16 + bpldelay1;
	int xpos2;
	uae_u8 *dataptr = line_data[lineno];
	int len = dp_for_drawing->plflinelen >> 2;

	if (len <= 0)
	    return;

	if (bpldelay1 == bpldelay2) {
	    switch (bplplanecnt) {
	     case 1: set_hires_h_0_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 2: set_hires_h_1_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: set_hires_h_2_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 4: set_hires_h_3_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: set_hires_h_4_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 6: set_hires_h_5_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: set_hires_h_6_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 8: set_hires_h_7_0 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }
	} else {
	    switch (bplplanecnt) {
	     case 1: case 2: set_hires_h_0_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 3: case 4: set_hires_h_1_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 5: case 6: set_hires_h_2_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	     case 7: case 8: set_hires_h_3_1 ((uae_u32 *)(pixdata.apixels + xpos1), dataptr, len); break;
	    }

	    xpos2 = xpos + 16 + bpldelay2;
	    switch (bplplanecnt) {
	     case 2: case 3: set_hires_h_0_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 4: case 5: set_hires_h_1_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 6: case 7: set_hires_h_2_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	     case 8:         set_hires_h_3_2 ((uae_u32 *)(pixdata.apixels + xpos2), dataptr, len); break;
	    }
	}
    }
}

#define pfield_doline_l_ecs pfield_doline_unaligned_l;
#define pfield_doline_l_aga pfield_doline_unaligned_l;
#define pfield_doline_h_ecs pfield_doline_unaligned_h;
#define pfield_doline_h_aga pfield_doline_unaligned_h;

static pfield_doline_def pfield_doline_h_ecs[] = {
    pfield_doline_unaligned_h, pfield_doline_unaligned_h, pfield_doline_unaligned_h
};    
static pfield_doline_def pfield_doline_l_ecs[] = {
    pfield_doline_unaligned_l, pfield_doline_unaligned_l, pfield_doline_unaligned_l
};

static pfield_doline_def pfield_doline_h_aga[] = {
    pfield_doline_unaligned_h, pfield_doline_unaligned_h, pfield_doline_unaligned_h
};    
static pfield_doline_def pfield_doline_l_aga[] = {
    pfield_doline_unaligned_l, pfield_doline_unaligned_l, pfield_doline_unaligned_l
};

#else /* not UNALIGNED_PROFITABLE */

static __inline__ void pfield_orword_superhires_h(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr |= superhirestab_h[data >> 8][0] << bit;
    *(pixptr+1) |= superhirestab_h[data & 255][0] << bit;
}

static __inline__ void pfield_orword_hires_h(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr |= hirestab_h[data >> 8][0] << bit;
    *(pixptr+1) |= hirestab_h[data >> 8][1] << bit;
    *(pixptr+2) |= hirestab_h[data & 255][0] << bit;
    *(pixptr+3) |= hirestab_h[data & 255][1] << bit;
}

static __inline__ void pfield_orword_lores_h(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr |= lorestab_h[data >> 8][0] << bit;
    *(pixptr+1) |= lorestab_h[data >> 8][1] << bit;
    *(pixptr+2) |= lorestab_h[data >> 8][2] << bit;
    *(pixptr+3) |= lorestab_h[data >> 8][3] << bit;
    *(pixptr+4) |= lorestab_h[data & 255][0] << bit;
    *(pixptr+5) |= lorestab_h[data & 255][1] << bit;
    *(pixptr+6) |= lorestab_h[data & 255][2] << bit;
    *(pixptr+7) |= lorestab_h[data & 255][3] << bit;
}

static __inline__ void pfield_setword_superhires_h(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr = superhirestab_h[data >> 8][0] << bit;
    *(pixptr+1) = superhirestab_h[data & 255][0] << bit;
}

static __inline__ void pfield_setword_hires_h(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr = hirestab_h[data >> 8][0] << bit;
    *(pixptr+1) = hirestab_h[data >> 8][1] << bit;
    *(pixptr+2) = hirestab_h[data & 255][0] << bit;
    *(pixptr+3) = hirestab_h[data & 255][1] << bit;
}

static __inline__ void pfield_setword_lores_h(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr = lorestab_h[data >> 8][0] << bit;
    *(pixptr+1) = lorestab_h[data >> 8][1] << bit;
    *(pixptr+2) = lorestab_h[data >> 8][2] << bit;
    *(pixptr+3) = lorestab_h[data >> 8][3] << bit;
    *(pixptr+4) = lorestab_h[data & 255][0] << bit;
    *(pixptr+5) = lorestab_h[data & 255][1] << bit;
    *(pixptr+6) = lorestab_h[data & 255][2] << bit;
    *(pixptr+7) = lorestab_h[data & 255][3] << bit;
}

static __inline__ void pfield_orword_superhires_l(int data, unsigned char *dp, int bi)
{
}

static __inline__ void pfield_orword_hires_l(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr |= hirestab_l[data >> 8][0] << bit;
    *(pixptr+1) |= hirestab_l[data & 255][0] << bit;
}

static __inline__ void pfield_orword_lores_l(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr |= lorestab_l[data >> 8][0] << bit;
    *(pixptr+1) |= lorestab_l[data >> 8][1] << bit;
    *(pixptr+2) |= lorestab_l[data & 255][0] << bit;
    *(pixptr+3) |= lorestab_l[data & 255][1] << bit;
}

static __inline__ void pfield_setword_superhires_l(int data, unsigned char *dp, int bi)
{
}

static __inline__ void pfield_setword_hires_l(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr = hirestab_l[data >> 8][0] << bit;
    *(pixptr+1) = hirestab_l[data & 255][0] << bit;
}

static __inline__ void pfield_setword_lores_l(int data, unsigned char *dp, int bit)
{
    uae_u32 *pixptr = (uae_u32 *)dp;

    *pixptr = lorestab_l[data >> 8][0] << bit;
    *(pixptr+1) = lorestab_l[data >> 8][1] << bit;
    *(pixptr+2) = lorestab_l[data & 255][0] << bit;
    *(pixptr+3) = lorestab_l[data & 255][1] << bit;
}

/* AGA DO_ONE_PLANE, supports large bitplane delays */

static uae_u16 agatemp[100];
#define DO_ONE_PLANE_AGA(POINTER,MULT,FUNC,DELAY,LL_SUB,P_ADD) { \
    int i; \
    uae_u16 data, *dptr; \
    uae_u8 *ptr = (POINTER); \
    dptr = &agatemp[3]; \
    for (i = dp_for_drawing->plflinelen; i > 0; i -= LL_SUB) { \
	*++dptr = do_get_mem_word ((uae_u16 *)ptr); \
	data = ((dptr[-((DELAY) >> 4)]) >> ((DELAY) & 15)) | ((dptr[-(((DELAY) >> 4) + 1)]) << (16 - ((DELAY) & 15))); \
	ptr += 2; \
	FUNC(data, app, MULT); \
	app += P_ADD; \
    } \
    *++dptr = 0; \
    data = ((dptr[-((DELAY) >> 4)]) >> ((DELAY) & 15)) | ((dptr[-(((DELAY) >> 4) + 1)]) << (16 - ((DELAY) & 15))); \
    FUNC(data, app, MULT); \
}

/* OCS/ECS DO_ONE_PLANE, DO_ONE_PLANE_AGA is compatible but slower */

#define DO_ONE_PLANE(POINTER, MULT, FUNC, DELAY, LL_SUB, P_ADD) { \
    int i; \
    uae_u32 bpldat = 0; \
    uae_u16 data; \
    uae_u8 *ptr = (POINTER); \
    for (i = dp_for_drawing->plflinelen; i > 0; i -= LL_SUB) { \
	bpldat = (bpldat << 16) | do_get_mem_word ((uae_u16 *)ptr); \
	data = bpldat >> (DELAY); \
	ptr+=2; \
	FUNC(data, app, MULT); \
	app += P_ADD; \
    } \
    data = (bpldat<<16) >> (DELAY); \
    FUNC(data, app, MULT); \
}

#ifdef SMART_UPDATE
#define DATA_POINTER(n) (dataptr + (n)*MAX_WORDS_PER_LINE*2)
#else
#define DATA_POINTER(n) (real_bplpt[n])
#endif

/* Combined PFIELD_DOLINE_ALIGNED for AGA/ECS and lores/hires */
/* Compile with good optimizing C compiler! */

#define PFIELD_DOLINE_ALIGNED(NAME, ONE_PLANE, AGA, RES, SHIFT, FUNC1, FUNC2) \
static void NAME (int lineno) \
{ \
    int xpos = dp_for_drawing->plfstrt * (2 << SHIFT) - DISPLAY_LEFT_SHIFT * (1 << SHIFT); \
    uae_u8 *dataptr = line_data[lineno]; \
    int xpos1 = xpos + ((16 >> RES) << SHIFT) + ( (bpldelay1 & ~((16 >> RES) - 1)) << SHIFT ); \
    int xpos2 = xpos + ((16 >> RES) << SHIFT) + ( (bpldelay2 & ~((16 >> RES) - 1)) << SHIFT ); \
    int delay1 = (bpldelay1 & ((16 >> RES) - 1)) << RES; \
    int delay2 = (bpldelay2 & ((16 >> RES) - 1)) << RES; \
    unsigned char *app = pixdata.apixels + xpos1; \
    if (bplplanecnt == 0) { \
	memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); \
	return; \
    } \
    ONE_PLANE(DATA_POINTER(0), 0, FUNC1, delay1, 8 >> RES, (16 >> RES) << SHIFT); \
    if ((AGA || RES <= RES_HIRES) && bplplanecnt > 2) { \
	app = pixdata.apixels + xpos1; \
	ONE_PLANE(DATA_POINTER(2), 2, FUNC2, delay1, 8 >> RES, (16 >> RES) << SHIFT); \
    } \
    if ((AGA || RES <= RES_LORES) && bplplanecnt > 4) { \
	app = pixdata.apixels + xpos1; \
	ONE_PLANE(DATA_POINTER(4), 4, FUNC2, delay1, 8 >> RES, (16 >> RES) << SHIFT); \
    } \
    if (AGA && bplplanecnt > 6) { \
	app = pixdata.apixels + xpos1; \
	ONE_PLANE(DATA_POINTER(6), 6, FUNC2, delay1, 8 >> RES, (16 >> RES) << SHIFT); \
    } \
    if (bplplanecnt > 1) { \
	app = pixdata.apixels + xpos2; \
	ONE_PLANE(DATA_POINTER(1), 1, FUNC2, delay2, 8 >> RES, (16 >> RES) << SHIFT); \
    } \
    if ((AGA || RES <= RES_HIRES) && bplplanecnt > 3) { \
	app = pixdata.apixels + xpos2; \
	ONE_PLANE(DATA_POINTER(3), 3, FUNC2, delay2, 8 >> RES, (16 >> RES) << SHIFT); \
    } \
    if ((AGA || RES <= RES_LORES) && bplplanecnt > 5) {\
	app = pixdata.apixels + xpos2; \
	ONE_PLANE(DATA_POINTER(5), 5, FUNC2, delay2, 8 >> RES, (16 >> RES) << SHIFT); \
    } \
    if (AGA && bplplanecnt > 7) { \
	app = pixdata.apixels + xpos2; \
	ONE_PLANE(DATA_POINTER(7), 7, FUNC2, delay2, 8 >> RES, (16 >> RES) << SHIFT); \
    } \
}

/* ECS LORES */
PFIELD_DOLINE_ALIGNED(pfield_doline_ecs_lores_h, DO_ONE_PLANE, FALSE, RES_LORES, 1,
			pfield_setword_lores_h, pfield_orword_lores_h)
PFIELD_DOLINE_ALIGNED(pfield_doline_ecs_lores_l, DO_ONE_PLANE, FALSE, RES_LORES, 0,
			pfield_setword_lores_l, pfield_orword_lores_l)

/* AGA LORES */
PFIELD_DOLINE_ALIGNED(pfield_doline_aga_lores_h, DO_ONE_PLANE_AGA, TRUE, RES_LORES, 1,
			pfield_setword_lores_h, pfield_orword_lores_h)
PFIELD_DOLINE_ALIGNED(pfield_doline_aga_lores_l, DO_ONE_PLANE_AGA, TRUE, RES_LORES, 0,
			pfield_setword_lores_l, pfield_orword_lores_l)

/* ECS HIRES */

PFIELD_DOLINE_ALIGNED(pfield_doline_ecs_hires_h, DO_ONE_PLANE, FALSE, RES_HIRES, 1,
			pfield_setword_hires_h, pfield_orword_hires_h)
PFIELD_DOLINE_ALIGNED(pfield_doline_ecs_hires_l, DO_ONE_PLANE, FALSE, RES_HIRES, 0,
			pfield_setword_hires_l, pfield_orword_hires_l)

/* AGA HIRES */

PFIELD_DOLINE_ALIGNED(pfield_doline_aga_hires_h, DO_ONE_PLANE_AGA, TRUE, RES_HIRES, 1,
			pfield_setword_hires_h, pfield_orword_hires_h)
PFIELD_DOLINE_ALIGNED(pfield_doline_aga_hires_l, DO_ONE_PLANE_AGA, TRUE, RES_HIRES, 0,
			pfield_setword_hires_l, pfield_orword_hires_l)

/* ECS SUPERHIRES */

PFIELD_DOLINE_ALIGNED(pfield_doline_ecs_superhires_h, DO_ONE_PLANE, FALSE, RES_SUPERHIRES, 1,
			pfield_setword_superhires_h, pfield_orword_superhires_h)
PFIELD_DOLINE_ALIGNED(pfield_doline_ecs_superhires_l, DO_ONE_PLANE, FALSE, RES_SUPERHIRES, 0,
			pfield_setword_superhires_l, pfield_orword_superhires_l)

/* AGA SUPERHIRES */

PFIELD_DOLINE_ALIGNED(pfield_doline_aga_superhires_h, DO_ONE_PLANE_AGA, TRUE, RES_SUPERHIRES, 1,
			pfield_setword_superhires_h, pfield_orword_superhires_h)
PFIELD_DOLINE_ALIGNED(pfield_doline_aga_superhires_l, DO_ONE_PLANE_AGA, TRUE, RES_SUPERHIRES, 0,
			pfield_setword_superhires_l, pfield_orword_superhires_l)


static pfield_doline_def pfield_doline_h_ecs[] = {
    pfield_doline_ecs_lores_h,pfield_doline_ecs_hires_h, pfield_doline_ecs_superhires_h
};
static pfield_doline_def pfield_doline_l_ecs[] = {
    pfield_doline_ecs_lores_l,pfield_doline_ecs_hires_l, pfield_doline_ecs_superhires_l
};
static pfield_doline_def pfield_doline_h_aga[] = {
    pfield_doline_aga_lores_h,pfield_doline_aga_hires_h, pfield_doline_aga_superhires_h
};
static pfield_doline_def pfield_doline_l_aga[] = {
    pfield_doline_aga_lores_l,pfield_doline_aga_hires_l, pfield_doline_aga_superhires_l
};

#endif /* UNALIGNED_PROFITABLE */
static void fix_delays(int delay, int *dst1, int *dst2)
{
    int delay1 = (delay & 0x0f) | ((delay & 0x0c00) >> 6);
    int delay2 = ((delay >>4) & 0x0f) | (((delay >> 4) & 0x0c00) >> 6);
    switch (fmode & 3)
	    {
	    case 1: /* bitplane delay 0-31 */
	    case 2:
	    delay1 &= 0x1f;
	    delay2 &= 0x1f;
	    break;
	    case 0: /* bitplane delay 0-15 */
	    delay1 &= 0x0f;
	    delay2 &= 0x0f;
	    break;
    }
    *dst1 = delay1;
    *dst2 = delay2;
}

static void pfield_adjust_delay (void)
{
    int ddf_left = dp_for_drawing->plfstrt;
    int ddf_right = dp_for_drawing->plfstrt + dp_for_drawing->plflinelen;
    int i;

    for (i = dip_for_drawing->last_delay_change-1; i >= dip_for_drawing->first_delay_change-1; i--) {
	int delayreg = i < dip_for_drawing->first_delay_change ? dp_for_drawing->bplcon1 : delay_changes[i].value;
	int delay1, delay2;
	int startpos = i == dip_for_drawing->last_delay_change - 1 ? ddf_right + 8 : delay_changes[i+1].linepos;
	int stoppos = i < dip_for_drawing->first_delay_change ? ddf_left : delay_changes[i].linepos;
	int j;
	fix_delays(delayreg,&delay1,&delay2);
	startpos = PIXEL_XPOS (startpos + RES_SHIFT(bplres));
	stoppos = PIXEL_XPOS (stoppos + RES_SHIFT(bplres));
	if (currprefs.gfx_lores == 0)
	    delay1 <<= 1, delay2 <<= 1;
	else if (currprefs.gfx_lores == 2)
	    startpos >>= 1, stoppos >>= 1;
	for (j = startpos-1; j >= stoppos; j--) {
	    pixdata.apixels [j] = (pixdata.apixels[j-delay1] & 0x55) | (pixdata.apixels[j-delay2] & 0xAA);
	}
    }
}

void init_row_map (void)
{
    int i;
    if (gfxvidinfo.height > 2048) {
	write_log ("Resolution too high, aborting\n");
	abort ();
    }
    for (i = 0; i < gfxvidinfo.height + 1; i++)
	row_map[i] = gfxvidinfo.bufmem + gfxvidinfo.rowbytes * i;
}

static void init_aspect_maps (void)
{
    int i, maxl;
    double native_lines_per_amiga_line;

    if (native2amiga_line_map)
	free (native2amiga_line_map);
    if (amiga2aspect_line_map)
	free (amiga2aspect_line_map);

    /* At least for this array the +1 is necessary. */
    amiga2aspect_line_map = (int *)malloc (sizeof (int) * (MAXVPOS+1)*2 + 1);
    native2amiga_line_map = (int *)malloc (sizeof (int) * gfxvidinfo.height);

    if (currprefs.gfx_correct_aspect)
	native_lines_per_amiga_line = ((double)gfxvidinfo.height
				       * (currprefs.gfx_lores ? 320 : 640)
				       / ((currprefs.ntscmode ? 200 : 256) << (currprefs.gfx_linedbl ? 1 : 0))
				       / gfxvidinfo.width);
    else
	native_lines_per_amiga_line = 1;

    maxl = (maxvpos+1) * (currprefs.gfx_linedbl ? 2 : 1);
    min_ypos_for_screen = minfirstline << (currprefs.gfx_linedbl ? 1 : 0);
    max_drawn_amiga_line = -1;
    for (i = 0; i < maxl; i++) {
	int v = (int)((i - min_ypos_for_screen) * native_lines_per_amiga_line);
	if (v >= gfxvidinfo.height && max_drawn_amiga_line == -1)
	    max_drawn_amiga_line = i - min_ypos_for_screen;
	if (i < min_ypos_for_screen || v >= gfxvidinfo.height)
	    v = -1;
	amiga2aspect_line_map[i] = v;
    }
    if (currprefs.gfx_linedbl)
	max_drawn_amiga_line >>= 1;

    if (currprefs.gfx_ycenter && !(currprefs.gfx_correct_aspect)) {
	extra_y_adjust = (gfxvidinfo.height - (maxvpos << (currprefs.gfx_linedbl ? 1 : 0))) >> 1;
	if (extra_y_adjust < 0)
	    extra_y_adjust = 0;
    }

    for (i = 0; i < gfxvidinfo.height; i++)
	native2amiga_line_map[i] = -1;

    if (native_lines_per_amiga_line < 1) {
	/* Must omit drawing some lines. */
	for (i = maxl - 1; i > min_ypos_for_screen; i--) {
	    if (amiga2aspect_line_map[i] == amiga2aspect_line_map[i-1]) {
		if (currprefs.gfx_linedbl && (i & 1) == 0 && amiga2aspect_line_map[i+1] != -1) {
		    /* If only the first line of a line pair would be omitted,
		     * omit the second one instead to avoid problems with line
		     * doubling. */
		    amiga2aspect_line_map[i] = amiga2aspect_line_map[i+1];
		    amiga2aspect_line_map[i+1] = -1;
		} else
		    amiga2aspect_line_map[i] = -1;
	    }
	}
    }

    for (i = maxl-1; i >= min_ypos_for_screen; i--) {
	int j;
	if (amiga2aspect_line_map[i] == -1)
	    continue;
	for (j = amiga2aspect_line_map[i]; j < gfxvidinfo.height && native2amiga_line_map[j] == -1; j++)
	    native2amiga_line_map[j] = i >> (currprefs.gfx_linedbl ? 1 : 0);
    }
}

/*
 * A raster line has been built in the graphics buffer. Tell the graphics code
 * to do anything necessary to display it.
 */
static void do_flush_line_1 (int lineno)
{
    if (lineno < first_drawn_line)
	first_drawn_line = lineno;
    if (lineno > last_drawn_line)
	last_drawn_line = lineno;

    if (gfxvidinfo.maxblocklines == 0)
	flush_line(lineno);
    else {
	if ((last_block_line+1) != lineno) {
	    if (first_block_line != -2)
		flush_block (first_block_line, last_block_line);
	    first_block_line = lineno;
	}
	last_block_line = lineno;
	if (last_block_line - first_block_line >= gfxvidinfo.maxblocklines) {
	    flush_block (first_block_line, last_block_line);
	    first_block_line = last_block_line = -2;
	}
    }
}

static __inline__ void do_flush_line (int lineno)
{
    /* We don't want to call X libraries from the second thread right now. */
#ifndef SUPPORT_PENGUINS
    do_flush_line_1 (lineno);
#else
    line_drawn[lineno] = 1;
#endif
}

/*
 * One drawing frame has been finished. Tell the graphics code about it.
 * Note that the actual flush_screen() call is a no-op for all reasonable
 * systems.
 */

static __inline__ void do_flush_screen (int start, int stop)
{
#ifdef SUPPORT_PENGUINS
    int i;
    for (i = 0; i < gfxvidinfo.height; i++) {
	if (line_drawn[i])
	    do_flush_line_1 (i);
    }
#endif
    if (gfxvidinfo.maxblocklines != 0 && first_block_line != -2) {
	flush_block (first_block_line, last_block_line);
    }
    if (start <= stop)
	flush_screen (start, stop);
}

static int drawing_color_matches;
static enum { color_match_acolors, color_match_full } color_match_type;

static void adjust_drawing_colors (int ctable, int bplham)
{
    if (!(currprefs.chipset & AGA_CHIPSET)) {
    if (drawing_color_matches != ctable) {
	if (bplham) {
	    memcpy (&colors_for_drawing, curr_color_tables + ctable,
		    sizeof colors_for_drawing);
	    color_match_type = color_match_full;
	} else {
	    memcpy (colors_for_drawing.acolors, curr_color_tables[ctable].acolors,
		    sizeof colors_for_drawing.acolors);
	    color_match_type = color_match_acolors;
	}
	drawing_color_matches = ctable;
    } else if (bplham && color_match_type != color_match_full) {
	memcpy (colors_for_drawing.color_regs, curr_color_tables[ctable].color_regs,
		sizeof colors_for_drawing.color_regs);
	color_match_type = color_match_full;
    }
    } else {
        /* ??? */
    if (drawing_color_matches != ctable) {
	memcpy (&colors_for_drawing, curr_color_tables + ctable,
		sizeof colors_for_drawing);
	drawing_color_matches = ctable;
    }
    }
}

static __inline__ void adjust_color0_for_color_change (void)
{
    drawing_color_matches = -1;
    if (dp_for_drawing->color0 != 0xFFFFFFFFul) {
	colors_for_drawing.color_regs[0] = dp_for_drawing->color0;
	if(!(currprefs.chipset & AGA_CHIPSET)) colors_for_drawing.acolors[0] = xcolors[dp_for_drawing->color0];
    }
}

/* Fetched data spends 9 lores pixels somewhere in the chips before it appears
 * on-screen. We don't emulate this. Instead, we cheat with the copper to
 * compensate (much easier that way). */
#define COPPER_MAGIC_FUDGE -9

static __inline__ void do_color_changes (line_draw_func worker)
{
    int lastpos = 0, nextpos, i;
    struct color_change *cc = curr_color_changes + dip_for_drawing->first_color_change;

    for (i = dip_for_drawing->first_color_change; i <= dip_for_drawing->last_color_change; i++, cc++) {
	if (i == dip_for_drawing->last_color_change)
	    nextpos = max_diwlastword;
	else
	    nextpos = PIXEL_XPOS (cc->linepos) + (COPPER_MAGIC_FUDGE << lores_shift);
	worker (lastpos, nextpos);
	if (i != dip_for_drawing->last_color_change) {
	    colors_for_drawing.color_regs[cc->regno] = cc->value;
    if(!(currprefs.chipset & AGA_CHIPSET)) colors_for_drawing.acolors[cc->regno] = xcolors[cc->value];
    }
	if (nextpos > lastpos) {
	    lastpos = nextpos;
	    if (lastpos >= linetoscr_right_x)
		break;
	}
    }
}

/* We only save hardware registers during the hardware frame. Now, when
 * drawing the frame, we expand the data into a slightly more useful
 * form. */
static void pfield_expand_dp_bplcon (void)
{
    bplres = RES_LORES;
    if (dp_for_drawing->bplcon0 & 0x8000) bplres = RES_HIRES;
    if (dp_for_drawing->bplcon0 & 0x0040) bplres = RES_SUPERHIRES;
    bplplanecnt = GET_PLANES(dp_for_drawing->bplcon0);
    bplham = (dp_for_drawing->bplcon0 & 0x800) == 0x800;
    fix_delays(dp_for_drawing->bplcon1, &bpldelay1, &bpldelay2); 
    if(currprefs.chipset & AGA_CHIPSET) {
    /* The KILLEHB bit exists in ECS, but is apparently meant for Genlock
     * stuff, and it's set by some demos (e.g. Andromeda Seven Seas) */
	bplehb = ((dp_for_drawing->bplcon0 & 0xFCC0) == 0x6000 && !(dp_for_drawing->bplcon2 & 0x200));
	/* Enable AGA pfield_doline only if it is really required */
	if ( !aga_active && (
	    fmode ||
	    (bplplanecnt > 2 && bplres == RES_SUPERHIRES) ||
	    (bplplanecnt > 4 && bplres == RES_HIRES) ||
	    (bplplanecnt > 5 && !bplehb) ) ) {
		if(currprefs.gfx_lores) pfield_doline = pfield_doline_l_aga; else pfield_doline = pfield_doline_h_aga;
		aga_active = 1;
		write_log("AGA bitplane features enabled\n");
	}
    } else {
	bplehb = (dp_for_drawing->bplcon0 & 0xFC00) == 0x6000;
    }

    plfpri[1] = 1 << 2*(dp_for_drawing->bplcon2 & 7);
    plfpri[2] = 1 << 2*((dp_for_drawing->bplcon2 >> 3) & 7);
    bpldualpf = (dp_for_drawing->bplcon0 & 0x400) == 0x400;
    bpldualpfpri = (dp_for_drawing->bplcon2 & 0x40) == 0x40;
    bpldualpf2of = (dp_for_drawing->bplcon3 >> 10) & 7;
    scoloff[0] = ((dp_for_drawing->bplcon4 >> 4) & 15) << 4;
    scoloff[1] = ((dp_for_drawing->bplcon4 >> 0) & 15) << 4;

    if (currprefs.gfx_lores == 0 && (((dp_for_drawing->bplcon3>>6)&3) < 2) ) {
	sprx_inc = 2, sprx_shift = 0;
    } else {
	sprx_inc = 1; sprx_shift = 1;
    }
}

/*
static void compute_sprite_resolution(void)
{
int emuwidth, spritewidth, amigawidth;

amigawidth = 320 << bplres;
emuwidth = currprefs.gfx_lores == 0 ? 320 : 640;
if (currprefs.chipset & AGA_CHIPSET) {
	spritewidth = (dp_for_drawing->bplcon3>>6)&3;
	switch (spritewidth)
		{
		case 0: // LORES=140ns HIRES=140ns SHRES=70ns
		if (bplres <= RES_HIRES)
			spritewidth = RES_LORES;
			else
			spritewidth = RES_HIRES;
		break;
		case 1: // 140ns
		spritewidth = RES_LORES;
		break;
		case 2: // 70ns
		spritewidth = RES_HIRES;
		break;
		case 3: // 35ns
		spritewidth = RES_SUPERHIRES;
		break;
	}
} else {
	switch (bplres)
		{
		case RES_LORES:
		case RES_HIRES:
		spritewidth = RES_LORES;
		break;
		case RES_SUPERHIRES:
		spritewidth = RES_HIRES;
		break;
	}
}
spritewidth = 320 << spritewidth;

sprx_inc = (amigawidth <<2) / spritewidth;
switch (sprx_inc)
	{
	case 16:
	sprx_shift=0;
	break;
	case 8:
	sprx_shift=0;
	break;
	case 4:
	sprx_shift=1;
	break;
	case 2:
	sprx_shift=2;
	break;
	case 1:
	sprx_shift=4;
	break;
}
}		 
*/

enum double_how {
    dh_buf,
    dh_line,
    dh_emerg
};

static __inline__ void pfield_draw_line (int lineno, int gfx_ypos, int follow_ypos)
{
    int border = 0;
    int do_double = 0;
    enum double_how dh;

    dp_for_drawing = line_decisions + lineno;
    dip_for_drawing = curr_drawinfo + lineno;
    switch (linestate[lineno]) {
     case LINE_REMEMBERED_AS_PREVIOUS:
	{
	    static int warned = 0;
	    if (!warned)
		write_log ("Shouldn't get here... this is a bug.\n"), warned++;
	}
	line_decisions[lineno].which = -2;
	return;

     case LINE_BLACK:
	line_decisions[lineno].which = -2;
	linestate[lineno] = LINE_REMEMBERED_AS_BLACK;
	border = 2;
	break;

     case LINE_REMEMBERED_AS_BLACK:
	return;

     case LINE_AS_PREVIOUS:
	dp_for_drawing--;
	dip_for_drawing--;
	if (dp_for_drawing->which != 1)
	    border = 1;
	line_decisions[lineno].which = -2;
	linestate[lineno] = LINE_DONE_AS_PREVIOUS;
	break;

     case LINE_DONE_AS_PREVIOUS:
	line_decisions[lineno].which = -2;
	/* fall through */
     case LINE_DONE:
	return;

     case LINE_DECIDED_DOUBLE:
	line_decisions[lineno+1].which = -2;
	if (follow_ypos != -1) {
	    do_double = 1;
	    linetoscr_double_offset = gfxvidinfo.rowbytes * (follow_ypos - gfx_ypos);
	    linestate[lineno + 1] = LINE_DONE_AS_PREVIOUS;
	}

	/* fall through */
     default:
	if (dp_for_drawing->which != 1)
	    border = 1;
	linestate[lineno] = LINE_DONE;
	break;
    }

    dh = dh_line;
    xlinebuffer = gfxvidinfo.linemem;
    if (xlinebuffer == 0 && do_double && border != 2 && dip_for_drawing->nr_color_changes != 0)
	xlinebuffer = gfxvidinfo.emergmem, dh = dh_emerg;
    if (xlinebuffer == 0)
	xlinebuffer = row_map[gfx_ypos], dh = dh_buf;
    xlinebuffer -= linetoscr_x_adjust_bytes;

    if (border == 0) {
	pfield_expand_dp_bplcon ();

#ifdef LORES_HACK
	if (gfxvidinfo.can_double && !bplres && !currprefs.gfx_lores
	    && dip_for_drawing->nr_color_changes == 0 && !bplham)
	    currprefs.gfx_lores = 2;
#endif
	pfield_init_linetoscr ();
	if (dip_for_drawing->first_delay_change != dip_for_drawing->last_delay_change) {
	    bpldelay1 = bpldelay2 = 0;
	    pfield_doline[bplres] (lineno);
	    pfield_adjust_delay ();
	} else {
	    pfield_doline[bplres] (lineno);
	}

	/* Check color0 adjust only if we have color changes - shouldn't happen
	 * otherwise. */
	adjust_drawing_colors (dp_for_drawing->ctable, bplham || bplehb);

	/* The problem is that we must call decode_ham() BEFORE we do the
	 * sprites. */
	if (bplham) {
	    init_ham_decoding(linetoscr_x_adjust);
	    if (dip_for_drawing->nr_color_changes == 0) {
		/* The easy case: need to do HAM decoding only once for the
		 * full line. */
		decode_ham (linetoscr_x_adjust, linetoscr_right_x);
	    } else /* Argh. */ {
		adjust_color0_for_color_change ();
		do_color_changes (decode_ham);
		adjust_drawing_colors (dp_for_drawing->ctable, bplham || bplehb);
	    }
	}

	if (dip_for_drawing->nr_sprites != 0)
	    walk_sprites (curr_sprite_positions + dip_for_drawing->first_sprite_draw, dip_for_drawing->nr_sprites);

	if (dip_for_drawing->nr_color_changes == 0) {
	    pfield_do_linetoscr_full (dh == dh_buf ? do_double : 0);
	    do_flush_line (gfx_ypos);
#if 0
	    if (dh == dh_emerg)
		abort ();
#endif
	    if (do_double) {
		/* If dh == dh_line, do_flush_line will re-use the rendered line
		 * from linemem.  If dh == dh_buf, the line will have been doubled
		 * by pfield_do_linetoscr_full.  */
		do_flush_line (follow_ypos);
	    }
	} else {
	    adjust_color0_for_color_change ();
	    do_color_changes (pfield_do_linetoscr);

	    if (dh == dh_emerg)
		memcpy (row_map[gfx_ypos], xlinebuffer + linetoscr_x_adjust_bytes, gfxvidinfo.width*gfxvidinfo.pixbytes);

	    do_flush_line (gfx_ypos);
	    if (do_double) {
		if (dh == dh_emerg)
		    memcpy (row_map[follow_ypos], xlinebuffer + linetoscr_x_adjust_bytes, gfxvidinfo.width*gfxvidinfo.pixbytes);
		else if (dh == dh_buf)
		    memcpy (row_map[follow_ypos], row_map[gfx_ypos], gfxvidinfo.width*gfxvidinfo.pixbytes);
		line_decisions[lineno + 1].which = -2;
		do_flush_line (follow_ypos);
	    }
	}
#ifdef LORES_HACK
	if (currprefs.gfx_lores == 2)
	    currprefs.gfx_lores = 0;
#endif
    } else if (border == 1) {
	adjust_drawing_colors (dp_for_drawing->ctable, 0);

	/* Check color0 adjust only if we have color changes - shouldn't happen
	 * otherwise. */

	if (dip_for_drawing->nr_color_changes == 0) {
	    fill_line ();
	    do_flush_line (gfx_ypos);
#if 0
	    if (dh == dh_emerg)
		abort ();
#endif
	    if (do_double) {
		if (dh == dh_buf) {
		    xlinebuffer = row_map[follow_ypos] - linetoscr_x_adjust_bytes;
		    fill_line ();
		}
		/* If dh == dh_line, do_flush_line will re-use the rendered line
		 * from linemem.  */
		do_flush_line (follow_ypos);
	    }
	    return;
	}

	adjust_color0_for_color_change ();
	do_color_changes (pfield_do_fill_line);

	if (dh == dh_emerg)
	    memcpy (row_map[gfx_ypos], xlinebuffer + linetoscr_x_adjust_bytes, gfxvidinfo.width*gfxvidinfo.pixbytes);

	do_flush_line (gfx_ypos);
	if (do_double) {
	    if (dh == dh_emerg)
		memcpy (row_map[follow_ypos], xlinebuffer + linetoscr_x_adjust_bytes, gfxvidinfo.width*gfxvidinfo.pixbytes);
	    else if (dh == dh_buf)
		memcpy (row_map[follow_ypos], row_map[gfx_ypos], gfxvidinfo.width*gfxvidinfo.pixbytes);
	    /* If dh == dh_line, do_flush_line will re-use the rendered line
	     * from linemem.  */
	    do_flush_line (follow_ypos);
	    line_decisions[lineno + 1].which = -2;
	}
    } else {
	if(currprefs.chipset & AGA_CHIPSET) {
	    uae_u32 tmp = colors_for_drawing.color_regs[0];
	    colors_for_drawing.color_regs[0] = 0x000000;
	    fill_line ();
	    do_flush_line (gfx_ypos);
	    colors_for_drawing.color_regs[0] = tmp;
	} else {
	    xcolnr tmp = colors_for_drawing.acolors[0];
	    colors_for_drawing.acolors[0] = xcolors[0];
	    fill_line ();
	    do_flush_line (gfx_ypos);
	    colors_for_drawing.acolors[0] = tmp;
	}
    }
}

#ifdef SUPPORT_PENGUINS
static smp_comm_pipe drawing_pipe;
static uae_sem_t drawing_lock;

static void *drawing_penguin (void *cruxmedo)
{
    int l;
    fprintf (stderr, "Hello, world!\n");

    for (;;) {
	/* Start of a frame. */
	int k;
	new_frame:
	k = read_comm_pipe_int_blocking (&drawing_pipe);

	switch (k) {
	 case -2:
	    /* Hoopy */
	    break;

	 default:
	    write_log ("Penguin got out of sync.\n");
	    /* what can we do? Try to reduce the damage */
	    for (;;) {
		k = read_comm_pipe_int_blocking (&drawing_pipe);
		if (k == -3)
		    break;
		if (k == -1) {
		    uae_sem_post (&drawing_lock);
		    goto new_frame;
		}
	    }
	 case -3:
	    UAE_PENGUIN_EXIT;
	    /* Can't happen */
	    return 0;
	}

	for (;;) {
	    int i, where;
	    int l = read_comm_pipe_int_blocking (&drawing_pipe);
	    switch (l) {
	     case -1:
		/* End-of-frame synchronization. */
		uae_sem_post (&drawing_lock);
		goto new_frame;
	     case -2:
		/* customreset called a bit too often. That's harmless. */
		continue;
	     case -3:
		write_log ("Penguin got out of sync.\n");
		UAE_PENGUIN_EXIT;
		return 0;
	    }

	    /* l is the line that has been finished for drawing. */
	    i = l - thisframe_y_adjust_real;
	    if (i < 0 || i >= max_ypos_thisframe)
		continue;

	    if (linestate[l] == LINE_UNDECIDED) {
		fprintf (stderr, "Line scheduled for drawing, but undecided %d!?\n", l);
		continue;
	    }
	    if (inhibit_frame != 0)
		continue;
	    where = amiga2aspect_line_map[i+min_ypos_for_screen];
	    if (where >= gfxvidinfo.height || where == -1)
		continue;

	    pfield_draw_line (l, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]);
	}
    }
}

static penguin_id our_penguin;

static void kill_drawing_penguin (void)
{
    /* ??? does libpthread do that for us? */
}

#endif

static int penguins_enabled_thisframe;

static void center_image (void)
{
    prev_x_adjust = linetoscr_x_adjust;
    prev_y_adjust = thisframe_y_adjust;

    if (currprefs.gfx_xcenter) 
    {
	    if (max_diwstop - min_diwstart < gfxvidinfo.width && currprefs.gfx_xcenter == 2)
	        /* Try to center. */
	        linetoscr_x_adjust = ((max_diwstop - min_diwstart - gfxvidinfo.width) / 2 + min_diwstart) & ~1;
	    else
	        linetoscr_x_adjust = max_diwstop - gfxvidinfo.width;

	    /* Would the old value be good enough? If so, leave it as it is if we want to
	     * be clever. */
	    if (currprefs.gfx_xcenter == 2) 
        {
	        if (linetoscr_x_adjust < prev_x_adjust && prev_x_adjust < min_diwstart)
	    	    linetoscr_x_adjust = prev_x_adjust;
	    }
    }
    else
	    linetoscr_x_adjust = max_diwlastword - gfxvidinfo.width;
    
    if (linetoscr_x_adjust < 0)
	    linetoscr_x_adjust = 0;

    linetoscr_x_adjust_bytes = linetoscr_x_adjust * gfxvidinfo.pixbytes;
    linetoscr_right_x = linetoscr_x_adjust + gfxvidinfo.width;
    
    if (linetoscr_right_x > max_diwlastword)
	    linetoscr_right_x = max_diwlastword;

    thisframe_y_adjust = minfirstline;
    if (currprefs.gfx_ycenter && thisframe_first_drawn_line != -1) 
    {
	    if (thisframe_last_drawn_line - thisframe_first_drawn_line < max_drawn_amiga_line && currprefs.gfx_ycenter == 2)
	        thisframe_y_adjust = (thisframe_last_drawn_line - thisframe_first_drawn_line - max_drawn_amiga_line) / 2 + thisframe_first_drawn_line;
	    else
	        thisframe_y_adjust = thisframe_first_drawn_line;

        /* Would the old value be good enough? If so, leave it as it is if we want to
	     * be clever. */
	    if (currprefs.gfx_ycenter == 2) 
        {
	        if (thisframe_y_adjust != prev_y_adjust
		    && prev_y_adjust <= thisframe_first_drawn_line
		    && prev_y_adjust + max_drawn_amiga_line > thisframe_last_drawn_line)
		    thisframe_y_adjust = prev_y_adjust;
	    }
	    /* Make sure the value makes sense */
	    if (thisframe_y_adjust + max_drawn_amiga_line > maxvpos)
	        thisframe_y_adjust = maxvpos - max_drawn_amiga_line;
	    if (thisframe_y_adjust < minfirstline)
	        thisframe_y_adjust = minfirstline;
    }
    thisframe_y_adjust_real = thisframe_y_adjust << (currprefs.gfx_linedbl ? 1 : 0);
    max_ypos_thisframe = (maxvpos - thisframe_y_adjust) << (currprefs.gfx_linedbl ? 1 : 0);

    /* @@@ interlace_seen used to be (bplcon0 & 4), but this is probably
     * better.  */
    if (prev_x_adjust != linetoscr_x_adjust || prev_y_adjust != thisframe_y_adjust)
	    frame_redraw_necessary |= interlace_seen && currprefs.gfx_linedbl ? 2 : 1;

    max_diwstop = 0;
    min_diwstart = 10000;
}

static void init_drawing_frame (void)
{
    int i, maxline;

    init_hardware_for_drawing_frame ();

    if (max_diwstop == 0)
	max_diwstop = diwlastword;
    if (min_diwstart > max_diwstop)
	min_diwstart = 0;

    if (thisframe_first_drawn_line == -1)
	thisframe_first_drawn_line = minfirstline;
    if (thisframe_first_drawn_line > thisframe_last_drawn_line)
	thisframe_last_drawn_line = thisframe_first_drawn_line;

    maxline = currprefs.gfx_linedbl ? (maxvpos+1) * 2 + 1 : (maxvpos+1) + 1;
#ifdef SMART_UPDATE
    for (i = 0; i < maxline; i++) {
	switch (linestate[i]) {
	 case LINE_DONE_AS_PREVIOUS:
	    linestate[i] = LINE_REMEMBERED_AS_PREVIOUS;
	    break;
	 case LINE_REMEMBERED_AS_BLACK:
	    break;
	 default:
	    linestate[i] = LINE_UNDECIDED;
	    break;
	}
    }
#else
    memset (linestate, LINE_UNDECIDED, maxline);
#endif
    last_drawn_line = 0;
    first_drawn_line = 32767;

    first_block_line = last_block_line = -2;
    if (currprefs.test_drawing_speed)
	frame_redraw_necessary = 1;
    else if (frame_redraw_necessary)
	frame_redraw_necessary--;

    center_image ();

    thisframe_first_drawn_line = -1;
    thisframe_last_drawn_line = -1;
#ifdef SUPPORT_PENGUINS
    penguins_enabled_thisframe = 1;
    /* Tell the other thread that it can now expect data from us. */
    write_comm_pipe_int (&drawing_pipe, -2, 1);
    memset (line_drawn, 0, sizeof line_drawn);
#endif

    drawing_color_matches = -1;
}

void finish_drawing_frame (void)
{
    int i;

#ifdef SUPPORT_PENGUINS
    /* Synchronize with other thread, then see whether there's something left for
     * us to draw. @@@ This is probably a big waste of cycles if the two threads
     * run at very different speeds - this one could draw stuff as well. */
    write_comm_pipe_int (&drawing_pipe, -1, 1);
    uae_sem_wait (&drawing_lock);
#else

#ifndef SMART_UPDATE
    /* @@@ This isn't exactly right yet. FIXME */
    if (!interlace_seen) {
	do_flush_screen (first_drawn_line, last_drawn_line);
	return;
    }
#endif
    if (! lockscr ()) {
	notice_screen_contents_lost ();
	return;
    }
    for (i = 0; i < max_ypos_thisframe; i++) {
	int where;
	int line = i + thisframe_y_adjust_real;

	if (linestate[line] == LINE_UNDECIDED)
	    break;

	where = amiga2aspect_line_map[i+min_ypos_for_screen];
	if (where >= gfxvidinfo.height)
	    break;
	if (where == -1)
	    continue;

	pfield_draw_line (line, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]);
    }
    unlockscr ();
#endif
    do_flush_screen (first_drawn_line, last_drawn_line);
}

void hardware_line_completed (int lineno)
{
#ifdef SUPPORT_PENGUINS
    if (framecnt == 0 && penguins_enabled_thisframe) {
	write_comm_pipe_int (&drawing_pipe, lineno, 0);
    }
#endif
#ifndef SMART_UPDATE
    {
	int i, where;
	/* l is the line that has been finished for drawing. */
	i = lineno - thisframe_y_adjust_real;
	if (i >= 0 && i < max_ypos_thisframe) {
	    where = amiga2aspect_line_map[i+min_ypos_for_screen];
	    if (where < gfxvidinfo.height && where != -1)
		pfield_draw_line (lineno, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]);
	}
    }
#endif
}

static __inline__ void check_picasso (void)
{
#ifdef PICASSO96
    if (picasso_on && picasso_redraw_necessary)
    {
	    picasso_refresh ();
        picasso_redraw_necessary = 0;
    }

    if (picasso_requested_on == picasso_on)
	return;

    picasso_on = picasso_requested_on;

    if (!picasso_on)
	clear_inhibit_frame (IHF_PICASSO);
    else
	set_inhibit_frame (IHF_PICASSO);

    gfx_set_picasso_state( picasso_on );
    picasso_enablescreen( picasso_on );

    notice_screen_contents_lost ();
    notice_new_xcolors ();
#endif
}
static void fill_line_dummy(char *a,int b, int c) {};
static void pfield_linetoscr_dummy(int a,int b,int c,int d) {};

static fill_line_def fill_line_aga[] = {
    fill_line_dummy, fill_line_8_aga, fill_line_16_aga, fill_line_24_aga, fill_line_32_aga,
};
static fill_line_def fill_line_ecs[] = {
    fill_line_dummy, fill_line_8_ecs, fill_line_16_ecs, fill_line_24_ecs, fill_line_32_ecs,
};
static pfield_linetoscr_def pfield_linetoscr_double_ecs[] = {
    pfield_linetoscr_dummy, pfield_linetoscr_full8_double_ecs, pfield_linetoscr_full16_double_ecs, pfield_linetoscr_full24_double_ecs, pfield_linetoscr_full32_double_ecs
};
#if 0 /* TW_X86 */
static pfield_linetoscr_def pfield_linetoscr_ecs[] = {
    pfield_linetoscr_dummy, pfield_linetoscr_8_ecs, pfield_linetoscr_16_ecs, pfield_linetoscr_24_ecs, pfield_linetoscr_32_ecs
};
#else
static pfield_linetoscr_def pfield_linetoscr_ecs[] = {
    pfield_linetoscr_dummy, pfield_linetoscr_full8_ecs, pfield_linetoscr_full16_ecs, pfield_linetoscr_full24_ecs, pfield_linetoscr_full32_ecs
};
#endif
static pfield_linetoscr_def pfield_linetoscr_double_aga[] = {
    pfield_linetoscr_dummy, pfield_linetoscr_full8_double_aga, pfield_linetoscr_full16_double_aga, pfield_linetoscr_24_double_slow_aga, pfield_linetoscr_full32_double_aga
};
static pfield_linetoscr_def pfield_linetoscr_aga[] = {
    pfield_linetoscr_dummy, pfield_linetoscr_8_aga, pfield_linetoscr_16_aga, pfield_linetoscr_24_aga, pfield_linetoscr_32_aga
};

static void init_funcs(void)
{
static int olddepth = -1;

    if(gfxvidinfo.pixbytes == olddepth) return;
    olddepth = gfxvidinfo.pixbytes;

    if(currprefs.chipset & AGA_CHIPSET) {
	pfield_linetoscr_full_double_all = pfield_linetoscr_double_aga[gfxvidinfo.pixbytes];
	pfield_linetoscr_full_all = pfield_linetoscr_aga[gfxvidinfo.pixbytes];
	fill_line_all = fill_line_aga[gfxvidinfo.pixbytes];
   } else {
	pfield_linetoscr_full_double_all = pfield_linetoscr_double_ecs[gfxvidinfo.pixbytes];
	pfield_linetoscr_full_all = pfield_linetoscr_ecs[gfxvidinfo.pixbytes];
	fill_line_all = fill_line_ecs[gfxvidinfo.pixbytes];
   }
   /* pfield_doline is replaced with AGA pfield_doline when AGA bitplanes or large delays are
    * used, this allows running non-AGA programs in AGA mode without any slow down (TW) */
   aga_active = 0;
   if(currprefs.gfx_lores) pfield_doline = pfield_doline_l_ecs; else pfield_doline = pfield_doline_h_ecs;
   pfield_linetoscr_all = pfield_linetoscr_full_all;
}

void vsync_handle_redraw (int long_frame, int lof_changed)
{
    last_redraw_point++;

    if (lof_changed || ! interlace_seen || last_redraw_point >= 2 || long_frame) {
	last_redraw_point = 0;
	interlace_seen = 0;

	if (framecnt == 0)
	    finish_drawing_frame ();

	/* At this point, we have finished both the hardware and the
	 * drawing frame. Essentially, we are outside of all loops and
	 * can do some things which would cause confusion if they were
	 * done at other times.
	 */

	if (savestate_wanted) custom_save_state();

    if (quit_program < 0) {
	    quit_program = -quit_program;
	    set_inhibit_frame (IHF_QUIT_PROGRAM);
	    regs.spcflags |= SPCFLAG_BRK;
	    filesys_prepare_reset ();
#ifdef SUPPORT_PENGUINS
	    if (quit_program == 1)
		/* Stop eating herring */
		write_comm_pipe_int (&drawing_pipe, -3, 1);
#endif
	    return;
	}

	count_frame ();
	check_picasso ();

	check_prefs_changed_audio ();
	check_prefs_changed_custom ();
	check_prefs_changed_cpu ();
	/* correct place for init_funcs() is inside "if (check_prefs_change_gfx ()) {" but it can't
	 * be here until check_prefs_changed_gfx() is modified to notice display depth changes  */
	init_funcs();
	if (check_prefs_changed_gfx ()) {
	    init_row_map ();
	    init_aspect_maps ();
	    notice_screen_contents_lost ();
	    notice_new_xcolors ();
	}

	if (inhibit_frame != 0)
	    framecnt = 1;

	if (framecnt == 0)
	    init_drawing_frame ();
    }
}

void hsync_record_line_state (int lineno, enum nln_how how, int changed)
{
    char *state;
    if (framecnt != 0)
	return;

    state = linestate + lineno;
    changed += frame_redraw_necessary;

    switch (how) {
     case nln_normal:
	*state = changed ? LINE_DECIDED : LINE_DONE;
	break;
     case nln_doubled:
	*state = changed ? LINE_DECIDED_DOUBLE : LINE_DONE;
	changed += state[1] != LINE_REMEMBERED_AS_PREVIOUS;
	state[1] = changed ? LINE_AS_PREVIOUS : LINE_DONE_AS_PREVIOUS;
	break;
     case nln_nblack:
	*state = changed ? LINE_DECIDED : LINE_DONE;
	if (state[1] != LINE_REMEMBERED_AS_BLACK)
	    state[1] = LINE_BLACK;
	break;
     case nln_lower:
	if (state[-1] == LINE_UNDECIDED)
	    state[-1] = LINE_BLACK;
	*state = changed ? LINE_DECIDED : LINE_DONE;
	break;
     case nln_upper:
	*state = changed ? LINE_DECIDED : LINE_DONE;
	if (state[1] == LINE_UNDECIDED
	    || state[1] == LINE_REMEMBERED_AS_PREVIOUS
	    || state[1] == LINE_AS_PREVIOUS)
	    state[1] = LINE_BLACK;
	break;
    }
}

void notice_interlace_seen (void)
{
    interlace_seen = 1;
    penguins_enabled_thisframe = 0;
}

void reset_drawing (void)
{
    int i;

    inhibit_frame = 0;
#ifdef SUPPORT_PENGUINS
    uae_sem_init (&gui_sem, 0, 1);
#endif
#ifdef PICASSO96
    InitPicasso96 ();
    picasso_on = 0;
    picasso_requested_on = 0;
    gfx_set_picasso_state (0);
#endif
    max_diwstop = 0;

    lores_factor = currprefs.gfx_lores ? 1 : 2;
    lores_shift = currprefs.gfx_lores ? 0 : 1;
    sprite_width = currprefs.gfx_lores ? 16 : 32;

    /*memset(blitcount, 0, sizeof(blitcount));  blitter debug */
    for (i = 0; i < sizeof linestate / sizeof *linestate; i++)
	linestate[i] = LINE_UNDECIDED;

    xlinebuffer = gfxvidinfo.bufmem;

    init_aspect_maps ();

    if (line_drawn == 0)
	line_drawn = (char *)malloc (gfxvidinfo.height);

    init_row_map();

    last_redraw_point = 0;

    init_drawing_frame ();
}

void drawing_init ()
{
    native2amiga_line_map = 0;
    amiga2aspect_line_map = 0;
    line_drawn = 0;

    init_funcs();
    gen_pfield_tables();

#ifdef SUPPORT_PENGUINS
    init_comm_pipe (&drawing_pipe, 800, 5);
    uae_sem_init (&drawing_lock, 0, 0);
    start_penguin (drawing_penguin, 0, &our_penguin);
    /*atexit(kill_drawing_penguin);*/
#endif
}

void drawing_cleanup( void )
{
    if( amiga2aspect_line_map )
        free( amiga2aspect_line_map );
    if( native2amiga_line_map )
        free( native2amiga_line_map );
    if( line_drawn )
        free( line_drawn );
}