/*
  1804941811 <- last update (yymmddhh'')
     _____________________________________________________________
    /                                                             \
    | Module:       display.cpp - code for explib's display classes
    \_____________________________________________________________/

    Description:    this implements the methods of the expansion libraries
                    display classes.

    Refers to:      display.h
    Used by:        everywhere
    Uses:           see #include

    Contents:
        see display.h

    Surveillance:

        only partially PC tracking, not in very time critical functions.

    Local Terminology:

        Abbrev. Meaning
        ------- ------------------------
        [spd]   this instruction is highly system performance dependent,
                on slower machines it may not work correctly.


    Developers: ID: Name:
    =========== --- -----
                JTH Juergen Thumm
  _____________________________________/VERSION HISTORY\____________________
 /Date_ Version Who What____________________________________________________\
 yymmdd ------- --- ---------------------------------------------------------
 930823 0.800   JTH split .h to .h and .cpp, added this header.
 940124             mousepos() added
 940130             loadcol()
 940203             PixelDisplay::charsize[]
 940302             AmigaDisplay
 940405             AmDisplay::AdjustPosn's debug & cleanup
 940414             AmDisplay: horizontal scrolling support
 940418             AmDisplay::AdjustPosn's WAIT search bug fix,
                    now ALWAYS using one more line in each section even
                    if no soft scrolling used to avoid case handling fuzz
 941030             PixelDisplay::plot/readpix: _ and check() deactivated
                    for performance reasons
 941115             WIN16 default system palette now has default colors.
 941125             PixDisp now uses RGB if WIN16 palette creation fails,
                    instead of hanging program due to ::open bail out.
 941127             Bug report: on 386 with 256 color VGA loadcolor()
                    crashed. Replaced UpdateColor by RealizePalette,
                    as used in WIN 3.1 book. Furthermore, WIN16 sys
                    palette is now just CREATED in ::open but by
                    default NOT used (USE_RGB set by default),
                    i.e. loadcolor() by default does not the critical
                    part of updating system colors.
                    PIX_SWAPRB no longer set by default, 'cause this
                    is just a bug fix for my Spea Truecolor Driver.
                    Code cleanup, replaced several check()'s by
                    situation dependent checks, WIN_CREF macro,
                    PD::plot now uses palette if USE_RGB not set.
                    PD::rectfill now using own Bitmap for plain areas.
 941211             PD now uses palette by default.
 941215             completed inlining of explicite inlines:
                        FrameBuffer::checkbnd,get,put.
                        PD::checkbnd.
 941216             all 'int' changed to cpix or pcol where needed.
 941221             PD::move/draw now use native WIN16 'MoveTo'/'DrawTo'.
 950325             PD: now using ColorManager
 950326             WIN_CREF replaced by PD::WinCol(pcol).
                    Found out that Windows also has stupid byteorder
                    for the colors. Adapted mapping functions accordingly.
 950402             PixelDisplay ctr: hwfrm/hwsize zero-init.
 950416             PD::lineby, PD::unline
 950418             PD::goPalette system color init fix: now initializing
                    all system palette entries with PC_NOCOLLAPSE.
 950503             ifndef ZOTH added.
 951216             cman: added nstatcols. this is then always added
                    to the result for sysmaxcols() and sysindexof().
                    PixelDisplay now creates additional color entries
                    in the Windows logical palette which map 1:1 to the
                    static system colors. These colors are NOT touched
                    by the colormanager and serve only the improvement
                    of blitting performance, as every logical color index
                    of PD's logical Windows palette is then mapped 1:1
                    into the physical system palette.
*/

#   include "expdef.h"
#   include "explib.h"
#   include "display.h"
#   include <limits.h>

//  ModInit(display_o);

//  Windows has 20 static colors, from which we will map the lower
//  10 ones into our own logical palette, to improve blitting performance.
#   define NLOWSTATCOLS 10

//  ----------------- COLOR MANAGER -----------------------------

ColorManager::ColorManager(ushort numberOfStaticColors)
 :  SYSMAXCOLS(236) // Windows Palette Manager: 236 user colors available
{
    truecol   = 0;  // palette mode by default
    nstatcols = numberOfStaticColors;
    reset();
}

ushort ColorManager::sysmaxcols(void)
{
    if(truecol)
        return  255U;

    return SYSMAXCOLS + nstatcols;
}

short ColorManager::colorremain(void)
{
    if(truecol)
        return  255U;

    return  (short)SYSMAXCOLS - (short)isysfree;
}

ushort ColorManager::sysindexofunused(void)
{
    return isysfree + nstatcols;
}

void ColorManager::reset(void)
{
    memset(map_pdsys, 0xFF, sizeof(map_pdsys));
    memset(sysrgb, 0, sizeof(sysrgb));
    isysfree = 0;
}

rcode ColorManager::loadpalette(ushort ncolors, ushort* palette)
{
    reset();    // drop old palette, if any

    ushort icolor=0;
    while(ncolors--)
        loadcol(icolor++, *palette++);

    return OK;
}

rcode ColorManager::goTrueColor(ushort ncolors, ushort* palette)
{
    truecol = 1;
    return loadpalette(ncolors, palette);
}

rcode ColorManager::goPalette(ushort ncolors, ushort* palette)
{
    truecol = 0;
    return loadpalette(ncolors, palette);
}

ushort ColorManager::sysindexof(ushort icolor)
{
    if(truecol)
    {
       perr(" CMan: called sysindexof() in truecolor mode");
       return 0;
    }

    if(icolor > 0xFFU) {SysErr(2503951141); return 0;}

    if(map_pdsys[icolor] >= isysfree)
    {
       perr(" CMan: color %d illegal isys %d",icolor,map_pdsys[icolor]);
       return 0;
    }

    // dmsg(" CMan: get %d isys %d rgb %lxh",
    //   icolor,map_pdsys[icolor],sysrgb[map_pdsys[icolor]]);

    return  map_pdsys[icolor] + nstatcols;
}

ulong ColorManager::rgb32of(ushort icolor)
{
 if(icolor > 0xFFU) {SysErr(2503950957); return 0x00FFFFFFUL;}

    if(truecol)
    {
        if(map_pdsys[icolor] != 0)
        {
            perr(" rbg32of: undefined truecolor %u", icolor);
            return  0;
        }

        return  sysrgb[icolor];
    }

 if(map_pdsys[icolor] >= isysfree)
 {
    perr("rgb32of: access to undefined color code %u", icolor);
    return 0x00FFFFFFUL;
 }

    return  sysrgb[map_pdsys[icolor]];
}

ushort ColorManager::rgb16of(ushort icolor)
{
    return  col32to16(rgb32of(icolor));
}

ulong ColorManager::colordiff(ulong rgb1, ulong rgb2)
{
 if(rgb1 > 0x00FFFFFFUL)    {SysErr(2503951019); return 0;}
 if(rgb2 > 0x00FFFFFFUL)    {SysErr(2503951020); return 0;}

    short r1,r2,g1,g2,b1,b2;

    splitrgb(rgb1,(ushort)r1,(ushort)g1,(ushort)b1);
    splitrgb(rgb2,(ushort)r2,(ushort)g2,(ushort)b2);

    return  (ulong)(abs(r1-r2)+abs(g1-g2)+abs(b1-b2));
}

rcode ColorManager::loadcol(ushort icolor, ushort rgb)
{
 if(icolor > 0xFF)  {SysErr(2303951925); return INTERNAL;}
 if(rgb > 0xFFF)    {SysErr(2303951926); return INTERNAL;}

    rcode rc = OK;

    ulong lrgb = col16to32(rgb);

    if(truecol)
    {
        map_pdsys[icolor]   = 0;    // mark as defined
        sysrgb[icolor]      = lrgb;
        return OK;
    }

    short iclosest  = -1;
    ulong cdclosest = ULONG_MAX;
    ulong cdiff;

    // over sysrgb
    for(ushort isys=0; isys<isysfree; isys++)
    {
        // if inRGB matches sysrgb[isys]
        if(sysrgb[isys] == lrgb)
            break;

        // remember closest color's index
        if(iclosest == -1)
            iclosest = isys;
        else
        {
            cdiff = colordiff(sysrgb[isys],lrgb);
            if(cdiff < cdclosest)
            {
                iclosest  = isys;
                cdclosest = cdiff;
            }
        }
    }

    if(isys >= isysfree)
    {
        // no match found:
        if(isysfree < SYSMAXCOLS)
        {
            // add new color
            sysrgb[isysfree]    = lrgb;
            map_pdsys[icolor]   = isysfree;
            isysfree++;

            // dmsg(" CMan %d isys %d rgb %lxh", icolor, map_pdsys[icolor], lrgb);

            // update Window's palette:
            rc  = SPECIAL;  // force syspal update
        }
        else
        {
            // color table full: take best match

            // we MUST have found a 'close' color
            if(iclosest < 0)    {SysErr(2303951929); return INTERNAL;}
            if(iclosest >= isysfree)    {SysErr(2303951930); return INTERNAL;}

            map_pdsys[icolor]   = iclosest;
            // dmsg(" CMan %d bmatch %d rgb %lxh i/o %lxh",
            //   icolor, map_pdsys[icolor], sysrgb[iclosest], lrgb);
        }
    }
    else
    {
        // 1:1 system color match found: take index
        map_pdsys[icolor]   = isys;
        // dmsg(" CMan reuse isys %d for %d", isys, icolor);
    }

    return rc;
}

ulong ColorManager::col16to32(ushort rgb)
{
    ushort r = ((rgb >> 4) & 0xF0);
    ushort g = ((rgb     ) & 0xF0);
    ushort b = ((rgb << 4) & 0xF0);

    return  ((ulong)b<<16)|((ulong)g<<8)|(ulong)r;
}

ushort ColorManager::col32to16(ulong rgb)
{
 if(rgb > 0x00FFFFFFUL) {SysErr(2503951017); return 0xFFFU;}
    ushort r,g,b;
    splitrgb(rgb,r,g,b);
    return (r<<4)|g|(b>>4);
}

//  ----------------- FRAME BUFFER ------------------------------

#ifdef  WIN16

rcode FrameBuffer::open(cpix xwidth, cpix xheight)
{_
    if(xheight > iadrmsk + 1)
    {
        perr("height %ld is too large for FrameBuffer", height);

        return  INDATA;
    }

    virtwidth   = xwidth;
    height      = xheight;  // limited by ladr[] size

    //  round width to next power of 2
    //  and store result in iwidth:

    for(iwbits=0; iwbits < 12; iwbits++)
    {
        iwidth  = 1UL << iwbits;

        if(iwidth >= virtwidth) break;
    }

    if(iwbits == 12)
    {
        perr("can't accept this FrameBuffer width");
        return  INDATA;
    }

    iwmask  = iwidth - 1UL;

    if(!(mem = (uchar HUGE *)farcalloc(height, iwidth)))
    {   MemErr(906941542);  return MEMORY;  }

    //  init line adress buffer

    for(ushort i=0; i<height; i++)

        ladr[i & iadrmsk] = (uchar FAR *)(mem + ((ulong)i << iwbits));

    return  OK;
}

void FrameBuffer::close()
{_
    if(mem) farfree(mem);
    mem = 0;
}

void FrameBuffer::clear(pcol c)
{_
 ulong  size = iwidth * height;
 uchar  HUGE *bp = mem;
 uchar  cb = (uchar)c;

    if(!mem)    {SysErr(709941605); return;}

    for(ulong i=0; i<size; i++)

        *bp++ = cb;
}

inline rcode FrameBuffer::checkbnd(cpix x, cpix y)
{
    if(     x < 0 || x >= virtwidth
        ||  y < 0 || y >= height    )
    {
        perr("graphical boundaries violated at 906941614");
        return FAILED;
    }

    return OK;
}

//  in here, just MINIMUM checks are performed
//  to avoid memory crashes.
//  PixelDisplay::plot/readpix is responsible for
//  complete checks of x and y.

