/* 
 * All modifications in this file to the original code are
 * (C) Copyright 1992, ..., 1998 the "DOSEMU-Development-Team".
 *
 * for details see file COPYING in the DOSEMU distribution
 */

/*
 * DANG_BEGIN_MODULE
 *
 * REMARK
 * Transform a 2D image (rescale and color space conversion).
 *
 * Here are functions to adapt the VGA graphics to various
 * X displays.
 *
 * /REMARK
 * DANG_END_MODULE
 *
 * Copyright (c) 1997 Steffen Winterfeldt
 *
 *
 * DANG_BEGIN_CHANGELOG
 *
 * 1997/07/08: Gamma correction now uses only integer operations.
 * -- sw (Steffen.Winterfeldt@itp.uni-leipzig.de)
 *
 * DANG_END_CHANGELOG
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>	/* mprotect() */
#include <sys/param.h>	/* EXEC_PAGESIZE */

#include "remap.h"

#define LUT_OFS_33  256 * 3
#define LUT_OFS_67  256 * 4
#define LUT_OFS_11  256 * 5
#define LUT_OFS_22  256 * 6
#define LUT_OFS_45  256 * 7

#ifdef REMAP_TEST
RemapFuncDesc *remap_test(void);
#endif
RemapFuncDesc *remap_opt(void);
RemapFuncDesc *remap_gen(void);

RemapFuncDesc *(*remap_list_funcs[])(void) = {
  remap_gen,
  remap_opt,
#ifdef REMAP_TEST
  remap_test,
#endif
  NULL
};

static RemapFuncDesc *remap_list = NULL;

static int base_init = 0;
static FILE *rdm = NULL;

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void set_remap_debug_msg(FILE *);
RemapObject remap_init(int, int, int);
void remap_done(RemapObject *);

static void do_base_init(void);

static void do_nothing() {};
static int do_nearly_nothing() { return 0; };
static RectArea do_nearly_something() { RectArea ra = {0, 0, 0, 0}; return ra; };

static unsigned u_pow(unsigned, unsigned);
static unsigned gamma_fix(unsigned, unsigned);
static int true_col_palette_update(RemapObject *, unsigned, unsigned, unsigned, unsigned, unsigned);
static int pseudo_col_palette_update(RemapObject *, unsigned, unsigned, unsigned, unsigned, unsigned);

void rgb_color_reduce(ColorSpaceDesc *, unsigned, RGBColor *);
unsigned rgb_color_reduced_2int(ColorSpaceDesc *, RGBColor);
unsigned rgb_color_2int(ColorSpaceDesc *, unsigned, RGBColor);
RGBColor int_2rgb_color(ColorSpaceDesc *, unsigned, unsigned);
void color_space_complete(ColorSpaceDesc *);
void rgb_lin_filt(RGBColor, RGBColor *, RGBColor *);
void rgb_bilin_filt(RGBColor, RGBColor *, RGBColor *, RGBColor *);

static void src_resize_update(RemapObject *, int, int, int);
static void dst_resize_update(RemapObject *, int, int, int);
static void resize_update(RemapObject *);

int bre_s(int, int, int);
int bre_s2(int, int, int);
int bre_acc(int, int, int);
int bre_d_0(int, int, int);
void bre_update(RemapObject *);
void bre_lin_filt_update(RemapObject *);
void bre_bilin_filt_update(RemapObject *);

static RemapFuncDesc *find_remap_func(unsigned, int, int, RemapFuncDesc *);
static RemapFuncDesc *find_best_remap_func(unsigned, int, int, RemapFuncDesc *);
static void install_remap_funcs(RemapObject *, int);
static void find_supported_modes(RemapObject *);

static RectArea remap_mem_1(RemapObject *, int, int);
static RectArea remap_rect_1(RemapObject *, int, int, int, int);

CodeObj code_init(void);
void code_done(CodeObj *);
void code_append_ins(CodeObj *, int, void *);

void _a_ret(CodeObj *);
void _a_movb_dl_dh(CodeObj *);
void _a_movb_dh_dl(CodeObj *);
void _a_movb_n_ecx_4eax_dl(CodeObj *, int);
void _a_movb_n_ecx_4eax_dh(CodeObj *, int);
void _a_movb_n_ecx_4ebx_dl(CodeObj *, int);
void _a_movb_n_ecx_4ebx_dh(CodeObj *, int);
void _a_movl_ebx_4eax_ebp(CodeObj *);
void _a_movl_ebx_4ecx_edx(CodeObj *);
void _a_movl_ecx_4eax_ebp(CodeObj *);
void _a_movl_edx_4eax_ebp(CodeObj *);
void _a_addl_edx_4eax_ebp(CodeObj *);
void _a_movb_esi_al(CodeObj *, int);
void _a_movb_esi_bl(CodeObj *, int);
void _a_movb_esi_cl(CodeObj *, int);
void _a_movb_esi_dl(CodeObj *, int);
void _a_movb_esi_dh(CodeObj *, int);
void _a_movl_ebp_edi(CodeObj *, int);
void _a_movw_bp_edi(CodeObj *, int);
void _a_movl_edx_edi(CodeObj *, int);
void _a_movw_dx_edi(CodeObj *, int);
void _a_movb_dl_edi(CodeObj *, int);
void _a_addl_n_edi(CodeObj *, int);
void _a_addl_n_esi(CodeObj *, int);
void _a_shrl_n_ebp(CodeObj *, int);
void _a_shll_n_edx(CodeObj *, int);


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*
 * set file handle for debug messages
 */
void set_remap_debug_msg(FILE *_rdm) { rdm = _rdm; }


/*
 * some basic intializations
 */
static void do_base_init()
{
  RemapFuncDesc *rfd0, *rfd;
  int i = 0;

  /* setup remap_list to hold a chained list of remap function descriptions */

  remap_list = NULL;

  while(remap_list_funcs[i] != NULL) {
    rfd0 = rfd = remap_list_funcs[i++]();
    if(rfd != NULL) {
      while(rfd->next != NULL) rfd = rfd->next;
      rfd->next = remap_list;
      remap_list = rfd0;
    }
  }
}


/*
 * initialize a remap object
 */
RemapObject remap_init(int src_mode, int dst_mode, int features)
{
  RemapObject ro;
  int color_lut_size = 256;

  ro.palette_update = do_nearly_nothing;
  ro.src_resize = src_resize_update;
  ro.dst_resize = dst_resize_update;
  ro.remap_rect = do_nearly_something;
  ro.remap_mem = do_nearly_something;
  ro.state = 0;
  ro.src_mode = src_mode;
  ro.dst_mode = dst_mode;
  ro.src_color_space = ro.dst_color_space = NULL;
  ro.src_image = ro.dst_image = NULL;
  ro.src_width = ro.src_height = ro.src_scan_len =
  ro.dst_width = ro.dst_height = ro.dst_scan_len =
  ro.src_x0 = ro.src_y0 = ro.src_x1 = ro.src_y1 =
  ro.dst_x0 = ro.dst_y0 = ro.dst_x1 = ro.dst_y1 =
  ro.src_offset = ro.dst_offset = 0;
  ro.bre_x = ro.bre_y = NULL;
  ro.true_color_lut = NULL;
  ro.gamma_lut = NULL;
  adjust_gamma(&ro, 100);
  ro.remap_func = ro.remap_func_init = NULL;
  ro.remap_func_flags = 0;
  ro.remap_func_name = "no_func";
  ro.co = malloc(sizeof(*ro.co));
  if(ro.co == NULL) {
    ro.state |= ROS_MALLOC_FAIL;
  }
  else {
    *ro.co = code_init();
  }
  ro.remap_line = NULL;
  ro.func_all = ro.func_1 = ro.func_2 = NULL;

  if(!base_init) {
    do_base_init();
    base_init = 1;
  }

  ro.src_color_space = calloc(1, sizeof(*ro.src_color_space));
  if(ro.src_color_space == NULL) ro.state |= ROS_MALLOC_FAIL;
  ro.dst_color_space = calloc(1, sizeof(*ro.dst_color_space));
  if(ro.dst_color_space == NULL) ro.state |= ROS_MALLOC_FAIL;

  ro.bre_x = calloc(1, sizeof(*ro.bre_x));
  if(ro.bre_x == NULL) ro.state |= ROS_MALLOC_FAIL;
  ro.bre_y = calloc(1, sizeof(*ro.bre_y));
  if(ro.bre_y == NULL) ro.state |= ROS_MALLOC_FAIL;

  install_remap_funcs(&ro, features);

  if(
    ro.func_all->flags & RFF_LIN_FILT ||
    ro.func_1->flags & RFF_LIN_FILT ||
    ro.func_2->flags & RFF_LIN_FILT
  ) {
    color_lut_size = 256 * 3;
  }

  if(
    ro.func_all->flags & RFF_BILIN_FILT ||
    ro.func_1->flags & RFF_BILIN_FILT ||
    ro.func_2->flags & RFF_BILIN_FILT
  ) {
    color_lut_size = 256 * 6;
  }

  if((ro.src_mode & (MODE_PSEUDO_8 | MODE_VGA_X | MODE_VGA_4)) == ro.src_mode) {
    if((ro.dst_mode & MODE_PSEUDO_8) == ro.dst_mode) {
      ro.palette_update = do_nearly_nothing;
    }
    if((ro.dst_mode & MODE_TRUE_8) == ro.dst_mode) {
      ro.true_color_lut = calloc(color_lut_size * 1, sizeof(*ro.true_color_lut));
      if(ro.true_color_lut == NULL) ro.state |= ROS_MALLOC_FAIL;
      ro.palette_update = pseudo_col_palette_update;
    }
    else if((ro.dst_mode & MODE_TRUE_COL) == ro.dst_mode) {
      /* **** really too much for now...  **** */
      ro.true_color_lut = calloc(color_lut_size * 3, sizeof(*ro.true_color_lut));
      if(ro.true_color_lut == NULL) ro.state |= ROS_MALLOC_FAIL;
      ro.palette_update = true_col_palette_update;
    }
  }

  if(
    (ro.src_mode & (MODE_TRUE_COL | MODE_PSEUDO_8 | MODE_VGA_X | MODE_VGA_4)) &&
    (ro.dst_mode & (MODE_TRUE_COL | MODE_PSEUDO_8)) == ro.dst_mode
  ) {
    ro.remap_mem = remap_mem_1;
    ro.remap_rect = remap_rect_1;
  }

  return ro;
}


/*
 * destroy a remap object
 */
#define FreeIt(_p_) if(_p_ != NULL) { free(_p_); _p_ = NULL; }
void remap_done(RemapObject *ro)
{
  FreeIt(ro->src_color_space)
  FreeIt(ro->dst_color_space)
  FreeIt(ro->gamma_lut)
  FreeIt(ro->bre_x)
  FreeIt(ro->bre_y)
  FreeIt(ro->true_color_lut)
  if(ro->co != NULL) {
    code_done(ro->co);
    free(ro->co);
    ro->co = NULL;
  }
}
#undef FreeIt


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
 * Calculate a^b (a >= 0, b >= 0).
 * a, b are fixed point numbers with 16 bit decimals.
 *
 * The result is accurate enough for our purpose (relative
 * error typically 10^-4, worst cases still better than 10^-2).
 */

