diff --git a/src/custom.cpp b/src/custom.cpp index 19c79f2b..c7abaa2e 100644 --- a/src/custom.cpp +++ b/src/custom.cpp @@ -1,6055 +1,6077 @@ - /* - * 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 "memory.h" -#include "custom.h" -#include "newcpu.h" -#include "cia.h" -#include "disk.h" -#include "savestate.h" -#include "blitter.h" -#include "xwin.h" -#include "inputdevice.h" -#include "keybuf.h" -#include "autoconf.h" -#include "traps.h" -#include "gui.h" -#include "picasso96.h" -#include "drawing.h" -#include "akiko.h" -#include "gfxboard.h" -#include "blkdev.h" - -#define SPR0_HPOS 0x15 -#define MAX_SPRITES 8 - -#define nocustom() (picasso_on) - -extern int screen_is_picasso; - -unsigned long last_synctime = 0; -static int frh_count = 0; -#define LAST_SPEEDUP_LINE 30 -#define SPEEDUP_CYCLES_JIT 2800 -#define SPEEDUP_CYCLES_NONJIT 600 -#define SPEEDUP_TIMELIMIT_JIT -1500 -#define SPEEDUP_TIMELIMIT_NONJIT -2000 -int pissoff_value = SPEEDUP_CYCLES_JIT * CYCLE_UNIT; -int speedup_timelimit = SPEEDUP_TIMELIMIT_JIT; - -/* Events */ - -static int rpt_did_reset; -struct ev eventtab[ev_max]; -struct ev2 eventtab2[ev2_max]; - -int vpos; -static int vpos_count, vpos_count_diff; -static int lof_store; // real bit in custom registers -static int lof_current; // what display device thinks -static int next_lineno, prev_lineno; -static int lof_changed = 0, lof_changing = 0; -static int lof_changed_previous_field; -static int vposw_change; -static bool lof_lace; - -#define LOF_TOGGLES_NEEDED 3 - -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]; - -/* - * Hardware registers of all sorts. - */ -static int REGPARAM3 custom_wput_1 (int, uaecptr, uae_u32, int) REGPARAM; - -static uae_u16 intena; -uae_u16 intreq; -uae_u16 dmacon; -uae_u16 adkcon; /* used by audio code */ -static uae_u32 last_custom_value1; - - -static uae_u32 cop1lc,cop2lc,copcon; - -int maxhpos = MAXHPOS_PAL; -int maxvpos = MAXVPOS_PAL; -int maxvpos_nom = MAXVPOS_PAL; // nominal value (same as maxvpos but "faked" maxvpos in fake 60hz modes) -int maxvpos_display = MAXVPOS_PAL; // value used for display size -static int maxvpos_total = 511; -int minfirstline = VBLANK_ENDLINE_PAL; -static int equ_vblank_endline = EQU_ENDLINE_PAL; -static bool equ_vblank_toggle = true; -int vblank_hz = VBLANK_HZ_PAL; -int syncbase; -static int fmode; -static uae_u16 beamcon0; -static uae_u16 new_beamcon0; -static uae_u16 vtotal = MAXVPOS_PAL, htotal = MAXHPOS_PAL; -static uae_u16 hsstop, hbstrt, hbstop, vsstop, vbstrt, vbstop, hsstrt, vsstrt, hcenter; -static int diw_hstrt, diw_hstop; -static int diw_hcounter; - -#define HSYNCTIME (maxhpos * CYCLE_UNIT) - -struct sprite { - uaecptr pt; - int xpos; - int vstart; - int vstop; - int dblscan; /* AGA SSCAN2 */ - int armed; - int dmastate; - int dmacycle; -}; - -static struct sprite spr[MAX_SPRITES]; -static int plfstrt_sprite; - -static int sprite_vblank_endline = VBLANK_SPRITE_PAL; - -static uae_u16 sprctl[MAX_SPRITES], sprpos[MAX_SPRITES]; -static uae_u16 sprdata[MAX_SPRITES][4], sprdatb[MAX_SPRITES][4]; - -static int last_sprite_point, nr_armed; -static int sprite_width, sprres; -int sprite_buffer_res; - -static uae_u16 bplxdat[8]; -static bool bpl1dat_written, bpl1dat_early, bpl1dat_written_at_least_once; -static bool bpldmawasactive; -static uae_s16 bpl1mod, bpl2mod; -static uaecptr bplpt[8]; - -static struct color_entry current_colors; -static unsigned int bplcon0; -static unsigned int 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 line_cyclebased; -static int bplcon1_hpos; - -/* The display and data fetch windows */ -enum diw_states -{ - DIW_waiting_start, DIW_waiting_stop -}; - -static int plffirstline, plflastline; -static int plfstrt_start, plfstrt, plfstop; -static int sprite_minx, sprite_maxx; -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; - -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_waitforever, - 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_delay1x, - COP_strobe_delay2x, - 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; - enum copper_states state, state_prev; - /* Instruction pointer. */ - uaecptr ip; - int hpos; - unsigned int ignore_next; - int vcmp, hcmp; - - /* When we schedule a copper event, knowing a few things about the future - of the copper list can reduce the number of sync_with_cpu calls - dramatically. */ - unsigned int first_sync; - unsigned int regtypes_modified; - - int strobe; /* COPJMP1 / COPJMP2 accessed */ - int moveaddr, movedata, movedelay; -}; - -#define REGTYPE_NONE 0 -#define REGTYPE_COLOR 1 -#define REGTYPE_SPRITE 2 -#define REGTYPE_PLANE 4 -#define REGTYPE_BLITTER 8 -#define REGTYPE_JOYPORT 16 -#define REGTYPE_DISK 32 -#define REGTYPE_POS 64 -#define REGTYPE_AUDIO 128 - -#define REGTYPE_ALL 255 -/* Always set in regtypes_modified, to enable a forced update when things like - DMACON, BPLCON0, COPJMPx get written. */ -#define REGTYPE_FORCE 256 - -static unsigned int regtypes[512]; - -static struct copper cop_state; -static int copper_enabled_thisline; - -/* - * Statistics - */ - -static unsigned long lastframetime = 0; -static unsigned long frametime = 0, timeframes = 0; -unsigned long hsync_counter = 0; - -/* Recording of custom chip register changes. */ -struct sprite_entry *curr_sprite_entries = 0; -struct color_change *curr_color_changes = 0; - -struct decision line_decisions[2 * (MAXVPOS + 2) + 1]; -struct draw_info curr_drawinfo[2 * (MAXVPOS + 2) + 1]; -#define COLOR_TABLE_SIZE (MAXVPOS + 2) * 2 -struct color_entry curr_color_tables[COLOR_TABLE_SIZE]; - -static int next_sprite_entry = 0; -static int next_sprite_forced = 1; - -static int next_color_change; -static int next_color_entry, remembered_color_entry; - -static struct decision thisline_decision; -static int fetch_cycle, fetch_modulo_cycle; -static int bitplane_dma_turned_on; - -enum plfstate -{ - plf_idle, - plf_start, - plf_active, - plf_wait_stop, - plf_passed_stop, - plf_passed_stop2, - plf_end, - plf_finished -} plf_state; - -enum fetchstate { - fetch_not_started, - fetch_started, - fetch_was_plane0 -} fetch_state; - -/* - * helper functions - */ - -#define nodraw() (framecnt != 0) - -void set_speedup_values(void) -{ - if(currprefs.m68k_speed < 0) { - if (currprefs.cachesize) { - pissoff_value = SPEEDUP_CYCLES_JIT * CYCLE_UNIT; - speedup_timelimit = SPEEDUP_TIMELIMIT_JIT; - } else { - pissoff_value = SPEEDUP_CYCLES_NONJIT * CYCLE_UNIT; - speedup_timelimit = SPEEDUP_TIMELIMIT_NONJIT; - } - } else { - pissoff_value = 0; - speedup_timelimit = 0; - } -} - - -void reset_frame_rate_hack (void) -{ - is_syncline = 0; - if (currprefs.m68k_speed >= 0) - return; - - rpt_did_reset = 1; - vsyncmintime = read_processor_time() + vsynctimebase; - write_log (_T("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 void update_mirrors(void) -{ - aga_mode = (currprefs.chipset_mask & CSMASK_AGA) != 0; -} - -STATIC_INLINE uae_u8 *pfield_xlateptr (uaecptr plpt, int bytecount) -{ - plpt &= chipmem_bank.mask; - if((plpt + bytecount) > chipmem_bank.allocated) - return NULL; - return chipmem_bank.baseaddr + plpt; -} - -STATIC_INLINE void docols (struct color_entry *colentry) -{ - int i; - - if (aga_mode) { - for (i = 0; i < 256; i++) { - int v = colentry->color_regs_aga[i]; - if (v < 0 || v > 16777215) - continue; - colentry->acolors[i] = CONVERT_RGB (v); - } - } else { - for (i = 0; i < 32; i++) { - int v = colentry->color_regs_ecs[i]; - if (v < 0 || v > 4095) - continue; - colentry->acolors[i] = xcolors[v]; - } - } -} - -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(curr_color_tables + i); - } -} - -static void do_sprites (int currhp); - -static void remember_ctable (void) -{ - /* This can happen when program crashes very badly */ - if (next_color_entry >= COLOR_TABLE_SIZE) - return; - if (remembered_color_entry < 0) { - /* 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; -} - -STATIC_INLINE int get_equ_vblank_endline (void) -{ - return equ_vblank_endline + (equ_vblank_toggle ? (lof_current ? 1 : 0) : 0); -} - -#define HARD_DDF_LIMITS_DISABLED ((beamcon0 & 0x80) || (bplcon0 & 0x40)) -/* The HRM says 0xD8, but that can't work... */ -#define HARD_DDF_STOP (HARD_DDF_LIMITS_DISABLED ? 0xff : 0xd6) -#define HARD_DDF_START_REAL 0x18 -/* Programmed rates or superhires (!) disable normal DMA limits */ -#define HARD_DDF_START (HARD_DDF_LIMITS_DISABLED ? 0x04 : 0x18) - -/* 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 <= get_equ_vblank_endline ()) - hdiw = diw_hcounter; - /* always mask, bad programs may have set maxhpos = 256 */ - 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 < 0) - thisline_decision.diwfirstword = diwfirstword < 0 ? PIXEL_XPOS(0) : diwfirstword; - hdiwstate = DIW_waiting_stop; - } - if (((hpos >= maxhpos && HARD_DDF_LIMITS_DISABLED) || (lhdiw >= diw_hstop && last_hdiw < diw_hstop)) && hdiwstate == DIW_waiting_stop) { - if (thisline_decision.diwlastword < 0) - 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 delaymask; -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 () -{ - return real_bitplane_number[fetchmode][bplcon0_res][bplcon0_planes]; -} - -STATIC_INLINE void add_modulo (int nr) -{ - 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; -} - -STATIC_INLINE 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) { - case 8: bplpt[7] += m2; - case 7: bplpt[6] += m1; - case 6: bplpt[5] += m2; - case 5: bplpt[4] += m1; - case 4: bplpt[3] += m2; - case 3: bplpt[2] += m1; - case 2: bplpt[1] += m2; - case 1: bplpt[0] += m1; - } -} - - -/* 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_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; - -/* 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 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 && !(aga_mode)) - rplanes = 4; - real_bitplane_number[fm][res][planes] = rplanes; - } - } - } -} - - -/* Used by the copper. */ -static int estimated_last_fetch_cycle; -static int cycle_diagram_shift; - -static void estimate_last_fetch_cycle (int hpos) -{ - 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_mask; - - estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit; - } else { - int starting_last_block_at = (fetch_cycle + fetchunit - 1) & ~fetchunit_mask; - 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]; -static uae_u32 fetched_aga0[MAX_PLANES]; -static uae_u32 fetched_aga1[MAX_PLANES]; - -/* Expansions from bplcon0/bplcon1. */ -static int toscr_res, toscr_res2p; -static int toscr_nr_planes, toscr_nr_planes2; -static int toscr_delay[2]; - -/* 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_REAL) & fetchstart_mask) << 1); -} - -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; - next_color_change++; - curr_color_changes[next_color_change].regno = -1; -} - -// OCS/ECS, lores, 7 planes = 4 "real" planes + BPL5DAT and BPL6DAT as static 5th and 6th plane -STATIC_INLINE int isocs7planes (void) -{ - return !(aga_mode) && bplcon0_res == 0 && bplcon0_planes == 7; -} - -STATIC_INLINE int is_bitplane_dma (int hpos) -{ - if (fetch_state == fetch_not_started || hpos < plfstrt || plf_state == plf_wait_stop) - 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 void update_denise (int hpos) -{ - toscr_res = GET_RES_DENISE (bplcon0d); - toscr_res2p = 2 << toscr_res; - 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; - } -} - -/* Expand bplcon0/bplcon1 into the toscr_xxx variables. */ -STATIC_INLINE 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; - - delay1 += delayoffset; - delay2 += delayoffset; - toscr_delay[0] = (delay1 & (delaymask >> toscr_res)) << toscr_res; - toscr_delay[0] |= shdelay1 >> (RES_MAX - toscr_res); - toscr_delay[1] = (delay2 & (delaymask >> toscr_res)) << toscr_res; - toscr_delay[1] |= shdelay2 >> (RES_MAX - toscr_res); -} - -STATIC_INLINE void compute_toscr_delay (int hpos, int bplcon1) -{ - update_denise (hpos); - compute_toscr_delay_1 (bplcon1); -} - -static int bpldmasetuphpos, bpldmasetuphpos_diff; -static int bpldmasetupphase; - -#ifdef WITH_INGAME_WARNING -static bool shres_warned = false; -#endif - -/* set currently active Agnus bitplane DMA sequence */ -static void setup_fmodes (int hpos) -{ - switch (fmode & 3) - { - case 0: - fetchmode = 0; - delaymask = 15; // (16 << fetchmode) - 1; - break; - case 1: - case 2: - fetchmode = 1; - delaymask = 31; // (16 << fetchmode) - 1; - break; - case 3: - fetchmode = 2; - delaymask = 63; // (16 << fetchmode) - 1; - break; - } - bplcon0_res = GET_RES_AGNUS (bplcon0); - if(bplcon0_res == RES_SUPERHIRES) { -#ifdef WITH_INGAME_WARNING - if(!shres_warned) { - InGameMessage("Superhires resolution requested.\nNot supported in UAE4ARM."); - shres_warned = true; - } -#endif - bplcon0_res == RES_LORES; - } - bplcon0_planes = GET_PLANES (bplcon0); - bplcon0_planes_limit = GET_PLANES_LIMIT (); - fetchunit = fetchunits[fetchmode * 4 + bplcon0_res]; - fetchunit_mask = fetchunit - 1; - int fetchstart_shift = fetchstarts[fetchmode * 4 + bplcon0_res]; - fetchstart = 1 << fetchstart_shift; - fetchstart_mask = fetchstart - 1; - int fm_maxplane_shift = fm_maxplanes[fetchmode * 4 + bplcon0_res]; - fm_maxplane = 1 << fm_maxplane_shift; - fetch_modulo_cycle = fetchunit - fetchstart; - - if (thisline_decision.plfleft < 0) { - thisline_decision.bplres = bplcon0_res; - thisline_decision.bplcon0 = bplcon0; - thisline_decision.nr_planes = bplcon0_planes; - } - - curr_diagram = cycle_diagram_table[fetchmode][bplcon0_res][bplcon0_planes_limit]; - estimate_last_fetch_cycle (hpos); - bpldmasetuphpos = -1; - bpldmasetupphase = 0; - line_cyclebased = vpos; -} - -static void BPLCON0_Denise (int hpos, uae_u16 v, bool); - -// 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 (3 + (copper_access ? 1 : 0) + (bplcon0_planes == 8 ? 1 : 0)) -#define BPLCON_DENISE_DELAY (copper_access ? 1 : 0) - -STATIC_INLINE void maybe_setup_fmodes (int hpos) -{ - switch (bpldmasetupphase) - { - case 0: - BPLCON0_Denise (hpos, bplcon0, false); - bpldmasetupphase++; - bpldmasetuphpos += bpldmasetuphpos_diff; - 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) -{ - line_cyclebased = vpos; - if (hpos + BPLCON_AGNUS_DELAY < 0x14) { - BPLCON0_Denise (hpos, bplcon0, false); - setup_fmodes (hpos); - return; - } - if (bpldmasetuphpos < 0) { - bpldmasetuphpos = hpos + BPLCON_DENISE_DELAY; - bpldmasetuphpos_diff = BPLCON_AGNUS_DELAY - BPLCON_DENISE_DELAY; - bpldmasetupphase = 0; - if (BPLCON_DENISE_DELAY == 0) { - maybe_setup_fmodes (hpos); - } - } -} - -STATIC_INLINE void update_toscr_planes (void) -{ - if (toscr_nr_planes2 > thisline_decision.nr_planes) { - if(thisline_decision.nr_planes > 0) { - int j; - for (j = thisline_decision.nr_planes; j < toscr_nr_planes2; j++) - memset ((uae_u32 *)(line_data[next_lineno] + 2 * MAX_WORDS_PER_LINE * j), 0, out_offs * 4); - } - thisline_decision.nr_planes = toscr_nr_planes2; - } -} - -STATIC_INLINE void maybe_first_bpl1dat (int hpos) -{ - if (thisline_decision.plfleft >= 0) { - // early bpl1dat crap fix (Sequential engine animation) - if (plfleft_real < 0) { - int i; - for (i = 0; i < MAX_PLANES; i++) { - todisplay[i][0] = 0; - todisplay[i][1] = 0; - todisplay[i][2] = 0; - todisplay[i][3] = 0; - } - plfleft_real = hpos; - bpl1dat_early = true; - } - } else { - plfleft_real = thisline_decision.plfleft = hpos; - compute_delay_offset (); - } -} - -STATIC_INLINE void fetch (int nr, int fm) -{ - if (nr < bplcon0_planes_limit) { - uaecptr p = bplpt[nr]; - if (nr == 0) - bpl1dat_written = true; - switch (fm) - { - case 0: - fetched[nr] = bplxdat[nr] = last_custom_value1 = chipmem_wget_indirect (p); - bplpt[nr] += 2; - break; - case 1: - fetched_aga0[nr] = chipmem_lget_indirect (p); - last_custom_value1 = (uae_u16)fetched_aga0[nr]; - bplpt[nr] += 4; - 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]; - bplpt[nr] += 8; - break; - } - if (plf_state == plf_passed_stop2 && fetch_cycle >= (fetch_cycle & ~fetchunit_mask) + fetch_modulo_cycle) { - add_modulo (nr); - } - } 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_delay[0]; - int delay2 = toscr_delay[1]; - 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; -} - -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_delay[0]; - int delay2 = toscr_delay[1]; - 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); - } - } -} - -static void toscr_2_0 (int nbits) { toscr_3_ecs (nbits); } -static void toscr_2_1 (int nbits) { toscr_3_aga (nbits, 1); } -static void toscr_2_2 (int nbits) { toscr_3_aga (nbits, 2); } - -STATIC_INLINE void toscr_1 (int nbits, int fm) -{ - switch (fm) { - case 0: - toscr_2_0 (nbits); - break; - case 1: - toscr_2_1 (nbits); - break; - case 2: - toscr_2_2 (nbits); - break; - } - - out_nbits += nbits; - if (out_nbits == 32) { - int i; - uae_u32 *dataptr32 = (uae_u32 *)(line_data[next_lineno]); - dataptr32 += out_offs; - - for (i = 0; i < thisline_decision.nr_planes; i++) { - *dataptr32 = outword[i]; - outword[i] = 0; - dataptr32 += MAX_WORDS_PER_LINE >> 1; - } - 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; - case 1: toscr_fm1 (nbits); break; - case 2: toscr_fm2 (nbits); break; - } -} - -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_INLINE 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 >= 0) - toscr (toscr_nbits, fm); - toscr_nbits = 0; -} - -STATIC_INLINE void fetch_start () -{ - 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_x (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]; - } - else - for (i = 0; i < MAX_PLANES; i++) { - if (fm == 2) - todisplay[i][1] = fetched_aga1[i]; - todisplay[i][0] = fetched_aga0[i]; - } - - 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); -} - -static void beginning_of_plane_block_0 (int hpos) { beginning_of_plane_block_x(hpos, 0); }; -static void beginning_of_plane_block_1 (int hpos) { beginning_of_plane_block_x(hpos, 1); }; -static void beginning_of_plane_block_2 (int hpos) { beginning_of_plane_block_x(hpos, 2); }; -STATIC_INLINE void beginning_of_plane_block (int hpos, int fm) -{ - if(fm == 0) - beginning_of_plane_block_0(hpos); - else if(fm == 1) - beginning_of_plane_block_1(hpos); - else - beginning_of_plane_block_2(hpos); -} - -/* 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 = toscr_delay[(plane & 1)]; - int tmp_nbits = out_nbits; - uae_u32 shiftbuffer = todisplay[plane][0]; - uae_u32 outval = outword[plane]; - uae_u32 fetchval = fetched[plane]; - uae_u32 *dataptr_start = (uae_u32 *)(line_data[next_lineno] + (2 * plane * MAX_WORDS_PER_LINE)); - uae_u32 *dataptr = dataptr_start + out_offs; - - if (dma) { - bplpt[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); - *dataptr++ = outval; - outval = t; - tmp_nbits = 16 - bits_left; - } else { - outval = (outval << 16) | t; - tmp_nbits += 16; - if (tmp_nbits == 32) { - *dataptr++ = outval; - tmp_nbits = 0; - } - } - shiftbuffer <<= 16; - nwords--; - if (dma) { - __asm__ ( - "ldrh %[val], [%[pt]], #2 \n\t" - "rev16 %[val], %[val] \n\t" - : [val] "=r" (fetchval), [pt] "+r" (real_pt) ); - } - } - fetched[plane] = fetchval; - todisplay[plane][0] = shiftbuffer; - outword[plane] = outval; -} - - -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 = toscr_delay[plane & 1]; - int tmp_nbits = out_nbits; - uae_u32 outval = outword[plane]; - uae_u32 fetchval0 = fetched_aga0[plane]; - uae_u32 fetchval1 = fetched_aga1[plane]; - uae_u32 *dataptr_start = (uae_u32 *)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE); - uae_u32 *dataptr = dataptr_start + out_offs; - - int offs = (16 << fm) - 16 + delay; - int off1 = offs >> 5; - if (off1 == 3) - off1 = 2; - offs -= off1 << 5; - - if (dma) { - bplpt[plane] += nwords * 2; - } - - if (real_pt == 0) - /* @@@ Don't do this, fall back on chipmem_wget instead. */ - return; - - /* Instead of shifting a 64 bit value more than 16 bits, we */ - /* move the pointer for x bytes and shift a 32 bit value less */ - /* than 16 bits. See (1) */ - int buffer_add = (offs >> 4); - offs &= 15; - - while (nwords > 0) { - int i; - uae_u32 *shiftbuffer = todisplay[plane]; - - shiftbuffer[0] = fetchval0; - if(fm == 2) - shiftbuffer[1] = fetchval1; - - /* (1) */ - if(buffer_add) - shiftbuffer = (uae_u32 *)((uae_u16 *)shiftbuffer + buffer_add); - - for (i = 0; i < (1 << fm); i++) { - int bits_left = 32 - tmp_nbits; - - uae_u32 t0 = shiftbuffer[off1]; - - t0 = (uae_u32)((t0 >> offs) & 0xFFFF); - - - if (weird_number_of_bits && bits_left < 16) { - outval <<= bits_left; - outval |= t0 >> (16 - bits_left); - *dataptr++ = outval; - outval = t0; - tmp_nbits = 16 - bits_left; - /* Instead of shifting 128 bit of data for 16 bit, */ - /* we move the pointer two bytes. See also (2) and (3) */ - /*aga_shift (shiftbuffer, 16, fm); */ - shiftbuffer = (uae_u32 *)((uae_u16 *)shiftbuffer - 1); - } else { - outval = (outval << 16) | t0; - /* (2) */ - /*aga_shift (shiftbuffer, 16, fm); */ - shiftbuffer = (uae_u32 *)((uae_u16 *)shiftbuffer - 1); - tmp_nbits += 16; - if (tmp_nbits == 32) { - *dataptr++ = outval; - tmp_nbits = 0; - } - } - } - - /* (3) */ - /* We have to move the data, but now, we can simply */ - /* copy long values and have to do it only once. */ - if(fm == 1) - todisplay[plane][1] = todisplay[plane][0]; - else { - todisplay[plane][3] = todisplay[plane][1]; - todisplay[plane][2] = todisplay[plane][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; -} - -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); } -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); } - -static void do_long_fetch (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; - 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; - } - - out_nbits += nwords * 16; - out_offs += out_nbits >> 5; - out_nbits &= 31; - - if (dma && toscr_nr_planes > 0) - fetch_state = fetch_was_plane0; -} - -STATIC_INLINE void finish_last_fetch (int pos, int fm) -{ - if (thisline_decision.plfleft < 0) - return; - if (plf_state >= plf_end) - return; - plf_state = plf_end; - pos += flush_plane_data (fm); - thisline_decision.plfright = pos; - thisline_decision.plflinelen = out_offs; - if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) { - bpl1dat_early = true; - ddfstate = DIW_waiting_start; - fetch_state = fetch_not_started; - } -} - -/* check special case where last fetch wraps to next line - * this makes totally corrupted and flickering display on - * real hardware due to refresh cycle conflicts - */ -static void maybe_finish_last_fetch (int pos, int fm) -{ - bool done = false; - - if (plf_state != plf_passed_stop2 || fetch_state != fetch_started || !dmaen (DMA_BITPLANE)) { - finish_last_fetch (pos, fm); - return; - } - do { - int cycle_start = fetch_cycle & fetchstart_mask; - switch (fm_maxplane) { - case 8: - switch (cycle_start) { - case 0: fetch (7, fm); break; - case 1: fetch (3, fm); break; - case 2: fetch (5, fm); break; - case 3: fetch (1, fm); break; - case 4: fetch (6, fm); break; - case 5: fetch (2, fm); break; - case 6: fetch (4, fm); break; - case 7: fetch (0, fm); break; - default: - goto end; - } - break; - case 4: - switch (cycle_start) { - case 0: fetch (3, fm); break; - case 1: fetch (1, fm); break; - case 2: fetch (2, fm); break; - case 3: fetch (0, fm); break; - default: - goto end; - } - break; - case 2: - switch (cycle_start) { - case 0: fetch (1, fm); break; - case 1: fetch (0, fm); break; - default: - goto end; - } - break; - } - fetch_cycle++; - toscr_nbits += toscr_res2p; - - if (toscr_nbits > 16) - toscr_nbits = 0; - if (toscr_nbits == 16) - flush_display (fm); - done = true; - } while ((fetch_cycle & fetchunit_mask) != 0); - -end: - finish_last_fetch (pos, fm); -} - - -/* make sure fetch that goes beyond maxhpos is finished */ -STATIC_INLINE void finish_final_fetch (void) -{ - if (plf_state < plf_end) { - finish_last_fetch (maxhpos, fetchmode); - if (plf_state != plf_end) - return; - } - plf_state = plf_finished; -} - -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_last_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 && plf_state != plf_wait_stop) { - /* 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); break; - case 1: fetch (3, fm); break; - case 2: fetch (5, fm); break; - case 3: fetch (1, fm); break; - case 4: fetch (6, fm); break; - case 5: fetch (2, fm); break; - case 6: fetch (4, fm); break; - case 7: fetch (0, fm); break; - } - break; - case 4: - switch (cycle_start) { - case 0: fetch (3, fm); break; - case 1: fetch (1, fm); break; - case 2: fetch (2, fm); break; - case 3: fetch (0, fm); break; - } - break; - case 2: - switch (cycle_start) { - case 0: fetch (1, fm); break; - case 1: fetch (0, fm); 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 = false; - } - - fetch_cycle++; - toscr_nbits += toscr_res2p; - - if (toscr_nbits > 16) { - write_log (_T("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); - case 1: return one_fetch_cycle_fm1 (pos, ddfstop_to_test, dma); - case 2: return one_fetch_cycle_fm2 (pos, ddfstop_to_test, dma); - default: write_log (_T("fm corrupt")); return 0; - } -} - -static void update_bpldats (int hpos) -{ - for (int i = 0; i < MAX_PLANES; i++) { - fetched_aga0[i] = bplxdat[i]; - fetched_aga1[i] = 0; - fetched[i] = bplxdat[i]; - } - beginning_of_plane_block (hpos, fetchmode); -} - -static void update_fetch_x (int until, int fm) -{ - int pos; - - if (nodraw ()) - return; - - pos = last_fetch_hpos; - update_toscr_planes (); - - // not optimized, update_fetch_x() is extremely rarely used. - for (; pos < until; pos++) { - - toscr_nbits += toscr_res2p; - - if (toscr_nbits > 16) { - write_log (_T("xtoscr_nbits > 16 (%d)"), toscr_nbits); - toscr_nbits = 0; - } - if (toscr_nbits == 16) - flush_display (fm); - - } - - if (until >= maxhpos) { - maybe_finish_last_fetch (pos, fm); - return; - } - - flush_display (fm); -} - -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 < HARD_DDF_STOP) - 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) { - maybe_finish_last_fetch (pos, fm); - return; - } - flush_display (fm); - return; - } - - if (fetch_state == fetch_was_plane0) - break; - - fetch_start (); - if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm)) - return; - } - - // vdiw getting cleared mid-scan = fetches also stop - if (diwstate == DIW_waiting_start && plf_state < plf_wait_stop) { - // ecs = continue at passed_stop - // ocs = line is done - if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) - plf_state = plf_wait_stop; - else - plf_state = plf_passed_stop2; - } - - /* Unrolled version of the for loop below. */ - if (plf_state < plf_wait_stop && line_cyclebased != vpos && line_cyclebased + 1 != vpos - && dma - && (fetch_cycle & fetchstart_mask) == (fm_maxplane & fetchstart_mask) - && 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 < 0) { - compute_delay_offset (); - compute_toscr_delay_1 (bplcon1); - } - do_long_fetch (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; - } - } - - for (; pos < until; pos++) { - if (fetch_state == fetch_was_plane0) { - beginning_of_plane_block (pos, fm); - estimate_last_fetch_cycle (pos); - } - fetch_start (); - - if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm)) - return; - } - if (until >= maxhpos) { - maybe_finish_last_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; - case 1: update_fetch_1 (hpos); break; - case 2: update_fetch_2 (hpos); break; - default: write_log (_T("fetchmode corrupt")); - } - } else if (bpl1dat_written_at_least_once) { - // "PIO" mode display - update_fetch_x (hpos, fetchmode); - bpl1dat_written = false; - } - maybe_check (hpos); - last_fetch_hpos = hpos; - } -} - -STATIC_INLINE void decide_fetch_safe (int hpos) -{ - decide_fetch (hpos); -} - -static void reset_bpl_vars (void) -{ - out_nbits = 0; - out_offs = 0; - toscr_nbits = 0; - thisline_decision.bplres = bplcon0_res; -} - -STATIC_INLINE void start_bpl_dma (int hstart) -{ - fetch_start (); - ddfstate = DIW_waiting_stop; - - if (!bpldmawasactive) { - - plfstrt_sprite = plfstrt; - fetch_cycle = 0; - - compute_toscr_delay (last_fetch_hpos, bplcon1); - - /* If someone already wrote BPL1DAT, clear the area between that point and - the real fetch start. */ - if (bpl1dat_written_at_least_once && hstart > last_fetch_hpos) { - update_fetch_x (hstart, fetchmode); - bpl1dat_written_at_least_once = false; - } else { - reset_bpl_vars (); - } - cycle_diagram_shift = hstart; - - bpldmawasactive = true; - - } else { - - // this is a hack, something weird happens without this - // (Subtle Shades) - thisline_decision.plfright++; - thisline_decision.plflinelen = ++out_offs; - } - - last_fetch_hpos = hstart; -} - -/* this may turn on datafetch if program turns dma on during the ddf */ -STATIC_INLINE 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)) { - bitplane_dma_turned_on = hpos; - 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_dma (hpos); -} - -/* 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; - line_cyclebased = vpos; - } - if (vpos == plflastline) { - diwstate = DIW_waiting_start; - line_cyclebased = vpos; - } - - if (hpos <= last_decide_line_hpos) - return; - - if (fetch_state == fetch_not_started) { - int start = (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? plfstrt - 4 : HARD_DDF_START_REAL - 2; - if (last_decide_line_hpos < start && hpos >= start) { - if (plf_state == plf_idle || plf_state == plf_end) - plf_state = plf_start; - } - if ((diwstate == DIW_waiting_stop || (currprefs.chipset_mask & CSMASK_ECS_AGNUS))) { - int ok = 0; - 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) && bitplane_dma_turned_on + 4 < hpos) { - start_bpl_dma (plfstrt); - estimate_last_fetch_cycle (plfstrt); - } - last_decide_line_hpos = hpos; - do_sprites (hpos); - return; - } - } - } - - if (hpos > last_sprite_hpos && last_sprite_hpos < SPR0_HPOS + 4 * MAX_SPRITES) - do_sprites (hpos); - - 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 < 0) - 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; - /* 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) - */ - 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; - curr_color_changes[idx + 1].regno = -1; - } - record_color_change2 (hpos, regno, value); -} - -static bool isbrdblank (int hpos, uae_u16 bplcon0, uae_u16 bplcon3) -{ - bool brdblank; - brdblank = (currprefs.chipset_mask & CSMASK_ECS_DENISE) && (bplcon0 & 1) && (bplcon3 & 0x20); - if (hpos >= 0 && current_colors.borderblank != brdblank) { - record_color_change (hpos, 0, COLOR_CHANGE_BRDBLANK | (brdblank ? 1 : 0) | (current_colors.bordersprite ? 2 : 0)); - current_colors.borderblank = brdblank; - remembered_color_entry = -1; - } - return brdblank; -} - - -static bool issprbrd (int hpos, uae_u16 bplcon0, uae_u16 bplcon3) -{ - bool brdsprt; - brdsprt = (aga_mode) && (bplcon0 & 1) && (bplcon3 & 0x02); - if (hpos >= 0 && current_colors.bordersprite != brdsprt) { - record_color_change (hpos, 0, COLOR_CHANGE_BRDBLANK | (current_colors.borderblank ? 1 : 0) | (brdsprt ? 2 : 0)); - current_colors.bordersprite = brdsprt; - remembered_color_entry = -1; - if (brdsprt && !current_colors.borderblank) - thisline_decision.bordersprite_seen = true; - } - return brdsprt && !current_colors.borderblank; -} - -static void record_register_change (int hpos, int regno, uae_u16 value) -{ - if (regno == 0x100) { // BPLCON0 - if (value & 0x800) - thisline_decision.ham_seen = 1; - isbrdblank (hpos, value, bplcon3); - issprbrd (hpos, value, bplcon3); - } else if (regno == 0x106) { // BPLCON3 - isbrdblank (hpos, bplcon0, value); - issprbrd (hpos, bplcon0, value); - } - 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; - 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; - case 1: - res = RES_LORES; - break; - case 2: - res = RES_HIRES; - break; - case 3: - res = RES_SUPERHIRES; - break; - } - return res; -} - -#define DO_PLAYFIELD_COLLISIONS \ -{ \ - if (!clxcon_bpl_enable) \ - clxdat |= 1; \ - else \ - if (!(clxdat & 1)) \ - do_playfield_collisions (); \ -} - -/* 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; - int planes = (aga_mode) ? 8 : 6; - - 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 (collided) - clxdat |= 1; -} - -#define DO_SPRITE_COLLISIONS \ -{ \ - if (!clxcon_bpl_enable) \ - clxdat |= 0x1FE; \ - else \ - do_sprite_collisions (); \ -} - -/* 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); - - 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; - int planes = (aga_mode) ? 8 : 6; - - 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 (clxcon_bpl_enable & (1 << l)) { - if (t != ((clxcon_bpl_match >> l) & 1)) - match = 0; - } - } - if (match) { - clxdat |= sprpix << (k * 4); - } - } - } - } -} - -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 int coltmp = 0; - - if ((sprxp >= sprite_minx && sprxp < sprite_maxx) || (bplcon3 & 2)) - col = (datab & 3) << (2 * num); - 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) { - write_log (_T("sprxp < e->pos")); - return; - } - - 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) { - uae_u8 state = 0x03 << (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 - DIW_DDF_OFFSET) << sprite_buffer_res; -} - -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 */ - if (currprefs.chipset_mask & CSMASK_ECS_DENISE) { - sprite_minx = min; - } else { - if (thisline_decision.plfleft >= 0x28 || bpldmawasactive) - sprite_minx = min; - } - } - /* sprites are visible from first BPL1DAT write to end of line - * ECS Denise/AGA: no limits - * OCS Denise: BPL1DAT write only enables sprite if hpos >= 0x28 or so. - * (undocumented feature) - */ - } -} - -static void decide_sprites (int hpos) -{ - int nrs[MAX_SPRITES * 2], posns[MAX_SPRITES * 2]; - int count, i; - int point = hpos * 2 + 0; - int width = sprite_width; - int sscanmask = 0x100 << sprite_buffer_res; - - if (thisline_decision.plfleft < 0 && !(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 (! 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]); - } - last_sprite_point = point; -} - -/* End of a horizontal scan line. Finish off all decisions that were not - * made yet. */ -STATIC_INLINE void finish_decisions (void) -{ - struct draw_info *dip; - int hpos = maxhpos; - - if (nodraw ()) - return; - - decide_diw (hpos); - decide_line (hpos); - decide_fetch_safe (hpos); - finish_final_fetch (); - - if (thisline_decision.plfleft >= 0 && thisline_decision.plflinelen < 0) { - 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 < 0) - thisline_decision.diwfirstword = min_diwlastword; - } - decide_sprites (hpos + 1); - - dip = curr_drawinfo + next_lineno; - - dip->last_sprite_entry = next_sprite_entry; - dip->last_color_change = next_color_change; - - if (thisline_decision.ctable < 0) { - remember_ctable(); - } - - dip->nr_color_changes = next_color_change - dip->first_color_change; - dip->nr_sprites = next_sprite_entry - dip->first_sprite_entry; - - line_decisions[next_lineno] = thisline_decision; - - /* 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 == get_equ_vblank_endline () - 1) - diw_hcounter++; - if ((currprefs.chipset_mask & CSMASK_ECS_DENISE) || vpos > get_equ_vblank_endline ()) { - diw_hcounter = maxhpos * 2; - last_hdiw = 2 - 1; - } -} - -/* 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 = false; - bpl1dat_written_at_least_once = false; - bpl1dat_early = false; - - plfleft_real = -1; - thisline_decision.plfleft = -1; - thisline_decision.plflinelen = -1; - thisline_decision.ham_seen = !! (bplcon0 & 0x800); - thisline_decision.ham_at_start = !! (bplcon0 & 0x800); - thisline_decision.bordersprite_seen = issprbrd (-1, bplcon0, bplcon3); - - /* 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 = min_diwlastword; - } - thisline_decision.ctable = -1; - - 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, true); - setup_fmodes (0); - } - bpldmasetuphpos = -1; - bpldmasetupphase = 0; - ddfstrt_old_hpos = -1; - bpldmawasactive = false; - bitplane_dma_turned_on = 0; - - 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); - if (aga_mode) { - memset (fetched_aga0, 0, sizeof fetched_aga0); - memset (fetched_aga1, 0, sizeof fetched_aga1); - } - memset (outword, 0, sizeof outword); - - last_decide_line_hpos = -1; - last_sprite_hpos = -1; - last_fetch_hpos = -1; - - /* These are for comparison. */ - thisline_decision.bplcon0 = bplcon0; - thisline_decision.bplcon2 = bplcon2; - thisline_decision.bplcon3 = bplcon3; - thisline_decision.bplcon4 = bplcon4; -} - -void compute_vsynctime (void) -{ - vblank_hz = currprefs.chipset_refreshrate; - - vsynctimebase = (int)(syncbase / vblank_hz); - if (screen_is_picasso) { - beamcon0 = new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20; - } - - if (currprefs.produce_sound > 1) { - float svpos = maxvpos_nom; - float shpos = maxhpos; - if (currprefs.ntscmode) { - shpos += 0.5; - } - if ((bplcon0 & 4)) { - svpos += 0.5; - } else if (lof_current) { - svpos += 1.0; - } - float clk = svpos * shpos * vblank_hz; - update_sound (clk); - } -} - -int current_maxvpos (void) -{ - return maxvpos + (lof_store ? 1 : 0); -} - -static void compute_framesync (void) -{ - int v; - - if (!picasso_on && !picasso_requested_on) { - if (abs (vblank_hz - 50) < 1 || abs (vblank_hz - 60) < 1) { - vsync_switchmode (vblank_hz); - } - v = (vblank_hz >= 55) ? 60 : 50; - } else { - v = (currprefs.ntscmode ? 60 : 50); - } - - changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = v; - lof_changing = 0; - - memset (line_decisions, 0, sizeof line_decisions); - - compute_vsynctime (); - - if (target_graphics_buffer_update ()) { - reset_drawing (); - } -} - -/* set PAL/NTSC or custom timing variables */ -static void init_hz (bool fullinit) -{ - int isntsc; - int omaxvpos = maxvpos; - int hzc = 0; - - if (fullinit) - vpos_count = 0; - - vpos_count_diff = vpos_count; - - if ((beamcon0 & 0xA0) != (new_beamcon0 & 0xA0)) - hzc = 1; - if (beamcon0 != new_beamcon0) { - vpos_count_diff = vpos_count = 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; - equ_vblank_toggle = true; - } 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; - equ_vblank_toggle = false; - } - // long/short field refresh rate adjustment - vblank_hz = vblank_hz * (maxvpos * 2 + 1) / ((maxvpos + lof_current) * 2); - - maxvpos_nom = maxvpos; - maxvpos_display = maxvpos; - if (vpos_count > 0) { - // we come here if vpos_count != maxvpos and beamcon0 didn't change - // (someone poked VPOSW) - if (vpos_count < 10) - vpos_count = 10; - float new_hz = (isntsc ? 15734.0 : 15625.0) / (float)vpos_count; - vblank_hz = (int)(new_hz + 0.5); - maxvpos_nom = vpos_count - (lof_current ? 1 : 0); - if ((maxvpos_nom >= 256 && maxvpos_nom <= 313) || (beamcon0 & 0x80)) { - maxvpos_display = maxvpos_nom; - } else if (maxvpos_nom < 256) { - maxvpos_display = 255; - } else { - maxvpos_display = 313; - } - reset_drawing (); - } else if (vpos_count == 0) { - // mode reset - vpos_count = maxvpos; - vpos_count_diff = maxvpos; - } - - 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; - float new_hz = 227.0 * 312.0 * 50.0 / (float)(maxvpos * maxhpos); - vblank_hz = (int)(new_hz + 0.5); - minfirstline = vsstop > vbstop ? vsstop : vbstop; - if (minfirstline > maxvpos / 2) - minfirstline = vsstop > vbstop ? vbstop : vsstop; - if (minfirstline < 2) - minfirstline = 2; - if (minfirstline >= maxvpos) - minfirstline = maxvpos - 1; - sprite_vblank_endline = minfirstline - 2; - maxvpos_nom = maxvpos; - maxvpos_display = maxvpos; - equ_vblank_endline = -1; - vpos_count = maxvpos_nom; - vpos_count_diff = maxvpos_nom; - hzc = 1; -#ifdef WITH_INGAME_WARNING - char _info[64]; - sprintf(_info, "Programmend HZ: %d", vblank_hz); - InGameMessage(_info); -#endif - } - if (maxvpos_nom >= MAXVPOS) - maxvpos_nom = MAXVPOS; - if (maxvpos_display >= MAXVPOS) - maxvpos_display = MAXVPOS; - if (maxvpos != omaxvpos) - hzc = 1; - /* limit to sane values */ - if (vblank_hz < 10) - vblank_hz = 10; - if (vblank_hz > 300) - vblank_hz = 300; - eventtab[ev_hsync].oldcycles = get_cycles (); - eventtab[ev_hsync].evtime = get_cycles() + HSYNCTIME; - events_schedule (); - if (hzc) { - reset_drawing (); - } - maxvpos_total = (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? (MAXVPOS_LINES_ECS - 1) : (MAXVPOS_LINES_OCS - 1); - if (maxvpos_total > MAXVPOS) - maxvpos_total = MAXVPOS; - - compute_framesync (); - -#ifdef PICASSO96 - init_hz_p96 (); -#endif - inputdevice_tablet_strobe (); - - if (fullinit) - vpos_count_diff = maxvpos_nom; -} - -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 = min_diwlastword; - diwlastword = max_diwlastword; - } - if (diwfirstword < min_diwlastword) - diwfirstword = min_diwlastword; - - 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; - } else { - /* OCS and ddfstrt >= ddfstop == ddfstop = max */ - if (plfstrt >= plfstop && plfstrt >= HARD_DDF_START) - plfstop = 0xff; - } -} - -/* display mode changed (lores, doubling etc..), recalculate everything */ -void init_custom (void) -{ - update_mirrors(); - create_cycle_diagram_table (); - reset_drawing (); - init_hz (); - 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); - put_long (m68k_areg (regs, 0), tv.tv_sec - (((365 * 8 + 2) * 24) * 60 * 60)); - put_long (m68k_areg (regs, 0) + 4, tv.tv_usec); - return 0; -#else - return 2; -#endif -} - - /* - * register functions - */ -STATIC_INLINE uae_u16 DENISEID (int *missing) -{ - *missing = 0; - if (aga_mode) { - return 0x00F8; - } - if (currprefs.chipset_mask & CSMASK_ECS_DENISE) - return 0xFFFC; - if (currprefs.cpu_model == 68000 && currprefs.cpu_compatible) - *missing = 1; - return 0xFFFF; -} -STATIC_INLINE uae_u16 DMACONR (int hpos) -{ - dmacon &= ~(0x4000 | 0x2000); - dmacon |= (bltstate == BLT_done ? 0 : 0x4000) - | (blt_info.blitzero ? 0x2000 : 0); - return dmacon; -} -STATIC_INLINE uae_u16 INTENAR (void) -{ - return intena; -} -STATIC_INLINE uae_u16 ADKCONR (void) -{ - return adkcon; -} - -STATIC_INLINE int GETVPOS (void) -{ - return vpos; -} -STATIC_INLINE int GETHPOS (void) -{ - return current_hpos (); -} - -#define CPU_ACCURATE (currprefs.cpu_model < 68020) - -// DFF006 = 0.W must be valid result but better do this only in 68000 modes (whdload black screen!) -// HPOS is shifted by 3 cycles and VPOS increases when shifted HPOS==1 -#define HPOS_OFFSET (CPU_ACCURATE ? HPOS_SHIFT : 0) -#define VPOS_INC_DELAY (HPOS_OFFSET ? 1 : 0) - -STATIC_INLINE uae_u16 VPOSR (void) -{ - unsigned int csbit = 0; - uae_u16 vp = GETVPOS (); - uae_u16 hp = GETHPOS (); - - if (hp + HPOS_OFFSET >= maxhpos + VPOS_INC_DELAY) { - vp++; - if (vp >= maxvpos + lof_store) - vp = 0; - } - vp = (vp >> 8) & 7; - - csbit |= (aga_mode) ? 0x2300 : 0; - csbit |= (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 0x2000 : 0; - if (currprefs.ntscmode) - csbit |= 0x1000; - - if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) - vp &= 1; - vp |= (lof_store ? 0x8000 : 0) | csbit; - return vp; -} - -static void VPOSW (uae_u16 v) -{ - int oldvpos = vpos; - if (lof_store != ((v & 0x8000) ? 1 : 0)) { - lof_store = (v & 0x8000) ? 1 : 0; - lof_changing = lof_store ? 1 : -1; - } - if (lof_changing) - return; - vpos &= 0x00ff; - v &= 7; - if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) - v &= 1; - vpos |= v << 8; - if (vpos != oldvpos) - vposw_change++; - if (vpos < oldvpos) - vpos = oldvpos; -} - -static void VHPOSW (uae_u16 v) -{ - int oldvpos = vpos; - bool changed = false; - - v >>= 8; - vpos &= 0xff00; - vpos |= v; - if (vpos != oldvpos && !changed) - vposw_change++; - if (vpos < oldvpos) { - vpos = oldvpos; - } else if (vpos < minfirstline && oldvpos < minfirstline) { - vpos = oldvpos; - } -} - -STATIC_INLINE uae_u16 VHPOSR (void) -{ - uae_u16 vp = GETVPOS (); - uae_u16 hp = GETHPOS (); - hp += HPOS_OFFSET; - if (hp >= maxhpos) { - hp -= maxhpos; - // vpos increases when hp==1, not when hp==0 - if (hp >= VPOS_INC_DELAY) { - vp++; - if (vp >= maxvpos + lof_store) - vp = 0; - } - } - if (HPOS_OFFSET) { - hp += 1; - if (hp >= maxhpos) - hp -= maxhpos; - } - - vp <<= 8; - vp |= hp; - 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.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 was_active = eventtab[ev_copper].active; - int oldstrobe = cop_state.strobe; - bool wasstopped = cop_state.state == COP_stop && !vblank; - - unset_special (SPCFLAG_COPPER); - cop_state.ignore_next = 0; - if (!oldstrobe) - cop_state.state_prev = cop_state.state; - if ((cop_state.state == COP_wait || cop_state.state == COP_waitforever) && !vblank) { - cop_state.state = COP_strobe_delay1x; - } else { - cop_state.state = vblank ? COP_start_delay : (copper_access ? COP_strobe_delay1 : COP_strobe_extra); - } - cop_state.hpos = current_hpos () & ~1; - copper_enabled_thisline = 0; - cop_state.strobe = num; - - eventtab[ev_copper].active = 0; - - if (nocustom()) { - immediate_copper (num); - return; - } - - if (was_active) - events_schedule (); - - if (dmaen (DMA_COPPER)) { - compute_spcflag_copper (current_hpos ()); - } else if (wasstopped || (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_safe (hpos); - - setclr (&dmacon, v); - dmacon &= 0x07FF; - - changed = dmacon ^ oldcon; - oldcop = (oldcon & DMA_COPPER) && (oldcon & DMA_MASTER); - newcop = (dmacon & DMA_COPPER) && (dmacon & DMA_MASTER); - - if (oldcop != newcop) { - eventtab[ev_copper].active = 0; - - 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) { - blitter_check_start (); - } - - 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)) { - line_cyclebased = vpos; - if (dmaen (DMA_BITPLANE)) - maybe_start_bpl_dma (hpos); - } - - events_schedule(); -} - -int intlev (void) -{ - uae_u16 imask = intreq & intena; - if (!(imask && (intena & 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; -} - -STATIC_INLINE void rethink_intreq (void) -{ - rethink_cias (); -#ifdef CD32 - rethink_akiko (); -#endif -} - -static void INTENA (uae_u16 v) -{ - uae_u16 old = intena; - setclr (&intena,v); - - if (!(v & 0x8000) && old == intena) - return; - - if (v & 0x8000) - doint(); -} - -void INTREQ_f (uae_u16 v) -{ - setclr (&intreq, v); -} - -bool INTREQ_0 (uae_u16 v) -{ - uae_u16 old = intreq; - setclr (&intreq, v); - - if (old == intreq) - return false; - if (v & 0x8000) - doint(); - return true; -} - -void INTREQ (uae_u16 data) -{ - if (INTREQ_0 (data)) - rethink_intreq (); -} - -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 (); -} - -static void BEAMCON0 (uae_u16 v) -{ - if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) { - if (!(currprefs.chipset_mask & CSMASK_ECS_DENISE)) - v &= 0x20; - new_beamcon0 = v; - } -} - -STATIC_INLINE void varsync (void) -{ - if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) - return; -#ifdef PICASSO96 - if (picasso_on) { - return; - } -#endif - if (!(beamcon0 & 0x80)) - return; - vpos_count = 0; -} - -static void BPLxPTH (int hpos, uae_u16 v, int num) -{ - decide_line (hpos); - decide_fetch_safe (hpos); - bplpt[num] = (bplpt[num] & 0x0000ffff) | ((uae_u32)v << 16); -} - -static void BPLxPTL (int hpos, uae_u16 v, int num) -{ - decide_line (hpos); - decide_fetch_safe (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 && num == 0 && is_bitplane_dma (hpos + 1) == num + 1) { - return; - } - bplpt[num] = (bplpt[num] & 0xffff0000) | (v & 0x0000fffe); -} - -static void BPLCON0_Denise (int hpos, uae_u16 v, bool immediate) -{ - if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) - v &= ~0x00F1; - else if (! (aga_mode)) - v &= ~0x00B0; - v &= ~(0x0200 | 0x0100 | 0x0080 | 0x0020); - - if (bplcon0d == v && !immediate) - return; - - bplcon0dd = -1; - - if (immediate) { - record_register_change (hpos, 0x100, v); - } else { - record_register_change (hpos, 0x100, (bplcon0d & ~(0x800 | 0x400 | 0x80)) | (v & (0x0800 | 0x400 | 0x80 | 0x01))); - } - - bplcon0d = v; - - if (currprefs.chipset_mask & CSMASK_ECS_DENISE) { - decide_sprites (hpos); - sprres = expand_sprres (v, bplcon3); - } - if (thisline_decision.plfleft < 0) - update_denise (hpos); -} - -static void BPLCON0 (int hpos, uae_u16 v) -{ - if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) - v &= ~0x00F1; - else if (! (aga_mode)) - v &= ~0x00B0; - v &= ~(0x0080 | 0x0020); - - if (bplcon0 == v) - return; - - line_cyclebased = vpos; - - bplcon0 = v; - - bpldmainitdelay (hpos); - - if (thisline_decision.plfleft < 0) - BPLCON0_Denise (hpos, v, true); -} - -STATIC_INLINE void BPLCON1 (int hpos, uae_u16 v) -{ - if (!(aga_mode)) - v &= 0xff; - if (bplcon1 == v) - return; - line_cyclebased = vpos; - decide_line (hpos); - decide_fetch_safe (hpos); - bplcon1_hpos = hpos; - bplcon1 = v; -} - -STATIC_INLINE void BPLCON2 (int hpos, uae_u16 v) -{ - if (!(aga_mode)) - v &= 0x7f; - if (bplcon2 == v) - return; - decide_line (hpos); - bplcon2 = v; - record_register_change (hpos, 0x104, v); -} - -STATIC_INLINE void BPLCON3 (int hpos, uae_u16 v) -{ - if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) - return; - if (!(aga_mode)) { - v &= 0x003f; - v |= 0x0c00; - } - if (bplcon3 == v) - return; - decide_line (hpos); - decide_sprites (hpos); - bplcon3 = v; - sprres = expand_sprres (bplcon0, bplcon3); - record_register_change (hpos, 0x106, v); -} - -STATIC_INLINE void BPLCON4 (int hpos, uae_u16 v) -{ - if (! (aga_mode)) - return; - if (bplcon4 == v) - return; - decide_line (hpos); - bplcon4 = v; - record_register_change (hpos, 0x10c, v); -} - -static void BPL1MOD (int hpos, uae_u16 v) -{ - v &= ~1; - if ((uae_s16)bpl1mod == (uae_s16)v) - return; - decide_line (hpos); - decide_fetch_safe (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_safe (hpos); - bpl2mod = v; -} - -/* Needed in special OCS/ECS "7-plane" mode, - * also handles CPU generated bitplane data - */ -static void BPLxDAT (int hpos, int num, uae_u16 v) -{ - // only BPL0DAT access can do anything visible - if (num == 0 && hpos >= 8) { - decide_line (hpos); - decide_fetch_safe (hpos); - } - bplxdat[num] = v; - if (num == 0 && hpos >= 8) { - bpl1dat_written = true; - bpl1dat_written_at_least_once = true; - if (thisline_decision.plfleft < 0) { - thisline_decision.plfleft = (hpos + 3) & ~3; - reset_bpl_vars (); - compute_delay_offset (); - } - update_bpldats (hpos); - } -} - -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 (!(aga_mode)) - 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; - line_cyclebased = vpos; - decide_line (hpos); - ddfstrt_old_hpos = hpos; - ddfstrt = v; - calcdiw (); -} - -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; - line_cyclebased = vpos; - decide_line (hpos); - decide_fetch_safe (hpos); - ddfstop = v; - calcdiw (); - if (fetch_state != fetch_not_started) - estimate_last_fetch_cycle (hpos); -} - -static void FMODE (int hpos, uae_u16 v) -{ - if (!(aga_mode)) - v = 0; - v &= 0xC00F; - if (fmode == v) - return; - line_cyclebased = vpos; - fmode = v; - sprite_width = GET_SPRITEWIDTH (fmode); - bpldmainitdelay (hpos); -} - -static void FNULL (uae_u16 v) -{ - -} - -static void BLTADAT (uae_u16 v) -{ - maybe_blit (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 (uae_u16 v) -{ - maybe_blit (0); - - if (bltcon1 & 2) - blt_info.bltbhold = v << (bltcon1 >> 12); - else - blt_info.bltbhold = v >> (bltcon1 >> 12); - blt_info.bltbdat = v; -} -STATIC_INLINE void BLTCDAT (uae_u16 v) { maybe_blit (0); blt_info.bltcdat = v; } - -STATIC_INLINE void BLTAMOD (uae_u16 v) { maybe_blit (1); blt_info.bltamod = (uae_s16)(v & 0xFFFE); } -STATIC_INLINE void BLTBMOD (uae_u16 v) { maybe_blit (1); blt_info.bltbmod = (uae_s16)(v & 0xFFFE); } -STATIC_INLINE void BLTCMOD (uae_u16 v) { maybe_blit (1); blt_info.bltcmod = (uae_s16)(v & 0xFFFE); } -STATIC_INLINE void BLTDMOD (uae_u16 v) { maybe_blit (1); blt_info.bltdmod = (uae_s16)(v & 0xFFFE); } - -STATIC_INLINE void BLTCON0 (uae_u16 v) { maybe_blit (2); bltcon0 = v; reset_blit (1); } - -/* The next category is "Most useless hardware register". - * And the winner is... */ -static void BLTCON0L (uae_u16 v) -{ - if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) - return; // ei voittoa. - maybe_blit (2); - bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF); - reset_blit (1); -} -STATIC_INLINE void BLTCON1 (uae_u16 v) { maybe_blit (2); bltcon1 = v; reset_blit (2); } - -STATIC_INLINE void BLTAFWM (uae_u16 v) { maybe_blit (2); blt_info.bltafwm = v; } -STATIC_INLINE void BLTALWM (uae_u16 v) { maybe_blit (2); blt_info.bltalwm = v; } - -STATIC_INLINE void BLTAPTH (uae_u16 v) -{ - maybe_blit (0); - bltapt = (bltapt & 0xffff) | ((uae_u32)v << 16); -} -STATIC_INLINE void BLTAPTL (uae_u16 v) -{ - maybe_blit (0); - bltapt = (bltapt & ~0xffff) | (v & 0xFFFE); -} -STATIC_INLINE void BLTBPTH (uae_u16 v) -{ - maybe_blit (0); - bltbpt = (bltbpt & 0xffff) | ((uae_u32)v << 16); -} -STATIC_INLINE void BLTBPTL (uae_u16 v) -{ - maybe_blit (0); - bltbpt = (bltbpt & ~0xffff) | (v & 0xFFFE); -} -STATIC_INLINE void BLTCPTH (uae_u16 v) -{ - maybe_blit (0); - bltcpt = (bltcpt & 0xffff) | ((uae_u32)v << 16); -} -STATIC_INLINE void BLTCPTL (uae_u16 v) -{ - maybe_blit (0); - bltcpt = (bltcpt & ~0xffff) | (v & 0xFFFE); -} -STATIC_INLINE void BLTDPTH (uae_u16 v) -{ - maybe_blit (0); - bltdpt = (bltdpt & 0xffff) | ((uae_u32)v << 16); -} -STATIC_INLINE void BLTDPTL (uae_u16 v) -{ - maybe_blit (0); - bltdpt = (bltdpt & ~0xffff) | (v & 0xFFFE); -} - -static void BLTSIZE (uae_u16 v) -{ - maybe_blit (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 (); -} - -static void BLTSIZV (uae_u16 v) -{ - if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) - return; - maybe_blit (0); - blt_info.vblitsize = v & 0x7FFF; -} - -static void BLTSIZH (uae_u16 v) -{ - if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) - return; - maybe_blit (0); - blt_info.hblitsize = v & 0x7FF; - if (!blt_info.vblitsize) - blt_info.vblitsize = 0x8000; - if (!blt_info.hblitsize) - blt_info.hblitsize = 0x0800; - do_blitter (); -} - -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 < sprite_vblank_endline) - return; - 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 (aga_mode) { - if(sprite_buffer_res) - sprxp |= ((sprctl[num] >> 4) & 1); - s->dblscan = sprpos[num] & 0x80; - } else if (sprite_buffer_res && currprefs.chipset_mask & CSMASK_ECS_DENISE) { - sprxp |= ((sprctl[num] >> 4) & 1); - } - s->xpos = sprxp; - s->vstart = sprpos[num] >> 8; - s->vstart |= (sprctl[num] & 0x04) ? 0x0100 : 0; - s->vstop = sprctl[num] >> 8; - s->vstop |= (sprctl[num] &0x02) ? 0x100 : 0; - if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) { - s->vstart |= (sprctl[num] & 0x40) ? 0x0200 : 0; - s->vstop |= (sprctl[num] & 0x20) ? 0x0200 : 0; - } - sprstartstop (s); -} - -STATIC_INLINE void SPRxCTL_1 (uae_u16 v, int num) -{ - struct sprite *s = &spr[num]; - sprctl[num] = v; - spr_arm (num, 0); - SPRxCTLPOS (num); -} -STATIC_INLINE void SPRxPOS_1 (uae_u16 v, int num) -{ - struct sprite *s = &spr[num]; - sprpos[num] = v; - SPRxCTLPOS (num); -} -STATIC_INLINE void SPRxDATA_1 (uae_u16 v, int num) -{ - sprdata[num][0] = v; - sprdata[num][1] = v; - sprdata[num][2] = v; - sprdata[num][3] = v; - spr_arm (num, 1); -} - -STATIC_INLINE void SPRxDATB_1 (uae_u16 v, int num) -{ - sprdatb[num][0] = v; - sprdatb[num][1] = v; - sprdatb[num][2] = v; - sprdatb[num][3] = v; -} -STATIC_INLINE void SPRxDATA (int hpos, uae_u16 v, int num) -{ - decide_sprites (hpos); - SPRxDATA_1 (v, num); -} -STATIC_INLINE void SPRxDATB (int hpos, uae_u16 v, int num) -{ - decide_sprites (hpos); - SPRxDATB_1 (v, num); -} - -STATIC_INLINE void SPRxCTL (int hpos, uae_u16 v, int num) -{ - decide_sprites (hpos); - SPRxCTL_1 (v, num); -} - -STATIC_INLINE void SPRxPOS (int hpos, uae_u16 v, int num) -{ - decide_sprites (hpos); - SPRxPOS_1 (v, num); -} - -static void SPRxPTH (int hpos, uae_u16 v, int num) -{ - decide_sprites (hpos); - if (hpos - 1 != MAXHPOS) { - spr[num].pt &= 0xffff; - spr[num].pt |= (uae_u32)v << 16; - } -} -static void SPRxPTL (int hpos, uae_u16 v, int num) -{ - decide_sprites (hpos); - if (hpos - 1 != MAXHPOS) { - spr[num].pt &= ~0xffff; - spr[num].pt |= v & ~1; - } -} - -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 (!(aga_mode)) - 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; -} - -static uae_u16 COLOR_READ (int num) -{ - int cr, cg, cb, colreg; - uae_u16 cval; - - if (!(aga_mode) || !(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); - } - return cval; -} - -static void COLOR_WRITE (int hpos, uae_u16 v, int num) -{ - v &= 0xFFF; - if (aga_mode) { - 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); - } - 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] = CONVERT_RGB (cval); - - } else { - 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] = xcolors[v]; - } -} - -/* 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) -{ - if (hpos + 1 >= maxhpos) // first refresh slot - return 1; - if ((hpos == maxhpos - 3) && (maxhpos & 1)) { - return -1; - } - return is_bitplane_dma (hpos); -} - - -/* The future, Conan? - We try to look ahead in the copper list to avoid doing continuous calls - to updat_copper (which is what happens when SPCFLAG_COPPER is set). If - we find that the same effect can be achieved by setting a delayed event - and then doing multiple copper insns in one batch, we can get a massive - speedup. - - We don't try to be precise here. All copper reads take exactly 2 cycles, - the effect of bitplane contention is ignored. Trying to get it exactly - right would be much more complex and as such carry a huge risk of getting - it subtly wrong; and it would also be more expensive - we want this code - to be fast. */ - -static void predict_copper (void) -{ - uaecptr ip = cop_state.ip; - unsigned int c_hpos = cop_state.hpos; - enum copper_states state = cop_state.state; - unsigned int w1, w2, cycle_count; - unsigned int modified = REGTYPE_FORCE; - - switch (state) { - case COP_read1: - break; - - case COP_read2: - w1 = cop_state.i1; - w2 = chipmem_wget_indirect (ip); - if (w1 & 1) { - if (w2 & 1) - return; // SKIP - state = COP_wait; // WAIT - c_hpos += 4; - } else { // MOVE - modified |= regtypes[w1 & 0x1FE]; - state = COP_read1; - c_hpos += 2; - } - ip += 2; - break; - - case COP_strobe_delay2: - case COP_strobe_delay2x: - case COP_start_delay: - c_hpos += 2; - state = COP_read1; - break; - - case COP_strobe_extra: - c_hpos += 6; - state = COP_read1; - break; - - case COP_strobe_delay1: - case COP_strobe_delay1x: - c_hpos += 4; - state = COP_read1; - break; - - case COP_stop: - case COP_waitforever: - case COP_bltwait: - case COP_skip_in2: - case COP_skip1: - return; - - case COP_wait_in2: - c_hpos += 2; - case COP_wait1: - w1 = cop_state.i1; - w2 = cop_state.i2; - state = COP_wait; - break; - - case COP_wait: - w1 = cop_state.i1; - w2 = cop_state.i2; - break; - - default: - return; - } - /* Only needed for COP_wait, but let's shut up the compiler. */ - cop_state.first_sync = c_hpos; - - while (c_hpos + 1 < maxhpos) { - if (state == COP_read1) { - w1 = chipmem_wget_indirect (ip); - if (w1 & 1) { - w2 = chipmem_wget_indirect (ip + 2); - if (w2 & 1) - break; // SKIP - state = COP_wait; // WAIT - c_hpos += 6; - } else { // MOVE - modified |= regtypes[w1 & 0x1FE]; - c_hpos += 4; - } - ip += 4; - } else { // state is COP_wait - if ((w2 & 0xFE) != 0xFE) - break; - else { - unsigned int vcmp = (w1 & (w2 | 0x8000)) >> 8; - unsigned int hcmp = (w1 & 0xFE); - - unsigned int vp = vpos & (((w2 >> 8) & 0x7F) | 0x80); - if (vp < vcmp) { - /* Whee. We can wait until the end of the line! */ - c_hpos = maxhpos; - } else if (vp > vcmp || hcmp <= c_hpos) { - state = COP_read1; - /* minimum wakeup time */ - c_hpos += 2; - } else { - state = COP_read1; - c_hpos = hcmp + 2; - } - /* If this is the current instruction, remember that we don't - need to sync CPU and copper anytime soon. */ - if (cop_state.ip == ip) { - cop_state.first_sync = c_hpos; - } - } - } - } - - cycle_count = c_hpos - cop_state.hpos; - if (cycle_count >= 8) { - cop_state.regtypes_modified = modified; - unset_special (SPCFLAG_COPPER); - eventtab[ev_copper].active = 1; - eventtab[ev_copper].evtime = get_cycles () + cycle_count * CYCLE_UNIT; - events_schedule (); - } -} - -STATIC_INLINE int custom_wput_copper (int hpos, uaecptr addr, uae_u32 value, int noget) -{ - int v; - - copper_access = 1; - v = custom_wput_1 (hpos, addr, value, noget); - copper_access = 0; - return v; -} - -// "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,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, /* 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, /* 8 */ - /* BPLxDAT */ - 0,0,0,0,0,0,0,0, /* 8 */ - /* SPRxPTH/SPRxPTL */ - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 16 */ - /* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */ - 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, - /* 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 update_copper (int until_hpos) -{ - int vp = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80); - int c_hpos = cop_state.hpos; - - if (nocustom()) { - eventtab[ev_copper].active = 0; - return; - } - - if(currprefs.fast_copper) { - if (eventtab[ev_copper].active) { - eventtab[ev_copper].active = 0; - return; - } - } - - if (cop_state.state == COP_wait && vp < cop_state.vcmp) { - eventtab[ev_copper].active = 0; - 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_safe (c_hpos); - - if (cop_state.movedelay) { // cop_state.movedelay is 0 or 1 - cop_state.movedelay = 0; - custom_wput_copper (c_hpos, cop_state.moveaddr, cop_state.movedata, 0); - if (! copper_enabled_thisline) - goto out; - } - - 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)) - continue; - cop_state.state = COP_wait1; - break; - case COP_skip_in2: - if (copper_cant_read (old_hpos)) - 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 - // Cycle is used and needs to be free. - if (copper_cant_read (old_hpos)) - continue; - if (old_hpos == maxhpos - 2) { - // if COP_strobe_delay2 would cross scanlines (positioned immediately - // after first strobe/refresh slot) it will disappear! - cop_state.state = COP_read1; - if (cop_state.strobe == 1) - cop_state.ip = cop1lc; - else - cop_state.ip = cop2lc; - cop_state.strobe = 0; - } else { - cop_state.state = COP_strobe_delay2; - cop_state.ip += 2; - } - break; - case COP_strobe_delay2: - // Second cycle after COPJMP. This is the strange one. - // This cycle does not need to be free - // But it still gets allocated by copper if it is free = CPU and blitter can't use it. - cop_state.state = COP_read1; - // 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_strobe_delay1x: - // First cycle after COPJMP and Copper was waiting. This is the buggy one. - // Cycle can be free and copper won't allocate it. - // If Blitter uses this cycle = Copper's address gets copied blitter DMA pointer.. - cop_state.state = COP_strobe_delay2x; - break; - case COP_strobe_delay2x: - // Second cycle fetches following word and tosses it away. Must be free cycle - // but is not allocated, blitter or cpu can still use it. - if (copper_cant_read (old_hpos)) - continue; - cop_state.state = COP_read1; - // 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: - // cycle after vblank strobe fetches word from old pointer first - if (copper_cant_read (old_hpos)) - continue; - cop_state.state = COP_read1; - cop_state.i1 = chipmem_wget_indirect (cop_state.ip); - cop_state.ip = cop1lc; - break; - - case COP_read1: - if (copper_cant_read (old_hpos)) - continue; - cop_state.i1 = chipmem_wget_indirect (cop_state.ip); - cop_state.ip += 2; - cop_state.state = COP_read2; - break; - - case COP_read2: - if (copper_cant_read (old_hpos)) - continue; - cop_state.i2 = chipmem_wget_indirect (cop_state.ip); - cop_state.ip += 2; - - 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; - } else { // MOVE - unsigned int reg = cop_state.i1 & 0x1FE; - uae_u16 data = cop_state.i2; - cop_state.state = COP_read1; - 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; - } - - 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 { - // 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 = 1; - } else { - custom_wput_copper (old_hpos, reg, data, 0); - } - } - } - break; - - case COP_wait1: - cop_state.state = COP_wait; - - cop_state.vcmp = (cop_state.i1 & (cop_state.i2 | 0x8000)) >> 8; - cop_state.hcmp = (cop_state.i1 & cop_state.i2 & 0xFE); - - vp = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80); - - if (cop_state.i1 == 0xFFFF && cop_state.i2 == 0xFFFE) { - cop_state.state = COP_waitforever; - 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: - { - int ch_comp = c_hpos; - if (ch_comp & 1) - ch_comp = 0; - if (copper_cant_read (old_hpos)) - continue; - - hp = ch_comp & (cop_state.i2 & 0xFE); - if (vp == cop_state.vcmp && hp < cop_state.hcmp) { - /* Position not reached yet. */ - if(currprefs.fast_copper) { - if ((cop_state.i2 & 0xFE) == 0xFE) { - int wait_finish = cop_state.hcmp - 2; - /* This will leave c_hpos untouched if it's equal to wait_finish. */ - if (wait_finish < c_hpos) - return; - else if (wait_finish <= until_hpos) { - c_hpos = wait_finish; - } else - c_hpos = until_hpos; - } - } - break; - } - - /* Now we know that the comparisons were successful. We might still - have to wait for the blitter though. */ - if ((cop_state.i2 & 0x8000) == 0) { - 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; - } - } - - 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)) - continue; - - vcmp = (cop_state.i1 & (cop_state.i2 | 0x8000)) >> 8; - hcmp = (cop_state.i1 & cop_state.i2 & 0xFE); - vp1 = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80); - hp1 = c_hpos & (cop_state.i2 & 0xFE); - - if ((vp1 > vcmp || (vp1 == vcmp && hp1 >= hcmp)) && ((cop_state.i2 & 0x8000) != 0 || bltstate == BLT_done)) - cop_state.ignore_next = 1; - - cop_state.state = COP_read1; - - break; - } - default: - break; - } - } - -out: - cop_state.hpos = c_hpos; - last_copper_hpos = until_hpos; - - if(currprefs.fast_copper) { - /* The test against maxhpos also prevents us from calling predict_copper - when we are being called from hsync_handler, which would not only be - stupid, but actively harmful. */ - if ((regs.spcflags & SPCFLAG_COPPER) && (c_hpos + 8 < maxhpos)) - predict_copper (); - } -} - -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_waitforever || cop_state.state == COP_bltwait || nocustom()) - return; - - if (cop_state.state == COP_wait) { - int vp = vpos & (((cop_state.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 & ~1)) - hpos = maxhpos & ~1; - cop_state.hpos = hpos; - } - - // if COPJMPx was written while DMA was disabled, advance to next state, - // COP_strobe_extra is single cycle only and does not need free bus. - // (copper state emulation does not run if DMA is disabled) - if (!wasenabled && cop_state.state == COP_strobe_extra) - cop_state.state = COP_strobe_delay1; - - copper_enabled_thisline = 1; - - if(currprefs.fast_copper) { - predict_copper (); - if (! eventtab[ev_copper].active) - set_special (SPCFLAG_COPPER); - } - else - set_special (SPCFLAG_COPPER); -} - -static void copper_handler (void) -{ - /* This will take effect immediately, within the same cycle. */ - set_special (SPCFLAG_COPPER); - - if (! copper_enabled_thisline) - return; - - eventtab[ev_copper].active = 0; -} - -/* -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 (void) -{ - int vp = vpos; - - if (cop_state.state != COP_bltwait) - return; - - int hpos = current_hpos() + 3; - hpos &= ~1; - if (hpos >= maxhpos) { - hpos -= maxhpos; - vp++; - } - cop_state.hpos = hpos; - cop_state.state = COP_read1; - - 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, unsigned int addr) -{ - if (eventtab[ev_copper].active) { - if (hpos != maxhpos) { - /* There might be reasons why we don't actually need to bother - updating the copper. */ - if (hpos < cop_state.first_sync) - return; - - if ((cop_state.regtypes_modified & regtypes[addr & 0x1FE]) == 0) - return; - } - - eventtab[ev_copper].active = 0; - if (do_schedule) - events_schedule (); - set_special (SPCFLAG_COPPER); - } - - /* Need to let the copper advance to the current position. */ - if (copper_enabled_thisline) - update_copper (hpos); -} - -STATIC_INLINE uae_u16 sprite_fetch (struct sprite *s) -{ - last_custom_value1 = chipmem_wget_indirect (s->pt); - s->pt += 2; - return last_custom_value1; -} -STATIC_INLINE uae_u16 sprite_fetch2(struct sprite *s) -{ - uae_u16 data = 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); - - if (isdma && s->dblscan && (fmode & 0x8000) && (vpos & 1) != (s->vstart & 1) && s->dmastate) { - spr_arm (num, 1); - return; - } - if (vpos == s->vstart) { - s->dmastate = 1; - } - if (vpos == s->vstop || vpos == sprite_vblank_endline) { - s->dmastate = 0; - } - - if (!isdma) - return; - - 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); - switch (sprite_width) - { - case 64: - s->pt += 4; - case 32: - s->pt += 2; - break; - } - if (cycle == 0) { - SPRxPOS_1 (data, num); - s->dmacycle = 1; - } else { - SPRxCTL_1 (data, num); - s->dmastate = 0; - sprstartstop (s); - } - } - if (vpos == sprite_vblank_endline) { - // s->vstart == sprite_vblank_endline won't enable the sprite. - s->dmastate = 0; - } - } - - if (s->dmastate && !posctl && dma) { - uae_u16 data; - - data = sprite_fetch (s); - if (cycle == 0) { - SPRxDATA_1 (data, num); - s->dmacycle = 1; - } else { - SPRxDATB_1 (data, num); - spr_arm (num, 1); - } - switch (sprite_width) - { - case 64: - { - uae_u16 data32 = sprite_fetch2 (s); - uae_u16 data641 = sprite_fetch2 (s); - uae_u16 data642 = sprite_fetch2 (s); - 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); - if (cycle == 0) - sprdata[num][1] = data32; - else - sprdatb[num][1] = data32; - } - break; - } - } -} - -static void do_sprites (int hpos) -{ - int maxspr, minspr; - int i; - - if (vpos == 0) { - // It is also possible armed status is cleared - // I am not sure so lets start with clearing - // data only - if (dmaen (DMA_SPRITE)) { - for (i = 0; i < MAX_SPRITES; i++) { - sprdata[i][0] = 0; - sprdatb[i][0] = 0; - sprdata[i][1] = 0; - sprdatb[i][1] = 0; - sprdata[i][2] = 0; - sprdatb[i][2] = 0; - sprdata[i][3] = 0; - sprdatb[i][3] = 0; - } - } - return; - } - if (vpos < sprite_vblank_endline) - return; - - 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) { - do_sprites_1 (num, cycle, i); - } - } - - last_sprite_hpos = hpos; -} - -static void init_sprites (void) -{ - memset (sprpos, 0, sizeof sprpos); - memset (sprctl, 0, sizeof sprctl); -} - -static void init_hardware_frame (void) -{ - next_lineno = 0; - prev_lineno = -1; - diwstate = DIW_waiting_start; - ddfstate = DIW_waiting_start; - plf_state = plf_end; -} - -void init_hardware_for_drawing_frame (void) -{ - /* Avoid this code in the first frame after a customreset. */ - if (next_sprite_entry > 0) { - int npixels = curr_sprite_entries[next_sprite_entry].first_pixel; - memset(spixels, 0, npixels * sizeof *spixels); - memset(spixstate.bytes, 0, npixels * sizeof *spixstate.bytes); - } - - next_color_change = 0; - next_sprite_entry = 0; - next_color_entry = 0; - remembered_color_entry = -1; - - curr_sprite_entries[0].first_pixel = 0; - next_sprite_forced = 1; -} - -STATIC_INLINE void rtg_vsync (void) -{ -#ifdef PICASSO96 - picasso_handle_vsync (); -#endif -} - -void fpscounter_reset (void) -{ - timeframes = 0; - frametime = 0; - lastframetime = read_processor_time (); -} - -static void fpscounter (void) -{ - frame_time_t now, last; - const int mcnt = 20; - - now = read_processor_time (); - last = now - lastframetime; - lastframetime = now; - - frametime += last; - timeframes++; - - if ((timeframes % mcnt) == 0) { - int fps = frametime == 0 ? 0 : (syncbase * mcnt) / (frametime / 10); - if(currprefs.gfx_framerate > 0) - fps = fps / (currprefs.gfx_framerate + 1); - if (fps > 9999) - fps = 9999; - gui_data.fps = (fps + 5) / 10; - frametime = 0; - } -} - -// vsync functions that are not hardware timing related -static void vsync_handler_pre (void) -{ - handle_events (); - -#ifdef PICASSO96 - if (currprefs.rtgmem_size) - rtg_vsync (); -#endif - - blkdev_vsync (); - CIA_vsync_prehandler(); - - if (quit_program > 0) { - /* prevent possible infinite loop at wait_cycles().. */ - framecnt = 0; - reset_decisions (); - return; - } - - if (timehack_alive > 0) - timehack_alive--; - - inputdevice_vsync (); - filesys_vsync (); - - vsync_handle_redraw (); - - fpscounter(); - - vsync_handle_check (); -} - -// emulated hardware vsync -static void vsync_handler_post (void) -{ - DISK_vsync (); - - if (bplcon0 & 4) { - lof_store = lof_store ? 0 : 1; - } - - lof_current = lof_store; - if (lof_changing) { - // still same? Trigger change now. - if ((!lof_store && lof_changing < 0) || (lof_store && lof_changing > 0)) { - lof_changed_previous_field++; - lof_changed = 1; - // lof toggling? decide as interlace. - if (lof_changed_previous_field >= LOF_TOGGLES_NEEDED) { - lof_changed_previous_field = LOF_TOGGLES_NEEDED; - if (lof_lace == false) - lof_lace = true; - else - lof_changed = 0; - } - if (bplcon0 & 4) - lof_changed = 0; - } - lof_changing = 0; - } else { - lof_changed_previous_field = 0; - lof_lace = false; - } - -#ifdef JIT - if (currprefs.cachesize) { - vsyncmintime = last_synctime; - frh_count = 0; - } - else -#endif - { - vsyncmintime = last_synctime + vsynctimebase * (maxvpos_display - LAST_SPEEDUP_LINE) / maxvpos_display; - } - - if ((beamcon0 & (0x20 | 0x80)) != (new_beamcon0 & (0x20 | 0x80))) { - init_hz (); - } else if (vpos_count > 0 && abs (vpos_count - vpos_count_diff) > 1 && vposw_change < 4) { - init_hz (); - } else if (lof_changed) { - compute_framesync (); - } - - lof_changed = 0; - vposw_change = 0; - - eventtab[ev_copper].active = 0; - COPJMP (1, 1); - - init_hardware_frame (); -} - -#ifdef JIT - -#define N_LINES 8 - -STATIC_INLINE int trigger_frh (int v) -{ - return (v & (N_LINES - 1)) == 0; -} - -STATIC_INLINE 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 < 0) { - frame_time_t curr_time = read_processor_time (); - vsyncmintime += vsynctimebase * N_LINES / maxvpos_display; - if (rpt_did_reset) { - vsyncmintime = curr_time + vsynctimebase; - rpt_did_reset = 0; - } - /* Allow this to be one frame's worth of cycles out */ - while (diff32 (curr_time, vsyncmintime + vsynctimebase) > 0) { - vsyncmintime += vsynctimebase * N_LINES / maxvpos_display; - } - } -} -#endif - -static uae_u16 dmal, dmal_hpos; - -static void dmal_func2 (void) -{ - event_remevent(ev_dmal); - - // Disk and Audio DMA bits are ignored by Agnus, Agnus only checks DMAL and master bit - if (!(dmacon & DMA_MASTER)) - return; - - while (dmal) { - if (dmal & 3) { - if (dmal_hpos >= 6) { - int nr = (dmal_hpos - 6) >> 1; - audio_dmal_do (nr, (dmal & 2) ? true : false); - } else { - uae_u16 dat; - int w = (dmal & 2) ? 1 : 0; - uaecptr pt = disk_getpt (); - // disk_fifostatus() needed in >100% disk speed modes - if (w) { - // write to disk - if (disk_fifostatus () <= 0) { - dat = chipmem_wget_indirect (pt); - DSKDAT (dat); - } - } else { - // read from disk - if (disk_fifostatus () >= 0) { - dat = DSKDATR (); - chipmem_wput_indirect (pt, dat); - } - } - } - } - - dmal_hpos += 2; - dmal >>= 2; - } -} - -STATIC_INLINE void events_dmal (void) -{ - if (currprefs.cachesize) { - dmal_func2 (); - } else { - event_newevent (ev_dmal, 7); - } -} - -static void events_dmal_hsync (void) -{ - uae_u16 dmal_disk = disk_dmal (); - - dmal = audio_dmal (); - if (!dmal && !dmal_disk) - return; - - if(dmal_disk) - { - dmal_hpos = 0; - dmal <<= 6; - dmal |= dmal_disk; - } - else - dmal_hpos = 6; - events_dmal (); -} - -static bool is_custom_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) -{ - eventtab[ev_hsync].evtime = get_cycles () + HSYNCTIME; - eventtab[ev_hsync].oldcycles = get_cycles (); -} - -// this finishes current line -static void hsync_handler_pre (bool onvsync) -{ - int hpos = current_hpos (); - - if (!nocustom()) { - /* Using 0x8A makes sure that we don't accidentally trip over the - modified_regtypes check. */ - sync_copper_with_cpu (maxhpos, 0, 0x8A); - finish_decisions (); - if (thisline_decision.plfleft >= 0) { - if (currprefs.collision_level > 1) - DO_SPRITE_COLLISIONS - if (currprefs.collision_level > 2) - DO_PLAYFIELD_COLLISIONS - } - hsync_record_line_state (next_lineno); - } - -#ifdef CD32 - AKIKO_hsync_handler (); -#endif - -#ifdef PICASSO96 - picasso_handle_hsync (); -#endif - - DISK_hsync (); - if (currprefs.produce_sound) - audio_hsync (); - - hsync_counter++; - - vpos++; - vpos_count++; - if (vpos >= maxvpos_total) - vpos=0; - if (onvsync) { - vpos = 0; - } - set_hpos (); -} - -// this prepares for new line -static void hsync_handler_post (bool onvsync) -{ - last_copper_hpos = 0; - - CIA_hsync_posthandler (); - if (onvsync) { - CIA_vsync_posthandler (); - } - - inputdevice_hsync (); - - last_custom_value1 = 0xffff; // refresh slots should set this to 0xffff - - if (!nocustom()) { - if (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][bplcon0_res][bplcon0_planes_limit], - cycle_diagram_free_cycles[fetchmode][bplcon0_res][bplcon0_planes_limit]); - } - } - - if (onvsync) { - vpos = 0; - vsync_handler_post (); - vpos_count = 0; - } - - if (vpos == 0) - send_interrupt (5); - - events_dmal_hsync (); - - if (currprefs.m68k_speed < 0) { -#ifdef JIT - if (currprefs.cachesize) { - frh_count++; - if (trigger_frh(frh_count) && vpos < maxvpos_display - LAST_SPEEDUP_LINE) { - frh_handler(); - } - is_syncline = trigger_frh(frh_count+1) && vpos < maxvpos_display - LAST_SPEEDUP_LINE + 1 && !rpt_did_reset; - } else { -#endif - is_syncline = vpos + 1 == maxvpos_display + lof_store - LAST_SPEEDUP_LINE; -#ifdef JIT - } -#endif - } - - if (!nocustom()) { - int lineno = vpos; - if (lineno >= MAXVPOS) - lineno %= MAXVPOS; - prev_lineno = next_lineno; - next_lineno = lineno; - reset_decisions (); - } - - if (uae_int_requested) { - INTREQ (0x8000 | 0x0008); - } - - { - extern void bsdsock_fake_int_handler (void); - extern int volatile bsd_int_requested; - if (bsd_int_requested) - bsdsock_fake_int_handler (); - } - - /* Default to no bitplane DMA overriding sprite DMA */ - plfstrt_sprite = 0xff; - /* See if there's a chance of a copper wait ending this line. */ - cop_state.hpos = 0; - compute_spcflag_copper (maxhpos); -} - -static void init_regtypes (void) -{ - int i; - for (i = 0; i < 512; i += 2) { - regtypes[i] = REGTYPE_ALL; - if ((i >= 0x20 && i < 0x28) || i == 0x08 || i == 0x7E) - regtypes[i] = REGTYPE_DISK; - else if (i >= 0x68 && i < 0x70) - regtypes[i] = REGTYPE_NONE; - else if (i >= 0x40 && i < 0x78) - regtypes[i] = REGTYPE_BLITTER; - else if (i >= 0xA0 && i < 0xE0 && (i & 0xF) < 0xE) - regtypes[i] = REGTYPE_AUDIO; - else if (i >= 0xA0 && i < 0xE0) - regtypes[i] = REGTYPE_NONE; - else if (i >= 0xE0 && i < 0x100) - regtypes[i] = REGTYPE_PLANE; - else if (i >= 0x120 && i < 0x180) - regtypes[i] = REGTYPE_SPRITE; - else if (i >= 0x180 && i < 0x1C0) - regtypes[i] = REGTYPE_COLOR; - else switch (i) { - case 0x02: - /* DMACONR - setting this to REGTYPE_BLITTER will cause it to - conflict with DMACON (since that is REGTYPE_ALL), and the - blitter registers (for the BBUSY bit), but nothing else, - which is (I think) what we want. */ - regtypes[i] = REGTYPE_BLITTER; - break; - case 0x04: case 0x06: case 0x2A: case 0x2C: - regtypes[i] = REGTYPE_POS; - break; - case 0x0A: case 0x0C: - case 0x0E: - regtypes[i] = REGTYPE_SPRITE; - break; - case 0x10: - regtypes[i] = REGTYPE_AUDIO; - break; - case 0x12: case 0x14: case 0x16: - case 0x36: - regtypes[i] = REGTYPE_JOYPORT; - break; - case 0x1A: - regtypes[i] = REGTYPE_DISK; - break; - case 0x102: - case 0x104: - case 0x106: - case 0x108: - case 0x10A: - regtypes[i] = REGTYPE_PLANE; - break; - case 0x88: case 0x8A: - case 0x8E: case 0x90: case 0x92: case 0x94: - case 0x96: - case 0x100: - regtypes[i] |= REGTYPE_FORCE; - break; - } - } -} - -static void hsync_handler (void) -{ - bool vs = is_custom_vsync (); - hsync_handler_pre (vs); - if (vs) { - vsync_handler_pre (); - if (savestate_check ()) { - uae_reset (0, 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_copper].handler = copper_handler; - eventtab[ev_copper].active = 0; - eventtab[ev_misc].handler = MISC_handler; - eventtab[ev_audio].handler = audio_evhandler; - eventtab[ev_blitter].handler = blitter_handler; - eventtab[ev_dmal].handler = dmal_func2; - - eventtab2[ev2_disk].handler = DISK_handler; - eventtab2[ev2_disk_motor0].handler = DISK_motordelay_func; - eventtab2[ev2_disk_motor1].handler = DISK_motordelay_func; - eventtab2[ev2_disk_motor2].handler = DISK_motordelay_func; - eventtab2[ev2_disk_motor3].handler = DISK_motordelay_func; - - events_schedule (); -} - -void custom_prepare (void) -{ - set_hpos (); - hsync_handler_post (true); -} - -void custom_reset (bool hardreset, bool keyboardreset) -{ - int i; - int zero = 0; - - target_reset (); - reset_all_systems (); - write_log (_T("Reset at %08X. Chipset mask = %08X\n"), M68K_GETPC, currprefs.chipset_mask); - - nr_armed = 0; - - if (! savestate_state) { - hsync_counter = 0; - currprefs.chipset_mask = changed_prefs.chipset_mask; - update_mirrors(); - - if (hardreset) { - if (!aga_mode) { - uae_u16 c = ((currprefs.chipset_mask & CSMASK_ECS_DENISE) && !(currprefs.chipset_mask & CSMASK_AGA)) ? 0xfff : 0x000; - for (i = 0; i < 32; i++) { - current_colors.color_regs_ecs[i] = c; - current_colors.acolors[i] = getxcolor (c); - } - } else { - uae_u32 c = 0; - for (i = 0; i < 256; i++) { - current_colors.color_regs_aga[i] = c; - current_colors.acolors[i] = getxcolor (c); - } - } - } - clxdat = 0; - - /* Clear the armed flags of all sprites. */ - memset (spr, 0, sizeof spr); - - dmacon = 0; - intreq = 0; - intena = 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 - - // Some more values to reset... - bltcon0 = bltcon1 = bltapt = bltbpt = bltcpt = bltdpt = 0; - memset(&blt_info, 0, sizeof blt_info); - last_custom_value1 = 0; - last_synctime = 0; - frh_count = 0; - intreq = 0; - cop1lc = cop2lc = 0; - bpl1mod = bpl2mod = 0; - bplcon1 = bplcon2 = 0; - diwstrt = diwstop = ddfstrt = ddfstop = ddfstrt_old_hpos = 0; - vsstop = 0; - copper_enabled_thisline = 0; - estimated_last_fetch_cycle = 0; - - FMODE (0, 0); - CLXCON (0); - CLXCON2 (0); - setup_fmodes (0); - sprite_width = GET_SPRITEWIDTH (fmode); - beamcon0 = new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20; - bltstate = BLT_done; - lof_store = lof_current = 0; - lof_lace = false; - init_sprites (); - } - -#ifdef AUTOCONFIG - expamem_reset (); -#endif - DISK_reset (); - CIA_reset (); -#ifdef JIT - compemu_reset (); -#endif - unset_special (~(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE)); - - vpos = 0; - vpos_count = vpos_count_diff = 0; - - inputdevice_reset(); - timehack_alive = 0; - - curr_sprite_entries[0].first_pixel = 0; - curr_sprite_entries[1].first_pixel = 0; - next_sprite_entry = 0; - memset (spixels, 0, sizeof spixels); - memset (&spixstate, 0, sizeof spixstate); - - cop_state.state = COP_stop; - cop_state.movedelay = 0; - cop_state.strobe = 0; - cop_state.ignore_next = 0; - diwstate = DIW_waiting_start; - - dmal = 0; - new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20; - time_per_frame = 1000 * 1000 / (currprefs.ntscmode ? 60 : 50); - init_hz_full (); - lof_changing = 0; - - audio_reset (); - if (!isrestore ()) { - /* must be called after audio_reset */ - adkcon = 0; - audio_update_adkmasks (); - } - - init_hardware_frame (); - drawing_init (); - - reset_decisions (); - - init_regtypes (); - - if (isrestore ()) { - uae_u16 v; - uae_u32 vv; - - audio_update_adkmasks (); - INTENA (0); - INTREQ (0); - - if (diwhigh) - diwhigh_written = 1; - else - diwhigh_written = 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] = (unsigned short)-1; - record_color_change (0, i, vv); - remembered_color_entry = -1; - current_colors.color_regs_ecs[i] = vv; - current_colors.acolors[i] = xcolors[vv]; - } - } 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); - } - } - CLXCON (clxcon); - CLXCON2 (clxcon2); - calcdiw (); - for (i = 0; i < 8; i++) { - SPRxCTLPOS (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); - - if (hardreset) - rtc_hardreset(); - -#ifdef PICASSO96 - picasso_reset (); -#endif -} - - -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; -} - -static int allocate_sprite_tables (void) -{ - if(curr_sprite_entries == 0) - curr_sprite_entries = xmalloc (struct sprite_entry, MAX_SPR_PIXELS / 16); - if(curr_color_changes == 0) - curr_color_changes = xmalloc (struct color_change, MAX_REG_CHANGE); - - return 1; -} - -int custom_init (void) -{ - if (!allocate_sprite_tables()) - return 0; - -#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 (); - - next_sprite_entry = 0; - 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, _T("Custom chipset"), - custom_lgeti, custom_wgeti, ABFLAG_IO, 0x1ff, 0xdff000 -}; - -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 (uaecptr addr, int noput) -{ - uae_u16 v; - int missing; -#ifdef JIT - special_mem |= S_READ; -#endif - switch (addr & 0x1fe) { - case 0x000: v = 0xffff; break; /* BPLDDAT */ - case 0x002: v = DMACONR (current_hpos ()); break; - case 0x004: v = VPOSR (); break; - case 0x006: v = VHPOSR (); break; - - case 0x008: v = 0xffff; 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 = 0x3000 /* no data */; break; - case 0x01A: v = DSKBYTR (current_hpos ()); break; - case 0x01C: v = INTENAR (); break; - case 0x01E: v = INTREQR (); break; - case 0x07C: - v = DENISEID (&missing); - if (missing) - goto writeonly; - 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: - if (!(aga_mode)) - goto writeonly; - v = COLOR_READ ((addr & 0x3E) / 2); - break; - - default: -writeonly: - /* 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: - * Can also return last CPU accessed value - * Remembers old last_custom_value1 - */ - v = last_custom_value1; - line_cyclebased = vpos; - if (!noput) { - uae_u16 l; - int hpos = current_hpos (); - if (aga_mode) { - l = 0; - } else { - l = currprefs.cpu_compatible && currprefs.cpu_model == 68000 ? regs.irc : 0xffff; - } - decide_line (hpos); - decide_fetch_safe (hpos); - custom_wput_1 (hpos, addr, l, 1); - } - return v; - } - return v; -} - -STATIC_INLINE uae_u32 custom_wget2 (uaecptr addr) -{ - uae_u32 v; - int hpos = current_hpos (); - - sync_copper_with_cpu (hpos, 1, addr); - v = custom_wget_1 (addr, 0); - 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 & 0xFFFF; - } - 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 & 0xFF; -} - -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) -{ - addr &= 0x1FE; - value &= 0xffff; - 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). Writing to DMA write registers won't do anything */; break; - - case 0x02A: VPOSW (value); break; - case 0x02C: VHPOSW (value); break; - case 0x02E: COPCON (value); break; - case 0x030: break; - case 0x032: break; - case 0x034: POTGO (value); break; - case 0x036: JOYTEST (value); break; - - case 0x040: BLTCON0 (value); break; - case 0x042: BLTCON1 (value); break; - - case 0x044: BLTAFWM (value); break; - case 0x046: BLTALWM (value); break; - - case 0x048: BLTCPTH (value); break; - case 0x04A: BLTCPTL (value); break; - case 0x04C: BLTBPTH (value); break; - case 0x04E: BLTBPTL (value); break; - case 0x050: BLTAPTH (value); break; - case 0x052: BLTAPTL (value); break; - case 0x054: BLTDPTH (value); break; - case 0x056: BLTDPTL (value); break; - - case 0x058: BLTSIZE (value); break; - case 0x05A: BLTCON0L (value); break; - case 0x05C: BLTSIZV (value); break; - case 0x05E: BLTSIZH (value); break; - - case 0x064: BLTAMOD (value); break; - case 0x062: BLTBMOD (value); break; - case 0x060: BLTCMOD (value); break; - case 0x066: BLTDMOD (value); break; - - case 0x070: BLTCDAT (value); break; - case 0x072: BLTBDAT (value); break; - case 0x074: BLTADAT (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; - case 0x106: BPLCON3 (hpos, value); break; - - case 0x108: BPL1MOD (hpos, value); break; - case 0x10A: BPL2MOD (hpos, value); break; - case 0x10C: BPLCON4 (hpos, value); break; - case 0x10E: CLXCON2 (value); break; - - 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 0x1C0: if (htotal != value) { htotal = value & (MAXHPOS_ROWS - 1); varsync (); } break; - case 0x1C2: if (hsstop != value) { hsstop = value & (MAXHPOS_ROWS - 1); varsync (); } break; - case 0x1C4: if (hbstrt != value) { hbstrt = value & (MAXHPOS_ROWS - 1); varsync (); } break; - case 0x1C6: if (hbstop != value) { hbstop = value & (MAXHPOS_ROWS - 1); varsync (); } break; - case 0x1C8: if (vtotal != value) { vtotal = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; - case 0x1CA: if (vsstop != value) { vsstop = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; - case 0x1CC: if (vbstrt < value || vbstrt > (value & (MAXVPOS_LINES_ECS - 1)) + 1) { vbstrt = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; - case 0x1CE: if (vbstop < value || vbstop > (value & (MAXVPOS_LINES_ECS - 1)) + 1) { vbstop = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; - case 0x1DC: BEAMCON0 (value); break; - case 0x1DE: if (hsstrt != value) { hsstrt = value & (MAXHPOS_ROWS - 1); varsync (); } break; - case 0x1E0: if (vsstrt != value) { vsstrt = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; - case 0x1E2: if (hcenter != value) { hcenter = value & (MAXHPOS_ROWS - 1); varsync (); } break; - case 0x1E4: DIWHIGH (hpos, value); break; - - case 0x1FC: FMODE (hpos, value); break; - case 0x1FE: FNULL (value); break; - - /* writing to read-only register causes read access */ - default: - if (!noget) { - custom_wget_1 (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 - sync_copper_with_cpu (hpos, 1, addr); - 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) -{ - uae_u16 rval; - - if (aga_mode) { - 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 - 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, ru16; - int dskpt; - int i; - - audio_reset (); - - changed_prefs.chipset_mask = currprefs.chipset_mask = RL & CSMASK_MASK; - update_mirrors(); - blt_info.bltddat = RW; /* 000 BLTDDAT */ - ru16 = RW; /* 002 DMACONR -> see also 096 */ - if((ru16 & 0x4000) == 0) - bltstate = BLT_done; - blt_info.blitzero = (ru16 & 0x2000 ? 1 : 0); - 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 -> see 09E */ - RW; /* 012 POT0DAT */ - RW; /* 014 POT1DAT */ - RW; /* 016 POTINP -> see 034 */ - RW; /* 018 SERDATR* */ - dskbytr = RW; /* 01A DSKBYTR */ - RW; /* 01C INTENAR -> see 09A */ - RW; /* 01E INTREQR -> see 09C */ - 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; /* 02A VPOSW */ - RW; /* 02C VHPOSW */ - COPCON(RW); /* 02E COPCON */ - RW; /* 030 SERDAT */ - RW; /* 032 SERPER* */ - potgo_value = 0; POTGO (RW); /* 034 POTGO */ - RW; /* 036 JOYTEST* */ - RW; /* 038 STREQU */ - RW; /* 03A STRVHBL */ - RW; /* 03C STRHOR */ - RW; /* 03E STRLONG */ - BLTCON0(RW); /* 040 BLTCON0 */ - BLTCON1(RW); /* 042 BLTCON1 */ - BLTAFWM(RW); /* 044 BLTAFWM */ - BLTALWM(RW); /* 046 BLTALWM */ - bltcpt = RL; /* 048-04B BLTCPT */ - bltbpt = RL; /* 04C-04F BLTBPT */ - bltapt = RL; /* 050-053 BLTAPT */ - bltdpt = RL; /* 054-057 BLTDPT */ - RW; /* 058 BLTSIZE */ - RW; /* 05A BLTCON0L -> see 040 */ - blt_info.vblitsize=RW; /* 05C BLTSIZV */ - blt_info.hblitsize=RW; /* 05E BLTSIZH */ - blt_info.bltcmod = RW; /* 060 BLTCMOD */ - blt_info.bltbmod = RW; /* 062 BLTBMOD */ - blt_info.bltamod = RW; /* 064 BLTAMOD */ - blt_info.bltdmod = RW; /* 066 BLTDMOD */ - RW; /* 068 ? */ - RW; /* 06A ? */ - RW; /* 06C ? */ - RW; /* 06E ? */ - blt_info.bltcdat =RW; /* 070 BLTCDAT */ - BLTBDAT(RW); /* 072 BLTBDAT */ - blt_info.bltadat=RW; /* 074 BLTADAT */ - RW; /* 076 ? */ - RW; /* 078 ? */ - RW; /* 07A ? */ - RW; /* 07C LISAID */ - ru16=RW; DSKSYNC(-1, ru16); /* 07E DSKSYNC */ - cop1lc = RL; /* 080/082 COP1LC */ - cop2lc = RL; /* 084/086 COP2LC */ - RW; /* 088 COPJMP1 */ - RW; /* 08A COPJMP2 */ - RW; /* 08C COPINS */ - 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 = RW; /* 09A INTENA */ - intreq = RW; /* 09C INTREQ */ - adkcon = RW; /* 09E ADKCON */ - /* 0A0 - 0DE Audio regs */ - for (i = 0; i < 8; 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 */ - /* 120 - 17E Sprite regs */ - for(i = 0; i < 32; i++) - current_colors.color_regs_ecs[i] = RW; /* 180-1BE 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 ? */ - - current_colors.borderblank = isbrdblank (-1, bplcon0, bplcon3); - DISK_restore_custom (dskpt, dsklen, dskbytr); - - FMODE (0, fmode); - - return src; -} - -#define SB save_u8 -#define SW save_u16 -#define SL save_u32 - -uae_u8 *save_custom (int *len, uae_u8 *dstptr, int full) -{ - uae_u8 *dstbak, *dst; - int i, dummy; - 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 (blt_info.bltddat); /* 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)); /* 02A VPOSW */ - SW (0); /* 02C VHPOSW */ - SW (copcon); /* 02E COPCON */ - SW (0); /* 030 SERDAT * */ - SW (0); /* 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 BLTBPT */ - SL (bltapt); /* 050-053 BLTAPT */ - SL (bltdpt); /* 054-057 BLTDPT */ - 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 (&dummy)); /* 07C DENISEID/LISAID */ - SW (dsksync); /* 07E DSKSYNC */ - SL (cop1lc); /* 080-083 COP1LC */ - SL (cop2lc); /* 084-087 COP2LC */ - SW (0); /* 088 COPJMP1 */ - SW (0); /* 08A COPJMP2 */ - SW (0); /* 08C COPINS */ - 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 */ - /* 0A0 - 0DE Audio regs */ - 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 */ - /* 120 - 17E Sprite regs */ - if (full) { - for (i = 0; i < 8; i++) { - SL (spr[i].pt); /* 120-13E SPRxPT */ - } - for (i = 0; i < 8; i++) { - 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++) - 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; -} - -uae_u8 *restore_custom_agacolors (uae_u8 *src) -{ - int i; - - for (i = 0; i < 256; i++) { - uae_u32 v = RL; - current_colors.color_regs_aga[i] = v; - } - 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++) - SL (current_colors.color_regs_aga[i]); - *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 & 1; - 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; - cia_set_overlay ((v & 2) != 0); - - currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p = RBB; - currprefs.cs_cd32cd = changed_prefs.cs_cd32cd = RBB; - currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram = 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 ((&get_mem_bank (0) != &chipmem_bank ? 2 : 0) | 1); - - SB (currprefs.cs_cd32c2p); - SB (currprefs.cs_cd32cd); - SB (currprefs.cs_cd32nvram); - - *len = dst - dstbak; - return dstbak; -} - -#endif /* SAVESTATE */ - -void check_prefs_changed_custom (void) -{ - currprefs.gfx_framerate = changed_prefs.gfx_framerate; - if (inputdevice_config_change_test ()) - inputdevice_copyconfig (&changed_prefs, &currprefs); - currprefs.immediate_blits = changed_prefs.immediate_blits; - currprefs.waiting_blits = changed_prefs.waiting_blits; - currprefs.collision_level = changed_prefs.collision_level; - currprefs.fast_copper = changed_prefs.fast_copper; - currprefs.cs_cd32cd = changed_prefs.cs_cd32cd; - currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p; - currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram; - - if (currprefs.chipset_mask != changed_prefs.chipset_mask || - currprefs.ntscmode != changed_prefs.ntscmode) { - 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 (); - } -} + /* + * 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 "memory.h" +#include "custom.h" +#include "newcpu.h" +#include "cia.h" +#include "disk.h" +#include "savestate.h" +#include "blitter.h" +#include "xwin.h" +#include "inputdevice.h" +#include "keybuf.h" +#include "autoconf.h" +#include "traps.h" +#include "gui.h" +#include "picasso96.h" +#include "drawing.h" +#include "akiko.h" +#include "gfxboard.h" +#include "blkdev.h" + +#define SPR0_HPOS 0x15 +#define MAX_SPRITES 8 + +#define nocustom() (picasso_on) + +extern int screen_is_picasso; + +unsigned long last_synctime = 0; +static int frh_count = 0; +#define LAST_SPEEDUP_LINE 30 +#define SPEEDUP_CYCLES_JIT 2800 +#define SPEEDUP_CYCLES_NONJIT 600 +#define SPEEDUP_TIMELIMIT_JIT -1500 +#define SPEEDUP_TIMELIMIT_NONJIT -2000 +int pissoff_value = SPEEDUP_CYCLES_JIT * CYCLE_UNIT; +int speedup_timelimit = SPEEDUP_TIMELIMIT_JIT; + +/* Events */ + +static int rpt_did_reset; +struct ev eventtab[ev_max]; +struct ev2 eventtab2[ev2_max]; + +int vpos; +static int vpos_count, vpos_count_diff; +static int lof_store; // real bit in custom registers +static int lof_current; // what display device thinks +static int next_lineno, prev_lineno; +static int lof_changed = 0, lof_changing = 0; +static int lof_changed_previous_field; +static int vposw_change; +static bool lof_lace; + +#define LOF_TOGGLES_NEEDED 3 + +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]; + +/* + * Hardware registers of all sorts. + */ +static int REGPARAM3 custom_wput_1 (int, uaecptr, uae_u32, int) REGPARAM; + +static uae_u16 intena; +uae_u16 intreq; +uae_u16 dmacon; +uae_u16 adkcon; /* used by audio code */ +static uae_u32 last_custom_value1; + + +static uae_u32 cop1lc,cop2lc,copcon; + +int maxhpos = MAXHPOS_PAL; +int maxvpos = MAXVPOS_PAL; +int maxvpos_nom = MAXVPOS_PAL; // nominal value (same as maxvpos but "faked" maxvpos in fake 60hz modes) +int maxvpos_display = MAXVPOS_PAL; // value used for display size +static int maxvpos_total = 511; +int minfirstline = VBLANK_ENDLINE_PAL; +static int equ_vblank_endline = EQU_ENDLINE_PAL; +static bool equ_vblank_toggle = true; +int vblank_hz = VBLANK_HZ_PAL; +int syncbase; +static int fmode; +static uae_u16 beamcon0; +static uae_u16 new_beamcon0; +static uae_u16 vtotal = MAXVPOS_PAL, htotal = MAXHPOS_PAL; +static uae_u16 hsstop, hbstrt, hbstop, vsstop, vbstrt, vbstop, hsstrt, vsstrt, hcenter; +static int diw_hstrt, diw_hstop; +static int diw_hcounter; + +#define HSYNCTIME (maxhpos * CYCLE_UNIT) + +struct sprite { + uaecptr pt; + int xpos; + int vstart; + int vstop; + int dblscan; /* AGA SSCAN2 */ + int armed; + int dmastate; + int dmacycle; +}; + +static struct sprite spr[MAX_SPRITES]; +static int plfstrt_sprite; + +static int sprite_vblank_endline = VBLANK_SPRITE_PAL; + +static uae_u16 sprctl[MAX_SPRITES], sprpos[MAX_SPRITES]; +static uae_u16 sprdata[MAX_SPRITES][4], sprdatb[MAX_SPRITES][4]; + +static int last_sprite_point, nr_armed; +static int sprite_width, sprres; +int sprite_buffer_res; + +static uae_u16 bplxdat[8]; +static bool bpl1dat_written, bpl1dat_early, bpl1dat_written_at_least_once; +static bool bpldmawasactive; +static uae_s16 bpl1mod, bpl2mod; +static uaecptr bplpt[8]; + +static struct color_entry current_colors; +static unsigned int bplcon0; +static unsigned int 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 line_cyclebased; +static int bplcon1_hpos; + +/* The display and data fetch windows */ +enum diw_states +{ + DIW_waiting_start, DIW_waiting_stop +}; + +static int plffirstline, plflastline; +static int plfstrt_start, plfstrt, plfstop; +static int sprite_minx, sprite_maxx; +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; + +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_waitforever, + 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_delay1x, + COP_strobe_delay2x, + 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; + enum copper_states state, state_prev; + /* Instruction pointer. */ + uaecptr ip; + int hpos; + unsigned int ignore_next; + int vcmp, hcmp; + + /* When we schedule a copper event, knowing a few things about the future + of the copper list can reduce the number of sync_with_cpu calls + dramatically. */ + unsigned int first_sync; + unsigned int regtypes_modified; + + int strobe; /* COPJMP1 / COPJMP2 accessed */ + int moveaddr, movedata, movedelay; +}; + +#define REGTYPE_NONE 0 +#define REGTYPE_COLOR 1 +#define REGTYPE_SPRITE 2 +#define REGTYPE_PLANE 4 +#define REGTYPE_BLITTER 8 +#define REGTYPE_JOYPORT 16 +#define REGTYPE_DISK 32 +#define REGTYPE_POS 64 +#define REGTYPE_AUDIO 128 + +#define REGTYPE_ALL 255 +/* Always set in regtypes_modified, to enable a forced update when things like + DMACON, BPLCON0, COPJMPx get written. */ +#define REGTYPE_FORCE 256 + +static unsigned int regtypes[512]; + +static struct copper cop_state; +static int copper_enabled_thisline; + +/* + * Statistics + */ + +static unsigned long lastframetime = 0; +static unsigned long frametime = 0, timeframes = 0; +unsigned long hsync_counter = 0; + +/* Recording of custom chip register changes. */ +struct sprite_entry *curr_sprite_entries = 0; +struct color_change *curr_color_changes = 0; + +struct decision line_decisions[2 * (MAXVPOS + 2) + 1]; +struct draw_info curr_drawinfo[2 * (MAXVPOS + 2) + 1]; +#define COLOR_TABLE_SIZE (MAXVPOS + 2) * 2 +struct color_entry curr_color_tables[COLOR_TABLE_SIZE]; + +static int next_sprite_entry = 0; +static int next_sprite_forced = 1; + +static int next_color_change; +static int next_color_entry, remembered_color_entry; + +static struct decision thisline_decision; +static int fetch_cycle, fetch_modulo_cycle; +static int bitplane_dma_turned_on; + +enum plfstate +{ + plf_idle, + plf_start, + plf_active, + plf_wait_stop, + plf_passed_stop, + plf_passed_stop2, + plf_end, + plf_finished +} plf_state; + +enum fetchstate { + fetch_not_started, + fetch_started, + fetch_was_plane0 +} fetch_state; + +/* + * helper functions + */ + +#define nodraw() (framecnt != 0) + +void set_speedup_values(void) +{ + if(currprefs.m68k_speed < 0) { + if (currprefs.cachesize) { + pissoff_value = SPEEDUP_CYCLES_JIT * CYCLE_UNIT; + speedup_timelimit = SPEEDUP_TIMELIMIT_JIT; + } else { + pissoff_value = SPEEDUP_CYCLES_NONJIT * CYCLE_UNIT; + speedup_timelimit = SPEEDUP_TIMELIMIT_NONJIT; + } + } else { + pissoff_value = 0; + speedup_timelimit = 0; + } +} + + +void reset_frame_rate_hack (void) +{ + is_syncline = 0; + if (currprefs.m68k_speed >= 0) + return; + + rpt_did_reset = 1; + vsyncmintime = read_processor_time() + vsynctimebase; + write_log (_T("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 void update_mirrors(void) +{ + aga_mode = (currprefs.chipset_mask & CSMASK_AGA) != 0; +} + +STATIC_INLINE uae_u8 *pfield_xlateptr (uaecptr plpt, int bytecount) +{ + plpt &= chipmem_bank.mask; + if((plpt + bytecount) > chipmem_bank.allocated) + return NULL; + return chipmem_bank.baseaddr + plpt; +} + +STATIC_INLINE void docols (struct color_entry *colentry) +{ + int i; + + if (aga_mode) { + for (i = 0; i < 256; i++) { + int v = colentry->color_regs_aga[i]; + if (v < 0 || v > 16777215) + continue; + colentry->acolors[i] = CONVERT_RGB (v); + } + } else { + for (i = 0; i < 32; i++) { + int v = colentry->color_regs_ecs[i]; + if (v < 0 || v > 4095) + continue; + colentry->acolors[i] = xcolors[v]; + } + } +} + +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(curr_color_tables + i); + } +} + +static void do_sprites (int currhp); + +static void remember_ctable (void) +{ + /* This can happen when program crashes very badly */ + if (next_color_entry >= COLOR_TABLE_SIZE) + return; + if (remembered_color_entry < 0) { + /* 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; +} + +STATIC_INLINE int get_equ_vblank_endline (void) +{ + return equ_vblank_endline + (equ_vblank_toggle ? (lof_current ? 1 : 0) : 0); +} + +#define HARD_DDF_LIMITS_DISABLED ((beamcon0 & 0x80) || (bplcon0 & 0x40)) +/* The HRM says 0xD8, but that can't work... */ +#define HARD_DDF_STOP (HARD_DDF_LIMITS_DISABLED ? 0xff : 0xd6) +#define HARD_DDF_START_REAL 0x18 +/* Programmed rates or superhires (!) disable normal DMA limits */ +#define HARD_DDF_START (HARD_DDF_LIMITS_DISABLED ? 0x04 : 0x18) + +/* 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 <= get_equ_vblank_endline ()) + hdiw = diw_hcounter; + /* always mask, bad programs may have set maxhpos = 256 */ + 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 < 0) + thisline_decision.diwfirstword = diwfirstword < 0 ? PIXEL_XPOS(0) : diwfirstword; + hdiwstate = DIW_waiting_stop; + } + if (((hpos >= maxhpos && HARD_DDF_LIMITS_DISABLED) || (lhdiw >= diw_hstop && last_hdiw < diw_hstop)) && hdiwstate == DIW_waiting_stop) { + if (thisline_decision.diwlastword < 0) + 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 delaymask; +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 () +{ + return real_bitplane_number[fetchmode][bplcon0_res][bplcon0_planes]; +} + +STATIC_INLINE void add_modulo (int nr) +{ + 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; +} + +STATIC_INLINE 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) { + case 8: bplpt[7] += m2; + case 7: bplpt[6] += m1; + case 6: bplpt[5] += m2; + case 5: bplpt[4] += m1; + case 4: bplpt[3] += m2; + case 3: bplpt[2] += m1; + case 2: bplpt[1] += m2; + case 1: bplpt[0] += m1; + } +} + + +/* 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_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; + +/* 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 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 && !(aga_mode)) + rplanes = 4; + real_bitplane_number[fm][res][planes] = rplanes; + } + } + } +} + + +/* Used by the copper. */ +static int estimated_last_fetch_cycle; +static int cycle_diagram_shift; + +static void estimate_last_fetch_cycle (int hpos) +{ + 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_mask; + + estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit; + } else { + int starting_last_block_at = (fetch_cycle + fetchunit - 1) & ~fetchunit_mask; + 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]; +static uae_u32 fetched_aga0[MAX_PLANES]; +static uae_u32 fetched_aga1[MAX_PLANES]; + +/* Expansions from bplcon0/bplcon1. */ +static int toscr_res, toscr_res2p; +static int toscr_nr_planes, toscr_nr_planes2; +static int toscr_delay[2]; + +/* 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_REAL) & fetchstart_mask) << 1); +} + +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; + next_color_change++; + curr_color_changes[next_color_change].regno = -1; +} + +// OCS/ECS, lores, 7 planes = 4 "real" planes + BPL5DAT and BPL6DAT as static 5th and 6th plane +STATIC_INLINE int isocs7planes (void) +{ + return !(aga_mode) && bplcon0_res == 0 && bplcon0_planes == 7; +} + +STATIC_INLINE int is_bitplane_dma (int hpos) +{ + if (fetch_state == fetch_not_started || hpos < plfstrt || plf_state == plf_wait_stop) + 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 void update_denise (int hpos) +{ + toscr_res = GET_RES_DENISE (bplcon0d); + toscr_res2p = 2 << toscr_res; + 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; + } +} + +/* Expand bplcon0/bplcon1 into the toscr_xxx variables. */ +STATIC_INLINE 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; + + delay1 += delayoffset; + delay2 += delayoffset; + toscr_delay[0] = (delay1 & (delaymask >> toscr_res)) << toscr_res; + toscr_delay[0] |= shdelay1 >> (RES_MAX - toscr_res); + toscr_delay[1] = (delay2 & (delaymask >> toscr_res)) << toscr_res; + toscr_delay[1] |= shdelay2 >> (RES_MAX - toscr_res); +} + +STATIC_INLINE void compute_toscr_delay (int hpos, int bplcon1) +{ + update_denise (hpos); + compute_toscr_delay_1 (bplcon1); +} + +static int bpldmasetuphpos, bpldmasetuphpos_diff; +static int bpldmasetupphase; + +#ifdef WITH_INGAME_WARNING +static bool shres_warned = false; +#endif + +/* set currently active Agnus bitplane DMA sequence */ +static void setup_fmodes (int hpos) +{ + switch (fmode & 3) + { + case 0: + fetchmode = 0; + delaymask = 15; // (16 << fetchmode) - 1; + break; + case 1: + case 2: + fetchmode = 1; + delaymask = 31; // (16 << fetchmode) - 1; + break; + case 3: + fetchmode = 2; + delaymask = 63; // (16 << fetchmode) - 1; + break; + } + bplcon0_res = GET_RES_AGNUS (bplcon0); + if(bplcon0_res == RES_SUPERHIRES) { +#ifdef WITH_INGAME_WARNING + if(!shres_warned) { + InGameMessage("Superhires resolution requested.\nNot supported in UAE4ARM."); + shres_warned = true; + } +#endif + bplcon0_res == RES_LORES; + } + bplcon0_planes = GET_PLANES (bplcon0); + bplcon0_planes_limit = GET_PLANES_LIMIT (); + fetchunit = fetchunits[fetchmode * 4 + bplcon0_res]; + fetchunit_mask = fetchunit - 1; + int fetchstart_shift = fetchstarts[fetchmode * 4 + bplcon0_res]; + fetchstart = 1 << fetchstart_shift; + fetchstart_mask = fetchstart - 1; + int fm_maxplane_shift = fm_maxplanes[fetchmode * 4 + bplcon0_res]; + fm_maxplane = 1 << fm_maxplane_shift; + fetch_modulo_cycle = fetchunit - fetchstart; + + if (thisline_decision.plfleft < 0) { + thisline_decision.bplres = bplcon0_res; + thisline_decision.bplcon0 = bplcon0; + thisline_decision.nr_planes = bplcon0_planes; + } + + curr_diagram = cycle_diagram_table[fetchmode][bplcon0_res][bplcon0_planes_limit]; + estimate_last_fetch_cycle (hpos); + bpldmasetuphpos = -1; + bpldmasetupphase = 0; + line_cyclebased = vpos; +} + +static void BPLCON0_Denise (int hpos, uae_u16 v, bool); + +// 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 (3 + (copper_access ? 1 : 0) + (bplcon0_planes == 8 ? 1 : 0)) +#define BPLCON_DENISE_DELAY (copper_access ? 1 : 0) + +STATIC_INLINE void maybe_setup_fmodes (int hpos) +{ + switch (bpldmasetupphase) + { + case 0: + BPLCON0_Denise (hpos, bplcon0, false); + bpldmasetupphase++; + bpldmasetuphpos += bpldmasetuphpos_diff; + 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) +{ + line_cyclebased = vpos; + if (hpos + BPLCON_AGNUS_DELAY < 0x14) { + BPLCON0_Denise (hpos, bplcon0, false); + setup_fmodes (hpos); + return; + } + if (bpldmasetuphpos < 0) { + bpldmasetuphpos = hpos + BPLCON_DENISE_DELAY; + bpldmasetuphpos_diff = BPLCON_AGNUS_DELAY - BPLCON_DENISE_DELAY; + bpldmasetupphase = 0; + if (BPLCON_DENISE_DELAY == 0) { + maybe_setup_fmodes (hpos); + } + } +} + +STATIC_INLINE void update_toscr_planes (void) +{ + if (toscr_nr_planes2 > thisline_decision.nr_planes) { + if(thisline_decision.nr_planes > 0) { + int j; + for (j = thisline_decision.nr_planes; j < toscr_nr_planes2; j++) + memset ((uae_u32 *)(line_data[next_lineno] + 2 * MAX_WORDS_PER_LINE * j), 0, out_offs * 4); + } + thisline_decision.nr_planes = toscr_nr_planes2; + } +} + +STATIC_INLINE void maybe_first_bpl1dat (int hpos) +{ + if (thisline_decision.plfleft >= 0) { + // early bpl1dat crap fix (Sequential engine animation) + if (plfleft_real < 0) { + int i; + for (i = 0; i < MAX_PLANES; i++) { + todisplay[i][0] = 0; + todisplay[i][1] = 0; + todisplay[i][2] = 0; + todisplay[i][3] = 0; + } + plfleft_real = hpos; + bpl1dat_early = true; + } + } else { + plfleft_real = thisline_decision.plfleft = hpos; + compute_delay_offset (); + } +} + +STATIC_INLINE void fetch (int nr, int fm) +{ + if (nr < bplcon0_planes_limit) { + uaecptr p = bplpt[nr]; + if (nr == 0) + bpl1dat_written = true; + switch (fm) + { + case 0: + fetched[nr] = bplxdat[nr] = last_custom_value1 = chipmem_wget_indirect (p); + bplpt[nr] += 2; + break; + case 1: + fetched_aga0[nr] = chipmem_lget_indirect (p); + last_custom_value1 = (uae_u16)fetched_aga0[nr]; + bplpt[nr] += 4; + 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]; + bplpt[nr] += 8; + break; + } + if (plf_state == plf_passed_stop2 && fetch_cycle >= (fetch_cycle & ~fetchunit_mask) + fetch_modulo_cycle) { + add_modulo (nr); + } + } 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_delay[0]; + int delay2 = toscr_delay[1]; + 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; +} + +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_delay[0]; + int delay2 = toscr_delay[1]; + 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); + } + } +} + +static void toscr_2_0 (int nbits) { toscr_3_ecs (nbits); } +static void toscr_2_1 (int nbits) { toscr_3_aga (nbits, 1); } +static void toscr_2_2 (int nbits) { toscr_3_aga (nbits, 2); } + +STATIC_INLINE void toscr_1 (int nbits, int fm) +{ + switch (fm) { + case 0: + toscr_2_0 (nbits); + break; + case 1: + toscr_2_1 (nbits); + break; + case 2: + toscr_2_2 (nbits); + break; + } + + out_nbits += nbits; + if (out_nbits == 32) { + int i; + uae_u32 *dataptr32 = (uae_u32 *)(line_data[next_lineno]); + dataptr32 += out_offs; + + for (i = 0; i < thisline_decision.nr_planes; i++) { + *dataptr32 = outword[i]; + outword[i] = 0; + dataptr32 += MAX_WORDS_PER_LINE >> 1; + } + 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; + case 1: toscr_fm1 (nbits); break; + case 2: toscr_fm2 (nbits); break; + } +} + +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_INLINE 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 >= 0) + toscr (toscr_nbits, fm); + toscr_nbits = 0; +} + +STATIC_INLINE void fetch_start () +{ + 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_x (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]; + } + else + for (i = 0; i < MAX_PLANES; i++) { + if (fm == 2) + todisplay[i][1] = fetched_aga1[i]; + todisplay[i][0] = fetched_aga0[i]; + } + + 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); +} + +static void beginning_of_plane_block_0 (int hpos) { beginning_of_plane_block_x(hpos, 0); }; +static void beginning_of_plane_block_1 (int hpos) { beginning_of_plane_block_x(hpos, 1); }; +static void beginning_of_plane_block_2 (int hpos) { beginning_of_plane_block_x(hpos, 2); }; +STATIC_INLINE void beginning_of_plane_block (int hpos, int fm) +{ + if(fm == 0) + beginning_of_plane_block_0(hpos); + else if(fm == 1) + beginning_of_plane_block_1(hpos); + else + beginning_of_plane_block_2(hpos); +} + +/* 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 = toscr_delay[(plane & 1)]; + int tmp_nbits = out_nbits; + uae_u32 shiftbuffer = todisplay[plane][0]; + uae_u32 outval = outword[plane]; + uae_u32 fetchval = fetched[plane]; + uae_u32 *dataptr_start = (uae_u32 *)(line_data[next_lineno] + (2 * plane * MAX_WORDS_PER_LINE)); + uae_u32 *dataptr = dataptr_start + out_offs; + + if (dma) { + bplpt[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); + *dataptr++ = outval; + outval = t; + tmp_nbits = 16 - bits_left; + } else { + outval = (outval << 16) | t; + tmp_nbits += 16; + if (tmp_nbits == 32) { + *dataptr++ = outval; + tmp_nbits = 0; + } + } + shiftbuffer <<= 16; + nwords--; + if (dma) { + __asm__ ( + "ldrh %[val], [%[pt]], #2 \n\t" + "rev16 %[val], %[val] \n\t" + : [val] "=r" (fetchval), [pt] "+r" (real_pt) ); + } + } + fetched[plane] = fetchval; + todisplay[plane][0] = shiftbuffer; + outword[plane] = outval; +} + + +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 = toscr_delay[plane & 1]; + int tmp_nbits = out_nbits; + uae_u32 outval = outword[plane]; + uae_u32 fetchval0 = fetched_aga0[plane]; + uae_u32 fetchval1 = fetched_aga1[plane]; + uae_u32 *dataptr_start = (uae_u32 *)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE); + uae_u32 *dataptr = dataptr_start + out_offs; + + int offs = (16 << fm) - 16 + delay; + int off1 = offs >> 5; + if (off1 == 3) + off1 = 2; + offs -= off1 << 5; + + if (dma) { + bplpt[plane] += nwords * 2; + } + + if (real_pt == 0) + /* @@@ Don't do this, fall back on chipmem_wget instead. */ + return; + + /* Instead of shifting a 64 bit value more than 16 bits, we */ + /* move the pointer for x bytes and shift a 32 bit value less */ + /* than 16 bits. See (1) */ + int buffer_add = (offs >> 4); + offs &= 15; + + while (nwords > 0) { + int i; + uae_u32 *shiftbuffer = todisplay[plane]; + + shiftbuffer[0] = fetchval0; + if(fm == 2) + shiftbuffer[1] = fetchval1; + + /* (1) */ + if(buffer_add) + shiftbuffer = (uae_u32 *)((uae_u16 *)shiftbuffer + buffer_add); + + for (i = 0; i < (1 << fm); i++) { + int bits_left = 32 - tmp_nbits; + + uae_u32 t0 = shiftbuffer[off1]; + + t0 = (uae_u32)((t0 >> offs) & 0xFFFF); + + + if (weird_number_of_bits && bits_left < 16) { + outval <<= bits_left; + outval |= t0 >> (16 - bits_left); + *dataptr++ = outval; + outval = t0; + tmp_nbits = 16 - bits_left; + /* Instead of shifting 128 bit of data for 16 bit, */ + /* we move the pointer two bytes. See also (2) and (3) */ + /*aga_shift (shiftbuffer, 16, fm); */ + shiftbuffer = (uae_u32 *)((uae_u16 *)shiftbuffer - 1); + } else { + outval = (outval << 16) | t0; + /* (2) */ + /*aga_shift (shiftbuffer, 16, fm); */ + shiftbuffer = (uae_u32 *)((uae_u16 *)shiftbuffer - 1); + tmp_nbits += 16; + if (tmp_nbits == 32) { + *dataptr++ = outval; + tmp_nbits = 0; + } + } + } + + /* (3) */ + /* We have to move the data, but now, we can simply */ + /* copy long values and have to do it only once. */ + if(fm == 1) + todisplay[plane][1] = todisplay[plane][0]; + else { + todisplay[plane][3] = todisplay[plane][1]; + todisplay[plane][2] = todisplay[plane][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; +} + +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); } +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); } + +static void do_long_fetch (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; + 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; + } + + out_nbits += nwords * 16; + out_offs += out_nbits >> 5; + out_nbits &= 31; + + if (dma && toscr_nr_planes > 0) + fetch_state = fetch_was_plane0; +} + +STATIC_INLINE void finish_last_fetch (int pos, int fm) +{ + if (thisline_decision.plfleft < 0) + return; + if (plf_state >= plf_end) + return; + plf_state = plf_end; + pos += flush_plane_data (fm); + thisline_decision.plfright = pos; + thisline_decision.plflinelen = out_offs; + if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) { + bpl1dat_early = true; + ddfstate = DIW_waiting_start; + fetch_state = fetch_not_started; + } +} + +/* check special case where last fetch wraps to next line + * this makes totally corrupted and flickering display on + * real hardware due to refresh cycle conflicts + */ +static void maybe_finish_last_fetch (int pos, int fm) +{ + bool done = false; + + if (plf_state != plf_passed_stop2 || fetch_state != fetch_started || !dmaen (DMA_BITPLANE)) { + finish_last_fetch (pos, fm); + return; + } + do { + int cycle_start = fetch_cycle & fetchstart_mask; + switch (fm_maxplane) { + case 8: + switch (cycle_start) { + case 0: fetch (7, fm); break; + case 1: fetch (3, fm); break; + case 2: fetch (5, fm); break; + case 3: fetch (1, fm); break; + case 4: fetch (6, fm); break; + case 5: fetch (2, fm); break; + case 6: fetch (4, fm); break; + case 7: fetch (0, fm); break; + default: + goto end; + } + break; + case 4: + switch (cycle_start) { + case 0: fetch (3, fm); break; + case 1: fetch (1, fm); break; + case 2: fetch (2, fm); break; + case 3: fetch (0, fm); break; + default: + goto end; + } + break; + case 2: + switch (cycle_start) { + case 0: fetch (1, fm); break; + case 1: fetch (0, fm); break; + default: + goto end; + } + break; + } + fetch_cycle++; + toscr_nbits += toscr_res2p; + + if (toscr_nbits > 16) + toscr_nbits = 0; + if (toscr_nbits == 16) + flush_display (fm); + done = true; + } while ((fetch_cycle & fetchunit_mask) != 0); + +end: + finish_last_fetch (pos, fm); +} + + +/* make sure fetch that goes beyond maxhpos is finished */ +STATIC_INLINE void finish_final_fetch (void) +{ + if (plf_state < plf_end) { + finish_last_fetch (maxhpos, fetchmode); + if (plf_state != plf_end) + return; + } + plf_state = plf_finished; +} + +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_last_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 && plf_state != plf_wait_stop) { + /* 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); break; + case 1: fetch (3, fm); break; + case 2: fetch (5, fm); break; + case 3: fetch (1, fm); break; + case 4: fetch (6, fm); break; + case 5: fetch (2, fm); break; + case 6: fetch (4, fm); break; + case 7: fetch (0, fm); break; + } + break; + case 4: + switch (cycle_start) { + case 0: fetch (3, fm); break; + case 1: fetch (1, fm); break; + case 2: fetch (2, fm); break; + case 3: fetch (0, fm); break; + } + break; + case 2: + switch (cycle_start) { + case 0: fetch (1, fm); break; + case 1: fetch (0, fm); 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 = false; + } + + fetch_cycle++; + toscr_nbits += toscr_res2p; + + if (toscr_nbits > 16) { + write_log (_T("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); + case 1: return one_fetch_cycle_fm1 (pos, ddfstop_to_test, dma); + case 2: return one_fetch_cycle_fm2 (pos, ddfstop_to_test, dma); + default: write_log (_T("fm corrupt")); return 0; + } +} + +static void update_bpldats (int hpos) +{ + for (int i = 0; i < MAX_PLANES; i++) { + fetched_aga0[i] = bplxdat[i]; + fetched_aga1[i] = 0; + fetched[i] = bplxdat[i]; + } + beginning_of_plane_block (hpos, fetchmode); +} + +static void update_fetch_x (int until, int fm) +{ + int pos; + + if (nodraw ()) + return; + + pos = last_fetch_hpos; + update_toscr_planes (); + + // not optimized, update_fetch_x() is extremely rarely used. + for (; pos < until; pos++) { + + toscr_nbits += toscr_res2p; + + if (toscr_nbits > 16) { + write_log (_T("xtoscr_nbits > 16 (%d)"), toscr_nbits); + toscr_nbits = 0; + } + if (toscr_nbits == 16) + flush_display (fm); + + } + + if (until >= maxhpos) { + maybe_finish_last_fetch (pos, fm); + return; + } + + flush_display (fm); +} + +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 < HARD_DDF_STOP) + 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) { + maybe_finish_last_fetch (pos, fm); + return; + } + flush_display (fm); + return; + } + + if (fetch_state == fetch_was_plane0) + break; + + fetch_start (); + if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm)) + return; + } + + // vdiw getting cleared mid-scan = fetches also stop + if (diwstate == DIW_waiting_start && plf_state < plf_wait_stop) { + // ecs = continue at passed_stop + // ocs = line is done + if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) + plf_state = plf_wait_stop; + else + plf_state = plf_passed_stop2; + } + + /* Unrolled version of the for loop below. */ + if (plf_state < plf_wait_stop && line_cyclebased != vpos && line_cyclebased + 1 != vpos + && dma + && (fetch_cycle & fetchstart_mask) == (fm_maxplane & fetchstart_mask) + && 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 < 0) { + compute_delay_offset (); + compute_toscr_delay_1 (bplcon1); + } + do_long_fetch (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; + } + } + + for (; pos < until; pos++) { + if (fetch_state == fetch_was_plane0) { + beginning_of_plane_block (pos, fm); + estimate_last_fetch_cycle (pos); + } + fetch_start (); + + if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm)) + return; + } + if (until >= maxhpos) { + maybe_finish_last_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; + case 1: update_fetch_1 (hpos); break; + case 2: update_fetch_2 (hpos); break; + default: write_log (_T("fetchmode corrupt")); + } + } else if (bpl1dat_written_at_least_once) { + // "PIO" mode display + update_fetch_x (hpos, fetchmode); + bpl1dat_written = false; + } + maybe_check (hpos); + last_fetch_hpos = hpos; + } +} + +STATIC_INLINE void decide_fetch_safe (int hpos) +{ + decide_fetch (hpos); +} + +static void reset_bpl_vars (void) +{ + out_nbits = 0; + out_offs = 0; + toscr_nbits = 0; + thisline_decision.bplres = bplcon0_res; +} + +STATIC_INLINE void start_bpl_dma (int hstart) +{ + fetch_start (); + ddfstate = DIW_waiting_stop; + + if (!bpldmawasactive) { + + plfstrt_sprite = plfstrt; + fetch_cycle = 0; + + compute_toscr_delay (last_fetch_hpos, bplcon1); + + /* If someone already wrote BPL1DAT, clear the area between that point and + the real fetch start. */ + if (bpl1dat_written_at_least_once && hstart > last_fetch_hpos) { + update_fetch_x (hstart, fetchmode); + bpl1dat_written_at_least_once = false; + } else { + reset_bpl_vars (); + } + cycle_diagram_shift = hstart; + + bpldmawasactive = true; + + } else { + + // this is a hack, something weird happens without this + // (Subtle Shades) + thisline_decision.plfright++; + thisline_decision.plflinelen = ++out_offs; + } + + last_fetch_hpos = hstart; +} + +/* this may turn on datafetch if program turns dma on during the ddf */ +STATIC_INLINE 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)) { + bitplane_dma_turned_on = hpos; + 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_dma (hpos); +} + +/* 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; + line_cyclebased = vpos; + } + if (vpos == plflastline) { + diwstate = DIW_waiting_start; + line_cyclebased = vpos; + } + + if (hpos <= last_decide_line_hpos) + return; + + if (fetch_state == fetch_not_started) { + int start = (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? plfstrt - 4 : HARD_DDF_START_REAL - 2; + if (last_decide_line_hpos < start && hpos >= start) { + if (plf_state == plf_idle || plf_state == plf_end) + plf_state = plf_start; + } + if ((diwstate == DIW_waiting_stop || (currprefs.chipset_mask & CSMASK_ECS_AGNUS))) { + int ok = 0; + 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) && bitplane_dma_turned_on + 4 < hpos) { + start_bpl_dma (plfstrt); + estimate_last_fetch_cycle (plfstrt); + } + last_decide_line_hpos = hpos; + do_sprites (hpos); + return; + } + } + } + + if (hpos > last_sprite_hpos && last_sprite_hpos < SPR0_HPOS + 4 * MAX_SPRITES) + do_sprites (hpos); + + 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 < 0) + 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; + /* 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) + */ + 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; + curr_color_changes[idx + 1].regno = -1; + } + record_color_change2 (hpos, regno, value); +} + +static bool isbrdblank (int hpos, uae_u16 bplcon0, uae_u16 bplcon3) +{ + bool brdblank; + brdblank = (currprefs.chipset_mask & CSMASK_ECS_DENISE) && (bplcon0 & 1) && (bplcon3 & 0x20); + if (hpos >= 0 && current_colors.borderblank != brdblank) { + record_color_change (hpos, 0, COLOR_CHANGE_BRDBLANK | (brdblank ? 1 : 0) | (current_colors.bordersprite ? 2 : 0)); + current_colors.borderblank = brdblank; + remembered_color_entry = -1; + } + return brdblank; +} + + +static bool issprbrd (int hpos, uae_u16 bplcon0, uae_u16 bplcon3) +{ + bool brdsprt; + brdsprt = (aga_mode) && (bplcon0 & 1) && (bplcon3 & 0x02); + if (hpos >= 0 && current_colors.bordersprite != brdsprt) { + record_color_change (hpos, 0, COLOR_CHANGE_BRDBLANK | (current_colors.borderblank ? 1 : 0) | (brdsprt ? 2 : 0)); + current_colors.bordersprite = brdsprt; + remembered_color_entry = -1; + if (brdsprt && !current_colors.borderblank) + thisline_decision.bordersprite_seen = true; + } + return brdsprt && !current_colors.borderblank; +} + +static void record_register_change (int hpos, int regno, uae_u16 value) +{ + if (regno == 0x100) { // BPLCON0 + if (value & 0x800) + thisline_decision.ham_seen = 1; + isbrdblank (hpos, value, bplcon3); + issprbrd (hpos, value, bplcon3); + } else if (regno == 0x106) { // BPLCON3 + isbrdblank (hpos, bplcon0, value); + issprbrd (hpos, bplcon0, value); + } + 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; + 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; + case 1: + res = RES_LORES; + break; + case 2: + res = RES_HIRES; + break; + case 3: + res = RES_SUPERHIRES; + break; + } + return res; +} + +#define DO_PLAYFIELD_COLLISIONS \ +{ \ + if (!clxcon_bpl_enable) \ + clxdat |= 1; \ + else \ + if (!(clxdat & 1)) \ + do_playfield_collisions (); \ +} + +/* 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; + int planes = (aga_mode) ? 8 : 6; + + 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 (collided) + clxdat |= 1; +} + +#define DO_SPRITE_COLLISIONS \ +{ \ + if (!clxcon_bpl_enable) \ + clxdat |= 0x1FE; \ + else \ + do_sprite_collisions (); \ +} + +/* 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); + + 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; + int planes = (aga_mode) ? 8 : 6; + + 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 (clxcon_bpl_enable & (1 << l)) { + if (t != ((clxcon_bpl_match >> l) & 1)) + match = 0; + } + } + if (match) { + clxdat |= sprpix << (k * 4); + } + } + } + } +} + +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 int coltmp = 0; + + if ((sprxp >= sprite_minx && sprxp < sprite_maxx) || (bplcon3 & 2)) + col = (datab & 3) << (2 * num); + 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) { + write_log (_T("sprxp < e->pos")); + return; + } + + 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) { + uae_u8 state = 0x03 << (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 - DIW_DDF_OFFSET) << sprite_buffer_res; +} + +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 */ + if (currprefs.chipset_mask & CSMASK_ECS_DENISE) { + sprite_minx = min; + } else { + if (thisline_decision.plfleft >= 0x28 || bpldmawasactive) + sprite_minx = min; + } + } + /* sprites are visible from first BPL1DAT write to end of line + * ECS Denise/AGA: no limits + * OCS Denise: BPL1DAT write only enables sprite if hpos >= 0x28 or so. + * (undocumented feature) + */ + } +} + +static void decide_sprites (int hpos) +{ + int nrs[MAX_SPRITES * 2], posns[MAX_SPRITES * 2]; + int count, i; + int point = hpos * 2 + 0; + int width = sprite_width; + int sscanmask = 0x100 << sprite_buffer_res; + + if (thisline_decision.plfleft < 0 && !(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 (! 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]); + } + last_sprite_point = point; +} + +/* End of a horizontal scan line. Finish off all decisions that were not + * made yet. */ +STATIC_INLINE void finish_decisions (void) +{ + struct draw_info *dip; + int hpos = maxhpos; + + if (nodraw ()) + return; + + decide_diw (hpos); + decide_line (hpos); + decide_fetch_safe (hpos); + finish_final_fetch (); + + if (thisline_decision.plfleft >= 0 && thisline_decision.plflinelen < 0) { + 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 < 0) + thisline_decision.diwfirstword = min_diwlastword; + } + decide_sprites (hpos + 1); + + dip = curr_drawinfo + next_lineno; + + dip->last_sprite_entry = next_sprite_entry; + dip->last_color_change = next_color_change; + + if (thisline_decision.ctable < 0) { + remember_ctable(); + } + + dip->nr_color_changes = next_color_change - dip->first_color_change; + dip->nr_sprites = next_sprite_entry - dip->first_sprite_entry; + + line_decisions[next_lineno] = thisline_decision; + + /* 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 == get_equ_vblank_endline () - 1) + diw_hcounter++; + if ((currprefs.chipset_mask & CSMASK_ECS_DENISE) || vpos > get_equ_vblank_endline ()) { + diw_hcounter = maxhpos * 2; + last_hdiw = 2 - 1; + } +} + +/* 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 = false; + bpl1dat_written_at_least_once = false; + bpl1dat_early = false; + + plfleft_real = -1; + thisline_decision.plfleft = -1; + thisline_decision.plflinelen = -1; + thisline_decision.ham_seen = !! (bplcon0 & 0x800); + thisline_decision.ham_at_start = !! (bplcon0 & 0x800); + thisline_decision.bordersprite_seen = issprbrd (-1, bplcon0, bplcon3); + + /* 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 = min_diwlastword; + } + thisline_decision.ctable = -1; + + 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, true); + setup_fmodes (0); + } + bpldmasetuphpos = -1; + bpldmasetupphase = 0; + ddfstrt_old_hpos = -1; + bpldmawasactive = false; + bitplane_dma_turned_on = 0; + + 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); + if (aga_mode) { + memset (fetched_aga0, 0, sizeof fetched_aga0); + memset (fetched_aga1, 0, sizeof fetched_aga1); + } + memset (outword, 0, sizeof outword); + + last_decide_line_hpos = -1; + last_sprite_hpos = -1; + last_fetch_hpos = -1; + + /* These are for comparison. */ + thisline_decision.bplcon0 = bplcon0; + thisline_decision.bplcon2 = bplcon2; + thisline_decision.bplcon3 = bplcon3; + thisline_decision.bplcon4 = bplcon4; +} + +void compute_vsynctime (void) +{ + vblank_hz = currprefs.chipset_refreshrate; + + vsynctimebase = (int)(syncbase / vblank_hz); + if (screen_is_picasso) { + beamcon0 = new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20; + } + + if (currprefs.produce_sound > 1) { + float svpos = maxvpos_nom; + float shpos = maxhpos; + if (currprefs.ntscmode) { + shpos += 0.5; + } + if ((bplcon0 & 4)) { + svpos += 0.5; + } else if (lof_current) { + svpos += 1.0; + } + float clk = svpos * shpos * vblank_hz; + update_sound (clk); + } +} + +int current_maxvpos (void) +{ + return maxvpos + (lof_store ? 1 : 0); +} + +static void compute_framesync (void) +{ + int v; + + if (!picasso_on && !picasso_requested_on) { + if (abs (vblank_hz - 50) < 1 || abs (vblank_hz - 60) < 1) { + vsync_switchmode (vblank_hz); + } + v = (vblank_hz >= 55) ? 60 : 50; + } else { + v = (currprefs.ntscmode ? 60 : 50); + } + + changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = v; + lof_changing = 0; + + memset (line_decisions, 0, sizeof line_decisions); + + compute_vsynctime (); + + if (target_graphics_buffer_update ()) { + reset_drawing (); + } +} + +/* set PAL/NTSC or custom timing variables */ +static void init_hz (bool fullinit) +{ + int isntsc; + int omaxvpos = maxvpos; + int hzc = 0; + + if (fullinit) + vpos_count = 0; + + vpos_count_diff = vpos_count; + + if ((beamcon0 & 0xA0) != (new_beamcon0 & 0xA0)) + hzc = 1; + if (beamcon0 != new_beamcon0) { + vpos_count_diff = vpos_count = 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; + equ_vblank_toggle = true; + } + 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; + equ_vblank_toggle = false; + } + // long/short field refresh rate adjustment +// vblank_hz = vblank_hz * (maxvpos * 2 + 1) / ((maxvpos + lof_current) * 2); + + maxvpos_nom = maxvpos; + maxvpos_display = maxvpos; + if (vpos_count > 0) { + // we come here if vpos_count != maxvpos and beamcon0 didn't change + // (someone poked VPOSW) + if (vpos_count < 10) + vpos_count = 10; + vblank_hz = (isntsc ? 15734.0 : 15625.0) / vpos_count; + maxvpos_nom = vpos_count - (lof_current ? 1 : 0); + if ((maxvpos_nom >= 256 && maxvpos_nom <= 313) || (beamcon0 & 0x80)) { + maxvpos_display = maxvpos_nom; + } + else if (maxvpos_nom < 256) { + maxvpos_display = 255; + } + else { + maxvpos_display = 313; + } + reset_drawing(); + } + else if (vpos_count == 0) { + // mode reset + vpos_count = maxvpos; + vpos_count_diff = maxvpos; + } + + 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); + + if ((beamcon0 & 0x1000) && (beamcon0 & 0x0200)) { // VARVBEN + VARVSYEN + minfirstline = vsstop > vbstop ? vsstop : vbstop; + if (minfirstline > maxvpos / 2) + minfirstline = vsstop > vbstop ? vbstop : vsstop; + } + else if (beamcon0 & 0x0200) { + minfirstline = vsstop; + if (minfirstline > maxvpos / 2) + minfirstline = 0; + } + else if (beamcon0 & 0x1000) { + minfirstline = vbstop; + if (minfirstline > maxvpos / 2) + minfirstline = 0; + } + + if (minfirstline < 2) + minfirstline = 2; + if (minfirstline >= maxvpos) + minfirstline = maxvpos - 1; + + sprite_vblank_endline = minfirstline - 2; + maxvpos_nom = maxvpos; + maxvpos_display = maxvpos; + equ_vblank_endline = -1; + vpos_count = maxvpos_nom; + vpos_count_diff = maxvpos_nom; + hzc = 1; + } + if (maxvpos_nom >= MAXVPOS) + maxvpos_nom = MAXVPOS; + if (maxvpos_display >= MAXVPOS) + maxvpos_display = MAXVPOS; + if (maxvpos != omaxvpos) + hzc = 1; + /* limit to sane values */ + if (vblank_hz < 10) + vblank_hz = 10; + if (vblank_hz > 300) + vblank_hz = 300; + eventtab[ev_hsync].oldcycles = get_cycles(); + eventtab[ev_hsync].evtime = get_cycles() + HSYNCTIME; + events_schedule(); + if (hzc) { + reset_drawing(); + } + maxvpos_total = (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? (MAXVPOS_LINES_ECS - 1) : (MAXVPOS_LINES_OCS - 1); + if (maxvpos_total > MAXVPOS) + maxvpos_total = MAXVPOS; + + compute_framesync(); + +#ifdef PICASSO96 + init_hz_p96(); +#endif + inputdevice_tablet_strobe(); + + if (fullinit) + vpos_count_diff = maxvpos_nom; +} + +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 = min_diwlastword; + diwlastword = max_diwlastword; + } + if (diwfirstword < min_diwlastword) + diwfirstword = min_diwlastword; + + 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; + } else { + /* OCS and ddfstrt >= ddfstop == ddfstop = max */ + if (plfstrt >= plfstop && plfstrt >= HARD_DDF_START) + plfstop = 0xff; + } +} + +/* display mode changed (lores, doubling etc..), recalculate everything */ +void init_custom (void) +{ + update_mirrors(); + create_cycle_diagram_table (); + reset_drawing (); + init_hz (); + 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); + put_long (m68k_areg (regs, 0), tv.tv_sec - (((365 * 8 + 2) * 24) * 60 * 60)); + put_long (m68k_areg (regs, 0) + 4, tv.tv_usec); + return 0; +#else + return 2; +#endif +} + + /* + * register functions + */ +STATIC_INLINE uae_u16 DENISEID (int *missing) +{ + *missing = 0; + if (aga_mode) { + return 0x00F8; + } + if (currprefs.chipset_mask & CSMASK_ECS_DENISE) + return 0xFFFC; + if (currprefs.cpu_model == 68000 && currprefs.cpu_compatible) + *missing = 1; + return 0xFFFF; +} +STATIC_INLINE uae_u16 DMACONR (int hpos) +{ + dmacon &= ~(0x4000 | 0x2000); + dmacon |= (bltstate == BLT_done ? 0 : 0x4000) + | (blt_info.blitzero ? 0x2000 : 0); + return dmacon; +} +STATIC_INLINE uae_u16 INTENAR (void) +{ + return intena; +} +STATIC_INLINE uae_u16 ADKCONR (void) +{ + return adkcon; +} + +STATIC_INLINE int GETVPOS (void) +{ + return vpos; +} +STATIC_INLINE int GETHPOS (void) +{ + return current_hpos (); +} + +#define CPU_ACCURATE (currprefs.cpu_model < 68020) + +// DFF006 = 0.W must be valid result but better do this only in 68000 modes (whdload black screen!) +// HPOS is shifted by 3 cycles and VPOS increases when shifted HPOS==1 +#define HPOS_OFFSET (CPU_ACCURATE ? HPOS_SHIFT : 0) +#define VPOS_INC_DELAY (HPOS_OFFSET ? 1 : 0) + +STATIC_INLINE uae_u16 VPOSR (void) +{ + unsigned int csbit = 0; + uae_u16 vp = GETVPOS (); + uae_u16 hp = GETHPOS (); + + if (hp + HPOS_OFFSET >= maxhpos + VPOS_INC_DELAY) { + vp++; + if (vp >= maxvpos + lof_store) + vp = 0; + } + vp = (vp >> 8) & 7; + + csbit |= (aga_mode) ? 0x2300 : 0; + csbit |= (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 0x2000 : 0; + if (currprefs.ntscmode) + csbit |= 0x1000; + + if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + vp &= 1; + vp |= (lof_store ? 0x8000 : 0) | csbit; + return vp; +} + +static void VPOSW (uae_u16 v) +{ + int oldvpos = vpos; + if (lof_store != ((v & 0x8000) ? 1 : 0)) { + lof_store = (v & 0x8000) ? 1 : 0; + lof_changing = lof_store ? 1 : -1; + } + if (lof_changing) + return; + vpos &= 0x00ff; + v &= 7; + if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + v &= 1; + vpos |= v << 8; + if (vpos != oldvpos) + vposw_change++; + if (vpos < oldvpos) + vpos = oldvpos; +} + +static void VHPOSW (uae_u16 v) +{ + int oldvpos = vpos; + bool changed = false; + + v >>= 8; + vpos &= 0xff00; + vpos |= v; + if (vpos != oldvpos && !changed) + vposw_change++; + if (vpos < oldvpos) { + vpos = oldvpos; + } else if (vpos < minfirstline && oldvpos < minfirstline) { + vpos = oldvpos; + } +} + +STATIC_INLINE uae_u16 VHPOSR (void) +{ + uae_u16 vp = GETVPOS (); + uae_u16 hp = GETHPOS (); + hp += HPOS_OFFSET; + if (hp >= maxhpos) { + hp -= maxhpos; + // vpos increases when hp==1, not when hp==0 + if (hp >= VPOS_INC_DELAY) { + vp++; + if (vp >= maxvpos + lof_store) + vp = 0; + } + } + if (HPOS_OFFSET) { + hp += 1; + if (hp >= maxhpos) + hp -= maxhpos; + } + + vp <<= 8; + vp |= hp; + 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.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 was_active = eventtab[ev_copper].active; + int oldstrobe = cop_state.strobe; + bool wasstopped = cop_state.state == COP_stop && !vblank; + + unset_special (SPCFLAG_COPPER); + cop_state.ignore_next = 0; + if (!oldstrobe) + cop_state.state_prev = cop_state.state; + if ((cop_state.state == COP_wait || cop_state.state == COP_waitforever) && !vblank) { + cop_state.state = COP_strobe_delay1x; + } else { + cop_state.state = vblank ? COP_start_delay : (copper_access ? COP_strobe_delay1 : COP_strobe_extra); + } + cop_state.hpos = current_hpos () & ~1; + copper_enabled_thisline = 0; + cop_state.strobe = num; + + eventtab[ev_copper].active = 0; + + if (nocustom()) { + immediate_copper (num); + return; + } + + if (was_active) + events_schedule (); + + if (dmaen (DMA_COPPER)) { + compute_spcflag_copper (current_hpos ()); + } else if (wasstopped || (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_safe (hpos); + + setclr (&dmacon, v); + dmacon &= 0x07FF; + + changed = dmacon ^ oldcon; + oldcop = (oldcon & DMA_COPPER) && (oldcon & DMA_MASTER); + newcop = (dmacon & DMA_COPPER) && (dmacon & DMA_MASTER); + + if (oldcop != newcop) { + eventtab[ev_copper].active = 0; + + 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) { + blitter_check_start (); + } + + 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)) { + line_cyclebased = vpos; + if (dmaen (DMA_BITPLANE)) + maybe_start_bpl_dma (hpos); + } + + events_schedule(); +} + +int intlev (void) +{ + uae_u16 imask = intreq & intena; + if (!(imask && (intena & 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; +} + +STATIC_INLINE void rethink_intreq (void) +{ + rethink_cias (); +#ifdef CD32 + rethink_akiko (); +#endif +} + +static void INTENA (uae_u16 v) +{ + uae_u16 old = intena; + setclr (&intena,v); + + if (!(v & 0x8000) && old == intena) + return; + + if (v & 0x8000) + doint(); +} + +void INTREQ_f (uae_u16 v) +{ + setclr (&intreq, v); +} + +bool INTREQ_0 (uae_u16 v) +{ + uae_u16 old = intreq; + setclr (&intreq, v); + + if (old == intreq) + return false; + if (v & 0x8000) + doint(); + return true; +} + +void INTREQ (uae_u16 data) +{ + if (INTREQ_0 (data)) + rethink_intreq (); +} + +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 (); +} + +static void BEAMCON0 (uae_u16 v) +{ + if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) { + if (!(currprefs.chipset_mask & CSMASK_ECS_DENISE)) + v &= 0x20; + new_beamcon0 = v; + } +} + +STATIC_INLINE void varsync (void) +{ + if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + return; +#ifdef PICASSO96 + if (picasso_on) { + return; + } +#endif + if (!(beamcon0 & 0x80)) + return; + vpos_count = 0; +} + +static void BPLxPTH (int hpos, uae_u16 v, int num) +{ + decide_line (hpos); + decide_fetch_safe (hpos); + bplpt[num] = (bplpt[num] & 0x0000ffff) | ((uae_u32)v << 16); +} + +static void BPLxPTL (int hpos, uae_u16 v, int num) +{ + decide_line (hpos); + decide_fetch_safe (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 && num == 0 && is_bitplane_dma (hpos + 1) == num + 1) { + return; + } + bplpt[num] = (bplpt[num] & 0xffff0000) | (v & 0x0000fffe); +} + +static void BPLCON0_Denise (int hpos, uae_u16 v, bool immediate) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) + v &= ~0x00F1; + else if (! (aga_mode)) + v &= ~0x00B0; + v &= ~(0x0200 | 0x0100 | 0x0080 | 0x0020); + + if (bplcon0d == v && !immediate) + return; + + bplcon0dd = -1; + + if (immediate) { + record_register_change (hpos, 0x100, v); + } else { + record_register_change (hpos, 0x100, (bplcon0d & ~(0x800 | 0x400 | 0x80)) | (v & (0x0800 | 0x400 | 0x80 | 0x01))); + } + + bplcon0d = v; + + if (currprefs.chipset_mask & CSMASK_ECS_DENISE) { + decide_sprites (hpos); + sprres = expand_sprres (v, bplcon3); + } + if (thisline_decision.plfleft < 0) + update_denise (hpos); +} + +static void BPLCON0 (int hpos, uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) + v &= ~0x00F1; + else if (! (aga_mode)) + v &= ~0x00B0; + v &= ~(0x0080 | 0x0020); + + if (bplcon0 == v) + return; + + line_cyclebased = vpos; + + bplcon0 = v; + + bpldmainitdelay (hpos); + + if (thisline_decision.plfleft < 0) + BPLCON0_Denise (hpos, v, true); +} + +STATIC_INLINE void BPLCON1 (int hpos, uae_u16 v) +{ + if (!(aga_mode)) + v &= 0xff; + if (bplcon1 == v) + return; + line_cyclebased = vpos; + decide_line (hpos); + decide_fetch_safe (hpos); + bplcon1_hpos = hpos; + bplcon1 = v; +} + +STATIC_INLINE void BPLCON2 (int hpos, uae_u16 v) +{ + if (!(aga_mode)) + v &= 0x7f; + if (bplcon2 == v) + return; + decide_line (hpos); + bplcon2 = v; + record_register_change (hpos, 0x104, v); +} + +STATIC_INLINE void BPLCON3 (int hpos, uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) + return; + if (!(aga_mode)) { + v &= 0x003f; + v |= 0x0c00; + } + if (bplcon3 == v) + return; + decide_line (hpos); + decide_sprites (hpos); + bplcon3 = v; + sprres = expand_sprres (bplcon0, bplcon3); + record_register_change (hpos, 0x106, v); +} + +STATIC_INLINE void BPLCON4 (int hpos, uae_u16 v) +{ + if (! (aga_mode)) + return; + if (bplcon4 == v) + return; + decide_line (hpos); + bplcon4 = v; + record_register_change (hpos, 0x10c, v); +} + +static void BPL1MOD (int hpos, uae_u16 v) +{ + v &= ~1; + if ((uae_s16)bpl1mod == (uae_s16)v) + return; + decide_line (hpos); + decide_fetch_safe (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_safe (hpos); + bpl2mod = v; +} + +/* Needed in special OCS/ECS "7-plane" mode, + * also handles CPU generated bitplane data + */ +static void BPLxDAT (int hpos, int num, uae_u16 v) +{ + // only BPL0DAT access can do anything visible + if (num == 0 && hpos >= 8) { + decide_line (hpos); + decide_fetch_safe (hpos); + } + bplxdat[num] = v; + if (num == 0 && hpos >= 8) { + bpl1dat_written = true; + bpl1dat_written_at_least_once = true; + if (thisline_decision.plfleft < 0) { + thisline_decision.plfleft = (hpos + 3) & ~3; + reset_bpl_vars (); + compute_delay_offset (); + } + update_bpldats (hpos); + } +} + +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 (!(aga_mode)) + 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; + line_cyclebased = vpos; + decide_line (hpos); + ddfstrt_old_hpos = hpos; + ddfstrt = v; + calcdiw (); +} + +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; + line_cyclebased = vpos; + decide_line (hpos); + decide_fetch_safe (hpos); + ddfstop = v; + calcdiw (); + if (fetch_state != fetch_not_started) + estimate_last_fetch_cycle (hpos); +} + +static void FMODE (int hpos, uae_u16 v) +{ + if (!(aga_mode)) + v = 0; + v &= 0xC00F; + if (fmode == v) + return; + line_cyclebased = vpos; + fmode = v; + sprite_width = GET_SPRITEWIDTH (fmode); + bpldmainitdelay (hpos); +} + +static void FNULL (uae_u16 v) +{ + +} + +static void BLTADAT (uae_u16 v) +{ + maybe_blit (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 (uae_u16 v) +{ + maybe_blit (0); + + if (bltcon1 & 2) + blt_info.bltbhold = v << (bltcon1 >> 12); + else + blt_info.bltbhold = v >> (bltcon1 >> 12); + blt_info.bltbdat = v; +} +STATIC_INLINE void BLTCDAT (uae_u16 v) { maybe_blit (0); blt_info.bltcdat = v; } + +STATIC_INLINE void BLTAMOD (uae_u16 v) { maybe_blit (1); blt_info.bltamod = (uae_s16)(v & 0xFFFE); } +STATIC_INLINE void BLTBMOD (uae_u16 v) { maybe_blit (1); blt_info.bltbmod = (uae_s16)(v & 0xFFFE); } +STATIC_INLINE void BLTCMOD (uae_u16 v) { maybe_blit (1); blt_info.bltcmod = (uae_s16)(v & 0xFFFE); } +STATIC_INLINE void BLTDMOD (uae_u16 v) { maybe_blit (1); blt_info.bltdmod = (uae_s16)(v & 0xFFFE); } + +STATIC_INLINE void BLTCON0 (uae_u16 v) { maybe_blit (2); bltcon0 = v; reset_blit (1); } + +/* The next category is "Most useless hardware register". + * And the winner is... */ +static void BLTCON0L (uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + return; // ei voittoa. + maybe_blit (2); + bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF); + reset_blit (1); +} +STATIC_INLINE void BLTCON1 (uae_u16 v) { maybe_blit (2); bltcon1 = v; reset_blit (2); } + +STATIC_INLINE void BLTAFWM (uae_u16 v) { maybe_blit (2); blt_info.bltafwm = v; } +STATIC_INLINE void BLTALWM (uae_u16 v) { maybe_blit (2); blt_info.bltalwm = v; } + +STATIC_INLINE void BLTAPTH (uae_u16 v) +{ + maybe_blit (0); + bltapt = (bltapt & 0xffff) | ((uae_u32)v << 16); +} +STATIC_INLINE void BLTAPTL (uae_u16 v) +{ + maybe_blit (0); + bltapt = (bltapt & ~0xffff) | (v & 0xFFFE); +} +STATIC_INLINE void BLTBPTH (uae_u16 v) +{ + maybe_blit (0); + bltbpt = (bltbpt & 0xffff) | ((uae_u32)v << 16); +} +STATIC_INLINE void BLTBPTL (uae_u16 v) +{ + maybe_blit (0); + bltbpt = (bltbpt & ~0xffff) | (v & 0xFFFE); +} +STATIC_INLINE void BLTCPTH (uae_u16 v) +{ + maybe_blit (0); + bltcpt = (bltcpt & 0xffff) | ((uae_u32)v << 16); +} +STATIC_INLINE void BLTCPTL (uae_u16 v) +{ + maybe_blit (0); + bltcpt = (bltcpt & ~0xffff) | (v & 0xFFFE); +} +STATIC_INLINE void BLTDPTH (uae_u16 v) +{ + maybe_blit (0); + bltdpt = (bltdpt & 0xffff) | ((uae_u32)v << 16); +} +STATIC_INLINE void BLTDPTL (uae_u16 v) +{ + maybe_blit (0); + bltdpt = (bltdpt & ~0xffff) | (v & 0xFFFE); +} + +static void BLTSIZE (uae_u16 v) +{ + maybe_blit (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 (); +} + +static void BLTSIZV (uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + return; + maybe_blit (0); + blt_info.vblitsize = v & 0x7FFF; +} + +static void BLTSIZH (uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + return; + maybe_blit (0); + blt_info.hblitsize = v & 0x7FF; + if (!blt_info.vblitsize) + blt_info.vblitsize = 0x8000; + if (!blt_info.hblitsize) + blt_info.hblitsize = 0x0800; + do_blitter (); +} + +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 < sprite_vblank_endline) + return; + 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 (aga_mode) { + if(sprite_buffer_res) + sprxp |= ((sprctl[num] >> 4) & 1); + s->dblscan = sprpos[num] & 0x80; + } else if (sprite_buffer_res && currprefs.chipset_mask & CSMASK_ECS_DENISE) { + sprxp |= ((sprctl[num] >> 4) & 1); + } + s->xpos = sprxp; + s->vstart = sprpos[num] >> 8; + s->vstart |= (sprctl[num] & 0x04) ? 0x0100 : 0; + s->vstop = sprctl[num] >> 8; + s->vstop |= (sprctl[num] &0x02) ? 0x100 : 0; + if (currprefs.chipset_mask & CSMASK_ECS_AGNUS) { + s->vstart |= (sprctl[num] & 0x40) ? 0x0200 : 0; + s->vstop |= (sprctl[num] & 0x20) ? 0x0200 : 0; + } + sprstartstop (s); +} + +STATIC_INLINE void SPRxCTL_1 (uae_u16 v, int num) +{ + struct sprite *s = &spr[num]; + sprctl[num] = v; + spr_arm (num, 0); + SPRxCTLPOS (num); +} +STATIC_INLINE void SPRxPOS_1 (uae_u16 v, int num) +{ + struct sprite *s = &spr[num]; + sprpos[num] = v; + SPRxCTLPOS (num); +} +STATIC_INLINE void SPRxDATA_1 (uae_u16 v, int num) +{ + sprdata[num][0] = v; + sprdata[num][1] = v; + sprdata[num][2] = v; + sprdata[num][3] = v; + spr_arm (num, 1); +} + +STATIC_INLINE void SPRxDATB_1 (uae_u16 v, int num) +{ + sprdatb[num][0] = v; + sprdatb[num][1] = v; + sprdatb[num][2] = v; + sprdatb[num][3] = v; +} +STATIC_INLINE void SPRxDATA (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + SPRxDATA_1 (v, num); +} +STATIC_INLINE void SPRxDATB (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + SPRxDATB_1 (v, num); +} + +STATIC_INLINE void SPRxCTL (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + SPRxCTL_1 (v, num); +} + +STATIC_INLINE void SPRxPOS (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + SPRxPOS_1 (v, num); +} + +static void SPRxPTH (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + if (hpos - 1 != MAXHPOS) { + spr[num].pt &= 0xffff; + spr[num].pt |= (uae_u32)v << 16; + } +} +static void SPRxPTL (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + if (hpos - 1 != MAXHPOS) { + spr[num].pt &= ~0xffff; + spr[num].pt |= v & ~1; + } +} + +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 (!(aga_mode)) + 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; +} + +static uae_u16 COLOR_READ (int num) +{ + int cr, cg, cb, colreg; + uae_u16 cval; + + if (!(aga_mode) || !(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); + } + return cval; +} + +static void COLOR_WRITE (int hpos, uae_u16 v, int num) +{ + v &= 0xFFF; + if (aga_mode) { + 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); + } + 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] = CONVERT_RGB (cval); + + } else { + 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] = xcolors[v]; + } +} + +/* 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) +{ + if (hpos + 1 >= maxhpos) // first refresh slot + return 1; + if ((hpos == maxhpos - 3) && (maxhpos & 1)) { + return -1; + } + return is_bitplane_dma (hpos); +} + + +/* The future, Conan? + We try to look ahead in the copper list to avoid doing continuous calls + to updat_copper (which is what happens when SPCFLAG_COPPER is set). If + we find that the same effect can be achieved by setting a delayed event + and then doing multiple copper insns in one batch, we can get a massive + speedup. + + We don't try to be precise here. All copper reads take exactly 2 cycles, + the effect of bitplane contention is ignored. Trying to get it exactly + right would be much more complex and as such carry a huge risk of getting + it subtly wrong; and it would also be more expensive - we want this code + to be fast. */ + +static void predict_copper (void) +{ + uaecptr ip = cop_state.ip; + unsigned int c_hpos = cop_state.hpos; + enum copper_states state = cop_state.state; + unsigned int w1, w2, cycle_count; + unsigned int modified = REGTYPE_FORCE; + + switch (state) { + case COP_read1: + break; + + case COP_read2: + w1 = cop_state.i1; + w2 = chipmem_wget_indirect (ip); + if (w1 & 1) { + if (w2 & 1) + return; // SKIP + state = COP_wait; // WAIT + c_hpos += 4; + } else { // MOVE + modified |= regtypes[w1 & 0x1FE]; + state = COP_read1; + c_hpos += 2; + } + ip += 2; + break; + + case COP_strobe_delay2: + case COP_strobe_delay2x: + case COP_start_delay: + c_hpos += 2; + state = COP_read1; + break; + + case COP_strobe_extra: + c_hpos += 6; + state = COP_read1; + break; + + case COP_strobe_delay1: + case COP_strobe_delay1x: + c_hpos += 4; + state = COP_read1; + break; + + case COP_stop: + case COP_waitforever: + case COP_bltwait: + case COP_skip_in2: + case COP_skip1: + return; + + case COP_wait_in2: + c_hpos += 2; + case COP_wait1: + w1 = cop_state.i1; + w2 = cop_state.i2; + state = COP_wait; + break; + + case COP_wait: + w1 = cop_state.i1; + w2 = cop_state.i2; + break; + + default: + return; + } + /* Only needed for COP_wait, but let's shut up the compiler. */ + cop_state.first_sync = c_hpos; + + while (c_hpos + 1 < maxhpos) { + if (state == COP_read1) { + w1 = chipmem_wget_indirect (ip); + if (w1 & 1) { + w2 = chipmem_wget_indirect (ip + 2); + if (w2 & 1) + break; // SKIP + state = COP_wait; // WAIT + c_hpos += 6; + } else { // MOVE + modified |= regtypes[w1 & 0x1FE]; + c_hpos += 4; + } + ip += 4; + } else { // state is COP_wait + if ((w2 & 0xFE) != 0xFE) + break; + else { + unsigned int vcmp = (w1 & (w2 | 0x8000)) >> 8; + unsigned int hcmp = (w1 & 0xFE); + + unsigned int vp = vpos & (((w2 >> 8) & 0x7F) | 0x80); + if (vp < vcmp) { + /* Whee. We can wait until the end of the line! */ + c_hpos = maxhpos; + } else if (vp > vcmp || hcmp <= c_hpos) { + state = COP_read1; + /* minimum wakeup time */ + c_hpos += 2; + } else { + state = COP_read1; + c_hpos = hcmp + 2; + } + /* If this is the current instruction, remember that we don't + need to sync CPU and copper anytime soon. */ + if (cop_state.ip == ip) { + cop_state.first_sync = c_hpos; + } + } + } + } + + cycle_count = c_hpos - cop_state.hpos; + if (cycle_count >= 8) { + cop_state.regtypes_modified = modified; + unset_special (SPCFLAG_COPPER); + eventtab[ev_copper].active = 1; + eventtab[ev_copper].evtime = get_cycles () + cycle_count * CYCLE_UNIT; + events_schedule (); + } +} + +STATIC_INLINE int custom_wput_copper (int hpos, uaecptr addr, uae_u32 value, int noget) +{ + int v; + + copper_access = 1; + v = custom_wput_1 (hpos, addr, value, noget); + copper_access = 0; + return v; +} + +// "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,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, /* 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, /* 8 */ + /* BPLxDAT */ + 0,0,0,0,0,0,0,0, /* 8 */ + /* SPRxPTH/SPRxPTL */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 16 */ + /* SPRxPOS/SPRxCTL/SPRxDATA/SPRxDATB */ + 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, + /* 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 update_copper (int until_hpos) +{ + int vp = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80); + int c_hpos = cop_state.hpos; + + if (nocustom()) { + eventtab[ev_copper].active = 0; + return; + } + + if(currprefs.fast_copper) { + if (eventtab[ev_copper].active) { + eventtab[ev_copper].active = 0; + return; + } + } + + if (cop_state.state == COP_wait && vp < cop_state.vcmp) { + eventtab[ev_copper].active = 0; + 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_safe (c_hpos); + + if (cop_state.movedelay) { // cop_state.movedelay is 0 or 1 + cop_state.movedelay = 0; + custom_wput_copper (c_hpos, cop_state.moveaddr, cop_state.movedata, 0); + if (! copper_enabled_thisline) + goto out; + } + + 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)) + continue; + cop_state.state = COP_wait1; + break; + case COP_skip_in2: + if (copper_cant_read (old_hpos)) + 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 + // Cycle is used and needs to be free. + if (copper_cant_read (old_hpos)) + continue; + if (old_hpos == maxhpos - 2) { + // if COP_strobe_delay2 would cross scanlines (positioned immediately + // after first strobe/refresh slot) it will disappear! + cop_state.state = COP_read1; + if (cop_state.strobe == 1) + cop_state.ip = cop1lc; + else + cop_state.ip = cop2lc; + cop_state.strobe = 0; + } else { + cop_state.state = COP_strobe_delay2; + cop_state.ip += 2; + } + break; + case COP_strobe_delay2: + // Second cycle after COPJMP. This is the strange one. + // This cycle does not need to be free + // But it still gets allocated by copper if it is free = CPU and blitter can't use it. + cop_state.state = COP_read1; + // 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_strobe_delay1x: + // First cycle after COPJMP and Copper was waiting. This is the buggy one. + // Cycle can be free and copper won't allocate it. + // If Blitter uses this cycle = Copper's address gets copied blitter DMA pointer.. + cop_state.state = COP_strobe_delay2x; + break; + case COP_strobe_delay2x: + // Second cycle fetches following word and tosses it away. Must be free cycle + // but is not allocated, blitter or cpu can still use it. + if (copper_cant_read (old_hpos)) + continue; + cop_state.state = COP_read1; + // 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: + // cycle after vblank strobe fetches word from old pointer first + if (copper_cant_read (old_hpos)) + continue; + cop_state.state = COP_read1; + cop_state.i1 = chipmem_wget_indirect (cop_state.ip); + cop_state.ip = cop1lc; + break; + + case COP_read1: + if (copper_cant_read (old_hpos)) + continue; + cop_state.i1 = chipmem_wget_indirect (cop_state.ip); + cop_state.ip += 2; + cop_state.state = COP_read2; + break; + + case COP_read2: + if (copper_cant_read (old_hpos)) + continue; + cop_state.i2 = chipmem_wget_indirect (cop_state.ip); + cop_state.ip += 2; + + 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; + } else { // MOVE + unsigned int reg = cop_state.i1 & 0x1FE; + uae_u16 data = cop_state.i2; + cop_state.state = COP_read1; + 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; + } + + 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 { + // 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 = 1; + } else { + custom_wput_copper (old_hpos, reg, data, 0); + } + } + } + break; + + case COP_wait1: + cop_state.state = COP_wait; + + cop_state.vcmp = (cop_state.i1 & (cop_state.i2 | 0x8000)) >> 8; + cop_state.hcmp = (cop_state.i1 & cop_state.i2 & 0xFE); + + vp = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80); + + if (cop_state.i1 == 0xFFFF && cop_state.i2 == 0xFFFE) { + cop_state.state = COP_waitforever; + 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: + { + int ch_comp = c_hpos; + if (ch_comp & 1) + ch_comp = 0; + if (copper_cant_read (old_hpos)) + continue; + + hp = ch_comp & (cop_state.i2 & 0xFE); + if (vp == cop_state.vcmp && hp < cop_state.hcmp) { + /* Position not reached yet. */ + if(currprefs.fast_copper) { + if ((cop_state.i2 & 0xFE) == 0xFE) { + int wait_finish = cop_state.hcmp - 2; + /* This will leave c_hpos untouched if it's equal to wait_finish. */ + if (wait_finish < c_hpos) + return; + else if (wait_finish <= until_hpos) { + c_hpos = wait_finish; + } else + c_hpos = until_hpos; + } + } + break; + } + + /* Now we know that the comparisons were successful. We might still + have to wait for the blitter though. */ + if ((cop_state.i2 & 0x8000) == 0) { + 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; + } + } + + 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)) + continue; + + vcmp = (cop_state.i1 & (cop_state.i2 | 0x8000)) >> 8; + hcmp = (cop_state.i1 & cop_state.i2 & 0xFE); + vp1 = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80); + hp1 = c_hpos & (cop_state.i2 & 0xFE); + + if ((vp1 > vcmp || (vp1 == vcmp && hp1 >= hcmp)) && ((cop_state.i2 & 0x8000) != 0 || bltstate == BLT_done)) + cop_state.ignore_next = 1; + + cop_state.state = COP_read1; + + break; + } + default: + break; + } + } + +out: + cop_state.hpos = c_hpos; + last_copper_hpos = until_hpos; + + if(currprefs.fast_copper) { + /* The test against maxhpos also prevents us from calling predict_copper + when we are being called from hsync_handler, which would not only be + stupid, but actively harmful. */ + if ((regs.spcflags & SPCFLAG_COPPER) && (c_hpos + 8 < maxhpos)) + predict_copper (); + } +} + +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_waitforever || cop_state.state == COP_bltwait || nocustom()) + return; + + if (cop_state.state == COP_wait) { + int vp = vpos & (((cop_state.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 & ~1)) + hpos = maxhpos & ~1; + cop_state.hpos = hpos; + } + + // if COPJMPx was written while DMA was disabled, advance to next state, + // COP_strobe_extra is single cycle only and does not need free bus. + // (copper state emulation does not run if DMA is disabled) + if (!wasenabled && cop_state.state == COP_strobe_extra) + cop_state.state = COP_strobe_delay1; + + copper_enabled_thisline = 1; + + if(currprefs.fast_copper) { + predict_copper (); + if (! eventtab[ev_copper].active) + set_special (SPCFLAG_COPPER); + } + else + set_special (SPCFLAG_COPPER); +} + +static void copper_handler (void) +{ + /* This will take effect immediately, within the same cycle. */ + set_special (SPCFLAG_COPPER); + + if (! copper_enabled_thisline) + return; + + eventtab[ev_copper].active = 0; +} + +/* +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 (void) +{ + int vp = vpos; + + if (cop_state.state != COP_bltwait) + return; + + int hpos = current_hpos() + 3; + hpos &= ~1; + if (hpos >= maxhpos) { + hpos -= maxhpos; + vp++; + } + cop_state.hpos = hpos; + cop_state.state = COP_read1; + + 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, unsigned int addr) +{ + if (eventtab[ev_copper].active) { + if (hpos != maxhpos) { + /* There might be reasons why we don't actually need to bother + updating the copper. */ + if (hpos < cop_state.first_sync) + return; + + if ((cop_state.regtypes_modified & regtypes[addr & 0x1FE]) == 0) + return; + } + + eventtab[ev_copper].active = 0; + if (do_schedule) + events_schedule (); + set_special (SPCFLAG_COPPER); + } + + /* Need to let the copper advance to the current position. */ + if (copper_enabled_thisline) + update_copper (hpos); +} + +STATIC_INLINE uae_u16 sprite_fetch (struct sprite *s) +{ + last_custom_value1 = chipmem_wget_indirect (s->pt); + s->pt += 2; + return last_custom_value1; +} +STATIC_INLINE uae_u16 sprite_fetch2(struct sprite *s) +{ + uae_u16 data = 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); + + if (isdma && s->dblscan && (fmode & 0x8000) && (vpos & 1) != (s->vstart & 1) && s->dmastate) { + spr_arm (num, 1); + return; + } + if (vpos == s->vstart) { + s->dmastate = 1; + } + if (vpos == s->vstop || vpos == sprite_vblank_endline) { + s->dmastate = 0; + } + + if (!isdma) + return; + + 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); + switch (sprite_width) + { + case 64: + s->pt += 4; + case 32: + s->pt += 2; + break; + } + if (cycle == 0) { + SPRxPOS_1 (data, num); + s->dmacycle = 1; + } else { + SPRxCTL_1 (data, num); + s->dmastate = 0; + sprstartstop (s); + } + } + if (vpos == sprite_vblank_endline) { + // s->vstart == sprite_vblank_endline won't enable the sprite. + s->dmastate = 0; + } + } + + if (s->dmastate && !posctl && dma) { + uae_u16 data; + + data = sprite_fetch (s); + if (cycle == 0) { + SPRxDATA_1 (data, num); + s->dmacycle = 1; + } else { + SPRxDATB_1 (data, num); + spr_arm (num, 1); + } + switch (sprite_width) + { + case 64: + { + uae_u16 data32 = sprite_fetch2 (s); + uae_u16 data641 = sprite_fetch2 (s); + uae_u16 data642 = sprite_fetch2 (s); + 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); + if (cycle == 0) + sprdata[num][1] = data32; + else + sprdatb[num][1] = data32; + } + break; + } + } +} + +static void do_sprites (int hpos) +{ + int maxspr, minspr; + int i; + + if (vpos == 0) { + // It is also possible armed status is cleared + // I am not sure so lets start with clearing + // data only + if (dmaen (DMA_SPRITE)) { + for (i = 0; i < MAX_SPRITES; i++) { + sprdata[i][0] = 0; + sprdatb[i][0] = 0; + sprdata[i][1] = 0; + sprdatb[i][1] = 0; + sprdata[i][2] = 0; + sprdatb[i][2] = 0; + sprdata[i][3] = 0; + sprdatb[i][3] = 0; + } + } + return; + } + if (vpos < sprite_vblank_endline) + return; + + 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) { + do_sprites_1 (num, cycle, i); + } + } + + last_sprite_hpos = hpos; +} + +static void init_sprites (void) +{ + memset (sprpos, 0, sizeof sprpos); + memset (sprctl, 0, sizeof sprctl); +} + +static void init_hardware_frame (void) +{ + next_lineno = 0; + prev_lineno = -1; + diwstate = DIW_waiting_start; + ddfstate = DIW_waiting_start; + plf_state = plf_end; +} + +void init_hardware_for_drawing_frame (void) +{ + /* Avoid this code in the first frame after a customreset. */ + if (next_sprite_entry > 0) { + int npixels = curr_sprite_entries[next_sprite_entry].first_pixel; + memset(spixels, 0, npixels * sizeof *spixels); + memset(spixstate.bytes, 0, npixels * sizeof *spixstate.bytes); + } + + next_color_change = 0; + next_sprite_entry = 0; + next_color_entry = 0; + remembered_color_entry = -1; + + curr_sprite_entries[0].first_pixel = 0; + next_sprite_forced = 1; +} + +STATIC_INLINE void rtg_vsync (void) +{ +#ifdef PICASSO96 + picasso_handle_vsync (); +#endif +} + +void fpscounter_reset (void) +{ + timeframes = 0; + frametime = 0; + lastframetime = read_processor_time (); +} + +static void fpscounter (void) +{ + frame_time_t now, last; + const int mcnt = 20; + + now = read_processor_time (); + last = now - lastframetime; + lastframetime = now; + + frametime += last; + timeframes++; + + if ((timeframes % mcnt) == 0) { + int fps = frametime == 0 ? 0 : (syncbase * mcnt) / (frametime / 10); + if(currprefs.gfx_framerate > 0) + fps = fps / (currprefs.gfx_framerate + 1); + if (fps > 9999) + fps = 9999; + gui_data.fps = (fps + 5) / 10; + frametime = 0; + } +} + +// vsync functions that are not hardware timing related +static void vsync_handler_pre (void) +{ + handle_events (); + +#ifdef PICASSO96 + if (currprefs.rtgmem_size) + rtg_vsync (); +#endif + + blkdev_vsync (); + CIA_vsync_prehandler(); + + if (quit_program > 0) { + /* prevent possible infinite loop at wait_cycles().. */ + framecnt = 0; + reset_decisions (); + return; + } + + if (timehack_alive > 0) + timehack_alive--; + + inputdevice_vsync (); + filesys_vsync (); + + vsync_handle_redraw (); + + fpscounter(); + + vsync_handle_check (); +} + +// emulated hardware vsync +static void vsync_handler_post (void) +{ + DISK_vsync(); + + if (bplcon0 & 4) { + lof_store = lof_store ? 0 : 1; + } + + lof_current = lof_store; + if (lof_changing) { + // still same? Trigger change now. + if ((!lof_store && lof_changing < 0) || (lof_store && lof_changing > 0)) { + lof_changed_previous_field++; + lof_changed = 1; + // lof toggling? decide as interlace. + if (lof_changed_previous_field >= LOF_TOGGLES_NEEDED) { + lof_changed_previous_field = LOF_TOGGLES_NEEDED; + if (lof_lace == false) + lof_lace = true; + else + lof_changed = 0; + } + if (bplcon0 & 4) + lof_changed = 0; + } + lof_changing = 0; + } + else { + lof_changed_previous_field = 0; + lof_lace = false; + } + +#ifdef JIT + if (currprefs.cachesize) { + vsyncmintime = last_synctime; + frh_count = 0; + } + else +#endif + { + vsyncmintime = last_synctime + vsynctimebase * (maxvpos_display - LAST_SPEEDUP_LINE) / maxvpos_display; + } + +#ifdef PICASSO96 + if (picasso_on) + { + vpos_count = (int) maxvpos * vblank_hz / 60; + vtotal = vpos_count; + } +#endif + if ((beamcon0 & (0x20 | 0x80)) != (new_beamcon0 & (0x20 | 0x80))) { + init_hz(); + } + else if (vpos_count > 0 && abs(vpos_count - vpos_count_diff) > 1 && vposw_change < 4) { + init_hz(); + } + else if (lof_changed) { + compute_framesync(); + } + + lof_changed = 0; + vposw_change = 0; + + eventtab[ev_copper].active = 0; + COPJMP(1, 1); + + init_hardware_frame(); +} + +#ifdef JIT + +#define N_LINES 8 + +STATIC_INLINE int trigger_frh (int v) +{ + return (v & (N_LINES - 1)) == 0; +} + +STATIC_INLINE 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 < 0) { + frame_time_t curr_time = read_processor_time (); + vsyncmintime += vsynctimebase * N_LINES / maxvpos_display; + if (rpt_did_reset) { + vsyncmintime = curr_time + vsynctimebase; + rpt_did_reset = 0; + } + /* Allow this to be one frame's worth of cycles out */ + while (diff32 (curr_time, vsyncmintime + vsynctimebase) > 0) { + vsyncmintime += vsynctimebase * N_LINES / maxvpos_display; + } + } +} +#endif + +static uae_u16 dmal, dmal_hpos; + +static void dmal_func2 (void) +{ + event_remevent(ev_dmal); + + // Disk and Audio DMA bits are ignored by Agnus, Agnus only checks DMAL and master bit + if (!(dmacon & DMA_MASTER)) + return; + + while (dmal) { + if (dmal & 3) { + if (dmal_hpos >= 6) { + int nr = (dmal_hpos - 6) >> 1; + audio_dmal_do (nr, (dmal & 2) ? true : false); + } else { + uae_u16 dat; + int w = (dmal & 2) ? 1 : 0; + uaecptr pt = disk_getpt (); + // disk_fifostatus() needed in >100% disk speed modes + if (w) { + // write to disk + if (disk_fifostatus () <= 0) { + dat = chipmem_wget_indirect (pt); + DSKDAT (dat); + } + } else { + // read from disk + if (disk_fifostatus () >= 0) { + dat = DSKDATR (); + chipmem_wput_indirect (pt, dat); + } + } + } + } + + dmal_hpos += 2; + dmal >>= 2; + } +} + +STATIC_INLINE void events_dmal (void) +{ + if (currprefs.cachesize) { + dmal_func2 (); + } else { + event_newevent (ev_dmal, 7); + } +} + +static void events_dmal_hsync (void) +{ + uae_u16 dmal_disk = disk_dmal (); + + dmal = audio_dmal (); + if (!dmal && !dmal_disk) + return; + + if(dmal_disk) + { + dmal_hpos = 0; + dmal <<= 6; + dmal |= dmal_disk; + } + else + dmal_hpos = 6; + events_dmal (); +} + +static bool is_custom_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) +{ + eventtab[ev_hsync].evtime = get_cycles () + HSYNCTIME; + eventtab[ev_hsync].oldcycles = get_cycles (); +} + +// this finishes current line +static void hsync_handler_pre (bool onvsync) +{ + int hpos = current_hpos (); + + if (!nocustom()) { + /* Using 0x8A makes sure that we don't accidentally trip over the + modified_regtypes check. */ + sync_copper_with_cpu (maxhpos, 0, 0x8A); + finish_decisions (); + if (thisline_decision.plfleft >= 0) { + if (currprefs.collision_level > 1) + DO_SPRITE_COLLISIONS + if (currprefs.collision_level > 2) + DO_PLAYFIELD_COLLISIONS + } + hsync_record_line_state (next_lineno); + } + +#ifdef CD32 + AKIKO_hsync_handler (); +#endif + +#ifdef PICASSO96 + picasso_handle_hsync (); +#endif + + DISK_hsync (); + if (currprefs.produce_sound) + audio_hsync (); + + hsync_counter++; + + vpos++; + vpos_count++; + if (vpos >= maxvpos_total) + vpos=0; + if (onvsync) { + vpos = 0; + } + set_hpos (); +} + +// this prepares for new line +static void hsync_handler_post (bool onvsync) +{ + last_copper_hpos = 0; + + CIA_hsync_posthandler (); + if (onvsync) { + CIA_vsync_posthandler (); + } + + inputdevice_hsync (); + + last_custom_value1 = 0xffff; // refresh slots should set this to 0xffff + + if (!nocustom()) { + if (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][bplcon0_res][bplcon0_planes_limit], + cycle_diagram_free_cycles[fetchmode][bplcon0_res][bplcon0_planes_limit]); + } + } + + if (onvsync) { + vpos = 0; + vsync_handler_post (); + vpos_count = 0; + } + + if (vpos == 0) + send_interrupt (5); + + events_dmal_hsync (); + + if (currprefs.m68k_speed < 0) { +#ifdef JIT + if (currprefs.cachesize) { + frh_count++; + if (trigger_frh(frh_count) && vpos < maxvpos_display - LAST_SPEEDUP_LINE) { + frh_handler(); + } + is_syncline = trigger_frh(frh_count+1) && vpos < maxvpos_display - LAST_SPEEDUP_LINE + 1 && !rpt_did_reset; + } else { +#endif + is_syncline = vpos + 1 == maxvpos_display + lof_store - LAST_SPEEDUP_LINE; +#ifdef JIT + } +#endif + } + + if (!nocustom()) { + int lineno = vpos; + if (lineno >= MAXVPOS) + lineno %= MAXVPOS; + prev_lineno = next_lineno; + next_lineno = lineno; + reset_decisions (); + } + + if (uae_int_requested) { + INTREQ (0x8000 | 0x0008); + } + + { + extern void bsdsock_fake_int_handler (void); + extern int volatile bsd_int_requested; + if (bsd_int_requested) + bsdsock_fake_int_handler (); + } + + /* Default to no bitplane DMA overriding sprite DMA */ + plfstrt_sprite = 0xff; + /* See if there's a chance of a copper wait ending this line. */ + cop_state.hpos = 0; + compute_spcflag_copper (maxhpos); +} + +static void init_regtypes (void) +{ + int i; + for (i = 0; i < 512; i += 2) { + regtypes[i] = REGTYPE_ALL; + if ((i >= 0x20 && i < 0x28) || i == 0x08 || i == 0x7E) + regtypes[i] = REGTYPE_DISK; + else if (i >= 0x68 && i < 0x70) + regtypes[i] = REGTYPE_NONE; + else if (i >= 0x40 && i < 0x78) + regtypes[i] = REGTYPE_BLITTER; + else if (i >= 0xA0 && i < 0xE0 && (i & 0xF) < 0xE) + regtypes[i] = REGTYPE_AUDIO; + else if (i >= 0xA0 && i < 0xE0) + regtypes[i] = REGTYPE_NONE; + else if (i >= 0xE0 && i < 0x100) + regtypes[i] = REGTYPE_PLANE; + else if (i >= 0x120 && i < 0x180) + regtypes[i] = REGTYPE_SPRITE; + else if (i >= 0x180 && i < 0x1C0) + regtypes[i] = REGTYPE_COLOR; + else switch (i) { + case 0x02: + /* DMACONR - setting this to REGTYPE_BLITTER will cause it to + conflict with DMACON (since that is REGTYPE_ALL), and the + blitter registers (for the BBUSY bit), but nothing else, + which is (I think) what we want. */ + regtypes[i] = REGTYPE_BLITTER; + break; + case 0x04: case 0x06: case 0x2A: case 0x2C: + regtypes[i] = REGTYPE_POS; + break; + case 0x0A: case 0x0C: + case 0x0E: + regtypes[i] = REGTYPE_SPRITE; + break; + case 0x10: + regtypes[i] = REGTYPE_AUDIO; + break; + case 0x12: case 0x14: case 0x16: + case 0x36: + regtypes[i] = REGTYPE_JOYPORT; + break; + case 0x1A: + regtypes[i] = REGTYPE_DISK; + break; + case 0x102: + case 0x104: + case 0x106: + case 0x108: + case 0x10A: + regtypes[i] = REGTYPE_PLANE; + break; + case 0x88: case 0x8A: + case 0x8E: case 0x90: case 0x92: case 0x94: + case 0x96: + case 0x100: + regtypes[i] |= REGTYPE_FORCE; + break; + } + } +} + +static void hsync_handler (void) +{ + bool vs = is_custom_vsync (); + hsync_handler_pre (vs); + if (vs) { + vsync_handler_pre (); + if (savestate_check ()) { + uae_reset (0, 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_copper].handler = copper_handler; + eventtab[ev_copper].active = 0; + eventtab[ev_misc].handler = MISC_handler; + eventtab[ev_audio].handler = audio_evhandler; + eventtab[ev_blitter].handler = blitter_handler; + eventtab[ev_dmal].handler = dmal_func2; + + eventtab2[ev2_disk].handler = DISK_handler; + eventtab2[ev2_disk_motor0].handler = DISK_motordelay_func; + eventtab2[ev2_disk_motor1].handler = DISK_motordelay_func; + eventtab2[ev2_disk_motor2].handler = DISK_motordelay_func; + eventtab2[ev2_disk_motor3].handler = DISK_motordelay_func; + + events_schedule (); +} + +void custom_prepare (void) +{ + set_hpos (); + hsync_handler_post (true); +} + +void custom_reset (bool hardreset, bool keyboardreset) +{ + int i; + int zero = 0; + + target_reset (); + reset_all_systems (); + write_log (_T("Reset at %08X. Chipset mask = %08X\n"), M68K_GETPC, currprefs.chipset_mask); + + nr_armed = 0; + + if (! savestate_state) { + hsync_counter = 0; + currprefs.chipset_mask = changed_prefs.chipset_mask; + update_mirrors(); + + if (hardreset) { + if (!aga_mode) { + uae_u16 c = ((currprefs.chipset_mask & CSMASK_ECS_DENISE) && !(currprefs.chipset_mask & CSMASK_AGA)) ? 0xfff : 0x000; + for (i = 0; i < 32; i++) { + current_colors.color_regs_ecs[i] = c; + current_colors.acolors[i] = getxcolor (c); + } + } else { + uae_u32 c = 0; + for (i = 0; i < 256; i++) { + current_colors.color_regs_aga[i] = c; + current_colors.acolors[i] = getxcolor (c); + } + } + } + clxdat = 0; + + /* Clear the armed flags of all sprites. */ + memset (spr, 0, sizeof spr); + + dmacon = 0; + intreq = 0; + intena = 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 + + // Some more values to reset... + bltcon0 = bltcon1 = bltapt = bltbpt = bltcpt = bltdpt = 0; + memset(&blt_info, 0, sizeof blt_info); + last_custom_value1 = 0; + last_synctime = 0; + frh_count = 0; + intreq = 0; + cop1lc = cop2lc = 0; + bpl1mod = bpl2mod = 0; + bplcon1 = bplcon2 = 0; + diwstrt = diwstop = ddfstrt = ddfstop = ddfstrt_old_hpos = 0; + vsstop = 0; + copper_enabled_thisline = 0; + estimated_last_fetch_cycle = 0; + + FMODE (0, 0); + CLXCON (0); + CLXCON2 (0); + setup_fmodes (0); + sprite_width = GET_SPRITEWIDTH (fmode); + beamcon0 = new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20; + bltstate = BLT_done; + lof_store = lof_current = 0; + lof_lace = false; + init_sprites (); + } + +#ifdef AUTOCONFIG + expamem_reset (); +#endif + DISK_reset (); + CIA_reset (); +#ifdef JIT + compemu_reset (); +#endif + unset_special (~(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE)); + + vpos = 0; + vpos_count = vpos_count_diff = 0; + + inputdevice_reset(); + timehack_alive = 0; + + curr_sprite_entries[0].first_pixel = 0; + curr_sprite_entries[1].first_pixel = 0; + next_sprite_entry = 0; + memset (spixels, 0, sizeof spixels); + memset (&spixstate, 0, sizeof spixstate); + + cop_state.state = COP_stop; + cop_state.movedelay = 0; + cop_state.strobe = 0; + cop_state.ignore_next = 0; + diwstate = DIW_waiting_start; + + dmal = 0; + new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20; + time_per_frame = 1000 * 1000 / (currprefs.ntscmode ? 60 : 50); + init_hz_full (); + lof_changing = 0; + + audio_reset (); + if (!isrestore ()) { + /* must be called after audio_reset */ + adkcon = 0; + audio_update_adkmasks (); + } + + init_hardware_frame (); + drawing_init (); + + reset_decisions (); + + init_regtypes (); + + if (isrestore ()) { + uae_u16 v; + uae_u32 vv; + + audio_update_adkmasks (); + INTENA (0); + INTREQ (0); + + if (diwhigh) + diwhigh_written = 1; + else + diwhigh_written = 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] = (unsigned short)-1; + record_color_change (0, i, vv); + remembered_color_entry = -1; + current_colors.color_regs_ecs[i] = vv; + current_colors.acolors[i] = xcolors[vv]; + } + } 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); + } + } + CLXCON (clxcon); + CLXCON2 (clxcon2); + calcdiw (); + for (i = 0; i < 8; i++) { + SPRxCTLPOS (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); + + if (hardreset) + rtc_hardreset(); + +#ifdef PICASSO96 + picasso_reset (); +#endif +} + + +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; +} + +static int allocate_sprite_tables (void) +{ + if(curr_sprite_entries == 0) + curr_sprite_entries = xmalloc (struct sprite_entry, MAX_SPR_PIXELS / 16); + if(curr_color_changes == 0) + curr_color_changes = xmalloc (struct color_change, MAX_REG_CHANGE); + + return 1; +} + +int custom_init (void) +{ + if (!allocate_sprite_tables()) + return 0; + +#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 (); + + next_sprite_entry = 0; + 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, _T("Custom chipset"), + custom_lgeti, custom_wgeti, ABFLAG_IO, 0x1ff, 0xdff000 +}; + +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 (uaecptr addr, int noput) +{ + uae_u16 v; + int missing; +#ifdef JIT + special_mem |= S_READ; +#endif + switch (addr & 0x1fe) { + case 0x000: v = 0xffff; break; /* BPLDDAT */ + case 0x002: v = DMACONR (current_hpos ()); break; + case 0x004: v = VPOSR (); break; + case 0x006: v = VHPOSR (); break; + + case 0x008: v = 0xffff; 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 = 0x3000 /* no data */; break; + case 0x01A: v = DSKBYTR (current_hpos ()); break; + case 0x01C: v = INTENAR (); break; + case 0x01E: v = INTREQR (); break; + case 0x07C: + v = DENISEID (&missing); + if (missing) + goto writeonly; + 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: + if (!(aga_mode)) + goto writeonly; + v = COLOR_READ ((addr & 0x3E) / 2); + break; + + default: +writeonly: + /* 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: + * Can also return last CPU accessed value + * Remembers old last_custom_value1 + */ + v = last_custom_value1; + line_cyclebased = vpos; + if (!noput) { + uae_u16 l; + int hpos = current_hpos (); + if (aga_mode) { + l = 0; + } else { + l = currprefs.cpu_compatible && currprefs.cpu_model == 68000 ? regs.irc : 0xffff; + } + decide_line (hpos); + decide_fetch_safe (hpos); + custom_wput_1 (hpos, addr, l, 1); + } + return v; + } + return v; +} + +STATIC_INLINE uae_u32 custom_wget2 (uaecptr addr) +{ + uae_u32 v; + int hpos = current_hpos (); + + sync_copper_with_cpu (hpos, 1, addr); + v = custom_wget_1 (addr, 0); + 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 & 0xFFFF; + } + 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 & 0xFF; +} + +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) +{ + addr &= 0x1FE; + value &= 0xffff; + 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). Writing to DMA write registers won't do anything */; break; + + case 0x02A: VPOSW (value); break; + case 0x02C: VHPOSW (value); break; + case 0x02E: COPCON (value); break; + case 0x030: break; + case 0x032: break; + case 0x034: POTGO (value); break; + case 0x036: JOYTEST (value); break; + + case 0x040: BLTCON0 (value); break; + case 0x042: BLTCON1 (value); break; + + case 0x044: BLTAFWM (value); break; + case 0x046: BLTALWM (value); break; + + case 0x048: BLTCPTH (value); break; + case 0x04A: BLTCPTL (value); break; + case 0x04C: BLTBPTH (value); break; + case 0x04E: BLTBPTL (value); break; + case 0x050: BLTAPTH (value); break; + case 0x052: BLTAPTL (value); break; + case 0x054: BLTDPTH (value); break; + case 0x056: BLTDPTL (value); break; + + case 0x058: BLTSIZE (value); break; + case 0x05A: BLTCON0L (value); break; + case 0x05C: BLTSIZV (value); break; + case 0x05E: BLTSIZH (value); break; + + case 0x064: BLTAMOD (value); break; + case 0x062: BLTBMOD (value); break; + case 0x060: BLTCMOD (value); break; + case 0x066: BLTDMOD (value); break; + + case 0x070: BLTCDAT (value); break; + case 0x072: BLTBDAT (value); break; + case 0x074: BLTADAT (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; + case 0x106: BPLCON3 (hpos, value); break; + + case 0x108: BPL1MOD (hpos, value); break; + case 0x10A: BPL2MOD (hpos, value); break; + case 0x10C: BPLCON4 (hpos, value); break; + case 0x10E: CLXCON2 (value); break; + + 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 0x1C0: if (htotal != value) { htotal = value & (MAXHPOS_ROWS - 1); varsync (); } break; + case 0x1C2: if (hsstop != value) { hsstop = value & (MAXHPOS_ROWS - 1); varsync (); } break; + case 0x1C4: if (hbstrt != value) { hbstrt = value & (MAXHPOS_ROWS - 1); varsync (); } break; + case 0x1C6: if (hbstop != value) { hbstop = value & (MAXHPOS_ROWS - 1); varsync (); } break; + case 0x1C8: if (vtotal != value) { vtotal = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; + case 0x1CA: if (vsstop != value) { vsstop = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; + case 0x1CC: if (vbstrt < value || vbstrt > (value & (MAXVPOS_LINES_ECS - 1)) + 1) { vbstrt = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; + case 0x1CE: if (vbstop < value || vbstop > (value & (MAXVPOS_LINES_ECS - 1)) + 1) { vbstop = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; + case 0x1DC: BEAMCON0 (value); break; + case 0x1DE: if (hsstrt != value) { hsstrt = value & (MAXHPOS_ROWS - 1); varsync (); } break; + case 0x1E0: if (vsstrt != value) { vsstrt = value & (MAXVPOS_LINES_ECS - 1); varsync (); } break; + case 0x1E2: if (hcenter != value) { hcenter = value & (MAXHPOS_ROWS - 1); varsync (); } break; + case 0x1E4: DIWHIGH (hpos, value); break; + + case 0x1FC: FMODE (hpos, value); break; + case 0x1FE: FNULL (value); break; + + /* writing to read-only register causes read access */ + default: + if (!noget) { + custom_wget_1 (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 + sync_copper_with_cpu (hpos, 1, addr); + 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) +{ + uae_u16 rval; + + if (aga_mode) { + 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 + 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, ru16; + int dskpt; + int i; + + audio_reset (); + + changed_prefs.chipset_mask = currprefs.chipset_mask = RL & CSMASK_MASK; + update_mirrors(); + blt_info.bltddat = RW; /* 000 BLTDDAT */ + ru16 = RW; /* 002 DMACONR -> see also 096 */ + if((ru16 & 0x4000) == 0) + bltstate = BLT_done; + blt_info.blitzero = (ru16 & 0x2000 ? 1 : 0); + 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 -> see 09E */ + RW; /* 012 POT0DAT */ + RW; /* 014 POT1DAT */ + RW; /* 016 POTINP -> see 034 */ + RW; /* 018 SERDATR* */ + dskbytr = RW; /* 01A DSKBYTR */ + RW; /* 01C INTENAR -> see 09A */ + RW; /* 01E INTREQR -> see 09C */ + 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; /* 02A VPOSW */ + RW; /* 02C VHPOSW */ + COPCON(RW); /* 02E COPCON */ + RW; /* 030 SERDAT */ + RW; /* 032 SERPER* */ + potgo_value = 0; POTGO (RW); /* 034 POTGO */ + RW; /* 036 JOYTEST* */ + RW; /* 038 STREQU */ + RW; /* 03A STRVHBL */ + RW; /* 03C STRHOR */ + RW; /* 03E STRLONG */ + BLTCON0(RW); /* 040 BLTCON0 */ + BLTCON1(RW); /* 042 BLTCON1 */ + BLTAFWM(RW); /* 044 BLTAFWM */ + BLTALWM(RW); /* 046 BLTALWM */ + bltcpt = RL; /* 048-04B BLTCPT */ + bltbpt = RL; /* 04C-04F BLTBPT */ + bltapt = RL; /* 050-053 BLTAPT */ + bltdpt = RL; /* 054-057 BLTDPT */ + RW; /* 058 BLTSIZE */ + RW; /* 05A BLTCON0L -> see 040 */ + blt_info.vblitsize=RW; /* 05C BLTSIZV */ + blt_info.hblitsize=RW; /* 05E BLTSIZH */ + blt_info.bltcmod = RW; /* 060 BLTCMOD */ + blt_info.bltbmod = RW; /* 062 BLTBMOD */ + blt_info.bltamod = RW; /* 064 BLTAMOD */ + blt_info.bltdmod = RW; /* 066 BLTDMOD */ + RW; /* 068 ? */ + RW; /* 06A ? */ + RW; /* 06C ? */ + RW; /* 06E ? */ + blt_info.bltcdat =RW; /* 070 BLTCDAT */ + BLTBDAT(RW); /* 072 BLTBDAT */ + blt_info.bltadat=RW; /* 074 BLTADAT */ + RW; /* 076 ? */ + RW; /* 078 ? */ + RW; /* 07A ? */ + RW; /* 07C LISAID */ + ru16=RW; DSKSYNC(-1, ru16); /* 07E DSKSYNC */ + cop1lc = RL; /* 080/082 COP1LC */ + cop2lc = RL; /* 084/086 COP2LC */ + RW; /* 088 COPJMP1 */ + RW; /* 08A COPJMP2 */ + RW; /* 08C COPINS */ + 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 = RW; /* 09A INTENA */ + intreq = RW; /* 09C INTREQ */ + adkcon = RW; /* 09E ADKCON */ + /* 0A0 - 0DE Audio regs */ + for (i = 0; i < 8; 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 */ + /* 120 - 17E Sprite regs */ + for(i = 0; i < 32; i++) + current_colors.color_regs_ecs[i] = RW; /* 180-1BE 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 ? */ + + current_colors.borderblank = isbrdblank (-1, bplcon0, bplcon3); + DISK_restore_custom (dskpt, dsklen, dskbytr); + + FMODE (0, fmode); + + return src; +} + +#define SB save_u8 +#define SW save_u16 +#define SL save_u32 + +uae_u8 *save_custom (int *len, uae_u8 *dstptr, int full) +{ + uae_u8 *dstbak, *dst; + int i, dummy; + 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 (blt_info.bltddat); /* 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)); /* 02A VPOSW */ + SW (0); /* 02C VHPOSW */ + SW (copcon); /* 02E COPCON */ + SW (0); /* 030 SERDAT * */ + SW (0); /* 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 BLTBPT */ + SL (bltapt); /* 050-053 BLTAPT */ + SL (bltdpt); /* 054-057 BLTDPT */ + 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 (&dummy)); /* 07C DENISEID/LISAID */ + SW (dsksync); /* 07E DSKSYNC */ + SL (cop1lc); /* 080-083 COP1LC */ + SL (cop2lc); /* 084-087 COP2LC */ + SW (0); /* 088 COPJMP1 */ + SW (0); /* 08A COPJMP2 */ + SW (0); /* 08C COPINS */ + 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 */ + /* 0A0 - 0DE Audio regs */ + 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 */ + /* 120 - 17E Sprite regs */ + if (full) { + for (i = 0; i < 8; i++) { + SL (spr[i].pt); /* 120-13E SPRxPT */ + } + for (i = 0; i < 8; i++) { + 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++) + 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; +} + +uae_u8 *restore_custom_agacolors (uae_u8 *src) +{ + int i; + + for (i = 0; i < 256; i++) { + uae_u32 v = RL; + current_colors.color_regs_aga[i] = v; + } + 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++) + SL (current_colors.color_regs_aga[i]); + *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 & 1; + 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; + cia_set_overlay ((v & 2) != 0); + + currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p = RBB; + currprefs.cs_cd32cd = changed_prefs.cs_cd32cd = RBB; + currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram = 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 ((&get_mem_bank (0) != &chipmem_bank ? 2 : 0) | 1); + + SB (currprefs.cs_cd32c2p); + SB (currprefs.cs_cd32cd); + SB (currprefs.cs_cd32nvram); + + *len = dst - dstbak; + return dstbak; +} + +#endif /* SAVESTATE */ + +void check_prefs_changed_custom (void) +{ + currprefs.gfx_framerate = changed_prefs.gfx_framerate; + if (inputdevice_config_change_test ()) + inputdevice_copyconfig (&changed_prefs, &currprefs); + currprefs.immediate_blits = changed_prefs.immediate_blits; + currprefs.waiting_blits = changed_prefs.waiting_blits; + currprefs.collision_level = changed_prefs.collision_level; + currprefs.fast_copper = changed_prefs.fast_copper; + currprefs.cs_cd32cd = changed_prefs.cs_cd32cd; + currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p; + currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram; + + if (currprefs.chipset_mask != changed_prefs.chipset_mask || + currprefs.ntscmode != changed_prefs.ntscmode) { + 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 (); + } +} diff --git a/src/include/sysdeps.h b/src/include/sysdeps.h index ff1671b2..c8255168 100644 --- a/src/include/sysdeps.h +++ b/src/include/sysdeps.h @@ -438,20 +438,41 @@ extern void gui_message (const TCHAR *,...); * Byte-swapping functions */ -#ifdef ARMV6_ASSEMBLY - -STATIC_INLINE uae_u32 do_byteswap_32(uae_u32 v) {__asm__ ( +/* Try to use system bswap_16/bswap_32 functions. */ +#if defined HAVE_BSWAP_16 && defined HAVE_BSWAP_32 +# include +# ifdef HAVE_BYTESWAP_H +# include +# endif +#else +# ifdef ARMV6_ASSEMBLY +STATIC_INLINE uae_u32 do_byteswap_32(uae_u32 v) { + __asm__( "rev %0, %0" - : "=r" (v) : "0" (v) ); return v;} + : "=r" (v) : "0" (v)); return v; +} -STATIC_INLINE uae_u32 do_byteswap_16(uae_u32 v) {__asm__ ( +STATIC_INLINE uae_u32 do_byteswap_16(uae_u32 v) { + __asm__( "revsh %0, %0\n\t" "uxth %0, %0" - : "=r" (v) : "0" (v) ); return v;} - -#endif - + : "=r" (v) : "0" (v)); return v; +} +#define bswap_16(x) do_byteswap_16(x) +#define bswap_32(x) do_byteswap_32(x) +# else +/* Else, if using SDL, try SDL's endian functions. */ +# ifdef USE_SDL +# include +# define bswap_16(x) SDL_Swap16(x) +# define bswap_32(x) SDL_Swap32(x) +# else +/* Otherwise, we'll roll our own. */ #define bswap_16(x) (((x) >> 8) | (((x) & 0xFF) << 8)) +#define bswap_32(x) (((x) << 24) | (((x) << 8) & 0x00FF0000) | (((x) >> 8) & 0x0000FF00) | ((x) >> 24)) +# endif +#endif +#endif #endif diff --git a/src/osdep/picasso96.cpp b/src/osdep/picasso96.cpp index e81a0651..c12cccf5 100644 --- a/src/osdep/picasso96.cpp +++ b/src/osdep/picasso96.cpp @@ -94,9 +94,9 @@ static struct PicassoResolution *newmodes = NULL; static int picasso_convert, host_mode; -/* These are the maximum resolutions... They are filled in by GetSupportedResolutions() */ -/* have to fill this in, otherwise problems occur on the Amiga side P96 s/w which expects -/* data here. */ +/* These are the maximum resolutions... They are filled in by GetSupportedResolutions() + have to fill this in, otherwise problems occur on the Amiga side P96 s/w which expects + data here. */ static struct ScreenResolution planar = { 320, 240 }; static struct ScreenResolution chunky = { 640, 480 }; static struct ScreenResolution hicolour = { 640, 480 }; @@ -1973,64 +1973,67 @@ FillRect: * d5: UBYTE Mask * d7: uae_u32 RGBFormat ***********************************************************/ -__attribute__((optimize("O2"))) static uae_u32 REGPARAM2 picasso_FillRect (TrapContext *ctx) +//__attribute__((optimize("O2"))) is needed otherwise this crashes with O3! +__attribute__((optimize("O2"))) static uae_u32 REGPARAM2 picasso_FillRect(TrapContext *ctx) { - uaecptr renderinfo = m68k_areg (regs, 1); - uae_u32 X = (uae_u16)m68k_dreg (regs, 0); - uae_u32 Y = (uae_u16)m68k_dreg (regs, 1); - uae_u32 Width = (uae_u16)m68k_dreg (regs, 2); - uae_u32 Height = (uae_u16)m68k_dreg (regs, 3); - uae_u32 Pen = m68k_dreg (regs, 4); - uae_u8 Mask = (uae_u8)m68k_dreg (regs, 5); - RGBFTYPE RGBFormat = (RGBFTYPE) m68k_dreg (regs, 7); - uae_u8 *oldstart; - int Bpp; - struct RenderInfo ri; - uae_u32 result = 0; + uaecptr renderinfo = m68k_areg(regs, 1); + uae_u32 X = (uae_u16)m68k_dreg(regs, 0); + uae_u32 Y = (uae_u16)m68k_dreg(regs, 1); + uae_u32 Width = (uae_u16)m68k_dreg(regs, 2); + uae_u32 Height = (uae_u16)m68k_dreg(regs, 3); + uae_u32 Pen = m68k_dreg(regs, 4); + uae_u8 Mask = (uae_u8)m68k_dreg(regs, 5); + RGBFTYPE RGBFormat = (RGBFTYPE) m68k_dreg(regs, 7); + uae_u8 *oldstart; + int Bpp; + struct RenderInfo ri; + uae_u32 result = 0; - if (NOBLITTER) - return 0; - if (CopyRenderInfoStructureA2U (renderinfo, &ri) && Y != 0xFFFF) { - Bpp = GetBytesPerPixel (RGBFormat); + if (NOBLITTER) + return 0; + if (CopyRenderInfoStructureA2U(renderinfo, &ri) && Y != 0xFFFF) { + Bpp = GetBytesPerPixel(RGBFormat); P96TRACE((_T("FillRect(%d, %d, %d, %d) Pen 0x%x BPP %d BPR %d Mask 0x%x\n"), X, Y, Width, Height, Pen, Bpp, ri.BytesPerRow, Mask)); - if (Bpp > 1) - Mask = 0xFF; + if (Bpp > 1) + Mask = 0xFF; - if (Mask == 0xFF) { + if (Mask == 0xFF) { - /* Do the fill-rect in the frame-buffer */ - do_fillrect_frame_buffer (&ri, X, Y, Width, Height, Pen, Bpp); - result = 1; + /* Do the fill-rect in the frame-buffer */ + do_fillrect_frame_buffer(&ri, X, Y, Width, Height, Pen, Bpp); + result = 1; - } else { + } + else { - /* We get here only if Mask != 0xFF */ - if (Bpp != 1) { - write_log (_T("WARNING - FillRect() has unhandled mask 0x%x with Bpp %d. Using fall-back routine.\n"), Mask, Bpp); - } else { - Pen &= Mask; - Mask = ~Mask; - oldstart = ri.Memory + Y * ri.BytesPerRow + X * Bpp; - { - uae_u8 *start = oldstart; - uae_u8 *end = start + Height * ri.BytesPerRow; - for (; start != end; start += ri.BytesPerRow) { - uae_u8 *p = start; - unsigned long cols; - for (cols = 0; cols < Width; cols++) { - uae_u32 tmpval = do_get_mem_byte (p + cols) & Mask; - do_put_mem_byte (p + cols, (uae_u8)(Pen | tmpval)); - } - } - } - result = 1; - } - } - } - return result; + /* We get here only if Mask != 0xFF */ + if (Bpp != 1) { + write_log(_T("WARNING - FillRect() has unhandled mask 0x%x with Bpp %d. Using fall-back routine.\n"), Mask, Bpp); + } + else { + Pen &= Mask; + Mask = ~Mask; + oldstart = ri.Memory + Y * ri.BytesPerRow + X * Bpp; + { + uae_u8 *start = oldstart; + uae_u8 *end = start + Height * ri.BytesPerRow; + for (; start != end; start += ri.BytesPerRow) { + uae_u8 *p = start; + unsigned long cols; + for (cols = 0; cols < Width; cols++) { + uae_u32 tmpval = do_get_mem_byte(p + cols) & Mask; + do_put_mem_byte(p + cols, (uae_u8)(Pen | tmpval)); + } + } + } + result = 1; + } + } + } + return result; } /*