/*
inline rcode FrameBuffer::put(cpix x, cpix y, pcol c)
{
    if((ulong)y >= (ulong)height)   return  FAILED;

    *(mem + ((ulong)y << iwbits) + ((ulong)x & iwmask)) = (uchar)c;

    return  OK;
}
*/

pcol FrameBuffer::getold(cpix x, cpix y)
{
    if((ulong)y >= (ulong)height)   return  FAILED;

    return  *(mem + ((ulong)y << iwbits) + ((ulong)x & iwmask));
}

rcode FrameBuffer::rectfill(cpix x1, cpix y1, cpix x2, cpix y2, pcol c)
{_
 uchar col = (uchar)c;

    if(checkbnd(x1,y1)) {SysErr(1409941148); return FAILED;}
    if(checkbnd(x2,y2)) {SysErr(1409941149); return FAILED;}

    // sort start/end coords

 cpix h,cx,cy;

    if(x2 < x1) {h=x1; x1=x2; x2=h;}
    if(y2 < y1) {h=y1; y1=y2; y2=h;}

 uchar FAR *lbp;

    for(cy=y1; cy <= y2; cy++)
    {
        // fill a line of the rectangle

        lbp = ladr[(ushort)cy & iadrmsk] + x1;

        for(cx=x1; cx <= x2; cx++)

            *lbp++  = (uchar)col;
    }

/*
 uchar HUGE *lbp;

    for(cy=y1; cy <= y2; cy++)
    {
        // fill a line of the rectangle

        lbp = mem + ((ulong)cy << iwbits) + x1;

        for(cx=x1; cx <= x2; cx++)

            *lbp++  = col;
    }
*/

    return  OK;
}

#endif

ushort PixelDisplayDA::SetWinMode(uchar x)
{
    if(x & WINM_TRUECOLOR)
    {
        // truecolor is only to be set using goTrueColor()!
        perr("PixelDisplayDA can't set truecolor");
        return pd->winmodes;
    }

    return pd->winmodes |= x;
}

ushort PixelDisplayDA::ClrWinMode(uchar x)
{
    return pd->winmodes &= 0xFFFFU ^ x;
}

//|| PixDisplCode
PixelDisplay::PixelDisplay()
 :  cman(NLOWSTATCOLS)
{_
#ifdef  AMIGA
    scr=0; win=0; rport=0; vport=0;
#endif

    frozen=0;
    charsize[0] = charsize[1] = 8;

#ifdef  WIN16
    cpos[0] = cpos[1] = 0;
    hlogpal = horgpal = horgbackpal = 0;

    winmodes    = 0;    // uses palette by default
#endif

    col         = 1;
    ibackcol    = 0;
    cdrawmode   = NORMAL;

    hbmpback    = 0;
    hdcfront    = 0;
    hdcback     = 0;
    hdccur      = 0;

    memset(hwfrm, 0, sizeof(hwfrm));
    memset(hwsize, 0, sizeof(hwsize));
}

PixelDisplay::~PixelDisplay()
{
}

#ifdef  AMIGA
void PixelDisplay::db_swap()
{_
    tmp.scr=scr;        scr=other.scr;      other.scr=tmp.scr;
    tmp.win=win;        win=other.win;      other.win=tmp.win;
    tmp.rport=rport;    rport=other.rport;  other.rport=tmp.rport;
    tmp.vport=vport;    vport=other.vport;  other.vport=tmp.vport;
}

void PixelDisplay::freeze()
{_
    ifn(copt & PIXD_DOUBLEBUF)  return; // ignore if no dbuffering

    db_swap();
    frozen = 1;
}

void PixelDisplay::unfreeze()
{_
    ifn(copt & PIXD_DOUBLEBUF)  return; // ignore if no dbuffering

    ScreenToFront(scr);
    frozen = 0;
//  ScreenToBack(other.scr);
}

void PixelDisplay::msg(const char *str,...)
{_
 char buf[320];
 cpix cs[2];
 va_list arg_ptr;

    if(check()) return;

  va_start(arg_ptr, str);

    vsprintf(buf, str, arg_ptr);

    cs[0]   = rport->cp_x;
    cs[1]   = rport->cp_y;

    SetAPen(rport, ncol-1);
    Move(rport, 8, 10);

    Text(rport, (STRPTR)buf, strlen(buf));

    SetAPen(rport, col);
    Move(rport, cs[0], cs[1]);

  va_end(arg_ptr);
}
#endif

ushort PixelDisplay::DefPalette[32] =
{
    0x000, 0xf00, 0xf00, 0xf00, 0xf00, 0xf00,   // 0..5
    0x0f0, 0x0f0, 0x0f0, 0x0f0, 0x0f0, 0x0f0,   // 6..11
    0x00f, 0x00f, 0x00f, 0x00f, 0x00f, 0x00f,   // 12..17
    0xff0, 0xff0, 0xff0, 0xff0, 0xff0, 0xff0,   // 18..23
    0xd00, 0xe00, 0xf00, 0xf04, 0xf08, 0xf0c,   // 24..29
    0xaaa, 0xccc                                // 30,31
};

/*
ushort PixelDisplay::DefPalette[32] =
{
    0x000, 0xfff, 0xf00, 0x0f0, 0x00f, 0xff0,   // 0..5
    0x0ff, 0x080, 0x070, 0x060, 0x050, 0x040,   // 6..11
    0x130, 0x220, 0x310, 0xf00, 0x500, 0xff0,   // 12..17
    0x700, 0x800, 0x900, 0x0f0, 0xb00, 0xc00,   // 18..23
    0xd00, 0xe00, 0xf00, 0xf04, 0xf08, 0xf0c,   // 24..29
    0xaaa, 0xccc                                // 30,31
};
*/

rcode PixelDisplay::open(

        cpix width, cpix height, ushort depth,

        ulong opt,

        ushort  InitNCol,
        ushort *InitPalette

        )
{_
#ifdef  AMIGA
 struct NewScreen nscr =
 {
    0,0, width,height, depth,
    1,0, 0, CUSTOMSCREEN,
    NULL,0, NULL,NULL
 };

 struct NewWindow nwin =
 {
    0,0, width,height, 3,0,
    MOUSEMOVE|MOUSEBUTTONS, BORDERLESS|REPORTMOUSE|RMBTRAP,
    NULL, NULL, 0,
    NULL, NULL, 0,0,0,0, CUSTOMSCREEN
 };
#endif

    copt = opt;

#ifdef  WIN16
    // reserve space for window title & border:
    width   -=  2;
    height  -= 16;
#endif

    hwfrm[0] = 0;
    hwfrm[1] = 0;
    hwfrm[2] = width  - 1;  // NEW /080994: absolute coords
    hwfrm[3] = height - 1;  // INCLUDING right/bottom

    hwsize[0] = width;  // use THIS as replacement for old
    hwsize[1] = height; // hwfrm[2]/[3] references

    ncol    = 1 << depth;

    if(ncol > PIXD_MAXCOLORS)
    {   perr("too many display colors");    return  INDATA; }

    if(!InitNCol)
    {
        InitNCol    = min(ncol, sizeof(DefPalette) / sizeof(ushort));
        InitPalette = DefPalette;
    }

#ifdef AMIGA
    if(width  >= 640)   ifn(opt & PIXD_NOHIRES) nscr.ViewModes  |= HIRES;

    if(height >= 400)   ifn(opt & PIXD_NOLACE)  nscr.ViewModes  |= LACE;

    if(!(scr = OpenScreen(&nscr)))  return MEMORY;
    nwin.Screen = scr;
    if(!(win = OpenWindow(&nwin)))
        {   CloseScreen(scr);   return MEMORY;  }

    rport   = win->RPort;
    vport   = &scr->ViewPort;

    if(opt & PIXD_DOUBLEBUF)
    {
        db_swap();

        nscr.Type   |= SCREENBEHIND;

        if(!(scr = OpenScreen(&nscr)))
        {   CloseWindow(other.win);
            CloseScreen(other.scr); return MEMORY;  }

        nwin.Screen = scr;

        if(!(win = OpenWindow(&nwin)))
        {   CloseScreen(scr);
            CloseWindow(other.win);
            CloseScreen(other.scr); return MEMORY;  }

        rport   = win->RPort;
        vport   = &scr->ViewPort;

        db_swap();
    }

#endif

#ifdef  WIN16

    horgpal     = 0;
    horgbackpal = 0;
    hlogpal     = 0;

    // make a copy of the global window handle to work with:
    win = explib_root.ibase.mainwin;

    // create frame buffer
    if(fbuf.open(width,height))
    {
        BeginPaint(win, &ps);   // this avoids
        EndPaint(win, &ps);     // deadlock

        return  MEMORY;
    }

    // open device context
    if(!(hdcfront = BeginPaint(win, &ps)))
        {   MemErr(609941726);  return MEMORY;  }
    hdccur  = hdcfront;

    // get width & height of chars of system's fixed font:
    charsize[0] = 16;   // as a
    charsize[1] = 16;   // default

    HGDIOBJ hobj = GetStockObject(SYSTEM_FIXED_FONT);

    if(hobj)
    {
     TEXTMETRIC tm;

        SelectObject(hdcfront, hobj);

        if(GetTextMetrics(hdcfront, (TEXTMETRIC FAR *)&tm))
        {
            charsize[0] = tm.tmAveCharWidth;
            charsize[1] = tm.tmHeight;
        }
        else
            perr("GetTextMetrics failed, using defaults");

        DeleteObject(hobj);
    }

    // coordinates are counted in pixels:
    SetMapMode(hdcfront, MM_TEXT);

    // goPalette expects hdc to be set. this is the case now.
    if(goPalette(InitNCol, InitPalette))
    {
        perr("can't create palette");
        return FAILED;
    }

#endif

#ifdef  AMIGA
    if(InitPalette)
      loadcolors(InitNCol, InitPalette);
    else
      loadcolors(min(ncol, sizeof(DefPalette) / sizeof(ushort)), DefPalette);
#endif

    return OK;
}

void PixelDisplay::killPalette(void)
{
#ifdef  WIN16
    if(hdcback  && horgbackpal) SelectPalette(hdcback , horgbackpal, FALSE);
    if(hdcfront && horgpal)     SelectPalette(hdcfront, horgpal, FALSE);
    if(hlogpal) DeleteObject(hlogpal);
#endif
    horgpal     = 0;
    horgbackpal = 0;
    hlogpal     = 0;
}

// Close Display:

void PixelDisplay::close()
{_
    killPalette();

#ifdef  AMIGA
    if(win) CloseWindow(win);
    if(scr) CloseScreen(scr);

    if(copt & PIXD_DOUBLEBUF)
    {
        db_swap();
        if(win) CloseWindow(win);
        if(scr) CloseScreen(scr);
        rport = 0;
        vport = 0;
        win = 0;
        scr = 0;
        db_swap();
    }

    rport = 0;
    vport = 0;
    win = 0;
    scr = 0;

#endif

#ifdef  WIN16
    freeBackMap();      // if any
    EndPaint(win, &ps); // close device context
    hdcfront = 0;
    hdccur   = 0;
    win = (HWND)0;
    fbuf.close();
#endif
}

#ifdef  WIN16
COLORREF PixelDisplay::WinCol(pcol c)
{
    if(winmodes & WINM_TRUECOLOR)
        return  cman.rgb32of(c);

    if(!hlogpal)    {SysErr(2603951047); return 0;}

    return  PALETTEINDEX(cman.sysindexof(c));
}
#endif

rcode PixelDisplay::goTrueColor(ushort ncolors, ushort* palette)
{
    if(check())
    {
        perr("goTrueColor w/o open() called");
        return FAILED;
    }

    killPalette();  // if already in truecol mode, does nothing

#ifdef  WIN16
    RealizePalette(hdcfront);
#endif

    if(!ncolors)
    {
        // use default palette
        ncolors = min(ncol, sizeof(DefPalette) / sizeof(ushort));
        palette = DefPalette;
    }

    winmodes |= WINM_TRUECOLOR;

    return  cman.goTrueColor(ncolors, palette);
}