unsigned u_pow(unsigned a, unsigned b)
{
  unsigned long long l, l2, r;
  unsigned b0;
  int i, j;

  if(a == 0) return b ? 0 : 1 << 16;

  b0 = b >> 16;
  r = 1 << 16;	/* 1.0 */

  for(l = a, i = 0; i < 16 && b0; i++) {
    if(b0 & 1) r = r * l >> 16;
    l = l * l >> 16;
    b0 >>= 1;
  }

  for(l = a, i = 0; i < 16 && (b & ((1 << 16) - 1)); i++) {
    for(l2 = l, j = 0; j < 10; j++) {
      l2 = (l2 >> 1) + (l << 15) / l2;
    }	/* l2 = sqrt(l) now */
    l = l2;
    if(b & (1 << 15)) r = r * l >> 16;
    b <<= 1;
  }

  return r;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void adjust_gamma(RemapObject *ro, unsigned gamma)
{
  int i;

  if(gamma == 100 || gamma == 0) {
    gamma = 100;
    if(ro->gamma_lut != NULL) { free(ro->gamma_lut); ro->gamma_lut = NULL; }
  }
  else {
    if(ro->gamma_lut == NULL) {
      ro->gamma_lut = malloc(256 * (sizeof *ro->gamma_lut));
      if(ro->gamma_lut == NULL) {
        ro->state |= ROS_MALLOC_FAIL;
        gamma = 100;
      }
    }
  }

  if(gamma != 100) {
    for(i = 0; i < 256; i++) {
      ro->gamma_lut[i] = gamma_fix(i, gamma);
    }
  }

  ro->gamma = gamma;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
unsigned gamma_fix(unsigned color, unsigned gamma)
{
  return gamma ? u_pow(color << 8, (100 << 16) / gamma) >> 8 : color;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void gamma_correct(RemapObject *ro, RGBColor *c, unsigned *bits)
{
  int i;

  if(*bits <= 1) return;
  if(ro->gamma_lut == NULL) return;
  i = *bits - 8;
  if(i > 0) {
    c->r >>= i; c->g >>= i; c->b >>= i;
  }
  if(i < 0) {
    i = -i;
    c->r <<= i; c->g <<= i; c->b <<= i;
  }
  c->r &= 255; c->g &= 255; c->b &= 255;
  c->r = ro->gamma_lut[c->r];
  c->g = ro->gamma_lut[c->g];
  c->b = ro->gamma_lut[c->b];
  *bits = 8;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int true_col_palette_update(RemapObject *ro, unsigned i, unsigned bits,
                                   unsigned r, unsigned g, unsigned b)
{
  RGBColor c = {r, g, b}, c1, c2, c3;
  unsigned u, u0, uo;

  if(i >= 256) return 0;

  gamma_correct(ro, &c, &bits);

  u0 = u = rgb_color_2int(ro->dst_color_space, bits, c);

  if(ro->dst_color_space->bits == 8) u |= u << 8;
  if(ro->dst_color_space->bits <= 16) u |= u << 16;

#if 0
  fprintf(rdm, "true_col_palette_update: pal[%u] = (0x%x, 0x%x, 0x%x) = 0x%08x\n", i, r, g, b, u);
#endif

  uo = ro->true_color_lut[i];
  ro->true_color_lut[i] = u;

  if(
    ro->func_all->flags & RFF_LIN_FILT ||
    ro->func_1->flags & RFF_LIN_FILT ||
    ro->func_2->flags & RFF_LIN_FILT ||
    ro->func_all->flags & RFF_BILIN_FILT ||
    ro->func_1->flags & RFF_BILIN_FILT ||
    ro->func_2->flags & RFF_BILIN_FILT
  ) {
    rgb_color_reduce(ro->dst_color_space, bits, &c);
    rgb_lin_filt(c, &c1, &c2);
    ro->true_color_lut[i + LUT_OFS_33] = rgb_color_reduced_2int(ro->dst_color_space, c1);
    ro->true_color_lut[i + LUT_OFS_67] = rgb_color_reduced_2int(ro->dst_color_space, c2);
  }

  if(
    ro->func_all->flags & RFF_BILIN_FILT ||
    ro->func_1->flags & RFF_BILIN_FILT ||
    ro->func_2->flags & RFF_BILIN_FILT
  ) {
    rgb_bilin_filt(c, &c1, &c2, &c3);
    ro->true_color_lut[i + LUT_OFS_11] = rgb_color_reduced_2int(ro->dst_color_space, c1);
    ro->true_color_lut[i + LUT_OFS_22] = rgb_color_reduced_2int(ro->dst_color_space, c2);
    ro->true_color_lut[i + LUT_OFS_45] = rgb_color_reduced_2int(ro->dst_color_space, c3);
  }

  if(ro->dst_color_space->bits > 8 && ro->dst_color_space->bits <= 16) {
    ro->true_color_lut[i + 256] = u0;
    ro->true_color_lut[i + 256 * 2] = u0 << 16;
  }

  return uo == u ? 0 : 1;
}

static int pseudo_col_palette_update(RemapObject *ro, unsigned i, unsigned bits,
                                   unsigned r, unsigned g, unsigned b)
{
  RGBColor c = {r, g, b};
  unsigned u, uo;

  gamma_correct(ro, &c, &bits);

  u = rgb_color_2int(ro->dst_color_space, bits, c);

#if 0
  fprintf(rdm, "pseudo_col_palette_update: pal[%u] = (0x%x, 0x%x, 0x%x) = 0x%02x:0x%02x\n", i, r, g, b, u0, u1);
#endif

  if(i < 256) {
    uo = ro->true_color_lut[i];
    ro->true_color_lut[i] = u;
    return u == uo ? 0 : 1;
  }

  return 0;
}

unsigned dit_col(int s_c, int d_c, int col, int dit, int lim)
{
  int k, l, k0, k1, kr;

  k0 = ((d_c - 1) * col + d_c - 2) / (s_c - 1);
  k1 = k0 + 1;
  if(k1 >= d_c) k1 = k0;
  k = ((s_c - 1) * k0) / (d_c - 1);
  l = ((s_c - 1) * k1) / (d_c - 1);
  if(k != l) {
    kr = ((col - k) * dit  + ((l - k) >> 1) ) / (l - k);
  }
  else {
    kr = 0;
  }

  return kr <= lim ? k0 : k1;		/* or < ? */
}

void rgb_color_reduce(ColorSpaceDesc *csd, unsigned bits, RGBColor *c)
{
  c->r &= (1 << bits) - 1;
  c->g &= (1 << bits) - 1;
  c->b &= (1 << bits) - 1;

  if(csd->r_mask || csd->g_mask || csd->b_mask) {
    c->r = csd->r_bits >= bits ? c->r << (csd->r_bits - bits) : c->r >> (bits - csd->r_bits);
    c->g = csd->g_bits >= bits ? c->g << (csd->g_bits - bits) : c->g >> (bits - csd->g_bits);
    c->b = csd->b_bits >= bits ? c->b << (csd->b_bits - bits) : c->b >> (bits - csd->b_bits);
  }
}

unsigned rgb_color_reduced_2int(ColorSpaceDesc *csd, RGBColor c)
{
  c.r <<= csd->r_shift;
  c.g <<= csd->g_shift;
  c.b <<= csd->b_shift;
  return c.r | c.g | c.b;
}

unsigned rgb_color_2int(ColorSpaceDesc *csd, unsigned bits, RGBColor c)
{
  unsigned r, g, b;
  unsigned i0, i1, i2, i3;

  c.r &= (1 << bits) - 1;
  c.g &= (1 << bits) - 1;
  c.b &= (1 << bits) - 1;

  if(csd->r_mask || csd->g_mask || csd->b_mask) {
    rgb_color_reduce(csd, bits, &c);
    return rgb_color_reduced_2int(csd, c);
  }

#ifdef REMAP_REAL_DITHER
  if(csd->r_bits && csd->g_bits && csd->b_bits && bits) {
    r = dit_col(1 << bits, csd->r_bits, c.r, 5, 1);
    g = dit_col(1 << bits, csd->g_bits, c.g, 5, 1);
    b = dit_col(1 << bits, csd->b_bits, c.b, 5, 1);
    i0 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift;

    r = dit_col(1 << bits, csd->r_bits, c.r, 5, 3);
    g = dit_col(1 << bits, csd->g_bits, c.g, 5, 3);
    b = dit_col(1 << bits, csd->b_bits, c.b, 5, 3);
    i1 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift;

    r = dit_col(1 << bits, csd->r_bits, c.r, 5, 4);
    g = dit_col(1 << bits, csd->g_bits, c.g, 5, 4);
    b = dit_col(1 << bits, csd->b_bits, c.b, 5, 4);
    i2 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift;

    r = dit_col(1 << bits, csd->r_bits, c.r, 5, 2);
    g = dit_col(1 << bits, csd->g_bits, c.g, 5, 2);
    b = dit_col(1 << bits, csd->b_bits, c.b, 5, 2);
    i3 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift;

    if(csd->pixel_lut != NULL) {
      i0 = csd->pixel_lut[i0];
      i1 = csd->pixel_lut[i1];
      i2 = csd->pixel_lut[i2];
      i3 = csd->pixel_lut[i3];
    }
    return i0 + (i1 << 8) + (i2 << 16) + (i3 << 24);
  }
#else
  /*
   * the following calculation is taken directly from X.c
   */
  if(csd->r_bits && csd->g_bits && csd->b_bits && bits) {
    r = (c.r * csd->r_bits) >> bits;
    g = (c.g * csd->g_bits) >> bits;
    b = (c.b * csd->b_bits) >> bits;

    i0 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift;

    r = (((c.r + (c.r - ((r << bits) / csd->r_bits))) * csd->r_bits) >> bits);
    g = (((c.g + (c.g - ((g << bits) / csd->g_bits))) * csd->g_bits) >> bits);
    b = (((c.b + (c.b - ((b << bits) / csd->b_bits))) * csd->b_bits) >> bits);

    if(r >= csd->r_bits) r = csd->r_bits - 1;
    if(g >= csd->g_bits) g = csd->g_bits - 1;
    if(b >= csd->b_bits) b = csd->b_bits - 1;

    i1 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift;

    if(csd->pixel_lut != NULL) {
      i0 = csd->pixel_lut[i0];
      i1 = csd->pixel_lut[i1];
    }

    return i0 + (i1 << 8) + (i1 << 16) + (i0 << 24);
  }
#endif

  return 0;
}

RGBColor int_2rgb_color(ColorSpaceDesc *csd, unsigned bits, unsigned u)
{
  RGBColor c = {0, 0, 0};
  unsigned nr = u & csd->r_mask, ng = u & csd->g_mask, nb = u & csd->b_mask;

  if(csd->r_mask || csd->g_mask || csd->b_mask) {
    nr >>= csd->r_shift;
    ng >>= csd->g_shift;
    nb >>= csd->b_shift;

    c.r = csd->r_bits >= bits ? nr >> (csd->r_bits - bits) : nr << (bits - csd->r_bits);
    c.g = csd->g_bits >= bits ? ng >> (csd->g_bits - bits) : ng << (bits - csd->g_bits);
    c.b = csd->b_bits >= bits ? nb >> (csd->b_bits - bits) : nb << (bits - csd->b_bits);

    return c;
  }

  return c;
}

void color_space_complete(ColorSpaceDesc *csd)
{
  unsigned ui;

  if((ui = csd->r_mask)) for(csd->r_shift = 0; !(ui & 1); ui >>= 1, csd->r_shift++);
  if(ui) for(; ui; ui >>= 1, csd->r_bits++);
  if((ui = csd->g_mask)) for(csd->g_shift = 0; !(ui & 1); ui >>= 1, csd->g_shift++);
  if(ui) for(; ui; ui >>= 1, csd->g_bits++);
  if((ui = csd->b_mask)) for(csd->b_shift = 0; !(ui & 1); ui >>= 1, csd->b_shift++);
  if(ui) for(; ui; ui >>= 1, csd->b_bits++);
}


void rgb_lin_filt(RGBColor c, RGBColor *c1, RGBColor *c2)
{
  unsigned u;

  u = (c.r + 1) / 3; c1->r = u; c2->r = c.r - u;
  u = (c.g + 1) / 3; c1->g = u; c2->g = c.g - u;
  u = (c.b + 1) / 3; c1->b = u; c2->b = c.b - u;
}

void rgb_bilin_filt(RGBColor c, RGBColor *c1, RGBColor *c2, RGBColor *c3)
{
  unsigned u;

#if 0
  u = c.r; c1->r = (u + 1) / 9; c2->r = (u * 2 + 1) / 9; c3->r = (u * 4 + 1) / 9;
  if(c1->r + 2 * c2->r + c3->r < u) c3->r++;
  if(c1->r + 2 * c2->r + c3->r < u) c2->r++;
  u = c.g; c1->g = (u + 1) / 9; c2->g = (u * 2 + 1) / 9; c3->g = (u * 4 + 1) / 9;
  if(c1->g + 2 * c2->g + c3->g < u) c3->g++;
  if(c1->g + 2 * c2->g + c3->g < u) c2->g++;
  u = c.b; c1->b = (u + 1) / 9; c2->b = (u * 2 + 1) / 9; c3->b = (u * 4 + 1) / 9;
  if(c1->b + 2 * c2->b + c3->b < u) c3->b++;
  if(c1->b + 2 * c2->b + c3->b < u) c2->b++;
#endif

  u = c.r; c1->r = (u + 4) / 9; c2->r = (u * 2 + 4) / 9; c3->r = u - c1->r - 2 * c2->r;
  u = c.g; c1->g = (u + 4) / 9; c2->g = (u * 2 + 4) / 9; c3->g = u - c1->g - 2 * c2->g;
  u = c.b; c1->b = (u + 4) / 9; c2->b = (u * 2 + 4) / 9; c3->b = u - c1->b - 2 * c2->b;

  /* magic !!! */
  if(c3->r == 114) c3->r--;
  if(c3->g == 114) c3->g--;
  if(c3->b == 114) c3->b--;

}



/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

static void src_resize_update(RemapObject *ro, int width, int height, int scan_len)
{
  ro->src_width = width;
  ro->src_height = height;
  ro->src_scan_len = scan_len;
  resize_update(ro);
}

static void dst_resize_update(RemapObject *ro, int width, int height, int scan_len)
{
  ro->dst_width = width;
  ro->dst_height = height;
  ro->dst_scan_len = scan_len;
  resize_update(ro);
}

static void resize_update(RemapObject *ro)
{
  ro->state &= ~(ROS_REMAP_FUNC_OK | ROS_REMAP_IGNORE);

  if(!(ro->state & (ROS_SCALE_ALL | ROS_SCALE_1 | ROS_SCALE_2))) {
    return;
  }

  if(
    ro->src_width == 0 || ro->src_height == 0 ||
    ro->dst_width == 0 || ro->dst_height == 0 ||
    ro->src_scan_len == 0 || ro->dst_scan_len == 0
  ) {
    ro->state |= ROS_REMAP_FUNC_OK | ROS_REMAP_IGNORE;
    return;
  }

  ro->remap_line = do_nothing;

  if(
    ro->src_width == ro->dst_width &&
    ro->src_height == ro->dst_height &&
    (ro->state & ROS_SCALE_1)
  ) {
    ro->remap_func = ro->func_1->func;
    ro->remap_func_flags = ro->func_1->flags;
    ro->remap_func_name = ro->func_1->func_name;
    ro->remap_func_init = ro->func_1->func_init;
  }
  else if(
    (ro->src_width << 1) == ro->dst_width &&
    (ro->src_height << 1) == ro->dst_height &&
    (ro->state & ROS_SCALE_2)
  ) {
    ro->remap_func = ro->func_2->func;
    ro->remap_func_flags = ro->func_2->flags;
    ro->remap_func_name = ro->func_2->func_name;
    ro->remap_func_init = ro->func_2->func_init;
  }
  else if(ro->state & ROS_SCALE_ALL) {
    ro->remap_func = ro->func_all->func;
    ro->remap_func_flags = ro->func_all->flags;
    ro->remap_func_name = ro->func_all->func_name;
    ro->remap_func_init = ro->func_all->func_init;
  }
  else {
    ro->remap_func = do_nothing;
    ro->remap_func_flags = 0;
    ro->remap_func_name = "do_nothing";
    ro->remap_func_init = NULL;
  }

  if(ro->remap_func_flags & RFF_BILIN_FILT)
    bre_bilin_filt_update(ro);
  else if(ro->remap_func_flags & RFF_LIN_FILT)
    bre_lin_filt_update(ro);
  else
    bre_update(ro);

  if(ro->remap_func_init != NULL) {
    ro->remap_func_init(ro);
  }

  if(ro->remap_func != NULL && ro->remap_func != do_nothing) { ro->state |= ROS_REMAP_FUNC_OK; }

#ifdef REMAP_RESIZE_DEBUG
  fprintf(rdm, "resize_update: using %s for remap %dx%d --> %dx%d\n",
    ro->remap_func_name, ro->src_width, ro->src_height, ro->dst_width, ro->dst_height
  );
#endif

}


int bre_s(int d, int s_len, int d_len)
{
  return (s_len / 2 + s_len * d) / d_len;
}

int bre_s2(int d, int s_len, int d_len)
{
  return (s_len * d) / d_len;
}

int bre_acc(int d, int s_len, int d_len)
{
  return (s_len / 2 + s_len * d) % d_len;
}

int bre_d_0(int s, int s_len, int d_len)
{
  return ((s_len + 1) / 2 - 1 + s * d_len) / s_len;
}

void bre_update(RemapObject *ro)
{
  int i, l, *ii;

  if(ro->bre_x != NULL) free(ro->bre_x);
  if(ro->bre_y != NULL) free(ro->bre_y);

  if(!(l = ro->dst_width)) l++;
  ro->bre_x = ii = malloc(l * sizeof(*ro->bre_x));

  if(ro->bre_x == NULL) {
    ro->state |= ROS_MALLOC_FAIL;
  }
  else {
    for(i = 0; i < l; i++) ii[i] = bre_s(i + 1, ro->src_width, l);

    if(ro->src_mode == MODE_VGA_X) {
      for(i = 0; i < l; i++) ii[i] = (ii[i] >> 2) + ((ii[i] & 3) << 16);
    }

    for(i = l - 1; i > 0; i--) ii[i] -= ii[i - 1];
  }

  if(!(l = ro->dst_height)) l++;
  ro->bre_y = ii = malloc(l * sizeof(*ro->bre_y));

  if(ro->bre_y == NULL) {
    ro->state |= ROS_MALLOC_FAIL;
  }
  else {
    for(i = 0; i < l; i++) {
      ii[i] = bre_s(i, ro->src_height, l) * ro->src_scan_len;
    }
  }

}

void bre_lin_filt_update(RemapObject *ro)
{
  int i, k, l, *ii;

  if(ro->bre_x != NULL) free(ro->bre_x);
  if(ro->bre_y != NULL) free(ro->bre_y);

  if(!(l = ro->dst_width)) l++;
  ro->bre_x = ii = malloc(2 * l * sizeof(*ro->bre_x));

  if(ro->bre_x == NULL) {
    ro->state |= ROS_MALLOC_FAIL;
  }
  else {
    k = ro->src_width;
    if(k == 0) k = 1;
    k = 3 * (k - 1);
    for(i = 0; i < l; i++) { ii[i] = bre_s2(i + 1, k, l - 1); }
    for(ii[l] = 0, i = 1; i < l; i++) { ii[l + i] = ii[i - 1] % 3; }

    for(i = 0; i < l; i++) { ii[i] /= 3; }
    ii[l - 1] = ro->src_width - 1;		/* just for fun... (value is never needed) */
    for(i = l - 1; i > 0; i--) { ii[i] -= ii[i - 1]; }
    ii[0] = 0;
    if(ii[2 * l - 1] != 0) fprintf(stderr, "**** oho: %d remains\n", ii[2 * l - 1]);
  }

  if(!(l = ro->dst_height)) l++;
  ro->bre_y = ii = malloc(l * sizeof(*ro->bre_y));

  if(ro->bre_y == NULL) {
    ro->state |= ROS_MALLOC_FAIL;
  }
  else {
    for(i = 0; i < l; i++) {
      ii[i] = bre_s(i, ro->src_height, l) * ro->src_scan_len;
    }
  }
}

void bre_bilin_filt_update(RemapObject *ro)
{
  int i, k, l, *ii;

  if(ro->bre_x != NULL) free(ro->bre_x);
  if(ro->bre_y != NULL) free(ro->bre_y);

  if(!(l = ro->dst_width)) l++;
  ro->bre_x = ii = malloc(2 * l * sizeof(*ro->bre_x));

  if(ro->bre_x == NULL) {
    ro->state |= ROS_MALLOC_FAIL;
  }
  else {
    k = ro->src_width;
    if(k == 0) k = 1;
    k = 3 * (k - 1);
    for(i = 0; i < l; i++) { ii[i] = bre_s2(i + 1, k, l - 1); }
    for(ii[l] = 0, i = 1; i < l; i++) { ii[l + i] = ii[i - 1] % 3; }

    for(i = 0; i < l; i++) { ii[i] /= 3; }
    ii[l - 1] = ro->src_width - 1;		/* just for fun... (value is never needed) */
    for(i = l - 1; i > 0; i--) { ii[i] -= ii[i - 1]; }
    ii[0] = 0;
    if(ii[2 * l - 1] != 0) fprintf(stderr, "**** oho: %d remains\n", ii[2 * l - 1]);
  }

  if(!(l = ro->dst_height)) l++;
  ro->bre_y = ii = malloc(2 * l * sizeof(*ro->bre_y));

  if(ro->bre_y == NULL) {
    ro->state |= ROS_MALLOC_FAIL;
  }
  else {
    k = ro->src_height;
    if(k == 0) k = 1;
    k = 3 * (k - 1);

    for(i = 0; i < l; i++) { ii[i] = bre_s2(i, k, l - 1); }
    for(i = 0; i < l; i++) { ii[l + i] = ii[i] % 3; }

    for(i = 0; i < l; i++) { ii[i] /= 3; }
    if(ii[2 * l - 1] != 0) fprintf(stderr, "**** oho: %d remains\n", ii[2 * l - 1]);
    if(ii[l - 1] >= ro->src_height) fprintf(stderr, "**** oho: %d lines is out of bounds\n", ii[l - 1]);
    for(i = 0; i < l; i++) { ii[i] *= ro->src_scan_len; }
  }
}

#ifdef REMAP_AREA_DEBUG
#define REMAP_AREA_DEBUG_FUNC(_ro_) remap_area_debug_func(_ro_)

static void remap_area_debug_func(RemapObject *ro)
{
  fprintf(rdm, "[%s]\n", ro->remap_func_name);
  fprintf(rdm, "  src_offset = %d, src_scan_len = %d, src_area = %d x %d, %d x %d\n",
    ro->src_offset, ro->src_scan_len, ro->src_x0, ro->src_y0, ro->src_x1, ro->src_y1
  );
  fprintf(rdm, "  dst_offset = %d, dst_scan_len = %d, dst_area = %d x %d, %d x %d\n",
    ro->dst_offset, ro->dst_scan_len, ro->dst_x0, ro->dst_y0, ro->dst_x1, ro->dst_y1
  );
}

#else
#define REMAP_AREA_DEBUG_FUNC(_ro_)
#endif

static RectArea remap_mem_1(RemapObject *ro, int offset, int len)
{
  RectArea ra = {0, 0, 0, 0};
  int i1, i2, j1, j2;
  int pixel_size = 1;

  if(ro->state & ROS_REMAP_IGNORE) return ra;
  if(ro->remap_func == NULL) return ra;

#ifdef REMAP_AREA_DEBUG
  fprintf(rdm, "remap_mem: ofs = %d, len = %d\n", offset, len);
  fprintf(rdm,
    "  src: base = 0x%x, width = %d, height = %d, scan_len = %d\n",
    (unsigned) ro->src_image, ro->src_width, ro->src_height, ro->src_scan_len
  );
  fprintf(rdm,
    "  dst: base = 0x%x, width = %d, height = %d, scan_len = %d\n",
    (unsigned) ro->dst_image, ro->dst_width, ro->dst_height, ro->dst_scan_len
  );
#endif

  if(offset < 0) len += offset, offset = 0;
  if(len <= 0) return ra;

  ra.width = ro->dst_width;

  i1 = offset / ro->src_scan_len;
  i2 = offset % ro->src_scan_len;
  j1 = (offset + len) / ro->src_scan_len;
  j2 = (offset + len) % ro->src_scan_len;

  /* make sure it's all visible */
  if(i2 >= ro->src_width) i1++, i2 = 0, offset = i1 * ro->src_scan_len;
  if(i1 >= ro->src_height || i1 > j1) return ra;
  if(j2 >= ro->src_width) j1++, j2 = 0;
  if(j1 >= ro->src_height) j1 = ro->src_height, j2 = 0;

  switch(ro->dst_mode) {
    case MODE_TRUE_15:
    case MODE_TRUE_16: pixel_size = 2; break;
    case MODE_TRUE_24: pixel_size = 3; break;
    case MODE_TRUE_32: pixel_size = 4; break;
    case MODE_TRUE_8:
    case MODE_PSEUDO_8:
    default: pixel_size = 1;
  }

  if(ro->remap_func_flags & RFF_REMAP_RECT) {
    if(i2) {
      ro->src_offset = offset;
      offset += ro->src_scan_len - i2;
      ro->src_x0 = i2;
      ro->src_x1 = ro->src_width;
      ro->src_y0 = i1;
      ro->src_y1 = ro->src_y0 + 1;
      ro->dst_x0 = bre_d_0(ro->src_x0, ro->src_width, ro->dst_width);
      ro->dst_x1 = ro->dst_width;
      ro->dst_y0 = bre_d_0(ro->src_y0, ro->src_height, ro->dst_height);
      ro->dst_y1 = bre_d_0(ro->src_y1, ro->src_height, ro->dst_height);
      ro->dst_offset = ro->dst_y0 * ro->dst_scan_len + ro->dst_x0 * pixel_size;
      ra.y = ro->dst_y0;
      ra.height = ro->dst_y1 - ra.y;
      REMAP_AREA_DEBUG_FUNC(ro);
      if(ro->dst_y0 != ro->dst_y1 && ro->dst_x1 != ro->dst_x0) {
        ro->remap_func(ro);
      }
      i2 = 0;
      i1++;
    }
    if(i1 < j1) {
      ro->src_offset = offset;
      offset += ro->src_scan_len * (j1 - i1);
      ro->src_x0 = ro->dst_x0 = 0;
      ro->src_x1 = ro->src_width;
      ro->src_y0 = i1;
      ro->src_y1 = j1;
      ro->dst_x0 = 0;
      ro->dst_x1 = ro->dst_width;
      ro->dst_y0 = bre_d_0(ro->src_y0, ro->src_height, ro->dst_height);
      ro->dst_y1 = bre_d_0(ro->src_y1, ro->src_height, ro->dst_height);
      ro->dst_offset = ro->dst_y0 * ro->dst_scan_len;
      ra.height = ro->dst_y1 - ra.y;
      REMAP_AREA_DEBUG_FUNC(ro);
      if(ro->dst_y0 != ro->dst_y1) {
        ro->remap_func(ro);
      }
    }
    if(j2) {
      ro->src_offset = offset;
      ro->src_x0 = 0;
      ro->src_x1 = j2;
      ro->src_y0 = j1;
      ro->src_y1 = ro->src_y0 + 1;
      ro->dst_x0 = 0;
      ro->dst_x1 = bre_d_0(ro->src_x1, ro->src_width, ro->dst_width);
      ro->dst_y0 = bre_d_0(ro->src_y0, ro->src_height, ro->dst_height);
      ro->dst_y1 = bre_d_0(ro->src_y1, ro->src_height, ro->dst_height);
      ro->dst_offset = ro->dst_y0 * ro->dst_scan_len;
      ra.height = ro->dst_y1 - ra.y;
      REMAP_AREA_DEBUG_FUNC(ro);
      if(ro->dst_y0 != ro->dst_y1 && ro->dst_x1 != ro->dst_x0) {
        ro->remap_func(ro);
      }
    }
  }
  else if(ro->remap_func_flags & RFF_REMAP_LINES) {
    ro->src_offset = i1 * ro->src_scan_len;
    ro->src_x0 = ro->dst_x0 = 0;
    ro->src_x1 = ro->src_width; ro->dst_x1 = ro->dst_width;
    ro->src_y0 = i1;
    ro->src_y1 = j1; if(j2) ro->src_y1++;
    ro->dst_y0 = bre_d_0(ro->src_y0, ro->src_height, ro->dst_height);
    ro->dst_y1 = bre_d_0(ro->src_y1, ro->src_height, ro->dst_height);
    ro->dst_offset = ro->dst_y0 * ro->dst_scan_len;
    ra.y = ro->dst_y0;
    ra.height = ro->dst_y1 - ro->dst_y0;
    REMAP_AREA_DEBUG_FUNC(ro);
    if(ro->dst_y0 != ro->dst_y1) {
      ro->remap_func(ro);
    }
  }
  else {
    ro->src_y0 = i1;
    ro->src_y1 = j1; if(j2) ro->src_y1++;
    ro->dst_y0 = bre_d_0(ro->src_y0, ro->src_height, ro->dst_height);
    ro->dst_y1 = bre_d_0(ro->src_y1, ro->src_height, ro->dst_height);
    ra.y = ro->dst_y0;
    ra.height = ro->dst_y1 - ro->dst_y0;
    ro->src_offset = ro->dst_offset = 0;
    ro->src_x0 = ro->dst_x0 = ro->src_y0 = ro->dst_y0 = 0;
    ro->src_x1 = ro->src_width; ro->dst_x1 = ro->dst_width;
    ro->src_y1 = ro->src_height; ro->dst_y1 = ro->dst_height;
    ra.height = ro->dst_height;
    REMAP_AREA_DEBUG_FUNC(ro);
    ro->remap_func(ro);
  }

  return ra;
}


static RectArea remap_rect_1(RemapObject *ro, int x0, int y0, int width, int height)
{
  RectArea ra = {0, 0, 0, 0};
  int x1, y1;
  int pixel_size = 1;

  if(ro->state & ROS_REMAP_IGNORE) return ra;
  if(ro->remap_func == NULL) return ra;

  if(x0 < 0) width -= x0, x0 = 0;
  if(y0 < 0) height -= y0, y0 = 0;

  if(x0 >= ro->src_width || y0 >= ro->src_height) return ra;
  if(width <= 0 || height <= 0) return ra;

  x1 = x0 + width;
  y1 = y0 + height;

  if(x1 > ro->src_width) x1 = ro->src_width, width = x1 - x0;
  if(y1 > ro->src_height) y1 = ro->src_height, height = y1 - y0;

  ra.x = bre_d_0(x0, ro->src_width, ro->dst_width);
  ra.y = bre_d_0(y0, ro->src_height, ro->dst_height);
  ra.width = bre_d_0(x1, ro->src_width, ro->dst_width) - ra.x;
  ra.height = bre_d_0(y1, ro->src_height, ro->dst_height) - ra.y;

  if(!(ra.width && ra.height)) return ra;

  switch(ro->dst_mode) {
    case MODE_TRUE_15:
    case MODE_TRUE_16: pixel_size = 2; break;
    case MODE_TRUE_24: pixel_size = 3; break;
    case MODE_TRUE_32: pixel_size = 4; break;
    case MODE_TRUE_8:
    case MODE_PSEUDO_8:
    default: pixel_size = 1;
  }

  if(ro->remap_func_flags & RFF_REMAP_RECT) {
    ro->src_x0 = x0;
    ro->src_x1 = x1;
    ro->src_y0 = y0;
    ro->src_y1 = y1;
    ro->src_offset = ro->src_y0 * ro->src_scan_len + ro->src_x0;
    ro->dst_x0 = ra.x;
    ro->dst_x1 = ra.x + ra.width;
    ro->dst_y0 = ra.y;
    ro->dst_y1 = ra.y + ra.height;
    ro->dst_offset = ro->dst_y0 * ro->dst_scan_len + ro->dst_x0;
    REMAP_AREA_DEBUG_FUNC(ro);
    ro->remap_func(ro);
  }
  else if(ro->remap_func_flags & RFF_REMAP_LINES) {
    ro->src_x0 = 0;
    ro->src_x1 = ro->src_width;
    ro->src_y0 = y0;
    ro->src_y1 = y1;
    ro->src_offset = ro->src_y0 * ro->src_scan_len;
    ro->dst_x0 = 0;
    ro->dst_x1 = ro->dst_width;
    ro->dst_y0 = ra.y;
    ro->dst_y1 = ra.y + ra.height;
    ro->dst_offset = ro->dst_y0 * ro->dst_scan_len;
    REMAP_AREA_DEBUG_FUNC(ro);
    ro->remap_func(ro);
  }
  else {
    ro->src_offset = ro->dst_offset = 0;
    ro->src_x0 = ro->dst_x0 = ro->src_y0 = ro->dst_y0 = 0;
    ro->src_x1 = ro->src_width; ro->dst_x1 = ro->dst_width;
    ro->src_y1 = ro->src_height; ro->dst_y1 = ro->dst_height;
    REMAP_AREA_DEBUG_FUNC(ro);
    ro->remap_func(ro);
  }

  return ra;
}


/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *       some functions for managing the list of remap functions
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

/*
 * Search for the first suitable remap function.
 */
static RemapFuncDesc *find_remap_func(unsigned flags, int src_mode, int dst_mode, RemapFuncDesc *rfd)
{
  while(rfd != NULL) {
    if((rfd->src_mode & src_mode) &&
       (rfd->dst_mode & dst_mode) &&
       (rfd->flags & (flags | RFF_BILIN_FILT | RFF_LIN_FILT)) == flags
    ) break;
    rfd = rfd->next;
  }
  return rfd;
}


/*
 * Searches for the best remap function available.
 * Prefers optimized ones over generic implementations and
 * functions that remap rectangular areas over those that
 * can remap only complete lines.
 */
static RemapFuncDesc *find_best_remap_func(unsigned flags, int src_mode, int dst_mode, RemapFuncDesc *rfd)
{
  RemapFuncDesc *rfd1 = NULL;

  unsigned f_list[6 * 3];
  int features = 6;
  int i;

  if(flags & RFF_BILIN_FILT) flags &= ~RFF_LIN_FILT;

  f_list[0] = flags | RFF_OPT_PENTIUM | RFF_REMAP_RECT;
  f_list[1] = flags | RFF_OPT_PENTIUM | RFF_REMAP_LINES;
  f_list[2] = flags | RFF_REMAP_RECT;
  f_list[3] = flags | RFF_REMAP_LINES;
  f_list[4] = flags | RFF_OPT_PENTIUM;
  f_list[5] = flags;

  for(i = 0; i < 6; i++) {
    f_list[features     + i] = (f_list[           i] & ~RFF_BILIN_FILT) | RFF_LIN_FILT;
    f_list[features * 2 + i] = f_list[features + i] & ~RFF_LIN_FILT;
  }

  features *= 3;

  for(i = 0; i < features; i++) {
    if((rfd1 = find_remap_func(f_list[i], src_mode, dst_mode, rfd)) != NULL) break;
  }

  return rfd1;
}


/*
 * Looks up all remap functions that are (possibly) needed
 * for a particular mode. Does _not_ set ro->remap_func!
 */
static void install_remap_funcs(RemapObject *ro, int remap_features)
{
  ro->func_all = find_best_remap_func(remap_features | RFF_SCALE_ALL, ro->src_mode, ro->dst_mode, remap_list);
  ro->func_1   = find_best_remap_func(remap_features | RFF_SCALE_1  , ro->src_mode, ro->dst_mode, remap_list);
  ro->func_2   = find_best_remap_func(remap_features | RFF_SCALE_2  , ro->src_mode, ro->dst_mode, remap_list);

  if(ro->func_1 == NULL) ro->func_1 = ro->func_all;
  if(ro->func_2 == NULL) ro->func_2 = ro->func_all;

  if(ro->func_all != NULL) ro->state |= ROS_SCALE_ALL;
  if(ro->func_1 != NULL) ro->state |= ROS_SCALE_1;
  if(ro->func_2 != NULL) ro->state |= ROS_SCALE_2;

  find_supported_modes(ro);
}


static void find_supported_modes(RemapObject *ro)
{
  RemapFuncDesc *rfd = remap_list;

  ro->supported_src_modes = 0;

  while(rfd != NULL) {
    if(rfd->dst_mode & ro->dst_mode) ro->supported_src_modes |= rfd->src_mode;
    rfd = rfd->next;
  }
}


/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *                       code generating functions
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

/*
 * initialize a code object
 */
CodeObj code_init(void)
{
  CodeObj co = {&do_nothing, NULL, NULL, 0, 0};
  return co;
}

/*
 * destroy a code object
 */
void code_done(CodeObj *co)
{
  if(co->mem != NULL) free(co->mem);
  co->mem = co->text = NULL;
  co->exec = &do_nothing;
  co->size = co->pc = 0;
}

/*
 * append new instructions
 */
void code_append_ins(CodeObj *co, int len, void *nc)
{
  /* EXEC_PAGESIZE is assumed to be a power of 2 */

  unsigned char *mem, *text;
  int size;

  if(co->size == -1) return;

  if(co->size < len + co->pc) {
    size = len + co->pc + 2 * EXEC_PAGESIZE - 1;
    mem = (unsigned char *) malloc(size);
    if(mem == NULL) {
      co->size = -1;
      co->exec = &do_nothing;
      return;
    }

    text = mem + EXEC_PAGESIZE - 1;
    text -= ((unsigned) text) & (EXEC_PAGESIZE - 1);
    size -= text - mem;

    if(mprotect(text, size, PROT_READ|PROT_WRITE|PROT_EXEC) < 0) {
      co->size = -1;
      co->exec = &do_nothing;
      free(mem);
      return;
    }

    if(co->pc) memcpy(text, co->text, co->pc);
    free(co->mem);
    co->mem = mem;
    co->text = text;
    co->exec = (void (*)(void)) text;
    co->size = size;
  }

  memcpy(co->text + co->pc, nc, len);
  co->pc += len;

#if 0
  fprintf(rdm, "[CodeObj]\n");
  fprintf(rdm, "  co.mem  = 0x%x\n", (unsigned) co->mem);
  fprintf(rdm, "  co.text = 0x%x\n", (unsigned) co->text);
  fprintf(rdm, "  co.size = %d\n", co->size);
  fprintf(rdm, "  co.pc   = %d\n", co->pc);
#endif
}


/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * some functions that generate specific instructions
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

void _a_ret(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tret\n", co->pc);
#endif
  code_append_ins(co, 1, &&_cf_001);
  return;
  _cf_001: asm("ret\n");
}

void _a_movb_dl_dh(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb %%dl,%%dh\n", co->pc);
#endif
  code_append_ins(co, 2, &&_cf_001);
  return;
  _cf_001: asm("movb %dl,%dh\n");
}

void _a_movb_dh_dl(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb %%dh,%%dl\n", co->pc);
#endif
  code_append_ins(co, 2, &&_cf_001);
  return;
  _cf_001: asm("movb %dh,%dl\n");
}

void _a_movb_n_ecx_4eax_dl(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003, _cf_004;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%ecx,%%eax,4),%%dl\n", co->pc);
#endif
    code_append_ins(co, &&_cf_002 - &&_cf_001, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%ecx,%%eax,4),%%dl\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, &&_cf_003 - &&_cf_002 - 1, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, &&_cf_004 - &&_cf_003 - 4, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%ecx,%eax,4),%dl\n");
  _cf_002: asm("movb 0x1(%ecx,%eax,4),%dl\n");
  _cf_003: asm("movb 0x100(%ecx,%eax,4),%dl\n");
  _cf_004:
}

void _a_movb_n_ecx_4eax_dh(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003, _cf_004;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%ecx,%%eax,4),%%dh\n", co->pc);
#endif
    code_append_ins(co, &&_cf_002 - &&_cf_001, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%ecx,%%eax,4),%%dh\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, &&_cf_003 - &&_cf_002 - 1, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, &&_cf_004 - &&_cf_003 - 4, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%ecx,%eax,4),%dh\n");
  _cf_002: asm("movb 0x1(%ecx,%eax,4),%dh\n");
  _cf_003: asm("movb 0x100(%ecx,%eax,4),%dh\n");
  _cf_004:
}

