From fd99fe2d4eaa20de6105873eb1aa04bb92eb185a Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sat, 18 Jun 2011 17:20:44 +0300 Subject: [PATCH] 2340b1 --- a2091.cpp | 21 +- cfgfile.cpp | 204 +- custom.cpp | 119 +- custom2.cpp | 7410 ++++++++++++++++++++++++++++++++++ filesys.cpp | 5 +- gayle.cpp | 64 +- include/gfxfilter.h | 3 + include/options.h | 24 +- include/xwin.h | 5 +- inputdevice.cpp | 30 +- main.cpp | 1 - od-win32/direct3d.cpp | 74 +- od-win32/direct3d.h | 2 + od-win32/dxwrap.cpp | 26 + od-win32/dxwrap.h | 2 + od-win32/picasso96_win.cpp | 6 +- od-win32/resources/resource | 7 +- od-win32/resources/winuae.rc | 27 +- od-win32/win32.cpp | 5 +- od-win32/win32.h | 6 +- od-win32/win32_scaler.cpp | 10 +- od-win32/win32gfx.cpp | 198 +- od-win32/win32gfx.h | 1 + od-win32/win32gui.cpp | 268 +- od-win32/winuaechangelog.txt | 29 + 25 files changed, 8292 insertions(+), 255 deletions(-) create mode 100644 custom2.cpp diff --git a/a2091.cpp b/a2091.cpp index 9b1a6534..7782959d 100644 --- 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 { diff --git a/cfgfile.cpp b/cfgfile.cpp index 613a41d5..2921e9bb 100644 --- a/cfgfile.cpp +++ b/cfgfile.cpp @@ -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); diff --git a/custom.cpp b/custom.cpp index 3bea78d6..410a78d9 100644 --- a/custom.cpp +++ b/custom.cpp @@ -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 index 00000000..72e15a81 --- /dev/null +++ b/custom2.cpp @@ -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 +#include + +#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 (¤t_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, ¤t_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], ¤t_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 diff --git a/filesys.cpp b/filesys.cpp index 5b3fab6c..f1083f98 100644 --- a/filesys.cpp +++ b/filesys.cpp @@ -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]; diff --git a/gayle.cpp b/gayle.cpp index 4baba08f..223206ec 100644 --- 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) diff --git a/include/gfxfilter.h b/include/gfxfilter.h index 92129c18..36b099a0 100644 --- a/include/gfxfilter.h +++ b/include/gfxfilter.h @@ -1,4 +1,7 @@ +#define AMIGA_WIDTH_MAX (752 / 2) +#define AMIGA_HEIGHT_MAX (574 / 2) + #ifdef GFXFILTER typedef unsigned char u8; diff --git a/include/options.h b/include/options.h index bbbbe00e..cc109bfa 100644 --- a/include/options.h +++ b/include/options.h @@ -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); diff --git a/include/xwin.h b/include/xwin.h index 883b8b28..e01aac67 100644 --- a/include/xwin.h +++ b/include/xwin.h @@ -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); diff --git a/inputdevice.cpp b/inputdevice.cpp index a4e63a33..56178406 100644 --- a/inputdevice.cpp +++ b/inputdevice.cpp @@ -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) diff --git a/main.cpp b/main.cpp index 64fb4d74..9a68e099 100644 --- 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; diff --git a/od-win32/direct3d.cpp b/od-win32/direct3d.cpp index 7d32d8b3..df64a04b 100644 --- a/od-win32/direct3d.cpp +++ b/od-win32/direct3d.cpp @@ -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) diff --git a/od-win32/direct3d.h b/od-win32/direct3d.h index 04d5d64c..91dfb22e 100644 --- a/od-win32/direct3d.h +++ b/od-win32/direct3d.h @@ -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 diff --git a/od-win32/dxwrap.cpp b/od-win32/dxwrap.cpp index 9b8f707f..1410f75b 100644 --- a/od-win32/dxwrap.cpp +++ b/od-win32/dxwrap.cpp @@ -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; +} diff --git a/od-win32/dxwrap.h b/od-win32/dxwrap.h index a2ff8b44..23af467d 100644 --- a/od-win32/dxwrap.h +++ b/od-win32/dxwrap.h @@ -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); diff --git a/od-win32/picasso96_win.cpp b/od-win32/picasso96_win.cpp index bd65b3d7..b56ec719 100644 --- a/od-win32/picasso96_win.cpp +++ b/od-win32/picasso96_win.cpp @@ -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) diff --git a/od-win32/resources/resource b/od-win32/resources/resource index 4e4a3d6f..e2543b6f 100644 --- a/od-win32/resources/resource +++ b/od-win32/resources/resource @@ -392,8 +392,10 @@ #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 @@ -430,11 +432,13 @@ #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 @@ -450,7 +454,6 @@ #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 diff --git a/od-win32/resources/winuae.rc b/od-win32/resources/winuae.rc index 1fa7c7fe..2e108c07 100644 --- a/od-win32/resources/winuae.rc +++ b/od-win32/resources/winuae.rc @@ -123,8 +123,7 @@ BEGIN GROUPBOX "Settings",IDC_SETTINGSTEXT,4,73,209,137 CONTROL "Blacker than black",IDC_BLACKER_THAN_BLACK,"Button",BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,11,125,92,10 LTEXT "Refresh:",IDC_REFRESHTEXT,11,173,28,8 - CONTROL "Slider1",IDC_FRAMERATE,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,168,75,20 - EDITTEXT IDC_RATETEXT,123,172,79,12,ES_CENTER | ES_READONLY + CONTROL "Slider1",IDC_FRAMERATE,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,168,99,20 GROUPBOX "Centering",IDC_STATIC,221,73,75,49 CONTROL "Horizontal",IDC_XCENTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,87,49,10 CONTROL "Vertical",IDC_YCENTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,229,103,49,10 @@ -135,14 +134,14 @@ BEGIN COMBOBOX IDC_DA_MODE,4,218,58,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP CONTROL "",IDC_DA_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,67,214,101,20 LTEXT "FPS adj.:",IDC_REFRESH2TEXT,9,193,32,8 - CONTROL "",IDC_FRAMERATE2,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,188,109,20 - EDITTEXT IDC_RATE2TEXT,156,192,46,12,ES_AUTOHSCROLL + CONTROL "",IDC_FRAMERATE2,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOP | WS_TABSTOP,40,188,99,20 + EDITTEXT IDC_RATE2TEXT,143,192,41,12,ES_AUTOHSCROLL COMBOBOX IDC_RESOLUTIONDEPTH,134,27,46,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP CONTROL "Filtered low resolution",IDC_LORES_SMOOTHED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,125,89,10 - COMBOBOX IDC_SCREENMODE_NATIVE,103,85,102,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP - COMBOBOX IDC_SCREENMODE_RTG,103,103,102,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP - RTEXT "Native mode:",IDC_STATIC,19,85,59,15,SS_CENTERIMAGE - RTEXT "RTG mode:",IDC_STATIC,19,101,59,15,SS_CENTERIMAGE + COMBOBOX IDC_SCREENMODE_NATIVE,57,85,67,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SCREENMODE_RTG,57,103,67,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + RTEXT "Native mode:",IDC_STATIC,9,85,44,15,SS_CENTERIMAGE + RTEXT "RTG mode:",IDC_STATIC,8,101,45,15,SS_CENTERIMAGE PUSHBUTTON "Reset to defaults",IDC_DA_RESET,221,218,75,14 RTEXT "Resolution:",IDC_STATIC,27,152,59,15,SS_CENTERIMAGE COMBOBOX IDC_LORES,103,152,102,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP @@ -152,6 +151,10 @@ BEGIN COMBOBOX IDC_DISPLAY_BUFFERCNT,187,47,97,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP CONTROL "Resolution autoswitch",IDC_AUTORESOLUTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,120,139,89,10 EDITTEXT IDC_DA_TEXT,167,218,46,12,ES_AUTOHSCROLL | ES_READONLY + COMBOBOX IDC_SCREENMODE_NATIVE2,129,85,78,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SCREENMODE_RTG2,129,103,78,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_RATE2BOX,143,173,60,150,CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_RATE2ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,194,193,8,10 END IDD_MEMORY DIALOGEX 0, 0, 300, 182 @@ -1061,8 +1064,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,3,2,0 - PRODUCTVERSION 2,3,2,0 + FILEVERSION 2,3,3,0 + PRODUCTVERSION 2,3,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -1078,12 +1081,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "WinUAE" - VALUE "FileVersion", "2.3.2.0" + VALUE "FileVersion", "2.3.3.0" VALUE "InternalName", "WinUAE" VALUE "LegalCopyright", "© 1996-2011 under the GNU Public License (GPL)" VALUE "OriginalFilename", "WinUAE.exe" VALUE "ProductName", "WinUAE" - VALUE "ProductVersion", "2.3.2.0" + VALUE "ProductVersion", "2.3.3.0" END END BLOCK "VarFileInfo" diff --git a/od-win32/win32.cpp b/od-win32/win32.cpp index 711a68ac..970d8e75 100644 --- a/od-win32/win32.cpp +++ b/od-win32/win32.cpp @@ -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 diff --git a/od-win32/win32.h b/od-win32/win32.h index e7c9e614..dbeca083 100644 --- a/od-win32/win32.h +++ b/od-win32/win32.h @@ -15,11 +15,11 @@ #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"" diff --git a/od-win32/win32_scaler.cpp b/od-win32/win32_scaler.cpp index ddb8c391..d1e120da 100644 --- a/od-win32/win32_scaler.cpp +++ b/od-win32/win32_scaler.cpp @@ -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); diff --git a/od-win32/win32gfx.cpp b/od-win32/win32gfx.cpp index 53ca29b4..0e864d17 100644 --- a/od-win32/win32gfx.cpp +++ b/od-win32/win32gfx.cpp @@ -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 = ¤tmodestruct; 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 +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) diff --git a/od-win32/win32gfx.h b/od-win32/win32gfx.h index 9fe40afa..d7eea63f 100644 --- a/od-win32/win32gfx.h +++ b/od-win32/win32gfx.h @@ -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); diff --git a/od-win32/win32gui.cpp b/od-win32/win32gui.cpp index 50f75a2d..d4f5cb8e 100644 --- a/od-win32/win32gui.cpp +++ b/od-win32/win32gui.cpp @@ -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; diff --git a/od-win32/winuaechangelog.txt b/od-win32/winuaechangelog.txt index 202ff921..b964916c 100644 --- a/od-win32/winuaechangelog.txt +++ b/od-win32/winuaechangelog.txt @@ -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 -- 2.47.3