rcode PixelDisplay::goPalette(ushort ncolors, ushort* palette)
{
    if(check())
    {
        perr("goPalette w/o open() called");
        return FAILED;
    }

#ifdef  WIN16
    killPalette();  // just in case this is called multiple times in a row

    if(!ncolors)
    {
        // use default palette
        ncolors = min(ncol, sizeof(DefPalette) / sizeof(ushort));
        palette = DefPalette;
    }

    winmodes &= (~0U ^ WINM_TRUECOLOR);

    // load colors logically into mapper object.
    // NOTE that after this, logical source color indexes
    // are NOT identical with the system palette color indexes,
    // so they must always be queried using 'cman.sysindexof(col)'.
    // NOTE that sysindexof() always returns indexes >= NLOWSTATCOLS,
    // because we don't touch the indices below that in our
    // logical palette.
    cman.goPalette(ncolors, palette);

    // create a temporar source palette,
    // which will initially contain default colors.
    LOGPALETTE  *psrcpal;

    ifn(psrcpal = (LPLOGPALETTE)(new char[sizeof(LOGPALETTE)
            + sizeof(PALETTEENTRY) * cman.sysmaxcols()]))
    {   MemErr(2909940939); return MEMORY;  }

    psrcpal->palVersion     = 0x300;
    psrcpal->palNumEntries  = cman.sysmaxcols();

    ushort r,g,b;
    ulong  rgb;

    // first, reset ALL system palette colors to a defined state
    for(ushort isys=0; isys<cman.sysmaxcols(); isys++)
    {
        if(isys < NLOWSTATCOLS)
        {
            // init static color portion of palette.
            // this will collapse and not be used by pixeldisplay.
            // but it will make sure that the logical color
            // indexes that we use will map 1:1 to the physical
            // system color indexes.
            rgb = GetSysColor(isys); // get static Windows color
            psrcpal->palPalEntry[isys].peRed    = GetRValue(rgb);
            psrcpal->palPalEntry[isys].peGreen  = GetGValue(rgb);
            psrcpal->palPalEntry[isys].peBlue   = GetBValue(rgb);
            psrcpal->palPalEntry[isys].peFlags  = 0; // provoke collapse
        }
        else
        {
            // init dynamic portion of palette.
            // subsequent loadcol() calls will fill these entries.
            psrcpal->palPalEntry[isys].peRed    = 0;
            psrcpal->palPalEntry[isys].peGreen  = 0;
            psrcpal->palPalEntry[isys].peBlue   = 0;
            psrcpal->palPalEntry[isys].peFlags  = PC_NOCOLLAPSE;
        }
    }

    // second, initially load the already defined colors
    for(ushort lpi=0; lpi<ncolors; lpi++)
    {
        isys = cman.sysindexof(lpi); // i.e. index NLOWSTATCOLS ff.
        cman.splitrgb(cman.rgb32of(lpi),r,g,b);

        psrcpal->palPalEntry[isys].peRed    = r;
        psrcpal->palPalEntry[isys].peGreen  = g;
        psrcpal->palPalEntry[isys].peBlue   = b;
        psrcpal->palPalEntry[isys].peFlags  = PC_NOCOLLAPSE;

        // dmsg(" PD set isys %d rgb %lxh", lpi, defcol);
    }

    // create logical palette from source palette
    ifn(hlogpal = CreatePalette(psrcpal))
    {
        perr("can't create palette");
        goTrueColor();
    }
    else
    {
        ifn(horgpal = SelectPalette(hdcfront, hlogpal, FALSE))
            perr("can't select palette");
        else
        {
            if(hdcback)
                ifn(horgbackpal = SelectPalette(hdcback, hlogpal, FALSE))
                    perr("can't select backpalette");

            RealizePalette(hdcfront);
            // dmsg("%d = RealizePalette %d", n, cman.sysmaxcols());
        }
    }

    // delete temporar source palette
    delete [] (char*)psrcpal;

#endif

    return OK;
}

void PixelDisplay::clear()
{_
    if(check()) return;

#ifdef  AMIGA
    ClearScreen(&scr->RastPort);
#else
    rectfill(hwfrm[0],hwfrm[1],hwfrm[2],hwfrm[3],ibackcol);
#endif
}

rcode PixelDisplay::color(pcol pen)
{
#ifdef  AMIGA
    if(rport)   SetAPen(rport, pen);
#endif

    col = pen;

    return OK;
}

rcode PixelDisplay::backcol(pcol pen)
{
#ifdef  AMIGA
    if(rport)   SetBPen(rport, pen);
#endif

#ifdef  WIN16
    SetBkColor(hdccur, WinCol(pen));
#endif

    ibackcol    = pen;

    return OK;
}

rcode PixelDisplay::move(cpix x, cpix y)
{
#ifdef  AMIGA
    if(rport)   Move(rport, x, y);
#endif

#ifdef  WIN16
    cpos[0] = x;
    cpos[1] = y;

    MoveTo(hdccur, x, y);
#endif

    return OK;
}

rcode PixelDisplay::plot(cpix x, cpix y, pcol xcol)
{
    //  NO 'AVAILABLE' CHECKS PERFORMED,
    //  for performance reasons.

    if(xcol != -1)  color(xcol);

#ifdef  AMIGA
    WritePixel(rport, x, y);
#endif

#ifdef  WIN16
    // put color code to frame buffer

    if(checkbnd(x,y))   return FAILED;

    fbuf.put(x,y,col);  // ignore result status

#ifndef NO_MEMTRACK
//  if(fbuf.getold(x,y) != col) SysErr(1217941757); // redundant check
#endif

    // map color code to real color and plot it

    if(hdccur == hdcfront && (winmodes & WINM_SETPIXSWAPRB))
    {
        ulong rgb = cman.rgb32of(col);

        SetPixel(hdccur, x, y,
                ((rgb & 0xFFUL)<<16)|(rgb & 0xFF00UL)|(rgb >> 16));
    }
    else
        SetPixel(hdccur, x, y, WinCol(col));
#endif

    return OK;
}

rcode PixelDisplay::plotby(cpix x, cpix y, pcol xcol)
{
    // NO color selection here - current color code is NOT changed!

    if(checkbnd(x,y))   return FAILED;

    // plot directly to display, bypassing framebuffer:
    if(hdccur == hdcfront && (winmodes & WINM_SETPIXSWAPRB))
    {
        ulong rgb = cman.rgb32of(xcol);

        SetPixel(hdccur, x, y,
                ((rgb & 0xFFUL)<<16)|(rgb & 0xFF00UL)|(rgb >> 16));
    }
    else
        SetPixel(hdccur, x, y, WinCol(xcol));

    return OK;
}

rcode PixelDisplay::unplot(cpix x, cpix y)
{
    if(checkbnd(x,y))   return FAILED;

    //  unplot a pixel set by plotby():
    pcol dcol = fbuf.get(x,y);

    if(hdccur == hdcfront && (winmodes & WINM_SETPIXSWAPRB))
    {
        ulong rgb = cman.rgb32of(dcol);

        SetPixel(hdccur, x, y,
                ((rgb & 0xFFUL)<<16)|(rgb & 0xFF00UL)|(rgb >> 16));
    }
    else
        SetPixel(hdccur, x, y, WinCol(dcol));

    return OK;
}

rcode PixelDisplay::draw(cpix x, cpix y)
{
    if(check()) {SysErr(908931728); return FAILED;}

#ifdef  AMIGA
    Draw(rport, x, y);
#endif

#ifdef  WIN16

    // draw line from cpos to (x,y) from hand
    // to keep virtual framebuffer updated:

    long sx = cpos[0];
    long sy = cpos[1];
    long ex = x;
    long ey = y;

    long dx = ex - sx;
    long dy = ey - sy;

    long adx    = abs(dx);
    long ady    = abs(dy);
    long amax   = max(adx, ady);

    long cx,cy;

    if(amax)
    {
        for(long i=0; i<amax; i++)
        {
            cx  = sx + i * dx / amax;
            cy  = sy + i * dy / amax;

            if(checkbnd((cpix)cx,(cpix)cy)==OK)

                fbuf.put((cpix)cx, (cpix)cy, col);
        }

        //  draw native windows line:

        HPEN    hpen;
        HANDLE  hold;

        if(hpen = CreatePen(PS_SOLID, 1, WinCol(col)))
        {
            if(hold = SelectObject(hdccur, hpen))
            {
                LineTo(hdccur, x, y);   // end point NOT included!

                SelectObject(hdccur, hold);
            }

            DeleteObject(hpen);
        }
    }

    plot(x,y);  // plot single or end point

#endif
    return OK;
}

pcol PixelDisplay::scanline(cpix start[2], cpix end[2],
    pcol forcolors[2], cpix firsthit[2], cpix *prehitpos)
{
    if(check()) {SysErr(908931728); return FAILED;}

    // scan from start to end for first matching color or display border

    long sx = start[0];
    long sy = start[1];
    long ex = end[0];
    long ey = end[1];

    long dx = ex - sx;
    long dy = ey - sy;

    long adx    = abs(dx);
    long ady    = abs(dy);
    long amax   = max(adx, ady);

    long cx=-1,cy=-1,ox,oy;
    pcol col,lowcol=forcolors[0],hicol=forcolors[1];

    if(amax)
    {
        for(long i=0; i<amax; i++)
        {
            ox  = cx;   // store for use
            oy  = cy;   // as prehitpos

            cx  = sx + i * dx / amax;
            cy  = sy + i * dy / amax;

            if(checkbnd((cpix)cx,(cpix)cy)==OK)
            {
#ifdef  AMIGA
                col = readpix(cx,cy);
#else
                col = fbuf.get(cx,cy);  // fast access
#endif
                if(col >= lowcol && col <= hicol)
                {
                    // found a matching color:

                    firsthit[0] = cx;
                    firsthit[1] = cy;

                    if(prehitpos)
                    {
                        // return also pos'n before match:

                        if(ox != -1)
                        {
                            prehitpos[0]    = ox;
                            prehitpos[1]    = oy;
                        }
                        else
                        {
                            prehitpos[0]    = cx;
                            prehitpos[1]    = cy;
                        }
                    }

                    return  col;
                }
            }
            else
                return -1;  // illegal coord reached
        }
    }

    // lines with just one pixel are NOT supported
    // and return always -1

    return  -1; // nothing found or illegal coords
}

#ifdef  AMIGA
// scan all messages currently in msgqueue until
// a) queue's empty or
// b) required class is found

IntuiMessage* PixelDisplay::scanMsg(Window* win, ulong mclass)
{_
 IntuiMessage* imsg;

    while(1)
    {
        if(!(imsg = (struct IntuiMessage *)GetMsg(win->UserPort)))
            return 0;

        if(imsg->Class == mclass)   break;

        ReplyMsg((struct Message *)imsg);   // throw away all unawaited msg'es
    }

    return imsg;
}

rcode PixelDisplay::testbutton()
{_
 IntuiMessage *imsg;
 struct Window *fwin;

    if(check()) {SysErr(908931729); return FAILED;}

    fwin = win;
    if((copt & PIXD_DOUBLEBUF) && frozen)   fwin = other.win;

    if(!(imsg = scanMsg(win, MOUSEBUTTONS)))        return FAILED;

    // remember mouse position:
    imousepos[0]    = imsg->MouseX;
    imousepos[1]    = imsg->MouseY;

    lmsg.mclass = imsg->Class;
    lmsg.mcode  = imsg->Code;
    ReplyMsg((struct Message *)imsg);

    return
        (lmsg.mclass == MOUSEBUTTONS && lmsg.mcode == SELECTDOWN) ?
            OK : FAILED;
}