void _a_movb_n_ecx_4ebx_dl(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003, _cf_004;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%ecx,%%ebx,4),%%dl\n", co->pc);
#endif
    code_append_ins(co, &&_cf_002 - &&_cf_001, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%ecx,%%ebx,4),%%dl\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, &&_cf_003 - &&_cf_002 - 1, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, &&_cf_004 - &&_cf_003 - 4, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%ecx,%ebx,4),%dl\n");
  _cf_002: asm("movb 0x1(%ecx,%ebx,4),%dl\n");
  _cf_003: asm("movb 0x100(%ecx,%ebx,4),%dl\n");
  _cf_004:
}

void _a_movb_n_ecx_4ebx_dh(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003, _cf_004;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%ecx,%%ebx,4),%%dh\n", co->pc);
#endif
    code_append_ins(co, &&_cf_002 - &&_cf_001, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%ecx,%%ebx,4),%%dh\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, &&_cf_003 - &&_cf_002 - 1, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, &&_cf_004 - &&_cf_003 - 4, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%ecx,%ebx,4),%dh\n");
  _cf_002: asm("movb 0x1(%ecx,%ebx,4),%dh\n");
  _cf_003: asm("movb 0x100(%ecx,%ebx,4),%dh\n");
  _cf_004:
}

void _a_movl_ebx_4eax_ebp(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovl (%%ebx,%%eax,4),%%ebp\n", co->pc);
#endif
  code_append_ins(co, 3, &&_cf_001);
  return;
  _cf_001: asm("movl (%ebx,%eax,4),%ebp\n");
}

