]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
2340b1
authorToni Wilen <twilen@winuae.net>
Sat, 18 Jun 2011 14:20:44 +0000 (17:20 +0300)
committerToni Wilen <twilen@winuae.net>
Sat, 18 Jun 2011 14:20:44 +0000 (17:20 +0300)
25 files changed:
a2091.cpp
cfgfile.cpp
custom.cpp
custom2.cpp [new file with mode: 0644]
filesys.cpp
gayle.cpp
include/gfxfilter.h
include/options.h
include/xwin.h
inputdevice.cpp
main.cpp
od-win32/direct3d.cpp
od-win32/direct3d.h
od-win32/dxwrap.cpp
od-win32/dxwrap.h
od-win32/picasso96_win.cpp
od-win32/resources/resource
od-win32/resources/winuae.rc
od-win32/win32.cpp
od-win32/win32.h
od-win32/win32_scaler.cpp
od-win32/win32gfx.cpp
od-win32/win32gfx.h
od-win32/win32gui.cpp
od-win32/winuaechangelog.txt

index 9b1a6534e2f645384694407dd4360fa47f90fd6e..7782959d2885b80034e66a10ec52bdd9866eedfe 100644 (file)
--- a/a2091.cpp
+++ b/a2091.cpp
@@ -946,14 +946,26 @@ static uae_u32 REGPARAM2 dmac_lgeti (uaecptr addr)
        special_mem |= S_READ;
 #endif
        addr &= 65535;
-       v = (dmac_wgeti(addr) << 16) | dmac_wgeti(addr + 2);
+       v = (dmac_wgeti (addr) << 16) | dmac_wgeti (addr + 2);
        return v;
 }
 
+static int REGPARAM2 dmac_check (uaecptr addr, uae_u32 size)
+{
+       return 1;
+}
+
+static uae_u8 *REGPARAM2 dmac_xlate (uaecptr addr)
+{
+       addr &= rom_mask;
+       addr += rombank * rom_size;
+       return rom + addr;
+}
+
 addrbank dmaca2091_bank = {
        dmac_lget, dmac_wget, dmac_bget,
        dmac_lput, dmac_wput, dmac_bput,
-       default_xlate, default_check, NULL, L"A2091/A590",
+       dmac_xlate, dmac_check, NULL, L"A2091/A590",
        dmac_lgeti, dmac_wgeti, ABFLAG_IO
 };
 
@@ -1348,13 +1360,16 @@ void a2091_init (void)
                rd = rl->rd; 
                z = read_rom (&rd);
                if (z) {
+                       int slotsize = 65536;
                        write_log (L"A590/A2091 BOOT ROM %d.%d ", rd->ver, rd->rev);
                        rom_size = rd->size;
-                       rom = xmalloc (uae_u8, rom_size);
+                       rom = xmalloc (uae_u8, slotsize);
                        if (rl->rd->id == 56)
                                rombankswitcher = 1;
                        zfile_fread (rom, rom_size, 1, z);
                        zfile_fclose (z);
+                       for (int i = 1; i < slotsize / rom_size; i++)
+                               memcpy (rom + i * rom_size, rom, rom_size);
                        rom_mask = rom_size - 1;
                }
        } else {
index 613a41d5bfc4778338d46e0dcc9d1b31c9023e14..2921e9bb96ec1b5c46f1c59847caea87a43f875c 100644 (file)
@@ -172,6 +172,7 @@ static const TCHAR *joyaf[] = { L"none", L"normal", L"toggle", 0 };
 static const TCHAR *epsonprinter[] = { L"none", L"ascii", L"epson_matrix_9pin", L"epson_matrix_24pin", L"epson_matrix_48pin", 0 };
 static const TCHAR *aspects[] = { L"none", L"vga", L"tv", 0 };
 static const TCHAR *vsyncmodes[] = { L"false", L"true", L"autoswitch", 0 };
+static const TCHAR *vsyncmodes2[] = { L"normal", L"busywait", 0 };
 static const TCHAR *filterapi[] = { L"directdraw", L"direct3d", 0 };
 static const TCHAR *dongles[] =
 {
@@ -752,7 +753,9 @@ void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type)
        cfgfile_write_bool (f, L"gfx_autoresolution", p->gfx_autoresolution);
        cfgfile_write (f, L"gfx_backbuffers", L"%d", p->gfx_backbuffers);
        cfgfile_write_str (f, L"gfx_vsync", vsyncmodes[p->gfx_avsync]);
+       cfgfile_write_str (f, L"gfx_vsyncmode", vsyncmodes2[p->gfx_avsyncmode]);
        cfgfile_write_str (f, L"gfx_vsync_picasso", vsyncmodes[p->gfx_pvsync]);
+       cfgfile_write_str (f, L"gfx_vsyncmode_picasso", vsyncmodes2[p->gfx_pvsyncmode]);
        cfgfile_write_bool (f, L"gfx_lores", p->gfx_resolution == 0);
        cfgfile_write_str (f, L"gfx_resolution", lorestype1[p->gfx_resolution]);
        cfgfile_write_str (f, L"gfx_lores_mode", loresmode[p->gfx_lores_mode]);
@@ -858,7 +861,56 @@ void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type)
                cfgfile_dwrite (f, L"chipset",L"ecs_denise");
        else
                cfgfile_dwrite (f, L"chipset", L"ocs");
-       cfgfile_write (f, L"chipset_refreshrate", L"%f", p->chipset_refreshrate);
+       if (p->chipset_refreshrate > 0)
+               cfgfile_write (f, L"chipset_refreshrate", L"%f", p->chipset_refreshrate);
+
+       for (int i = 0; i < MAX_CHIPSET_REFRESH_TOTAL; i++) {
+               if (p->cr[i].rate <= 0)
+                       continue;
+               struct chipset_refresh *cr = &p->cr[i];
+               _stprintf (tmp, L"%f", cr->rate);
+               TCHAR *s = tmp + _tcslen (tmp);
+               if (cr->label[0] > 0 && i < MAX_CHIPSET_REFRESH)
+                       s += _stprintf (s, L",t=%s", cr->label);
+               if (cr->horiz > 0)
+                       s += _stprintf (s, L",h=%d", cr->horiz);
+               if (cr->vert > 0)
+                       s += _stprintf (s, L",v=%d", cr->vert);
+               if (cr->locked)
+                       _tcscat (s, L",locked");
+               if (cr->ntsc > 0)
+                       _tcscat (s, L",ntsc");
+               else if (cr->ntsc == 0)
+                       _tcscat (s, L",pal");
+               if (cr->lace > 0)
+                       _tcscat (s, L",lace");
+               else if (cr->lace == 0)
+                       _tcscat (s, L",nlace");
+               if (cr->framelength > 0)
+                       _tcscat (s, L",lof");
+               else if (cr->framelength == 0)
+                       _tcscat (s, L",shf");
+               if (cr->vsync > 0)
+                       _tcscat (s, L",vsync");
+               else if (cr->vsync == 0)
+                       _tcscat (s, L",nvsync");
+               if (cr->commands[0]) {
+                       _tcscat (s, L",");
+                       _tcscat (s, cr->commands);
+                       for (int j = 0; j < _tcslen (s); j++) {
+                               if (s[j] == '\n')
+                                       s[j] = ',';
+                       }
+                       s[_tcslen (s) - 1] = 0;
+               }
+               if (i == CHIPSET_REFRESH_PAL)
+                       cfgfile_dwrite (f, L"displaydata_pal", tmp);
+               else if (i == CHIPSET_REFRESH_NTSC)
+                       cfgfile_dwrite (f, L"displaydata_ntsc", tmp);
+               else
+                       cfgfile_dwrite (f, L"displaydata", tmp);
+       }
+
        cfgfile_write_str (f, L"collision_level", collmode[p->collision_level]);
 
        cfgfile_write_str(f, L"chipset_compatible", cscompa[p->cs_compatible]);
@@ -1456,15 +1508,23 @@ static int cfgfile_parse_host (struct uae_prefs *p, TCHAR *option, TCHAR *value)
                        return 1;
                return cfgfile_yesno (option, value, L"gfx_vsync_picasso", &p->gfx_pvsync);
        }
+       if (cfgfile_strval (option, value, L"gfx_vsyncmode", &p->gfx_avsyncmode, vsyncmodes2, 0))
+               return 1;
+       if (cfgfile_strval (option, value, L"gfx_vsyncmode_picasso", &p->gfx_pvsyncmode, vsyncmodes2, 0))
+               return 1;
 
        if (cfgfile_yesno (option, value, L"show_leds", &vb)) {
                if (vb)
                        p->leds_on_screen |= STATUSLINE_CHIPSET;
+               else
+                       p->leds_on_screen &= ~STATUSLINE_CHIPSET;
                return 1;
        }
        if (cfgfile_yesno (option, value, L"show_leds_rtg", &vb)) {
                if (vb)
                        p->leds_on_screen |= STATUSLINE_RTG;
+               else
+                       p->leds_on_screen &= ~STATUSLINE_RTG;
                return 1;
        }
 
@@ -1780,6 +1840,95 @@ static int cfgfile_parse_host (struct uae_prefs *p, TCHAR *option, TCHAR *value)
                return 1;
        }
 
+       if (_tcscmp (option, L"displaydata") == 0 || _tcscmp (option, L"displaydata_pal") == 0 || _tcscmp (option, L"displaydata_ntsc") == 0) {
+               _tcsncpy (tmpbuf, value, sizeof tmpbuf / sizeof (TCHAR) - 1);
+               tmpbuf[sizeof tmpbuf / sizeof (TCHAR) - 1] = '\0';
+
+               int vert = -1, horiz = -1, lace = -1, ntsc = -1, framelength = -1, vsync = -1;
+               int locked = 0;
+               double rate = -1;
+               TCHAR cmd[MAX_DPATH], label[16] = { 0 };
+               TCHAR *tmpp = tmpbuf;
+               TCHAR *end = tmpbuf + _tcslen (tmpbuf);
+               cmd[0] = 0;
+               for (;;) {
+                       TCHAR *next = _tcschr (tmpp, ',');
+                       TCHAR *equals = _tcschr (tmpp, '=');
+
+                       if (!next)
+                               next = end;
+                       if (equals == NULL || equals > next)
+                               equals = NULL;
+                       else
+                               equals++;
+                       *next = 0;
+
+                       if (rate < 0)
+                               rate = _tstof (tmpp);
+                       else if (!_tcsnicmp (tmpp, L"v=", 2))
+                               vert = _tstol (equals);
+                       else if (!_tcsnicmp (tmpp, L"h=", 2))
+                               horiz = _tstol (equals);
+                       else if (!_tcsnicmp (tmpp, L"t=", 2))
+                               _tcsncpy (label, equals, sizeof label / sizeof (TCHAR) - 1);
+                       else if (equals) {
+                               if (_tcslen (cmd) + _tcslen (tmpp) + 2 < sizeof (cmd) / sizeof(TCHAR)) {
+                                       _tcscat (cmd, tmpp);
+                                       _tcscat (cmd, L"\n");
+                               }
+                       }
+                       if (!_tcsnicmp (tmpp, L"locked", 4))
+                               locked = 1;
+                       if (!_tcsnicmp (tmpp, L"nlace", 5))
+                               lace = 0;
+                       if (!_tcsnicmp (tmpp, L"lace", 4))
+                               lace = 1;
+                       if (!_tcsnicmp (tmpp, L"nvsync", 5))
+                               vsync = 0;
+                       if (!_tcsnicmp (tmpp, L"vsync", 4))
+                               vsync = 1;
+                       if (!_tcsnicmp (tmpp, L"ntsc", 4))
+                               ntsc = 1;
+                       if (!_tcsnicmp (tmpp, L"pal", 3))
+                               ntsc = 0;
+                       if (!_tcsnicmp (tmpp, L"lof", 3))
+                               framelength = 1;
+                       if (!_tcsnicmp (tmpp, L"shf", 3))
+                               framelength = 0;
+                       tmpp = next;
+                       if (tmpp >= end)
+                               break;
+                       tmpp++;
+               }
+               if (rate > 0) {
+                       for (int i = 0; i < MAX_CHIPSET_REFRESH; i++) {
+                               if (_tcscmp (option, L"displaydata_pal") == 0) {
+                                       i = CHIPSET_REFRESH_PAL;
+                                       p->cr[i].rate = -1;
+                                       _tcscpy (label, L"PAL");
+                               } else if (_tcscmp (option, L"displaydata_ntsc") == 0) {
+                                       i = CHIPSET_REFRESH_NTSC;
+                                       p->cr[i].rate = -1;
+                                       _tcscpy (label, L"NTSC");
+                               }
+                               if (p->cr[i].rate <= 0) {
+                                       p->cr[i].horiz = horiz;
+                                       p->cr[i].vert = vert;
+                                       p->cr[i].lace = lace;
+                                       p->cr[i].ntsc = ntsc;
+                                       p->cr[i].vsync = vsync;
+                                       p->cr[i].locked = locked != 0;
+                                       p->cr[i].framelength = framelength;
+                                       p->cr[i].rate = rate;
+                                       _tcscpy (p->cr[i].commands, cmd);
+                                       _tcscpy (p->cr[i].label, label);
+                                       break;
+                               }
+                       }
+               }
+               return 1;
+       }
+
        return 0;
 }
 
@@ -2144,17 +2293,17 @@ static int cfgfile_parse_hardware (struct uae_prefs *p, const TCHAR *option, TCH
        }
 
        if (cfgfile_string (option, value, L"mmu_model", tmpbuf, sizeof tmpbuf / sizeof (TCHAR))) {
-               p->mmu_model = _tstol(tmpbuf);
+               p->mmu_model = _tstol (tmpbuf);
                return 1;
        }
 
        if (cfgfile_string (option, value, L"fpu_model", tmpbuf, sizeof tmpbuf / sizeof (TCHAR))) {
-               p->fpu_model = _tstol(tmpbuf);
+               p->fpu_model = _tstol (tmpbuf);
                return 1;
        }
 
        if (cfgfile_string (option, value, L"cpu_model", tmpbuf, sizeof tmpbuf / sizeof (TCHAR))) {
-               p->cpu_model = _tstol(tmpbuf);
+               p->cpu_model = _tstol (tmpbuf);
                p->fpu_model = 0;
                return 1;
        }
@@ -2564,6 +2713,24 @@ static void cfgfile_parse_separated_line (struct uae_prefs *p, TCHAR *line1b, TC
        }
 }
 
+void cfgfile_parse_lines (struct uae_prefs *p, const TCHAR *lines, int type)
+{
+       TCHAR *buf = my_strdup (lines);
+       TCHAR *t = buf;
+       for (;;) {
+               if (_tcslen (t) == 0)
+                       break;
+               TCHAR *t2 = _tcschr (t, '\n');
+               if (t2)
+                       *t2 = 0;
+               cfgfile_parse_line (p, t, type);
+               if (!t2)
+                       break;
+               t = t2 + 1;
+       }
+       xfree (buf);
+}
+
 void cfgfile_parse_line (struct uae_prefs *p, TCHAR *line, int type)
 {
        TCHAR line1b[CONFIG_BLEN], line2b[CONFIG_BLEN];
@@ -3700,7 +3867,7 @@ void default_prefs (struct uae_prefs *p, int type)
        p->gfx_max_vertical = VRES_DOUBLE;
        p->color_mode = 2;
        p->gfx_blackerthanblack = 0;
-       p->gfx_backbuffers = 2;
+       p->gfx_backbuffers = 1;
 
        target_default_options (p, type);
 
@@ -3837,6 +4004,33 @@ void default_prefs (struct uae_prefs *p, int type)
 
        blkdev_default_prefs (p);
 
+       p->cr_selected = -1;
+       struct chipset_refresh *cr;
+       for (int i = 0; i < MAX_CHIPSET_REFRESH_TOTAL; i++) {
+               cr = &p->cr[i];
+               cr->rate = -1;
+       }
+       cr = &p->cr[CHIPSET_REFRESH_PAL];
+       cr->horiz = -1;
+       cr->vert = -1;
+       cr->lace = -1;
+       cr->vsync = - 1;
+       cr->framelength = -1;
+       cr->rate = 50.0;
+       cr->ntsc = 0;
+       cr->locked = false;
+       _tcscpy (cr->label, L"PAL");
+       cr = &p->cr[CHIPSET_REFRESH_NTSC];
+       cr->horiz = -1;
+       cr->vert = -1;
+       cr->lace = -1;
+       cr->vsync = - 1;
+       cr->framelength = -1;
+       cr->rate = 60.0;
+       cr->ntsc = 1;
+       cr->locked = false;
+       _tcscpy (cr->label, L"NTSC");
+
        zfile_fclose (default_file);
        default_file = NULL;
        f = zfile_fopen_empty (NULL, L"configstore", 100000);
index 3bea78d63f51a5ccab2470784c429d07bd81b69d..410a78d9c574e25575e383ab0d700b1fa120cb08 100644 (file)
@@ -1663,6 +1663,34 @@ STATIC_INLINE int one_fetch_cycle (int pos, int ddfstop_to_test, int dma, int fm
        }
 }
 
+static void update_fetch_x (int hpos, int fm)
+{
+       int pos;
+
+       if (nodraw ())
+               return;
+
+       pos = last_fetch_hpos;
+       update_toscr_planes ();
+       for (int i = 0; i < 8; i++)
+               fetched[i] = bplxdat[i];
+       beginning_of_plane_block (hpos, fm);
+       for (; pos < hpos; pos++) {
+
+               toscr_nbits += 2 << toscr_res;
+
+               if (toscr_nbits > 16) {
+                       uae_abort (L"toscr_nbits > 16 (%d)", toscr_nbits);
+                       toscr_nbits = 0;
+               }
+               if (toscr_nbits == 16)
+                       flush_display (fm);
+
+       }
+       flush_display (fm);
+       bpl1dat_written = 0;
+}
+
 STATIC_INLINE void update_fetch (int until, int fm)
 {
        int pos;
@@ -1790,6 +1818,9 @@ STATIC_INLINE void decide_fetch (int hpos)
 #endif
                        default: uae_abort (L"fetchmode corrupt");
                        }
+               } else if (0 && bpl1dat_written) {
+                       // "pio" mode display
+                       update_fetch_x (hpos, fetchmode);
                }
                maybe_check (hpos);
                last_fetch_hpos = hpos;
@@ -2519,6 +2550,7 @@ static void finish_decisions (void)
        decide_diw (hpos);
        decide_line (hpos);
        decide_fetch (hpos);
+       finish_final_fetch (hpos, fetchmode);
 
        record_color_change2 (hsyncstartpos, 0xffff, 0);
        if (thisline_decision.plfleft != -1 && thisline_decision.plflinelen == -1) {
@@ -2692,9 +2724,9 @@ static int islinetoggle (void)
 
 static int isvsync (void)
 {
-       if (currprefs.gfx_afullscreen != GFX_FULLSCREEN || picasso_on || !currprefs.gfx_avsync)
+       if (picasso_on || !currprefs.gfx_avsync || (currprefs.gfx_avsync == 0 && !currprefs.gfx_afullscreen))
                return 0;
-       return currprefs.gfx_avsync;
+       return currprefs.gfx_avsyncmode == 0 ? 1 : -1;
 }
 
 int vsynctime_orig;
@@ -2746,7 +2778,7 @@ int current_maxvpos (void)
 /* set PAL/NTSC or custom timing variables */
 void init_hz (bool fullinit)
 {
-       int isntsc;
+       int isntsc, islace;
        int odbl = doublescan, omaxvpos = maxvpos;
        double ovblank = vblank_hz;
        int hzc = 0;
@@ -2754,14 +2786,6 @@ void init_hz (bool fullinit)
        if (fullinit)
                vpos_count = vpos_count_prev = 0;
 
-       if (vsync_switchmode (-1, 0))
-               currprefs.gfx_avsync = changed_prefs.gfx_avsync = vsync_switchmode (-1, 0) ? 2 : 0;
-
-       if (!isvsync () && (DBLEQU (currprefs.chipset_refreshrate, 50) && !currprefs.ntscmode) ||
-               (DBLEQU (currprefs.chipset_refreshrate, 60) && currprefs.ntscmode)) {
-                       currprefs.chipset_refreshrate = changed_prefs.chipset_refreshrate = 0.0;
-       }
-
        doublescan = 0;
        if ((beamcon0 & 0xA0) != (new_beamcon0 & 0xA0))
                hzc = 1;
@@ -2771,6 +2795,7 @@ void init_hz (bool fullinit)
        }
        beamcon0 = new_beamcon0;
        isntsc = (beamcon0 & 0x20) ? 0 : 1;
+       islace = (bplcon0 & 4) ? 1 : 0;
        if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
                isntsc = currprefs.ntscmode ? 1 : 0;
        if (!isntsc) {
@@ -2847,16 +2872,49 @@ void init_hz (bool fullinit)
        eventtab[ev_hsync].evtime = get_cycles () + HSYNCTIME;
        events_schedule ();
        if (hzc) {
-               interlace_seen = (bplcon0 & 4) ? 1 : 0;
+               interlace_seen = islace;
                reset_drawing ();
        }
-       if ((DBLEQU (vblank_hz, 50) || DBLEQU (vblank_hz, 60)) && isvsync () == 2) {
-               if (getvsyncrate (currprefs.gfx_refreshrate) != vblank_hz)
-                       vsync_switchmode (vblank_hz, currprefs.gfx_refreshrate);
-       }
-       if (isvsync ()) {
-               changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = abs (currprefs.gfx_refreshrate);
+
+       for (int i = 0; i < MAX_CHIPSET_REFRESH_TOTAL; i++) {
+               struct chipset_refresh *cr = &currprefs.cr[i];
+               if ((cr->horiz < 0 || cr->horiz == maxhpos) &&
+                       (cr->vert < 0 || cr->vert == maxvpos_nom) &&
+                       (cr->ntsc < 0 || (cr->ntsc > 0 && isntsc) || (cr->ntsc == 0 && !isntsc)) &&
+                       (cr->lace < 0 || (cr->lace > 0 && islace) || (cr->lace == 0 && !islace)) &&
+                       (cr->framelength < 0 || (cr->framelength > 0 && lof_store) || (cr->framelength == 0 && !lof_store)) &&
+                       (cr->vsync < 0 || (cr->vsync > 0 && isvsync ()) || (cr->vsync == 0 && !isvsync ()))) {
+                               double v = -1;
+
+                               if (isvsync ()) {
+                                       if (i == CHIPSET_REFRESH_PAL || i == CHIPSET_REFRESH_NTSC) {
+                                               if ((abs (vblank_hz - 50) < 1 || abs (vblank_hz - 60) < 1) && currprefs.gfx_avsync == 2 && currprefs.gfx_afullscreen > 0) {
+                                                       vsync_switchmode (vblank_hz > 55 ? 60 : 50);
+                                               }
+                                       }
+                                       if (isvsync () < 0) {
+                                               double v2;
+                                               changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = cr->rate;
+                                               v2 = vblank_calibrate (cr->locked);
+                                               if (!cr->locked)
+                                                       v = v2;
+                                       }
+                               } else {
+                                       if (cr->locked == true)
+                                               v = cr->rate;
+                                       else
+                                               v = vblank_hz;
+                               }
+                               if (v < 0)
+                                       v = cr->rate;
+                               if (v > 0) {
+                                       changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = v;
+                                       cfgfile_parse_lines (&changed_prefs, cr->commands, -1);
+                               }
+                               break;
+               }
        }
+
        maxvpos_total = (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 2047 : 511;
        if (maxvpos_total > MAXVPOS)
                maxvpos_total = MAXVPOS;
@@ -2875,7 +2933,7 @@ void init_hz (bool fullinit)
        inputdevice_tablet_strobe ();
        write_log (L"%s mode%s%s V=%.4fHz H=%0.4fHz (%dx%d+%d)\n",
                isntsc ? L"NTSC" : L"PAL",
-               (bplcon0 & 4) ? L" interlaced" : L"",
+               islace ? L" laced" : L"",
                doublescan > 0 ? L" dblscan" : L"",
                vblank_hz, vblank_hz * maxvpos_nom,
                maxhpos, maxvpos, lof_store ? 1 : 0);
@@ -2954,7 +3012,7 @@ void init_custom (void)
        update_mirrors();
        create_cycle_diagram_table ();
        reset_drawing ();
-       init_hz_full ();
+       init_hz ();
        calcdiw ();
 }
 
@@ -4980,9 +5038,16 @@ static void framewait (void)
 {
        frame_time_t curr_time;
        frame_time_t start;
+       int vs = isvsync ();
 
-       if (isvsync ()) {
+       if (vs > 0) {
                vsyncmintime = vsynctime;
+               update_screen ();
+               return;
+       } else if (vs < 0) {
+               vsyncmintime = vsynctime;
+               vsync_busywait ();
+               update_screen ();
                return;
        }
        for (;;) {
@@ -4991,6 +5056,7 @@ static void framewait (void)
                        break;
                sleep_millis (2);
        }
+       update_screen ();
        curr_time = start = read_processor_time ();
        while (rpt_vsync () < 0);
        curr_time = read_processor_time ();
@@ -5102,6 +5168,7 @@ static void vsync_handler_post (void)
                                        if ((long int)(curr_time - vsyncmintime) > 0 || rpt_did_reset)
                                                vsyncmintime = curr_time + vsynctime;
                                        rpt_did_reset = 0;
+                                       update_screen ();
                                } else if (rpt_available) {
                                        framewait ();
                                }
@@ -5109,11 +5176,15 @@ static void vsync_handler_post (void)
                        } else {
                                if (rpt_available && currprefs.m68k_speed == 0) {
                                        framewait ();
+                               } else {
+                                       update_screen ();
                                }
                        }
 #endif
-       } else {
+       } else if (currprefs.m68k_speed == 0) {
                framewait ();
+       } else {
+               update_screen ();
        }
 
 #if CUSTOM_DEBUG > 1
@@ -7094,13 +7165,9 @@ void check_prefs_changed_custom (void)
        currprefs.cs_slowmemisfast = changed_prefs.cs_slowmemisfast;
 
        if (currprefs.chipset_mask != changed_prefs.chipset_mask ||
-               currprefs.gfx_avsync != changed_prefs.gfx_avsync ||
-               currprefs.gfx_pvsync != changed_prefs.gfx_pvsync ||
                currprefs.picasso96_nocustom != changed_prefs.picasso96_nocustom ||
                currprefs.ntscmode != changed_prefs.ntscmode) {
                        currprefs.picasso96_nocustom = changed_prefs.picasso96_nocustom;
-                       currprefs.gfx_avsync = changed_prefs.gfx_avsync;
-                       currprefs.gfx_pvsync = changed_prefs.gfx_pvsync;
                        currprefs.chipset_mask = changed_prefs.chipset_mask;
                        if (currprefs.ntscmode != changed_prefs.ntscmode) {
                                currprefs.ntscmode = changed_prefs.ntscmode;
diff --git a/custom2.cpp b/custom2.cpp
new file mode 100644 (file)
index 0000000..72e15a8
--- /dev/null
@@ -0,0 +1,7410 @@
+/*
+* UAE - The Un*x Amiga Emulator
+*
+* Custom chip emulation
+*
+* Copyright 1995-2002 Bernd Schmidt
+* Copyright 1995 Alessandro Bissacco
+* Copyright 2000-2010 Toni Wilen
+*/
+
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <ctype.h>
+#include <assert.h>
+
+#include "options.h"
+#include "uae.h"
+#include "gensound.h"
+#include "audio.h"
+#include "sounddep/sound.h"
+#include "events.h"
+#include "memory.h"
+#include "custom.h"
+#include "newcpu.h"
+#include "cia.h"
+#include "disk.h"
+#include "blitter.h"
+#include "xwin.h"
+#include "inputdevice.h"
+#include "inputrecord.h"
+#include "keybuf.h"
+#include "serial.h"
+#include "autoconf.h"
+#include "traps.h"
+#include "gui.h"
+#include "picasso96.h"
+#include "drawing.h"
+#include "savestate.h"
+#include "ar.h"
+#ifdef AVIOUTPUT
+#include "avioutput.h"
+#endif
+#include "debug.h"
+#include "akiko.h"
+#include "cdtv.h"
+#if defined(ENFORCER)
+#include "enforcer.h"
+#endif
+#include "gayle.h"
+#include "gfxfilter.h"
+#include "a2091.h"
+#include "a2065.h"
+#include "ncr_scsi.h"
+#include "blkdev.h"
+#include "sampler.h"
+
+#define CUSTOM_DEBUG 0
+#define SPRITE_DEBUG 0
+#define SPRITE_DEBUG_MINY 0x0
+#define SPRITE_DEBUG_MAXY 0x100
+#define SPR0_HPOS 0x15
+#define MAX_SPRITES 8
+#define SPRITE_COLLISIONS
+#define SPEEDUP
+#define AUTOSCALE_SPRITES 1
+
+#define SPRBORDER 0
+
+STATIC_INLINE bool nocustom (void)
+{
+       if (picasso_on && currprefs.picasso96_nocustom)
+               return 1;
+       return 0;
+}
+
+void uae_abort (const TCHAR *format,...)
+{
+       static int nomore;
+       va_list parms;
+       TCHAR buffer[1000];
+
+       va_start (parms, format);
+       _vsntprintf (buffer, sizeof (buffer) - 1, format, parms );
+       va_end (parms);
+       if (nomore) {
+               write_log (L"%s\n", buffer);
+               return;
+       }
+       gui_message (buffer);
+       nomore = 1;
+}
+
+#if 0
+void customhack_put (struct customhack *ch, uae_u16 v, int hpos)
+{
+       ch->v = v;
+       ch->vpos = vpos;
+       ch->hpos = hpos;
+}
+
+uae_u16 customhack_get (struct customhack *ch, int hpos)
+{
+       if (ch->vpos == vpos && ch->hpos == hpos) {
+               ch->vpos = -1;
+               return 0xffff;
+       }
+       return ch->v;
+}
+#endif
+
+uae_u16 last_custom_value1;
+
+static unsigned int n_consecutive_skipped = 0;
+static unsigned int total_skipped = 0;
+
+STATIC_INLINE void sync_copper (int hpos);
+
+
+/* Events */
+
+unsigned long int event_cycles, nextevent, is_lastline, currcycle;
+long cycles_to_next_event;
+long max_cycles_to_next_event;
+long cycles_to_hsync_event;
+unsigned long int vsync_cycles;
+static int extra_cycle;
+unsigned long start_cycles;
+
+static int rpt_did_reset;
+struct ev eventtab[ev_max];
+struct ev2 eventtab2[ev2_max];
+
+volatile frame_time_t vsynctime, vsyncmintime;
+
+int vpos;
+static int vpos_count, vpos_count_prev;
+static int lof_store; // real bit in custom registers
+static int lof_current; // what display device thinks
+static int lol;
+static int next_lineno, prev_lineno;
+static enum nln_how nextline_how;
+static int lof_changed = 0;
+static int scandoubled_line;
+
+/* Stupid genlock-detection prevention hack.
+* We should stop calling vsync_handler() and
+* hstop_handler() completely but it is not
+* worth the trouble..
+*/
+static int vpos_previous, hpos_previous;
+static int vpos_lpen, hpos_lpen, lightpen_triggered;
+int lightpen_x, lightpen_y, lightpen_cx, lightpen_cy;
+
+static uae_u32 sprtaba[256],sprtabb[256];
+static uae_u32 sprite_ab_merge[256];
+/* Tables for collision detection.  */
+static uae_u32 sprclx[16], clxmask[16];
+
+/* AGA T genlock bit in color registers */
+static uae_u8 color_regs_aga_genlock[256];
+
+/*
+* Hardware registers of all sorts.
+*/
+
+static int REGPARAM3 custom_wput_1 (int, uaecptr, uae_u32, int) REGPARAM;
+
+static uae_u16 cregs[256];
+
+uae_u16 intena, intreq;
+uae_u16 dmacon;
+uae_u16 adkcon; /* used by audio code */
+
+static uae_u32 cop1lc, cop2lc, copcon;
+
+int maxhpos = MAXHPOS_PAL;
+int maxhpos_short = MAXHPOS_PAL;
+int maxvpos = MAXVPOS_PAL;
+int maxvpos_nom = MAXVPOS_PAL; // nominal value (same as maxvpos but "faked" maxvpos in fake 60hz modes)
+int hsyncendpos, hsyncstartpos;
+static int maxvpos_total = 511;
+int minfirstline = VBLANK_ENDLINE_PAL;
+int equ_vblank_endline = EQU_ENDLINE_PAL;
+double vblank_hz = VBLANK_HZ_PAL, fake_vblank_hz, vblank_hz_stored;
+int vblank_skip, doublescan;
+frame_time_t syncbase;
+static int fmode;
+uae_u16 beamcon0, new_beamcon0;
+uae_u16 vtotal = MAXVPOS_PAL, htotal = MAXHPOS_PAL;
+static int maxvpos_stored, maxhpos_stored;
+static uae_u16 hsstop, hbstrt, hbstop, vsstop, vbstrt, vbstop, hsstrt, vsstrt, hcenter;
+static int ciavsyncmode;
+static int diw_hstrt, diw_hstop;
+static int diw_hcounter;
+
+#define HSYNCTIME (maxhpos * CYCLE_UNIT);
+
+/* This is but an educated guess. It seems to be correct, but this stuff
+* isn't documented well. */
+struct sprite {
+       uaecptr pt;
+       int xpos;
+       int vstart;
+       int vstop;
+       int dblscan; /* AGA SSCAN2 */
+       int armed;
+       int dmastate;
+       int dmacycle;
+       int ptxhpos;
+};
+
+static struct sprite spr[MAX_SPRITES];
+static int plfstrt_sprite;
+
+uaecptr sprite_0;
+int sprite_0_width, sprite_0_height, sprite_0_doubled;
+uae_u32 sprite_0_colors[4];
+static uae_u8 magic_sprite_mask = 0xff;
+
+static int sprite_vblank_endline = VBLANK_SPRITE_PAL;
+
+static unsigned int sprctl[MAX_SPRITES], sprpos[MAX_SPRITES];
+#ifdef AGA
+static uae_u16 sprdata[MAX_SPRITES][4], sprdatb[MAX_SPRITES][4];
+#else
+static uae_u16 sprdata[MAX_SPRITES][1], sprdatb[MAX_SPRITES][1];
+#endif
+static int sprite_last_drawn_at[MAX_SPRITES];
+static int last_sprite_point, nr_armed;
+static int sprite_width, sprres;
+int sprite_buffer_res;
+
+#ifdef CPUEMU_12
+uae_u8 cycle_line[256];
+#endif
+
+static uae_u16 bplxdat[8];
+static bool bpl1dat_written, bpl1dat_early, bpl1dat_done;
+static uae_s16 bpl1mod, bpl2mod;
+static uaecptr prevbpl[2][MAXVPOS][8];
+static uaecptr bplpt[8], bplptx[8];
+
+/*static int blitcount[256];  blitter debug */
+
+static struct color_entry current_colors;
+static unsigned int bplcon0, bplcon1, bplcon2, bplcon3, bplcon4;
+static unsigned int bplcon0d, bplcon0dd, bplcon0_res, bplcon0_planes, bplcon0_planes_limit;
+static unsigned int diwstrt, diwstop, diwhigh;
+static int diwhigh_written;
+static unsigned int ddfstrt, ddfstop, ddfstrt_old_hpos;
+static int ddf_change, badmode, diw_change;
+static int bplcon1_hpos;
+
+/* The display and data fetch windows */
+
+enum diw_states
+{
+       DIW_waiting_start, DIW_waiting_stop
+};
+
+static int plffirstline, plflastline;
+int plffirstline_total, plflastline_total;
+static int autoscale_bordercolors;
+static int plfstrt_start, plfstrt, plfstop;
+static int sprite_minx, sprite_maxx;
+static int first_bpl_vpos;
+static int last_ddf_pix_hpos;
+static int last_decide_line_hpos;
+static int last_fetch_hpos, last_sprite_hpos;
+static int diwfirstword, diwlastword;
+static int plfleft_real;
+static int last_hdiw;
+static enum diw_states diwstate, hdiwstate, ddfstate;
+int first_planes_vpos, last_planes_vpos;
+int diwfirstword_total, diwlastword_total;
+int ddffirstword_total, ddflastword_total;
+int firstword_bplcon1;
+
+static int last_copper_hpos;
+static int copper_access;
+
+/* Sprite collisions */
+static unsigned int clxdat, clxcon, clxcon2, clxcon_bpl_enable, clxcon_bpl_match;
+
+enum copper_states {
+       COP_stop,
+       COP_read1,
+       COP_read2,
+       COP_bltwait,
+       COP_wait_in2,
+       COP_skip_in2,
+       COP_wait1,
+       COP_wait,
+       COP_skip1,
+       COP_strobe_delay1,
+       COP_strobe_delay2,
+       COP_strobe_extra, // just to skip current cycle when CPU wrote to COPJMP
+       COP_start_delay
+};
+
+struct copper {
+       /* The current instruction words.  */
+       unsigned int i1, i2;
+       unsigned int saved_i1, saved_i2;
+       enum copper_states state, state_prev;
+       /* Instruction pointer.  */
+       uaecptr ip, saved_ip;
+       int hpos, vpos;
+       unsigned int ignore_next;
+       int vcmp, hcmp;
+
+       int strobe; /* COPJMP1 / COPJMP2 accessed */
+       int last_write, last_write_hpos;
+       int moveaddr, movedata, movedelay;
+};
+
+static struct copper cop_state;
+static int copper_enabled_thisline;
+static int cop_min_waittime;
+
+/*
+* Statistics
+*/
+unsigned long int frametime = 0, lastframetime = 0, timeframes = 0;
+unsigned long hsync_counter = 0, vsync_counter = 0;
+unsigned long int idletime;
+int bogusframe;
+
+/* Recording of custom chip register changes.  */
+static int current_change_set;
+static struct sprite_entry sprite_entries[2][MAX_SPR_PIXELS / 16];
+static struct color_change color_changes[2][MAX_REG_CHANGE];
+
+struct decision line_decisions[2 * (MAXVPOS + 2) + 1];
+static struct draw_info line_drawinfo[2][2 * (MAXVPOS + 2) + 1];
+#define COLOR_TABLE_SIZE (MAXVPOS + 2) * 2
+static struct color_entry color_tables[2][COLOR_TABLE_SIZE];
+
+static int next_sprite_entry = 0;
+static int prev_next_sprite_entry;
+static int next_sprite_forced = 1;
+
+struct sprite_entry *curr_sprite_entries, *prev_sprite_entries;
+struct color_change *curr_color_changes, *prev_color_changes;
+struct draw_info *curr_drawinfo, *prev_drawinfo;
+struct color_entry *curr_color_tables, *prev_color_tables;
+
+static int next_color_change;
+static int next_color_entry, remembered_color_entry;
+static int color_src_match, color_dest_match, color_compare_result;
+
+static uae_u32 thisline_changed;
+
+#ifdef SMART_UPDATE
+#define MARK_LINE_CHANGED do { thisline_changed = 1; } while (0)
+#else
+#define MARK_LINE_CHANGED do { ; } while (0)
+#endif
+
+static struct decision thisline_decision;
+static int fetch_cycle, fetch_modulo_cycle;
+
+enum plfstate
+{
+       plf_idle,
+       plf_start,
+       plf_active,
+       plf_passed_stop,
+       plf_passed_stop2,
+       plf_end
+} plf_state;
+
+enum fetchstate {
+       fetch_not_started,
+       fetch_started,
+       fetch_was_plane0
+} fetch_state;
+
+/*
+* helper functions
+*/
+
+STATIC_INLINE int ecsshres(void)
+{
+       return bplcon0_res == RES_SUPERHIRES && (currprefs.chipset_mask & CSMASK_ECS_DENISE) && !(currprefs.chipset_mask & CSMASK_AGA);
+}
+
+STATIC_INLINE int nodraw (void)
+{
+       return !currprefs.cpu_cycle_exact && framecnt != 0;
+}
+
+static int doflickerfix (void)
+{
+       return currprefs.gfx_vresolution && doublescan < 0 && vpos < MAXVPOS;
+}
+
+uae_u32 get_copper_address (int copno)
+{
+       switch (copno) {
+       case 1: return cop1lc;
+       case 2: return cop2lc;
+       case -1: return cop_state.ip;
+       default: return 0;
+       }
+}
+
+int rpt_available = 0;
+
+void reset_frame_rate_hack (void)
+{
+       if (currprefs.m68k_speed != -1)
+               return;
+
+       if (! rpt_available) {
+               currprefs.m68k_speed = 0;
+               return;
+       }
+
+       rpt_did_reset = 1;
+       is_lastline = 0;
+       vsyncmintime = read_processor_time () + vsynctime;
+       write_log (L"Resetting frame rate hack\n");
+}
+
+STATIC_INLINE void setclr (uae_u16 *p, uae_u16 val)
+{
+       if (val & 0x8000)
+               *p |= val & 0x7FFF;
+       else
+               *p &= ~val;
+}
+
+STATIC_INLINE void alloc_cycle (int hpos, int type)
+{
+#ifdef CPUEMU_12
+#if 0
+       if (cycle_line[hpos])
+               write_log (L"hpos=%d, old=%d, new=%d\n", hpos, cycle_line[hpos], type);
+       if ((type == CYCLE_CPU || type == CYCLE_COPPER) && (hpos & 1))
+               write_log (L"odd %d cycle %d\n", hpos);
+       if (!(hpos & 1) && (type == CYCLE_SPRITE || type == CYCLE_REFRESH || type == CYCLE_MISC))
+               write_log (L"even %d cycle %d\n", type, hpos);
+#endif
+       cycle_line[hpos] = type;
+#endif
+}
+STATIC_INLINE void alloc_cycle_maybe (int hpos, int type)
+{
+       if (cycle_line[hpos] == 0)
+               alloc_cycle (hpos, type);
+}
+
+void alloc_cycle_ext(int hpos, int type)
+{
+       alloc_cycle (hpos, type);
+}
+
+static void hsyncdelay (void)
+{
+#if 0
+       static int prevhpos;
+       while (current_hpos () == prevhpos)
+               do_cycles(CYCLE_UNIT);
+       prevhpos = current_hpos();
+#endif
+}
+
+static void update_mirrors (void)
+{
+       aga_mode = (currprefs.chipset_mask & CSMASK_AGA) ? 1 : 0;
+       direct_rgb = aga_mode;
+}
+
+STATIC_INLINE uae_u8 *pfield_xlateptr (uaecptr plpt, int bytecount)
+{
+       if (!chipmem_check_indirect (plpt, bytecount)) {
+               static int count = 0;
+               if (!count)
+                       count++, write_log (L"Warning: Bad playfield pointer\n");
+               return NULL;
+       }
+       return chipmem_xlate_indirect (plpt);
+}
+
+STATIC_INLINE void docols (struct color_entry *colentry)
+{
+       int i;
+
+#ifdef AGA
+       if (currprefs.chipset_mask & CSMASK_AGA) {
+               for (i = 0; i < 256; i++) {
+                       int v = color_reg_get (colentry, i);
+                       if (v < 0 || v > 16777215)
+                               continue;
+                       colentry->acolors[i] = getxcolor (v);
+               }
+       } else {
+#endif
+               for (i = 0; i < 32; i++) {
+                       int v = color_reg_get (colentry, i);
+                       if (v < 0 || v > 4095)
+                               continue;
+                       colentry->acolors[i] = getxcolor (v);
+               }
+#ifdef AGA
+       }
+#endif
+}
+
+extern struct color_entry colors_for_drawing;
+
+void notice_new_xcolors (void)
+{
+       int i;
+
+       update_mirrors ();
+       docols (&current_colors);
+       docols (&colors_for_drawing);
+       for (i = 0; i < (MAXVPOS + 1) * 2; i++) {
+               docols (color_tables[0] + i);
+               docols (color_tables[1] + i);
+       }
+}
+
+static void do_sprites (int currhp);
+
+static void remember_ctable (void)
+{
+       /* This can happen when program program crashes very badly */
+       if (next_color_entry >= COLOR_TABLE_SIZE)
+               return;
+       if (remembered_color_entry == -1) {
+               /* The colors changed since we last recorded a color map. Record a
+               * new one. */
+               color_reg_cpy (curr_color_tables + next_color_entry, &current_colors);
+               remembered_color_entry = next_color_entry++;
+       }
+       thisline_decision.ctable = remembered_color_entry;
+       if (color_src_match == -1 || color_dest_match != remembered_color_entry
+               || line_decisions[next_lineno].ctable != color_src_match)
+       {
+               /* The remembered comparison didn't help us - need to compare again. */
+               int oldctable = line_decisions[next_lineno].ctable;
+               int changed = 0;
+
+               if (oldctable == -1) {
+                       changed = 1;
+                       color_src_match = color_dest_match = -1;
+               } else {
+                       color_compare_result = color_reg_cmp (&prev_color_tables[oldctable], &current_colors) != 0;
+                       if (color_compare_result)
+                               changed = 1;
+                       color_src_match = oldctable;
+                       color_dest_match = remembered_color_entry;
+               }
+               thisline_changed |= changed;
+       } else {
+               /* We know the result of the comparison */
+               if (color_compare_result)
+                       thisline_changed = 1;
+       }
+}
+
+static void remember_ctable_for_border (void)
+{
+       remember_ctable ();
+}
+
+/* Called to determine the state of the horizontal display window state
+* machine at the current position. It might have changed since we last
+* checked.  */
+static void decide_diw (int hpos)
+{
+       /* Last hpos = hpos + 0.5, eg. normal PAL end hpos is 227.5 * 2 = 455
+          OCS Denise: 9 bit hdiw counter does not reset during lines 0 to 9
+          (PAL) or lines 0 to 10 (NTSC). A1000 PAL: 1 to 9, NTSC: 1 to 10.
+          ECS Denise and AGA: no above "features"
+       */
+
+       int hdiw = hpos >= maxhpos ? maxhpos * 2 + 1 : hpos * 2 + 2;
+       if (!(currprefs.chipset_mask & CSMASK_ECS_DENISE) && vpos <= equ_vblank_endline) {
+               hdiw = diw_hcounter;
+               hdiw &= 511;
+       }
+       for (;;) {
+               int lhdiw = hdiw;
+               if (last_hdiw > lhdiw)
+                       lhdiw = 512;
+
+               if (lhdiw >= diw_hstrt && last_hdiw < diw_hstrt && hdiwstate == DIW_waiting_start) {
+                       if (thisline_decision.diwfirstword == -1)
+                               thisline_decision.diwfirstword = diwfirstword < 0 ? 0 : diwfirstword;
+                       hdiwstate = DIW_waiting_stop;
+               }
+               if (lhdiw >= diw_hstop && last_hdiw < diw_hstop && hdiwstate == DIW_waiting_stop) {
+                       if (thisline_decision.diwlastword == -1)
+                               thisline_decision.diwlastword = diwlastword < 0 ? 0 : diwlastword;
+                       hdiwstate = DIW_waiting_start;
+               }
+               if (lhdiw != 512)
+                       break;
+               last_hdiw = 0 - 1;
+       }
+       last_hdiw = hdiw;
+}
+
+static int fetchmode;
+static int real_bitplane_number[3][3][9];
+
+/* Disable bitplane DMA if planes > available DMA slots. This is needed
+e.g. by the Sanity WOC demo (at the "Party Effect").  */
+STATIC_INLINE int GET_PLANES_LIMIT (uae_u16 bc0)
+{
+       int res = GET_RES_AGNUS (bc0);
+       int planes = GET_PLANES (bc0);
+       return real_bitplane_number[fetchmode][res][planes];
+}
+
+/* The HRM says 0xD8, but that can't work... */
+#define HARD_DDF_STOP 0xd4
+#define HARD_DDF_START 0x18
+
+static void add_modulos (void)
+{
+       int m1, m2;
+
+       if (fmode & 0x4000) {
+               if (((diwstrt >> 8) ^ vpos) & 1)
+                       m1 = m2 = bpl2mod;
+               else
+                       m1 = m2 = bpl1mod;
+       } else {
+               m1 = bpl1mod;
+               m2 = bpl2mod;
+       }
+
+       switch (bplcon0_planes_limit) {
+#ifdef AGA
+       case 8: bplpt[7] += m2; bplptx[7] += m2;
+       case 7: bplpt[6] += m1; bplptx[6] += m1;
+#endif
+       case 6: bplpt[5] += m2; bplptx[5] += m2;
+       case 5: bplpt[4] += m1; bplptx[4] += m1;
+       case 4: bplpt[3] += m2; bplptx[3] += m2;
+       case 3: bplpt[2] += m1; bplptx[2] += m1;
+       case 2: bplpt[1] += m2; bplptx[1] += m2;
+       case 1: bplpt[0] += m1; bplptx[0] += m1;
+       }
+}
+
+static void finish_playfield_line (void)
+{
+       /* The latter condition might be able to happen in interlaced frames. */
+       if (vpos >= minfirstline && (thisframe_first_drawn_line == -1 || vpos < thisframe_first_drawn_line))
+               thisframe_first_drawn_line = vpos;
+       thisframe_last_drawn_line = vpos;
+
+#ifdef SMART_UPDATE
+       if (line_decisions[next_lineno].plflinelen != thisline_decision.plflinelen
+               || line_decisions[next_lineno].plfleft != thisline_decision.plfleft
+               || line_decisions[next_lineno].bplcon0 != thisline_decision.bplcon0
+               || line_decisions[next_lineno].bplcon2 != thisline_decision.bplcon2
+#ifdef ECS_DENISE
+               || line_decisions[next_lineno].bplcon3 != thisline_decision.bplcon3
+#endif
+#ifdef AGA
+               || line_decisions[next_lineno].bplcon4 != thisline_decision.bplcon4
+#endif
+               )
+#endif /* SMART_UPDATE */
+               thisline_changed = 1;
+}
+
+/* The fetch unit mainly controls ddf stop.  It's the number of cycles that
+are contained in an indivisible block during which ddf is active.  E.g.
+if DDF starts at 0x30, and fetchunit is 8, then possible DDF stops are
+0x30 + n * 8.  */
+static int fetchunit, fetchunit_mask;
+/* The delay before fetching the same bitplane again.  Can be larger than
+the number of bitplanes; in that case there are additional empty cycles
+with no data fetch (this happens for high fetchmodes and low
+resolutions).  */
+static int fetchstart, fetchstart_shift, fetchstart_mask;
+/* fm_maxplane holds the maximum number of planes possible with the current
+fetch mode.  This selects the cycle diagram:
+8 planes: 73516240
+4 planes: 3120
+2 planes: 10.  */
+static int fm_maxplane, fm_maxplane_shift;
+
+/* The corresponding values, by fetchmode and display resolution.  */
+static const int fetchunits[] = { 8,8,8,0, 16,8,8,0, 32,16,8,0 };
+static const int fetchstarts[] = { 3,2,1,0, 4,3,2,0, 5,4,3,0 };
+static const int fm_maxplanes[] = { 3,2,1,0, 3,3,2,0, 3,3,3,0 };
+
+static int cycle_diagram_table[3][3][9][32];
+static int cycle_diagram_free_cycles[3][3][9];
+static int cycle_diagram_total_cycles[3][3][9];
+static int *curr_diagram;
+static const int cycle_sequences[3 * 8] = { 2,1,2,1,2,1,2,1, 4,2,3,1,4,2,3,1, 8,4,6,2,7,3,5,1 };
+
+static void debug_cycle_diagram (void)
+{
+       int fm, res, planes, cycle, v;
+       TCHAR aa;
+
+       for (fm = 0; fm <= 2; fm++) {
+               write_log (L"FMODE %d\n=======\n", fm);
+               for (res = 0; res <= 2; res++) {
+                       for (planes = 0; planes <= 8; planes++) {
+                               write_log (L"%d: ",planes);
+                               for (cycle = 0; cycle < 32; cycle++) {
+                                       v=cycle_diagram_table[fm][res][planes][cycle];
+                                       if (v==0) aa='-'; else if(v>0) aa='1'; else aa='X';
+                                       write_log (L"%c",aa);
+                               }
+                               write_log (L" %d:%d\n",
+                                       cycle_diagram_free_cycles[fm][res][planes], cycle_diagram_total_cycles[fm][res][planes]);
+                       }
+                       write_log (L"\n");
+               }
+       }
+       fm=0;
+}
+
+static void create_cycle_diagram_table (void)
+{
+       int fm, res, cycle, planes, rplanes, v;
+       int fetch_start, max_planes, freecycles;
+       const int *cycle_sequence;
+
+       for (fm = 0; fm <= 2; fm++) {
+               for (res = 0; res <= 2; res++) {
+                       max_planes = fm_maxplanes[fm * 4 + res];
+                       fetch_start = 1 << fetchstarts[fm * 4 + res];
+                       cycle_sequence = &cycle_sequences[(max_planes - 1) * 8];
+                       max_planes = 1 << max_planes;
+                       for (planes = 0; planes <= 8; planes++) {
+                               freecycles = 0;
+                               for (cycle = 0; cycle < 32; cycle++)
+                                       cycle_diagram_table[fm][res][planes][cycle] = -1;
+                               if (planes <= max_planes) {
+                                       for (cycle = 0; cycle < fetch_start; cycle++) {
+                                               if (cycle < max_planes && planes >= cycle_sequence[cycle & 7]) {
+                                                       v = cycle_sequence[cycle & 7];
+                                               } else {
+                                                       v = 0;
+                                                       freecycles++;
+                                               }
+                                               cycle_diagram_table[fm][res][planes][cycle] = v;
+                                       }
+                               }
+                               cycle_diagram_free_cycles[fm][res][planes] = freecycles;
+                               cycle_diagram_total_cycles[fm][res][planes] = fetch_start;
+                               rplanes = planes;
+                               if (rplanes > max_planes)
+                                       rplanes = 0;
+                               if (rplanes == 7 && fm == 0 && res == 0 && !(currprefs.chipset_mask & CSMASK_AGA))
+                                       rplanes = 4;
+                               real_bitplane_number[fm][res][planes] = rplanes;
+                       }
+               }
+       }
+#if 0
+       debug_cycle_diagram ();
+#endif
+}
+
+
+/* Used by the copper.  */
+static int estimated_last_fetch_cycle;
+static int cycle_diagram_shift;
+
+static void estimate_last_fetch_cycle (int hpos)
+{
+       int fetchunit = fetchunits[fetchmode * 4 + bplcon0_res];
+
+       if (plf_state < plf_passed_stop) {
+               int stop = plfstop < hpos || plfstop > HARD_DDF_STOP ? HARD_DDF_STOP : plfstop;
+               /* We know that fetching is up-to-date up until hpos, so we can use fetch_cycle.  */
+               int fetch_cycle_at_stop = fetch_cycle + (stop - hpos);
+               int starting_last_block_at = (fetch_cycle_at_stop + fetchunit - 1) & ~(fetchunit - 1);
+
+               estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit;
+       } else {
+               int starting_last_block_at = (fetch_cycle + fetchunit - 1) & ~(fetchunit - 1);
+               if (plf_state == plf_passed_stop2)
+                       starting_last_block_at -= fetchunit;
+
+               estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit;
+       }
+}
+
+static uae_u32 outword[MAX_PLANES];
+static int out_nbits, out_offs;
+static uae_u32 todisplay[MAX_PLANES][4];
+static uae_u32 fetched[MAX_PLANES];
+#ifdef AGA
+static uae_u32 fetched_aga0[MAX_PLANES];
+static uae_u32 fetched_aga1[MAX_PLANES];
+#endif
+
+/* Expansions from bplcon0/bplcon1.  */
+static int toscr_res, toscr_nr_planes, toscr_nr_planes2, fetchwidth;
+static int toscr_delay1, toscr_delay2;
+
+/* The number of bits left from the last fetched words.
+This is an optimization - conceptually, we have to make sure the result is
+the same as if toscr is called in each clock cycle.  However, to speed this
+up, we accumulate display data; this variable keeps track of how much.
+Thus, once we do call toscr_nbits (which happens at least every 16 bits),
+we can do more work at once.  */
+static int toscr_nbits;
+
+/* undocumented bitplane delay hardware feature */
+static int delayoffset;
+
+STATIC_INLINE void compute_delay_offset (void)
+{
+       delayoffset = (16 << fetchmode) - (((plfstrt - HARD_DDF_START) & fetchstart_mask) << 1);
+#if 0
+       /* maybe we can finally get rid of this stupid table.. */
+       if (tmp == 4)
+               delayoffset = 4; // Loons Docs
+       else if (tmp == 8)
+               delayoffset = 8;
+       else if (tmp == 12) // Loons Docs
+               delayoffset = 4;
+       else if (tmp == 16) /* Overkill AGA */
+               delayoffset = 48;
+       else if (tmp == 24) /* AB 2 */
+               delayoffset = 8;
+       else if (tmp == 32)
+               delayoffset = 32;
+       else if (tmp == 48) /* Pinball Illusions AGA, ingame */
+               delayoffset = 16;
+       else /* what about 40 and 56? */
+               delayoffset = 0;
+       //write_log (L"%d:%d ", vpos, delayoffset);
+#endif
+}
+
+static void record_color_change2 (int hpos, int regno, unsigned long value)
+{
+       int pos = hpos * 2;
+       if (regno == 0x1000 + 0x10c)
+               pos++; // BPLCON4 change needs 1 lores pixel delay
+       curr_color_changes[next_color_change].linepos = pos;
+       curr_color_changes[next_color_change].regno = regno;
+       curr_color_changes[next_color_change++].value = value;
+       curr_color_changes[next_color_change].regno = -1;
+}
+
+static int isehb (uae_u16 bplcon0, uae_u16 bplcon2)
+{
+       int bplehb;
+       if (currprefs.chipset_mask & CSMASK_AGA)
+               bplehb = (bplcon0 & 0x7010) == 0x6000;
+       else if (currprefs.chipset_mask & CSMASK_ECS_DENISE)
+               bplehb = ((bplcon0 & 0xFC00) == 0x6000 || (bplcon0 & 0xFC00) == 0x7000);
+       else
+               bplehb = ((bplcon0 & 0xFC00) == 0x6000 || (bplcon0 & 0xFC00) == 0x7000) && !currprefs.cs_denisenoehb;
+       return bplehb;
+}
+
+// OCS/ECS, lores, 7 planes = 4 "real" planes + BPL5DAT and BPL6DAT as static 5th and 6th plane
+STATIC_INLINE int isocs7planes (void)
+{
+       return !(currprefs.chipset_mask & CSMASK_AGA) && bplcon0_res == 0 && bplcon0_planes == 7;
+}
+
+int is_bitplane_dma (int hpos)
+{
+       if (fetch_state == fetch_not_started || hpos < plfstrt)
+               return 0;
+       if ((plf_state == plf_end && hpos >= thisline_decision.plfright)
+               || hpos >= estimated_last_fetch_cycle)
+               return 0;
+       return curr_diagram[(hpos - cycle_diagram_shift) & fetchstart_mask];
+}
+
+STATIC_INLINE int is_bitplane_dma_inline (int hpos)
+{
+       if (fetch_state == fetch_not_started || hpos < plfstrt)
+               return 0;
+       if ((plf_state == plf_end && hpos >= thisline_decision.plfright)
+               || hpos >= estimated_last_fetch_cycle)
+               return 0;
+       return curr_diagram[(hpos - cycle_diagram_shift) & fetchstart_mask];
+}
+
+static void update_denise (int hpos)
+{
+       toscr_res = GET_RES_DENISE (bplcon0d);
+       if (bplcon0dd != bplcon0d) {
+               record_color_change2 (hpos, 0x100 + 0x1000, bplcon0d);
+               bplcon0dd = bplcon0d;
+       }
+       toscr_nr_planes = GET_PLANES (bplcon0d);
+       if (isocs7planes ()) {
+               if (toscr_nr_planes2 < 6)
+                       toscr_nr_planes2 = 6;
+       } else {
+               toscr_nr_planes2 = toscr_nr_planes;
+       }
+}
+
+static int bpldmasetuphpos;
+static int bpldmasetupphase;
+
+/* set currently active Agnus bitplane DMA sequence */
+static void setup_fmodes (int hpos)
+{
+       switch (fmode & 3)
+       {
+       case 0:
+               fetchmode = 0;
+               break;
+       case 1:
+       case 2:
+               fetchmode = 1;
+               break;
+       case 3:
+               fetchmode = 2;
+               break;
+       }
+       badmode = GET_RES_AGNUS (bplcon0) != GET_RES_DENISE (bplcon0);
+       bplcon0_res = GET_RES_AGNUS (bplcon0);
+       bplcon0_planes = GET_PLANES (bplcon0);
+       bplcon0_planes_limit = GET_PLANES_LIMIT (bplcon0);
+       fetchunit = fetchunits[fetchmode * 4 + bplcon0_res];
+       fetchunit_mask = fetchunit - 1;
+       fetchstart_shift = fetchstarts[fetchmode * 4 + bplcon0_res];
+       fetchstart = 1 << fetchstart_shift;
+       fetchstart_mask = fetchstart - 1;
+       fm_maxplane_shift = fm_maxplanes[fetchmode * 4 + bplcon0_res];
+       fm_maxplane = 1 << fm_maxplane_shift;
+       fetch_modulo_cycle = fetchunit - fetchstart;
+       if (is_bitplane_dma (hpos - 1))
+               cycle_line[hpos - 1] = 1;
+       curr_diagram = cycle_diagram_table[fetchmode][bplcon0_res][bplcon0_planes_limit];
+       estimate_last_fetch_cycle (hpos);
+       if (bpldmasetuphpos >= 0 && debug_dma)
+               record_dma_event (DMA_EVENT_BPLFETCHUPDATE, hpos, vpos);
+       bpldmasetuphpos = -1;
+       bpldmasetupphase = 0;
+       ddf_change = vpos;
+}
+
+static void BPLCON0_Denise (int hpos, uae_u16 v);
+
+// writing to BPLCON0 adds 4 cycle delay before Agnus bitplane DMA sequence changes
+// (Note that Denise sees the change after 1 cycle)
+// AGA needs extra cycle in some specific situations (Brian The Lion "dialog") but not
+// in all situations (Superstardust weapon panel)
+#define BPLCON_AGNUS_DELAY (4 + (bplcon0_planes == 8 ? 1 : 0))
+#define BPLCON_DENISE_DELAY 1
+
+static void maybe_setup_fmodes (int hpos)
+{
+       switch (bpldmasetupphase)
+       {
+       case 0:
+               BPLCON0_Denise (hpos, bplcon0);
+               bpldmasetupphase++;
+               bpldmasetuphpos += BPLCON_AGNUS_DELAY - BPLCON_DENISE_DELAY;
+               break;
+       case 1:
+               setup_fmodes (hpos);
+               break;
+       }
+}
+
+STATIC_INLINE void maybe_check (int hpos)
+{
+       if (bpldmasetuphpos > 0 && hpos >= bpldmasetuphpos)
+               maybe_setup_fmodes (hpos);
+}
+
+static void bpldmainitdelay (int hpos)
+{
+       int hposa;
+
+       hposa = hpos + BPLCON_AGNUS_DELAY;
+       ddf_change = vpos;
+       if (hposa < 0x14) {
+               BPLCON0_Denise (hpos, bplcon0);
+               setup_fmodes (hpos);
+               return;
+       }
+       if (bpldmasetuphpos < 0) {
+               bpldmasetupphase = 0;
+               bpldmasetuphpos = hpos + BPLCON_DENISE_DELAY;
+       }
+}
+
+/* Expand bplcon0/bplcon1 into the toscr_xxx variables.  */
+static void compute_toscr_delay_1 (int bplcon1)
+{
+       int delay1 = (bplcon1 & 0x0f) | ((bplcon1 & 0x0c00) >> 6);
+       int delay2 = ((bplcon1 >> 4) & 0x0f) | (((bplcon1 >> 4) & 0x0c00) >> 6);
+       int shdelay1 = (bplcon1 >> 12) & 3;
+       int shdelay2 = (bplcon1 >> 8) & 3;
+       int delaymask;
+       int fetchwidth = 16 << fetchmode;
+
+       delay1 += delayoffset;
+       delay2 += delayoffset;
+       delaymask = (fetchwidth - 1) >> toscr_res;
+       toscr_delay1 = (delay1 & delaymask) << toscr_res;
+       toscr_delay1 |= shdelay1 >> (RES_MAX - toscr_res);
+       toscr_delay2 = (delay2 & delaymask) << toscr_res;
+       toscr_delay2 |= shdelay2 >> (RES_MAX - toscr_res);
+}
+
+static void compute_toscr_delay (int hpos, int bplcon1)
+{
+       update_denise (hpos);
+       compute_toscr_delay_1 (bplcon1);
+}
+
+STATIC_INLINE void clear_fetchbuffer (uae_u32 *ptr, int nwords)
+{
+       int i;
+
+       if (! thisline_changed) {
+               for (i = 0; i < nwords; i++) {
+                       if (ptr[i]) {
+                               thisline_changed = 1;
+                               break;
+                       }
+               }
+       }
+       memset (ptr, 0, nwords * 4);
+}
+
+static void update_toscr_planes (void)
+{
+       if (toscr_nr_planes2 > thisline_decision.nr_planes) {
+               int j;
+               for (j = thisline_decision.nr_planes; j < toscr_nr_planes2; j++)
+                       clear_fetchbuffer ((uae_u32 *)(line_data[next_lineno] + 2 * MAX_WORDS_PER_LINE * j), out_offs);
+               thisline_decision.nr_planes = toscr_nr_planes2;
+       }
+}
+
+STATIC_INLINE void maybe_first_bpl1dat (int hpos)
+{
+       if (thisline_decision.plfleft != -1) {
+               // early bpl1dat crap fix (Sequential engine animation)
+               if (plfleft_real == -1) {
+                       int i;
+                       for (i = 0; i < thisline_decision.nr_planes; i++) {
+                               todisplay[i][0] = 0;
+#ifdef AGA
+                               todisplay[i][1] = 0;
+                               todisplay[i][2] = 0;
+                               todisplay[i][3] = 0;
+#endif
+                       }
+                       plfleft_real = hpos;
+                       bpl1dat_early = 1;
+               }
+       } else {
+               plfleft_real = thisline_decision.plfleft = hpos;
+               compute_delay_offset ();
+       }
+}
+
+STATIC_INLINE void fetch (int nr, int fm, int hpos)
+{
+       if (nr < bplcon0_planes_limit) {
+               uaecptr p = bplpt[nr];
+               bplpt[nr] += 2 << fm;
+               bplptx[nr] += 2 << fm;
+               if (nr == 0)
+                       bpl1dat_written = 1;
+#ifdef DEBUGGER
+               if (debug_dma)
+                       record_dma (0x110 + nr * 2, chipmem_wget_indirect (p), p, hpos, vpos, DMARECORD_BITPLANE);
+#endif
+               switch (fm)
+               {
+               case 0:
+                       fetched[nr] = bplxdat[nr] = last_custom_value1 = chipmem_wget_indirect (p);
+                       break;
+#ifdef AGA
+               case 1:
+                       fetched_aga0[nr] = chipmem_lget_indirect (p);
+                       last_custom_value1 = (uae_u16)fetched_aga0[nr];
+                       break;
+               case 2:
+                       fetched_aga1[nr] = chipmem_lget_indirect (p);
+                       fetched_aga0[nr] = chipmem_lget_indirect (p + 4);
+                       last_custom_value1 = (uae_u16)fetched_aga0[nr];
+                       break;
+#endif
+               }
+               if (plf_state == plf_passed_stop2 && fetch_cycle >= (fetch_cycle & ~fetchunit_mask) + fetch_modulo_cycle) {
+                       int mod;
+                       if (fmode & 0x4000) {
+                               if (((diwstrt >> 8) ^ vpos) & 1)
+                                       mod = bpl2mod;
+                               else
+                                       mod = bpl1mod;
+                       } else if (nr & 1)
+                               mod = bpl2mod;
+                       else
+                               mod = bpl1mod;
+                       bplpt[nr] += mod;
+                       bplptx[nr] += mod;
+               }
+       } else {
+               // use whatever left in BPLxDAT if no DMA
+               // normally useless but "7-planes" feature won't work without this
+               fetched[nr] = bplxdat[nr];
+       }
+}
+
+STATIC_INLINE void toscr_3_ecs (int nbits)
+{
+       int delay1 = toscr_delay1;
+       int delay2 = toscr_delay2;
+       int i;
+       uae_u32 mask = 0xFFFF >> (16 - nbits);
+
+       for (i = 0; i < toscr_nr_planes2; i += 2) {
+               outword[i] <<= nbits;
+               outword[i] |= (todisplay[i][0] >> (16 - nbits + delay1)) & mask;
+               todisplay[i][0] <<= nbits;
+       }
+       for (i = 1; i < toscr_nr_planes2; i += 2) {
+               outword[i] <<= nbits;
+               outword[i] |= (todisplay[i][0] >> (16 - nbits + delay2)) & mask;
+               todisplay[i][0] <<= nbits;
+       }
+}
+
+STATIC_INLINE void shift32plus (uae_u32 *p, int n)
+{
+       uae_u32 t = p[1];
+       t <<= n;
+       t |= p[0] >> (32 - n);
+       p[1] = t;
+}
+
+#ifdef AGA
+STATIC_INLINE void aga_shift (uae_u32 *p, int n, int fm)
+{
+       if (fm == 2) {
+               shift32plus (p + 2, n);
+               shift32plus (p + 1, n);
+       }
+       shift32plus (p + 0, n);
+       p[0] <<= n;
+}
+
+STATIC_INLINE void toscr_3_aga (int nbits, int fm)
+{
+       int delay1 = toscr_delay1;
+       int delay2 = toscr_delay2;
+       int i;
+       uae_u32 mask = 0xFFFF >> (16 - nbits);
+
+       {
+               int offs = (16 << fm) - nbits + delay1;
+               int off1 = offs >> 5;
+               if (off1 == 3)
+                       off1 = 2;
+               offs -= off1 * 32;
+               for (i = 0; i < toscr_nr_planes2; i += 2) {
+                       uae_u32 t0 = todisplay[i][off1];
+                       uae_u32 t1 = todisplay[i][off1 + 1];
+                       uae_u64 t = (((uae_u64)t1) << 32) | t0;
+                       outword[i] <<= nbits;
+                       outword[i] |= (t >> offs) & mask;
+                       aga_shift (todisplay[i], nbits, fm);
+               }
+       }
+       {
+               int offs = (16 << fm) - nbits + delay2;
+               int off1 = offs >> 5;
+               if (off1 == 3)
+                       off1 = 2;
+               offs -= off1 * 32;
+               for (i = 1; i < toscr_nr_planes2; i += 2) {
+                       uae_u32 t0 = todisplay[i][off1];
+                       uae_u32 t1 = todisplay[i][off1 + 1];
+                       uae_u64 t = (((uae_u64)t1) << 32) | t0;
+                       outword[i] <<= nbits;
+                       outword[i] |= (t >> offs) & mask;
+                       aga_shift (todisplay[i], nbits, fm);
+               }
+       }
+}
+
+#endif
+
+static void toscr_2_0 (int nbits) { toscr_3_ecs (nbits); }
+#ifdef AGA
+static void toscr_2_1 (int nbits) { toscr_3_aga (nbits, 1); }
+static void toscr_2_2 (int nbits) { toscr_3_aga (nbits, 2); }
+#endif
+
+STATIC_INLINE void toscr_1 (int nbits, int fm)
+{
+       switch (fm) {
+       case 0:
+               toscr_2_0 (nbits);
+               break;
+#ifdef AGA
+       case 1:
+               toscr_2_1 (nbits);
+               break;
+       case 2:
+               toscr_2_2 (nbits);
+               break;
+#endif
+       }
+       out_nbits += nbits;
+       if (out_nbits == 32) {
+               int i;
+               uae_u8 *dataptr = line_data[next_lineno] + out_offs * 4;
+               for (i = 0; i < thisline_decision.nr_planes; i++) {
+                       uae_u32 *dataptr32 = (uae_u32 *)dataptr;
+                       if (*dataptr32 != outword[i]) {
+                               thisline_changed = 1;
+                               *dataptr32 = outword[i];
+                       }
+                       outword[i] = 0;
+                       dataptr += MAX_WORDS_PER_LINE * 2;
+               }
+               out_offs++;
+               out_nbits = 0;
+       }
+}
+
+static void toscr_fm0 (int);
+static void toscr_fm1 (int);
+static void toscr_fm2 (int);
+
+STATIC_INLINE void toscr (int nbits, int fm)
+{
+       switch (fm) {
+       case 0: toscr_fm0 (nbits); break;
+#ifdef AGA
+       case 1: toscr_fm1 (nbits); break;
+       case 2: toscr_fm2 (nbits); break;
+#endif
+       }
+}
+
+STATIC_INLINE void toscr_0 (int nbits, int fm)
+{
+       int t;
+
+       if (nbits > 16) {
+               toscr (16, fm);
+               nbits -= 16;
+       }
+
+       t = 32 - out_nbits;
+       if (t < nbits) {
+               toscr_1 (t, fm);
+               nbits -= t;
+       }
+       toscr_1 (nbits, fm);
+}
+
+static void toscr_fm0 (int nbits) { toscr_0 (nbits, 0); }
+static void toscr_fm1 (int nbits) { toscr_0 (nbits, 1); }
+static void toscr_fm2 (int nbits) { toscr_0 (nbits, 2); }
+
+static int flush_plane_data (int fm)
+{
+       int i = 0;
+
+       if (out_nbits <= 16) {
+               i += 16;
+               toscr_1 (16, fm);
+       }
+       if (out_nbits != 0) {
+               i += 32 - out_nbits;
+               toscr_1 (32 - out_nbits, fm);
+       }
+       i += 32;
+
+       toscr_1 (16, fm);
+       toscr_1 (16, fm);
+
+       if (fm == 2) {
+               /* flush AGA full 64-bit shift register */
+               i += 32;
+               toscr_1 (16, fm);
+               toscr_1 (16, fm);
+       }
+
+       if (bpl1dat_early) {
+               // clear possible crap in right border if
+               // bpl1dat was written "out of sync"
+               toscr_1 (16, fm);
+               toscr_1 (16, fm);
+       }
+
+       return i >> (1 + toscr_res);
+}
+
+STATIC_INLINE void flush_display (int fm)
+{
+       if (toscr_nbits > 0 && thisline_decision.plfleft != -1)
+               toscr (toscr_nbits, fm);
+       toscr_nbits = 0;
+}
+
+STATIC_INLINE void fetch_start (int hpos)
+{
+       fetch_state = fetch_started;
+}
+
+
+/* Called when all planes have been fetched, i.e. when a new block
+of data is available to be displayed.  The data in fetched[] is
+moved into todisplay[].  */
+STATIC_INLINE void beginning_of_plane_block (int hpos, int fm)
+{
+       int i;
+       int oleft = thisline_decision.plfleft;
+       static uae_u16 bplcon1t, bplcon1t2;
+
+       flush_display (fm);
+
+       if (fm == 0)
+               for (i = 0; i < MAX_PLANES; i++) {
+                       todisplay[i][0] |= fetched[i];
+               }
+#ifdef AGA
+       else
+               for (i = 0; i < MAX_PLANES; i++) {
+                       if (fm == 2)
+                               todisplay[i][1] = fetched_aga1[i];
+                       todisplay[i][0] = fetched_aga0[i];
+               }
+#endif
+
+               update_denise (hpos);
+               maybe_first_bpl1dat (hpos);
+
+               bplcon1t2 = bplcon1t;
+               bplcon1t = bplcon1;
+               // writing to BPLCON1 1 cycle after BPL1DAT access will
+               // not (except first BPL1DAT write) affect the display
+               // until next display block
+               if (bplcon1_hpos != hpos || oleft < 0)
+                       bplcon1t2 = bplcon1t;
+               compute_toscr_delay (hpos, bplcon1t2);
+}
+
+#ifdef SPEEDUP
+
+/* The usual inlining tricks - don't touch unless you know what you are doing. */
+STATIC_INLINE void long_fetch_ecs (int plane, int nwords, int weird_number_of_bits, int dma)
+{
+       uae_u16 *real_pt = (uae_u16 *)pfield_xlateptr (bplpt[plane], nwords * 2);
+       int delay = (plane & 1) ? toscr_delay2 : toscr_delay1;
+       int tmp_nbits = out_nbits;
+       uae_u32 shiftbuffer = todisplay[plane][0];
+       uae_u32 outval = outword[plane];
+       uae_u32 fetchval = fetched[plane];
+       uae_u32 *dataptr = (uae_u32 *)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE + 4 * out_offs);
+
+       if (dma) {
+               bplpt[plane] += nwords * 2;
+               bplptx[plane] += nwords * 2;
+       }
+
+       if (real_pt == 0)
+               /* @@@ Don't do this, fall back on chipmem_wget instead.  */
+               return;
+
+       while (nwords > 0) {
+               int bits_left = 32 - tmp_nbits;
+               uae_u32 t;
+
+               shiftbuffer |= fetchval;
+
+               t = (shiftbuffer >> delay) & 0xFFFF;
+
+               if (weird_number_of_bits && bits_left < 16) {
+                       outval <<= bits_left;
+                       outval |= t >> (16 - bits_left);
+                       thisline_changed |= *dataptr ^ outval;
+                       *dataptr++ = outval;
+
+                       outval = t;
+                       tmp_nbits = 16 - bits_left;
+                       shiftbuffer <<= 16;
+               } else {
+                       outval = (outval << 16) | t;
+                       shiftbuffer <<= 16;
+                       tmp_nbits += 16;
+                       if (tmp_nbits == 32) {
+                               thisline_changed |= *dataptr ^ outval;
+                               *dataptr++ = outval;
+                               tmp_nbits = 0;
+                       }
+               }
+               nwords--;
+               if (dma) {
+                       fetchval = do_get_mem_word (real_pt);
+                       real_pt++;
+               }
+       }
+       fetched[plane] = fetchval;
+       todisplay[plane][0] = shiftbuffer;
+       outword[plane] = outval;
+}
+
+#ifdef AGA
+STATIC_INLINE void long_fetch_aga (int plane, int nwords, int weird_number_of_bits, int fm, int dma)
+{
+       uae_u32 *real_pt = (uae_u32 *)pfield_xlateptr (bplpt[plane], nwords * 2);
+       int delay = (plane & 1) ? toscr_delay2 : toscr_delay1;
+       int tmp_nbits = out_nbits;
+       uae_u32 *shiftbuffer = todisplay[plane];
+       uae_u32 outval = outword[plane];
+       uae_u32 fetchval0 = fetched_aga0[plane];
+       uae_u32 fetchval1 = fetched_aga1[plane];
+       uae_u32 *dataptr = (uae_u32 *)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE + 4 * out_offs);
+       int offs = (16 << fm) - 16 + delay;
+       int off1 = offs >> 5;
+       if (off1 == 3)
+               off1 = 2;
+       offs -= off1 * 32;
+
+       if (dma) {
+               bplpt[plane] += nwords * 2;
+               bplptx[plane] += nwords * 2;
+       }
+
+       if (real_pt == 0)
+               /* @@@ Don't do this, fall back on chipmem_wget instead.  */
+               return;
+
+       while (nwords > 0) {
+               int i;
+
+               shiftbuffer[0] = fetchval0;
+               if (fm == 2)
+                       shiftbuffer[1] = fetchval1;
+
+               for (i = 0; i < (1 << fm); i++) {
+                       int bits_left = 32 - tmp_nbits;
+
+                       uae_u32 t0 = shiftbuffer[off1];
+                       uae_u32 t1 = shiftbuffer[off1 + 1];
+                       uae_u64 t = (((uae_u64)t1) << 32) | t0;
+
+                       t0 = (uae_u32)((t >> offs) & 0xFFFF);
+
+                       if (weird_number_of_bits && bits_left < 16) {
+                               outval <<= bits_left;
+                               outval |= t0 >> (16 - bits_left);
+
+                               thisline_changed |= *dataptr ^ outval;
+                               *dataptr++ = outval;
+
+                               outval = t0;
+                               tmp_nbits = 16 - bits_left;
+                               aga_shift (shiftbuffer, 16, fm);
+                       } else {
+                               outval = (outval << 16) | t0;
+                               aga_shift (shiftbuffer, 16, fm);
+                               tmp_nbits += 16;
+                               if (tmp_nbits == 32) {
+                                       thisline_changed |= *dataptr ^ outval;
+                                       *dataptr++ = outval;
+                                       tmp_nbits = 0;
+                               }
+                       }
+               }
+
+               nwords -= 1 << fm;
+
+               if (dma) {
+                       if (fm == 1)
+                               fetchval0 = do_get_mem_long (real_pt);
+                       else {
+                               fetchval1 = do_get_mem_long (real_pt);
+                               fetchval0 = do_get_mem_long (real_pt + 1);
+                       }
+                       real_pt += fm;
+               }
+       }
+       fetched_aga0[plane] = fetchval0;
+       fetched_aga1[plane] = fetchval1;
+       outword[plane] = outval;
+}
+#endif
+
+static void long_fetch_ecs_0 (int hpos, int nwords, int dma) { long_fetch_ecs (hpos, nwords, 0, dma); }
+static void long_fetch_ecs_1 (int hpos, int nwords, int dma) { long_fetch_ecs (hpos, nwords, 1, dma); }
+#ifdef AGA
+static void long_fetch_aga_1_0 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 0, 1, dma); }
+static void long_fetch_aga_1_1 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 1, 1, dma); }
+static void long_fetch_aga_2_0 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 0, 2, dma); }
+static void long_fetch_aga_2_1 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 1, 2, dma); }
+#endif
+
+static void do_long_fetch (int hpos, int nwords, int dma, int fm)
+{
+       int i;
+
+       flush_display (fm);
+       switch (fm) {
+       case 0:
+               if (out_nbits & 15) {
+                       for (i = 0; i < toscr_nr_planes; i++)
+                               long_fetch_ecs_1 (i, nwords, dma);
+               } else {
+                       for (i = 0; i < toscr_nr_planes; i++)
+                               long_fetch_ecs_0 (i, nwords, dma);
+               }
+               break;
+#ifdef AGA
+       case 1:
+               if (out_nbits & 15) {
+                       for (i = 0; i < toscr_nr_planes; i++)
+                               long_fetch_aga_1_1 (i, nwords, dma);
+               } else {
+                       for (i = 0; i < toscr_nr_planes; i++)
+                               long_fetch_aga_1_0 (i, nwords, dma);
+               }
+               break;
+       case 2:
+               if (out_nbits & 15) {
+                       for (i = 0; i < toscr_nr_planes; i++)
+                               long_fetch_aga_2_1 (i, nwords, dma);
+               } else {
+                       for (i = 0; i < toscr_nr_planes; i++)
+                               long_fetch_aga_2_0 (i, nwords, dma);
+               }
+               break;
+#endif
+       }
+
+       out_nbits += nwords * 16;
+       out_offs += out_nbits >> 5;
+       out_nbits &= 31;
+
+       if (dma && toscr_nr_planes > 0)
+               fetch_state = fetch_was_plane0;
+}
+
+#endif
+
+/* make sure fetch that goes beyond maxhpos is finished */
+static void finish_final_fetch (int pos, int fm)
+{
+       if (thisline_decision.plfleft == -1)
+               return;
+       if (plf_state == plf_end)
+               return;
+       plf_state = plf_end;
+       ddfstate = DIW_waiting_start;
+       pos += flush_plane_data (fm);
+       thisline_decision.plfright = pos;
+       thisline_decision.plflinelen = out_offs;
+       finish_playfield_line ();
+}
+
+STATIC_INLINE int one_fetch_cycle_0 (int pos, int ddfstop_to_test, int dma, int fm)
+{
+       if (plf_state < plf_passed_stop && pos == ddfstop_to_test)
+               plf_state = plf_passed_stop;
+
+       if ((fetch_cycle & fetchunit_mask) == 0) {
+               if (plf_state == plf_passed_stop2) {
+                       finish_final_fetch (pos, fm);
+                       return 1;
+               }
+               if (plf_state == plf_passed_stop) {
+                       plf_state = plf_passed_stop2;
+               } else if (plf_state == plf_passed_stop2) {
+                       plf_state = plf_end;
+               }
+
+       }
+
+       maybe_check (pos);
+
+       if (dma) {
+               /* fetchstart_mask can be larger than fm_maxplane if FMODE > 0.  This means
+               that the remaining cycles are idle; we'll fall through the whole switch
+               without doing anything.  */
+               int cycle_start = fetch_cycle & fetchstart_mask;
+               switch (fm_maxplane) {
+               case 8:
+                       switch (cycle_start) {
+                       case 0: fetch (7, fm, pos); break;
+                       case 1: fetch (3, fm, pos); break;
+                       case 2: fetch (5, fm, pos); break;
+                       case 3: fetch (1, fm, pos); break;
+                       case 4: fetch (6, fm, pos); break;
+                       case 5: fetch (2, fm, pos); break;
+                       case 6: fetch (4, fm, pos); break;
+                       case 7: fetch (0, fm, pos); break;
+                       }
+                       break;
+               case 4:
+                       switch (cycle_start) {
+                       case 0: fetch (3, fm, pos); break;
+                       case 1: fetch (1, fm, pos); break;
+                       case 2: fetch (2, fm, pos); break;
+                       case 3: fetch (0, fm, pos); break;
+                       }
+                       break;
+               case 2:
+                       switch (cycle_start) {
+                       case 0: fetch (1, fm, pos); break;
+                       case 1: fetch (0, fm, pos); break;
+                       }
+                       break;
+               }
+       }
+
+       if (bpl1dat_written) {
+               // do this here because if program plays with BPLCON0 during scanline
+               // it is possible that one DMA BPL1DAT write is completely missed
+               // and we must not draw anything at all in next dma block if this happens
+               // (Disposable Hero titlescreen)
+               fetch_state = fetch_was_plane0;
+               bpl1dat_written = 0;
+       }
+
+       fetch_cycle++;
+       toscr_nbits += 2 << toscr_res;
+
+       if (toscr_nbits > 16) {
+               uae_abort (L"toscr_nbits > 16 (%d)", toscr_nbits);
+               toscr_nbits = 0;
+       }
+       if (toscr_nbits == 16)
+               flush_display (fm);
+
+       return 0;
+}
+
+static int one_fetch_cycle_fm0 (int pos, int ddfstop_to_test, int dma) { return one_fetch_cycle_0 (pos, ddfstop_to_test, dma, 0); }
+static int one_fetch_cycle_fm1 (int pos, int ddfstop_to_test, int dma) { return one_fetch_cycle_0 (pos, ddfstop_to_test, dma, 1); }
+static int one_fetch_cycle_fm2 (int pos, int ddfstop_to_test, int dma) { return one_fetch_cycle_0 (pos, ddfstop_to_test, dma, 2); }
+
+STATIC_INLINE int one_fetch_cycle (int pos, int ddfstop_to_test, int dma, int fm)
+{
+       switch (fm) {
+       case 0: return one_fetch_cycle_fm0 (pos, ddfstop_to_test, dma);
+#ifdef AGA
+       case 1: return one_fetch_cycle_fm1 (pos, ddfstop_to_test, dma);
+       case 2: return one_fetch_cycle_fm2 (pos, ddfstop_to_test, dma);
+#endif
+       default: uae_abort (L"fm corrupt"); return 0;
+       }
+}
+
+static void update_fetch_x (int hpos, int fm)
+{
+       int pos;
+
+       if (nodraw ())
+               return;
+
+       if (hpos < 0x18)
+               return;
+       pos = last_fetch_hpos;
+       if (pos < 0x18)
+               pos = 0x18;
+       update_toscr_planes ();
+       for (int i = 0; i < 8; i++) {
+               fetched[i] = bplxdat[i];
+               bplxdat[i] = 0;
+       }
+       beginning_of_plane_block (hpos, fm);
+       for (; pos < hpos; pos++) {
+
+               toscr_nbits += 2 << toscr_res;
+
+               if (toscr_nbits > 16) {
+                       uae_abort (L"toscr_nbits > 16 (%d)", toscr_nbits);
+                       toscr_nbits = 0;
+               }
+               if (toscr_nbits == 16)
+                       flush_display (fm);
+
+       }
+       flush_display (fm);
+       bpl1dat_written = 0;
+}
+
+STATIC_INLINE void update_fetch (int until, int fm)
+{
+       int pos;
+       int dma = dmaen (DMA_BITPLANE);
+
+       int ddfstop_to_test;
+
+       if (nodraw () || plf_state == plf_end)
+               return;
+
+       /* We need an explicit test against HARD_DDF_STOP here to guard against
+       programs that move the DDFSTOP before our current position before we
+       reach it.  */
+       ddfstop_to_test = HARD_DDF_STOP;
+       if (ddfstop >= last_fetch_hpos && plfstop < ddfstop_to_test)
+               ddfstop_to_test = plfstop;
+
+       update_toscr_planes ();
+
+       pos = last_fetch_hpos;
+       cycle_diagram_shift = last_fetch_hpos - fetch_cycle;
+
+       /* First, a loop that prepares us for the speedup code.  We want to enter
+       the SPEEDUP case with fetch_state == fetch_was_plane0, and then unroll
+       whole blocks, so that we end on the same fetch_state again.  */
+       for (; ; pos++) {
+               if (pos == until) {
+                       if (until >= maxhpos) {
+                               finish_final_fetch (pos, fm);
+                               return;
+                       }
+                       flush_display (fm);
+                       return;
+               }
+
+               if (fetch_state == fetch_was_plane0)
+                       break;
+
+               fetch_start (pos);
+               if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm))
+                       return;
+       }
+
+#ifdef SPEEDUP
+       /* Unrolled version of the for loop below.  */
+       if (plf_state < plf_passed_stop && ddf_change != vpos && ddf_change + 1 != vpos
+               && dma
+               && (fetch_cycle & fetchstart_mask) == (fm_maxplane & fetchstart_mask)
+               && !badmode && !debug_dma
+# if 0
+               /* @@@ We handle this case, but the code would be simpler if we
+               * disallowed it - it may even be possible to guarantee that
+               * this condition never is false.  Later.  */
+               && (out_nbits & 15) == 0
+# endif
+               && toscr_nr_planes == thisline_decision.nr_planes)
+       {
+               int offs = (pos - fetch_cycle) & fetchunit_mask;
+               int ddf2 = ((ddfstop_to_test - offs + fetchunit - 1) & ~fetchunit_mask) + offs;
+               int ddf3 = ddf2 + fetchunit;
+               int stop = until < ddf2 ? until : until < ddf3 ? ddf2 : ddf3;
+               int count;
+
+               count = stop - pos;
+
+               if (count >= fetchstart) {
+                       count &= ~fetchstart_mask;
+
+                       if (thisline_decision.plfleft == -1) {
+                               compute_delay_offset ();
+                               compute_toscr_delay_1 (bplcon1);
+                       }
+
+                       do_long_fetch (pos, count >> (3 - toscr_res), dma, fm);
+
+                       /* This must come _after_ do_long_fetch so as not to confuse flush_display
+                       into thinking the first fetch has produced any output worth emitting to
+                       the screen.  But the calculation of delay_offset must happen _before_.  */
+                       maybe_first_bpl1dat (pos);
+
+                       if (pos <= ddfstop_to_test && pos + count > ddfstop_to_test)
+                               plf_state = plf_passed_stop;
+                       if (pos <= ddfstop_to_test && pos + count > ddf2)
+                               plf_state = plf_passed_stop2;
+                       if (pos <= ddf2 && pos + count >= ddf2 + fm_maxplane)
+                               add_modulos ();
+                       pos += count;
+                       fetch_cycle += count;
+               }
+       } else {
+#endif
+#ifdef SPEEDUP
+       }
+#endif
+       for (; pos < until; pos++) {
+               if (fetch_state == fetch_was_plane0) {
+                       beginning_of_plane_block (pos, fm);
+                       estimate_last_fetch_cycle (pos);
+               }
+               fetch_start (pos);
+
+               if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm))
+                       return;
+       }
+       if (until >= maxhpos) {
+               finish_final_fetch (pos, fm);
+               return;
+       }
+       flush_display (fm);
+}
+
+static void update_fetch_0 (int hpos) { update_fetch (hpos, 0); }
+static void update_fetch_1 (int hpos) { update_fetch (hpos, 1); }
+static void update_fetch_2 (int hpos) { update_fetch (hpos, 2); }
+
+STATIC_INLINE void decide_fetch (int hpos)
+{
+       if (hpos > last_fetch_hpos) {
+               if (fetch_state != fetch_not_started) {
+                       switch (fetchmode) {
+                       case 0: update_fetch_0 (hpos); break;
+#ifdef AGA
+                       case 1: update_fetch_1 (hpos); break;
+                       case 2: update_fetch_2 (hpos); break;
+#endif
+                       default: uae_abort (L"fetchmode corrupt");
+                       }
+               } else if (bpl1dat_written) {
+                       // "PIO" mode display
+#if 1
+                       if (!bpl1dat_done) {
+                               bpl1dat_done = true;
+                               //plfleft_real = thisline_decision.plfleft;
+                       }
+                       update_fetch_x (hpos, fetchmode);
+#endif
+               }
+               maybe_check (hpos);
+               last_fetch_hpos = hpos;
+       }
+}
+
+static void start_bpl_processing (int hpos, int hstart, bool dma)
+{
+       if (first_bpl_vpos < 0)
+               first_bpl_vpos = vpos;
+
+       if (doflickerfix () && interlace_seen && !scandoubled_line) {
+               int i;
+               for (i = 0; i < 8; i++) {
+                       prevbpl[lof_current][vpos][i] = bplptx[i];
+                       if (!lof_current && (bplcon0 & 4))
+                               bplpt[i] = prevbpl[1 - lof_current][vpos][i];
+                       if (!(bplcon0 & 4) || interlace_seen < 0)
+                               prevbpl[1 - lof_current][vpos][i] = prevbpl[lof_current][vpos][i] = 0;
+               }
+       }
+
+       plfstrt_sprite = plfstrt;
+       if (dma)
+               fetch_start (hpos);
+       fetch_cycle = 0;
+       last_fetch_hpos = hstart;
+       cycle_diagram_shift = last_fetch_hpos;
+       out_nbits = 0;
+       out_offs = 0;
+       toscr_nbits = 0;
+       thisline_decision.bplres = bplcon0_res;
+
+       ddfstate = DIW_waiting_stop;
+       compute_toscr_delay (last_fetch_hpos, bplcon1);
+
+       /* If someone already wrote BPL1DAT, clear the area between that point and
+       the real fetch start.  */
+       if (!nodraw ()) {
+               if (thisline_decision.plfleft != -1) {
+                       out_nbits = (plfstrt - thisline_decision.plfleft) << (1 + toscr_res);
+                       out_offs = out_nbits >> 5;
+                       out_nbits &= 31;
+               }
+               update_toscr_planes ();
+       }
+}
+
+/* this may turn on datafetch if program turns dma on during the ddf */
+static void maybe_start_bpl_dma (int hpos)
+{
+       /* OCS: BPL DMA never restarts if DMA is turned on during DDF
+       * ECS/AGA: BPL DMA restarts but only if DMA was turned off
+       outside of DDF or during current line, otherwise display
+       processing jumps immediately to "DDFSTOP passed"-condition */
+       if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               return;
+       if (fetch_state != fetch_not_started)
+               return;
+       if (diwstate != DIW_waiting_stop)
+               return;
+       if (hpos <= plfstrt)
+               return;
+       if (hpos > plfstop - fetchunit)
+               return;
+       if (ddfstate != DIW_waiting_start)
+               plf_state = plf_passed_stop;
+       start_bpl_processing (hpos, hpos, true);
+}
+
+/* This function is responsible for turning on datafetch if necessary. */
+STATIC_INLINE void decide_line (int hpos)
+{
+       /* Take care of the vertical DIW.  */
+       if (vpos == plffirstline) {
+               diwstate = DIW_waiting_stop;
+               ddf_change = vpos;
+       }
+       if (vpos == plflastline) {
+               diwstate = DIW_waiting_start;
+               ddf_change = vpos;
+       }
+
+       if (hpos <= last_decide_line_hpos)
+               return;
+
+       if (fetch_state == fetch_not_started && (diwstate == DIW_waiting_stop || (currprefs.chipset_mask & CSMASK_ECS_AGNUS))) {
+               int ok = 0;
+               if (last_decide_line_hpos < plfstrt_start && hpos >= plfstrt_start) {
+                       if (plf_state == plf_idle)
+                               plf_state = plf_start;
+               }
+               if (last_decide_line_hpos < plfstrt && hpos >= plfstrt) {
+                       if (plf_state == plf_start)
+                               plf_state = plf_active;
+                       if (plf_state == plf_active)
+                               ok = 1;
+                       /* hack warning.. Writing to DDFSTRT when DMA should start must be ignored
+                       * (correct fix would be emulate this delay for every custom register, but why bother..) */
+                       if (hpos - 2 == ddfstrt_old_hpos)
+                               ok = 0;
+               }
+               if (ok && diwstate == DIW_waiting_stop) {
+                       if (dmaen (DMA_BITPLANE)) {
+                               start_bpl_processing (hpos, plfstrt, true);
+                               estimate_last_fetch_cycle (plfstrt);
+                       }
+                       last_decide_line_hpos = hpos;
+#ifndef        CUSTOM_SIMPLE
+                       do_sprites (hpos);
+#endif
+                       return;
+               }
+       }
+
+#ifndef        CUSTOM_SIMPLE
+       if (hpos > last_sprite_hpos && last_sprite_hpos < SPR0_HPOS + 4 * MAX_SPRITES)
+               do_sprites (hpos);
+#endif
+
+       last_decide_line_hpos = hpos;
+}
+
+/* Called when a color is about to be changed (write to a color register),
+* but the new color has not been entered into the table yet. */
+static void record_color_change (int hpos, int regno, unsigned long value)
+{
+       if (regno < 0x1000 && nodraw ())
+               return;
+       /* Early positions don't appear on-screen. */
+       if (vpos < minfirstline)
+               return;
+
+       decide_diw (hpos);
+       decide_line (hpos);
+
+       if (thisline_decision.ctable == -1)
+               remember_ctable ();
+
+       if  ((regno < 0x1000 || regno == 0x1000 + 0x10c) && hpos < HBLANK_OFFSET && !(beamcon0 & 0x80) && prev_lineno >= 0) {
+               struct draw_info *pdip = curr_drawinfo + prev_lineno;
+               int idx = pdip->last_color_change;
+               int extrahpos = regno == 0x1000 + 0x10c ? 1 : 0;
+               bool lastsync = false;
+               /* Move color changes in horizontal cycles 0 to HBLANK_OFFSET to end of previous line.
+               * Cycles 0 to HBLANK_OFFSET are visible in right border on real Amigas. (because of late hsync)
+               */
+               if (curr_color_changes[idx - 1].regno == 0xffff) {
+                       idx--;
+                       lastsync = true;
+               }
+               pdip->last_color_change++;
+               pdip->nr_color_changes++;
+               curr_color_changes[idx].linepos = (hpos + maxhpos) * 2 + extrahpos;
+               curr_color_changes[idx].regno = regno;
+               curr_color_changes[idx].value = value;
+               if (lastsync) {
+                       curr_color_changes[idx + 1].linepos = hsyncstartpos * 2;
+                       curr_color_changes[idx + 1].regno = 0xffff;
+                       curr_color_changes[idx + 2].regno = -1;
+               } else {
+                       curr_color_changes[idx + 1].regno = -1;
+               }
+       }
+       record_color_change2 (hpos, regno, value);
+
+       if (regno == 0 && value != 0 && vpos >= 20) {
+               // autoscale if COLOR00 changes in top or bottom of screen
+               if (vpos >= minfirstline) {
+                       int vpos2 = autoscale_bordercolors ? minfirstline : vpos;
+                       if (first_planes_vpos == 0)
+                               first_planes_vpos = vpos2 - 2;
+                       if (plffirstline_total == current_maxvpos ())
+                               plffirstline_total = vpos2 - 2;
+                       if (vpos2 > last_planes_vpos || vpos2 > plflastline_total)
+                               plflastline_total = last_planes_vpos = vpos2 + 3;
+                       autoscale_bordercolors = 0;
+               } else {
+                       autoscale_bordercolors++;
+               }
+       }
+}
+
+static void record_register_change (int hpos, int regno, unsigned long value)
+{
+       if (regno == 0x100) { // BPLCON0
+               if (value & 0x800)
+                       thisline_decision.ham_seen = 1;
+               thisline_decision.ehb_seen = !! isehb (value, bplcon2);
+       } else if (regno == 0x104) { // BPLCON2
+               thisline_decision.ehb_seen = !! isehb (value, bplcon2);
+       }
+       record_color_change (hpos, regno + 0x1000, value);
+}
+
+typedef int sprbuf_res_t, cclockres_t, hwres_t,        bplres_t;
+
+static int expand_sprres (uae_u16 con0, uae_u16 con3)
+{
+       int res;
+
+       switch ((con3 >> 6) & 3)
+       {
+       default:
+               res = RES_LORES;
+               break;
+#ifdef ECS_DENISE
+       case 0: /* ECS defaults (LORES,HIRES=LORES sprite,SHRES=HIRES sprite) */
+               if ((currprefs.chipset_mask & CSMASK_ECS_DENISE) && GET_RES_DENISE (con0) == RES_SUPERHIRES)
+                       res = RES_HIRES;
+               else
+                       res = RES_LORES;
+               break;
+#endif
+#ifdef AGA
+       case 1:
+               res = RES_LORES;
+               break;
+       case 2:
+               res = RES_HIRES;
+               break;
+       case 3:
+               res = RES_SUPERHIRES;
+               break;
+#endif
+       }
+       return res;
+}
+
+/* handle very rarely needed playfield collision (CLXDAT bit 0) */
+/* only known game needing this is Rotor */
+static void do_playfield_collisions (void)
+{
+       int bplres = bplcon0_res;
+       hwres_t ddf_left = thisline_decision.plfleft * 2 << bplres;
+       hwres_t hw_diwlast = coord_window_to_diw_x (thisline_decision.diwlastword);
+       hwres_t hw_diwfirst = coord_window_to_diw_x (thisline_decision.diwfirstword);
+       int i, collided, minpos, maxpos;
+#ifdef AGA
+       int planes = (currprefs.chipset_mask & CSMASK_AGA) ? 8 : 6;
+#else
+       int planes = 6;
+#endif
+
+       if (clxcon_bpl_enable == 0) {
+               clxdat |= 1;
+               return;
+       }
+       if (clxdat & 1)
+               return;
+
+       collided = 0;
+       minpos = thisline_decision.plfleft * 2;
+       if (minpos < hw_diwfirst)
+               minpos = hw_diwfirst;
+       maxpos = thisline_decision.plfright * 2;
+       if (maxpos > hw_diwlast)
+               maxpos = hw_diwlast;
+       for (i = minpos; i < maxpos && !collided; i+= 32) {
+               int offs = ((i << bplres) - ddf_left) >> 3;
+               int j;
+               uae_u32 total = 0xffffffff;
+               for (j = 0; j < planes; j++) {
+                       int ena = (clxcon_bpl_enable >> j) & 1;
+                       int match = (clxcon_bpl_match >> j) & 1;
+                       uae_u32 t = 0xffffffff;
+                       if (ena) {
+                               if (j < thisline_decision.nr_planes) {
+                                       t = *(uae_u32 *)(line_data[next_lineno] + offs + 2 * j * MAX_WORDS_PER_LINE);
+                                       t ^= (match & 1) - 1;
+                               } else {
+                                       t = (match & 1) - 1;
+                               }
+                       }
+                       total &= t;
+               }
+               if (total) {
+                       collided = 1;
+#if 0
+                       {
+                               int k;
+                               for (k = 0; k < 1; k++) {
+                                       uae_u32 *ldata = (uae_u32 *)(line_data[next_lineno] + offs + 2 * k * MAX_WORDS_PER_LINE);
+                                       *ldata ^= 0x5555555555;
+                               }
+                       }
+#endif
+
+               }
+       }
+       if (collided)
+               clxdat |= 1;
+}
+
+/* Sprite-to-sprite collisions are taken care of in record_sprite.  This one does
+playfield/sprite collisions. */
+static void do_sprite_collisions (void)
+{
+       int nr_sprites = curr_drawinfo[next_lineno].nr_sprites;
+       int first = curr_drawinfo[next_lineno].first_sprite_entry;
+       int i;
+       unsigned int collision_mask = clxmask[clxcon >> 12];
+       int bplres = bplcon0_res;
+       hwres_t ddf_left = thisline_decision.plfleft * 2 << bplres;
+       hwres_t hw_diwlast = coord_window_to_diw_x (thisline_decision.diwlastword);
+       hwres_t hw_diwfirst = coord_window_to_diw_x (thisline_decision.diwfirstword);
+
+       if (clxcon_bpl_enable == 0) {
+               clxdat |= 0x1FE;
+               return;
+       }
+
+       for (i = 0; i < nr_sprites; i++) {
+               struct sprite_entry *e = curr_sprite_entries + first + i;
+               sprbuf_res_t j;
+               sprbuf_res_t minpos = e->pos;
+               sprbuf_res_t maxpos = e->max;
+               hwres_t minp1 = minpos >> sprite_buffer_res;
+               hwres_t maxp1 = maxpos >> sprite_buffer_res;
+
+               if (maxp1 > hw_diwlast)
+                       maxpos = hw_diwlast << sprite_buffer_res;
+               if (maxp1 > thisline_decision.plfright * 2)
+                       maxpos = thisline_decision.plfright * 2 << sprite_buffer_res;
+               if (minp1 < hw_diwfirst)
+                       minpos = hw_diwfirst << sprite_buffer_res;
+               if (minp1 < thisline_decision.plfleft * 2)
+                       minpos = thisline_decision.plfleft * 2 << sprite_buffer_res;
+
+               for (j = minpos; j < maxpos; j++) {
+                       int sprpix = spixels[e->first_pixel + j - e->pos] & collision_mask;
+                       int k, offs, match = 1;
+
+                       if (sprpix == 0)
+                               continue;
+
+                       offs = ((j << bplres) >> sprite_buffer_res) - ddf_left;
+                       sprpix = sprite_ab_merge[sprpix & 255] | (sprite_ab_merge[sprpix >> 8] << 2);
+                       sprpix <<= 1;
+
+                       /* Loop over number of playfields.  */
+                       for (k = 1; k >= 0; k--) {
+                               int l;
+#ifdef AGA
+                               int planes = (currprefs.chipset_mask & CSMASK_AGA) ? 8 : 6;
+#else
+                               int planes = 6;
+#endif
+                               if (bplcon0 & 0x400)
+                                       match = 1;
+                               for (l = k; match && l < planes; l += 2) {
+                                       int t = 0;
+                                       if (l < thisline_decision.nr_planes) {
+                                               uae_u32 *ldata = (uae_u32 *)(line_data[next_lineno] + 2 * l * MAX_WORDS_PER_LINE);
+                                               uae_u32 word = ldata[offs >> 5];
+                                               t = (word >> (31 - (offs & 31))) & 1;
+#if 0 /* debug: draw collision mask */
+                                               if (1) {
+                                                       int m;
+                                                       for (m = 0; m < 5; m++) {
+                                                               ldata = (uae_u32 *)(line_data[next_lineno] + 2 * m * MAX_WORDS_PER_LINE);
+                                                               ldata[(offs >> 5) + 1] |= 15 << (31 - (offs & 31));
+                                                       }
+                                               }
+#endif
+                                       }
+                                       if (clxcon_bpl_enable & (1 << l)) {
+                                               if (t != ((clxcon_bpl_match >> l) & 1))
+                                                       match = 0;
+                                       }
+                               }
+                               if (match) {
+#if 0 /* debug: mark lines where collisions are detected */
+                                       if (0) {
+                                               int l;
+                                               for (l = 0; l < 5; l++) {
+                                                       uae_u32 *ldata = (uae_u32 *)(line_data[next_lineno] + 2 * l * MAX_WORDS_PER_LINE);
+                                                       ldata[(offs >> 5) + 1] |= 15 << (31 - (offs & 31));
+                                               }
+                                       }
+#endif
+                                       clxdat |= sprpix << (k * 4);
+                               }
+                       }
+               }
+       }
+#if 0
+       {
+               static int olx;
+               if (clxdat != olx)
+                       write_log (L"%d: %04X\n", vpos, clxdat);
+               olx = clxdat;
+       }
+#endif
+}
+
+STATIC_INLINE void record_sprite_1 (int sprxp, uae_u16 *buf, uae_u32 datab, int num, int dbl,
+       unsigned int mask, int do_collisions, uae_u32 collision_mask)
+{
+       int j = 0;
+       while (datab) {
+               unsigned int col = 0;
+               unsigned coltmp = 0;
+
+               if ((sprxp >= sprite_minx && sprxp < sprite_maxx) || (bplcon3 & 2))
+                       col = (datab & 3) << (2 * num);
+#if 0
+               if (sprxp == sprite_minx || sprxp == sprite_maxx - 1)
+                       col ^= (rand () << 16) | rand ();
+#endif
+               if ((j & mask) == 0) {
+                       unsigned int tmp = (*buf) | col;
+                       *buf++ = tmp;
+                       if (do_collisions)
+                               coltmp |= tmp;
+                       sprxp++;
+               }
+               if (dbl > 0) {
+                       unsigned int tmp = (*buf) | col;
+                       *buf++ = tmp;
+                       if (do_collisions)
+                               coltmp |= tmp;
+                       sprxp++;
+               }
+               if (dbl > 1) {
+                       unsigned int tmp;
+                       tmp = (*buf) | col;
+                       *buf++ = tmp;
+                       if (do_collisions)
+                               coltmp |= tmp;
+                       tmp = (*buf) | col;
+                       *buf++ = tmp;
+                       if (do_collisions)
+                               coltmp |= tmp;
+                       sprxp++;
+                       sprxp++;
+               }
+               j++;
+               datab >>= 2;
+               if (do_collisions) {
+                       coltmp &= collision_mask;
+                       if (coltmp) {
+                               unsigned int shrunk_tmp = sprite_ab_merge[coltmp & 255] | (sprite_ab_merge[coltmp >> 8] << 2);
+                               clxdat |= sprclx[shrunk_tmp];
+                       }
+               }
+       }
+}
+
+/* DATAB contains the sprite data; 16 pixels in two-bit packets.  Bits 0/1
+determine the color of the leftmost pixel, bits 2/3 the color of the next
+etc.
+This function assumes that for all sprites in a given line, SPRXP either
+stays equal or increases between successive calls.
+
+The data is recorded either in lores pixels (if OCS/ECS), or in hires or
+superhires pixels (if AGA).  */
+
+static void record_sprite (int line, int num, int sprxp, uae_u16 *data, uae_u16 *datb, unsigned int ctl)
+{
+       struct sprite_entry *e = curr_sprite_entries + next_sprite_entry;
+       int i;
+       int word_offs;
+       uae_u32 collision_mask;
+       int width, dbl, half;
+       unsigned int mask = 0;
+       int attachment;
+
+       half = 0;
+       dbl = sprite_buffer_res - sprres;
+       if (dbl < 0) {
+               half = -dbl;
+               dbl = 0;
+               mask = 1 << half;
+       }
+       width = (sprite_width << sprite_buffer_res) >> sprres;
+       attachment = sprctl[num | 1] & 0x80;
+
+       /* Try to coalesce entries if they aren't too far apart  */
+       if (!next_sprite_forced && e[-1].max + sprite_width >= sprxp) {
+               e--;
+       } else {
+               next_sprite_entry++;
+               e->pos = sprxp;
+               e->has_attached = 0;
+       }
+
+       if (sprxp < e->pos)
+               uae_abort (L"sprxp < e->pos");
+
+       e->max = sprxp + width;
+       e[1].first_pixel = e->first_pixel + ((e->max - e->pos + 3) & ~3);
+       next_sprite_forced = 0;
+
+       collision_mask = clxmask[clxcon >> 12];
+       word_offs = e->first_pixel + sprxp - e->pos;
+
+       for (i = 0; i < sprite_width; i += 16) {
+               unsigned int da = *data;
+               unsigned int db = *datb;
+               uae_u32 datab = ((sprtaba[da & 0xFF] << 16) | sprtaba[da >> 8]
+               | (sprtabb[db & 0xFF] << 16) | sprtabb[db >> 8]);
+               int off = (i << dbl) >> half;
+               uae_u16 *buf = spixels + word_offs + off;
+               if (currprefs.collision_level > 0 && collision_mask)
+                       record_sprite_1 (sprxp + off, buf, datab, num, dbl, mask, 1, collision_mask);
+               else
+                       record_sprite_1 (sprxp + off, buf, datab, num, dbl, mask, 0, collision_mask);
+               data++;
+               datb++;
+       }
+
+       /* We have 8 bits per pixel in spixstate, two for every sprite pair.  The
+       low order bit records whether the attach bit was set for this pair.  */
+       if (attachment && !ecsshres ()) {
+               uae_u32 state = 0x01010101 << (num & ~1);
+               uae_u8 *stb1 = spixstate.bytes + word_offs;
+               for (i = 0; i < width; i += 8) {
+                       stb1[0] |= state;
+                       stb1[1] |= state;
+                       stb1[2] |= state;
+                       stb1[3] |= state;
+                       stb1[4] |= state;
+                       stb1[5] |= state;
+                       stb1[6] |= state;
+                       stb1[7] |= state;
+                       stb1 += 8;
+               }
+               e->has_attached = 1;
+       }
+}
+
+static void add_sprite (int *countp, int num, int sprxp, int posns[], int nrs[])
+{
+       int count = *countp;
+       int j, bestp;
+
+       /* Sort the sprites in order of ascending X position before recording them.  */
+       for (bestp = 0; bestp < count; bestp++) {
+               if (posns[bestp] > sprxp)
+                       break;
+               if (posns[bestp] == sprxp && nrs[bestp] < num)
+                       break;
+       }
+       for (j = count; j > bestp; j--) {
+               posns[j] = posns[j - 1];
+               nrs[j] = nrs[j - 1];
+       }
+       posns[j] = sprxp;
+       nrs[j] = num;
+       count++;
+       *countp = count;
+}
+
+static int tospritexdiw (int diw)
+{
+       return  coord_window_to_hw_x (diw - (DIW_DDF_OFFSET << lores_shift)) << sprite_buffer_res;
+}
+static int tospritexddf (int ddf)
+{
+       return (ddf * 2) << sprite_buffer_res;
+}
+static int fromspritexdiw (int ddf)
+{
+       return coord_hw_to_window_x (ddf >> sprite_buffer_res) + (DIW_DDF_OFFSET << lores_shift);
+}
+
+static void calcsprite (void)
+{
+       sprite_maxx = 0x7fff;
+       sprite_minx = 0;
+       if (thisline_decision.diwlastword >= 0)
+               sprite_maxx = tospritexdiw (thisline_decision.diwlastword);
+       if (thisline_decision.diwfirstword >= 0)
+               sprite_minx = tospritexdiw (thisline_decision.diwfirstword);
+       if (thisline_decision.plfleft >= 0) {
+               int min, max;
+               min = tospritexddf (thisline_decision.plfleft);
+               max = tospritexddf (thisline_decision.plfright);
+               if (min > sprite_minx && min < max) /* min < max = full line ddf */
+                       sprite_minx = min;
+               /* sprites are visible from first BPL1DAT write to end of line
+               * (undocumented feature)
+               */
+       }
+}
+
+static void decide_sprites (int hpos)
+{
+       int nrs[MAX_SPRITES * 2], posns[MAX_SPRITES * 2];
+       int count, i;
+       /* apparantly writes to custom registers happen in the 3/4th of cycle
+       * and sprite xpos comparator sees it immediately */
+       int point = hpos * 2 - 3;
+       int width = sprite_width;
+       int sscanmask = 0x100 << sprite_buffer_res;
+       int gotdata = 0;
+
+       if (thisline_decision.plfleft == -1 && !(bplcon3 & 2))
+               return;
+
+       if (nodraw () || hpos < 0x14 || nr_armed == 0 || point == last_sprite_point)
+               return;
+
+       decide_diw (hpos);
+       decide_line (hpos);
+       calcsprite ();
+
+       count = 0;
+       for (i = 0; i < MAX_SPRITES; i++) {
+               int sprxp = (fmode & 0x8000) ? (spr[i].xpos & ~sscanmask) : spr[i].xpos;
+               int hw_xp = sprxp >> sprite_buffer_res;
+
+               if (spr[i].xpos < 0)
+                       continue;
+
+               if (!((debug_sprite_mask & magic_sprite_mask) & (1 << i)))
+                       continue;
+
+               if (! spr[i].armed)
+                       continue;
+
+               if (hw_xp > last_sprite_point && hw_xp <= point)
+                       add_sprite (&count, i, sprxp, posns, nrs);
+
+               /* SSCAN2-bit is fun.. */
+               if ((fmode & 0x8000) && !(sprxp & sscanmask)) {
+                       sprxp |= sscanmask;
+                       hw_xp = sprxp >> sprite_buffer_res;
+                       if (hw_xp > last_sprite_point && hw_xp <= point)
+                               add_sprite (&count, MAX_SPRITES + i, sprxp, posns, nrs);
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               int nr = nrs[i] & (MAX_SPRITES - 1);
+               record_sprite (next_lineno, nr, posns[i], sprdata[nr], sprdatb[nr], sprctl[nr]);
+               /* get left and right sprite edge if brdsprt enabled */
+#if AUTOSCALE_SPRITES
+               if (dmaen (DMA_SPRITE) && (bplcon0 & 1) && (bplcon3 & 0x02) && !(bplcon3 & 0x20)) {
+                       int j, jj;
+                       for (j = 0, jj = 0; j < sprite_width; j+= 16, jj++) {
+                               int nx = fromspritexdiw (posns[i] + j);
+                               if (sprdata[nr][jj] || sprdatb[nr][jj]) {
+                                       if (diwfirstword_total > nx && nx >= (48 << currprefs.gfx_resolution))
+                                               diwfirstword_total = nx;
+                                       if (diwlastword_total < nx + 16 && nx <= (448 << currprefs.gfx_resolution))
+                                               diwlastword_total = nx + 16;
+                               }
+                       }
+                       gotdata = 1;
+               }
+#endif
+       }
+       last_sprite_point = point;
+
+#if AUTOSCALE_SPRITES
+       /* get upper and lower sprite position if brdsprt enabled */
+       if (gotdata) {
+               if (vpos < first_planes_vpos)
+                       first_planes_vpos = vpos;
+               if (vpos < plffirstline_total)
+                       plffirstline_total = vpos;
+               if (vpos > last_planes_vpos)
+                       last_planes_vpos = vpos;
+               if (vpos > plflastline_total)
+                       plflastline_total = vpos;
+       }
+#endif
+}
+
+STATIC_INLINE int sprites_differ (struct draw_info *dip, struct draw_info *dip_old)
+{
+       struct sprite_entry *this_first = curr_sprite_entries + dip->first_sprite_entry;
+       struct sprite_entry *this_last = curr_sprite_entries + dip->last_sprite_entry;
+       struct sprite_entry *prev_first = prev_sprite_entries + dip_old->first_sprite_entry;
+       int npixels;
+       int i;
+
+       if (dip->nr_sprites != dip_old->nr_sprites)
+               return 1;
+
+       if (dip->nr_sprites == 0)
+               return 0;
+
+       for (i = 0; i < dip->nr_sprites; i++)
+               if (this_first[i].pos != prev_first[i].pos
+                       || this_first[i].max != prev_first[i].max
+                       || this_first[i].has_attached != prev_first[i].has_attached)
+                       return 1;
+
+       npixels = this_last->first_pixel + (this_last->max - this_last->pos) - this_first->first_pixel;
+       if (memcmp (spixels + this_first->first_pixel, spixels + prev_first->first_pixel,
+               npixels * sizeof (uae_u16)) != 0)
+               return 1;
+       if (memcmp (spixstate.bytes + this_first->first_pixel, spixstate.bytes + prev_first->first_pixel, npixels) != 0)
+               return 1;
+       return 0;
+}
+
+STATIC_INLINE int color_changes_differ (struct draw_info *dip, struct draw_info *dip_old)
+{
+       if (dip->nr_color_changes != dip_old->nr_color_changes)
+               return 1;
+
+       if (dip->nr_color_changes == 0)
+               return 0;
+       if (memcmp (curr_color_changes + dip->first_color_change,
+               prev_color_changes + dip_old->first_color_change,
+               dip->nr_color_changes * sizeof *curr_color_changes) != 0)
+               return 1;
+       return 0;
+}
+
+/* End of a horizontal scan line. Finish off all decisions that were not
+* made yet. */
+static void finish_decisions (void)
+{
+       struct draw_info *dip;
+       struct draw_info *dip_old;
+       struct decision *dp;
+       int changed;
+       int hpos = maxhpos;
+
+       if (nodraw ())
+               return;
+
+       decide_diw (hpos);
+       decide_line (hpos);
+       decide_fetch (hpos);
+       if (bpl1dat_done) {
+               finish_final_fetch (hpos, fetchmode);
+               bpl1dat_done = 0;
+       }
+
+       record_color_change2 (hsyncstartpos, 0xffff, 0);
+       if (thisline_decision.plfleft != -1 && thisline_decision.plflinelen == -1) {
+               if (fetch_state != fetch_not_started) {
+                       write_log (L"fetch_state=%d plfleft=%d,len=%d,vpos=%d,hpos=%d\n",
+                               fetch_state, thisline_decision.plfleft, thisline_decision.plflinelen,
+                               vpos, hpos);
+                       uae_abort (L"fetch_state != fetch_not_started");
+               }
+               thisline_decision.plfright = thisline_decision.plfleft;
+               thisline_decision.plflinelen = 0;
+               thisline_decision.bplres = RES_LORES;
+       }
+
+       /* Large DIWSTOP values can cause the stop position never to be
+       * reached, so the state machine always stays in the same state and
+       * there's a more-or-less full-screen DIW. */
+       if (hdiwstate == DIW_waiting_stop) {
+               thisline_decision.diwlastword = max_diwlastword;
+               if (thisline_decision.diwfirstword == -1)
+                       thisline_decision.diwfirstword = 0;
+       }
+
+       if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword)
+               MARK_LINE_CHANGED;
+       if (thisline_decision.diwlastword != line_decisions[next_lineno].diwlastword)
+               MARK_LINE_CHANGED;
+
+       dip = curr_drawinfo + next_lineno;
+       dip_old = prev_drawinfo + next_lineno;
+       dp = line_decisions + next_lineno;
+       changed = thisline_changed;
+       if (thisline_decision.plfleft != -1 && thisline_decision.nr_planes > 0)
+               record_diw_line (thisline_decision.plfleft, diwfirstword, diwlastword);
+
+       decide_sprites (hpos + 1);
+
+       dip->last_sprite_entry = next_sprite_entry;
+       dip->last_color_change = next_color_change;
+
+       if (thisline_decision.ctable == -1) {
+               if (thisline_decision.plfleft == -1)
+                       remember_ctable_for_border ();
+               else
+                       remember_ctable ();
+       }
+
+       dip->nr_color_changes = next_color_change - dip->first_color_change;
+       dip->nr_sprites = next_sprite_entry - dip->first_sprite_entry;
+
+       if (thisline_decision.plfleft != line_decisions[next_lineno].plfleft)
+               changed = 1;
+       if (! changed && color_changes_differ (dip, dip_old))
+               changed = 1;
+       if (!changed && thisline_decision.plfleft != -1 && sprites_differ (dip, dip_old))
+               changed = 1;
+
+       if (changed) {
+               thisline_changed = 1;
+               *dp = thisline_decision;
+       } else
+               /* The only one that may differ: */
+               dp->ctable = thisline_decision.ctable;
+
+       /* leave free space for possible extra color changes at the end of line */
+       next_color_change += (HBLANK_OFFSET + 1) / 2;
+
+       diw_hcounter += maxhpos * 2;
+       if (!(currprefs.chipset_mask & CSMASK_ECS_DENISE) && vpos == equ_vblank_endline - 1)
+               diw_hcounter++;
+       if ((currprefs.chipset_mask & CSMASK_ECS_DENISE) || vpos > equ_vblank_endline || (currprefs.cs_dipagnus && vpos == 0)) {
+               diw_hcounter = maxhpos * 2;
+               last_hdiw = 2 - 1;
+       }
+
+       if (next_color_change >= MAX_REG_CHANGE - 30) {
+               write_log (L"color_change buffer overflow!\n");
+               next_color_change = 0;
+               dip->nr_color_changes = 0;
+               dip->first_color_change = 0;
+       }
+}
+
+/* Set the state of all decisions to "undecided" for a new scanline. */
+static void reset_decisions (void)
+{
+       if (nodraw ())
+               return;
+
+       toscr_nr_planes = toscr_nr_planes2 = 0;
+       thisline_decision.bplres = bplcon0_res;
+       thisline_decision.nr_planes = 0;
+       bpl1dat_written = 0;
+       bpl1dat_early = 0;
+
+       plfleft_real = -1;
+       thisline_decision.plfleft = -1;
+       thisline_decision.plflinelen = -1;
+       thisline_decision.ham_seen = !! (bplcon0 & 0x800);
+       thisline_decision.ehb_seen = !! isehb (bplcon0, bplcon2);
+       thisline_decision.ham_at_start = !! (bplcon0 & 0x800);
+
+       /* decided_res shouldn't be touched before it's initialized by decide_line(). */
+       thisline_decision.diwfirstword = -1;
+       thisline_decision.diwlastword = -1;
+       if (hdiwstate == DIW_waiting_stop) {
+               thisline_decision.diwfirstword = 0;
+               if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword)
+                       MARK_LINE_CHANGED;
+       }
+       thisline_decision.ctable = -1;
+
+       thisline_changed = 0;
+       curr_drawinfo[next_lineno].first_color_change = next_color_change;
+       curr_drawinfo[next_lineno].first_sprite_entry = next_sprite_entry;
+       next_sprite_forced = 1;
+
+       last_sprite_point = 0;
+       fetch_state = fetch_not_started;
+       bplcon1_hpos = -1;
+       if (bpldmasetuphpos >= 0) {
+               // this can happen in "too fast" modes
+               BPLCON0_Denise (0, bplcon0);
+               setup_fmodes (0);
+       }
+       bpldmasetuphpos = -1;
+       bpldmasetupphase = 0;
+       ddfstrt_old_hpos = -1;
+
+       if (plf_state > plf_active)
+               plf_state = plf_idle;
+       if (plf_state == plf_active && !(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               plf_state = plf_idle;
+
+       memset (todisplay, 0, sizeof todisplay);
+       memset (fetched, 0, sizeof fetched);
+#ifdef AGA
+       if (currprefs.chipset_mask & CSMASK_AGA) {
+               memset (fetched_aga0, 0, sizeof fetched_aga0);
+               memset (fetched_aga1, 0, sizeof fetched_aga1);
+       }
+#endif
+       memset (outword, 0, sizeof outword);
+
+       last_decide_line_hpos = -1;
+       last_ddf_pix_hpos = -1;
+       last_sprite_hpos = -1;
+       last_fetch_hpos = -1;
+
+       /* These are for comparison. */
+       thisline_decision.bplcon0 = bplcon0;
+       thisline_decision.bplcon2 = bplcon2;
+#ifdef ECS_DENISE
+       thisline_decision.bplcon3 = bplcon3;
+#endif
+#ifdef AGA
+       thisline_decision.bplcon4 = bplcon4;
+#endif
+}
+
+static int islinetoggle (void)
+{
+       int linetoggle = 0;
+       if (!(beamcon0 & 0x0800) && !(beamcon0 & 0x0020) && (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) {
+               linetoggle = 1; // NTSC and !LOLDIS -> LOL toggles every line
+       } else if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS) && currprefs.ntscmode) {
+               linetoggle = 1; // hardwired NTSC Agnus
+       }
+       return linetoggle;
+}
+
+static int isvsync (void)
+{
+       if (picasso_on || !currprefs.gfx_avsync || (currprefs.gfx_avsync == 0 && !currprefs.gfx_afullscreen))
+               return 0;
+       return currprefs.gfx_avsyncmode == 0 ? 1 : -1;
+}
+
+int vsynctime_orig;
+
+void compute_vsynctime (void)
+{
+       fake_vblank_hz = 0;
+       if (abs (currprefs.chipset_refreshrate) > 0.1) {
+               vblank_hz = currprefs.chipset_refreshrate;
+               if (isvsync ()) {
+                       vblank_skip = 1;
+                       if (!fake_vblank_hz && getvsyncrate (vblank_hz) != vblank_hz) {
+                               vblank_hz = getvsyncrate (vblank_hz);
+                               vblank_skip = -1;
+                       }
+               }
+       }
+       if (!fake_vblank_hz)
+               fake_vblank_hz = vblank_hz;
+       if (currprefs.turbo_emulation)
+               vsynctime = vsynctime_orig = 1;
+       else
+               vsynctime = vsynctime_orig = syncbase / fake_vblank_hz;
+       if (!picasso_on) {
+               updatedisplayarea ();
+       }
+       if (currprefs.produce_sound > 1)
+               update_sound (fake_vblank_hz, (bplcon0 & 4) ? -1 : lof_store, islinetoggle ());
+}
+
+
+static void dumpsync (void)
+{
+       static int cnt = 100;
+       if (cnt < 0)
+               return;
+       cnt--;
+       write_log (L"BEAMCON0=%04X VTOTAL=%04X  HTOTAL=%04X\n", new_beamcon0, vtotal, htotal);
+       write_log (L"  HSSTOP=%04X HBSTRT=%04X  HBSTOP=%04X\n", hsstop, hbstrt, hbstop);
+       write_log (L"  VSSTOP=%04X VBSTRT=%04X  VBSTOP=%04X\n", vsstop, vbstrt, vbstop);
+       write_log (L"  HSSTRT=%04X VSSTRT=%04X HCENTER=%04X\n", hsstrt, vsstrt, hcenter);
+}
+
+int current_maxvpos (void)
+{
+       return maxvpos + (lof_store ? 1 : 0);
+}
+
+/* set PAL/NTSC or custom timing variables */
+void init_hz (bool fullinit)
+{
+       int isntsc;
+       int odbl = doublescan, omaxvpos = maxvpos;
+       double ovblank = vblank_hz;
+       int hzc = 0;
+
+       if (fullinit)
+               vpos_count = vpos_count_prev = 0;
+
+       if (vsync_switchmode (-1, 0))
+               currprefs.gfx_avsync = changed_prefs.gfx_avsync = vsync_switchmode (-1, 0) ? 2 : 0;
+
+       if (!isvsync () && (DBLEQU (currprefs.chipset_refreshrate, 50) && !currprefs.ntscmode) ||
+               (DBLEQU (currprefs.chipset_refreshrate, 60) && currprefs.ntscmode)) {
+                       currprefs.chipset_refreshrate = changed_prefs.chipset_refreshrate = 0.0;
+       }
+
+       doublescan = 0;
+       if ((beamcon0 & 0xA0) != (new_beamcon0 & 0xA0))
+               hzc = 1;
+       if (beamcon0 != new_beamcon0) {
+               write_log (L"BEAMCON0 %04x -> %04x PC%=%08x\n", beamcon0, new_beamcon0, M68K_GETPC);
+               vpos_count = vpos_count_prev = 0;
+       }
+       beamcon0 = new_beamcon0;
+       isntsc = (beamcon0 & 0x20) ? 0 : 1;
+       if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               isntsc = currprefs.ntscmode ? 1 : 0;
+       if (!isntsc) {
+               maxvpos = MAXVPOS_PAL;
+               maxhpos = MAXHPOS_PAL;
+               minfirstline = VBLANK_ENDLINE_PAL;
+               vblank_hz = VBLANK_HZ_PAL;
+               sprite_vblank_endline = VBLANK_SPRITE_PAL;
+               equ_vblank_endline = EQU_ENDLINE_PAL;
+       } else {
+               maxvpos = MAXVPOS_NTSC;
+               maxhpos = MAXHPOS_NTSC;
+               minfirstline = VBLANK_ENDLINE_NTSC;
+               vblank_hz = VBLANK_HZ_NTSC;
+               sprite_vblank_endline = VBLANK_SPRITE_NTSC;
+               equ_vblank_endline = EQU_ENDLINE_NTSC;
+       }
+       maxvpos_nom = maxvpos;
+       if (vpos_count > 0) {
+               // we come here if vpos_count != maxvpos (someone poked VPOSW)
+               if (vpos_count < 10)
+                       vpos_count = 10;
+               vblank_hz = (isntsc ? 15734 : 15625.0) / vpos_count;
+               maxvpos_nom = vpos_count - (lof_current ? 1 : 0);
+               reset_drawing ();
+       }
+       if (beamcon0 & 0x80) {
+               // programmable scanrates (ECS Agnus)
+               if (vtotal >= MAXVPOS)
+                       vtotal = MAXVPOS - 1;
+               maxvpos = vtotal + 1;
+               if (htotal >= MAXHPOS)
+                       htotal = MAXHPOS - 1;
+               maxhpos = htotal + 1;
+               vblank_hz = 227.0 * 312.0 * 50.0 / (maxvpos * maxhpos);
+               minfirstline = vsstop;
+               if (minfirstline < 2)
+                       minfirstline = 2;
+               if (minfirstline >= maxvpos)
+                       minfirstline = maxvpos - 1;
+               sprite_vblank_endline = minfirstline - 2;
+               maxvpos_nom = maxvpos;
+               equ_vblank_endline = -1;
+               doublescan = htotal <= 164 ? 1 : 0;
+               dumpsync ();
+               hzc = 1;
+       }
+       if (maxvpos_nom >= MAXVPOS)
+               maxvpos_nom = MAXVPOS;
+       if (currprefs.gfx_scandoubler && doublescan == 0)
+               doublescan = -1;
+       if (doublescan != odbl || maxvpos != omaxvpos)
+               hzc = 1;
+       /* limit to sane values */
+       if (vblank_hz < 10)
+               vblank_hz = 10;
+       if (vblank_hz > 300)
+               vblank_hz = 300;
+       maxhpos_short = maxhpos;
+       if (beamcon0 & 0x80) {
+               if (hbstrt > maxhpos)
+                       hsyncstartpos = hbstrt;
+               else
+                       hsyncstartpos = maxhpos + hbstrt;
+               if (hbstop > maxhpos)
+                       hsyncendpos = hbstop;
+               else
+                       hsyncendpos = maxhpos + hbstop;
+       } else {
+               hsyncstartpos = maxhpos_short + 13;
+               hsyncendpos = 24;
+       }
+       eventtab[ev_hsync].oldcycles = get_cycles ();
+       eventtab[ev_hsync].evtime = get_cycles () + HSYNCTIME;
+       events_schedule ();
+       if (hzc) {
+               interlace_seen = (bplcon0 & 4) ? 1 : 0;
+               reset_drawing ();
+       }
+       if ((DBLEQU (vblank_hz, 50) || DBLEQU (vblank_hz, 60)) && isvsync () && currprefs.gfx_avsync == 2 && currprefs.gfx_afullscreen > 0) {
+               if (getvsyncrate (currprefs.gfx_refreshrate) != vblank_hz)
+                       vsync_switchmode (vblank_hz, currprefs.gfx_refreshrate);
+       }
+       if (isvsync () > 0) {
+               changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = abs (currprefs.gfx_refreshrate);
+       }
+       maxvpos_total = (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 2047 : 511;
+       if (maxvpos_total > MAXVPOS)
+               maxvpos_total = MAXVPOS;
+       if (!p96refresh_active) {
+               maxvpos_stored = maxvpos;
+               maxhpos_stored = maxhpos;
+               vblank_hz_stored = vblank_hz;
+       }
+
+       compute_vsynctime ();
+#ifdef PICASSO96
+       init_hz_p96 ();
+#endif
+       if (vblank_hz != ovblank)
+               updatedisplayarea ();
+       inputdevice_tablet_strobe ();
+       write_log (L"%s mode%s%s V=%.4fHz H=%0.4fHz (%dx%d+%d)\n",
+               isntsc ? L"NTSC" : L"PAL",
+               (bplcon0 & 4) ? L" interlaced" : L"",
+               doublescan > 0 ? L" dblscan" : L"",
+               vblank_hz, vblank_hz * maxvpos_nom,
+               maxhpos, maxvpos, lof_store ? 1 : 0);
+       config_changed = 1;
+}
+
+void init_hz (void)
+{
+       init_hz (false);
+}
+void init_hz_full (void)
+{
+       init_hz (true);
+}
+
+static void calcdiw (void)
+{
+       int hstrt = diwstrt & 0xFF;
+       int hstop = diwstop & 0xFF;
+       int vstrt = diwstrt >> 8;
+       int vstop = diwstop >> 8;
+
+       // vertical in ECS Agnus
+       if (diwhigh_written && (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) {
+               vstrt |= (diwhigh & 7) << 8;
+               vstop |= ((diwhigh >> 8) & 7) << 8;
+       } else {
+               if ((vstop & 0x80) == 0)
+                       vstop |= 0x100;
+       }
+       // horizontal in ECS Denise
+       if (diwhigh_written && (currprefs.chipset_mask & CSMASK_ECS_DENISE)) {
+               hstrt |= ((diwhigh >> 5) & 1) << 8;
+               hstop |= ((diwhigh >> 13) & 1) << 8;
+       } else {
+               hstop += 0x100;
+       }
+
+       diw_hstrt = hstrt;
+       diw_hstop = hstop;
+
+       diwfirstword = coord_diw_to_window_x (hstrt);
+       diwlastword = coord_diw_to_window_x (hstop);
+       if (diwfirstword >= diwlastword) {
+               diwfirstword = 0;
+               diwlastword = max_diwlastword;
+       }
+       if (diwfirstword < 0)
+               diwfirstword = 0;
+
+       plffirstline = vstrt;
+       plflastline = vstop;
+
+       plfstrt = ddfstrt;
+       plfstop = ddfstop;
+       /* probably not the correct place.. should use plf_state instead */
+       if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) {
+               /* ECS/AGA and ddfstop > maxhpos == always-on display */
+               if (plfstop > maxhpos)
+                       plfstrt = 0;
+               if (plfstrt < HARD_DDF_START)
+                       plfstrt = HARD_DDF_START;
+               plfstrt_start = plfstrt - 4;
+       } else {
+               /* OCS and ddfstrt >= ddfstop == ddfstop = max */
+               if (plfstrt >= plfstop && plfstrt >= HARD_DDF_START)
+                       plfstop = 0xff;
+               plfstrt_start = HARD_DDF_START - 2;
+       }
+       diw_change = 2;
+}
+
+/* display mode changed (lores, doubling etc..), recalculate everything */
+void init_custom (void)
+{
+       update_mirrors();
+       create_cycle_diagram_table ();
+       reset_drawing ();
+       init_hz_full ();
+       calcdiw ();
+}
+
+static int timehack_alive = 0;
+
+static uae_u32 REGPARAM2 timehack_helper (TrapContext *context)
+{
+#ifdef HAVE_GETTIMEOFDAY
+       struct timeval tv;
+       if (m68k_dreg (regs, 0) == 0)
+               return timehack_alive;
+
+       timehack_alive = 10;
+
+       gettimeofday (&tv, NULL);
+       x_put_long (m68k_areg (regs, 0), tv.tv_sec - (((365 * 8 + 2) * 24) * 60 * 60));
+       x_put_long (m68k_areg (regs, 0) + 4, tv.tv_usec);
+       return 0;
+#else
+       return 2;
+#endif
+}
+
+/*
+* register functions
+*/
+STATIC_INLINE uae_u16 DENISEID (void)
+{
+       if (currprefs.cs_deniserev >= 0)
+               return currprefs.cs_deniserev;
+#ifdef AGA
+       if (currprefs.chipset_mask & CSMASK_AGA) {
+               if (currprefs.cs_ide == IDE_A4000)
+                       return 0xFCF8;
+               return 0x00F8;
+       }
+#endif
+       if (currprefs.chipset_mask & CSMASK_ECS_DENISE)
+               return 0xFFFC;
+       return 0xFFFF;
+}
+STATIC_INLINE uae_u16 DMACONR (int hpos)
+{
+       decide_line (hpos);
+       decide_fetch (hpos);
+       decide_blitter (hpos);
+       dmacon &= ~(0x4000 | 0x2000);
+       dmacon |= ((blit_interrupt || (!blit_interrupt && currprefs.cs_agnusbltbusybug && !blt_info.got_cycle)) ? 0 : 0x4000)
+               | (blt_info.blitzero ? 0x2000 : 0);
+       return dmacon;
+}
+STATIC_INLINE uae_u16 INTENAR (void)
+{
+       return intena;
+}
+uae_u16 INTREQR (void)
+{
+       return intreq;
+}
+STATIC_INLINE uae_u16 ADKCONR (void)
+{
+       return adkcon;
+}
+
+STATIC_INLINE int islightpentriggered (void)
+{
+       if (beamcon0 & 0x2000) // LPENDIS
+               return 0;
+       return lightpen_triggered > 0;
+}
+STATIC_INLINE int issyncstopped (void)
+{
+       return (bplcon0 & 2) && !currprefs.genlock;
+}
+
+STATIC_INLINE int GETVPOS (void)
+{
+       return islightpentriggered () ? vpos_lpen : (issyncstopped () ? vpos_previous : vpos);
+}
+STATIC_INLINE int GETHPOS (void)
+{
+       return islightpentriggered () ? hpos_lpen : (issyncstopped () ? hpos_previous : current_hpos ());
+}
+
+
+// DFF006 = 0.W must be valid result but better do this only in 68000 modes (whdload black screen!)
+
+#define HPOS_OFFSET (currprefs.cpu_model < 68020 ? 3 : 0)
+
+STATIC_INLINE uae_u16 VPOSR (void)
+{
+       unsigned int csbit = 0;
+       uae_u16 vp = GETVPOS ();
+       uae_u16 hp = GETHPOS ();
+
+       if (hp + HPOS_OFFSET >= maxhpos) {
+               vp++;
+               if (vp >= maxvpos + lof_store)
+                       vp = 0;
+       }
+       vp = (vp >> 8) & 7;
+
+       if (currprefs.cs_agnusrev >= 0) {
+               csbit |= currprefs.cs_agnusrev << 8;
+       } else {
+#ifdef AGA
+               csbit |= (currprefs.chipset_mask & CSMASK_AGA) ? 0x2300 : 0;
+#endif
+               csbit |= (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 0x2000 : 0;
+               if (currprefs.chipmem_size > 1024 * 1024 && (currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+                       csbit |= 0x2100;
+               if (currprefs.ntscmode)
+                       csbit |= 0x1000;
+       }
+
+       if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               vp &= 1;
+       vp = vp | (lof_store ? 0x8000 : 0) | csbit;
+       if (currprefs.chipset_mask & CSMASK_ECS_AGNUS)
+               vp |= lol ? 0x80 : 0;
+#if 0
+       if (M68K_GETPC < 0x00f00000 || M68K_GETPC >= 0x10000000)
+               write_log (L"VPOSR %04x at %08x\n", vp, M68K_GETPC);
+#endif
+       if (currprefs.cpu_model >= 68020)
+               hsyncdelay ();
+       return vp;
+}
+
+static void VPOSW (uae_u16 v)
+{
+#if 0
+       if (M68K_GETPC < 0xf00000 || 1)
+               write_log (L"VPOSW %04X PC=%08x\n", v, M68K_GETPC);
+#endif
+       if (lof_store != ((v & 0x8000) ? 1 : 0)) {
+               lof_changed = 1;
+               lof_store = (v & 0x8000) ? 1 : 0;
+       }
+       if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) {
+               lol = (v & 0x0080) ? 1 : 0;
+               if (!islinetoggle ())
+                       lol = 0;
+       }
+       if (lof_changed)
+               return;
+       vpos &= 0x00ff;
+       v &= 7;
+       if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               v &= 1;
+       vpos |= v << 8;
+}
+
+static void VHPOSW (uae_u16 v)
+{
+#if 0
+       if (M68K_GETPC < 0xf00000 || 1)
+               write_log (L"VHPOSW %04X PC=%08x\n", v, M68K_GETPC);
+#endif
+       v >>= 8; // lets ignore hpos for now
+       vpos &= 0xff00;
+       vpos |= v;
+}
+
+STATIC_INLINE uae_u16 VHPOSR (void)
+{
+       uae_u16 vp = GETVPOS ();
+       uae_u16 hp = GETHPOS ();
+
+       hp += HPOS_OFFSET;
+       if (hp >= maxhpos) {
+               hp -= maxhpos;
+               vp++;
+               if (vp >= maxvpos + lof_store)
+                       vp = 0;
+       }
+       if (HPOS_OFFSET) {
+               hp += 1;
+               if (hp >= maxhpos)
+                       hp -= maxhpos;
+       }
+
+       vp <<= 8;
+       vp |= hp;
+       if (currprefs.cpu_model >= 68020)
+               hsyncdelay ();
+#if 0
+       if (M68K_GETPC < 0x00f00000 || M68K_GETPC >= 0x10000000)
+               write_log (L"VPOS %04x %04x at %08x\n", VPOSR (), vp, M68K_GETPC);
+#endif
+       return vp;
+}
+
+static int test_copper_dangerous (unsigned int address)
+{
+       int addr = address & 0x01fe;
+       if (addr < ((copcon & 2) ? ((currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 0 : 0x40) : 0x80)) {
+               cop_state.state = COP_stop;
+               copper_enabled_thisline = 0;
+               unset_special (SPCFLAG_COPPER);
+               return 1;
+       }
+       return 0;
+}
+
+static void immediate_copper (int num)
+{
+       int pos = 0;
+       int oldpos = 0;
+
+       cop_state.state = COP_stop;
+       cop_state.vpos = vpos;
+       cop_state.hpos = current_hpos () & ~1;
+       cop_state.ip = num == 1 ? cop1lc : cop2lc;
+
+       while (pos < (maxvpos << 5)) {
+               if (oldpos > pos)
+                       pos = oldpos;
+               if (!dmaen(DMA_COPPER))
+                       break;
+               if (cop_state.ip >= currprefs.chipmem_size)
+                       break;
+               pos++;
+               oldpos = pos;
+               cop_state.i1 = chipmem_wget_indirect (cop_state.ip);
+               cop_state.i2 = chipmem_wget_indirect (cop_state.ip + 2);
+               cop_state.ip += 4;
+               if (!(cop_state.i1 & 1)) { // move
+                       cop_state.i1 &= 0x1fe;
+                       if (cop_state.i1 == 0x88) {
+                               cop_state.ip = cop1lc;
+                               continue;
+                       }
+                       if (cop_state.i1 == 0x8a) {
+                               cop_state.ip = cop2lc;
+                               continue;
+                       }
+                       if (test_copper_dangerous (cop_state.i1))
+                               break;
+                       custom_wput_1 (0, cop_state.i1, cop_state.i2, 0);
+               } else { // wait or skip
+                       if ((cop_state.i1 >> 8) > ((pos >> 5) & 0xff))
+                               pos = (((pos >> 5) & 0x100) | ((cop_state.i1 >> 8)) << 5) | ((cop_state.i1 & 0xff) >> 3);
+                       if (cop_state.i1 >= 0xffdf && cop_state.i2 == 0xfffe)
+                               break;
+               }
+       }
+       cop_state.state = COP_stop;
+       unset_special (SPCFLAG_COPPER);
+}
+
+STATIC_INLINE void COP1LCH (uae_u16 v)
+{
+       cop1lc = (cop1lc & 0xffff) | ((uae_u32)v << 16);
+}
+STATIC_INLINE void COP1LCL (uae_u16 v)
+{
+       cop1lc = (cop1lc & ~0xffff) | (v & 0xfffe);
+}
+STATIC_INLINE void COP2LCH (uae_u16 v)
+{
+       cop2lc = (cop2lc & 0xffff) | ((uae_u32)v << 16);
+}
+STATIC_INLINE void COP2LCL (uae_u16 v)
+{
+       cop2lc = (cop2lc & ~0xffff) | (v & 0xfffe);
+}
+
+static void compute_spcflag_copper (int hpos);
+
+// vblank = copper starts at hpos=2
+// normal COPJMP write: takes 2 more cycles
+static void COPJMP (int num, int vblank)
+{
+       int oldstrobe = cop_state.strobe;
+
+#if CUSTOM_DEBUG > 0
+       if (dmaen (DMA_COPPER) && (cop_state.saved_i1 != 0xffff || cop_state.saved_i2 != 0xfffe))
+               write_log (L"vblank without copper ending %08x (%08x %08x)\n", cop_state.ip, cop1lc, cop2lc);
+#endif
+
+       unset_special (SPCFLAG_COPPER);
+       cop_state.ignore_next = 0;
+       if (!oldstrobe)
+               cop_state.state_prev = cop_state.state;
+       cop_state.state = vblank ? COP_start_delay : (copper_access ? COP_strobe_delay1 : COP_strobe_extra);
+       cop_state.vpos = vpos;
+       cop_state.hpos = current_hpos () & ~1;
+       copper_enabled_thisline = 0;
+       cop_state.strobe = num;
+
+       if (nocustom ()) {
+               immediate_copper (num);
+               return;
+       }
+
+       if (dmaen (DMA_COPPER)) {
+               compute_spcflag_copper (current_hpos ());
+       } else if (oldstrobe > 0 && oldstrobe != num && cop_state.state_prev == COP_wait) {
+               /* dma disabled, copper idle and accessed both COPxJMPs -> copper stops! */
+               cop_state.state = COP_stop;
+       }
+}
+
+STATIC_INLINE void COPCON (uae_u16 a)
+{
+       copcon = a;
+}
+
+static void DMACON (int hpos, uae_u16 v)
+{
+       int oldcop, newcop;
+       uae_u16 changed;
+
+       uae_u16 oldcon = dmacon;
+
+       decide_line (hpos);
+       decide_fetch (hpos);
+       decide_blitter (hpos);
+
+       setclr (&dmacon, v);
+       dmacon &= 0x1FFF;
+
+       changed = dmacon ^ oldcon;
+
+       oldcop = (oldcon & DMA_COPPER) && (oldcon & DMA_MASTER);
+       newcop = (dmacon & DMA_COPPER) && (dmacon & DMA_MASTER);
+
+       if (oldcop != newcop) {
+               if (newcop && !oldcop) {
+                       compute_spcflag_copper (hpos);
+               } else if (!newcop) {
+                       copper_enabled_thisline = 0;
+                       unset_special (SPCFLAG_COPPER);
+               }
+       }
+       if ((dmacon & DMA_BLITPRI) > (oldcon & DMA_BLITPRI) && bltstate != BLT_done)
+               set_special (SPCFLAG_BLTNASTY);
+
+       if (dmaen (DMA_BLITTER) && bltstate == BLT_init)
+               bltstate = BLT_work;
+
+       if ((dmacon & (DMA_BLITPRI | DMA_BLITTER | DMA_MASTER)) != (DMA_BLITPRI | DMA_BLITTER | DMA_MASTER))
+               unset_special (SPCFLAG_BLTNASTY);
+
+       if (changed & (DMA_MASTER | 0x0f))
+               audio_state_machine ();
+
+       if (changed & (DMA_MASTER | DMA_BITPLANE)) {
+               ddf_change = vpos;
+               if (dmaen (DMA_BITPLANE))
+                       maybe_start_bpl_dma (hpos);
+       }
+
+       events_schedule();
+}
+
+
+void MISC_handler (void)
+{
+       static bool dorecheck;
+       bool recheck;
+       int i;
+       evt mintime;
+       evt ct = get_cycles ();
+       static int recursive;
+
+       if (recursive) {
+               dorecheck = true;
+               return;
+       }
+       recursive++;
+       eventtab[ev_misc].active = 0;
+       recheck = true;
+       while (recheck) {
+               recheck = false;
+               mintime = ~0L;
+               for (i = 0; i < ev2_max; i++) {
+                       if (eventtab2[i].active) {
+                               if (eventtab2[i].evtime == ct) {
+                                       eventtab2[i].active = false;
+                                       eventtab2[i].handler (eventtab2[i].data);
+                                       if (dorecheck || eventtab2[i].active) {
+                                               recheck = true;
+                                               dorecheck = false;
+                                       }
+                               } else {
+                                       evt eventtime = eventtab2[i].evtime - ct;
+                                       if (eventtime < mintime)
+                                               mintime = eventtime;
+                               }
+                       }
+               }
+       }
+       if (mintime != ~0L) {
+               eventtab[ev_misc].active = true;
+               eventtab[ev_misc].oldcycles = ct;
+               eventtab[ev_misc].evtime = ct + mintime;
+               events_schedule ();
+       }
+       recursive--;
+}
+
+static int irq_nmi;
+
+void NMI_delayed (void)
+{
+       irq_nmi = 1;
+}
+
+static uae_u16 intreq_internal, intena_internal;
+
+int intlev (void)
+{
+       uae_u16 imask = intreq_internal & intena_internal;
+       if (irq_nmi) {
+               irq_nmi = 0;
+               return 7;
+       }
+       if (!(imask && (intena_internal & 0x4000)))
+               return -1;
+       if (imask & (0x4000 | 0x2000))                                          // 13 14
+               return 6;
+       if (imask & (0x1000 | 0x0800))                                          // 11 12
+               return 5;
+       if (imask & (0x0400 | 0x0200 | 0x0100 | 0x0080))        // 7 8 9 10
+               return 4;
+       if (imask & (0x0040 | 0x0020 | 0x0010))                         // 4 5 6
+               return 3;
+       if (imask & 0x0008)                                                                     // 3
+               return 2;
+       if (imask & (0x0001 | 0x0002 | 0x0004))                         // 0 1 2
+               return 1;
+       return -1;
+}
+
+#define INT_PROCESSING_DELAY (3 * CYCLE_UNIT)
+STATIC_INLINE int use_eventmode (uae_u16 v)
+{
+       if (!currprefs.cpu_cycle_exact)
+               return 0;
+       if (currprefs.cpu_cycle_exact && currprefs.cpu_model <= 68020)
+               return 1;
+       if (v & 0x8000)
+               return 1;
+       return 0;
+}
+
+static void send_interrupt_do (uae_u32 v)
+{
+       INTREQ_0 (0x8000 | (1 << v));
+}
+
+void send_interrupt (int num, int delay)
+{
+       if (use_eventmode (0x8000) && delay > 0) {
+               if (!(intreq & (1 << num)))
+                       event2_newevent_xx (-1, delay, num, send_interrupt_do);
+       } else {
+               send_interrupt_do (num);
+       }
+}
+
+static void send_intena_do (uae_u32 v)
+{
+       intena_internal = v;
+       doint ();
+}
+static void send_intreq_do (uae_u32 v)
+{
+       intreq_internal = v;
+       doint ();
+}
+
+static void INTENA (uae_u16 v)
+{
+       uae_u16 old = intena;
+       setclr (&intena, v);
+
+       if (!(v & 0x8000) && old == intena)
+               return;
+       if (use_eventmode (v)) {
+               event2_newevent_xx (-1, INT_PROCESSING_DELAY, intena, send_intena_do);
+       } else {
+               intena_internal = intena;
+               if (v & 0x8000)
+                       doint ();
+       }
+#if 0
+       if (v & 0x40)
+               write_log (L"INTENA %04X (%04X) %p\n", intena, v, M68K_GETPC);
+#endif
+}
+
+void INTREQ_nodelay (uae_u16 v)
+{
+       setclr (&intreq, v);
+       setclr (&intreq_internal, v);
+       doint ();
+}
+
+void INTREQ_f (uae_u16 v)
+{
+       if (use_eventmode (v)) {
+               send_intreq_do (v);
+       } else {
+               setclr (&intreq, v);
+               setclr (&intreq_internal, v);
+       }
+}
+
+void INTREQ_0 (uae_u16 v)
+{
+#if 0
+       if (!(v & 0x8000) && (v & (0x80 | 0x100 | 0x200 | 0x400)))
+               write_log (L"audirq clear %d\n", v);
+#endif
+
+       uae_u16 old = intreq;
+       setclr (&intreq, v);
+
+       if (!(v & 0x8000) && old == intreq)
+               return;
+
+       if (use_eventmode (v)) {
+               event2_newevent_xx (-1, INT_PROCESSING_DELAY, intreq, send_intreq_do);
+       } else {
+               intreq_internal = intreq;
+               if (v & 0x8000)
+                       doint ();
+       }
+}
+
+void INTREQ (uae_u16 data)
+{
+       INTREQ_0 (data);
+       serial_check_irq ();
+       rethink_cias ();
+#ifdef A2065
+       rethink_a2065 ();
+#endif
+#ifdef A2091
+       rethink_a2091 ();
+#endif
+#ifdef CDTV
+       rethink_cdtv ();
+#endif
+#ifdef CD32
+       rethink_akiko ();
+#endif
+       rethink_gayle ();
+}
+
+static void ADKCON (int hpos, uae_u16 v)
+{
+       if (currprefs.produce_sound > 0)
+               update_audio ();
+       DISK_update (hpos);
+       DISK_update_adkcon (hpos, v);
+       setclr (&adkcon, v);
+       audio_update_adkmasks ();
+       if ((v >> 11) & 1)
+               serial_uartbreak ((adkcon >> 11) & 1);
+}
+
+static void BEAMCON0 (uae_u16 v)
+{
+       if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) {
+               if (!(currprefs.chipset_mask & CSMASK_ECS_DENISE))
+                       v &= 0x20;
+               if (v != new_beamcon0) {
+                       new_beamcon0 = v;
+                       if (v & ~0x20)
+                               write_log (L"warning: %04X written to BEAMCON0 PC=%08X\n", v, M68K_GETPC);
+               }
+       }
+}
+
+#ifndef CUSTOM_SIMPLE
+
+static void varsync (void)
+{
+       if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               return;
+#ifdef PICASSO96
+       if (picasso_on && p96refresh_active) {
+               vtotal = p96refresh_active;
+               return;
+       }
+#endif
+       if (!(beamcon0 & 0x80))
+               return;
+       vpos_count = 0;
+       dumpsync ();
+}
+
+#ifdef PICASSO96
+void set_picasso_hack_rate (int hz)
+{
+       if (!picasso_on)
+               return;
+       vpos_count = 0;
+       p96refresh_active = maxvpos_stored * vblank_hz_stored / hz;
+       if (!currprefs.cs_ciaatod)
+               changed_prefs.cs_ciaatod = currprefs.cs_ciaatod = currprefs.ntscmode ? 2 : 1;
+       if (p96refresh_active > 0) {
+               new_beamcon0 |= 0x80;
+       }
+}
+#endif
+
+#endif
+
+static void BPLxPTH (int hpos, uae_u16 v, int num)
+{
+       decide_line (hpos);
+       decide_fetch (hpos);
+       bplpt[num] = (bplpt[num] & 0x0000ffff) | ((uae_u32)v << 16);
+       bplptx[num] = (bplptx[num] & 0x0000ffff) | ((uae_u32)v << 16);
+       //write_log (L"%d:%d:BPL%dPTH %08X COP=%08x\n", hpos, vpos, num, bplpt[num], cop_state.ip);
+}
+static void BPLxPTL (int hpos, uae_u16 v, int num)
+{
+       decide_line (hpos);
+       decide_fetch (hpos);
+       /* chipset feature: BPLxPTL write and next cycle doing DMA fetch using same pointer register ->
+        * this write goes nowhere (same happens with all DMA channels, not just BPL)
+        * (intro MoreNewStuffy by PlasmaForce)
+        */
+       /* only detect copper accesses to prevent too fast CPU mode glitches */
+       if (copper_access && is_bitplane_dma (hpos + 1) == num + 1)
+               return;
+       bplpt[num] = (bplpt[num] & 0xffff0000) | (v & 0x0000fffe);
+       bplptx[num] = (bplptx[num] & 0xffff0000) | (v & 0x0000fffe);
+       //write_log (L"%d:%d:BPL%dPTL %08X COP=%08x\n", hpos, vpos, num, bplpt[num], cop_state.ip);
+}
+
+static void BPLCON0_Denise (int hpos, uae_u16 v)
+{
+       if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE))
+               v &= ~0x00F1;
+       else if (! (currprefs.chipset_mask & CSMASK_AGA))
+               v &= ~0x00B0;
+       v &= ~(0x0200 | 0x0100 | 0x0080 | 0x0020);
+#if SPRBORDER
+       v |= 1;
+#endif
+
+       if (bplcon0d == v)
+               return;
+
+       bplcon0dd = -1;
+       // fake unused 0x0080 bit as an EHB bit (see below)
+       if (isehb (bplcon0d, bplcon2))
+               v |= 0x80;
+
+       record_register_change (hpos, 0x100, (bplcon0d & ~(0x800 | 0x400 | 0x80)) | (v & (0x0800 | 0x400 | 0x80)));
+
+       bplcon0d = v & ~0x80;
+
+#ifdef ECS_DENISE
+       if (currprefs.chipset_mask & CSMASK_ECS_DENISE) {
+               decide_sprites (hpos);
+               sprres = expand_sprres (v, bplcon3);
+       }
+#endif
+       if (thisline_decision.plfleft == -1)
+               update_denise (hpos);
+}
+
+static void BPLCON0 (int hpos, uae_u16 v)
+{
+       if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE))
+               v &= ~0x00F1;
+       else if (! (currprefs.chipset_mask & CSMASK_AGA))
+               v &= ~0x00B0;
+       v &= ~(0x0200 | 0x0100 | 0x0080 | 0x0020);
+
+#if SPRBORDER
+       v |= 1;
+#endif
+       if (bplcon0 == v)
+               return;
+
+       if (!issyncstopped ()) {
+               vpos_previous = vpos;
+               hpos_previous = hpos;
+       }
+
+       bplcon0 = v;
+
+       bpldmainitdelay (hpos);
+
+       if (thisline_decision.plfleft == -1)
+               BPLCON0_Denise (hpos, v);
+}
+
+STATIC_INLINE void BPLCON1 (int hpos, uae_u16 v)
+{
+       if (!(currprefs.chipset_mask & CSMASK_AGA))
+               v &= 0xff;
+       if (bplcon1 == v)
+               return;
+       ddf_change = vpos;
+       decide_line (hpos);
+       decide_fetch (hpos);
+       bplcon1_hpos = hpos;
+       bplcon1 = v;
+}
+
+STATIC_INLINE void BPLCON2 (int hpos, uae_u16 v)
+{
+       if (!(currprefs.chipset_mask & CSMASK_AGA))
+               v &= 0x7f;
+       if (bplcon2 == v)
+               return;
+       decide_line (hpos);
+       bplcon2 = v;
+       record_register_change (hpos, 0x104, v);
+}
+
+#ifdef ECS_DENISE
+STATIC_INLINE void BPLCON3 (int hpos, uae_u16 v)
+{
+       if (!(currprefs.chipset_mask & CSMASK_ECS_DENISE))
+               return;
+       if (!(currprefs.chipset_mask & CSMASK_AGA)) {
+               v &= 0x003f;
+               v |= 0x0c00;
+       }
+#if SPRBORDER
+       v |= 2;
+#endif
+       if (bplcon3 == v)
+               return;
+       decide_line (hpos);
+       decide_sprites (hpos);
+       bplcon3 = v;
+       sprres = expand_sprres (bplcon0, bplcon3);
+       record_register_change (hpos, 0x106, v);
+}
+#endif
+#ifdef AGA
+STATIC_INLINE void BPLCON4 (int hpos, uae_u16 v)
+{
+       if (!(currprefs.chipset_mask & CSMASK_AGA))
+               return;
+       if (bplcon4 == v)
+               return;
+       decide_line (hpos);
+       bplcon4 = v;
+       record_register_change (hpos, 0x10c, v);
+}
+#endif
+
+static void BPL1MOD (int hpos, uae_u16 v)
+{
+       v &= ~1;
+       if ((uae_s16)bpl1mod == (uae_s16)v)
+               return;
+       decide_line (hpos);
+       decide_fetch (hpos);
+       bpl1mod = v;
+}
+
+static void BPL2MOD (int hpos, uae_u16 v)
+{
+       v &= ~1;
+       if ((uae_s16)bpl2mod == (uae_s16)v)
+               return;
+       decide_line (hpos);
+       decide_fetch (hpos);
+       bpl2mod = v;
+}
+
+/* needed in special OCS/ECS "7-plane" mode. */
+/* (in reality only BPL0DAT, BPL5DAT and BPL6DAT needed) */
+static void BPLxDAT (int hpos, int num, uae_u16 v)
+{
+       decide_line (hpos);
+       decide_fetch (hpos);
+       bplxdat[num] = v;
+       if (num == 0) {
+               bpl1dat_written = 1;
+               if (thisline_decision.plfleft == -1) {
+                       start_bpl_processing (hpos, hpos, false);
+                       thisline_decision.plfleft = hpos;
+                       compute_delay_offset ();
+               }
+       }
+}
+
+static void DIWSTRT (int hpos, uae_u16 v)
+{
+       if (diwstrt == v && ! diwhigh_written)
+               return;
+       decide_diw (hpos);
+       decide_line (hpos);
+       diwhigh_written = 0;
+       diwstrt = v;
+       calcdiw ();
+}
+
+static void DIWSTOP (int hpos, uae_u16 v)
+{
+       if (diwstop == v && ! diwhigh_written)
+               return;
+       decide_diw (hpos);
+       decide_line (hpos);
+       diwhigh_written = 0;
+       diwstop = v;
+       calcdiw ();
+}
+
+static void DIWHIGH (int hpos, uae_u16 v)
+{
+       if (!(currprefs.chipset_mask & (CSMASK_ECS_DENISE | CSMASK_ECS_AGNUS)))
+               return;
+       if (!(currprefs.chipset_mask & CSMASK_AGA))
+               v &= ~(0x0008 | 0x0010 | 0x1000 | 0x0800);
+       v &= ~(0x8000 | 0x4000 | 0x0080 | 0x0040);
+       if (diwhigh_written && diwhigh == v)
+               return;
+       decide_line (hpos);
+       diwhigh_written = 1;
+       diwhigh = v;
+       calcdiw ();
+}
+
+static void DDFSTRT (int hpos, uae_u16 v)
+{
+       v &= 0xfe;
+       if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               v &= 0xfc;
+       if (ddfstrt == v && hpos + 2 != ddfstrt)
+               return;
+       ddf_change = vpos;
+       decide_line (hpos);
+       ddfstrt_old_hpos = hpos;
+       ddfstrt = v;
+       calcdiw ();
+       if (ddfstop > 0xD4 && (ddfstrt & 4) == 4) {
+               static int last_warned;
+               last_warned = (last_warned + 1) & 4095;
+               if (last_warned == 0)
+                       write_log (L"WARNING! Very strange DDF values (%x %x).\n", ddfstrt, ddfstop);
+       }
+}
+
+static void DDFSTOP (int hpos, uae_u16 v)
+{
+       v &= 0xfe;
+       if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               v &= 0xfc;
+       if (ddfstop == v && hpos + 2 != ddfstop)
+               return;
+       ddf_change = vpos;
+       decide_line (hpos);
+       decide_fetch (hpos);
+       decide_blitter (hpos);
+       ddfstop = v;
+       calcdiw ();
+       if (fetch_state != fetch_not_started)
+               estimate_last_fetch_cycle (hpos);
+       if (ddfstop > 0xD4 && (ddfstrt & 4) == 4) {
+               static int last_warned;
+               if (last_warned == 0)
+                       write_log (L"WARNING! Very strange DDF values (%x).\n", ddfstop);
+               last_warned = (last_warned + 1) & 4095;
+       }
+}
+
+static void FMODE (int hpos, uae_u16 v)
+{
+       if (! (currprefs.chipset_mask & CSMASK_AGA))
+               v = 0;
+       v &= 0xC00F;
+       if (fmode == v)
+               return;
+       ddf_change = vpos;
+       fmode = v;
+       sprite_width = GET_SPRITEWIDTH (fmode);
+       bpldmainitdelay (hpos);
+}
+
+static void FNULL (uae_u16 v)
+{
+
+}
+
+static void BLTADAT (int hpos, uae_u16 v)
+{
+       maybe_blit (hpos, 0);
+
+       blt_info.bltadat = v;
+}
+/*
+* "Loading data shifts it immediately" says the HRM. Well, that may
+* be true for BLTBDAT, but not for BLTADAT - it appears the A data must be
+* loaded for every word so that AFWM and ALWM can be applied.
+*/
+static void BLTBDAT (int hpos, uae_u16 v)
+{
+       maybe_blit (hpos, 0);
+
+       if (bltcon1 & 2)
+               blt_info.bltbhold = v << (bltcon1 >> 12);
+       else
+               blt_info.bltbhold = v >> (bltcon1 >> 12);
+       blt_info.bltbdat = v;
+}
+static void BLTCDAT (int hpos, uae_u16 v) { maybe_blit (hpos, 0); blt_info.bltcdat = v; reset_blit (0); }
+
+static void BLTAMOD (int hpos, uae_u16 v) { maybe_blit (hpos, 1); blt_info.bltamod = (uae_s16)(v & 0xFFFE); reset_blit (0); }
+static void BLTBMOD (int hpos, uae_u16 v) { maybe_blit (hpos, 1); blt_info.bltbmod = (uae_s16)(v & 0xFFFE); reset_blit (0); }
+static void BLTCMOD (int hpos, uae_u16 v) { maybe_blit (hpos, 1); blt_info.bltcmod = (uae_s16)(v & 0xFFFE); reset_blit (0); }
+static void BLTDMOD (int hpos, uae_u16 v) { maybe_blit (hpos, 1); blt_info.bltdmod = (uae_s16)(v & 0xFFFE); reset_blit (0); }
+
+static void BLTCON0 (int hpos, uae_u16 v) { maybe_blit (hpos, 2); bltcon0 = v; reset_blit (1); }
+/* The next category is "Most useless hardware register".
+* And the winner is... */
+static void BLTCON0L (int hpos, uae_u16 v)
+{
+       if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               return; // ei voittoa.
+       maybe_blit (hpos, 2); bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF);
+       reset_blit (1);
+}
+static void BLTCON1 (int hpos, uae_u16 v) { maybe_blit (hpos, 2); bltcon1 = v; reset_blit (2); }
+
+static void BLTAFWM (int hpos, uae_u16 v) { maybe_blit (hpos, 2); blt_info.bltafwm = v; reset_blit (0); }
+static void BLTALWM (int hpos, uae_u16 v) { maybe_blit (hpos, 2); blt_info.bltalwm = v; reset_blit (0); }
+
+static void BLTAPTH (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltapt = (bltapt & 0xffff) | ((uae_u32)v << 16); }
+static void BLTAPTL (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltapt = (bltapt & ~0xffff) | (v & 0xFFFE); }
+static void BLTBPTH (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltbpt = (bltbpt & 0xffff) | ((uae_u32)v << 16); }
+static void BLTBPTL (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltbpt = (bltbpt & ~0xffff) | (v & 0xFFFE); }
+static void BLTCPTH (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltcpt = (bltcpt & 0xffff) | ((uae_u32)v << 16); }
+static void BLTCPTL (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltcpt = (bltcpt & ~0xffff) | (v & 0xFFFE); }
+static void BLTDPTH (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltdpt = (bltdpt & 0xffff) | ((uae_u32)v << 16); }
+static void BLTDPTL (int hpos, uae_u16 v) { maybe_blit (hpos, 0); bltdpt = (bltdpt & ~0xffff) | (v & 0xFFFE); }
+
+static void BLTSIZE (int hpos, uae_u16 v)
+{
+       maybe_blit (hpos, 0);
+
+       blt_info.vblitsize = v >> 6;
+       blt_info.hblitsize = v & 0x3F;
+       if (!blt_info.vblitsize)
+               blt_info.vblitsize = 1024;
+       if (!blt_info.hblitsize)
+               blt_info.hblitsize = 64;
+       do_blitter (hpos, copper_access);
+}
+
+static void BLTSIZV (int hpos, uae_u16 v)
+{
+       if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               return;
+       maybe_blit (hpos, 0);
+       blt_info.vblitsize = v & 0x7FFF;
+}
+
+static void BLTSIZH (int hpos, uae_u16 v)
+{
+       if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS))
+               return;
+       maybe_blit (hpos, 0);
+       blt_info.hblitsize = v & 0x7FF;
+       if (!blt_info.vblitsize)
+               blt_info.vblitsize = 0x8000;
+       if (!blt_info.hblitsize)
+               blt_info.hblitsize = 0x0800;
+       do_blitter (hpos, copper_access);
+}
+
+STATIC_INLINE void spr_arm (int num, int state)
+{
+       switch (state) {
+       case 0:
+               nr_armed -= spr[num].armed;
+               spr[num].armed = 0;
+               break;
+       default:
+               nr_armed += 1 - spr[num].armed;
+               spr[num].armed = 1;
+               break;
+       }
+}
+
+STATIC_INLINE void sprstartstop (struct sprite *s)
+{
+       if (vpos == s->vstart)
+               s->dmastate = 1;
+       if (vpos == s->vstop)
+               s->dmastate = 0;
+}
+
+STATIC_INLINE void SPRxCTLPOS (int num)
+{
+       int sprxp;
+       struct sprite *s = &spr[num];
+
+       sprstartstop (s);
+       sprxp = (sprpos[num] & 0xFF) * 2 + (sprctl[num] & 1);
+       sprxp <<= sprite_buffer_res;
+       /* Quite a bit salad in this register... */
+       if (0) {
+       }
+#ifdef AGA
+       else if (currprefs.chipset_mask & CSMASK_AGA) {
+               sprxp |= ((sprctl[num] >> 3) & 3) >> (RES_MAX - sprite_buffer_res);
+               s->dblscan = sprpos[num] & 0x80;
+       }
+#endif
+#ifdef ECS_DENISE
+       else if (currprefs.chipset_mask & CSMASK_ECS_DENISE) {
+               sprxp |= ((sprctl[num] >> 3) & 2) >> (RES_MAX - sprite_buffer_res);
+       }
+#endif
+       s->xpos = sprxp;
+       s->vstart = (sprpos[num] >> 8) | ((sprctl[num] << 6) & 0x100);
+       s->vstop = (sprctl[num] >> 8) | ((sprctl[num] << 7) & 0x100);
+       if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) {
+               s->vstart |= (sprctl[num] << 3) & 0x200;
+               s->vstop |= (sprctl[num] << 4) & 0x200;
+       }
+       sprstartstop (s);
+}
+
+STATIC_INLINE void SPRxCTL_1 (uae_u16 v, int num, int hpos)
+{
+       struct sprite *s = &spr[num];
+       sprctl[num] = v;
+       spr_arm (num, 0);
+       SPRxCTLPOS (num);
+#if SPRITE_DEBUG > 0
+       if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+               write_log (L"%d:%d:SPR%dCTL %04X P=%06X VSTRT=%d VSTOP=%d HSTRT=%d D=%d A=%d CP=%x PC=%x\n",
+                       vpos, hpos, num, v, s->pt, s->vstart, s->vstop, s->xpos, spr[num].dmastate, spr[num].armed, cop_state.ip, M68K_GETPC);
+       }
+#endif
+
+}
+STATIC_INLINE void SPRxPOS_1 (uae_u16 v, int num, int hpos)
+{
+       struct sprite *s = &spr[num];
+       sprpos[num] = v;
+       SPRxCTLPOS (num);
+#if SPRITE_DEBUG > 0
+       if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+               write_log (L"%d:%d:SPR%dPOS %04X P=%06X VSTRT=%d VSTOP=%d HSTRT=%d D=%d A=%d CP=%x PC=%x\n",
+                       vpos, hpos, num, v, s->pt, s->vstart, s->vstop, s->xpos, spr[num].dmastate, spr[num].armed, cop_state.ip, M68K_GETPC);
+       }
+#endif
+}
+STATIC_INLINE void SPRxDATA_1 (uae_u16 v, int num, int hpos)
+{
+       sprdata[num][0] = v;
+#ifdef AGA
+       sprdata[num][1] = v;
+       sprdata[num][2] = v;
+       sprdata[num][3] = v;
+#endif
+       spr_arm (num, 1);
+#if SPRITE_DEBUG > 1
+       if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+               write_log (L"%d:%d:SPR%dDATA %04X P=%06X D=%d A=%d PC=%x\n",
+                       vpos, hpos, num, v, spr[num].pt, spr[num].dmastate, spr[num].armed, M68K_GETPC);
+       }
+#endif
+}
+STATIC_INLINE void SPRxDATB_1 (uae_u16 v, int num, int hpos)
+{
+       sprdatb[num][0] = v;
+#ifdef AGA
+       sprdatb[num][1] = v;
+       sprdatb[num][2] = v;
+       sprdatb[num][3] = v;
+#endif
+#if SPRITE_DEBUG > 1
+       if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+               write_log (L"%d:%d:SPR%dDATB %04X P=%06X D=%d A=%d PC=%x\n",
+                       vpos, hpos, num, v, spr[num].pt, spr[num].dmastate, spr[num].armed, M68K_GETPC);
+       }
+#endif
+}
+static void SPRxDATA (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxDATA_1 (v, num, hpos); }
+static void SPRxDATB (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxDATB_1 (v, num, hpos); }
+static void SPRxCTL (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxCTL_1 (v, num, hpos); }
+static void SPRxPOS (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxPOS_1 (v, num, hpos); }
+static void SPRxPTH (int hpos, uae_u16 v, int num)
+{
+       decide_sprites (hpos);
+       if (hpos - 1 != spr[num].ptxhpos) {
+               spr[num].pt &= 0xffff;
+               spr[num].pt |= (uae_u32)v << 16;
+       }
+#if SPRITE_DEBUG > 0
+       if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+               write_log (L"%d:%d:SPR%dPTH %06X\n", vpos, hpos, num, spr[num].pt);
+       }
+#endif
+}
+static void SPRxPTL (int hpos, uae_u16 v, int num)
+{
+       decide_sprites (hpos);
+       if (hpos - 1 != spr[num].ptxhpos) {
+               spr[num].pt &= ~0xffff;
+               spr[num].pt |= v & ~1;
+       }
+#if SPRITE_DEBUG > 0
+       if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+               write_log (L"%d:%d:SPR%dPTL %06X\n", vpos, hpos, num, spr[num].pt);
+       }
+#endif
+}
+
+static void CLXCON (uae_u16 v)
+{
+       clxcon = v;
+       clxcon_bpl_enable = (v >> 6) & 63;
+       clxcon_bpl_match = v & 63;
+}
+
+static void CLXCON2 (uae_u16 v)
+{
+       if (!(currprefs.chipset_mask & CSMASK_AGA))
+               return;
+       clxcon2 = v;
+       clxcon_bpl_enable |= v & (0x40 | 0x80);
+       clxcon_bpl_match |= (v & (0x01 | 0x02)) << 6;
+}
+
+static uae_u16 CLXDAT (void)
+{
+       uae_u16 v = clxdat | 0x8000;
+       clxdat = 0;
+       return v;
+}
+
+#ifdef AGA
+
+void dump_aga_custom (void)
+{
+       int c1, c2, c3, c4;
+       uae_u32 rgb1, rgb2, rgb3, rgb4;
+
+       for (c1 = 0; c1 < 64; c1++) {
+               c2 = c1 + 64;
+               c3 = c2 + 64;
+               c4 = c3 + 64;
+               rgb1 = current_colors.color_regs_aga[c1] | (color_regs_aga_genlock[c1] << 31);
+               rgb2 = current_colors.color_regs_aga[c2] | (color_regs_aga_genlock[c2] << 31);
+               rgb3 = current_colors.color_regs_aga[c3] | (color_regs_aga_genlock[c3] << 31);
+               rgb4 = current_colors.color_regs_aga[c4] | (color_regs_aga_genlock[c4] << 31);
+               console_out_f (L"%3d %08X %3d %08X %3d %08X %3d %08X\n",
+                       c1, rgb1, c2, rgb2, c3, rgb3, c4, rgb4);
+       }
+}
+
+static uae_u16 COLOR_READ (int num)
+{
+       int cr, cg, cb, colreg;
+       uae_u16 cval;
+
+       if (!(currprefs.chipset_mask & CSMASK_AGA) || !(bplcon2 & 0x0100))
+               return 0xffff;
+
+       colreg = ((bplcon3 >> 13) & 7) * 32 + num;
+       cr = current_colors.color_regs_aga[colreg] >> 16;
+       cg = (current_colors.color_regs_aga[colreg] >> 8) & 0xFF;
+       cb = current_colors.color_regs_aga[colreg] & 0xFF;
+       if (bplcon3 & 0x200) {
+               cval = ((cr & 15) << 8) | ((cg & 15) << 4) | ((cb & 15) << 0);
+       } else {
+               cval = ((cr >> 4) << 8) | ((cg >> 4) << 4) | ((cb >> 4) << 0);
+               if (color_regs_aga_genlock[num])
+                       cval |= 0x8000;
+       }
+       return cval;
+}
+#endif
+
+static void COLOR_WRITE (int hpos, uae_u16 v, int num)
+{
+       v &= 0xFFF;
+#ifdef AGA
+       if (currprefs.chipset_mask & CSMASK_AGA) {
+               int r,g,b;
+               int cr,cg,cb;
+               int colreg;
+               uae_u32 cval;
+
+               /* writing is disabled when RDRAM=1 */
+               if (bplcon2 & 0x0100)
+                       return;
+
+               colreg = ((bplcon3 >> 13) & 7) * 32 + num;
+               r = (v & 0xF00) >> 8;
+               g = (v & 0xF0) >> 4;
+               b = (v & 0xF) >> 0;
+               cr = current_colors.color_regs_aga[colreg] >> 16;
+               cg = (current_colors.color_regs_aga[colreg] >> 8) & 0xFF;
+               cb = current_colors.color_regs_aga[colreg] & 0xFF;
+
+               if (bplcon3 & 0x200) {
+                       cr &= 0xF0; cr |= r;
+                       cg &= 0xF0; cg |= g;
+                       cb &= 0xF0; cb |= b;
+               } else {
+                       cr = r + (r << 4);
+                       cg = g + (g << 4);
+                       cb = b + (b << 4);
+                       color_regs_aga_genlock[colreg] = v >> 15;
+               }
+               cval = (cr << 16) | (cg << 8) | cb;
+               if (cval == current_colors.color_regs_aga[colreg])
+                       return;
+
+               /* Call this with the old table still intact. */
+               record_color_change (hpos, colreg, cval);
+               remembered_color_entry = -1;
+               current_colors.color_regs_aga[colreg] = cval;
+               current_colors.acolors[colreg] = getxcolor (cval);
+       } else {
+#endif
+               if (current_colors.color_regs_ecs[num] == v)
+                       return;
+               /* Call this with the old table still intact. */
+               record_color_change (hpos, num, v);
+               remembered_color_entry = -1;
+               current_colors.color_regs_ecs[num] = v;
+               current_colors.acolors[num] = getxcolor (v);
+#ifdef AGA
+       }
+#endif
+}
+
+/* The copper code.  The biggest nightmare in the whole emulator.
+
+Alright.  The current theory:
+1. Copper moves happen 2 cycles after state READ2 is reached.
+It can't happen immediately when we reach READ2, because the
+data needs time to get back from the bus.  An additional 2
+cycles are needed for non-Agnus registers, to take into account
+the delay for moving data from chip to chip.
+2. As stated in the HRM, a WAIT really does need an extra cycle
+to wake up.  This is implemented by _not_ falling through from
+a successful wait to READ1, but by starting the next cycle.
+(Note: the extra cycle for the WAIT apparently really needs a
+free cycle; i.e. contention with the bitplane fetch can slow
+it down).
+3. Apparently, to compensate for the extra wake up cycle, a WAIT
+will use the _incremented_ horizontal position, so the WAIT
+cycle normally finishes two clocks earlier than the position
+it was waiting for.  The extra cycle then takes us to the
+position that was waited for.
+If the earlier cycle is busy with a bitplane, things change a bit.
+E.g., waiting for position 0x50 in a 6 plane display: In cycle
+0x4e, we fetch BPL5, so the wait wakes up in 0x50, the extra cycle
+takes us to 0x54 (since 0x52 is busy), then we have READ1/READ2,
+and the next register write is at 0x5c.
+4. The last cycle in a line is not usable for the copper.
+5. A 4 cycle delay also applies to the WAIT instruction.  This means
+that the second of two back-to-back WAITs (or a WAIT whose
+condition is immediately true) takes 8 cycles.
+6. This also applies to a SKIP instruction.  The copper does not
+fetch the next instruction while waiting for the second word of
+a WAIT or a SKIP to arrive.
+7. A SKIP also seems to need an unexplained additional two cycles
+after its second word arrives; this is _not_ a memory cycle (I
+think, the documentation is pretty clear on this).
+8. Two additional cycles are inserted when writing to COPJMP1/2.  */
+
+/* Determine which cycles are available for the copper in a display
+* with a agiven number of planes.  */
+
+STATIC_INLINE int copper_cant_read (int hpos, int alloc)
+{
+       if (hpos + 1 >= maxhpos) // first refresh slot
+               return 1;
+       if ((hpos == maxhpos - 3) && (maxhpos & 1)) {
+               if (alloc)
+                       alloc_cycle (hpos, CYCLE_COPPER);
+               return -1;
+       }
+       return is_bitplane_dma_inline (hpos);
+}
+
+static int custom_wput_copper (int hpos, uaecptr addr, uae_u32 value, int noget)
+{
+       int v;
+
+       value = debug_wputpeekdma (0xdff000 + addr, value);
+       copper_access = 1;
+       v = custom_wput_1 (hpos, addr, value, noget);
+       copper_access = 0;
+       return v;
+}
+
+static void dump_copper (TCHAR *error, int until_hpos)
+{
+       write_log (L"%s: vpos=%d until_hpos=%d\n",
+               error, vpos, until_hpos);
+       write_log (L"cvcmp=%d chcmp=%d chpos=%d cvpos=%d ci1=%04X ci2=%04X\n",
+               cop_state.vcmp,cop_state.hcmp,cop_state.hpos,cop_state.vpos,cop_state.saved_i1,cop_state.saved_i2);
+       write_log (L"cstate=%d ip=%x SPCFLAGS=%x\n",
+               cop_state.state, cop_state.ip, regs.spcflags);
+}
+
+// "emulate" chip internal delays, not the right place but fast and 99.9% programs
+// use only copper to write BPLCON1 etc.. (exception is HulkaMania/TSP..)
+// this table should be filled with zeros and done somewhere else..
+static int customdelay[]= {
+       1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* 32 0x00 - 0x3e */
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 - 0x5e */
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 - 0x7e */
+       0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, /* 0x80 - 0x9e */
+       1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0, /* 32 0xa0 - 0xde */
+       /* BPLxPTH/BPLxPTL */
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16 */
+       /* BPLCON0-3,BPLMOD1-2 */
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16 */
+       /* SPRxPTH/SPRxPTL */
+       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 16 */
+       /* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */
+       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+       /* COLORxx */
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       /* RESERVED */
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+static void copper_write (uae_u32 v)
+{
+       custom_wput_copper (current_hpos (), v >> 16, v & 0xffff, 0);
+}
+
+static void update_copper (int until_hpos)
+{
+       int vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80);
+       int c_hpos = cop_state.hpos;
+
+       if (nocustom ())
+               return;
+
+       if (cop_state.state == COP_wait && vp < cop_state.vcmp) {
+               dump_copper (L"error2", until_hpos);
+               copper_enabled_thisline = 0;
+               cop_state.state = COP_stop;
+               unset_special (SPCFLAG_COPPER);
+               return;
+       }
+
+       if (until_hpos <= last_copper_hpos)
+               return;
+
+       if (until_hpos > (maxhpos & ~1))
+               until_hpos = maxhpos & ~1;
+
+       for (;;) {
+               int old_hpos = c_hpos;
+               int hp;
+
+               if (c_hpos >= until_hpos)
+                       break;
+
+
+               /* So we know about the fetch state.  */
+               decide_line (c_hpos);
+               decide_fetch (c_hpos);
+
+               if (cop_state.movedelay > 0) {
+                       cop_state.movedelay--;
+                       if (cop_state.movedelay == 0) {
+                               custom_wput_copper (c_hpos, cop_state.moveaddr, cop_state.movedata, 0);
+                       }
+               }
+
+               if ((c_hpos == maxhpos - 3) && (maxhpos & 1))
+                       c_hpos += 1;
+               else
+                       c_hpos += 2;
+
+               switch (cop_state.state)
+               {
+               case COP_wait_in2:
+                       if (copper_cant_read (old_hpos, 0))
+                               continue;
+                       cop_state.state = COP_wait1;
+                       break;
+               case COP_skip_in2:
+                       if (copper_cant_read (old_hpos, 0))
+                               continue;
+                       cop_state.state = COP_skip1;
+                       break;
+               case COP_strobe_extra:
+                       // wait 1 copper cycle doing nothing
+                       cop_state.state = COP_strobe_delay1;
+                       break;
+               case COP_strobe_delay1:
+                       // first cycle after COPJMP is just like normal first read cycle
+                       if (copper_cant_read (old_hpos, 1))
+                               continue;
+                       cop_state.state = COP_strobe_delay2;
+                       alloc_cycle (old_hpos, CYCLE_COPPER);
+#ifdef DEBUGGER
+                       if (debug_dma)
+                               record_dma (0x8c, chipmem_wget_indirect (cop_state.ip), cop_state.ip, old_hpos, vpos, DMARECORD_COPPER);
+#endif
+                       cop_state.ip += 2;
+                       break;
+               case COP_strobe_delay2:
+                       // second cycle after COPJMP is like second read cycle except
+                       // there is 0x1FE as a target register
+                       // (following word is still read normally and tossed away)
+                       if (copper_cant_read (old_hpos, 1))
+                               continue;
+                       cop_state.state = COP_read1;
+                       alloc_cycle (old_hpos, CYCLE_COPPER);
+                       if (debug_dma)
+                               record_dma (0x1fe, chipmem_wget_indirect (cop_state.ip), cop_state.ip, old_hpos, vpos, DMARECORD_COPPER);
+                       // next cycle finally reads from new pointer
+                       if (cop_state.strobe == 1)
+                               cop_state.ip = cop1lc;
+                       else
+                               cop_state.ip = cop2lc;
+                       cop_state.strobe = 0;
+                       break;
+               case COP_start_delay:
+                       if (copper_cant_read (old_hpos, 1))
+                               continue;
+                       cop_state.state = COP_read1;
+                       alloc_cycle (old_hpos, CYCLE_COPPER);
+                       if (debug_dma)
+                               record_dma (0x1fe, 0, 0xffffffff, old_hpos, vpos, DMARECORD_COPPER);
+                       cop_state.ip = cop1lc;
+                       break;
+
+               case COP_read1:
+                       if (copper_cant_read (old_hpos, 1))
+                               continue;
+                       cop_state.i1 = last_custom_value1 = chipmem_wget_indirect (cop_state.ip);
+                       alloc_cycle (old_hpos, CYCLE_COPPER);
+#ifdef DEBUGGER
+                       if (debug_dma)
+                               record_dma (0x8c, cop_state.i1, cop_state.ip, old_hpos, vpos, DMARECORD_COPPER);
+#endif
+                       cop_state.ip += 2;
+                       cop_state.state = COP_read2;
+                       break;
+
+               case COP_read2:
+                       if (copper_cant_read (old_hpos, 1))
+                               continue;
+                       cop_state.i2 = last_custom_value1 = chipmem_wget_indirect (cop_state.ip);
+                       alloc_cycle (old_hpos, CYCLE_COPPER);
+                       cop_state.ip += 2;
+                       cop_state.saved_i1 = cop_state.i1;
+                       cop_state.saved_i2 = cop_state.i2;
+                       cop_state.saved_ip = cop_state.ip;
+
+                       if (cop_state.i1 & 1) { // WAIT or SKIP
+                               cop_state.ignore_next = 0;
+                               if (cop_state.i2 & 1)
+                                       cop_state.state = COP_skip_in2;
+                               else
+                                       cop_state.state = COP_wait_in2;
+#ifdef DEBUGGER
+                               if (debug_dma)
+                                       record_dma (0x8c, cop_state.i2, cop_state.ip - 2, old_hpos, vpos, DMARECORD_COPPER);
+#endif
+                       } else { // MOVE
+#ifdef DEBUGGER
+                               uaecptr debugip = cop_state.ip;
+#endif
+                               unsigned int reg = cop_state.i1 & 0x1FE;
+                               uae_u16 data = cop_state.i2;
+                               cop_state.state = COP_read1;
+#ifdef DEBUGGER
+                               if (debug_dma)
+                                       record_dma (reg, data, cop_state.ip - 2, old_hpos, vpos, DMARECORD_COPPER);
+#endif
+                               test_copper_dangerous (reg);
+                               if (! copper_enabled_thisline)
+                                       goto out; // was "dangerous" register -> copper stopped
+                               if (cop_state.ignore_next) {
+                                       reg = 0x1fe;
+                                       cop_state.ignore_next = 0;
+                               }
+
+                               cop_state.last_write = reg;
+                               cop_state.last_write_hpos = old_hpos;
+                               if (reg == 0x88) {
+                                       cop_state.strobe = 1;
+                                       cop_state.state = COP_strobe_delay1;
+                               } else if (reg == 0x8a) {
+                                       cop_state.strobe = 2;
+                                       cop_state.state = COP_strobe_delay1;
+                               } else {
+#if 0
+                                       event2_newevent2 (1, (reg << 16) | data, copper_write);
+#else
+                                       // FIX: all copper writes happen 1 cycle later than CPU writes
+                                       if (customdelay[reg / 2]) {
+                                               cop_state.moveaddr = reg;
+                                               cop_state.movedata = data;
+                                               cop_state.movedelay = customdelay[cop_state.moveaddr / 2];
+                                       } else {
+                                               int hpos2 = old_hpos;
+                                               custom_wput_copper (hpos2, reg, data, 0);
+                                               hpos2++;
+                                               if (!nocustom () && reg >= 0x140 && reg < 0x180 && hpos2 >= SPR0_HPOS && hpos2 < SPR0_HPOS + 4 * MAX_SPRITES) {
+                                                       do_sprites (hpos2);
+                                               }
+                                       }
+#endif
+                               }
+#ifdef DEBUGGER
+                               if (debug_copper)
+                                       record_copper (debugip - 4, old_hpos, vpos);
+#endif
+                       }
+                       break;
+
+               case COP_wait1:
+                       /* There's a nasty case here.  As stated in the "Theory" comment above, we
+                       test against the incremented copper position.  I believe this means that
+                       we have to increment the _vertical_ position at the last cycle in the line,
+                       and set the horizontal position to 0.
+                       Normally, this isn't going to make a difference, since we consider these
+                       last cycles unavailable for the copper, so waking up in the last cycle has
+                       the same effect as waking up at the start of the line.  However, there is
+                       one possible problem:  If we're at 0xFFE0, any wait for an earlier position
+                       must _not_ complete (since, in effect, the current position will be back
+                       at 0/0).  This can be seen in the Superfrog copper list.
+                       Things get monstrously complicated if we try to handle this "properly" by
+                       incrementing vpos and setting c_hpos to 0.  Especially the various speedup
+                       hacks really assume that vpos remains constant during one line.  Hence,
+                       this hack: defer the entire decision until the next line if necessary.  */
+                       if (c_hpos >= (maxhpos & ~1) || (c_hpos & 1))
+                               break;
+
+                       cop_state.state = COP_wait;
+
+                       cop_state.vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8;
+                       cop_state.hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xFE);
+
+                       vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80);
+
+                       if (cop_state.saved_i1 == 0xFFFF && cop_state.saved_i2 == 0xFFFE) {
+                               cop_state.state = COP_stop;
+                               copper_enabled_thisline = 0;
+                               unset_special (SPCFLAG_COPPER);
+                               goto out;
+                       }
+                       if (vp < cop_state.vcmp) {
+                               copper_enabled_thisline = 0;
+                               unset_special (SPCFLAG_COPPER);
+                               goto out;
+                       }
+
+                       /* fall through */
+               case COP_wait:
+                       if (copper_cant_read (old_hpos, 0))
+                               continue;
+
+                       hp = c_hpos & (cop_state.saved_i2 & 0xFE);
+                       if (vp <= cop_state.vcmp && hp < cop_state.hcmp)
+                               break;
+
+                       /* Now we know that the comparisons were successful.  We might still
+                       have to wait for the blitter though.  */
+                       if ((cop_state.saved_i2 & 0x8000) == 0) {
+                               decide_blitter (old_hpos);
+                               if (bltstate != BLT_done) {
+                                       /* We need to wait for the blitter.  */
+                                       cop_state.state = COP_bltwait;
+                                       copper_enabled_thisline = 0;
+                                       unset_special (SPCFLAG_COPPER);
+                                       goto out;
+                               } else {
+                                       if (debug_dma)
+                                               record_dma_event (DMA_EVENT_COPPERWAKE, old_hpos, vp);
+                               }
+                       }
+
+#ifdef DEBUGGER
+                       if (debug_copper)
+                               record_copper (cop_state.ip - 4, old_hpos, vpos);
+#endif
+
+                       cop_state.state = COP_read1;
+                       break;
+
+               case COP_skip1:
+                       {
+                               unsigned int vcmp, hcmp, vp1, hp1;
+
+                               if (c_hpos >= (maxhpos & ~1) || (c_hpos & 1))
+                                       break;
+                               if (copper_cant_read (old_hpos, 0))
+                                       continue;
+
+                               vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8;
+                               hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xFE);
+                               vp1 = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80);
+                               hp1 = c_hpos & (cop_state.saved_i2 & 0xFE);
+
+                               if ((vp1 > vcmp || (vp1 == vcmp && hp1 >= hcmp)) && ((cop_state.saved_i2 & 0x8000) != 0 || bltstate == BLT_done))
+                                       cop_state.ignore_next = 1;
+
+                               cop_state.state = COP_read1;
+
+#ifdef DEBUGGER
+                               if (debug_copper)
+                                       record_copper (cop_state.ip - 4, old_hpos, vpos);
+#endif
+
+                               break;
+                       }
+               default:
+                       break;
+               }
+       }
+
+out:
+       cop_state.hpos = c_hpos;
+       last_copper_hpos = until_hpos;
+}
+
+static void compute_spcflag_copper (int hpos)
+{
+       int wasenabled = copper_enabled_thisline;
+
+       copper_enabled_thisline = 0;
+       unset_special (SPCFLAG_COPPER);
+       if (!dmaen (DMA_COPPER) || cop_state.state == COP_stop || cop_state.state == COP_bltwait || nocustom ())
+               return;
+
+       if (cop_state.state == COP_wait) {
+               int vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80);
+
+               if (vp < cop_state.vcmp)
+                       return;
+       }
+       // do not use past cycles if starting for the first time in this line
+       // (write to DMACON for example) hpos+1 for long lines
+       if (!wasenabled && cop_state.hpos < hpos && hpos < maxhpos) {
+               hpos = (hpos + 2) & ~1;
+               if (hpos > (maxhpos_short & ~1))
+                       hpos = maxhpos_short & ~1;
+               cop_state.hpos = hpos;
+       }
+       copper_enabled_thisline = 1;
+       set_special (SPCFLAG_COPPER);
+}
+
+/*
+Copper writes to BLTSIZE: 3 blitter idle cycles, blitter normal cycle starts
+(CPU write to BLTSIZE only have 2 idle cycles at start)
+
+BFD=0 wait: 1 cycle (or 2 if hpos is not aligned) delay before wait ends
+*/
+void blitter_done_notify (int hpos)
+{
+       int vp = vpos;
+
+       if (cop_state.state != COP_bltwait)
+               return;
+
+       hpos += 3;
+       hpos &= ~1;
+       if (hpos >= maxhpos) {
+               hpos -= maxhpos;
+               vp++;
+       }
+       cop_state.hpos = hpos;
+       cop_state.vpos = vp;
+       cop_state.state = COP_read1;
+       if (debug_dma)
+               record_dma_event (DMA_EVENT_COPPERWAKE, hpos, vp);
+
+       if (dmaen (DMA_COPPER) && vp == vpos) {
+               copper_enabled_thisline = 1;
+               set_special (SPCFLAG_COPPER);
+       }
+}
+
+void do_copper (void)
+{
+       int hpos = current_hpos ();
+       update_copper (hpos);
+}
+
+/* ADDR is the address that is going to be read/written; this access is
+the reason why we want to update the copper.  This function is also
+used from hsync_handler to finish up the line; for this case, we check
+hpos against maxhpos.  */
+STATIC_INLINE void sync_copper_with_cpu (int hpos, int do_schedule)
+{
+       /* Need to let the copper advance to the current position.  */
+       if (copper_enabled_thisline)
+               update_copper (hpos);
+}
+
+static void cursorsprite (void)
+{
+       if (!dmaen (DMA_SPRITE) || first_planes_vpos == 0)
+               return;
+       sprite_0 = spr[0].pt;
+       sprite_0_height = spr[0].vstop - spr[0].vstart;
+       sprite_0_colors[0] = 0;
+       sprite_0_doubled = 0;
+       if (sprres == 0)
+               sprite_0_doubled = 1;
+       if (currprefs.chipset_mask & CSMASK_AGA) {
+               int sbasecol = ((bplcon4 >> 4) & 15) << 4;
+               sprite_0_colors[1] = current_colors.color_regs_aga[sbasecol + 1];
+               sprite_0_colors[2] = current_colors.color_regs_aga[sbasecol + 2];
+               sprite_0_colors[3] = current_colors.color_regs_aga[sbasecol + 3];
+       } else {
+               sprite_0_colors[1] = xcolors[current_colors.color_regs_ecs[17]];
+               sprite_0_colors[2] = xcolors[current_colors.color_regs_ecs[18]];
+               sprite_0_colors[3] = xcolors[current_colors.color_regs_ecs[19]];
+       }
+       sprite_0_width = sprite_width;
+       if (currprefs.input_tablet && currprefs.input_magic_mouse) {
+               if (currprefs.input_magic_mouse_cursor == MAGICMOUSE_HOST_ONLY && mousehack_alive ())
+                       magic_sprite_mask &= ~1;
+               else
+                       magic_sprite_mask |= 1;
+       }
+}
+
+STATIC_INLINE uae_u16 sprite_fetch (struct sprite *s, int dma, int hpos, int cycle, int mode)
+{
+       uae_u16 data = last_custom_value1;
+       if (dma) {
+               if (cycle && currprefs.cpu_cycle_exact)
+                       s->ptxhpos = hpos;
+               data = last_custom_value1 = chipmem_wget_indirect (s->pt);
+               alloc_cycle (hpos, CYCLE_SPRITE);
+#ifdef DEBUGGER
+               if (debug_dma)
+                       record_dma ((s - &spr[0]) * 8 + 0x140 + mode * 4 + cycle * 2, data, s->pt, hpos, vpos, DMARECORD_SPRITE);
+#endif
+       }
+       s->pt += 2;
+       return data;
+}
+STATIC_INLINE uae_u16 sprite_fetch2 (struct sprite *s, int hpos, int cycle, int mode)
+{
+       uae_u16 data = last_custom_value1 = chipmem_wget_indirect (s->pt);
+       s->pt += 2;
+       return data;
+}
+
+STATIC_INLINE void do_sprites_1 (int num, int cycle, int hpos)
+{
+       struct sprite *s = &spr[num];
+       int dma, posctl = 0;
+       uae_u16 data;
+       // fetch both sprite pairs even if DMA was switched off between sprites
+       int isdma = dmaen (DMA_SPRITE) || ((num & 1) && spr[num & ~1].dmacycle);
+
+       if (isdma && vpos == sprite_vblank_endline)
+               spr_arm (num, 0);
+
+#ifdef AGA
+       if (isdma && s->dblscan && (fmode & 0x8000) && (vpos & 1) != (s->vstart & 1) && s->dmastate) {
+               spr_arm (num, 1);
+               return;
+       }
+#endif
+#if SPRITE_DEBUG > 3
+       if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY)
+               write_log (L"%d:%d:slot%d:%d\n", vpos, hpos, num, cycle);
+#endif
+       if (vpos == s->vstart) {
+#if SPRITE_DEBUG > 0
+               if (!s->dmastate && vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY)
+                       write_log (L"%d:%d:SPR%d START\n", vpos, hpos, num);
+#endif
+               s->dmastate = 1;
+               if (num == 0 && cycle == 0)
+                       cursorsprite ();
+       }
+       if (vpos == s->vstop || vpos == sprite_vblank_endline) {
+#if SPRITE_DEBUG > 0
+               if (s->dmastate && vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY)
+                       write_log (L"%d:%d:SPR%d STOP\n", vpos, hpos, num);
+#endif
+               s->dmastate = 0;
+#if 0
+               // roots 2.0 flower zoomer bottom part missing if this enabled
+               if (vpos == s->vstop) {
+                       spr_arm (num, 0);
+                       //return;
+               }
+#endif
+       }
+
+       if (!isdma)
+               return;
+       if (cycle && !s->dmacycle)
+               return; /* Superfrog intro flashing bee fix */
+
+       dma = hpos < plfstrt_sprite || diwstate != DIW_waiting_stop;
+       if (vpos == s->vstop || vpos == sprite_vblank_endline) {
+               s->dmastate = 0;
+               posctl = 1;
+               if (dma) {
+                       data = sprite_fetch (s, dma, hpos, cycle, 0);
+                       switch (sprite_width)
+                       {
+                       case 64:
+                               sprite_fetch2 (s, hpos, cycle, 0);
+                               sprite_fetch2 (s, hpos, cycle, 0);
+                       case 32:
+                               sprite_fetch2 (s, hpos, cycle, 0);
+                               break;
+                       }
+                       //write_log (L"%d:%d: %04X=%04X\n", vpos, hpos, 0x140 + cycle * 2 + num * 8, data);
+                       if (cycle == 0) {
+                               SPRxPOS_1 (data, num, hpos);
+                               s->dmacycle = 1;
+                       } else {
+                               SPRxCTL_1 (data, num, hpos);
+                               s->dmastate = 0;
+                               sprstartstop (s);
+                       }
+               }
+#if SPRITE_DEBUG > 1
+               if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+                       write_log (L"%d:%d:dma:P=%06X ", vpos, hpos, s->pt);
+               }
+#endif
+       }
+       if (s->dmastate && !posctl && dma) {
+               uae_u16 data;
+
+               data = sprite_fetch (s, dma, hpos, cycle, 1);
+#if SPRITE_DEBUG > 1
+               if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY) {
+                       write_log (L"%d:%d:dma:P=%06X ", vpos, hpos, s->pt);
+               }
+#endif
+               if (cycle == 0) {
+                       SPRxDATA_1 (data, num, hpos);
+                       s->dmacycle = 1;
+               } else {
+                       SPRxDATB_1 (data, num, hpos);
+                       spr_arm (num, 1);
+               }
+#ifdef AGA
+               switch (sprite_width)
+               {
+               case 64:
+                       {
+                               uae_u16 data32 = sprite_fetch2 (s, hpos, cycle, 1);
+                               uae_u16 data641 = sprite_fetch2 (s, hpos, cycle, 1);
+                               uae_u16 data642 = sprite_fetch2 (s, hpos, cycle, 1);
+                               if (dma) {
+                                       if (cycle == 0) {
+                                               sprdata[num][3] = data642;
+                                               sprdata[num][2] = data641;
+                                               sprdata[num][1] = data32;
+                                       } else {
+                                               sprdatb[num][3] = data642;
+                                               sprdatb[num][2] = data641;
+                                               sprdatb[num][1] = data32;
+                                       }
+                               }
+                       }
+                       break;
+               case 32:
+                       {
+                               uae_u16 data32 = sprite_fetch2 (s, hpos, cycle, 1);
+                               if (dma) {
+                                       if (cycle == 0)
+                                               sprdata[num][1] = data32;
+                                       else
+                                               sprdatb[num][1] = data32;
+                               }
+                       }
+                       break;
+               }
+#endif
+       }
+}
+
+static void do_sprites (int hpos)
+{
+       int maxspr, minspr;
+       int i;
+
+       if (vpos < sprite_vblank_endline)
+               return;
+
+       if (doflickerfix () && interlace_seen && (next_lineno & 1))
+               return;
+
+#ifndef CUSTOM_SIMPLE
+       maxspr = hpos;
+       minspr = last_sprite_hpos + 1;
+
+       if (minspr >= maxspr || last_sprite_hpos == hpos)
+               return;
+
+       if (maxspr >= SPR0_HPOS + MAX_SPRITES * 4)
+               maxspr = SPR0_HPOS + MAX_SPRITES * 4 - 1;
+       if (minspr < SPR0_HPOS)
+               minspr = SPR0_HPOS;
+
+       if (minspr == maxspr)
+               return;
+
+       for (i = minspr; i <= maxspr; i++) {
+               int cycle = -1;
+               int num = (i - SPR0_HPOS) / 4;
+               switch ((i - SPR0_HPOS) & 3)
+               {
+               case 0:
+                       cycle = 0;
+                       spr[num].dmacycle = 0;
+                       break;
+               case 2:
+                       cycle = 1;
+                       break;
+               }
+               if (cycle >= 0) {
+                       spr[num].ptxhpos = MAXHPOS;
+                       do_sprites_1 (num, cycle, i);
+               }
+       }
+
+       last_sprite_hpos = hpos;
+#else
+       for (i = 0; i < MAX_SPRITES * 2; i++) {
+               spr[i / 2].dmacycle = 1;
+               do_sprites_1 (i / 2, i & 1, 0);
+       }
+#endif
+}
+
+static void init_sprites (void)
+{
+       memset (sprpos, 0, sizeof sprpos);
+       memset (sprctl, 0, sizeof sprctl);
+}
+
+static void init_hardware_frame (void)
+{
+       int i;
+
+       first_bpl_vpos = -1;
+       next_lineno = 0;
+       prev_lineno = -1;
+       nextline_how = nln_normal;
+       diwstate = DIW_waiting_start;
+       ddfstate = DIW_waiting_start;
+       first_planes_vpos = 0;
+       last_planes_vpos = 0;
+       diwfirstword_total = max_diwlastword;
+       diwlastword_total = 0;
+       ddffirstword_total = max_diwlastword;
+       ddflastword_total = 0;
+       plflastline_total = 0;
+       plffirstline_total = current_maxvpos ();
+       autoscale_bordercolors = 0;
+       for (i = 0; i < MAX_SPRITES; i++)
+               spr[i].ptxhpos = MAXHPOS;
+}
+
+void init_hardware_for_drawing_frame (void)
+{
+       /* Avoid this code in the first frame after a customreset.  */
+       if (prev_sprite_entries) {
+               int first_pixel = prev_sprite_entries[0].first_pixel;
+               int npixels = prev_sprite_entries[prev_next_sprite_entry].first_pixel - first_pixel;
+               memset (spixels + first_pixel, 0, npixels * sizeof *spixels);
+               memset (spixstate.bytes + first_pixel, 0, npixels * sizeof *spixstate.bytes);
+       }
+       prev_next_sprite_entry = next_sprite_entry;
+
+       next_color_change = 0;
+       next_sprite_entry = 0;
+       next_color_entry = 0;
+       remembered_color_entry = -1;
+
+       prev_sprite_entries = sprite_entries[current_change_set];
+       curr_sprite_entries = sprite_entries[current_change_set ^ 1];
+       prev_color_changes = color_changes[current_change_set];
+       curr_color_changes = color_changes[current_change_set ^ 1];
+       prev_color_tables = color_tables[current_change_set];
+       curr_color_tables = color_tables[current_change_set ^ 1];
+
+       prev_drawinfo = line_drawinfo[current_change_set];
+       curr_drawinfo = line_drawinfo[current_change_set ^ 1];
+       current_change_set ^= 1;
+
+       color_src_match = color_dest_match = -1;
+
+       /* Use both halves of the array in alternating fashion.  */
+       curr_sprite_entries[0].first_pixel = current_change_set * MAX_SPR_PIXELS;
+       next_sprite_forced = 1;
+}
+
+static void do_savestate(void);
+
+static int rpt_vsync (void)
+{
+       int v = read_processor_time () - vsyncmintime;
+       if (v > (int)syncbase || v < -((int)syncbase)) {
+               vsyncmintime = read_processor_time ();
+               v = 0;
+       }
+       return v;
+}
+
+static void framewait (void)
+{
+       frame_time_t curr_time;
+       frame_time_t start;
+       int vs = isvsync ();
+
+       if (vs > 0) {
+               vsyncmintime = vsynctime;
+               update_screen ();
+               return;
+       } else if (vs < 0) {
+               vsyncmintime = vsynctime;
+               vsync_busywait ();
+               update_screen ();
+               return;
+       }
+       for (;;) {
+               double v = rpt_vsync () / (syncbase / 1000.0);
+               if (v >= -4)
+                       break;
+               sleep_millis (2);
+       }
+       update_screen ();
+       curr_time = start = read_processor_time ();
+       while (rpt_vsync () < 0);
+       curr_time = read_processor_time ();
+       vsyncmintime = curr_time + vsynctime;
+       idletime += read_processor_time() - start;
+}
+
+static frame_time_t frametime2;
+
+void fpscounter_reset (void)
+{
+       timeframes = 0;
+       frametime2 = 0;
+       bogusframe = 2;
+       lastframetime = read_processor_time ();
+       idletime = 0;
+}
+
+static void fpscounter (void)
+{
+       frame_time_t now, last;
+       int mcnt = 10;
+
+       now = read_processor_time ();
+       last = now - lastframetime;
+       lastframetime = now;
+
+       if (bogusframe)
+               return;
+
+       frametime += last;
+       frametime2 += last;
+       timeframes++;
+       if ((timeframes % mcnt) == 0) {
+               double idle = 1000 - (idletime == 0 ? 0.0 : (double)idletime * 1000.0 / (vsynctime * mcnt));
+               int fps = frametime2 == 0 ? 0 : (syncbase * mcnt) / (frametime2 / 10);
+               if (fps > 9999)
+                       fps = 9999;
+               if (idle < 0)
+                       idle = 0;
+               if (idle > 100 * 10)
+                       idle = 100 * 10;
+               if (fake_vblank_hz * 10 > fps) {
+                       double mult = (double)fake_vblank_hz * 10.0 / fps;
+                       idle *= mult;
+               }
+               if (currprefs.turbo_emulation && idle < 100 * 10)
+                       idle = 100 * 10;
+               gui_fps (fps, (int)idle);
+               frametime2 = 0;
+               idletime = 0;
+       }
+}
+
+// vsync functions that are not hardware timing related
+static void vsync_handler_pre (void)
+{
+       if (bogusframe > 0)
+               bogusframe--;
+
+       handle_events ();
+
+#ifdef PICASSO96
+       picasso_handle_vsync ();
+#endif
+       audio_vsync ();
+       blkdev_vsync ();
+       CIA_vsync_prehandler ();
+
+       if (quit_program > 0) {
+               /* prevent possible infinite loop at wait_cycles().. */
+               framecnt = 0;
+               reset_decisions ();
+               return;
+       }
+
+       config_check_vsync ();
+       if (timehack_alive > 0)
+               timehack_alive--;
+
+       inputdevice_vsync ();
+
+       filesys_vsync ();
+
+       sampler_vsync ();
+
+       vsync_handle_redraw (lof_store, lof_changed);
+}
+
+// emulated hardware vsync
+static void vsync_handler_post (void)
+{
+       fpscounter ();
+
+       if (!isvsync ()
+#ifdef AVIOUTPUT
+               && ((avioutput_framelimiter && avioutput_enabled) || !avioutput_enabled)
+#endif
+               ) {
+#ifdef JIT
+                       if (!currprefs.cachesize) {
+#endif
+                               if (currprefs.m68k_speed == -1) {
+                                       frame_time_t curr_time = read_processor_time ();
+                                       vsyncmintime += vsynctime;
+                                       /* @@@ Mathias? How do you think we should do this? */
+                                       /* If we are too far behind, or we just did a reset, adjust the
+                                       * needed time. */
+                                       if ((long int)(curr_time - vsyncmintime) > 0 || rpt_did_reset)
+                                               vsyncmintime = curr_time + vsynctime;
+                                       rpt_did_reset = 0;
+                                       update_screen ();
+                               } else if (rpt_available) {
+                                       framewait ();
+                               }
+#ifdef JIT
+                       } else {
+                               if (rpt_available && currprefs.m68k_speed == 0) {
+                                       framewait ();
+                               } else {
+                                       update_screen ();
+                               }
+                       }
+#endif
+       } else if (currprefs.m68k_speed == 0) {
+               framewait ();
+       } else {
+               update_screen ();
+       }
+
+#if CUSTOM_DEBUG > 1
+       if ((intreq & 0x0020) && (intena & 0x0020))
+               write_log (L"vblank interrupt not cleared\n");
+#endif
+       DISK_vsync ();
+       if (bplcon0 & 4)
+               lof_store = lof_store ? 0 : 1;
+       lof_current = lof_store;
+
+       if (debug_copper)
+               record_copper_reset ();
+       if (debug_dma)
+               record_dma_reset ();
+
+       if (p96refresh_active) {
+               vpos_count = p96refresh_active;
+               vtotal = vpos_count;
+       }
+       if ((beamcon0 & (0x20 | 0x80)) != (new_beamcon0 & (0x20 | 0x80)) || (abs (vpos_count - vpos_count_prev)  > 1))
+               init_hz ();
+       if (lof_changed)
+               compute_vsynctime ();
+       vpos_count_prev = vpos_count;
+
+       lof_changed = 0;
+
+       COPJMP (1, 1);
+
+       init_hardware_frame ();
+
+       vsync_cycles = get_cycles ();
+}
+
+#ifdef JIT
+
+#define N_LINES 8
+
+STATIC_INLINE int trigger_frh (int v)
+{
+       return (v & (N_LINES - 1)) == 0;
+}
+
+static long int diff32 (frame_time_t x, frame_time_t y)
+{
+       return (long int)(x - y);
+}
+static void frh_handler (void)
+{
+       if (currprefs.m68k_speed == -1) {
+               frame_time_t curr_time = read_processor_time ();
+               vsyncmintime += vsynctime * N_LINES / maxvpos_nom;
+               /* @@@ Mathias? How do you think we should do this? */
+               /* If we are too far behind, or we just did a reset, adjust the
+               * needed time. */
+               if (rpt_did_reset) {
+                       vsyncmintime = curr_time + vsynctime;
+                       rpt_did_reset = 0;
+               }
+               /* Allow this to be one frame's worth of cycles out */
+               while (diff32 (curr_time, vsyncmintime + vsynctime) > 0) {
+                       vsyncmintime += vsynctime * N_LINES / maxvpos_nom;
+                       if (currprefs.turbo_emulation)
+                               break;
+               }
+       }
+}
+#endif
+
+static void copper_check (int n)
+{
+       if (cop_state.state == COP_wait) {
+               int vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80);
+               if (vp < cop_state.vcmp) {
+                       if (copper_enabled_thisline)
+                               write_log (L"COPPER BUG %d: vp=%d vpos=%d vcmp=%d act=%d thisline=%d\n", n, vp, vpos, cop_state.vcmp, copper_enabled_thisline);
+               }
+       }
+}
+
+/*
+
+0 0 -
+1 1 --
+2 2 -
+3 3 --
+4 4 -
+5 5 --
+
+0 x -
+1 0 --
+2 1 -
+3 2 --
+4 3 -
+5 4 --
+
+*/
+
+static void hsync_scandoubler (void)
+{
+       int i, idx1;
+       struct draw_info *dip1;
+       uaecptr bpltmp[8], bpltmpx[8];
+
+       next_lineno++;
+       scandoubled_line = 1;
+       debug_dma = 0;
+
+       for (i = 0; i < 8; i++) {
+               int diff;
+               bpltmp[i] = bplpt[i];
+               bpltmpx[i] = bplptx[i];
+               if (prevbpl[lof_store][vpos][i] && prevbpl[1 - lof_store][vpos][i]) {
+                       diff = prevbpl[lof_store][vpos][i] - prevbpl[1 - lof_store][vpos][i];
+                       if (lof_store) {
+                               if (bplcon0 & 4)
+                                       bplpt[i] = prevbpl[lof_store][vpos][i] - diff;
+                       } else {
+                               if (bplcon0 & 4)
+                                       bplpt[i] = prevbpl[lof_store][vpos][i];
+                               else
+                                       bplpt[i] = bplpt[i] - diff;
+
+                       }
+               }
+       }
+
+       reset_decisions ();
+       plf_state = plf_idle;
+
+       // copy color changes
+       dip1 = curr_drawinfo + next_lineno - 1;
+       for (idx1 = dip1->first_color_change; idx1 < dip1->last_color_change; idx1++) {
+               struct color_change *cs2 = &curr_color_changes[idx1];
+               int regno = cs2->regno;
+               int hpos = cs2->linepos;
+               if (regno < 0x1000 && hpos < HBLANK_OFFSET && !(beamcon0 & 0x80) && prev_lineno >= 0) {
+                       struct draw_info *pdip = curr_drawinfo + next_lineno - 1;
+                       int idx = pdip->last_color_change;
+                       pdip->last_color_change++;
+                       pdip->nr_color_changes++;
+                       curr_color_changes[idx].linepos = hpos + maxhpos + 1;
+                       curr_color_changes[idx].regno = regno;
+                       curr_color_changes[idx].value = cs2->value;
+                       curr_color_changes[idx + 1].regno = -1;
+               } else {
+                       struct color_change *cs1 = &curr_color_changes[next_color_change];
+                       memcpy (cs1, cs2, sizeof (struct color_change));
+                       next_color_change++;
+               }
+       }
+       curr_color_changes[next_color_change].regno = -1;
+
+       finish_decisions ();
+       hsync_record_line_state (next_lineno, nln_normal, thisline_changed);
+       hardware_line_completed (next_lineno);
+       scandoubled_line = 0;
+
+       for (i = 0; i < 8; i++) {
+               bplpt[i] = bpltmp[i];
+               bplptx[i] = bpltmpx[i];
+       }
+}
+
+static void events_dmal (int);
+static uae_u16 dmal, dmal_hpos;
+
+static void dmal_emu (uae_u32 v)
+{
+       // Disk and Audio DMA bits are ignored by Agnus, Agnus only checks DMAL and master bit
+       if (!(dmacon & DMA_MASTER))
+               return;
+       int hpos = current_hpos ();
+       if (v >= 6) {
+               v -= 6;
+               int nr = v / 2;
+               uaecptr pt = audio_getpt (nr, (v & 1) != 0);
+               uae_u16 dat = chipmem_wget_indirect (pt);
+#ifdef DEBUGGER
+               if (debug_dma)
+                       record_dma (0xaa + nr * 16, dat, pt, hpos, vpos, DMARECORD_AUDIO);
+#endif
+               last_custom_value1 = dat;
+               AUDxDAT (nr, dat, pt);
+       } else {
+               uae_u16 dat;
+               int w = v & 1;
+               uaecptr pt = disk_getpt ();
+               // disk_fifostatus() needed in >100% disk speed modes
+               if (w) {
+                       if (disk_fifostatus () <= 0) {
+                               dat = chipmem_wget_indirect (pt);
+                               last_custom_value1 = dat;
+                               DSKDAT (dat);
+                       }
+               } else {
+                       if (disk_fifostatus () >= 0) {
+                               dat = DSKDATR ();
+                               chipmem_wput_indirect (pt, dat);
+                       }
+               }
+#ifdef DEBUGGER
+               if (debug_dma)
+                       record_dma (w ? 0x26 : 0x08, dat, pt, hpos, vpos, DMARECORD_DISK);
+#endif
+       }
+}
+
+static void dmal_func (uae_u32 v)
+{
+       dmal_emu (v);
+       events_dmal (0);
+}
+
+static void dmal_func2 (uae_u32 v)
+{
+       while (dmal) {
+               if (dmal & 3)
+                       dmal_emu (dmal_hpos + ((dmal & 2) ? 1 : 0));
+               dmal_hpos += 2;
+               dmal >>= 2;
+       }
+}
+
+static void events_dmal (int hp)
+{
+       if (!dmal)
+               return;
+       if (currprefs.cpu_cycle_exact) {
+               while (dmal) {
+                       if (dmal & 3)
+                               break;
+                       hp += 2;
+                       dmal >>= 2;
+                       dmal_hpos += 2;
+               }
+               event2_newevent2 (hp, dmal_hpos + ((dmal & 2) ? 1 : 0), dmal_func);
+               dmal &= ~3;
+       } else if (currprefs.cachesize) {
+               dmal_func2 (0);
+       } else {
+               event2_newevent2 (hp, 13, dmal_func2);
+       }
+}
+
+static void events_dmal_hsync (void)
+{
+       if (dmal)
+               write_log (L"DMAL error!? %04x\n", dmal);
+       dmal = audio_dmal ();
+       dmal <<= 6;
+       dmal |= disk_dmal ();
+       if (!dmal)
+               return;
+       dmal_hpos = 0;
+       if (currprefs.cpu_cycle_exact) {
+               for (int i = 0; i < 6 + 8; i += 2) {
+                       if (dmal & (3 << i)) {
+                               alloc_cycle_ext (i + 7, CYCLE_MISC);
+                       }
+               }
+       }
+       events_dmal (7);
+}
+
+static bool is_vsync (void)
+{
+       int vp = vpos + 1;
+       int vpc = vpos_count + 1;
+       /* Agnus vpos counter keeps counting until it wraps around if VPOSW writes put it past maxvpos */
+       if (vp >= maxvpos_total)
+               vp = 0;
+       if (vp == maxvpos + lof_store || vp == maxvpos + lof_store + 1 || vpc >= MAXVPOS) {
+               // vpos_count >= MAXVPOS just to not crash if VPOSW writes prevent vsync completely
+               return true;
+       }
+       return false;
+}
+
+static void set_hpos (void)
+{
+       maxhpos = maxhpos_short + lol;
+       eventtab[ev_hsync].evtime = get_cycles () + HSYNCTIME;
+       eventtab[ev_hsync].oldcycles = get_cycles ();
+}
+
+// this finishes current line
+static void hsync_handler_pre (bool isvsync)
+{
+       int hpos = current_hpos ();
+
+       if (!nocustom ()) {
+               sync_copper_with_cpu (maxhpos, 0);
+               finish_decisions ();
+               if (thisline_decision.plfleft != -1) {
+                       if (currprefs.collision_level > 1)
+                               do_sprite_collisions ();
+                       if (currprefs.collision_level > 2)
+                               do_playfield_collisions ();
+               }
+               hsync_record_line_state (next_lineno, nextline_how, thisline_changed);
+               /* reset light pen latch */
+               if (vpos == sprite_vblank_endline) {
+                       lightpen_triggered = 0;
+                       sprite_0 = 0;
+               }
+               if (lightpen_cx > 0 && (bplcon0 & 8) && !lightpen_triggered && lightpen_cy == vpos) {
+                       vpos_lpen = vpos;
+                       hpos_lpen = lightpen_cx;
+                       lightpen_triggered = 1;
+               }
+               hardware_line_completed (next_lineno);
+               if (doflickerfix () && interlace_seen)
+                       hsync_scandoubler ();
+       }
+
+#ifdef A2065
+       a2065_hsync_handler ();
+#endif
+#ifdef CD32
+       AKIKO_hsync_handler ();
+#endif
+#ifdef CDTV
+       CDTV_hsync_handler ();
+#endif
+       decide_blitter (-1);
+
+#ifdef PICASSO96
+       picasso_handle_hsync ();
+#endif
+       {
+               void ahi_hsync (void);
+               ahi_hsync ();
+       }
+
+       DISK_hsync ();
+       if (currprefs.produce_sound)
+               audio_hsync ();
+       CIA_hsync_prehandler ();
+
+       hsync_counter++;
+
+       if (islinetoggle ())
+               lol ^= 1;
+       else
+               lol = 0;
+
+       vpos++;
+       vpos_count++;
+       if (vpos >= maxvpos_total)
+               vpos = 0;
+       if (isvsync) {
+               vpos = 0;
+               vsync_counter++;
+       }
+       set_hpos ();
+#if 0
+       static int ppp = 2;
+       if (input_record && hsync_counter == 100 * 313 + 1) {
+               ppp--;
+               if (ppp == 0)
+                       activate_debugger ();
+       }
+#endif
+}
+
+// this prepares for new line
+static void hsync_handler_post (bool isvsync)
+{
+       last_copper_hpos = 0;
+#ifdef CPUEMU_12
+       if (currprefs.cpu_cycle_exact || currprefs.blitter_cycle_exact) {
+               memset (cycle_line, 0, sizeof cycle_line);
+       }
+#endif
+
+       bool ciasyncs = !(bplcon0 & 2) || ((bplcon0 & 2) && currprefs.genlock);
+       CIA_hsync_posthandler (ciasyncs);
+       if (currprefs.cs_ciaatod > 0) {
+               static int cia_hsync;
+               cia_hsync -= 256;
+               if (cia_hsync <= 0) {
+                       CIA_vsync_posthandler (1);
+                       cia_hsync += ((MAXVPOS_PAL * MAXHPOS_PAL * 50 * 256) / (maxhpos * (currprefs.cs_ciaatod == 2 ? 60 : 50)));
+               }
+       } else if (currprefs.cs_ciaatod == 0 && isvsync) {
+               CIA_vsync_posthandler (ciasyncs);
+       }
+
+       if (vpos == equ_vblank_endline + 1 && lof_current != lof_store) {
+               // argh, line=0 field decision was wrong, someone did
+               // something stupid and changed LOF
+               // lof_current = lof_store;
+               // don't really know what to do here exactly without corrupt display
+       }
+
+       inputdevice_hsync ();
+
+       last_custom_value1 = 0xffff; // refresh slots should set this to 0xffff
+
+       if (!nocustom ()) {
+               if (!currprefs.blitter_cycle_exact && bltstate != BLT_done && dmaen (DMA_BITPLANE) && diwstate == DIW_waiting_stop) {
+                       blitter_slowdown (thisline_decision.plfleft, thisline_decision.plfright - (16 << fetchmode),
+                               cycle_diagram_total_cycles[fetchmode][GET_RES_AGNUS (bplcon0)][GET_PLANES_LIMIT (bplcon0)],
+                               cycle_diagram_free_cycles[fetchmode][GET_RES_AGNUS (bplcon0)][GET_PLANES_LIMIT (bplcon0)]);
+               }
+       }
+
+       if (isvsync) {
+               // vpos_count >= MAXVPOS just to not crash if VPOSW writes prevent vsync completely
+               if ((bplcon0 & 8) && !lightpen_triggered) {
+                       vpos_lpen = vpos - 1;
+                       hpos_lpen = maxhpos;
+                       lightpen_triggered = 1;
+               }
+               vpos = 0;
+               vsync_handler_post ();
+               vpos_count = 0;
+       }
+       // DIP Agnus (8361): vblank interrupt is triggered on line 1!
+       if (currprefs.cs_dipagnus) {
+               if (vpos == 1)
+                       send_interrupt (5, 1 * CYCLE_UNIT);
+       } else {
+               if (vpos == 0)
+                       send_interrupt (5, 1 * CYCLE_UNIT);
+       }
+
+#ifdef CPUEMU_12
+       if (currprefs.cpu_cycle_exact || currprefs.blitter_cycle_exact) {
+               int hp = maxhpos - 1, i;
+               for (i = 0; i < 4; i++) {
+                       alloc_cycle (hp, i == 0 ? CYCLE_STROBE : CYCLE_REFRESH); /* strobe */
+#ifdef DEBUGGER
+                       if (debug_dma) {
+                               uae_u16 strobe = 0x3c;
+                               if (vpos < equ_vblank_endline)
+                                       strobe = 0x38;
+                               else if (vpos < minfirstline)
+                                       strobe = 0x3a;
+                               else if (vpos + 1 == maxvpos + lof_store)
+                                       strobe = 0x38;
+                               else if ((currprefs.chipset_mask & CSMASK_ECS_AGNUS) && lol)
+                                       strobe = 0x3e;
+                               record_dma (i == 0 ? strobe : 0x1fe, 0xffff, 0xffffffff, hp, vpos, DMARECORD_REFRESH);
+                       }
+#endif
+                       hp += 2;
+                       if (hp >= maxhpos)
+                               hp -= maxhpos;
+               }
+       }
+#endif
+
+
+       events_dmal_hsync ();
+
+#ifdef JIT
+       if (currprefs.cachesize) {
+               if (currprefs.m68k_speed == -1) {
+                       static int count = 0;
+                       count++;
+                       if (trigger_frh (count)) {
+                               frh_handler ();
+                       }
+                       is_lastline = trigger_frh (count + 1) && ! rpt_did_reset;
+               } else {
+                       is_lastline = 0;
+               }
+       } else {
+#endif
+               is_lastline = vpos + 1 == maxvpos + lof_store && currprefs.m68k_speed == -1;
+#ifdef JIT
+       }
+#endif
+
+       if (!nocustom ()) {
+               int lineno = vpos;
+               if (lineno >= MAXVPOS)
+                       lineno %= MAXVPOS;
+               if ((bplcon0 & 4) && currprefs.gfx_vresolution)
+                       notice_interlace_seen ();
+               nextline_how = nln_normal;
+               if (doflickerfix () && interlace_seen) {
+                       lineno *= 2;
+               } else if (currprefs.gfx_vresolution && (doublescan <= 0 || interlace_seen > 0)) {
+                       lineno *= 2;
+                       nextline_how = currprefs.gfx_vresolution > VRES_NONDOUBLE && currprefs.gfx_scanlines == false ? nln_doubled : nln_nblack;
+                       if ((bplcon0 & 4) || (interlace_seen > 0 && !lof_current)) {
+                               if (!lof_current) {
+                                       lineno++;
+                                       nextline_how = nln_lower;
+                               } else {
+                                       nextline_how = nln_upper;
+                               }
+                       }
+               }
+               prev_lineno = next_lineno;
+               next_lineno = lineno;
+               reset_decisions ();
+       }
+
+       if (uae_int_requested) {
+               INTREQ (0x8000 | 0x0008);
+       }
+
+       {
+               extern int volatile uaenet_int_requested;
+               extern int volatile uaenet_vsync_requested;
+               if (uaenet_int_requested || (uaenet_vsync_requested && vpos == 10)) {
+                       INTREQ (0x8000 | 0x2000);
+               }
+       }
+
+       {
+               extern void bsdsock_fake_int_handler (void);
+               extern int volatile bsd_int_requested;
+               if (bsd_int_requested)
+                       bsdsock_fake_int_handler ();
+       }
+
+       plfstrt_sprite = plfstrt;
+       /* See if there's a chance of a copper wait ending this line.  */
+       cop_state.hpos = 0;
+       cop_state.last_write = 0;
+       compute_spcflag_copper (maxhpos);
+
+       serial_hsynchandler ();
+#ifdef CUSTOM_SIMPLE
+       do_sprites (0);
+#endif
+
+       gayle_hsync ();
+       scsi_hsync ();
+
+       //copper_check (2);
+
+       if (GET_PLANES (bplcon0) > 0 && dmaen (DMA_BITPLANE)) {
+               if (vpos > last_planes_vpos)
+                       last_planes_vpos = vpos;
+               if (vpos >= minfirstline && first_planes_vpos == 0) {
+                       first_planes_vpos = vpos > minfirstline ? vpos - 1 : vpos;
+               } else if (vpos == current_maxvpos () - 1) {
+                       last_planes_vpos = vpos - 1;
+               }
+       }
+       if (diw_change == 0) {
+               if (vpos >= first_planes_vpos && vpos <= last_planes_vpos) {
+                       if (diwlastword > diwlastword_total)
+                               diwlastword_total = diwlastword;
+                       if (diwfirstword < diwfirstword_total) {
+                               diwfirstword_total = diwfirstword;
+                               firstword_bplcon1 = bplcon1;
+                       }
+               }
+               if (diwstate == DIW_waiting_stop) {
+                       int f = 8 << fetchmode;
+                       if (plfstrt + f < ddffirstword_total + f)
+                               ddffirstword_total = plfstrt + f;
+                       if (plfstop + 2 * f > ddflastword_total + 2 * f)
+                               ddflastword_total = plfstop + 2 * f;
+               }
+               if ((plffirstline < plffirstline_total || (plffirstline_total == minfirstline && vpos > minfirstline)) && plffirstline < vpos / 2) {
+                       firstword_bplcon1 = bplcon1;
+                       if (plffirstline < minfirstline)
+                               plffirstline_total = minfirstline;
+                       else
+                               plffirstline_total = plffirstline;
+               }
+               if (plflastline > plflastline_total && plflastline > plffirstline_total && plflastline > maxvpos / 2)
+                       plflastline_total = plflastline;
+       }
+       if (diw_change > 0)
+               diw_change--;
+
+
+#if 0
+       {
+               static int skip;
+               if (M68K_GETPC >= 0x0C0D7A2 && M68K_GETPC < 0x00C0D7B2 && vpos == 0xf3) {
+                       if (!skip)
+                               activate_debugger ();
+                       skip = 1;
+               }
+               if (vpos != 0xf3)
+                       skip = 0;
+       }
+#endif
+}
+
+static void hsync_handler (void)
+{
+       bool vs = is_vsync ();
+       hsync_handler_pre (vs);
+       if (vs) {
+               vsync_handler_pre ();
+               if (savestate_check ()) {
+                       uae_reset (0);
+                       return;
+               }
+       }
+       hsync_handler_post (vs);
+}
+
+void init_eventtab (void)
+{
+       int i;
+
+       nextevent = 0;
+       for (i = 0; i < ev_max; i++) {
+               eventtab[i].active = 0;
+               eventtab[i].oldcycles = get_cycles ();
+       }
+       for (i = 0; i < ev2_max; i++) {
+               eventtab2[i].active = 0;
+       }
+
+       eventtab[ev_cia].handler = CIA_handler;
+       eventtab[ev_hsync].handler = hsync_handler;
+       eventtab[ev_hsync].evtime = get_cycles () + HSYNCTIME;
+       eventtab[ev_hsync].active = 1;
+       eventtab[ev_misc].handler = MISC_handler;
+       eventtab[ev_audio].handler = audio_evhandler;
+
+       eventtab2[ev2_blitter].handler = blitter_handler;
+       eventtab2[ev2_disk].handler = DISK_handler;
+
+       events_schedule ();
+}
+
+void custom_prepare (void)
+{
+       set_hpos ();
+       hsync_handler_post (true);
+}
+
+void custom_reset (int hardreset)
+{
+       int i;
+       int zero = 0;
+
+       target_reset ();
+       reset_all_systems ();
+       write_log (L"Reset at %08X\n", M68K_GETPC);
+       memory_map_dump ();
+
+       lightpen_x = lightpen_y = -1;
+       lightpen_triggered = 0;
+       lightpen_cx = lightpen_cy = -1;
+       if (!savestate_state) {
+               extra_cycle = 0;
+               hsync_counter = 0;
+               vsync_counter = 0;
+               currprefs.chipset_mask = changed_prefs.chipset_mask;
+               update_mirrors ();
+               if (!aga_mode) {
+                       for (i = 0; i < 32; i++) {
+                               current_colors.color_regs_ecs[i] = 0;
+                               current_colors.acolors[i] = getxcolor (0);
+                       }
+#ifdef AGA
+               } else {
+                       for (i = 0; i < 256; i++) {
+                               current_colors.color_regs_aga[i] = 0;
+                               current_colors.acolors[i] = getxcolor (0);
+                       }
+#endif
+               }
+
+               clxdat = 0;
+
+               /* Clear the armed flags of all sprites.  */
+               memset (spr, 0, sizeof spr);
+               nr_armed = 0;
+
+               dmacon = 0;
+               intreq_internal = 0;
+               intena = intena_internal = 0;
+
+               copcon = 0;
+               DSKLEN (0, 0);
+
+               bplcon0 = 0;
+               bplcon4 = 0x0011; /* Get AGA chipset into ECS compatibility mode */
+               bplcon3 = 0x0C00;
+
+               diwhigh = 0;
+               diwhigh_written = 0;
+               hdiwstate = DIW_waiting_start; // this does not reset at vblank
+
+               FMODE (0, 0);
+               CLXCON (0);
+               setup_fmodes (0);
+               sprite_width = GET_SPRITEWIDTH (fmode);
+               beamcon0 = new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20;
+               bltstate = BLT_done;
+               blit_interrupt = 1;
+               lof_store = lof_current = 1;
+       }
+
+       gayle_reset (hardreset);
+#ifdef AUTOCONFIG
+       expamem_reset ();
+#endif
+       a1000_reset ();
+       DISK_reset ();
+       CIA_reset ();
+       gayle_reset (0);
+#ifdef A2091
+       a2091_reset ();
+#endif
+#ifdef NCR
+       ncr_reset ();
+#endif
+#ifdef JIT
+       compemu_reset ();
+#endif
+       unset_special (~(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE));
+
+       vpos = 0;
+       vpos_count = vpos_count_prev = 0;
+
+       inputdevice_reset ();
+       timehack_alive = 0;
+
+       curr_sprite_entries = 0;
+       prev_sprite_entries = 0;
+       sprite_entries[0][0].first_pixel = 0;
+       sprite_entries[1][0].first_pixel = MAX_SPR_PIXELS;
+       sprite_entries[0][1].first_pixel = 0;
+       sprite_entries[1][1].first_pixel = MAX_SPR_PIXELS;
+       memset (spixels, 0, 2 * MAX_SPR_PIXELS * sizeof *spixels);
+       memset (&spixstate, 0, sizeof spixstate);
+
+       cop_state.state = COP_stop;
+       diwstate = DIW_waiting_start;
+
+       dmal = 0;
+       init_hz_full ();
+       vpos_lpen = -1;
+
+       audio_reset ();
+       if (!isrestore ()) {
+               /* must be called after audio_reset */
+               adkcon = 0;
+               serial_uartbreak (0);
+               audio_update_adkmasks ();
+       }
+
+       init_sprites ();
+
+       init_hardware_frame ();
+       drawing_init ();
+
+       reset_decisions ();
+
+       bogusframe = 1;
+
+       if (isrestore ()) {
+               uae_u16 v;
+               uae_u32 vv;
+
+               audio_update_adkmasks ();
+               INTENA (0);
+               INTREQ (0);
+               COPJMP (1, 1);
+               v = bplcon0;
+               BPLCON0 (0, 0);
+               BPLCON0 (0, v);
+               FMODE (0, fmode);
+               if (!(currprefs.chipset_mask & CSMASK_AGA)) {
+                       for(i = 0 ; i < 32 ; i++)  {
+                               vv = current_colors.color_regs_ecs[i];
+                               current_colors.color_regs_ecs[i] = -1;
+                               record_color_change (0, i, vv);
+                               remembered_color_entry = -1;
+                               current_colors.color_regs_ecs[i] = vv;
+                               current_colors.acolors[i] = xcolors[vv];
+                       }
+#ifdef AGA
+               } else {
+                       for(i = 0 ; i < 256 ; i++)  {
+                               vv = current_colors.color_regs_aga[i];
+                               current_colors.color_regs_aga[i] = -1;
+                               record_color_change (0, i, vv);
+                               remembered_color_entry = -1;
+                               current_colors.color_regs_aga[i] = vv;
+                               current_colors.acolors[i] = CONVERT_RGB (vv);
+                       }
+#endif
+               }
+               CLXCON (clxcon);
+               CLXCON2 (clxcon2);
+               calcdiw ();
+               write_log (L"CPU=%d Chipset=%s %s\n",
+                       currprefs.cpu_model,
+                       (currprefs.chipset_mask & CSMASK_AGA) ? L"AGA" :
+                       (currprefs.chipset_mask & CSMASK_ECS_AGNUS | CSMASK_ECS_DENISE) == (CSMASK_ECS_AGNUS | CSMASK_ECS_DENISE) ? L"Full ECS" :
+                       (currprefs.chipset_mask & CSMASK_ECS_DENISE) ? L"ECS Denise" :
+                       (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? L"ECS" :
+                       L"OCS", currprefs.ntscmode ? L"NTSC" : L"PAL");
+               write_log (L"State restored\n");
+               for (i = 0; i < 8; i++)
+                       nr_armed += spr[i].armed != 0;
+               if (! currprefs.produce_sound) {
+                       eventtab[ev_audio].active = 0;
+                       events_schedule ();
+               }
+       }
+       sprres = expand_sprres (bplcon0, bplcon3);
+       sprite_width = GET_SPRITEWIDTH (fmode);
+       setup_fmodes (0);
+
+#ifdef ACTION_REPLAY
+       /* Doing this here ensures we can use the 'reset' command from within AR */
+       action_replay_reset ();
+#endif
+#if defined(ENFORCER)
+       enforcer_disable ();
+#endif
+
+       if (hardreset)
+               rtc_hardreset();
+
+       picasso_reset ();
+}
+
+void dumpcustom (void)
+{
+       console_out_f (L"DMACON: %04x INTENA: %04x (%04x) INTREQ: %04x (%04x) VPOS: %x HPOS: %x\n", DMACONR (current_hpos ()),
+               intena, intena_internal, intreq, intreq_internal, vpos, current_hpos ());
+       console_out_f (L"COP1LC: %08lx, COP2LC: %08lx COPPTR: %08lx\n", (unsigned long)cop1lc, (unsigned long)cop2lc, cop_state.ip);
+       console_out_f (L"DIWSTRT: %04x DIWSTOP: %04x DDFSTRT: %04x DDFSTOP: %04x\n",
+               (unsigned int)diwstrt, (unsigned int)diwstop, (unsigned int)ddfstrt, (unsigned int)ddfstop);
+       console_out_f (L"BPLCON 0: %04x 1: %04x 2: %04x 3: %04x 4: %04x LOF=%d/%d HDIW=%d VDIW=%d\n",
+               bplcon0, bplcon1, bplcon2, bplcon3, bplcon4,
+               lof_current, lof_store,
+               hdiwstate == DIW_waiting_start ? 0 : 1, diwstate == DIW_waiting_start ? 0 : 1);
+       if (timeframes) {
+               console_out_f (L"Average frame time: %.2f ms [frames: %d time: %d]\n",
+                       (double)frametime / timeframes, timeframes, frametime);
+               if (total_skipped)
+                       console_out_f (L"Skipped frames: %d\n", total_skipped);
+       }
+}
+
+static void gen_custom_tables (void)
+{
+       int i;
+       for (i = 0; i < 256; i++) {
+               sprtaba[i] = ((((i >> 7) & 1) << 0)
+                       | (((i >> 6) & 1) << 2)
+                       | (((i >> 5) & 1) << 4)
+                       | (((i >> 4) & 1) << 6)
+                       | (((i >> 3) & 1) << 8)
+                       | (((i >> 2) & 1) << 10)
+                       | (((i >> 1) & 1) << 12)
+                       | (((i >> 0) & 1) << 14));
+               sprtabb[i] = sprtaba[i] * 2;
+               sprite_ab_merge[i] = (((i & 15) ? 1 : 0)
+                       | ((i & 240) ? 2 : 0));
+       }
+       for (i = 0; i < 16; i++) {
+               clxmask[i] = (((i & 1) ? 0xF : 0x3)
+                       | ((i & 2) ? 0xF0 : 0x30)
+                       | ((i & 4) ? 0xF00 : 0x300)
+                       | ((i & 8) ? 0xF000 : 0x3000));
+               sprclx[i] = (((i & 0x3) == 0x3 ? 1 : 0)
+                       | ((i & 0x5) == 0x5 ? 2 : 0)
+                       | ((i & 0x9) == 0x9 ? 4 : 0)
+                       | ((i & 0x6) == 0x6 ? 8 : 0)
+                       | ((i & 0xA) == 0xA ? 16 : 0)
+                       | ((i & 0xC) == 0xC ? 32 : 0)) << 9;
+       }
+}
+
+/* mousehack is now in "filesys boot rom" */
+static uae_u32 REGPARAM2 mousehack_helper_old (struct TrapContext *ctx)
+{
+       return 0;
+}
+
+int custom_init (void)
+{
+
+#ifdef AUTOCONFIG
+       if (uae_boot_rom) {
+               uaecptr pos;
+               pos = here ();
+
+               org (rtarea_base + 0xFF70);
+               calltrap (deftrap (mousehack_helper_old));
+               dw (RTS);
+
+               org (rtarea_base + 0xFFA0);
+               calltrap (deftrap (timehack_helper));
+               dw (RTS);
+
+               org (pos);
+       }
+#endif
+
+       gen_custom_tables ();
+       build_blitfilltable ();
+
+       drawing_init ();
+
+       create_cycle_diagram_table ();
+
+       return 1;
+}
+
+/* Custom chip memory bank */
+
+static uae_u32 REGPARAM3 custom_lget (uaecptr) REGPARAM;
+static uae_u32 REGPARAM3 custom_wget (uaecptr) REGPARAM;
+static uae_u32 REGPARAM3 custom_bget (uaecptr) REGPARAM;
+static uae_u32 REGPARAM3 custom_lgeti (uaecptr) REGPARAM;
+static uae_u32 REGPARAM3 custom_wgeti (uaecptr) REGPARAM;
+static void REGPARAM3 custom_lput (uaecptr, uae_u32) REGPARAM;
+static void REGPARAM3 custom_wput (uaecptr, uae_u32) REGPARAM;
+static void REGPARAM3 custom_bput (uaecptr, uae_u32) REGPARAM;
+
+addrbank custom_bank = {
+       custom_lget, custom_wget, custom_bget,
+       custom_lput, custom_wput, custom_bput,
+       default_xlate, default_check, NULL, L"Custom chipset",
+       custom_lgeti, custom_wgeti, ABFLAG_IO
+};
+
+static uae_u32 REGPARAM2 custom_wgeti (uaecptr addr)
+{
+       if (currprefs.cpu_model >= 68020)
+               return dummy_wgeti (addr);
+       return custom_wget (addr);
+}
+static uae_u32 REGPARAM2 custom_lgeti (uaecptr addr)
+{
+       if (currprefs.cpu_model >= 68020)
+               return dummy_lgeti (addr);
+       return custom_lget (addr);
+}
+
+STATIC_INLINE uae_u32 REGPARAM2 custom_wget_1 (int hpos, uaecptr addr, int noput)
+{
+       uae_u16 v;
+#ifdef JIT
+       special_mem |= S_READ;
+#endif
+       addr &= 0xfff;
+#if CUSTOM_DEBUG > 2
+       write_log (L"%d:%d:wget: %04X=%04X pc=%p\n", current_hpos(), vpos, addr, addr & 0x1fe, m68k_getpc ());
+#endif
+       switch (addr & 0x1fe) {
+       case 0x002: v = DMACONR (hpos); break;
+       case 0x004: v = VPOSR (); break;
+       case 0x006: v = VHPOSR (); break;
+
+       case 0x00A: v = JOY0DAT (); break;
+       case 0x00C: v = JOY1DAT (); break;
+       case 0x00E: v = CLXDAT (); break;
+       case 0x010: v = ADKCONR (); break;
+
+       case 0x012: v = POT0DAT (); break;
+       case 0x014: v = POT1DAT (); break;
+       case 0x016: v = POTGOR (); break;
+       case 0x018: v = SERDATR (); break;
+       case 0x01A: v = DSKBYTR (hpos); break;
+       case 0x01C: v = INTENAR (); break;
+       case 0x01E: v = INTREQR (); break;
+       case 0x07C: v = DENISEID (); break;
+
+#ifdef AGA
+       case 0x180: case 0x182: case 0x184: case 0x186: case 0x188: case 0x18A:
+       case 0x18C: case 0x18E: case 0x190: case 0x192: case 0x194: case 0x196:
+       case 0x198: case 0x19A: case 0x19C: case 0x19E: case 0x1A0: case 0x1A2:
+       case 0x1A4: case 0x1A6: case 0x1A8: case 0x1AA: case 0x1AC: case 0x1AE:
+       case 0x1B0: case 0x1B2: case 0x1B4: case 0x1B6: case 0x1B8: case 0x1BA:
+       case 0x1BC: case 0x1BE:
+               v = COLOR_READ ((addr & 0x3E) / 2);
+               break;
+#endif
+
+       default:
+               /* OCS/ECS:
+               * reading write-only register causes write with last value in chip
+               * bus (custom registers, chipram, slowram)
+               * and finally returns either all ones or something weird if DMA happens
+               * in next (or previous) cycle.. FIXME.
+               *
+               * OCS-only special case: DFF000 (BLTDDAT) will always return whatever was left in bus
+               *
+               * AGA:
+               * only writes to custom registers change last value, read returns
+               * last value which then changes to all ones (following read will return
+               * all ones)
+               */
+               v = last_custom_value1;
+               if (!noput) {
+                       int r;
+                       uae_u16 old = last_custom_value1;
+                       uae_u16 l = currprefs.cpu_compatible && currprefs.cpu_model == 68000 ? regs.irc : 0xffff;
+                       decide_line (hpos);
+                       decide_fetch (hpos);
+                       decide_blitter (hpos);
+                       debug_wputpeek (0xdff000 + addr, l);
+                       r = custom_wput_1 (hpos, addr, l, 1);
+                       if (r) { // register don't exist
+                               if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) {
+                                       v = l;
+                               } else {
+                                       if ((addr & 0x1fe) == 0) {
+                                               if (is_cycle_ce ())
+                                                       v = last_custom_value1;
+                                               else
+                                                       v = l;
+                                       }
+                               }
+                       } else {
+                               v = 0xffff;
+                       }
+#if CUSTOM_DEBUG > 0
+                       write_log (L"%08X read = %04X. Value written=%04X PC=%08x\n", 0xdff000 | addr, v, l, M68K_GETPC);
+#endif
+                       return v;
+               }
+       }
+       last_custom_value1 = v;
+       return v;
+}
+
+STATIC_INLINE uae_u32 custom_wget2 (uaecptr addr)
+{
+       uae_u32 v;
+       int hpos = current_hpos ();
+
+       sync_copper_with_cpu (hpos, 1);
+       v = custom_wget_1 (hpos, addr, 0);
+#ifdef ACTION_REPLAY
+#ifdef ACTION_REPLAY_COMMON
+       addr &= 0x1ff;
+       ar_custom[addr + 0] = (uae_u8)(v >> 8);
+       ar_custom[addr + 1] = (uae_u8)(v);
+#endif
+#endif
+       return v;
+}
+
+static uae_u32 REGPARAM2 custom_wget (uaecptr addr)
+{
+       uae_u32 v;
+
+       if (addr & 1) {
+               /* think about move.w $dff005,d0.. (68020+ only) */
+               addr &= ~1;
+               v = custom_wget2 (addr) << 8;
+               v |= custom_wget2 (addr + 2) >> 8;
+               return v;
+       }
+       return custom_wget2 (addr);
+}
+
+static uae_u32 REGPARAM2 custom_bget (uaecptr addr)
+{
+       uae_u32 v;
+#ifdef JIT
+       special_mem |= S_READ;
+#endif
+       v = custom_wget2 (addr & ~1);
+       v >>= (addr & 1 ? 0 : 8);
+       return v;
+}
+
+static uae_u32 REGPARAM2 custom_lget (uaecptr addr)
+{
+#ifdef JIT
+       special_mem |= S_READ;
+#endif
+       return ((uae_u32)custom_wget (addr) << 16) | custom_wget (addr + 2);
+}
+static int REGPARAM2 custom_wput_1 (int hpos, uaecptr addr, uae_u32 value, int noget)
+{
+       if (!noget)
+               last_custom_value1 = value;
+       addr &= 0x1FE;
+       value &= 0xffff;
+#ifdef ACTION_REPLAY
+#ifdef ACTION_REPLAY_COMMON
+       ar_custom[addr+0]=(uae_u8)(value>>8);
+       ar_custom[addr+1]=(uae_u8)(value);
+#endif
+#endif
+       switch (addr) {
+       case 0x00E: CLXDAT (); break;
+
+       case 0x020: DSKPTH (value); break;
+       case 0x022: DSKPTL (value); break;
+       case 0x024: DSKLEN (value, hpos); break;
+       case 0x026: DSKDAT (value); break;
+
+       case 0x02A: VPOSW (value); break;
+       case 0x02C: VHPOSW (value); break;
+       case 0x02E: COPCON (value); break;
+       case 0x030: SERDAT (value); break;
+       case 0x032: SERPER (value); break;
+       case 0x034: POTGO (value); break;
+
+       case 0x040: BLTCON0 (hpos, value); break;
+       case 0x042: BLTCON1 (hpos, value); break;
+
+       case 0x044: BLTAFWM (hpos, value); break;
+       case 0x046: BLTALWM (hpos, value); break;
+
+       case 0x050: BLTAPTH (hpos, value); break;
+       case 0x052: BLTAPTL (hpos, value); break;
+       case 0x04C: BLTBPTH (hpos, value); break;
+       case 0x04E: BLTBPTL (hpos, value); break;
+       case 0x048: BLTCPTH (hpos, value); break;
+       case 0x04A: BLTCPTL (hpos, value); break;
+       case 0x054: BLTDPTH (hpos, value); break;
+       case 0x056: BLTDPTL (hpos, value); break;
+
+       case 0x058: BLTSIZE (hpos, value); break;
+
+       case 0x064: BLTAMOD (hpos, value); break;
+       case 0x062: BLTBMOD (hpos, value); break;
+       case 0x060: BLTCMOD (hpos, value); break;
+       case 0x066: BLTDMOD (hpos, value); break;
+
+       case 0x070: BLTCDAT (hpos, value); break;
+       case 0x072: BLTBDAT (hpos, value); break;
+       case 0x074: BLTADAT (hpos, value); break;
+
+       case 0x07E: DSKSYNC (hpos, value); break;
+
+       case 0x080: COP1LCH (value); break;
+       case 0x082: COP1LCL (value); break;
+       case 0x084: COP2LCH (value); break;
+       case 0x086: COP2LCL (value); break;
+
+       case 0x088: COPJMP (1, 0); break;
+       case 0x08A: COPJMP (2, 0); break;
+
+       case 0x08E: DIWSTRT (hpos, value); break;
+       case 0x090: DIWSTOP (hpos, value); break;
+       case 0x092: DDFSTRT (hpos, value); break;
+       case 0x094: DDFSTOP (hpos, value); break;
+
+       case 0x096: DMACON (hpos, value); break;
+       case 0x098: CLXCON (value); break;
+       case 0x09A: INTENA (value); break;
+       case 0x09C: INTREQ (value); break;
+       case 0x09E: ADKCON (hpos, value); break;
+
+       case 0x0A0: AUDxLCH (0, value); break;
+       case 0x0A2: AUDxLCL (0, value); break;
+       case 0x0A4: AUDxLEN (0, value); break;
+       case 0x0A6: AUDxPER (0, value); break;
+       case 0x0A8: AUDxVOL (0, value); break;
+       case 0x0AA: AUDxDAT (0, value); break;
+
+       case 0x0B0: AUDxLCH (1, value); break;
+       case 0x0B2: AUDxLCL (1, value); break;
+       case 0x0B4: AUDxLEN (1, value); break;
+       case 0x0B6: AUDxPER (1, value); break;
+       case 0x0B8: AUDxVOL (1, value); break;
+       case 0x0BA: AUDxDAT (1, value); break;
+
+       case 0x0C0: AUDxLCH (2, value); break;
+       case 0x0C2: AUDxLCL (2, value); break;
+       case 0x0C4: AUDxLEN (2, value); break;
+       case 0x0C6: AUDxPER (2, value); break;
+       case 0x0C8: AUDxVOL (2, value); break;
+       case 0x0CA: AUDxDAT (2, value); break;
+
+       case 0x0D0: AUDxLCH (3, value); break;
+       case 0x0D2: AUDxLCL (3, value); break;
+       case 0x0D4: AUDxLEN (3, value); break;
+       case 0x0D6: AUDxPER (3, value); break;
+       case 0x0D8: AUDxVOL (3, value); break;
+       case 0x0DA: AUDxDAT (3, value); break;
+
+       case 0x0E0: BPLxPTH (hpos, value, 0); break;
+       case 0x0E2: BPLxPTL (hpos, value, 0); break;
+       case 0x0E4: BPLxPTH (hpos, value, 1); break;
+       case 0x0E6: BPLxPTL (hpos, value, 1); break;
+       case 0x0E8: BPLxPTH (hpos, value, 2); break;
+       case 0x0EA: BPLxPTL (hpos, value, 2); break;
+       case 0x0EC: BPLxPTH (hpos, value, 3); break;
+       case 0x0EE: BPLxPTL (hpos, value, 3); break;
+       case 0x0F0: BPLxPTH (hpos, value, 4); break;
+       case 0x0F2: BPLxPTL (hpos, value, 4); break;
+       case 0x0F4: BPLxPTH (hpos, value, 5); break;
+       case 0x0F6: BPLxPTL (hpos, value, 5); break;
+       case 0x0F8: BPLxPTH (hpos, value, 6); break;
+       case 0x0FA: BPLxPTL (hpos, value, 6); break;
+       case 0x0FC: BPLxPTH (hpos, value, 7); break;
+       case 0x0FE: BPLxPTL (hpos, value, 7); break;
+
+       case 0x100: BPLCON0 (hpos, value); break;
+       case 0x102: BPLCON1 (hpos, value); break;
+       case 0x104: BPLCON2 (hpos, value); break;
+#ifdef ECS_DENISE
+       case 0x106: BPLCON3 (hpos, value); break;
+#endif
+
+       case 0x108: BPL1MOD (hpos, value); break;
+       case 0x10A: BPL2MOD (hpos, value); break;
+#ifdef AGA
+       case 0x10E: CLXCON2 (value); break;
+#endif
+
+       case 0x110: BPLxDAT (hpos, 0, value); break;
+       case 0x112: BPLxDAT (hpos, 1, value); break;
+       case 0x114: BPLxDAT (hpos, 2, value); break;
+       case 0x116: BPLxDAT (hpos, 3, value); break;
+       case 0x118: BPLxDAT (hpos, 4, value); break;
+       case 0x11A: BPLxDAT (hpos, 5, value); break;
+       case 0x11C: BPLxDAT (hpos, 6, value); break;
+       case 0x11E: BPLxDAT (hpos, 7, value); break;
+
+       case 0x180: case 0x182: case 0x184: case 0x186: case 0x188: case 0x18A:
+       case 0x18C: case 0x18E: case 0x190: case 0x192: case 0x194: case 0x196:
+       case 0x198: case 0x19A: case 0x19C: case 0x19E: case 0x1A0: case 0x1A2:
+       case 0x1A4: case 0x1A6: case 0x1A8: case 0x1AA: case 0x1AC: case 0x1AE:
+       case 0x1B0: case 0x1B2: case 0x1B4: case 0x1B6: case 0x1B8: case 0x1BA:
+       case 0x1BC: case 0x1BE:
+               COLOR_WRITE (hpos, value & 0xFFF, (addr & 0x3E) / 2);
+               break;
+       case 0x120: case 0x124: case 0x128: case 0x12C:
+       case 0x130: case 0x134: case 0x138: case 0x13C:
+               SPRxPTH (hpos, value, (addr - 0x120) / 4);
+               break;
+       case 0x122: case 0x126: case 0x12A: case 0x12E:
+       case 0x132: case 0x136: case 0x13A: case 0x13E:
+               SPRxPTL (hpos, value, (addr - 0x122) / 4);
+               break;
+       case 0x140: case 0x148: case 0x150: case 0x158:
+       case 0x160: case 0x168: case 0x170: case 0x178:
+               SPRxPOS (hpos, value, (addr - 0x140) / 8);
+               break;
+       case 0x142: case 0x14A: case 0x152: case 0x15A:
+       case 0x162: case 0x16A: case 0x172: case 0x17A:
+               SPRxCTL (hpos, value, (addr - 0x142) / 8);
+               break;
+       case 0x144: case 0x14C: case 0x154: case 0x15C:
+       case 0x164: case 0x16C: case 0x174: case 0x17C:
+               SPRxDATA (hpos, value, (addr - 0x144) / 8);
+               break;
+       case 0x146: case 0x14E: case 0x156: case 0x15E:
+       case 0x166: case 0x16E: case 0x176: case 0x17E:
+               SPRxDATB (hpos, value, (addr - 0x146) / 8);
+               break;
+
+       case 0x36: JOYTEST (value); break;
+       case 0x5A: BLTCON0L (hpos, value); break;
+       case 0x5C: BLTSIZV (hpos, value); break;
+       case 0x5E: BLTSIZH (hpos, value); break;
+       case 0x1E4: DIWHIGH (hpos, value); break;
+#ifdef AGA
+       case 0x10C: BPLCON4 (hpos, value); break;
+#endif
+
+#ifndef CUSTOM_SIMPLE
+       case 0x1DC: BEAMCON0 (value); break;
+#ifdef ECS_DENISE
+       case 0x1C0: if (htotal != value) { htotal = value; varsync (); } break;
+       case 0x1C2: if (hsstop != value) { hsstop = value; varsync (); } break;
+       case 0x1C4: if (hbstrt != value) { hbstrt = value; varsync (); } break;
+       case 0x1C6: if (hbstop != value) { hbstop = value; varsync (); } break;
+       case 0x1C8: if (vtotal != value) { vtotal = value; varsync (); } break;
+       case 0x1CA: if (vsstop != value) { vsstop = value; varsync (); } break;
+       case 0x1CC: if (vbstrt < value || vbstrt > value + 1) { vbstrt = value; varsync (); } break;
+       case 0x1CE: if (vbstop < value || vbstop > value + 1) { vbstop = value; varsync (); } break;
+       case 0x1DE: if (hsstrt != value) { hsstrt = value; varsync (); } break;
+       case 0x1E0: if (vsstrt != value) { vsstrt = value; varsync (); } break;
+       case 0x1E2: if (hcenter != value) { hcenter = value; varsync (); } break;
+#endif
+#endif
+
+#ifdef AGA
+       case 0x1FC: FMODE (hpos, value); break;
+#endif
+       case 0x1FE: FNULL (value); break;
+
+               /* writing to read-only register causes read access */
+       default:
+               if (!noget) {
+#if CUSTOM_DEBUG > 0
+                       write_log (L"%04X written %08x\n", addr, M68K_GETPC);
+#endif
+                       custom_wget_1 (hpos, addr, 1);
+               }
+               return 1;
+       }
+       return 0;
+}
+
+static void REGPARAM2 custom_wput (uaecptr addr, uae_u32 value)
+{
+       int hpos = current_hpos ();
+#ifdef JIT
+       special_mem |= S_WRITE;
+#endif
+#if CUSTOM_DEBUG > 2
+       write_log (L"%d:%d:wput: %04X %04X pc=%p\n", hpos, vpos, addr & 0x01fe, value & 0xffff, m68k_getpc ());
+#endif
+       sync_copper_with_cpu (hpos, 1);
+       if (addr & 1) {
+               addr &= ~1;
+               custom_wput_1 (hpos, addr, (value >> 8) | (value & 0xff00), 0);
+               custom_wput_1 (hpos, addr + 2, (value << 8) | (value & 0x00ff), 0);
+               return;
+       }
+       custom_wput_1 (hpos, addr, value, 0);
+}
+
+static void REGPARAM2 custom_bput (uaecptr addr, uae_u32 value)
+{
+       static int warned;
+       uae_u16 rval;
+
+       if (currprefs.chipset_mask & CSMASK_AGA) {
+               if (addr & 1) {
+                       rval = value & 0xff;
+               } else {
+                       rval = (value << 8) | (value & 0xFF);
+               }
+       } else {
+               rval = (value << 8) | (value & 0xff);
+       }
+
+#ifdef JIT
+       special_mem |= S_WRITE;
+#endif
+       if (currprefs.cpu_model == 68060) {
+               if (addr & 1)
+                       custom_wput (addr & ~1, rval);
+               else
+                       custom_wput (addr, value << 8);
+       } else {
+               custom_wput (addr & ~1, rval);
+       }
+}
+
+static void REGPARAM2 custom_lput(uaecptr addr, uae_u32 value)
+{
+#ifdef JIT
+       special_mem |= S_WRITE;
+#endif
+       custom_wput (addr & 0xfffe, value >> 16);
+       custom_wput ((addr + 2) & 0xfffe, (uae_u16)value);
+}
+
+#ifdef SAVESTATE
+
+void custom_prepare_savestate (void)
+{
+       int i;
+
+       for (i = 0; i < ev2_max; i++) {
+               if (eventtab2[i].active) {
+                       eventtab2[i].active = 0;
+                       eventtab2[i].handler (eventtab2[i].data);
+               }
+       }
+}
+
+#define RB restore_u8 ()
+#define SRB (uae_s8)restore_u8 ()
+#define RBB restore_u8 () != 0
+#define RW restore_u16 ()
+#define RL restore_u32 ()
+
+uae_u8 *restore_custom (uae_u8 *src)
+{
+       uae_u16 dsklen, dskbytr;
+       int dskpt;
+       int i;
+
+       audio_reset ();
+
+       changed_prefs.chipset_mask = currprefs.chipset_mask = RL & CSMASK_MASK;
+       update_mirrors ();
+       RW;                                             /* 000 BLTDDAT */
+       RW;                                             /* 002 DMACONR */
+       RW;                                             /* 004 VPOSR */
+       RW;                                             /* 006 VHPOSR */
+       RW;                                             /* 008 DSKDATR (dummy register) */
+       JOYSET(0, RW);                  /* 00A JOY0DAT */
+       JOYSET(1, RW);                  /* 00C JOY1DAT */
+       clxdat = RW;                    /* 00E CLXDAT */
+       RW;                                             /* 010 ADKCONR */
+       RW;                                             /* 012 POT0DAT* */
+       RW;                                             /* 014 POT1DAT* */
+       RW;                                             /* 016 POTINP* */
+       RW;                                             /* 018 SERDATR* */
+       dskbytr = RW;                   /* 01A DSKBYTR */
+       RW;                                             /* 01C INTENAR */
+       RW;                                             /* 01E INTREQR */
+       dskpt = RL;                             /* 020-022 DSKPT */
+       dsklen = RW;                    /* 024 DSKLEN */
+       RW;                                             /* 026 DSKDAT */
+       RW;                                             /* 028 REFPTR */
+       i = RW; lof_store = lof_current = (i & 0x8000) ? 1 : 0; lol = (i & 0x0080) ? 1 : 0; /* 02A VPOSW */
+       RW;                                             /* 02C VHPOSW */
+       COPCON (RW);                    /* 02E COPCON */
+       RW;                                             /* 030 SERDAT* */
+       RW;                                             /* 032 SERPER* */
+       POTGO (RW);                             /* 034 POTGO */
+       RW;                                             /* 036 JOYTEST* */
+       RW;                                             /* 038 STREQU */
+       RW;                                             /* 03A STRVHBL */
+       RW;                                             /* 03C STRHOR */
+       RW;                                             /* 03E STRLONG */
+       BLTCON0 (0, RW);                /* 040 BLTCON0 */
+       BLTCON1 (0, RW);                /* 042 BLTCON1 */
+       BLTAFWM (0, RW);                /* 044 BLTAFWM */
+       BLTALWM (0, RW);                /* 046 BLTALWM */
+       BLTCPTH (0, RW);BLTCPTL(0, RW); /* 048-04B BLTCPT */
+       BLTBPTH (0, RW);BLTBPTL(0, RW); /* 04C-04F BLTBPT */
+       BLTAPTH (0, RW);BLTAPTL(0, RW); /* 050-053 BLTAPT */
+       BLTDPTH (0, RW);BLTDPTL(0, RW); /* 054-057 BLTDPT */
+       RW;                                             /* 058 BLTSIZE */
+       RW;                                             /* 05A BLTCON0L */
+       blt_info.vblitsize = RW;/* 05C BLTSIZV */
+       blt_info.hblitsize = RW;/* 05E BLTSIZH */
+       BLTCMOD (0, RW);                /* 060 BLTCMOD */
+       BLTBMOD (0, RW);                /* 062 BLTBMOD */
+       BLTAMOD (0, RW);                /* 064 BLTAMOD */
+       BLTDMOD (0, RW);                /* 066 BLTDMOD */
+       RW;                                             /* 068 ? */
+       RW;                                             /* 06A ? */
+       RW;                                             /* 06C ? */
+       RW;                                             /* 06E ? */
+       BLTCDAT (0, RW);                /* 070 BLTCDAT */
+       BLTBDAT (0, RW);                /* 072 BLTBDAT */
+       BLTADAT (0, RW);                /* 074 BLTADAT */
+       RW;                                             /* 076 ? */
+       RW;                                             /* 078 ? */
+       RW;                                             /* 07A ? */
+       RW;                                             /* 07C LISAID */
+       DSKSYNC (-1, RW);               /* 07E DSKSYNC */
+       cop1lc = RL;                    /* 080/082 COP1LC */
+       cop2lc = RL;                    /* 084/086 COP2LC */
+       RW;                                             /* 088 ? */
+       RW;                                             /* 08A ? */
+       RW;                                             /* 08C ? */
+       diwstrt = RW;                   /* 08E DIWSTRT */
+       diwstop = RW;                   /* 090 DIWSTOP */
+       ddfstrt = RW;                   /* 092 DDFSTRT */
+       ddfstop = RW;                   /* 094 DDFSTOP */
+       dmacon = RW & ~(0x2000|0x4000); /* 096 DMACON */
+       CLXCON (RW);                    /* 098 CLXCON */
+       intena = intena_internal = RW;  /* 09A INTENA */
+       intreq = RW;                    /* 09C INTREQ */
+       intreq_internal = intreq;
+       adkcon = RW;                    /* 09E ADKCON */
+       for (i = 0; i < 8; i++)
+               bplptx[i] = bplpt[i] = RL;
+       bplcon0 = RW;                   /* 100 BPLCON0 */
+       bplcon1 = RW;                   /* 102 BPLCON1 */
+       bplcon2 = RW;                   /* 104 BPLCON2 */
+       bplcon3 = RW;                   /* 106 BPLCON3 */
+       bpl1mod = RW;                   /* 108 BPL1MOD */
+       bpl2mod = RW;                   /* 10A BPL2MOD */
+       bplcon4 = RW;                   /* 10C BPLCON4 */
+       clxcon2 = RW;                   /* 10E CLXCON2* */
+       for(i = 0; i < 8; i++)
+               bplxdat[i] = RW;        /*     BPLXDAT */
+       for(i = 0; i < 32; i++)
+               current_colors.color_regs_ecs[i] = RW; /* 180 COLORxx */
+       htotal = RW;                    /* 1C0 HTOTAL */
+       hsstop = RW;                    /* 1C2 HSTOP ? */
+       hbstrt = RW;                    /* 1C4 HBSTRT ? */
+       hbstop = RW;                    /* 1C6 HBSTOP ? */
+       vtotal = RW;                    /* 1C8 VTOTAL */
+       vsstop = RW;                    /* 1CA VSSTOP */
+       vbstrt = RW;                    /* 1CC VBSTRT */
+       vbstop = RW;                    /* 1CE VBSTOP */
+       RW;                                             /* 1D0 ? */
+       RW;                                             /* 1D2 ? */
+       RW;                                             /* 1D4 ? */
+       RW;                                             /* 1D6 ? */
+       RW;                                             /* 1D8 ? */
+       RW;                                             /* 1DA ? */
+       new_beamcon0 = RW;              /* 1DC BEAMCON0 */
+       hsstrt = RW;                    /* 1DE HSSTRT */
+       vsstrt = RW;                    /* 1E0 VSSTT  */
+       hcenter = RW;                   /* 1E2 HCENTER */
+       diwhigh = RW;                   /* 1E4 DIWHIGH */
+       diwhigh_written = (diwhigh & 0x8000) ? 1 : 0;
+       hdiwstate = (diwhigh & 0x4000) ? DIW_waiting_stop : DIW_waiting_start;
+       diwhigh &= 0x3fff;
+       RW;                                             /* 1E6 ? */
+       RW;                                             /* 1E8 ? */
+       RW;                                             /* 1EA ? */
+       RW;                                             /* 1EC ? */
+       RW;                                             /* 1EE ? */
+       RW;                                             /* 1F0 ? */
+       RW;                                             /* 1F2 ? */
+       RW;                                             /* 1F4 ? */
+       RW;                                             /* 1F6 ? */
+       RW;                                             /* 1F8 ? */
+       i = RW;                                 /* 1FA ? */
+       if (i & 0x8000)
+               currprefs.ntscmode = changed_prefs.ntscmode = i & 1;
+       fmode = RW;                             /* 1FC FMODE */
+       last_custom_value1 = RW;/* 1FE ? */
+
+       DISK_restore_custom (dskpt, dsklen, dskbytr);
+
+       return src;
+}
+
+#endif /* SAVESTATE */
+
+#if defined SAVESTATE || defined DEBUGGER
+
+#define SB save_u8
+#define SW save_u16
+#define SL save_u32
+
+extern uae_u16 serper;
+
+uae_u8 *save_custom (int *len, uae_u8 *dstptr, int full)
+{
+       uae_u8 *dstbak, *dst;
+       int i;
+       uae_u32 dskpt;
+       uae_u16 dsklen, dsksync, dskbytr;
+
+       DISK_save_custom (&dskpt, &dsklen, &dsksync, &dskbytr);
+
+       if (dstptr)
+               dstbak = dst = dstptr;
+       else
+               dstbak = dst = xmalloc (uae_u8, 8 + 256 * 2);
+
+       SL (currprefs.chipset_mask);
+       SW (0);                                 /* 000 BLTDDAT */
+       SW (dmacon);                    /* 002 DMACONR */
+       SW (VPOSR ());                  /* 004 VPOSR */
+       SW (VHPOSR ());                 /* 006 VHPOSR */
+       SW (0);                                 /* 008 DSKDATR */
+       SW (JOYGET (0));                /* 00A JOY0DAT */
+       SW (JOYGET (1));                /* 00C JOY1DAT */
+       SW (clxdat | 0x8000);   /* 00E CLXDAT */
+       SW (ADKCONR ());                /* 010 ADKCONR */
+       SW (POT0DAT ());                /* 012 POT0DAT */
+       SW (POT1DAT ());                /* 014 POT1DAT */
+       SW (0)  ;                               /* 016 POTINP * */
+       SW (0);                                 /* 018 SERDATR * */
+       SW (dskbytr);                   /* 01A DSKBYTR */
+       SW (INTENAR ());                /* 01C INTENAR */
+       SW (INTREQR ());                /* 01E INTREQR */
+       SL (dskpt);                             /* 020-023 DSKPT */
+       SW (dsklen);                    /* 024 DSKLEN */
+       SW (0);                                 /* 026 DSKDAT */
+       SW (0);                                 /* 028 REFPTR */
+       SW ((lof_store ? 0x8001 : 0) | (lol ? 0x0080 : 0));/* 02A VPOSW */
+       SW (0);                                 /* 02C VHPOSW */
+       SW (copcon);                    /* 02E COPCON */
+       SW (serper);                    /* 030 SERDAT * */
+       SW (serdat);                    /* 032 SERPER * */
+       SW (potgo_value);               /* 034 POTGO */
+       SW (0);                                 /* 036 JOYTEST * */
+       SW (0);                                 /* 038 STREQU */
+       SW (0);                                 /* 03A STRVBL */
+       SW (0);                                 /* 03C STRHOR */
+       SW (0);                                 /* 03E STRLONG */
+       SW (bltcon0);                   /* 040 BLTCON0 */
+       SW (bltcon1);                   /* 042 BLTCON1 */
+       SW (blt_info.bltafwm);  /* 044 BLTAFWM */
+       SW (blt_info.bltalwm);  /* 046 BLTALWM */
+       SL (bltcpt);                    /* 048-04B BLTCPT */
+       SL (bltbpt);                    /* 04C-04F BLTCPT */
+       SL (bltapt);                    /* 050-053 BLTCPT */
+       SL (bltdpt);                    /* 054-057 BLTCPT */
+       SW (0);                                 /* 058 BLTSIZE */
+       SW (0);                                 /* 05A BLTCON0L (use BLTCON0 instead) */
+       SW (blt_info.vblitsize);/* 05C BLTSIZV */
+       SW (blt_info.hblitsize);/* 05E BLTSIZH */
+       SW (blt_info.bltcmod);  /* 060 BLTCMOD */
+       SW (blt_info.bltbmod);  /* 062 BLTBMOD */
+       SW (blt_info.bltamod);  /* 064 BLTAMOD */
+       SW (blt_info.bltdmod);  /* 066 BLTDMOD */
+       SW (0);                                 /* 068 ? */
+       SW (0);                                 /* 06A ? */
+       SW (0);                                 /* 06C ? */
+       SW (0);                                 /* 06E ? */
+       SW (blt_info.bltcdat);  /* 070 BLTCDAT */
+       SW (blt_info.bltbdat);  /* 072 BLTBDAT */
+       SW (blt_info.bltadat);  /* 074 BLTADAT */
+       SW (0);                                 /* 076 ? */
+       SW (0);                                 /* 078 ? */
+       SW (0);                                 /* 07A ? */
+       SW (DENISEID ());               /* 07C DENISEID/LISAID */
+       SW (dsksync);                   /* 07E DSKSYNC */
+       SL (cop1lc);                    /* 080-083 COP1LC */
+       SL (cop2lc);                    /* 084-087 COP2LC */
+       SW (0);                                 /* 088 ? */
+       SW (0);                                 /* 08A ? */
+       SW (0);                                 /* 08C ? */
+       SW (diwstrt);                   /* 08E DIWSTRT */
+       SW (diwstop);                   /* 090 DIWSTOP */
+       SW (ddfstrt);                   /* 092 DDFSTRT */
+       SW (ddfstop);                   /* 094 DDFSTOP */
+       SW (dmacon);                    /* 096 DMACON */
+       SW (clxcon);                    /* 098 CLXCON */
+       SW (intena);                    /* 09A INTENA */
+       SW (intreq);                    /* 09C INTREQ */
+       SW (adkcon);                    /* 09E ADKCON */
+       for (i = 0; full && i < 32; i++)
+               SW (0);
+       for (i = 0; i < 8; i++)
+               SL (bplpt[i]);          /* 0E0-0FE BPLxPT */
+       SW (bplcon0);                   /* 100 BPLCON0 */
+       SW (bplcon1);                   /* 102 BPLCON1 */
+       SW (bplcon2);                   /* 104 BPLCON2 */
+       SW (bplcon3);                   /* 106 BPLCON3 */
+       SW (bpl1mod);                   /* 108 BPL1MOD */
+       SW (bpl2mod);                   /* 10A BPL2MOD */
+       SW (bplcon4);                   /* 10C BPLCON4 */
+       SW (clxcon2);                   /* 10E CLXCON2 */
+       for (i = 0;i < 8; i++)
+               SW (bplxdat[i]);        /* 110 BPLxDAT */
+       if (full) {
+               for (i = 0; i < 8; i++) {
+                       SL (spr[i].pt); /* 120-13E SPRxPT */
+                       SW (sprpos[i]); /* 1x0 SPRxPOS */
+                       SW (sprctl[i]); /* 1x2 SPRxPOS */
+                       SW (sprdata[i][0]);     /* 1x4 SPRxDATA */
+                       SW (sprdatb[i][0]);     /* 1x6 SPRxDATB */
+               }
+       }
+       for ( i = 0; i < 32; i++) {
+               if (currprefs.chipset_mask & CSMASK_AGA) {
+                       uae_u32 v = current_colors.color_regs_aga[i];
+                       uae_u16 v2;
+                       v &= 0x00f0f0f0;
+                       v2 = (v >> 4) & 15;
+                       v2 |= ((v >> 12) & 15) << 4;
+                       v2 |= ((v >> 20) & 15) << 8;
+                       SW (v2);
+               } else {
+                       SW (current_colors.color_regs_ecs[i]); /* 180-1BE COLORxx */
+               }
+       }
+       SW (htotal);            /* 1C0 HTOTAL */
+       SW (hsstop);            /* 1C2 HSTOP*/
+       SW (hbstrt);            /* 1C4 HBSTRT */
+       SW (hbstop);            /* 1C6 HBSTOP */
+       SW (vtotal);            /* 1C8 VTOTAL */
+       SW (vsstop);            /* 1CA VSSTOP */
+       SW (vbstrt);            /* 1CC VBSTRT */
+       SW (vbstop);            /* 1CE VBSTOP */
+       SW (0);                 /* 1D0 */
+       SW (0);                 /* 1D2 */
+       SW (0);                 /* 1D4 */
+       SW (0);                 /* 1D6 */
+       SW (0);                 /* 1D8 */
+       SW (0);                 /* 1DA */
+       SW (beamcon0);          /* 1DC BEAMCON0 */
+       SW (hsstrt);            /* 1DE HSSTRT */
+       SW (vsstrt);            /* 1E0 VSSTRT */
+       SW (hcenter);           /* 1E2 HCENTER */
+       SW (diwhigh | (diwhigh_written ? 0x8000 : 0) | (hdiwstate == DIW_waiting_stop ? 0x4000 : 0)); /* 1E4 DIWHIGH */
+       SW (0);                 /* 1E6 */
+       SW (0);                 /* 1E8 */
+       SW (0);                 /* 1EA */
+       SW (0);                 /* 1EC */
+       SW (0);                 /* 1EE */
+       SW (0);                 /* 1F0 */
+       SW (0);                 /* 1F2 */
+       SW (0);                 /* 1F4 */
+       SW (0);                 /* 1F6 */
+       SW (0);                 /* 1F8 */
+       SW (0x8000 | (currprefs.ntscmode ? 1 : 0));                     /* 1FA (re-used for NTSC) */
+       SW (fmode);                     /* 1FC FMODE */
+       SW (last_custom_value1);        /* 1FE */
+
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+#endif /* SAVESTATE || DEBUGGER */
+
+#ifdef SAVESTATE
+
+uae_u8 *restore_custom_agacolors (uae_u8 *src)
+{
+       int i;
+
+       for (i = 0; i < 256; i++) {
+#ifdef AGA
+               uae_u32 v = RL;
+               color_regs_aga_genlock[i] = 0;
+               if (v & 0x80000000)
+                       color_regs_aga_genlock[i] = 1;
+               v &= 0x00ffffff;
+               current_colors.color_regs_aga[i] = v;
+#else
+               RL;
+#endif
+       }
+       return src;
+}
+
+uae_u8 *save_custom_agacolors (int *len, uae_u8 *dstptr)
+{
+       uae_u8 *dstbak, *dst;
+       int i;
+
+       if (dstptr)
+               dstbak = dst = dstptr;
+       else
+               dstbak = dst = xmalloc (uae_u8, 256 * 4);
+       for (i = 0; i < 256; i++)
+#ifdef AGA
+               SL (current_colors.color_regs_aga[i] | (color_regs_aga_genlock[i] ? 0x80000000 : 0));
+#else
+               SL (0);
+#endif
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+uae_u8 *restore_custom_sprite (int num, uae_u8 *src)
+{
+       memset (&spr[num], 0, sizeof (struct sprite));
+       spr[num].pt = RL;               /* 120-13E SPRxPT */
+       sprpos[num] = RW;               /* 1x0 SPRxPOS */
+       sprctl[num] = RW;               /* 1x2 SPRxPOS */
+       sprdata[num][0] = RW;   /* 1x4 SPRxDATA */
+       sprdatb[num][0] = RW;   /* 1x6 SPRxDATB */
+       sprdata[num][1] = RW;
+       sprdatb[num][1] = RW;
+       sprdata[num][2] = RW;
+       sprdatb[num][2] = RW;
+       sprdata[num][3] = RW;
+       sprdatb[num][3] = RW;
+       spr[num].armed = RB;
+       return src;
+}
+
+uae_u8 *save_custom_sprite (int num, int *len, uae_u8 *dstptr)
+{
+       uae_u8 *dstbak, *dst;
+
+       if (dstptr)
+               dstbak = dst = dstptr;
+       else
+               dstbak = dst = xmalloc (uae_u8, 30);
+       SL (spr[num].pt);               /* 120-13E SPRxPT */
+       SW (sprpos[num]);               /* 1x0 SPRxPOS */
+       SW (sprctl[num]);               /* 1x2 SPRxPOS */
+       SW (sprdata[num][0]);   /* 1x4 SPRxDATA */
+       SW (sprdatb[num][0]);   /* 1x6 SPRxDATB */
+       SW (sprdata[num][1]);
+       SW (sprdatb[num][1]);
+       SW (sprdata[num][2]);
+       SW (sprdatb[num][2]);
+       SW (sprdata[num][3]);
+       SW (sprdatb[num][3]);
+       SB (spr[num].armed ? 1 : 0);
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+uae_u8 *restore_custom_extra (uae_u8 *src)
+{
+       uae_u32 v = restore_u32 ();
+
+       if (!(v & 1))
+               v = 0;
+       currprefs.cs_compatible = changed_prefs.cs_compatible = v >> 24;
+
+       currprefs.genlock = changed_prefs.genlock = RBB;
+       currprefs.cs_rtc = changed_prefs.cs_rtc = RB;
+       currprefs.cs_rtc_adjust = changed_prefs.cs_rtc_adjust = RL;
+
+       currprefs.cs_a1000ram = changed_prefs.cs_a1000ram = RBB;
+       currprefs.cs_slowmemisfast = changed_prefs.cs_slowmemisfast = RBB;
+
+       currprefs.cs_a2091 = changed_prefs.cs_a2091 = RBB;
+       currprefs.cs_a4091 = changed_prefs.cs_a4091 = RBB;
+       currprefs.cs_cdtvscsi = changed_prefs.cs_cdtvscsi = RBB;
+
+       currprefs.cs_pcmcia = changed_prefs.cs_pcmcia = RBB;
+       currprefs.cs_ciaatod = changed_prefs.cs_ciaatod = RB;
+       currprefs.cs_ciaoverlay = changed_prefs.cs_ciaoverlay = RBB;
+
+       currprefs.cs_agnusbltbusybug = changed_prefs.cs_agnusbltbusybug = RBB;
+       currprefs.cs_denisenoehb = changed_prefs.cs_denisenoehb = RBB;
+
+       currprefs.cs_agnusrev = changed_prefs.cs_agnusrev = SRB;
+       currprefs.cs_deniserev = changed_prefs.cs_deniserev = SRB;
+       currprefs.cs_fatgaryrev = changed_prefs.cs_fatgaryrev = SRB;
+       currprefs.cs_ramseyrev = changed_prefs.cs_ramseyrev = SRB;
+
+       currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p = RBB;
+       currprefs.cs_cd32cd = changed_prefs.cs_cd32cd = RBB;
+       currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram = RBB;
+       currprefs.cs_cdtvcd = changed_prefs.cs_cdtvcd = RBB;
+       currprefs.cs_cdtvram = changed_prefs.cs_cdtvram = RBB;
+       currprefs.cs_cdtvcard = changed_prefs.cs_cdtvcard = RB;
+
+       currprefs.cs_df0idhw = changed_prefs.cs_df0idhw = RBB;
+       currprefs.cs_dipagnus = changed_prefs.cs_dipagnus = RBB;
+       currprefs.cs_ide = changed_prefs.cs_ide = RB;
+       currprefs.cs_mbdmac = changed_prefs.cs_mbdmac = RB;
+       currprefs.cs_ksmirror_a8 = changed_prefs.cs_ksmirror_a8 = RBB;
+       currprefs.cs_ksmirror_e0 = changed_prefs.cs_ksmirror_e0 = RBB;
+       currprefs.cs_resetwarning = changed_prefs.cs_resetwarning = RBB;
+
+       return src;
+}
+
+uae_u8 *save_custom_extra (int *len, uae_u8 *dstptr)
+{
+       uae_u8 *dstbak, *dst;
+
+       if (dstptr)
+               dstbak = dst = dstptr;
+       else
+               dstbak = dst = xmalloc (uae_u8, 1000);
+
+       SL ((currprefs.cs_compatible << 24) | 1);
+       SB (currprefs.genlock ? 1 : 0);
+       SB (currprefs.cs_rtc);
+       SL (currprefs.cs_rtc_adjust);
+
+       SB (currprefs.cs_a1000ram ? 1 : 0);
+       SB (currprefs.cs_slowmemisfast ? 1 : 0);
+
+       SB (currprefs.cs_a2091 ? 1 : 0);
+       SB (currprefs.cs_a4091 ? 1 : 0);
+       SB (currprefs.cs_cdtvscsi ? 1 : 0);
+
+       SB (currprefs.cs_pcmcia ? 1 : 0);
+       SB (currprefs.cs_ciaatod);
+       SB (currprefs.cs_ciaoverlay ? 1 : 0);
+
+       SB (currprefs.cs_agnusbltbusybug ? 1 : 0);
+       SB (currprefs.cs_denisenoehb ? 1 : 0);
+
+       SB (currprefs.cs_agnusrev);
+       SB (currprefs.cs_deniserev);
+       SB (currprefs.cs_fatgaryrev);
+       SB (currprefs.cs_ramseyrev);
+
+       SB (currprefs.cs_cd32c2p);
+       SB (currprefs.cs_cd32cd);
+       SB (currprefs.cs_cd32nvram);
+       SB (currprefs.cs_cdtvcd ? 1 : 0);
+       SB (currprefs.cs_cdtvram ? 1 : 0);
+       SB (currprefs.cs_cdtvcard);
+
+       SB (currprefs.cs_df0idhw ? 1 : 0);
+       SB (currprefs.cs_dipagnus ? 1 : 0);
+       SB (currprefs.cs_ide);
+       SB (currprefs.cs_mbdmac);
+       SB (currprefs.cs_ksmirror_a8 ? 1 : 0);
+       SB (currprefs.cs_ksmirror_e0 ? 1 : 0);
+       SB (currprefs.cs_resetwarning ? 1 : 0);
+
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+uae_u8 *restore_custom_event_delay (uae_u8 *src)
+{
+       if (restore_u32 () != 1)
+               return src;
+       int cnt = restore_u8 ();
+       for (int i = 0; i < cnt; i++) {
+               uae_u8 type = restore_u8 ();
+               evt e = restore_u64 ();
+               uae_u32 data = restore_u32 ();
+               if (type == 1)
+                       event2_newevent_xx (-1, e, data, send_interrupt_do);
+       }
+       return src;
+}
+uae_u8 *save_custom_event_delay (int *len, uae_u8 *dstptr)
+{
+       uae_u8 *dstbak, *dst;
+       int cnt = 0;
+
+       for (int i = ev2_misc;  i < ev2_max; i++) {
+               struct ev2 *e = &eventtab2[i];
+               if (e->active && e->handler == send_interrupt_do) {
+                       cnt++;
+               }
+       }
+       if (cnt == 0)
+               return NULL;
+
+       if (dstptr)
+               dstbak = dst = dstptr;
+       else
+               dstbak = dst = xmalloc (uae_u8, 1000);
+
+       save_u32 (1);
+       save_u8 (cnt);
+       for (int i = ev2_misc;  i < ev2_max; i++) {
+               struct ev2 *e = &eventtab2[i];
+               if (e->active && e->handler == send_interrupt_do) {
+                       save_u8 (1);
+                       save_u64 (e->evtime - get_cycles ());
+                       save_u32 (e->data);
+               
+               }
+       }
+
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+
+uae_u8 *save_cycles (int *len, uae_u8 *dstptr)
+{
+       uae_u8 *dstbak, *dst;
+       if (dstptr)
+               dstbak = dst = dstptr;
+       else
+               dstbak = dst = xmalloc (uae_u8, 1000);
+       save_u32 (1);
+       save_u32 (CYCLE_UNIT);
+       save_u64 (get_cycles ());
+       save_u32 (extra_cycle);
+       write_log (L"SAVECYCLES %08X\n", get_cycles ());
+       *len = dst - dstbak;
+       return dstbak;
+}
+
+uae_u8 *restore_cycles (uae_u8 *src)
+{
+       if (restore_u32 () != 1)
+               return src;
+       restore_u32 ();
+       start_cycles = restore_u64 ();
+       extra_cycle = restore_u32 ();
+       write_log (L"RESTORECYCLES %08X\n", start_cycles);
+       return src;
+}
+
+#endif /* SAVESTATE */
+
+void check_prefs_changed_custom (void)
+{
+       if (!config_changed)
+               return;
+       currprefs.gfx_framerate = changed_prefs.gfx_framerate;
+       if (currprefs.turbo_emulation != changed_prefs.turbo_emulation)
+               warpmode (changed_prefs.turbo_emulation);
+       if (inputdevice_config_change_test ()) 
+               inputdevice_copyconfig (&changed_prefs, &currprefs);
+       currprefs.immediate_blits = changed_prefs.immediate_blits;
+       currprefs.collision_level = changed_prefs.collision_level;
+
+       currprefs.cs_ciaatod = changed_prefs.cs_ciaatod;
+       currprefs.cs_rtc = changed_prefs.cs_rtc;
+       currprefs.cs_cd32cd = changed_prefs.cs_cd32cd;
+       currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p;
+       currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram;
+       currprefs.cs_cdtvcd = changed_prefs.cs_cdtvcd;
+       currprefs.cs_ide = changed_prefs.cs_ide;
+       currprefs.cs_pcmcia = changed_prefs.cs_pcmcia;
+       currprefs.cs_fatgaryrev = changed_prefs.cs_fatgaryrev;
+       currprefs.cs_ramseyrev = changed_prefs.cs_ramseyrev;
+       currprefs.cs_agnusrev = changed_prefs.cs_agnusrev;
+       currprefs.cs_deniserev = changed_prefs.cs_deniserev;
+       currprefs.cs_mbdmac = changed_prefs.cs_mbdmac;
+       currprefs.cs_df0idhw = changed_prefs.cs_df0idhw;
+       currprefs.cs_slowmemisfast = changed_prefs.cs_slowmemisfast;
+
+       if (currprefs.chipset_mask != changed_prefs.chipset_mask ||
+               currprefs.picasso96_nocustom != changed_prefs.picasso96_nocustom ||
+               currprefs.ntscmode != changed_prefs.ntscmode) {
+                       currprefs.picasso96_nocustom = changed_prefs.picasso96_nocustom;
+                       currprefs.chipset_mask = changed_prefs.chipset_mask;
+                       if (currprefs.ntscmode != changed_prefs.ntscmode) {
+                               currprefs.ntscmode = changed_prefs.ntscmode;
+                               new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20;
+                       }
+                       init_custom ();
+       }
+#ifdef GFXFILTER
+       currprefs.gfx_filter_horiz_zoom = changed_prefs.gfx_filter_horiz_zoom;
+       currprefs.gfx_filter_vert_zoom = changed_prefs.gfx_filter_vert_zoom;
+       currprefs.gfx_filter_horiz_offset = changed_prefs.gfx_filter_horiz_offset;
+       currprefs.gfx_filter_vert_offset = changed_prefs.gfx_filter_vert_offset;
+       currprefs.gfx_filter_scanlines = changed_prefs.gfx_filter_scanlines;
+#endif
+}
+
+#ifdef CPUEMU_12
+
+STATIC_INLINE void sync_copper (int hpos)
+{
+       if (copper_enabled_thisline)
+               update_copper (hpos);
+}
+
+STATIC_INLINE void decide_fetch_ce (int hpos)
+{
+       if ((ddf_change == vpos || ddf_change + 1 == vpos) && vpos < current_maxvpos ())
+               decide_fetch (hpos);
+}
+
+#define BLIT_NASTY 4
+
+// blitter not in nasty mode = CPU gets one cycle if it has been waiting
+// at least 4 cycles (all DMA cycles count, not just blitter cycles, even
+// blitter idle cycles do count!)
+
+extern int cpu_tracer;
+STATIC_INLINE int dma_cycle (void)
+{
+       int hpos, hpos_old;
+
+       blitter_nasty = 1;
+       if (cpu_tracer == -1)
+               return current_hpos ();
+       for (;;) {
+               int bpldma;
+               int blitpri = dmacon & DMA_BLITPRI;
+               hpos_old = current_hpos ();
+               hpos = hpos_old + 1;
+               sync_copper (hpos);
+               decide_line (hpos);
+               decide_fetch_ce (hpos);
+               bpldma = is_bitplane_dma (hpos_old);
+               if (bltstate != BLT_done) {
+                       if (!blitpri && blitter_nasty >= BLIT_NASTY && cycle_line[hpos_old] == 0 && !bpldma) {
+                               alloc_cycle (hpos_old, CYCLE_CPUNASTY);
+                               break;
+                       }
+                       decide_blitter (hpos);
+                       // copper may have been waiting for the blitter
+                       sync_copper (hpos);
+               }
+               if (cycle_line[hpos_old] == 0 && !bpldma) {
+                       alloc_cycle (hpos_old, CYCLE_CPU);
+                       break;
+               }
+               regs.ce020memcycles -= CYCLE_UNIT;
+               do_cycles (1 * CYCLE_UNIT);
+               /* bus was allocated to dma channel, wait for next cycle.. */
+       }
+       return hpos_old;
+}
+
+STATIC_INLINE void checknasty (int hpos, int vpos)
+{
+       if (blitter_nasty >= BLIT_NASTY && !(dmacon & DMA_BLITPRI))
+               record_dma_event (DMA_EVENT_BLITNASTY, hpos, vpos);
+}
+
+uae_u32 wait_cpu_cycle_read (uaecptr addr, int mode)
+{
+       uae_u32 v = 0;
+       int hpos;
+       struct dma_rec *dr;
+
+       hpos = dma_cycle ();
+       x_do_cycles_pre (CYCLE_UNIT);
+
+#ifdef DEBUGGER
+       if (debug_dma) {
+               int reg = 0x1000;
+               if (mode < 0)
+                       reg |= 4;
+               else if (mode > 0)
+                       reg |= 2;
+               else
+                       reg |= 1;
+               dr = record_dma (reg, v, addr, hpos, vpos, DMARECORD_CPU);
+               checknasty (hpos, vpos);
+       }
+#endif
+       if (mode < 0)
+               v = get_long (addr);
+       else if (mode > 0)
+               v = get_word (addr);
+       else if (mode == 0)
+               v = get_byte (addr);
+
+#ifdef DEBUGGER
+       if (debug_dma)
+               dr->dat = v;
+#endif
+
+       x_do_cycles_post (CYCLE_UNIT, v);
+       return v;
+}
+
+uae_u32 wait_cpu_cycle_read_ce020 (uaecptr addr, int mode)
+{
+       uae_u32 v = 0;
+       int hpos;
+       struct dma_rec *dr;
+
+       hpos = dma_cycle ();
+       x_do_cycles_pre (CYCLE_UNIT);
+
+#ifdef DEBUGGER
+       if (debug_dma) {
+               int reg = 0x1000;
+               if (mode < 0)
+                       reg |= 4;
+               else if (mode > 0)
+                       reg |= 2;
+               else
+                       reg |= 1;
+               dr = record_dma (reg, v, addr, hpos, vpos, DMARECORD_CPU);
+               checknasty (hpos, vpos);
+       }
+#endif
+       if (mode < 0)
+               v = get_long (addr);
+       else if (mode > 0)
+               v = get_word (addr);
+       else if (mode == 0)
+               v = get_byte (addr);
+
+#ifdef DEBUGGER
+       if (debug_dma)
+               dr->dat = v;
+#endif
+
+       regs.ce020memcycles -= CYCLE_UNIT;
+       return v;
+}
+
+void wait_cpu_cycle_write (uaecptr addr, int mode, uae_u32 v)
+{
+       int hpos;
+
+       hpos = dma_cycle ();
+       x_do_cycles_pre (CYCLE_UNIT);
+
+#ifdef DEBUGGER
+       if (debug_dma) {
+               int reg = 0x1100;
+               if (mode < 0)
+                       reg |= 4;
+               else if (mode > 0)
+                       reg |= 2;
+               else
+                       reg |= 1;
+               record_dma (reg, v, addr, hpos, vpos, DMARECORD_CPU);
+               checknasty (hpos, vpos);
+       }
+#endif
+
+       if (mode < 0)
+               put_long (addr, v);
+       else if (mode > 0)
+               put_word (addr, v);
+       else if (mode == 0)
+               put_byte (addr, v);
+       x_do_cycles_post (CYCLE_UNIT, v);
+
+}
+
+void wait_cpu_cycle_write_ce020 (uaecptr addr, int mode, uae_u32 v)
+{
+       int hpos;
+
+       hpos = dma_cycle ();
+       x_do_cycles_pre (CYCLE_UNIT);
+
+#ifdef DEBUGGER
+       if (debug_dma) {
+               int reg = 0x1100;
+               if (mode < 0)
+                       reg |= 4;
+               else if (mode > 0)
+                       reg |= 2;
+               else
+                       reg |= 1;
+               record_dma (reg, v, addr, hpos, vpos, DMARECORD_CPU);
+               checknasty (hpos, vpos);
+       }
+#endif
+
+       if (mode < 0)
+               put_long (addr, v);
+       else if (mode > 0)
+               put_word (addr, v);
+       else if (mode == 0)
+               put_byte (addr, v);
+
+       regs.ce020memcycles -= CYCLE_UNIT;
+}
+
+void do_cycles_ce (unsigned long cycles)
+{
+       unsigned long c;
+
+       c = cycles + extra_cycle;
+       while (c >= CYCLE_UNIT) {
+               int hpos = current_hpos () + 1;
+               sync_copper (hpos);
+               decide_line (hpos);
+               decide_fetch_ce (hpos);
+               if (bltstate != BLT_done)
+                       decide_blitter (hpos);
+               do_cycles (1 * CYCLE_UNIT);
+               c -= CYCLE_UNIT;
+       }
+       extra_cycle = c;
+}
+
+int is_cycle_ce (void)
+{
+       int hpos = current_hpos ();
+       return cycle_line[hpos];
+}
+
+#endif
+
+bool ispal (void)
+{
+       if (beamcon0 & 0x80)
+               return currprefs.ntscmode == 0;
+       return maxvpos_nom >= MAXVPOS_NTSC + (MAXVPOS_PAL - MAXVPOS_NTSC) / 2;
+}
\ No newline at end of file
index 5b3fab6cc1d9cb9457af5fed77c2d6adcfcfbc34..f1083f981540244ee110cc3ffb98a35f04a2bec4 100644 (file)
@@ -5747,7 +5747,7 @@ static int rdb_mount (UnitInfo *uip, int unit_no, int partnum, uaecptr parmpacke
        for (;;) {
                if (fileblock == -1) {
                        if (!fsnode)
-                               write_log (L"RDB: required FS %08X not in FileSystem.resource or in RDB\n", dostype);
+                               write_log (L"RDB: FS %08X not in FileSystem.resource or in RDB\n", dostype);
                        goto error;
                }
                if (!legalrdbblock (uip, fileblock)) {
@@ -5761,7 +5761,8 @@ static int rdb_mount (UnitInfo *uip, int unit_no, int partnum, uaecptr parmpacke
                        goto error;
                }
                fileblock = rl (buf + 16);
-               if ((dostype >> 8) == (rl (buf + 32) >> 8))
+               uae_u32 rdbdostype = rl (buf + 32);
+               if (((dostype >> 8) == (rdbdostype >> 8) && (dostype != 0x444f5300 && (dostype & 0xffffff00) == 0x444f5300)) || (dostype == rdbdostype))
                        break;
        }
        newversion = (buf[36] << 8) | buf[37];
index 4baba08f60e28728a5b317b434a2c715c64ad203..223206ecad2d418b0df4588ce0e9239a9e393901 100644 (file)
--- a/gayle.cpp
+++ b/gayle.cpp
@@ -174,6 +174,7 @@ struct ide_hdf
        int num;
        int type;
        int blocksize;
+       int maxtransferstate;
 };
 
 static struct ide_hdf *idedrive[4];
@@ -551,7 +552,7 @@ static int get_nsec (int lba48)
        else
                return ide_nsector == 0 ? 256 : ide_nsector;
 }
-static void dec_nsec (int lba48, int v)
+static int dec_nsec (int lba48, int v)
 {
        if (lba48) {
                uae_u16 nsec;
@@ -559,8 +560,10 @@ static void dec_nsec (int lba48, int v)
                ide_nsector -= v;
                ide_nsector2 = nsec >> 8;
                ide_nsector = nsec & 0xff;
+               return (ide_nsector2 << 8) | ide_nsector;
        } else {
                ide_nsector -= v;
+               return ide_nsector;
        }
 }
 
@@ -602,6 +605,28 @@ static void put_lbachs (struct ide_hdf *ide, uae_u64 lba, unsigned int cyl, unsi
        }
 }
 
+static void check_maxtransfer (int state)
+{
+       if (state == 1) {
+               // transfer was started
+               if (ide->maxtransferstate < 2 && ide_nsector == 0) {
+                       ide->maxtransferstate = 1;
+               } else if (ide->maxtransferstate == 2) {
+                       // second transfer was started (part of split)
+                       write_log (L"IDE maxtransfer check detected split >256 block transfer\n");
+                       ide->maxtransferstate = 0;
+               } else {
+                       ide->maxtransferstate = 0;
+               }
+       } else if (state == 2) {
+               // address was read
+               if (ide->maxtransferstate == 1)
+                       ide->maxtransferstate++;
+               else
+                       ide->maxtransferstate = 0;
+       }
+}
+
 static void ide_read_sectors (int flags)
 {
        unsigned int cyl, head, sec, nsec;
@@ -613,6 +638,7 @@ static void ide_read_sectors (int flags)
                ide_fail ();
                return;
        }
+       check_maxtransfer(1);
        gui_flicker_led (LED_HD, ide->num, 1);
        nsec = get_nsec (lba48);
        get_lbachs (ide, &lba, &cyl, &head, &sec, lba48);
@@ -641,6 +667,7 @@ static void ide_write_sectors (int flags)
                ide_fail ();
                return;
        }
+       check_maxtransfer(1);
        gui_flicker_led (LED_HD, ide->num, 2);
        nsec = get_nsec (lba48);
        get_lbachs (ide, &lba, &cyl, &head, &sec, lba48);
@@ -720,7 +747,10 @@ static void ide_do_command (uae_u8 cmd)
 
 static uae_u16 ide_get_data (void)
 {
-       int irq = 0;
+       unsigned int cyl, head, sec, nsec;
+       uae_u64 lba;
+       bool irq = false;
+       bool last = false;
        uae_u16 v;
 
        if (IDE_LOG > 4)
@@ -732,12 +762,10 @@ static uae_u16 ide_get_data (void)
                        return 0xffff;
                return 0;
        }
+       nsec = 0;
        if (ide->data_offset == 0 && ide->data_size >= 0) {
-               unsigned int cyl, head, sec, nsec;
-               uae_u64 lba;
-
-               nsec = get_nsec (ide->lba48);
                get_lbachs (ide, &lba, &cyl, &head, &sec, ide->lba48);
+               nsec = get_nsec (ide->lba48);
                if (nsec * ide->blocksize > ide->hdhfd.size - lba * ide->blocksize)
                        nsec = (ide->hdhfd.size - lba * ide->blocksize) / ide->blocksize;
                if (nsec <= 0) {
@@ -748,8 +776,8 @@ static uae_u16 ide_get_data (void)
                if (nsec > ide->data_multi)
                        nsec = ide->data_multi;
                hdf_read (&ide->hdhfd.hfd, ide->secbuf, lba * ide->blocksize, nsec * ide->blocksize);
-               put_lbachs (ide, lba, cyl, head, sec, nsec, ide->lba48);
-               dec_nsec (ide->lba48, nsec);
+               if (!dec_nsec (ide->lba48, nsec))
+                       last = true;
                if (IDE_LOG > 1)
                        write_log (L"IDE%d read, read %d bytes to buffer\n", ide->num, nsec * ide->blocksize);
        }
@@ -761,7 +789,7 @@ static uae_u16 ide_get_data (void)
        } else {
                ide->data_size -= 2;
                if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) {
-                       irq = 1;
+                       irq = true;
                        ide->data_offset = 0;
                }
        }
@@ -770,13 +798,16 @@ static uae_u16 ide_get_data (void)
                if (IDE_LOG > 1)
                        write_log (L"IDE%d read finished\n", ide->num);
        }
+       if (nsec) {
+               put_lbachs (ide, lba, cyl, head, sec, last ? nsec - 1 : nsec, ide->lba48);
+       }
        if (irq) {
                ide_interrupt ();
        }
        return v;
 }
 
-static void ide_write_drive (void)
+static void ide_write_drive (bool last)
 {
        unsigned int cyl, head, sec, nsec;
        uae_u64 lba;
@@ -786,7 +817,7 @@ static void ide_write_drive (void)
                return;
        get_lbachs (ide, &lba, &cyl, &head, &sec, ide->lba48);
        hdf_write (&ide->hdhfd.hfd, ide->secbuf, lba * ide->blocksize, ide->data_offset);
-       put_lbachs (ide, lba, cyl, head, sec, nsec, ide->lba48);
+       put_lbachs (ide, lba, cyl, head, sec, last ? nsec - 1 : nsec, ide->lba48);
        dec_nsec (ide->lba48, nsec);
        if (IDE_LOG > 1)
                write_log (L"IDE%d write interrupt, %d bytes written\n", ide->num, ide->data_offset);
@@ -808,15 +839,15 @@ static void ide_put_data (uae_u16 v)
        ide->secbuf[ide->data_offset + 0] = v >> 8;
        ide->data_offset += 2;
        ide->data_size -= 2;
-       if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) {
-               irq = 1;
-               ide_write_drive ();
-       }
        if (ide->data_size == 0) {
-               ide_write_drive ();
+               irq = 1;
+               ide_write_drive (true);
                ide->status &= ~IDE_STATUS_DRQ;
                if (IDE_LOG > 1)
                        write_log (L"IDE%d write finished\n", ide->num);
+       } else if (((ide->data_offset % ide->blocksize) == 0) && ((ide->data_offset / ide->blocksize) % ide->data_multi) == 0) {
+               irq = 1;
+               ide_write_drive (false);
        }
        if (irq)
                ide_interrupt ();
@@ -899,6 +930,7 @@ static uae_u32 ide_read (uaecptr addr)
                        v = ide_sector2;
                else
                        v = ide_sector;
+               check_maxtransfer (2);
                break;
        case IDE_LCYL:
                if (ide_devcon & 0x80)
index 92129c183f504f6642fc5087bfd16bcf483eef9b..36b099a0bb6ea01207b81f9c0e766c28967e0b6e 100644 (file)
@@ -1,4 +1,7 @@
 
+#define AMIGA_WIDTH_MAX (752 / 2)
+#define AMIGA_HEIGHT_MAX (574 / 2)
+
 #ifdef GFXFILTER
 
 typedef unsigned char u8;
index bbbbe00e9957cf432380e7a6c7bf09676c05141a..cc109bfa8e62fe2f566892ea787635007cb943b0 100644 (file)
@@ -9,7 +9,7 @@
 
 #define UAEMAJOR 2
 #define UAEMINOR 3
-#define UAESUBREV 2
+#define UAESUBREV 3
 
 typedef enum { KBD_LANG_US, KBD_LANG_DK, KBD_LANG_DE, KBD_LANG_SE, KBD_LANG_FR, KBD_LANG_IT, KBD_LANG_ES } KbdLang;
 
@@ -136,6 +136,24 @@ enum { CP_GENERIC = 1, CP_CDTV, CP_CD32, CP_A500, CP_A500P, CP_A600, CP_A1000,
 #define AUTOSCALE_CENTER 6
 #define AUTOSCALE_MANUAL 7 // use gfx_xcenter_pos and gfx_ycenter_pos
 
+#define MAX_CHIPSET_REFRESH 10
+#define MAX_CHIPSET_REFRESH_TOTAL (MAX_CHIPSET_REFRESH + 2)
+#define CHIPSET_REFRESH_PAL (MAX_CHIPSET_REFRESH + 0)
+#define CHIPSET_REFRESH_NTSC (MAX_CHIPSET_REFRESH + 1)
+struct chipset_refresh
+{
+       bool locked;
+       int horiz;
+       int vert;
+       int lace;
+       int ntsc;
+       int vsync;
+       int framelength;
+       double rate;
+       TCHAR label[16];
+       TCHAR commands[256];
+};
+
 struct uae_prefs {
 
        struct strlist *all_lines;
@@ -214,6 +232,7 @@ struct uae_prefs {
        bool gfx_scandoubler;
        int gfx_refreshrate;
        int gfx_avsync, gfx_pvsync;
+       int gfx_avsyncmode, gfx_pvsyncmode;
        int gfx_resolution;
        int gfx_vresolution;
        int gfx_lores_mode;
@@ -254,6 +273,8 @@ struct uae_prefs {
        bool ntscmode;
        bool genlock;
        double chipset_refreshrate;
+       struct chipset_refresh cr[MAX_CHIPSET_REFRESH + 2];
+       int cr_selected;
        int collision_level;
        int leds_on_screen;
        struct wh osd_pos;
@@ -493,6 +514,7 @@ extern void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type
 extern int cfgfile_load (struct uae_prefs *p, const TCHAR *filename, int *type, int ignorelink, int userconfig);
 extern int cfgfile_save (struct uae_prefs *p, const TCHAR *filename, int);
 extern void cfgfile_parse_line (struct uae_prefs *p, TCHAR *, int);
+extern void cfgfile_parse_lines (struct uae_prefs *p, const TCHAR *, int);
 extern int cfgfile_parse_option (struct uae_prefs *p, TCHAR *option, TCHAR *value, int);
 extern int cfgfile_get_description (const TCHAR *filename, TCHAR *description, TCHAR *hostlink, TCHAR *hardwarelink, int *type);
 extern void cfgfile_show_usage (void);
index 883b8b2898eff5d1ea388120c576d021152d94d6..e01aac67633b4f80357a53334f240abea94736b2 100644 (file)
@@ -25,7 +25,9 @@ extern int isfullscreen (void);
 extern void toggle_fullscreen (int);
 extern void toggle_mousegrab (void);
 extern void desktop_coords (int *dw, int *dh, int *x, int *y, int *w, int *h);
-extern bool vsync_switchmode (int, int);
+extern bool vsync_switchmode (int);
+extern bool vsync_busywait (void);
+extern double vblank_calibrate (bool);
 extern void doflashscreen (void);
 extern int flashscreen;
 extern void updatedisplayarea (void);
@@ -34,6 +36,7 @@ extern void flush_line (int);
 extern void flush_block (int, int);
 extern void flush_screen (int, int);
 extern void flush_clear_screen (void);
+extern void update_screen (void);
 
 extern int lockscr (int);
 extern void unlockscr (void);
index a4e63a33d0f25b2fbf7206778a8492c65c8e0290..56178406cb053b9fa613517a752a2d5322ea0ba6 100644 (file)
@@ -3044,7 +3044,7 @@ static int getoldport (struct uae_input_device *id)
        return -1;
 }
 
-static int switchdevice (struct uae_input_device *id, int num, int button)
+static int switchdevice (struct uae_input_device *id, int num, bool buttonmode)
 {
        int i, j;
        int ismouse = 0;
@@ -3055,8 +3055,6 @@ static int switchdevice (struct uae_input_device *id, int num, int button)
 
        if (num >= 4)
                return 0;
-       if (!button)
-               return 0;
        for (i = 0; i < MAX_INPUT_DEVICES; i++) {
                if (id == &joysticks[i]) {
                        name = idev[IDTYPE_JOYSTICK].get_uniquename (i);
@@ -3079,8 +3077,12 @@ static int switchdevice (struct uae_input_device *id, int num, int button)
        }
        if (!name)
                return 0;
-       if (num == 0 && otherbuttonpressed)
-               newport = newport ? 0 : 1;
+       if (buttonmode) {
+               if (num == 0 && otherbuttonpressed)
+                       newport = newport ? 0 : 1;
+       } else {
+               newport = num ? 1 : 0;
+       }
        if (currprefs.input_selected_setting == GAMEPORT_INPUT_SETTINGS) {
                if ((num == 0 || num == 1) && currprefs.jports[newport].id != JPORT_CUSTOM) {
                        int om = jsem_ismouse (num, &currprefs);
@@ -3187,6 +3189,7 @@ static void process_custom_event (struct uae_input_device *id, int offset, int s
 
 static void setbuttonstateall (struct uae_input_device *id, struct uae_input_device2 *id2, int button, int state)
 {
+       static frame_time_t switchdevice_timeout;
        int i;
        uae_u32 mask = 1 << button;
        uae_u32 omask = id2->buttonmask & mask;
@@ -3197,8 +3200,15 @@ static void setbuttonstateall (struct uae_input_device *id, struct uae_input_dev
        if (input_play)
                return;
        if (!id->enabled) {
-               if (state)
-                       switchdevice (id, button, 1);
+               frame_time_t t = read_processor_time ();
+               if (state) {
+                       switchdevice_timeout = t;
+               } else {
+                       int port = button;
+                       if (t - switchdevice_timeout >= syncbase) // 1s
+                               port ^= 1;
+                       switchdevice (id, port, true);
+               }
                return;
        }
        if (button >= ID_BUTTON_TOTAL)
@@ -5504,7 +5514,7 @@ void setjoystickstate (int joy, int axis, int state, int max)
                return;
        if (!joysticks[joy].enabled) {
                if (v1)
-                       switchdevice (&joysticks[joy], axis * 2 + (v1 < 0 ? 0 : 1), 0);
+                       switchdevice (&joysticks[joy], axis * 2 + (v1 < 0 ? 0 : 1), false);
                return;
        }
        for (i = 0; i < MAX_INPUT_SUB_EVENT; i++)
@@ -5699,7 +5709,7 @@ int inputdevice_joyport_config (struct uae_prefs *p, TCHAR *value, int portnum,
                break;
        case 0:
                {
-                       int start = JPORT_NONE, got = 0, max = 0;
+                       int start = JPORT_NONE, got = 0, max = -1;
                        TCHAR *pp = 0;
                        if (_tcsncmp (value, L"kbd", 3) == 0) {
                                start = JSEM_KBDLAYOUT;
@@ -5723,7 +5733,7 @@ int inputdevice_joyport_config (struct uae_prefs *p, TCHAR *value, int portnum,
                                start = JPORT_CUSTOM;
                        }
                        if (got) {
-                               if (pp) {
+                               if (pp && max != 0) {
                                        int v = _tstol (pp);
                                        if (start >= 0) {
                                                if (start == JSEM_KBDLAYOUT && v > 0)
index 64fb4d74cf53a02ed3446e28e8182f105c46aa7a..9a68e099d2fb4f5287aa428b4c907f7dfca68ac1 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -171,7 +171,6 @@ void fixup_cpu (struct uae_prefs *p)
                p->mmu_model = 0;
 }
 
-
 void fixup_prefs (struct uae_prefs *p)
 {
        int err = 0;
index 7d32d8b36643d24ce6cdd0cd2cd91a201b5461fa..df64a04b26d98435863a5b5bb5900d12d9481612 100644 (file)
@@ -79,7 +79,7 @@ static int max_texture_w, max_texture_h;
 static int tin_w, tin_h, tout_w, tout_h, window_h, window_w;
 static int t_depth, mult, multx;
 static int required_sl_texture_w, required_sl_texture_h;
-static int vsync2, guimode;
+static int vsync2, guimode, maxscanline;
 static int resetcount;
 static int cursor_x, cursor_y, cursor_v;
 
@@ -204,6 +204,13 @@ static TCHAR *D3DX_ErrorString (HRESULT hr, LPD3DXBUFFER Errors)
        return buffer;
 }
 
+static int isd3d (void)
+{
+       if (devicelost || !d3ddev || !d3d_enabled)
+               return 0;
+       return 1;
+}
+
 static LPD3DXEFFECT postEffect;
 static D3DXHANDLE postSourceTextureHandle;
 static D3DXHANDLE postMaskTextureHandle;
@@ -716,9 +723,10 @@ static LPD3DXEFFECT psEffect_LoadEffect (const TCHAR *shaderfile, int full)
        static int first;
        DWORD compileflags = psEnabled ? 0 : D3DXSHADER_USE_LEGACY_D3DX9_31_DLL;
        int canusefile = 0, existsfile = 0;
+       bool plugin_path;
 
        compileflags |= EFFECTCOMPILERFLAGS;
-       get_plugin_path (tmp, sizeof tmp / sizeof (TCHAR), L"filtershaders\\direct3d");
+       plugin_path = get_plugin_path (tmp, sizeof tmp / sizeof (TCHAR), L"filtershaders\\direct3d");
        _tcscat (tmp, shaderfile);
        if (!full) {
                struct zfile *z = zfile_fopen (tmp, L"r", 0);
@@ -743,7 +751,7 @@ static LPD3DXEFFECT psEffect_LoadEffect (const TCHAR *shaderfile, int full)
                if (FAILED (hr)) {
                        const char *str = psEnabled ? fx20 : fx10;
                        int len = strlen (str);
-                       if (!existsfile || canusefile == 0) {
+                       if ((!existsfile || canusefile == 0) && plugin_path) {
                                struct zfile *z = zfile_fopen (tmp, L"w", 0);
                                if (z) {
                                        zfile_fwrite ((void*)str, len, 1, z);
@@ -1818,6 +1826,55 @@ void D3D_free (void)
        ddraw_fs_hack_free ();
 }
 
+static bool getvblankstate (int *vpos)
+{
+       HRESULT hr;
+       D3DRASTER_STATUS rt;
+
+       *vpos = 0;
+       if (!isd3d ())
+               return false;
+       hr = d3ddev->GetRasterStatus (0, &rt);
+       if (FAILED (hr)) {
+               write_log (L"%s: GetRasterStatus %s\n", D3DHEAD, D3D_ErrorString (hr));
+               return false;
+       }
+       *vpos = rt.ScanLine;
+       if (rt.ScanLine > maxscanline)
+               maxscanline = rt.ScanLine;
+       if (rt.InVBlank != 0)
+               *vpos = -1;
+       return true;
+}
+
+bool D3D_waitvblankstate (bool state)
+{
+       int vpos;
+       for (;;) {
+               if (!getvblankstate (&vpos))
+                       return false;
+               if (vpos < 0 || vpos >= maxscanline - 5) {
+                       if (state)
+                               return true;
+               } else {
+                       if (!state)
+                               return true;
+               }
+       }
+}
+
+bool D3D_vblank_busywait (void)
+{
+       int vpos;
+
+       for (;;) {
+               if (!getvblankstate (&vpos))
+                       return false;
+               if (vpos <= 0 || vpos >= maxscanline - 5)
+                       return true;
+       }
+}
+
 const TCHAR *D3D_init (HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int depth, int mmult)
 {
        HRESULT ret, hr;
@@ -1918,7 +1975,7 @@ const TCHAR *D3D_init (HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int depth
        if (isfullscreen() > 0) {
                dpp.FullScreen_RefreshRateInHz = currprefs.gfx_refreshrate > 0 ? currprefs.gfx_refreshrate : 0;
                modeex.RefreshRate = dpp.FullScreen_RefreshRateInHz;
-               if (currprefs.gfx_avsync) {
+               if (currprefs.gfx_avsync && currprefs.gfx_avsyncmode == 0) {
                        dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
                        if (getvsyncrate (dpp.FullScreen_RefreshRateInHz) != dpp.FullScreen_RefreshRateInHz) {
                                if (d3dCaps.PresentationIntervals & D3DPRESENT_INTERVAL_TWO)
@@ -2073,19 +2130,12 @@ const TCHAR *D3D_init (HWND ahwnd, int w_w, int w_h, int t_w, int t_h, int depth
                _stprintf (errmsg, L"%s: initialization failed.", D3DHEAD);
                return errmsg;
        }
-
+       maxscanline = 0;
        d3d_enabled = 1;
 
        return 0;
 }
 
-static int isd3d (void)
-{
-       if (devicelost || !d3ddev || !d3d_enabled)
-               return 0;
-       return 1;
-}
-
 static HRESULT reset (void)
 {
        if (d3dex)
index 04d5d64c1f95be55cb5885754c1c765128d199f8..91dfb22e6586bc32a079f614883d81e4b34ea060 100644 (file)
@@ -15,6 +15,8 @@ extern void D3D_clear (void);
 extern int D3D_canshaders (void);
 extern int D3D_goodenough (void);
 extern void D3D_setcursor (int x, int y, int visible);
+extern bool D3D_vblank_busywait (void);
+extern bool D3D_waitvblankstate (bool);
 extern LPDIRECT3DTEXTURE9 cursorsurfaced3d;
 
 #define CURSORMAXWIDTH 64
index 9b8f707f4fc568dfab55eb0426c14f7190007d80..1410f75bd54b5cf6f67e7661190246be56716094 100644 (file)
@@ -1174,3 +1174,29 @@ void dx_check (void)
                return;
        dxdata.islost = 1;
 }
+
+bool DirectDraw_waitvblankstate (bool state)
+{
+       BOOL vb;
+       HRESULT hr;
+
+       dx_check ();
+       if (dxdata.islost)
+               return false;
+       for (;;) {
+               hr = IDirectDraw7_GetVerticalBlankStatus (dxdata.maindd, &vb);
+               if (FAILED (hr)) {
+                       write_log (L"IDirectDraw7_GetVerticalBlankStatus %s\n", DXError (hr));
+                       return false;
+               }
+               if ((vb && state) || (!vb && !state))
+                       return true;
+       }
+}
+
+bool DirectDraw_vblank_busywait (void)
+{
+       if (!DirectDraw_waitvblankstate (true))
+               return false;
+       return true;
+}
index a2ff8b440b131f4d3905f14c42441fd034ea4e97..23af467dbf0c7c60de9d2c06a10d59613615c378 100644 (file)
@@ -130,6 +130,8 @@ int DirectDraw_BlitRectCK (LPDIRECTDRAWSURFACE7 dst, RECT *dstrect, LPDIRECTDRAW
 void DirectDraw_FillSurface (LPDIRECTDRAWSURFACE7 dst, RECT *rect, uae_u32 color);
 void DirectDraw_Fill (RECT *rect, uae_u32 color);
 void DirectDraw_FillPrimary (void);
+bool DirectDraw_vblank_busywait (void);
+bool DirectDraw_waitvblankstate (bool);
 
 void dx_check (void);
 int dx_islost (void);
index bd65b3d7787dcd06967fbf705b9849f4ffa85a6a..b56ec719a84b14d8b6d02840702b3c39b85f0688 100644 (file)
@@ -664,7 +664,11 @@ static void picasso_trigger_vblank (void)
 
 static int isvsync (void)
 {
-       return currprefs.gfx_pfullscreen && currprefs.gfx_pvsync;
+       if (currprefs.gfx_pfullscreen && currprefs.gfx_pvsync)
+               return 1;
+       if (currprefs.gfx_pvsync && currprefs.gfx_pvsyncmode)
+               return -1;
+       return 0;
 }
 
 void picasso_handle_vsync (void)
index 4e4a3d6f223a5555dc6e8f834dc930ddc15a5c82..e2543b6fae24501e21238cc1c4037df581d68abb 100644 (file)
 #define IDC_SAMPLERLIST                 1028
 #define IDC_DISPLAY_BUFFERCNT           1028
 #define IDC_PORT0_JOYSMODE              1029
+#define IDC_SCREENMODE_NATIVE2          1029
 #define IDC_SLOWMEM                     1030
 #define IDC_PORT1_JOYSMODE              1030
+#define IDC_SCREENMODE_RTG2             1030
 #define IDC_MBMEM2                      1031
 #define IDC_PORT2_JOYS                  1031
 #define IDC_PORT3_JOYS                  1032
 #define IDC_BLACKER_THAN_BLACK          1170
 #define IDC_BLITIMM                     1174
 #define IDC_LORES                       1176
+#define IDC_RATE2BOX                    1177
 #define IDC_LORES_SMOOTHED              1179
 #define IDC_FLICKERFIXER                1180
 #define IDC_AUTORESOLUTION              1181
+#define IDC_AUTORESOLUTION2             1182
+#define IDC_RATE2ENABLE                 1182
 #define IDC_FRAMERATE                   1185
-#define IDC_RATETEXT                    1186
 #define IDC_XSIZE                       1187
 #define IDC_YSIZE                       1188
 #define IDC_INPUTAUTOFIRERATE           1188
 #define IDC_DA_SLIDER                   1193
 #define IDC_FRAMERATE2                  1194
 #define IDC_RATE2TEXT                   1195
-#define IDC_RATE2TEXT2                  1196
 #define IDC_DA_TEXT                     1196
 #define IDC_CPU0                        1200
 #define IDC_CPU1                        1201
index 1fa7c7fe511af8466197b71b5ac82bf92011d271..2e108c070f109a4db9964f76cca80c863e94fe8c 100644 (file)
@@ -123,8 +123,7 @@ BEGIN
     GROUPBOX        "Settings",IDC_SETTINGSTEXT,4,73,209,137\r
     CONTROL         "Blacker than black",IDC_BLACKER_THAN_BLACK,"Button",BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,11,125,92,10\r
     LTEXT           "Refresh:",IDC_REFRESHTEXT,11,173,28,8\r
-    CONTROL         "Slider1",IDC_FRAMERATE,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,168,75,20\r
-    EDITTEXT        IDC_RATETEXT,123,172,79,12,ES_CENTER | ES_READONLY\r
+    CONTROL         "Slider1",IDC_FRAMERATE,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,168,99,20\r
     GROUPBOX        "Centering",IDC_STATIC,221,73,75,49\r
     CONTROL         "Horizontal",IDC_XCENTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,87,49,10\r
     CONTROL         "Vertical",IDC_YCENTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,103,49,10\r
@@ -135,14 +134,14 @@ BEGIN
     COMBOBOX        IDC_DA_MODE,4,218,58,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
     CONTROL         "",IDC_DA_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,67,214,101,20\r
     LTEXT           "FPS adj.:",IDC_REFRESH2TEXT,9,193,32,8\r
-    CONTROL         "",IDC_FRAMERATE2,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,188,109,20\r
-    EDITTEXT        IDC_RATE2TEXT,156,192,46,12,ES_AUTOHSCROLL\r
+    CONTROL         "",IDC_FRAMERATE2,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,188,99,20\r
+    EDITTEXT        IDC_RATE2TEXT,143,192,41,12,ES_AUTOHSCROLL\r
     COMBOBOX        IDC_RESOLUTIONDEPTH,134,27,46,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
     CONTROL         "Filtered low resolution",IDC_LORES_SMOOTHED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,125,89,10\r
-    COMBOBOX        IDC_SCREENMODE_NATIVE,103,85,102,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
-    COMBOBOX        IDC_SCREENMODE_RTG,103,103,102,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
-    RTEXT           "Native mode:",IDC_STATIC,19,85,59,15,SS_CENTERIMAGE\r
-    RTEXT           "RTG mode:",IDC_STATIC,19,101,59,15,SS_CENTERIMAGE\r
+    COMBOBOX        IDC_SCREENMODE_NATIVE,57,85,67,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
+    COMBOBOX        IDC_SCREENMODE_RTG,57,103,67,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
+    RTEXT           "Native mode:",IDC_STATIC,9,85,44,15,SS_CENTERIMAGE\r
+    RTEXT           "RTG mode:",IDC_STATIC,8,101,45,15,SS_CENTERIMAGE\r
     PUSHBUTTON      "Reset to defaults",IDC_DA_RESET,221,218,75,14\r
     RTEXT           "Resolution:",IDC_STATIC,27,152,59,15,SS_CENTERIMAGE\r
     COMBOBOX        IDC_LORES,103,152,102,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
@@ -152,6 +151,10 @@ BEGIN
     COMBOBOX        IDC_DISPLAY_BUFFERCNT,187,47,97,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
     CONTROL         "Resolution autoswitch",IDC_AUTORESOLUTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,139,89,10\r
     EDITTEXT        IDC_DA_TEXT,167,218,46,12,ES_AUTOHSCROLL | ES_READONLY\r
+    COMBOBOX        IDC_SCREENMODE_NATIVE2,129,85,78,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
+    COMBOBOX        IDC_SCREENMODE_RTG2,129,103,78,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
+    COMBOBOX        IDC_RATE2BOX,143,173,60,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP\r
+    CONTROL         "",IDC_RATE2ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,194,193,8,10\r
 END\r
 \r
 IDD_MEMORY DIALOGEX 0, 0, 300, 182\r
@@ -1061,8 +1064,8 @@ END
 //\r
 \r
 VS_VERSION_INFO VERSIONINFO\r
- FILEVERSION 2,3,2,0\r
- PRODUCTVERSION 2,3,2,0\r
+ FILEVERSION 2,3,3,0\r
+ PRODUCTVERSION 2,3,3,0\r
  FILEFLAGSMASK 0x3fL\r
 #ifdef _DEBUG\r
  FILEFLAGS 0x1L\r
@@ -1078,12 +1081,12 @@ BEGIN
         BLOCK "040904b0"\r
         BEGIN\r
             VALUE "FileDescription", "WinUAE"\r
-            VALUE "FileVersion", "2.3.2.0"\r
+            VALUE "FileVersion", "2.3.3.0"\r
             VALUE "InternalName", "WinUAE"\r
             VALUE "LegalCopyright", "© 1996-2011 under the GNU Public License (GPL)"\r
             VALUE "OriginalFilename", "WinUAE.exe"\r
             VALUE "ProductName", "WinUAE"\r
-            VALUE "ProductVersion", "2.3.2.0"\r
+            VALUE "ProductVersion", "2.3.3.0"\r
         END\r
     END\r
     BLOCK "VarFileInfo"\r
index 711a68acbc74f28b4946b663ed2f9f5ee57b4ce7..970d8e75cdc769c8cfd970c541ab9f8ecce8c0a0 100644 (file)
@@ -320,7 +320,7 @@ static void figure_processor_speed_rdtsc (void)
        oldpri = GetThreadPriority (th);
        SetThreadPriority (th, THREAD_PRIORITY_HIGHEST);
        dummythread_die = -1;
-       CloseHandle((HANDLE)_beginthread (&dummythread, 0, 0));
+       _beginthread (&dummythread, 0, 0);
        sleep_millis (500);
        clockrate = win32_read_processor_time ();
        sleep_millis (500);
@@ -2719,6 +2719,9 @@ void target_quit (void)
 
 void target_fixup_options (struct uae_prefs *p)
 {
+       if (p->gfx_avsync)
+               p->gfx_avsyncmode = 1;
+
 #ifdef RETROPLATFORM
        rp_fixup_options (p);
 #endif
index e7c9e6143e88318ba1f16a52adf476de9d4ed62a..dbeca0830b4d422b80a951dbe72bf1c04c49dfad 100644 (file)
 #define GETBDM(x) (((x) - ((x / 10000) * 10000)) / 100)
 #define GETBDD(x) ((x) % 100)
 
-#define WINUAEPUBLICBETA 0
+#define WINUAEPUBLICBETA 1
 #define LANG_DLL 1
 
-#define WINUAEBETA L""
-#define WINUAEDATE MAKEBD(2011, 6, 2)
+#define WINUAEBETA L"Beta 1"
+#define WINUAEDATE MAKEBD(2011, 6, 18)
 #define WINUAEEXTRA L""
 #define WINUAEREV L""
 
index ddb8c39122314320cae0d321c27bc517792eb875..d1e120da65d917dd174e4de28a547e5813f9f530 100644 (file)
@@ -113,7 +113,7 @@ static int vblscale2 (int v)
 static void fixh (int *ah, int *th, int minh)
 {
        if (!(beamcon0 & 0x80)) {
-               int max = ((572 / 2) * scale) << currprefs.gfx_vresolution;
+               int max = (AMIGA_HEIGHT_MAX * scale) << currprefs.gfx_vresolution;
                if (minh && max < minh)
                        max = minh;
                if (*ah > max)
@@ -240,8 +240,8 @@ void getfilterrect2 (RECT *sr, RECT *dr, RECT *zr, int dst_width, int dst_height
                filterymult = 1000 / scale;
 
                if (scalemode == AUTOSCALE_STATIC_MAX || scalemode == AUTOSCALE_STATIC_NOMINAL) {
-                       cw = (752 / 2) << currprefs.gfx_resolution;
-                       ch = (572 / 2) << currprefs.gfx_vresolution;
+                       cw = AMIGA_WIDTH_MAX << currprefs.gfx_resolution;
+                       ch = AMIGA_HEIGHT_MAX << currprefs.gfx_vresolution;
                        cx = 0;
                        cy = 0;
                        cv = 1;
@@ -272,14 +272,14 @@ void getfilterrect2 (RECT *sr, RECT *dr, RECT *zr, int dst_width, int dst_height
 
                        v = currprefs.gfx_xcenter_size;
                        if (v <= 0)
-                               cw = 752 * 2;
+                               cw = AMIGA_WIDTH_MAX * 4;
                        else
                                cw = v;
                        cw >>=  (RES_MAX - currprefs.gfx_resolution);
 
                        v = currprefs.gfx_ycenter_size;
                        if (v <= 0)
-                               ch = 572;
+                               ch = AMIGA_HEIGHT_MAX * 2;
                        else
                                ch = v;
                        ch >>= (VRES_MAX - currprefs.gfx_vresolution);
index 53ca29b4bbf9941619d72e103e6fb163b05ae970..0e864d172e313c4e4b6bf953607e7e8262deebcf 100644 (file)
@@ -79,6 +79,7 @@
 
 struct uae_filter *usedfilter;
 static int scalepicasso;
+static double remembered_vblank;
 
 struct winuae_currentmode {
        unsigned int flags;
@@ -105,6 +106,7 @@ int window_extra_width, window_extra_height;
 
 static struct winuae_currentmode *currentmode = &currentmodestruct;
 static int wasfullwindow_a, wasfullwindow_p;
+static int vblankbase;
 
 int screen_is_picasso = 0;
 
@@ -285,8 +287,7 @@ static int rgbformat_bits (RGBFTYPE t)
 }
 
 static int set_ddraw_2 (void)
-{
-       HRESULT ddrval;
+{HRESULT ddrval;
        int bits = (currentmode->current_depth + 7) & ~7;
        int width = currentmode->native_width;
        int height = currentmode->native_height;
@@ -827,6 +828,10 @@ void flush_block (int first, int last)
 }
 
 void flush_screen (int a, int b)
+{
+}
+
+void update_screen (void)
 {
        if (dx_islost ())
                return;
@@ -1138,14 +1143,14 @@ static void update_gfxparams (void)
                currentmode->current_height = picasso96_state.Height;
                currentmode->frequency = abs (currprefs.gfx_refreshrate > default_freq ? currprefs.gfx_refreshrate : default_freq);
                if (currprefs.gfx_pvsync)
-                       currentmode->vsync = 1;
+                       currentmode->vsync = 1 + currprefs.gfx_pvsyncmode;
        } else {
 #endif
                currentmode->current_width = currprefs.gfx_size.width;
                currentmode->current_height = currprefs.gfx_size.height;
                currentmode->frequency = abs (currprefs.gfx_refreshrate);
                if (currprefs.gfx_avsync)
-                       currentmode->vsync = 1;
+                       currentmode->vsync = 1 + currprefs.gfx_avsyncmode;
 #ifdef PICASSO96
        }
 #endif
@@ -1269,6 +1274,8 @@ int check_prefs_changed_gfx (void)
        c |= currprefs.gfx_pfullscreen != changed_prefs.gfx_pfullscreen ? 16 : 0;
        c |= currprefs.gfx_avsync != changed_prefs.gfx_avsync ? 2 | 16 : 0;
        c |= currprefs.gfx_pvsync != changed_prefs.gfx_pvsync ? 2 | 16 : 0;
+       c |= currprefs.gfx_avsyncmode != changed_prefs.gfx_avsyncmode ? 2 | 16 : 0;
+       c |= currprefs.gfx_pvsyncmode != changed_prefs.gfx_pvsyncmode ? 2 | 16 : 0;
        c |= currprefs.gfx_refreshrate != changed_prefs.gfx_refreshrate ? 2 | 16 : 0;
        c |= currprefs.gfx_autoresolution != changed_prefs.gfx_autoresolution ? (2|8) : 0;
        c |= currprefs.gfx_api != changed_prefs.gfx_api ? (1|8|32) : 0;
@@ -1408,11 +1415,20 @@ int check_prefs_changed_gfx (void)
                return 1;
        }
 
+       for (int i = 0; i < MAX_CHIPSET_REFRESH_TOTAL; i++) {
+               if (currprefs.cr[i].rate != changed_prefs.cr[i].rate ||
+                       currprefs.cr[i].locked != changed_prefs.cr[i].locked) {
+                               memcpy (&currprefs.cr[i], &changed_prefs.cr[i], sizeof (struct chipset_refresh));
+                               init_hz_full ();
+               }
+       }
+#if 0
        if (currprefs.chipset_refreshrate != changed_prefs.chipset_refreshrate) {
                currprefs.chipset_refreshrate = changed_prefs.chipset_refreshrate;
                init_hz_full ();
                return 1;
        }
+#endif
 
        if (currprefs.gfx_filter_autoscale != changed_prefs.gfx_filter_autoscale ||
                currprefs.gfx_xcenter_pos != changed_prefs.gfx_xcenter_pos ||
@@ -1695,6 +1711,8 @@ static int reopen (int full)
        currprefs.gfx_pfullscreen = changed_prefs.gfx_pfullscreen;
        currprefs.gfx_avsync = changed_prefs.gfx_avsync;
        currprefs.gfx_pvsync = changed_prefs.gfx_pvsync;
+       currprefs.gfx_avsyncmode = changed_prefs.gfx_avsyncmode;
+       currprefs.gfx_pvsyncmode = changed_prefs.gfx_pvsyncmode;
        currprefs.gfx_refreshrate = changed_prefs.gfx_refreshrate;
        config_changed = 1;
 
@@ -1709,40 +1727,18 @@ static int reopen (int full)
        return 0;
 }
 
-bool vsync_switchmode (int hz, int oldhz)
+bool vsync_switchmode (int hz)
 {
-       static bool tempvsync;
+       static struct PicassoResolution *oldmode;
+       static int oldhz;
        int w = currentmode->native_width;
        int h = currentmode->native_height;
        int d = currentmode->native_depth / 8;
        struct MultiDisplay *md = getdisplay (&currprefs);
        struct PicassoResolution *found;
        int newh, i, cnt;
-       int dbl = getvsyncrate (currprefs.chipset_refreshrate) != currprefs.chipset_refreshrate ? 2 : 1;
 
-       if (hz < 0)
-               return tempvsync;
-
-       newh = h * oldhz / hz;
-       hz = hz * dbl;
-
-       found = NULL;
-       for (i = 0; md->DisplayModes[i].depth >= 0 && !found; i++) {
-               struct PicassoResolution *r = &md->DisplayModes[i];
-               if (r->res.width == w && r->res.height == h && r->depth == d) {
-                       int j;
-                       for (j = 0; r->refresh[j] > 0; j++) {
-                               if (r->refresh[j] == oldhz) {
-                                       found = r;
-                                       break;
-                               }
-                       }
-               }
-       }
-       if (found == NULL) {
-               write_log (L"refresh rate changed to %d but original rate was not found\n", hz);
-               return 0;
-       }
+       newh = h * (currprefs.ntscmode ? 60 : 50) / hz;
 
        found = NULL;
        for (cnt = 0; cnt <= abs (newh - h) + 1 && !found; cnt++) {
@@ -1751,27 +1747,37 @@ bool vsync_switchmode (int hz, int oldhz)
                        if (r->res.width == w && (r->res.height == newh + cnt || r->res.height == newh - cnt) && r->depth == d) {
                                int j;
                                for (j = 0; r->refresh[j] > 0; j++) {
-                                       if (r->refresh[j] == hz) {
+                                       if (r->refresh[j] == hz || r->refresh[j] == hz * 2) {
                                                found = r;
+                                               hz = r->refresh[j];
                                                break;
                                        }
                                }
                        }
                }
        }
+       if (found == oldmode && hz == oldhz)
+               return true;
+       oldmode = found;
+       oldhz = hz;
        if (!found) {
-               tempvsync = currprefs.gfx_avsync != 0;
                changed_prefs.gfx_avsync = 0;
+               if (currprefs.gfx_avsync != changed_prefs.gfx_avsync) {
+                       config_changed = 1;
+               }
                write_log (L"refresh rate changed to %d but no matching screenmode found, vsync disabled\n", hz);
+               return false;
        } else {
                newh = found->res.height;
                changed_prefs.gfx_size_fs.height = newh;
                changed_prefs.gfx_refreshrate = hz;
-               write_log (L"refresh rate changed to %d, new screenmode %dx%d\n", hz, w, newh);
+               if (changed_prefs.gfx_size_fs.height != currprefs.gfx_size_fs.height ||
+                       changed_prefs.gfx_refreshrate != currprefs.gfx_refreshrate) {
+                       write_log (L"refresh rate changed to %d, new screenmode %dx%d\n", hz, w, newh);
+                       config_changed = 1;
+               }
+               return true;
        }
-       config_changed = 1;
-       reopen (1);
-       return 0;
 }
 
 #ifdef PICASSO96
@@ -2146,6 +2152,125 @@ static int getbestmode (int nextbest)
        return 0;
 }
 
+
+bool waitvblankstate (bool state)
+{
+       if (currprefs.gfx_api) {
+               return D3D_waitvblankstate (state);
+       } else {
+               return DirectDraw_waitvblankstate (state);
+       }
+}
+
+#include <process.h>
+static volatile int dummythread_die;
+int dummy_counter;
+static void _cdecl dummythread (void *dummy)
+{
+       SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_LOWEST);
+       while (!dummythread_die)
+               dummy_counter++;
+}
+
+double vblank_calibrate (bool waitonly)
+{
+       frame_time_t t1, t2;
+       double tsum, tsum2, tval, tfirst;
+       int maxcnt, maxtotal, total, cnt, tcnt2;
+       HANDLE th;
+       
+       if (remembered_vblank > 0)
+               return remembered_vblank;
+       if (waitonly) {
+               vblankbase = (syncbase / currprefs.chipset_refreshrate) * 3 / 4;
+               remembered_vblank = -1;
+               return -1;
+       }
+       th = GetCurrentThread ();
+       int oldpri = GetThreadPriority (th);
+       SetThreadPriority (th, THREAD_PRIORITY_HIGHEST);
+       dummythread_die = -1;
+       dummy_counter = 0;
+       _beginthread (&dummythread, 0, 0);
+       sleep_millis (100);
+       maxtotal = 10;
+       maxcnt = maxtotal;
+       tsum2 = 0;
+       tcnt2 = 0;
+       for (maxcnt = 0; maxcnt < maxtotal; maxcnt++) {
+               total = 10;
+               tsum = 0;
+               cnt = total;
+               for (cnt = 0; cnt < total; cnt++) {
+                       if (!waitvblankstate (true))
+                               return -1;
+                       if (!waitvblankstate (false))
+                               return -1;
+                       if (!waitvblankstate (true))
+                               return -1;
+                       t1 = read_processor_time ();
+                       if (!waitvblankstate (false))
+                               return -1;
+                       if (!waitvblankstate (true))
+                               return -1;
+                       t2 = read_processor_time ();
+                       tval = (double)syncbase / (t2 - t1);
+                       if (cnt == 0)
+                               tfirst = tval;
+                       if (abs (tval - tfirst) > 1) {
+                               write_log (L"very unstable vsync! %.6f vs %.6f, retrying..\n", tval, tfirst);
+                               break;
+                       }
+                       tsum2 += tval;
+                       tcnt2++;
+                       if (abs (tval - tfirst) > 0.1) {
+                               write_log (L"unstable vsync! %.6f vs %.6f\n", tval, tfirst);
+                               break;
+                       }
+                       tsum += tval;
+               }
+               if (cnt >= total)
+                       break;
+       }
+       dummythread_die = 0;
+       SetThreadPriority (th, oldpri);
+       if (maxcnt >= maxtotal) {
+               tsum = tsum2 / tcnt2;
+               write_log (L"unstable vsync reporting, using average value\n");
+       } else {
+               tsum /= total;
+       }
+       if (tsum >= 85)
+               tsum /= 2;
+       vblankbase = (syncbase / tsum) * 3 / 4;
+       write_log (L"VSync calibration: %.6fHz\n", tsum);
+       remembered_vblank = tsum;
+       return tsum;
+}
+
+bool vsync_busywait (void)
+{
+       bool v;
+       static frame_time_t prevtime;
+
+       if (currprefs.turbo_emulation)
+               return true;
+
+       while (read_processor_time () - prevtime < vblankbase)
+               sleep_millis (1);
+       v = false;
+       if (currprefs.gfx_api) {
+               v = D3D_vblank_busywait ();
+       } else {
+               v = DirectDraw_vblank_busywait ();
+       }
+       if (v) {
+               prevtime = read_processor_time ();
+               return true;
+       }
+       return false;
+}
+
 static int create_windows_2 (void)
 {
        static int firstwindow = 1;
@@ -2416,6 +2541,7 @@ static BOOL doInit (void)
        int tmp_depth;
        int ret = 0;
 
+       remembered_vblank = 0;
        if (wasfullwindow_a == 0)
                wasfullwindow_a = currprefs.gfx_afullscreen == GFX_FULLWINDOW ? 1 : -1;
        if (wasfullwindow_p == 0)
index 9fe40afae9ec6a51aafecea72784898e77156d68..d7eea63f995d0b97eb77f52964ef2faf592ca4d4 100644 (file)
@@ -33,6 +33,7 @@ extern void releasehdc (HDC hdc);
 extern void close_windows (void);
 extern void updatewinfsmode (struct uae_prefs *p);
 extern int is3dmode (void);
+extern int vblankbase;
 
 void DX_Fill (int dstx, int dsty, int width, int height, uae_u32 color);
 void DX_Blit (int x, int y, int w, int h);
index 50f75a2d8fea59e133947e9b3f09d6f05144dc84..d4f5cb8e9d53cd783d0371f174e8cae925ee2eae 100644 (file)
@@ -4434,19 +4434,19 @@ static void SetupRichText(HWND hDlg, urlinfo *url)
        CHARFORMAT CharFormat;
        CharFormat.cbSize = sizeof (CharFormat);
 
-       SetDlgItemText(hDlg, url->id, url->display);
-       SendDlgItemMessage(hDlg, url->id, EM_GETCHARFORMAT, 0, (LPARAM)&CharFormat);
+       SetDlgItemText (hDlg, url->id, url->display);
+       SendDlgItemMessage (hDlg, url->id, EM_GETCHARFORMAT, 0, (LPARAM)&CharFormat);
        CharFormat.dwMask   |= CFM_UNDERLINE | CFM_SIZE | CFM_FACE | CFM_COLOR;
        CharFormat.dwEffects = url->state ? CFE_UNDERLINE : 0;
        CharFormat.yHeight = 10 * 20; /* height in twips, where a twip is 1/20th of a point - for a pt.size of 18 */
 
-       CharFormat.crTextColor = GetSysColor(COLOR_ACTIVECAPTION);
+       CharFormat.crTextColor = GetSysColor (COLOR_ACTIVECAPTION);
        _tcscpy (CharFormat.szFaceName, os_vista ? L"Segoe UI" : L"Tahoma");
-       SendDlgItemMessage(hDlg, url->id, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CharFormat);
-       SendDlgItemMessage(hDlg, url->id, EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_3DFACE));
+       SendDlgItemMessage (hDlg, url->id, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&CharFormat);
+       SendDlgItemMessage (hDlg, url->id, EM_SETBKGNDCOLOR, 0, GetSysColor (COLOR_3DFACE));
 }
 
-static void url_handler(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+static void url_handler (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 {
        static int last_rectangle = -1;
        int i;
@@ -4459,35 +4459,35 @@ static void url_handler(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
        for (i = 0; urls[i].id >= 0; i++)
        {
                RECT rect;
-               GetWindowRect(GetDlgItem(hDlg, urls[i].id), &rect);
-               ScreenToClient(hDlg, (POINT*)&rect);
-               ScreenToClient(hDlg, (POINT*)&rect.right);
-               if(PtInRect(&rect, point))
+               GetWindowRect (GetDlgItem(hDlg, urls[i].id), &rect);
+               ScreenToClient (hDlg, (POINT*)&rect);
+               ScreenToClient (hDlg, (POINT*)&rect.right);
+               if (PtInRect (&rect, point))
                {
                        if(msg == WM_LBUTTONDOWN)
                        {
                                ShellExecute (NULL, NULL, urls[i].url , NULL, NULL, SW_SHOWNORMAL);
-                               SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));
+                               SetCursor (LoadCursor(NULL, MAKEINTRESOURCE (IDC_ARROW)));
                        }
                        else
                        {
                                if(i != last_rectangle)
                                {
                                        // try and load the system hand (Win2000+)
-                                       m_hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND) );
+                                       m_hCursor = LoadCursor (NULL, MAKEINTRESOURCE (IDC_HAND) );
                                        if (!m_hCursor)
                                        {
                                                // retry with our fallback hand
-                                               m_hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_MYHAND) );
+                                               m_hCursor = LoadCursor (hInst, MAKEINTRESOURCE (IDC_MYHAND) );
                                        }
-                                       SetCursor(m_hCursor);
+                                       SetCursor (m_hCursor);
                                        urls[i].state = TRUE;
-                                       SetupRichText(hDlg, &urls[i]);
+                                       SetupRichText (hDlg, &urls[i]);
 
                                        if(last_rectangle != -1)
                                        {
                                                urls[last_rectangle].state = FALSE;
-                                               SetupRichText(hDlg, &urls[last_rectangle]);
+                                               SetupRichText (hDlg, &urls[last_rectangle]);
                                        }
                                }
                        }
@@ -4499,9 +4499,9 @@ static void url_handler(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
 
        if(!found && last_rectangle >= 0)
        {
-               SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));
+               SetCursor (LoadCursor (NULL, MAKEINTRESOURCE (IDC_ARROW)));
                urls[last_rectangle].state = FALSE;
-               SetupRichText(hDlg, &urls[last_rectangle]);
+               SetupRichText (hDlg, &urls[last_rectangle]);
                last_rectangle = -1;
        }
 }
@@ -5302,11 +5302,10 @@ static void enable_for_displaydlg (HWND hDlg)
        rtg = FALSE;
 #endif
        ew (hDlg, IDC_SCREENMODE_RTG, rtg);
+       ew (hDlg, IDC_SCREENMODE_RTG2, rtg);
        ew (hDlg, IDC_XCENTER, TRUE);
        ew (hDlg, IDC_YCENTER, TRUE);
        ew (hDlg, IDC_LM_SCANLINES, TRUE);
-       ew (hDlg, IDC_FRAMERATE2, !workprefs.gfx_avsync);
-       ew (hDlg, IDC_RATE2TEXT, !workprefs.gfx_avsync);
        ew (hDlg, IDC_FRAMERATE, !workprefs.cpu_cycle_exact);
        ew (hDlg, IDC_LORES, !workprefs.gfx_autoresolution);
        ew (hDlg, IDC_LM_NORMAL, !workprefs.gfx_autoresolution);
@@ -5332,14 +5331,6 @@ static void enable_for_chipsetdlg (HWND hDlg)
        ew (hDlg, IDC_CS_EXT, workprefs.cs_compatible ? TRUE : FALSE);
 }
 
-static DWORD idnth[] = { IDS_SECOND, IDS_THIRD, IDS_FOURTH, IDS_FIFTH, IDS_SIXTH, IDS_SEVENTH, IDS_EIGHTH, IDS_NINTH, IDS_TENTH, -1 };
-static void LoadNthString( DWORD value, TCHAR *nth, DWORD dwNthMax )
-{
-       nth[0] = 0;
-       if (value >= 1 && value <= 9)
-               WIN32GUI_LoadUIString(idnth[value - 1], nth, dwNthMax);
-}
-
 static int fakerefreshrates[] = { 50, 60, 100, 120, 0 };
 struct storedrefreshrate
 {
@@ -5572,6 +5563,7 @@ static void init_display_mode (HWND hDlg)
 
 }
 
+#if 0
 static int display_toselect (int fs, int vsync, int p96)
 {
        if (p96)
@@ -5634,13 +5626,12 @@ static void display_fromselect (int val, int *fs, int *vsync, int p96)
                break;
        }
 }
+#endif
 
 static void values_to_displaydlg (HWND hDlg)
 {
        TCHAR buffer[MAX_DPATH], buffer2[MAX_DPATH];
-       TCHAR Nth[MAX_NTH_LENGTH];
-       TCHAR *blah[1] = { Nth };
-       TCHAR *string = NULL;
+       int rates[MAX_CHIPSET_REFRESH_TOTAL];
        int v;
        double d;
 
@@ -5649,71 +5640,90 @@ static void values_to_displaydlg (HWND hDlg)
        SetDlgItemInt (hDlg, IDC_XSIZE, workprefs.gfx_size_win.width, FALSE);
        SetDlgItemInt (hDlg, IDC_YSIZE, workprefs.gfx_size_win.height, FALSE);
 
-       d = workprefs.chipset_refreshrate;
-       if (abs (d) < 1)
-               d = currprefs.ntscmode ? 60.0 : 50.0;
-       SendDlgItemMessage (hDlg, IDC_FRAMERATE2, TBM_SETPOS, TRUE, (LPARAM)d);
-       _stprintf (buffer, L"%0.6f", d);
+       SendDlgItemMessage(hDlg, IDC_RATE2BOX, CB_RESETCONTENT, 0, 0);
+       v = 0;
+       for (int i = 0; i < MAX_CHIPSET_REFRESH_TOTAL; i++) {
+               struct chipset_refresh *cr = &workprefs.cr[i];
+               if (cr->rate > 0) {
+                       _tcscpy (buffer, cr->label);
+                       if (!buffer[0])
+                               _stprintf (buffer, L"%d", i);
+                       SendDlgItemMessage(hDlg, IDC_RATE2BOX, CB_ADDSTRING, 0, (LPARAM)buffer);
+                       d = workprefs.chipset_refreshrate;
+                       if (abs (d) < 1)
+                               d = currprefs.ntscmode ? 60.0 : 50.0;
+                       if (abs (cr->rate - d) < 1.5 && workprefs.cr_selected < 0) {
+                               workprefs.cr_selected = i;
+                       }
+                       rates[i] = v;
+                       v++;
+               }
+       }
+       if (workprefs.cr_selected < 0 || workprefs.cr[workprefs.cr_selected].rate <= 0)
+               workprefs.cr_selected = CHIPSET_REFRESH_PAL;
+       struct chipset_refresh *selectcr = &workprefs.cr[workprefs.cr_selected];
+       SendDlgItemMessage(hDlg, IDC_RATE2BOX, CB_SETCURSEL, rates[workprefs.cr_selected], 0);
+       SendDlgItemMessage (hDlg, IDC_FRAMERATE2, TBM_SETPOS, TRUE, (LPARAM)(selectcr->rate + 0.5));
+       _stprintf (buffer, L"%.6f", selectcr->locked || full_property_sheet ? selectcr->rate : workprefs.chipset_refreshrate);
        SetDlgItemText (hDlg, IDC_RATE2TEXT, buffer);
+       CheckDlgButton (hDlg, IDC_RATE2ENABLE, selectcr->locked);
+       ew (hDlg, IDC_RATE2TEXT, selectcr->locked != 0);
+       ew (hDlg, IDC_FRAMERATE2, selectcr->locked != 0);
 
        v = workprefs.cpu_cycle_exact ? 1 : workprefs.gfx_framerate;
        SendDlgItemMessage (hDlg, IDC_FRAMERATE, TBM_SETPOS, TRUE, (int)v);
-       WIN32GUI_LoadUIString(IDS_FRAMERATE, buffer, sizeof buffer / sizeof (TCHAR));
-       LoadNthString (v - 1, Nth, MAX_NTH_LENGTH);
-       if (FormatMessage (FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
-               buffer, 0, 0, (LPTSTR)&string, MAX_FRAMERATE_LENGTH + MAX_NTH_LENGTH, (va_list *)blah ) == 0)
-       {
-               DWORD dwLastError = GetLastError();
-               _stprintf (buffer, L"Every %s Frame", nth[v - 1]);
-               SetDlgItemText(hDlg, IDC_RATETEXT, buffer);
-       } else {
-               SetDlgItemText( hDlg, IDC_RATETEXT, string);
-               LocalFree(string);
-       }
 
        CheckRadioButton (hDlg, IDC_LM_NORMAL, IDC_LM_SCANLINES, IDC_LM_NORMAL + workprefs.gfx_vresolution + (workprefs.gfx_scanlines ? 1 : 0));
 
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_RESETCONTENT, 0, 0);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE2, CB_RESETCONTENT, 0, 0);
 
        WIN32GUI_LoadUIString(IDS_SCREEN_WINDOWED, buffer, sizeof buffer / sizeof (TCHAR));
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_ADDSTRING, 0, (LPARAM)buffer);
-
        WIN32GUI_LoadUIString(IDS_SCREEN_FULLSCREEN, buffer, sizeof buffer / sizeof (TCHAR));
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_ADDSTRING, 0, (LPARAM)buffer);
-
-       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC, buffer2, sizeof buffer2 / sizeof (TCHAR));
-       _stprintf (buffer + _tcslen (buffer), L" + %s", buffer2);
-       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_ADDSTRING, 0, (LPARAM)buffer);
-
-       WIN32GUI_LoadUIString(IDS_SCREEN_FULLSCREEN, buffer, sizeof buffer / sizeof (TCHAR));
-       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC_AUTOSWITCH, buffer2, sizeof buffer2 / sizeof (TCHAR));
-       _stprintf (buffer + _tcslen (buffer), L" + %s", buffer2);
-       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_ADDSTRING, 0, (LPARAM)buffer);
-
-
        WIN32GUI_LoadUIString(IDS_SCREEN_FULLWINDOW, buffer, sizeof buffer / sizeof (TCHAR));
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_ADDSTRING, 0, (LPARAM)buffer);
 
-       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_SETCURSEL,
-               display_toselect (workprefs.gfx_afullscreen, workprefs.gfx_avsync, 0), 0);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE2, CB_ADDSTRING, 0, (LPARAM)L"-");
+       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC, buffer, sizeof buffer / sizeof (TCHAR));
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE2, CB_ADDSTRING, 0, (LPARAM)buffer);
+       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC_AUTOSWITCH, buffer, sizeof buffer / sizeof (TCHAR));
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE2, CB_ADDSTRING, 0, (LPARAM)buffer);
+       _tcscpy(buffer2, L" (new)");
+       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC, buffer, sizeof buffer / sizeof (TCHAR));
+       _tcscat (buffer, buffer2);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE2, CB_ADDSTRING, 0, (LPARAM)buffer);
+       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC_AUTOSWITCH, buffer, sizeof buffer / sizeof (TCHAR));
+       _tcscat (buffer, buffer2);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE2, CB_ADDSTRING, 0, (LPARAM)buffer);
 
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE, CB_SETCURSEL,
+               workprefs.gfx_afullscreen, 0);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_NATIVE2, CB_SETCURSEL,
+               workprefs.gfx_avsync == 0 ? 0 : workprefs.gfx_avsync + workprefs.gfx_avsyncmode * 2, 0);
 
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_RESETCONTENT, 0, 0);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG2, CB_RESETCONTENT, 0, 0);
+
        WIN32GUI_LoadUIString(IDS_SCREEN_WINDOWED, buffer, sizeof buffer / sizeof (TCHAR));
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_ADDSTRING, 0, (LPARAM)buffer);
-       _stprintf (buffer + _tcslen (buffer), L" + %s", buffer2);
-       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_ADDSTRING, 0, (LPARAM)buffer);
        WIN32GUI_LoadUIString(IDS_SCREEN_FULLSCREEN, buffer, sizeof buffer / sizeof (TCHAR));
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_ADDSTRING, 0, (LPARAM)buffer);
-       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC, buffer2, sizeof buffer2 / sizeof (TCHAR));
-       _stprintf (buffer + _tcslen (buffer), L" + %s", buffer2);
-       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_ADDSTRING, 0, (LPARAM)buffer);
        WIN32GUI_LoadUIString(IDS_SCREEN_FULLWINDOW, buffer, sizeof buffer / sizeof (TCHAR));
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_ADDSTRING, 0, (LPARAM)buffer);
-       _stprintf (buffer + _tcslen (buffer), L" + %s", buffer2);
-       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_ADDSTRING, 0, (LPARAM)buffer);
+
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG2, CB_ADDSTRING, 0, (LPARAM)L"-");
+       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC, buffer, sizeof buffer / sizeof (TCHAR));
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG2, CB_ADDSTRING, 0, (LPARAM)buffer);
+       WIN32GUI_LoadUIString(IDS_SCREEN_VSYNC, buffer, sizeof buffer / sizeof (TCHAR));
+       _tcscat (buffer, buffer2);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG2, CB_ADDSTRING, 0, (LPARAM)buffer);
+
        SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG, CB_SETCURSEL,
-               display_toselect (workprefs.gfx_pfullscreen, workprefs.gfx_pvsync, 1), 0);
+               workprefs.gfx_pfullscreen, 0);
+       SendDlgItemMessage(hDlg, IDC_SCREENMODE_RTG2, CB_SETCURSEL,
+               workprefs.gfx_pvsync == 0 ? 0 : workprefs.gfx_pvsync + workprefs.gfx_pvsyncmode, 0);
 
        SendDlgItemMessage(hDlg, IDC_LORES, CB_RESETCONTENT, 0, 0);
        WIN32GUI_LoadUIString(IDS_RES_LORES, buffer, sizeof buffer / sizeof (TCHAR));
@@ -5783,73 +5793,92 @@ static void values_from_displaydlg (HWND hDlg, UINT msg, WPARAM wParam, LPARAM l
        int gfx_width = workprefs.gfx_size_win.width;
        int gfx_height = workprefs.gfx_size_win.height;
        LRESULT posn;
-       bool updaterate = false;
        TCHAR tmp[200];
 
-       display_fromselect (SendDlgItemMessage (hDlg, IDC_SCREENMODE_NATIVE, CB_GETCURSEL, 0, 0),
-               &workprefs.gfx_afullscreen, &workprefs.gfx_avsync, 0);
-       display_fromselect (SendDlgItemMessage (hDlg, IDC_SCREENMODE_RTG, CB_GETCURSEL, 0, 0),
-               &workprefs.gfx_pfullscreen, &workprefs.gfx_pvsync, 1);
+       workprefs.gfx_afullscreen = SendDlgItemMessage (hDlg, IDC_SCREENMODE_NATIVE, CB_GETCURSEL, 0, 0);
+       i = SendDlgItemMessage (hDlg, IDC_SCREENMODE_NATIVE2, CB_GETCURSEL, 0, 0);
+       workprefs.gfx_avsync = 0;
+       workprefs.gfx_avsyncmode = 0;
+       if (i > 0) {
+               i--;
+               workprefs.gfx_avsync = (i & 1) + 1;
+               workprefs.gfx_avsyncmode = i >= 2 ? 1 : 0;
+       }
 
-       workprefs.gfx_lores_mode     = ischecked (hDlg, IDC_LORES_SMOOTHED);
-       workprefs.gfx_scandoubler     = ischecked (hDlg, IDC_FLICKERFIXER);
+       workprefs.gfx_pfullscreen = SendDlgItemMessage (hDlg, IDC_SCREENMODE_RTG, CB_GETCURSEL, 0, 0);
+       i = SendDlgItemMessage (hDlg, IDC_SCREENMODE_RTG2, CB_GETCURSEL, 0, 0);
+       workprefs.gfx_pvsync = 0;
+       workprefs.gfx_pvsyncmode = 0;
+       if (i > 0) {
+               i--;
+               workprefs.gfx_pvsync = 1;
+               workprefs.gfx_pvsyncmode = i >= 1 ? 1 : 0;
+       }
+       
+       workprefs.gfx_lores_mode = ischecked (hDlg, IDC_LORES_SMOOTHED);
+       workprefs.gfx_scandoubler = ischecked (hDlg, IDC_FLICKERFIXER);
        workprefs.gfx_blackerthanblack = ischecked (hDlg, IDC_BLACKER_THAN_BLACK);
        workprefs.gfx_vresolution = (ischecked (hDlg, IDC_LM_DOUBLED) || ischecked (hDlg, IDC_LM_SCANLINES)) ? VRES_DOUBLE : VRES_NONDOUBLE;
        workprefs.gfx_scanlines = ischecked (hDlg, IDC_LM_SCANLINES);   
        workprefs.gfx_backbuffers = SendDlgItemMessage (hDlg, IDC_DISPLAY_BUFFERCNT, CB_GETCURSEL, 0, 0);
        workprefs.gfx_framerate = SendDlgItemMessage (hDlg, IDC_FRAMERATE, TBM_GETPOS, 0, 0);
 
-       if (msg == WM_HSCROLL) {
-               i = SendDlgItemMessage (hDlg, IDC_FRAMERATE2, TBM_GETPOS, 0, 0);
-               if (i != (int)workprefs.chipset_refreshrate)
-                       workprefs.chipset_refreshrate = (double)i;
-               updaterate = true;
-       } else if (LOWORD (wParam) == IDC_RATE2TEXT && HIWORD (wParam) == EN_KILLFOCUS) {
-               if (GetDlgItemText(hDlg, IDC_RATE2TEXT, tmp, sizeof tmp / sizeof (TCHAR))) {
-                       workprefs.chipset_refreshrate = _tstof (tmp);
-                       SendDlgItemMessage (hDlg, IDC_FRAMERATE2, TBM_SETPOS, TRUE, (LPARAM)workprefs.chipset_refreshrate);
+       bool updaterate = false, updateslider = false;
+       TCHAR label[16];
+       label[0] = 0;
+       SendDlgItemMessage (hDlg, IDC_RATE2BOX, WM_GETTEXT, sizeof label / sizeof (TCHAR), (LPARAM)label);
+       struct chipset_refresh *cr;
+       for (i = 0; i < MAX_CHIPSET_REFRESH_TOTAL; i++) {
+               cr = &workprefs.cr[i];
+               if (!_tcscmp (label, cr->label) || (cr->label[0] == 0 && _tstol (label) == i)) {
+                       if (workprefs.cr_selected != i) {
+                               workprefs.cr_selected = i;
+                               updaterate = true;
+                               updateslider = true;
+                       }
+                       break;
+               }
+       }
+       cr = &workprefs.cr[workprefs.cr_selected];
+       cr->locked = ischecked (hDlg, IDC_RATE2ENABLE) != 0;
+       if (!cr->locked) {
+               if (msg == WM_HSCROLL) {
+                       i = SendDlgItemMessage (hDlg, IDC_FRAMERATE2, TBM_GETPOS, 0, 0);
+                       if (i != (int)cr->rate)
+                               cr->rate = (double)i;
                        updaterate = true;
+               } else if (LOWORD (wParam) == IDC_RATE2TEXT && HIWORD (wParam) == EN_KILLFOCUS) {
+                       if (GetDlgItemText(hDlg, IDC_RATE2TEXT, tmp, sizeof tmp / sizeof (TCHAR))) {
+                               cr->rate = _tstof (tmp);
+                               updaterate = true;
+                               updateslider = true;
+                       }
+               }
+               if (cr->rate > 0 && cr->rate < 1) {
+                       cr->rate = currprefs.ntscmode ? 60.0 : 50.0;
+                       updaterate = true;
+               }
+               if (cr->rate > 300) {
+                       cr->rate = currprefs.ntscmode ? 60.0 : 50.0;
+                       updaterate = true;
+               }
+               if (updaterate) {
+                       TCHAR buffer[20];
+                       _stprintf (buffer, L"%.6f", cr->rate);
+                       SetDlgItemText (hDlg, IDC_RATE2TEXT, buffer);
+               }
+               if (updateslider) {
+                       SendDlgItemMessage (hDlg, IDC_FRAMERATE2, TBM_SETPOS, TRUE, (LPARAM)cr->rate);
                }
        }
 
-       if (workprefs.chipset_refreshrate > 0 && workprefs.chipset_refreshrate < 1) {
-               workprefs.chipset_refreshrate = currprefs.ntscmode ? 60.0 : 50.0;
-               updaterate = true;
-       }
-       if (workprefs.chipset_refreshrate > 300) {
-               workprefs.chipset_refreshrate = currprefs.ntscmode ? 60.0 : 50.0;
-               updaterate = true;
-       }
-
-       TCHAR buffer[MAX_FRAMERATE_LENGTH];
-       TCHAR Nth[MAX_NTH_LENGTH];
-       TCHAR *blah[1] = { Nth };
-       TCHAR *string = NULL;
-
-       WIN32GUI_LoadUIString(IDS_FRAMERATE, buffer, MAX_FRAMERATE_LENGTH);
-       LoadNthString(workprefs.gfx_framerate - 1, Nth, MAX_NTH_LENGTH);
-       if(FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
-               buffer, 0, 0, (LPTSTR)&string, MAX_FRAMERATE_LENGTH + MAX_NTH_LENGTH, (va_list *)blah ) == 0) {
-               DWORD dwLastError = GetLastError();
-               _stprintf (buffer, L"Every %s Frame", nth[workprefs.gfx_framerate - 1]);
-               SetDlgItemText(hDlg, IDC_RATETEXT, buffer);
-       } else {
-               SetDlgItemText(hDlg, IDC_RATETEXT, string);
-               LocalFree(string);
-       }
-       if (updaterate) {
-               _stprintf (buffer, L"%.06f", workprefs.chipset_refreshrate);
-               SetDlgItemText (hDlg, IDC_RATE2TEXT, buffer);
-       }
-       workprefs.gfx_size_win.width  = GetDlgItemInt (hDlg, IDC_XSIZE, &success, FALSE);
+       workprefs.gfx_size_win.width = GetDlgItemInt (hDlg, IDC_XSIZE, &success, FALSE);
        if(!success)
                workprefs.gfx_size_win.width = 800;
        workprefs.gfx_size_win.height = GetDlgItemInt (hDlg, IDC_YSIZE, &success, FALSE);
        if(!success)
                workprefs.gfx_size_win.height = 600;
 
-       if (DBLEQU (workprefs.chipset_refreshrate, currprefs.ntscmode ? 60.0 : 50.0))
-               workprefs.chipset_refreshrate = 0.0;
        workprefs.gfx_xcenter = ischecked (hDlg, IDC_XCENTER) ? 2 : 0; /* Smart centering */
        workprefs.gfx_ycenter = ischecked (hDlg, IDC_YCENTER) ? 2 : 0; /* Smart centering */
        workprefs.gfx_autoresolution = ischecked (hDlg, IDC_AUTORESOLUTION);
@@ -5982,6 +6011,9 @@ static INT_PTR CALLBACK DisplayDlgProc (HWND hDlg, UINT msg, WPARAM wParam, LPAR
                handle_da (hDlg);
                values_from_displaydlg (hDlg, msg, wParam, lParam);
                enable_for_displaydlg (hDlg);
+               if (LOWORD (wParam) == IDC_RATE2ENABLE) {
+                       values_to_displaydlg (hDlg);
+               }
                recursive--;
                break;
 
index 202ff92108f36a53a440b0d1ace161555fb06cef..b964916c42bcf3b02a61216a6e99f3a1c18ae30b 100644 (file)
@@ -1,4 +1,33 @@
 
+- new vsync option
+  * does not need extra buffers (very little input lag), make sure "no buffering"
+    is selected
+  * works also in windowed/fullwindow mode
+  * attemps to autodetect exact refresh rate, ignores what Windows/driver reports
+  * less busy waiting, no more continuous 100% CPU usage
+  * much more stable than "normal" vsync
+  - old vsync is currently always forced to new mode
+  Usual vsync restrictions still apply: do not use fastest possible modes, 50Hz capable
+  display and driver required if you want proper PAL speed.
+  Old vsync support may disappear later.
+- keep old joystick Gameports setting if no joysticks detected when loading config file
+- another method to enable unconfigured joystick: quick firebutton press = joystick port,
+  long press = mouse port
+- unconfigured joystick move left/right can also be used to enable joystick
+- DirectDraw and 2x filter = non-centered image, broke few releases ago
+- IDE multiple sector writes fixed (2.3.2 final fixed spurious interrupts but fix wasn't
+  complete, last interrupt didn't always arrive causing driver to hang)
+- set IDE CHS/LBA address after read/write command has finished as explained in ATA-1
+  spec (last accessed sector, not next sector). This is undefined in later ATA spec
+  versions, but AOS IDE driver >256 sector transfers corrupt data if this is not
+  handled exactly as ATA-1 spec says. (Infamous "max transfer problem")
+- detect and log split >256 block IDE transfers
+- only load OFS from RDB if partition and RDB filesystem dostype matches exactly
+- create _winuae.fx only if full D3D plugin path exists
+- A590/A2091 boot ROM was mapped non-executable, crashed on 68020+ CPUs
+
+2.3.2
+
 Beta 8 (RC1):
 
 - always disable FPU when using GUI to change CPU to 68000 or 68010