short PixelDisplay::whichbutton()
{
 short r=0;

    if(lmsg.mclass == MOUSEBUTTONS)
    {
        if(lmsg.mcode == IECODE_LBUTTON)    r |= 1;
        if(lmsg.mcode == IECODE_RBUTTON)    r |= 2;
    }

    return r;
}
#endif

rcode PixelDisplay::mousepos(cpix pos[2])
{
    pos[0]  = imousepos[0];
    pos[1]  = imousepos[1];

    return  OK;
}

#ifdef  AMIGA
rcode PixelDisplay::waitbutton()
{_
 IntuiMessage *imsg;
 struct Window *fwin;

    if(check()) {SysErr(1208930003); return FAILED;}

    fwin = win;
    if((copt & PIXD_DOUBLEBUF) && frozen)   fwin = other.win;

    while(1)
    {
        if(!(imsg = scanMsg(win, MOUSEBUTTONS)))    continue;

        // remember mouse position:
        imousepos[0]    = imsg->MouseX;
        imousepos[1]    = imsg->MouseY;

        lmsg.mclass = imsg->Class;
        lmsg.mcode  = imsg->Code;
        ReplyMsg((struct Message *)imsg);

        if(lmsg.mcode == SELECTDOWN)    break;
    }

    while(1)
    {
        if(!(imsg = scanMsg(win, MOUSEBUTTONS)))    continue;

        // remember mouse position:
        imousepos[0]    = imsg->MouseX;
        imousepos[1]    = imsg->MouseY;

        lmsg.mclass = imsg->Class;
        lmsg.mcode  = imsg->Code;
        ReplyMsg((struct Message *)imsg);

        if(lmsg.mcode == SELECTUP)  break;
    }

    return OK;
}
#endif

rcode PixelDisplay::line(cpix x1, cpix y1, cpix x2, cpix y2, pcol xcol)
{_
    if(check()) {SysErr(908931730); return FAILED;}

    if(xcol != -1)  color(xcol);

#ifdef  AMIGA
    Move(rport, x1, y1);
    Draw(rport, x2, y2);
#endif

#ifdef  WIN16
    move(x1,y1);
    draw(x2,y2);
#endif

    return OK;
}

rcode PixelDisplay::lineby(cpix x1, cpix y1, cpix x2, cpix y2, pcol xcol)
{
    if(check()) {SysErr(1604951801);    return FAILED;}

    if(xcol != -1)  color(xcol);

#ifdef  WIN16

    //  just draw a native windows line:

    MoveTo(hdccur, x1, y1);

    HPEN    hpen;
    HANDLE  hold;

    if(hpen = CreatePen(PS_SOLID, 1, WinCol(col)))
    {
        if(hold = SelectObject(hdccur, hpen))
        {
            LineTo(hdccur, x2, y2); // end point NOT included!

            SelectObject(hdccur, hold);
        }

        DeleteObject(hpen);
    }

    //  unplot single or end point:

    plotby(x2,y2,col);

#endif

    return OK;
}

rcode PixelDisplay::unline(cpix x1, cpix y1, cpix x2, cpix y2)
{
    if(check()) {SysErr(1604951806); return FAILED;}

#ifdef  WIN16

    //  un-plot all points of the line by hand

    long sx = x1;
    long sy = y1;
    long ex = x2;
    long ey = y2;

    long dx = ex - sx;
    long dy = ey - sy;

    long adx    = abs(dx);
    long ady    = abs(dy);
    long amax   = max(adx, ady);

    long cx,cy;

    if(amax)
    {
        for(long i=0; i<amax; i++)
        {
            cx  = sx + i * dx / amax;
            cy  = sy + i * dy / amax;

            if(checkbnd((cpix)cx,(cpix)cy)==OK)

                plotby( (cpix)cx, (cpix)cy,
                        fbuf.get((cpix)cx, (cpix)cy) );
        }
    }

    unplot(x2,y2);  // unplot single or end point

#endif

    return OK;
}

//  ----------- Text Output ------------------

rcode PixelDisplay::text(cpix x, cpix y, char *format, ...)
{_
 static uchar buf[200+2];
 va_list ap;

    va_start(ap, format);

    buf[200]    = 0xEE;     // mark as 'end of buffer'

    vsprintf(buf, format, ap);

    if(buf[200] != 0xEE)    // check for buffer overflow
    {   SysErr(2510941753); buf[200] = '\0';    }

    va_end(ap);

    return  text(x,y, (const char *)buf);
}

rcode PixelDisplay::text(const char *str)
{_
#ifdef  AMIGA
 cpix x=rport->cp_x,y=rport->cp_y;

    if(check()) {SysErr(908931731); return FAILED;}

    Move(rport, x, y+6);
    Text(rport, (STRPTR)str, strlen(str));
    Move(rport, x, y);
#endif

#ifdef  WIN16

    HGDIOBJ hold = 0;   // system-selected font
    HGDIOBJ hobj = GetStockObject(SYSTEM_FIXED_FONT);
    if(hobj)    hold = SelectObject(hdccur, hobj);

    SetTextColor(hdccur, WinCol(col));
    TextOut(hdccur, cpos[0], cpos[1], str, strlen(str));

    if(hold)    SelectObject(hdccur, hold); // re-select old font
    if(hobj)    DeleteObject(hobj);         // delete fixfont handle

#endif

    return OK;
}

rcode PixelDisplay::text(cpix x, cpix y, const char *str)
{_
#ifdef  AMIGA
    if(check()) {SysErr(908931732); return FAILED;}

    Move(rport, x, y+6);
    Text(rport, (STRPTR)str, strlen(str));
#endif

#ifdef  WIN16
    move(x,y);
    text(str);
#endif
    return OK;
}

rcode PixelDisplay::verttext(const char *str)
{_
#ifdef  AMIGA
 cpix x=rport->cp_x,y=rport->cp_y;
 short n=0;

    if(check()) {SysErr(1608931515); return FAILED;}

    while(*str)
    {   Move(rport, x, y + n++ * 8);
        Text(rport, (STRPTR)str++, 1);  }
#else

#endif
    return OK;
}

rcode PixelDisplay::verttext(cpix x, cpix y, const char *str)
{_
#ifdef  AMIGA
 short n=0;

    if(check()) {SysErr(1608931515); return FAILED;}

    while(*str)
    {   Move(rport, x, y + n++ * 8);
        Text(rport, (STRPTR)str++, 1);  }
#else

#endif
    return OK;
}

rcode PixelDisplay::text()
{_
#ifdef  AMIGA
    if(check()) {SysErr(908931733); return FAILED;}

    Move(rport, 10, rport->cp_y + 9);
#endif

#ifdef  WIN16
    move(10, cpos[1] + 9);
#endif

    return OK;
}

void PixelDisplay::drawmode(PIXD_DrawModes dm)
{
    cdrawmode = dm;

#ifdef  AMIGA
    if(dm == NORMAL)    SetDrMd(rport, JAM1);
    if(dm == NORMAL2)   SetDrMd(rport, JAM2);
    if(dm == INVERSE)   SetDrMd(rport, COMPLEMENT);
#endif

#ifdef  WIN16
    if(dm == NORMAL)    SetBkMode(hdccur, TRANSPARENT);
    if(dm == NORMAL2)   SetBkMode(hdccur, OPAQUE);
#endif
}

rcode PixelDisplay::loadcolors(short ncol, ushort *palette)
{_
 rcode rc=OK;

#ifdef  AMIGA
    LoadRGB4(vport, palette, ncol);

    if(copt & PIXD_DOUBLEBUF)
        LoadRGB4(other.vport, palette, ncol);
#else
    // load color list to virtual palette
    for(short i=0; i<ncol; i++)
        if(rc=loadcol(i, palette[i]))
            break;
#endif

    return rc;
}

bool globalBoolPDRealizedPalette = 0;

rcode PixelDisplay::loadcol(pcol num, ushort rgb)
{_
    if((ulong)num >= (ulong)ncol)
    {   SysErr(2909941551); return INTERNAL;    }

#ifdef  AMIGA
    SetRGB4(vport, num, (rgb>>8)&15, (rgb>>4)&15, rgb&15);
#endif

#ifdef  WIN16
    if(cman.loadcol((ushort)num, rgb) == SPECIAL)
    {
        // system palette update must be done

        if((winmodes & WINM_TRUECOLOR) || !hlogpal || !hdcfront)
            return  OK; // true color: don't update

        ushort  isys = cman.sysindexof((ushort)num);

        // dmsg(" PD update syscol %d with %xh",isys,rgb);

        ushort r = ((rgb >> 4) & 0xF0);
        ushort g = ((rgb     ) & 0xF0);
        ushort b = ((rgb << 4) & 0xF0);

        PALETTEENTRY pe;

        pe.peRed    = (uchar)r;
        pe.peGreen  = (uchar)g;
        pe.peBlue   = (uchar)b;
        pe.peFlags  = PC_NOCOLLAPSE;

        SetPaletteEntries(hlogpal, isys, 1, &pe);

        RealizePalette(hdcfront);
        globalBoolPDRealizedPalette = 1; // just for statistics
    }
#endif

    return OK;
}

rcode PixelDisplay::rectfill(cpix x1, cpix y1, cpix x2, cpix y2, pcol xcol)
{_
    if(xcol != -1)  color(xcol);

#ifdef  AMIGA
    RectFill(rport, x1,y1, x2,y2);
#endif

#ifdef  WIN16

    if(checkbnd(x1,y1)) return FAILED;
    if(checkbnd(x2,y2)) return FAILED;

    fbuf.rectfill(x1,y1,x2,y2,col); // fill rect in virtual frame buffer

    SetTextColor(hdccur, WinCol(col));

    HBRUSH  brush;

    if(brush = CreateSolidBrush(WinCol(col)))
    {
        RECT rect;

        rect.left   = x1;
        rect.top    = y1;
        rect.right  = x2 + 1;
        rect.bottom = y2;

        FillRect(hdccur, &rect, brush); // really paint a filled rectangle

        DeleteObject(brush);
    }
    else
        perr("rectfill: can't create brush");

#endif

    return OK;
}

//  --------------------------------------------

#ifndef ZOTH

//|| VecDisplCode
VectorDisplay::VectorDisplay(
    int vl,  int  vt, int  vw, int  vh,
    int hwl, int hwt, int hww, int hwh
    )
{_
    SetDimensions(vl, vt, vw, vh,  hwl, hwt, hww, hwh);
}

rcode VectorDisplay::SetDimensions(
    int vl,  int  vt, int  vw, int  vh,
    int hwl, int hwt, int hww, int hwh
    )
{_
    if(hwl != -1)   hwfrm[0] = hwl; // if -1, keep old value
    if(hwt != -1)   hwfrm[1] = hwt;
    if(hww != -1)   hwfrm[2] = hww;
    if(hwh != -1)   hwfrm[3] = hwh;

    infrm[0] =  vl; infrm[1] =  vt; infrm[2] =  vw; infrm[3] =  vh;

    vhdiv[0] = infrm[2] * 100 / hwfrm[2];   // calc width  divisor
    vhdiv[1] = infrm[3] * 100 / hwfrm[3];   // calc height divisor

    cs.v[0] = cs.v[1] = 0;  // do NOT use move() here!

    dmsg("vdisp:dim: %d %d %d %d, %d %d %d %d, %d %d",
        hwfrm[0], hwfrm[1], hwfrm[2], hwfrm[3],
        infrm[0], infrm[1], infrm[2], infrm[3],
        vhdiv[0], vhdiv[1]);

    return OK;
}

rcode VectorDisplay::move(int vx, int vy)
{_
 rcode r;

    dmsg("vdisp.move(%d,%d)",vx,vy);

    cs.v[0] = vx; cs.v[1] = vy;

    if(r=xform(vx, vy, cs.h[0], cs.h[1]))   return r;

    if(r=PixelDisplay::move(cs.h[0], cs.h[1]))  return r;

    dmsg("vdisp.cs now [%d %d %d %d]",cs.v[0],cs.v[1],cs.h[0],cs.h[1]);

    return OK;
}