void _a_movl_ebx_4ecx_edx(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovl (%%ebx,%%ecx,4),%%edx\n", co->pc);
#endif
  code_append_ins(co, 3, &&_cf_001);
  return;
  _cf_001: asm("movl (%ebx,%ecx,4),%edx\n");
}

void _a_movl_ecx_4eax_ebp(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovl (%%ecx,%%eax,4),%%ebp\n", co->pc);
#endif
  code_append_ins(co, 3, &&_cf_001);
  return;
  _cf_001: asm("movl (%ecx,%eax,4),%ebp\n");
}

void _a_movl_edx_4eax_ebp(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovl (%%edx,%%eax,4),%%ebp\n", co->pc);
#endif
  code_append_ins(co, 3, &&_cf_001);
  return;
  _cf_001: asm("movl (%edx,%eax,4),%ebp\n");
}

void _a_addl_edx_4eax_ebp(CodeObj *co)
{
  __label__ _cf_001;
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\taddl (%%edx,%%eax,4),%%ebp\n", co->pc);
#endif
  code_append_ins(co, 3, &&_cf_001);
  return;
  _cf_001: asm("addl (%edx,%eax,4),%ebp\n");
}

void _a_movb_esi_al(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%esi),%%al\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%esi),%%al\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%esi),%al\n");
  _cf_002: asm("movb 0x1(%esi),%al\n");
  _cf_003: asm("movb 0x100(%esi),%al\n");
}