rcode VectorDisplay::plot(int vx, int vy, int col)
{_
 rcode r;
 int hwc[2];

    if(r=xform(vx, vy, hwc[0], hwc[1]))     return r;

    if(r=PixelDisplay::plot(hwc[0], hwc[1], col))   return r;

    return OK;
}

int VectorDisplay::readpix(int vx, int vy)
{_
 rcode r;
 int hwc[2];

    if(r=xform(vx, vy, hwc[0], hwc[1]))     return r;

    return PixelDisplay::readpix(hwc[0], hwc[1]);
}

rcode VectorDisplay::draw(int vx, int vy)
{_
 int ht[2]; // hw target position

    if(xform(vx, vy, ht[0], ht[1]))
        return FAILED;

    PixelDisplay::draw(ht[0], ht[1]);

    // set gfx cursor to new position:
    cs.v[0] = vx; cs.v[1] = vy;
    cs.h[0] = ht[0]; cs.h[1] = ht[1];

    dmsg("draw:cs now [%d %d %d %d]",cs.v[0],cs.v[1],cs.h[0],cs.h[1]);

    return OK;
}

rcode VectorDisplay::line(int x1, int y1, int x2, int y2, int col)
{_
 rcode r;

    if(col != -1)
        if(r=PixelDisplay::color(col))  return r;

    if(r=move(x1, y1))  return r;

    if(r=draw(x2, y2))  return r;

    return OK;
}

rcode VectorDisplay::text(int vx, int vy, const char *str)
{_
 rcode r;

    if(r=move(vx,vy))   return r;

    if(r=PixelDisplay::text(str))   return r;

    return OK;
}

rcode VectorDisplay::verttext(int vx, int vy, const char *str)
{_
 rcode r;

    if(r=move(vx,vy))   return r;

    if(r=PixelDisplay::verttext(str))   return r;

    return OK;
}

rcode VectorDisplay::cin(int x, int y, int frm[4])
{_
    if(x < frm[0] || x >= frm[0] + frm[2])  return FAILED;
    if(y < frm[1] || y >= frm[1] + frm[3])  return FAILED;

    return  OK;
}

rcode VectorDisplay::xform(int vx, int vy, int &hx, int &hy)
{_
    if(cin(vx,vy, infrm))
    {
        hx = hwfrm[2]/2 + hwfrm[0];     // take middle of x
        hy = hwfrm[3]/2 + hwfrm[1];     // and  middle of y

        return FAILED;  // error: vx,vy were illegal
    }

    hx  = (vx - infrm[0]) * 100 / vhdiv[0] + hwfrm[0];
    hy  = (vy - infrm[1]) * 100 / vhdiv[1] + hwfrm[1];

    if(cin(hx,hy, hwfrm))
    {
        //SysErr(908931646);
        return FAILED;
    }

    return OK;
}

rcode VectorDisplay::xback(int hx, int hy, int &vx, int &vy)
{_
    if(cin(hx,hy, hwfrm))
    {
        vx = infrm[2]/2 + infrm[0];     // take middle of vx
        vy = infrm[3]/2 + infrm[1];     // and  middle of vy

        return FAILED;  // error: hx,hy were illegal
    }

    vx  = (hx - hwfrm[0]) * vhdiv[0] / 100 + infrm[0];
    vy  = (hy - hwfrm[1]) * vhdiv[1] / 100 + infrm[1];

    if(cin(vx,vy, infrm))
    {
        SysErr(2401941800); return FAILED;
    }

    return OK;
}

rcode VectorDisplay::xbacksize(int hx, int hy, int &vx, int &vy)
{_
    vx  = hx * vhdiv[0] / 100;
    vy  = hy * vhdiv[1] / 100;

    if(vx > infrm[2] || vy > infrm[3])  return FAILED;

    return OK;
}

rcode VectorDisplay::mousepos(int pos[2])
{_
 rcode rc;
 cpix ppos[2];

    if(rc=PixelDisplay::mousepos(ppos)) return rc;

    rc=xback(ppos[0], ppos[1], pos[0], pos[1]);

    return rc;
}

#endif

//  ---------------- structure displaying --------------------
/*
//|| SViewCode

StructView::~StructView()
{_
    if(vd)
    {
        vd->close();

        delete vd;
    }
}

// open display, draw the object list:

rcode StructView::open()
{_
 rcode r;

    vd = new VectorDisplay(0,0,dispcbnd[0]+10,dispcbnd[1]+10, 10,20,600,420);

    if(r=vd->PixelDisplay::open(((PixelDisplay::Options)1)))
    {
        perr("cannot open display");
        return r;
    }

    return redraw();
}

// add a single user object to the object list:

rcode StructView::addobj(char *id, void *adr, ...)
{_
 va_list arg_ptr;
 void   *dep;
 Obj    *obj;
 int    idx=0;
 int    dncnt=0;    // dependency number count in the parm list

  va_start(arg_ptr, adr);

    if(!(obj = new Obj))
    {   MemErr(908931309);  return MEMORY;  }

    obj->id = id; obj->adr = adr;

    // get all horizontal dependencies:

    while((dep = va_arg(arg_ptr, void *)) != ALE)
    {
        dncnt++;

        if(obj->ndep == maxdep) // too many dependency entries!
        {   SysErr(908931735); return INTERNAL; }

        if(dep)
        {
            obj->dep[obj->ndep]         = dep;
            obj->deptype[obj->ndep]     = horiz;
            obj->orgdepn[obj->ndep++]   = dncnt;
        }
    }

    // get all vertical dependencies:

    while((dep = va_arg(arg_ptr, void *)) != ALE)
    {
        dncnt++;

        if(obj->ndep == maxdep) // too many dependency entries!
        {   SysErr(908931736); return INTERNAL; }

        if(dep)
        {
            obj->dep[obj->ndep]         = dep;
            obj->deptype[obj->ndep]     = vert;
            obj->orgdepn[obj->ndep++]   = dncnt;
        }
    }

    ols.add((ListNode*)obj);

    // dmsg("addobj:added:");
    // HexDump((void*)obj, sizeof(Obj), dmsg);

    solved  = no;   // results of last solve now invalid

  va_end(arg_ptr);

  return OK;
}

// remove whole object list, so that a new list of objects may be added:

void StructView::remobjlist()
{_
 Obj *o;

    while(o = (Obj*)ols.first())
    {
        ols.remove((ListNode*)o);

        delete o;
    }

    solved  = no;   // of course, nothing's solved now
}

// compile all adresses between the added objects,
// the display positions were the objects should be drawn
// and the display's virtual coordinate boundaries:

rcode StructView::solve()
{_
 Obj *o,*o2,*o3,*o4;
 int i,i2;
 rcode r;
 uchar us[2]={0,0};
 int rmaxlev=0; // real max level used
 int cmax[2]={100,100};

    // reset all obj's display coords & obj adr table cnts:

    for(o=(Obj*)ols.first(); o; o=(Obj*)o->next())
    {
        o->x = o->y = o->ndepo = o->level = 0;
        memset(o->solved, 0, sizeof(o->solved));
        memset(o->depocnt, 0, sizeof(o->depocnt));
        o->corrupt = 0;
        o->picposdone = 0;
    }

    memset(nopl, 0, sizeof(nopl));

    // PASS1: now walk through list and solve the references:

    for(o=(Obj*)ols.first(); o; o=(Obj*)o->next())
    {
     int hoc[2],voc[2]; // horiz/vert object cursor

        hoc[0]=voc[0]=o->x;
        hoc[1]=voc[1]=o->y;

        dmsg("0908931413: o=%lx",o);

        for(o2=(Obj*)ols.first(); o2; o2=(Obj*)o2->next())
        {
            // is o2 a depender of o ?

            for(i=0; i < o->ndep && o->dep[i] != o2->adr; i++);

            if(i < o->ndep)
            {
                // notify that this entry was solved:

                o->solved[i] = 1;

                // check for circular dependencies:
                // was o already entered as a depender of o2?

                for(i2=0; i2 < o2->ndepo; i2++)

                    if(o2->depo[i2] == o)   break;

                if(i2 < o2->ndepo)
                {
                    // yea; well then, increment counter:

                    o2->depocnt[i2]++;
                }
                else
                {
                    // remind this link for the later drawing

                    o->depo[o->ndepo]   = o2;

                    o->depocnt[o->ndepo++]++;

                    // now, if o2 wasn't already handled in a loop before,
                    // find now a nice position for it in the picture.

                    if(!o2->picposdone)
                    {

                    switch(o->deptype[i])
                    {
                     case horiz:    // o2 follows o horizontal

                        o2->x   = max(o2->x, hoc[0] + 100);
                        o2->y   = max(o2->y, hoc[1]);   hoc[1] += 100;

                        o2->level   = max(o->level, o2->level);

                        break;

                     case vert:     // o2 is located below o

                        o2->x   = max(o2->x, voc[0]);   voc[0] += 100;
                        o2->y   = max(o2->y, voc[1] + 300);

                        o2->level   = max(o->level+1, o2->level);

                        break;
                    }

                    // difficult to believe, but despite the fact we
                    // didn't change o's position in the above statements,
                    // it has now it's right position!

                    o->picposdone = 1;
                    // this is due to a loop before.

                    cmax[0] = max(cmax[0], o2->x);
                    cmax[1] = max(cmax[1], o2->y);

                    }   // endif o2's picpos not already calculated

                }   // endelse already linked
            }

        }

        // HexDump((void*)o, sizeof(Obj), dmsg);

        cmax[0] = max(cmax[0], max(hoc[0], voc[0]));
        cmax[1] = max(cmax[1], max(hoc[1], voc[1]));
    }

    // PASS2: count no. of objects per level

    for(o=(Obj*)ols.first(); o; o=(Obj*)o->next())

        if(o->level < maxlevels)
        {
            nopl[o->level]++;
            rmaxlev = max(rmaxlev, o->level);
        }
        else
            {SysErr(1008931343); break;}

    // PASS3: per level ...

    for(i=0; i <= rmaxlev; i++)
    {
        if(!nopl[i])    break;  // might be: nothing at all

        int xstep = cmax[0] / nopl[i];  // x increment per object
        int cur_x = 0;

        // ... readjust object's x-coords

        for(o=(Obj*)ols.first(); o; o=(Obj*)o->next())

         if(o->level == i)
         {
            o->x     = cur_x;

            cur_x   += xstep;
         }
    }

    // now show list of all unsolved references:

    for(o=(Obj*)ols.first(); o; o=(Obj*)o->next())
    {
      us[0] = 0;

      for(i=0; i < o->ndep; i++)

        if(!o->solved[i])
        {
            perr("StructView: dependency no. %d of \"%s\" (%lx) unsolved",
                 o->orgdepn[i], o->id, o->adr);

            o->corrupt = 1;

            us[0] = 1;
        }

      if(us[0])
      {
        us[1] = 1;

        // HexDump((void*)o, sizeof(Obj), perr);
      }
    }

    dmsg("cmax: %d %d",cmax[0],cmax[1]);

    dispcbnd[0]=cmax[0]; dispcbnd[1]=cmax[1];

    r = OK;

    if(us[1])
    {
        perr("StructView: ... corrupt entries drawn in extra color.");

        r = INDATA;
    }

    solved = yes;

    return r;
}

// re-draw the display.
// use this after complete recreation and resolving of the object list.
// NOTE: this will automatically call solve() if it's not solved.

rcode StructView::redraw()
{_
 Obj *o,*o2;
 int i;
 rcode r,rsolve=OK;
 int color;

    if(!solved)
    {
        rsolve = solve();   // might return INDATA code
    }

    vd->freeze();   // current frontmost display stays as is

    vd->clear();    // this clears now the background display

    // adapt display to new virtual coordinate dimensions:

    vd->SetDimensions(0,0,dispcbnd[0]+10,dispcbnd[1]+10);

    // draw the solved object list:

    for(o=(Obj*)ols.first(); o; o=(Obj*)o->next())
    {
        for(i=0; i < o->ndepo; i++)
        {
            o2 = o->depo[i];

            color = o->depocnt[i];  // take no. of links as color

            vd->line(o->x, o->y, o2->x, o2->y, color);
            // dmsg("line(%d %d %d %d)",o->x,o->y,o2->x,o2->y);
        }

        vd->color(o->corrupt ? vd->ncol-1 : 1);

        if(nopl[o->level] < 80)
            vd->verttext(o->x, o->y, o->id);
        // if there are more than 80 objects in a level,
        // hide their text 'cause it's no longer readable,
        // and the performance jams too.
    }

    if(rsolve)
    {
        vd->color(vd->ncol-1);
        vd->msg("GAME OVER - currupt entries have this color.");
    }

    vd->unfreeze(); // pull background display to front

    return rsolve;
}

// check if user pressed mousebutton on the display

rcode StructView::testbutton()
{_
    return vd->testbutton();
}

rcode StructView::waitbutton()
{_
    return vd->waitbutton();
}

// close display; if forgotten, it's called by the destructor.

void StructView::close()
{_
    vd->close();    vd = 0;
}
*/

#ifdef  AMIGA

//  ---------------- Object-oriented displaying --------------------

void DisplayObj::wind(int maxpos[2])
{_
 DisplayObj* m;

    if(pos[0] >= 10)            pos[0] -= rand() * 3 / RAND_MAX;
    if(pos[0] <= maxpos[0]-10)  pos[0] += rand() * 3 / RAND_MAX;

    if(pos[1] >= 10)            pos[1] -= rand() * 3 / RAND_MAX;
    if(pos[1] <= maxpos[1]-10)  pos[1] += rand() * 3 / RAND_MAX;

    if(visible)
        for(m=first(); m; m=m->next())
            m->wind(maxpos);
}

// check if a place(x,y) seems to has no pixels around

rcode DisplayObj::freeplace(int x, int y, int step[2], VectorDisplay* vd)
{_
 int c,i;

    for(i=0; i<30; i++)
    {
        c = vd->readpix(x + step[0] * i, y);
        ifn(c == BACK_COLOR || c == LINE_COLOR)
            return FAILED;
    }

    return OK;
}

void DisplayObj::walk(int border[4], int step[2], VectorDisplay* vd)
{_
 DisplayObj* m;
 int c; // Pixel color
 int cs[2]; // char sizes in virtual coords
 bool stepped=0;
 char freep[2]={0,0};

    if(1)   // (walkcnt)
    {
     cs[0]  = ObjectDisplay::charsize[0];
     cs[1]  = ObjectDisplay::charsize[1];

     // because the objects are usually clustered near the top,
     // down-walking comes first:

     if(pos[1] < border[3] - step[1]*3)
        if(freeplace(pos[0], pos[1]+cs[1]+step[1]*2, step, vd) == OK)
            freep[1]    = 1;

     if(!stepped && pos[1] >= border[1] + step[1]*3)
        if(freeplace(pos[0], pos[1]-step[1]*2, step, vd) == OK)
            freep[0]    = 1;

     if(freep[0] && !freep[1])
     {
        // above is empty and beneath full: step up
        pos[1]  -= step[1]; stepped=1;
     }
     else
     if(!freep[0] && freep[1])
     {
        // above is full but beneath empty: step down
        pos[1]  += step[1]; stepped=1;
     }

     // if(stepped) walkcnt--;
    }

    if(visible)
        for(m=first(); m; m=m->next())
            m->walk(border, step, vd);
}

rcode DisplayObj::whoisclicked(int mpos[2], int cfs[2], DisplayObj* &result)
{_
 DisplayObj* m;

    if(     mpos[0]>=pos[0] && mpos[0]<=pos[0]+cfs[0]
        &&  mpos[1]>=pos[1] && mpos[1]<=pos[1]+cfs[1]
      )
    {
        result  = this;
        return OK;
    }

    //printf("chk: %s : pos %d %d mpos %d %d cfs %d %d\n",
    //  idstr, pos[0],pos[1],mpos[0],mpos[1],cfs[0],cfs[1]);

    if(visible)
        for(m=first(); m; m=m->next())
            if(m->whoisclicked(mpos, cfs, result) == OK)
                return OK;

    return FAILED;
}

void DisplayObj::add(DisplayObj* m)
{_
    m->parent   = this;
    DisplayObjList::add(m);
}

rcode DisplayObj::drawya(
                int pos[2],     // draw ya at this position
                int step[2],    // draw your members in this distance to you
                VectorDisplay* vd,  // drawing commands to this display
                bool reallydraw,    // if 0, don't exec drawing commands
                int maxpos[2]   // if your pos>this, adjust this
                )
{_
 rcode rc=OK;
 DisplayObj* m, *pred1, *pred2;
 int mpos[2];

    // if in pos'n calculation mode, remember my position:
    if(!reallydraw)
    {
        this->pos[0]    = pos[0];
        this->pos[1]    = pos[1];

        walkcnt = 30;   // walk 30 steps then stop

        // is my position a new maximum position?

        maxpos[0]   = max(maxpos[0], pos[0]);
        maxpos[1]   = max(maxpos[1], pos[1]);
    }

    // draw your members:

    if(visible)
    {
     if(!reallydraw)
     {
        mpos[0] = pos[0] + step[0]; // + rand() * step[0] / RAND_MAX;
        mpos[1] = rand() * step[1] / RAND_MAX;

        // if there are DisplayObj's before us in our level,
        // let's get the maximum vertical pos'n of all members of them
        // as the starting position of our first member:

        // for all our predecessors:
        for(pred1=prev(); pred1; pred1=pred1->prev())
            if(pred1->visible && (pred2 = pred1->last()))   // get last member
                mpos[1] = max(mpos[1], pred2->pos[1] + step[1]);
     }

     // 1. draw lines to members

     for(m=first(); m; m=m->next())
     {
        // draw line from you to member:
        if(reallydraw)
          vd->line(this->pos[0],this->pos[1],m->pos[0],m->pos[1],LINE_COLOR);
     }
    }

    // 2. over that, draw your idstring

    if(reallydraw)
    {
        // objects with members to be drawn in other color
        vd->color(first() ? 3 : 1);
        vd->text(this->pos[0],this->pos[1],idstr);
        vd->color(1);
    }

    if(visible)
    {
     // 3. above all, draw members themselves

     for(m=first(); m; m=m->next())
     {
        // let member draw it's stuff:
        if(rc=m->drawya(mpos, step, vd, reallydraw, maxpos))    break;

        mpos[1] += step[1] + rand() * step[1] / RAND_MAX;

        dmsg("obj %s : mpos %d %d",idstr,mpos[0],mpos[1]);
     }
    }

    return OK;
}

int ObjectDisplay::charsize[2];

rcode ObjectDisplay::add(DisplayObj* dobj)
{_
    ListHead::add(dobj);

    return OK;
}

DisplayObj* ObjectDisplay::whatobj(int mpos[2])
{_
    return 0;
}

rcode ObjectDisplay::run()
{_
 rcode rc=OK;
 int mpos[2];   // mouse position
 DisplayObj* dobj,*root;
 int cg[4];     // close gadget pos'n & size

 int bord[4];   // walk area border in absolute coords
 int step[2];   // probing & walking step sizes

    redraw();

    while(1)
    {
      if(testbutton() == OK)
      {
        mousepos(mpos);

        if(    mpos[0]>=cg[0] && mpos[0]<=cg[0]+cg[2]
            && mpos[1]>=cg[1] && mpos[1]<=cg[1]+cg[3]   )   break;

        dobj=0;
        if(root=first())
            root->whoisclicked(mpos, cfsize, dobj);

        // if an obj with members was clicked:
        if(dobj && dobj->first())
        {
            dobj->visible   ^= 1;
        }

        redraw();

        // remake close gadget
        cg[2]   = infrm[2]/20;
        cg[3]   = infrm[3]/20;
        cg[0]   = cg[2];
        cg[1]   = infrm[3]-cg[3]*2;
      }
      else
      {
        // let objects walk to free spaces

        if(root=first())
        {
         int rootpos[2]={0,0};

            bord[0] = infrm[2]/20;  // xmin
            bord[1] = infrm[3]/20;  // ymin
            bord[2] = infrm[2]-bord[0]; // xmax
            bord[3] = infrm[3]-bord[1]; // ymax

            step[0] = infrm[2]/300; // must be < bord[0]
            step[1] = max(1,infrm[3]/500);  // must be < bord[1]

            root->walk(bord, step, this);
            freeze();
            clear();    // blank drawing area
            root->drawya(rootpos, step, this, 1, &infrm[2]);
            unfreeze();

            Delay(10);
        }
      }

      // redraw close gadget:
      line(cg[0],cg[1],cg[0]+cg[2],cg[1]+cg[3],2);
      line(cg[0]+cg[2],cg[1],cg[0],cg[1]+cg[3],2);
    }

    close();

    return rc;
}

rcode ObjectDisplay::redraw()
{_
 rcode  rc=OK;
 DisplayObj* dobj;
 int    rootpos[2]={0,0};
 int    step[2]={200,100};
 int    maxpos[2]={500,500};

    if(!isopen)
        if(rc=open(PIXD_DOUBLEBUF)) return rc;
        else    isopen=1;

    msg("*");

    freeze();

    clear();    // blank drawing area

    // pass 1: determine max. virtual drawing coord's

    for(dobj=first(); dobj; dobj=dobj->next())
        if(rc = dobj->drawya(rootpos, step, this, 0, maxpos) )
            break;

    SetDimensions(0,0, maxpos[0]*100/90, maxpos[1]*100/90, 10,10,540,490);

    // determine single char size
    xbacksize(8,8, charsize[0],charsize[1]);

    // determine click field size
    cfsize[0]=charsize[0]*10;
    cfsize[1]=charsize[1];
    //printf("maxpos %d %d cfsize %d %d\n",maxpos[0],maxpos[1],
    //  cfsize[0],cfsize[1]);

    // pass 2: really draw objects

    for(dobj=first(); dobj; dobj=dobj->next())
        if(rc=dobj->drawya(rootpos, step, this, 1, maxpos))
            break;

    unfreeze();

    return rc;
}

//   _____________________________________________________________
//  /                                                             \
//  |   AMIGA Dynamic Display
//  \_____________________________________________________________/

//|| AmDisplay

AmigaDisplay::AmigaDisplay()
{_
    memset(&clist, 0, sizeof(clist));
    memset(&UCL,   0, sizeof(UCL));
    UCL.FirstCopList    = &clist;
    UCL.CopList         = &clist;
    OldUCL  = 0;

    //  init empty user copper list:

    clist.MaxCount  = 250;  // supports upto so many instructions
    clist.Count     = 0;

    if(!(clist.CopIns = (CopIns*)AllocMem(clist.MaxCount * 6, MEMF_CLEAR)))
    {   dead=1; return; }

    clist.CopPtr    = clist.CopIns;
    clist.DyOffset  = 0;    // no special start y offset

    dead    = 0;
}

rcode AmigaDisplay::open(int width, int height, int depth)
{_
 rcode rc;

    rc = PixelDisplay::open(width, height, depth, PIXD_NOHIRES|PIXD_NOLACE);

    ifn(rc)
    {
        OldUCL  = vport->UCopIns;   // remember old user copper list (if any)
        vport->UCopIns  = &UCL;     // set VPort's UserCopperList
    }
/*
    BitMap* bmap = scr->RastPort.BitMap;
    bvlsize = bmap->ByterPerRow * bmap->Depth;

    ifn(blankvline = AllocMem(bvlsize, MEMF_CHIP|MEMF_CLEAR))
        rc = MEMORY;
*/
    return rc;
}