void _a_movb_esi_bl(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%esi),%%bl\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%esi),%%bl\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%esi),%bl\n");
  _cf_002: asm("movb 0x1(%esi),%bl\n");
  _cf_003: asm("movb 0x100(%esi),%bl\n");
}

void _a_movb_esi_cl(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%esi),%%cl\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%esi),%%cl\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%esi),%cl\n");
  _cf_002: asm("movb 0x1(%esi),%cl\n");
  _cf_003: asm("movb 0x100(%esi),%cl\n");
}

void _a_movb_esi_dl(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%esi),%%dl\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%esi),%%dl\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%esi),%dl\n");
  _cf_002: asm("movb 0x1(%esi),%dl\n");
  _cf_003: asm("movb 0x100(%esi),%dl\n");
}

void _a_movb_esi_dh(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb (%%esi),%%dh\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb $%d(%%esi),%%dh\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb (%esi),%dh\n");
  _cf_002: asm("movb 0x1(%esi),%dh\n");
  _cf_003: asm("movb 0x100(%esi),%dh\n");
}

void _a_movl_ebp_edi(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovl %%ebp,(%%edi)\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovl %%ebp,$%d(%%edi)\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movl %ebp,(%edi)\n");
  _cf_002: asm("movl %ebp,0x1(%edi)\n");
  _cf_003: asm("movl %ebp,0x100(%edi)\n");
}

void _a_movw_bp_edi(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovw %%bp,(%%edi)\n", co->pc);
#endif
    code_append_ins(co, 3, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovw %%bp,$%d(%%edi)\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 3, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 3, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movw %bp,(%edi)\n");
  _cf_002: asm("movw %bp,0x1(%edi)\n");
  _cf_003: asm("movw %bp,0x100(%edi)\n");
}

void _a_movl_edx_edi(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovl %%edx,(%%edi)\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovl %%edx,$%d(%%edi)\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movl %edx,(%edi)\n");
  _cf_002: asm("movl %edx,0x1(%edi)\n");
  _cf_003: asm("movl %edx,0x100(%edi)\n");
}

void _a_movw_dx_edi(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovw %%dx,(%%edi)\n", co->pc);
#endif
    code_append_ins(co, 3, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovw %%dx,$%d(%%edi)\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 3, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 3, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movw %dx,(%edi)\n");
  _cf_002: asm("movw %dx,0x1(%edi)\n");
  _cf_003: asm("movw %dx,0x100(%edi)\n");
}

void _a_movb_dl_edi(CodeObj *co, int ofs)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!ofs) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tmovb %%dl,(%%edi)\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tmovb %%dl,$%d(%%edi)\n", co->pc, ofs);
#endif
  if(ofs >=-128 && ofs <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &ofs);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &ofs);
  return;
  _cf_001: asm("movb %dl,(%edi)\n");
  _cf_002: asm("movb %dl,0x1(%edi)\n");
  _cf_003: asm("movb %dl,0x100(%edi)\n");
}

void _a_addl_n_edi(CodeObj *co, int n)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!n) return;

  if(n == 1) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tincl %%edi\n", co->pc);
#endif
    code_append_ins(co, 1, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\taddl $%d,%%edi\n", co->pc, n);
#endif
  if(n >=-128 && n <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &n);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &n);
  return;
  _cf_001: asm("incl %edi\n");
  _cf_002: asm("addl $1,%edi\n");
  _cf_003: asm("addl $0x100,%edi\n");
}