void AmigaDisplay::close()
{_
    vport->UCopIns  = OldUCL;   // reinstall old user coplist (or 0 if none)

    // if(blankvline)   FreeMem(blankvline, bvlsize);

    PixelDisplay::close();
}

AmigaDisplay::~AmigaDisplay()
{_p ((ulong)this, (ulong)clist.CopIns, -1, -1);

    CHK_DESTR;  // compiler bug fix

    if(clist.CopIns)    FreeMem(clist.CopIns, clist.MaxCount * 6);

    secs.deleteall();   // del all remaining display sectn's
}

#define copwait(w1,w2)  \
    \
    *uwp++  = 0x0001;   \
    *uwp++  = w1;       \
    *uwp++  = w2;       \
    clist.Count++;      \
    remain--;

#define copmove(w1,w2)  \
    \
    *uwp++  = 0x8000;   \
    *uwp++  = w1;       \
    *uwp++  = w2;       \
    clist.Count++;      \
    remain--;

//|| ReDisplay

rcode AmigaDisplay::ReDisplay()
{_
 AmDSection* sec;
 short remain   = clist.MaxCount - 30;
 BitMap*  bmap  = scr->RastPort.BitMap;
 uword nplanes  = bmap->Depth;
 ulong mempos;
 uword shift;

    // create new copper instruction list, taking care of all
    // AmDSection entries in our AmDSection's list:

    // it is expected that the AmDSection's are sorted by their video_y
    // (lowest first)

    uword* uwp  = (uword *)clist.CopIns;
    clist.Count = 0;

    // set copper list start offset:

    if(sec=secs.first())
        clist.DyOffset  = sec->video_y;
    else
        clist.DyOffset  = 0;

    for(sec=secs.first(); sec && (remain > 0); sec=sec->next())
    {
        if(1)   // (hwsize[0] > 320)
        {

        // wait for this section's previous line:

        copwait(sec->video_y-1, 0);

        // set copper display commands for a 320 horizontal
        // pixel sized section window in a large screen:

        copmove(0xF08E, 0x2981-16   );  // DIWSTRT
        copmove(0xF090, 0x29C1      );  // DIWSTOP

        copmove(0xF092, 0x38-16 );  // DDFSTRT
        // 0x38 for 320 pix, -16 for hscroll support
        copmove(0xF094, 0xD0    );  // DDFSTOP

        copmove(0xF108, ((hwsize[0] - 320) >> 3) - 4    );  // BPL1MOD
        copmove(0xF10A, ((hwsize[0] - 320) >> 3) - 4    );  // BPL2MOD
        // 40 for 320 pix in 640 width, -4 for hscroll support

        shift   = 15 - (sec->mem_x & 15);       // pixel stepping
        copmove(0xF102, shift | (shift << 4));  // BPLCON1, for all bp's

        // in THIS line we would only see trash,
        // so switch off bitplane DMA during this line:

        copmove(0xF096, 1<<8    );          // disable bitplane DMA
        copwait(sec->video_y-1, 200 );      // wait for large hpos
        copmove(0xF096, (1<<15) | 1<<8  );  // enable bitplane DMA

        } // endif horizontal scrolling at all

        // wait for section's start line:

        copwait(sec->video_y,   0);

        // Start displaying from memory for all bitplanes:

        for(uword pl=0; pl<nplanes; pl++)
        {
            // let bitplane "pl" start displaying:

            mempos  = (ulong)bmap->Planes[pl];
            mempos += (ulong)bmap->BytesPerRow * (ulong)sec->mem_y;
            mempos += (ulong)(sec->mem_x >> 4) << 1;    // word stepping

            *uwp++  = 0x8000;           // MOVE
            *uwp++  = 0xF0E0 + pl * 4;  // BPL1PTH, BPL2PTH, ...
            *uwp++  = (uword)(mempos >> 16);    // adress, high word

            *uwp++  = 0x8000;           // MOVE
            *uwp++  = 0xF0E2 + pl * 4;  // BPL1PTL, BPL2PTL, ...
            *uwp++  = (uword)mempos;    // adress, low word

            clist.Count += 2;   // this were 2 instructions
            remain      -= 2;
        }

        // NOTE: with 5 bitplanes,
        //       these are upto 22 coplist entries per sec'n!

        sec->CopyNewToOld();    // new posn's become 'old' ones
    }

    //  terminate new instruction list:

    *uwp++  = 0x0001;   // WAIT
    *uwp++  = 0x2710;   // vpos 10000
    *uwp++  = 0x00FF;   // hpos 255

    clist.Count++;
    remain--;

    clist.CopPtr    = (struct CopIns *)uwp;

    //  let's redisplay the changed screen:

    RethinkDisplay();

    if(remain < 0)
    {
        MemErr(1404941931);
        perr("[TOO MANY DISPLAY SECTIONS]");

        return  RC_OVFLOW;
    }
    else
        return  OK;
}

//  The following code applies to NON-LACE screens only!

void AmigaDisplay::EncodeCopInstr(uword src[3], uword dest[2])
{
    ifn(src[0] & 0xF)   // MOVE (8000 == short frame)
    {
        dest[0] = src[1] & 0x1FE;
        dest[1] = src[2];
    }
    else
    if((src[0] & 0xF) == 0x1)   // WAIT
    {
        dest[0] = (src[1] << 8) | (src[2] >> 1) | 0x1;
        dest[1] = 1 << 15;  // no masks, set BFD (Blitter Finish Disable)
    }
    else    // SKIP
    {
        dest[0] = (src[1] << 8) | (src[2] >> 1) | 0x1;
        dest[1] = 1 << 15;  // no masks, set BFD (Blitter Finish Disable)
    }
}

void AmigaDisplay::DecodeCopInstr(uword src[2], uword dest[3])
{
    ifn(src[0] & 0x1)   // MOVE
    {
        dest[0] = 0x8000;
        dest[1] = src[0] & 0x1FE;
        dest[2] = src[1];
    }
    else    // WAIT, SKIP
    {
        dest[0] = 0x0001;
        dest[1] = src[0] >> 8;
        dest[2] = (src[0]  & 0xFE) << 1;
    }
}

void AmigaDisplay::AdjustPositions()
{_
 extern struct GfxBase  *GfxBase;

 AmDSection*        sec;
 BitMap*            bmap    = scr->RastPort.BitMap;
 volatile uword*    VHPOS   = (uword*)0xDFF006; // beam position register
 volatile uword*    backcol = (uword*)0xDFF180;
 uword*             uwp;
 uword*             syp     = GfxBase->ActiView->LOFCprList->start;

 const  int MAXPLANES = 8;
 uword  op_ins[MAXPLANES][2][3];    // old pseudo instructions
 uword  oc_ins[MAXPLANES][2][2];    // old coded  instructions
 ulong  newmpos[MAXPLANES];         // new memory pos'n per plane

 ulong  mempos;
 int    symax   = GfxBase->ActiView->LOFCprList->MaxCount;
 int    sycnt   = 0;
 uword  nplanes = bmap->Depth;
 uword  hwvpos, beamvpos, pl, shift, svdelta;
 bool   waitforbeam;

    if(nplanes > MAXPLANES) {SysErr(303941337); return;}

/*
    {
    static bool clda=1;

    if(clda)
    {
        clda = 0;

        // create system copper list disassembly:

        dmsg("SYSTEM COPPERLIST DISASSEMBLY");

        uword*  uwp     = syp;
        int     maxcnt  = symax;

        char*   s1;
        uword   ci[3];

        while(maxcnt--)
        {
            DecodeCopInstr(uwp, ci);

            s1  = (ci[0] == 0x8000) ? "MOVE" : "WSKP";

            dmsg("%s %04X %04X [%d %d]\t%04X %04X [%d %d]",
                s1, ci[1],ci[2], ci[1],ci[2],
                *uwp, *(uwp+1), *uwp, *(uwp+1));

            uwp += 2;
        }
    }
    }
*/

/*
    MIND THIS:
    ==========

    User CopList pseudo instruction WAIT vpos 270
    will be encoded in system coplist as:

    1.  WAIT for vpos 255
    2.  WAIT for vpos 15
*/

    // it is expected that the AmDSection's are sorted by their video_y
    // (lowest first)

    bool firstwait = 1; // reaching first WAIT command
    svdelta = 0;        // system vertical delta defaults to 0

    for(sec = secs.first(); sec; sec = sec->next())
    {
        // dmsg("section %d update", sec->old.vp[1]);

        if(1)   // (hwsize[0] > 320)
        {
        // horizontal scroll support:
        // 1. search WAIT for line before section

        while(sycnt < symax)
        {
            if(*syp & 0x1)  // WAIT or SKIP
            {
                hwvpos  = *syp >> 8;    // get hardware instr. vpos

                if(firstwait)
                {
                    // remember by what value all PseudoCopInstruction
                    // verticall pos'n arguments are incremented
                    // during system coplist creation:

                    svdelta     = hwvpos;
                    firstwait   = 0;
                }

                if(hwvpos != 255)       // WAIT 255 are very special!
                if(hwvpos >= ((sec->old.vp[1] - 1 + svdelta) & 0xFF))
                    break;
            }

            syp     += 2;
            sycnt   += 1;
        }

        if(sycnt >= symax)  {SysErr(1404941906); return;}

        // 2. search MOVE into pixel scroll register

        while(sycnt < symax)
        {
            if(*syp == 0x102)   // MOVE into BPLCON1

                break;

            syp     += 2;
            sycnt   += 1;
        }

        if(sycnt >= symax)  {SysErr(1404941907); return;}

        if(hwsize[0] > 320)
        {
            // if no horizontal scrolling is selected,
            // the following is skipped for better performance.

            // to avoid flickering, check now if beam isn't near
            // section start before modifying coplist:

            do
            {
                beamvpos    =   *VHPOS >> 8;
            }
            while(      beamvpos >= ((hwvpos - 20) & 0xFF)  // [spd]
                    &&  beamvpos <= hwvpos      );

            // adjust pixel scroll value:

            shift       = 15 - (sec->mem_x & 15);

            *(syp+1)    = shift | (shift << 4);

        } // endif horizontal scrolling supported

        } // endif (1)

        // goto old wait instruction:
        // NOTE that hw ypos will usually be larger than
        //      the original source ypos.

        while(sycnt < symax)
        {
            if(*syp & 0x1)  // WAIT or SKIP
            {
                hwvpos  = *syp >> 8;    // get hardware instr. vpos

                if(firstwait)
                {
                    svdelta     = hwvpos;
                    firstwait   = 0;
                }

                if(hwvpos != 255)       // WAIT 255 are very special!
                if(hwvpos >= ((sec->old.vp[1] + svdelta) & 0xFF))
                    break;
            }

            syp     += 2;
            sycnt   += 1;
        }

        if(sycnt >= symax)  {SysErr(1604941840); return;}

        waitforbeam = 1;    // before editing bplane instr, check beam pos

        // change memory display start for all bitplanes:

        // I. do the long lasting preparations

        for(pl = 0; pl < nplanes; pl++)
        {
            //  1. re-calc old bitplane pos'n setup instruction:

            mempos  = (ulong)bmap->Planes[pl];
            mempos += (ulong)bmap->BytesPerRow * (ulong)sec->old.mp[1];
            mempos += (ulong)(sec->old.mp[0] >> 4) << 1;    // word stepping

            uwp = &op_ins[pl][0][0];

            *uwp++  = 0x8000;           // MOVE
            *uwp++  = 0xF0E0 + pl * 4;  // BPL1PTH, BPL2PTH, ...
            *uwp++  = (uword)(mempos >> 16);    // adress, high word

            *uwp++  = 0x8000;           // MOVE
            *uwp++  = 0xF0E2 + pl * 4;  // BPL1PTL, BPL2PTL, ...
            *uwp++  = (uword)mempos;    // adress, low word

            EncodeCopInstr(&op_ins[pl][0][0], &oc_ins[pl][0][0]);
            EncodeCopInstr(&op_ins[pl][1][0], &oc_ins[pl][1][0]);

            //  2. calc new bitplane pos'n:

            mempos  = (ulong)bmap->Planes[pl];
            mempos += (ulong)bmap->BytesPerRow * (ulong)sec->mem_y;
            mempos += (ulong)(sec->mem_x >> 4) << 1;    // word stepping

            newmpos[pl] = mempos;
        }

        // II. do the fastest possible change on all bitplane instructions

        for(pl = 0; pl < nplanes; pl++)
        {
            //  3. replace old bitplane instruction by new one
            //     in the system's hardware copper list:

            while(sycnt < symax)
            {
                // 3.1. step to old bplane instructions:

                if(     *(syp+0) == oc_ins[pl][0][0]    // MOVE+Reg. coded
                    &&  *(syp+1) == oc_ins[pl][0][1]    // adress high word
                    &&  *(syp+2) == oc_ins[pl][1][0]    // MOVE+Reg. coded
                    &&  *(syp+3) == oc_ins[pl][1][1]    // adress low word
                  )
                {
                    // 3.2. replace adress words by new ones:

                    // first check if current beam position
                    // is in or shortly in front of the position to
                    // be changed. if so, wait 'til beam passed it,
                    // to avoid parallel access clash with copper:

                    if(waitforbeam)
                    {
                        waitforbeam = 0;

                        do
                        {
                            beamvpos    =   *VHPOS >> 8;
                        }
                        while(      beamvpos >= ((hwvpos - 10) & 0xFF)
                                &&  beamvpos <= hwvpos      );  // [spd]
                    }

                    // update mempos adress HIGH and LOW word:

                    *(syp+1)    =   (uword)(newmpos[pl] >> 16);
                    *(syp+3)    =   (uword) newmpos[pl];

                    syp     += 4;
                    sycnt   += 2;

                    break;

                }   // endif bplane move match

                // the bplane instruction didn't match, we tried
                // it too early. let's walk on forward.

                if(*syp & 0x1)  // found another WAIT or SKIP command
                {
                    hwvpos  = *syp >> 8;    // get hardware instr. vpos
                }

                syp     += 2;
                sycnt   += 1;

                if(sycnt >= symax)  {SysErr(504941624); return;}

            }   // endfor system hw instr's

        }   // endfor planes

        sec->CopyNewToOld();    // new posn's become 'old' ones

    }   // endfor sections

    _
}