void _a_addl_n_esi(CodeObj *co, int n)
{
  __label__ _cf_001, _cf_002, _cf_003;

  if(!n) return;

  if(n == 1) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tincl %%esi\n", co->pc);
#endif
    code_append_ins(co, 1, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\taddl $%d,%%esi\n", co->pc, n);
#endif
  if(n >=-128 && n <=127) {
    code_append_ins(co, 2, &&_cf_002);
    code_append_ins(co, 1, &n);
    return;
  }
  code_append_ins(co, 2, &&_cf_003);
  code_append_ins(co, 4, &n);
  return;
  _cf_001: asm("incl %esi\n");
  _cf_002: asm("addl $1,%esi\n");
  _cf_003: asm("addl $0x100,%esi\n");
}

void _a_shrl_n_ebp(CodeObj *co, int n)
{
  __label__ _cf_001, _cf_002;

  n &= 0x1f;

  if(!n) return;

  if(n == 1) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tshrl %%ebp\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tshrl $%d,%%ebp\n", co->pc, n);
#endif
  code_append_ins(co, 2, &&_cf_002);
  code_append_ins(co, 1, &n);
  return;
  _cf_001: asm("shrl %ebp\n");
  _cf_002: asm("shrl $2,%ebp\n");
}

void _a_shll_n_edx(CodeObj *co, int n)
{
  __label__ _cf_001, _cf_002;

  n &= 0x1f;

  if(!n) return;

  if(n == 1) {
#ifdef REMAP_CODE_DEBUG
    fprintf(rdm, "0x%04x:\tshll %%edx\n", co->pc);
#endif
    code_append_ins(co, 2, &&_cf_001);
    return;
  }
#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "0x%04x:\tshll $%d,%%edx\n", co->pc, n);
#endif
  code_append_ins(co, 2, &&_cf_002);
  code_append_ins(co, 1, &n);
  return;
  _cf_001: asm("shll %edx\n");
  _cf_002: asm("shll $2,%edx\n");
}


/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *                     Pentium optimized functions
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

void pent_8to8_1(RemapObject *);
void pent_8to8_all(RemapObject *);
static void pent_8to8_all_init(RemapObject *);

void pent_8to8p_all(RemapObject *);
static void pent_8to8p_all_init(RemapObject *);

void pent_8to16_test(RemapObject *);
void pent_8to16_1(RemapObject *);
void pent_8to16_all(RemapObject *);
static void pent_8to16_all_init(RemapObject *);

void pent_8to32_1(RemapObject *);
void pent_8to32_all(RemapObject *);
static void pent_8to32_all_init(RemapObject *);

static RemapFuncDesc remap_opt_list[] = {

  REMAP_DESC(
    RFF_SCALE_1   | RFF_OPT_PENTIUM | RFF_REMAP_RECT,
    MODE_PSEUDO_8,
    MODE_TRUE_8,
    pent_8to8_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL   | RFF_OPT_PENTIUM | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_TRUE_8,
    pent_8to8_all,
    pent_8to8_all_init
  ),

  REMAP_DESC(
    RFF_SCALE_ALL   | RFF_OPT_PENTIUM | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_PSEUDO_8,
    pent_8to8p_all,
    pent_8to8p_all_init
  ),

#if 0
  REMAP_DESC(RFF_SCALE_ALL | RFF_OPT_PENTIUM | RFF_REMAP_LINES,
    MODE_PSEUDO_8,
    MODE_TRUE_15 | MODE_TRUE_16,
    pent_8to16_test,
    NULL
  ),
#endif

  REMAP_DESC(
    RFF_SCALE_1   | RFF_OPT_PENTIUM | RFF_REMAP_RECT,
    MODE_PSEUDO_8,
    MODE_TRUE_15 | MODE_TRUE_16,
    pent_8to16_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL | RFF_OPT_PENTIUM | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_TRUE_15 | MODE_TRUE_16,
    pent_8to16_all,
    pent_8to16_all_init
  ),

  REMAP_DESC(
    RFF_SCALE_1   | RFF_OPT_PENTIUM | RFF_REMAP_RECT,
    MODE_PSEUDO_8,
    MODE_TRUE_32,
    pent_8to32_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL   | RFF_OPT_PENTIUM | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_TRUE_32,
    pent_8to32_all,
    pent_8to32_all_init
  )

};

/*
 * returns chained list of modes
 */
RemapFuncDesc *remap_opt()
{
  int i;

  for(i = 0; i < sizeof(remap_opt_list) / sizeof(*remap_opt_list) - 1; i++) {
    remap_opt_list[i].next = remap_opt_list + i + 1;
  }

  return remap_opt_list;
}


/*
 * create code that remaps a complete line;
 * needed for pent_8to8_all
 */
void pent_8to8_all_init(RemapObject *ro)
{
  int i, j, d_o_r, s_o_r, d_o, s_o;
  int d_len = ro->dst_width & ~3;
  int *bre_x = ro->bre_x;
  int b0, b1, b2, b3;
  CodeObj *co = ro->co;

  if(co != NULL) { code_done(co); }
  *co = code_init();

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
  fprintf(rdm, "pent_8to8_all_line_remap:\n");
#endif

/*
 *      esi, edi: src, dst
 *      eax, ebx: 0
 *           ecx: true_color_lut
 *           edx: [4*eax], [4*ebx]
 */

  for(i = s_o_r = d_o = s_o = d_o_r = 0; i < d_len; ) {
    if(d_o > 127) {
      j = d_o + 128;
      if(j > ro->dst_scan_len - d_o_r) j = ro->dst_scan_len - d_o_r;
      _a_addl_n_edi(co, j);
      d_o_r += j;
      d_o -= j;
    }
    if(s_o > 127) {
      _a_addl_n_esi(co, j = s_o + 128);
      s_o_r += j;
      s_o -= j;
    }

    b0 = 0;
    b1 = bre_x[i++];
    b2 = b1 + bre_x[i++];
    b3 = b2 + bre_x[i++];

    _a_movb_esi_al(co, s_o + b3);
    if(b3 != b2) {
      _a_movb_esi_bl(co, s_o + b2);
    }
    _a_movb_n_ecx_4eax_dh(co, 1);
    if(b3 == b2) {
      _a_movb_n_ecx_4eax_dl(co, 0);
    }
    else {
      _a_movb_n_ecx_4ebx_dl(co, 0);
    }

    _a_shll_n_edx(co, 16);

    if(b1 == b2 && b2 != b3) {
      if(b0 != b1) {
        _a_movb_esi_al(co, s_o + b0);
      }
      _a_movb_n_ecx_4ebx_dh(co, 1);
      if(b0 == b1) {
        _a_movb_n_ecx_4ebx_dl(co, 0);
      }
      else {
        _a_movb_n_ecx_4eax_dl(co, 0);
      }
    }
    else {
      if(b1 != b3) {
        _a_movb_esi_al(co, s_o + b1);
      }
      if(b0 != b1) {
        _a_movb_esi_bl(co, s_o + b0);
      }
      _a_movb_n_ecx_4eax_dh(co, 1);
      if(b0 == b1) {
        _a_movb_n_ecx_4eax_dl(co, 0);
      }
      else {
        _a_movb_n_ecx_4ebx_dl(co, 0);
      }
    }

    _a_movl_edx_edi(co, d_o);

    d_o += 4;
    s_o += b3 + bre_x[i++];
  }

  if(ro->dst_width & 2) {
    _a_movb_esi_al(co, s_o);
    s_o += bre_x[i++];
    _a_movb_esi_bl(co, s_o);
    s_o += bre_x[i++];
    _a_movb_n_ecx_4eax_dl(co, 0);
    _a_movb_n_ecx_4ebx_dh(co, 1);
    _a_movw_dx_edi(co, d_o);
    d_o += 2;
  }

  if(ro->dst_width & 1) {
    _a_movb_esi_al(co, s_o);
    _a_movb_n_ecx_4eax_dl(co, 0);
    _a_movb_dl_edi(co, d_o);
  }

  _a_addl_n_edi(co, ro->dst_scan_len - d_o_r);

  _a_ret(co);

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
#endif

  ro->remap_line = co->exec;
}


/*
 * create code that remaps a complete line;
 * needed for pent_8to8p_all
 */
void pent_8to8p_all_init(RemapObject *ro)
{
  int i, j, d_o_r, s_o_r, d_o, s_o;
  int d_len = ro->dst_width & ~3;
  int *bre_x = ro->bre_x;
  int b0, b1, b2, b3;
  CodeObj *co = ro->co;

  if(co != NULL) { code_done(co); }
  *co = code_init();

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
  fprintf(rdm, "pent_8to8p_all_line_remap:\n");
#endif

/*
 *           esi, edi: src, dst
 * eax, ebx, ecx, edx: 0
 */

  for(i = s_o_r = d_o = s_o = d_o_r = 0; i < d_len; ) {
    if(d_o > 127) {
      j = d_o + 128;
      if(j > ro->dst_scan_len - d_o_r) j = ro->dst_scan_len - d_o_r;
      _a_addl_n_edi(co, j);
      d_o_r += j;
      d_o -= j;
    }
    if(s_o > 127) {
      _a_addl_n_esi(co, j = s_o + 128);
      s_o_r += j;
      s_o -= j;
    }

    b0 = 0;
    b1 = bre_x[i++];
    b2 = b1 + bre_x[i++];
    b3 = b2 + bre_x[i++];

    _a_movb_esi_dh(co, s_o + b3);
    if(b3 == b2) {
      _a_movb_dh_dl(co);
    }
    else {
      _a_movb_esi_dl(co, s_o + b2);
    }

    _a_shll_n_edx(co, 16);

    _a_movb_esi_dh(co, s_o + b1);
    if(b3 == b2) {
      _a_movb_dh_dl(co);
    }
    else {
      _a_movb_esi_dl(co, s_o + b0);
    }

    _a_movl_edx_edi(co, d_o);

    d_o += 4;
    s_o += b3 + bre_x[i++];
  }

  if(ro->dst_width & 2) {
    _a_movb_esi_dl(co, s_o);
    s_o += bre_x[i++];
    _a_movb_esi_dh(co, s_o);
    s_o += bre_x[i++];
    _a_movw_dx_edi(co, d_o);
    d_o += 2;
  }

  if(ro->dst_width & 1) {
    _a_movb_esi_dl(co, s_o);
    _a_movb_dl_edi(co, d_o);
  }

  _a_addl_n_edi(co, ro->dst_scan_len - d_o_r);

  _a_ret(co);

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
#endif

  ro->remap_line = co->exec;
}


/*
 * create code that remaps a complete line;
 * needed for pent_8to16_all
 */
void pent_8to16_all_init(RemapObject *ro)
{
  int i, j, d_o_r, s_o_r, d_o, s_o;
  int d_len = ro->dst_width & ~1;
  int *bre_x = ro->bre_x;
  int last_dx0 = -1, last_dx = -1;
  CodeObj *co = ro->co;

  if(co != NULL) { code_done(co); }
  *co = code_init();

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
  fprintf(rdm, "pent_8to16_all_line_remap:\n");
#endif

/*
 *      esi, edi: src, dst
 *           eax: 0
 * ebx, ecx, edx: &true_color_lut[0, 100, 200]
 *           ebp: [4*eax]
 */

  for(i = s_o_r = d_o = s_o = d_o_r = 0; i < d_len; i += 2) {
    if(d_o > 127) {
      j = d_o + 128;
      if(j > ro->dst_scan_len - d_o_r) j = ro->dst_scan_len - d_o_r;
      _a_addl_n_edi(co, j);
      d_o_r += j;
      d_o -= j;
    }

    if(ro->src_mode != MODE_VGA_X) {
      if(s_o > 127) {
        _a_addl_n_esi(co, j = s_o + 128);
        s_o_r += j;
        s_o -= j;
      }
    }

    if(bre_x[i]) {
      if(last_dx) {
        _a_movb_esi_al(co, s_o);
        _a_movl_ecx_4eax_ebp(co);
      }
      else {
        _a_shrl_n_ebp(co, 16);
      }
      s_o += bre_x[i];
      _a_movb_esi_al(co, s_o);
      _a_addl_edx_4eax_ebp(co);
      _a_movl_ebp_edi(co, d_o);
    }
    else {
      if(last_dx) {
        _a_movb_esi_al(co, s_o);
        _a_movl_ebx_4eax_ebp(co);
      }
      else {
        if(last_dx0) _a_movl_ebx_4eax_ebp(co);
      }
      _a_movl_ebp_edi(co, d_o);
    }

    last_dx0 = bre_x[i];
    d_o += 4;
    s_o += (last_dx = bre_x[i + 1]);
  }

  if(ro->dst_width & 1) {
    if(last_dx) {
      _a_movb_esi_al(co, s_o);
      _a_movl_ecx_4eax_ebp(co);
    }
    else {
      _a_shrl_n_ebp(co, 16);
    }
    _a_movw_bp_edi(co, d_o);
  }

  _a_addl_n_edi(co, ro->dst_scan_len - d_o_r);

  _a_ret(co);

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
#endif

  ro->remap_line = co->exec;
}


/*
 * create code that remaps a complete line;
 * needed for pent_8to32_all
 */
void pent_8to32_all_init(RemapObject *ro)
{
  int i, j, k, d_o_r, s_o_r, d_o, s_o;
  int d_len = ro->dst_width;
  int *bre_x = ro->bre_x;
  int l0, l1;
  int b0, b1;
  CodeObj *co = ro->co;

  if(co != NULL) { code_done(co); }
  *co = code_init();

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
  fprintf(rdm, "pent_8to32_all_line_remap:\n");
#endif

/*
 *      esi, edi: src, dst
 *      eax, ecx: 0
 *           ebx: true_color_lut
 *      ebp, edx: [4*eax], [4*ecx]
 */

  l0 = l1 = 1;
  b0 = b1 = j = 0;

  for(i = s_o_r = d_o = s_o = d_o_r = 0; i < d_len; i++) {
    if(bre_x[i] == 0) {
      if(j == 0)
        l0++;
      else
        l1++;
    }
    else {
      if(j == 0)
        b0 = bre_x[i];
      else
        b1 = bre_x[i];
      j++;
    }

    if(j == 2 || i == d_len - 1) {

      if(d_o > 127) {
        k = d_o + 128;
        if(k > ro->dst_scan_len - d_o_r) k = ro->dst_scan_len - d_o_r;
        _a_addl_n_edi(co, k);
        d_o_r += k;
        d_o -= k;
      }

      if(ro->src_mode != MODE_VGA_X) {
        if(s_o > 127) {
          _a_addl_n_esi(co, k = s_o + 128);
          s_o_r += k;
          s_o -= k;
        }
      }

      if(j == 1) {
        _a_movb_esi_al(co, s_o);
        _a_movl_ebx_4eax_ebp(co);
        _a_movl_ebp_edi(co, d_o);
      }

      if(j == 2) {
        _a_movb_esi_al(co, s_o);
        _a_movb_esi_cl(co, s_o + b0);
        _a_movl_ebx_4eax_ebp(co);
        _a_movl_ebx_4ecx_edx(co);
        for(k = 0; k < l0; k++) _a_movl_ebp_edi(co, d_o + (k << 2));
        for(k = 0; k < l1; k++) _a_movl_edx_edi(co, d_o + ((k + l0) << 2));

        d_o += (l0 + l1) << 2;
        s_o += b0 + b1;
      }

      l0 = l1 = 1;
      j = 0;
    }
  
  }

  _a_addl_n_edi(co, ro->dst_scan_len - d_o_r);

  _a_ret(co);

#ifdef REMAP_CODE_DEBUG
  fprintf(rdm, "- - - - - - - - \n");
#endif

  ro->remap_line = co->exec;
}


/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *                     some not so optimized remap functions
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

void gen_8to8p_1(RemapObject *);
void gen_8to8p_all(RemapObject *);
void gen_8to8_1(RemapObject *);
void gen_8to8_all(RemapObject *);
void gen_8to16_all(RemapObject *);
void gen_8to32_1(RemapObject *);
void gen_8to32_all(RemapObject *);
void gen_8to32_bilin(RemapObject *);
void gen_8to16_bilin(RemapObject *);
void gen_8to32_lin(RemapObject *);
void gen_8to16_lin(RemapObject *);

void gen_4to8p_all(RemapObject *);
void gen_4to8_all(RemapObject *);
void gen_4to16_all(RemapObject *);
void gen_4to32_all(RemapObject *);

void gen_16to16_1(RemapObject *);
void gen_24to24_1(RemapObject *);
void gen_32to32_1(RemapObject *);
void gen_24to32_1(RemapObject *);

static RemapFuncDesc remap_gen_list[] = {

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_LINES,
    MODE_PSEUDO_8,
    MODE_PSEUDO_8,
    gen_8to8p_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_PSEUDO_8,
    gen_8to8p_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_RECT,
    MODE_PSEUDO_8,
    MODE_TRUE_8,
    gen_8to8_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_TRUE_8,
    gen_8to8_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_TRUE_15 | MODE_TRUE_16,
    gen_8to16_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_RECT,
    MODE_PSEUDO_8,
    MODE_TRUE_32,
    gen_8to32_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_X | MODE_PSEUDO_8,
    MODE_TRUE_32,
    gen_8to32_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES | RFF_BILIN_FILT,
    MODE_PSEUDO_8,
    MODE_TRUE_32,
    gen_8to32_bilin,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES | RFF_BILIN_FILT,
    MODE_PSEUDO_8,
    MODE_TRUE_15 | MODE_TRUE_16,
    gen_8to16_bilin,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES | RFF_LIN_FILT,
    MODE_PSEUDO_8,
    MODE_TRUE_32,
    gen_8to32_lin,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES | RFF_LIN_FILT,
    MODE_PSEUDO_8,
    MODE_TRUE_15 | MODE_TRUE_16,
    gen_8to16_lin,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_4,
    MODE_PSEUDO_8,
    gen_4to8p_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_4,
    MODE_TRUE_8,
    gen_4to8_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_4,
    MODE_TRUE_15 | MODE_TRUE_16,
    gen_4to16_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_ALL  | RFF_REMAP_LINES,
    MODE_VGA_4,
    MODE_TRUE_32,
    gen_4to32_all,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_LINES,
    MODE_TRUE_15,
    MODE_TRUE_15,
    gen_16to16_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_LINES,
    MODE_TRUE_16,
    MODE_TRUE_16,
    gen_16to16_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_LINES,
    MODE_TRUE_24,
    MODE_TRUE_24,
    gen_24to24_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_LINES,
    MODE_TRUE_32,
    MODE_TRUE_32,
    gen_32to32_1,
    NULL
  ),

  REMAP_DESC(
    RFF_SCALE_1  | RFF_REMAP_LINES,
    MODE_TRUE_24,
    MODE_TRUE_32,
    gen_24to32_1,
    NULL
  )
};

/*
 * returns chained list of modes
 */
RemapFuncDesc *remap_gen()
{
  int i;

  for(i = 0; i < sizeof(remap_gen_list) / sizeof(*remap_gen_list) - 1; i++) {
    remap_gen_list[i].next = remap_gen_list + i + 1;
  }

  return remap_gen_list;
}


/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 * now the implementation
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */


/*
 * 8 bit pseudo color --> 8 bit pseudo color (private color map)
 */
void gen_8to8p_1(RemapObject *ro)
{
  int i;
  unsigned char *src, *dst;

  src = ro->src_image + ro->src_offset;
  dst = ro->dst_image + ro->dst_offset;

  for(i = ro->src_y0; i < ro->src_y1; i++) {
    memcpy(dst, src, ro->src_width);
    src += ro->src_scan_len;
    dst += ro->dst_scan_len;
  }
}


/*
 * 8 bit pseudo color --> 8 bit pseudo color (private color map)
 * supports arbitrary scaling
 */
void gen_8to8p_all(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len;
  int *bre_x;
  int *bre_y = ro->bre_y;

  unsigned char *src, *src0, *dst;

  src0 = ro->src_image;
  dst = ro->dst_image + ro->dst_offset;
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      dst[d_x++] = src[s_x];
      s_x += *(bre_x++);
    }
  }
}


/*
 * 8 bit pseudo color --> 8 bit true color (shared color map)
 */
void gen_8to8_1(RemapObject *ro)
{
  int i, j, l, k;
  unsigned char *src, *dst;
  unsigned char *lut = (unsigned char*) ro->true_color_lut;

  src = ro->src_image + ro->src_offset;
  dst = ro->dst_image + ro->dst_offset;

  l = ro->src_x1 - ro->src_x0;

  for(j = ro->src_y0; j < ro->src_y1; j++) {
    k = (j & 1) << 1;
    for(i = 0; i < l; i++) {
      dst[i] = lut[4 * src[i] + (k ^= 1)];
    }
    dst += ro->dst_scan_len;
    src += ro->src_scan_len;
  }
}


/*
 * 8 bit pseudo color --> 8 bit true color (shared color map)
 * supports arbitrary scaling
 */
void gen_8to8_all(RemapObject *ro)
{
  int k;
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len;
  int *bre_x;
  int *bre_y = ro->bre_y;
  unsigned char *src, *src0, *dst;
  unsigned char *lut = (unsigned char*) ro->true_color_lut;

  src0 = ro->src_image;
  dst = ro->dst_image + ro->dst_offset;
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    k = (d_y & 1) << 1;
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      dst[d_x++] = lut[4 * src[s_x] + (k ^= 1)];
      s_x += *(bre_x++);
    }
  }
}


/*
 * 8 bit pseudo color --> 15/16 bit true color
 * supports arbitrary scaling
 */
void gen_8to16_all(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 1;
  int *bre_x;
  int *bre_y = ro->bre_y;

  unsigned char *src, *src0;
  unsigned short *dst;

  src0 = ro->src_image;
  dst = (unsigned short *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      dst[d_x++] = ro->true_color_lut[src[s_x]];
      s_x += *(bre_x++);
    }
  }
}


/*
 * 8 bit pseudo color --> 32 bit true color
 */
void gen_8to32_1(RemapObject *ro)
{
  int i, j, l;
  unsigned char *src;
  unsigned *dst;

  src = ro->src_image + ro->src_offset;
  dst = (unsigned *) (ro->dst_image + ro->dst_offset);
  l = (ro->src_x1 - ro->src_x0);

  for(j = ro->src_y0; j < ro->src_y1; j++) {
    for(i = 0; i < l; i++) {
      dst[i] = ro->true_color_lut[src[i]];
    }
    dst += ro->dst_scan_len >> 2;
    src += ro->src_scan_len;
  }
}


/*
 * 8 bit pseudo color --> 32 bit true color
 * supports arbitrary scaling
 */
void gen_8to32_all(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 2;
  int *bre_x;
  int *bre_y = ro->bre_y;

  unsigned char *src, *src0;
  unsigned *dst;

  src0 = ro->src_image;
  dst = (unsigned *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      dst[d_x++] = ro->true_color_lut[src[s_x]];
      s_x += *(bre_x++);
    }
  }
}


/*
 * 8 bit pseudo color --> 32 bit true color
 * supports arbitrary scaling
 */