//  The following code applies to NON-LACE screens only!

void AmDSection::EncodeCopInstr(uword src[3], uword dest[2])
{
    ifn(src[0] & 0xF)   // MOVE (8000 == short frame)
    {
        dest[0] = src[1] & 0x1FE;
        dest[1] = src[2];
    }
    else
    if((src[0] & 0xF) == 0x1)   // WAIT
    {
        dest[0] = (src[1] << 8) | (src[2] >> 1) | 0x1;
        dest[1] = 1 << 15;  // no masks, set BFD (Blitter Finish Disable)
    }
    else    // SKIP
    {
        dest[0] = (src[1] << 8) | (src[2] >> 1) | 0x1;
        dest[1] = 1 << 15;  // no masks, set BFD (Blitter Finish Disable)
    }
}

void AmDSection::DecodeCopInstr(uword src[2], uword dest[3])
{
    ifn(src[0] & 0x1)   // MOVE
    {
        dest[0] = 0x8000;
        dest[1] = src[0] & 0x1FE;
        dest[2] = src[1];
    }
    else    // WAIT, SKIP
    {
        dest[0] = 0x0001;
        dest[1] = src[0] >> 8;
        dest[2] = (src[0]  & 0xFE) << 1;
    }
}

/*
rcode AmDSection::UpdateCLEntry(

    uword** hwclpos,    // pos'n in system's hardware coplist
    int     iremain,    // remaining instructions 'til end of coplist

    Screen* scr

    )
{
 rcode  rc=OK;
 uword  *uwp,*syp,*eol,pl;
 uword  sinstr[2][3];
 BitMap*  bmap = scr->RastPort.BitMap;
 uword nplanes = bmap->Depth;
 ulong mempos;

 // performance enhancements:
 uword  *pibase;    // plane instruction base

    eol = *hwclpos + iremain * 2;   // calc end of system hwcoplist

    // do we have a pointer to custom instructions
    // in the system's hardware copper list?

    uwp = hwcl.syshwpos;

    if(     uwp // yes...

            // ... and does it still point into the sypent syshwclist?
        &&  (       uwp >= *hwclpos
                &&  uwp <  eol  )

            // ... and does it still point to our old hw instruction?
        &&  (       *uwp        == hwcl.instr[0][0][0]      // set bitplane's
                &&  *(uwp+1)    == hwcl.instr[0][0][1]      // high pointer
                &&  *(uwp+2)    == hwcl.instr[0][1][0]      // set bitplane's
                &&  *(uwp+3)    == hwcl.instr[0][1][1]  )   // low pointer
      )
    {
        syp = uwp;      // all valid: jump into syslist directly
    }
    else
    {
        syp = *hwclpos; // begin sequential search at supplied startpos

        hwcl.syshwpos   = 0;
    }

    // if there are no 'old' custom hardware instructions,
    // re-calculate them now:

    ifn(hwcl.icnt)
    {
        for(pl=0; pl<nplanes; pl++)
        {
            //  1. re-calc old bitplane pos'n setup instruction:

            mempos  = (ulong)bmap->Planes[pl];
            mempos += (ulong)bmap->BytesPerRow * (ulong)old.mp[1];
            mempos += (ulong)(old.mp[0] >> 4) << 1; // word stepping

            uwp     = &sinstr[0][0];

            *uwp++  = 0x8000;           // MOVE
            *uwp++  = 0xF0E0 + pl * 4;  // BPL1PTH, BPL2PTH, ...
            *uwp++  = (uword)(mempos >> 16);    // adress, high word

            *uwp++  = 0x8000;           // MOVE
            *uwp++  = 0xF0E2 + pl * 4;  // BPL1PTL, BPL2PTL, ...
            *uwp++  = (uword)mempos;    // adress, low word

            EncodeCopInstr(&sinstr[0][0], &hwcl.instr[pl][0][0]);
            EncodeCopInstr(&sinstr[1][0], &hwcl.instr[pl][1][0]);
        }

        hwcl.icnt   = pl;
    }

    // search from 'syp' for bitplane adress setup instructions
    // and update their adress part:

    for(pl=0; pl<nplanes; pl++)
    {
        pibase  = &hwcl.instr[pl][0][0];

        if(     *(syp+0)    ==  *pibase     // 'MOVE'
            &&  *(syp+1)    ==  *(pibase+1) //  high adress part
            &&  *(syp+2)    ==  *(pibase+2) // 'MOVE'
            &&  *(syp+3)    ==  *(pibase+3) //  low  adress part
          )
        {
            // update adress part...

            mempos  = (ulong)bmap->Planes[pl];
            mempos += (ulong)bmap->BytesPerRow * (ulong)mem_y;
            mempos += (ulong)(mem_x >> 4) << 1; // word stepping

            // ...in instruction block and system hw instructions

            *(syp+1)    =   *(pibase+1) = (uword)(mempos >> 16);
            *(syp+3)    =   *(pibase+3) = (uword)mempos;

            syp += 4;

        }
        else
        {
            syp += 2;

            if(syp >= eol)  {   rc = NOTAVAIL; break;   }
        }

    }   // endfor planes

    *hwclpos = syp; // for next display sections to continue

    return  rc;
}
*/
#endif  // AMIGA

rcode PixelDisplay::allocBackMap(void)
{
    if(!(hbmpback = CreateCompatibleBitmap(hdcfront, hwsize[0], hwsize[1])))
    {   MemErr(1211950954); return MEMORY;  }

    if(!(hdcback = CreateCompatibleDC(hdcfront)))
    {   MemErr(1211950957); return MEMORY;  }

    SelectObject(hdcback, hbmpback);

    if(hlogpal)
        ifn(horgbackpal = SelectPalette(hdcback, hlogpal, FALSE))
            perr("can't select backpalette");

    return OK;
}

void PixelDisplay::freeBackMap(void)
{
    if(hdcback  && horgbackpal) SelectPalette(hdcback, horgbackpal, FALSE);
    if(hdcback)     DeleteDC(hdcback);
    if(hbmpback)    DeleteObject(hbmpback);
    hdcback     = 0;
    hbmpback    = 0;
    horgbackpal = 0;
}

rcode PixelDisplay::copyToBackMap(void)
{
    if(!hdcback)    {SysErr(1211950955); return INTERNAL;}

    BitBlt(hdcback,0,0,hwsize[0],hwsize[1],hdcfront,0,0,SRCCOPY);
    return OK;
}

rcode PixelDisplay::copyFromBackMap(void)
{
    if(!hdcback)    {SysErr(2912951434); return INTERNAL;}

    BitBlt(hdcfront,0,0,hwsize[0],hwsize[1],hdcback,0,0,SRCCOPY);
    return OK;
}

rcode PixelDisplay::transRect(
    cpix    x0,
    cpix    y0,
    cpix    x1,
    cpix    y1,
    short   toback  // else toFront
    )
{
    if(!hdcback)    {SysErr(212951137); return INTERNAL;}
    if(    checkbnd(x0,y0)
        && checkbnd(x0,y1)
        && checkbnd(x1,y0)
        && checkbnd(x1,y1)
      )
        return INDATA;  // all coords off screen: forget it

    cpix xh,yh;
    if (x1 < x0) { xh=x1; x1=x0; x0=xh; }
    if (y1 < y0) { yh=y1; y1=y0; y0=yh; }
    cpix w = x1 - x0;
    cpix h = y1 - y0;

    if(toback)
        BitBlt(hdcback,x0,y0,w,h,hdcfront,x0,y0,SRCCOPY);
    else
        BitBlt(hdcfront,x0,y0,w,h,hdcback,x0,y0,SRCCOPY);

    return OK;
}

rcode PixelDisplay::transPix(
    cpix    x0,
    cpix    y0,
    short   toback  // else toFront
    )
{
    if(!hdcback)    {SysErr(212952019); return INTERNAL;}
    if(checkbnd(x0,y0))
        return INDATA;  // coord off screen: forget it

    if(toback)
        BitBlt(hdcback,x0,y0,1,1,hdcfront,x0,y0,SRCCOPY);
    else
        BitBlt(hdcfront,x0,y0,1,1,hdcback,x0,y0,SRCCOPY);

    return OK;
}

rcode PixelDisplay::setLevel(short frontback)
{
    if(frontback==0)    hdccur = hdcfront;
    else
    if(frontback==1)
    {
        if(hdcback)
            hdccur = hdcback;
        else
        {   perr("setLevel: no hdcback available"); return INDATA;  }
    }
    else
    {   SysErr(212951213); return INDATA;   }

    return OK;
}

rcode PixelDisplay::setUnusedSystemColorsTo(ushort rgb)
{
    if(!hlogpal || !hdcfront)
    {   SysErr(1612951653); return INTERNAL;    }

    if(!cman.colorremain())
        return FAILED;

    uchar r = ((rgb >> 8) & 0x0F) << 4;
    uchar g = ((rgb >> 4) & 0x0F) << 4;
    uchar b = ( rgb       & 0x0F) << 4;

    PALETTEENTRY pe;

    for(ushort isys=cman.sysindexofunused(); isys<cman.sysmaxcols(); isys++)
    {
        pe.peRed    = (uchar)r;
        pe.peGreen  = (uchar)g;
        pe.peBlue   = (uchar)b;
        pe.peFlags  = PC_NOCOLLAPSE;

        SetPaletteEntries(hlogpal, isys, 1, &pe);
    }

    RealizePalette(hdcfront);
    globalBoolPDRealizedPalette = 1; // just for statistics

    return OK;
}