void gen_8to32_bilin(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 2;
  int s_scan_len = ro->src_scan_len;
  int *bre_x;
  int *bre_y = ro->bre_y;

  unsigned char *src, *src0, *src1;
  unsigned *dst;
  unsigned *lut = ro->true_color_lut;

  src0 = ro->src_image;
  dst = (unsigned *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    src1 = src + s_scan_len;

    switch(*(bre_y + d_y - 1 + ro->dst_height)) {
      case 0:
        for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
          switch(*(bre_x + d_x_len)) {
            case 0:
              dst[d_x++] = lut[src[s_x]];
              break;
            case 1:
              dst[d_x++] = lut[src[s_x] + LUT_OFS_67] + lut[src[s_x + 1] + LUT_OFS_33];
              break;
            case 2:
              dst[d_x++] = lut[src[s_x] + LUT_OFS_33] + lut[src[s_x + 1] + LUT_OFS_67];
              break;
            default:
              fprintf(stderr, "***** oops\n");
          }
          s_x += *(bre_x++);
        }
        break;

      case 1:
        for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
          switch(*(bre_x + d_x_len)) {
            case 0:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_67]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_33];
              break;
            case 1:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_45] + lut[src[s_x + 1             ] + LUT_OFS_22]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_11];
              break;
            case 2:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_22] + lut[src[s_x + 1             ] + LUT_OFS_45]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_11] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22];
              break;
            default:
              fprintf(stderr, "***** oops\n");
          }
          s_x += *(bre_x++);
        }
        break;

      case 2:
        for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
          switch(*(bre_x + d_x_len)) {
            case 0:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_33]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_67];
              break;
            case 1:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_22] + lut[src[s_x + 1             ] + LUT_OFS_11]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_45] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22];
              break;
            case 2:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_11] + lut[src[s_x + 1             ] + LUT_OFS_22]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_45];
              break;
            default:
              fprintf(stderr, "***** oops\n");
          }
          s_x += *(bre_x++);
        }
        break;

      default:
        fprintf(stderr, "###### oops\n");
    }

  }
}


/*
 * 8 bit pseudo color --> 15/16 bit true color
 * supports arbitrary scaling
 */
void gen_8to16_bilin(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 1;
  int s_scan_len = ro->src_scan_len;
  int *bre_x;
  int *bre_y = ro->bre_y;

  unsigned char *src, *src0, *src1;
  unsigned short *dst;
  unsigned *lut = ro->true_color_lut;

  src0 = ro->src_image;
  dst = (unsigned short *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    src1 = src + s_scan_len;

    switch(*(bre_y + d_y - 1 + ro->dst_height)) {
      case 0:
        for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
          switch(*(bre_x + d_x_len)) {
            case 0:
              dst[d_x++] = lut[src[s_x]];
              break;
            case 1:
              dst[d_x++] = lut[src[s_x] + LUT_OFS_67] + lut[src[s_x + 1] + LUT_OFS_33];
              break;
            case 2:
              dst[d_x++] = lut[src[s_x] + LUT_OFS_33] + lut[src[s_x + 1] + LUT_OFS_67];
              break;
            default:
              fprintf(stderr, "***** oops\n");
          }
          s_x += *(bre_x++);
        }
        break;

      case 1:
        for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
          switch(*(bre_x + d_x_len)) {
            case 0:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_67]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_33];
              break;
            case 1:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_45] + lut[src[s_x + 1             ] + LUT_OFS_22]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_11];
              break;
            case 2:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_22] + lut[src[s_x + 1             ] + LUT_OFS_45]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_11] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22];
              break;
            default:
              fprintf(stderr, "***** oops\n");
          }
          s_x += *(bre_x++);
        }
        break;

      case 2:
        for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
          switch(*(bre_x + d_x_len)) {
            case 0:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_33]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_67];
              break;
            case 1:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_22] + lut[src[s_x + 1             ] + LUT_OFS_11]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_45] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22];
              break;
            case 2:
              dst[d_x++] = lut[src[s_x             ] + LUT_OFS_11] + lut[src[s_x + 1             ] + LUT_OFS_22]
                         + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_45];
              break;
            default:
              fprintf(stderr, "***** oops\n");
          }
          s_x += *(bre_x++);
        }
        break;

      default:
        fprintf(stderr, "###### oops\n");
    }

  }
}


/*
 * 8 bit pseudo color --> 32 bit true color
 * supports arbitrary scaling
 */
void gen_8to32_lin(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 2;
  int *bre_x;
  int *bre_y = ro->bre_y;

  unsigned char *src, *src0;
  unsigned *dst;

  src0 = ro->src_image;
  dst = (unsigned *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      switch(*(bre_x + d_x_len)) {
        case 0:
          dst[d_x++] = ro->true_color_lut[src[s_x]];
          break;
        case 1:
          dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_67] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_33];
          break;
        case 2:
          dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_33] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_67];
          break;
        default:
          fprintf(stderr, "***** oops\n");
      }
      s_x += *(bre_x++);
    }
  }
}


/*
 * 8 bit pseudo color --> 15/16 bit true color
 * supports arbitrary scaling
 */
void gen_8to16_lin(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 1;
  int *bre_x;
  int *bre_y = ro->bre_y;

  unsigned char *src, *src0;
  unsigned short *dst;

  src0 = ro->src_image;
  dst = (unsigned short *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      switch(*(bre_x + d_x_len)) {
        case 0:
          dst[d_x++] = ro->true_color_lut[src[s_x]];
          break;
        case 1:
          dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_67] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_33];
          break;
        case 2:
          dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_33] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_67];
          break;
        default:
          fprintf(stderr, "***** oops\n");
      }
      s_x += *(bre_x++);
    }
  }
}

/*
 * 4 bit pseudo color --> 8 bit pseudo color (private color map)
 * supports arbitrary scaling
 *
 * -- very basic and slow --
 */
void gen_4to8p_all(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len;
  int *bre_x;
  int *bre_y = ro->bre_y;
  unsigned char c0, c1, c2, c3;
  int i;

  unsigned char *src, *src0, *dst;

  src0 = ro->src_image;
  dst = ro->dst_image + ro->dst_offset;
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      i = s_x >> 3;
      c0 = src[i]; c1 = src[i + 0x10000]; c2 = src[i + 0x20000]; c3 = src[i + 0x30000];
      i = (s_x & 7) ^ 7;
      c0 >>= i; c1 >>= i; c2 >>= i; c3 >>= i;
      c0 &= 1; c1 &= 1; c2 &= 1; c3 &= 1;
      c0 |= (c1 << 1) + (c2 << 2) + (c3 << 3);
      dst[d_x++] = c0;
      s_x += *(bre_x++);
    }
  }
}


/*
 * 4 bit pseudo color --> 8 bit true color (shared color map)
 * supports arbitrary scaling
 *
 * -- very basic and slow --
 */
void gen_4to8_all(RemapObject *ro)
{
  int k;
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len;
  int *bre_x;
  int *bre_y = ro->bre_y;
  unsigned char *src, *src0, *dst;
  unsigned char *lut = (unsigned char*) ro->true_color_lut;
  unsigned char c0, c1, c2, c3;
  int i;

  src0 = ro->src_image;
  dst = ro->dst_image + ro->dst_offset;
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    k = (d_y & 1) << 1;
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      i = s_x >> 3;
      c0 = src[i]; c1 = src[i + 0x10000]; c2 = src[i + 0x20000]; c3 = src[i + 0x30000];
      i = (s_x & 7) ^ 7;
      c0 >>= i; c1 >>= i; c2 >>= i; c3 >>= i;
      c0 &= 1; c1 &= 1; c2 &= 1; c3 &= 1;
      c0 |= (c1 << 1) + (c2 << 2) + (c3 << 3);
      dst[d_x++] = lut[4 * c0 + (k ^= 1)];
      s_x += *(bre_x++);
    }
  }
}


/*
 * 4 bit pseudo color --> 15/16 bit true color
 * supports arbitrary scaling
 *
 * -- very basic and slow --
 */
void gen_4to16_all(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 1;
  int *bre_x;
  int *bre_y = ro->bre_y;
  unsigned char c0, c1, c2, c3;
  int i;

  unsigned char *src, *src0;
  unsigned short *dst;

  src0 = ro->src_image;
  dst = (unsigned short *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      i = s_x >> 3;
      c0 = src[i]; c1 = src[i + 0x10000]; c2 = src[i + 0x20000]; c3 = src[i + 0x30000];
      i = (s_x & 7) ^ 7;
      c0 >>= i; c1 >>= i; c2 >>= i; c3 >>= i;
      c0 &= 1; c1 &= 1; c2 &= 1; c3 &= 1;
      c0 |= (c1 << 1) + (c2 << 2) + (c3 << 3);
      dst[d_x++] = ro->true_color_lut[c0];
      s_x += *(bre_x++);
    }
  }
}


/*
 * 4 bit pseudo color --> 32 bit true color
 * supports arbitrary scaling
 *
 * -- very basic and slow --
 */
void gen_4to32_all(RemapObject *ro)
{
  int d_x_len;
  int s_x, d_x, d_y;
  int d_scan_len = ro->dst_scan_len >> 2;
  int *bre_x;
  int *bre_y = ro->bre_y;
  unsigned char c0, c1, c2, c3;
  int i;


  unsigned char *src, *src0;
  unsigned *dst;

  src0 = ro->src_image;
  dst = (unsigned *) (ro->dst_image + ro->dst_offset);
  d_x_len = ro->dst_width;

  for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) {
    src = src0 + bre_y[d_y++];
    for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) {
      i = s_x >> 3;
      c0 = src[i]; c1 = src[i + 0x10000]; c2 = src[i + 0x20000]; c3 = src[i + 0x30000];
      i = (s_x & 7) ^ 7;
      c0 >>= i; c1 >>= i; c2 >>= i; c3 >>= i;
      c0 &= 1; c1 &= 1; c2 &= 1; c3 &= 1;
      c0 |= (c1 << 1) + (c2 << 2) + (c3 << 3);
      dst[d_x++] = ro->true_color_lut[c0];
      s_x += *(bre_x++);
    }
  }
}


/*
 * 16 bit true color --> 16 bit true color
 * 15 bit true color --> 15 bit true color
 */
void gen_16to16_1(RemapObject *ro)
{
  int i;
  unsigned char *src, *dst;

  src = ro->src_image + ro->src_offset;
  dst = ro->dst_image + ro->dst_offset;

  for(i = ro->src_y0; i < ro->src_y1; i++) {
    memcpy(dst, src, ro->src_width << 1);
    src += ro->src_scan_len;
    dst += ro->dst_scan_len;
  }
}


/*
 * 24 bit true color --> 24 bit true color
 */
void gen_24to24_1(RemapObject *ro)
{
  int i;
  unsigned char *src, *dst;

  src = ro->src_image + ro->src_offset;
  dst = ro->dst_image + ro->dst_offset;

  for(i = ro->src_y0; i < ro->src_y1; i++) {
    memcpy(dst, src, ro->src_width * 3);
    src += ro->src_scan_len;
    dst += ro->dst_scan_len;
  }
}


/*
 * 32 bit true color --> 32 bit true color
 */
void gen_32to32_1(RemapObject *ro)
{
  int i;
  unsigned char *src, *dst;

  src = ro->src_image + ro->src_offset;
  dst = ro->dst_image + ro->dst_offset;

  for(i = ro->src_y0; i < ro->src_y1; i++) {
    memcpy(dst, src, ro->src_width << 2);
    src += ro->src_scan_len;
    dst += ro->dst_scan_len;
  }
}


/*
 * 24 bit true color --> 32 bit true color
 * *** ingnores color space description ***
 */
void gen_24to32_1(RemapObject *ro)
{
  int i, j;
  unsigned char *src, *dst, *src_1;
  unsigned *dst_4;

  src = ro->src_image + ro->src_offset;
  dst = ro->dst_image + ro->dst_offset;

  for(i = ro->src_y0; i < ro->src_y1; i++) {
    src_1 = src;
    dst_4 = (unsigned *) dst;

    for(j = 0; j < ro->src_width; j++) {
      *dst_4++ = src_1[0] + (src_1[1] << 8) + (src_1[2] << 16);
      src_1 += 3;
    }

    src += ro->src_scan_len;
    dst += ro->dst_scan_len;
  }
}


/*
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 *
 *                     some test remap functions
 *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 */

#ifdef REMAP_TEST

void test_8to32_(RemapObject *);

static RemapFuncDesc remap_test_list[] = {

};

/*
 * returns chained list of modes
 */
RemapFuncDesc *remap_test()
{
  int i;

  for(i = 0; i < sizeof(remap_test_list) / sizeof(*remap_test_list) - 1; i++) {
    remap_test_list[i].next = remap_test_list + i + 1;
  }

  return remap_test_list;
}


#endif


