mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
16764 lines
426 KiB
C++
16764 lines
426 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* Custom chip emulation
|
|
*
|
|
* Copyright 1995-2002 Bernd Schmidt
|
|
* Copyright 1995 Alessandro Bissacco
|
|
* Copyright 2000-2023 Toni Wilen
|
|
*/
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "options.h"
|
|
#include "uae.h"
|
|
#include "gensound.h"
|
|
#include "audio.h"
|
|
#include "sounddep/sound.h"
|
|
#include "events.h"
|
|
#include "memory.h"
|
|
#include "custom.h"
|
|
#include "newcpu.h"
|
|
#include "cia.h"
|
|
#include "disk.h"
|
|
#include "blitter.h"
|
|
#include "xwin.h"
|
|
#include "inputdevice.h"
|
|
#ifdef SERIAL_PORT
|
|
#include "serial.h"
|
|
#endif
|
|
#include "autoconf.h"
|
|
#include "traps.h"
|
|
#include "gui.h"
|
|
#include "picasso96.h"
|
|
#include "drawing.h"
|
|
#include "savestate.h"
|
|
#include "ar.h"
|
|
#include "debug.h"
|
|
#include "akiko.h"
|
|
#if defined(ENFORCER)
|
|
#include "enforcer.h"
|
|
#endif
|
|
#include "threaddep/thread.h"
|
|
#include "luascript.h"
|
|
#include "devices.h"
|
|
#include "rommgr.h"
|
|
#include "specialmonitors.h"
|
|
|
|
#define BPL_ERASE_TEST 0
|
|
|
|
#define FRAMEWAIT_MIN_MS 2
|
|
#define FRAMEWAIT_SPLIT 4
|
|
|
|
#define CYCLE_CONFLICT_LOGGING 0
|
|
|
|
#define SPEEDUP 1
|
|
|
|
#define CUSTOM_DEBUG 0
|
|
#define SPRITE_DEBUG 0
|
|
#define SPRITE_DEBUG_MINY 0
|
|
#define SPRITE_DEBUG_MAXY 0x30
|
|
#define MAX_SPRITES 8
|
|
#define AUTOSCALE_SPRITES 1
|
|
#define ALL_SUBPIXEL 1
|
|
|
|
#define RGA_COPPER_PIPELINE_DEPTH 2
|
|
#define RGA_SPRITE_PIPELINE_DEPTH 2
|
|
#define REFRESH_FIRST_HPOS 3
|
|
#define DMAL_FIRST_HPOS 11
|
|
#define SPR_FIRST_HPOS 25
|
|
#define COPPER_CYCLE_POLARITY 0
|
|
#define HARDWIRED_DMA_TRIGGER_HPOS 1
|
|
|
|
#define REF_RAS_ADD_AGA 0x000
|
|
#define REF_RAS_ADD_ECS 0x200
|
|
#define REF_RAS_ADD_OCS 0x002
|
|
|
|
#define SPRBORDER 0
|
|
|
|
#define EXTRAWIDTH_BROADCAST 15
|
|
#define EXTRAHEIGHT_BROADCAST_TOP 0
|
|
#define EXTRAHEIGHT_BROADCAST_BOTTOM 0
|
|
#define EXTRAWIDTH_EXTREME 33
|
|
#define EXTRAHEIGHT_EXTREME 24
|
|
#define EXTRAWIDTH_ULTRA 77
|
|
|
|
#define LORES_TO_SHRES_SHIFT 2
|
|
|
|
#ifdef SERIAL_PORT
|
|
extern uae_u16 serper;
|
|
#endif
|
|
|
|
static void uae_abort (const TCHAR *format,...)
|
|
{
|
|
static int nomore;
|
|
va_list parms;
|
|
TCHAR buffer[1000];
|
|
|
|
va_start (parms, format);
|
|
_vsntprintf (buffer, sizeof (buffer) / sizeof(TCHAR) - 1, format, parms );
|
|
va_end (parms);
|
|
if (nomore) {
|
|
write_log (_T("%s\n"), buffer);
|
|
return;
|
|
}
|
|
gui_message (buffer);
|
|
nomore = 1;
|
|
}
|
|
|
|
static uae_u32 total_skipped = 0;
|
|
|
|
extern int cpu_last_stop_vpos, cpu_stopped_lines;
|
|
static int cpu_sleepmode, cpu_sleepmode_cnt;
|
|
|
|
extern int vsync_activeheight, vsync_totalheight;
|
|
extern float vsync_vblank, vsync_hblank;
|
|
|
|
/* Events */
|
|
|
|
evt_t vsync_cycles;
|
|
static int extra_cycle;
|
|
|
|
static int rpt_did_reset;
|
|
struct ev eventtab[ev_max];
|
|
struct ev2 eventtab2[ev2_max];
|
|
|
|
int vpos, vpos_prev, vposh;
|
|
static int hpos_hsync_extra;
|
|
static int vpos_count, vpos_count_diff;
|
|
int lof_store; // real bit in custom registers
|
|
int lof_display; // what display device thinks
|
|
int scandoubled_line;
|
|
static bool lof_lastline, lof_prev_lastline;
|
|
static int lol, lol_prev;
|
|
static int next_lineno;
|
|
static int linear_vpos;
|
|
static enum nln_how nextline_how;
|
|
static int lof_changed = 0, lof_changing = 0, interlace_changed = 0;
|
|
static int lof_changed_previous_field;
|
|
static int vposw_change;
|
|
static bool lof_lace;
|
|
static bool prevlofs[3];
|
|
static bool bplcon0_interlace_seen;
|
|
static bool vsync_rendered, frame_rendered, frame_shown;
|
|
static frame_time_t vsynctimeperline;
|
|
static frame_time_t frameskiptime;
|
|
static bool genlockhtoggle;
|
|
static bool genlockvtoggle;
|
|
static bool graphicsbuffer_retry;
|
|
static int cia_hsync;
|
|
static bool toscr_scanline_complex_bplcon1, toscr_scanline_complex_bplcon1_off;
|
|
static int toscr_hend;
|
|
static int nosignal_cnt, nosignal_status;
|
|
static bool nosignal_trigger;
|
|
int display_reset;
|
|
static evt_t line_start_cycles;
|
|
static bool initial_frame;
|
|
static evt_t custom_color_write_cycle;
|
|
static int color_writes_num;
|
|
static bool line_equ_freerun;
|
|
|
|
#define LOF_TOGGLES_NEEDED 3
|
|
//#define NLACE_CNT_NEEDED 50
|
|
static int lof_togglecnt_lace, lof_togglecnt_nlace; //, nlace_cnt;
|
|
|
|
/* Stupid genlock-detection prevention hack.
|
|
* We should stop calling vsync_handler() and
|
|
* hstop_handler() completely but it is not
|
|
* worth the trouble..
|
|
*/
|
|
static int vpos_previous, hpos_previous;
|
|
static int vpos_lpen, hpos_lpen, hhpos_lpen, lightpen_triggered;
|
|
int lightpen_x[2], lightpen_y[2];
|
|
int lightpen_cx[2], lightpen_cy[2], lightpen_active, lightpen_enabled, lightpen_enabled2;
|
|
|
|
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];
|
|
|
|
/* T genlock bit in ECS Denise and AGA color registers */
|
|
static uae_u8 color_regs_genlock[256];
|
|
|
|
/*
|
|
* Hardware registers of all sorts.
|
|
*/
|
|
|
|
static uae_u16 cregs[256];
|
|
|
|
uae_u16 intena, intreq;
|
|
static uae_u16 intena2, intreq2;
|
|
uae_u16 dmacon;
|
|
uae_u16 adkcon; /* used by audio code */
|
|
uae_u16 last_custom_value;
|
|
static bool dmacon_bpl, vdiwstate_bpl;
|
|
|
|
static uae_u32 cop1lc, cop2lc, copcon;
|
|
|
|
/*
|
|
* Horizontal defaults
|
|
*
|
|
* 0x00 0 HCB
|
|
* 0x01 1 HC1 (HSTART)
|
|
* 0x09 9 VR1 (HBLANK start)
|
|
* 0x12 18 SHS (Horizontal sync start)
|
|
* 0x1a 26 VER1 PAL
|
|
* 0x1b 27 VER1 NTSC
|
|
* 0x23 35 RHS (Horizontal sync end)
|
|
* 0x73 115 VR2
|
|
* 0x84 132 CEN (HCENTER)
|
|
* 0x8c 140 VER2 PAL (CEN->VER2 = CSYNC qualising pulse 2)
|
|
* 0x8d 141 VER2 NTSC
|
|
* 0xe2 226 HC226 (short line, selected if LOL=1, NTSC only)
|
|
* 0xe3 227 HC227 (NTSC long line/PAL)
|
|
*
|
|
* SHS->VER1 = CSYNC equalising pulse 1
|
|
* CEN->VER2 = CSYNC equalising pulse 2
|
|
*
|
|
* HC1->SHS = Inactivate part of CSYNC Vsync+Hsync pulse 1
|
|
* VR2->CEN = Inactivate part of CSYNC Vsync+HSync pulse 2
|
|
*
|
|
*
|
|
* Vertical defaults
|
|
*
|
|
* PAL
|
|
*
|
|
* 0 SVB
|
|
* 2 VC2
|
|
* 3 VC3
|
|
* 5 VC5
|
|
* 7 VC7
|
|
* 8 VC8
|
|
* 25 RVB (Vertical blank end)
|
|
* 311 VC311 short field (Vertical blank start)
|
|
* 312 VC312 long field (Vertical blank start)
|
|
*
|
|
* Odd field:
|
|
*
|
|
* HSYNC SHS to RHS
|
|
* VSYNC VC2/CEN to VC5/SHS
|
|
* CSYNC HSYNC + if VSYNC active: SHS to VER1 and CEN to VER2
|
|
*
|
|
* Vertical blank = VC312 + HC1 to RVB + HC1
|
|
* Vertical equalization = SVB + VR1 to VC7 + VR2
|
|
*
|
|
* Even field:
|
|
*
|
|
* HSYNC SHS to RHS
|
|
* VSYNC VC3/SHS to VC5/CEN
|
|
* CSYNC HSYNC + if VSYNC active: SHS to VER1 and CEN to VER2
|
|
*
|
|
* Vertical blank = VC311 + HC1 to RVB + HC1
|
|
* Vertical equalization start = SVB + VR2 to VC8 + VR1
|
|
*
|
|
*
|
|
* NTSC
|
|
*
|
|
* 0 SVB
|
|
* 3 VC3
|
|
* 6 VC6
|
|
* 9 VC9
|
|
* 20 RVB (Vertical blank end)
|
|
* 261 VC261 short field (Vertical blank start)
|
|
* 262 VC262 long field (Vertical blank start)
|
|
*
|
|
* Odd field:
|
|
*
|
|
* HSYNC SHS to RHS
|
|
* VSYNC VC3/SHS to VC6/SHS
|
|
* CSYNC HSYNC + if VSYNC active: SHS to VER1 and CEN to VER2
|
|
*
|
|
* Vertical blank = VC262/HC1 to RVB/HC1
|
|
* Vertical equalization = SVB/VR1 to VC9/VR1
|
|
*
|
|
* Even field:
|
|
*
|
|
* HSYNC SHS to RHS
|
|
* VSYNC VC3/CEN to VC6/CEN
|
|
* CSYNC HSYNC + if VSYNC active: SHS to VER1 and CEN to VER2
|
|
*
|
|
* Vertical blank = VC261/HC1 to RVB/HC1
|
|
* Vertical equalization = SVB/VR2 to VC9/VR2
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Bitplane DMA enable logic OCS/ECS differences:
|
|
*
|
|
* OCS: DDFSTRT hard start limit flag is disabled when horizontal hard start position matches.
|
|
* It is enabled during active bitplane DMA's last cycle. (Ending due to either DDFSTOP or hardstop match).
|
|
* ECS: DDFTSTR/STOP hard limit work as documented.
|
|
* It is cleared when hard start matches and set when hard stop matches.
|
|
*
|
|
* OCS hard start limit bug: if line didn't have BPL DMA active, next line's BPL DMA can start earlier than start limit.
|
|
* (See music disk Ode to Ramon by Digital Force, bottom scroller "scanline affect" )
|
|
* This feature also prevents multiple DDFSTRT/STOP regions in same scanline. ECS/AGA does not have this limit.
|
|
*
|
|
* DDFSTRT match is ignored if DMACON BPLEN is not enabled. ECS/AGA allows it. Does not affect DDFSTOP.
|
|
* Switch DMACON BPLEN off, wait value to DDFSTRT that matches during current scanline,
|
|
* switch BPLEN on: if OCS, bitplane DMA won't start, ECS/AGA: bitplane DMA starts.
|
|
*
|
|
*/
|
|
|
|
|
|
int maxhpos = MAXHPOS_PAL;
|
|
int maxhpos_short = MAXHPOS_PAL;
|
|
int maxvpos = MAXVPOS_PAL;
|
|
int maxvpos_nom = MAXVPOS_PAL; // nominal value (same as maxvpos but "faked" maxvpos in fake 60hz modes)
|
|
int maxvpos_display = MAXVPOS_PAL; // value used for display size
|
|
int maxhpos_display = AMIGA_WIDTH_MAX;
|
|
int maxvsize_display = AMIGA_HEIGHT_MAX;
|
|
int maxvpos_display_vsync; // extra lines from top visible in bottom
|
|
static bool maxvpos_display_vsync_next;
|
|
static int vblank_extraline;
|
|
static int maxhposm1;
|
|
int maxhposm0 = MAXHPOS_PAL;
|
|
static bool maxhposeven, maxhposeven_prev;
|
|
static int hsyncendpos, hsyncstartpos;
|
|
int hsync_end_left_border;
|
|
static int hsyncstartpos_start, hsyncstartpos_start_cycles;
|
|
|
|
static int hsyncstartpos_start_hw;
|
|
int hsyncstartpos_hw;
|
|
int hsyncendpos_hw;
|
|
|
|
int denisehtotal;
|
|
static int maxvpos_total = 511;
|
|
int minfirstline = VBLANK_ENDLINE_PAL;
|
|
int minfirstline_linear = VBLANK_ENDLINE_PAL;
|
|
static int firstblankedline;
|
|
static int equ_vblank_endline = EQU_ENDLINE_PAL;
|
|
static bool equ_vblank_toggle = true;
|
|
float vblank_hz = VBLANK_HZ_PAL, fake_vblank_hz, vblank_hz_stored, vblank_hz_nom;
|
|
float hblank_hz;
|
|
static float vblank_hz_lof, vblank_hz_shf, vblank_hz_lace;
|
|
static int vblank_hz_mult, vblank_hz_state;
|
|
static struct chipset_refresh *stored_chipset_refresh;
|
|
int doublescan;
|
|
int programmedmode;
|
|
frame_time_t syncbase;
|
|
static int fmode_saved, fmode;
|
|
uae_u16 beamcon0, new_beamcon0;
|
|
uae_u16 bemcon0_hsync_mask, bemcon0_vsync_mask;
|
|
static uae_u16 beamcon0_saved;
|
|
static uae_u16 bplcon0_saved, bplcon1_saved, bplcon2_saved;
|
|
static uae_u16 bplcon3_saved, bplcon4_saved;
|
|
static int varsync_changed, varsync_maybe_changed[2];
|
|
static int varhblank_lines, varhblank_val[2];
|
|
static uae_u16 vt_old, ht_old, hs_old, vs_old;
|
|
uae_u16 vtotal, htotal;
|
|
static int maxvpos_stored, maxhpos_stored;
|
|
uae_u16 hsstop, hsstrt;
|
|
uae_u16 hbstop, hbstrt;
|
|
static int hsstop_detect, hsstop_detect2;
|
|
static uae_u16 vsstop, vsstrt;
|
|
static uae_u16 vbstop, vbstrt;
|
|
static uae_u16 hcenter, hcenter_v2, hcenter_v2_end, hcenter_sync_v2;
|
|
static bool hcenter_active;
|
|
static uae_u16 hbstop_v, hbstrt_v, hbstop_v2, hbstrt_v2, hbstop_sync_v2, hbstrt_sync_v2, hbstop_reg, hbstrt_reg;
|
|
static uae_u16 hsstrt_v2, hsstop_v2;
|
|
static int vsstrt_m, vsstop_m, vbstrt_m, vbstop_m;
|
|
static bool vb_state, vb_end_line;
|
|
static bool vs_state_var, vs_state_on, vs_state_on_old, vs_state_hw, vs_state_var_old;
|
|
static bool vb_end_next_line, vb_end_next_line2;
|
|
static int vb_start_line;
|
|
static bool ocs_blanked;
|
|
static uae_u16 sprhstrt, sprhstop, bplhstrt, bplhstop, hhpos, hhpos_hpos;
|
|
static uae_u16 sprhstrt_v, sprhstop_v, bplhstrt_v, bplhstop_v;
|
|
static int hhbpl, hhspr;
|
|
static int ciavsyncmode;
|
|
static int diw_hstrt, diw_hstop;
|
|
static int hdiw_counter;
|
|
static int hstrobe_hdiw_min;
|
|
static uae_u32 ref_ras_add;
|
|
static uaecptr refptr, refptr_p;
|
|
static uae_u32 refmask;
|
|
static int refresh_handled_slot;
|
|
static bool refptr_preupdated;
|
|
static bool hstrobe_conflict;
|
|
static bool vhposw_modified;
|
|
static int line_disabled;
|
|
static bool custom_disabled;
|
|
|
|
#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;
|
|
int width;
|
|
bool ecs_denise_hires;
|
|
bool firstslotdone;
|
|
|
|
uae_u16 ctl, pos;
|
|
#ifdef AGA
|
|
uae_u16 data[4], datb[4];
|
|
#else
|
|
uae_u16 data[1], datb[1];
|
|
#endif
|
|
};
|
|
|
|
static struct sprite spr[MAX_SPRITES];
|
|
static int plfstrt_sprite;
|
|
static int sprbplconflict, sprbplconflict_hpos;
|
|
static int sprbplconflict2, sprbplconflict_hpos2;
|
|
static uae_u16 sprbplconflict_dat;
|
|
uaecptr sprite_0;
|
|
int sprite_0_width, sprite_0_height, sprite_0_doubled;
|
|
uae_u32 sprite_0_colors[4];
|
|
static uae_u8 magic_sprite_mask = 0xff;
|
|
|
|
static int hardwired_vbstrt, hardwired_vbstop;
|
|
|
|
static int last_sprite_point, last_sprite_point_abs;
|
|
static int nr_armed;
|
|
static int sprite_width, sprres;
|
|
static int sprite_sprctlmask;
|
|
int sprite_buffer_res;
|
|
|
|
uae_u8 cycle_line_slot[MAX_CHIPSETSLOTS + RGA_PIPELINE_ADJUST + MAX_CHIPSETSLOTS_EXTRA];
|
|
uae_u16 cycle_line_pipe[MAX_CHIPSETSLOTS + RGA_PIPELINE_ADJUST + MAX_CHIPSETSLOTS_EXTRA];
|
|
static uae_u8 cycle_line_slot_last;
|
|
|
|
static uae_s16 bpl1mod, bpl2mod, bpl1mod_prev, bpl2mod_prev;
|
|
static int bpl1mod_hpos, bpl2mod_hpos;
|
|
static uaecptr prevbpl[2][MAXVPOS][MAX_PLANES];
|
|
static uaecptr bplpt[MAX_PLANES], bplptx[MAX_PLANES];
|
|
|
|
static struct color_entry current_colors;
|
|
uae_u16 bplcon0;
|
|
static uae_u16 bplcon1, bplcon2, bplcon3, bplcon4;
|
|
static int bplcon0d, bplcon0d_old;
|
|
static bool bplcon0d_change;
|
|
static uae_u32 bplcon0_res, bplcon0_planes, bplcon0_planes_limit;
|
|
static int diwstrt, diwstop, diwhigh;
|
|
static int diwhigh_written;
|
|
static int ddfstrt, ddfstop, plfstop_prev;
|
|
static int ddfstrt_hpos, ddfstop_hpos;
|
|
static int line_cyclebased, diw_change;
|
|
static int bpl_shifter;
|
|
|
|
static void SET_LINE_CYCLEBASED(int hpos);
|
|
|
|
/* The display and data fetch windows */
|
|
|
|
enum class diw_states
|
|
{
|
|
DIW_waiting_start, DIW_waiting_stop
|
|
};
|
|
|
|
static int plffirstline, plflastline;
|
|
int plffirstline_total, plflastline_total;
|
|
int vblank_firstline_hw;
|
|
static int autoscale_bordercolors;
|
|
static int plfstrt, plfstop;
|
|
static int sprite_minx;
|
|
static int first_bpl_vpos;
|
|
static int last_decide_line_hpos;
|
|
static int last_fetch_hpos, last_decide_sprite_hpos;
|
|
static int diwfirstword, diwlastword;
|
|
static int last_diwlastword;
|
|
static int hb_last_diwlastword;
|
|
static int last_hdiw;
|
|
static diw_states vdiwstate, hdiwstate, hdiwstate_blank;
|
|
static int bpl_hstart;
|
|
static bool exthblank, exthblank_state, hcenterblank_state;
|
|
static int hsyncdebug;
|
|
static int last_diw_hpos;
|
|
static int last_recorded_diw_hpos;
|
|
static int last_hblank_start;
|
|
static int collision_hpos;
|
|
|
|
int first_planes_vpos, last_planes_vpos;
|
|
static int first_bplcon0, first_bplcon0_old;
|
|
static int first_planes_vpos_old, last_planes_vpos_old;
|
|
int diwfirstword_total, diwlastword_total;
|
|
int ddffirstword_total, ddflastword_total;
|
|
static int diwfirstword_total_old, diwlastword_total_old;
|
|
static int ddffirstword_total_old, ddflastword_total_old;
|
|
bool vertical_changed, horizontal_changed;
|
|
int firstword_bplcon1;
|
|
|
|
static int last_copper_hpos;
|
|
static int copper_access;
|
|
|
|
/* Sprite collisions */
|
|
static unsigned int clxdat, clxcon, clxcon2, clxcon_bpl_enable, clxcon_bpl_match;
|
|
|
|
enum copper_states {
|
|
COP_stop,
|
|
COP_waitforever,
|
|
COP_read1,
|
|
COP_read2,
|
|
COP_bltwait,
|
|
COP_bltwait2,
|
|
COP_wait_in2,
|
|
COP_skip_in2,
|
|
COP_wait1,
|
|
COP_wait,
|
|
COP_skip,
|
|
COP_skip1,
|
|
COP_strobe_delay1,
|
|
COP_strobe_delay2,
|
|
COP_strobe_delay3,
|
|
COP_strobe_delay4,
|
|
COP_strobe_delay5,
|
|
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. */
|
|
uae_u16 ir[2];
|
|
enum copper_states state, state_prev;
|
|
/* Instruction pointer. */
|
|
uaecptr ip;
|
|
// following move does not enable COPRS
|
|
int ignore_next;
|
|
int vcmp, hcmp;
|
|
|
|
int strobe; /* COPJMP1 / COPJMP2 accessed */
|
|
int last_strobe;
|
|
int moveaddr, movedata, movedelay;
|
|
uaecptr moveptr;
|
|
uaecptr vblankip;
|
|
};
|
|
|
|
static struct copper cop_state;
|
|
static int copper_enabled_thisline;
|
|
static evt_t copper_bad_cycle;
|
|
static uaecptr copper_bad_cycle_pc_old;
|
|
static evt_t copper_bad_cycle_start;
|
|
static uaecptr copper_bad_cycle_pc_new;
|
|
|
|
static evt_t copper_dma_change_cycle;
|
|
static evt_t blitter_dma_change_cycle;
|
|
static evt_t sprite_dma_change_cycle_on, sprite_dma_change_cycle_off;
|
|
|
|
/*
|
|
* Statistics
|
|
*/
|
|
uae_u32 timeframes;
|
|
evt_t frametime;
|
|
frame_time_t lastframetime;
|
|
uae_u32 hsync_counter, vsync_counter;
|
|
frame_time_t idletime;
|
|
int bogusframe;
|
|
static int display_vsync_counter;
|
|
|
|
/* Recording of custom chip register changes. */
|
|
static int current_change_set;
|
|
#define MAX_SPRITE_ENTRIES ((MAXVPOS + MAXVPOS_WRAPLINES) * 16)
|
|
static struct sprite_entry sprite_entries[2][MAX_SPRITE_ENTRIES];
|
|
static struct color_change color_changes[2][MAX_REG_CHANGE];
|
|
|
|
struct decision line_decisions[2 * (MAXVPOS + MAXVPOS_WRAPLINES) + 1];
|
|
static struct draw_info line_drawinfo[2][2 * (MAXVPOS + MAXVPOS_WRAPLINES) + 1];
|
|
#define COLOR_TABLE_SIZE (MAXVPOS + MAXVPOS_WRAPLINES) * 2
|
|
static struct color_entry color_tables[2][COLOR_TABLE_SIZE];
|
|
|
|
static int next_sprite_entry = 0, end_sprite_entry;
|
|
static int prev_next_sprite_entry;
|
|
static int next_sprite_forced = 1;
|
|
static int spixels_max;
|
|
|
|
struct sprite_entry *curr_sprite_entries, *prev_sprite_entries;
|
|
struct color_change *curr_color_changes, *prev_color_changes;
|
|
struct draw_info *curr_drawinfo, *prev_drawinfo;
|
|
struct color_entry *curr_color_tables, *prev_color_tables;
|
|
|
|
static int next_color_change, last_color_change;
|
|
static int next_color_entry, remembered_color_entry;
|
|
static int color_src_match, color_dest_match, color_compare_result;
|
|
|
|
static uae_u32 thisline_changed;
|
|
|
|
#ifdef SMART_UPDATE
|
|
#define MARK_LINE_CHANGED do { thisline_changed = 1; } while (0)
|
|
#else
|
|
#define MARK_LINE_CHANGED do { ; } while (0)
|
|
#endif
|
|
|
|
static struct decision thisline_decision;
|
|
static int fetch_cycle, fetch_modulo_cycle;
|
|
|
|
static bool ddf_limit, ddf_limit_latch, ddfstrt_match, hwi_old;
|
|
static int ddf_stopping, ddf_enable_on;
|
|
static int bprun, bprun_end;
|
|
static int bprun_cycle;
|
|
static int bprun_pipeline_flush_delay;
|
|
static bool plane0, plane0p, plane0p_enabled, plane0p_forced, plane0_first_done;
|
|
static int last_bpl1dat_hpos;
|
|
static bool last_bpl1dat_hpos_early;
|
|
static bool harddis_v, harddis_h;
|
|
static uae_u16 dmal_alloc_mask;
|
|
|
|
#define RGA_PIPELINE_OFFSET_BPL_WRITE 3
|
|
#define RGA_PIPELINE_OFFSET_COPPER 2
|
|
#define RGA_PIPELINE_OFFSET_SPRITE 2
|
|
#define RGA_PIPELINE_OFFSET_DMAL 2
|
|
|
|
struct custom_store custom_storage[256];
|
|
|
|
#define TOSCR_RES_PIXELS_LORES 4
|
|
#define TOSCR_RES_PIXELS_HIRES 2
|
|
#define TOSCR_RES_PIXELS_SHRES 1
|
|
|
|
#define TOSCR_NBITS 16
|
|
#define TOSCR_NBITSHR 32
|
|
|
|
static int out_nbits, out_offs;
|
|
static uae_u32 todisplay[MAX_PLANES], todisplay2[MAX_PLANES];
|
|
static uae_u32 outword[MAX_PLANES];
|
|
static uae_u64 outword64[MAX_PLANES], outword64_extra[MAX_PLANES];
|
|
static uae_u16 fetched[MAX_PLANES];
|
|
static uae_u16 todisplay_fetched;
|
|
#ifdef AGA
|
|
static uae_u64 todisplay_aga[MAX_PLANES], todisplay2_aga[MAX_PLANES];
|
|
static bool todisplay2_lastbit[MAX_PLANES];
|
|
static uae_u64 fetched_aga[MAX_PLANES];
|
|
static uae_u32 fetched_aga_spr[MAX_PLANES];
|
|
#endif
|
|
|
|
/* Expansions from bplcon0/bplcon1. */
|
|
static int toscr_res2p, toscr_res_mult;
|
|
static int toscr_res, toscr_res_old;
|
|
static int toscr_res_pixels, toscr_res_pixels_shift, toscr_res_pixels_shift_old;
|
|
static int toscr_res_pixels_hr, toscr_res_pixels_shift_hr, toscr_res_pixels_mask_hr, toscr_res_pixels_mask_hr_old, toscr_res_pixels_mask_hr2;
|
|
static int toscr_nr_planes, toscr_nr_planes2, toscr_nr_planes3, toscr_nr_planes_agnus;
|
|
static bool toscr_nr_changed;
|
|
static int toscr_nr_planes_shifter, toscr_nr_planes_shifter_new;
|
|
static int fetchwidth;
|
|
static int toscr_delay_adjusted[2], toscr_delay_sh[2];
|
|
static bool shdelay_disabled;
|
|
static int delay_cycles, delay_cycles2;
|
|
static int delay_lastcycle[2], delay_hsynccycle;
|
|
static int vhposr_delay_offset;
|
|
static bool bplcon1_written;
|
|
static bool bplcon0_planes_changed;
|
|
static bool sprites_enabled_this_line;
|
|
static int shifter_mask, toscr_delay_shifter[2];
|
|
static int out_subpix[2];
|
|
static bool speedup_first;
|
|
|
|
static void events_dmal(int);
|
|
static uae_u16 dmal, dmal_hpos;
|
|
static uae_u16 dmal_htotal_mask;
|
|
static bool dmal_ce;
|
|
|
|
static void update_copper(int until_hpos);
|
|
static void decide_sprites_fetch(int endhpos);
|
|
static void decide_line(int endhpos);
|
|
static void decide_sprites(int hpos, bool quick, bool halfcycle);
|
|
static void decide_sprites(int hpos);
|
|
|
|
/* 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;
|
|
|
|
static int REGPARAM3 custom_wput_1(int, uaecptr, uae_u32, int) REGPARAM;
|
|
|
|
/*
|
|
* helper functions
|
|
*/
|
|
|
|
static bool safecpu(void)
|
|
{
|
|
return currprefs.cpu_model == 68000 && currprefs.cpu_cycle_exact && currprefs.blitter_cycle_exact && currprefs.m68k_speed == 0 && !(currprefs.cs_hacks & 16);
|
|
}
|
|
|
|
static void check_nocustom(void)
|
|
{
|
|
struct amigadisplay* ad = &adisplays[0];
|
|
if (ad->picasso_on && currprefs.picasso96_nocustom) {
|
|
custom_disabled = true;
|
|
line_disabled |= 2;
|
|
} else {
|
|
custom_disabled = false;
|
|
line_disabled &= ~2;
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE bool ecsshres(void)
|
|
{
|
|
return bplcon0_res == RES_SUPERHIRES && ecs_denise && !aga_mode;
|
|
}
|
|
|
|
STATIC_INLINE bool nodraw(void)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
bool nd = !currprefs.cpu_memory_cycle_exact && ad->framecnt != 0;
|
|
return nd;
|
|
}
|
|
|
|
STATIC_INLINE int diw_to_hpos(int diw)
|
|
{
|
|
diw /= 4;
|
|
diw += DDF_OFFSET;
|
|
diw /= 2;
|
|
return diw;
|
|
}
|
|
|
|
STATIC_INLINE int hpos_to_diw(int pos)
|
|
{
|
|
return (pos * 2 - DDF_OFFSET) * 4;
|
|
}
|
|
|
|
STATIC_INLINE int hpos_to_diwx(int pos)
|
|
{
|
|
return ((pos + hpos_hsync_extra) * 2 - DDF_OFFSET) * 4;
|
|
}
|
|
|
|
static bool doflickerfix_possible(void)
|
|
{
|
|
return currprefs.gfx_vresolution && doublescan < 0 && vpos < MAXVPOS;
|
|
}
|
|
static bool doflickerfix_active(void)
|
|
{
|
|
return interlace_seen > 0 && doflickerfix_possible();
|
|
}
|
|
|
|
uae_u32 get_copper_address(int copno)
|
|
{
|
|
switch (copno) {
|
|
case 1: return cop1lc;
|
|
case 2: return cop2lc;
|
|
case 3: return cop_state.vblankip;
|
|
case -1: return cop_state.ip;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
void reset_frame_rate_hack(void)
|
|
{
|
|
if (currprefs.m68k_speed >= 0) {
|
|
return;
|
|
}
|
|
|
|
rpt_did_reset = 1;
|
|
events_reset_syncline();
|
|
vsyncmintime = read_processor_time() + vsynctimebase;
|
|
write_log(_T("Resetting frame rate hack\n"));
|
|
}
|
|
|
|
static void setclr(uae_u16 *p, uae_u16 val)
|
|
{
|
|
if (val & 0x8000) {
|
|
*p |= val & 0x7FFF;
|
|
} else {
|
|
*p &= ~val;
|
|
}
|
|
}
|
|
|
|
static int adjust_hr(int v)
|
|
{
|
|
if (currprefs.gfx_resolution >= RES_HIRES) {
|
|
if (currprefs.chipset_hr) {
|
|
v &= ~(3 >> currprefs.gfx_resolution);
|
|
} else {
|
|
v &= ~3;
|
|
}
|
|
} else {
|
|
v &= ~1;
|
|
}
|
|
return v;
|
|
}
|
|
static int adjust_hr2(int v)
|
|
{
|
|
if (currprefs.gfx_resolution >= RES_HIRES) {
|
|
if (currprefs.chipset_hr) {
|
|
v &= ~(1 >> currprefs.gfx_resolution);
|
|
} else {
|
|
v &= ~1;
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
|
|
STATIC_INLINE bool is_last_line(void)
|
|
{
|
|
return vpos + 1 == maxvpos + lof_store;
|
|
}
|
|
|
|
static void alloc_cycle(int hpos, int type)
|
|
{
|
|
#ifdef CPUEMU_13
|
|
#if CYCLE_CONFLICT_LOGGING
|
|
if (cycle_line_slot[hpos] && cycle_line_slot[hpos] != type) {
|
|
write_log(_T("alloc cycle conflict %d: %02x <- %02x\n"), hpos, type, cycle_line_slot[hpos]);
|
|
}
|
|
#endif
|
|
cycle_line_slot[hpos] = type;
|
|
#endif
|
|
}
|
|
static void alloc_cycle_maybe(int hpos, int type)
|
|
{
|
|
if ((cycle_line_slot[hpos] & CYCLE_MASK) == 0) {
|
|
alloc_cycle(hpos, type);
|
|
}
|
|
}
|
|
|
|
void alloc_cycle_ext(int hpos, int type)
|
|
{
|
|
alloc_cycle(hpos, type);
|
|
}
|
|
|
|
uaecptr alloc_cycle_blitter_conflict_or(int hpos, int chnum, bool *skip)
|
|
{
|
|
uaecptr orptr = 0;
|
|
if (copper_bad_cycle && line_start_cycles + hpos * CYCLE_UNIT == copper_bad_cycle) {
|
|
orptr = copper_bad_cycle_pc_old;
|
|
} else if (hpos == sprbplconflict_hpos2) {
|
|
uae_u16 bltdat = chnum == 4 ? 0x000 : (3 - chnum) * 2 + 0x70;
|
|
uae_u16 rga = bltdat & sprbplconflict2;
|
|
int spnum = (sprbplconflict2 - 0x140) / 8;
|
|
orptr = spr[spnum].pt;
|
|
if (rga != bltdat) {
|
|
*skip = true;
|
|
}
|
|
}
|
|
return orptr;
|
|
}
|
|
|
|
bool alloc_cycle_blitter(int hpos, uaecptr *ptr, int chnum, int add)
|
|
{
|
|
bool skipadd = false;
|
|
if (copper_bad_cycle && line_start_cycles + hpos * CYCLE_UNIT == copper_bad_cycle) {
|
|
write_log("Copper PT=%08x/%08x. Blitter CH=%d MOD=%d PT=%08x. Conflict bug!\n", copper_bad_cycle_pc_old, copper_bad_cycle_pc_new, chnum, add, *ptr);
|
|
cop_state.ip += add;
|
|
*ptr = copper_bad_cycle_pc_old;
|
|
skipadd = true;
|
|
copper_bad_cycle = 0;
|
|
} else if (hpos == sprbplconflict_hpos2) {
|
|
uae_u16 v = chipmem_wget_indirect(*ptr);
|
|
uae_u16 bltdat = chnum == 4 ? 0x000 : (3 - chnum) * 2 + 0x70;
|
|
uae_u16 rga = bltdat & sprbplconflict2;
|
|
int spnum = (sprbplconflict2 - 0x140) / 8;
|
|
uaecptr pt = spr[spnum].pt;
|
|
spr[spnum].pt = *ptr + 2 + add;
|
|
custom_wput_1(hpos, rga, v, 1);
|
|
sprbplconflict_hpos2 = -1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(v);
|
|
record_dma_read(rga, *ptr, hpos, vpos, DMARECORD_SPRITE, spnum);
|
|
}
|
|
#endif
|
|
write_log("Sprite %d. Blitter CH=%d MOD=%d PT=%08x. Conflict bug!\n", spnum, chnum, add, *ptr);
|
|
}
|
|
alloc_cycle(hpos, CYCLE_BLITTER);
|
|
return skipadd;
|
|
}
|
|
|
|
static int expand_sprres(uae_u16 con0, uae_u16 con3)
|
|
{
|
|
int res;
|
|
|
|
switch ((con3 >> 6) & 3)
|
|
{
|
|
default:
|
|
res = RES_LORES;
|
|
break;
|
|
#ifdef ECS_DENISE
|
|
case 0: /* ECS defaults (LORES,HIRES=LORES sprite,SHRES=HIRES sprite) */
|
|
if (ecs_denise && GET_RES_DENISE(con0) == RES_SUPERHIRES)
|
|
res = RES_HIRES;
|
|
else
|
|
res = RES_LORES;
|
|
break;
|
|
#endif
|
|
#ifdef AGA
|
|
case 1:
|
|
res = RES_LORES;
|
|
break;
|
|
case 2:
|
|
res = RES_HIRES;
|
|
break;
|
|
case 3:
|
|
res = RES_SUPERHIRES;
|
|
break;
|
|
#endif
|
|
}
|
|
return res;
|
|
}
|
|
|
|
STATIC_INLINE uae_u8 *pfield_xlateptr(uaecptr plpt, int bytecount)
|
|
{
|
|
if (!chipmem_check_indirect(plpt, bytecount)) {
|
|
static int count = 0;
|
|
if (!count) {
|
|
count++;
|
|
write_log(_T("Warning: Bad playfield pointer %08x\n"), plpt);
|
|
}
|
|
return NULL;
|
|
}
|
|
return chipmem_xlate_indirect(plpt);
|
|
}
|
|
|
|
static void docols(struct color_entry *colentry)
|
|
{
|
|
#ifdef AGA
|
|
if (aga_mode) {
|
|
for (int i = 0; i < 256; i++) {
|
|
int v = color_reg_get(colentry, i);
|
|
if (v < 0 || v > 16777215)
|
|
continue;
|
|
colentry->acolors[i] = getxcolor(v);
|
|
}
|
|
} else {
|
|
#endif
|
|
for (int i = 0; i < 32; i++) {
|
|
int v = color_reg_get(colentry, i);
|
|
if (v < 0 || v > 4095)
|
|
continue;
|
|
colentry->acolors[i] = getxcolor(v);
|
|
}
|
|
#ifdef AGA
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
if (color_src_match < 0 || color_dest_match != remembered_color_entry
|
|
|| line_decisions[next_lineno].ctable != color_src_match)
|
|
{
|
|
/* The remembered comparison didn't help us - need to compare again. */
|
|
int oldctable = line_decisions[next_lineno].ctable;
|
|
int changed = 0;
|
|
|
|
if (oldctable < 0) {
|
|
changed = 1;
|
|
color_src_match = color_dest_match = -1;
|
|
} else {
|
|
color_compare_result = color_reg_cmp(&prev_color_tables[oldctable], ¤t_colors) != 0;
|
|
if (color_compare_result) {
|
|
changed = 1;
|
|
}
|
|
color_src_match = oldctable;
|
|
color_dest_match = remembered_color_entry;
|
|
}
|
|
thisline_changed |= changed;
|
|
} else {
|
|
/* We know the result of the comparison */
|
|
if (color_compare_result) {
|
|
thisline_changed = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void remember_ctable_for_border(void)
|
|
{
|
|
remember_ctable();
|
|
}
|
|
|
|
static int get_equ_vblank_endline(void)
|
|
{
|
|
if (new_beamcon0 & (BEAMCON0_BLANKEN | BEAMCON0_VARCSYEN)) {
|
|
return -1;
|
|
}
|
|
return equ_vblank_endline + (equ_vblank_toggle ? (lof_store ? 1 : 0) : 0) + (agnusa1000 ? 1 : 0);
|
|
}
|
|
static int get_equ_vblank_startline(void)
|
|
{
|
|
if (new_beamcon0 & (BEAMCON0_BLANKEN | BEAMCON0_VARCSYEN)) {
|
|
return 30000;
|
|
}
|
|
if (agnusa1000) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int output_res(int res)
|
|
{
|
|
if (currprefs.chipset_hr && res < currprefs.gfx_resolution) {
|
|
return currprefs.gfx_resolution;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void reset_bpl_vars()
|
|
{
|
|
out_subpix[0] = 0;
|
|
out_subpix[1] = 0;
|
|
out_nbits = 0;
|
|
out_offs = 0;
|
|
toscr_nbits = 0;
|
|
thisline_decision.bplres = output_res(bplcon0_res);
|
|
}
|
|
|
|
STATIC_INLINE bool line_hidden(void)
|
|
{
|
|
return linear_vpos >= maxvpos_display_vsync && linear_vpos < minfirstline - 1 && currprefs.gfx_overscanmode < OVERSCANMODE_ULTRA;
|
|
}
|
|
|
|
// hblank start = enable border (bitplane not visible until next BPL1DAT)..
|
|
static void hblank_reset(int hblankpos)
|
|
{
|
|
if (thisline_decision.plfleft < 0) {
|
|
return;
|
|
}
|
|
// border re-enable takes 1 lores pixel
|
|
hb_last_diwlastword = hblankpos + 4;
|
|
}
|
|
|
|
static void addcc(int pos, int reg, int v)
|
|
{
|
|
color_change *cc = &curr_color_changes[next_color_change++];
|
|
cc->linepos = pos;
|
|
cc->regno = reg;
|
|
cc->value = v;
|
|
cc++;
|
|
cc->regno = -1;
|
|
}
|
|
|
|
static void addccmp(int pos, int reg, int v, int chpos)
|
|
{
|
|
if (pos < chpos && pos >= last_recorded_diw_hpos) {
|
|
addcc(pos, reg, v);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
static void syncdebugmarkers(int chpos)
|
|
{
|
|
bool pal = (beamcon0 & BEAMCON0_PAL) != 0;
|
|
if (hsyncdebug == 3) {
|
|
// CSYNC
|
|
int con = 1;
|
|
int coff = 0;
|
|
if (beamcon0 & BEAMCON0_CSYTRUE) {
|
|
con = 0;
|
|
coff = 1;
|
|
}
|
|
bool varcsy = (beamcon0 & BEAMCON0_VARCSYEN) != 0;
|
|
bool blanken = (beamcon0 & BEAMCON0_BLANKEN) != 0;
|
|
int hc1 = 1 + maxhpos;
|
|
hc1 <<= CCK_SHRES_SHIFT;
|
|
int nline = pal ? 8 : 10;
|
|
addccmp(hbstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x204, 1, chpos);
|
|
addccmp(hbstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x204, 0, chpos);
|
|
if (blanken) {
|
|
addccmp(hbstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hbstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
} else if (!varcsy) {
|
|
int ver1 = pal ? 26 : 27;
|
|
int ver2 = pal ? 140 : 141;
|
|
int vr2 = 115;
|
|
ver1 <<= CCK_SHRES_SHIFT;
|
|
ver2 <<= CCK_SHRES_SHIFT;
|
|
vr2 <<= CCK_SHRES_SHIFT;
|
|
if (vpos >= nline) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
}
|
|
if (pal) {
|
|
// PAL
|
|
if (lof_display) {
|
|
// EVEN
|
|
if (vpos == 0) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 1 || vpos == 2 || vpos == 6 || vpos == 7) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 3 || vpos == 4) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(vr2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 5) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(vr2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
} else {
|
|
// ODD
|
|
if (vpos == 0 || vpos == 1 || vpos == 5 || vpos == 6) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 2) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 3 || vpos == 4) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(vr2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 7) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
}
|
|
} else {
|
|
// NTSC
|
|
if (lof_display) {
|
|
// EVEN
|
|
if (vpos == 0) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 1 || vpos == 2 || vpos == 7 || vpos == 8 || vpos == 9) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 4 || vpos == 5) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(vr2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 3) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 6) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(vr2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
} else {
|
|
// ODD
|
|
if (vpos == 0 || vpos == 1 || vpos == 2 || vpos == 6 || vpos == 7 || vpos == 8) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 3) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(ver1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 4 || vpos == 5) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(vr2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hc1, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
if (vpos == 9) {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
thisline_changed = 1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (vs_state_var && !vs_state_var_old && lof_display) {
|
|
// First VS=1 line, LOF=1: VS=1 at HCENTER start
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
if (hsstop_v2 < hcenter_sync_v2) {
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
}
|
|
if (hbstrt_sync_v2 >= hcenter_sync_v2) {
|
|
addccmp(hbstrt_sync_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
}
|
|
if (hbstop_sync_v2 >= hcenter_sync_v2) {
|
|
addccmp(hbstop_sync_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
}
|
|
addccmp(hcenter_sync_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
thisline_changed = 1;
|
|
} else if (vs_state_var) {
|
|
addccmp(hbstrt_sync_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hbstop_sync_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hcenter_sync_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
thisline_changed = 1;
|
|
} else {
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, con, chpos);
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, coff, chpos);
|
|
}
|
|
}
|
|
} else {
|
|
// H/V SYNC
|
|
int hon = 1;
|
|
int hoff = 0;
|
|
if (beamcon0 & BEAMCON0_HSYTRUE) {
|
|
hon = 0;
|
|
hoff = 1;
|
|
}
|
|
if (hcenter_v2 < chpos && hcenter_v2 >= last_recorded_diw_hpos) {
|
|
if (pal) {
|
|
if (vs_state_var && !lof_display) {
|
|
addcc(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x206, 0);
|
|
} else if (!vs_state_var && vs_state_var_old && lof_display) {
|
|
addcc(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x206, 1);
|
|
}
|
|
} else {
|
|
if (vs_state_var && lof_display) {
|
|
addcc(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x206, 0);
|
|
} else if (!vs_state_var && vs_state_var_old && lof_display) {
|
|
addcc(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x206, 1);
|
|
}
|
|
}
|
|
}
|
|
addccmp(hsstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, hon, chpos);
|
|
addccmp(hsstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x202, hoff, chpos);
|
|
addccmp(hbstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x204, 1, chpos);
|
|
addccmp(hbstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x204, 0, chpos);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void record_color_change2(int hpos, int regno, uae_u32 value)
|
|
{
|
|
if (line_disabled) {
|
|
return;
|
|
}
|
|
|
|
if (next_color_change > MAX_REG_CHANGE - 30) {
|
|
return;
|
|
}
|
|
|
|
int pos = hpos < 0 ? -hpos : hpos_to_diwx(hpos);
|
|
int start_color_change = next_color_change;
|
|
|
|
// AGA has extra hires pixel delay in color changes
|
|
if ((regno < RECORDED_REGISTER_CHANGE_OFFSET || regno == RECORDED_REGISTER_CHANGE_OFFSET + 0x10c || regno == RECORDED_REGISTER_CHANGE_OFFSET + 0x201) && aga_mode) {
|
|
if (currprefs.chipset_hr) {
|
|
pos += 2;
|
|
}
|
|
if (regno == RECORDED_REGISTER_CHANGE_OFFSET + 0x201) {
|
|
// EHB delay
|
|
pos += 2;
|
|
}
|
|
if (regno == RECORDED_REGISTER_CHANGE_OFFSET + 0x10c) {
|
|
// BPLCON4:
|
|
// Bitplane XOR change: 2 hires pixel delay
|
|
// Sprite bank change: 1 hires pixel delay
|
|
if (!currprefs.chipset_hr) {
|
|
pos += 2;
|
|
}
|
|
if (value & 0xff00) {
|
|
thisline_decision.xor_seen = true;
|
|
}
|
|
pos += 2;
|
|
if ((value & 0x00ff) != (bplcon4 & 0x00ff)) {
|
|
// Sprite bank delay
|
|
addcc(pos, regno | 1, value);
|
|
}
|
|
pos += 2;
|
|
}
|
|
}
|
|
|
|
// HCENTER blanking (ECS Denise only)
|
|
if (hcenter_active && vs_state_on && lof_display) {
|
|
int chpos = pos;
|
|
if (!hcenterblank_state && hcenter_v2 < chpos && hcenter_v2 >= last_recorded_diw_hpos) {
|
|
hcenterblank_state = true;
|
|
if ((bplcon0 & 1) && (bplcon3 & 1)) {
|
|
addcc(hcenter_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 1);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HBS, diw_to_hpos(hcenter_v2), vpos);
|
|
}
|
|
#endif
|
|
thisline_changed = 1;
|
|
}
|
|
}
|
|
if (hcenterblank_state && hcenter_v2_end < chpos && hcenter_v2_end >= last_recorded_diw_hpos) {
|
|
hcenterblank_state = false;
|
|
if ((bplcon0 & 1) && (bplcon3 & 1)) {
|
|
addcc(hcenter_v2_end, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 0);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HBE, diw_to_hpos(hcenter_v2), vpos);
|
|
}
|
|
#endif
|
|
thisline_changed = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exthblank) {
|
|
int chpos = pos;
|
|
// inject programmed hblank start and end in color changes
|
|
if (hbstrt_v2 <= hbstop_v2) {
|
|
if (hbstrt_v2 < chpos && hbstrt_v2 >= last_recorded_diw_hpos) {
|
|
addcc(hbstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 1);
|
|
hblank_reset(hbstrt_v2);
|
|
exthblank_state = true;
|
|
last_hblank_start = hbstrt_v2;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HBS, diw_to_hpos(hbstrt_v2), vpos);
|
|
}
|
|
#endif
|
|
}
|
|
if (hbstop_v2 < chpos && hbstop_v2 >= last_recorded_diw_hpos) {
|
|
// do_color_changes() HBLANK workaround
|
|
if (next_color_change == last_color_change && exthblank_state) {
|
|
addcc(0, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 1);
|
|
}
|
|
addcc(hbstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 0);
|
|
exthblank_state = false;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HBE, diw_to_hpos(hbstop_v2), vpos);
|
|
}
|
|
#endif
|
|
}
|
|
} else if (hbstrt_v2 > hbstop_v2) { // equal: blank disable wins
|
|
if (hbstop_v2 < chpos && hbstop_v2 >= last_recorded_diw_hpos) {
|
|
if (next_color_change == last_color_change && exthblank_state) {
|
|
addcc(0, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 1);
|
|
}
|
|
addcc(hbstop_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 0);
|
|
exthblank_state = false;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HBE, diw_to_hpos(hbstop_v2), vpos);
|
|
}
|
|
#endif
|
|
}
|
|
if (hbstrt_v2 < chpos && hbstrt_v2 >= last_recorded_diw_hpos) {
|
|
addcc(hbstrt_v2, RECORDED_REGISTER_CHANGE_OFFSET + 0x200, 1);
|
|
hblank_reset(hbstrt_v2);
|
|
exthblank_state = true;
|
|
last_hblank_start = hbstrt_v2;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HBS, diw_to_hpos(hbstrt_v2), vpos);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
// inject hsync and end in color changes (ultra mode debug)
|
|
if (hsyncdebug) {
|
|
syncdebugmarkers(pos);
|
|
}
|
|
#endif
|
|
|
|
if (regno != 0xffff) {
|
|
addcc(pos, regno, value);
|
|
}
|
|
|
|
if (pos > last_recorded_diw_hpos) {
|
|
last_recorded_diw_hpos = pos;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
int cchanges = next_color_change - start_color_change;
|
|
if (cchanges > 1 && hsyncdebug) {
|
|
if (cchanges == 2) {
|
|
color_change *cc1 = &curr_color_changes[start_color_change];
|
|
color_change *cc2 = &curr_color_changes[start_color_change + 1];
|
|
if (cc1->linepos > cc2->linepos) {
|
|
color_change cc = *cc2;
|
|
*cc2 = *cc1;
|
|
*cc1 = cc;
|
|
}
|
|
} else {
|
|
// debug only, can be slow
|
|
for (int i = 0; i < cchanges; i++) {
|
|
color_change *cc1 = &curr_color_changes[start_color_change + i];
|
|
for(int j = i + 1; j < cchanges; j++) {
|
|
color_change *cc2 = &curr_color_changes[start_color_change + j];
|
|
if (cc1->linepos > cc2->linepos) {
|
|
color_change cc = *cc2;
|
|
*cc2 = *cc1;
|
|
*cc1 = cc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void sync_color_changes(int hpos)
|
|
{
|
|
record_color_change2(hpos, 0xffff, 0);
|
|
}
|
|
|
|
static void insert_actborder(int diw, bool onoff, bool blank)
|
|
{
|
|
if (custom_disabled) {
|
|
return;
|
|
}
|
|
|
|
int i;
|
|
// already exists?
|
|
for (i = last_color_change; i < next_color_change; i++) {
|
|
struct color_change *cc = &curr_color_changes[i];
|
|
if (cc->linepos == diw && cc->regno == 0 && (cc->value & COLOR_CHANGE_MASK) == COLOR_CHANGE_ACTBORDER) {
|
|
cc->value = COLOR_CHANGE_ACTBORDER | (onoff ? 1 : 0) | (blank ? 2 : 0);
|
|
return;
|
|
}
|
|
}
|
|
// find slot to insert into
|
|
for (i = last_color_change; i < next_color_change; i++) {
|
|
struct color_change *cc = &curr_color_changes[i];
|
|
if (cc->linepos > diw) {
|
|
break;
|
|
}
|
|
}
|
|
if (i < next_color_change) {
|
|
int num = next_color_change - i;
|
|
memmove(&curr_color_changes[i + 1], &curr_color_changes[i], sizeof(struct color_change) * num);
|
|
}
|
|
|
|
struct color_change *cc = &curr_color_changes[i];
|
|
cc->linepos = diw;
|
|
cc->regno = 0;
|
|
cc->value = COLOR_CHANGE_ACTBORDER | (onoff ? 1 : 0) | (blank ? 2 : 0);
|
|
next_color_change++;
|
|
}
|
|
|
|
// hdiw opened again in same scanline
|
|
// erase (color0 or bblank) area between previous end and new start
|
|
static void hdiw_restart(int diw_last, int diw_current, bool blank)
|
|
{
|
|
if (diw_last >= diw_current || line_hidden() || custom_disabled) {
|
|
return;
|
|
}
|
|
// update state
|
|
diw_current = adjust_hr(diw_current);
|
|
diw_last = adjust_hr(diw_last);
|
|
record_color_change2(-diw_current, 0xffff, 0);
|
|
|
|
// find slot to insert into
|
|
int i;
|
|
for (i = last_color_change; i < next_color_change; i++) {
|
|
struct color_change *cc = &curr_color_changes[i];
|
|
if (cc->linepos > diw_last) {
|
|
break;
|
|
}
|
|
}
|
|
if (i < next_color_change) {
|
|
int num = next_color_change - i;
|
|
memmove(&curr_color_changes[i + 1], &curr_color_changes[i], sizeof(struct color_change) * num);
|
|
}
|
|
|
|
struct color_change *cc = &curr_color_changes[i];
|
|
cc->linepos = diw_last;
|
|
cc->regno = 0;
|
|
cc->value = COLOR_CHANGE_ACTBORDER | 1 | (blank ? 2 : 0);
|
|
next_color_change++;
|
|
|
|
record_color_change2(-diw_current, 0, COLOR_CHANGE_ACTBORDER | 0);
|
|
}
|
|
|
|
/* 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 int hdiw_denisecounter, hdiw_denisecounter_abs, hdiw_denisecounter_reset;
|
|
|
|
// hblank start
|
|
static void decide_hdiw_blank_check_start(int hpos, int start_diw_hpos, int end_diw_hpos)
|
|
{
|
|
if (hdiwstate_blank == diw_states::DIW_waiting_start) {
|
|
if (hbstrt_reg >= start_diw_hpos && hbstrt_reg < end_diw_hpos) {
|
|
int val = hdiw_denisecounter_abs + (hbstrt_reg - start_diw_hpos);
|
|
insert_actborder(val, true, true);
|
|
MARK_LINE_CHANGED;
|
|
hdiwstate_blank = diw_states::DIW_waiting_stop;
|
|
}
|
|
}
|
|
}
|
|
// hblank end
|
|
static void decide_hdiw_blank_check_stop(int hpos, int start_diw_hpos, int end_diw_hpos)
|
|
{
|
|
if (hdiwstate_blank == diw_states::DIW_waiting_stop) {
|
|
if (hbstop_reg >= start_diw_hpos && hbstop_reg < end_diw_hpos) {
|
|
int val = hdiw_denisecounter_abs + (hbstop_reg - start_diw_hpos);
|
|
insert_actborder(val, false, true);
|
|
MARK_LINE_CHANGED;
|
|
hdiwstate_blank = diw_states::DIW_waiting_start;
|
|
}
|
|
}
|
|
}
|
|
// hdiw open
|
|
static void decide_hdiw_check_start(int start_diw_hpos, int end_diw_hpos)
|
|
{
|
|
if (hdiwstate == diw_states::DIW_waiting_start) {
|
|
if (diw_hstrt >= start_diw_hpos && diw_hstrt < end_diw_hpos) {
|
|
int val = hdiw_denisecounter_abs + (diw_hstrt - start_diw_hpos);
|
|
int first = coord_diw_shres_to_window_x(val);
|
|
if (last_diwlastword >= 0) {
|
|
// was closed previously in same line: fill closed part using color0.
|
|
if (denisea1000) {
|
|
last_diwlastword += 4 << shres_shift;
|
|
if (last_diwlastword < val) {
|
|
hdiw_restart(last_diwlastword, val, false);
|
|
}
|
|
} else {
|
|
hdiw_restart(last_diwlastword, val, false);
|
|
}
|
|
last_diwlastword = -1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HDIWS, diw_to_hpos(val), vpos);
|
|
}
|
|
#endif
|
|
}
|
|
if (thisline_decision.diwfirstword < 0) {
|
|
thisline_decision.diwfirstword = adjust_hr2(first);
|
|
// opened before first BPL1DAT?
|
|
if (!plane0_first_done) {
|
|
plane0p_enabled = ecs_denise && (bplcon0 & 1) && (bplcon3 & 0x20);
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HDIWS, diw_to_hpos(first), vpos);
|
|
}
|
|
#endif
|
|
}
|
|
hdiwstate = diw_states::DIW_waiting_stop;
|
|
}
|
|
}
|
|
}
|
|
// hdiw close
|
|
static void decide_hdiw_check_stop(int start_diw_hpos, int end_diw_hpos)
|
|
{
|
|
if (hdiwstate == diw_states::DIW_waiting_stop) {
|
|
if (diw_hstop >= start_diw_hpos && diw_hstop < end_diw_hpos) {
|
|
int val = hdiw_denisecounter_abs + (diw_hstop - start_diw_hpos);
|
|
int last = coord_diw_shres_to_window_x(val);
|
|
if (last > thisline_decision.diwlastword) {
|
|
thisline_decision.diwlastword = adjust_hr2(last);
|
|
// if HDIW opens again in same line
|
|
last_diwlastword = val;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_HDIWE, diw_to_hpos(last), vpos);
|
|
}
|
|
#endif
|
|
}
|
|
hdiwstate = diw_states::DIW_waiting_start;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void decide_hdiw_check(int hpos, int start_diw_hpos, int end_diw_hpos)
|
|
{
|
|
if ((diw_hstrt >= start_diw_hpos && diw_hstrt < end_diw_hpos) &&
|
|
(diw_hstop >= start_diw_hpos && diw_hstop < end_diw_hpos)) {
|
|
if (diw_hstrt > diw_hstop) {
|
|
decide_hdiw_check_stop(start_diw_hpos, end_diw_hpos);
|
|
decide_hdiw_check_start(start_diw_hpos, end_diw_hpos);
|
|
} else {
|
|
decide_hdiw_check_start(start_diw_hpos, end_diw_hpos);
|
|
decide_hdiw_check_stop(start_diw_hpos, end_diw_hpos);
|
|
}
|
|
} else {
|
|
decide_hdiw_check_start(start_diw_hpos, end_diw_hpos);
|
|
decide_hdiw_check_stop(start_diw_hpos, end_diw_hpos);
|
|
}
|
|
// check also hblank if there is chance it has been moved to visible area
|
|
if (hstrobe_conflict || vhposw_modified) {
|
|
decide_hdiw_blank_check_start(hpos, start_diw_hpos, end_diw_hpos);
|
|
}
|
|
if (hdiwstate_blank == diw_states::DIW_waiting_stop) {
|
|
decide_hdiw_blank_check_stop(hpos, start_diw_hpos, end_diw_hpos);
|
|
}
|
|
hdiw_denisecounter_abs += end_diw_hpos - start_diw_hpos;
|
|
}
|
|
|
|
static void decide_hdiw2(int hpos, bool halfcycle)
|
|
{
|
|
int hp = (hpos << 1) + halfcycle; // cck->lores
|
|
int c = (hp - last_diw_hpos) << (CCK_SHRES_SHIFT - 1); // ->shres
|
|
if (c <= 0) {
|
|
return;
|
|
}
|
|
if (last_diw_hpos < (HARDWIRED_DMA_TRIGGER_HPOS << 1) && hp >= (HARDWIRED_DMA_TRIGGER_HPOS << 1)) {
|
|
hdiw_denisecounter_reset = (REFRESH_FIRST_HPOS - HARDWIRED_DMA_TRIGGER_HPOS + 3) << CCK_SHRES_SHIFT;
|
|
//hdiw_denisecounter_reset += 1 << (CCK_SHRES_SHIFT - 1);
|
|
}
|
|
|
|
int start = hdiw_denisecounter;
|
|
int max = 512 << (CCK_SHRES_SHIFT - 1);
|
|
|
|
if (hdiw_denisecounter_reset > 0 && c >= hdiw_denisecounter_reset && !line_equ_freerun && !hstrobe_conflict) {
|
|
// strobe crossed?
|
|
hdiw_denisecounter += hdiw_denisecounter_reset;
|
|
decide_hdiw_check(hpos, start, hdiw_denisecounter);
|
|
hdiw_denisecounter = 2 << LORES_TO_SHRES_SHIFT;
|
|
start = hdiw_denisecounter;
|
|
hdiw_denisecounter += c - hdiw_denisecounter_reset;
|
|
decide_hdiw_check(hpos, start, hdiw_denisecounter);
|
|
hdiw_denisecounter_reset = -1;
|
|
} else {
|
|
hdiw_denisecounter += c;
|
|
// wrap around?
|
|
if (hdiw_denisecounter >= max) {
|
|
decide_hdiw_check(hpos, start, max);
|
|
hdiw_denisecounter &= max - 1;
|
|
if (hdiw_denisecounter > 0) {
|
|
decide_hdiw_check(hpos, 0, hdiw_denisecounter);
|
|
}
|
|
} else {
|
|
decide_hdiw_check(hpos, start, hdiw_denisecounter);
|
|
}
|
|
if (hdiw_denisecounter_reset >= 0) {
|
|
hdiw_denisecounter_reset -= c;
|
|
}
|
|
}
|
|
|
|
last_diw_hpos = hp;
|
|
}
|
|
|
|
static void decide_hdiw(int hpos, bool halfcycle = false)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (!debug_dma) {
|
|
decide_hdiw2(hpos, halfcycle);
|
|
} else {
|
|
for (int i = (last_diw_hpos >> 1); i <= hpos; i++) {
|
|
decide_hdiw2(i, halfcycle);
|
|
halfcycle = false;
|
|
record_dma_denise(i, hdiw_denisecounter >> 2);
|
|
}
|
|
}
|
|
#else
|
|
decide_hdiw2(hpos, halfcycle);
|
|
#endif
|
|
}
|
|
|
|
static void sync_changes(int hpos)
|
|
{
|
|
decide_hdiw(hpos);
|
|
decide_sprites(hpos);
|
|
record_color_change2(hpos, 0xffff, 0);
|
|
|
|
}
|
|
|
|
static int fetchmode, fetchmode_size, fetchmode_mask, fetchmode_bytes;
|
|
static int fetchmode_fmode_bpl, fetchmode_fmode_spr;
|
|
static int real_bitplane_number[3][3][9];
|
|
|
|
/* Disable bitplane DMA if planes > available DMA slots. This is needed
|
|
e.g. by the Sanity WOC demo (at the "Party Effect"). */
|
|
STATIC_INLINE int GET_PLANES_LIMIT(uae_u16 bc0)
|
|
{
|
|
int res = GET_RES_AGNUS(bc0);
|
|
int planes = GET_PLANES(bc0);
|
|
return real_bitplane_number[fetchmode][res][planes];
|
|
}
|
|
|
|
static void add_mod(int nr, uae_s16 mod)
|
|
{
|
|
#ifdef AGA
|
|
if (fetchmode_fmode_bpl == 1 || fetchmode_fmode_bpl == 2) {
|
|
// FMODE=1/2, unaligned bpl and modulo: bit 1 carry is ignored.
|
|
if ((bplpt[nr] & 2) && (mod & 2)) {
|
|
mod -= 4;
|
|
}
|
|
} else if (fetchmode_fmode_bpl == 3) {
|
|
// FMODE=3, unaligned bpl and modulo: bit 2 carry is ignored.
|
|
if ((bplpt[nr] & (4 | 2)) + (mod & (4 | 2)) >= 8) {
|
|
mod -= 8;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bplpt[nr] += mod;
|
|
bplptx[nr] += mod;
|
|
}
|
|
|
|
static void add_modulo(int hpos, int nr)
|
|
{
|
|
uae_s16 mod, m1, m2;
|
|
|
|
m1 = bpl1mod;
|
|
m2 = bpl2mod;
|
|
if (bpl1mod_hpos == hpos) {
|
|
m1 = bpl1mod_prev;
|
|
}
|
|
if (bpl2mod_hpos == hpos) {
|
|
m2 = bpl2mod_prev;
|
|
}
|
|
if (fmode & 0x4000) {
|
|
if (((diwstrt >> 8) ^ (vposh ^ 1)) & 1) {
|
|
mod = m2;
|
|
} else {
|
|
mod = m1;
|
|
}
|
|
} else if (nr & 1) {
|
|
mod = m2;
|
|
} else {
|
|
mod = m1;
|
|
}
|
|
add_mod(nr, mod);
|
|
}
|
|
|
|
static void add_modulos(void)
|
|
{
|
|
uae_s16 m1, m2;
|
|
|
|
if (fmode & 0x4000) {
|
|
if (((diwstrt >> 8) ^ (vposh ^ 1)) & 1) {
|
|
m1 = m2 = bpl2mod;
|
|
} else {
|
|
m1 = m2 = bpl1mod;
|
|
}
|
|
} else {
|
|
m1 = bpl1mod;
|
|
m2 = bpl2mod;
|
|
}
|
|
|
|
switch (bplcon0_planes_limit) {
|
|
#ifdef AGA
|
|
case 8: add_mod(7, m2);
|
|
case 7: add_mod(6, m1);
|
|
#endif
|
|
case 6: add_mod(5, m2);
|
|
case 5: add_mod(4, m1);
|
|
case 4: add_mod(3, m2);
|
|
case 3: add_mod(2, m1);
|
|
case 2: add_mod(1, m2);
|
|
case 1: add_mod(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 uae_u32 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 uae_u32 fetchstart, fetchstart_shift, fetchstart_mask;
|
|
/* fm_maxplane holds the maximum number of planes possible with the current
|
|
fetch mode. This selects the cycle diagram:
|
|
8 planes: 73516240
|
|
4 planes: 3120
|
|
2 planes: 10. */
|
|
static uae_u32 fm_maxplane, fm_maxplane_shift;
|
|
|
|
/* The corresponding values, by fetchmode and display resolution. */
|
|
static const uae_u8 fetchunits[] = { 8,8,8,0, 16,8,8,0, 32,16,8,0 };
|
|
static const uae_u8 fetchstarts[] = { 3,2,1,0, 4,3,2,0, 5,4,3,0 };
|
|
static const uae_u8 fm_maxplanes[] = { 3,2,1,0, 3,3,2,0, 3,3,3,0 };
|
|
|
|
static uae_s8 cycle_diagram_table[3][3][9][32];
|
|
static uae_s8 cycle_diagram_free_cycles[3][3][9];
|
|
static uae_s8 cycle_diagram_total_cycles[3][3][9];
|
|
static uae_s8 *curr_diagram;
|
|
static const uae_s8 cycle_sequences[3 * 8] = { 2,1,2,1,2,1,2,1, 4,2,3,1,4,2,3,1, 8,4,6,2,7,3,5,1 };
|
|
|
|
static void debug_cycle_diagram(void)
|
|
{
|
|
int fm, res, planes, cycle, v;
|
|
TCHAR aa;
|
|
|
|
for (fm = 0; fm <= 2; fm++) {
|
|
write_log(_T("FMODE %d\n=======\n"), fm);
|
|
for (res = 0; res <= 2; res++) {
|
|
for (planes = 0; planes <= 8; planes++) {
|
|
write_log(_T("%d: "), planes);
|
|
for (cycle = 0; cycle < 32; cycle++) {
|
|
v = cycle_diagram_table[fm][res][planes][cycle];
|
|
if (v == 0) aa = '-'; else if (v > 0) aa = '1'; else aa = 'X';
|
|
write_log(_T("%c"), aa);
|
|
}
|
|
write_log(_T(" %d:%d\n"), cycle_diagram_free_cycles[fm][res][planes], cycle_diagram_total_cycles[fm][res][planes]);
|
|
}
|
|
write_log(_T("\n"));
|
|
}
|
|
}
|
|
fm = 0;
|
|
}
|
|
|
|
static void create_cycle_diagram_table(void)
|
|
{
|
|
int fm, res, cycle, planes, rplanes, v;
|
|
int fetch_start, max_planes, freecycles;
|
|
const uae_s8 *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 = -1;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
debug_cycle_diagram();
|
|
#endif
|
|
}
|
|
|
|
static void clear_bitplane_pipeline(int type)
|
|
{
|
|
// clear bitplane allocations
|
|
int safepos = hsyncstartpos_start_cycles - 1;
|
|
int count = RGA_PIPELINE_ADJUST + 1;
|
|
if (type) {
|
|
for (int i = 0; i < maxhposm0 + RGA_PIPELINE_ADJUST; i++) {
|
|
if (i < safepos || i >= safepos + count) {
|
|
uae_u16 v = cycle_line_pipe[i];
|
|
if (v & CYCLE_PIPE_BITPLANE) {
|
|
cycle_line_pipe[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = safepos; i < safepos + count; i++) {
|
|
uae_u16 v = cycle_line_pipe[i];
|
|
if (v & CYCLE_PIPE_BITPLANE) {
|
|
cycle_line_pipe[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define HARD_DDF_STOP 0xd8
|
|
|
|
#define ESTIMATED_FETCH_MODE 1
|
|
#define OPTIMIZED_ESTIMATE 1
|
|
|
|
static uae_s8 estimated_cycles_buf0[MAX_CHIPSETSLOTS + MAX_CHIPSETSLOTS_EXTRA];
|
|
static uae_s8 estimated_cycles_buf1[MAX_CHIPSETSLOTS + MAX_CHIPSETSLOTS_EXTRA];
|
|
static uae_s8 estimated_cycles_empty[MAX_CHIPSETSLOTS + MAX_CHIPSETSLOTS_EXTRA];
|
|
static int estimate_cycles_empty_index = -1;
|
|
static uae_u16 estimated_bplcon0, estimated_fm, estimated_plfstrt, estimated_plfstop;
|
|
static uae_s8 *estimated_cycles = estimated_cycles_empty;
|
|
static bool estimated_empty;
|
|
static int estimated_maxhpos[2];
|
|
|
|
#if ESTIMATED_FETCH_MODE
|
|
|
|
static void end_estimate_last_fetch_cycle(int hpos)
|
|
{
|
|
#if OPTIMIZED_ESTIMATE
|
|
if (estimate_cycles_empty_index >= 0) {
|
|
for (int i = 0; i < RGA_PIPELINE_ADJUST; i++) {
|
|
int pos = (estimate_cycles_empty_index + i) % maxhpos;
|
|
estimated_cycles_empty[pos] = 0;
|
|
}
|
|
estimate_cycles_empty_index = -1;
|
|
}
|
|
if (estimated_cycles_empty != estimated_cycles) {
|
|
estimated_cycles = estimated_cycles_empty;
|
|
estimate_cycles_empty_index = hpos;
|
|
uae_s8 *est = NULL;
|
|
if (maxhpos == estimated_maxhpos[0]) {
|
|
est = estimated_cycles_buf0;
|
|
} else if (maxhpos == estimated_maxhpos[1]) {
|
|
est = estimated_cycles_buf1;
|
|
}
|
|
if (est) {
|
|
for (int i = 0; i < RGA_PIPELINE_ADJUST; i++) {
|
|
int pos = (hpos + i) % maxhpos;
|
|
estimated_cycles_empty[pos] = est[pos];
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
estimated_cycles = estimated_cycles_buf0;
|
|
int start_pos = (hpos + RGA_PIPELINE_ADJUST) % maxhpos;
|
|
if (start_pos >= hpos) {
|
|
memset(estimated_cycles + start_pos, 0, maxhpos - start_pos);
|
|
memset(estimated_cycles, 0, hpos);
|
|
} else {
|
|
memset(estimated_cycles + start_pos, 0, hpos - start_pos);
|
|
}
|
|
#endif
|
|
estimated_empty = true;
|
|
}
|
|
|
|
static void estimate_last_fetch_cycle(int hpos)
|
|
{
|
|
if (!bprun) {
|
|
|
|
end_estimate_last_fetch_cycle(hpos);
|
|
|
|
} else {
|
|
|
|
#if OPTIMIZED_ESTIMATE
|
|
if (estimated_bplcon0 == bplcon0 && bpl_hstart == hpos && estimated_plfstrt == bpl_hstart && estimated_plfstop == plfstop && estimated_fm == fetchmode) {
|
|
if (maxhpos == estimated_maxhpos[0]) {
|
|
estimated_cycles = estimated_cycles_buf0;
|
|
return;
|
|
}
|
|
if (maxhpos == estimated_maxhpos[1]) {
|
|
estimated_cycles = estimated_cycles_buf1;
|
|
return;
|
|
}
|
|
} else {
|
|
estimated_maxhpos[0] = estimated_maxhpos[1] = -1;
|
|
}
|
|
#endif
|
|
#if 0
|
|
// plfstrt=24 plfstop=32 bpl_hstart=56 hard_ddf_stop=216 ddf_stopping=0 fetch_cycle=136 fetchunit=8 lastfetchunit=8
|
|
bpl_hstart = 56;
|
|
plfstrt = 24;
|
|
plfstop = 32;
|
|
bplcon0_res = 0;
|
|
fetchmode = 0;
|
|
fetch_cycle = 136;
|
|
ddf_stopping = 0;
|
|
hpos = 0;
|
|
curr_diagram = cycle_diagram_table[fetchmode][bplcon0_res][8];
|
|
#endif
|
|
int hard_ddf_stop = harddis_h ? 0x100 : HARD_DDF_STOP;
|
|
int start = bpl_hstart;
|
|
int adjusted_plfstop = plfstop;
|
|
int estimated_cycle_count;
|
|
int fetchunit = fetchunits[fetchmode * 4 + bplcon0_res];
|
|
// Last fetch is always max 8 even if fetchunit is larger.
|
|
int lastfetchunit = fetchunit >= 8 ? 8 : fetchunit;
|
|
|
|
if (!ddf_stopping) {
|
|
int stop;
|
|
if (ecs_agnus) {
|
|
// ECS: stop wins if start == stop
|
|
stop = adjusted_plfstop < hpos || adjusted_plfstop > hard_ddf_stop ? hard_ddf_stop : adjusted_plfstop;
|
|
} else {
|
|
// OCS: start wins if start == stop
|
|
stop = adjusted_plfstop <= hpos || adjusted_plfstop > hard_ddf_stop ? hard_ddf_stop : adjusted_plfstop;
|
|
}
|
|
/* We know that fetching is up-to-date up until hpos, so we can use fetch_cycle. */
|
|
if (start > stop) {
|
|
// weird case handling
|
|
if (stop < fetch_cycle) {
|
|
stop = hard_ddf_stop;
|
|
}
|
|
int starting_last_block_at = (stop + fetchunit - 1) & ~(fetchunit - 1);
|
|
estimated_cycle_count = (starting_last_block_at - fetch_cycle) + lastfetchunit;
|
|
if (estimated_cycle_count < 0) {
|
|
estimated_cycle_count += maxhpos;
|
|
}
|
|
} else {
|
|
int fetch_cycle_at_stop = fetch_cycle + (stop - start);
|
|
int starting_last_block_at = (fetch_cycle_at_stop + fetchunit - 1) & ~(fetchunit - 1);
|
|
estimated_cycle_count = (starting_last_block_at - fetch_cycle) + lastfetchunit;
|
|
}
|
|
} else {
|
|
int fc = fetch_cycle;
|
|
int starting_last_block_at = (fc + fetchunit - 1) & ~(fetchunit - 1);
|
|
if (ddf_stopping == 2) {
|
|
if (starting_last_block_at <= fetchunit) {
|
|
starting_last_block_at = 0;
|
|
} else {
|
|
starting_last_block_at -= fetchunit;
|
|
}
|
|
}
|
|
estimated_cycle_count = (starting_last_block_at - fc) + lastfetchunit;
|
|
}
|
|
|
|
#if OPTIMIZED_ESTIMATE
|
|
estimated_cycles = maxhposeven ? estimated_cycles_buf1 : estimated_cycles_buf0;
|
|
#else
|
|
estimated_cycles = estimated_cycles_buf0;
|
|
#endif
|
|
// bitplane DMA end can wrap around, even in non-overrun cases
|
|
int start_pos = (hpos + RGA_PIPELINE_ADJUST) % maxhpos;
|
|
int start_pos2 = start_pos;
|
|
int end_pos = (hpos + RGA_PIPELINE_ADJUST + estimated_cycle_count) % maxhpos;
|
|
int off = (start_pos - (bpl_hstart + RGA_PIPELINE_ADJUST)) & (fetchunit - 1);
|
|
while (start_pos != end_pos && end_pos >= 0) {
|
|
int off2 = off & fetchstart_mask;
|
|
|
|
#if 0
|
|
if (start_pos < 0 || start_pos > maxhpos || fetchstart > 32 || end_pos < 0 || end_pos > maxhpos) {
|
|
gui_message(_T("ERROR: %d %d %d %d %d %d %d %d %d %d\n%d %d %d %d %d %d %d %d %04x %d\n"),
|
|
hpos, bpl_hstart, estimated_cycle_count, start_pos, start_pos2, end_pos, off, off2, fetchstart, maxhpos,
|
|
plfstrt, plfstop, hard_ddf_stop, ddf_stopping, fetch_cycle, fetchunit, lastfetchunit, vpos, bplcon0, fmode);
|
|
}
|
|
#endif
|
|
|
|
if (!off2 && start_pos + fetchstart <= end_pos) {
|
|
memcpy(estimated_cycles + start_pos, curr_diagram, fetchstart);
|
|
start_pos += fetchstart;
|
|
} else {
|
|
estimated_cycles[start_pos] = curr_diagram[off2];
|
|
start_pos++;
|
|
if (start_pos >= maxhpos) {
|
|
start_pos = 0;
|
|
}
|
|
if (start_pos == REFRESH_FIRST_HPOS) {
|
|
// bpl sequencer repeated this cycle
|
|
// (request was done at cycle 0)
|
|
off--;
|
|
}
|
|
off++;
|
|
}
|
|
}
|
|
#if OPTIMIZED_ESTIMATE
|
|
if (estimated_empty && bpl_hstart == hpos && !ddf_stopping) {
|
|
estimated_empty = false;
|
|
// zero rest of buffer
|
|
if (end_pos != start_pos2) {
|
|
if (end_pos > start_pos2) {
|
|
memset(estimated_cycles + end_pos, 0, maxhpos - end_pos);
|
|
memset(estimated_cycles, 0, start_pos2);
|
|
} else {
|
|
memset(estimated_cycles + end_pos, 0, start_pos2 - end_pos);
|
|
}
|
|
}
|
|
estimated_bplcon0 = bplcon0;
|
|
estimated_plfstrt = bpl_hstart;
|
|
estimated_plfstop = plfstop;
|
|
estimated_fm = fetchmode;
|
|
estimated_maxhpos[maxhposeven] = maxhpos;
|
|
} else {
|
|
estimated_fm = 0xffff;
|
|
estimated_maxhpos[0] = -1;
|
|
estimated_maxhpos[1] = -1;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
struct bpl_estimate {
|
|
uae_u16 startend;
|
|
uae_u16 start_pos;
|
|
uae_u16 end_pos;
|
|
uae_u16 vpos;
|
|
uae_s8 *cycle_diagram;
|
|
uae_u16 ce_offset;
|
|
};
|
|
|
|
#define MAX_BPL_ESTIMATES 2
|
|
static struct bpl_estimate bpl_estimates[MAX_BPL_ESTIMATES];
|
|
static int bpl_estimate_index;
|
|
|
|
static void end_estimate_last_fetch_cycle(int hpos)
|
|
{
|
|
for (int i = 0; i < MAX_BPL_ESTIMATES; i++) {
|
|
struct bpl_estimate *be = &bpl_estimates[i];
|
|
if (be->startend && be->vpos == vposh) {
|
|
if (be->end_pos > hpos + RGA_PIPELINE_ADJUST) {
|
|
be->end_pos = hpos + RGA_PIPELINE_ADJUST;
|
|
} else if (be->start_pos > be->end_pos && ((hpos + RGA_PIPELINE_ADJUST) % maxhpos) > be->start_pos) {
|
|
be->end_pos = (hpos + RGA_PIPELINE_ADJUST) % maxhpos;
|
|
be->startend = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void estimate_last_fetch_cycle(int hpos)
|
|
{
|
|
int hard_ddf_stop = harddis_h ? 0x100 : HARD_DDF_STOP;
|
|
int start = bpl_hstart;
|
|
int adjusted_plfstop = plfstop;
|
|
int estimated_cycle_count;
|
|
int fetchunit = fetchunits[fetchmode * 4 + bplcon0_res];
|
|
// Last fetch is always max 8 even if fetchunit is larger.
|
|
int lastfetchunit = fetchunit >= 8 ? 8 : fetchunit;
|
|
|
|
end_estimate_last_fetch_cycle(hpos);
|
|
|
|
if (!bprun) {
|
|
return;
|
|
}
|
|
|
|
bpl_estimate_index++;
|
|
bpl_estimate_index &= MAX_BPL_ESTIMATES - 1;
|
|
struct bpl_estimate *be = &bpl_estimates[bpl_estimate_index];
|
|
|
|
if (!ddf_stopping) {
|
|
int stop;
|
|
if (ecs_agnus) {
|
|
// ECS: stop wins if start == stop
|
|
stop = adjusted_plfstop < hpos || adjusted_plfstop > hard_ddf_stop ? hard_ddf_stop : adjusted_plfstop;
|
|
} else {
|
|
// OCS: start wins if start == stop
|
|
stop = adjusted_plfstop <= hpos || adjusted_plfstop > hard_ddf_stop ? hard_ddf_stop : adjusted_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 - start);
|
|
int starting_last_block_at = (fetch_cycle_at_stop + fetchunit - 1) & ~(fetchunit - 1);
|
|
estimated_cycle_count = (starting_last_block_at - fetch_cycle) + lastfetchunit;
|
|
} else {
|
|
int fc = fetch_cycle;
|
|
int starting_last_block_at = (fc + fetchunit - 1) & ~(fetchunit - 1);
|
|
if (ddf_stopping == 2) {
|
|
starting_last_block_at -= fetchunit;
|
|
}
|
|
estimated_cycle_count = (starting_last_block_at - fc) + lastfetchunit;
|
|
}
|
|
|
|
// bitplane DMA end can wrap around, even in non-overrun cases
|
|
be->start_pos = (hpos + RGA_PIPELINE_ADJUST) % maxhpos;
|
|
be->end_pos = (hpos + RGA_PIPELINE_ADJUST + estimated_cycle_count) % maxhpos;
|
|
if (be->end_pos > be->start_pos) {
|
|
be->startend = 1;
|
|
} else {
|
|
be->startend = 2;
|
|
}
|
|
be->vpos = vposh;
|
|
be->cycle_diagram = curr_diagram;
|
|
be->ce_offset = ((bpl_hstart + RGA_PIPELINE_ADJUST) - be->start_pos) & (fetchunit - 1);
|
|
}
|
|
#endif
|
|
|
|
static void calcdiw(void);
|
|
static void set_chipset_mode(void)
|
|
{
|
|
fmode = fmode_saved;
|
|
bplcon0 = bplcon0_saved;
|
|
bplcon1 = bplcon1_saved;
|
|
bplcon2 = bplcon2_saved;
|
|
bplcon3 = bplcon3_saved;
|
|
bplcon4 = bplcon4_saved;
|
|
if (!aga_mode) {
|
|
fmode = 0;
|
|
bplcon0 &= ~(0x0010 | 0x0020);
|
|
bplcon1 &= ~(0xff00);
|
|
bplcon2 &= ~(0x0100 | 0x0080);
|
|
bplcon3 &= 0x003f;
|
|
bplcon3 |= 0x0c00;
|
|
bplcon4 = 0x0011;
|
|
if (!ecs_agnus) {
|
|
bplcon0 &= ~0x0080;
|
|
diwhigh_written = 0;
|
|
}
|
|
if (!ecs_denise) {
|
|
bplcon0 &= ~0x0001;
|
|
bplcon2 &= 0x007f;
|
|
bplcon3 = 0x0c00;
|
|
}
|
|
}
|
|
sprres = expand_sprres(bplcon0, bplcon3);
|
|
sprite_width = GET_SPRITEWIDTH(fmode);
|
|
for (int i = 0; i < MAX_SPRITES; i++) {
|
|
spr[i].width = sprite_width;
|
|
}
|
|
shdelay_disabled = false;
|
|
exthblank_state = false;
|
|
refmask = 0x1fffff;
|
|
ref_ras_add = REF_RAS_ADD_AGA;
|
|
if (!aga_mode) {
|
|
ref_ras_add = REF_RAS_ADD_ECS;
|
|
refmask >>= 1;
|
|
if (!ecs_agnus) {
|
|
ref_ras_add = REF_RAS_ADD_OCS;
|
|
refmask >>= 1;
|
|
}
|
|
}
|
|
calcdiw();
|
|
}
|
|
|
|
static void update_mirrors(void)
|
|
{
|
|
aga_mode = (currprefs.chipset_mask & CSMASK_AGA) != 0;
|
|
ecs_agnus = (currprefs.chipset_mask & CSMASK_ECS_AGNUS) != 0;
|
|
ecs_denise = (currprefs.chipset_mask & CSMASK_ECS_DENISE) != 0;
|
|
agnusa1000 = currprefs.cs_agnusmodel == AGNUSMODEL_A1000 || currprefs.cs_agnusmodel == AGNUSMODEL_VELVET;
|
|
if (agnusa1000) {
|
|
ecs_agnus = false;
|
|
aga_mode = false;
|
|
}
|
|
denisea1000_noehb = currprefs.cs_denisemodel == DENISEMODEL_VELVET || currprefs.cs_denisemodel == DENISEMODEL_A1000NOEHB;
|
|
denisea1000 = currprefs.cs_denisemodel == DENISEMODEL_VELVET || currprefs.cs_denisemodel == DENISEMODEL_A1000NOEHB || currprefs.cs_denisemodel == DENISEMODEL_A1000;
|
|
direct_rgb = aga_mode;
|
|
if (aga_mode) {
|
|
sprite_sprctlmask = 0x01 | 0x08 | 0x10;
|
|
} else if (ecs_denise) {
|
|
sprite_sprctlmask = 0x01 | 0x10;
|
|
} else {
|
|
sprite_sprctlmask = 0x01;
|
|
}
|
|
if (aga_mode) {
|
|
for (int i = 0; i < 256; i++) {
|
|
current_colors.acolors[i] = getxcolor(current_colors.color_regs_aga[i]);
|
|
}
|
|
}
|
|
set_chipset_mode();
|
|
hsyncdebug = 0;
|
|
if (currprefs.gfx_overscanmode >= OVERSCANMODE_ULTRA) {
|
|
hsyncdebug = currprefs.gfx_overscanmode - OVERSCANMODE_ULTRA + 1;
|
|
}
|
|
}
|
|
|
|
extern struct color_entry colors_for_drawing;
|
|
|
|
void notice_new_xcolors(void)
|
|
{
|
|
update_mirrors();
|
|
docols(¤t_colors);
|
|
docols(&colors_for_drawing);
|
|
for (int i = 0; i < (MAXVPOS + 1) * 2; i++) {
|
|
docols(color_tables[0] + i);
|
|
docols(color_tables[1] + i);
|
|
}
|
|
}
|
|
|
|
static bool isehb(uae_u16 bplcon0, uae_u16 bplcon2)
|
|
{
|
|
bool bplehb;
|
|
if (aga_mode) {
|
|
bplehb = (bplcon0 & 0x7010) == 0x6000;
|
|
} else if (ecs_denise) {
|
|
bplehb = ((bplcon0 & 0xFC00) == 0x6000 || (bplcon0 & 0xFC00) == 0x7000);
|
|
} else {
|
|
bplehb = ((bplcon0 & 0xFC00) == 0x6000 || (bplcon0 & 0xFC00) == 0x7000) && !denisea1000_noehb;
|
|
}
|
|
return bplehb;
|
|
}
|
|
|
|
// OCS/ECS, lores, 7 planes = 4 "real" planes + BPL5DAT and BPL6DAT as static 5th and 6th plane
|
|
STATIC_INLINE int isocs7planes(void)
|
|
{
|
|
return !aga_mode && bplcon0_res == 0 && bplcon0_planes == 7;
|
|
}
|
|
|
|
int get_sprite_dma_rel(int hpos, int off)
|
|
{
|
|
int offset = get_rga_pipeline(hpos, off);
|
|
uae_u16 v = cycle_line_pipe[offset];
|
|
if (v & CYCLE_PIPE_SPRITE) {
|
|
return v & 7;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int get_bitplane_dma_rel(int hpos, int off)
|
|
{
|
|
int offset = get_rga_pipeline(hpos, off);
|
|
uae_u16 v = cycle_line_pipe[offset];
|
|
if (v & CYCLE_PIPE_BITPLANE) {
|
|
return v & 0x0f;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int islinetoggle(void)
|
|
{
|
|
int linetoggle = 0;
|
|
if (!(new_beamcon0 & BEAMCON0_LOLDIS) && !(new_beamcon0 & BEAMCON0_PAL) && ecs_agnus) {
|
|
linetoggle = 1; // NTSC and !LOLDIS -> LOL toggles every line
|
|
} else if (!ecs_agnus && currprefs.ntscmode) {
|
|
linetoggle = 1; // hardwired NTSC Agnus
|
|
}
|
|
return linetoggle;
|
|
}
|
|
|
|
static void compute_shifter_mask(void)
|
|
{
|
|
int shifter_size = 16 << (fetchmode + LORES_TO_SHRES_SHIFT - toscr_res);
|
|
shifter_mask = shifter_size - 1;
|
|
}
|
|
|
|
/* Expand bplcon0/bplcon1 into the toscr_xxx variables. */
|
|
static void compute_toscr_delay(int bplcon1)
|
|
{
|
|
int delay1 = (bplcon1 & 0x0f) | ((bplcon1 & 0x0c00) >> 6);
|
|
int delay2 = ((bplcon1 >> 4) & 0x0f) | (((bplcon1 >> 4) & 0x0c00) >> 6);
|
|
int delaymask = fetchmode_mask >> toscr_res;
|
|
|
|
compute_shifter_mask();
|
|
|
|
toscr_delay_shifter[0] = (delay1 & delaymask) << LORES_TO_SHRES_SHIFT;
|
|
toscr_delay_shifter[1] = (delay2 & delaymask) << LORES_TO_SHRES_SHIFT;
|
|
|
|
int shdelay1 = (bplcon1 >> 8) & 3;
|
|
int shdelay2 = (bplcon1 >> 12) & 3;
|
|
if (!currprefs.chipset_hr) {
|
|
// subpixel scrolling not possible
|
|
if (toscr_res == RES_HIRES) {
|
|
shdelay1 &= ~1;
|
|
shdelay2 &= ~1;
|
|
} else if (toscr_res == RES_LORES) {
|
|
shdelay1 &= ~3;
|
|
shdelay2 &= ~3;
|
|
}
|
|
}
|
|
toscr_delay_shifter[0] |= shdelay1;
|
|
toscr_delay_shifter[1] |= shdelay2;
|
|
|
|
if (toscr_delay_shifter[0] != toscr_delay_shifter[1] || 1) {
|
|
toscr_scanline_complex_bplcon1 = true;
|
|
toscr_scanline_complex_bplcon1_off = false;
|
|
} else {
|
|
if (toscr_scanline_complex_bplcon1) {
|
|
toscr_scanline_complex_bplcon1_off = true;
|
|
}
|
|
}
|
|
|
|
#if SPEEDUP
|
|
/* SPEEDUP code still needs this hack */
|
|
int delayoffset = fetchmode_size - (((bpl_hstart - 0x18) & fetchstart_mask) << 1);
|
|
delay1 += delayoffset;
|
|
delay2 += delayoffset;
|
|
toscr_delay_adjusted[0] = (delay1 & delaymask) << toscr_res;
|
|
toscr_delay_adjusted[0] |= shdelay1 >> (RES_MAX - toscr_res);
|
|
toscr_delay_adjusted[1] = (delay2 & delaymask) << toscr_res;
|
|
toscr_delay_adjusted[1] |= shdelay2 >> (RES_MAX - toscr_res);
|
|
toscr_nr_planes = GET_PLANES_LIMIT(bplcon0);
|
|
#endif
|
|
}
|
|
|
|
static void set_delay_lastcycle(void)
|
|
{
|
|
delay_lastcycle[0] = (((maxhpos + 0) * 2) + 2) << LORES_TO_SHRES_SHIFT;
|
|
delay_lastcycle[1] = delay_lastcycle[0];
|
|
if (islinetoggle()) {
|
|
delay_lastcycle[1] += 1 << LORES_TO_SHRES_SHIFT;
|
|
}
|
|
delay_hsynccycle = (((maxhpos + hsyncstartpos_start_cycles) * 2) - DDF_OFFSET) << LORES_TO_SHRES_SHIFT;
|
|
}
|
|
|
|
static void update_toscr_vars(void)
|
|
{
|
|
toscr_res_pixels_shift = 2 - toscr_res;
|
|
toscr_res_pixels_shift_hr = 2 - currprefs.gfx_resolution;
|
|
if (currprefs.chipset_hr) {
|
|
if (toscr_res < currprefs.gfx_resolution) {
|
|
toscr_res_mult = currprefs.gfx_resolution - toscr_res;
|
|
} else {
|
|
toscr_res_mult = 0;
|
|
}
|
|
toscr_res_pixels_mask_hr = (1 << toscr_res_mult) - 1;
|
|
if (toscr_res > currprefs.gfx_resolution) {
|
|
toscr_res_pixels_shift_hr -= toscr_res - currprefs.gfx_resolution;
|
|
if (toscr_res_pixels_shift_hr < 0) {
|
|
toscr_res_pixels_shift_hr = 0;
|
|
}
|
|
}
|
|
toscr_res_pixels_hr = 1 << toscr_res_pixels_shift_hr;
|
|
} else {
|
|
toscr_res_mult = 0;
|
|
}
|
|
toscr_res_pixels = 1 << toscr_res_pixels_shift;
|
|
toscr_res2p = 2 << toscr_res;
|
|
|
|
}
|
|
|
|
/* fetchstart_mask can be larger than fm_maxplane if FMODE > 0.
|
|
This means that the remaining cycles are idle.
|
|
*/
|
|
static const uae_u8 bpl_sequence_8[32] = { 8, 4, 6, 2, 7, 3, 5, 1 };
|
|
static const uae_u8 bpl_sequence_4[32] = { 4, 2, 3, 1 };
|
|
static const uae_u8 bpl_sequence_2[32] = { 2, 1 };
|
|
static const uae_u8 *bpl_sequence;
|
|
|
|
/* set currently active Agnus bitplane DMA sequence */
|
|
static void setup_fmodes(int hpos, uae_u16 con0)
|
|
{
|
|
switch (fmode & 3)
|
|
{
|
|
case 0:
|
|
fetchmode = 0;
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
fetchmode = 1;
|
|
break;
|
|
case 3:
|
|
fetchmode = 2;
|
|
break;
|
|
}
|
|
if (GET_RES_AGNUS(con0) != GET_RES_DENISE(con0)) {
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
bplcon0_res = GET_RES_AGNUS(con0);
|
|
toscr_res_old = -1;
|
|
update_toscr_vars();
|
|
|
|
bplcon0_planes = GET_PLANES(con0);
|
|
bplcon0_planes_limit = GET_PLANES_LIMIT(con0);
|
|
fetchunit = fetchunits[fetchmode * 4 + bplcon0_res];
|
|
fetchunit_mask = fetchunit - 1;
|
|
fetchstart_shift = fetchstarts[fetchmode * 4 + bplcon0_res];
|
|
fetchstart = 1 << fetchstart_shift;
|
|
fetchstart_mask = fetchstart - 1;
|
|
fm_maxplane_shift = fm_maxplanes[fetchmode * 4 + bplcon0_res];
|
|
fm_maxplane = 1 << fm_maxplane_shift;
|
|
switch (fm_maxplane) {
|
|
default:
|
|
case 8:
|
|
bpl_sequence = bpl_sequence_8;
|
|
break;
|
|
case 4:
|
|
bpl_sequence = bpl_sequence_4;
|
|
break;
|
|
case 2:
|
|
bpl_sequence = bpl_sequence_2;
|
|
break;
|
|
}
|
|
fetch_modulo_cycle = fetchunit - fetchstart;
|
|
fetchmode_size = 16 << fetchmode;
|
|
fetchmode_bytes = 2 << fetchmode;
|
|
fetchmode_mask = fetchmode_size - 1;
|
|
fetchmode_fmode_bpl = fmode & 3;
|
|
fetchmode_fmode_spr = (fmode >> 2) & 3;
|
|
compute_toscr_delay(bplcon1);
|
|
|
|
if (thisline_decision.plfleft < 0) {
|
|
thisline_decision.bplres = output_res(bplcon0_res);
|
|
thisline_decision.bplcon0 = con0;
|
|
thisline_decision.nr_planes = bplcon0_planes;
|
|
}
|
|
|
|
curr_diagram = cycle_diagram_table[fetchmode][bplcon0_res][bplcon0_planes_limit];
|
|
estimate_last_fetch_cycle(hpos);
|
|
toscr_nr_planes_agnus = bplcon0_planes;
|
|
if (isocs7planes()) {
|
|
toscr_nr_planes_agnus = 6;
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
|
|
static int REGPARAM2 custom_wput_1(int hpos, uaecptr addr, uae_u32 value, int noget);
|
|
|
|
static uae_u16 get_strobe_reg(int slot)
|
|
{
|
|
uae_u16 strobe = 0x1fe;
|
|
if (slot == 0) {
|
|
strobe = 0x3c;
|
|
if (vb_start_line > 1 && vpos < get_equ_vblank_endline() && vpos >= get_equ_vblank_startline()) {
|
|
strobe = 0x38;
|
|
} else if (vb_start_line > 1 || vb_end_line || vb_end_next_line) {
|
|
strobe = 0x3a;
|
|
}
|
|
} else if (slot == 1 && ecs_agnus && lol) {
|
|
strobe = 0x3e;
|
|
}
|
|
return strobe;
|
|
}
|
|
|
|
static uae_u16 fetch16(uaecptr p, int nr)
|
|
{
|
|
uae_u16 v;
|
|
if (aga_mode) {
|
|
// AGA always does 32-bit fetches, this is needed
|
|
// to emulate 64 pixel wide sprite side-effects.
|
|
uae_u32 vv = chipmem_lget_indirect(p & ~3);
|
|
if (p & 2) {
|
|
v = (uae_u16)vv;
|
|
if (nr >= 0) {
|
|
fetched_aga_spr[nr] = (v << 16) | v;
|
|
}
|
|
} else {
|
|
v = vv >> 16;
|
|
if (nr >= 0) {
|
|
fetched_aga_spr[nr] = vv;
|
|
}
|
|
}
|
|
} else {
|
|
v = chipmem_wget_indirect(p);
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(v);
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_read_value(v);
|
|
}
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 fetch32(uaecptr p)
|
|
{
|
|
uae_u32 v;
|
|
uaecptr pm = p & ~3;
|
|
if (p & 2) {
|
|
v = chipmem_lget_indirect(pm) & 0x0000ffff;
|
|
v |= v << 16;
|
|
} else if (fetchmode_fmode_bpl & 2) { // optimized (fetchmode_fmode & 3) == 2
|
|
v = chipmem_lget_indirect(pm) & 0xffff0000;
|
|
v |= v >> 16;
|
|
} else {
|
|
v = chipmem_lget_indirect(pm);
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value_long(v, p - pm);
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_read_value_wide(v, false);
|
|
}
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u64 fetch64(uaecptr p)
|
|
{
|
|
uae_u64 v;
|
|
uaecptr pm = p & ~7;
|
|
uaecptr pm1, pm2;
|
|
if (p & 4) {
|
|
pm1 = pm + 4;
|
|
pm2 = pm + 4;
|
|
} else {
|
|
pm1 = pm;
|
|
pm2 = pm + 4;
|
|
}
|
|
if (p & 2) {
|
|
uae_u32 v1 = chipmem_lget_indirect(pm1) & 0x0000ffff;
|
|
uae_u32 v2 = chipmem_lget_indirect(pm2) & 0x0000ffff;
|
|
v1 |= v1 << 16;
|
|
v2 |= v2 << 16;
|
|
v = (((uae_u64)v1) << 32) | v2;
|
|
} else {
|
|
v = ((uae_u64)chipmem_lget_indirect(pm1)) << 32;
|
|
v |= chipmem_lget_indirect(pm2);
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value_long((uae_u32)(v >> 32), p - pm1);
|
|
debug_getpeekdma_value_long((uae_u32)(v >> 0), p - pm2);
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_read_value_wide(v, true);
|
|
}
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
// Nasty chipset bitplane/sprite DMA conflict bug
|
|
//
|
|
// If bitplane DMA ends before sprite slots and last bitplane slot also has active sprite slot, sprite DMA is not stolen by bitplane DMA but sprite DMA conflicts with bitplane DMA:
|
|
// Target DMA custom register becomes AND of last bitplane BPLxDAT register and conflicting sprite register, for example 0x110 (BPL1DAT) & 0x168 (SPR5POS) = 0x100 (BPLCON0).
|
|
// Source DMA address becomes OR of bitplane and sprite pointer addresses.
|
|
// DMA reads word (or larger if FMODE>0) from OR'd DMA address and writes it to new target custom register.
|
|
// Bitplane +2/+4/+8 and possible modulo is added to OR'd DMA address.
|
|
// DMA address gets written back to both bitplane and sprite pointers.
|
|
// This is fully repeatable, no random side-effects noticed.
|
|
|
|
static int bplsprchipsetbug(int nr, int fm, int hpos)
|
|
{
|
|
uae_u16 breg = 0x110 + nr * 2;
|
|
uae_u16 sreg = sprbplconflict;
|
|
uae_u16 creg = breg & sreg;
|
|
int snum = (sreg - 0x140) / 8;
|
|
uaecptr px = bplpt[nr] | spr[snum].pt;
|
|
|
|
if (creg < 0x110 || creg >= 0x120) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(creg, px, hpos, vpos, DMARECORD_BITPLANE, nr);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
uae_u64 v;
|
|
uae_u16 v2;
|
|
if (fm == 0) {
|
|
v = fetch16(px, -1);
|
|
v2 = (uae_u16)v;
|
|
} else if (fm == 1) {
|
|
v = fetch32(px);
|
|
v2 = (uae_u16)(v >> 16);
|
|
} else {
|
|
v = fetch64(px);
|
|
v2 = v >> 48;
|
|
}
|
|
bplpt[nr] = px;
|
|
|
|
if (creg < 0x110 || creg >= 0x120) {
|
|
custom_wput_1(hpos, creg, v2, 1);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value_wide(v, false);
|
|
record_dma_read(breg, px, hpos, vpos, DMARECORD_BITPLANE, nr);
|
|
}
|
|
#endif
|
|
nr = -1;
|
|
|
|
} else {
|
|
|
|
uaecptr px = bplpt[nr] | spr[snum].pt;
|
|
spr[snum].pt = px;
|
|
bplpt[nr] = px;
|
|
|
|
nr = (creg - 0x110) / 2;
|
|
}
|
|
|
|
return nr;
|
|
}
|
|
|
|
bool get_ras_cas(uaecptr addr, int *rasp, int *casp)
|
|
{
|
|
int ras, cas;
|
|
bool ret = false;
|
|
if (aga_mode) {
|
|
ras = (((addr >> 12) & 127) << 1) | ((addr >> 9) & 1);
|
|
cas = (addr >> 0) & 0xff;
|
|
if ((addr >> 8) & 1) {
|
|
ras |= 0x100;
|
|
}
|
|
if ((addr >> 19) & 1) {
|
|
ras |= 0x200;
|
|
}
|
|
if ((addr >> 10) & 1) {
|
|
cas |= 0x100;
|
|
}
|
|
if ((addr >> 11) & 1) {
|
|
cas |= 0x200;
|
|
}
|
|
} else if (ecs_agnus && currprefs.chipmem.size > 0x100000) {
|
|
ras = (addr >> 9) & 0x1ff;
|
|
cas = (addr >> 1) & 0xff;
|
|
if ((addr >> 19) & 1) {
|
|
ras |= 0x200;
|
|
}
|
|
if ((addr >> 18) & 1) {
|
|
cas |= 0x100;
|
|
}
|
|
} else if (ecs_agnus) {
|
|
ras = (addr >> 9) & 0x1ff;
|
|
cas = (addr >> 1) & 0xff;
|
|
if ((addr >> 19) & 1) {
|
|
ret = true;
|
|
}
|
|
if ((addr >> 18) & 1) {
|
|
cas |= 0x100;
|
|
}
|
|
} else {
|
|
ras = (addr >> 1) & 0xff;
|
|
cas = (addr >> 9) & 0xff;
|
|
if ((addr >> 17) & 1) {
|
|
ras |= 0x100;
|
|
}
|
|
if ((addr >> 18) & 1) {
|
|
cas |= 0x100;
|
|
}
|
|
}
|
|
*rasp = ras;
|
|
*casp = cas;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static uaecptr update_refptr(int slot, int end, bool process, bool overwrite)
|
|
{
|
|
if (refptr_preupdated) {
|
|
refptr_preupdated = false;
|
|
refptr = refptr_p;
|
|
}
|
|
|
|
uaecptr rp = refptr;
|
|
for (int i = slot + 1; i < end; i++) {
|
|
if (!(refresh_handled_slot & (1 << i))) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
int hp = REFRESH_FIRST_HPOS + i * 2;
|
|
if (overwrite || (!overwrite && !record_dma_check(hp, vpos))) {
|
|
uae_u16 strobe = get_strobe_reg(i);
|
|
record_dma_clear(hp, vpos);
|
|
record_dma_read(strobe, rp & refmask, hp, vpos, DMARECORD_REFRESH, i);
|
|
record_dma_read_value(0xffff);
|
|
}
|
|
}
|
|
#endif
|
|
rp += ref_ras_add;
|
|
}
|
|
if (process) {
|
|
refresh_handled_slot |= 1 << i;
|
|
}
|
|
}
|
|
return rp;
|
|
}
|
|
|
|
// Strobe+refresh (always first, second possible if ECS and NTSC) slot conflict
|
|
static void fetch_strobe_conflict(int nr, int fm, int hpos, bool addmodulo)
|
|
{
|
|
int slot = (hpos - REFRESH_FIRST_HPOS) / 2;
|
|
static int warned1 = 30;
|
|
|
|
refptr = update_refptr(-1, slot, true, true);
|
|
refresh_handled_slot |= 1 << slot;
|
|
|
|
uae_u16 rreg = get_strobe_reg(slot);
|
|
uae_u16 breg = 0x110 + nr * 2;
|
|
uae_u16 creg = rreg & breg;
|
|
|
|
uaecptr bplptx = bplpt[nr];
|
|
uaecptr refptx = refptr;
|
|
uaecptr px = bplptx | refptx;
|
|
|
|
if (creg != rreg) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_clear(hpos, vpos);
|
|
record_dma_read(creg, px, hpos, vpos, DMARECORD_REFRESH, nr);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value_wide(0xffff, false);
|
|
record_dma_read(breg, px, hpos, vpos, DMARECORD_BITPLANE, nr);
|
|
}
|
|
#endif
|
|
|
|
if (addmodulo) {
|
|
// modulo is OR'd with REF_RAS_ADD
|
|
bplpt[nr] = px;
|
|
uae_s16 m1 = bpl1mod, m2 = bpl2mod;
|
|
uae_s16 m3 = bpl1mod_prev, m4 = bpl2mod_prev;
|
|
bpl1mod |= ref_ras_add;
|
|
bpl2mod |= ref_ras_add;
|
|
bpl1mod_prev |= ref_ras_add;
|
|
bpl2mod_prev |= ref_ras_add;
|
|
add_modulo(hpos, nr);
|
|
bpl1mod = m1; bpl2mod = m2;
|
|
bpl1mod_prev = m3; bpl2mod_prev = m4;
|
|
px = bplpt[nr];
|
|
} else {
|
|
px += ref_ras_add;
|
|
}
|
|
|
|
px &= refmask;
|
|
bplpt[nr] = px;
|
|
refptr = px;
|
|
update_refptr(slot, 4, false, true);
|
|
|
|
if (warned1 >= 0) {
|
|
write_log(_T("WARNING: BPL strobe refresh conflict, slot %d!\n"), slot);
|
|
warned1--;
|
|
}
|
|
|
|
// decide sprites before sprite offset change
|
|
decide_sprites(hpos + 1, true, false);
|
|
|
|
hstrobe_conflict = true;
|
|
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
|
|
// refresh only slot conflict
|
|
static void fetch_refresh_conflict(int nr, int fm, int hpos, bool addmodulo)
|
|
{
|
|
int slot = (hpos - REFRESH_FIRST_HPOS) / 2;
|
|
static int warned1 = 30;
|
|
|
|
refptr = update_refptr(-1, slot, true, true);
|
|
refresh_handled_slot |= 1 << slot;
|
|
|
|
uaecptr bplptx = bplpt[nr];
|
|
uaecptr refptx = refptr;
|
|
uaecptr px = bplptx | refptx;
|
|
uaecptr p = px;
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_clear(hpos, vpos);
|
|
record_dma_read(0x1fe, px, hpos, vpos, DMARECORD_REFRESH, slot);
|
|
}
|
|
#endif
|
|
|
|
if (addmodulo) {
|
|
// modulo is OR'd with REF_RAS_ADD
|
|
bplpt[nr] = px;
|
|
uae_s16 m1 = bpl1mod, m2 = bpl2mod;
|
|
uae_s16 m3 = bpl1mod_prev, m4 = bpl2mod_prev;
|
|
bpl1mod |= ref_ras_add;
|
|
bpl2mod |= ref_ras_add;
|
|
bpl1mod_prev |= ref_ras_add;
|
|
bpl2mod_prev |= ref_ras_add;
|
|
add_modulo(hpos, nr);
|
|
bpl1mod = m1; bpl2mod = m2;
|
|
bpl1mod_prev = m3; bpl2mod_prev = m4;
|
|
px = bplpt[nr];
|
|
} else {
|
|
px += ref_ras_add;
|
|
}
|
|
|
|
px &= refmask;
|
|
bplpt[nr] = px;
|
|
refptr = px;
|
|
update_refptr(slot, 4, false, true);
|
|
|
|
if (warned1 >= 0) {
|
|
write_log(_T("WARNING: BPL refresh conflict, slot %d!\n"), slot);
|
|
warned1--;
|
|
}
|
|
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x110 + nr * 2, p, hpos, vpos, DMARECORD_BITPLANE, nr);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(p, MW_MASK_BPL_0 << nr, 0x110 + nr * 2, 0xe0 + nr * 4);
|
|
}
|
|
#endif
|
|
|
|
switch (fm)
|
|
{
|
|
case 0:
|
|
{
|
|
fetched_aga[nr] = fetched[nr] = 0xffff;
|
|
regs.chipset_latch_rw = 0xffff;
|
|
last_custom_value = (uae_u16)regs.chipset_latch_rw;
|
|
break;
|
|
}
|
|
#ifdef AGA
|
|
case 1:
|
|
{
|
|
fetched_aga[nr] = 0xffffffff;
|
|
regs.chipset_latch_rw = (uae_u32)fetched_aga[nr];
|
|
last_custom_value = (uae_u16)regs.chipset_latch_rw;
|
|
fetched[nr] = (uae_u16)fetched_aga[nr];
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
fetched_aga[nr] = 0xffffffffffffffff;
|
|
regs.chipset_latch_rw = (uae_u32)fetched_aga[nr];
|
|
last_custom_value = (uae_u16)regs.chipset_latch_rw;
|
|
fetched[nr] = (uae_u16)fetched_aga[nr];
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static bool fetch(int nr, int fm, int hpos, bool addmodulo)
|
|
{
|
|
bool sprbplconflict = false;
|
|
bool stroberefbplconflict = false;
|
|
bool refbplconflict = false;
|
|
int snum = 0;
|
|
|
|
uae_u8 dmaslot = cycle_line_slot[hpos];
|
|
uaecptr p = bplpt[nr];
|
|
|
|
if (dmaslot > CYCLE_BITPLANE) {
|
|
if (dmaslot == CYCLE_STROBE) {
|
|
// strobe refresh conflict
|
|
fetch_strobe_conflict(nr, fm, hpos, addmodulo);
|
|
return false;
|
|
} else if (dmaslot == CYCLE_REFRESH) {
|
|
// refresh conflict
|
|
fetch_refresh_conflict(nr, fm, hpos, addmodulo);
|
|
return nr == 0;
|
|
} else if (dmaslot == CYCLE_MISC) {
|
|
// DMAL conflict
|
|
// AUDxDAT AND BPLxDAT = read-only register
|
|
// DSKDATR AND BLPxDAT = read-only register
|
|
// DSKDAT AND BPLxDAT = read-only register
|
|
return false;
|
|
}
|
|
} else {
|
|
if (hpos == sprbplconflict_hpos) {
|
|
nr = bplsprchipsetbug(nr, fm, hpos);
|
|
if (nr < 0) {
|
|
return false;
|
|
}
|
|
p = bplpt[nr];
|
|
}
|
|
|
|
// normal BPL cycle
|
|
cycle_line_slot[hpos] = CYCLE_BITPLANE;
|
|
|
|
bplpt[nr] += fetchmode_bytes;
|
|
bplptx[nr] += fetchmode_bytes;
|
|
if (addmodulo) {
|
|
add_modulo(hpos, nr);
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x110 + nr * 2, p, hpos, vpos, DMARECORD_BITPLANE, nr);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(p, MW_MASK_BPL_0 << nr, 0x110 + nr * 2, 0xe0 + nr * 4);
|
|
}
|
|
#endif
|
|
|
|
switch (fm)
|
|
{
|
|
case 0:
|
|
{
|
|
uae_u16 v = fetch16(p, nr);
|
|
fetched_aga[nr] = fetched[nr] = v;
|
|
regs.chipset_latch_rw = v;
|
|
last_custom_value = (uae_u16)regs.chipset_latch_rw;
|
|
break;
|
|
}
|
|
#ifdef AGA
|
|
case 1:
|
|
{
|
|
fetched_aga[nr] = fetch32(p);
|
|
regs.chipset_latch_rw = (uae_u32)fetched_aga[nr];
|
|
last_custom_value = (uae_u16)regs.chipset_latch_rw;
|
|
fetched[nr] = (uae_u16)fetched_aga[nr];
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
fetched_aga[nr] = fetch64(p);
|
|
regs.chipset_latch_rw = (uae_u32)fetched_aga[nr];
|
|
last_custom_value = (uae_u16)regs.chipset_latch_rw;
|
|
fetched[nr] = (uae_u16)fetched_aga[nr];
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return nr == 0;
|
|
}
|
|
|
|
STATIC_INLINE void toscr_3_ecs(int oddeven, int step, int nbits)
|
|
{
|
|
int shift = 16 - nbits;
|
|
|
|
for (int i = oddeven; i < toscr_nr_planes2 && nbits; i += step) {
|
|
outword[i] <<= nbits;
|
|
outword[i] |= todisplay2[i] >> shift;
|
|
todisplay2[i] <<= nbits;
|
|
}
|
|
}
|
|
|
|
#ifdef AGA
|
|
|
|
STATIC_INLINE void toscr_3_aga(int oddeven, int step, int nbits, int fm_size)
|
|
{
|
|
uae_u32 mask = 0xFFFF >> (16 - nbits);
|
|
int shift = fm_size - nbits;
|
|
|
|
if (shift == 64) {
|
|
for (int i = oddeven; i < toscr_nr_planes2; i += step) {
|
|
outword[i] <<= nbits;
|
|
todisplay2_aga[i] <<= nbits;
|
|
}
|
|
} else {
|
|
for (int i = oddeven; i < toscr_nr_planes2 && bits; i += step) {
|
|
outword[i] <<= nbits;
|
|
outword[i] |= (todisplay2_aga[i] >> shift) & mask;
|
|
todisplay2_aga[i] <<= nbits;
|
|
}
|
|
}
|
|
}
|
|
|
|
// very, very slow and unoptimized..
|
|
STATIC_INLINE void toscr_3_aga_hr(int oddeven, int step, int nbits, int fm_size_minusone)
|
|
{
|
|
for (int i = oddeven; i < toscr_nr_planes2; i += step) {
|
|
int subpix = out_subpix[oddeven];
|
|
for (int j = 0; j < nbits; j++) {
|
|
uae_u32 bit = (todisplay2_aga[i] >> fm_size_minusone) & 1;
|
|
outword64[i] <<= 1;
|
|
outword64[i] |= bit;
|
|
subpix++;
|
|
subpix &= toscr_res_pixels_mask_hr;
|
|
if (subpix == 0) {
|
|
todisplay2_aga[i] <<= 1;
|
|
}
|
|
}
|
|
}
|
|
out_subpix[oddeven] += nbits;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void toscr_2_0(int nbits) { toscr_3_ecs(0, 1, nbits); }
|
|
static void toscr_2_0_oe(int oddeven, int step, int nbits) { toscr_3_ecs(oddeven, step, nbits); }
|
|
#ifdef AGA
|
|
static void toscr_2_1(int nbits) { toscr_3_aga(0, 1, nbits, 32); }
|
|
static void toscr_2_1_oe(int oddeven, int step, int nbits) { toscr_3_aga(oddeven, step, nbits, 32); }
|
|
static void toscr_2_2(int nbits) { toscr_3_aga(0, 1, nbits, 64); }
|
|
static void toscr_2_2_oe(int oddeven, int step, int nbits) { toscr_3_aga(oddeven, step, nbits, 64); }
|
|
|
|
static void toscr_2_0_hr(int nbits) { toscr_3_aga_hr(0, 1, nbits, 16 - 1); }
|
|
static void toscr_2_0_hr_oe(int oddeven, int step, int nbits) { toscr_3_aga_hr(oddeven, step, nbits, 16 - 1); }
|
|
static void toscr_2_1_hr(int nbits) { toscr_3_aga_hr(0, 1, nbits, 32 - 1); }
|
|
static void toscr_2_1_hr_oe(int oddeven, int step, int nbits) { toscr_3_aga_hr(oddeven, step, nbits, 32 - 1); }
|
|
static void toscr_2_2_hr(int nbits) { toscr_3_aga_hr(0, 1, nbits, 64 - 1); }
|
|
static void toscr_2_2_hr_oe(int oddeven, int step, int nbits) { toscr_3_aga_hr(oddeven, step, nbits, 64 - 1); }
|
|
#endif
|
|
|
|
STATIC_INLINE void do_tosrc(int oddeven, int step, int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0:
|
|
if (step == 2)
|
|
toscr_2_0_oe(oddeven, step, nbits);
|
|
else
|
|
toscr_2_0(nbits);
|
|
break;
|
|
#ifdef AGA
|
|
case 1:
|
|
if (step == 2)
|
|
toscr_2_1_oe(oddeven, step, nbits);
|
|
else
|
|
toscr_2_1(nbits);
|
|
break;
|
|
case 2:
|
|
if (step == 2)
|
|
toscr_2_2_oe(oddeven, step, nbits);
|
|
else
|
|
toscr_2_2(nbits);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef AGA
|
|
STATIC_INLINE void do_tosrc_hr(int oddeven, int step, int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0:
|
|
if (step == 2)
|
|
toscr_2_0_hr_oe(oddeven, step, nbits);
|
|
else
|
|
toscr_2_0_hr(nbits);
|
|
break;
|
|
case 1:
|
|
if (step == 2)
|
|
toscr_2_1_hr_oe(oddeven, step, nbits);
|
|
else
|
|
toscr_2_1_hr(nbits);
|
|
break;
|
|
case 2:
|
|
if (step == 2)
|
|
toscr_2_2_hr_oe(oddeven, step, nbits);
|
|
else
|
|
toscr_2_2_hr(nbits);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
STATIC_INLINE void do_delays_3_ecs(int nbits)
|
|
{
|
|
int delaypos = delay_cycles;
|
|
for (int oddeven = 0; oddeven < 2; oddeven++) {
|
|
int delay = toscr_delay_shifter[oddeven];
|
|
int diff = ((delay - delaypos) & shifter_mask) >> toscr_res_pixels_shift;
|
|
int nbits2 = nbits;
|
|
if (nbits2 > diff) {
|
|
do_tosrc(oddeven, 2, diff, 0);
|
|
nbits2 -= diff;
|
|
if (todisplay_fetched & (oddeven + 1)) {
|
|
for (int i = oddeven; i < toscr_nr_planes_shifter; i += 2) {
|
|
todisplay2[i] = todisplay[i];
|
|
}
|
|
todisplay_fetched -= oddeven + 1;
|
|
}
|
|
}
|
|
if (nbits2) {
|
|
do_tosrc(oddeven, 2, nbits2, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void do_delays_fast_3_ecs(int nbits)
|
|
{
|
|
int delaypos = delay_cycles;
|
|
int delay = toscr_delay_shifter[0];
|
|
int diff = ((delay - delaypos) & shifter_mask) >> toscr_res_pixels_shift;
|
|
int nbits2 = nbits;
|
|
if (nbits2 > diff) {
|
|
do_tosrc(0, 1, diff, 0);
|
|
nbits2 -= diff;
|
|
if (todisplay_fetched) {
|
|
memcpy(todisplay2, todisplay, toscr_nr_planes_shifter * sizeof(uae_u32));
|
|
todisplay_fetched = 0;
|
|
}
|
|
}
|
|
if (nbits2) {
|
|
do_tosrc(0, 1, nbits2, 0);
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void do_delays_3_aga(int nbits, int fm)
|
|
{
|
|
int delaypos = delay_cycles;
|
|
for (int oddeven = 0; oddeven < 2; oddeven++) {
|
|
int delay = toscr_delay_shifter[oddeven];
|
|
int diff = ((delay - delaypos) & shifter_mask) >> toscr_res_pixels_shift;
|
|
int nbits2 = nbits;
|
|
if (nbits2 > diff) {
|
|
do_tosrc(oddeven, 2, diff, fm);
|
|
nbits2 -= diff;
|
|
if (todisplay_fetched & (oddeven + 1)) {
|
|
for (int i = oddeven; i < toscr_nr_planes_shifter; i += 2) {
|
|
todisplay2_aga[i] = todisplay_aga[i];
|
|
}
|
|
todisplay_fetched -= oddeven + 1;
|
|
}
|
|
}
|
|
if (nbits2) {
|
|
do_tosrc(oddeven, 2, nbits2, fm);
|
|
}
|
|
}
|
|
}
|
|
|
|
static const uae_u64 firstpixel[] = { 0x8000, 0x80000000, 0x8000000000000000, 0x8000000000000000 };
|
|
|
|
STATIC_INLINE void do_delays_fast_3_aga(int nbits, int fm)
|
|
{
|
|
int delaypos = delay_cycles;
|
|
int delay = toscr_delay_shifter[0];
|
|
int diff = ((delay - delaypos) & shifter_mask) >> toscr_res_pixels_shift;
|
|
int nbits2 = nbits;
|
|
if (nbits2 > diff) {
|
|
do_tosrc(0, 1, diff, fm);
|
|
nbits2 -= diff;
|
|
if (todisplay_fetched) {
|
|
int i;
|
|
for (i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
todisplay2_aga[i] = todisplay_aga[i];
|
|
todisplay2_lastbit[i] = todisplay_aga[i] & 1;
|
|
}
|
|
todisplay_fetched = 0;
|
|
// Chipset bug. if bitplane gets disabled mid-scanline and its last pixel in shifter
|
|
// is one, it gets duplicated.
|
|
if (toscr_nr_changed) {
|
|
while (i < toscr_nr_planes2) {
|
|
if (todisplay2_lastbit[i]) {
|
|
todisplay2_aga[i] = firstpixel[fm];
|
|
todisplay2_lastbit[i] = 0;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nbits2) {
|
|
do_tosrc(0, 1, nbits2, fm);
|
|
}
|
|
}
|
|
|
|
static bool todisplay_copy_hr_oddeven(int oddeven, int fm)
|
|
{
|
|
if (todisplay_fetched & (oddeven + 1)) {
|
|
int i;
|
|
for (i = oddeven; i < toscr_nr_planes_shifter; i += 2) {
|
|
todisplay2_aga[i] = todisplay_aga[i];
|
|
todisplay2_lastbit[i] = todisplay_aga[i] & 1;
|
|
}
|
|
todisplay_fetched -= oddeven + 1;
|
|
out_subpix[oddeven] = 0;
|
|
if (toscr_nr_changed) {
|
|
while (i < toscr_nr_planes2) {
|
|
if (todisplay2_lastbit[i]) {
|
|
todisplay2_aga[i] = firstpixel[fm];
|
|
todisplay2_lastbit[i] = 0;
|
|
}
|
|
i += 2;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
STATIC_INLINE void do_delays_3_aga_hr2(int nbits, int fm)
|
|
{
|
|
int delaypos = delay_cycles;
|
|
for (int oddeven = 0; oddeven < 2; oddeven++) {
|
|
int delay = toscr_delay_shifter[oddeven];
|
|
int diff = ((delay - delaypos) & shifter_mask) >> toscr_res_pixels_shift_hr;
|
|
int nbits2 = nbits;
|
|
if (nbits2 > diff) {
|
|
do_tosrc_hr(oddeven, 2, diff, fm);
|
|
nbits2 -= diff;
|
|
todisplay_copy_hr_oddeven(oddeven, fm);
|
|
}
|
|
if (nbits2) {
|
|
do_tosrc_hr(oddeven, 2, nbits2, fm);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is very very slow. But really rarely needed.
|
|
static void pull_toscr_output_bits(int nbits, int planes, uae_u32 *ptrs)
|
|
{
|
|
uae_u64 mask = (1 << nbits) - 1;
|
|
if (out_nbits >= nbits) {
|
|
for (int i = 0; i < planes; i++) {
|
|
ptrs[i] = (uae_u32)(outword64[i] & mask);
|
|
}
|
|
return;
|
|
}
|
|
int tbits = nbits - out_nbits;
|
|
uae_u64 nmask = (1 << out_nbits) - 1;
|
|
for (int i = 0; i < planes; i++) {
|
|
ptrs[i] = 0;
|
|
uae_u64 v = outword64_extra[i];
|
|
ptrs[i] = (uae_u32)((v << out_nbits) & mask);
|
|
ptrs[i] |= outword64[i] & nmask;
|
|
}
|
|
}
|
|
|
|
static void push_toscr_output_bits(int nbits, int planes, uae_u32 *ptrs)
|
|
{
|
|
uae_u64 mask = (1 << nbits) - 1;
|
|
if (out_nbits >= nbits) {
|
|
for (int i = 0; i < planes; i++) {
|
|
outword64[i] &= ~mask;
|
|
outword64[i] |= ptrs[i];
|
|
}
|
|
return;
|
|
}
|
|
int tbits = nbits - out_nbits;
|
|
uae_u64 tmask = (1 << tbits) - 1;
|
|
uae_u64 nmask = (1 << out_nbits) - 1;
|
|
for (int i = 0; i < planes; i++) {
|
|
uae_u64 v = outword64_extra[i];
|
|
v &= ~tmask;
|
|
v |= (ptrs[i] >> out_nbits) & tmask;
|
|
outword64_extra[i] = v;
|
|
outword64[i] &= ~nmask;
|
|
outword64[i] |= ptrs[i] & nmask;
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void toscr_1_hr_nbits(void)
|
|
{
|
|
if (out_offs < MAX_WORDS_PER_LINE * 2 / 4 - 1) {
|
|
uae_u8 *dataptr = line_data[next_lineno] + out_offs * 4;
|
|
for (int i = 0; i < toscr_nr_planes2; i++) {
|
|
uae_u64 *dataptr64 = (uae_u64 *)dataptr;
|
|
uae_u64 v = outword64[i];
|
|
// uae_u64 v = outword64_extra[i];
|
|
// outword64_extra[i] = outword64[i];
|
|
v = (v >> 32) | (v << 32);
|
|
if (thisline_changed || *dataptr64 != v) {
|
|
thisline_changed = 1;
|
|
*dataptr64 = v;
|
|
}
|
|
dataptr += MAX_WORDS_PER_LINE * 2;
|
|
}
|
|
out_offs += 2;
|
|
}
|
|
out_nbits = 0;
|
|
}
|
|
|
|
#define TOSCR_SPC_LORES 1
|
|
#define TOSCR_SPC_HIRES 2
|
|
#define TOSCR_SPC_LORES_END 16
|
|
#define TOSCR_SPC_HIRES_END 32
|
|
#define TOSCR_SPC_LORES_START 64
|
|
#define TOSCR_SPC_HIRES_START 128
|
|
#define TOSCR_SPC_CLEAR 256
|
|
#define TOSCR_SPC_CLEAR2 512
|
|
#define TOSCR_SPC_6HIRESTOLORES 1024
|
|
#define TOSCR_SPC_SETHIRES 2048
|
|
#define TOSCR_SPC_SETLORES 4096
|
|
#define TOSCR_SPC_DUAL 8192
|
|
#define TOSCR_SPC_MARK 16384
|
|
#define TOSCR_SPC_SKIP 32768
|
|
|
|
static const uae_u16 toscr_spc_aga_lores_to_hires[] =
|
|
{
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_HIRES_END,
|
|
0, 0, 0, 0
|
|
};
|
|
static const uae_u16 toscr_spc_aga_hires_to_lores[] =
|
|
{
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
0, 0, 0, 0
|
|
};
|
|
|
|
static const uae_u16 toscr_spc_ecs_lores_to_hires[] =
|
|
{
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
0, 0, 0, 0
|
|
};
|
|
static const uae_u16 toscr_spc_ecs_hires_to_lores[] =
|
|
{
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END | TOSCR_SPC_6HIRESTOLORES,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
0, 0, 0, 0
|
|
};
|
|
|
|
#if 0
|
|
static const uae_u16 toscr_spc_ocs_lores_to_hires[] =
|
|
{
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR | TOSCR_SPC_DUAL,
|
|
|
|
0, 0, 0, 0
|
|
};
|
|
|
|
static const uae_u16 toscr_spc_ocs_hires_to_lores[] =
|
|
{
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_LORES_START | TOSCR_SPC_LORES_END,
|
|
0, 0, 0, 0
|
|
};
|
|
#else
|
|
|
|
static const uae_u16 toscr_spc_ocs_lores_to_hires[] =
|
|
{
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END | TOSCR_SPC_CLEAR,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
TOSCR_SPC_LORES | TOSCR_SPC_HIRES_START | TOSCR_SPC_HIRES_END,
|
|
0, 0, 0, 0
|
|
};
|
|
|
|
static const uae_u16 toscr_spc_ocs_hires_to_lores[] =
|
|
{
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
TOSCR_SPC_HIRES | TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES_END,
|
|
0, 0, 0, 0
|
|
};
|
|
#endif
|
|
|
|
|
|
static const uae_u16 *toscr_special_skip_ptr = NULL;
|
|
|
|
static int process_special_pixel(int delaypos, int fm, uae_u16 cmd)
|
|
{
|
|
int ret = 0;
|
|
int delay1 = (bplcon1 & 0x0f) | ((bplcon1 & 0x0c00) >> 6);
|
|
int delay2 = ((bplcon1 >> 4) & 0x0f) | (((bplcon1 >> 4) & 0x0c00) >> 6);
|
|
int delaymask, shiftmask;
|
|
if (cmd & TOSCR_SPC_LORES) {
|
|
delaymask = fetchmode_mask >> 0;
|
|
shiftmask = 63;
|
|
} else {
|
|
delaymask = fetchmode_mask >> 1;
|
|
shiftmask = 31;
|
|
}
|
|
if (cmd & TOSCR_SPC_HIRES_START) {
|
|
toscr_res_pixels_mask_hr = 1 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
if (cmd & TOSCR_SPC_LORES_START) {
|
|
toscr_res_pixels_mask_hr = 3 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
int shifter[2];
|
|
shifter[0] = (delay1 & delaymask) << LORES_TO_SHRES_SHIFT;
|
|
shifter[1] = (delay2 & delaymask) << LORES_TO_SHRES_SHIFT;
|
|
for (int oddeven = 0; oddeven < 2; oddeven++) {
|
|
int delay = shifter[oddeven];
|
|
int diff = ((delay - delaypos) & shiftmask) >> toscr_res_pixels_shift_hr;
|
|
if (diff == 0) {
|
|
if (todisplay_copy_hr_oddeven(oddeven, fm)) {
|
|
ret |= 1 << oddeven;
|
|
}
|
|
}
|
|
do_tosrc_hr(oddeven, 2, 1, fm);
|
|
}
|
|
if (cmd & TOSCR_SPC_LORES_END) {
|
|
toscr_res_pixels_mask_hr = 3 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
if (cmd & TOSCR_SPC_HIRES_END) {
|
|
toscr_res_pixels_mask_hr = 1 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void flush_display(int fm);
|
|
STATIC_INLINE void do_delays_3_aga_hr(int nbits, int fm)
|
|
{
|
|
#if 0
|
|
if (0 && (toscr_special_skip_ptr == toscr_spc_ocs_hires_to_lores || toscr_special_skip_ptr == toscr_spc_ocs_lores_to_hires)) {
|
|
int difsize = 0;
|
|
if (toscr_special_res_change_count > 0) {
|
|
int delaypos = delay_cycles;
|
|
while (nbits > 0) {
|
|
if (toscr_special_res_change_count2 > 0) {
|
|
toscr_special_res_change_count2--;
|
|
}
|
|
uae_u16 cmd = 0;
|
|
if (toscr_special_skip_ptr == toscr_spc_ocs_hires_to_lores) {
|
|
if (toscr_special_res_change_count2 > 0) {
|
|
cmd = TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES | TOSCR_SPC_LORES_END;
|
|
} else {
|
|
cmd = TOSCR_SPC_LORES_START | TOSCR_SPC_LORES | TOSCR_SPC_LORES_END;
|
|
}
|
|
} else if (toscr_special_skip_ptr == toscr_spc_ocs_lores_to_hires) {
|
|
if (toscr_special_res_change_count2 > 0) {
|
|
cmd = TOSCR_SPC_LORES_START | TOSCR_SPC_LORES | TOSCR_SPC_HIRES_END;
|
|
} else {
|
|
cmd = TOSCR_SPC_HIRES_START | TOSCR_SPC_LORES | TOSCR_SPC_HIRES_END;
|
|
}
|
|
}
|
|
if (process_special_pixel(delaypos, fm, cmd)) {
|
|
toscr_special_res_change_count_done = toscr_special_res_change_count;
|
|
}
|
|
delaypos += 1 << toscr_res_pixels_shift_hr;
|
|
nbits--;
|
|
difsize++;
|
|
toscr_special_res_change_count--;
|
|
if (toscr_special_res_change_count == 0) {
|
|
uae_u32 ptrs[MAX_PLANES];
|
|
if (toscr_special_skip_ptr == toscr_spc_ocs_hires_to_lores) {
|
|
int c = 8;
|
|
pull_toscr_output_bits(c, toscr_nr_planes_shifter, ptrs);
|
|
for(int i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
uae_u32 v = ptrs[i];
|
|
for (int j = 0; j < c; j += 2) {
|
|
int m1 = 1 << (j + 1);
|
|
int m2 = 1 << (j + 0);
|
|
if (v & m1) {
|
|
v |= m2;
|
|
} else {
|
|
v &= ~m2;
|
|
}
|
|
}
|
|
ptrs[i] = v;
|
|
}
|
|
push_toscr_output_bits(c, toscr_nr_planes_shifter, ptrs);
|
|
} else if (toscr_special_skip_ptr == toscr_spc_ocs_lores_to_hires) {
|
|
int c = 8;
|
|
pull_toscr_output_bits(c, toscr_nr_planes_shifter, ptrs);
|
|
for (int i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
uae_u32 v = ptrs[i];
|
|
for (int j = 0; j < c; j += 2) {
|
|
int m1 = 1 << (j + 0);
|
|
v &= ~m1;
|
|
}
|
|
ptrs[i] = v;
|
|
//ptrs[i] = 0x5555;
|
|
}
|
|
push_toscr_output_bits(c, toscr_nr_planes_shifter, ptrs);
|
|
}
|
|
toscr_special_skip_ptr = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (nbits > 0) {
|
|
delay_cycles += difsize * (1 << toscr_res_pixels_shift_hr);
|
|
do_delays_3_aga_hr2(nbits, fm);
|
|
delay_cycles -= difsize * (1 << toscr_res_pixels_shift_hr);
|
|
}
|
|
}
|
|
#endif
|
|
if (toscr_special_skip_ptr) {
|
|
int difsize = 0;
|
|
int delaypos = delay_cycles;
|
|
while (nbits > 0) {
|
|
uae_u16 cmd = *toscr_special_skip_ptr;
|
|
if (cmd & TOSCR_SPC_SKIP) {
|
|
delay_cycles += difsize * (1 << toscr_res_pixels_shift_hr);
|
|
do_delays_3_aga_hr2(1, fm);
|
|
delay_cycles -= difsize * (1 << toscr_res_pixels_shift_hr);
|
|
delaypos += 1 << toscr_res_pixels_shift_hr;
|
|
nbits--;
|
|
difsize++;
|
|
toscr_special_skip_ptr++;
|
|
} else {
|
|
uae_u64 toda[MAX_PLANES];
|
|
if (cmd & TOSCR_SPC_DUAL) {
|
|
for (int i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
toda[i] = todisplay2_aga[i];
|
|
}
|
|
}
|
|
if (cmd & TOSCR_SPC_CLEAR) {
|
|
for (int i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
todisplay2_aga[i] &= ~0x8000;
|
|
}
|
|
}
|
|
if (cmd & TOSCR_SPC_CLEAR2) {
|
|
for (int i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
todisplay2_aga[i] &= 0xff00;
|
|
}
|
|
}
|
|
if ((cmd & TOSCR_SPC_MARK) && (vpos & 1)) {
|
|
for (int i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
todisplay2_aga[i] ^= 0x8000;
|
|
}
|
|
}
|
|
process_special_pixel(delaypos, fm, cmd);
|
|
#if 0
|
|
int delay1 = (bplcon1 & 0x0f) | ((bplcon1 & 0x0c00) >> 6);
|
|
int delay2 = ((bplcon1 >> 4) & 0x0f) | (((bplcon1 >> 4) & 0x0c00) >> 6);
|
|
int delaymask, shiftmask;
|
|
if (cmd & TOSCR_SPC_LORES) {
|
|
delaymask = fetchmode_mask >> 0;
|
|
shiftmask = 63;
|
|
} else {
|
|
delaymask = fetchmode_mask >> 1;
|
|
shiftmask = 31;
|
|
}
|
|
if (cmd & TOSCR_SPC_HIRES_START) {
|
|
toscr_res_pixels_mask_hr = 1 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
if (cmd & TOSCR_SPC_LORES_START) {
|
|
toscr_res_pixels_mask_hr = 3 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
int shifter[2];
|
|
shifter[0] = (delay1 & delaymask) << LORES_TO_SHRES_SHIFT;
|
|
shifter[1] = (delay2 & delaymask) << LORES_TO_SHRES_SHIFT;
|
|
for (int oddeven = 0; oddeven < 2; oddeven++) {
|
|
int delay = shifter[oddeven];
|
|
int diff = ((delay - delaypos) & shiftmask) >> toscr_res_pixels_shift_hr;
|
|
if (diff == 0) {
|
|
todisplay_copy_hr_oddeven(oddeven, fm);
|
|
}
|
|
do_tosrc_hr(oddeven, 2, 1, fm);
|
|
}
|
|
if (cmd & TOSCR_SPC_LORES_END) {
|
|
toscr_res_pixels_mask_hr = 3 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
if (cmd & TOSCR_SPC_HIRES_END) {
|
|
toscr_res_pixels_mask_hr = 1 >> toscr_res_pixels_shift_hr;
|
|
}
|
|
#endif
|
|
if (cmd & TOSCR_SPC_DUAL) {
|
|
for (int i = 0; i < toscr_nr_planes_shifter; i++) {
|
|
todisplay2_aga[i] = toda[i];
|
|
}
|
|
}
|
|
|
|
delaypos += 1 << toscr_res_pixels_shift_hr;
|
|
nbits--;
|
|
difsize++;
|
|
|
|
int sh = 1 << toscr_res_pixels_shift_hr;
|
|
toscr_special_skip_ptr += sh;
|
|
if (*toscr_special_skip_ptr == 0) {
|
|
toscr_special_skip_ptr = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (nbits > 0) {
|
|
delay_cycles += difsize * (1 << toscr_res_pixels_shift_hr);
|
|
do_delays_3_aga_hr2(nbits, fm);
|
|
delay_cycles -= difsize * (1 << toscr_res_pixels_shift_hr);
|
|
}
|
|
} else {
|
|
do_delays_3_aga_hr2(nbits, fm);
|
|
}
|
|
}
|
|
|
|
|
|
STATIC_INLINE void do_delays_fast_3_aga_hr(int nbits, int fm)
|
|
{
|
|
int delaypos = delay_cycles;
|
|
int delay = toscr_delay_shifter[0];
|
|
int diff = ((delay - delaypos) & shifter_mask) >> toscr_res_pixels_shift_hr;
|
|
int nbits2 = nbits;
|
|
if (nbits2 > diff) {
|
|
do_tosrc_hr(0, 1, diff, fm);
|
|
nbits2 -= diff;
|
|
if (todisplay_fetched) {
|
|
memcpy(todisplay2_aga, todisplay_aga, toscr_nr_planes_shifter * sizeof(uae_u64));
|
|
todisplay_fetched = 0;
|
|
out_subpix[0] = 0;
|
|
out_subpix[1] = 0;
|
|
}
|
|
}
|
|
if (nbits2) {
|
|
do_tosrc_hr(0, 1, nbits2, fm);
|
|
}
|
|
}
|
|
|
|
static void do_delays_2_0(int nbits) { do_delays_3_ecs(nbits); }
|
|
#ifdef AGA
|
|
static void do_delays_2_1(int nbits) { do_delays_3_aga(nbits, 1); }
|
|
static void do_delays_2_2(int nbits) { do_delays_3_aga(nbits, 2); }
|
|
|
|
static void do_delays_2_0_hr(int nbits) { do_delays_3_aga_hr(nbits, 0); }
|
|
static void do_delays_2_1_hr(int nbits) { do_delays_3_aga_hr(nbits, 1); }
|
|
static void do_delays_2_2_hr(int nbits) { do_delays_3_aga_hr(nbits, 2); }
|
|
static void do_delays_fast_2_0_hr(int nbits) { do_delays_fast_3_aga_hr(nbits, 0); }
|
|
static void do_delays_fast_2_1_hr(int nbits) { do_delays_fast_3_aga_hr(nbits, 1); }
|
|
static void do_delays_fast_2_2_hr(int nbits) { do_delays_fast_3_aga_hr(nbits, 2); }
|
|
#endif
|
|
|
|
static void do_delays_fast_2_0(int nbits) { do_delays_fast_3_ecs(nbits); }
|
|
#ifdef AGA
|
|
static void do_delays_fast_2_1(int nbits) { do_delays_fast_3_aga(nbits, 1); }
|
|
static void do_delays_fast_2_2(int nbits) { do_delays_fast_3_aga(nbits, 2); }
|
|
#endif
|
|
|
|
|
|
// slower version, odd and even delays are different or crosses maxhpos
|
|
STATIC_INLINE void do_delays(int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0:
|
|
do_delays_2_0(nbits);
|
|
break;
|
|
#ifdef AGA
|
|
case 1:
|
|
do_delays_2_1(nbits);
|
|
break;
|
|
case 2:
|
|
do_delays_2_2(nbits);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// common optimized case: odd delay == even delay
|
|
STATIC_INLINE void do_delays_fast(int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0:
|
|
do_delays_fast_2_0(nbits);
|
|
break;
|
|
#ifdef AGA
|
|
case 1:
|
|
do_delays_fast_2_1(nbits);
|
|
break;
|
|
case 2:
|
|
do_delays_fast_2_2(nbits);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef AGA
|
|
STATIC_INLINE void do_delays_hr(int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0:
|
|
do_delays_2_0_hr(nbits);
|
|
break;
|
|
case 1:
|
|
do_delays_2_1_hr(nbits);
|
|
break;
|
|
case 2:
|
|
do_delays_2_2_hr(nbits);
|
|
break;
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void do_delays_fast_hr(int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0:
|
|
do_delays_fast_2_0_hr(nbits);
|
|
break;
|
|
case 1:
|
|
do_delays_fast_2_1_hr(nbits);
|
|
break;
|
|
case 2:
|
|
do_delays_fast_2_2_hr(nbits);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
STATIC_INLINE void do_delays_null(int nbits)
|
|
{
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
outword[i] <<= nbits;
|
|
outword64[i] <<= nbits;
|
|
}
|
|
}
|
|
|
|
static uae_u32 todisplay2_saved[MAX_PLANES], todisplay_saved[MAX_PLANES];
|
|
static uae_u64 todisplay2_aga_saved[MAX_PLANES], todisplay_aga_saved[MAX_PLANES];
|
|
static uae_u16 todisplay_fetched_saved;
|
|
|
|
STATIC_INLINE void toscr_special(int nbits, int fm)
|
|
{
|
|
int total = nbits << toscr_res_pixels_shift;
|
|
bool slow = false;
|
|
if (delay_cycles2 <= delay_lastcycle[lol] && delay_cycles2 + total > delay_lastcycle[lol]) {
|
|
slow = true;
|
|
}
|
|
if (delay_cycles2 <= delay_hsynccycle && delay_cycles2 + total > delay_hsynccycle) {
|
|
slow = true;
|
|
}
|
|
if (slow) {
|
|
for (int i = 0; i < nbits; i++) {
|
|
if (toscr_hend == 0 && delay_cycles2 == delay_lastcycle[lol]) {
|
|
toscr_hend = 1;
|
|
if (!hstrobe_conflict) {
|
|
delay_cycles = 2 << LORES_TO_SHRES_SHIFT;
|
|
}
|
|
}
|
|
if (delay_cycles2 == delay_hsynccycle) {
|
|
toscr_hend = 2;
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
todisplay_saved[i] = todisplay[i];
|
|
todisplay_aga_saved[i] = todisplay_aga[i];
|
|
todisplay2_saved[i] = todisplay2[i];
|
|
todisplay2_aga_saved[i] = todisplay2_aga[i];
|
|
}
|
|
todisplay_fetched_saved = todisplay_fetched;
|
|
}
|
|
if (toscr_hend <= 1) {
|
|
if (!toscr_scanline_complex_bplcon1) {
|
|
do_delays_fast(1, fm);
|
|
} else {
|
|
do_delays(1, fm);
|
|
}
|
|
} else {
|
|
do_delays_null(1);
|
|
}
|
|
delay_cycles += toscr_res_pixels;
|
|
delay_cycles2 += toscr_res_pixels;
|
|
}
|
|
} else {
|
|
if (toscr_hend <= 1) {
|
|
if (!toscr_scanline_complex_bplcon1) {
|
|
do_delays_fast(nbits, fm);
|
|
} else {
|
|
do_delays(nbits, fm);
|
|
}
|
|
} else {
|
|
do_delays_null(nbits);
|
|
}
|
|
delay_cycles += total;
|
|
delay_cycles2 += total;
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void toscr_special_hr(int nbits, int fm)
|
|
{
|
|
int total = nbits << toscr_res_pixels_shift_hr;
|
|
bool slow = false;
|
|
if (delay_cycles2 <= delay_lastcycle[lol] && delay_cycles2 + total > delay_lastcycle[lol]) {
|
|
slow = true;
|
|
}
|
|
if (delay_cycles2 <= delay_hsynccycle && delay_cycles2 + total > delay_hsynccycle) {
|
|
slow = true;
|
|
}
|
|
if (slow) {
|
|
for (int i = 0; i < nbits; i++) {
|
|
if (toscr_hend == 0 && delay_cycles2 == delay_lastcycle[lol]) {
|
|
toscr_hend = 1;
|
|
if (!hstrobe_conflict) {
|
|
delay_cycles = 2 << LORES_TO_SHRES_SHIFT;
|
|
}
|
|
}
|
|
if (delay_cycles2 == delay_hsynccycle) {
|
|
toscr_hend = 2;
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
todisplay_saved[i] = todisplay[i];
|
|
todisplay_aga_saved[i] = todisplay_aga[i];
|
|
todisplay2_saved[i] = todisplay2[i];
|
|
todisplay2_aga_saved[i] = todisplay2_aga[i];
|
|
}
|
|
todisplay_fetched_saved = todisplay_fetched;
|
|
}
|
|
if (toscr_hend <= 1) {
|
|
if (!toscr_scanline_complex_bplcon1) {
|
|
do_delays_fast_hr(1, fm);
|
|
} else {
|
|
do_delays_hr(1, fm);
|
|
}
|
|
} else {
|
|
do_delays_null(1);
|
|
}
|
|
delay_cycles += toscr_res_pixels_hr;
|
|
delay_cycles2 += toscr_res_pixels_hr;
|
|
}
|
|
} else {
|
|
if (toscr_hend <= 1) {
|
|
if (!toscr_scanline_complex_bplcon1) {
|
|
do_delays_fast_hr(nbits, fm);
|
|
} else {
|
|
do_delays_hr(nbits, fm);
|
|
}
|
|
} else {
|
|
do_delays_null(nbits);
|
|
}
|
|
delay_cycles += total;
|
|
delay_cycles2 += total;
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void toscr_1(int nbits, int fm)
|
|
{
|
|
int total = nbits << toscr_res_pixels_shift;
|
|
if (toscr_hend || delay_cycles2 + total > delay_lastcycle[lol]) {
|
|
toscr_special(nbits, fm);
|
|
} else if (!toscr_scanline_complex_bplcon1) {
|
|
// Most common case.
|
|
do_delays_fast(nbits, fm);
|
|
delay_cycles += total;
|
|
delay_cycles2 += total;
|
|
} else {
|
|
// if scanline has at least one complex case (odd != even)
|
|
// all possible remaining odd == even cases in same scanline
|
|
// must also use complex case routine.
|
|
do_delays(nbits, fm);
|
|
delay_cycles += total;
|
|
delay_cycles2 += total;
|
|
}
|
|
|
|
out_nbits += nbits;
|
|
if (out_nbits == 32) {
|
|
if (out_offs < MAX_WORDS_PER_LINE * 2 / 4) {
|
|
uae_u8 *dataptr = line_data[next_lineno] + out_offs * 4;
|
|
for (int i = 0; i < toscr_nr_planes2; i++) {
|
|
uae_u32 *dataptr32 = (uae_u32*)dataptr;
|
|
if (*dataptr32 != outword[i]) {
|
|
thisline_changed = 1;
|
|
*dataptr32 = outword[i];
|
|
}
|
|
dataptr += MAX_WORDS_PER_LINE * 2;
|
|
}
|
|
out_offs++;
|
|
}
|
|
out_nbits = 0;
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void toscr_1_hr(int nbits, int fm)
|
|
{
|
|
toscr_special_hr(nbits, fm);
|
|
|
|
out_nbits += nbits;
|
|
if (out_nbits == 64) {
|
|
toscr_1_hr_nbits();
|
|
}
|
|
}
|
|
|
|
static void toscr_fm0(int);
|
|
static void toscr_fm1(int);
|
|
static void toscr_fm2(int);
|
|
static void toscr_hr_fm0(int);
|
|
static void toscr_hr_fm1(int);
|
|
static void toscr_hr_fm2(int);
|
|
|
|
STATIC_INLINE void toscr(int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0: toscr_fm0(nbits); break;
|
|
#ifdef AGA
|
|
case 1: toscr_fm1(nbits); break;
|
|
case 2: toscr_fm2(nbits); break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void toscr_hr(int nbits, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0: toscr_hr_fm0(nbits); break;
|
|
#ifdef AGA
|
|
case 1: toscr_hr_fm1(nbits); break;
|
|
case 2: toscr_hr_fm2(nbits); break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void toscr_0(int nbits, int fm)
|
|
{
|
|
int t;
|
|
|
|
while(nbits > TOSCR_NBITS) {
|
|
toscr(TOSCR_NBITS, fm);
|
|
nbits -= TOSCR_NBITS;
|
|
}
|
|
|
|
t = 2 * TOSCR_NBITS - out_nbits;
|
|
if (t < nbits) {
|
|
toscr_1(t, fm);
|
|
nbits -= t;
|
|
}
|
|
toscr_1(nbits, fm);
|
|
}
|
|
|
|
STATIC_INLINE void toscr_0_hr(int nbits, int fm)
|
|
{
|
|
nbits <<= toscr_res_mult;
|
|
while(nbits > 0) {
|
|
int n = 2 * TOSCR_NBITSHR - out_nbits;
|
|
if (n > nbits)
|
|
n = nbits;
|
|
toscr_1_hr(n, fm);
|
|
nbits -= n;
|
|
}
|
|
}
|
|
|
|
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 void toscr_hr_fm0(int nbits) { toscr_0_hr(nbits, 0); }
|
|
static void toscr_hr_fm1(int nbits) { toscr_0_hr(nbits, 1); }
|
|
static void toscr_hr_fm2(int nbits) { toscr_0_hr(nbits, 2); }
|
|
|
|
static void flush_plane_data_n(int fm)
|
|
{
|
|
// flush pending data
|
|
if (out_nbits <= TOSCR_NBITS) {
|
|
toscr_1(TOSCR_NBITS, fm);
|
|
}
|
|
if (out_nbits != 0) {
|
|
toscr_1(2 * TOSCR_NBITS - out_nbits, fm);
|
|
}
|
|
// flush until hsync
|
|
int maxcnt = 32;
|
|
while ((toscr_hend == 0 || toscr_hend == 1) && maxcnt-- >= 0) {
|
|
toscr_1(TOSCR_NBITS, fm);
|
|
}
|
|
}
|
|
|
|
static void flush_plane_data_hr(int fm)
|
|
{
|
|
// flush pending data
|
|
if (out_nbits <= TOSCR_NBITSHR) {
|
|
toscr_1_hr(TOSCR_NBITSHR, fm);
|
|
}
|
|
if (out_nbits != 0) {
|
|
toscr_1_hr(2 * TOSCR_NBITSHR - out_nbits, fm);
|
|
}
|
|
// flush until hsync
|
|
int maxcnt = 32;
|
|
while ((toscr_hend == 0 || toscr_hend == 1) && maxcnt-- >= 0) {
|
|
toscr_1_hr(TOSCR_NBITSHR, fm);
|
|
}
|
|
}
|
|
|
|
static void quick_add_delay_cycles(int total)
|
|
{
|
|
while (total > 0) {
|
|
int total2 = total;
|
|
if (delay_cycles2 <= delay_lastcycle[lol] && delay_cycles2 + total2 > delay_lastcycle[lol]) {
|
|
total2 = delay_lastcycle[lol] - delay_cycles2;
|
|
}
|
|
if (delay_cycles2 <= delay_hsynccycle && delay_cycles2 + total2 > delay_hsynccycle) {
|
|
total2 = delay_hsynccycle - delay_cycles2;
|
|
}
|
|
delay_cycles += total2;
|
|
delay_cycles2 += total2;
|
|
|
|
#if 0
|
|
0 if (toscr_special_res_change_count > 0) {
|
|
int tot = total2;
|
|
while (tot > 0) {
|
|
if (toscr_special_res_change_count2 > 0) {
|
|
toscr_special_res_change_count2--;
|
|
}
|
|
toscr_special_res_change_count--;
|
|
if (toscr_special_res_change_count == 0) {
|
|
break;
|
|
}
|
|
tot--;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (toscr_special_skip_ptr) {
|
|
int tot = total2;
|
|
while (tot > 0) {
|
|
toscr_special_skip_ptr++;
|
|
tot--;
|
|
if (*toscr_special_skip_ptr == 0) {
|
|
toscr_special_skip_ptr = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
total -= total2;
|
|
if (total <= 0) {
|
|
break;
|
|
}
|
|
if (currprefs.chipset_hr) {
|
|
toscr_special_hr(1, fetchmode);
|
|
} else {
|
|
toscr_special(1, fetchmode);
|
|
}
|
|
}
|
|
}
|
|
|
|
// flush remaining data but leave data after hsync
|
|
static void flush_plane_data(int fm)
|
|
{
|
|
if (currprefs.chipset_hr) {
|
|
flush_plane_data_hr(fm);
|
|
} else {
|
|
flush_plane_data_n(fm);
|
|
}
|
|
}
|
|
|
|
static void flush_display(int fm)
|
|
{
|
|
if (toscr_nbits && bpl_shifter) {
|
|
if (currprefs.chipset_hr) {
|
|
toscr_hr(toscr_nbits, fm);
|
|
} else {
|
|
toscr(toscr_nbits, fm);
|
|
}
|
|
} else if (toscr_nbits) {
|
|
int total;
|
|
if (currprefs.chipset_hr) {
|
|
total = toscr_nbits << (toscr_res_mult + toscr_res_pixels_shift_hr);
|
|
} else {
|
|
total = toscr_nbits << toscr_res_pixels_shift;
|
|
}
|
|
quick_add_delay_cycles(total);
|
|
}
|
|
toscr_nbits = 0;
|
|
}
|
|
|
|
static void record_color_change(int hpos, int regno, uae_u32 value);
|
|
|
|
static void hack_shres_delay(int hpos)
|
|
{
|
|
#if 0
|
|
if (currprefs.chipset_hr)
|
|
return;
|
|
if (!aga_mode && !toscr_delay_sh[0] && !toscr_delay_sh[1])
|
|
return;
|
|
int o0 = toscr_delay_sh[0];
|
|
int o1 = toscr_delay_sh[1];
|
|
int shdelay1 = (bplcon1 >> 8) & 3;
|
|
int shdelay2 = (bplcon1 >> 12) & 3;
|
|
if (shdelay1 != shdelay2) {
|
|
shdelay_disabled = true;
|
|
}
|
|
if (shdelay_disabled) {
|
|
toscr_delay_sh[0] = 0;
|
|
toscr_delay_sh[1] = 0;
|
|
} else {
|
|
toscr_delay_sh[0] = (shdelay1 & 3) >> toscr_res;
|
|
toscr_delay_sh[1] = (shdelay2 & 3) >> toscr_res;
|
|
}
|
|
if (hpos >= 0 && (toscr_delay_sh[0] != o0 || toscr_delay_sh[1] != o1)) {
|
|
record_color_change(hpos, 0, COLOR_CHANGE_SHRES_DELAY | toscr_delay_sh[0]);
|
|
current_colors.extra &= ~(1 << CE_SHRES_DELAY_SHIFT);
|
|
current_colors.extra &= ~(1 << (CE_SHRES_DELAY_SHIFT + 1));
|
|
current_colors.extra |= toscr_delay_sh[0] << CE_SHRES_DELAY_SHIFT;
|
|
remembered_color_entry = -1;
|
|
thisline_changed = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void update_denise_vars(void)
|
|
{
|
|
int res = GET_RES_DENISE(bplcon0d);
|
|
if (res == toscr_res_old)
|
|
return;
|
|
flush_display(fetchmode);
|
|
toscr_res = res;
|
|
toscr_res_old = res;
|
|
update_toscr_vars();
|
|
compute_toscr_delay(bplcon1);
|
|
compute_shifter_mask();
|
|
}
|
|
|
|
static void update_denise(int hpos)
|
|
{
|
|
update_denise_vars();
|
|
if (bplcon0d_old != bplcon0d) {
|
|
bplcon0d_old = bplcon0d;
|
|
record_color_change2(hpos, 0x100 + RECORDED_REGISTER_CHANGE_OFFSET, bplcon0d);
|
|
toscr_nr_planes = GET_PLANES(bplcon0d);
|
|
hack_shres_delay(hpos);
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void clear_fetchbuffer(uae_u32 *ptr, int nwords)
|
|
{
|
|
thisline_changed = 1;
|
|
memset(ptr, 0, nwords * 4);
|
|
}
|
|
|
|
static void update_toscr_planes(int newplanes, int fm)
|
|
{
|
|
// This must be called just before new bitplane block starts,
|
|
// not when depth value changes. Depth can change early and can leave
|
|
// 16+ pixel horizontal line of old data visible.
|
|
if (out_offs) {
|
|
for (int j = toscr_nr_planes2; j < newplanes ; j++) {
|
|
clear_fetchbuffer((uae_u32 *)(line_data[next_lineno] + 2 * MAX_WORDS_PER_LINE * j), out_offs);
|
|
if (thisline_decision.plfleft >= 0) {
|
|
todisplay[j] = 0;
|
|
#ifdef AGA
|
|
if (fm) {
|
|
todisplay_aga[j] = 0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hbstrt_bordercheck(int hpos, bool early)
|
|
{
|
|
if (exthblank) {
|
|
sync_color_changes(hpos);
|
|
}
|
|
if (hb_last_diwlastword < 0) {
|
|
return;
|
|
}
|
|
// if HBSTRT re-enabled border (and HDIW was already open), BPL1DAT access will disable border again.
|
|
uae_u16 pos = hpos_to_diwx(hpos);
|
|
if (early) {
|
|
pos -= 2;
|
|
}
|
|
pos -= 2; // 1 hires pixel early
|
|
pos = adjust_hr(pos);
|
|
hdiw_restart(hb_last_diwlastword, pos, false);
|
|
hb_last_diwlastword = -1;
|
|
}
|
|
|
|
static void beginning_of_plane_block_early(int hpos)
|
|
{
|
|
plane0p = false;
|
|
plane0p_enabled = false;
|
|
if (thisline_decision.plfleft >= 0) {
|
|
return;
|
|
}
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
flush_display(fetchmode);
|
|
reset_bpl_vars();
|
|
bpl_shifter = 1;
|
|
int left = hpos + hpos_hsync_extra;
|
|
thisline_decision.plfleft = left * 2;
|
|
if (hdiwstate == diw_states::DIW_waiting_stop && thisline_decision.diwfirstword < 0) {
|
|
// 1.5 lores pixels
|
|
int v = hpos_to_diwx(hpos);
|
|
v += 6;
|
|
v = adjust_hr(v);
|
|
thisline_decision.diwfirstword = coord_diw_shres_to_window_x(v);
|
|
|
|
}
|
|
hbstrt_bordercheck(hpos, false);
|
|
}
|
|
|
|
static void start_noborder(int hpos)
|
|
{
|
|
bpl_shifter = 1;
|
|
reset_bpl_vars();
|
|
if (thisline_decision.plfleft < 0) {
|
|
thisline_decision.plfleft = hpos * 2;
|
|
if (hdiwstate == diw_states::DIW_waiting_stop && thisline_decision.diwfirstword < 0) {
|
|
thisline_decision.diwfirstword = min_diwlastword;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 void beginning_of_plane_block(int hpos)
|
|
{
|
|
flush_display(fetchmode);
|
|
|
|
if (fetchmode == 0 && (!currprefs.chipset_hr || !ALL_SUBPIXEL))
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
todisplay[i] = fetched[i];
|
|
}
|
|
#ifdef AGA
|
|
else
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
todisplay_aga[i] = fetched_aga[i];
|
|
}
|
|
#endif
|
|
todisplay_fetched = 3;
|
|
plane0_first_done = true;
|
|
if (hpos > last_bpl1dat_hpos) {
|
|
last_bpl1dat_hpos = hpos;
|
|
}
|
|
// HBLANK start to HSYNC start?
|
|
if (!exthblank && ecs_denise && hpos >= 12 && hpos < hsyncstartpos_start_cycles) {
|
|
last_bpl1dat_hpos_early = true;
|
|
}
|
|
|
|
// do not mistake end of bitplane as start of low value hblank programmed mode
|
|
if (bpl_shifter <= 0 && hpos > REFRESH_FIRST_HPOS) {
|
|
if (ecs_denise || hpos >= OCS_DENISE_HBLANK_DISABLE_HPOS || hdiwstate == diw_states::DIW_waiting_stop) {
|
|
start_noborder(hpos + hpos_hsync_extra);
|
|
}
|
|
}
|
|
|
|
hbstrt_bordercheck(hpos, true);
|
|
|
|
update_denise(hpos);
|
|
if (toscr_nr_planes_agnus > thisline_decision.nr_planes) {
|
|
thisline_decision.nr_planes = toscr_nr_planes_agnus;
|
|
}
|
|
}
|
|
|
|
|
|
#if SPEEDUP
|
|
|
|
#define MAX_FETCH_TEMP 128
|
|
static uae_u32 fetch_tmp[MAX_FETCH_TEMP];
|
|
|
|
/* The usual inlining tricks - don't touch unless you know what you are doing. */
|
|
STATIC_INLINE void long_fetch_16(int plane, int nwords, int weird_number_of_bits)
|
|
{
|
|
uaecptr bpladdr = bplpt[plane];
|
|
int bploffset = 0;
|
|
uae_u16 *real_pt = (uae_u16*)pfield_xlateptr(bpladdr, nwords * 2);
|
|
int delay = toscr_delay_adjusted[plane & 1];
|
|
int tmp_nbits = out_nbits;
|
|
uae_u32 shiftbuffer;
|
|
uae_u32 outval = outword[plane];
|
|
uae_u32 fetchval = fetched[plane];
|
|
uae_u32 *dataptr = (uae_u32*)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE + 4 * out_offs);
|
|
|
|
bplpt[plane] += nwords * 2;
|
|
bplptx[plane] += nwords * 2;
|
|
|
|
if (real_pt == NULL) {
|
|
if (nwords > MAX_FETCH_TEMP) {
|
|
return;
|
|
}
|
|
for (int i = 0; i < nwords; i++) {
|
|
fetch_tmp[i] = chipmem_wget_indirect(bpladdr);
|
|
bpladdr += 2;
|
|
}
|
|
}
|
|
|
|
shiftbuffer = todisplay2[plane] << delay;
|
|
|
|
while (nwords > 0) {
|
|
int bits_left = 32 - tmp_nbits;
|
|
uae_u32 t;
|
|
|
|
shiftbuffer |= fetchval;
|
|
|
|
t = (shiftbuffer >> delay) & 0xffff;
|
|
|
|
if (weird_number_of_bits && bits_left < 16) {
|
|
outval <<= bits_left;
|
|
outval |= t >> (16 - bits_left);
|
|
thisline_changed |= *dataptr ^ outval;
|
|
*dataptr++ = outval;
|
|
|
|
outval = t;
|
|
tmp_nbits = 16 - bits_left;
|
|
} else {
|
|
outval = (outval << 16) | t;
|
|
tmp_nbits += 16;
|
|
if (tmp_nbits == 32) {
|
|
thisline_changed |= *dataptr ^ outval;
|
|
*dataptr++ = outval;
|
|
tmp_nbits = 0;
|
|
}
|
|
}
|
|
shiftbuffer <<= 16;
|
|
nwords--;
|
|
if (real_pt) {
|
|
fetchval = do_get_mem_word(real_pt);
|
|
real_pt++;
|
|
} else {
|
|
fetchval = fetch_tmp[bploffset];
|
|
bploffset++;
|
|
}
|
|
}
|
|
fetched[plane] = fetchval;
|
|
todisplay2[plane] = shiftbuffer >> delay;
|
|
outword[plane] = outval;
|
|
}
|
|
|
|
#ifdef AGA
|
|
STATIC_INLINE void long_fetch_32 (int plane, int nwords, int weird_number_of_bits)
|
|
{
|
|
uaecptr bpladdr = bplpt[plane] & ~3;
|
|
int bploffset = 0;
|
|
uae_u32 *real_pt = (uae_u32*)pfield_xlateptr(bpladdr, nwords * 2);
|
|
int delay = toscr_delay_adjusted[plane & 1];
|
|
int tmp_nbits = out_nbits;
|
|
uae_u64 shiftbuffer;
|
|
uae_u32 outval = outword[plane];
|
|
uae_u32 fetchval = (uae_u32)fetched_aga[plane];
|
|
uae_u32 *dataptr = (uae_u32*)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE + 4 * out_offs);
|
|
bool unaligned = (bplpt[plane] & 2) != 0;
|
|
|
|
bplpt[plane] += nwords * 2;
|
|
bplptx[plane] += nwords * 2;
|
|
|
|
if (real_pt == NULL) {
|
|
if (nwords > MAX_FETCH_TEMP) {
|
|
return;
|
|
}
|
|
for (int i = 0; i < nwords; i++) {
|
|
fetch_tmp[i] = chipmem_lget_indirect(bpladdr);
|
|
bpladdr += 4;
|
|
}
|
|
}
|
|
|
|
shiftbuffer = todisplay2_aga[plane] << delay;
|
|
|
|
while (nwords > 0) {
|
|
|
|
shiftbuffer |= fetchval;
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
uae_u32 t;
|
|
int bits_left = 32 - tmp_nbits;
|
|
|
|
t = (shiftbuffer >> (16 + delay)) & 0xffff;
|
|
|
|
if (weird_number_of_bits && bits_left < 16) {
|
|
outval <<= bits_left;
|
|
outval |= t >> (16 - bits_left);
|
|
|
|
thisline_changed |= *dataptr ^ outval;
|
|
*dataptr++ = outval;
|
|
|
|
outval = t;
|
|
tmp_nbits = 16 - bits_left;
|
|
} else {
|
|
outval = (outval << 16) | t;
|
|
tmp_nbits += 16;
|
|
if (tmp_nbits == 32) {
|
|
thisline_changed |= *dataptr ^ outval;
|
|
*dataptr++ = outval;
|
|
tmp_nbits = 0;
|
|
}
|
|
}
|
|
shiftbuffer <<= 16;
|
|
}
|
|
nwords -= 2;
|
|
if (real_pt) {
|
|
fetchval = do_get_mem_long(real_pt);
|
|
real_pt++;
|
|
} else {
|
|
fetchval = fetch_tmp[bploffset];
|
|
bploffset++;
|
|
}
|
|
if (unaligned) {
|
|
fetchval &= 0x0000ffff;
|
|
fetchval |= fetchval << 16;
|
|
} else if (fetchmode_fmode_bpl & 2) {
|
|
fetchval &= 0xffff0000;
|
|
fetchval |= fetchval >> 16;
|
|
}
|
|
|
|
}
|
|
fetched_aga[plane] = fetchval;
|
|
todisplay2_aga[plane] = (shiftbuffer >> delay) & 0xffffffff;
|
|
outword[plane] = outval;
|
|
}
|
|
|
|
#ifdef HAVE_UAE_U128
|
|
/* uae_u128 is available, custom shift functions not necessary */
|
|
#else
|
|
|
|
STATIC_INLINE void shift32plus(uae_u64 *p, int n)
|
|
{
|
|
uae_u64 t = p[1];
|
|
t <<= n;
|
|
t |= p[0] >> (64 - n);
|
|
p[1] = t;
|
|
}
|
|
|
|
STATIC_INLINE void aga_shift(uae_u64 *p, int n)
|
|
{
|
|
if (n == 0) return;
|
|
shift32plus(p, n);
|
|
p[0] <<= n;
|
|
}
|
|
|
|
STATIC_INLINE void shift32plusn(uae_u64 *p, int n)
|
|
{
|
|
uae_u64 t = p[0];
|
|
t >>= n;
|
|
t |= p[1] << (64 - n);
|
|
p[0] = t;
|
|
}
|
|
|
|
STATIC_INLINE void aga_shift_n(uae_u64 *p, int n)
|
|
{
|
|
if (n == 0) return;
|
|
shift32plusn(p, n);
|
|
p[1] >>= n;
|
|
}
|
|
|
|
#endif
|
|
|
|
STATIC_INLINE void long_fetch_64(int plane, int nwords, int weird_number_of_bits)
|
|
{
|
|
uaecptr bpladdr = bplpt[plane] & ~7;
|
|
int bploffset = 0;
|
|
uae_u32 *real_pt = (uae_u32*)pfield_xlateptr(bpladdr, nwords * 2);
|
|
int delay = toscr_delay_adjusted[plane & 1];
|
|
int tmp_nbits = out_nbits;
|
|
#ifdef HAVE_UAE_U128
|
|
uae_u128 shiftbuffer;
|
|
#else
|
|
uae_u64 shiftbuffer[2];
|
|
#endif
|
|
uae_u32 outval = outword[plane];
|
|
uae_u64 fetchval = fetched_aga[plane];
|
|
uae_u32 *dataptr = (uae_u32*)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE + 4 * out_offs);
|
|
int shift = (64 - 16) + delay;
|
|
bool unaligned2 = (bplpt[plane] & 2) != 0;
|
|
bool unaligned4 = (bplpt[plane] & 4) != 0;
|
|
|
|
bplpt[plane] += nwords * 2;
|
|
bplptx[plane] += nwords * 2;
|
|
|
|
if (real_pt == NULL) {
|
|
if (nwords * 2 > MAX_FETCH_TEMP) {
|
|
return;
|
|
}
|
|
for (int i = 0; i < nwords * 2; i++) {
|
|
fetch_tmp[i] = chipmem_lget_indirect(bpladdr);
|
|
bpladdr += 4;
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_UAE_U128
|
|
shiftbuffer = todisplay2_aga[plane] << delay;
|
|
#else
|
|
shiftbuffer[1] = 0;
|
|
shiftbuffer[0] = todisplay2_aga[plane];
|
|
aga_shift(shiftbuffer, delay);
|
|
#endif
|
|
|
|
while (nwords > 0) {
|
|
#ifdef HAVE_UAE_U128
|
|
shiftbuffer |= fetchval;
|
|
#else
|
|
shiftbuffer[0] |= fetchval;
|
|
#endif
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
uae_u32 t;
|
|
int bits_left = 32 - tmp_nbits;
|
|
|
|
#ifdef HAVE_UAE_U128
|
|
t = (shiftbuffer >> shift) & 0xffff;
|
|
#else
|
|
if (64 - shift > 0) {
|
|
t = (uae_u32)(shiftbuffer[1] << (64 - shift));
|
|
t |= shiftbuffer[0] >> shift;
|
|
} else {
|
|
t = (uae_u32)(shiftbuffer[1] >> (shift - 64));
|
|
}
|
|
t &= 0xffff;
|
|
#endif
|
|
|
|
if (weird_number_of_bits && bits_left < 16) {
|
|
outval <<= bits_left;
|
|
outval |= t >> (16 - bits_left);
|
|
|
|
thisline_changed |= *dataptr ^ outval;
|
|
*dataptr++ = outval;
|
|
|
|
outval = t;
|
|
tmp_nbits = 16 - bits_left;
|
|
} else {
|
|
outval = (outval << 16) | t;
|
|
tmp_nbits += 16;
|
|
if (tmp_nbits == 32) {
|
|
thisline_changed |= *dataptr ^ outval;
|
|
*dataptr++ = outval;
|
|
tmp_nbits = 0;
|
|
}
|
|
}
|
|
#ifdef HAVE_UAE_U128
|
|
shiftbuffer <<= 16;
|
|
#else
|
|
aga_shift(shiftbuffer, 16);
|
|
#endif
|
|
}
|
|
|
|
nwords -= 4;
|
|
|
|
int offset1 = 0;
|
|
int offset2 = 1;
|
|
if (unaligned4) {
|
|
offset1 = 1;
|
|
}
|
|
uae_u32 v1, v2;
|
|
if (real_pt) {
|
|
v1 = do_get_mem_long(real_pt + offset1);
|
|
v2 = do_get_mem_long(real_pt + offset2);
|
|
real_pt += 2;
|
|
} else {
|
|
v1 = fetch_tmp[bploffset + offset1];
|
|
v2 = fetch_tmp[bploffset + offset2];
|
|
bploffset += 2;
|
|
}
|
|
if (unaligned2) {
|
|
v1 &= 0x0000ffff;
|
|
v1 |= v1 << 16;
|
|
v2 &= 0x0000ffff;
|
|
v2 |= v2 << 16;
|
|
fetchval = (((uae_u64)v1) << 32) | v2;
|
|
} else {
|
|
fetchval = ((uae_u64)v1) << 32;
|
|
fetchval |= v2;
|
|
}
|
|
}
|
|
fetched_aga[plane] = fetchval;
|
|
#ifdef HAVE_UAE_U128
|
|
todisplay2_aga[plane] = shiftbuffer >> delay;
|
|
#else
|
|
aga_shift_n(shiftbuffer, delay);
|
|
todisplay2_aga[plane] = shiftbuffer[0];
|
|
#endif
|
|
outword[plane] = outval;
|
|
}
|
|
#endif
|
|
|
|
static void long_fetch_16_0 (int hpos, int nwords) { long_fetch_16 (hpos, nwords, 0); }
|
|
static void long_fetch_16_1 (int hpos, int nwords) { long_fetch_16 (hpos, nwords, 1); }
|
|
#ifdef AGA
|
|
static void long_fetch_32_0 (int hpos, int nwords) { long_fetch_32 (hpos, nwords, 0); }
|
|
static void long_fetch_32_1 (int hpos, int nwords) { long_fetch_32 (hpos, nwords, 1); }
|
|
static void long_fetch_64_0 (int hpos, int nwords) { long_fetch_64 (hpos, nwords, 0); }
|
|
static void long_fetch_64_1 (int hpos, int nwords) { long_fetch_64 (hpos, nwords, 1); }
|
|
#endif
|
|
|
|
static void do_long_fetch(int hpos, int nwords, int fm)
|
|
{
|
|
|
|
beginning_of_plane_block(hpos);
|
|
|
|
// adjust to current resolution
|
|
int rnwords = nwords >> (3 - toscr_res);
|
|
|
|
switch (fm) {
|
|
case 0:
|
|
if (out_nbits & 15) {
|
|
for (int i = 0; i < toscr_nr_planes; i++) {
|
|
long_fetch_16_1(i, rnwords);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < toscr_nr_planes; i++) {
|
|
long_fetch_16_0(i, rnwords);
|
|
}
|
|
}
|
|
break;
|
|
#ifdef AGA
|
|
case 1:
|
|
if (out_nbits & 15) {
|
|
for (int i = 0; i < toscr_nr_planes; i++) {
|
|
long_fetch_32_1(i, rnwords);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < toscr_nr_planes; i++) {
|
|
long_fetch_32_0(i, rnwords);
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
if (out_nbits & 15) {
|
|
for (int i = 0; i < toscr_nr_planes; i++) {
|
|
long_fetch_64_1(i, rnwords);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < toscr_nr_planes; i++) {
|
|
long_fetch_64_0(i, rnwords);
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
plane0 = toscr_nr_planes > 0;
|
|
|
|
int cnt = nwords;
|
|
while (exthblank) {
|
|
cnt -= fetchstart;
|
|
hpos += fetchstart;
|
|
sync_color_changes(hpos);
|
|
if (hb_last_diwlastword >= 0) {
|
|
hbstrt_bordercheck(hpos, false);
|
|
}
|
|
if (cnt <= 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
out_nbits += rnwords * 16;
|
|
out_offs += out_nbits >> 5;
|
|
out_nbits &= 31;
|
|
quick_add_delay_cycles((rnwords * 16) << toscr_res_pixels_shift);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* make sure fetch that goes beyond maxhpos is finished */
|
|
static void finish_final_fetch(int hpos)
|
|
{
|
|
clear_bitplane_pipeline(1);
|
|
|
|
if (thisline_decision.plfleft < 0) {
|
|
// registers that affect programmed vblank must be checked even if line is blank
|
|
if (line_decisions[next_lineno].bplcon3 != thisline_decision.bplcon3
|
|
|| (line_decisions[next_lineno].bplcon0 & 1) != (thisline_decision.bplcon0 & 1)
|
|
|| line_decisions[next_lineno].vb != thisline_decision.vb
|
|
)
|
|
thisline_changed = 1;
|
|
return;
|
|
}
|
|
|
|
flush_display(fetchmode);
|
|
|
|
// This is really the end of scanline, we can finally flush all remaining data.
|
|
//thisline_decision.plfright += flush_plane_data(fetchmode, hpos);
|
|
flush_plane_data(fetchmode);
|
|
|
|
// This can overflow if display setup is really bad.
|
|
if (out_offs > MAX_PIXELS_PER_LINE / 32) {
|
|
out_offs = MAX_PIXELS_PER_LINE / 32;
|
|
}
|
|
thisline_decision.plflinelen = out_offs;
|
|
|
|
/* The latter condition might be able to happen in interlaced frames. */
|
|
if (vposh >= minfirstline && (thisframe_first_drawn_line < 0 || vposh < thisframe_first_drawn_line)) {
|
|
thisframe_first_drawn_line = vposh;
|
|
}
|
|
thisframe_last_drawn_line = vposh;
|
|
|
|
#ifdef SMART_UPDATE
|
|
if (line_decisions[next_lineno].plflinelen != thisline_decision.plflinelen
|
|
|| line_decisions[next_lineno].plfleft != thisline_decision.plfleft
|
|
|| line_decisions[next_lineno].bplcon0 != thisline_decision.bplcon0
|
|
|| line_decisions[next_lineno].bplcon2 != thisline_decision.bplcon2
|
|
#ifdef ECS_DENISE
|
|
|| line_decisions[next_lineno].bplcon3 != thisline_decision.bplcon3
|
|
#endif
|
|
#ifdef AGA
|
|
|| line_decisions[next_lineno].bplcon4bm != thisline_decision.bplcon4bm
|
|
|| line_decisions[next_lineno].fmode != thisline_decision.fmode
|
|
|| line_decisions[next_lineno].vb != thisline_decision.vb
|
|
#endif
|
|
)
|
|
#endif /* SMART_UPDATE */
|
|
thisline_changed = 1;
|
|
}
|
|
|
|
STATIC_INLINE int bpl_select_plane(int hpos, int plane, bool modulo)
|
|
{
|
|
if (plane >= 1 && plane <= bplcon0_planes_limit) {
|
|
int offset = get_rga_pipeline(hpos, RGA_PIPELINE_OFFSET_BPL_WRITE);
|
|
#if CYCLE_CONFLICT_LOGGING
|
|
if (cycle_line_pipe[offset]) {
|
|
write_log(_T("bitplane slot already allocated by %04x!\n"), cycle_line_pipe[offset]);
|
|
}
|
|
#endif
|
|
cycle_line_pipe[offset] = plane | (modulo ? CYCLE_PIPE_MODULO : 0) | CYCLE_PIPE_BITPLANE;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
static void do_copper_fetch(int hpos, uae_u16 id);
|
|
static void do_sprite_fetch(int hpos, uae_u16 dat);
|
|
|
|
static void set_maxhpos(int maxhp)
|
|
{
|
|
maxhposm0 = maxhp;
|
|
maxhposm1 = maxhp - 1;
|
|
maxhposeven = (maxhp & 1) == 0;
|
|
}
|
|
|
|
static void scandoubler_bpl_dma_start(void)
|
|
{
|
|
if (!scandoubled_line && doflickerfix_active()) {
|
|
int lof = 1 - lof_display;
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
prevbpl[lof][vpos][i] = bplptx[i];
|
|
if (!lof && (bplcon0 & 4)) {
|
|
bplpt[i] = prevbpl[1 - lof][vpos][i];
|
|
}
|
|
if (!(bplcon0 & 4) || interlace_seen < 0) {
|
|
prevbpl[1 - lof][vpos][i] = prevbpl[lof][vpos][i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void bpl_dma_normal_stop(int hpos)
|
|
{
|
|
ddf_stopping = 0;
|
|
bprun = 0;
|
|
bprun_end = hpos;
|
|
plfstrt_sprite = 0x100;
|
|
bprun_pipeline_flush_delay = 8;
|
|
if (!ecs_agnus) {
|
|
ddf_limit = true;
|
|
}
|
|
end_estimate_last_fetch_cycle(hpos);
|
|
}
|
|
|
|
STATIC_INLINE bool islastbplseq(void)
|
|
{
|
|
bool last = (bprun_cycle & fetchunit_mask) == fetchunit_mask;
|
|
// AGA stops all fetches after 8 cycles
|
|
if (aga_mode && ddf_stopping == 2) {
|
|
last = (bprun_cycle & 7) == 7;
|
|
}
|
|
return last;
|
|
}
|
|
|
|
STATIC_INLINE void one_fetch_cycle_0(int hpos, int fm)
|
|
{
|
|
bool last = islastbplseq();
|
|
|
|
if (bprun > 0) {
|
|
int cycle_pos = bprun_cycle & fetchstart_mask;
|
|
// 226 -> 0 (even to even cycle)
|
|
bool skip = hpos == maxhposm1 && !maxhposeven;
|
|
if (dmacon_bpl && !skip) {
|
|
bool domod = false;
|
|
if (ddf_stopping == 2) {
|
|
int cycle = bprun_cycle & 7;
|
|
if (fm_maxplane == 8 || (fm_maxplane == 4 && cycle >= 4) || (fm_maxplane == 2 && cycle >= 6)) {
|
|
domod = true;
|
|
}
|
|
}
|
|
int plane = bpl_sequence[cycle_pos];
|
|
bpl_select_plane(hpos, plane, domod);
|
|
}
|
|
if (skip) {
|
|
last = false;
|
|
} else {
|
|
bprun_cycle++;
|
|
}
|
|
} else {
|
|
bpl_select_plane(hpos, 0, false);
|
|
}
|
|
if (last && bprun > 0) {
|
|
if (ddf_stopping == 2) {
|
|
bpl_dma_normal_stop(hpos);
|
|
}
|
|
if (ddf_stopping == 1) {
|
|
ddf_stopping = 2;
|
|
}
|
|
}
|
|
uae_u16 datreg = cycle_line_pipe[hpos];
|
|
plane0 = false;
|
|
if (datreg & CYCLE_PIPE_BITPLANE) {
|
|
plane0 = fetch((datreg - 1) & 7, fm, hpos, (datreg & CYCLE_PIPE_MODULO) != 0);
|
|
} else if (datreg & CYCLE_PIPE_COPPER) {
|
|
do_copper_fetch(hpos, datreg);
|
|
cycle_line_pipe[hpos] = 0;
|
|
}
|
|
|
|
if (plane0p_enabled) {
|
|
int offset = get_rga_pipeline(hpos, 1);
|
|
uae_u16 d = cycle_line_pipe[offset];
|
|
if ((d & CYCLE_PIPE_BITPLANE) && (d & 7) == 1) {
|
|
decide_hdiw(hpos);
|
|
if (hdiwstate == diw_states::DIW_waiting_stop || plane0p_forced) {
|
|
plane0p = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
fetch_cycle++;
|
|
toscr_nbits += toscr_res2p;
|
|
|
|
if (bplcon1_written) {
|
|
flush_display(fm);
|
|
compute_toscr_delay(bplcon1);
|
|
bplcon1_written = false;
|
|
}
|
|
|
|
// BPLCON0 modification immediately after BPL1DAT can affect BPL1DAT finished plane block
|
|
if (bplcon0_planes_changed) {
|
|
if (bprun) {
|
|
if (((hpos - bpl_hstart) & fetchstart_mask) < fetchstart_mask) {
|
|
flush_display(fm);
|
|
toscr_nr_planes_shifter = toscr_nr_planes_shifter_new;
|
|
bplcon0_planes_changed = false;
|
|
}
|
|
} else {
|
|
flush_display(fm);
|
|
toscr_nr_planes_shifter = toscr_nr_planes_shifter_new;
|
|
bplcon0_planes_changed = false;
|
|
}
|
|
}
|
|
|
|
if (toscr_nbits == TOSCR_NBITS) {
|
|
flush_display(fm);
|
|
}
|
|
|
|
if (bprun_pipeline_flush_delay > 0) {
|
|
bprun_pipeline_flush_delay--;
|
|
}
|
|
}
|
|
|
|
static void one_fetch_cycle_fm0(int pos) { one_fetch_cycle_0(pos, 0); }
|
|
static void one_fetch_cycle_fm1(int pos) { one_fetch_cycle_0(pos, 1); }
|
|
static void one_fetch_cycle_fm2(int pos) { one_fetch_cycle_0(pos, 2); }
|
|
|
|
STATIC_INLINE void one_fetch_cycle(int pos, int fm)
|
|
{
|
|
switch (fm) {
|
|
case 0:
|
|
one_fetch_cycle_fm0(pos);
|
|
break;
|
|
#ifdef AGA
|
|
case 1:
|
|
one_fetch_cycle_fm1(pos);
|
|
break;
|
|
case 2:
|
|
one_fetch_cycle_fm2(pos);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void update_fetch(int until, int fm)
|
|
{
|
|
int hpos = last_fetch_hpos;
|
|
|
|
if (hpos >= until) {
|
|
return;
|
|
}
|
|
|
|
/* First, a loop that prepares us for the speedup code. We want to enter
|
|
the SPEEDUP case with fetch_state == plane0 or it is the very
|
|
first fetch cycle (which equals to same state as fetch_was_plane0)
|
|
and then unroll whole blocks, so that we end on the same fetch_state again. */
|
|
for(;;) {
|
|
if (hpos == until || hpos >= maxhpos) {
|
|
if (until >= maxhpos) {
|
|
flush_display(fm);
|
|
}
|
|
last_fetch_hpos = until;
|
|
return;
|
|
}
|
|
if (plane0p) {
|
|
beginning_of_plane_block_early(hpos);
|
|
}
|
|
if (plane0) {
|
|
break;
|
|
}
|
|
one_fetch_cycle(hpos, fm);
|
|
hpos++;
|
|
}
|
|
|
|
#if SPEEDUP
|
|
// Unrolled simple version of the for loop below.
|
|
if (bprun && !line_cyclebased && dmacon_bpl && !ddf_stopping
|
|
&& plane0
|
|
&& (ecs_denise || hpos >= OCS_DENISE_HBLANK_DISABLE_HPOS)
|
|
&& !currprefs.chipset_hr
|
|
#ifdef DEBUGGER
|
|
&& !debug_dma
|
|
#endif
|
|
&& GET_PLANES_LIMIT(bplcon0) == toscr_nr_planes_agnus)
|
|
{
|
|
int hard_ddf_stop = harddis_h ? 0x100 : HARD_DDF_STOP;
|
|
int adjusted_plfstop = plfstop;
|
|
int ddfstop_to_test_ddf = hard_ddf_stop;
|
|
if (adjusted_plfstop >= last_fetch_hpos && adjusted_plfstop < ddfstop_to_test_ddf) {
|
|
ddfstop_to_test_ddf = adjusted_plfstop;
|
|
}
|
|
int ddfstop_to_test = ddfstop_to_test_ddf;
|
|
int offs = (fetch_cycle - 4) & 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 - hpos;
|
|
if (count >= fetchstart) {
|
|
count &= ~fetchstart_mask;
|
|
|
|
int stoppos = hpos + count;
|
|
|
|
if (speedup_first) {
|
|
compute_toscr_delay(bplcon1);
|
|
speedup_first = false;
|
|
}
|
|
|
|
do_long_fetch(hpos, count, fm);
|
|
|
|
if ((hpos <= adjusted_plfstop && stoppos > adjusted_plfstop) || (hpos <= hard_ddf_stop && stoppos > hard_ddf_stop)) {
|
|
ddf_stopping = 1;
|
|
}
|
|
if (hpos <= ddfstop_to_test && stoppos > ddf2) {
|
|
ddf_stopping = 2;
|
|
}
|
|
if (hpos <= ddf2 && stoppos >= ddf2 + fm_maxplane) {
|
|
add_modulos();
|
|
bpl_dma_normal_stop(stoppos);
|
|
plane0 = false;
|
|
}
|
|
|
|
// Copy pipelined data to new hpos. They are guaranteed to be same because
|
|
// above routine always works from BPL1DAT to BPL1DAT.
|
|
if (bprun) {
|
|
for (int i = RGA_PIPELINE_ADJUST - 1; i >= 0; i--) {
|
|
int roffset = get_rga_pipeline(hpos, i);
|
|
int woffset = get_rga_pipeline(hpos, count + i);
|
|
cycle_line_pipe[woffset] = cycle_line_pipe[roffset];
|
|
cycle_line_pipe[roffset] = 0;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < RGA_PIPELINE_ADJUST; i++) {
|
|
int roffset = get_rga_pipeline(hpos, i);
|
|
cycle_line_pipe[roffset] = 0;
|
|
}
|
|
}
|
|
|
|
hpos += count;
|
|
fetch_cycle += count;
|
|
bprun_cycle += count;
|
|
if (bprun_pipeline_flush_delay > 0) {
|
|
bprun_pipeline_flush_delay -= count;
|
|
if (bprun_pipeline_flush_delay < 0) {
|
|
bprun_pipeline_flush_delay = 0;
|
|
}
|
|
}
|
|
if (plane0) {
|
|
last_bpl1dat_hpos = hpos;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
while (hpos < until) {
|
|
if (plane0p) {
|
|
beginning_of_plane_block_early(hpos);
|
|
}
|
|
if (plane0) {
|
|
beginning_of_plane_block(hpos);
|
|
}
|
|
one_fetch_cycle(hpos, fm);
|
|
hpos++;
|
|
}
|
|
last_fetch_hpos = hpos;
|
|
}
|
|
|
|
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 void decide_bpl_fetch(int endhpos)
|
|
{
|
|
int hpos = last_fetch_hpos;
|
|
if (hpos >= endhpos) {
|
|
return;
|
|
}
|
|
|
|
if (1 && (nodraw() || !bprun_pipeline_flush_delay)) {
|
|
if (hpos < endhpos) {
|
|
if (thisline_decision.plfleft >= 0) {
|
|
while (hpos < endhpos) {
|
|
if (toscr_nbits == TOSCR_NBITS) {
|
|
flush_display(fetchmode);
|
|
}
|
|
toscr_nbits += toscr_res2p;
|
|
hpos++;
|
|
}
|
|
} else {
|
|
int diff = endhpos - hpos;
|
|
int total = diff << (1 + LORES_TO_SHRES_SHIFT);
|
|
quick_add_delay_cycles(total);
|
|
}
|
|
last_fetch_hpos = endhpos;
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (fetchmode) {
|
|
case 0: update_fetch_0(endhpos); break;
|
|
#ifdef AGA
|
|
case 1: update_fetch_1(endhpos); break;
|
|
case 2: update_fetch_2(endhpos); break;
|
|
#endif
|
|
default: uae_abort(_T("fetchmode corrupt"));
|
|
}
|
|
}
|
|
|
|
static void vdiw_change(uae_u32 v)
|
|
{
|
|
vdiwstate_bpl = v != 0;
|
|
}
|
|
|
|
/* Take care of the vertical DIW. */
|
|
static void decide_vline(int hpos)
|
|
{
|
|
bool forceoff = (vb_start_line == 1 && !harddis_v);
|
|
bool start = vpos == plffirstline && !forceoff;
|
|
// VB start line forces vertical display window off (if HARDDIS=0)
|
|
bool end = vpos == plflastline || forceoff;
|
|
|
|
if (start && !end) {
|
|
if (vdiwstate != diw_states::DIW_waiting_stop) {
|
|
event2_newevent_xx(-1, CYCLE_UNIT, 1, vdiw_change);
|
|
}
|
|
vdiwstate = diw_states::DIW_waiting_stop;
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
} else if (end) {
|
|
if (vdiwstate != diw_states::DIW_waiting_start) {
|
|
event2_newevent_xx(-1, CYCLE_UNIT, 0, vdiw_change);
|
|
}
|
|
vdiwstate = diw_states::DIW_waiting_start;
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
}
|
|
|
|
static void decide_line_decision_fetches(int endhpos)
|
|
{
|
|
decide_bpl_fetch(endhpos);
|
|
decide_sprites_fetch(endhpos);
|
|
}
|
|
|
|
static void decide_fetch_safe(int endhpos)
|
|
{
|
|
int hpos = last_fetch_hpos;
|
|
if (!blt_info.blitter_dangerous_bpl && !copper_enabled_thisline) {
|
|
decide_line_decision_fetches(endhpos);
|
|
decide_blitter(endhpos);
|
|
} else if (copper_enabled_thisline && blt_info.blitter_dangerous_bpl) {
|
|
while (hpos <= endhpos) {
|
|
decide_line_decision_fetches(hpos);
|
|
update_copper(hpos);
|
|
decide_blitter(hpos);
|
|
hpos++;
|
|
}
|
|
} else {
|
|
while (hpos <= endhpos) {
|
|
decide_line_decision_fetches(hpos);
|
|
decide_blitter(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, uae_u32 value)
|
|
{
|
|
if (regno < RECORDED_REGISTER_CHANGE_OFFSET && nodraw())
|
|
return;
|
|
|
|
decide_hdiw(hpos);
|
|
|
|
/* vsync period don't appear on-screen. */
|
|
if (line_hidden())
|
|
return;
|
|
|
|
decide_line(hpos);
|
|
|
|
if (thisline_decision.ctable < 0) {
|
|
remember_ctable();
|
|
}
|
|
|
|
hpos += vhposr_delay_offset;
|
|
|
|
record_color_change2(hpos, regno, value);
|
|
}
|
|
|
|
static void setextblank(void)
|
|
{
|
|
bool extblank = (bplcon0 & 1) && (bplcon3 & 0x01);
|
|
current_colors.extra &= ~(1 << CE_EXTBLANKSET);
|
|
if (extblank) {
|
|
current_colors.extra |= 1 << CE_EXTBLANKSET;
|
|
}
|
|
}
|
|
|
|
static bool isbrdblank(int hpos, uae_u16 bplcon0, uae_u16 bplcon3)
|
|
{
|
|
bool brdblank, brdntrans, extblank;
|
|
#ifdef ECS_DENISE
|
|
brdblank = (ecs_denise && (bplcon0 & 1) && (bplcon3 & 0x20)) || (currprefs.genlock_effects & (1 << 5));
|
|
brdntrans = (ecs_denise && (bplcon0 & 1) && (bplcon3 & 0x10)) || (currprefs.genlock_effects & (1 << 4));
|
|
// ECSENA=0: hardwired horizontal, strobe vertical
|
|
// ECSENA=1: EXTBLKEN=0: hardwired blanking, strobe vertical
|
|
// ECSENA=1: EXTBLKEN=1: blanking equals HSYNC if VARCSYEN=1, blanking equals HBSTRT-HBSTOP if VARCSYEN=0, no vertical, AGA: programmed horizontal, strobe vertical
|
|
extblank = (bplcon0 & 1) && (bplcon3 & 1);
|
|
#else
|
|
brdblank = false;
|
|
brdntrans = false;
|
|
extblank = false;
|
|
#endif
|
|
if (hpos >= 0 && (ce_is_borderblank(current_colors.extra) != brdblank || ce_is_borderntrans(current_colors.extra) != brdntrans || ce_is_extblankset(current_colors.extra) != extblank)) {
|
|
record_color_change(hpos, 0, COLOR_CHANGE_BRDBLANK | (brdblank ? 1 : 0) | (ce_is_bordersprite(current_colors.extra) ? 2 : 0) | (brdntrans ? 4 : 0) | (extblank ? 8 : 0));
|
|
current_colors.extra &= ~(1 << CE_BORDERBLANK);
|
|
current_colors.extra &= ~(1 << CE_BORDERNTRANS);
|
|
current_colors.extra &= ~(1 << CE_EXTBLANKSET);
|
|
current_colors.extra |= brdblank ? (1 << CE_BORDERBLANK) : 0;
|
|
current_colors.extra |= brdntrans ? (1 << CE_BORDERNTRANS) : 0;
|
|
current_colors.extra |= extblank ? (1 << CE_EXTBLANKSET) : 0;
|
|
remembered_color_entry = -1;
|
|
}
|
|
return brdblank;
|
|
}
|
|
|
|
static bool brdblankactive(void)
|
|
{
|
|
return (bplcon0 & 1) && (bplcon3 & 0x20);
|
|
}
|
|
|
|
static bool brdspractive(void)
|
|
{
|
|
return (bplcon3 & 2) && (bplcon0 & 1);
|
|
}
|
|
|
|
static bool issprbrd(int hpos, uae_u16 bplcon0, uae_u16 bplcon3)
|
|
{
|
|
bool brdsprt;
|
|
#ifdef AGA
|
|
brdsprt = aga_mode && brdspractive();
|
|
#else
|
|
brdsprt = false;
|
|
#endif
|
|
if (hpos >= 0 && ce_is_bordersprite(current_colors.extra) != brdsprt) {
|
|
record_color_change(hpos, 0, COLOR_CHANGE_BRDBLANK | (ce_is_borderblank(current_colors.extra) ? 1 : 0) | (brdsprt ? 2 : 0) |
|
|
(ce_is_borderntrans(current_colors.extra) ? 4 : 0) | (ce_is_extblankset(current_colors.extra) ? 8 : 0));
|
|
current_colors.extra &= ~(1 << CE_BORDERSPRITE);
|
|
current_colors.extra |= brdsprt ? (1 << CE_BORDERSPRITE) : 0;
|
|
remembered_color_entry = -1;
|
|
if (brdsprt && !ce_is_borderblank(current_colors.extra)) {
|
|
thisline_decision.bordersprite_seen = true;
|
|
}
|
|
}
|
|
if (!plane0_first_done) {
|
|
plane0p_enabled = ecs_denise && (bplcon0 & 1) && (bplcon3 & 0x20);
|
|
}
|
|
return brdsprt && !ce_is_borderblank(current_colors.extra);
|
|
}
|
|
|
|
static bool isham(uae_u16 bplcon0)
|
|
{
|
|
int p = GET_PLANES(bplcon0);
|
|
if (!(bplcon0 & 0x800))
|
|
return 0;
|
|
if (aga_mode) {
|
|
// AGA only has 6 or 8 plane HAM
|
|
if (p == 6 || p == 8)
|
|
return 1;
|
|
} else {
|
|
// OCS/ECS also supports 5 plane HAM
|
|
if (GET_RES_DENISE(bplcon0) > 0)
|
|
return 0;
|
|
if (p >= 5)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void record_register_change(int hpos, int regno, uae_u16 value)
|
|
{
|
|
if (regno == 0x0100 || regno == 0x0101) { // BPLCON0
|
|
if (value & 0x800) {
|
|
thisline_decision.ham_seen |= isham(value);
|
|
}
|
|
thisline_decision.ehb_seen |= isehb(value, bplcon2);
|
|
isbrdblank(hpos, value, bplcon3);
|
|
issprbrd(hpos, value, bplcon3);
|
|
} else if (regno == 0x104) { // BPLCON2
|
|
thisline_decision.ehb_seen |= isehb(bplcon0, value);
|
|
} else if (regno == 0x106) { // BPLCON3
|
|
isbrdblank(hpos, bplcon0, value);
|
|
issprbrd(hpos, bplcon0, value);
|
|
}
|
|
record_color_change(hpos, regno + RECORDED_REGISTER_CHANGE_OFFSET, value);
|
|
}
|
|
|
|
typedef int sprbuf_res_t, cclockres_t, hwres_t, bplres_t;
|
|
|
|
/* handle very rarely needed playfield collision (CLXDAT bit 0) */
|
|
/* only known game needing this is Rotor */
|
|
static void do_playfield_collisions(int startpos, int endpos)
|
|
{
|
|
int bplres = output_res(bplcon0_res);
|
|
hwres_t ddf_left = (thisline_decision.plfleft - DDF_OFFSET) << bplres;
|
|
hwres_t hw_diwlast = coord_window_to_diw_x(endpos);
|
|
hwres_t hw_diwfirst = coord_window_to_diw_x(startpos);
|
|
int i, collided, minpos, maxpos;
|
|
#ifdef AGA
|
|
int planes = aga_mode ? 8 : 6;
|
|
#else
|
|
int planes = 6;
|
|
#endif
|
|
|
|
if (clxcon_bpl_enable == 0) {
|
|
clxdat |= 1;
|
|
return;
|
|
}
|
|
// collision bit already set?
|
|
if (clxdat & 1) {
|
|
return;
|
|
}
|
|
|
|
collided = 0;
|
|
minpos = thisline_decision.plfleft - DDF_OFFSET;
|
|
if (minpos < hw_diwfirst) {
|
|
minpos = hw_diwfirst;
|
|
}
|
|
maxpos = thisline_decision.plfright - DDF_OFFSET;
|
|
if (maxpos > hw_diwlast) {
|
|
maxpos = hw_diwlast;
|
|
}
|
|
|
|
for (i = minpos; i < maxpos && !collided; i += 32) {
|
|
int offs = ((i << bplres) - ddf_left) >> 3;
|
|
int j;
|
|
uae_u32 total = 0xffffffff;
|
|
for (j = 0; j < planes; j++) {
|
|
int ena = (clxcon_bpl_enable >> j) & 1;
|
|
int match = (clxcon_bpl_match >> j) & 1;
|
|
uae_u32 t = 0xffffffff;
|
|
if (ena) {
|
|
if (j < thisline_decision.nr_planes) {
|
|
t = *(uae_u32 *)(line_data[next_lineno] + offs + 2 * j * MAX_WORDS_PER_LINE);
|
|
t ^= (match & 1) - 1;
|
|
} else {
|
|
t = (match & 1) - 1;
|
|
}
|
|
}
|
|
total &= t;
|
|
}
|
|
if (total) {
|
|
collided = 1;
|
|
#if 0
|
|
{
|
|
int k;
|
|
for (k = 0; k < 1; k++) {
|
|
uae_u32 *ldata = (uae_u32 *)(line_data[next_lineno] + offs + 2 * k * MAX_WORDS_PER_LINE);
|
|
*ldata ^= 0x5555555555;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
}
|
|
if (collided) {
|
|
clxdat |= 1;
|
|
}
|
|
}
|
|
|
|
/* Sprite-to-sprite collisions are taken care of in record_sprite. This one does
|
|
playfield/sprite collisions. */
|
|
static void do_sprite_collisions(int startpos, int endpos)
|
|
{
|
|
int nr_sprites = curr_drawinfo[next_lineno].nr_sprites;
|
|
int first = curr_drawinfo[next_lineno].first_sprite_entry;
|
|
uae_u32 collision_mask = clxmask[clxcon >> 12];
|
|
int bplres = output_res(bplcon0_res);
|
|
int ddf_left = (thisline_decision.plfleft - DDF_OFFSET) << bplres;
|
|
int hw_diwlast = coord_window_to_diw_x(endpos);
|
|
int hw_diwfirst = coord_window_to_diw_x(startpos);
|
|
|
|
if (clxcon_bpl_enable == 0 && !nr_sprites) {
|
|
return;
|
|
}
|
|
|
|
// all sprite to bitplane collision bits already set?
|
|
if ((clxdat & 0x1fe) == 0x1fe) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < nr_sprites; i++) {
|
|
struct sprite_entry *e = curr_sprite_entries + first + i;
|
|
sprbuf_res_t minpos = e->pos;
|
|
sprbuf_res_t maxpos = e->max;
|
|
int minp1 = minpos >> sprite_buffer_res;
|
|
int maxp1 = maxpos >> sprite_buffer_res;
|
|
|
|
if (maxp1 > hw_diwlast) {
|
|
maxp1 = hw_diwlast;
|
|
maxpos = hw_diwlast << sprite_buffer_res;
|
|
}
|
|
if (maxp1 > thisline_decision.plfright - DDF_OFFSET) {
|
|
maxpos = (thisline_decision.plfright - DDF_OFFSET) << sprite_buffer_res;
|
|
}
|
|
if (minp1 < hw_diwfirst) {
|
|
minp1 = hw_diwfirst;
|
|
minpos = hw_diwfirst << sprite_buffer_res;
|
|
}
|
|
if (minp1 < thisline_decision.plfleft - DDF_OFFSET) {
|
|
minpos = (thisline_decision.plfleft - DDF_OFFSET) << sprite_buffer_res;
|
|
}
|
|
|
|
for (sprbuf_res_t j = minpos; j < maxpos; j++) {
|
|
int sprpix = spixels[e->first_pixel + j - e->pos] & collision_mask;
|
|
int 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;
|
|
|
|
// both odd and even collision bits already set?
|
|
if (((clxdat & (sprpix << 0)) == (sprpix << 0)) && ((clxdat & (sprpix << 4)) == (sprpix << 4))) {
|
|
continue;
|
|
}
|
|
|
|
/* Loop over number of playfields. */
|
|
for (int k = 1; k >= 0; k--) {
|
|
#ifdef AGA
|
|
int planes = aga_mode ? 8 : 6;
|
|
#else
|
|
int planes = 6;
|
|
#endif
|
|
if (bplcon0 & 0x400) {
|
|
match = 1;
|
|
}
|
|
for (int l = k; match && l < planes; l += 2) {
|
|
int t = 0;
|
|
if (l < thisline_decision.nr_planes) {
|
|
uae_u32 *ldata = (uae_u32 *)(line_data[next_lineno] + 2 * l * MAX_WORDS_PER_LINE);
|
|
uae_u32 word = ldata[offs >> 5];
|
|
t = (word >> (31 - (offs & 31))) & 1;
|
|
#if 0 /* debug: draw collision mask */
|
|
if (1) {
|
|
for (int m = 0; m < 5; m++) {
|
|
ldata = (uae_u32 *)(line_data[next_lineno] + 2 * m * MAX_WORDS_PER_LINE);
|
|
ldata[(offs >> 5) + 1] |= 15 << (31 - (offs & 31));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (clxcon_bpl_enable & (1 << l)) {
|
|
if (t != ((clxcon_bpl_match >> l) & 1)) {
|
|
match = 0;
|
|
}
|
|
}
|
|
}
|
|
if (match) {
|
|
#if 0 /* debug: mark lines where collisions are detected */
|
|
if (0) {
|
|
int l;
|
|
for (l = 0; l < 5; l++) {
|
|
uae_u32 *ldata = (uae_u32 *)(line_data[next_lineno] + 2 * l * MAX_WORDS_PER_LINE);
|
|
ldata[(offs >> 5) + 1] |= 15 << (31 - (offs & 31));
|
|
}
|
|
}
|
|
#endif
|
|
clxdat |= sprpix << (k * 4);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
{
|
|
static int olx;
|
|
if (clxdat != olx)
|
|
write_log(_T("%d: %04X\n"), vpos, clxdat);
|
|
olx = clxdat;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void check_collisions(int hpos)
|
|
{
|
|
hpos += hpos_hsync_extra;
|
|
if (hpos < collision_hpos) {
|
|
return;
|
|
}
|
|
int startpos = thisline_decision.diwfirstword;
|
|
int endpos = thisline_decision.diwlastword;
|
|
if (startpos < 0 || endpos < 0) {
|
|
return;
|
|
}
|
|
int start = coord_diw_shres_to_window_x(collision_hpos << CCK_SHRES_SHIFT);
|
|
if (startpos < start) {
|
|
startpos = start;
|
|
}
|
|
int end = coord_diw_shres_to_window_x(hpos << CCK_SHRES_SHIFT);
|
|
if (endpos > end) {
|
|
endpos = end;
|
|
}
|
|
if (sprites_enabled_this_line || brdspractive()) {
|
|
if (currprefs.collision_level > 1) {
|
|
do_sprite_collisions(startpos, endpos);
|
|
}
|
|
}
|
|
if (currprefs.collision_level > 2) {
|
|
do_playfield_collisions(startpos, endpos);
|
|
}
|
|
collision_hpos = hpos;
|
|
}
|
|
|
|
static int tospritexdiw(int diw)
|
|
{
|
|
int v = (coord_window_to_hw_x(diw) - DIW_DDF_OFFSET) << sprite_buffer_res;
|
|
v -= (1 << sprite_buffer_res) - 1;
|
|
return v;
|
|
}
|
|
static int tospritexddf(int ddf)
|
|
{
|
|
return (ddf - DIW_DDF_OFFSET - DDF_OFFSET) << sprite_buffer_res;
|
|
}
|
|
static int fromspritexdiw(int ddf)
|
|
{
|
|
return coord_hw_to_window_x_lores(ddf >> sprite_buffer_res) + (DIW_DDF_OFFSET << lores_shift);
|
|
}
|
|
|
|
static 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)
|
|
{
|
|
uae_u16 erasemask = ~(3 << (2 * num));
|
|
int j = 0;
|
|
while (datab) {
|
|
unsigned int col = 0;
|
|
unsigned coltmp = 0;
|
|
|
|
if ((sprxp >= sprite_minx) || brdspractive() || hstrobe_conflict)
|
|
col = (datab & 3) << (2 * num);
|
|
#if 0
|
|
if (sprxp == sprite_minx)
|
|
col ^= (uaerand () << 16) | uaerand ();
|
|
#endif
|
|
if ((j & mask) == 0) {
|
|
unsigned int tmp = ((*buf) & erasemask) | col;
|
|
*buf++ = tmp;
|
|
if (do_collisions)
|
|
coltmp |= tmp;
|
|
sprxp++;
|
|
}
|
|
if (dbl > 0) {
|
|
unsigned int tmp = ((*buf) & erasemask) | col;
|
|
*buf++ = tmp;
|
|
if (do_collisions)
|
|
coltmp |= tmp;
|
|
sprxp++;
|
|
}
|
|
if (dbl > 1) {
|
|
unsigned int tmp;
|
|
tmp = ((*buf) & erasemask) | col;
|
|
*buf++ = tmp;
|
|
if (do_collisions)
|
|
coltmp |= tmp;
|
|
tmp = ((*buf) & erasemask) | 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 num, int sprxp, uae_u16 *data, uae_u16 *datb, unsigned int ctl)
|
|
{
|
|
struct sprite_entry *e = curr_sprite_entries + next_sprite_entry;
|
|
int word_offs;
|
|
uae_u32 collision_mask;
|
|
int width, dbl, half;
|
|
unsigned int mask = 0;
|
|
int attachment;
|
|
int spr_width;
|
|
|
|
// do nothing if buffer is full (shouldn't happen normally)
|
|
if (next_sprite_entry >= end_sprite_entry) {
|
|
return;
|
|
}
|
|
if (e->first_pixel >= spixels_max) {
|
|
return;
|
|
}
|
|
|
|
half = 0;
|
|
dbl = sprite_buffer_res - sprres;
|
|
if (dbl < 0) {
|
|
half = -dbl;
|
|
dbl = 0;
|
|
mask = 1 << half;
|
|
}
|
|
spr_width = spr[num].width;
|
|
width = (spr_width << sprite_buffer_res) >> sprres;
|
|
attachment = spr[num | 1].ctl & 0x80;
|
|
|
|
/* Try to coalesce entries if they aren't too far apart */
|
|
/* Don't coelesce 64-bit wide sprites, needed to support FMODE change tricks */
|
|
if (next_sprite_entry > 0 && !next_sprite_forced && e[-1].max + spr_width >= sprxp) {
|
|
e--;
|
|
} else {
|
|
next_sprite_entry++;
|
|
e->pos = sprxp;
|
|
e->has_attached = 0;
|
|
}
|
|
|
|
if (sprxp < e->pos) {
|
|
write_log(_T("sprxp (%d) < e->pos (%d)\n"), 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 (int i = 0; i < spr_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_u32 state = 0x01010101 << (num & ~1);
|
|
uae_u8 *stb1 = spixstate.stb + word_offs;
|
|
for (int 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;
|
|
}
|
|
/* 64 pixel wide sprites' first 32 pixels work differently than
|
|
* last 32 pixels if FMODE is changed when sprite is being drawn
|
|
*/
|
|
if (spr_width == 64) {
|
|
uae_u16 *stbfm = spixstate.stbfm + word_offs;
|
|
uae_u16 state = (3 << (2 * num));
|
|
for (int i = 0; i < width / 2; i += 8) {
|
|
stbfm[0] |= state;
|
|
stbfm[1] |= state;
|
|
stbfm[2] |= state;
|
|
stbfm[3] |= state;
|
|
stbfm[4] |= state;
|
|
stbfm[5] |= state;
|
|
stbfm[6] |= state;
|
|
stbfm[7] |= state;
|
|
stbfm += 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
sprites_enabled_this_line = true;
|
|
}
|
|
|
|
static void calcsprite(void)
|
|
{
|
|
sprite_minx = 0;
|
|
if (thisline_decision.diwfirstword >= 0) {
|
|
sprite_minx = tospritexdiw(thisline_decision.diwfirstword);
|
|
}
|
|
if (thisline_decision.plfleft >= 0) {
|
|
int min = tospritexddf(thisline_decision.plfleft);
|
|
int max = tospritexddf(thisline_decision.plfright);
|
|
if (min > sprite_minx && min < max) { /* min < max = full line ddf */
|
|
sprite_minx = min;
|
|
}
|
|
/* sprites are visible from first BPL1DAT write to end of line
|
|
* ECS Denise/AGA: no limits
|
|
* OCS Denise: BPL1DAT write only enables sprite if hpos >= 0x2e (OCS_DENISE_HBLANK_DISABLE_HPOS).
|
|
* (undocumented feature)
|
|
*/
|
|
}
|
|
}
|
|
|
|
static int last_sprite_hpos, last_sprite_hpos_reset;
|
|
|
|
static void decide_sprites2(int start, int end, int *countp, int *nrs, int *posns)
|
|
{
|
|
int count = *countp;
|
|
int sscanmask = 0x100 << sprite_buffer_res;
|
|
|
|
if (nodraw()) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < MAX_SPRITES; i++) {
|
|
struct sprite *s = &spr[i];
|
|
int xpos = spr[i].xpos;
|
|
int sprxp = (fmode & 0x8000) ? (xpos & ~sscanmask) : xpos;
|
|
int hw_xp = sprxp >> sprite_buffer_res;
|
|
|
|
// Sprite does not start if X=0 but SSCAN2 sprite does. Don't do X == 0 check here.
|
|
if (sprxp < 0) {
|
|
continue;
|
|
}
|
|
|
|
if (!((debug_sprite_mask & magic_sprite_mask) & (1 << i))) {
|
|
continue;
|
|
}
|
|
|
|
if (!spr[i].armed) {
|
|
continue;
|
|
}
|
|
|
|
// ECS Denise and superhires resolution: sprites 4 to 7 are disabled.
|
|
if (!aga_mode && ecs_denise && GET_RES_AGNUS(bplcon0) == RES_SUPERHIRES && i >= 4) {
|
|
continue;
|
|
}
|
|
|
|
if (s->ecs_denise_hires && (bplcon0d & 0x0040)) {
|
|
xpos |= 2 >> (RES_MAX - sprite_buffer_res);
|
|
sprxp = xpos;
|
|
hw_xp = sprxp >> sprite_buffer_res;
|
|
}
|
|
|
|
if (hw_xp >= start && hw_xp < end) {
|
|
int xdiff = hw_xp - start;
|
|
int sprxp_abs = (last_sprite_point_abs + xdiff) << sprite_buffer_res;
|
|
// add hires/shres back
|
|
sprxp_abs |= xpos & (3 >> (RES_MAX - sprite_buffer_res));
|
|
add_sprite(&count, i, sprxp_abs, posns, nrs);
|
|
}
|
|
|
|
/* SSCAN2-bit is fun.. */
|
|
if (1 && (fmode & 0x8000) && !(sprxp & sscanmask)) {
|
|
sprxp |= sscanmask;
|
|
hw_xp = sprxp >> sprite_buffer_res;
|
|
if (hw_xp >= start && hw_xp < end) {
|
|
int xdiff = hw_xp - start;
|
|
int sprxp_abs = (last_sprite_point_abs + xdiff) << sprite_buffer_res;
|
|
sprxp_abs |= xpos & (3 >> (RES_MAX - sprite_buffer_res));
|
|
add_sprite(&count, MAX_SPRITES + i, sprxp_abs, posns, nrs);
|
|
}
|
|
}
|
|
}
|
|
*countp = count;
|
|
|
|
|
|
}
|
|
|
|
static void decide_sprites(int hpos, bool quick, bool halfcycle = false)
|
|
{
|
|
int count = 0;
|
|
int gotdata = 0;
|
|
int nrs[MAX_SPRITES * 2], posns[MAX_SPRITES * 2];
|
|
int hp = (hpos << 1) + halfcycle;
|
|
|
|
// don't bother with sprites during vblank lines
|
|
if (vb_start_line > 1) {
|
|
last_sprite_hpos = hp;
|
|
return;
|
|
}
|
|
|
|
int c = hp - last_sprite_hpos;
|
|
if (c <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (!quick) {
|
|
decide_hdiw(hpos);
|
|
decide_line(hpos);
|
|
}
|
|
|
|
if (last_sprite_hpos < (HARDWIRED_DMA_TRIGGER_HPOS << 1) && hp >= (HARDWIRED_DMA_TRIGGER_HPOS << 1)) {
|
|
last_sprite_hpos_reset = (REFRESH_FIRST_HPOS - HARDWIRED_DMA_TRIGGER_HPOS + 2) << 1;
|
|
last_sprite_hpos_reset += 1;
|
|
}
|
|
|
|
int start = last_sprite_point;
|
|
int max = 512;
|
|
|
|
if (last_sprite_hpos_reset > 0 && c >= last_sprite_hpos_reset && !line_equ_freerun && !hstrobe_conflict) {
|
|
// strobe crossed?
|
|
last_sprite_point += last_sprite_hpos_reset + 0;
|
|
if (nr_armed) {
|
|
decide_sprites2(start, last_sprite_point, &count, nrs, posns);
|
|
}
|
|
last_sprite_point_abs += last_sprite_point - start;
|
|
last_sprite_point = 2;
|
|
start = last_sprite_point;
|
|
last_sprite_point += c - last_sprite_hpos_reset;
|
|
if (nr_armed) {
|
|
decide_sprites2(start, last_sprite_point, &count, nrs, posns);
|
|
}
|
|
last_sprite_point_abs += last_sprite_point - start;
|
|
last_sprite_hpos_reset = -1;
|
|
} else {
|
|
last_sprite_point += c;
|
|
// wrap around?
|
|
if (last_sprite_point >= max) {
|
|
if (nr_armed) {
|
|
decide_sprites2(start, max, &count, nrs, posns);
|
|
}
|
|
last_sprite_point_abs += max - start;
|
|
last_sprite_point &= max - 1;
|
|
if (last_sprite_point > 0) {
|
|
if (nr_armed) {
|
|
decide_sprites2(0, last_sprite_point, &count, nrs, posns);
|
|
}
|
|
last_sprite_point_abs += last_sprite_point - 0;
|
|
}
|
|
} else {
|
|
if (nr_armed) {
|
|
decide_sprites2(start, last_sprite_point, &count, nrs, posns);
|
|
}
|
|
last_sprite_point_abs += last_sprite_point - start;
|
|
}
|
|
if (last_sprite_hpos_reset >= 0) {
|
|
last_sprite_hpos_reset -= c;
|
|
}
|
|
}
|
|
last_sprite_hpos = hp;
|
|
|
|
if (count > 0) {
|
|
calcsprite();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
int nr = nrs[i] & (MAX_SPRITES - 1);
|
|
struct sprite *s = &spr[nr];
|
|
|
|
// ECS Denise weird behavior in shres
|
|
if (s->ecs_denise_hires && !(bplcon0d & 0x40)) {
|
|
s->data[0] &= 0x7fff;
|
|
s->datb[0] &= 0x7fff;
|
|
}
|
|
|
|
record_sprite(nr, posns[i], s->data, s->datb, s->ctl);
|
|
|
|
/* get left and right sprite edge if brdsprt enabled */
|
|
#if AUTOSCALE_SPRITES
|
|
if (dmaen(DMA_SPRITE) && brdspractive() && !(bplcon3 & 0x20) && nr > 0) {
|
|
int j, jj;
|
|
for (j = 0, jj = 0; j < sprite_width; j += 16, jj++) {
|
|
int nx = fromspritexdiw(posns[i] + j);
|
|
if (s->data[jj] || s->datb[jj]) {
|
|
if (diwfirstword_total > nx && nx >= (48 << currprefs.gfx_resolution)) {
|
|
diwfirstword_total = nx;
|
|
}
|
|
if (diwlastword_total < nx + 16 && nx <= (448 << currprefs.gfx_resolution)) {
|
|
diwlastword_total = nx + 16;
|
|
}
|
|
}
|
|
}
|
|
gotdata = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if AUTOSCALE_SPRITES
|
|
/* get upper and lower sprite position if brdsprt enabled */
|
|
if (gotdata) {
|
|
if (vpos < first_planes_vpos) {
|
|
first_planes_vpos = vpos;
|
|
}
|
|
if (vpos < plffirstline_total) {
|
|
plffirstline_total = vpos;
|
|
}
|
|
if (vpos > last_planes_vpos) {
|
|
last_planes_vpos = vpos;
|
|
}
|
|
if (vpos > plflastline_total) {
|
|
plflastline_total = vpos;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void decide_sprites(int hpos)
|
|
{
|
|
decide_sprites(hpos, false, false);
|
|
}
|
|
|
|
static int sprites_differ(struct draw_info *dip, struct draw_info *dip_old)
|
|
{
|
|
struct sprite_entry *this_first = curr_sprite_entries + dip->first_sprite_entry;
|
|
struct sprite_entry *this_last = curr_sprite_entries + dip->last_sprite_entry;
|
|
struct sprite_entry *prev_first = prev_sprite_entries + dip_old->first_sprite_entry;
|
|
|
|
if (dip->nr_sprites != dip_old->nr_sprites) {
|
|
return 1;
|
|
}
|
|
|
|
if (dip->nr_sprites == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
#if 0
|
|
int npixels;
|
|
for (int i = 0; i < dip->nr_sprites; i++) {
|
|
if (this_first[i].pos != prev_first[i].pos
|
|
|| this_first[i].max != prev_first[i].max
|
|
|| this_first[i].has_attached != prev_first[i].has_attached) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
npixels = this_last->first_pixel + (this_last->max - this_last->pos) - this_first->first_pixel;
|
|
if (memcmp(spixels + this_first->first_pixel, spixels + prev_first->first_pixel, npixels * sizeof(uae_u16)) != 0) {
|
|
return 1;
|
|
}
|
|
if (memcmp(spixstate.stb + this_first->first_pixel, spixstate.stb + prev_first->first_pixel, npixels) != 0) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static bool color_changes_differ(struct draw_info *dip, struct draw_info *dip_old)
|
|
{
|
|
if (dip->nr_color_changes != dip_old->nr_color_changes) {
|
|
return true;
|
|
}
|
|
|
|
if (dip->nr_color_changes == 0) {
|
|
return false;
|
|
}
|
|
if (memcmp(curr_color_changes + dip->first_color_change,
|
|
prev_color_changes + dip_old->first_color_change,
|
|
dip->nr_color_changes * sizeof * curr_color_changes) != 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* End of a horizontal scan line. Finish off all decisions that were not
|
|
* made yet. */
|
|
|
|
static void sync_copper(int hpos);
|
|
|
|
static void finish_partial_decision(int hpos)
|
|
{
|
|
sync_copper(hpos);
|
|
decide_hdiw(hpos);
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
decide_sprites(hpos);
|
|
if (thisline_decision.plfleft >= 0 && thisline_decision.plfright < (hpos + hpos_hsync_extra) * 2) {
|
|
thisline_decision.plfright = (hpos + hpos_hsync_extra) * 2;
|
|
}
|
|
}
|
|
|
|
static void hstrobe_conflict_check(uae_u32 v)
|
|
{
|
|
int hpos = current_hpos();
|
|
finish_partial_decision(hpos);
|
|
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
uae_u16 datreg = cycle_line_pipe[hpos];
|
|
// not conflict?
|
|
if (!(datreg & CYCLE_PIPE_BITPLANE)) {
|
|
hstrobe_conflict = false;
|
|
int pos = hpos_to_diwx(hpos);
|
|
addcc(pos, 0, COLOR_CHANGE_ACTBORDER | 2);
|
|
MARK_LINE_CHANGED;
|
|
}
|
|
}
|
|
|
|
static void finish_decisions(int hpos)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
struct draw_info *dip;
|
|
struct draw_info *dip_old;
|
|
struct decision *dp;
|
|
int changed;
|
|
|
|
// update decisions to start of hsync
|
|
finish_partial_decision(hpos);
|
|
|
|
finish_final_fetch(hpos);
|
|
|
|
if (thisline_decision.plfleft >= 0 && thisline_decision.plflinelen < 0) {
|
|
thisline_decision.plfright = thisline_decision.plfleft;
|
|
thisline_decision.plflinelen = 0;
|
|
thisline_decision.bplres = output_res(RES_LORES);
|
|
}
|
|
|
|
#if 0
|
|
if (hstrobe_conflict) {
|
|
sync_color_changes(hpos + DDF_OFFSET / 2 + 1);
|
|
decide_hstrobe_hdiw(hpos);
|
|
}
|
|
#endif
|
|
|
|
// Add DDF_OFFSET to detect blanking changes past hpos max
|
|
sync_color_changes(hpos + DDF_OFFSET / 2 + 1);
|
|
|
|
/* 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_states::DIW_waiting_stop && thisline_decision.diwfirstword >= 0) || hstrobe_conflict) {
|
|
thisline_decision.diwlastword = max_diwlastword;
|
|
}
|
|
|
|
if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword) {
|
|
MARK_LINE_CHANGED;
|
|
}
|
|
if (thisline_decision.diwlastword != line_decisions[next_lineno].diwlastword) {
|
|
MARK_LINE_CHANGED;
|
|
}
|
|
|
|
dip = curr_drawinfo + next_lineno;
|
|
dip_old = prev_drawinfo + next_lineno;
|
|
dp = line_decisions + next_lineno;
|
|
changed = thisline_changed | ad->custom_frame_redraw_necessary;
|
|
if (thisline_decision.plfleft >= 0 && thisline_decision.nr_planes > 0) {
|
|
record_diw_line(thisline_decision.plfleft, diwfirstword, diwlastword);
|
|
}
|
|
|
|
dip->last_sprite_entry = next_sprite_entry - 1;
|
|
dip->last_color_change = next_color_change;
|
|
|
|
if (thisline_decision.ctable < 0) {
|
|
if (thisline_decision.plfleft < 0) {
|
|
remember_ctable_for_border();
|
|
} else {
|
|
remember_ctable();
|
|
}
|
|
}
|
|
|
|
dip->nr_color_changes = next_color_change - dip->first_color_change;
|
|
if (dip->nr_color_changes < 0) {
|
|
write_log("negative nr_color_changes: %d. FIXME!\n", dip->nr_color_changes);
|
|
dip->nr_color_changes = 0;
|
|
}
|
|
dip->nr_sprites = next_sprite_entry - dip->first_sprite_entry;
|
|
|
|
if (thisline_decision.plfleft != line_decisions[next_lineno].plfleft) {
|
|
changed = 1;
|
|
}
|
|
if (!changed && color_changes_differ(dip, dip_old)) {
|
|
changed = 1;
|
|
}
|
|
if (!changed && /* bitplane visible in this line OR border sprites enabled */
|
|
(thisline_decision.plfleft >= 0 || ((thisline_decision.bplcon0 & 1) && (thisline_decision.bplcon3 & 0x02) && !(thisline_decision.bplcon3 & 0x20)))
|
|
&& sprites_differ(dip, dip_old))
|
|
{
|
|
changed = 1;
|
|
}
|
|
|
|
if (changed) {
|
|
thisline_changed = 1;
|
|
*dp = thisline_decision;
|
|
} else {
|
|
/* The only one that may differ: */
|
|
dp->ctable = thisline_decision.ctable;
|
|
}
|
|
|
|
check_collisions(hpos);
|
|
|
|
if (next_color_change >= MAX_REG_CHANGE - 30) {
|
|
write_log(_T("color_change buffer overflow!\n"));
|
|
next_color_change = 0;
|
|
last_color_change = 0;
|
|
dip->nr_color_changes = 0;
|
|
dip->first_color_change = 0;
|
|
dip->last_color_change = 0;
|
|
}
|
|
}
|
|
|
|
/* Set the state of all decisions to "undecided" for a new scanline. */
|
|
static void reset_decisions_scanline_start(void)
|
|
{
|
|
if (nodraw())
|
|
return;
|
|
|
|
if (hstrobe_conflict) {
|
|
event2_newevent_xx(-1, REFRESH_FIRST_HPOS * CYCLE_UNIT, 0, hstrobe_conflict_check);
|
|
}
|
|
|
|
last_decide_line_hpos = 0;
|
|
last_decide_sprite_hpos = 0;
|
|
last_fetch_hpos = 0;
|
|
last_copper_hpos = 0;
|
|
ddfstrt_hpos = -1;
|
|
ddfstop_hpos = -1;
|
|
last_diw_hpos = 0;
|
|
last_sprite_hpos = 0;
|
|
last_sprite_hpos_reset = 0;
|
|
blt_info.finishhpos = -1;
|
|
|
|
/* Default to no bitplane DMA overriding sprite DMA */
|
|
plfstrt_sprite = 0x100;
|
|
sprbplconflict_hpos = -1;
|
|
sprbplconflict_hpos2 = -1;
|
|
bprun_end = 0;
|
|
|
|
// clear sprite allocations
|
|
for (int i = 0; i < maxhposm0; i++) {
|
|
uae_u16 v = cycle_line_pipe[i];
|
|
if (v & CYCLE_PIPE_SPRITE) {
|
|
cycle_line_pipe[i] = 0;
|
|
}
|
|
}
|
|
|
|
if (!ecs_denise) {
|
|
if (!ecs_agnus) {
|
|
if (vb_start_line == 2 + vblank_extraline) {
|
|
record_color_change2(0, 0, COLOR_CHANGE_BLANK | 1);
|
|
}
|
|
} else {
|
|
if (vb_start_line == 1 + vblank_extraline) {
|
|
record_color_change2(0, 0, COLOR_CHANGE_BLANK | 1);
|
|
}
|
|
}
|
|
if (vb_end_next_line2) {
|
|
record_color_change2(0, 0, COLOR_CHANGE_BLANK | 0);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void get_strobe_conflict_shift(int hpos)
|
|
{
|
|
// Because Denise hcounter is not synced to Agnus hcounter,
|
|
// BPLCON1 offset changes every line, causing jagged display.
|
|
int unalign = hpos * 2 + hdiw_counter;
|
|
unalign /= 2;
|
|
unalign -= 4;
|
|
unalign &= 7;
|
|
unalign *= 2;
|
|
delay_cycles = unalign << LORES_TO_SHRES_SHIFT;
|
|
}
|
|
|
|
static uae_u16 BPLCON0_Denise_mask(uae_u16 v)
|
|
{
|
|
if (!ecs_denise) {
|
|
v &= ~0x00F1;
|
|
} else if (!aga_mode) {
|
|
v &= ~0x00B0;
|
|
}
|
|
|
|
v &= ~((currprefs.cs_color_burst ? 0x0000 : 0x0200) | 0x0100 | 0x0080 | 0x0020);
|
|
#if SPRBORDER
|
|
v |= 1;
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u16 BPLCON0_Agnus_mask(uae_u16 v)
|
|
{
|
|
if (!ecs_agnus) {
|
|
v &= 0xff0f;
|
|
} else if (!aga_mode) {
|
|
v &= 0xffcf;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static void reset_decisions_hsync_start(void)
|
|
{
|
|
if (nodraw()) {
|
|
return;
|
|
}
|
|
|
|
int hpos = current_hpos();
|
|
|
|
thisline_decision.diwfirstword = -1;
|
|
thisline_decision.diwlastword = -1;
|
|
// hdiw already open?
|
|
if (hdiwstate == diw_states::DIW_waiting_stop) {
|
|
thisline_decision.diwfirstword = min_diwlastword;
|
|
if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword) {
|
|
MARK_LINE_CHANGED;
|
|
}
|
|
}
|
|
|
|
thisline_decision.ctable = -1;
|
|
thisline_changed = 0;
|
|
|
|
curr_drawinfo[next_lineno].first_color_change = next_color_change;
|
|
last_color_change = next_color_change;
|
|
curr_drawinfo[next_lineno].first_sprite_entry = next_sprite_entry;
|
|
next_sprite_forced = 1;
|
|
last_sprite_point_abs = ((1 + (hpos - REFRESH_FIRST_HPOS - 2)) << 1) + 1;
|
|
hdiw_denisecounter_abs = ((1 + (hpos - REFRESH_FIRST_HPOS - 2)) << CCK_SHRES_SHIFT) + 0;
|
|
|
|
sprites_enabled_this_line = false;
|
|
bpl1mod_hpos = -1;
|
|
bpl2mod_hpos = -1;
|
|
last_recorded_diw_hpos = 0;
|
|
collision_hpos = 0;
|
|
|
|
vhposr_delay_offset = 0;
|
|
vhposw_modified = false;
|
|
|
|
compute_toscr_delay(bplcon1);
|
|
|
|
last_diwlastword = -1;
|
|
hb_last_diwlastword = -1;
|
|
|
|
if (line_cyclebased > 0) {
|
|
line_cyclebased--;
|
|
}
|
|
|
|
if (toscr_scanline_complex_bplcon1_off) {
|
|
toscr_scanline_complex_bplcon1_off = false;
|
|
toscr_scanline_complex_bplcon1 = false;
|
|
}
|
|
|
|
#if BPL_ERASE_TEST
|
|
for (int n = 0; n < 8; n++) {
|
|
static uae_u8 v;
|
|
v += 2;
|
|
uae_u8 *dataptr = line_data[next_lineno] + n * MAX_WORDS_PER_LINE * 2;
|
|
memset(dataptr, v ^ vpos, MAX_WORDS_PER_LINE * 2);
|
|
}
|
|
#endif
|
|
|
|
if (toscr_nr_planes3 < toscr_nr_planes2 && toscr_nr_planes3 > 0 && out_offs > 0) {
|
|
for (int n = toscr_nr_planes3; n < toscr_nr_planes2; n++) {
|
|
uae_u8 *dataptr = line_data[next_lineno] + n * MAX_WORDS_PER_LINE * 2;
|
|
memset(dataptr, 0, out_offs * 4);
|
|
}
|
|
}
|
|
|
|
reset_bpl_vars();
|
|
|
|
/* These are for comparison. */
|
|
thisline_decision.bplcon0 = bplcon0;
|
|
thisline_decision.bplcon2 = bplcon2;
|
|
#ifdef ECS_DENISE
|
|
thisline_decision.bplcon3 = bplcon3;
|
|
#endif
|
|
#ifdef AGA
|
|
thisline_decision.bplcon4bm = bplcon4;
|
|
thisline_decision.bplcon4sp = bplcon4;
|
|
thisline_decision.fmode = fmode;
|
|
|
|
if (!aga_mode && ecs_denise && exthblank) {
|
|
// ECS Denise + EXTBLANK: VBLANK blanking is different
|
|
thisline_decision.vb = VB_NOVB;
|
|
if (new_beamcon0 & BEAMCON0_BLANKEN) {
|
|
// blanking working same as AGA
|
|
thisline_decision.vb = vb_start_line >= 1 + vblank_extraline || vb_end_next_line ? 0 : VB_NOVB;
|
|
}
|
|
} else if (ecs_agnus) {
|
|
// Visible vblank end is delayed by 1 line
|
|
thisline_decision.vb = vb_start_line >= 1 + vblank_extraline || vb_end_next_line ? 0 : VB_NOVB;
|
|
} else {
|
|
thisline_decision.vb = vb_start_line >= 2 + vblank_extraline || vb_end_next_line ? 0 : VB_NOVB;
|
|
}
|
|
|
|
// if programmed vblank
|
|
if ((new_beamcon0 & BEAMCON0_VARVBEN) && ecs_agnus) {
|
|
if (!thisline_decision.vb) {
|
|
thisline_decision.vb |= VB_PRGVB;
|
|
}
|
|
}
|
|
|
|
// doublescan last line garbage workaround
|
|
if (doflickerfix_active()) {
|
|
if (!(thisline_decision.vb & VB_PRGVB)) {
|
|
if ((vpos < maxvpos_display_vsync) || (lof_display && vpos >= maxvpos - 1) || (!lof_display && vpos >= maxvpos - 1)) {
|
|
thisline_decision.vb = VB_XBORDER;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (currprefs.gfx_overscanmode >= OVERSCANMODE_ULTRA) {
|
|
if (thisline_decision.vb != VB_NOVB) {
|
|
thisline_decision.vb = VB_VB | VB_NOVB;
|
|
}
|
|
if ((vs_state_var && (new_beamcon0 & BEAMCON0_VARVSYEN)) || (vs_state_hw && !(new_beamcon0 & BEAMCON0_VARVSYEN))) {
|
|
thisline_decision.vb |= VB_VS;
|
|
}
|
|
}
|
|
if (nosignal_status == 1) {
|
|
thisline_decision.vb = VB_XBLANK;
|
|
MARK_LINE_CHANGED;
|
|
} else if (nosignal_status < 0) {
|
|
MARK_LINE_CHANGED;
|
|
}
|
|
|
|
int left = thisline_decision.plfleft;
|
|
|
|
bpl_shifter = 0;
|
|
thisline_decision.plfleft = -1;
|
|
thisline_decision.plflinelen = -1;
|
|
thisline_decision.plfright = -1;
|
|
thisline_decision.ham_seen = isham(bplcon0);
|
|
thisline_decision.ehb_seen = isehb(bplcon0, bplcon2);
|
|
thisline_decision.ham_at_start = (bplcon0 & 0x800) != 0;
|
|
thisline_decision.bordersprite_seen = issprbrd(-1, bplcon0, bplcon3);
|
|
thisline_decision.xor_seen = (bplcon4 & 0xff00) != 0;
|
|
thisline_decision.nr_planes = toscr_nr_planes_agnus;
|
|
|
|
// workaround for glitches in faster modes
|
|
// update Denise state immediately if bitplane DMA is idle and shifters are empty
|
|
if (!bprun && !plane0 && !plane0p) {
|
|
uae_u16 bcon0 = BPLCON0_Denise_mask(bplcon0);
|
|
toscr_nr_planes_shifter = GET_PLANES(bcon0);
|
|
}
|
|
|
|
toscr_nr_planes2 = GET_PLANES(bplcon0d);
|
|
if (isocs7planes()) {
|
|
if (toscr_nr_planes2 < 6) {
|
|
toscr_nr_planes2 = 6;
|
|
}
|
|
}
|
|
toscr_nr_planes3 = toscr_nr_planes2;
|
|
toscr_nr_changed = false;
|
|
|
|
thisline_decision.max_planes = toscr_nr_planes2 > toscr_nr_planes_agnus ? toscr_nr_planes2 : toscr_nr_planes_agnus;
|
|
|
|
hpos_hsync_extra = 0;
|
|
bool normalstart = true;
|
|
plane0p_enabled = ecs_denise && (bplcon0 & 1) && (bplcon3 & 0x20);
|
|
plane0p_forced = false;
|
|
plane0_first_done = false;
|
|
speedup_first = true;
|
|
delay_cycles = ((hpos) * 2 - DDF_OFFSET + 0) << LORES_TO_SHRES_SHIFT;
|
|
delay_cycles2 = delay_cycles;
|
|
set_delay_lastcycle();
|
|
if (hstrobe_conflict) {
|
|
get_strobe_conflict_shift(hpos);
|
|
}
|
|
|
|
if (1 && fetchmode >= 2 && !doflickerfix_active()) {
|
|
// handle bitplane data wrap around
|
|
bool toshift = false;
|
|
if ((exthblank || (beamcon0 & bemcon0_hsync_mask)) && (thisline_decision.bplres == 0 || currprefs.chipset_hr)) {
|
|
for (int i = 0; i < thisline_decision.nr_planes; i++) {
|
|
if (todisplay2_aga_saved[i]) {
|
|
toshift = true;
|
|
}
|
|
}
|
|
}
|
|
if (bprun != 0 || todisplay_fetched || plane0 || plane0p || toshift) {
|
|
normalstart = false;
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
if (toscr_hend == 2) {
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
todisplay[i] = todisplay_saved[i];
|
|
todisplay_aga[i] = todisplay_aga_saved[i];
|
|
todisplay2[i] = todisplay2_saved[i];
|
|
todisplay2_aga[i] = todisplay2_aga_saved[i];
|
|
}
|
|
todisplay_fetched = todisplay_fetched_saved;
|
|
}
|
|
if (plane0p) {
|
|
beginning_of_plane_block_early(hpos);
|
|
plane0p = false;
|
|
}
|
|
if (plane0) {
|
|
beginning_of_plane_block(hpos);
|
|
plane0 = false;
|
|
}
|
|
// make sure bprun stays enabled until next line
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
plane0p_enabled = true;
|
|
plane0p_forced = true;
|
|
bpl_shifter = -1;
|
|
}
|
|
}
|
|
|
|
// HBLANK start before HSYNC (or HBLANK never triggered) and BPL1DAT between HBLANK and HSYNC?
|
|
if (exthblank) {
|
|
int hblankstart = -1;
|
|
if (last_hblank_start >= 0) {
|
|
hblankstart = diw_to_hpos(last_hblank_start + 4);
|
|
} else if (exthblank_state) {
|
|
hblankstart = last_bpl1dat_hpos - 1;
|
|
} else {
|
|
// AGA only, ECS Denise sees HBLANK in CSYNC blank line which always opens border.
|
|
if (aga_mode) {
|
|
// no hblank start: Border stays in closed state
|
|
start_noborder(hpos);
|
|
}
|
|
}
|
|
if (hblankstart >= 0) {
|
|
int bpl1 = last_bpl1dat_hpos;
|
|
if (bpl1 < hblankstart) {
|
|
bpl1 += maxhpos;
|
|
}
|
|
int hp = hpos;
|
|
if (hp < hblankstart || hp < bpl1) {
|
|
hp += maxhpos;
|
|
}
|
|
int comp = maxhpos / 2 - 1;
|
|
if (bpl1 > hblankstart && bpl1 < hp && (bpl1 - hblankstart) < comp && (bpl1 - hp) < comp) {
|
|
// Close border. (HBLANK start opens border, BPL1DAT closes it)
|
|
start_noborder(hpos);
|
|
}
|
|
}
|
|
}
|
|
// vblank end and bitplane was active inside vblank: borderblank bug
|
|
if (ecs_agnus && ecs_denise && vb_end_next_line2 && last_bpl1dat_hpos >= 0) {
|
|
start_noborder(hpos);
|
|
}
|
|
|
|
if (last_bpl1dat_hpos_early && !bpl_shifter) {
|
|
// BPLxDAT access already happened between refresh slot and hsync (this hpos):
|
|
// Close border
|
|
start_noborder(hpos);
|
|
}
|
|
|
|
if (normalstart) {
|
|
plane0 = false;
|
|
plane0p = false;
|
|
memset(outword, 0, sizeof outword);
|
|
// fetched[] must not be cleared (Sony VX-90 / Royal Amiga Force)
|
|
todisplay_fetched = 0;
|
|
//memset(todisplay, 0, sizeof todisplay);
|
|
memset(todisplay2, 0, sizeof todisplay2);
|
|
#ifdef AGA
|
|
if (aga_mode || ALL_SUBPIXEL) {
|
|
//memset(todisplay_aga, 0, sizeof todisplay_aga);
|
|
memset(todisplay2_aga, 0, sizeof todisplay2_aga);
|
|
memset(outword64, 0, sizeof outword64);
|
|
memset(outword64_extra, 0, sizeof outword64_extra);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma && (new_beamcon0 & bemcon0_hsync_mask)) {
|
|
record_dma_event(DMA_EVENT_HSS, hpos, vpos);
|
|
record_dma_event(DMA_EVENT_HSE, hsstop, vpos);
|
|
}
|
|
#endif
|
|
|
|
toscr_hend = 0;
|
|
last_bpl1dat_hpos = -1;
|
|
last_bpl1dat_hpos_early = false;
|
|
last_hblank_start = -1;
|
|
hcenterblank_state = false;
|
|
hstrobe_hdiw_min = hsyncstartpos_start_cycles;
|
|
if (hstrobe_conflict) {
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
}
|
|
|
|
frame_time_t vsynctimebase_orig;
|
|
|
|
void compute_vsynctime(void)
|
|
{
|
|
float svpos = maxvpos_nom + 0.0f;
|
|
float shpos = maxhpos_short + 0.0f;
|
|
float syncadjust = 1.0;
|
|
|
|
fake_vblank_hz = 0;
|
|
vblank_hz_mult = 0;
|
|
vblank_hz_state = 1;
|
|
if (fabs (currprefs.chipset_refreshrate) > 0.1) {
|
|
syncadjust = currprefs.chipset_refreshrate / vblank_hz_nom;
|
|
vblank_hz = currprefs.chipset_refreshrate;
|
|
if (isvsync_chipset() && !currprefs.gfx_variable_sync) {
|
|
int mult = 0;
|
|
if (getvsyncrate(0, vblank_hz, &mult) != vblank_hz) {
|
|
vblank_hz = getvsyncrate(0, vblank_hz, &vblank_hz_mult);
|
|
if (vblank_hz_mult > 0) {
|
|
vblank_hz_state = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!fake_vblank_hz)
|
|
fake_vblank_hz = vblank_hz;
|
|
|
|
if (currprefs.turbo_emulation) {
|
|
if (currprefs.turbo_emulation_limit > 0) {
|
|
vsynctimebase = (frame_time_t)(syncbase / currprefs.turbo_emulation_limit);
|
|
} else {
|
|
vsynctimebase = 1;
|
|
}
|
|
} else {
|
|
vsynctimebase = (frame_time_t)(syncbase / fake_vblank_hz);
|
|
}
|
|
vsynctimebase_orig = vsynctimebase;
|
|
|
|
if (islinetoggle()) {
|
|
shpos += 0.5f;
|
|
}
|
|
if (interlace_seen) {
|
|
svpos += 0.5f;
|
|
} else if (lof_display) {
|
|
svpos += 1.0f;
|
|
}
|
|
if (currprefs.produce_sound > 1) {
|
|
float clk = svpos * shpos * fake_vblank_hz;
|
|
write_log(_T("SNDRATE %.1f*%.1f*%.6f=%.6f\n"), svpos, shpos, fake_vblank_hz, clk);
|
|
devices_update_sound(clk, syncadjust);
|
|
}
|
|
devices_update_sync(svpos, syncadjust);
|
|
}
|
|
|
|
void getsyncregisters(uae_u16 *phsstrt, uae_u16 *phsstop, uae_u16 *pvsstrt, uae_u16 *pvsstop)
|
|
{
|
|
*phsstrt = hsstrt;
|
|
*phsstop = hsstop_detect2 / 2;
|
|
*pvsstrt = vsstrt;
|
|
*pvsstop = vsstop;
|
|
}
|
|
|
|
static void dumpsync(void)
|
|
{
|
|
static int cnt = 100;
|
|
if (cnt < 0) {
|
|
return;
|
|
}
|
|
cnt--;
|
|
write_log(_T("BEAMCON0=%04X VTOTAL=%03d HTOTAL=%03d (%03X)\n"), new_beamcon0, vtotal, htotal, htotal);
|
|
write_log(_T(" HS=%04X-%04X HB=%04X-%04X HC=%04X\n"), hsstrt, hsstop, hbstrt, hbstop, hcenter);
|
|
write_log(_T(" VS=%04d-%04d VB=%04d-%04d\n"), vsstrt, vsstop, vbstrt, vbstop);
|
|
write_log(_T(" HSYNCSTART=%04X.%d HSYNCEND=%04X.%d\n"),
|
|
hsyncstartpos >> CCK_SHRES_SHIFT, hsyncstartpos & ((1 << CCK_SHRES_SHIFT) - 1),
|
|
hsyncendpos >> CCK_SHRES_SHIFT, hsyncendpos & ((1 << CCK_SHRES_SHIFT) - 1));
|
|
write_log(_T(" Lines=%d-%d\n"),
|
|
minfirstline, maxvpos_display + maxvpos_display_vsync);
|
|
write_log(_T("PC=%08x COP=%08x\n"), M68K_GETPC, cop_state.ip);
|
|
}
|
|
|
|
int current_maxvpos(void)
|
|
{
|
|
return maxvpos + (lof_store ? 1 : 0);
|
|
}
|
|
|
|
#if 0
|
|
static void checklacecount(bool lace)
|
|
{
|
|
if (!interlace_changed) {
|
|
if (nlace_cnt >= NLACE_CNT_NEEDED && lace) {
|
|
lof_togglecnt_lace = LOF_TOGGLES_NEEDED;
|
|
lof_togglecnt_nlace = 0;
|
|
//write_log (_T("immediate lace\n"));
|
|
nlace_cnt = 0;
|
|
} else if (nlace_cnt <= -NLACE_CNT_NEEDED && !lace) {
|
|
lof_togglecnt_nlace = LOF_TOGGLES_NEEDED;
|
|
lof_togglecnt_lace = 0;
|
|
//write_log (_T("immediate nlace\n"));
|
|
nlace_cnt = 0;
|
|
}
|
|
}
|
|
if (lace) {
|
|
if (nlace_cnt > 0)
|
|
nlace_cnt = 0;
|
|
nlace_cnt--;
|
|
if (nlace_cnt < -NLACE_CNT_NEEDED * 2)
|
|
nlace_cnt = -NLACE_CNT_NEEDED * 2;
|
|
} else if (!lace) {
|
|
if (nlace_cnt < 0)
|
|
nlace_cnt = 0;
|
|
nlace_cnt++;
|
|
if (nlace_cnt > NLACE_CNT_NEEDED * 2)
|
|
nlace_cnt = NLACE_CNT_NEEDED * 2;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void set_hcenter(void)
|
|
{
|
|
hcenter_sync_v2 = (hcenter & 0xff) << CCK_SHRES_SHIFT;
|
|
if (!aga_mode && ecs_denise) {
|
|
if (beamcon0 & bemcon0_vsync_mask) {
|
|
hcenter_v2 = (hcenter & 0xff) << CCK_SHRES_SHIFT;
|
|
} else {
|
|
hcenter_v2 = 132 << CCK_SHRES_SHIFT;
|
|
}
|
|
uae_u16 hc = hcenter_v2;
|
|
hcenter_v2 -= 3;
|
|
if (hc >= (1 << CCK_SHRES_SHIFT) && hcenter_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hcenter_v2 += maxhpos << CCK_SHRES_SHIFT;
|
|
}
|
|
hcenter_v2 = adjust_hr(hcenter_v2);
|
|
if (hcenter_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hcenter_v2 = 0;
|
|
}
|
|
hcenter_v2_end = hcenter_v2;
|
|
hcenter_v2_end += ((beamcon0 & BEAMCON0_PAL) ? 8 : 9) << CCK_SHRES_SHIFT;
|
|
if (hcenter_v2_end >= maxhpos << CCK_SHRES_SHIFT) {
|
|
hcenter_v2_end -= maxhpos << CCK_SHRES_SHIFT;
|
|
}
|
|
hcenter_active = true;
|
|
} else {
|
|
if (beamcon0 & bemcon0_vsync_mask) {
|
|
hcenter_v2 = (hcenter & 0xff) << CCK_SHRES_SHIFT;
|
|
} else {
|
|
hcenter_v2 = 132 << CCK_SHRES_SHIFT;
|
|
}
|
|
hcenter_active = false;
|
|
}
|
|
}
|
|
|
|
static void updateextblk(void)
|
|
{
|
|
hsyncstartpos_start_hw = 13;
|
|
hsyncstartpos_hw = maxhpos_short + hsyncstartpos_start_hw;
|
|
hsyncendpos_hw = 24;
|
|
|
|
hbstrt_v = (hbstrt & 0xff) << CCK_SHRES_SHIFT;
|
|
hbstop_v = (hbstop & 0xff) << CCK_SHRES_SHIFT;
|
|
// Agnus/Alice side HBxxxx, used by VARCSY
|
|
hbstrt_sync_v2 = hbstrt_v;
|
|
hbstop_sync_v2 = hbstop_v;
|
|
|
|
if (aga_mode) {
|
|
// AGA horizontal blanking is simple
|
|
hbstrt_v |= (hbstrt >> 8) & 7;
|
|
hbstop_v |= (hbstop >> 8) & 7;
|
|
// hblank has 1 lores pixel offset
|
|
hbstrt_v2 = hbstrt_v - 4;
|
|
if (hbstrt_v >= (1 << CCK_SHRES_SHIFT) && hbstrt_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hbstrt_v2 += maxhpos_short << CCK_SHRES_SHIFT;
|
|
}
|
|
hbstop_v2 = hbstop_v - 4;
|
|
if (hbstop_v >= (1 << CCK_SHRES_SHIFT) && hbstop_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hbstop_v2 += maxhpos_short << CCK_SHRES_SHIFT;
|
|
}
|
|
exthblank = (bplcon0 & 1) && (bplcon3 & 1);
|
|
} else {
|
|
// ECS Denise CSYNC horizontal blanking is complex
|
|
if (new_beamcon0 & BEAMCON0_VARCSYEN) {
|
|
// ECS Denise HBLANK uses CSYNC input and matches programmed HSYNC period.
|
|
hbstrt_v2 = (hsstrt & 0xff) << CCK_SHRES_SHIFT;
|
|
hbstop_v2 = (hsstop & 0xff) << CCK_SHRES_SHIFT;
|
|
} else if (new_beamcon0 & BEAMCON0_BLANKEN) {
|
|
// HBSTRT/HBSTOP blanking
|
|
hbstrt_v2 = hbstrt_v;
|
|
hbstop_v2 = hbstop_v;
|
|
} else {
|
|
// ECS Denise HBLANK uses CSYNC input and matches hardwired HSYNC period.
|
|
hbstrt_v2 = 18 << CCK_SHRES_SHIFT;
|
|
hbstop_v2 = 35 << CCK_SHRES_SHIFT;
|
|
}
|
|
if (new_beamcon0 & BEAMCON0_CSYTRUE) {
|
|
uae_u16 tmp = hbstrt_v2;
|
|
hbstrt_v2 = hbstop_v2;
|
|
hbstop_v2 = tmp;
|
|
}
|
|
hbstrt_v = hbstrt_v2;
|
|
hbstop_v = hbstop_v2;
|
|
// hblank has 1 lores pixel offset
|
|
hbstrt_v2 -= 4;
|
|
hbstop_v2 -= 4;
|
|
if (hbstrt_v >= (1 << CCK_SHRES_SHIFT) && hbstrt_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hbstrt_v2 += maxhpos_short << CCK_SHRES_SHIFT;
|
|
}
|
|
if (hbstop_v >= (1 << CCK_SHRES_SHIFT) && hbstop_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hbstop_v2 += maxhpos_short << CCK_SHRES_SHIFT;
|
|
}
|
|
exthblank = (bplcon0 & 1) && (bplcon3 & 1);
|
|
}
|
|
|
|
if (!exthblank) {
|
|
hbstrt_v2 = (8 << CCK_SHRES_SHIFT) - 3;
|
|
hbstop_v2 = (47 << CCK_SHRES_SHIFT) - 7;
|
|
hbstrt_v2 = adjust_hr(hbstrt_v2);
|
|
hbstop_v2 = adjust_hr(hbstop_v2);
|
|
}
|
|
|
|
hbstrt_reg = hbstrt_v2;
|
|
hbstop_reg = hbstop_v2;
|
|
|
|
if ((new_beamcon0 & bemcon0_hsync_mask) && (!currprefs.monitoremu || currprefs.cs_hvcsync > 0)) {
|
|
|
|
hsyncstartpos = hsstrt + 2;
|
|
|
|
if (hsyncstartpos >= maxhpos_short) {
|
|
hsyncstartpos -= maxhpos_short;
|
|
}
|
|
hsyncendpos = hsstop;
|
|
|
|
hsstop_detect2 = (hsstrt + 21) * 2;
|
|
if (hsstop_detect2 >= maxhpos_short * 2) {
|
|
hsstop_detect2 -= maxhpos_short * 2;
|
|
}
|
|
|
|
hsyncstartpos_start = hsyncstartpos;
|
|
if (hsyncstartpos < maxhpos_short / 2) {
|
|
hsyncstartpos = maxhpos_short + hsyncstartpos_start;
|
|
denisehtotal = hsyncstartpos;
|
|
} else {
|
|
denisehtotal = maxhpos_short + hsyncstartpos;
|
|
}
|
|
// make sure possible BPL DMA cycles before first refresh slot are processed before hsync
|
|
if (hsyncstartpos_start < REFRESH_FIRST_HPOS + 1) {
|
|
hsyncstartpos_start = REFRESH_FIRST_HPOS + 1;
|
|
}
|
|
|
|
hsstrt_v2 = (hsstrt & 0xff) << CCK_SHRES_SHIFT;
|
|
hsstop_v2 = (hsstop & 0xff) << CCK_SHRES_SHIFT;
|
|
|
|
} else {
|
|
|
|
hsyncstartpos_start = hsyncstartpos_start_hw;
|
|
hsyncstartpos = hsyncstartpos_hw;
|
|
denisehtotal = maxhpos_short + 7;
|
|
hsstop_detect2 = (35 + 9) * 2;
|
|
|
|
hsstrt_v2 = 18 << CCK_SHRES_SHIFT;
|
|
hsstop_v2 = 35 << CCK_SHRES_SHIFT;
|
|
hsyncendpos = hsyncendpos_hw;
|
|
|
|
}
|
|
|
|
hsstrt_v2 = adjust_hr(hsstrt_v2);
|
|
hsstop_v2 = adjust_hr(hsstop_v2);
|
|
|
|
|
|
// Out of range left. Denise/Lisa hcounter starts from 2 (skips first 2 lores pixels)
|
|
if (hbstrt_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hbstrt_v2 = 0xffff;
|
|
}
|
|
if (hbstop_v2 < (1 << CCK_SHRES_SHIFT)) {
|
|
hbstop_v2 = 0xffff;
|
|
}
|
|
// Out of range right
|
|
if (hbstrt_v2 >= (maxhpos_short << CCK_SHRES_SHIFT) + (1 << CCK_SHRES_SHIFT)) {
|
|
hbstrt_v2 = 0xffff;
|
|
}
|
|
if (hbstop_v2 >= (maxhpos_short << CCK_SHRES_SHIFT) + (1 << CCK_SHRES_SHIFT)) {
|
|
hbstop_v2 = 0xffff;
|
|
}
|
|
// hsync start offset adjustment
|
|
if (hbstrt_v2 <= (hsyncstartpos_start << CCK_SHRES_SHIFT)) {
|
|
hbstrt_v2 += maxhpos_short << CCK_SHRES_SHIFT;
|
|
if (hbstrt_v2 > (maxhpos_short + hsyncstartpos_start_cycles) << CCK_SHRES_SHIFT) {
|
|
hbstrt_v2 = 1 << CCK_SHRES_SHIFT;
|
|
}
|
|
}
|
|
if (hbstop_v2 <= (hsyncstartpos_start << CCK_SHRES_SHIFT)) {
|
|
hbstop_v2 += maxhpos_short << CCK_SHRES_SHIFT;
|
|
if (hbstop_v2 > (maxhpos_short + hsyncstartpos_start_cycles) << CCK_SHRES_SHIFT) {
|
|
hbstop_v2 = 1 << CCK_SHRES_SHIFT;
|
|
}
|
|
}
|
|
|
|
int strt = hbstrt_v2;
|
|
int stop = hbstop_v2;
|
|
hbstrt_v2 = adjust_hr(hbstrt_v2);
|
|
hbstop_v2 = adjust_hr(hbstop_v2);
|
|
// if same after rounding: increase start/stop by minimum unit
|
|
if (hbstrt_v2 == hbstop_v2) {
|
|
int add = currprefs.chipset_hr ? (1 << currprefs.gfx_resolution) : 4;
|
|
if (strt < stop) {
|
|
hbstop_v2 += add;
|
|
if (hbstop_v2 >= (maxhpos_short << CCK_SHRES_SHIFT) + (1 << CCK_SHRES_SHIFT)) {
|
|
hbstop_v2 = 0;
|
|
}
|
|
} else if (strt > stop) {
|
|
hbstrt_v2 += add;
|
|
if (hbstrt_v2 >= (maxhpos_short << CCK_SHRES_SHIFT) + (1 << CCK_SHRES_SHIFT)) {
|
|
hbstrt_v2 = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
hsyncstartpos_start_hw <<= CCK_SHRES_SHIFT;
|
|
hsyncstartpos_hw <<= CCK_SHRES_SHIFT;
|
|
hsyncendpos_hw <<= CCK_SHRES_SHIFT;
|
|
|
|
hsyncstartpos_start_cycles = hsyncstartpos_start;
|
|
|
|
hsyncstartpos_start <<= CCK_SHRES_SHIFT;
|
|
hsyncstartpos <<= CCK_SHRES_SHIFT;
|
|
hsyncendpos <<= CCK_SHRES_SHIFT;
|
|
|
|
denisehtotal <<= CCK_SHRES_SHIFT;
|
|
|
|
// ECS Denise has 1 extra lores pixel in right border
|
|
if (currprefs.gfx_overscanmode >= OVERSCANMODE_ULTRA) {
|
|
denisehtotal += 2 << (CCK_SHRES_SHIFT - 1);
|
|
} else if (ecs_denise) {
|
|
denisehtotal += 1 << (CCK_SHRES_SHIFT - 1);
|
|
}
|
|
|
|
set_hcenter();
|
|
|
|
calcdiw();
|
|
}
|
|
|
|
struct chipset_refresh *get_chipset_refresh(struct uae_prefs *p)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
int islace = interlace_seen ? 1 : 0;
|
|
int isntsc = (beamcon0 & BEAMCON0_PAL) ? 0 : 1;
|
|
int custom = programmedmode == 1 ? 1 : 0;
|
|
|
|
if (!ecs_agnus) {
|
|
isntsc = currprefs.ntscmode ? 1 : 0;
|
|
}
|
|
|
|
int def = -1;
|
|
for (int i = 0; i < MAX_CHIPSET_REFRESH_TOTAL; i++) {
|
|
struct chipset_refresh *cr = &p->cr[i];
|
|
if (cr->defaultdata)
|
|
def = i;
|
|
if (cr->inuse) {
|
|
if ((cr->horiz < 0 || cr->horiz == maxhpos) &&
|
|
(cr->vert < 0 || cr->vert == maxvpos_display) &&
|
|
(cr->ntsc < 0 || (cr->ntsc == 1 && isntsc && !custom) || (cr->ntsc == 0 && !isntsc && !custom) || (cr->ntsc == 2 && custom)) &&
|
|
(cr->lace < 0 || (cr->lace > 0 && islace) || (cr->lace == 0 && !islace)) &&
|
|
(cr->resolution == 0 || cr->resolution == 7 || (cr->resolution & (1 << detected_screen_resolution))) &&
|
|
(cr->framelength < 0 || (cr->framelength > 0 && lof_store) || (cr->framelength == 0 && !lof_store) || (cr->framelength >= 0 && islace)) &&
|
|
((cr->rtg && ad->picasso_on) || (!cr->rtg && !ad->picasso_on)) &&
|
|
(cr->vsync < 0 || (cr->vsync > 0 && isvsync_chipset ()) || (cr->vsync == 0 && !isvsync_chipset ())))
|
|
return cr;
|
|
}
|
|
}
|
|
if (def >= 0) {
|
|
return &p->cr[def];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool changed_chipset_refresh(void)
|
|
{
|
|
return stored_chipset_refresh != get_chipset_refresh(&currprefs);
|
|
}
|
|
|
|
void compute_framesync(void)
|
|
{
|
|
struct vidbuf_description *vidinfo = &adisplays[0].gfxvidinfo;
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
int islace = interlace_seen ? 1 : 0;
|
|
int isntsc = (beamcon0 & BEAMCON0_PAL) ? 0 : 1;
|
|
bool found = false;
|
|
|
|
if (islace) {
|
|
vblank_hz = vblank_hz_lace;
|
|
} else if (lof_store) {
|
|
vblank_hz = vblank_hz_lof;
|
|
} else {
|
|
vblank_hz = vblank_hz_shf;
|
|
}
|
|
|
|
vblank_hz = target_adjust_vblank_hz(0, vblank_hz);
|
|
|
|
struct chipset_refresh *cr = get_chipset_refresh(&currprefs);
|
|
while (cr) {
|
|
float v = -1;
|
|
if (!ad->picasso_on && !ad->picasso_requested_on) {
|
|
if (isvsync_chipset ()) {
|
|
if (!currprefs.gfx_variable_sync) {
|
|
if (cr->index == CHIPSET_REFRESH_PAL || cr->index == CHIPSET_REFRESH_NTSC) {
|
|
if ((fabs(vblank_hz - 50.0f) < 1 || fabs(vblank_hz - 60.0f) < 1 || fabs(vblank_hz - 100.0) < 1 || fabs(vblank_hz - 120.0f) < 1) && currprefs.gfx_apmode[0].gfx_vsync == 2 && currprefs.gfx_apmode[0].gfx_fullscreen > 0) {
|
|
vsync_switchmode(0, (int)vblank_hz);
|
|
}
|
|
}
|
|
if (isvsync_chipset() < 0) {
|
|
|
|
float v2;
|
|
v2 = target_getcurrentvblankrate(0);
|
|
if (!cr->locked)
|
|
v = v2;
|
|
} else if (isvsync_chipset() > 0) {
|
|
if (currprefs.gfx_apmode[0].gfx_refreshrate) {
|
|
v = (float)abs(currprefs.gfx_apmode[0].gfx_refreshrate);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (cr->locked == false) {
|
|
changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = vblank_hz;
|
|
cfgfile_parse_lines (&changed_prefs, cr->commands, -1);
|
|
if (cr->commands[0]) {
|
|
write_log(_T("CMD1: '%s'\n"), cr->commands);
|
|
}
|
|
break;
|
|
} else {
|
|
v = cr->rate;
|
|
}
|
|
}
|
|
if (v < 0)
|
|
v = cr->rate;
|
|
if (v > 0) {
|
|
changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = v;
|
|
cfgfile_parse_lines (&changed_prefs, cr->commands, -1);
|
|
if (cr->commands[0]) {
|
|
write_log(_T("CMD2: '%s'\n"), cr->commands);
|
|
}
|
|
}
|
|
} else {
|
|
if (cr->locked == false)
|
|
v = vblank_hz;
|
|
else
|
|
v = cr->rate;
|
|
changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = v;
|
|
cfgfile_parse_lines (&changed_prefs, cr->commands, -1);
|
|
if (cr->commands[0]) {
|
|
write_log(_T("CMD3: '%s'\n"), cr->commands);
|
|
}
|
|
}
|
|
found = true;
|
|
break;
|
|
}
|
|
if (!found) {
|
|
changed_prefs.chipset_refreshrate = currprefs.chipset_refreshrate = vblank_hz;
|
|
}
|
|
stored_chipset_refresh = cr;
|
|
interlace_changed = 0;
|
|
lof_togglecnt_lace = 0;
|
|
lof_togglecnt_nlace = 0;
|
|
//nlace_cnt = NLACE_CNT_NEEDED;
|
|
lof_changing = 0;
|
|
vidinfo->drawbuffer.inxoffset = -1;
|
|
vidinfo->drawbuffer.inyoffset = -1;
|
|
updateextblk();
|
|
|
|
hsync_end_left_border = hsstop_detect;
|
|
|
|
int res = GET_RES_AGNUS(bplcon0);
|
|
int eres = 0;
|
|
|
|
int res2 = currprefs.gfx_resolution;
|
|
|
|
if (doublescan > 0) {
|
|
res2++;
|
|
eres++;
|
|
}
|
|
if (res2 > RES_MAX) {
|
|
res2 = RES_MAX;
|
|
eres--;
|
|
}
|
|
|
|
hsync_end_left_border <<= eres;
|
|
|
|
int vres2 = currprefs.gfx_vresolution;
|
|
if (doublescan > 0 && !islace) {
|
|
vres2--;
|
|
}
|
|
|
|
if (vres2 < 0) {
|
|
vres2 = 0;
|
|
}
|
|
if (vres2 > VRES_QUAD) {
|
|
vres2 = VRES_QUAD;
|
|
}
|
|
|
|
vidinfo->drawbuffer.inwidth = maxhpos_display << res2;
|
|
vidinfo->drawbuffer.inwidth2 = vidinfo->drawbuffer.inwidth;
|
|
vidinfo->drawbuffer.extrawidth = -2;
|
|
if (currprefs.gfx_extrawidth > 0) {
|
|
vidinfo->drawbuffer.extrawidth = currprefs.gfx_extrawidth << res2;
|
|
}
|
|
vidinfo->drawbuffer.extraheight = -2;
|
|
if (currprefs.gfx_extraheight > 0) {
|
|
vidinfo->drawbuffer.extraheight = currprefs.gfx_extraheight << vres2;
|
|
}
|
|
if (vidinfo->drawbuffer.extrawidth == -2 && ((new_beamcon0 & (BEAMCON0_VARVBEN | bemcon0_vsync_mask)) || currprefs.gfx_overscanmode >= OVERSCANMODE_EXTREME)) {
|
|
vidinfo->drawbuffer.extrawidth = -1;
|
|
}
|
|
vidinfo->drawbuffer.inheight = maxvsize_display << vres2;
|
|
vidinfo->drawbuffer.inheight2 = vidinfo->drawbuffer.inheight;
|
|
vidinfo->drawbuffer.inxoffset = 0;
|
|
|
|
//write_log(_T("Width %d Height %d\n"), vidinfo->drawbuffer.inwidth, vidinfo->drawbuffer.inheight);
|
|
|
|
if (vidinfo->drawbuffer.inwidth < 16)
|
|
vidinfo->drawbuffer.inwidth = 16;
|
|
if (vidinfo->drawbuffer.inwidth2 < 16)
|
|
vidinfo->drawbuffer.inwidth2 = 16;
|
|
if (vidinfo->drawbuffer.inheight < 1)
|
|
vidinfo->drawbuffer.inheight = 1;
|
|
if (vidinfo->drawbuffer.inheight2 < 1)
|
|
vidinfo->drawbuffer.inheight2 = 1;
|
|
|
|
if (vidinfo->drawbuffer.inwidth > vidinfo->drawbuffer.width_allocated)
|
|
vidinfo->drawbuffer.inwidth = vidinfo->drawbuffer.width_allocated;
|
|
if (vidinfo->drawbuffer.inwidth2 > vidinfo->drawbuffer.width_allocated)
|
|
vidinfo->drawbuffer.inwidth2 = vidinfo->drawbuffer.width_allocated;
|
|
|
|
if (vidinfo->drawbuffer.inheight > vidinfo->drawbuffer.height_allocated)
|
|
vidinfo->drawbuffer.inheight = vidinfo->drawbuffer.height_allocated;
|
|
if (vidinfo->drawbuffer.inheight2 > vidinfo->drawbuffer.height_allocated)
|
|
vidinfo->drawbuffer.inheight2 = vidinfo->drawbuffer.height_allocated;
|
|
|
|
vidinfo->drawbuffer.outwidth = vidinfo->drawbuffer.inwidth;
|
|
vidinfo->drawbuffer.outheight = vidinfo->drawbuffer.inheight;
|
|
|
|
if (vidinfo->drawbuffer.outwidth > vidinfo->drawbuffer.width_allocated)
|
|
vidinfo->drawbuffer.outwidth = vidinfo->drawbuffer.width_allocated;
|
|
|
|
if (vidinfo->drawbuffer.outheight > vidinfo->drawbuffer.height_allocated)
|
|
vidinfo->drawbuffer.outheight = vidinfo->drawbuffer.height_allocated;
|
|
|
|
memset(line_decisions, 0, sizeof(line_decisions));
|
|
memset(line_drawinfo, 0, sizeof(line_drawinfo));
|
|
for (int i = 0; i < sizeof(line_decisions) / sizeof(*line_decisions); i++) {
|
|
line_decisions[i].plfleft = -2;
|
|
}
|
|
|
|
check_nocustom();
|
|
|
|
compute_vsynctime();
|
|
|
|
hblank_hz = (currprefs.ntscmode ? CHIPSET_CLOCK_NTSC : CHIPSET_CLOCK_PAL) / (maxhpos + (islinetoggle() ? 0.5f : 0.0f));
|
|
|
|
write_log(_T("%s mode%s%s V=%.4fHz H=%0.4fHz (%dx%d+%d) IDX=%d (%s) D=%d RTG=%d/%d\n"),
|
|
isntsc ? _T("NTSC") : _T("PAL"),
|
|
islace ? _T(" lace") : (lof_lace ? _T(" loflace") : _T("")),
|
|
doublescan > 0 ? _T(" dblscan") : _T(""),
|
|
vblank_hz,
|
|
hblank_hz,
|
|
maxhpos, maxvpos, lof_store ? 1 : 0,
|
|
cr ? cr->index : -1,
|
|
cr != NULL && cr->label != NULL ? cr->label : _T("<?>"),
|
|
currprefs.gfx_apmode[ad->picasso_on ? 1 : 0].gfx_display, ad->picasso_on, ad->picasso_requested_on
|
|
);
|
|
|
|
set_config_changed();
|
|
|
|
if (currprefs.monitoremu_mon != 0) {
|
|
target_graphics_buffer_update(currprefs.monitoremu_mon);
|
|
}
|
|
if (target_graphics_buffer_update(0)) {
|
|
reset_drawing();
|
|
}
|
|
}
|
|
|
|
/* set PAL/NTSC or custom timing variables */
|
|
static void init_beamcon0(bool fakehz)
|
|
{
|
|
int isntsc, islace;
|
|
int hpos = current_hpos();
|
|
int oldmaxhpos = maxhpos;
|
|
|
|
beamcon0 = new_beamcon0;
|
|
|
|
isntsc = (beamcon0 & BEAMCON0_PAL) ? 0 : 1;
|
|
islace = (interlace_seen) ? 1 : 0;
|
|
if (!ecs_agnus) {
|
|
isntsc = currprefs.ntscmode ? 1 : 0;
|
|
}
|
|
|
|
if (currprefs.cs_hvcsync == 0) {
|
|
bemcon0_hsync_mask = BEAMCON0_VARHSYEN | BEAMCON0_VARCSYEN;
|
|
bemcon0_vsync_mask = BEAMCON0_VARVSYEN | BEAMCON0_VARCSYEN;
|
|
} else if (currprefs.cs_hvcsync == 1) {
|
|
bemcon0_hsync_mask = BEAMCON0_VARCSYEN;
|
|
bemcon0_vsync_mask = BEAMCON0_VARCSYEN;
|
|
} else {
|
|
bemcon0_hsync_mask = BEAMCON0_VARHSYEN;
|
|
bemcon0_vsync_mask = BEAMCON0_VARVSYEN;
|
|
}
|
|
|
|
float clk = (float)(currprefs.ntscmode ? CHIPSET_CLOCK_NTSC : CHIPSET_CLOCK_PAL);
|
|
if (!isntsc) {
|
|
maxvpos = MAXVPOS_PAL;
|
|
maxhpos = MAXHPOS_PAL;
|
|
minfirstline = VBLANK_ENDLINE_PAL;
|
|
vblank_hz_nom = vblank_hz = VBLANK_HZ_PAL;
|
|
hardwired_vbstop = VBLANK_STOP_PAL;
|
|
hardwired_vbstrt = maxvpos;
|
|
equ_vblank_endline = EQU_ENDLINE_PAL;
|
|
equ_vblank_toggle = true;
|
|
vblank_hz_shf = clk / ((maxvpos + 0.0f) * maxhpos);
|
|
vblank_hz_lof = clk / ((maxvpos + 1.0f) * maxhpos);
|
|
vblank_hz_lace = clk / ((maxvpos + 0.5f) * maxhpos);
|
|
} else {
|
|
maxvpos = MAXVPOS_NTSC;
|
|
maxhpos = MAXHPOS_NTSC;
|
|
minfirstline = VBLANK_ENDLINE_NTSC;
|
|
vblank_hz_nom = vblank_hz = VBLANK_HZ_NTSC;
|
|
hardwired_vbstop = VBLANK_STOP_NTSC;
|
|
hardwired_vbstrt = maxvpos;
|
|
equ_vblank_endline = EQU_ENDLINE_NTSC;
|
|
equ_vblank_toggle = false;
|
|
vblank_hz_shf = clk / ((maxvpos + 0.0f) * (maxhpos + 0.5f));
|
|
vblank_hz_lof = clk / ((maxvpos + 1.0f) * (maxhpos + 0.5f));
|
|
vblank_hz_lace = clk / ((maxvpos + 0.5f) * (maxhpos + 0.5f));
|
|
}
|
|
|
|
dmal_htotal_mask = 0xffff;
|
|
if (beamcon0 & BEAMCON0_VARBEAMEN) {
|
|
// programmable scanrates (ECS Agnus)
|
|
if (vtotal >= MAXVPOS) {
|
|
vtotal = MAXVPOS - 1;
|
|
}
|
|
maxvpos = vtotal + 1;
|
|
if (htotal >= MAXHPOS) {
|
|
htotal = MAXHPOS - 1;
|
|
}
|
|
maxhpos = htotal + 1;
|
|
|
|
if (maxhpos < DMAL_FIRST_HPOS) {
|
|
dmal_htotal_mask = 0;
|
|
} else if (maxhpos < DMAL_FIRST_HPOS + 6 * 2) {
|
|
int cnt = (maxhpos - DMAL_FIRST_HPOS) / 2;
|
|
dmal_htotal_mask = (1 << cnt) - 1;
|
|
}
|
|
}
|
|
|
|
// after vsync, it seems earlier possible visible line is vsync+3.
|
|
int vsync_startline = 3;
|
|
if ((beamcon0 & BEAMCON0_VARVBEN) && (beamcon0 & bemcon0_vsync_mask)) {
|
|
vsync_startline += vsstrt;
|
|
if (vsync_startline >= maxvpos / 2) {
|
|
vsync_startline = 3;
|
|
}
|
|
}
|
|
|
|
maxvpos_nom = maxvpos;
|
|
maxvpos_display = maxvpos;
|
|
maxvpos_display_vsync = 1;
|
|
// A1000 Agnus VBSTRT=first line, OCS and later: VBSTRT=last line
|
|
if (agnusa1000) {
|
|
maxvpos_display_vsync++;
|
|
}
|
|
|
|
vblank_firstline_hw = minfirstline;
|
|
|
|
// calculate max possible display width in lores pixels
|
|
if (beamcon0 & BEAMCON0_VARBEAMEN) {
|
|
// assume VGA-like monitor if VARBEAMEN
|
|
if (currprefs.gfx_overscanmode >= OVERSCANMODE_ULTRA) {
|
|
maxhpos_display = maxhpos + 7;
|
|
hsstop_detect = hsstrt * 2;
|
|
if (hsstop_detect > maxhpos / 2 * 2 || hsstop_detect < 4 * 2) {
|
|
hsstop_detect = 4 * 2;
|
|
}
|
|
minfirstline = 0;
|
|
} else {
|
|
int hp2 = maxhpos * 2;
|
|
if (exthblank) {
|
|
|
|
int hb = 1;
|
|
int hbstrtx = (hbstrt & 0xff) * 2;
|
|
int hbstopx = (hbstop & 0xff) * 2;
|
|
if (aga_mode) {
|
|
hbstrtx |= (hbstrt & 0x400) ? 1 : 0;
|
|
hbstopx |= (hbstop & 0x400) ? 1 : 0;
|
|
}
|
|
if (hbstrtx > hp2) {
|
|
hbstrtx = hp2;
|
|
}
|
|
if (hbstopx > hp2) {
|
|
hbstopx = hp2;
|
|
}
|
|
if (hbstrtx > hp2 / 2 && hbstopx < hp2 / 2) {
|
|
hb = (hp2 - hbstrtx) + hbstopx;
|
|
} else if (hbstopx < hp2 / 2 && hbstrtx < hbstopx) {
|
|
hb = hbstopx - hbstrtx;
|
|
}
|
|
if (hbstopx > hp2 / 2) {
|
|
hbstopx = 0;
|
|
}
|
|
if (hb < 1) {
|
|
hb = 1;
|
|
}
|
|
|
|
#if 0
|
|
// HSYNC adjustment
|
|
int hsz = 0;
|
|
if (hsstrt > maxhpos / 2 && hsstop > hsstrt) {
|
|
hsz = hsstop - hsstrt;
|
|
} else if (hsstrt > maxhpos / 2 && hsstop < maxhpos / 2) {
|
|
hsz = (maxhpos - hsstrt) - hsstop;
|
|
} else if (hsstop < maxhpos / 2 && hsstrt < hsstop) {
|
|
hsz = hsstop - hsstrt;
|
|
}
|
|
#endif
|
|
maxhpos_display = maxhpos - ((hb + 1) / 2);
|
|
hsstop_detect = hbstopx - 1;
|
|
|
|
} else {
|
|
|
|
// hardwired
|
|
int hbstrtx = 7 * 2;
|
|
int hbstopx = 46 * 2;
|
|
int hb = hbstopx - hbstrtx;
|
|
maxhpos_display = maxhpos - (hb / 2);
|
|
hsstop_detect = 46 * 2 - 2;
|
|
}
|
|
|
|
if (currprefs.gfx_overscanmode >= OVERSCANMODE_EXTREME) {
|
|
int diff = (maxhpos - 2) - maxhpos_display;
|
|
if (diff > 0) {
|
|
hsstop_detect -= (diff / 2) * 2;
|
|
}
|
|
maxhpos_display = maxhpos - 2;
|
|
maxvpos_display_vsync += 2;
|
|
} else if (currprefs.gfx_overscanmode == OVERSCANMODE_BROADCAST) {
|
|
maxhpos_display += EXTRAWIDTH_BROADCAST;
|
|
hsstop_detect--;
|
|
maxvpos_display_vsync++;
|
|
}
|
|
|
|
}
|
|
maxhpos_display *= 2;
|
|
|
|
} else {
|
|
|
|
maxhpos_display = AMIGA_WIDTH_MAX;
|
|
hsstop_detect = hsstop_detect2;
|
|
|
|
if (beamcon0 & bemcon0_hsync_mask) {
|
|
if (currprefs.gfx_overscanmode < OVERSCANMODE_BROADCAST) {
|
|
hsstop_detect += 7;
|
|
} else if (currprefs.gfx_overscanmode == OVERSCANMODE_BROADCAST) {
|
|
hsstop_detect += 5;
|
|
} else if (currprefs.gfx_overscanmode >= OVERSCANMODE_ULTRA) {
|
|
maxhpos_display += EXTRAWIDTH_ULTRA;
|
|
maxvpos_display_vsync += vsync_startline;
|
|
minfirstline = 0;
|
|
hsstop_detect = hsyncstartpos_start_cycles * 2 - 4;
|
|
}
|
|
} else {
|
|
|
|
if (currprefs.gfx_overscanmode >= OVERSCANMODE_ULTRA) {
|
|
maxhpos_display += EXTRAWIDTH_ULTRA;
|
|
maxvpos_display_vsync += vsync_startline;
|
|
hsstop_detect = 18 * 2;
|
|
minfirstline = 1;
|
|
}
|
|
}
|
|
|
|
if (currprefs.gfx_overscanmode < OVERSCANMODE_BROADCAST) {
|
|
// one pixel row missing from right border if OCS
|
|
if (!ecs_denise) {
|
|
maxhpos_display--;
|
|
}
|
|
} else if (currprefs.gfx_overscanmode == OVERSCANMODE_EXTREME) {
|
|
maxhpos_display += EXTRAWIDTH_EXTREME;
|
|
maxvpos_display_vsync += 2;
|
|
hsstop_detect -= 4;
|
|
minfirstline--;
|
|
} else if (currprefs.gfx_overscanmode == OVERSCANMODE_BROADCAST) {
|
|
maxhpos_display += EXTRAWIDTH_BROADCAST;
|
|
maxvpos_display_vsync++;
|
|
minfirstline--;
|
|
hsstop_detect -= 1;
|
|
}
|
|
}
|
|
|
|
if (currprefs.gfx_extrawidth > 0) {
|
|
maxhpos_display += currprefs.gfx_extrawidth;
|
|
}
|
|
|
|
if (hsstop_detect < 0) {
|
|
hsstop_detect = 0;
|
|
}
|
|
if (minfirstline < 0) {
|
|
minfirstline = 0;
|
|
}
|
|
|
|
vblank_extraline = !agnusa1000 && !ecs_denise ? 1 : 0;
|
|
|
|
int minfirstline_hw = minfirstline;
|
|
if (currprefs.gfx_overscanmode >= OVERSCANMODE_ULTRA) {
|
|
minfirstline = 0;
|
|
minfirstline_hw = 0;
|
|
} else if (currprefs.gfx_overscanmode == OVERSCANMODE_EXTREME) {
|
|
minfirstline_hw -= EXTRAHEIGHT_EXTREME / 2;
|
|
} else if (currprefs.gfx_overscanmode == OVERSCANMODE_BROADCAST) {
|
|
minfirstline_hw -= EXTRAHEIGHT_BROADCAST_TOP;
|
|
}
|
|
if (beamcon0 & BEAMCON0_VARBEAMEN) {
|
|
minfirstline_hw = 0;
|
|
}
|
|
|
|
if (fakehz && vpos_count > maxvpos_display_vsync) {
|
|
// 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.0f : 15625.0f) / vpos_count;
|
|
vblank_hz_nom = vblank_hz_shf = vblank_hz_lof = vblank_hz_lace = (float)vblank_hz;
|
|
maxvpos_nom = vpos_count - (lof_store ? 1 : 0);
|
|
if ((maxvpos_nom >= 256 && maxvpos_nom <= 313) || (beamcon0 & BEAMCON0_VARBEAMEN)) {
|
|
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 & BEAMCON0_VARVBEN) && (beamcon0 & bemcon0_vsync_mask)) {
|
|
minfirstline = vsync_startline;
|
|
if (minfirstline > maxvpos / 2) {
|
|
minfirstline = vsync_startline;
|
|
}
|
|
minfirstline++;
|
|
firstblankedline = vbstrt;
|
|
if (vsstrt > 0 && vsstrt < maxvpos / 2) {
|
|
maxvpos_display_vsync += vsstrt - 1;
|
|
}
|
|
} else if (beamcon0 & bemcon0_vsync_mask) {
|
|
firstblankedline = maxvpos + 1;
|
|
} else if (beamcon0 & BEAMCON0_VARVBEN) {
|
|
firstblankedline = vbstrt;
|
|
if (vsstrt > 0 && vsstrt < maxvpos / 2) {
|
|
maxvpos_display_vsync += vsstrt - 1;
|
|
}
|
|
} else {
|
|
firstblankedline = maxvpos + 1;
|
|
}
|
|
if (beamcon0 & (BEAMCON0_VARVBEN | bemcon0_vsync_mask)) {
|
|
programmedmode = 2;
|
|
}
|
|
|
|
int eh = currprefs.gfx_extraheight;
|
|
if (eh > 0) {
|
|
if (beamcon0 & bemcon0_vsync_mask) {
|
|
maxvpos_display_vsync += eh / 2;
|
|
minfirstline -= eh / 2;
|
|
} else {
|
|
maxvpos_display_vsync = 3;
|
|
minfirstline -= eh / 2;
|
|
}
|
|
}
|
|
|
|
if (maxvpos_display_vsync <= 0) {
|
|
maxvpos_display_vsync = 0;
|
|
}
|
|
|
|
if (minfirstline < vsync_startline) {
|
|
minfirstline = vsync_startline;
|
|
}
|
|
|
|
if (minfirstline >= maxvpos) {
|
|
minfirstline = maxvpos - 1;
|
|
}
|
|
|
|
if (firstblankedline < minfirstline) {
|
|
firstblankedline = maxvpos + maxvpos_display_vsync + 1;
|
|
}
|
|
|
|
if (minfirstline < minfirstline_hw) {
|
|
minfirstline = minfirstline_hw;
|
|
}
|
|
|
|
if (beamcon0 & bemcon0_vsync_mask) {
|
|
if (maxvpos_display_vsync >= vsstrt + 3) {
|
|
maxvpos_display_vsync = vsstrt + 3;
|
|
}
|
|
if (minfirstline < vsync_startline) {
|
|
minfirstline = vsync_startline;
|
|
}
|
|
}
|
|
|
|
if (beamcon0 & BEAMCON0_VARBEAMEN) {
|
|
vblank_hz_nom = vblank_hz = clk / (maxvpos * maxhpos);
|
|
vblank_hz_shf = vblank_hz;
|
|
vblank_hz_lof = clk / ((maxvpos + 1.0f) * maxhpos);
|
|
vblank_hz_lace = clk / ((maxvpos + 0.5f) * maxhpos);
|
|
|
|
maxvpos_nom = maxvpos;
|
|
maxvpos_display = maxvpos;
|
|
equ_vblank_endline = -1;
|
|
|
|
programmedmode = 2;
|
|
if ((htotal < 226 || htotal > 229) || (vtotal < 256 || vtotal > 320)) {
|
|
doublescan = htotal <= 164 && vtotal >= 350 ? 1 : 0;
|
|
// if superhires and wide enough: not doublescan
|
|
if (doublescan && htotal >= 140 && (bplcon0 & 0x0040))
|
|
doublescan = 0;
|
|
programmedmode = 1;
|
|
}
|
|
|
|
vpos_count = maxvpos_nom;
|
|
vpos_count_diff = maxvpos_nom;
|
|
}
|
|
|
|
// exclude possible extra lines that are inside vertical blank
|
|
if (currprefs.gfx_overscanmode < OVERSCANMODE_EXTREME) {
|
|
if (beamcon0 & BEAMCON0_VARBEAMEN) {
|
|
if (firstblankedline < maxvpos / 2 && maxvpos_display_vsync > firstblankedline) {
|
|
maxvpos_display_vsync = firstblankedline;
|
|
} else if (firstblankedline <= maxvpos) {
|
|
maxvpos_display_vsync = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (maxvpos_display_vsync <= 0) {
|
|
maxvpos_display_vsync = 1;
|
|
}
|
|
|
|
if (maxvpos_nom >= MAXVPOS) {
|
|
maxvpos_nom = MAXVPOS;
|
|
}
|
|
if (maxvpos_display >= MAXVPOS) {
|
|
maxvpos_display = MAXVPOS;
|
|
}
|
|
if (currprefs.gfx_scandoubler && doublescan == 0) {
|
|
doublescan = -1;
|
|
}
|
|
/* limit to sane values */
|
|
if (vblank_hz < 10) {
|
|
vblank_hz = 10;
|
|
}
|
|
if (vblank_hz > 300) {
|
|
vblank_hz = 300;
|
|
}
|
|
maxhpos_short = maxhpos;
|
|
minfirstline_linear = minfirstline;
|
|
set_delay_lastcycle();
|
|
updateextblk();
|
|
maxvpos_total = ecs_agnus ? (MAXVPOS_LINES_ECS - 1) : (MAXVPOS_LINES_OCS - 1);
|
|
if (maxvpos_total > MAXVPOS) {
|
|
maxvpos_total = MAXVPOS;
|
|
}
|
|
set_maxhpos(maxhpos);
|
|
estimated_fm = 0xffff;
|
|
maxvsize_display = maxvpos_display + maxvpos_display_vsync - minfirstline;
|
|
|
|
if ((currprefs.m68k_speed == 0 || copper_access) && maxhpos != oldmaxhpos) {
|
|
if (hpos >= maxhpos) {
|
|
// count until 255 -> 0 wrap around
|
|
eventtab[ev_hsync].evtime = line_start_cycles + 256 * CYCLE_UNIT;
|
|
// mark current line lenght = 0x100
|
|
set_maxhpos(0x100);
|
|
} else {
|
|
// adjust current scanline length to match new maxhpos
|
|
eventtab[ev_hsync].evtime = line_start_cycles + maxhpos * CYCLE_UNIT;
|
|
}
|
|
current_hpos();
|
|
events_schedule();
|
|
}
|
|
}
|
|
|
|
static void init_hz(bool checkvposw)
|
|
{
|
|
int isntsc, islace;
|
|
int odbl = doublescan;
|
|
double ovblank = vblank_hz;
|
|
int hzc = 0;
|
|
int omaxhpos = maxhpos;
|
|
int omaxvpos = maxvpos;
|
|
|
|
if (!checkvposw) {
|
|
vpos_count = 0;
|
|
}
|
|
|
|
isntsc = (new_beamcon0 & BEAMCON0_PAL) ? 0 : 1;
|
|
islace = (interlace_seen) ? 1 : 0;
|
|
if (!ecs_agnus) {
|
|
isntsc = currprefs.ntscmode ? 1 : 0;
|
|
}
|
|
|
|
vpos_count_diff = vpos_count;
|
|
|
|
doublescan = 0;
|
|
programmedmode = 0;
|
|
if ((beamcon0 & (BEAMCON0_VARBEAMEN | BEAMCON0_PAL)) != (new_beamcon0 & (BEAMCON0_VARBEAMEN | BEAMCON0_PAL))) {
|
|
hzc = 1;
|
|
}
|
|
if (beamcon0 != new_beamcon0) {
|
|
vpos_count_diff = vpos_count = 0;
|
|
}
|
|
if ((beamcon0 & (BEAMCON0_VARVBEN | BEAMCON0_VARVSYEN | BEAMCON0_VARCSYEN)) != (new_beamcon0 & (BEAMCON0_VARVBEN | BEAMCON0_VARVSYEN | BEAMCON0_VARCSYEN))) {
|
|
hzc = 1;
|
|
}
|
|
|
|
init_beamcon0(checkvposw);
|
|
|
|
if (doublescan != odbl || maxvpos != omaxvpos) {
|
|
hzc = 1;
|
|
}
|
|
|
|
eventtab[ev_hsync].oldcycles = get_cycles();
|
|
eventtab[ev_hsync].evtime = get_cycles() + HSYNCTIME;
|
|
events_schedule();
|
|
if (hzc) {
|
|
interlace_seen = islace;
|
|
reset_drawing();
|
|
}
|
|
|
|
if (maxvpos != omaxvpos || maxhpos != omaxhpos) {
|
|
nosignal_trigger = true;
|
|
}
|
|
|
|
#ifdef PICASSO96
|
|
if (!p96refresh_active) {
|
|
maxvpos_stored = maxvpos;
|
|
maxhpos_stored = maxhpos;
|
|
vblank_hz_stored = vblank_hz;
|
|
}
|
|
#endif
|
|
|
|
compute_framesync();
|
|
devices_syncchange();
|
|
|
|
#ifdef PICASSO96
|
|
init_hz_p96(0);
|
|
#endif
|
|
if (vblank_hz != ovblank) {
|
|
updatedisplayarea(0);
|
|
}
|
|
inputdevice_tablet_strobe();
|
|
|
|
if (varsync_changed > 0) {
|
|
varsync_changed = 0;
|
|
dumpsync();
|
|
}
|
|
}
|
|
|
|
static void init_hz_vposw(void)
|
|
{
|
|
init_hz(true);
|
|
}
|
|
|
|
void init_hz_normal(void)
|
|
{
|
|
init_hz(false);
|
|
}
|
|
|
|
static void calcdiw(void)
|
|
{
|
|
int hstrt = (diwstrt & 0xFF) << 2;
|
|
int hstop = (diwstop & 0xFF) << 2;
|
|
int vstrt = diwstrt >> 8;
|
|
int vstop = diwstop >> 8;
|
|
|
|
// ECS Agnus/AGA: DIWHIGH vertical high bits.
|
|
if (diwhigh_written && ecs_agnus) {
|
|
if (aga_mode) {
|
|
vstrt |= (diwhigh & 7) << 8;
|
|
vstop |= ((diwhigh >> 8) & 7) << 8;
|
|
} else {
|
|
// ECS Agnus has undocumented V11!
|
|
vstrt |= (diwhigh & 15) << 8;
|
|
vstop |= ((diwhigh >> 8) & 15) << 8;
|
|
}
|
|
} else {
|
|
if ((vstop & 0x80) == 0)
|
|
vstop |= 0x100;
|
|
}
|
|
// ECS Denise/AGA: horizontal DIWHIGH high bit.
|
|
if (diwhigh_written && ecs_denise) {
|
|
hstrt |= ((diwhigh >> 5) & 1) << (8 + 2);
|
|
hstop |= ((diwhigh >> 13) & 1) << (8 + 2);
|
|
} else {
|
|
hstop |= 0x100 << 2;
|
|
}
|
|
// AGA only: horizontal DIWHIGH hires/shres bits.
|
|
if (diwhigh_written && aga_mode) {
|
|
hstrt |= (diwhigh >> 3) & 3;
|
|
hstop |= (diwhigh >> 11) & 3;
|
|
}
|
|
|
|
diw_hstrt = hstrt;
|
|
diw_hstop = hstop;
|
|
|
|
diwfirstword = coord_diw_shres_to_window_x(hstrt);
|
|
diwlastword = coord_diw_shres_to_window_x(hstop);
|
|
int mindiw = min_diwlastword;
|
|
if (diwfirstword < mindiw) {
|
|
diwfirstword = mindiw;
|
|
}
|
|
|
|
int hpos = current_hpos();
|
|
|
|
if (vstrt == vpos && vstop != vpos && vdiwstate == diw_states::DIW_waiting_start) {
|
|
// This may start BPL DMA immediately.
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
|
|
plffirstline = vstrt;
|
|
plflastline = vstop;
|
|
|
|
plfstrt = ddfstrt;
|
|
plfstop = ddfstop;
|
|
if (!ecs_agnus) {
|
|
plfstrt &= 0x00fc;
|
|
plfstop &= 0x00fc;
|
|
}
|
|
|
|
diw_change = 2;
|
|
|
|
decide_vline(hpos);
|
|
}
|
|
|
|
/* display mode changed (lores, doubling etc..), recalculate everything */
|
|
void init_custom(void)
|
|
{
|
|
check_nocustom();
|
|
update_mirrors();
|
|
create_cycle_diagram_table();
|
|
reset_drawing();
|
|
init_hz_normal();
|
|
calcdiw();
|
|
update_toscr_vars();
|
|
compute_shifter_mask();
|
|
compute_toscr_delay(bplcon1);
|
|
set_delay_lastcycle();
|
|
}
|
|
|
|
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 uae_u16 DENISEID(int *missing)
|
|
{
|
|
*missing = 0;
|
|
if (currprefs.cs_deniserev >= 0) {
|
|
return currprefs.cs_deniserev;
|
|
}
|
|
#ifdef AGA
|
|
if (aga_mode) {
|
|
if (currprefs.cs_ide == IDE_A4000) {
|
|
return 0xFCF8;
|
|
}
|
|
return 0x00F8;
|
|
}
|
|
#endif
|
|
if (ecs_denise) {
|
|
return 0xFFFC;
|
|
}
|
|
if (currprefs.cpu_model == 68000 && (currprefs.cpu_compatible || currprefs.cpu_memory_cycle_exact)) {
|
|
*missing = 1;
|
|
}
|
|
return 0xFFFF;
|
|
}
|
|
|
|
static bool blit_busy(int hpos, bool dmaconr)
|
|
{
|
|
// DMACONR latch load takes 1 cycle. Copper sees it immediately.
|
|
if (dmaconr && blt_info.finishhpos == hpos) {
|
|
return true;
|
|
}
|
|
if (!blt_info.blit_main && !blt_info.blit_finald) {
|
|
return false;
|
|
}
|
|
// AGA apparently fixes both bugs.
|
|
if (agnusa1000) {
|
|
// Blitter busy bug: A1000 Agnus only sets busy-bit when blitter gets first DMA slot.
|
|
if (!blt_info.got_cycle) {
|
|
return false;
|
|
}
|
|
if (blt_info.blit_pending) {
|
|
return true;
|
|
}
|
|
// Blitter is considered finished even if last D has not yet been written
|
|
if (!blt_info.blit_main) {
|
|
return false;
|
|
}
|
|
} else if (!aga_mode) {
|
|
if (blt_info.blit_pending) {
|
|
return true;
|
|
}
|
|
// Blitter is considered finished even if last D has not yet been written
|
|
if (!blt_info.blit_main) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
STATIC_INLINE uae_u16 DMACONR(int hpos)
|
|
{
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
dmacon &= ~(0x4000 | 0x2000);
|
|
dmacon |= (blit_busy(hpos, true) ? 0x4000 : 0x0000) | (blt_info.blitzero ? 0x2000 : 0);
|
|
return dmacon;
|
|
}
|
|
STATIC_INLINE uae_u16 INTENAR(void)
|
|
{
|
|
return intena & 0x7fff;
|
|
}
|
|
uae_u16 INTREQR(void)
|
|
{
|
|
return intreq & 0x7fff;
|
|
}
|
|
STATIC_INLINE uae_u16 ADKCONR(void)
|
|
{
|
|
return adkcon & 0x7fff;
|
|
}
|
|
|
|
STATIC_INLINE int islightpentriggered(void)
|
|
{
|
|
if (beamcon0 & BEAMCON0_LPENDIS)
|
|
return 0;
|
|
return lightpen_triggered != 0;
|
|
}
|
|
STATIC_INLINE int issyncstopped(void)
|
|
{
|
|
return (bplcon0 & 2) && (!currprefs.genlock || currprefs.genlock_effects);
|
|
}
|
|
|
|
STATIC_INLINE int GETVPOS(void)
|
|
{
|
|
return islightpentriggered() ? vpos_lpen : (issyncstopped() ? vpos_previous : vpos);
|
|
}
|
|
STATIC_INLINE int GETHPOS(void)
|
|
{
|
|
return islightpentriggered() ? hpos_lpen : (issyncstopped() ? hpos_previous : current_hpos());
|
|
}
|
|
|
|
// fake changing hpos when rom genlock test runs and genlock is connected
|
|
static bool hsyncdelay(void)
|
|
{
|
|
if (!currprefs.genlock || currprefs.genlock_effects) {
|
|
return false;
|
|
}
|
|
if (currprefs.cpu_memory_cycle_exact || currprefs.m68k_speed >= 0) {
|
|
return false;
|
|
}
|
|
if (bplcon0 == (0x0100 | 0x0002)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define CPU_ACCURATE (currprefs.cpu_model < 68020 || (currprefs.cpu_model == 68020 && currprefs.cpu_memory_cycle_exact))
|
|
|
|
static void check_line_enabled(void)
|
|
{
|
|
line_disabled &= ~3;
|
|
line_disabled |= line_hidden() ? 1 : 0;
|
|
line_disabled |= custom_disabled ? 2 : 0;
|
|
}
|
|
|
|
static void vb_check(void)
|
|
{
|
|
check_line_enabled();
|
|
|
|
// A1000 Agnus VBSTRT=first line, OCS and later: VBSTRT=last line
|
|
if (agnusa1000) {
|
|
if (vpos == 0) {
|
|
vb_start_line = 1;
|
|
vb_state = true;
|
|
}
|
|
} else {
|
|
if (vpos == hardwired_vbstrt + lof_store - 1) {
|
|
vb_start_line = 1;
|
|
vb_state = true;
|
|
}
|
|
}
|
|
if (vpos == hardwired_vbstop - 1) {
|
|
vb_end_line = true;
|
|
vb_state = false;
|
|
}
|
|
if (vpos == hardwired_vbstop) {
|
|
vb_end_line = false;
|
|
vb_state = false;
|
|
vb_start_line = 0;
|
|
vb_end_next_line = true;
|
|
}
|
|
}
|
|
|
|
static void vhpos_adj(uae_u16 *hpp, uae_u16 *vpp)
|
|
{
|
|
uae_u16 hp = *hpp;
|
|
uae_u16 vp = *vpp;
|
|
|
|
hp++;
|
|
if (hp == maxhposm0) {
|
|
hp = 0;
|
|
} else if (hp == 1) {
|
|
// HP=0-1: VP = previous line.
|
|
vp = vpos_prev;
|
|
}
|
|
|
|
*hpp = hp;
|
|
*vpp = vp;
|
|
}
|
|
|
|
static uae_u16 VPOSR(void)
|
|
{
|
|
uae_u16 csbit = 0;
|
|
uae_u16 vp = GETVPOS();
|
|
uae_u16 hp = GETHPOS();
|
|
int lofr = lof_store;
|
|
int lolr = lol;
|
|
|
|
if (hp == 0) {
|
|
// LOF and LOL toggles when HPOS=1
|
|
// Return pre-toggled value if VPOSR is read when HPOS=0
|
|
if (vp == 0) {
|
|
if ((bplcon0 & 4) && CPU_ACCURATE) {
|
|
lofr = lofr ? 0 : 1;
|
|
}
|
|
}
|
|
if (islinetoggle()) {
|
|
lolr = lolr ? 0 : 1;
|
|
}
|
|
}
|
|
vhpos_adj(&hp, &vp);
|
|
|
|
vp = (vp >> 8) & 7;
|
|
|
|
if (currprefs.cs_agnusrev >= 0) {
|
|
csbit |= currprefs.cs_agnusrev << 8;
|
|
} else {
|
|
#ifdef AGA
|
|
csbit |= aga_mode ? 0x2300 : 0;
|
|
#endif
|
|
csbit |= ecs_agnus ? 0x2000 : 0;
|
|
#if 0 /* apparently "8372 (Fat-hr) (agnushr),rev 5" does not exist */
|
|
if (currprefs.chipmem_size > 1024 * 1024 && (currprefs.chipset_mask & CSMASK_ECS_AGNUS))
|
|
csbit |= 0x2100;
|
|
#endif
|
|
if (currprefs.ntscmode) {
|
|
csbit |= 0x1000;
|
|
}
|
|
}
|
|
|
|
if (!ecs_agnus) {
|
|
vp &= 1;
|
|
} else {
|
|
vp |= lolr ? 0x80 : 0;
|
|
}
|
|
vp |= (lofr ? 0x8000 : 0) | csbit;
|
|
hsyncdelay();
|
|
#if 0
|
|
if (1 || (M68K_GETPC < 0x00f00000 || M68K_GETPC >= 0x10000000))
|
|
write_log (_T("VPOSR %04x at %08x\n"), vp, M68K_GETPC);
|
|
#endif
|
|
return vp;
|
|
}
|
|
|
|
static void VPOSW(int hpos, uae_u16 v)
|
|
{
|
|
int oldvpos = vpos;
|
|
int newvpos = vpos;
|
|
|
|
#if 0
|
|
if (M68K_GETPC < 0xf00000 || 1)
|
|
write_log (_T("VPOSW %04X PC=%08x\n"), v, M68K_GETPC);
|
|
#endif
|
|
if (lof_store != ((v & 0x8000) ? 1 : 0)) {
|
|
lof_store = (v & 0x8000) ? 1 : 0;
|
|
lof_changing = lof_store ? 1 : -1;
|
|
}
|
|
if (ecs_agnus) {
|
|
// LOL is always reset when VPOSW is written to.
|
|
lol = 0;
|
|
}
|
|
if (lof_changing) {
|
|
return;
|
|
}
|
|
newvpos &= 0x00ff;
|
|
v &= 7;
|
|
if (!ecs_agnus) {
|
|
v &= 1;
|
|
}
|
|
newvpos |= v << 8;
|
|
|
|
if (newvpos != oldvpos) {
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
cia_adjust_eclock_phase((newvpos - oldvpos) * maxhpos);
|
|
vposw_change++;
|
|
vpos = newvpos;
|
|
vb_check();
|
|
decide_vline(hpos);
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
}
|
|
|
|
static void VHPOSW_delayed(uae_u32 v)
|
|
{
|
|
int oldvpos = vpos;
|
|
int newvpos = vpos;
|
|
int hpos_org = current_hpos();
|
|
int hnew = hpos_org;
|
|
bool enabled = false;
|
|
|
|
decide_vline(hpos_org);
|
|
decide_hdiw(hpos_org);
|
|
decide_line(hpos_org);
|
|
decide_fetch_safe(hpos_org);
|
|
decide_sprites(hpos_org);
|
|
flush_display(fetchmode);
|
|
|
|
#if 0
|
|
if (M68K_GETPC < 0xf00000 || 1)
|
|
write_log (_T("VHPOSW %04X PC=%08x\n"), v, M68K_GETPC);
|
|
#endif
|
|
|
|
bool cpu_accurate = currprefs.m68k_speed >= 0 && !currprefs.cachesize && currprefs.cpu_memory_cycle_exact;
|
|
|
|
if (cpu_accurate || copper_access) {
|
|
enabled = true;
|
|
int hpos = hpos_org;
|
|
hnew = (v & 0xff);
|
|
int hnew_org = hnew;
|
|
bool newinc = false;
|
|
if (hpos == 0 || hpos == 1) {
|
|
hpos += maxhpos;
|
|
}
|
|
if (hnew == 0 || hnew == 1) {
|
|
hnew += maxhpos;
|
|
newinc = true;
|
|
}
|
|
int hdiff = hnew - hpos;
|
|
|
|
//write_log("%02x %02x %d\n", hpos_org, hnew_org, hdiff);
|
|
|
|
if (hdiff <= -maxhpos / 2 || hdiff >= maxhpos / 2) {
|
|
hdiff = 0;
|
|
}
|
|
if (copper_access && (hdiff & 1)) {
|
|
write_log("VHPOSW write %04X. New horizontal value is odd. Copper confusion possible.\n", v);
|
|
}
|
|
|
|
if (hdiff) {
|
|
bool fail = false;
|
|
if (hdiff & 1) {
|
|
vhposr_delay_offset = 1;
|
|
}
|
|
|
|
evt_t hsync_evtime = eventtab[ev_hsync].evtime;
|
|
evt_t hsync_oldcycles = eventtab[ev_hsync].oldcycles;
|
|
evt_t hsynch_evtime = eventtab[ev_hsynch].evtime;
|
|
evt_t hsynch_oldcycles = eventtab[ev_hsynch].oldcycles;
|
|
|
|
set_maxhpos(maxhpos);
|
|
if (newinc && hnew == maxhpos + 1) {
|
|
// 0000 -> 0001 (0 and 1 are part of previous line, vpos increases when hpos=1). No need to do anything
|
|
} else if (hnew_org == 0 && hpos_org > 1) {
|
|
fail = true;
|
|
} else if (hpos < maxhpos && hnew >= maxhpos) {
|
|
// maxhpos check skip: counter counts until it wraps around 0xFF->0x00
|
|
int hdiff2 = (0x100 - hnew) - (maxhpos - hpos);
|
|
hdiff2 *= CYCLE_UNIT;
|
|
eventtab[ev_hsync].evtime += hdiff2;
|
|
eventtab[ev_hsync].oldcycles = get_cycles() - hnew * CYCLE_UNIT;
|
|
eventtab[ev_hsynch].evtime += hdiff2;
|
|
eventtab[ev_hsynch].oldcycles += hdiff2;
|
|
// mark current line lenght = 0x100
|
|
set_maxhpos(0x100);
|
|
} else if (hdiff) {
|
|
hdiff = -hdiff;
|
|
hdiff *= CYCLE_UNIT;
|
|
eventtab[ev_hsync].evtime += hdiff;
|
|
eventtab[ev_hsync].oldcycles += hdiff;
|
|
eventtab[ev_hsynch].evtime += hdiff;
|
|
eventtab[ev_hsynch].oldcycles += hdiff;
|
|
}
|
|
|
|
int hpos2 = current_hpos_safe();
|
|
if (hpos2 < 0 || hpos2 > 255 || fail) {
|
|
eventtab[ev_hsync].evtime = hsync_evtime;
|
|
eventtab[ev_hsync].oldcycles = hsync_oldcycles;
|
|
eventtab[ev_hsynch].evtime = hsynch_evtime;
|
|
eventtab[ev_hsynch].oldcycles = hsynch_oldcycles;
|
|
hdiff = 0;
|
|
hpos_org = -1;
|
|
set_maxhpos(maxhpos);
|
|
}
|
|
|
|
events_schedule();
|
|
|
|
#ifdef DEBUGGER
|
|
if (newvpos == oldvpos && hdiff) {
|
|
record_dma_reoffset(vpos, hpos, hnew);
|
|
}
|
|
#endif
|
|
|
|
if (hdiff) {
|
|
int hold = hpos;
|
|
memset(cycle_line_slot + MAX_CHIPSETSLOTS + RGA_PIPELINE_ADJUST, 0, sizeof(uae_u8) * MAX_CHIPSETSLOTS_EXTRA);
|
|
memset(cycle_line_pipe + MAX_CHIPSETSLOTS + RGA_PIPELINE_ADJUST, 0, sizeof(uae_u16) * MAX_CHIPSETSLOTS_EXTRA);
|
|
memset(blitter_pipe + MAX_CHIPSETSLOTS + RGA_PIPELINE_ADJUST, 0, sizeof(uae_u16) * MAX_CHIPSETSLOTS_EXTRA);
|
|
int total = (MAX_CHIPSETSLOTS + RGA_PIPELINE_ADJUST + MAX_CHIPSETSLOTS_EXTRA) - (hnew > hold ? hnew : hold);
|
|
if (total > 0) {
|
|
memmove(cycle_line_slot + hnew, cycle_line_slot + hold, total * sizeof(uae_u8));
|
|
memmove(cycle_line_pipe + hnew, cycle_line_pipe + hold, total * sizeof(uae_u16));
|
|
memmove(blitter_pipe + hnew, blitter_pipe + hold, total * sizeof(uae_u16));
|
|
if (hnew > hold) {
|
|
int t = hnew - hold;
|
|
memset(cycle_line_slot, 0, t * sizeof(uae_u8));
|
|
memset(cycle_line_pipe, 0, t * sizeof(uae_u16));
|
|
memset(blitter_pipe, 0, t * sizeof(uae_u16));
|
|
}
|
|
}
|
|
int nhp = hnew + 1;
|
|
set_blitter_last(nhp);
|
|
last_copper_hpos = nhp;
|
|
last_fetch_hpos = nhp;
|
|
last_decide_line_hpos = nhp;
|
|
last_decide_sprite_hpos = nhp;
|
|
}
|
|
}
|
|
|
|
delay_cycles = ((hnew + 0) & 0xff) << CCK_SHRES_SHIFT;
|
|
|
|
last_diw_hpos = hnew << 1;
|
|
hdiw_denisecounter = ((hnew + 0) & 0xff) << CCK_SHRES_SHIFT;
|
|
}
|
|
|
|
newvpos &= 0xff00;
|
|
newvpos |= v >> 8;
|
|
if (enabled && (hpos_org == 0 || hpos_org == 1)) {
|
|
newvpos++;
|
|
}
|
|
if (newvpos > vpos || (newvpos <= maxvpos && vpos > maxvpos) || cpu_accurate || copper_access) {
|
|
if (newvpos != oldvpos) {
|
|
vposw_change++;
|
|
#ifdef DEBUGGER
|
|
if (hpos_org >= 0) {
|
|
record_dma_hsync(hpos_org);
|
|
if (debug_dma) {
|
|
int vp = vpos;
|
|
vpos = newvpos;
|
|
record_dma_hsync(maxhpos);
|
|
vpos = vp;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
vpos = newvpos;
|
|
#ifdef DEBUGGER
|
|
record_dma_denise(hnew, hdiw_denisecounter >> 2);
|
|
#endif
|
|
vb_check();
|
|
decide_vline(hnew);
|
|
vhposw_modified = true;
|
|
}
|
|
}
|
|
|
|
static void VHPOSW_delayed1(uae_u32 v)
|
|
{
|
|
int hpos = current_hpos();
|
|
decide_hdiw(hpos);
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
decide_sprites(hpos, false, true);
|
|
|
|
int hnew = v & 0xff;
|
|
last_sprite_hpos = (hnew << 1) - 1;
|
|
last_sprite_point = (((hnew + 0) & 0xff) << 1) + 0;
|
|
|
|
event2_newevent_xx(-1, 1 * CYCLE_UNIT, v, VHPOSW_delayed);
|
|
}
|
|
|
|
static void VHPOSW(uae_u16 v)
|
|
{
|
|
event2_newevent_xx(-1, 1 * CYCLE_UNIT, v, VHPOSW_delayed1);
|
|
}
|
|
|
|
static uae_u16 VHPOSR(void)
|
|
{
|
|
static uae_u16 oldhp;
|
|
uae_u16 vp = GETVPOS();
|
|
uae_u16 hp = GETHPOS();
|
|
|
|
vhpos_adj(&hp, &vp);
|
|
|
|
vp <<= 8;
|
|
|
|
if (hsyncdelay()) {
|
|
// fake continuously changing hpos in fastest possible modes
|
|
hp = oldhp % maxhpos;
|
|
oldhp++;
|
|
}
|
|
|
|
vp |= hp;
|
|
|
|
#if 0
|
|
if (M68K_GETPC < 0x00f00000 || M68K_GETPC >= 0x10000000)
|
|
write_log (_T("VPOS %04x %04x at %08x\n"), VPOSR (), vp, M68K_GETPC);
|
|
#endif
|
|
return vp;
|
|
}
|
|
|
|
static uae_u16 HHPOSR(void)
|
|
{
|
|
uae_u16 v;
|
|
if (islightpentriggered()) {
|
|
v = hhpos_lpen;
|
|
} else {
|
|
uae_u16 max = (new_beamcon0 & BEAMCON0_DUAL) ? htotal : maxhpos - 1;
|
|
v = hhpos + current_hpos() - hhpos_hpos;
|
|
if (hhpos <= max || v >= 0x100) {
|
|
if (max) {
|
|
v %= max;
|
|
} else {
|
|
v = 0;
|
|
}
|
|
}
|
|
}
|
|
v &= 0xff;
|
|
return v;
|
|
}
|
|
|
|
static void HHPOS(int hpos, uae_u16 v)
|
|
{
|
|
hhpos = v & (MAXHPOS_ROWS - 1);
|
|
hhpos_hpos = hpos;
|
|
}
|
|
|
|
static void SPRHSTRT(int hpos, uae_u16 v)
|
|
{
|
|
sprhstrt = v;
|
|
sprhstrt_v = v & (MAXVPOS_LINES_ECS - 1);
|
|
}
|
|
static void SPRHSTOP(int hpos, uae_u16 v)
|
|
{
|
|
sprhstop = v;
|
|
sprhstop_v = v & (MAXVPOS_LINES_ECS - 1);
|
|
}
|
|
static void BPLHSTRT(int hpos, uae_u16 v)
|
|
{
|
|
bplhstrt = v;
|
|
bplhstrt_v = v & (MAXVPOS_LINES_ECS - 1);
|
|
}
|
|
static void BPLHSTOP(int hpos, uae_u16 v)
|
|
{
|
|
bplhstop = v;
|
|
bplhstop_v = v & (MAXVPOS_LINES_ECS - 1);
|
|
}
|
|
|
|
/*
|
|
|
|
REFPTR bit mapping to hidden refresh DMA pointer register.
|
|
|
|
OCS Agnus (RAS/CAS 9/9, 8-bit ROR refresh):
|
|
|
|
B RAS CAS
|
|
0: 000 080
|
|
1: 101 000
|
|
2: 002 100
|
|
3: 004 000
|
|
4: 008 000
|
|
5: 010 000
|
|
6: 020 000
|
|
7: 040 000
|
|
8: 080 000
|
|
9: 000 001
|
|
A: 000 002
|
|
B: 000 004
|
|
C: 000 008
|
|
D: 000 010
|
|
E: 000 020
|
|
F: 000 040
|
|
|
|
ECS Agnus 1M (RAS/CAS 9/9, 9-bit ROR refresh):
|
|
|
|
B RAS CAS
|
|
0: 080 000
|
|
1: 100 001
|
|
2: 000 102
|
|
3: 000 004
|
|
4: 000 008
|
|
5: 000 010
|
|
6: 000 020
|
|
7: 000 040
|
|
8: 000 080
|
|
9: 001 000
|
|
A: 002 000
|
|
B: 004 000
|
|
C: 008 000
|
|
D: 010 000
|
|
E: 020 000
|
|
F: 040 000
|
|
|
|
ECS Agnus 2M (RAS/CAS 10/10, 9-bit ROR refresh):
|
|
|
|
B RAS CAS
|
|
0: 080 000
|
|
1: 100 001
|
|
2: 000 102
|
|
3: 200 004
|
|
4: 000 208
|
|
5: 000 010
|
|
6: 000 020
|
|
7: 000 040
|
|
8: 000 080
|
|
9: 001 000
|
|
A: 002 000
|
|
B: 004 000
|
|
C: 008 000
|
|
D: 010 000
|
|
E: 020 000
|
|
F: 040 000
|
|
|
|
AGA (RAS/CAS 10/10, CBR refresh)
|
|
|
|
0: 040/000
|
|
1: 080/001
|
|
2: 100/002
|
|
3: 200/004
|
|
4: 001/008
|
|
5: 000/010
|
|
6: 000/020
|
|
7: 000/040
|
|
8: 000/080
|
|
9: 000/100
|
|
A: 000/200
|
|
B: 002/000
|
|
C: 004/000
|
|
D: 008/000
|
|
E: 010/000
|
|
F: 020/000
|
|
|
|
*/
|
|
|
|
static void REFPTR(int hpos, uae_u16 v)
|
|
{
|
|
int diff = hpos - REFRESH_FIRST_HPOS;
|
|
// write exactly 1 cycle before refresh slot: write ignored.
|
|
if (diff == -1 || diff == 1 || diff == 3 || diff == 5) {
|
|
return;
|
|
}
|
|
int slot = diff / 2;
|
|
if (slot >= 0) {
|
|
if (slot > 4) {
|
|
slot = 4;
|
|
}
|
|
update_refptr(-1, slot, true, true);
|
|
}
|
|
|
|
if (aga_mode) {
|
|
refptr = 0;
|
|
if (v & (1 << 0)) {
|
|
refptr |= 0x020000;
|
|
}
|
|
if (v & (1 << 1)) {
|
|
refptr |= 0x040000 | 0x000001;
|
|
}
|
|
if (v & (1 << 2)) {
|
|
refptr |= 0x000100 | 0x000002;
|
|
}
|
|
if (v & (1 << 3)) {
|
|
refptr |= 0x080000 | 0x000004;
|
|
}
|
|
if (v & (1 << 4)) {
|
|
refptr |= 0x000200 | 0x000008;
|
|
}
|
|
if (v & (1 << 5)) {
|
|
refptr |= 0x000010;
|
|
}
|
|
if (v & (1 << 6)) {
|
|
refptr |= 0x000020;
|
|
}
|
|
if (v & (1 << 7)) {
|
|
refptr |= 0x000040;
|
|
}
|
|
if (v & (1 << 8)) {
|
|
refptr |= 0x000080;
|
|
}
|
|
if (v & (1 << 9)) {
|
|
refptr |= 0x000400;
|
|
}
|
|
if (v & (1 << 10)) {
|
|
refptr |= 0x000800;
|
|
}
|
|
if (v & (1 << 11)) {
|
|
refptr |= 0x001000;
|
|
}
|
|
if (v & (1 << 12)) {
|
|
refptr |= 0x002000;
|
|
}
|
|
if (v & (1 << 13)) {
|
|
refptr |= 0x004000;
|
|
}
|
|
if (v & (1 << 14)) {
|
|
refptr |= 0x008000;
|
|
}
|
|
if (v & (1 << 15)) {
|
|
refptr |= 0x010000;
|
|
}
|
|
refptr <<= 1;
|
|
} else if (ecs_agnus) {
|
|
refptr = v & 0xfffe;
|
|
if (v & 1) {
|
|
refptr |= 0x10000;
|
|
}
|
|
if (v & 2) {
|
|
refptr |= 0x20000;
|
|
}
|
|
if (v & 4) {
|
|
refptr |= 0x40000;
|
|
}
|
|
if (v & 8) {
|
|
refptr |= 0x80000;
|
|
}
|
|
if (currprefs.chipmem.size > 0x100000) {
|
|
if (v & 16) {
|
|
refptr |= 0x100000;
|
|
}
|
|
}
|
|
} else {
|
|
refptr = v & 0xfffe;
|
|
if (v & 1) {
|
|
refptr |= 0x10000;
|
|
}
|
|
if (v & 2) {
|
|
refptr |= 0x20000;
|
|
}
|
|
if (v & 4) {
|
|
refptr |= 0x40000;
|
|
}
|
|
}
|
|
#if 0
|
|
int ras, cas;
|
|
bool x = get_ras_cas(refptr, &ras, &cas);
|
|
write_log("%04x %08x %c %03x %03X\n", v, refptr, x ? '+' : ' ', ras, cas);
|
|
#endif
|
|
}
|
|
|
|
static int test_copper_dangerous(uaecptr address)
|
|
{
|
|
int addr = address & 0x01fe;
|
|
if (addr < ((copcon & 2) ? (ecs_agnus ? 0 : 0x40) : 0x80)) {
|
|
cop_state.state = COP_stop;
|
|
copper_enabled_thisline = 0;
|
|
unset_special (SPCFLAG_COPPER);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// if DMA was changed during same cycle: previous value is used
|
|
static bool is_blitter_dma(void)
|
|
{
|
|
bool dma = dmaen(DMA_BLITTER);
|
|
if (get_cycles() == blitter_dma_change_cycle) {
|
|
return dma == false;
|
|
}
|
|
return dma;
|
|
}
|
|
static bool is_copper_dma(bool checksame)
|
|
{
|
|
bool dma = dmaen(DMA_COPPER);
|
|
if (checksame && get_cycles() == copper_dma_change_cycle) {
|
|
return dma == false;
|
|
}
|
|
return dma;
|
|
}
|
|
|
|
static void immediate_copper(int num)
|
|
{
|
|
int pos = 0;
|
|
int oldpos = 0;
|
|
|
|
cop_state.state = COP_stop;
|
|
cop_state.ip = num == 1 ? cop1lc : cop2lc;
|
|
|
|
while (pos < (maxvpos << 5)) {
|
|
if (oldpos > pos) {
|
|
pos = oldpos;
|
|
}
|
|
if (!is_copper_dma(false)) {
|
|
break;
|
|
}
|
|
if (cop_state.ip >= currprefs.chipmem.size &&
|
|
(cop_state.ip < currprefs.z3chipmem.start_address || cop_state.ip >= currprefs.z3chipmem.start_address + currprefs.z3chipmem.size) &&
|
|
(cop_state.ip < debugmem_bank.start || cop_state.ip >= debugmem_bank.start + debugmem_bank.allocated_size))
|
|
break;
|
|
pos++;
|
|
oldpos = pos;
|
|
cop_state.ir[0] = chipmem_wget_indirect (cop_state.ip);
|
|
cop_state.ir[1] = chipmem_wget_indirect (cop_state.ip + 2);
|
|
cop_state.ip += 4;
|
|
if (!(cop_state.ir[0] & 1)) { // move
|
|
cop_state.ir[0] &= 0x1fe;
|
|
if (cop_state.ir[0] == 0x88) {
|
|
cop_state.ip = cop1lc;
|
|
continue;
|
|
}
|
|
if (cop_state.ir[0] == 0x8a) {
|
|
cop_state.ip = cop2lc;
|
|
continue;
|
|
}
|
|
if (test_copper_dangerous(cop_state.ir[0])) {
|
|
break;
|
|
}
|
|
custom_wput_1 (0, cop_state.ir[0], cop_state.ir[1], 0);
|
|
} else { // wait or skip
|
|
if ((cop_state.ir[0] >> 8) > ((pos >> 5) & 0xff))
|
|
pos = (((pos >> 5) & 0x100) | ((cop_state.ir[0] >> 8)) << 5) | ((cop_state.ir[0] & 0xff) >> 3);
|
|
if (cop_state.ir[0] >= 0xffdf && cop_state.ir[1] == 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(void);
|
|
|
|
// normal COPJMP write: takes 2 more cycles
|
|
static void COPJMP(int num, int vblank)
|
|
{
|
|
bool wasstopped = cop_state.state == COP_stop && !vblank;
|
|
|
|
unset_special(SPCFLAG_COPPER);
|
|
cop_state.ignore_next = 0;
|
|
|
|
if (!cop_state.strobe) {
|
|
cop_state.state_prev = cop_state.state;
|
|
}
|
|
if ((cop_state.state == COP_wait1 || cop_state.state == COP_waitforever) && !vblank && is_copper_dma(false)) {
|
|
if (blt_info.blit_main) {
|
|
static int warned = 100;
|
|
if (warned > 0) {
|
|
write_log(_T("Potential buggy copper cycle conflict with blitter PC=%08x, COP=%08x\n"), M68K_GETPC, cop_state.ip);
|
|
warned--;
|
|
}
|
|
}
|
|
int hp = current_hpos();
|
|
if ((hp & 1) && safecpu()) {
|
|
// CPU unaligned COPJMP while waiting
|
|
cop_state.state = COP_strobe_delay1x;
|
|
copper_bad_cycle_start = get_cycles();
|
|
} else {
|
|
cop_state.state = COP_strobe_delay1;
|
|
}
|
|
} else {
|
|
if (vblank) {
|
|
cop_state.state = COP_strobe_delay2;
|
|
switch (cop_state.state_prev)
|
|
{
|
|
case copper_states::COP_read1:
|
|
// Wake up is delayed by 1 copper cycle if copper is currently loading words
|
|
cop_state.state = COP_strobe_delay3;
|
|
break;
|
|
case copper_states::COP_read2:
|
|
// Wake up is delayed by 1 copper cycle if copper is currently loading words
|
|
cop_state.state = COP_strobe_delay4;
|
|
break;
|
|
}
|
|
} else {
|
|
cop_state.state = copper_access ? COP_strobe_delay1 : COP_strobe_extra;
|
|
}
|
|
}
|
|
cop_state.vblankip = cop1lc;
|
|
copper_enabled_thisline = 0;
|
|
cop_state.strobe |= num;
|
|
cop_state.last_strobe = num;
|
|
|
|
if (custom_disabled) {
|
|
immediate_copper(num);
|
|
return;
|
|
}
|
|
|
|
if (wasstopped) {
|
|
/* dma disabled, copper idle and accessed both COPxJMPs -> copper stops! */
|
|
cop_state.state = COP_stop;
|
|
} else if (is_copper_dma(false)) {
|
|
compute_spcflag_copper();
|
|
}
|
|
}
|
|
|
|
STATIC_INLINE void COPCON(uae_u16 a)
|
|
{
|
|
copcon = a;
|
|
}
|
|
|
|
static void check_copper_stop(void)
|
|
{
|
|
if (copper_enabled_thisline < 0 && !(is_copper_dma(false) && (dmacon & DMA_MASTER))) {
|
|
copper_enabled_thisline = 0;
|
|
unset_special(SPCFLAG_COPPER);
|
|
}
|
|
}
|
|
|
|
static void copper_stop(void)
|
|
{
|
|
copper_enabled_thisline = 0;
|
|
unset_special(SPCFLAG_COPPER);
|
|
}
|
|
|
|
static void bitplane_dma_change(uae_u32 v)
|
|
{
|
|
dmacon_bpl = (v & DMA_BITPLANE) && (v & DMA_MASTER);
|
|
}
|
|
|
|
static void compute_spcflag_copper_delayed(uae_u32 v)
|
|
{
|
|
compute_spcflag_copper();
|
|
}
|
|
|
|
static void DMACON(int hpos, uae_u16 v)
|
|
{
|
|
uae_u16 changed;
|
|
uae_u16 oldcon = dmacon;
|
|
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
|
|
setclr(&dmacon, v);
|
|
dmacon &= 0x07FF;
|
|
|
|
changed = dmacon ^ oldcon;
|
|
#if 0
|
|
if (changed)
|
|
write_log(_T("%04x -> %04x %08x\n"), oldcon, dmacon, m68k_getpc ());
|
|
#endif
|
|
|
|
int oldcop = (oldcon & DMA_COPPER) && (oldcon & DMA_MASTER);
|
|
int newcop = (dmacon & DMA_COPPER) && (dmacon & DMA_MASTER);
|
|
if (!oldcop && newcop) {
|
|
bootwarpmode();
|
|
}
|
|
if (newcop && !oldcop) {
|
|
if (copper_access || safecpu()) {
|
|
copper_dma_change_cycle = get_cycles();
|
|
event2_newevent_xx(-1, CYCLE_UNIT, 0, compute_spcflag_copper_delayed);
|
|
} else {
|
|
compute_spcflag_copper();
|
|
}
|
|
} else if (!newcop && oldcop) {
|
|
if (copper_access || safecpu()) {
|
|
copper_dma_change_cycle = get_cycles();
|
|
}
|
|
compute_spcflag_copper();
|
|
}
|
|
|
|
int oldblt = (oldcon & DMA_BLITTER) && (oldcon & DMA_MASTER);
|
|
int newblt = (dmacon & DMA_BLITTER) && (dmacon & DMA_MASTER);
|
|
if (oldblt != newblt && (copper_access || safecpu())) {
|
|
if (copper_access) {
|
|
blitter_dma_change_cycle = get_cycles();
|
|
} else {
|
|
// because of CPU vs blitter emulation side-effect
|
|
blitter_dma_change_cycle = get_cycles() + CYCLE_UNIT;
|
|
}
|
|
}
|
|
|
|
int oldspr = (oldcon & DMA_SPRITE) && (oldcon & DMA_MASTER);
|
|
int newspr = (dmacon & DMA_SPRITE) && (dmacon & DMA_MASTER);
|
|
if (!oldspr && newspr) {
|
|
if (copper_access || safecpu()) {
|
|
sprite_dma_change_cycle_on = get_cycles() + CYCLE_UNIT;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
int oldb = (oldcon & DMA_BLITTER) && (oldcon & DMA_MASTER);
|
|
int newb = (dmacon & DMA_BLITTER) && (dmacon & DMA_MASTER);
|
|
int oldbn = (oldcon & DMA_BLITPRI) != 0;
|
|
int newbn = (dmacon & DMA_BLITPRI) != 0;
|
|
if (oldbn != newbn)
|
|
write_log (_T("BLITTER NASTY: %d -> %d %08x\n"), oldbn, newbn, m68k_getpc ());
|
|
#endif
|
|
|
|
#if SPRITE_DEBUG > 0
|
|
{
|
|
int olds = (oldcon & DMA_SPRITE) && (oldcon & DMA_MASTER);
|
|
int news = (dmacon & DMA_SPRITE) && (dmacon & DMA_MASTER);
|
|
if (olds != news)
|
|
write_log (_T("SPRITE DMA: %d -> %d %08x\n"), olds, news, m68k_getpc ());
|
|
}
|
|
#endif
|
|
|
|
if ((dmacon & DMA_BLITPRI) > (oldcon & DMA_BLITPRI) && (blt_info.blit_main || blt_info.blit_finald || blt_info.blit_queued)) {
|
|
set_special(SPCFLAG_BLTNASTY);
|
|
}
|
|
|
|
if (dmaen(DMA_BLITTER) && blt_info.blit_pending) {
|
|
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 | DMA_AUD3 | DMA_AUD2 | DMA_AUD1 | DMA_AUD0)) {
|
|
audio_state_machine();
|
|
}
|
|
|
|
if (changed & (DMA_MASTER | DMA_BITPLANE)) {
|
|
// if ECS, DDFSTRT has already passed but DMA was off and DMA gets turned on: BPRUN actvates 1 cycle earlier
|
|
bool bpl = (dmacon & DMA_BITPLANE) && (dmacon & DMA_MASTER);
|
|
if (ecs_agnus && bpl && !dmacon_bpl && ddf_enable_on > 0) {
|
|
dmacon_bpl = true;
|
|
} else {
|
|
event2_newevent_xx(-1, CYCLE_UNIT, dmacon, bitplane_dma_change);
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
}
|
|
|
|
static int irq_forced;
|
|
static evt_t irq_forced_delay;
|
|
|
|
void IRQ_forced(int lvl, int delay)
|
|
{
|
|
irq_forced = lvl;
|
|
irq_forced_delay = 0;
|
|
if (delay > 0 && currprefs.cpu_compatible) {
|
|
irq_forced_delay = get_cycles() + delay * CYCLE_UNIT;
|
|
} else if (delay < 0) {
|
|
irq_forced_delay = -1;
|
|
}
|
|
doint();
|
|
}
|
|
|
|
void intlev_ack(int lvl)
|
|
{
|
|
if (!irq_forced) {
|
|
return;
|
|
}
|
|
if (lvl >= irq_forced && irq_forced_delay < 0) {
|
|
irq_forced_delay = 0;
|
|
irq_forced = 0;
|
|
}
|
|
}
|
|
|
|
int intlev(void)
|
|
{
|
|
if (irq_forced) {
|
|
int lvl = irq_forced;
|
|
if (irq_forced_delay == 0) {
|
|
irq_forced = 0;
|
|
} else if (irq_forced_delay > 0 && get_cycles() > irq_forced_delay) {
|
|
irq_forced = 0;
|
|
irq_forced_delay = 0;
|
|
}
|
|
return lvl;
|
|
}
|
|
uae_u16 imask = intreq2 & intena2;
|
|
if (!(imask && (intena2 & 0x4000)))
|
|
return 0;
|
|
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 0;
|
|
}
|
|
|
|
void rethink_uae_int(void)
|
|
{
|
|
bool irq2 = false;
|
|
bool irq6 = false;
|
|
|
|
if (uae_int_requested) {
|
|
if (uae_int_requested & 0xff00) {
|
|
irq6 = true;
|
|
}
|
|
if (uae_int_requested & 0x00ff) {
|
|
irq2 = true;
|
|
}
|
|
}
|
|
|
|
{
|
|
extern void bsdsock_fake_int_handler(void);
|
|
extern int volatile bsd_int_requested;
|
|
if (bsd_int_requested) {
|
|
bsdsock_fake_int_handler();
|
|
}
|
|
}
|
|
if (irq6) {
|
|
safe_interrupt_set(IRQ_SOURCE_UAE, 0, true);
|
|
}
|
|
if (irq2) {
|
|
safe_interrupt_set(IRQ_SOURCE_UAE, 0, false);
|
|
}
|
|
}
|
|
|
|
static void rethink_intreq(void)
|
|
{
|
|
#ifdef SERIAL_PORT
|
|
serial_rethink();
|
|
#endif
|
|
devices_rethink();
|
|
}
|
|
|
|
static void intreq_checks(uae_u16 oldreq, uae_u16 newreq)
|
|
{
|
|
if ((oldreq & 0x0800) && !(newreq & 0x0800)) {
|
|
serial_rbf_clear();
|
|
}
|
|
}
|
|
|
|
static void event_doint_delay_do_ext(uae_u32 v)
|
|
{
|
|
uae_u16 old = intreq2;
|
|
setclr(&intreq, (1 << v) | 0x8000);
|
|
setclr(&intreq2, (1 << v) | 0x8000);
|
|
intreq_checks(old, intreq2);
|
|
|
|
doint();
|
|
}
|
|
|
|
static void event_send_interrupt_do_ext(uae_u32 v)
|
|
{
|
|
//uae_u16 old = intreq;
|
|
//setclr(&intreq, (1 << v) | 0x8000);
|
|
//intreq_checks(old);
|
|
|
|
event2_newevent_xx(-1, 1 * CYCLE_UNIT, v, event_doint_delay_do_ext);
|
|
}
|
|
|
|
// external delayed interrupt
|
|
void INTREQ_INT(int num, int delay)
|
|
{
|
|
if (m68k_interrupt_delay) {
|
|
if (delay < CYCLE_UNIT) {
|
|
delay *= CYCLE_UNIT;
|
|
}
|
|
event2_newevent_xx(-1, delay + CYCLE_UNIT, num, event_doint_delay_do_ext);
|
|
} else {
|
|
event_doint_delay_do_ext(num);
|
|
}
|
|
}
|
|
|
|
static void event_doint_delay_do_intreq(uae_u32 v)
|
|
{
|
|
uae_u16 old = intreq2;
|
|
setclr(&intreq2, v);
|
|
intreq_checks(old, intreq2);
|
|
|
|
doint();
|
|
}
|
|
|
|
static void doint_delay_intreq(uae_u16 v)
|
|
{
|
|
if (m68k_interrupt_delay) {
|
|
// INTREQ or INTENA write: IPL line changes 0.5 CCKs later.
|
|
// 68000 needs one more CPU clock (0.5 CCK) before it detects it.
|
|
event2_newevent_xx(-1, 1 * CYCLE_UNIT, v, event_doint_delay_do_intreq);
|
|
} else {
|
|
event_doint_delay_do_intreq(v);
|
|
}
|
|
}
|
|
|
|
static void event_doint_delay_do_intena(uae_u32 v)
|
|
{
|
|
setclr(&intena2, v);
|
|
|
|
doint();
|
|
}
|
|
|
|
static void doint_delay_intena(uae_u16 v)
|
|
{
|
|
if (m68k_interrupt_delay) {
|
|
// INTREQ or INTENA write: IPL line changes 0.5 CCKs later.
|
|
// 68000 needs one more CPU clock (0.5 CCK) before it detects it.
|
|
event2_newevent_xx(-1, 1 * CYCLE_UNIT, v, event_doint_delay_do_intena);
|
|
}
|
|
else {
|
|
event_doint_delay_do_intena(v);
|
|
}
|
|
}
|
|
|
|
static void INTENA(uae_u16 v)
|
|
{
|
|
uae_u16 old = intena;
|
|
setclr(&intena, v);
|
|
|
|
if (old != intena) {
|
|
doint_delay_intena(v);
|
|
}
|
|
}
|
|
|
|
static void INTREQ_nodelay(uae_u16 v)
|
|
{
|
|
uae_u16 old = intreq;
|
|
setclr(&intreq, v);
|
|
intreq2 = intreq;
|
|
intreq_checks(old, intreq);
|
|
doint();
|
|
}
|
|
|
|
void INTREQ_f(uae_u16 v)
|
|
{
|
|
uae_u16 old = intreq;
|
|
setclr(&intreq, v);
|
|
intreq2 = intreq;
|
|
intreq_checks(old, intreq);
|
|
}
|
|
|
|
bool INTREQ_0(uae_u16 v)
|
|
{
|
|
uae_u16 old = intreq;
|
|
setclr(&intreq, v);
|
|
|
|
if (old != intreq) {
|
|
doint_delay_intreq(v);
|
|
}
|
|
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);
|
|
DISK_update_predict();
|
|
audio_update_adkmasks();
|
|
#ifdef SERIAL_PORT
|
|
if ((v >> 11) & 1) {
|
|
serial_uartbreak((adkcon >> 11) & 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void check_harddis(void)
|
|
{
|
|
// VARBEAMEN, HARDDIS, SHRES, UHRES
|
|
harddis_h = ecs_agnus && ((new_beamcon0 & BEAMCON0_VARBEAMEN) || (new_beamcon0 & BEAMCON0_HARDDIS) || (bplcon0 & 0x0040) || (bplcon0 & 0x0080));
|
|
// VARBEAMEN, VARVBEN, HARDDIS
|
|
harddis_v = ecs_agnus && ((new_beamcon0 & BEAMCON0_VARBEAMEN) || (new_beamcon0 & BEAMCON0_VARVBEN) || (new_beamcon0 & BEAMCON0_HARDDIS));
|
|
}
|
|
|
|
static void BEAMCON0(int hpos, uae_u16 v)
|
|
{
|
|
if (ecs_agnus) {
|
|
bool beamcon0_changed = false;
|
|
if (v != new_beamcon0) {
|
|
sync_changes(hpos);
|
|
new_beamcon0 = v;
|
|
write_log(_T("BEAMCON0 = %04X, PC=%08X\n"), v, M68K_GETPC);
|
|
// not LPENDIS, LOLDIS, PAL, BLANKEN, SYNC INVERT
|
|
if (v & ~(BEAMCON0_LPENDIS | BEAMCON0_LOLDIS | BEAMCON0_PAL | BEAMCON0_BLANKEN | BEAMCON0_CSYTRUE | BEAMCON0_VSYTRUE | BEAMCON0_HSYTRUE)) {
|
|
dumpsync();
|
|
}
|
|
beamcon0_changed = true;
|
|
}
|
|
beamcon0_saved = v;
|
|
record_register_change(hpos, 0x1dc, new_beamcon0);
|
|
check_harddis();
|
|
calcdiw();
|
|
if (beamcon0_changed) {
|
|
init_beamcon0(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void varsync(int reg, bool resync, int oldval)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
if (!ecs_agnus) {
|
|
return;
|
|
}
|
|
#ifdef PICASSO96
|
|
if (ad->picasso_on && p96refresh_active) {
|
|
vtotal = p96refresh_active;
|
|
return;
|
|
}
|
|
#endif
|
|
thisline_changed = 1;
|
|
updateextblk();
|
|
|
|
// VB
|
|
if ((reg == 0x1cc || reg == 0x1ce) && (beamcon0 & BEAMCON0_VARVBEN)) {
|
|
// check for >1 because of interlace modes
|
|
if ((reg == 0x1cc && abs(vbstrt - oldval) > 1) || (reg == 0x1ce && abs(vbstop - oldval) > 1)) {
|
|
// check VB changes only if there are not many changes per frame
|
|
varsync_maybe_changed[reg == 0x1cc ? 0 : 1]++;
|
|
}
|
|
}
|
|
// HB
|
|
if ((reg == 0x1c4 || reg == 0x1c6) && exthblank) {
|
|
// only do resync if HB value has been valid at least 66% of field
|
|
varhblank_lines = (maxvpos / 3) * 2;
|
|
}
|
|
|
|
if (!resync) {
|
|
return;
|
|
}
|
|
|
|
// TOTAL
|
|
if ((reg == 0x1c0 || reg == 0x1c8) && (beamcon0 & BEAMCON0_VARBEAMEN)) {
|
|
varsync_changed = 1;
|
|
nosignal_trigger = true;
|
|
}
|
|
// VB
|
|
if ((reg == 0x1cc || reg == 0x1ce) && (beamcon0 & BEAMCON0_VARVBEN)) {
|
|
varsync_changed = 1;
|
|
}
|
|
// VS
|
|
if ((reg == 0x1e0 || reg == 0x1ca) && (beamcon0 & bemcon0_vsync_mask)) {
|
|
varsync_changed = 1;
|
|
nosignal_trigger = true;
|
|
}
|
|
// HS
|
|
if ((reg == 0x1de || reg == 0x1c2) && (beamcon0 & bemcon0_hsync_mask)) {
|
|
varsync_changed = 1;
|
|
}
|
|
|
|
if (varsync_changed) {
|
|
varsync_maybe_changed[0] = 0;
|
|
varsync_maybe_changed[1] = 0;
|
|
}
|
|
|
|
if (varsync_changed) {
|
|
init_beamcon0(false);
|
|
}
|
|
}
|
|
|
|
#ifdef PICASSO96
|
|
void set_picasso_hack_rate(int hz)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
if (!ad->picasso_on)
|
|
return;
|
|
vpos_count = 0;
|
|
p96refresh_active = (int)(maxvpos_stored * vblank_hz_stored / hz);
|
|
if (!currprefs.cs_ciaatod)
|
|
changed_prefs.cs_ciaatod = currprefs.cs_ciaatod = currprefs.ntscmode ? 2 : 1;
|
|
if (p96refresh_active > 0) {
|
|
new_beamcon0 |= BEAMCON0_VARBEAMEN;
|
|
}
|
|
varsync_changed = 1;
|
|
}
|
|
#endif
|
|
|
|
/* "Dangerous" blitter D-channel: Writing to memory which is also currently
|
|
* read by bitplane DMA
|
|
*/
|
|
static void dcheck_is_blit_dangerous(void)
|
|
{
|
|
check_is_blit_dangerous(bplpt, bplcon0_planes, 50 << bplcon0_res);
|
|
}
|
|
|
|
static void BPLxPTH(int hpos, uae_u16 v, int num)
|
|
{
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
if (copper_access) {
|
|
int n = get_bitplane_dma_rel(hpos, 1);
|
|
if (n == num + 1) {
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bplpt[num] = (bplpt[num] & 0x0000ffff) | ((uae_u32)v << 16);
|
|
bplptx[num] = (bplptx[num] & 0x0000ffff) | ((uae_u32)v << 16);
|
|
dcheck_is_blit_dangerous ();
|
|
//write_log (_T("%d:%d:BPL%dPTH %08X COP=%08x\n"), hpos, vpos, num, bplpt[num], cop_state.ip);
|
|
}
|
|
static void BPLxPTL(int hpos, uae_u16 v, int num)
|
|
{
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
/* only detect copper accesses to prevent too fast CPU mode glitches */
|
|
if (copper_access) {
|
|
int n = get_bitplane_dma_rel(hpos, 1);
|
|
if (n == num + 1) {
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
return;
|
|
}
|
|
}
|
|
bplpt[num] = (bplpt[num] & 0xffff0000) | (v & 0x0000fffe);
|
|
bplptx[num] = (bplptx[num] & 0xffff0000) | (v & 0x0000fffe);
|
|
dcheck_is_blit_dangerous ();
|
|
//write_log (_T("%d:%d:BPL%dPTL %08X COP=%08x\n"), hpos, vpos, num, bplpt[num], cop_state.ip);
|
|
}
|
|
|
|
static void BPLCON0_Denise(int hpos, uae_u16 v)
|
|
{
|
|
v = BPLCON0_Denise_mask(v);
|
|
|
|
if (bplcon0d == v && !bplcon0d_change) {
|
|
return;
|
|
}
|
|
bplcon0d_change = false;
|
|
|
|
// fake unused 0x0080 bit as an EHB bit (see below)
|
|
if (isehb(bplcon0d, bplcon2)) {
|
|
v |= 0x0080;
|
|
}
|
|
|
|
record_register_change(hpos, 0x101, v);
|
|
|
|
bplcon0d = v & ~0x0080;
|
|
bplcon0d_old = -1;
|
|
|
|
#ifdef ECS_DENISE
|
|
if (ecs_denise) {
|
|
decide_sprites(hpos);
|
|
sprres = expand_sprres(v, bplcon3);
|
|
}
|
|
#endif
|
|
if (thisline_decision.plfleft < 0) {
|
|
update_denise(hpos);
|
|
}
|
|
}
|
|
|
|
static void bpldmainit(int hpos, uae_u16 bplcon0)
|
|
{
|
|
BPLCON0_Denise(hpos, bplcon0);
|
|
setup_fmodes(hpos, bplcon0);
|
|
}
|
|
|
|
static void BPLCON0(int hpos, uae_u16 v);
|
|
|
|
static void bplcon0_denise_reschange(int res, int oldres)
|
|
{
|
|
flush_display(fetchmode);
|
|
|
|
toscr_res = res;
|
|
toscr_res_old = res;
|
|
update_toscr_vars();
|
|
compute_toscr_delay(bplcon1);
|
|
|
|
if (aga_mode) {
|
|
if (oldres == RES_LORES && res == RES_HIRES) {
|
|
toscr_special_skip_ptr = toscr_spc_aga_lores_to_hires;
|
|
}
|
|
if (oldres == RES_HIRES && res == RES_LORES) {
|
|
toscr_special_skip_ptr = toscr_spc_aga_hires_to_lores;
|
|
}
|
|
} else if (0 && ecs_denise) {
|
|
if (oldres == RES_LORES && res == RES_HIRES) {
|
|
toscr_special_skip_ptr = toscr_spc_ecs_lores_to_hires;
|
|
}
|
|
if (oldres == RES_HIRES && res == RES_LORES) {
|
|
toscr_special_skip_ptr = toscr_spc_ecs_hires_to_lores;
|
|
}
|
|
} else if (1) {
|
|
if (oldres == RES_LORES && res == RES_HIRES) {
|
|
toscr_special_skip_ptr = toscr_spc_ocs_lores_to_hires;
|
|
}
|
|
if (oldres == RES_HIRES && res == RES_LORES) {
|
|
toscr_special_skip_ptr = toscr_spc_ocs_hires_to_lores;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void bplcon0_denise_change_early(int hpos, uae_u16 con0)
|
|
{
|
|
uae_u16 dcon0 = BPLCON0_Denise_mask(con0);
|
|
uae_u16 dcon0o = BPLCON0_Denise_mask(bplcon0);
|
|
|
|
int np = GET_PLANES(dcon0);
|
|
int res = GET_RES_DENISE(dcon0);
|
|
if (bplcon0d != dcon0) {
|
|
bplcon0d = dcon0;
|
|
bplcon0d_change = true;
|
|
}
|
|
if (np == toscr_nr_planes_shifter_new) {
|
|
toscr_nr_planes_shifter = np;
|
|
} else {
|
|
bplcon0_planes_changed = true;
|
|
}
|
|
|
|
decide_hdiw(hpos);
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
|
|
if (aga_mode) {
|
|
int e1 = isehb(dcon0, bplcon2);
|
|
int e2 = isehb(dcon0o, bplcon2);
|
|
if (e1 ^ e2) {
|
|
record_register_change(hpos, 0x201, dcon0);
|
|
}
|
|
}
|
|
|
|
toscr_nr_planes_shifter_new = np;
|
|
if (ecs_denise || aga_mode) {
|
|
toscr_nr_changed = np != toscr_nr_planes3 && res == toscr_res;
|
|
}
|
|
toscr_nr_planes3 = np;
|
|
|
|
if (currprefs.chipset_hr && res != toscr_res) {
|
|
bplcon0_denise_reschange(res, toscr_res);
|
|
}
|
|
|
|
if (isocs7planes()) {
|
|
if (toscr_nr_planes_shifter_new < 6) {
|
|
toscr_nr_planes_shifter_new = 6;
|
|
}
|
|
}
|
|
|
|
if (np > toscr_nr_planes2) {
|
|
update_toscr_planes(np, fetchmode);
|
|
toscr_nr_planes2 = np;
|
|
thisline_decision.max_planes = np;
|
|
}
|
|
}
|
|
|
|
static void BPLCON0(int hpos, uae_u16 v)
|
|
{
|
|
uae_u16 old = bplcon0;
|
|
bplcon0_saved = v;
|
|
v = BPLCON0_Agnus_mask(v);
|
|
|
|
#if SPRBORDER
|
|
v |= 1;
|
|
#endif
|
|
if (bplcon0 == v) {
|
|
return;
|
|
}
|
|
|
|
decide_hdiw(hpos);
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
|
|
if ((v & 1) != (bplcon0 & 1)) {
|
|
sync_color_changes(hpos);
|
|
}
|
|
|
|
if (!issyncstopped()) {
|
|
vpos_previous = vpos;
|
|
hpos_previous = hpos;
|
|
}
|
|
|
|
if (v & 4) {
|
|
bplcon0_interlace_seen = true;
|
|
}
|
|
|
|
if ((v & 8) && !lightpen_triggered && vb_state) {
|
|
// setting lightpen bit immediately freezes VPOSR if inside vblank and not already frozen
|
|
hhpos_lpen = HHPOSR();
|
|
lightpen_triggered = 1;
|
|
vpos_lpen = vpos;
|
|
hpos_lpen = hpos;
|
|
}
|
|
if (!(v & 8)) {
|
|
// clearing lightpen bit immediately returns VPOSR back to normal
|
|
lightpen_triggered = 0;
|
|
}
|
|
|
|
bplcon0 = v;
|
|
|
|
check_harddis();
|
|
|
|
if ((old & 1) != (bplcon0 & 1)) {
|
|
updateextblk();
|
|
}
|
|
|
|
bpldmainit(hpos, bplcon0);
|
|
|
|
if (!copper_access) {
|
|
bplcon0_denise_change_early(hpos, bplcon0);
|
|
}
|
|
}
|
|
|
|
static void BPLCON1(int hpos, uae_u16 v)
|
|
{
|
|
bplcon1_saved = v;
|
|
if (!aga_mode) {
|
|
v &= 0xff;
|
|
}
|
|
if (bplcon1 == v) {
|
|
return;
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
bplcon1_written = true;
|
|
bplcon1 = v;
|
|
hack_shres_delay(hpos);
|
|
}
|
|
|
|
static void BPLCON2(int hpos, uae_u16 v)
|
|
{
|
|
bplcon2_saved = v;
|
|
if (!aga_mode) {
|
|
v &= ~(0x100 | 0x80); // RDRAM and SOGEN
|
|
}
|
|
if (!ecs_denise) {
|
|
v &= 0x7f;
|
|
}
|
|
v &= ~0x8000; // unused
|
|
if (bplcon2 == v) {
|
|
return;
|
|
}
|
|
decide_line(hpos);
|
|
bplcon2 = v;
|
|
record_register_change(hpos, 0x104, bplcon2);
|
|
}
|
|
|
|
#ifdef ECS_DENISE
|
|
static void BPLCON3(int hpos, uae_u16 v)
|
|
{
|
|
uae_u16 old = bplcon3;
|
|
bplcon3_saved = v;
|
|
if (!ecs_denise) {
|
|
return;
|
|
}
|
|
if (!aga_mode) {
|
|
v &= (0x0020 | 0x0010 | 0x004 | 0x001);
|
|
v |= 0x0c00;
|
|
}
|
|
#if SPRBORDER
|
|
v |= 2;
|
|
#endif
|
|
if (bplcon3 == v) {
|
|
return;
|
|
}
|
|
decide_line(hpos);
|
|
decide_sprites(hpos);
|
|
if ((bplcon3 & 1) != (v & 1)) {
|
|
sync_color_changes(hpos);
|
|
}
|
|
bplcon3 = v;
|
|
if ((bplcon3 & 1) != (old & 1)) {
|
|
updateextblk();
|
|
}
|
|
sprres = expand_sprres(bplcon0, bplcon3);
|
|
record_register_change(hpos, 0x106, v);
|
|
}
|
|
#endif
|
|
#ifdef AGA
|
|
static void BPLCON4(int hpos, uae_u16 v)
|
|
{
|
|
bplcon4_saved = v;
|
|
if (!aga_mode) {
|
|
return;
|
|
}
|
|
if (bplcon4 == v) {
|
|
return;
|
|
}
|
|
decide_line(hpos);
|
|
record_register_change(hpos, 0x10c, v);
|
|
bplcon4 = v;
|
|
}
|
|
#endif
|
|
|
|
static void BPL1MOD(int hpos, uae_u16 v)
|
|
{
|
|
v &= ~1;
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
bpl1mod_prev = bpl1mod;
|
|
bpl1mod_hpos = hpos + 1;
|
|
if (bpl1mod_hpos >= maxhpos) {
|
|
bpl1mod_hpos -= maxhpos;
|
|
}
|
|
bpl1mod = v;
|
|
}
|
|
|
|
static void BPL2MOD(int hpos, uae_u16 v)
|
|
{
|
|
v &= ~1;
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
bpl2mod_prev = bpl2mod;
|
|
bpl2mod_hpos = hpos + 1;
|
|
if (bpl2mod_hpos >= maxhpos) {
|
|
bpl2mod_hpos -= maxhpos;
|
|
}
|
|
bpl2mod = v;
|
|
}
|
|
|
|
/* Needed in special OCS/ECS "7-plane" mode,
|
|
* also handles CPU generated bitplane data
|
|
*/
|
|
static void BPLxDAT_next(uae_u32 vv)
|
|
{
|
|
int num = vv >> 16;
|
|
uae_u16 data = vv;
|
|
|
|
int hpos = current_hpos();
|
|
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
flush_display(fetchmode);
|
|
|
|
fetched[num] = data;
|
|
if ((fmode & 3) == 3) {
|
|
fetched_aga[num] = ((uae_u64)last_custom_value << 48) | ((uae_u64)data << 32) | ((uae_u64)data << 16) | data;
|
|
} else if ((fmode & 3) == 2) {
|
|
fetched_aga[num] = ((uae_u32)last_custom_value << 16) | data;
|
|
} else if ((fmode & 3) == 1) {
|
|
fetched_aga[num] = ((uae_u32)data << 16) | data;
|
|
} else {
|
|
fetched_aga[num] = data;
|
|
}
|
|
|
|
if (num == 0) {
|
|
// ECS/AGA: HSYNC start - 1: $0C is first possible.
|
|
if ((ecs_denise && hpos != hsyncstartpos_start_cycles - 1) || (!ecs_denise && hpos >= OCS_DENISE_HBLANK_DISABLE_HPOS)) {
|
|
beginning_of_plane_block(hpos);
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
if (bplcon0_planes_changed) {
|
|
flush_display(fetchmode);
|
|
toscr_nr_planes_shifter = toscr_nr_planes_shifter_new;
|
|
bplcon0_planes_changed = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void BPLxDAT(int hpos, int num, uae_u16 data)
|
|
{
|
|
uae_u32 vv = (num << 16) | data;
|
|
if (!num) {
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
event2_newevent_xx_ce(1 * CYCLE_UNIT, vv, BPLxDAT_next);
|
|
}
|
|
|
|
static void DIWSTRT_next(uae_u32 v)
|
|
{
|
|
int hpos = current_hpos();
|
|
decide_hdiw(hpos);
|
|
decide_line(hpos);
|
|
diwhigh_written = 0;
|
|
diwstrt = v;
|
|
calcdiw();
|
|
}
|
|
static void DIWSTRT(int hpos, uae_u16 v)
|
|
{
|
|
if (diwstrt == v && !diwhigh_written) {
|
|
return;
|
|
}
|
|
event2_newevent_xx_ce(1 * CYCLE_UNIT, v, DIWSTRT_next);
|
|
}
|
|
|
|
static void DIWSTOP_next(uae_u32 v)
|
|
{
|
|
int hpos = current_hpos();
|
|
decide_hdiw(hpos);
|
|
decide_line(hpos);
|
|
diwhigh_written = 0;
|
|
diwstop = v;
|
|
calcdiw();
|
|
}
|
|
static void DIWSTOP(int hpos, uae_u16 v)
|
|
{
|
|
if (diwstop == v && !diwhigh_written) {
|
|
return;
|
|
}
|
|
event2_newevent_xx_ce(1 * CYCLE_UNIT, v, DIWSTOP_next);
|
|
}
|
|
|
|
static void DIWHIGH_next(uae_u32 v)
|
|
{
|
|
int hpos = current_hpos();
|
|
// DIWHIGH has 1.5CCK delay
|
|
decide_hdiw(hpos, true);
|
|
decide_line(hpos);
|
|
diwhigh_written = 1;
|
|
diwhigh = v;
|
|
calcdiw();
|
|
}
|
|
static void DIWHIGH(int hpos, uae_u16 v)
|
|
{
|
|
if (!ecs_agnus && !ecs_denise) {
|
|
return;
|
|
}
|
|
if (!aga_mode) {
|
|
v &= ~(0x0010 | 0x1000);
|
|
}
|
|
v &= ~(0x8000 | 0x4000 | 0x0080 | 0x0040);
|
|
if (diwhigh_written && diwhigh == v) {
|
|
return;
|
|
}
|
|
event2_newevent_xx_ce(1 * CYCLE_UNIT, v, DIWHIGH_next);
|
|
}
|
|
|
|
static void DDFSTRT(int hpos, uae_u16 v)
|
|
{
|
|
v &= 0xfe;
|
|
if (!ecs_agnus) {
|
|
v &= 0xfc;
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
ddfstrt = v;
|
|
calcdiw();
|
|
// DDFSTRT modified when DDFSTRT==hpos: neither value matches
|
|
ddfstrt_hpos = hpos;
|
|
}
|
|
|
|
static void DDFSTOP(int hpos, uae_u16 v)
|
|
{
|
|
v &= 0xfe;
|
|
if (!ecs_agnus) {
|
|
v &= 0xfc;
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
plfstop_prev = plfstop;
|
|
ddfstop = v;
|
|
calcdiw();
|
|
// DDFSTOP modified when DDFSTOP==hpos: old value matches
|
|
ddfstop_hpos = hpos;
|
|
estimate_last_fetch_cycle(hpos);
|
|
}
|
|
|
|
static void FMODE(int hpos, uae_u16 v)
|
|
{
|
|
if (!aga_mode) {
|
|
if (currprefs.monitoremu) {
|
|
specialmonitor_store_fmode(vpos, hpos, v);
|
|
}
|
|
fmode_saved = v;
|
|
v = 0;
|
|
}
|
|
v &= 0xC00F;
|
|
if (fmode == v) {
|
|
return;
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
if (aga_mode) {
|
|
fmode_saved = v;
|
|
}
|
|
set_chipset_mode();
|
|
bpldmainit(hpos, bplcon0);
|
|
record_register_change(hpos, 0x1fc, fmode);
|
|
}
|
|
|
|
static void FNULL(uae_u16 v)
|
|
{
|
|
|
|
}
|
|
|
|
static void BLTADAT(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
blt_info.bltadat = v;
|
|
}
|
|
/*
|
|
* "Loading data shifts it immediately" says the HRM. Well, that may
|
|
* be true for BLTBDAT, but not for BLTADAT - it appears the A data must be
|
|
* loaded for every word so that AFWM and ALWM can be applied.
|
|
*/
|
|
static void BLTBDAT(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
int shift = bltcon1 >> 12;
|
|
if (bltcon1 & 2) {
|
|
blt_info.bltbhold = (((uae_u32)v << 16) | blt_info.bltbold) >> (16 - shift);
|
|
} else {
|
|
blt_info.bltbhold = (((uae_u32)blt_info.bltbold << 16) | v) >> shift;
|
|
}
|
|
blt_info.bltbdat = v;
|
|
blt_info.bltbold = v;
|
|
}
|
|
static void BLTCDAT(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
blt_info.bltcdat = v;
|
|
reset_blit(0);
|
|
}
|
|
static void BLTAMOD(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 1);
|
|
blt_info.bltamod = (uae_s16)(v & 0xFFFE);
|
|
reset_blit(0);
|
|
}
|
|
static void BLTBMOD(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 1);
|
|
blt_info.bltbmod = (uae_s16)(v & 0xFFFE);
|
|
reset_blit(0);
|
|
}
|
|
static void BLTCMOD(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 1);
|
|
blt_info.bltcmod = (uae_s16)(v & 0xFFFE);
|
|
reset_blit(0);
|
|
}
|
|
static void BLTDMOD(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 1);
|
|
blt_info.bltdmod = (uae_s16)(v & 0xFFFE);
|
|
reset_blit(0);
|
|
}
|
|
static void BLTCON0(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit (hpos, 2);
|
|
bltcon0 = v;
|
|
reset_blit(1);
|
|
}
|
|
/* The next category is "Most useless hardware register".
|
|
* And the winner is... */
|
|
static void BLTCON0L(int hpos, uae_u16 v)
|
|
{
|
|
if (!ecs_agnus)
|
|
return; // ei voittoa.
|
|
maybe_blit(hpos, 2); bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF);
|
|
reset_blit(1);
|
|
}
|
|
static void BLTCON1(int hpos, uae_u16 v) {
|
|
maybe_blit(hpos, 2);
|
|
bltcon1 = v;
|
|
reset_blit(2);
|
|
}
|
|
static void BLTAFWM(int hpos, uae_u16 v) {
|
|
maybe_blit(hpos, 2);
|
|
blt_info.bltafwm = v;
|
|
reset_blit(0);
|
|
}
|
|
static void BLTALWM(int hpos, uae_u16 v) {
|
|
maybe_blit(hpos, 2);
|
|
blt_info.bltalwm = v;
|
|
reset_blit(0);
|
|
}
|
|
static void setblitx(int hpos, int n)
|
|
{
|
|
bltptxpos = (hpos + 1) % maxhpos;
|
|
bltptxc = copper_access ? n : -n;
|
|
}
|
|
static void BLTAPTH(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
bltptx = bltapt;
|
|
setblitx(hpos, 1);
|
|
bltapt = (bltapt & 0xffff) | ((uae_u32)v << 16);
|
|
}
|
|
static void BLTAPTL(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
bltptx = bltapt;
|
|
setblitx(hpos, 1);
|
|
bltapt = (bltapt & ~0xffff) | (v & 0xfffe);
|
|
}
|
|
static void BLTBPTH(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
bltptx = bltbpt;
|
|
bltptxpos = hpos;
|
|
bltptxc = copper_access ? 2 : -2;
|
|
bltbpt = (bltbpt & 0xffff) | ((uae_u32)v << 16);
|
|
}
|
|
static void BLTBPTL(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
bltptx = bltbpt;
|
|
setblitx(hpos, 2);
|
|
bltbpt = (bltbpt & ~0xffff) | (v & 0xfffe);
|
|
}
|
|
static void BLTCPTH(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
bltptx = bltcpt;
|
|
setblitx(hpos, 3);
|
|
bltcpt = (bltcpt & 0xffff) | ((uae_u32)v << 16);
|
|
}
|
|
static void BLTCPTL(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
bltptx = bltcpt;
|
|
setblitx(hpos, 3);
|
|
bltcpt = (bltcpt & ~0xffff) | (v & 0xfffe);
|
|
}
|
|
static void BLTDPTH (int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
|
|
if (blt_info.blit_finald && copper_access) {
|
|
static int warned = 100;
|
|
if (warned > 0) {
|
|
warned--;
|
|
write_log("Possible Copper Blitter wait bug detected COP=%08x\n", cop_state.ip);
|
|
}
|
|
}
|
|
bltptx = bltdpt;
|
|
setblitx(hpos, 4);
|
|
bltdpt = (bltdpt & 0xffff) | ((uae_u32)v << 16);
|
|
}
|
|
static void BLTDPTL(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
|
|
if (blt_info.blit_finald && copper_access) {
|
|
static int warned = 100;
|
|
if (warned > 0) {
|
|
warned--;
|
|
write_log("Possible Copper Blitter wait bug detected COP=%08x\n", cop_state.ip);
|
|
}
|
|
}
|
|
|
|
bltptx = bltdpt;
|
|
setblitx(hpos, 4);
|
|
bltdpt = (bltdpt & ~0xffff) | (v & 0xfffe);
|
|
}
|
|
|
|
static void BLTSIZE(int hpos, uae_u16 v)
|
|
{
|
|
maybe_blit(hpos, 0);
|
|
blt_info.vblitsize = v >> 6;
|
|
blt_info.hblitsize = v & 0x3F;
|
|
if (!blt_info.vblitsize) {
|
|
blt_info.vblitsize = 1024;
|
|
}
|
|
if (!blt_info.hblitsize) {
|
|
blt_info.hblitsize = 64;
|
|
}
|
|
do_blitter(hpos, copper_access, copper_access ? cop_state.ip : M68K_GETPC);
|
|
dcheck_is_blit_dangerous();
|
|
}
|
|
|
|
static void BLTSIZV(int hpos, uae_u16 v)
|
|
{
|
|
if (!ecs_agnus) {
|
|
return;
|
|
}
|
|
maybe_blit(hpos, 0);
|
|
blt_info.vblitsize = v & 0x7FFF;
|
|
if (!blt_info.vblitsize) {
|
|
blt_info.vblitsize = 0x8000;
|
|
}
|
|
}
|
|
|
|
static void BLTSIZH(int hpos, uae_u16 v)
|
|
{
|
|
if (!ecs_agnus) {
|
|
return;
|
|
}
|
|
maybe_blit(hpos, 0);
|
|
blt_info.hblitsize = v & 0x7FF;
|
|
if (!blt_info.vblitsize) {
|
|
blt_info.vblitsize = 0x8000;
|
|
}
|
|
if (!blt_info.hblitsize) {
|
|
blt_info.hblitsize = 0x0800;
|
|
}
|
|
do_blitter(hpos, copper_access, copper_access ? cop_state.ip : M68K_GETPC);
|
|
dcheck_is_blit_dangerous();
|
|
}
|
|
|
|
static void spr_arm(int num, int state)
|
|
{
|
|
#if SPRITE_DEBUG > 0
|
|
if (spr[num].armed != state)
|
|
write_log (_T("SPR%d ARM=%d\n"), num, state);
|
|
#endif
|
|
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 void sprstartstop(struct sprite *s)
|
|
{
|
|
if (vb_state || vb_end_line) {
|
|
return;
|
|
}
|
|
if (vpos == s->vstart) {
|
|
s->dmastate = 1;
|
|
}
|
|
if (vpos == s->vstop) {
|
|
s->dmastate = 0;
|
|
s->dmacycle = 0;
|
|
}
|
|
}
|
|
|
|
static void SPRxCTLPOS(int num)
|
|
{
|
|
int sprxp;
|
|
struct sprite *s = &spr[num];
|
|
|
|
sprstartstop(s);
|
|
sprxp = (s->pos & 0xFF) * 2 + (s->ctl & 1);
|
|
sprxp <<= sprite_buffer_res;
|
|
s->dblscan = 0;
|
|
s->ecs_denise_hires = 0;
|
|
/* Quite a bit salad in this register... */
|
|
if (0) {
|
|
}
|
|
#ifdef AGA
|
|
else if (aga_mode) {
|
|
sprxp |= ((s->ctl >> 3) & 3) >> (RES_MAX - sprite_buffer_res);
|
|
s->dblscan = s->pos & 0x80;
|
|
}
|
|
#endif
|
|
#ifdef ECS_DENISE
|
|
else if (ecs_denise && (s->ctl & 0x10)) {
|
|
// This bit only works as documented if superhires bitplane resolution.
|
|
// If bitplane resolution is lores or hires: sprite's first pixel row
|
|
// becomes transparent.
|
|
s->ecs_denise_hires = 1;
|
|
}
|
|
#endif
|
|
s->xpos = sprxp;
|
|
s->vstart = s->pos >> 8;
|
|
s->vstart |= (s->ctl & 0x04) ? 0x0100 : 0;
|
|
s->vstop = s->ctl >> 8;
|
|
s->vstop |= (s->ctl & 0x02) ? 0x100 : 0;
|
|
if (ecs_agnus) {
|
|
s->vstart |= (s->ctl & 0x40) ? 0x0200 : 0;
|
|
s->vstop |= (s->ctl & 0x20) ? 0x0200 : 0;
|
|
}
|
|
sprstartstop(s);
|
|
}
|
|
|
|
static void SPRxCTL_1(uae_u16 v, int num, int hpos)
|
|
{
|
|
struct sprite *s = &spr[num];
|
|
s->ctl = v;
|
|
spr_arm(num, 0);
|
|
SPRxCTLPOS(num);
|
|
|
|
#if 0
|
|
if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) {
|
|
if (s->ctl & (0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)) {
|
|
write_log(_T("ECS sprite %04x\n"), s->ctl);
|
|
}
|
|
}
|
|
#endif
|
|
#if SPRITE_DEBUG > 0
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log (_T("%d:%d:SPR%dCTL %04X P=%06X VSTRT=%d VSTOP=%d HSTRT=%d D=%d A=%d CP=%x PC=%x\n"),
|
|
vpos, hpos, num, v, s->pt, s->vstart, s->vstop, s->xpos, spr[num].dmastate, spr[num].armed, cop_state.ip, M68K_GETPC);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
static void SPRxPOS_1(uae_u16 v, int num, int hpos)
|
|
{
|
|
struct sprite *s = &spr[num];
|
|
|
|
s->pos = v;
|
|
SPRxCTLPOS(num);
|
|
#if SPRITE_DEBUG > 0
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log (_T("%d:%d:SPR%dPOS %04X P=%06X VSTRT=%d VSTOP=%d HSTRT=%d D=%d A=%d CP=%x PC=%x\n"),
|
|
vpos, hpos, num, v, s->pt, s->vstart, s->vstop, s->xpos, spr[num].dmastate, spr[num].armed, cop_state.ip, M68K_GETPC);
|
|
}
|
|
#endif
|
|
}
|
|
static void SPRxDATA_1(uae_u16 v, int num, int hpos)
|
|
{
|
|
struct sprite *s = &spr[num];
|
|
s->data[0] = v;
|
|
#ifdef AGA
|
|
if (aga_mode) {
|
|
s->data[1] = v;
|
|
s->data[2] = v;
|
|
s->data[3] = v;
|
|
s->width = sprite_width;
|
|
}
|
|
#endif
|
|
spr_arm(num, 1);
|
|
#if SPRITE_DEBUG >= 256
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log (_T("%d:%d:SPR%dDATA %04X P=%06X D=%d A=%d PC=%x\n"),
|
|
vpos, hpos, num, v, spr[num].pt, spr[num].dmastate, spr[num].armed, M68K_GETPC);
|
|
}
|
|
#endif
|
|
}
|
|
static void SPRxDATB_1(uae_u16 v, int num, int hpos)
|
|
{
|
|
struct sprite *s = &spr[num];
|
|
s->datb[0] = v;
|
|
#ifdef AGA
|
|
if (aga_mode) {
|
|
s->datb[1] = v;
|
|
s->datb[2] = v;
|
|
s->datb[3] = v;
|
|
s->width = sprite_width;
|
|
}
|
|
#endif
|
|
#if SPRITE_DEBUG >= 256
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log (_T("%d:%d:SPR%dDATB %04X P=%06X D=%d A=%d PC=%x\n"),
|
|
vpos, hpos, num, v, spr[num].pt, spr[num].dmastate, spr[num].armed, M68K_GETPC);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Undocumented AGA feature: if sprite is 64 pixel wide, SPRxDATx is written and next
|
|
// cycle is DMA fetch: sprite's first 32 pixels get replaced with bitplane data.
|
|
static void sprite_get_bpl_data(int hpos, struct sprite *s, uae_u16 *dat)
|
|
{
|
|
int nr = get_bitplane_dma_rel(hpos, 1);
|
|
uae_u32 v = (uae_u32)((fmode & 3) ? fetched_aga[nr] : fetched_aga_spr[nr]);
|
|
dat[0] = v >> 16;
|
|
dat[1] = (uae_u16)v;
|
|
}
|
|
|
|
/*
|
|
SPRxDATA and SPRxDATB is moved to shift register when SPRxPOS matches.
|
|
|
|
When copper writes to SPRxDATx exactly when SPRxPOS matches:
|
|
- If sprite low x bit (SPRCTL bit 0) is not set, shift register copy
|
|
is done first (previously loaded SPRxDATx value is shown) and then
|
|
new SPRxDATx gets stored for future use.
|
|
- If sprite low x bit is set, new SPRxDATx is stored, then SPRxPOS
|
|
matches and value written to SPRxDATx is visible.
|
|
|
|
- Writing to SPRxPOS when SPRxPOS matches: shift register
|
|
copy is always done first, then new SPRxPOS value is stored
|
|
for future use. (SPRxCTL not tested)
|
|
*/
|
|
|
|
static void SPRxDATA(int hpos, uae_u16 v, int num)
|
|
{
|
|
struct sprite *s = &spr[num];
|
|
decide_sprites(hpos, false);
|
|
SPRxDATA_1(v, num, hpos);
|
|
// if 32 (16-bit double CAS only) or 64 pixel wide sprite and SPRxDATx write:
|
|
// - first 16 pixel part: previous chipset bus data
|
|
// - following 16 pixel parts: written data
|
|
if (fmode & 8) {
|
|
if ((fmode & 4) && get_bitplane_dma_rel(hpos, -1)) {
|
|
sprite_get_bpl_data(hpos, s, &s->data[0]);
|
|
} else {
|
|
s->data[0] = last_custom_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SPRxDATB(int hpos, uae_u16 v, int num)
|
|
{
|
|
struct sprite *s = &spr[num];
|
|
decide_sprites(hpos, false);
|
|
SPRxDATB_1(v, num, hpos);
|
|
// See above
|
|
if (fmode & 8) {
|
|
if ((fmode & 4) && get_bitplane_dma_rel(hpos, -1)) {
|
|
sprite_get_bpl_data(hpos, s, &s->datb[0]);
|
|
} else {
|
|
s->datb[0] = last_custom_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SPRxCTL_2(uae_u32 vv)
|
|
{
|
|
int hpos = current_hpos();
|
|
uae_u16 v = vv >> 16;
|
|
int num = vv & 7;
|
|
#if SPRITE_DEBUG > 0
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log(_T("%d:%d:SPR%dCTLC %06X\n"), vpos, hpos, num, spr[num].pt);
|
|
}
|
|
#endif
|
|
|
|
decide_sprites(hpos);
|
|
SPRxCTL_1(v, num, hpos);
|
|
}
|
|
static void SPRxCTL(int hpos, uae_u16 v, int num)
|
|
{
|
|
uae_u32 vv = (v << 16) | num;
|
|
event2_newevent_xx_ce(1 * CYCLE_UNIT, vv, SPRxCTL_2);
|
|
}
|
|
|
|
static void SPRxPOS_2(uae_u32 vv)
|
|
{
|
|
int hpos = current_hpos();
|
|
uae_u16 v = vv >> 16;
|
|
int num = vv & 7;
|
|
struct sprite *s = &spr[num];
|
|
#if SPRITE_DEBUG > 0
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log(_T("%d:%d:SPR%dPOSC %06X\n"), vpos, hpos, num, s->pt);
|
|
}
|
|
#endif
|
|
decide_sprites(hpos);
|
|
SPRxPOS_1(v, num, hpos);
|
|
}
|
|
static void SPRxPOS(int hpos, uae_u16 v, int num)
|
|
{
|
|
uae_u32 vv = (v << 16) | num;
|
|
event2_newevent_xx_ce(1 * CYCLE_UNIT, vv, SPRxPOS_2);
|
|
}
|
|
|
|
static void SPRxPTH(int hpos, uae_u16 v, int num)
|
|
{
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
decide_sprites(hpos);
|
|
if (get_sprite_dma_rel(hpos, 1) != num || (!copper_access && !currprefs.cpu_memory_cycle_exact)) {
|
|
spr[num].pt &= 0xffff;
|
|
spr[num].pt |= (uae_u32)v << 16;
|
|
#if 0
|
|
} else {
|
|
write_log(_T("SPRxPTH %d\n"), num);
|
|
#endif
|
|
}
|
|
#if SPRITE_DEBUG > 0
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log (_T("%d:%d:SPR%dPTH %06X\n"), vpos, hpos, num, spr[num].pt);
|
|
}
|
|
#endif
|
|
}
|
|
static void SPRxPTL(int hpos, uae_u16 v, int num)
|
|
{
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
decide_sprites(hpos);
|
|
if (get_sprite_dma_rel(hpos, 1) != num || (!copper_access && !currprefs.cpu_memory_cycle_exact)) {
|
|
spr[num].pt &= ~0xffff;
|
|
spr[num].pt |= v & ~1;
|
|
#if 0
|
|
} else {
|
|
write_log(_T("SPRxPTL %d\n"), num);
|
|
#endif
|
|
}
|
|
#if SPRITE_DEBUG > 0
|
|
if (vpos >= SPRITE_DEBUG_MINY && vpos <= SPRITE_DEBUG_MAXY && (SPRITE_DEBUG & (1 << num))) {
|
|
write_log (_T("%d:%d:SPR%dPTL %06X\n"), vpos, hpos, num, spr[num].pt);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void CLXCON(int hpos, uae_u16 v)
|
|
{
|
|
if (hpos >= 0) {
|
|
check_collisions(hpos);
|
|
}
|
|
clxcon = v;
|
|
clxcon_bpl_enable = (v >> 6) & 63;
|
|
clxcon_bpl_match = v & 63;
|
|
//write_log("%08x %04x %d %d\n", M68K_GETPC, v, clxcon_bpl_enable, clxcon_bpl_match);
|
|
}
|
|
|
|
static void CLXCON2(int hpos, uae_u16 v)
|
|
{
|
|
if (!aga_mode)
|
|
return;
|
|
if (hpos >= 0) {
|
|
check_collisions(hpos);
|
|
}
|
|
clxcon2 = v;
|
|
clxcon_bpl_enable |= v & (0x40 | 0x80);
|
|
clxcon_bpl_match |= (v & (0x01 | 0x02)) << 6;
|
|
}
|
|
|
|
static uae_u16 CLXDAT(int hpos)
|
|
{
|
|
if (hpos >= 0) {
|
|
check_collisions(hpos);
|
|
}
|
|
uae_u16 v = clxdat | 0x8000;
|
|
//write_log("%08x %04x\n", M68K_GETPC, v);
|
|
clxdat = 0;
|
|
return v;
|
|
}
|
|
|
|
#ifdef AGA
|
|
|
|
void dump_aga_custom(void)
|
|
{
|
|
int c1, c2, c3, c4;
|
|
uae_u32 rgb1, rgb2, rgb3, rgb4;
|
|
|
|
for (c1 = 0; c1 < 64; c1++) {
|
|
c2 = c1 + 64;
|
|
c3 = c2 + 64;
|
|
c4 = c3 + 64;
|
|
rgb1 = current_colors.color_regs_aga[c1];
|
|
rgb2 = current_colors.color_regs_aga[c2];
|
|
rgb3 = current_colors.color_regs_aga[c3];
|
|
rgb4 = current_colors.color_regs_aga[c4];
|
|
console_out_f (_T("%3d %08X %3d %08X %3d %08X %3d %08X\n"),
|
|
c1, rgb1, c2, rgb2, c3, rgb3, c4, rgb4);
|
|
}
|
|
}
|
|
|
|
static uae_u16 COLOR_READ(int num)
|
|
{
|
|
int cr, cg, cb, colreg;
|
|
uae_u16 cval;
|
|
|
|
if (!aga_mode || !(bplcon2 & 0x0100))
|
|
return 0xffff;
|
|
|
|
colreg = ((bplcon3 >> 13) & 7) * 32 + num;
|
|
cr = (current_colors.color_regs_aga[colreg] >> 16) & 0xFF;
|
|
cg = (current_colors.color_regs_aga[colreg] >> 8) & 0xFF;
|
|
cb = current_colors.color_regs_aga[colreg] & 0xFF;
|
|
if (bplcon3 & 0x200) {
|
|
cval = ((cr & 15) << 8) | ((cg & 15) << 4) | ((cb & 15) << 0);
|
|
} else {
|
|
cval = ((cr >> 4) << 8) | ((cg >> 4) << 4) | ((cb >> 4) << 0);
|
|
if (color_regs_genlock[num])
|
|
cval |= 0x8000;
|
|
}
|
|
return cval;
|
|
}
|
|
#endif
|
|
|
|
static void checkautoscalecol0(void)
|
|
{
|
|
if (!copper_access)
|
|
return;
|
|
if (vpos < 20)
|
|
return;
|
|
if (isbrdblank(-1, bplcon0, bplcon3))
|
|
return;
|
|
// autoscale if copper changes COLOR00 on top or bottom of screen
|
|
if (vpos >= minfirstline) {
|
|
int vpos2 = autoscale_bordercolors ? minfirstline : vpos;
|
|
if (first_planes_vpos == 0) {
|
|
first_planes_vpos = vpos2 - 2;
|
|
}
|
|
if (plffirstline_total == current_maxvpos()) {
|
|
plffirstline_total = vpos2 - 2;
|
|
}
|
|
if (vpos2 > last_planes_vpos || vpos2 > plflastline_total) {
|
|
plflastline_total = last_planes_vpos = vpos2 + 3;
|
|
}
|
|
autoscale_bordercolors = 0;
|
|
} else {
|
|
autoscale_bordercolors++;
|
|
}
|
|
}
|
|
|
|
bool get_custom_color_reg(int colreg, uae_u8 *r, uae_u8 *g, uae_u8 *b)
|
|
{
|
|
if (colreg < 0)
|
|
return false;
|
|
if (colreg >= 32 && !aga_mode) {
|
|
return false;
|
|
}
|
|
if (colreg >= 256) {
|
|
return false;
|
|
}
|
|
if (aga_mode) {
|
|
*r = (current_colors.color_regs_aga[colreg] >> 16) & 0xFF;
|
|
*g = (current_colors.color_regs_aga[colreg] >> 8) & 0xFF;
|
|
*b = current_colors.color_regs_aga[colreg] & 0xFF;
|
|
} else {
|
|
*r = (current_colors.color_regs_ecs[colreg] >> 8) & 15;
|
|
*r |= (*r) << 4;
|
|
*g = (current_colors.color_regs_ecs[colreg] >> 4) & 15;
|
|
*g |= (*g) << 4;
|
|
*b = (current_colors.color_regs_ecs[colreg] >> 0) & 15;
|
|
*b |= (*b) << 4;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void COLOR_WRITE(int hpos, uae_u16 v, int num)
|
|
{
|
|
bool colzero = false;
|
|
bool samecycle = false;
|
|
|
|
// skip color register write color change state update if COLOR0 register was already written in same cycle
|
|
// fast CPU modes can write tens of thousands of color registers in single frame.
|
|
if (currprefs.m68k_speed < 0 && num == 0) {
|
|
if (line_start_cycles == custom_color_write_cycle) {
|
|
if (color_writes_num++ > maxhpos / 2) {
|
|
samecycle = true;
|
|
}
|
|
} else {
|
|
color_writes_num = 0;
|
|
custom_color_write_cycle = line_start_cycles;
|
|
}
|
|
}
|
|
|
|
#ifdef AGA
|
|
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) & 0xFF;
|
|
cg = (current_colors.color_regs_aga[colreg] >> 8) & 0xFF;
|
|
cb = current_colors.color_regs_aga[colreg] & 0xFF;
|
|
|
|
if (bplcon3 & 0x200) {
|
|
cr &= 0xF0; cr |= r;
|
|
cg &= 0xF0; cg |= g;
|
|
cb &= 0xF0; cb |= b;
|
|
} else {
|
|
cr = r + (r << 4);
|
|
cg = g + (g << 4);
|
|
cb = b + (b << 4);
|
|
color_regs_genlock[colreg] = v >> 15;
|
|
}
|
|
cval = (cr << 16) | (cg << 8) | cb | (color_regs_genlock[colreg] ? 0x80000000 : 0);
|
|
if (cval && colreg == 0)
|
|
colzero = true;
|
|
|
|
if (cval == current_colors.color_regs_aga[colreg])
|
|
return;
|
|
|
|
if (colreg == 0) {
|
|
checkautoscalecol0();
|
|
}
|
|
|
|
/* Call this with the old table still intact. */
|
|
if (!samecycle) {
|
|
record_color_change(hpos, colreg, cval);
|
|
}
|
|
remembered_color_entry = -1;
|
|
current_colors.color_regs_aga[colreg] = cval;
|
|
current_colors.acolors[colreg] = getxcolor(cval);
|
|
|
|
} else {
|
|
#endif
|
|
v &= 0x8fff;
|
|
if (!ecs_denise) {
|
|
v &= 0xfff;
|
|
}
|
|
color_regs_genlock[num] = v >> 15;
|
|
if (num && v == 0) {
|
|
colzero = true;
|
|
}
|
|
if (current_colors.color_regs_ecs[num] == v) {
|
|
return;
|
|
}
|
|
if (num == 0) {
|
|
checkautoscalecol0();
|
|
}
|
|
|
|
/* Call this with the old table still intact. */
|
|
if (!samecycle) {
|
|
record_color_change(hpos, num, v);
|
|
}
|
|
remembered_color_entry = -1;
|
|
current_colors.color_regs_ecs[num] = v;
|
|
current_colors.acolors[num] = getxcolor(v);
|
|
#ifdef AGA
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if ESTIMATED_FETCH_MODE
|
|
bool bitplane_dma_access(int hpos, int coffset)
|
|
{
|
|
if (line_cyclebased) {
|
|
int offset = get_rga_pipeline(hpos, coffset);
|
|
if (cycle_line_pipe[offset] & CYCLE_PIPE_BITPLANE) {
|
|
return true;
|
|
}
|
|
} else {
|
|
hpos += coffset;
|
|
if (hpos >= maxhpos) {
|
|
hpos -= maxhpos;
|
|
}
|
|
if (estimated_cycles[hpos] > 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
#else
|
|
bool bitplane_dma_access(int hpos, int offset)
|
|
{
|
|
hpos += offset;
|
|
if (hpos >= maxhpos) {
|
|
hpos -= maxhpos;
|
|
}
|
|
|
|
int i = bpl_estimate_index;
|
|
for (int j = 0; j < MAX_BPL_ESTIMATES; j++) {
|
|
struct bpl_estimate *be = &bpl_estimates[i];
|
|
if (be->vpos == vposh && be->startend) {
|
|
uae_u16 bploffset = 0xffff;
|
|
if (be->startend == 1) {
|
|
if (hpos >= be->start_pos && hpos < be->end_pos) {
|
|
bploffset = hpos - be->start_pos;
|
|
}
|
|
} else if (be->startend == 2) {
|
|
if (hpos >= be->start_pos) {
|
|
bploffset = hpos - be->start_pos;
|
|
} else if (hpos < be->end_pos) {
|
|
bploffset = maxhpos - be->start_pos + hpos;
|
|
}
|
|
}
|
|
if (bploffset < 0xffff) {
|
|
uae_u16 idx = (bploffset - be->ce_offset) & fetchstart_mask;
|
|
uae_s8 *cd = be->cycle_diagram;
|
|
int v = cd[idx];
|
|
if (v > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
i--;
|
|
if (i < 0) {
|
|
i += MAX_BPL_ESTIMATES;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool blitter_cant_access(int hpos)
|
|
{
|
|
if (!is_blitter_dma()) {
|
|
return true;
|
|
}
|
|
// bitplane dma check
|
|
int coffset = RGA_PIPELINE_OFFSET_BLITTER;
|
|
if (bitplane_dma_access(hpos, coffset)) {
|
|
return true;
|
|
}
|
|
// other DMA channel check
|
|
int offset = get_rga_pipeline(hpos, coffset);
|
|
if (cycle_line_pipe[offset] != 0 || blitter_pipe[offset] != 0) {
|
|
return true;
|
|
}
|
|
// static cycles are not in cycle_line_pipe
|
|
uae_u8 v = cycle_line_slot[offset];
|
|
if (v == CYCLE_REFRESH || v == CYCLE_STROBE) {
|
|
return true;
|
|
}
|
|
// DMAL is not in cycle_line_pipe
|
|
if (!dmal_alloc_mask || (hpos & 1) != ((DMAL_FIRST_HPOS - RGA_PIPELINE_OFFSET_BLITTER) & 1)) {
|
|
return false;
|
|
}
|
|
int dmaloffset = hpos - (DMAL_FIRST_HPOS - RGA_PIPELINE_OFFSET_BLITTER);
|
|
// 3 disk + 4 audio
|
|
if (dmaloffset >= 0 && dmaloffset < 3 * 2 + 4 * 2) {
|
|
if (dmal_alloc_mask & (3 << dmaloffset)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool copper_cant_read(int hpos, uae_u16 alloc)
|
|
{
|
|
if (!is_copper_dma(true)) {
|
|
return true;
|
|
}
|
|
|
|
int coffset = RGA_PIPELINE_OFFSET_COPPER;
|
|
if (hpos == maxhposm1 && maxhposeven == COPPER_CYCLE_POLARITY) {
|
|
// if copper used last cycle of scanline and it is even cycle and
|
|
// it wants next available copper cycle:
|
|
// next scanline's cycles 1 and 2 gets allocated.
|
|
// cycle 1 is not used and also not usable by CPU or blitter.
|
|
// cycle 2 is used by the copper.
|
|
int offset = get_rga_pipeline(hpos, coffset);
|
|
if (alloc && !bitplane_dma_access(hpos, coffset) && !cycle_line_pipe[offset]) {
|
|
cycle_line_pipe[offset] = CYCLE_PIPE_NONE | CYCLE_PIPE_COPPER;
|
|
blitter_pipe[offset] = CYCLE_PIPE_COPPER;
|
|
#ifdef DEBUGGER
|
|
int dvpos = vpos + 1;
|
|
if (is_last_line()) {
|
|
dvpos = 0;
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_event2(DMA_EVENT2_COPPERUSE, offset, dvpos);
|
|
}
|
|
#endif
|
|
}
|
|
coffset++;
|
|
}
|
|
|
|
if (bitplane_dma_access(hpos, coffset)) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_COPPERWANTED, hpos, vpos);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
int offset = get_rga_pipeline(hpos, coffset);
|
|
|
|
uae_u16 v = cycle_line_pipe[offset];
|
|
if (v != 0 && !(v & CYCLE_PIPE_COPPER)) {
|
|
#if CYCLE_CONFLICT_LOGGING
|
|
if ((v & CYCLE_PIPE_BLITTER) || (v & CYCLE_PIPE_CPUSTEAL) || (v & CYCLE_PIPE_SPRITE)) {
|
|
write_log(_T("Copper's cycle stolen by lower priority channel %04x!?\n"), v);
|
|
}
|
|
#endif
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_COPPERWANTED, hpos, vpos);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
if (alloc) {
|
|
cycle_line_pipe[offset] = alloc;
|
|
// Keep copper cycles, without it blitter would
|
|
// think copper cycles are free cycles because they
|
|
// are cleared after copper has processed them
|
|
blitter_pipe[offset] = CYCLE_PIPE_COPPER;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int custom_wput_copper(int hpos, uaecptr pt, uaecptr addr, uae_u32 value, int noget)
|
|
{
|
|
int v;
|
|
|
|
#ifdef DEBUGGER
|
|
value = debug_putpeekdma_chipset(0xdff000 + addr, value, MW_MASK_COPPER, 0x08c);
|
|
#endif
|
|
copper_access = 1;
|
|
v = custom_wput_1(hpos, addr, value, noget);
|
|
copper_access = 0;
|
|
return v;
|
|
}
|
|
|
|
static void bprun_start(int hpos)
|
|
{
|
|
bpl_hstart = hpos;
|
|
fetch_cycle = 0;
|
|
estimate_last_fetch_cycle(hpos);
|
|
}
|
|
|
|
static void decide_line(int endhpos)
|
|
{
|
|
int hpos = last_decide_line_hpos;
|
|
if (hpos >= endhpos) {
|
|
return;
|
|
}
|
|
|
|
bool ecs = ecs_agnus;
|
|
|
|
while (hpos < endhpos) {
|
|
|
|
// Do copper DMA first because we need to know
|
|
// new DDFSTRT etc values before bitplane decisions
|
|
if (cop_state.movedelay > 0) {
|
|
cop_state.movedelay--;
|
|
if (cop_state.movedelay == 0) {
|
|
custom_wput_copper(hpos, cop_state.moveptr, cop_state.moveaddr, cop_state.movedata, 0);
|
|
}
|
|
}
|
|
uae_u16 datreg = cycle_line_pipe[hpos];
|
|
if (datreg & CYCLE_PIPE_COPPER) {
|
|
do_copper_fetch(hpos, datreg);
|
|
cycle_line_pipe[hpos] = 0;
|
|
}
|
|
|
|
bool dma = dmacon_bpl;
|
|
bool diw = vdiwstate_bpl;
|
|
|
|
if (ecs) {
|
|
// ECS/AGA
|
|
|
|
// BPRUN latched: on
|
|
if (bprun < 0 && (hpos & 1)) {
|
|
decide_line_decision_fetches(hpos);
|
|
bprun = 1;
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
bprun_cycle = 0;
|
|
scandoubler_bpl_dma_start();
|
|
}
|
|
|
|
// BPRUN latched: off
|
|
if (bprun == 3) {
|
|
decide_line_decision_fetches(hpos);
|
|
if (ddf_stopping == 1) {
|
|
// If bpl sequencer counter was all ones (last cycle of block): ddf passed jumps to last step.
|
|
if (islastbplseq()) {
|
|
ddf_stopping = 2;
|
|
}
|
|
}
|
|
bprun = 0;
|
|
bprun_end = hpos;
|
|
plfstrt_sprite = 0x100;
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
end_estimate_last_fetch_cycle(hpos);
|
|
}
|
|
|
|
// DDFSTRT == DDFSTOP: BPRUN gets enabled and DDF passed state in next cycle.
|
|
if (ddf_enable_on < 0) {
|
|
ddf_enable_on = 0;
|
|
if (bprun && !ddf_stopping) {
|
|
decide_line_decision_fetches(hpos);
|
|
ddf_stopping = 1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_DDFSTOP2, hpos, vpos);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Hard start limit
|
|
if (hpos == (0x18 | 0)) {
|
|
ddf_limit = false;
|
|
}
|
|
|
|
// DDFSTRT
|
|
if (hpos == (plfstrt | 0) && hpos != ddfstrt_hpos) {
|
|
ddf_enable_on = 1;
|
|
}
|
|
|
|
// Hard stop limit
|
|
if (hpos == (0xd7 + 0)) {
|
|
// Triggers DDFSTOP condition if hard limits are not disabled.
|
|
ddf_limit = true;
|
|
if (bprun && !ddf_stopping) {
|
|
if (!harddis_h) {
|
|
decide_line_decision_fetches(hpos);
|
|
ddf_stopping = 1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_DDFSTOP2, hpos, vpos);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// DDFSTOP
|
|
// Triggers DDFSTOP condition.
|
|
// Clears DDF allowed flag.
|
|
if ((hpos == plfstop && hpos != ddfstop_hpos) || (hpos == plfstop_prev && hpos == ddfstop_hpos)) {
|
|
if (bprun && !ddf_stopping) {
|
|
decide_line_decision_fetches(hpos);
|
|
ddf_stopping = 1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_DDFSTOP, hpos, vpos);
|
|
}
|
|
#endif
|
|
}
|
|
if (plfstop != plfstrt) {
|
|
if (ddf_enable_on) {
|
|
ddf_enable_on = -1;
|
|
} else {
|
|
ddf_enable_on = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// BPRUN can only start if DMA, DIW or DDF state has changed since last time
|
|
if (!(hpos & 1)) {
|
|
bool hwi = dma && diw && ddf_enable_on > 0 && (!ddf_limit || harddis_h);
|
|
if (!bprun && dma && diw && hwi && !hwi_old) {
|
|
decide_line_decision_fetches(hpos);
|
|
// Bitplane sequencer activated
|
|
bprun = -1;
|
|
if (plfstrt_sprite > hpos + 1) {
|
|
plfstrt_sprite = hpos + 1;
|
|
}
|
|
bprun_start(hpos);
|
|
if (ddf_stopping) {
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_DDFSTRT, hpos, vpos);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
hwi_old = hwi;
|
|
}
|
|
|
|
if (bprun == 2) {
|
|
bprun = 3;
|
|
// If DDF has passed, jumps to last step.
|
|
// (For example Scoopex Crash landing crack intro)
|
|
if (ddf_stopping == 1) {
|
|
ddf_stopping = 2;
|
|
} else if (ddf_stopping == 0) {
|
|
// If DDF has not passed, set it as passed.
|
|
ddf_stopping = 1;
|
|
}
|
|
}
|
|
|
|
// DIW or DMA switched off: clear BPRUN
|
|
if ((!dma || !diw) && bprun == 1) {
|
|
decide_line_decision_fetches(hpos);
|
|
bprun = 2;
|
|
if (ddf_stopping == 1) {
|
|
ddf_stopping = 2;
|
|
bprun = 3;
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
|
|
} else {
|
|
// OCS
|
|
|
|
// BPRUN latched: On
|
|
if (bprun < 0 && (hpos & 1)) {
|
|
decide_line_decision_fetches(hpos);
|
|
bprun = 1;
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
bprun_cycle = 0;
|
|
scandoubler_bpl_dma_start();
|
|
}
|
|
// BPRUN latched: off
|
|
if (bprun == 3) {
|
|
decide_line_decision_fetches(hpos);
|
|
bprun = 0;
|
|
bprun_end = hpos;
|
|
plfstrt_sprite = 0x100;
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
end_estimate_last_fetch_cycle(hpos);
|
|
}
|
|
|
|
// Hard start limit
|
|
if (hpos == 0x18) {
|
|
ddf_limit = false;
|
|
}
|
|
|
|
// DDFSTOP
|
|
// Triggers DDFSTOP condition.
|
|
if ((hpos == plfstop && hpos != ddfstop_hpos) || (hpos == plfstop_prev && hpos == ddfstop_hpos)) {
|
|
if (bprun && !ddf_stopping) {
|
|
decide_line_decision_fetches(hpos);
|
|
ddf_stopping = 1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_DDFSTOP, hpos, vpos);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Hard stop limit
|
|
// Triggers DDFSTOP condition. Last cycle of bitplane DMA resets DDFSTRT limit.
|
|
if (hpos == (0xd7 + 0)) {
|
|
if (bprun && !ddf_stopping) {
|
|
decide_line_decision_fetches(hpos);
|
|
ddf_stopping = 1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_DDFSTOP2, hpos, vpos);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// DDFSTRT
|
|
if (hpos == (plfstrt | 0) && hpos != ddfstrt_hpos) {
|
|
ddfstrt_match = true;
|
|
} else {
|
|
ddfstrt_match = false;
|
|
}
|
|
|
|
if (!ddf_limit && ddfstrt_match && !bprun && dma && diw) {
|
|
decide_line_decision_fetches(hpos);
|
|
// Bitplane sequencer activated
|
|
bprun = -1;
|
|
if (plfstrt_sprite > hpos + 0) {
|
|
plfstrt_sprite = hpos + 0;
|
|
}
|
|
bprun_start(hpos);
|
|
if (ddf_stopping) {
|
|
bprun_pipeline_flush_delay = maxhpos;
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_DDFSTRT, hpos, vpos);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (bprun == 2) {
|
|
// If DDF has passed, jumps to last step.
|
|
// (For example Scoopex Crash landing crack intro)
|
|
if (ddf_stopping == 1) {
|
|
ddf_stopping = 2;
|
|
}
|
|
bprun = 3;
|
|
}
|
|
|
|
// DMA or DIW off: clear BPRUN
|
|
if ((!dma || !diw) && bprun == 1) {
|
|
decide_line_decision_fetches(hpos);
|
|
bprun = 2;
|
|
if (ddf_stopping == 1) {
|
|
ddf_stopping = 2;
|
|
bprun = 3;
|
|
}
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
}
|
|
|
|
}
|
|
|
|
if (line_cyclebased) {
|
|
decide_bpl_fetch(hpos);
|
|
}
|
|
|
|
hpos++;
|
|
}
|
|
|
|
decide_sprites_fetch(endhpos);
|
|
|
|
last_decide_line_hpos = endhpos;
|
|
}
|
|
|
|
static void setstrobecopip(void)
|
|
{
|
|
if (cop_state.strobe == 3) {
|
|
cop_state.ip = cop1lc | cop2lc;
|
|
} else if (cop_state.strobe == 1) {
|
|
cop_state.ip = cop1lc;
|
|
} else {
|
|
cop_state.ip = cop2lc;
|
|
}
|
|
cop_state.strobe = 0;
|
|
}
|
|
|
|
/*
|
|
CPU write COPJMP wakeup sequence when copper is waiting:
|
|
- Idle cycle (can be used by other DMA channel)
|
|
- Read word from current copper pointer (next word after wait instruction) to 1FE
|
|
This cycle can conflict with blitter DMA.
|
|
Normal copper cycles resume
|
|
- Write word from new copper pointer to 8C
|
|
*/
|
|
|
|
static void do_copper_fetch(int hpos, uae_u16 id)
|
|
{
|
|
if (scandoubled_line) {
|
|
return;
|
|
}
|
|
|
|
if (id & CYCLE_PIPE_NONE) {
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
return;
|
|
}
|
|
|
|
switch (cop_state.state)
|
|
{
|
|
case COP_strobe_delay1:
|
|
case COP_strobe_delay3:
|
|
{
|
|
// fake MOVE phase 1
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x8c, cop_state.ip, hpos, vpos, DMARECORD_COPPER, 0);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(cop_state.ip, MW_MASK_COPPER, 0x8c, cop_state.last_strobe == 2 ? 0x84 : 0x80);
|
|
}
|
|
#endif
|
|
cop_state.ir[0] = regs.chipset_latch_rw = last_custom_value = chipmem_wget_indirect(cop_state.ip);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(cop_state.ir[0]);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(cop_state.ir[0]);
|
|
}
|
|
#endif
|
|
cop_state.ip += 2;
|
|
if (cop_state.state == COP_strobe_delay3) {
|
|
cop_state.state = COP_strobe_delay5;
|
|
setstrobecopip();
|
|
} else {
|
|
cop_state.state = COP_strobe_delay2;
|
|
}
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
break;
|
|
}
|
|
case COP_strobe_delay2:
|
|
case COP_strobe_delay4:
|
|
{
|
|
// fake MOVE phase 2
|
|
#ifdef DEBUGGER
|
|
uae_u16 reg = 0x8c; // COPINS
|
|
if (debug_dma) {
|
|
record_dma_read(reg, cop_state.ip, hpos, vpos, DMARECORD_COPPER, 0);
|
|
}
|
|
#endif
|
|
cop_state.ir[1] = chipmem_wget_indirect(cop_state.ip);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(cop_state.ir[1]);
|
|
record_dma_event2(DMA_EVENT2_COPPERUSE, hpos, vpos);
|
|
}
|
|
#endif
|
|
if (cop_state.state == COP_strobe_delay4) {
|
|
cop_state.state = COP_strobe_delay5;
|
|
} else {
|
|
cop_state.state = COP_read1;
|
|
}
|
|
// Next cycle finally reads from new pointer
|
|
setstrobecopip();
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
}
|
|
break;
|
|
case COP_strobe_delay5:
|
|
{
|
|
// COPJMP when previous instruction is mid-cycle
|
|
cop_state.state = COP_read1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event2(DMA_EVENT2_COPPERUSE, hpos, vpos);
|
|
}
|
|
#endif
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
}
|
|
break;
|
|
case COP_strobe_delay2x:
|
|
#ifdef DEBUGGER
|
|
{
|
|
if (debug_dma) {
|
|
record_dma_read(0x1fe, cop_state.ip, hpos, vpos, DMARECORD_COPPER, 2);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(cop_state.ip, MW_MASK_COPPER, 0x1fe, 0x1fe);
|
|
}
|
|
uae_u16 v = chipmem_wget_indirect(cop_state.ip);
|
|
if (debug_dma) {
|
|
record_dma_read_value(v);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(v);
|
|
}
|
|
}
|
|
#endif
|
|
cop_state.state = COP_read1;
|
|
// Next cycle finally reads from new pointer
|
|
setstrobecopip();
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
break;
|
|
case COP_start_delay:
|
|
cop_state.state = COP_read1;
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x1fe, cop_state.ip, hpos, vpos, DMARECORD_COPPER, 2);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(cop_state.ip, MW_MASK_COPPER, 0x1fe, 0x1fe);
|
|
}
|
|
#endif
|
|
cop_state.ir[0] = regs.chipset_latch_rw = last_custom_value = chipmem_wget_indirect(cop_state.ip);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(cop_state.ir[0]);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(cop_state.ir[0]);
|
|
}
|
|
#endif
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
cop_state.ip = cop1lc;
|
|
break;
|
|
case COP_read1:
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x8c, cop_state.ip, hpos, vpos, DMARECORD_COPPER, 0);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(cop_state.ip, MW_MASK_COPPER, 0x8c, cop_state.last_strobe == 2 ? 0x84 : 0x80);
|
|
}
|
|
#endif
|
|
cop_state.ir[0] = regs.chipset_latch_rw = last_custom_value = chipmem_wget_indirect(cop_state.ip);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(cop_state.ir[0]);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(cop_state.ir[0]);
|
|
}
|
|
#endif
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
cop_state.ip += 2;
|
|
cop_state.state = COP_read2;
|
|
break;
|
|
case COP_read2:
|
|
if (cop_state.ir[0] & 1) {
|
|
// WAIT or SKIP
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x8c, cop_state.ip, hpos, vpos, DMARECORD_COPPER, (cop_state.ir[0] & 1) ? 1 : 0);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(cop_state.ip, MW_MASK_COPPER, 0x8c, cop_state.last_strobe == 2 ? 0x84 : 0x80);
|
|
}
|
|
#endif
|
|
cop_state.ir[1] = chipmem_wget_indirect(cop_state.ip);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(cop_state.ir[1]);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(cop_state.ir[1]);
|
|
}
|
|
#endif
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
cop_state.ip += 2;
|
|
|
|
#ifdef DEBUGGER
|
|
uaecptr debugip = cop_state.ip;
|
|
#endif
|
|
cop_state.ignore_next = 0;
|
|
if (cop_state.ir[1] & 1) {
|
|
cop_state.state = COP_skip_in2;
|
|
} else {
|
|
cop_state.state = COP_wait_in2;
|
|
}
|
|
|
|
cop_state.vcmp = (cop_state.ir[0] & (cop_state.ir[1] | 0x8000)) >> 8;
|
|
cop_state.hcmp = (cop_state.ir[0] & cop_state.ir[1] & 0xFE);
|
|
|
|
#ifdef DEBUGGER
|
|
record_copper(debugip - 4, debugip, cop_state.ir[0], cop_state.ir[1], hpos, vpos);
|
|
#endif
|
|
|
|
} else {
|
|
// MOVE
|
|
uae_u16 reg = cop_state.ir[0] & 0x1FE;
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(reg, cop_state.ip, hpos, vpos, DMARECORD_COPPER, 0);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(cop_state.ip, MW_MASK_COPPER, 0x8c, cop_state.last_strobe == 2 ? 0x84 : 0x80);
|
|
}
|
|
#endif
|
|
cop_state.ir[1] = chipmem_wget_indirect(cop_state.ip);
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(cop_state.ir[1]);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(cop_state.ir[1]);
|
|
}
|
|
#endif
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
cop_state.ip += 2;
|
|
|
|
#ifdef DEBUGGER
|
|
uaecptr debugip = cop_state.ip;
|
|
#endif
|
|
uae_u16 data = cop_state.ir[1];
|
|
cop_state.state = COP_read1;
|
|
|
|
test_copper_dangerous(reg);
|
|
// was "dangerous" register -> copper stopped
|
|
if (!copper_enabled_thisline)
|
|
return;
|
|
|
|
// Previous instruction was SKIP that skipped
|
|
if (cop_state.ignore_next > 0) {
|
|
reg = 0x1fe;
|
|
}
|
|
|
|
if (reg == 0x88) {
|
|
cop_state.strobe |= 1;
|
|
cop_state.last_strobe = 1;
|
|
cop_state.state = COP_strobe_delay1;
|
|
} else if (reg == 0x8a) {
|
|
cop_state.strobe |= 2;
|
|
cop_state.last_strobe = 2;
|
|
cop_state.state = COP_strobe_delay1;
|
|
} else {
|
|
if (reg == 0x100) {
|
|
// BPLCON0 new value is needed early
|
|
bplcon0_denise_change_early(hpos, data);
|
|
#if 1
|
|
cop_state.moveaddr = reg;
|
|
cop_state.movedata = data;
|
|
cop_state.movedelay = 1;
|
|
cop_state.moveptr = cop_state.ip;
|
|
#else
|
|
custom_wput_copper(hpos, cop_state.ip, reg, data, 0);
|
|
#endif
|
|
} else {
|
|
custom_wput_copper(hpos, cop_state.ip, reg, data, 0);
|
|
}
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (debug_copper && cop_state.ignore_next <= 0) {
|
|
uaecptr next = 0xffffffff;
|
|
if (reg == 0x88) {
|
|
next = cop1lc;
|
|
} else if (reg == 0x8a) {
|
|
next = cop2lc;
|
|
}
|
|
record_copper(debugip - 4, next, cop_state.ir[0], cop_state.ir[1], hpos, vpos);
|
|
}
|
|
#endif
|
|
cop_state.ignore_next = 0;
|
|
}
|
|
regs.chipset_latch_rw = last_custom_value = cop_state.ir[1];
|
|
check_copper_stop();
|
|
break;
|
|
|
|
case COP_strobe_extra:
|
|
// do nothing, happens if CPU wrote to COPxJMP but copper had already requested DMA cycle
|
|
// Cycle will complete but nothing will happen because COPxJMP write resets copper state.
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x8c, cop_state.ip, hpos, vpos, DMARECORD_COPPER, 0);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(cop_state.ip, MW_MASK_COPPER, 0x8c, cop_state.last_strobe == 2 ? 0x84 : 0x80);
|
|
}
|
|
#endif
|
|
alloc_cycle(hpos, CYCLE_COPPER);
|
|
break;
|
|
default:
|
|
write_log(_T("copper_fetch invalid state %d! %02x\n"), cop_state.state, id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int coppercomp(int hpos, bool blitwait)
|
|
{
|
|
int hpos_cmp = hpos;
|
|
int vpos_cmp = vpos;
|
|
|
|
// If waiting for last cycle of line and last cycle is even cycle:
|
|
// Horizontal counter has already wrapped around to zero.
|
|
if (hpos_cmp == maxhposm1 && maxhposeven == COPPER_CYCLE_POLARITY) {
|
|
hpos_cmp = 0;
|
|
}
|
|
|
|
int vp = vpos_cmp & (((cop_state.ir[1] >> 8) & 0x7F) | 0x80);
|
|
int hp = hpos_cmp & (cop_state.ir[1] & 0xFE);
|
|
|
|
if (vp < cop_state.vcmp) {
|
|
return -1;
|
|
}
|
|
|
|
// Cycle 0: copper can't wake up
|
|
if (hpos == 0) {
|
|
return 1;
|
|
}
|
|
|
|
if ((cop_state.ir[1] & 0x8000) == 0) {
|
|
decide_blitter(hpos);
|
|
if (blit_busy(-1, false)) {
|
|
if (blitwait) {
|
|
/* We need to wait for the blitter. */
|
|
cop_state.state = COP_bltwait;
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (vp > cop_state.vcmp) {
|
|
return 0;
|
|
}
|
|
if (vp == cop_state.vcmp && hp >= cop_state.hcmp) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void update_copper(int until_hpos)
|
|
{
|
|
if (1 && (custom_disabled || !copper_enabled_thisline)) {
|
|
last_copper_hpos = until_hpos;
|
|
return;
|
|
}
|
|
|
|
int hpos = last_copper_hpos;
|
|
while (hpos < until_hpos) {
|
|
|
|
int hp = hpos + 1;
|
|
// So we know about the fetch state.
|
|
decide_line(hp);
|
|
// bitplane only, don't want blitter to steal our cycles.
|
|
decide_bpl_fetch(hp);
|
|
|
|
// Handle copper DMA here if no bitplanes enabled.
|
|
// NOTE: can use odd cycles if DMA request was done during last cycle of line and it was even cycle (always in PAL).
|
|
// request cycle 226 (even), request always completes in 2 cycles = cycle 1 (odd).
|
|
// pipelined copper DMA read?
|
|
bool copper_dma = (cycle_line_pipe[hpos] & CYCLE_PIPE_COPPER) != 0;
|
|
if (copper_dma) {
|
|
uae_u16 v = cycle_line_pipe[hpos];
|
|
do_copper_fetch(hpos, v);
|
|
cycle_line_pipe[hpos] = 0;
|
|
}
|
|
|
|
if (!copper_enabled_thisline) {
|
|
goto next;
|
|
}
|
|
|
|
if ((hpos & 1) != COPPER_CYCLE_POLARITY) {
|
|
goto next;
|
|
}
|
|
|
|
// cycle 0 is only available if previous line ends to odd cycle
|
|
if (hpos == 0 && !maxhposeven_prev && cop_state.state != COP_start_delay) {
|
|
goto next;
|
|
}
|
|
|
|
#if CYCLE_CONFLICT_LOGGING
|
|
{
|
|
uae_u8 c = cycle_line_slot[hpos] & CYCLE_MASK;
|
|
if (c && c != CYCLE_BITPLANE && c != CYCLE_COPPER) {
|
|
write_log(_T("Only bitplanes has higher priority can copper. Cycle conflict %d!!\n"), c);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
switch (cop_state.state)
|
|
{
|
|
case COP_strobe_delay1:
|
|
// First 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.
|
|
if (copper_cant_read(hpos, 0)) {
|
|
cop_state.state = COP_strobe_delay2;
|
|
} else {
|
|
if (hpos == maxhposm1 && maxhposeven == COPPER_CYCLE_POLARITY) {
|
|
// if COP_strobe_delay2 would cross scanlines, it will be skipped!
|
|
cop_state.state = COP_read1;
|
|
setstrobecopip();
|
|
} else {
|
|
copper_cant_read(hpos, CYCLE_PIPE_COPPER | 0x08);
|
|
}
|
|
}
|
|
break;
|
|
case COP_strobe_delay2:
|
|
case COP_strobe_delay3:
|
|
case COP_strobe_delay4:
|
|
case COP_strobe_delay5:
|
|
// Second cycle after COPJMP does basically skipped MOVE (MOVE to 1FE)
|
|
// Cycle is used and needs to be free.
|
|
copper_cant_read(hpos, CYCLE_PIPE_COPPER);
|
|
break;
|
|
|
|
case COP_strobe_delay1x:
|
|
// First cycle after CPU write to COPJMP while Copper was waiting.
|
|
// Cycle can be free and copper won't allocate it.
|
|
if (copper_cant_read(hpos, 0)) {
|
|
// becomes normal non-buggy cycle if cycle was not free
|
|
cop_state.state = COP_strobe_delay2;
|
|
} else {
|
|
cop_state.state = COP_strobe_delay2x;
|
|
}
|
|
break;
|
|
case COP_strobe_delay2x:
|
|
// Second cycle fetches following word and tosses it away.
|
|
// Cycle can be free and copper won't allocate it.
|
|
// If Blitter uses this cycle = Copper's new PC gets copied to blitter DMA pointer..
|
|
if (!copper_cant_read(hpos, CYCLE_PIPE_COPPER | 0x09)) {
|
|
copper_bad_cycle = get_cycles();
|
|
if (copper_bad_cycle - copper_bad_cycle_start != 3 * CYCLE_UNIT) {
|
|
copper_bad_cycle = 0;
|
|
} else {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_SPECIAL, hpos, vpos);
|
|
}
|
|
#endif
|
|
// early COPJMP processing
|
|
cop_state.state = COP_read1;
|
|
copper_bad_cycle_pc_old = cop_state.ip;
|
|
setstrobecopip();
|
|
copper_bad_cycle_pc_new = cop_state.ip;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case COP_start_delay:
|
|
// cycle after vblank strobe fetches word from old pointer first
|
|
copper_cant_read(hpos, CYCLE_PIPE_COPPER | 0x01);
|
|
break;
|
|
|
|
// Request IR1
|
|
case COP_read1:
|
|
copper_cant_read(hpos, CYCLE_PIPE_COPPER | 0x02);
|
|
break;
|
|
|
|
// Request IR2
|
|
case COP_read2:
|
|
copper_cant_read(hpos, CYCLE_PIPE_COPPER | 0x03);
|
|
break;
|
|
|
|
// WAIT: Got IR2, first idle cycle.
|
|
// Need free cycle, cycle not allocated.
|
|
case COP_wait_in2:
|
|
{
|
|
if (copper_cant_read(hpos, 0)) {
|
|
goto next;
|
|
}
|
|
cop_state.state = COP_wait1;
|
|
}
|
|
break;
|
|
|
|
// WAIT: Second idle cycle. Wait until comparison matches.
|
|
// Need free cycle, cycle not allocated.
|
|
case COP_wait1:
|
|
{
|
|
int comp = coppercomp(hpos, true);
|
|
if (comp < 0) {
|
|
// If we need to wait for later scanline or blitter: no need to emulate copper cycle-by-cycle
|
|
if (cop_state.ir[0] == 0xFFFF && cop_state.ir[1] == 0xFFFE && maxhpos < 250) {
|
|
cop_state.state = COP_waitforever;
|
|
}
|
|
copper_enabled_thisline = 0;
|
|
unset_special(SPCFLAG_COPPER);
|
|
goto next;
|
|
}
|
|
|
|
if (comp) {
|
|
goto next;
|
|
}
|
|
|
|
if (copper_cant_read(hpos, 0)) {
|
|
goto next;
|
|
}
|
|
|
|
cop_state.state = COP_wait;
|
|
}
|
|
break;
|
|
|
|
// Wait finished, request IR1.
|
|
case COP_wait:
|
|
{
|
|
if (copper_cant_read(hpos, CYCLE_PIPE_COPPER | 0x04)) {
|
|
goto next;
|
|
}
|
|
#ifdef DEBUGGER
|
|
if (debug_dma)
|
|
record_dma_event(DMA_EVENT_COPPERWAKE, hpos, vpos);
|
|
if (debug_copper)
|
|
record_copper(cop_state.ip - 4, 0xffffffff, cop_state.ir[0], cop_state.ir[1], hpos, vpos);
|
|
#endif
|
|
cop_state.state = COP_read1;
|
|
}
|
|
break;
|
|
|
|
// SKIP: Got IR2. First idle cycle.
|
|
// Free cycle needed, cycle not allocated.
|
|
case COP_skip_in2:
|
|
|
|
if (copper_cant_read(hpos, 0)) {
|
|
goto next;
|
|
}
|
|
cop_state.state = COP_skip1;
|
|
break;
|
|
|
|
// SKIP: Second idle cycle. Do nothing.
|
|
// Free cycle needed, cycle not allocated.
|
|
case COP_skip1:
|
|
|
|
if (copper_cant_read(hpos, 0)) {
|
|
goto next;
|
|
}
|
|
|
|
cop_state.state = COP_skip;
|
|
break;
|
|
|
|
// Check comparison. SKIP finished. Request IR1.
|
|
case COP_skip:
|
|
if (copper_cant_read(hpos, CYCLE_PIPE_COPPER | 0x005)) {
|
|
goto next;
|
|
}
|
|
|
|
if (!coppercomp(hpos, false)) {
|
|
cop_state.ignore_next = 1;
|
|
} else {
|
|
cop_state.ignore_next = -1;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma && cop_state.ignore_next > 0)
|
|
record_dma_event(DMA_EVENT_COPPERSKIP, hpos, vpos);
|
|
if (debug_copper)
|
|
record_copper(cop_state.ip - 4, 0xffffffff, cop_state.ir[0], cop_state.ir[1], hpos, vpos);
|
|
#endif
|
|
|
|
cop_state.state = COP_read1;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
next:
|
|
if (copper_enabled_thisline) {
|
|
switch (cop_state.state)
|
|
{
|
|
case COP_strobe_extra:
|
|
// Wait 1 copper cycle doing nothing
|
|
cop_state.state = COP_strobe_delay1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hpos++;
|
|
last_copper_hpos = hpos;
|
|
}
|
|
}
|
|
|
|
static void compute_spcflag_copper(void)
|
|
{
|
|
copper_enabled_thisline = 0;
|
|
unset_special(SPCFLAG_COPPER);
|
|
if (!is_copper_dma(true) || cop_state.state == COP_stop || cop_state.state == COP_waitforever || cop_state.state == COP_bltwait || cop_state.state == COP_bltwait2 || custom_disabled)
|
|
return;
|
|
if (cop_state.state == COP_wait1 && is_copper_dma(false)) {
|
|
int vp = vpos & (((cop_state.ir[1] >> 8) & 0x7F) | 0x80);
|
|
if (vp < cop_state.vcmp) {
|
|
return;
|
|
}
|
|
}
|
|
last_copper_hpos = current_hpos();
|
|
copper_enabled_thisline = 1;
|
|
set_special(SPCFLAG_COPPER);
|
|
}
|
|
|
|
static void blitter_done_notify_wakeup(uae_u32 temp)
|
|
{
|
|
if (cop_state.state != COP_bltwait2) {
|
|
return;
|
|
}
|
|
// blitter_done_notify() might be called too early, wait a bit if blitter is still busy.
|
|
if (blit_busy(-1, false)) {
|
|
event2_newevent_xx(-1, 1 * CYCLE_UNIT, 0, blitter_done_notify_wakeup);
|
|
return;
|
|
}
|
|
|
|
cop_state.state = COP_wait1;
|
|
compute_spcflag_copper();
|
|
#ifdef DEBUGGER
|
|
if (copper_enabled_thisline) {
|
|
int hpos = current_hpos();
|
|
if (debug_dma) {
|
|
record_dma_event(DMA_EVENT_COPPERWAKE, hpos, vpos);
|
|
}
|
|
if (debug_copper) {
|
|
record_copper_blitwait(cop_state.ip - 4, hpos, vpos);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void blitter_done_notify(int blitline)
|
|
{
|
|
if (cop_state.state != COP_bltwait) {
|
|
return;
|
|
}
|
|
cop_state.state = COP_bltwait2;
|
|
event2_newevent_xx(-1, 1 * CYCLE_UNIT, 0, blitter_done_notify_wakeup);
|
|
}
|
|
|
|
static void sync_copper(int hpos)
|
|
{
|
|
if (copper_enabled_thisline) {
|
|
update_copper(hpos);
|
|
}
|
|
}
|
|
|
|
static void cursorsprite(void)
|
|
{
|
|
if (!dmaen(DMA_SPRITE) || first_planes_vpos == 0) {
|
|
return;
|
|
}
|
|
struct sprite *s = &spr[0];
|
|
sprite_0 = s->pt;
|
|
sprite_0_height = s->vstop - s->vstart;
|
|
sprite_0_colors[0] = 0;
|
|
sprite_0_doubled = 0;
|
|
if (sprres == 0) {
|
|
sprite_0_doubled = 1;
|
|
}
|
|
if (spr[0].dblscan) {
|
|
sprite_0_height /= 2;
|
|
}
|
|
if (aga_mode) {
|
|
int sbasecol = ((bplcon4 >> 4) & 15) << 4;
|
|
sprite_0_colors[1] = current_colors.color_regs_aga[sbasecol + 1];
|
|
sprite_0_colors[2] = current_colors.color_regs_aga[sbasecol + 2];
|
|
sprite_0_colors[3] = current_colors.color_regs_aga[sbasecol + 3];
|
|
} else {
|
|
sprite_0_colors[1] = xcolors[current_colors.color_regs_ecs[17]];
|
|
sprite_0_colors[2] = xcolors[current_colors.color_regs_ecs[18]];
|
|
sprite_0_colors[3] = xcolors[current_colors.color_regs_ecs[19]];
|
|
}
|
|
sprite_0_width = sprite_width;
|
|
if (currprefs.input_tablet && (currprefs.input_mouse_untrap & MOUSEUNTRAP_MAGIC)) {
|
|
if (currprefs.input_magic_mouse_cursor == MAGICMOUSE_HOST_ONLY && mousehack_alive()) {
|
|
magic_sprite_mask &= ~1;
|
|
} else {
|
|
magic_sprite_mask |= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static uae_u16 sprite_fetch(struct sprite *s, uaecptr pt, int hpos, int slot, int mode)
|
|
{
|
|
uae_u16 data = regs.chipset_latch_rw;
|
|
|
|
#if CYCLE_CONFLICT_LOGGING
|
|
if ((hpos & 1) != (SPR_FIRST_HPOS & 1)) {
|
|
int num = s - &spr[0];
|
|
write_log(_T("Sprite %d, hpos %d wrong cycle polarity!\n"), num, hpos);
|
|
}
|
|
#endif
|
|
#ifdef DEBUGGER
|
|
int num = addrdiff(s, &spr[0]);
|
|
if (debug_dma) {
|
|
record_dma_read(num * 8 + 0x140 + mode * 4 + slot * 2, pt, hpos, vpos, DMARECORD_SPRITE, num);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(pt, MW_MASK_SPR_0 << num, num * 8 + 0x140 + mode * 4 + slot * 2, num * 4 + 0x120);
|
|
}
|
|
#endif
|
|
data = regs.chipset_latch_rw = chipmem_wget_indirect(pt);
|
|
alloc_cycle(hpos, CYCLE_SPRITE);
|
|
return data;
|
|
}
|
|
|
|
static void sprite_fetch_full(struct sprite *s, int hpos, int slot, int mode, uae_u16 *v0, uae_u32 *v1, uae_u32 *v2)
|
|
{
|
|
uae_u32 data321 = 0, data322 = 0;
|
|
uae_u16 data16;
|
|
|
|
if (sprite_width == 16) {
|
|
|
|
data16 = sprite_fetch(s, s->pt, hpos, slot, mode);
|
|
|
|
#ifdef DEBUGGER
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(data16);
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_read_value(data16);
|
|
}
|
|
#endif
|
|
|
|
s->pt += 2;
|
|
|
|
} else if (sprite_width == 64) {
|
|
|
|
uaecptr pm = s->pt & ~7;
|
|
uaecptr pm1, pm2;
|
|
if (s->pt & 4) {
|
|
pm1 = pm + 4;
|
|
pm2 = pm + 4;
|
|
} else {
|
|
pm1 = pm;
|
|
pm2 = pm + 4;
|
|
}
|
|
data321 = sprite_fetch(s, pm1, hpos, slot, mode) << 16;
|
|
data321 |= chipmem_wget_indirect(pm1 + 2);
|
|
data322 = chipmem_wget_indirect(pm2) << 16;
|
|
data322 |= chipmem_wget_indirect(pm2 + 2);
|
|
if (s->pt & 2) {
|
|
data321 &= 0x0000ffff;
|
|
data322 &= 0x0000ffff;
|
|
data321 |= data321 << 16;
|
|
data322 |= data322 << 16;
|
|
}
|
|
data16 = data321 >> 16;
|
|
|
|
#ifdef DEBUGGER
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value_long(data321, pm1 - s->pt);
|
|
debug_getpeekdma_value_long(data322, pm2 - s->pt);
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_read_value_wide((((uae_u64)data321) << 32) | data322, true);
|
|
}
|
|
#endif
|
|
|
|
s->pt += 8;
|
|
|
|
} else { // 32
|
|
|
|
uaecptr pm = s->pt & ~3;
|
|
data321 = sprite_fetch(s, pm, hpos, slot, mode) << 16;
|
|
data321 |= chipmem_wget_indirect(pm + 2);
|
|
if (s->pt & 2) {
|
|
data321 &= 0x0000ffff;
|
|
data321 |= data321 << 16;
|
|
} else if (fetchmode_fmode_spr & 2) {
|
|
data321 &= 0xffff0000;
|
|
data321 |= data321 >> 16;
|
|
}
|
|
data16 = data321 >> 16;
|
|
|
|
#ifdef DEBUGGER
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value_long(data321, pm - s->pt);
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_read_value_wide(data321, true);
|
|
}
|
|
#endif
|
|
|
|
s->pt += 4;
|
|
}
|
|
|
|
*v0 = data16;
|
|
*v1 = data321;
|
|
*v2 = data322;
|
|
}
|
|
|
|
static void do_sprite_fetch(int hpos, uae_u16 dat)
|
|
{
|
|
int num = dat & 7;
|
|
struct sprite *s = &spr[num];
|
|
uae_u32 data321, data322;
|
|
uae_u16 data;
|
|
bool slot = (dat & 8) != 0;
|
|
bool dmastate = (dat & 0x10) != 0;
|
|
|
|
// render sprites first
|
|
decide_sprites(hpos, true);
|
|
|
|
sprite_fetch_full(s, hpos, slot, dmastate, &data, &data321, &data322);
|
|
|
|
if (dmastate) {
|
|
if (!slot) {
|
|
SPRxDATA_1(data, num, hpos);
|
|
} else {
|
|
SPRxDATB_1(data, num, hpos);
|
|
}
|
|
#ifdef AGA
|
|
switch (sprite_width)
|
|
{
|
|
case 64:
|
|
if (!slot) {
|
|
s->data[1] = data321;
|
|
s->data[2] = data322 >> 16;
|
|
s->data[3] = data322;
|
|
} else {
|
|
s->datb[1] = data321;
|
|
s->datb[2] = data322 >> 16;
|
|
s->datb[3] = data322;
|
|
}
|
|
break;
|
|
case 32:
|
|
if (!slot) {
|
|
s->data[1] = data321;
|
|
s->data[2] = data;
|
|
s->data[3] = data321;
|
|
} else {
|
|
s->datb[1] = data321;
|
|
s->datb[2] = data;
|
|
s->datb[3] = data321;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
} else {
|
|
if (!slot) {
|
|
SPRxPOS_1(data, num, hpos);
|
|
} else {
|
|
SPRxCTL_1(data, num, hpos);
|
|
// This is needed to disarm previous field's sprite.
|
|
// It can be seen on OCS Agnus + ECS Denise combination where
|
|
// this cycle is disabled due to weird DDFTSTR=$18 copper list
|
|
// which causes corrupted sprite to "wrap around" the display.
|
|
s->dmastate = 0;
|
|
sprstartstop(s);
|
|
}
|
|
// Sprite can't start if SPRxPOS/CTL DMA line matched on line after VBLANK ended
|
|
if (vb_end_line || vb_end_next_line) {
|
|
s->dmastate = 0;
|
|
s->dmacycle = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void sprite_stolen_cycle(uae_u32 num)
|
|
{
|
|
uae_u32 v = regs.chipset_latch_rw;
|
|
struct sprite *s = &spr[num & 7];
|
|
if (num & 0x100) {
|
|
s->datb[0] = v;
|
|
s->datb[1] = v >> 16;
|
|
}
|
|
}
|
|
|
|
static void decide_sprites_fetch(int endhpos)
|
|
{
|
|
int hpos = last_decide_sprite_hpos;
|
|
if (hpos >= endhpos) {
|
|
return;
|
|
}
|
|
if (vb_state || (doflickerfix_active() && (next_lineno & 1))) {
|
|
last_decide_sprite_hpos = endhpos;
|
|
return;
|
|
}
|
|
|
|
while (hpos < endhpos) {
|
|
if (hpos >= SPR_FIRST_HPOS - RGA_SPRITE_PIPELINE_DEPTH && hpos < SPR_FIRST_HPOS + MAX_SPRITES * 4) {
|
|
|
|
if (hpos < SPR_FIRST_HPOS + MAX_SPRITES * 4 - RGA_SPRITE_PIPELINE_DEPTH) {
|
|
int num = (hpos - (SPR_FIRST_HPOS - RGA_SPRITE_PIPELINE_DEPTH)) / 4;
|
|
int slot = (hpos - (SPR_FIRST_HPOS - RGA_SPRITE_PIPELINE_DEPTH)) & 3;
|
|
if (slot == 0 || slot == 2) {
|
|
struct sprite *s = &spr[num];
|
|
if (slot == 0) {
|
|
s->firstslotdone = false;
|
|
if (!s->dmacycle && s->dmastate) {
|
|
s->dmacycle = 1;
|
|
}
|
|
if (vpos == s->vstart && !vb_end_line && !vb_end_next_line) {
|
|
s->dmastate = 1;
|
|
s->dmacycle = 1;
|
|
if (num == 0 && slot == 0) {
|
|
cursorsprite();
|
|
}
|
|
}
|
|
if (vpos == s->vstop || vb_end_next_line) {
|
|
s->dmastate = 0;
|
|
s->dmacycle = 1;
|
|
}
|
|
}
|
|
if (dmaen(DMA_SPRITE) && s->dmacycle && !vb_end_line) {
|
|
bool dodma = false;
|
|
|
|
decide_bpl_fetch(hpos + 1);
|
|
|
|
if (hpos <= plfstrt_sprite) {
|
|
dodma = true;
|
|
} else {
|
|
// bitplane stole this cycle
|
|
if (slot == 2 && sprite_width > 16 && s->firstslotdone) {
|
|
event2_newevent_xx(-1, RGA_PIPELINE_OFFSET_SPRITE * CYCLE_UNIT, num | (s->dmastate ? 0x100 : 0), sprite_stolen_cycle);
|
|
}
|
|
}
|
|
#ifdef AGA
|
|
if (dodma && s->dblscan && (fmode & 0x8000) && (vpos & 1) != (s->vstart & 1) && s->dmastate) {
|
|
spr_arm(num, 1);
|
|
dodma = false;
|
|
}
|
|
#endif
|
|
if (dodma) {
|
|
int offset = get_rga_pipeline(hpos, RGA_PIPELINE_OFFSET_SPRITE);
|
|
uae_u8 dat = CYCLE_PIPE_SPRITE | (s->dmastate ? 0x10 : 0x00) | (s->dmacycle == 1 ? 0 : 8) | num;
|
|
#if 0
|
|
if (cycle_line_pipe[offset]) {
|
|
write_log(_T("sprite cycle already allocated! %02x\n"), cycle_line_pipe[offset]);
|
|
}
|
|
#endif
|
|
evt_t t = line_start_cycles + hpos * CYCLE_UNIT;
|
|
if (t == sprite_dma_change_cycle_on) {
|
|
// if sprite DMA is switched on just when sprite DMA is decided, channel is still decided but it is not allocated!
|
|
sprbplconflict2 = 0x140 + num * 8 + slot + (s->dmacycle ? 4 : 0);
|
|
sprbplconflict_hpos = sprbplconflict_hpos2 = hpos + RGA_PIPELINE_OFFSET_SPRITE;
|
|
sprbplconflict_dat = dat & ~CYCLE_PIPE_SPRITE;
|
|
} else if (bprun_end == hpos) {
|
|
// last bitplane cycle is available for sprites (if bitplane ends before all sprites)
|
|
sprbplconflict = 0x140 + num * 8 + slot + (s->dmacycle ? 4 : 0);
|
|
sprbplconflict_hpos = hpos + RGA_PIPELINE_OFFSET_SPRITE;
|
|
sprbplconflict_dat = dat & ~CYCLE_PIPE_SPRITE;
|
|
} else {
|
|
cycle_line_pipe[offset] = dat;
|
|
}
|
|
s->firstslotdone = true;
|
|
}
|
|
}
|
|
if (!vb_end_line && s->dmacycle) {
|
|
s->dmacycle++;
|
|
if (s->dmacycle > 2) {
|
|
s->dmacycle = 0;
|
|
if (s->dmastate) {
|
|
s->dmacycle = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool sprite_dma = (cycle_line_pipe[hpos] & CYCLE_PIPE_SPRITE) != 0;
|
|
if (sprite_dma) {
|
|
uae_u16 dat = cycle_line_pipe[hpos];
|
|
do_sprite_fetch(hpos, dat);
|
|
}
|
|
if (hpos == sprbplconflict_hpos2) {
|
|
decide_bpl_fetch(hpos + 1);
|
|
decide_blitter(hpos + 1);
|
|
int num = sprbplconflict_dat & 7;
|
|
struct sprite *s = &spr[num];
|
|
#ifdef DEBUGGER
|
|
int slot = (sprbplconflict_dat & 8) != 0;
|
|
int mode = (sprbplconflict_dat & 0x10) != 0;
|
|
if (debug_dma) {
|
|
record_dma_read(num * 8 + 0x140 + mode * 4 + slot * 2, s->pt, hpos, vpos, DMARECORD_SPRITE, num);
|
|
record_dma_event(DMA_EVENT_SPECIAL, hpos, vpos);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(s->pt, MW_MASK_SPR_0 << num, num * 8 + 0x140 + mode * 4 + slot * 2, num * 4 + 0x120);
|
|
}
|
|
#endif
|
|
if (hpos == sprbplconflict_hpos2) {
|
|
s->pt += sprite_width / 8;
|
|
}
|
|
}
|
|
}
|
|
hpos++;
|
|
}
|
|
last_decide_sprite_hpos = endhpos;
|
|
}
|
|
|
|
static void init_sprites(void)
|
|
{
|
|
for (int i = 0; i < MAX_SPRITES; i++) {
|
|
struct sprite *s = &spr[i];
|
|
memset(s, 0, sizeof(struct sprite));
|
|
}
|
|
}
|
|
|
|
// line=0
|
|
static void init_hardware_frame(void)
|
|
{
|
|
if (!harddis_v) {
|
|
vdiwstate = diw_states::DIW_waiting_start;
|
|
vdiw_change(0);
|
|
}
|
|
}
|
|
|
|
static int calculate_lineno(int vp)
|
|
{
|
|
int lineno = vp;
|
|
if (lineno >= MAXVPOS) {
|
|
lineno %= MAXVPOS;
|
|
}
|
|
nextline_how = nln_normal;
|
|
if (doflickerfix_active()) {
|
|
lineno *= 2;
|
|
lineno++;
|
|
} else if (!interlace_seen && doublescan <= 0 && currprefs.gfx_vresolution && currprefs.gfx_pscanlines > 1) {
|
|
lineno *= 2;
|
|
if (display_vsync_counter & 1) {
|
|
lineno++;
|
|
nextline_how = currprefs.gfx_pscanlines == 3 ? nln_lower_black_always : nln_lower_black;
|
|
} else {
|
|
nextline_how = currprefs.gfx_pscanlines == 3 ? nln_upper_black_always : nln_upper_black;
|
|
}
|
|
} else if ((doublescan <= 0 || interlace_seen > 0) && currprefs.gfx_vresolution && currprefs.gfx_iscanlines) {
|
|
lineno *= 2;
|
|
if (interlace_seen) {
|
|
if (!lof_display) {
|
|
lineno++;
|
|
nextline_how = currprefs.gfx_iscanlines == 2 ? nln_lower_black_always : nln_lower_black;
|
|
} else {
|
|
nextline_how = currprefs.gfx_iscanlines == 2 ? nln_upper_black_always : nln_upper_black;
|
|
}
|
|
} else {
|
|
nextline_how = currprefs.gfx_vresolution > VRES_NONDOUBLE && currprefs.gfx_pscanlines == 1 ? nln_nblack : nln_doubled;
|
|
}
|
|
} else if (currprefs.gfx_vresolution && (doublescan <= 0 || interlace_seen > 0)) {
|
|
lineno *= 2;
|
|
if (interlace_seen) {
|
|
if (!lof_display) {
|
|
lineno++;
|
|
nextline_how = nln_lower;
|
|
} else {
|
|
nextline_how = nln_upper;
|
|
}
|
|
} else {
|
|
nextline_how = currprefs.gfx_vresolution > VRES_NONDOUBLE && currprefs.gfx_pscanlines == 1 ? nln_nblack : nln_doubled;
|
|
}
|
|
}
|
|
return lineno;
|
|
}
|
|
|
|
// vsync start
|
|
void init_hardware_for_drawing_frame(void)
|
|
{
|
|
/* Avoid this code in the first frame after a customreset. */
|
|
if (prev_sprite_entries) {
|
|
int first_pixel = prev_sprite_entries[0].first_pixel;
|
|
int npixels = prev_sprite_entries[prev_next_sprite_entry].first_pixel - first_pixel;
|
|
if (npixels > 0) {
|
|
memset(spixels + first_pixel, 0, npixels * sizeof *spixels);
|
|
memset(spixstate.stb + first_pixel, 0, npixels * sizeof *spixstate.stb);
|
|
if (aga_mode) {
|
|
memset(spixstate.stbfm + first_pixel, 0, npixels * sizeof *spixstate.stbfm);
|
|
}
|
|
}
|
|
}
|
|
|
|
prev_next_sprite_entry = next_sprite_entry;
|
|
next_sprite_entry = 0;
|
|
end_sprite_entry = MAX_SPRITE_ENTRIES - 2;
|
|
spixels_max = sizeof(spixels) / sizeof(*spixels) - MAX_PIXELS_PER_LINE;
|
|
|
|
linear_vpos = vpos;
|
|
next_lineno = calculate_lineno(linear_vpos);
|
|
last_color_change = 0;
|
|
next_color_change = 0;
|
|
next_color_entry = 0;
|
|
remembered_color_entry = -1;
|
|
|
|
prev_sprite_entries = sprite_entries[current_change_set];
|
|
curr_sprite_entries = sprite_entries[current_change_set ^ 1];
|
|
prev_color_changes = color_changes[current_change_set];
|
|
curr_color_changes = color_changes[current_change_set ^ 1];
|
|
prev_color_tables = color_tables[current_change_set];
|
|
curr_color_tables = color_tables[current_change_set ^ 1];
|
|
|
|
prev_drawinfo = line_drawinfo[current_change_set];
|
|
curr_drawinfo = line_drawinfo[current_change_set ^ 1];
|
|
current_change_set ^= 1;
|
|
|
|
color_src_match = color_dest_match = -1;
|
|
|
|
/* Use both halves of the array in alternating fashion. */
|
|
curr_sprite_entries[0].first_pixel = current_change_set * MAX_SPR_PIXELS;
|
|
next_sprite_forced = 1;
|
|
}
|
|
|
|
static frame_time_t rpt_vsync(int adjust)
|
|
{
|
|
frame_time_t curr_time = read_processor_time();
|
|
frame_time_t v = curr_time - vsyncwaittime + adjust;
|
|
if (v > syncbase || v < -syncbase) {
|
|
vsyncmintime = vsyncmaxtime = vsyncwaittime = curr_time;
|
|
v = 0;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static void rtg_vsync (void)
|
|
{
|
|
#ifdef PICASSO96
|
|
frame_time_t start, end;
|
|
start = read_processor_time();
|
|
picasso_handle_vsync();
|
|
end = read_processor_time();
|
|
frameskiptime += end - start;
|
|
#endif
|
|
}
|
|
|
|
static void rtg_vsynccheck(void)
|
|
{
|
|
#if 0
|
|
if (vblank_found_rtg) {
|
|
vblank_found_rtg = false;
|
|
rtg_vsync();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
static void maybe_process_pull_audio(void)
|
|
{
|
|
audio_finish_pull();
|
|
}
|
|
|
|
static bool crender_screen(int monid, int mode, bool immediate)
|
|
{
|
|
bool v = render_screen(monid, mode, immediate);
|
|
if (display_reset > 0) {
|
|
display_reset--;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
// moving average algorithm
|
|
#define MAVG_MAX_SIZE 128
|
|
struct mavg_data
|
|
{
|
|
frame_time_t values[MAVG_MAX_SIZE];
|
|
int size;
|
|
int offset;
|
|
frame_time_t mavg;
|
|
};
|
|
|
|
static void mavg_clear (struct mavg_data *md)
|
|
{
|
|
md->size = 0;
|
|
md->offset = 0;
|
|
md->mavg = 0;
|
|
}
|
|
|
|
static frame_time_t mavg(struct mavg_data *md, frame_time_t newval, int size)
|
|
{
|
|
if (md->size < size) {
|
|
md->values[md->size++] = newval;
|
|
md->mavg += newval;
|
|
} else {
|
|
md->mavg -= md->values[md->offset];
|
|
md->values[md->offset] = newval;
|
|
md->mavg += newval;
|
|
md->offset++;
|
|
if (md->offset >= size) {
|
|
md->offset -= size;
|
|
}
|
|
}
|
|
return md->mavg / md->size;
|
|
}
|
|
|
|
#define MAVG_VSYNC_SIZE 128
|
|
#ifdef DEBUGGER
|
|
extern int log_vsync, debug_vsync_min_delay, debug_vsync_forced_delay;
|
|
#endif
|
|
static bool framewait(void)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
frame_time_t curr_time;
|
|
frame_time_t start;
|
|
int vs = isvsync_chipset();
|
|
int status = 0;
|
|
|
|
events_reset_syncline();
|
|
|
|
static struct mavg_data ma_frameskipt;
|
|
frame_time_t frameskipt_avg = mavg(&ma_frameskipt, frameskiptime, MAVG_VSYNC_SIZE);
|
|
|
|
frameskiptime = 0;
|
|
|
|
if (vs > 0) {
|
|
|
|
static struct mavg_data ma_legacy;
|
|
static frame_time_t vsync_time;
|
|
frame_time_t t;
|
|
|
|
curr_time = read_processor_time();
|
|
vsyncwaittime = vsyncmaxtime = curr_time + vsynctimebase;
|
|
if (!frame_rendered && !ad->picasso_on) {
|
|
frame_rendered = crender_screen(0, 1, false);
|
|
}
|
|
|
|
start = read_processor_time();
|
|
t = 0;
|
|
if (start - vsync_time >= 0 && start - vsync_time < vsynctimebase) {
|
|
t += start - vsync_time;
|
|
}
|
|
|
|
if (!frame_shown) {
|
|
show_screen(0, 1);
|
|
if (currprefs.gfx_apmode[0].gfx_strobo) {
|
|
show_screen(0, 4);
|
|
}
|
|
}
|
|
|
|
maybe_process_pull_audio();
|
|
|
|
frame_time_t legacy_avg = mavg(&ma_legacy, t, MAVG_VSYNC_SIZE);
|
|
if (t > legacy_avg) {
|
|
legacy_avg = t;
|
|
}
|
|
t = legacy_avg;
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_vsync_min_delay && t < debug_vsync_min_delay * vsynctimebase / 100) {
|
|
t = debug_vsync_min_delay * vsynctimebase / 100;
|
|
}
|
|
if (debug_vsync_forced_delay > 0) {
|
|
t = debug_vsync_forced_delay * vsynctimebase / 100;
|
|
}
|
|
#endif
|
|
|
|
vsync_time = read_processor_time();
|
|
if (t > vsynctimebase * 2 / 3) {
|
|
t = vsynctimebase * 2 / 3;
|
|
}
|
|
|
|
if (currprefs.m68k_speed < 0) {
|
|
vsynctimeperline = (vsynctimebase - t) / (maxvpos_display + 1);
|
|
} else {
|
|
vsynctimeperline = (vsynctimebase - t) / 3;
|
|
}
|
|
|
|
if (vsynctimeperline < 1) {
|
|
vsynctimeperline = 1;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if (0 || (log_vsync & 2)) {
|
|
write_log (_T("%06d %06d/%06d %03d%%\n"), t, vsynctimeperline, vsynctimebase, t * 100 / vsynctimebase);
|
|
}
|
|
#endif
|
|
|
|
frame_shown = true;
|
|
return 1;
|
|
|
|
} else if (vs < 0) {
|
|
|
|
if (!vblank_hz_state) {
|
|
return status != 0;
|
|
}
|
|
|
|
frame_shown = true;
|
|
status = 1;
|
|
return status != 0;
|
|
}
|
|
|
|
status = 1;
|
|
|
|
int clockadjust = 0;
|
|
frame_time_t vstb = vsynctimebase;
|
|
|
|
if (currprefs.m68k_speed < 0 && !cpu_sleepmode && !currprefs.cpu_memory_cycle_exact) {
|
|
|
|
if (!frame_rendered && !ad->picasso_on)
|
|
frame_rendered = crender_screen(0, 1, false);
|
|
|
|
if (currprefs.m68k_speed_throttle) {
|
|
// this delay can safely overshoot frame time by 1-2 ms, following code will compensate for it.
|
|
for (;;) {
|
|
curr_time = read_processor_time();
|
|
if (vsyncwaittime - curr_time <= 0 || vsyncwaittime - curr_time > 2 * vsynctimebase) {
|
|
break;
|
|
}
|
|
rtg_vsynccheck ();
|
|
if (cpu_sleep_millis(1) < 0) {
|
|
curr_time = read_processor_time();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
curr_time = read_processor_time();
|
|
}
|
|
|
|
int max;
|
|
frame_time_t adjust = 0;
|
|
if (curr_time - vsyncwaittime > 0 && curr_time - vsyncwaittime < vstb / 2) {
|
|
adjust += curr_time - vsyncwaittime;
|
|
}
|
|
adjust += clockadjust;
|
|
max = (int)(vstb * (1000.0 + currprefs.m68k_speed_throttle) / 1000.0 - adjust);
|
|
vsyncwaittime = curr_time + vstb - adjust;
|
|
vsyncmintime = curr_time;
|
|
|
|
if (max < 0) {
|
|
max = 0;
|
|
vsynctimeperline = 1;
|
|
} else {
|
|
vsynctimeperline = max / (maxvpos_display + 1);
|
|
}
|
|
vsyncmaxtime = curr_time + max;
|
|
|
|
if (0)
|
|
write_log (_T("%06d:%06d/%06d %d %d\n"), adjust, vsynctimeperline, vstb, max, maxvpos_display);
|
|
|
|
} else {
|
|
|
|
frame_time_t t = 0;
|
|
|
|
start = read_processor_time();
|
|
if (!frame_rendered && !ad->picasso_on) {
|
|
frame_rendered = crender_screen(0, 1, false);
|
|
t = read_processor_time() - start;
|
|
}
|
|
if (!currprefs.cpu_thread) {
|
|
while (!currprefs.turbo_emulation) {
|
|
float v = rpt_vsync(clockadjust) / (syncbase / 1000.0f);
|
|
if (v >= -FRAMEWAIT_MIN_MS)
|
|
break;
|
|
rtg_vsynccheck();
|
|
maybe_process_pull_audio();
|
|
if (cpu_sleep_millis(1) < 0)
|
|
break;
|
|
}
|
|
while (rpt_vsync(clockadjust) < 0) {
|
|
rtg_vsynccheck();
|
|
if (audio_is_pull_event()) {
|
|
maybe_process_pull_audio();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
idletime += read_processor_time() - start;
|
|
curr_time = read_processor_time();
|
|
vsyncmintime = curr_time;
|
|
vsyncmaxtime = vsyncwaittime = curr_time + vstb;
|
|
if (frame_rendered) {
|
|
show_screen(0, 0);
|
|
t += read_processor_time() - curr_time;
|
|
}
|
|
t += frameskipt_avg;
|
|
|
|
vsynctimeperline = (vstb - t) / FRAMEWAIT_SPLIT;
|
|
if (vsynctimeperline < 1) {
|
|
vsynctimeperline = 1;
|
|
} else if (vsynctimeperline > vstb / FRAMEWAIT_SPLIT) {
|
|
vsynctimeperline = vstb / FRAMEWAIT_SPLIT;
|
|
}
|
|
|
|
frame_shown = true;
|
|
|
|
}
|
|
return status != 0;
|
|
}
|
|
|
|
static void reset_cpu_idle(void)
|
|
{
|
|
cpu_sleepmode_cnt = 0;
|
|
if (cpu_sleepmode) {
|
|
cpu_sleepmode = 0;
|
|
//write_log(_T("woken\n"));
|
|
}
|
|
}
|
|
|
|
#define FPSCOUNTER_MAVG_SIZE 10
|
|
static struct mavg_data fps_mavg, idle_mavg;
|
|
|
|
void fpscounter_reset(void)
|
|
{
|
|
mavg_clear(&fps_mavg);
|
|
mavg_clear(&idle_mavg);
|
|
bogusframe = 2;
|
|
lastframetime = read_processor_time();
|
|
idletime = 0;
|
|
}
|
|
|
|
static void fpscounter(bool frameok)
|
|
{
|
|
frame_time_t now, last;
|
|
|
|
now = read_processor_time();
|
|
last = now - lastframetime;
|
|
lastframetime = now;
|
|
|
|
if (bogusframe || last < 0) {
|
|
return;
|
|
}
|
|
|
|
mavg(&fps_mavg, last / 10, FPSCOUNTER_MAVG_SIZE);
|
|
mavg(&idle_mavg, idletime / 10, FPSCOUNTER_MAVG_SIZE);
|
|
idletime = 0;
|
|
|
|
frametime += last;
|
|
timeframes++;
|
|
|
|
if ((timeframes & 7) == 0) {
|
|
double idle = 1000 - (idle_mavg.mavg == 0 ? 0.0 : (double)idle_mavg.mavg * 1000.0 / vsynctimebase);
|
|
int fps = fps_mavg.mavg == 0 ? 0 : (int)(syncbase * 10 / fps_mavg.mavg);
|
|
if (fps > 99999)
|
|
fps = 99999;
|
|
if (idle < 0)
|
|
idle = 0;
|
|
if (idle > 100 * 10)
|
|
idle = 100 * 10;
|
|
if (fake_vblank_hz * 10 > fps) {
|
|
double mult = (double)fake_vblank_hz * 10.0 / fps;
|
|
idle *= mult;
|
|
}
|
|
if (currprefs.turbo_emulation && idle < 100 * 10)
|
|
idle = 100 * 10;
|
|
gui_data.fps = fps;
|
|
gui_data.idle = (int)idle;
|
|
gui_data.fps_color = nosignal_status == 1 ? 2 : (nosignal_status == 2 ? 3 : (frameok ? 0 : 1));
|
|
if ((timeframes & 15) == 0) {
|
|
gui_fps(fps, (int)idle, gui_data.fps_color);
|
|
}
|
|
}
|
|
}
|
|
|
|
// vsync functions that are not hardware timing related
|
|
// called when vsync starts which is not necessarily last line
|
|
// it can line 0 or even later.
|
|
static void vsync_handler_render(void)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
|
|
#if 1
|
|
if (currprefs.m68k_speed < 0) {
|
|
if (regs.stopped) {
|
|
if (cpu_last_stop_vpos >= 0) {
|
|
cpu_stopped_lines += maxvpos - cpu_last_stop_vpos;
|
|
} else {
|
|
cpu_stopped_lines = 0;
|
|
}
|
|
}
|
|
int mv = 12 - currprefs.cpu_idle / 15;
|
|
if (mv >= 1 && mv <= 11) {
|
|
mv = 11 - mv;
|
|
if (cpu_stopped_lines >= maxvpos * (mv * 10) / 100) {
|
|
cpu_sleepmode_cnt++;
|
|
if (cpu_sleepmode_cnt >= 50) {
|
|
cpu_sleepmode_cnt = 50;
|
|
if (!cpu_sleepmode) {
|
|
cpu_sleepmode = 1;
|
|
//write_log(_T("sleep\n"));
|
|
}
|
|
}
|
|
} else {
|
|
reset_cpu_idle();
|
|
}
|
|
} else {
|
|
reset_cpu_idle();
|
|
}
|
|
}
|
|
if (regs.halted < 0) {
|
|
reset_cpu_idle();
|
|
}
|
|
cpu_last_stop_vpos = 0;
|
|
cpu_stopped_lines = 0;
|
|
#endif
|
|
|
|
#ifdef PICASSO96
|
|
if (isvsync_rtg() >= 0) {
|
|
rtg_vsync();
|
|
}
|
|
#endif
|
|
|
|
if (!vsync_rendered) {
|
|
frame_time_t start, end;
|
|
start = read_processor_time();
|
|
vsync_handle_redraw(lof_store, lof_changed, bplcon0, bplcon3, isvsync_chipset() >= 0, initial_frame);
|
|
initial_frame = false;
|
|
vsync_rendered = true;
|
|
end = read_processor_time();
|
|
frameskiptime += end - start;
|
|
}
|
|
|
|
linear_vpos = vpos;
|
|
next_lineno = calculate_lineno(linear_vpos);
|
|
|
|
bool frameok = framewait();
|
|
|
|
if (!ad->picasso_on) {
|
|
if (!frame_rendered && vblank_hz_state) {
|
|
frame_rendered = crender_screen(0, 1, false);
|
|
}
|
|
if (frame_rendered && !frame_shown) {
|
|
frame_shown = show_screen_maybe(0, isvsync_chipset () >= 0);
|
|
}
|
|
}
|
|
|
|
fpscounter(frameok);
|
|
|
|
bool waspaused = false;
|
|
while (handle_events()) {
|
|
if (!waspaused) {
|
|
if (crender_screen(0, 1, true)) {
|
|
show_screen(0, 0);
|
|
}
|
|
waspaused = true;
|
|
}
|
|
// we are paused, do all config checks but don't do any emulation
|
|
if (vsync_handle_check()) {
|
|
redraw_frame();
|
|
if (crender_screen(0, 1, true)) {
|
|
show_screen(0, 0);
|
|
}
|
|
}
|
|
config_check_vsync();
|
|
}
|
|
|
|
vsync_rendered = false;
|
|
frame_shown = false;
|
|
frame_rendered = false;
|
|
|
|
if (quit_program > 0) {
|
|
/* prevent possible infinite loop at wait_cycles().. */
|
|
ad->framecnt = 0;
|
|
reset_decisions_scanline_start();
|
|
reset_decisions_hsync_start();
|
|
return;
|
|
}
|
|
|
|
if (vblank_hz_mult > 0) {
|
|
vblank_hz_state ^= 1;
|
|
} else {
|
|
vblank_hz_state = 1;
|
|
}
|
|
|
|
nextline_how = nln_normal;
|
|
|
|
//checklacecount (bplcon0_interlace_seen || lof_lace);
|
|
}
|
|
|
|
static bool vsync_display_rendered;
|
|
|
|
static void vsync_display_render(void)
|
|
{
|
|
if (!vsync_display_rendered) {
|
|
vsyncmintimepre = read_processor_time();
|
|
vsync_handler_render();
|
|
vsync_display_rendered = true;
|
|
}
|
|
}
|
|
|
|
static void vsync_check_vsyncmode(void)
|
|
{
|
|
if (varsync_maybe_changed[0] == 1 || varsync_maybe_changed[1] == 1 || varhblank_lines == -1) {
|
|
init_hz_normal();
|
|
} else if (varsync_changed == 1) {
|
|
init_hz_normal();
|
|
} else if (vpos_count > 0 && abs(vpos_count - vpos_count_diff) > 1 && vposw_change && vposw_change < 4) {
|
|
init_hz_vposw();
|
|
} else if (interlace_changed || changed_chipset_refresh() || lof_changed) {
|
|
compute_framesync();
|
|
display_reset = 2;
|
|
}
|
|
if (varsync_changed > 0) {
|
|
varsync_changed--;
|
|
}
|
|
varsync_maybe_changed[0] = 0;
|
|
varsync_maybe_changed[1] = 0;
|
|
varhblank_lines = 0;
|
|
}
|
|
|
|
static void check_display_mode_change(void)
|
|
{
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
int vt, ht, hs, vs;
|
|
|
|
if (new_beamcon0 & BEAMCON0_VARBEAMEN) {
|
|
vt = vtotal;
|
|
ht = htotal;
|
|
} else {
|
|
vt = maxvpos;
|
|
ht = maxhpos;
|
|
}
|
|
if (new_beamcon0 & (BEAMCON0_VARHSYEN | BEAMCON0_VARCSYEN)) {
|
|
hs = hsstrt;
|
|
} else {
|
|
hs = 18;
|
|
}
|
|
if (new_beamcon0 & (BEAMCON0_VARVSYEN | BEAMCON0_VARCSYEN)) {
|
|
vs = vsstrt;
|
|
} else {
|
|
vs = (new_beamcon0 & BEAMCON0_PAL) ? 2 : 3;
|
|
}
|
|
|
|
// recalculate display if vtotal, htotal, hsync start or vsync start changed > 1
|
|
if ((abs(vt - vt_old) > 1 || abs(ht - ht_old) > 1 || abs(hs - hs_old) > 1 || abs(vs - vs_old) > 1) && vt_old && ht_old) {
|
|
varsync_changed = 1;
|
|
if (!ad->picasso_on) {
|
|
nosignal_trigger = true;
|
|
display_reset = 2;
|
|
}
|
|
}
|
|
|
|
vt_old = vt;
|
|
ht_old = ht;
|
|
vs_old = vs;
|
|
hs_old = hs;
|
|
}
|
|
|
|
// emulated hardware vsync
|
|
static void vsync_handler_post(void)
|
|
{
|
|
int monid = 0;
|
|
static frame_time_t prevtime;
|
|
|
|
//write_log (_T("%d %d %d\n"), vsynctimebase, read_processor_time () - vsyncmintime, read_processor_time () - prevtime);
|
|
prevtime = read_processor_time();
|
|
|
|
check_nocustom();
|
|
|
|
#if CUSTOM_DEBUG > 1
|
|
if ((intreq & 0x0020) && (intena & 0x0020))
|
|
write_log(_T("vblank interrupt not cleared\n"));
|
|
#endif
|
|
DISK_vsync ();
|
|
|
|
#ifdef WITH_LUA
|
|
uae_lua_run_handler("on_uae_vsync");
|
|
#endif
|
|
|
|
if (bplcon0 & 4) {
|
|
lof_store = lof_store ? 0 : 1;
|
|
}
|
|
if ((bplcon0 & 2) && currprefs.genlock) {
|
|
genlockvtoggle = lof_store ? 1 : 0;
|
|
}
|
|
|
|
if ((bplcon0 & 2) && (!currprefs.genlock || currprefs.genlock_effects)) {
|
|
nosignal_trigger = true;
|
|
}
|
|
// Inverted CSYNC
|
|
if ((beamcon0 & BEAMCON0_CSYTRUE) && currprefs.cs_hvcsync == 1) {
|
|
nosignal_trigger = true;
|
|
}
|
|
// BLANKEN set: horizontal blanking is merged with CSYNC
|
|
if ((beamcon0 & BEAMCON0_BLANKEN) && currprefs.cs_hvcsync == 1) {
|
|
nosignal_trigger = true;
|
|
}
|
|
if ((beamcon0 & BEAMCON0_CSCBEN) && currprefs.cs_hvcsync == 2) {
|
|
nosignal_trigger = true;
|
|
}
|
|
if (beamcon0 & BEAMCON0_VARBEAMEN) {
|
|
if (htotal < 50 || htotal > 250) {
|
|
nosignal_trigger = true;
|
|
}
|
|
if (vtotal < 100 || vtotal > 1000) {
|
|
nosignal_trigger = true;
|
|
}
|
|
// CSY output is invalid (no vsync part included) if HTOTAL is too small + hardwired CSYNC.
|
|
int csyh = (beamcon0 & 0x20) ? 0x8c : 0x8d;
|
|
if (htotal < csyh && !(beamcon0 & BEAMCON0_VARCSYEN) && currprefs.cs_hvcsync == 1) {
|
|
nosignal_trigger = true;
|
|
}
|
|
}
|
|
// Too small or large HSYNC
|
|
if (beamcon0 & (BEAMCON0_VARHSYEN | BEAMCON0_VARCSYEN)) {
|
|
if (hsstop < hsstrt) {
|
|
hsstop += maxhpos;
|
|
}
|
|
if (hsstop - hsstrt <= 2 || hsstop - hsstrt > 80) {
|
|
nosignal_trigger = true;
|
|
}
|
|
}
|
|
// Too small or large VSYNC
|
|
if (beamcon0 & (BEAMCON0_VARVSYEN | BEAMCON0_VARCSYEN)) {
|
|
if (vsstop < vsstrt) {
|
|
vsstop += maxvpos;
|
|
}
|
|
if (vsstop - vsstrt <= 1 || vsstop - vsstrt > 80) {
|
|
nosignal_trigger = true;
|
|
}
|
|
}
|
|
|
|
if (lof_prev_lastline != lof_lastline) {
|
|
if (lof_togglecnt_lace < LOF_TOGGLES_NEEDED) {
|
|
lof_togglecnt_lace++;
|
|
}
|
|
if (lof_togglecnt_lace >= LOF_TOGGLES_NEEDED) {
|
|
lof_togglecnt_nlace = 0;
|
|
}
|
|
} else {
|
|
// only 1-2 vblanks with bplcon0 lace bit set?
|
|
// lets check if lof has changed
|
|
if (!(bplcon0 & 4) && lof_togglecnt_lace > 0 && lof_togglecnt_lace < LOF_TOGGLES_NEEDED && !interlace_seen) {
|
|
lof_changed = 1;
|
|
}
|
|
lof_togglecnt_nlace = LOF_TOGGLES_NEEDED;
|
|
lof_togglecnt_lace = 0;
|
|
#if 0
|
|
if (lof_togglecnt_nlace < LOF_TOGGLES_NEEDED)
|
|
lof_togglecnt_nlace++;
|
|
if (lof_togglecnt_nlace >= LOF_TOGGLES_NEEDED)
|
|
lof_togglecnt_lace = 0;
|
|
#endif
|
|
}
|
|
lof_prev_lastline = lof_lastline;
|
|
if (lof_togglecnt_lace >= LOF_TOGGLES_NEEDED) {
|
|
interlace_changed = notice_interlace_seen(monid, true);
|
|
if (interlace_changed) {
|
|
notice_screen_contents_lost(monid);
|
|
}
|
|
} else if (lof_togglecnt_nlace >= LOF_TOGGLES_NEEDED) {
|
|
interlace_changed = notice_interlace_seen(monid, false);
|
|
if (interlace_changed) {
|
|
notice_screen_contents_lost(monid);
|
|
}
|
|
}
|
|
if (lof_changing) {
|
|
// if not interlace and LOF was changed during previous field but LOF is now same as beginning of previous field: do nothing
|
|
if (!(bplcon0 & 4) && ((lof_changing > 0 && prevlofs[0]) || (lof_changing < 0 && !prevlofs[0]))) {
|
|
lof_changing = 0;
|
|
}
|
|
}
|
|
if (lof_changing) {
|
|
if (lof_store != (prevlofs[0] ? 1 : 0) && prevlofs[0] == prevlofs[1] && prevlofs[1] == prevlofs[2]) {
|
|
// resync if LOF was not toggling previously
|
|
nosignal_trigger = true;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
prevlofs[2] = prevlofs[1];
|
|
prevlofs[1] = prevlofs[0];
|
|
prevlofs[0] = lof_store;
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_copper) {
|
|
record_copper_reset();
|
|
}
|
|
if (debug_dma) {
|
|
record_dma_reset(0);
|
|
}
|
|
#endif
|
|
|
|
#ifdef PICASSO96
|
|
if (p96refresh_active) {
|
|
vpos_count = p96refresh_active;
|
|
vpos_count_diff = p96refresh_active;
|
|
vtotal = vpos_count;
|
|
}
|
|
#endif
|
|
|
|
devices_vsync_post();
|
|
|
|
check_display_mode_change();
|
|
|
|
vsync_check_vsyncmode();
|
|
|
|
if (bogusframe > 0) {
|
|
bogusframe--;
|
|
}
|
|
|
|
if (nosignal_status < 0) {
|
|
nosignal_status = 0;
|
|
}
|
|
if (nosignal_cnt) {
|
|
nosignal_cnt--;
|
|
if (nosignal_cnt == 0) {
|
|
nosignal_status = -1;
|
|
}
|
|
}
|
|
if (nosignal_trigger) {
|
|
struct amigadisplay *ad = &adisplays[0];
|
|
nosignal_trigger = false;
|
|
if (!ad->specialmonitoron) {
|
|
if (currprefs.gfx_monitorblankdelay > 0) {
|
|
nosignal_status = 1;
|
|
nosignal_cnt = (int)(currprefs.gfx_monitorblankdelay / (1000.0f / vblank_hz));
|
|
if (nosignal_cnt <= 0) {
|
|
nosignal_cnt = 1;
|
|
}
|
|
} else {
|
|
nosignal_status = 2;
|
|
nosignal_cnt = (int)(vblank_hz / 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
config_check_vsync();
|
|
if (timehack_alive > 0) {
|
|
timehack_alive--;
|
|
}
|
|
|
|
lof_changed = 0;
|
|
vposw_change = 0;
|
|
bplcon0_interlace_seen = false;
|
|
|
|
vsync_handle_check();
|
|
|
|
init_hardware_frame();
|
|
|
|
vsync_cycles = get_cycles();
|
|
}
|
|
|
|
static void copper_check(int n)
|
|
{
|
|
if (cop_state.state == COP_wait) {
|
|
int vp = vpos & (((cop_state.ir[1] >> 8) & 0x7F) | 0x80);
|
|
if (vp < cop_state.vcmp) {
|
|
if (copper_enabled_thisline) {
|
|
write_log(_T("COPPER BUG %d: vp=%d vpos=%d vcmp=%d thisline=%d\n"), n, vp, vpos, cop_state.vcmp, copper_enabled_thisline);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void reset_scandoubler_sync(int hpos)
|
|
{
|
|
last_decide_line_hpos = hpos;
|
|
last_decide_sprite_hpos = hpos;
|
|
last_fetch_hpos = hpos;
|
|
last_copper_hpos = hpos;
|
|
last_diw_hpos = hpos;
|
|
last_sprite_hpos = hpos;
|
|
sprites_enabled_this_line = false;
|
|
plfstrt_sprite = 0x100;
|
|
bprun = 0;
|
|
bprun_cycle = 0;
|
|
ddf_stopping = 0;
|
|
int hnew = hpos - (REFRESH_FIRST_HPOS - HARDWIRED_DMA_TRIGGER_HPOS + 2);
|
|
hdiw_denisecounter = ((hnew + 0) & 0xff) << CCK_SHRES_SHIFT;
|
|
last_sprite_hpos = last_sprite_point = (((hnew + 0) & 0xff) << 1) + 1;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
0 0 -
|
|
1 1 --
|
|
2 2 -
|
|
3 3 --
|
|
4 4 -
|
|
5 5 --
|
|
|
|
0 x -+
|
|
1 0 --
|
|
2 1 -
|
|
3 2 --
|
|
4 3 -
|
|
5 4 --
|
|
|
|
*/
|
|
|
|
static void hsync_scandoubler(int hpos)
|
|
{
|
|
uae_u16 odmacon = dmacon;
|
|
int ocop = copper_enabled_thisline;
|
|
uaecptr bpltmp[MAX_PLANES], bpltmpx[MAX_PLANES];
|
|
int lof = lof_display;
|
|
|
|
if (vb_start_line > 2) {
|
|
return;
|
|
}
|
|
|
|
scandoubled_line = 1;
|
|
line_disabled |= 8;
|
|
last_hdiw = 0 - 1;
|
|
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
bpltmp[i] = bplpt[i];
|
|
bpltmpx[i] = bplptx[i];
|
|
uaecptr pb1 = prevbpl[lof][vpos][i];
|
|
uaecptr pb2 = prevbpl[1 - lof][vpos][i];
|
|
if (pb1 && pb2) {
|
|
int diff = pb1 - pb2;
|
|
if (lof) {
|
|
if (bplcon0 & 4) {
|
|
bplpt[i] = pb1 - diff;
|
|
}
|
|
} else {
|
|
if (bplcon0 & 4) {
|
|
bplpt[i] = pb1;
|
|
} else {
|
|
bplpt[i] = bplpt[i] - diff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uae_u8 cycle_line_slot_tmp[MAX_CHIPSETSLOTS];
|
|
uae_u16 cycle_line_pipe_tmp[MAX_CHIPSETSLOTS];
|
|
|
|
memcpy(cycle_line_slot_tmp, cycle_line_slot, sizeof(uae_u8) * MAX_CHIPSETSLOTS);
|
|
memcpy(cycle_line_pipe_tmp, cycle_line_pipe, sizeof(uae_u16) * MAX_CHIPSETSLOTS);
|
|
|
|
reset_decisions_scanline_start();
|
|
reset_scandoubler_sync(hpos);
|
|
reset_decisions_hsync_start();
|
|
|
|
// Bitplane and sprites only
|
|
dmacon = odmacon & (DMA_MASTER | DMA_BITPLANE | DMA_SPRITE);
|
|
copper_enabled_thisline = 0;
|
|
|
|
// copy color changes
|
|
struct draw_info *dip1 = curr_drawinfo + next_lineno - 1;
|
|
for (int idx1 = dip1->first_color_change; idx1 < dip1->last_color_change; idx1++) {
|
|
struct color_change *cs2 = &curr_color_changes[idx1];
|
|
struct color_change *cs1 = &curr_color_changes[next_color_change];
|
|
memcpy(cs1, cs2, sizeof(struct color_change));
|
|
next_color_change++;
|
|
}
|
|
curr_color_changes[next_color_change].regno = -1;
|
|
|
|
hpos_hsync_extra = 0;
|
|
finish_partial_decision(maxhpos);
|
|
hpos_hsync_extra = maxhpos;
|
|
finish_decisions(hpos);
|
|
hsync_record_line_state(next_lineno, nln_normal, thisline_changed);
|
|
|
|
scandoubled_line = 0;
|
|
line_disabled &= ~8;
|
|
|
|
dmacon = odmacon;
|
|
copper_enabled_thisline = ocop;
|
|
|
|
memcpy(cycle_line_slot, cycle_line_slot_tmp, sizeof(uae_u8) * MAX_CHIPSETSLOTS);
|
|
memcpy(cycle_line_pipe, cycle_line_pipe_tmp, sizeof(uae_u16) * MAX_CHIPSETSLOTS);
|
|
|
|
for (int i = 0; i < MAX_PLANES; i++) {
|
|
bplpt[i] = bpltmp[i];
|
|
bplptx[i] = bpltmpx[i];
|
|
}
|
|
|
|
reset_decisions_scanline_start();
|
|
reset_scandoubler_sync(hpos);
|
|
}
|
|
|
|
static void dmal_emu(uae_u32 val)
|
|
{
|
|
// Disk and Audio DMA bits are ignored by Agnus. Including DMA master bit.
|
|
int hpos = current_hpos();
|
|
|
|
if (currprefs.cpu_memory_cycle_exact) {
|
|
// BPL conflict
|
|
if (bitplane_dma_access(hpos, 0)) {
|
|
return;
|
|
}
|
|
}
|
|
int dmalbits = val & 3;
|
|
int dmalpos = val >> 8;
|
|
|
|
if (dmalpos >= 6 && dmalpos < 14) {
|
|
dmalpos -= 6;
|
|
int nr = dmalpos / 2;
|
|
uaecptr pt = audio_getpt(nr, (dmalbits & 1) != 0);
|
|
if (dmal_ce) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0xaa + nr * 16, pt, hpos, vpos, DMARECORD_AUDIO, nr);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(pt, MW_MASK_AUDIO_0 << nr, 0xaa + nr * 16, 0xa0 + nr * 16);
|
|
}
|
|
#endif
|
|
}
|
|
uae_u16 dat = chipmem_wget_indirect(pt);
|
|
if (dmal_ce) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(dat);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(dat);
|
|
}
|
|
#endif
|
|
}
|
|
regs.chipset_latch_rw = last_custom_value = dat;
|
|
AUDxDAT(nr, dat, pt);
|
|
} else if (dmalpos >= 0 && dmalpos < 6) {
|
|
uae_u16 dat = 0;
|
|
int s = (dmalpos / 2);
|
|
int w = (dmalbits & 3) == 3;
|
|
// disk_fifostatus() needed in >100% disk speed modes
|
|
if (w) {
|
|
// write to disk
|
|
if (disk_fifostatus() <= 0) {
|
|
uaecptr pt = disk_getpt();
|
|
if (dmal_ce) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(0x26, pt, hpos, vpos, DMARECORD_DISK, dmalpos / 2);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_chipram(pt, MW_MASK_DISK, 0x26, 0x20);
|
|
}
|
|
#endif
|
|
}
|
|
dat = chipmem_wget_indirect(pt);
|
|
if (dmal_ce) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value(dat);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_getpeekdma_value(dat);
|
|
}
|
|
#endif
|
|
}
|
|
regs.chipset_latch_rw = last_custom_value = dat;
|
|
DSKDAT(dat);
|
|
}
|
|
} else {
|
|
// read from disk
|
|
if (disk_fifostatus() >= 0) {
|
|
uaecptr pt = disk_getpt();
|
|
dat = DSKDATR(s);
|
|
if (dmal_ce) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_write(0x08, dat, pt, hpos, vpos, DMARECORD_DISK, dmalpos / 2);
|
|
}
|
|
if (memwatch_enabled) {
|
|
debug_putpeekdma_chipram(pt, dat, MW_MASK_DISK, 0x08, 0x20);
|
|
}
|
|
#endif
|
|
}
|
|
chipmem_wput_indirect(pt, dat);
|
|
regs.chipset_latch_rw = last_custom_value = dat;
|
|
}
|
|
}
|
|
} else {
|
|
write_log(_T("invalid DMAL position %d\n"), dmalpos);
|
|
}
|
|
}
|
|
|
|
static void dmal_func(uae_u32 v)
|
|
{
|
|
dmal_emu(v);
|
|
events_dmal(0);
|
|
}
|
|
|
|
static void dmal_func2(uae_u32 v)
|
|
{
|
|
while (dmal) {
|
|
if (dmal & 3) {
|
|
dmal_emu((dmal_hpos << 8) | (dmal & 3));
|
|
}
|
|
dmal_hpos += 2;
|
|
dmal >>= 2;
|
|
}
|
|
}
|
|
|
|
static void events_dmal(int hpos)
|
|
{
|
|
dmal_ce = false;
|
|
if (!dmal)
|
|
return;
|
|
if (currprefs.cachesize) {
|
|
dmal_func2(0);
|
|
} else if (currprefs.cpu_compatible) {
|
|
while (dmal) {
|
|
if (dmal & 3)
|
|
break;
|
|
hpos += 2;
|
|
dmal >>= 2;
|
|
dmal_hpos += 2;
|
|
}
|
|
dmal_ce = true;
|
|
event2_newevent2(hpos, (dmal_hpos << 8) | (dmal & 3), dmal_func);
|
|
dmal &= ~3;
|
|
} else {
|
|
event2_newevent2(hpos, 0, dmal_func2);
|
|
}
|
|
}
|
|
|
|
static void events_dmal_hsync2(uae_u32 v)
|
|
{
|
|
int dmal_first = DMAL_FIRST_HPOS - v;
|
|
// 3 disk + 4 audio
|
|
uae_u16 dmalt = audio_dmal();
|
|
dmalt <<= (3 * 2);
|
|
dmalt |= disk_dmal();
|
|
dmalt &= dmal_htotal_mask;
|
|
dmal |= dmalt;
|
|
dmal_alloc_mask = dmal;
|
|
if (!dmal) {
|
|
return;
|
|
}
|
|
if (hstrobe_conflict) {
|
|
return;
|
|
}
|
|
dmal_hpos = 0;
|
|
if (currprefs.cpu_memory_cycle_exact) {
|
|
for (int i = 0; i < 3 * 2 + 4 * 2; i += 2) {
|
|
if (dmal & (3 << i)) {
|
|
alloc_cycle_ext(DMAL_FIRST_HPOS + i, CYCLE_MISC);
|
|
}
|
|
}
|
|
}
|
|
events_dmal(dmal_first);
|
|
}
|
|
|
|
static void events_dmal_hsync(void)
|
|
{
|
|
if (currprefs.cpu_compatible) {
|
|
int delay = REFRESH_FIRST_HPOS + 1;
|
|
event2_newevent2(delay, delay, events_dmal_hsync2);
|
|
} else {
|
|
events_dmal_hsync2(0);
|
|
}
|
|
}
|
|
|
|
static void lightpen_trigger_func(uae_u32 v)
|
|
{
|
|
vpos_lpen = vpos;
|
|
hpos_lpen = v;
|
|
hhpos_lpen = HHPOSR();
|
|
lightpen_triggered = 1;
|
|
}
|
|
|
|
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 bool do_render_slice(int mode, int slicecnt, int lastline)
|
|
{
|
|
draw_lines(lastline, slicecnt);
|
|
crender_screen(0, mode, true);
|
|
return true;
|
|
}
|
|
|
|
static bool do_display_slice(void)
|
|
{
|
|
show_screen(0, -1);
|
|
inputdevice_hsync(true);
|
|
return true;
|
|
}
|
|
|
|
static void reset_autoscale(void)
|
|
{
|
|
first_bpl_vpos = -1;
|
|
if (first_bplcon0 != first_bplcon0_old) {
|
|
vertical_changed = horizontal_changed = true;
|
|
}
|
|
first_bplcon0_old = first_bplcon0;
|
|
|
|
if (first_planes_vpos != first_planes_vpos_old ||
|
|
last_planes_vpos != last_planes_vpos_old) {
|
|
vertical_changed = true;
|
|
}
|
|
first_planes_vpos_old = first_planes_vpos;
|
|
last_planes_vpos_old = last_planes_vpos;
|
|
|
|
if (diwfirstword_total != diwfirstword_total_old ||
|
|
diwlastword_total != diwlastword_total_old ||
|
|
ddffirstword_total != ddffirstword_total_old ||
|
|
ddflastword_total != ddflastword_total_old) {
|
|
horizontal_changed = true;
|
|
}
|
|
diwfirstword_total_old = diwfirstword_total;
|
|
diwlastword_total_old = diwlastword_total;
|
|
ddffirstword_total_old = ddffirstword_total;
|
|
ddflastword_total_old = ddflastword_total;
|
|
|
|
first_planes_vpos = 0;
|
|
last_planes_vpos = 0;
|
|
diwfirstword_total = max_diwlastword;
|
|
diwlastword_total = 0;
|
|
ddffirstword_total = max_diwlastword;
|
|
ddflastword_total = 0;
|
|
plflastline_total = 0;
|
|
plffirstline_total = current_maxvpos();
|
|
first_bplcon0 = 0;
|
|
autoscale_bordercolors = 0;
|
|
}
|
|
|
|
static void hautoscale_check(int vp)
|
|
{
|
|
int vp_start = vp;
|
|
int vp_end = vp + 1;
|
|
// border detection/autoscale
|
|
if (GET_PLANES(bplcon0) > 0 && dmaen(DMA_BITPLANE) && !vb_state && !vb_end_line && !vb_start_line) {
|
|
if (first_bplcon0 == 0) {
|
|
first_bplcon0 = bplcon0;
|
|
}
|
|
if (vp_end > last_planes_vpos) {
|
|
last_planes_vpos = vp_end;
|
|
}
|
|
if (vp_start >= minfirstline && first_planes_vpos == 0) {
|
|
first_planes_vpos = vp_start;
|
|
} else if (vp_end >= current_maxvpos() - 1) {
|
|
last_planes_vpos = current_maxvpos();
|
|
}
|
|
}
|
|
if (diw_change == 0) {
|
|
if (vp_start >= first_planes_vpos && vp_end <= last_planes_vpos) {
|
|
int diwfirstword_lores = diwfirstword;
|
|
int diwlastword_lores = diwlastword;
|
|
// >0x1c7? Calculate "real" right edge for scaling modes
|
|
if (diwlastword_lores < min_diwlastword && diwfirstword_lores >= 2) {
|
|
if (diwlastword_lores < diwfirstword_lores) {
|
|
diwlastword_lores = max_diwlastword + (diwlastword_lores - 2);
|
|
} else {
|
|
diwlastword_lores = min_diwlastword;
|
|
}
|
|
}
|
|
if (diwlastword_lores > diwlastword_total) {
|
|
diwlastword_total = diwlastword_lores;
|
|
if (diwlastword_total > coord_diw_shres_to_window_x(hsyncstartpos)) {
|
|
diwlastword_total = coord_diw_shres_to_window_x(hsyncstartpos);
|
|
}
|
|
}
|
|
if (diwfirstword_lores < diwfirstword_total) {
|
|
diwfirstword_total = diwfirstword_lores;
|
|
if (diwfirstword_total < coord_diw_shres_to_window_x(hsyncendpos)) {
|
|
diwfirstword_total = coord_diw_shres_to_window_x(hsyncendpos);
|
|
}
|
|
firstword_bplcon1 = bplcon1;
|
|
}
|
|
}
|
|
if (vdiwstate == diw_states::DIW_waiting_stop) {
|
|
int f = 8 << fetchmode;
|
|
if (plfstrt + f < ddffirstword_total + f) {
|
|
ddffirstword_total = plfstrt + f;
|
|
}
|
|
if (plfstop + 2 * f > ddflastword_total + 2 * f) {
|
|
ddflastword_total = plfstop + 2 * f;
|
|
}
|
|
}
|
|
if ((plffirstline < plffirstline_total || (plffirstline_total == minfirstline && vp_start > minfirstline)) && plffirstline < maxvpos / 2) {
|
|
firstword_bplcon1 = bplcon1;
|
|
if (plffirstline < minfirstline) {
|
|
plffirstline_total = minfirstline;
|
|
} else {
|
|
plffirstline_total = plffirstline;
|
|
}
|
|
}
|
|
if (plflastline > plflastline_total && plflastline > plffirstline_total && plflastline > maxvpos / 2) {
|
|
plflastline_total = plflastline;
|
|
}
|
|
}
|
|
if (diw_change > 0) {
|
|
diw_change--;
|
|
}
|
|
}
|
|
|
|
static void hsync_handlerh(bool onvsync)
|
|
{
|
|
int hpos = current_hpos();
|
|
|
|
if (!custom_disabled) {
|
|
|
|
if (doflickerfix_active()) {
|
|
finish_decisions(hpos);
|
|
hsync_record_line_state(next_lineno, nextline_how, thisline_changed);
|
|
next_lineno++;
|
|
hsync_scandoubler(hpos);
|
|
} else {
|
|
finish_decisions(hpos);
|
|
hsync_record_line_state(next_lineno, nextline_how, thisline_changed);
|
|
}
|
|
|
|
notice_resolution_seen(GET_RES_AGNUS(bplcon0), interlace_seen != 0);
|
|
|
|
hautoscale_check(vposh);
|
|
|
|
next_lineno = calculate_lineno(linear_vpos);
|
|
reset_decisions_hsync_start();
|
|
}
|
|
|
|
linear_vpos++;
|
|
if (vpos >= minfirstline && minfirstline_linear == minfirstline) {
|
|
minfirstline_linear = linear_vpos;
|
|
}
|
|
vposh++;
|
|
|
|
hpos_hsync_extra = 0;
|
|
estimate_last_fetch_cycle(hpos);
|
|
|
|
if (vb_end_next_line && !ecs_denise) {
|
|
record_color_change2(hpos, 0, COLOR_CHANGE_BLANK | 1);
|
|
}
|
|
|
|
eventtab[ev_hsynch].evtime = get_cycles() + HSYNCTIME;
|
|
eventtab[ev_hsynch].active = 1;
|
|
eventtab[ev_hsynch].oldcycles = get_cycles();
|
|
events_schedule();
|
|
}
|
|
|
|
static void set_hpos(void)
|
|
{
|
|
line_start_cycles = (get_cycles() + CYCLE_UNIT - 1) & ~(CYCLE_UNIT - 1);
|
|
maxhposeven_prev = maxhposeven;
|
|
maxhpos = maxhpos_short + lol;
|
|
set_maxhpos(maxhpos);
|
|
eventtab[ev_hsync].evtime = line_start_cycles + HSYNCTIME;
|
|
eventtab[ev_hsync].oldcycles = line_start_cycles;
|
|
}
|
|
|
|
// this finishes current line
|
|
static void hsync_handler_pre(bool onvsync)
|
|
{
|
|
if (!custom_disabled) {
|
|
|
|
// make sure decisions are done to end of scanline
|
|
finish_partial_decision(maxhpos);
|
|
clear_bitplane_pipeline(0);
|
|
|
|
/* reset light pen latch */
|
|
if (vb_end_line) {
|
|
lightpen_triggered = 0;
|
|
sprite_0 = 0;
|
|
}
|
|
|
|
if (!lightpen_triggered && (bplcon0 & 8)) {
|
|
// lightpen always triggers at the beginning of the last line
|
|
if (vb_start_line == 1) {
|
|
vpos_lpen = vpos;
|
|
hpos_lpen = 1;
|
|
hhpos_lpen = HHPOSR();
|
|
lightpen_triggered = 1;
|
|
} else if (lightpen_enabled && !vb_state) {
|
|
int lpnum = inputdevice_get_lightpen_id();
|
|
if (lpnum < 0) {
|
|
lpnum = 0;
|
|
}
|
|
if (lightpen_cx[lpnum] > 0 && lightpen_cy[lpnum] == vpos) {
|
|
event2_newevent_xx(-1, lightpen_cx[lpnum] * CYCLE_UNIT, lightpen_cx[lpnum], lightpen_trigger_func);
|
|
}
|
|
}
|
|
}
|
|
|
|
hdiw_counter += maxhpos * 2;
|
|
if (!hstrobe_conflict) {
|
|
// OCS Denise freerunning horizontal counter
|
|
if (!ecs_denise && vpos == get_equ_vblank_endline() - 1) {
|
|
hdiw_counter++;
|
|
}
|
|
if (ecs_denise || vpos > get_equ_vblank_endline() || (agnusa1000 && vpos == 0)) {
|
|
hdiw_counter = maxhpos * 2;
|
|
}
|
|
}
|
|
hdiw_counter &= 511;
|
|
}
|
|
|
|
devices_hsync();
|
|
|
|
if (hsyncdebug) {
|
|
sync_color_changes(maxhpos);
|
|
}
|
|
|
|
hsync_counter++;
|
|
|
|
int currentmaxhp = current_hpos();
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_hsync(currentmaxhp);
|
|
}
|
|
#endif
|
|
// just to be sure
|
|
if (currentmaxhp > 0) {
|
|
cycle_line_slot_last = cycle_line_slot[currentmaxhp - 1];
|
|
} else {
|
|
cycle_line_slot_last = 0;
|
|
}
|
|
set_hpos();
|
|
|
|
vpos_prev = vpos;
|
|
vpos++;
|
|
vpos_count++;
|
|
if (vpos >= maxvpos_total) {
|
|
vpos = 0;
|
|
}
|
|
|
|
line_equ_freerun = !ecs_denise && vpos >= get_equ_vblank_startline() && vpos <= get_equ_vblank_endline();
|
|
|
|
if (onvsync) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_vsync(vpos);
|
|
}
|
|
#endif
|
|
vpos = 0;
|
|
vsync_counter++;
|
|
}
|
|
check_line_enabled();
|
|
|
|
hpos_hsync_extra = maxhpos;
|
|
|
|
lol_prev = lol;
|
|
if (islinetoggle())
|
|
lol = lol ? 0 : 1;
|
|
else
|
|
lol = 0;
|
|
|
|
// to record decisions correctly between end of scanline and start of hsync
|
|
if (!eventtab[ev_hsynch].active) {
|
|
eventtab[ev_hsynch].evtime = get_cycles() + hsyncstartpos_start_cycles * CYCLE_UNIT;
|
|
eventtab[ev_hsynch].active = 1;
|
|
events_schedule();
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
if (!(refresh_handled_slot & (1 << i))) {
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
debug_mark_refreshed(refptr_p + i * ref_ras_add);
|
|
}
|
|
#endif
|
|
if (!refptr_preupdated) {
|
|
refptr += ref_ras_add;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
debug_hsync();
|
|
#endif
|
|
}
|
|
|
|
// low latency vsync
|
|
|
|
#define LLV_DEBUG 0
|
|
|
|
static bool sync_timeout_check(frame_time_t max)
|
|
{
|
|
#if LLV_DEBUG
|
|
return true;
|
|
#else
|
|
frame_time_t rpt = read_processor_time();
|
|
return rpt - max <= 0;
|
|
#endif
|
|
}
|
|
|
|
extern int busywait;
|
|
static void scanlinesleep(int currline, int nextline)
|
|
{
|
|
if (currline < 0)
|
|
return;
|
|
if (currline >= nextline)
|
|
return;
|
|
if (vsync_hblank) {
|
|
int diff = (int)(vsync_hblank / (nextline - currline));
|
|
int us = 1000000 / diff;
|
|
if (us < target_sleep_nanos(-1)) { // spin if less than minimum sleep time
|
|
target_spin(nextline - currline - 1);
|
|
return;
|
|
}
|
|
}
|
|
if (busywait) {
|
|
target_spin(1);
|
|
return;
|
|
}
|
|
if (currprefs.m68k_speed < 0)
|
|
sleep_millis_main(1);
|
|
else
|
|
target_sleep_nanos(500);
|
|
}
|
|
|
|
static void linesync_first_last_line(int *first, int *last)
|
|
{
|
|
int x, y, w, h;
|
|
*first = minfirstline;
|
|
*last = maxvpos_display;
|
|
get_custom_raw_limits(&w, &h, &x, &y);
|
|
if (y > 0)
|
|
*first += y;
|
|
//write_log(_T("y=%d h=%d mfl=%d maxvpos=%d\n"), y, h, minfirstline, maxvpos_display);
|
|
}
|
|
|
|
static int vsyncnextscanline;
|
|
static int vsyncnextscanline_add;
|
|
static int nextwaitvpos;
|
|
static int display_slice_cnt;
|
|
static int display_slice_lines;
|
|
static int display_slices;
|
|
static bool display_rendered;
|
|
static int vsyncnextstep;
|
|
|
|
static bool linesync_beam_single_dual(void)
|
|
{
|
|
bool was_syncline = is_syncline != 0;
|
|
frame_time_t maxtime = read_processor_time() + 2 * vsynctimebase;
|
|
int vp;
|
|
|
|
if (is_last_line()) {
|
|
do_render_slice(-1, 0, vpos);
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp >= 0)
|
|
break;
|
|
}
|
|
vsyncmintime = read_processor_time() + vsynctimebase;
|
|
vsync_clear();
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight - 1 || vp < 0)
|
|
break;
|
|
scanlinesleep(vp, vsync_activeheight - 1);
|
|
}
|
|
frame_rendered = true;
|
|
frame_shown = true;
|
|
do_display_slice();
|
|
int vv = (int)vsync_vblank;
|
|
while (vv >= 85) {
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp < vsync_activeheight / 2)
|
|
break;
|
|
}
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight / 2)
|
|
break;
|
|
}
|
|
show_screen(0, 3);
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight - 1 || vp < vsync_activeheight / 2)
|
|
break;
|
|
}
|
|
show_screen(0, 2);
|
|
vv -= currprefs.ntscmode ? 60 : 50;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool linesync_beam_single_single(void)
|
|
{
|
|
bool was_syncline = is_syncline != 0;
|
|
frame_time_t maxtime = read_processor_time() + 2 * vsynctimebase;
|
|
int vp;
|
|
|
|
if (is_last_line()) {
|
|
if (vsyncnextstep != 1) {
|
|
vsyncnextstep = 1;
|
|
do_render_slice(-1, 0, vpos);
|
|
// wait until out of vblank
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp >= 0 && vp < vsync_activeheight / 2)
|
|
break;
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -3;
|
|
return 0;
|
|
}
|
|
target_spin(0);
|
|
}
|
|
}
|
|
if (vsyncnextstep != 2) {
|
|
vsyncnextstep = 2;
|
|
vsync_clear();
|
|
// wait until second half of display
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight / 2)
|
|
break;
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -2;
|
|
is_syncline_end = vsync_activeheight - 1;
|
|
return 0;
|
|
}
|
|
scanlinesleep(vp, vsync_activeheight / 2);
|
|
}
|
|
}
|
|
if (vsyncnextstep != 3) {
|
|
vsyncnextstep = 3;
|
|
// wait until end of display (or start of next field)
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight - 1 || vp < vsync_activeheight / 2)
|
|
break;
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -2;
|
|
is_syncline_end = vsync_activeheight - 1;
|
|
return 0;
|
|
}
|
|
scanlinesleep(vp, vsync_activeheight - 1);
|
|
}
|
|
}
|
|
if (vsyncnextstep != 4) {
|
|
vsyncnextstep = 4;
|
|
do_display_slice();
|
|
frame_rendered = true;
|
|
frame_shown = true;
|
|
// wait until first half of display
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
vp = target_get_display_scanline(-1);
|
|
if (vp < vsync_activeheight / 2)
|
|
break;
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -(100 + vsync_activeheight / 2);
|
|
return 0;
|
|
}
|
|
target_spin(0);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool linesync_beam_multi_dual(void)
|
|
{
|
|
frame_time_t maxtime = read_processor_time() + 2 * vsynctimebase;
|
|
bool input_read_done = false;
|
|
bool was_syncline = is_syncline != 0;
|
|
|
|
events_reset_syncline();
|
|
if (vpos == 0 && !was_syncline) {
|
|
int firstline, lastline;
|
|
linesync_first_last_line(&firstline, &lastline);
|
|
|
|
display_slices = currprefs.gfx_display_sections;
|
|
if (display_slices <= 0)
|
|
display_slices = 1;
|
|
display_slice_cnt = 0;
|
|
vsyncnextscanline = vsync_activeheight / display_slices + 1;
|
|
vsyncnextscanline_add = vsync_activeheight / display_slices;
|
|
display_slice_lines = (lastline - firstline) / display_slices + 1;
|
|
nextwaitvpos = firstline + display_slice_lines + display_slice_lines / 2;
|
|
if (display_slices <= 1)
|
|
nextwaitvpos = lastline + 1;
|
|
if (display_slices <= 2 && vsyncnextscanline > vsync_activeheight * 2 / 3)
|
|
vsyncnextscanline = vsync_activeheight * 2 / 3;
|
|
|
|
display_rendered = false;
|
|
frame_rendered = true;
|
|
frame_shown = true;
|
|
}
|
|
|
|
if (!display_slices)
|
|
return false;
|
|
|
|
if (vpos >= nextwaitvpos || is_last_line()) {
|
|
|
|
if (display_slice_cnt == 0) {
|
|
|
|
if (!was_syncline) {
|
|
do_render_slice(is_last_line() ? 1 : 2, display_slice_cnt, vpos);
|
|
display_rendered = true;
|
|
}
|
|
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight - 1 || vp < 0)
|
|
break;
|
|
}
|
|
|
|
do_display_slice();
|
|
display_rendered = false;
|
|
input_read_done = true;
|
|
|
|
} else {
|
|
|
|
if (!currprefs.turbo_emulation) {
|
|
if (!was_syncline && !display_rendered) {
|
|
do_render_slice(0, display_slice_cnt, vpos - 1);
|
|
display_rendered = true;
|
|
}
|
|
while(sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp == -1) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
continue;
|
|
}
|
|
if (vp < 0 || vp >= vsyncnextscanline)
|
|
break;
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = vsyncnextscanline;
|
|
return 0;
|
|
}
|
|
maybe_process_pull_audio();
|
|
scanlinesleep(vp, vsyncnextscanline);
|
|
}
|
|
do_display_slice();
|
|
}
|
|
|
|
if (is_last_line()) {
|
|
// wait extra frames
|
|
int vv = (int)vsync_vblank;
|
|
for(;;) {
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight - 1 || vp < 0)
|
|
break;
|
|
}
|
|
show_screen(0, 3);
|
|
show_screen(0, 2);
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp >= vsync_activeheight / 2)
|
|
break;
|
|
}
|
|
vv -= currprefs.ntscmode ? 60 : 50;
|
|
if (vv < 85)
|
|
break;
|
|
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp < vsync_activeheight / 2 && vp >= 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
input_read_done = true;
|
|
display_rendered = false;
|
|
|
|
vsyncnextscanline += vsync_activeheight / display_slices;
|
|
vsync_clear();
|
|
}
|
|
|
|
nextwaitvpos += display_slice_lines;
|
|
display_slice_cnt++;
|
|
|
|
|
|
}
|
|
return input_read_done;
|
|
}
|
|
|
|
static bool linesync_beam_multi_single(void)
|
|
{
|
|
frame_time_t maxtime = read_processor_time() + 2 * vsynctimebase;
|
|
bool input_read_done = false;
|
|
bool was_syncline = is_syncline != 0;
|
|
|
|
events_reset_syncline();
|
|
if (vpos == 0 && !was_syncline) {
|
|
int firstline, lastline;
|
|
linesync_first_last_line(&firstline, &lastline);
|
|
|
|
display_slices = currprefs.gfx_display_sections;
|
|
if (!display_slices)
|
|
display_slices = 1;
|
|
display_slice_cnt = 0;
|
|
vsyncnextscanline = vsync_activeheight / display_slices + 1;
|
|
vsyncnextscanline_add = vsync_activeheight / display_slices;
|
|
display_slice_lines = (lastline - firstline) / display_slices + 1;
|
|
nextwaitvpos = firstline + display_slice_lines + display_slice_lines / 2;
|
|
if (display_slices <= 1)
|
|
nextwaitvpos = lastline + 1;
|
|
if (display_slices <= 2 && vsyncnextscanline > vsync_activeheight * 2 / 3)
|
|
vsyncnextscanline = vsync_activeheight * 2 / 3;
|
|
|
|
display_rendered = false;
|
|
frame_rendered = true;
|
|
frame_shown = true;
|
|
}
|
|
|
|
if (!display_slices)
|
|
return false;
|
|
|
|
if (is_last_line()) {
|
|
|
|
if (!was_syncline && !display_rendered) {
|
|
do_render_slice(1, display_slice_cnt, vpos);
|
|
display_rendered = true;
|
|
}
|
|
// if 2 slices: make sure we are out of vblank.
|
|
if (display_slices <= 2) {
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp != -1)
|
|
break;
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -3;
|
|
return 0;
|
|
}
|
|
target_spin(0);
|
|
}
|
|
vsync_clear();
|
|
}
|
|
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp < 0 || vp >= vsyncnextscanline)
|
|
break;
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = vsyncnextscanline;
|
|
return 0;
|
|
}
|
|
scanlinesleep(vp, vsyncnextscanline);
|
|
#if LLV_DEBUG
|
|
write_log(_T("1:%d:%d:%d:%d."), vpos, vp, nextwaitvpos, vsyncnextscanline);
|
|
#endif
|
|
}
|
|
do_display_slice();
|
|
input_read_done = true;
|
|
display_slice_cnt = -1;
|
|
display_rendered = false;
|
|
#if LLV_DEBUG
|
|
write_log("\n");
|
|
#endif
|
|
|
|
} else if (vpos >= nextwaitvpos) {
|
|
|
|
// topmost/first slice?
|
|
if (display_slice_cnt == 0) {
|
|
|
|
if (!currprefs.turbo_emulation) {
|
|
if (!was_syncline) {
|
|
do_render_slice(2, display_slice_cnt, vpos - 1);
|
|
display_rendered = true;
|
|
}
|
|
|
|
// flip slightly early because flip regularly gets delayed if done during vblank
|
|
int lastflipline = vsync_activeheight - vsyncnextscanline_add / 5;
|
|
while (sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
maybe_process_pull_audio();
|
|
if (vp < vsync_activeheight / 2 || vp >= lastflipline)
|
|
break;
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline_end = lastflipline;
|
|
is_syncline = -2;
|
|
return 0;
|
|
}
|
|
target_spin(0);
|
|
#if LLV_DEBUG
|
|
write_log(_T("2:%d:%d:%d:%d."), vpos, vp, nextwaitvpos, vsyncnextscanline);
|
|
#endif
|
|
}
|
|
do_display_slice();
|
|
display_rendered = false;
|
|
input_read_done = true;
|
|
|
|
#if 1
|
|
// if flipped before vblank, wait for vblank
|
|
while (sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp < vsync_activeheight / 2)
|
|
break;
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -1;
|
|
is_syncline_end = vp;
|
|
return 0;
|
|
}
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} else {
|
|
|
|
// skip if too close
|
|
int vp2 = target_get_display_scanline(-1);
|
|
if (!currprefs.turbo_emulation && (currprefs.m68k_speed < 0 || vp2 < vsyncnextscanline - vsyncnextscanline_add / 10)) {
|
|
if (!was_syncline && !display_rendered) {
|
|
do_render_slice(0, display_slice_cnt, vpos - 1);
|
|
display_rendered = true;
|
|
}
|
|
while (sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
// We are still in vblank and second slice? Poll until vblank ends.
|
|
if (display_slice_cnt == 1 && vp == -1) {
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -3;
|
|
return 0;
|
|
}
|
|
|
|
target_spin(0);
|
|
#if LLV_DEBUG
|
|
write_log(_T("3:%d:%d:%d:%d."), vpos, vp, nextwaitvpos, vsyncnextscanline);
|
|
#endif
|
|
continue;
|
|
}
|
|
if (vp < 0 || vp >= vsyncnextscanline)
|
|
break;
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = vsyncnextscanline;
|
|
return 0;
|
|
}
|
|
scanlinesleep(vp, vsyncnextscanline);
|
|
#if LLV_DEBUG
|
|
write_log(_T("4:%d:%d:%d:%d."), vpos, vp, nextwaitvpos, vsyncnextscanline);
|
|
#endif
|
|
}
|
|
do_display_slice();
|
|
input_read_done = true;
|
|
display_rendered = false;
|
|
}
|
|
vsyncnextscanline += vsyncnextscanline_add;
|
|
vsync_clear();
|
|
}
|
|
nextwaitvpos += display_slice_lines;
|
|
display_slice_cnt++;
|
|
#if LLV_DEBUG
|
|
write_log("\n");
|
|
#endif
|
|
}
|
|
|
|
return input_read_done;
|
|
}
|
|
|
|
static bool linesync_beam_vrr(void)
|
|
{
|
|
frame_time_t maxtime = read_processor_time() + 2 * vsynctimebase;
|
|
bool input_read_done = false;
|
|
bool was_syncline = is_syncline != 0;
|
|
|
|
events_reset_syncline();
|
|
if (vpos == 0 && !was_syncline) {
|
|
int firstline, lastline;
|
|
linesync_first_last_line(&firstline, &lastline);
|
|
|
|
display_slices = currprefs.gfx_display_sections;
|
|
if (!display_slices)
|
|
display_slices = 1;
|
|
display_slice_cnt = 0;
|
|
vsyncnextscanline = vsync_activeheight / display_slices + 1;
|
|
vsyncnextscanline_add = vsync_activeheight / display_slices;
|
|
display_slice_lines = (lastline - firstline) / display_slices + 1;
|
|
nextwaitvpos = firstline + display_slice_lines + display_slice_lines / 2;
|
|
if (display_slices <= 1)
|
|
nextwaitvpos = lastline + 1;
|
|
if (display_slices <= 2 && vsyncnextscanline > vsync_activeheight * 2 / 3)
|
|
vsyncnextscanline = vsync_activeheight * 2 / 3;
|
|
|
|
display_rendered = false;
|
|
frame_rendered = true;
|
|
frame_shown = true;
|
|
}
|
|
|
|
if (!display_slices)
|
|
return false;
|
|
|
|
if (is_last_line()) {
|
|
|
|
if (!was_syncline && !display_rendered) {
|
|
do_render_slice(1, display_slice_cnt, vpos);
|
|
display_rendered = true;
|
|
}
|
|
|
|
while (!currprefs.turbo_emulation && sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
if (vp < 0 || vp >= vsyncnextscanline)
|
|
break;
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = vsyncnextscanline;
|
|
return 0;
|
|
}
|
|
scanlinesleep(vp, vsyncnextscanline);
|
|
#if LLV_DEBUG
|
|
write_log(_T("1:%d:%d:%d:%d."), vpos, vp, nextwaitvpos, vsyncnextscanline);
|
|
#endif
|
|
}
|
|
do_display_slice();
|
|
input_read_done = true;
|
|
display_slice_cnt = -1;
|
|
display_rendered = false;
|
|
#if LLV_DEBUG
|
|
write_log("\n");
|
|
#endif
|
|
|
|
} else if (vpos >= nextwaitvpos) {
|
|
|
|
// topmost/first slice?
|
|
if (display_slice_cnt == 0) {
|
|
|
|
if (!currprefs.turbo_emulation) {
|
|
|
|
frame_time_t rpt;
|
|
for (;;) {
|
|
rpt = read_processor_time();
|
|
if (rpt - (vsyncmintime - vsynctimebase * 2 / 3) >= 0 || rpt - vsyncmintime < -2 * vsynctimebase)
|
|
break;
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -1;
|
|
is_syncline_end = target_get_display_scanline(-1);
|
|
return 0;
|
|
}
|
|
target_spin(0);
|
|
}
|
|
|
|
if (!was_syncline) {
|
|
do_render_slice(2, display_slice_cnt, vpos - 1);
|
|
display_rendered = true;
|
|
}
|
|
|
|
for (;;) {
|
|
rpt = read_processor_time();
|
|
if (rpt - vsyncmintime >= 0 || rpt - vsyncmintime < -2 * vsynctimebase)
|
|
break;
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = -1;
|
|
is_syncline_end = target_get_display_scanline(-1);
|
|
return 0;
|
|
}
|
|
target_spin(0);
|
|
}
|
|
|
|
if (rpt - vsyncmintime < vsynctimebase && rpt - vsyncmintime > -vsynctimebase) {
|
|
vsyncmintime += vsynctimebase;
|
|
} else {
|
|
vsyncmintime = rpt + vsynctimebase;
|
|
}
|
|
do_display_slice();
|
|
display_rendered = false;
|
|
input_read_done = true;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!currprefs.turbo_emulation) {
|
|
if (!was_syncline && !display_rendered) {
|
|
do_render_slice(0, display_slice_cnt, vpos - 1);
|
|
display_rendered = true;
|
|
}
|
|
while (sync_timeout_check(maxtime)) {
|
|
int vp = target_get_display_scanline(-1);
|
|
// We are still in vblank and second slice? Poll until vblank ends.
|
|
if (display_slice_cnt == 1 && vp == -1) {
|
|
maybe_process_pull_audio();
|
|
target_spin(0);
|
|
#if LLV_DEBUG
|
|
write_log(_T("3:%d:%d:%d:%d."), vpos, vp, nextwaitvpos, vsyncnextscanline);
|
|
#endif
|
|
continue;
|
|
}
|
|
if (vp < 0 || vp >= vsyncnextscanline)
|
|
break;
|
|
maybe_process_pull_audio();
|
|
if (currprefs.m68k_speed < 0 && !was_syncline) {
|
|
is_syncline = vsyncnextscanline;
|
|
return 0;
|
|
}
|
|
scanlinesleep(vp, vsyncnextscanline);
|
|
#if LLV_DEBUG
|
|
write_log(_T("4:%d:%d:%d:%d."), vpos, vp, nextwaitvpos, vsyncnextscanline);
|
|
#endif
|
|
}
|
|
do_display_slice();
|
|
input_read_done = true;
|
|
display_rendered = false;
|
|
}
|
|
vsyncnextscanline += vsyncnextscanline_add;
|
|
vsync_clear();
|
|
}
|
|
nextwaitvpos += display_slice_lines;
|
|
display_slice_cnt++;
|
|
#if LLV_DEBUG
|
|
write_log("\n");
|
|
#endif
|
|
}
|
|
|
|
return input_read_done;
|
|
}
|
|
|
|
// called when extra CPU wait is done
|
|
void vsync_event_done(void)
|
|
{
|
|
if (!isvsync_chipset()) {
|
|
events_reset_syncline();
|
|
return;
|
|
}
|
|
if (currprefs.gfx_display_sections <= 1) {
|
|
if (vsync_vblank >= 85)
|
|
linesync_beam_single_dual();
|
|
else
|
|
linesync_beam_single_single();
|
|
} else {
|
|
if (currprefs.gfx_variable_sync)
|
|
linesync_beam_vrr();
|
|
else if (vsync_vblank >= 85)
|
|
linesync_beam_multi_dual();
|
|
else
|
|
linesync_beam_multi_single();
|
|
}
|
|
}
|
|
|
|
static void check_vblank_copjmp(uae_u32 v)
|
|
{
|
|
COPJMP(1, 1);
|
|
}
|
|
|
|
static void delayed_framestart(uae_u32 v)
|
|
{
|
|
check_vblank_copjmp(0);
|
|
INTREQ_INT(5, 0); // total REFRESH_FIRST_HPOS - 1 + 1 (in INTREQ_INT)
|
|
}
|
|
|
|
// this prepares for new line
|
|
static void hsync_handler_post(bool onvsync)
|
|
{
|
|
memset(cycle_line_slot, 0, maxhposm1 + 1);
|
|
|
|
// genlock active:
|
|
// vertical: interlaced = toggles every other field, non-interlaced = both fields (normal)
|
|
// horizontal: PAL = every line, NTSC = every other line
|
|
genlockhtoggle = !genlockhtoggle;
|
|
bool ciahsyncs = !(bplcon0 & 2) || ((bplcon0 & 2) && currprefs.genlock && (!currprefs.ntscmode || genlockhtoggle));
|
|
bool ciavsyncs = !(bplcon0 & 2) || ((bplcon0 & 2) && currprefs.genlock && genlockvtoggle);
|
|
|
|
CIA_hsync_posthandler(false, false);
|
|
if (currprefs.cs_cd32cd) {
|
|
CIA_hsync_posthandler(true, true);
|
|
CIAB_tod_handler(35);
|
|
} else if (ciahsyncs) {
|
|
CIA_hsync_posthandler(true, ciahsyncs);
|
|
if (beamcon0 & BEAMCON0_VARHSYEN) {
|
|
if (hsstop < (maxhpos & ~1) && hsstrt < maxhpos) {
|
|
CIAB_tod_handler(hsstop);
|
|
}
|
|
} else {
|
|
CIAB_tod_handler(35); // hsync end
|
|
}
|
|
}
|
|
|
|
if (currprefs.cs_cd32cd) {
|
|
|
|
if (cia_hsync < maxhpos) {
|
|
CIAA_tod_handler(cia_hsync);
|
|
cia_hsync += (akiko_ntscmode() ? 262 : 313) * maxhpos;
|
|
} else {
|
|
cia_hsync -= maxhpos;
|
|
}
|
|
|
|
} else if (currprefs.cs_ciaatod > 0) {
|
|
#if 0
|
|
static uae_s32 oldtick;
|
|
uae_s32 tick = read_system_time (); // milliseconds
|
|
int ms = 1000 / (currprefs.cs_ciaatod == 2 ? 60 : 50);
|
|
if (tick - oldtick > 2000 || tick - oldtick < -2000) {
|
|
oldtick = tick - ms;
|
|
write_log (_T("RESET\n"));
|
|
}
|
|
if (tick - oldtick >= ms) {
|
|
CIA_vsync_posthandler (1);
|
|
oldtick += ms;
|
|
}
|
|
#else
|
|
if (cia_hsync < maxhpos) {
|
|
int newcount;
|
|
CIAA_tod_handler(cia_hsync);
|
|
newcount = (int)((vblank_hz * (2 * maxvpos + (interlace_seen ? 1 : 0)) * (2 * maxhpos + (islinetoggle () ? 1 : 0))) / ((currprefs.cs_ciaatod == 2 ? 60 : 50) * 4));
|
|
cia_hsync += newcount;
|
|
} else {
|
|
cia_hsync -= maxhpos;
|
|
}
|
|
#endif
|
|
} else if (currprefs.cs_ciaatod == 0 && ciavsyncs) {
|
|
// CIA-A TOD counter increases when vsync pulse ends
|
|
if (beamcon0 & BEAMCON0_VARVSYEN) {
|
|
if (vsstop == vpos) {
|
|
// Always uses HCENTER and HSSTRT registers. Even if VARHSYEN=0.
|
|
CIAA_tod_handler(lof_store ? hcenter : hsstrt);
|
|
}
|
|
} else {
|
|
if (vpos == (currprefs.ntscmode ? VSYNC_ENDLINE_NTSC : VSYNC_ENDLINE_PAL)) {
|
|
CIAA_tod_handler(lof_store ? 132 : 18);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!custom_disabled) {
|
|
if (!currprefs.blitter_cycle_exact && blt_info.blit_main && dmaen (DMA_BITPLANE) && vdiwstate == diw_states::DIW_waiting_stop) {
|
|
blitter_slowdown(thisline_decision.plfleft, thisline_decision.plfright - (16 << fetchmode),
|
|
cycle_diagram_total_cycles[fetchmode][GET_RES_AGNUS (bplcon0)][GET_PLANES_LIMIT (bplcon0)],
|
|
cycle_diagram_free_cycles[fetchmode][GET_RES_AGNUS (bplcon0)][GET_PLANES_LIMIT (bplcon0)]);
|
|
}
|
|
}
|
|
|
|
if (onvsync) {
|
|
// vpos_count >= MAXVPOS just to not crash if VPOSW writes prevent vsync completely
|
|
vpos = 0;
|
|
vsync_handler_post();
|
|
vpos_count = 0;
|
|
}
|
|
|
|
// vblank interrupt = next line after VBSTRT
|
|
if (vpos == 0 && vb_start_line == 1) {
|
|
// first refresh (strobe) slot triggers vblank interrupt
|
|
// copper and vblank trigger in same line
|
|
event2_newevent_xx(-1, 2 * CYCLE_UNIT, 0, delayed_framestart);
|
|
} else if (vb_start_line == 1) {
|
|
INTREQ_INT(5, REFRESH_FIRST_HPOS - 1);
|
|
} else if (vpos == 0) {
|
|
event2_newevent_xx(-1, 2 * CYCLE_UNIT, 0, check_vblank_copjmp);
|
|
}
|
|
|
|
// lastline - 1?
|
|
if (vpos + 1 == maxvpos + lof_store || vpos + 1 == maxvpos + lof_store + 1) {
|
|
lof_lastline = lof_store != 0;
|
|
}
|
|
|
|
if (vb_end_next_line2) {
|
|
vb_end_next_line2 = false;
|
|
}
|
|
if (vb_end_next_line) {
|
|
vb_end_next_line2 = true;
|
|
vb_end_next_line = false;
|
|
}
|
|
if (vb_start_line) {
|
|
vb_start_line++;
|
|
}
|
|
if (vb_end_line) {
|
|
vb_start_line = 0;
|
|
vb_end_line = false;
|
|
vb_end_next_line = true;
|
|
}
|
|
|
|
vs_state_var_old = vs_state_var;
|
|
if (ecs_agnus) {
|
|
if (vsstrt_m == vpos) {
|
|
vsstrt_m = -1;
|
|
}
|
|
if (vsstop_m == vpos) {
|
|
vsstop_m = -1;
|
|
}
|
|
|
|
if (vbstrt_m == vpos) {
|
|
vbstrt_m = -1;
|
|
}
|
|
if (vbstop_m == vpos) {
|
|
vbstop_m = -1;
|
|
}
|
|
|
|
if (vsstrt == vpos) {
|
|
vsstrt_m = vpos;
|
|
vs_state_var = true;
|
|
}
|
|
if (vsstop == vpos) {
|
|
vsstop_m = vpos;
|
|
vs_state_var = false;
|
|
}
|
|
|
|
if (new_beamcon0 & BEAMCON0_VARVBEN) {
|
|
if (vbstrt == vpos) {
|
|
vbstrt_m = vpos;
|
|
vb_start_line = 1;
|
|
vb_state = true;
|
|
}
|
|
int vbs = vbstop - 1;
|
|
if (vbs < 0) {
|
|
vbs += maxvpos;
|
|
}
|
|
if (vbs == vpos) {
|
|
vbstop_m = vpos;
|
|
vb_end_line = true;
|
|
vb_state = false;
|
|
}
|
|
}
|
|
|
|
if (vpos == sprhstrt_v) {
|
|
hhspr = 1;
|
|
}
|
|
if (vpos == sprhstop_v) {
|
|
hhspr = 0;
|
|
}
|
|
if (vpos == bplhstrt_v) {
|
|
hhbpl = 1;
|
|
}
|
|
if (vpos == bplhstop_v) {
|
|
hhbpl = 0;
|
|
}
|
|
uae_u16 add = maxhpos - 1;
|
|
uae_u16 max = (new_beamcon0 & BEAMCON0_DUAL) ? htotal : add;
|
|
uae_u16 hhpos_old = hhpos;
|
|
hhpos += add;
|
|
if (hhpos_old <= max || hhpos >= 0x100) {
|
|
if (max)
|
|
hhpos %= max;
|
|
else
|
|
hhpos = 0;
|
|
}
|
|
if (hhpos_hpos) {
|
|
hhpos -= add - hhpos_hpos;
|
|
hhpos_hpos = 0;
|
|
}
|
|
hhpos &= 0xff;
|
|
}
|
|
|
|
if (beamcon0 & BEAMCON0_PAL) {
|
|
if (vpos == 2 && !lof_store) {
|
|
vs_state_hw = true;
|
|
}
|
|
if (vpos == 3 && lof_store) {
|
|
vs_state_hw = true;
|
|
}
|
|
if (vpos == 5 + 1) {
|
|
vs_state_hw = false;
|
|
}
|
|
} else {
|
|
if (vpos == 3) {
|
|
vs_state_hw = true;
|
|
}
|
|
if (vpos == 6 + 1) {
|
|
vs_state_hw = false;
|
|
}
|
|
}
|
|
|
|
vs_state_on_old = vs_state_on;
|
|
if (new_beamcon0 & bemcon0_vsync_mask) {
|
|
vs_state_on = vs_state_var;
|
|
} else {
|
|
vs_state_on = vs_state_hw;
|
|
}
|
|
|
|
if (!(new_beamcon0 & BEAMCON0_VARVBEN)) {
|
|
vb_check();
|
|
}
|
|
|
|
if (varhblank_lines > 0) {
|
|
varhblank_lines--;
|
|
if (!varhblank_lines) {
|
|
if (varhblank_val[0] != hbstrt || varhblank_val[1] != hbstop) {
|
|
varhblank_lines = -1;
|
|
varhblank_val[0] = hbstrt;
|
|
varhblank_val[1] = hbstop;
|
|
}
|
|
}
|
|
}
|
|
|
|
decide_vline(0);
|
|
|
|
int hp = REFRESH_FIRST_HPOS;
|
|
refptr_p = refptr;
|
|
for (int i = 0; i < 4; i++) {
|
|
uae_u16 strobe = get_strobe_reg(i);
|
|
alloc_cycle(hp, strobe != 0x1fe ? CYCLE_STROBE : CYCLE_REFRESH);
|
|
// assume refresh pointer not changed or conflicted
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read(strobe, refptr & refmask, hp, vpos, DMARECORD_REFRESH, i);
|
|
record_dma_read_value(0xffff);
|
|
}
|
|
#endif
|
|
refptr += ref_ras_add;
|
|
hp += 2;
|
|
}
|
|
refresh_handled_slot = 0;
|
|
refptr_preupdated = true;
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
if (vs_state_on) {
|
|
record_dma_event(DMA_EVENT_VS, REFRESH_FIRST_HPOS, vpos);
|
|
}
|
|
if (vb_start_line) {
|
|
record_dma_event(DMA_EVENT_VB, REFRESH_FIRST_HPOS, vpos);
|
|
}
|
|
if (vdiwstate == diw_states::DIW_waiting_stop) {
|
|
record_dma_event(DMA_EVENT_VDIW, REFRESH_FIRST_HPOS, vpos);
|
|
}
|
|
if (lof_store) {
|
|
record_dma_event(DMA_EVENT_LOF, REFRESH_FIRST_HPOS + 2, vpos);
|
|
}
|
|
if (lol) {
|
|
record_dma_event(DMA_EVENT_LOL, REFRESH_FIRST_HPOS + 2, vpos);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
events_dmal_hsync();
|
|
#if 0
|
|
// AF testing stuff
|
|
static int cnt = 0;
|
|
cnt++;
|
|
if (cnt == 500) {
|
|
int port_insert_custom (int inputmap_port, int devicetype, DWORD flags, const TCHAR *custom);
|
|
//port_insert_custom (0, 2, 0, _T("Left=0xCB Right=0xCD Up=0xC8 Down=0xD0 Fire=0x39 Fire.autorepeat=0xD2"));
|
|
port_insert_custom (1, 2, 0, _T("Left=0x1E Right=0x20 Up=0x11 Down=0x1F Fire=0x38"));
|
|
} else if (0 && cnt == 1000) {
|
|
TCHAR out[256];
|
|
bool port_get_custom (int inputmap_port, TCHAR *out);
|
|
port_get_custom (0, out);
|
|
port_get_custom (1, out);
|
|
}
|
|
#endif
|
|
bool input_read_done = false;
|
|
|
|
if (currprefs.cpu_thread) {
|
|
|
|
maybe_process_pull_audio();
|
|
|
|
} else if (isvsync_chipset() < 0) {
|
|
|
|
if (currprefs.gfx_display_sections <= 1) {
|
|
if (vsync_vblank >= 85)
|
|
input_read_done = linesync_beam_single_dual();
|
|
else
|
|
input_read_done = linesync_beam_single_single();
|
|
} else {
|
|
if (currprefs.gfx_variable_sync)
|
|
input_read_done = linesync_beam_vrr();
|
|
else if (vsync_vblank >= 85)
|
|
input_read_done = linesync_beam_multi_dual();
|
|
else
|
|
input_read_done = linesync_beam_multi_single();
|
|
}
|
|
|
|
} else if (!currprefs.cpu_thread && !cpu_sleepmode && currprefs.m68k_speed < 0 && !currprefs.cpu_memory_cycle_exact) {
|
|
|
|
static int sleeps_remaining;
|
|
if (is_last_line ()) {
|
|
sleeps_remaining = (165 - currprefs.cpu_idle) / 6;
|
|
if (sleeps_remaining < 0)
|
|
sleeps_remaining = 0;
|
|
/* really last line, just run the cpu emulation until whole vsync time has been used */
|
|
if (regs.stopped && currprefs.cpu_idle) {
|
|
// CPU in STOP state: sleep if enough time left.
|
|
frame_time_t rpt = read_processor_time();
|
|
while (vsync_isdone(NULL) <= 0 && vsyncmintime - (rpt + vsynctimebase / 10) > 0 && vsyncmintime - rpt < vsynctimebase) {
|
|
maybe_process_pull_audio();
|
|
// if (!execute_other_cpu(rpt + vsynctimebase / 10)) {
|
|
if (cpu_sleep_millis(1) < 0)
|
|
break;
|
|
// }
|
|
rpt = read_processor_time();
|
|
}
|
|
} else if (currprefs.m68k_speed_throttle) {
|
|
vsyncmintime = read_processor_time(); /* end of CPU emulation time */
|
|
events_reset_syncline();
|
|
maybe_process_pull_audio();
|
|
} else {
|
|
vsyncmintime = vsyncmaxtime; /* emulate if still time left */
|
|
is_syncline_end = read_processor_time() + vsynctimebase; /* far enough in future, we never wait that long */
|
|
is_syncline = -12;
|
|
maybe_process_pull_audio();
|
|
}
|
|
} else {
|
|
static int linecounter;
|
|
/* end of scanline, run cpu emulation as long as we still have time */
|
|
vsyncmintime += vsynctimeperline;
|
|
linecounter++;
|
|
events_reset_syncline();
|
|
if (vsync_isdone(NULL) <= 0 && !currprefs.turbo_emulation) {
|
|
if (vsyncmaxtime - vsyncmintime > 0) {
|
|
if (vsyncwaittime - vsyncmintime > 0) {
|
|
frame_time_t rpt = read_processor_time();
|
|
/* Extra time left? Do some extra CPU emulation */
|
|
if (vsyncmintime - rpt > 0) {
|
|
if (regs.stopped && currprefs.cpu_idle && sleeps_remaining > 0) {
|
|
// STOP STATE: sleep.
|
|
cpu_sleep_millis(1);
|
|
sleeps_remaining--;
|
|
maybe_process_pull_audio();
|
|
} else {
|
|
is_syncline = -11;
|
|
/* limit extra time */
|
|
is_syncline_end = rpt + vsynctimeperline;
|
|
linecounter = 0;
|
|
}
|
|
}
|
|
}
|
|
if (!isvsync ()) {
|
|
// extra cpu emulation time if previous 10 lines without extra time.
|
|
if (!is_syncline && linecounter >= 10 && (!regs.stopped || !currprefs.cpu_idle)) {
|
|
is_syncline = -10;
|
|
is_syncline_end = read_processor_time() + vsynctimeperline;
|
|
linecounter = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
maybe_process_pull_audio();
|
|
}
|
|
|
|
} else if (!currprefs.cpu_thread) {
|
|
|
|
// the rest
|
|
static int nextwaitvpos;
|
|
if (vpos == 0)
|
|
nextwaitvpos = maxvpos_display * 1 / 4;
|
|
if (audio_is_pull() > 0 && !currprefs.turbo_emulation) {
|
|
maybe_process_pull_audio();
|
|
frame_time_t rpt = read_processor_time();
|
|
while (audio_pull_buffer() > 1 && (!isvsync() || (vsync_isdone(NULL) <= 0 && vsyncmintime - (rpt + vsynctimebase / 10) > 0 && vsyncmintime - rpt < vsynctimebase))) {
|
|
cpu_sleep_millis(1);
|
|
maybe_process_pull_audio();
|
|
rpt = read_processor_time();
|
|
}
|
|
}
|
|
if (vpos + 1 < maxvpos + lof_store && vpos >= nextwaitvpos && vpos < maxvpos - (maxvpos / 3) && (audio_is_pull() <= 0 || (audio_is_pull() > 0 && audio_pull_buffer()))) {
|
|
nextwaitvpos += maxvpos_display * 1 / 3;
|
|
vsyncmintime += vsynctimeperline;
|
|
if (vsync_isdone(NULL) <= 0 && !currprefs.turbo_emulation) {
|
|
frame_time_t rpt = read_processor_time();
|
|
// sleep if more than 2ms "free" time
|
|
while (vsync_isdone(NULL) <= 0 && vsyncmintime - (rpt + vsynctimebase / 10) > 0 && vsyncmintime - rpt < vsynctimebase) {
|
|
maybe_process_pull_audio();
|
|
// if (!execute_other_cpu(rpt + vsynctimebase / 10)) {
|
|
if (cpu_sleep_millis(1) < 0)
|
|
break;
|
|
// }
|
|
rpt = read_processor_time();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!input_read_done) {
|
|
inputdevice_hsync(false);
|
|
}
|
|
|
|
reset_decisions_scanline_start();
|
|
|
|
rethink_uae_int();
|
|
|
|
/* See if there's a chance of a copper wait ending this line. */
|
|
compute_spcflag_copper();
|
|
|
|
// check reset and process it immediately, don't wait for vsync
|
|
if (quit_program == -UAE_RESET || quit_program == -UAE_RESET_KEYBOARD || quit_program == -UAE_RESET_HARD) {
|
|
quit_program = -quit_program;
|
|
set_special(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE);
|
|
}
|
|
|
|
|
|
#if 0
|
|
/* fastest possible + last line and no vflip wait: render the frame as early as possible */
|
|
if (is_last_line () && isvsync_chipset () <= -2 && !vsync_rendered && currprefs.gfx_apmode[0].gfx_vflip == 0) {
|
|
frame_time_t start, end;
|
|
start = read_processor_time ();
|
|
vsync_rendered = true;
|
|
vsync_handle_redraw (lof_store, lof_changed, bplcon0, bplcon3);
|
|
if (vblank_hz_state) {
|
|
frame_rendered = crender_screen(1, true);
|
|
}
|
|
end = read_processor_time ();
|
|
frameskiptime += end - start;
|
|
}
|
|
#endif
|
|
|
|
rtg_vsynccheck ();
|
|
|
|
#if 0
|
|
{
|
|
static int skip;
|
|
if (M68K_GETPC >= 0x0C0D7A2 && M68K_GETPC < 0x00C0D7B2 && vpos == 0xf3) {
|
|
if (!skip)
|
|
activate_debugger ();
|
|
skip = 1;
|
|
}
|
|
if (vpos != 0xf3)
|
|
skip = 0;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
if (vpos == 300 && get_cycles() < 312 * 227 * CYCLE_UNIT)
|
|
activate_debugger();
|
|
#endif
|
|
}
|
|
|
|
static bool vsync_line;
|
|
// executed at start of scanline
|
|
static void hsync_handler(void)
|
|
{
|
|
bool vs = is_custom_vsync();
|
|
hsync_handler_pre(vs);
|
|
if (vs) {
|
|
devices_vsync_pre();
|
|
if (savestate_check()) {
|
|
uae_reset(0, 0);
|
|
return;
|
|
}
|
|
eventtab[ev_hsynch].evtime = get_cycles() + hsyncstartpos_start_cycles * CYCLE_UNIT;
|
|
eventtab[ev_hsynch].active = 1;
|
|
events_schedule();
|
|
|
|
}
|
|
if (vpos == maxvpos_display_vsync && !maxvpos_display_vsync_next) {
|
|
hsync_record_line_state_last(next_lineno, nextline_how, 0);
|
|
inputdevice_read_msg(true);
|
|
vsync_display_render();
|
|
vsync_display_rendered = false;
|
|
lof_display = lof_store;
|
|
hstrobe_conflict = false;
|
|
minfirstline_linear = minfirstline;
|
|
reset_autoscale();
|
|
display_vsync_counter++;
|
|
maxvpos_display_vsync_next = true;
|
|
} else if (vpos != maxvpos_display_vsync && maxvpos_display_vsync_next) {
|
|
// protect against weird VPOSW writes causing continuous vblanks
|
|
maxvpos_display_vsync_next = false;
|
|
}
|
|
vsync_line = vs;
|
|
hsync_handler_post(vs);
|
|
}
|
|
|
|
// executed at start of hsync
|
|
static void hsync_handlerh(void)
|
|
{
|
|
if (vsync_line) {
|
|
eventtab[ev_hsynch].evtime = get_cycles() + HSYNCTIME;
|
|
eventtab[ev_hsynch].active = 1;
|
|
events_schedule();
|
|
}
|
|
if (vpos == maxvpos_display_vsync) {
|
|
vposh = maxvpos_display_vsync;
|
|
}
|
|
hsync_handlerh(vsync_line);
|
|
}
|
|
|
|
static void audio_evhandler2(void)
|
|
{
|
|
// update copper first
|
|
// if copper had written to audio registers
|
|
if (copper_enabled_thisline) {
|
|
int hpos = current_hpos();
|
|
sync_copper(hpos);
|
|
}
|
|
audio_evhandler();
|
|
}
|
|
|
|
void init_eventtab(void)
|
|
{
|
|
if (!savestate_state) {
|
|
clear_events();
|
|
}
|
|
|
|
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_hsynch].handler = hsync_handlerh;
|
|
eventtab[ev_hsynch].evtime = get_cycles() + HSYNCTIME;
|
|
eventtab[ev_hsynch].active = 0;
|
|
eventtab[ev_misc].handler = MISC_handler;
|
|
eventtab[ev_audio].handler = audio_evhandler2;
|
|
|
|
eventtab2[ev2_blitter].handler = blitter_handler;
|
|
|
|
events_schedule();
|
|
}
|
|
|
|
void custom_prepare(void)
|
|
{
|
|
set_hpos();
|
|
hsync_handler_post(true);
|
|
}
|
|
|
|
void custom_cpuchange(void)
|
|
{
|
|
// both values needs to be same but also different
|
|
// after CPU mode changes
|
|
intreq = intreq | 0x8000;
|
|
intena = intena | 0x8000;
|
|
intreq2 = intreq;
|
|
intena2 = intena;
|
|
}
|
|
|
|
|
|
void custom_reset(bool hardreset, bool keyboardreset)
|
|
{
|
|
if (hardreset) {
|
|
board_prefs_changed(-1, -1);
|
|
initial_frame = true;
|
|
}
|
|
|
|
target_reset();
|
|
devices_reset(hardreset);
|
|
write_log(_T("Reset at %08X. Chipset mask = %08X\n"), M68K_GETPC, currprefs.chipset_mask);
|
|
memory_map_dump();
|
|
|
|
bool ntsc = currprefs.ntscmode;
|
|
|
|
lightpen_active = 0;
|
|
lightpen_triggered = 0;
|
|
lightpen_cx[0] = lightpen_cy[0] = -1;
|
|
lightpen_cx[1] = lightpen_cy[1] = -1;
|
|
lightpen_x[0] = -1;
|
|
lightpen_y[0] = -1;
|
|
lightpen_x[1] = -1;
|
|
lightpen_y[1] = -1;
|
|
nr_armed = 0;
|
|
next_lineno = 0;
|
|
vb_start_line = 1;
|
|
if (agnusa1000) {
|
|
vb_start_line = 0;
|
|
}
|
|
vb_state = true;
|
|
vs_state_var = false;
|
|
vs_state_hw = false;
|
|
vs_state_on = false;
|
|
dmal_htotal_mask = 0xffff;
|
|
memset(custom_storage, 0, sizeof(custom_storage));
|
|
toscr_res_old = -1;
|
|
toscr_nbits = 0;
|
|
update_denise_vars();
|
|
estimated_fm = 0xffff;
|
|
exthblank = false;
|
|
exthblank_state = false;
|
|
hbstrt_v2 = 0;
|
|
hbstop_v2 = 0;
|
|
hcenter_v2 = 0;
|
|
hcenter_active = false;
|
|
set_hcenter();
|
|
display_reset = 1;
|
|
copper_bad_cycle = 0;
|
|
copper_dma_change_cycle = -1;
|
|
blitter_dma_change_cycle = -1;
|
|
sprite_dma_change_cycle_on = -1;
|
|
custom_color_write_cycle = -1;
|
|
vt_old = 0;
|
|
ht_old = 0;
|
|
hdiwstate_blank = diw_states::DIW_waiting_start;
|
|
maxvpos_display_vsync_next = false;
|
|
|
|
irq_forced_delay = 0;
|
|
irq_forced = 0;
|
|
|
|
if (hardreset || savestate_state) {
|
|
maxhpos = ntsc ? MAXHPOS_NTSC : MAXHPOS_PAL;
|
|
maxhpos_short = ntsc ? MAXHPOS_NTSC : MAXHPOS_PAL;
|
|
maxvpos = ntsc ? MAXVPOS_NTSC : MAXVPOS_PAL;
|
|
maxvpos_nom = ntsc ? MAXVPOS_NTSC : MAXVPOS_PAL;
|
|
maxvpos_display = ntsc ? MAXVPOS_NTSC : MAXVPOS_PAL;
|
|
}
|
|
|
|
if (!savestate_state) {
|
|
cia_hsync = 0;
|
|
extra_cycle = 0;
|
|
hsync_counter = 0;
|
|
vsync_counter = 0;
|
|
display_vsync_counter = 0;
|
|
currprefs.chipset_mask = changed_prefs.chipset_mask;
|
|
update_mirrors();
|
|
blitter_reset();
|
|
|
|
if (hardreset) {
|
|
|
|
vtotal = MAXVPOS_LINES_ECS - 1;
|
|
htotal = MAXHPOS_ROWS - 1;
|
|
hbstrt = 0;
|
|
hbstop = 0;
|
|
hsstrt = 0;
|
|
hsstop = 0;
|
|
vbstrt = 0;
|
|
vbstop = 0;
|
|
vsstrt = 0;
|
|
vsstop = 0;
|
|
hcenter = 0;
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
uae_u16 c;
|
|
if (i == 0) {
|
|
c = ((ecs_denise && !aga_mode) || denisea1000) ? 0xfff : 0x000;
|
|
} else {
|
|
c |= uaerand();
|
|
c |= uaerand();
|
|
}
|
|
c &= 0xfff;
|
|
current_colors.color_regs_ecs[i] = c;
|
|
}
|
|
for (int i = 0; i < 256; i++) {
|
|
uae_u32 c = 0;
|
|
if (i > 0) {
|
|
c |= uaerand();
|
|
c |= uaerand();
|
|
}
|
|
c &= 0xffffff;
|
|
current_colors.color_regs_aga[i] = c;
|
|
}
|
|
if (!aga_mode) {
|
|
for (int i = 0; i < 32; i++) {
|
|
current_colors.acolors[i] = getxcolor(current_colors.color_regs_ecs[i]);
|
|
}
|
|
#ifdef AGA
|
|
} else {
|
|
for (int i = 0; i < 256; i++) {
|
|
current_colors.acolors[i] = getxcolor(current_colors.color_regs_aga[i]);
|
|
}
|
|
#endif
|
|
}
|
|
lof_store = lof_display = 0;
|
|
lof_lace = false;
|
|
setextblank();
|
|
}
|
|
|
|
clxdat = 0;
|
|
|
|
/* Clear the armed flags of all sprites. */
|
|
memset(spr, 0, sizeof spr);
|
|
|
|
dmacon = 0;
|
|
intreq = intreq2 = 0;
|
|
intena = intena2 = 0;
|
|
|
|
copcon = 0;
|
|
DSKLEN (0, 0);
|
|
|
|
bplcon0 = 0;
|
|
bplcon4 = 0x0011; /* Get AGA chipset into ECS compatibility mode */
|
|
bplcon3 = 0x0C00;
|
|
|
|
bplcon0_saved = bplcon0;
|
|
bplcon1_saved = bplcon1;
|
|
bplcon2_saved = bplcon2;
|
|
bplcon3_saved = bplcon3;
|
|
bplcon4_saved = bplcon4;
|
|
|
|
diwhigh = 0;
|
|
diwhigh_written = 0;
|
|
hdiwstate = diw_states::DIW_waiting_start; // this does not reset at vblank
|
|
|
|
refptr = 0;
|
|
if (aga_mode) {
|
|
refptr = 0x1ffffe;
|
|
}
|
|
refptr_p = refptr;
|
|
FMODE(0, 0);
|
|
CLXCON(-1, 0);
|
|
CLXCON2(-1, 0);
|
|
setup_fmodes(0, bplcon0);
|
|
beamcon0 = new_beamcon0 = beamcon0_saved = currprefs.ntscmode ? 0x00 : BEAMCON0_PAL;
|
|
blt_info.blit_main = 0;
|
|
blt_info.blit_finald = 0;
|
|
blt_info.blit_pending = 0;
|
|
blt_info.blit_interrupt = 1;
|
|
blt_info.blit_queued = 0;
|
|
init_sprites();
|
|
|
|
maxhpos = ntsc ? MAXHPOS_NTSC : MAXHPOS_PAL;
|
|
maxhpos_short = maxhpos;
|
|
updateextblk();
|
|
|
|
if (currprefs.cs_compatible == CP_DRACO) {
|
|
// fake draco interrupts
|
|
INTENA(0x8000 | 0x4000 | 0x1000 | 0x2000 | 0x0080 | 0x0010 | 0x0008 | 0x0001);
|
|
}
|
|
}
|
|
|
|
specialmonitor_reset();
|
|
|
|
unset_special (~(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE));
|
|
|
|
vpos = 0;
|
|
vpos_prev = maxvpos - 1;
|
|
vpos_count = vpos_count_diff = 0;
|
|
|
|
inputdevice_reset();
|
|
timehack_alive = 0;
|
|
|
|
curr_sprite_entries = 0;
|
|
prev_sprite_entries = 0;
|
|
sprite_entries[0][0].first_pixel = 0;
|
|
sprite_entries[1][0].first_pixel = MAX_SPR_PIXELS;
|
|
sprite_entries[0][1].first_pixel = 0;
|
|
sprite_entries[1][1].first_pixel = MAX_SPR_PIXELS;
|
|
memset(spixels, 0, 2 * MAX_SPR_PIXELS * sizeof *spixels);
|
|
memset(&spixstate, 0, sizeof spixstate);
|
|
toscr_delay_sh[0] = 0;
|
|
toscr_delay_sh[1] = 0;
|
|
|
|
vdiwstate = diw_states::DIW_waiting_start;
|
|
vdiw_change(0);
|
|
check_harddis();
|
|
|
|
dmal = 0;
|
|
init_hz_normal();
|
|
// init_hz sets vpos_count
|
|
vpos_count = 0;
|
|
vpos_lpen = -1;
|
|
lof_changing = 0;
|
|
lof_togglecnt_nlace = lof_togglecnt_lace = 0;
|
|
//nlace_cnt = NLACE_CNT_NEEDED;
|
|
|
|
audio_reset();
|
|
if (!isrestore()) {
|
|
memset(&cop_state, 0, sizeof(cop_state));
|
|
cop_state.state = COP_stop;
|
|
/* must be called after audio_reset */
|
|
adkcon = 0;
|
|
#ifdef SERIAL_PORT
|
|
serial_uartbreak(0);
|
|
#endif
|
|
audio_update_adkmasks();
|
|
}
|
|
|
|
init_hardware_frame();
|
|
drawing_init();
|
|
|
|
reset_decisions_scanline_start();
|
|
reset_decisions_hsync_start();
|
|
|
|
bogusframe = 1;
|
|
vsync_rendered = false;
|
|
frame_shown = false;
|
|
frame_rendered = false;
|
|
|
|
if (isrestore()) {
|
|
uae_u16 v;
|
|
uae_u32 vv;
|
|
|
|
audio_update_adkmasks();
|
|
INTENA(0);
|
|
INTREQ(0);
|
|
COPJMP(1, 1);
|
|
v = bplcon0;
|
|
BPLCON0(0, 0);
|
|
BPLCON0(0, v);
|
|
FMODE(0, fmode);
|
|
if (!aga_mode) {
|
|
for(int i = 0 ; i < 32 ; i++) {
|
|
vv = current_colors.color_regs_ecs[i];
|
|
current_colors.color_regs_ecs[i] = -1;
|
|
record_color_change(0, i, vv);
|
|
remembered_color_entry = -1;
|
|
current_colors.color_regs_ecs[i] = vv;
|
|
current_colors.acolors[i] = xcolors[vv];
|
|
}
|
|
#ifdef AGA
|
|
} else {
|
|
for(int i = 0 ; i < 256 ; i++) {
|
|
vv = current_colors.color_regs_aga[i];
|
|
current_colors.color_regs_aga[i] = -1;
|
|
record_color_change(0, i, vv);
|
|
remembered_color_entry = -1;
|
|
current_colors.color_regs_aga[i] = vv;
|
|
current_colors.acolors[i] = CONVERT_RGB(vv);
|
|
}
|
|
#endif
|
|
}
|
|
CLXCON(-1, clxcon);
|
|
CLXCON2(-1, clxcon2);
|
|
calcdiw();
|
|
#ifdef SERIAL_PORT
|
|
v = serper;
|
|
serper = 0;
|
|
SERPER(v);
|
|
#endif
|
|
for (int i = 0; i < 8; i++) {
|
|
SPRxCTLPOS(i);
|
|
nr_armed += spr[i].armed != 0;
|
|
}
|
|
if (!currprefs.produce_sound) {
|
|
eventtab[ev_audio].active = 0;
|
|
events_schedule();
|
|
}
|
|
|
|
write_log(_T("CPU=%d Chipset=%s %s\n"),
|
|
currprefs.cpu_model,
|
|
(aga_mode ? _T("AGA") :
|
|
(ecs_agnus && ecs_denise ? _T("Full ECS") :
|
|
(ecs_denise ? _T("ECS Denise") :
|
|
(ecs_agnus ? _T("ECS") : _T("OCS"))))),
|
|
currprefs.ntscmode ? _T("NTSC") : _T("PAL"));
|
|
write_log(_T("State restored\n"));
|
|
}
|
|
|
|
sprres = expand_sprres(bplcon0, bplcon3);
|
|
sprite_width = GET_SPRITEWIDTH(fmode);
|
|
for (int i = 0; i < MAX_SPRITES; i++) {
|
|
spr[i].width = sprite_width;
|
|
}
|
|
setup_fmodes(0, bplcon0);
|
|
shdelay_disabled = false;
|
|
|
|
#ifdef ACTION_REPLAY
|
|
/* Doing this here ensures we can use the 'reset' command from within AR */
|
|
action_replay_reset(hardreset, keyboardreset);
|
|
#endif
|
|
#if defined(ENFORCER)
|
|
enforcer_disable();
|
|
#endif
|
|
|
|
if (hardreset) {
|
|
rtc_hardreset();
|
|
}
|
|
|
|
// must be last
|
|
#ifdef AUTOCONFIG
|
|
expamem_reset(hardreset);
|
|
#endif
|
|
|
|
#if 0
|
|
record_dma_reset(-1);
|
|
#endif
|
|
|
|
}
|
|
|
|
void custom_dumpstate(int mode)
|
|
{
|
|
if (!mode) {
|
|
console_out_f(_T("VPOS: %03d ($%03x) HPOS: %03d ($%03x) COP: $%08x\n"),
|
|
vpos, vpos, current_hpos(), current_hpos(),
|
|
cop_state.ip);
|
|
}
|
|
}
|
|
|
|
void dumpcustom(void)
|
|
{
|
|
console_out_f(_T("DMACON: $%04x INTENA: $%04x ($%04x) INTREQ: $%04x ($%04x) VPOS: %03d ($%03x) HPOS: %03d ($%03x)\n"),
|
|
DMACONR(current_hpos()),
|
|
intena, intena, intreq, intreq, vpos, vpos, current_hpos(), current_hpos());
|
|
console_out_f(_T("INT: $%04x IPL: %d\n"), intena & intreq, intlev());
|
|
console_out_f(_T("COP1LC: $%08x, COP2LC: $%08x COPPTR: $%08x\n"), cop1lc, cop2lc, cop_state.ip);
|
|
console_out_f(_T("DIWSTRT: $%04x DIWSTOP: $%04x DDFSTRT: $%04x DDFSTOP: $%04x\n"),
|
|
diwstrt, diwstop, ddfstrt, ddfstop);
|
|
console_out_f(_T("BPLCON 0: $%04x 1: $%04x 2: $%04x 3: $%04x 4: $%04x LOF=%d/%d HDIW=%d VDIW=%d\n"),
|
|
bplcon0, bplcon1, bplcon2, bplcon3, bplcon4,
|
|
lof_display, lof_store,
|
|
hdiwstate == diw_states::DIW_waiting_start ? 0 : 1, vdiwstate == diw_states::DIW_waiting_start ? 0 : 1);
|
|
if (timeframes && vsync_counter) {
|
|
console_out_f(_T("Average frame time: %.2f ms [frames: %d time: %d]\n"),
|
|
(double)frametime / timeframes, vsync_counter, frametime);
|
|
if (total_skipped)
|
|
console_out_f(_T("Skipped frames: %d\n"), total_skipped);
|
|
}
|
|
}
|
|
|
|
static void gen_custom_tables(void)
|
|
{
|
|
for (int 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 (int i = 0; i < 16; i++) {
|
|
clxmask[i] = (((i & 1) ? 0xF : 0x3)
|
|
| ((i & 2) ? 0xF0 : 0x30)
|
|
| ((i & 4) ? 0xF00 : 0x300)
|
|
| ((i & 8) ? 0xF000 : 0x3000));
|
|
sprclx[i] = (((i & 0x3) == 0x3 ? 1 : 0)
|
|
| ((i & 0x5) == 0x5 ? 2 : 0)
|
|
| ((i & 0x9) == 0x9 ? 4 : 0)
|
|
| ((i & 0x6) == 0x6 ? 8 : 0)
|
|
| ((i & 0xA) == 0xA ? 16 : 0)
|
|
| ((i & 0xC) == 0xC ? 32 : 0)) << 9;
|
|
}
|
|
}
|
|
|
|
/* mousehack is now in "filesys boot rom" */
|
|
static uae_u32 REGPARAM2 mousehack_helper_old(struct TrapContext *ctx)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int custom_init(void)
|
|
{
|
|
|
|
#ifdef AUTOCONFIG
|
|
if (uae_boot_rom_type) {
|
|
uaecptr pos;
|
|
pos = here();
|
|
|
|
org(rtarea_base + 0xFF70);
|
|
calltrap(deftrap(mousehack_helper_old));
|
|
dw(RTS);
|
|
|
|
org(rtarea_base + 0xFFA0);
|
|
calltrap(deftrap(timehack_helper));
|
|
dw(RTS);
|
|
|
|
org(pos);
|
|
}
|
|
#endif
|
|
|
|
gen_custom_tables();
|
|
build_blitfilltable();
|
|
|
|
drawing_init();
|
|
|
|
update_mirrors();
|
|
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, NULL, _T("Custom chipset"),
|
|
custom_lgeti, custom_wgeti,
|
|
ABFLAG_IO, S_READ, S_WRITE, NULL, 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 uae_u32 REGPARAM2 custom_wget_1(int hpos, uaecptr addr, int noput, bool isbyte)
|
|
{
|
|
uae_u16 v;
|
|
int missing;
|
|
#if CUSTOM_DEBUG > 2
|
|
write_log (_T("%d:%d:wget: %04X=%04X pc=%p\n"), current_hpos(), vpos, addr, addr & 0x1fe, m68k_getpc ());
|
|
#endif
|
|
#ifdef DEBUGGER
|
|
if (memwatch_access_validator)
|
|
debug_check_reg(addr, 0, 0);
|
|
#endif
|
|
|
|
addr &= 0xfff;
|
|
|
|
switch (addr & 0x1fe) {
|
|
case 0x002: v = DMACONR(hpos); break;
|
|
case 0x004: v = VPOSR(); break;
|
|
case 0x006: v = VHPOSR(); break;
|
|
|
|
case 0x00A: v = JOY0DAT(); break;
|
|
case 0x00C: v = JOY1DAT(); break;
|
|
case 0x00E: v = CLXDAT(hpos); break;
|
|
case 0x010: v = ADKCONR(); break;
|
|
|
|
case 0x012: v = POT0DAT(); break;
|
|
case 0x014: v = POT1DAT(); break;
|
|
case 0x016: v = POTGOR(); break;
|
|
#ifdef SERIAL_PORT
|
|
case 0x018: v = SERDATR(); break;
|
|
#else
|
|
case 0x018: v = 0x3000 /* no data */; break;
|
|
#endif
|
|
case 0x01A: v = DSKBYTR(hpos); break;
|
|
case 0x01C: v = INTENAR(); break;
|
|
case 0x01E: v = INTREQR(); break;
|
|
case 0x07C:
|
|
v = DENISEID(&missing);
|
|
if (missing)
|
|
goto writeonly;
|
|
break;
|
|
|
|
case 0x1DA:
|
|
if (!ecs_agnus)
|
|
goto writeonly;
|
|
v = HHPOSR();
|
|
break;
|
|
|
|
#ifdef AGA
|
|
case 0x180: case 0x182: case 0x184: case 0x186: case 0x188: case 0x18A:
|
|
case 0x18C: case 0x18E: case 0x190: case 0x192: case 0x194: case 0x196:
|
|
case 0x198: case 0x19A: case 0x19C: case 0x19E: case 0x1A0: case 0x1A2:
|
|
case 0x1A4: case 0x1A6: case 0x1A8: case 0x1AA: case 0x1AC: case 0x1AE:
|
|
case 0x1B0: case 0x1B2: case 0x1B4: case 0x1B6: case 0x1B8: case 0x1BA:
|
|
case 0x1BC: case 0x1BE:
|
|
if (!aga_mode)
|
|
goto writeonly;
|
|
v = COLOR_READ((addr & 0x3E) / 2);
|
|
break;
|
|
#endif
|
|
|
|
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 regs.chipset_latch_rw
|
|
*/
|
|
v = regs.chipset_latch_rw;
|
|
SET_LINE_CYCLEBASED(hpos);
|
|
if (!noput) {
|
|
int r, c, bmdma;
|
|
uae_u16 l;
|
|
|
|
if (aga_mode) {
|
|
l = 0;
|
|
} else {
|
|
// last chip bus value (read or write) is written to register
|
|
if (currprefs.cpu_compatible && currprefs.cpu_model == 68000) {
|
|
if (isbyte)
|
|
l = (regs.chipset_latch_rw << 8) | (regs.chipset_latch_rw & 0xff);
|
|
else
|
|
l = regs.chipset_latch_rw;
|
|
} else {
|
|
l = regs.chipset_latch_rw;
|
|
}
|
|
}
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
decide_blitter(hpos);
|
|
#ifdef DEBUGGER
|
|
debug_wputpeek(0xdff000 + addr, l);
|
|
#endif
|
|
r = custom_wput_1(hpos, addr, l, 1);
|
|
|
|
// CPU gets back (OCS/ECS only):
|
|
// - if last cycle was DMA cycle: DMA cycle data
|
|
// - if last cycle was not DMA cycle: FFFF or some ANDed old data.
|
|
//
|
|
if (hpos == 0) {
|
|
int hp = maxhpos - 1;
|
|
c = cycle_line_slot_last & CYCLE_MASK;
|
|
bmdma = bitplane_dma_access(hp, 0);
|
|
} else {
|
|
int hp = hpos - 1;
|
|
c = cycle_line_slot[hp] & CYCLE_MASK;
|
|
bmdma = bitplane_dma_access(hp, 0);
|
|
}
|
|
if (aga_mode) {
|
|
if (bmdma || (c > CYCLE_REFRESH && c < CYCLE_CPU)) {
|
|
v = regs.chipset_latch_rw;
|
|
} else if (c == CYCLE_CPU) {
|
|
v = regs.db;
|
|
} else {
|
|
v = regs.chipset_latch_rw >> ((addr & 2) ? 0 : 16);
|
|
}
|
|
} else {
|
|
if (bmdma || (c > CYCLE_REFRESH && c < CYCLE_CPU)) {
|
|
v = regs.chipset_latch_rw;
|
|
} else {
|
|
// refresh checked because refresh cycles do not always
|
|
// set regs.chipset_latch_rw for performance reasons.
|
|
v = 0xffff;
|
|
}
|
|
}
|
|
#if CUSTOM_DEBUG > 0
|
|
write_log (_T("%08X read = %04X. Value written=%04X PC=%08x\n"), 0xdff000 | addr, v, l, M68K_GETPC);
|
|
#endif
|
|
return v;
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 custom_wget2(uaecptr addr, bool byte)
|
|
{
|
|
uae_u32 v;
|
|
int hpos = current_hpos();
|
|
|
|
sync_copper(hpos);
|
|
v = custom_wget_1 (hpos, addr, 0, byte);
|
|
#ifdef ACTION_REPLAY
|
|
#ifdef ACTION_REPLAY_COMMON
|
|
addr &= 0x1fe;
|
|
ar_custom[addr + 0] = (uae_u8)(v >> 8);
|
|
ar_custom[addr + 1] = (uae_u8)(v);
|
|
#endif
|
|
#endif
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 custom_wget(uaecptr addr)
|
|
{
|
|
uae_u32 v;
|
|
|
|
if ((addr & 0xffff) < 0x8000 && currprefs.cs_fatgaryrev >= 0)
|
|
return dummy_get(addr, 2, false, 0);
|
|
if (addr & 1) {
|
|
#ifdef DEBUGGER
|
|
debug_invalid_reg(addr, 2, 0);
|
|
#endif
|
|
/* think about move.w $dff005,d0.. (68020+ only) */
|
|
addr &= ~1;
|
|
v = custom_wget2(addr, false) << 8;
|
|
v |= custom_wget2(addr + 2, false) >> 8;
|
|
return v;
|
|
}
|
|
return custom_wget2(addr, false);
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 custom_bget(uaecptr addr)
|
|
{
|
|
uae_u32 v;
|
|
if ((addr & 0xffff) < 0x8000 && currprefs.cs_fatgaryrev >= 0)
|
|
return dummy_get(addr, 1, false, 0);
|
|
#ifdef DEBUGGER
|
|
debug_invalid_reg(addr, 1, 0);
|
|
#endif
|
|
v = custom_wget2(addr & ~1, true);
|
|
v >>= (addr & 1) ? 0 : 8;
|
|
return v;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 custom_lget (uaecptr addr)
|
|
{
|
|
if ((addr & 0xffff) < 0x8000 && currprefs.cs_fatgaryrev >= 0)
|
|
return dummy_get(addr, 4, false, 0);
|
|
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)
|
|
{
|
|
uaecptr oaddr = addr;
|
|
addr &= 0x1FE;
|
|
value &= 0xffff;
|
|
custom_storage[addr >> 1].value = (uae_u16)value;
|
|
custom_storage[addr >> 1].pc = copper_access ? cop_state.ip | 1 : M68K_GETPC;
|
|
#ifdef ACTION_REPLAY
|
|
#ifdef ACTION_REPLAY_COMMON
|
|
ar_custom[addr + 0]=(uae_u8)(value >> 8);
|
|
ar_custom[addr + 1]=(uae_u8)(value);
|
|
#endif
|
|
#endif
|
|
#ifdef DEBUGGER
|
|
if (memwatch_access_validator) {
|
|
debug_check_reg(oaddr, 1, value);
|
|
}
|
|
#endif
|
|
|
|
switch (addr) {
|
|
case 0x00E: CLXDAT(hpos); 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 0x028: REFPTR(hpos, value); break;
|
|
case 0x02A: VPOSW(hpos, value); break;
|
|
case 0x02C: VHPOSW(value); break;
|
|
case 0x02E: COPCON(value); break;
|
|
#ifdef SERIAL_PORT
|
|
case 0x030: SERDAT(value); break;
|
|
case 0x032: SERPER(value); break;
|
|
#else
|
|
case 0x030: break;
|
|
case 0x032: break;
|
|
#endif
|
|
case 0x034: POTGO(value); break;
|
|
|
|
case 0x040: BLTCON0(hpos, value); break;
|
|
case 0x042: BLTCON1(hpos, value); break;
|
|
|
|
case 0x044: BLTAFWM(hpos, value); break;
|
|
case 0x046: BLTALWM(hpos, value); break;
|
|
|
|
case 0x050: BLTAPTH(hpos, value); break;
|
|
case 0x052: BLTAPTL(hpos, value); break;
|
|
case 0x04C: BLTBPTH(hpos, value); break;
|
|
case 0x04E: BLTBPTL(hpos, value); break;
|
|
case 0x048: BLTCPTH(hpos, value); break;
|
|
case 0x04A: BLTCPTL(hpos, value); break;
|
|
case 0x054: BLTDPTH(hpos, value); break;
|
|
case 0x056: BLTDPTL(hpos, value); break;
|
|
|
|
case 0x058: BLTSIZE(hpos, value); break;
|
|
|
|
case 0x064: BLTAMOD(hpos, value); break;
|
|
case 0x062: BLTBMOD(hpos, value); break;
|
|
case 0x060: BLTCMOD(hpos, value); break;
|
|
case 0x066: BLTDMOD(hpos, value); break;
|
|
|
|
case 0x070: BLTCDAT(hpos, value); break;
|
|
case 0x072: BLTBDAT(hpos, value); break;
|
|
case 0x074: BLTADAT(hpos, value); break;
|
|
|
|
case 0x07E: DSKSYNC(hpos, value); break;
|
|
|
|
case 0x080: COP1LCH(value); break;
|
|
case 0x082: COP1LCL(value); break;
|
|
case 0x084: COP2LCH(value); break;
|
|
case 0x086: COP2LCL(value); break;
|
|
|
|
case 0x088: COPJMP(1, 0); break;
|
|
case 0x08A: COPJMP(2, 0); break;
|
|
|
|
case 0x08E: DIWSTRT(hpos, value); break;
|
|
case 0x090: DIWSTOP(hpos, value); break;
|
|
case 0x092: DDFSTRT(hpos, value); break;
|
|
case 0x094: DDFSTOP(hpos, value); break;
|
|
|
|
case 0x096: DMACON(hpos, value); break;
|
|
case 0x098: CLXCON(hpos, value); break;
|
|
case 0x09A: INTENA(value); break;
|
|
case 0x09C: INTREQ(value); break;
|
|
case 0x09E: ADKCON(hpos, value); break;
|
|
|
|
case 0x0A0: AUDxLCH(0, value); break;
|
|
case 0x0A2: AUDxLCL(0, value); break;
|
|
case 0x0A4: AUDxLEN(0, value); break;
|
|
case 0x0A6: AUDxPER(0, value); break;
|
|
case 0x0A8: AUDxVOL(0, value); break;
|
|
case 0x0AA: AUDxDAT(0, value); break;
|
|
|
|
case 0x0B0: AUDxLCH(1, value); break;
|
|
case 0x0B2: AUDxLCL(1, value); break;
|
|
case 0x0B4: AUDxLEN(1, value); break;
|
|
case 0x0B6: AUDxPER(1, value); break;
|
|
case 0x0B8: AUDxVOL(1, value); break;
|
|
case 0x0BA: AUDxDAT(1, value); break;
|
|
|
|
case 0x0C0: AUDxLCH(2, value); break;
|
|
case 0x0C2: AUDxLCL(2, value); break;
|
|
case 0x0C4: AUDxLEN(2, value); break;
|
|
case 0x0C6: AUDxPER(2, value); break;
|
|
case 0x0C8: AUDxVOL(2, value); break;
|
|
case 0x0CA: AUDxDAT(2, value); break;
|
|
|
|
case 0x0D0: AUDxLCH(3, value); break;
|
|
case 0x0D2: AUDxLCL(3, value); break;
|
|
case 0x0D4: AUDxLEN(3, value); break;
|
|
case 0x0D6: AUDxPER(3, value); break;
|
|
case 0x0D8: AUDxVOL(3, value); break;
|
|
case 0x0DA: AUDxDAT(3, value); break;
|
|
|
|
case 0x0E0: BPLxPTH(hpos, value, 0); break;
|
|
case 0x0E2: BPLxPTL(hpos, value, 0); break;
|
|
case 0x0E4: BPLxPTH(hpos, value, 1); break;
|
|
case 0x0E6: BPLxPTL(hpos, value, 1); break;
|
|
case 0x0E8: BPLxPTH(hpos, value, 2); break;
|
|
case 0x0EA: BPLxPTL(hpos, value, 2); break;
|
|
case 0x0EC: BPLxPTH(hpos, value, 3); break;
|
|
case 0x0EE: BPLxPTL(hpos, value, 3); break;
|
|
case 0x0F0: BPLxPTH(hpos, value, 4); break;
|
|
case 0x0F2: BPLxPTL(hpos, value, 4); break;
|
|
case 0x0F4: BPLxPTH(hpos, value, 5); break;
|
|
case 0x0F6: BPLxPTL(hpos, value, 5); break;
|
|
case 0x0F8: BPLxPTH(hpos, value, 6); break;
|
|
case 0x0FA: BPLxPTL(hpos, value, 6); break;
|
|
case 0x0FC: BPLxPTH(hpos, value, 7); break;
|
|
case 0x0FE: BPLxPTL(hpos, value, 7); break;
|
|
|
|
case 0x100: BPLCON0(hpos, value); break;
|
|
case 0x102: BPLCON1(hpos, value); break;
|
|
case 0x104: BPLCON2(hpos, value); break;
|
|
#ifdef ECS_DENISE
|
|
case 0x106: BPLCON3(hpos, value); break;
|
|
#endif
|
|
|
|
case 0x108: BPL1MOD(hpos, value); break;
|
|
case 0x10A: BPL2MOD(hpos, value); break;
|
|
#ifdef AGA
|
|
case 0x10E: CLXCON2(hpos, value); break;
|
|
#endif
|
|
|
|
case 0x110: BPLxDAT(hpos, 0, value); break;
|
|
case 0x112: BPLxDAT(hpos, 1, value); break;
|
|
case 0x114: BPLxDAT(hpos, 2, value); break;
|
|
case 0x116: BPLxDAT(hpos, 3, value); break;
|
|
case 0x118: BPLxDAT(hpos, 4, value); break;
|
|
case 0x11A: BPLxDAT(hpos, 5, value); break;
|
|
case 0x11C: BPLxDAT(hpos, 6, value); break;
|
|
case 0x11E: BPLxDAT(hpos, 7, value); break;
|
|
|
|
case 0x180: case 0x182: case 0x184: case 0x186: case 0x188: case 0x18A:
|
|
case 0x18C: case 0x18E: case 0x190: case 0x192: case 0x194: case 0x196:
|
|
case 0x198: case 0x19A: case 0x19C: case 0x19E: case 0x1A0: case 0x1A2:
|
|
case 0x1A4: case 0x1A6: case 0x1A8: case 0x1AA: case 0x1AC: case 0x1AE:
|
|
case 0x1B0: case 0x1B2: case 0x1B4: case 0x1B6: case 0x1B8: case 0x1BA:
|
|
case 0x1BC: case 0x1BE:
|
|
COLOR_WRITE(hpos, value & 0xFFF, (addr & 0x3E) / 2);
|
|
break;
|
|
case 0x120: case 0x124: case 0x128: case 0x12C:
|
|
case 0x130: case 0x134: case 0x138: case 0x13C:
|
|
SPRxPTH(hpos, value, (addr - 0x120) / 4);
|
|
break;
|
|
case 0x122: case 0x126: case 0x12A: case 0x12E:
|
|
case 0x132: case 0x136: case 0x13A: case 0x13E:
|
|
SPRxPTL(hpos, value, (addr - 0x122) / 4);
|
|
break;
|
|
case 0x140: case 0x148: case 0x150: case 0x158:
|
|
case 0x160: case 0x168: case 0x170: case 0x178:
|
|
SPRxPOS(hpos, value, (addr - 0x140) / 8);
|
|
break;
|
|
case 0x142: case 0x14A: case 0x152: case 0x15A:
|
|
case 0x162: case 0x16A: case 0x172: case 0x17A:
|
|
SPRxCTL(hpos, value, (addr - 0x142) / 8);
|
|
break;
|
|
case 0x144: case 0x14C: case 0x154: case 0x15C:
|
|
case 0x164: case 0x16C: case 0x174: case 0x17C:
|
|
SPRxDATA(hpos, value, (addr - 0x144) / 8);
|
|
break;
|
|
case 0x146: case 0x14E: case 0x156: case 0x15E:
|
|
case 0x166: case 0x16E: case 0x176: case 0x17E:
|
|
SPRxDATB(hpos, value, (addr - 0x146) / 8);
|
|
break;
|
|
|
|
case 0x36: JOYTEST(value); break;
|
|
case 0x5A: BLTCON0L(hpos, value); break;
|
|
case 0x5C: BLTSIZV(hpos, value); break;
|
|
case 0x5E: BLTSIZH(hpos, value); break;
|
|
case 0x1E4: DIWHIGH(hpos, value); break;
|
|
#ifdef AGA
|
|
case 0x10C: BPLCON4(hpos, value); break;
|
|
#endif
|
|
|
|
case 0x1DC: BEAMCON0(hpos, value); break;
|
|
case 0x1C0:
|
|
if (htotal != value) {
|
|
sync_changes(hpos);
|
|
htotal = value & (MAXHPOS_ROWS - 1);
|
|
varsync(addr, 1, -1);
|
|
}
|
|
break;
|
|
case 0x1C2:
|
|
if (hsstop != value) {
|
|
sync_changes(hpos);
|
|
hsstop = value & (MAXHPOS_ROWS - 1);
|
|
varsync(addr, 1, -1);
|
|
}
|
|
break;
|
|
case 0x1C4:
|
|
if (hbstrt != value) {
|
|
sync_changes(hpos);
|
|
hbstrt = value & 0x7ff;
|
|
varsync(addr, 0, -1);
|
|
}
|
|
break;
|
|
case 0x1C6:
|
|
if (hbstop != value) {
|
|
sync_changes(hpos);
|
|
hbstop = value & 0x7ff;
|
|
varsync(addr, 0, -1);
|
|
}
|
|
break;
|
|
case 0x1C8:
|
|
if (vtotal != value) {
|
|
vtotal = value & (MAXVPOS_LINES_ECS - 1);
|
|
varsync(addr, 1, -1);
|
|
}
|
|
break;
|
|
case 0x1CA:
|
|
if (vsstop != value) {
|
|
sync_changes(hpos);
|
|
vsstop = value & (MAXVPOS_LINES_ECS - 1);
|
|
varsync(addr, 1, -1);
|
|
}
|
|
break;
|
|
case 0x1CC:
|
|
if (vbstrt != value) {
|
|
sync_changes(hpos);
|
|
uae_u16 old = vbstrt;
|
|
vbstrt = value & (MAXVPOS_LINES_ECS - 1);
|
|
varsync(addr, 0, old);
|
|
}
|
|
break;
|
|
case 0x1CE:
|
|
if (vbstop != value) {
|
|
sync_changes(hpos);
|
|
uae_u16 old = vbstop;
|
|
vbstop = value & (MAXVPOS_LINES_ECS - 1);
|
|
varsync(addr, 0, old);
|
|
}
|
|
break;
|
|
case 0x1DE:
|
|
if (hsstrt != value) {
|
|
sync_changes(hpos);
|
|
hsstrt = value & (MAXHPOS_ROWS - 1);
|
|
varsync(addr, 1, -1);
|
|
}
|
|
break;
|
|
case 0x1E0:
|
|
if (vsstrt != value) {
|
|
sync_changes(hpos);
|
|
vsstrt = value & (MAXVPOS_LINES_ECS - 1);
|
|
varsync(addr, 1, -1);
|
|
}
|
|
break;
|
|
case 0x1E2:
|
|
if (hcenter != value) {
|
|
sync_changes(hpos);
|
|
hcenter = value & (MAXHPOS_ROWS - 1);
|
|
varsync(addr, 0, -1);
|
|
}
|
|
break;
|
|
|
|
case 0x1D0: SPRHSTRT(hpos, value); break;
|
|
case 0x1D2: SPRHSTOP(hpos, value); break;
|
|
case 0x1D4: BPLHSTRT(hpos, value); break;
|
|
case 0x1D6: BPLHSTOP(hpos, value); break;
|
|
case 0x1D8: HHPOS(hpos, value); break;
|
|
|
|
#ifdef AGA
|
|
case 0x1FC: FMODE(hpos, value); break;
|
|
#endif
|
|
case 0x1FE: FNULL(value); break;
|
|
|
|
/* writing to read-only register causes read access */
|
|
default:
|
|
if (!noget) {
|
|
#if CUSTOM_DEBUG > 0
|
|
write_log(_T("%04X written %08x\n"), addr, M68K_GETPC);
|
|
#endif
|
|
custom_wget_1(hpos, addr, 1, false);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void REGPARAM2 custom_wput(uaecptr addr, uae_u32 value)
|
|
{
|
|
int hpos = current_hpos ();
|
|
|
|
if ((addr & 0xffff) < 0x8000 && currprefs.cs_fatgaryrev >= 0) {
|
|
dummy_put(addr, 2, value);
|
|
return;
|
|
}
|
|
#if CUSTOM_DEBUG > 2
|
|
write_log (_T("%d:%d:wput: %04X %04X pc=%p\n"), hpos, vpos, addr & 0x01fe, value & 0xffff, m68k_getpc ());
|
|
#endif
|
|
sync_copper(hpos);
|
|
if (addr & 1) {
|
|
#ifdef DEBUGGER
|
|
debug_invalid_reg(addr, -2, value);
|
|
#endif
|
|
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 ((addr & 0xffff) < 0x8000 && currprefs.cs_fatgaryrev >= 0) {
|
|
dummy_put(addr, 1, value);
|
|
return;
|
|
}
|
|
#ifdef DEBUGGER
|
|
debug_invalid_reg(addr, -1, value);
|
|
#endif
|
|
if (aga_mode) {
|
|
if (addr & 1) {
|
|
rval = value & 0xff;
|
|
} else {
|
|
rval = (value << 8) | (value & 0xFF);
|
|
}
|
|
} else {
|
|
rval = (value << 8) | (value & 0xff);
|
|
}
|
|
|
|
if (currprefs.cs_bytecustomwritebug) {
|
|
if (addr & 1)
|
|
custom_wput (addr & ~1, rval);
|
|
else
|
|
custom_wput (addr, value << 8);
|
|
} else {
|
|
custom_wput (addr & ~1, rval);
|
|
}
|
|
}
|
|
|
|
static void REGPARAM2 custom_lput (uaecptr addr, uae_u32 value)
|
|
{
|
|
if ((addr & 0xffff) < 0x8000 && currprefs.cs_fatgaryrev >= 0) {
|
|
dummy_put(addr, 4, value);
|
|
return;
|
|
}
|
|
custom_wput (addr & 0xfffe, value >> 16);
|
|
custom_wput ((addr + 2) & 0xfffe, (uae_u16)value);
|
|
}
|
|
|
|
#ifdef SAVESTATE
|
|
|
|
void custom_prepare_savestate(void)
|
|
{
|
|
if (!currprefs.cpu_cycle_exact) {
|
|
for (int i = 0; i < ev2_max; i++) {
|
|
if (eventtab2[i].active) {
|
|
eventtab2[i].active = 0;
|
|
eventtab2[i].handler(eventtab2[i].data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void restore_custom_finish(void)
|
|
{
|
|
if ((bplcon0 & 2) && !currprefs.genlock) {
|
|
changed_prefs.genlock = currprefs.genlock = 1;
|
|
write_log(_T("statefile with BPLCON0 ERSY set without Genlock. Enabling Genlock.\n"));
|
|
}
|
|
}
|
|
|
|
void restore_custom_start(void)
|
|
{
|
|
memset(&cop_state, 0, sizeof(cop_state));
|
|
cop_state.state = COP_stop;
|
|
}
|
|
|
|
#define RB restore_u8()
|
|
#define SRB (uae_s8)restore_u8()
|
|
#define RBB restore_u8() != 0
|
|
#define RW restore_u16()
|
|
#define RL restore_u32()
|
|
|
|
uae_u8 *restore_custom(uae_u8 *src)
|
|
{
|
|
uae_u16 dsklen, dskbytr;
|
|
int dskpt;
|
|
int i;
|
|
|
|
audio_reset();
|
|
|
|
changed_prefs.chipset_mask = currprefs.chipset_mask = RL & CSMASK_MASK;
|
|
update_mirrors();
|
|
blt_info.bltddat = RW; /* 000 BLTDDAT */
|
|
RW; /* 002 DMACONR */
|
|
RW; /* 004 VPOSR */
|
|
RW; /* 006 VHPOSR */
|
|
RW; /* 008 DSKDATR (dummy register) */
|
|
JOYSET(0, RW); /* 00A JOY0DAT */
|
|
JOYSET(1, RW); /* 00C JOY1DAT */
|
|
clxdat = RW; /* 00E CLXDAT */
|
|
RW; /* 010 ADKCONR */
|
|
RW; /* 012 POT0DAT* */
|
|
RW; /* 014 POT1DAT* */
|
|
RW; /* 016 POTINP* */
|
|
RW; /* 018 SERDATR* */
|
|
dskbytr = RW; /* 01A DSKBYTR */
|
|
RW; /* 01C INTENAR */
|
|
RW; /* 01E INTREQR */
|
|
dskpt = RL; /* 020-022 DSKPT */
|
|
dsklen = RW; /* 024 DSKLEN */
|
|
RW; /* 026 DSKDAT */
|
|
refptr = RW; /* 028 REFPTR */
|
|
i = RW; lof_store = lof_display = (i & 0x8000) ? 1 : 0; lol = (i & 0x0080) ? 1 : 0; /* 02A VPOSW */
|
|
RW; /* 02C VHPOSW */
|
|
COPCON(RW); /* 02E COPCON */
|
|
RW; /* 030 SERDAT* */
|
|
#ifdef SERIAL_PORT
|
|
serper = RW; /* 032 SERPER* */
|
|
#else
|
|
RW; /* 032 SERPER* */
|
|
#endif
|
|
potgo_value = 0; POTGO(RW); /* 034 POTGO */
|
|
RW; /* 036 JOYTEST* */
|
|
RW; /* 038 STREQU */
|
|
RW; /* 03A STRVHBL */
|
|
RW; /* 03C STRHOR */
|
|
RW; /* 03E STRLONG */
|
|
BLTCON0(0, RW); /* 040 BLTCON0 */
|
|
BLTCON1(0, RW); /* 042 BLTCON1 */
|
|
BLTAFWM(0, RW); /* 044 BLTAFWM */
|
|
BLTALWM(0, RW); /* 046 BLTALWM */
|
|
BLTCPTH(0, RW);BLTCPTL(0, RW); /* 048-04B BLTCPT */
|
|
BLTBPTH(0, RW);BLTBPTL(0, RW); /* 04C-04F BLTBPT */
|
|
BLTAPTH(0, RW);BLTAPTL(0, RW); /* 050-053 BLTAPT */
|
|
BLTDPTH(0, RW);BLTDPTL(0, RW); /* 054-057 BLTDPT */
|
|
RW; /* 058 BLTSIZE */
|
|
RW; /* 05A BLTCON0L */
|
|
blt_info.vblitsize = RW;/* 05C BLTSIZV */
|
|
blt_info.hblitsize = RW;/* 05E BLTSIZH */
|
|
BLTCMOD(0, RW); /* 060 BLTCMOD */
|
|
BLTBMOD(0, RW); /* 062 BLTBMOD */
|
|
BLTAMOD(0, RW); /* 064 BLTAMOD */
|
|
BLTDMOD(0, RW); /* 066 BLTDMOD */
|
|
RW; /* 068 ? */
|
|
RW; /* 06A ? */
|
|
RW; /* 06C ? */
|
|
RW; /* 06E ? */
|
|
BLTCDAT(0, RW); /* 070 BLTCDAT */
|
|
BLTBDAT(0, RW); /* 072 BLTBDAT */
|
|
BLTADAT(0, RW); /* 074 BLTADAT */
|
|
RW; /* 076 ? */
|
|
RW; /* 078 ? */
|
|
RW; /* 07A ? */
|
|
RW; /* 07C LISAID */
|
|
DSKSYNC(-1, RW); /* 07E DSKSYNC */
|
|
cop1lc = RL; /* 080/082 COP1LC */
|
|
cop2lc = RL; /* 084/086 COP2LC */
|
|
RW; /* 088 ? */
|
|
RW; /* 08A ? */
|
|
RW; /* 08C ? */
|
|
diwstrt = RW; /* 08E DIWSTRT */
|
|
diwstop = RW; /* 090 DIWSTOP */
|
|
ddfstrt = RW; /* 092 DDFSTRT */
|
|
ddfstop = RW; /* 094 DDFSTOP */
|
|
dmacon = RW & ~(0x2000|0x4000); /* 096 DMACON */
|
|
CLXCON(-1, RW); /* 098 CLXCON */
|
|
intena = RW; /* 09A INTENA */
|
|
intreq = RW; /* 09C INTREQ */
|
|
adkcon = RW; /* 09E ADKCON */
|
|
for (i = 0; i < 8; i++)
|
|
bplptx[i] = bplpt[i] = RL;
|
|
bplcon0 = RW; /* 100 BPLCON0 */
|
|
bplcon1 = RW; /* 102 BPLCON1 */
|
|
bplcon2 = RW; /* 104 BPLCON2 */
|
|
bplcon3 = RW; /* 106 BPLCON3 */
|
|
bpl1mod = RW; /* 108 BPL1MOD */
|
|
bpl2mod = RW; /* 10A BPL2MOD */
|
|
bplcon4 = RW; /* 10C BPLCON4 */
|
|
clxcon2 = RW; /* 10E CLXCON2* */
|
|
for (i = 0; i < 8; i++) {
|
|
fetched[i] = RW; /* BPLXDAT */
|
|
fetched_aga[i] = fetched[i];
|
|
}
|
|
for (i = 0; i < 32; i++) {
|
|
uae_u16 v = RW;
|
|
color_regs_genlock[i] = (v & 0x8000) != 0;
|
|
current_colors.color_regs_ecs[i] = v & 0xfff; /* 180 COLORxx */
|
|
}
|
|
htotal = RW; /* 1C0 HTOTAL */
|
|
hsstop = RW; /* 1C2 HSTOP ? */
|
|
hbstrt = RW; /* 1C4 HBSTRT ? */
|
|
hbstop = RW; /* 1C6 HBSTOP ? */
|
|
vtotal = RW; /* 1C8 VTOTAL */
|
|
vsstop = RW; /* 1CA VSSTOP */
|
|
vbstrt = RW; /* 1CC VBSTRT */
|
|
vbstop = RW; /* 1CE VBSTOP */
|
|
SPRHSTRT(-1, RW); /* 1D0 SPRHSTART */
|
|
SPRHSTOP(-1, RW); /* 1D2 SPRHSTOP */
|
|
BPLHSTRT(-1, RW); /* 1D4 BPLHSTRT */
|
|
BPLHSTOP(-1, RW); /* 1D6 BPLHSTOP */
|
|
hhpos = RW; /* 1D8 HHPOSW */
|
|
RW; /* 1DA HHPOSR */
|
|
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_states::DIW_waiting_stop : diw_states::DIW_waiting_start;
|
|
vdiwstate = (diwhigh & 0x0080) ? diw_states::DIW_waiting_start : diw_states::DIW_waiting_stop;
|
|
diwhigh &= 0x3f3f;
|
|
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_value = RW; /* 1FE ? */
|
|
refptr = RL; /* full refresh pointer */
|
|
|
|
bplcon0_saved = bplcon0;
|
|
bplcon1_saved = bplcon1;
|
|
bplcon2_saved = bplcon2;
|
|
bplcon3_saved = bplcon3;
|
|
bplcon4_saved = bplcon4;
|
|
fmode_saved = fmode;
|
|
beamcon0_saved = new_beamcon0;
|
|
bplcon0d = bplcon0;
|
|
bplcon0d_old = 0;
|
|
bitplane_dma_change(dmacon);
|
|
vdiw_change(vdiwstate == diw_states::DIW_waiting_stop);
|
|
|
|
intreq2 = intreq;
|
|
intena2 = intena;
|
|
|
|
current_colors.extra = 0;
|
|
if (isbrdblank(-1, bplcon0, bplcon3)) {
|
|
current_colors.extra |= 1 << CE_BORDERBLANK;
|
|
}
|
|
if (issprbrd(-1, bplcon0, bplcon3)) {
|
|
current_colors.extra |= 1 << CE_BORDERSPRITE;
|
|
}
|
|
if (ecs_denise && (bplcon0 & 1) && (bplcon3 & 0x10)) {
|
|
current_colors.extra |= 1 << CE_BORDERNTRANS;
|
|
}
|
|
setextblank();
|
|
|
|
lof_prev_lastline = lof_lastline = lof_store != 0;
|
|
|
|
DISK_restore_custom(dskpt, dsklen, dskbytr);
|
|
|
|
return src;
|
|
}
|
|
|
|
#endif /* SAVESTATE */
|
|
|
|
#if defined SAVESTATE || defined DEBUGGER
|
|
|
|
#define SB save_u8
|
|
#define SW save_u16
|
|
#define SL save_u32
|
|
|
|
uae_u8 *save_custom(size_t *len, uae_u8 *dstptr, int full)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
int i, dummy;
|
|
uae_u32 dskpt;
|
|
uae_u16 dsklen, dsksync, dskbytr;
|
|
uae_u16 v;
|
|
|
|
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(refptr); /* 028 REFPTR */
|
|
SW((lof_store ? 0x8001 : 0) | (lol ? 0x0080 : 0));/* 02A VPOSW */
|
|
SW(0); /* 02C VHPOSW */
|
|
SW(copcon); /* 02E COPCON */
|
|
#ifdef SERIAL_PORT
|
|
SW(serdat); /* 030 SERDAT * */
|
|
SW(serper); /* 032 SERPER * */
|
|
#else
|
|
SW(0); /* 030 SERDAT * */
|
|
SW(0); /* 032 SERPER * */
|
|
#endif
|
|
SW(potgo_value); /* 034 POTGO */
|
|
SW(0); /* 036 JOYTEST * */
|
|
SW(0); /* 038 STREQU */
|
|
SW(0); /* 03A STRVBL */
|
|
SW(0); /* 03C STRHOR */
|
|
SW(0); /* 03E STRLONG */
|
|
SW(bltcon0); /* 040 BLTCON0 */
|
|
SW(bltcon1); /* 042 BLTCON1 */
|
|
SW(blt_info.bltafwm); /* 044 BLTAFWM */
|
|
SW(blt_info.bltalwm); /* 046 BLTALWM */
|
|
SL(bltcpt); /* 048-04B BLTCPT */
|
|
SL(bltbpt); /* 04C-04F BLTCPT */
|
|
SL(bltapt); /* 050-053 BLTCPT */
|
|
SL(bltdpt); /* 054-057 BLTCPT */
|
|
if (blt_info.vblitsize > 1024 || blt_info.hblitsize > 64) {
|
|
v = 0;
|
|
} else {
|
|
v = (blt_info.vblitsize << 6) | (blt_info.hblitsize & 63);
|
|
}
|
|
SW(v); /* 058 BLTSIZE */
|
|
SW(bltcon0 & 0xff); /* 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 ? */
|
|
SW(0); /* 08A ? */
|
|
SW(0); /* 08C ? */
|
|
SW(diwstrt); /* 08E DIWSTRT */
|
|
SW(diwstop); /* 090 DIWSTOP */
|
|
SW(ddfstrt); /* 092 DDFSTRT */
|
|
SW(ddfstop); /* 094 DDFSTOP */
|
|
SW(dmacon); /* 096 DMACON */
|
|
SW(clxcon); /* 098 CLXCON */
|
|
SW(intena); /* 09A INTENA */
|
|
SW(intreq); /* 09C INTREQ */
|
|
SW(adkcon); /* 09E ADKCON */
|
|
for (i = 0; full && i < 32; i++)
|
|
SW(0);
|
|
for (i = 0; i < 8; i++)
|
|
SL(bplpt[i]); /* 0E0-0FE BPLxPT */
|
|
SW(bplcon0); /* 100 BPLCON0 */
|
|
SW(bplcon1); /* 102 BPLCON1 */
|
|
SW(bplcon2); /* 104 BPLCON2 */
|
|
SW(bplcon3); /* 106 BPLCON3 */
|
|
SW(bpl1mod); /* 108 BPL1MOD */
|
|
SW(bpl2mod); /* 10A BPL2MOD */
|
|
SW(bplcon4); /* 10C BPLCON4 */
|
|
SW(clxcon2); /* 10E CLXCON2 */
|
|
for (i = 0;i < 8; i++)
|
|
SW (fetched[i]); /* 110 BPLxDAT */
|
|
if (full) {
|
|
for (i = 0; i < 8; i++) {
|
|
SL (spr[i].pt); /* 120-13E SPRxPT */
|
|
}
|
|
for (i = 0; i < 8; i++) {
|
|
struct sprite *s = &spr[i];
|
|
SW(s->pos); /* 1x0 SPRxPOS */
|
|
SW(s->ctl); /* 1x2 SPRxPOS */
|
|
SW(s->data[0]); /* 1x4 SPRxDATA */
|
|
SW(s->datb[0]); /* 1x6 SPRxDATB */
|
|
}
|
|
}
|
|
for (i = 0; i < 32; i++) {
|
|
if (aga_mode) {
|
|
uae_u32 v = current_colors.color_regs_aga[i];
|
|
uae_u16 v2;
|
|
v &= 0x00f0f0f0;
|
|
v2 = (v >> 4) & 15;
|
|
v2 |= ((v >> 12) & 15) << 4;
|
|
v2 |= ((v >> 20) & 15) << 8;
|
|
SW(v2);
|
|
} else {
|
|
uae_u16 v = current_colors.color_regs_ecs[i];
|
|
if (color_regs_genlock[i])
|
|
v |= 0x8000;
|
|
SW(v); /* 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(sprhstrt); /* 1D0 SPRHSTRT */
|
|
SW(sprhstop); /* 1D2 SPRHSTOP */
|
|
SW(bplhstrt); /* 1D4 BPLHSTRT */
|
|
SW(bplhstop); /* 1D6 BPLHSTOP */
|
|
SW(hhpos); /* 1D8 HHPOSW */
|
|
SW(HHPOSR()); /* 1DA HHPOSR */
|
|
SW(new_beamcon0); /* 1DC BEAMCON0 */
|
|
SW(hsstrt); /* 1DE HSSTRT */
|
|
SW(vsstrt); /* 1E0 VSSTRT */
|
|
SW(hcenter); /* 1E2 HCENTER */
|
|
SW(diwhigh | (diwhigh_written ? 0x8000 : 0) | (hdiwstate == diw_states::DIW_waiting_stop ? 0x4000 : 0) | (vdiwstate == diw_states::DIW_waiting_start ? 0x0080 : 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_value); /* 1FE */
|
|
SL(refptr);
|
|
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
#endif /* SAVESTATE || DEBUGGER */
|
|
|
|
#ifdef SAVESTATE
|
|
|
|
uae_u8 *restore_custom_agacolors(uae_u8 *src)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
#ifdef AGA
|
|
uae_u32 v = RL;
|
|
color_regs_genlock[i] = 0;
|
|
if (v & 0x80000000)
|
|
color_regs_genlock[i] = 1;
|
|
v &= 0x80ffffff;
|
|
current_colors.color_regs_aga[i] = v;
|
|
#else
|
|
RL;
|
|
#endif
|
|
}
|
|
return src;
|
|
}
|
|
|
|
uae_u8 *save_custom_agacolors(size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
|
|
if (!aga_mode) {
|
|
int i;
|
|
for (i = 0; i < 256; i++) {
|
|
if (current_colors.color_regs_aga[i] || color_regs_genlock[i])
|
|
break;
|
|
}
|
|
if (i == 256)
|
|
return NULL;
|
|
}
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc (uae_u8, 256 * 4);
|
|
for (int i = 0; i < 256; i++)
|
|
#ifdef AGA
|
|
SL (current_colors.color_regs_aga[i] | (color_regs_genlock[i] ? 0x80000000 : 0));
|
|
#else
|
|
SL (0);
|
|
#endif
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_custom_sprite(int num, uae_u8 *src)
|
|
{
|
|
struct sprite *s = &spr[num];
|
|
memset (s, 0, sizeof (struct sprite));
|
|
s->pt = RL; /* 120-13E SPRxPT */
|
|
s->pos = RW; /* 1x0 SPRxPOS */
|
|
s->ctl = RW; /* 1x2 SPRxPOS */
|
|
s->data[0] = RW; /* 1x4 SPRxDATA */
|
|
s->datb[0] = RW; /* 1x6 SPRxDATB */
|
|
s->data[1] = RW;
|
|
s->datb[1] = RW;
|
|
s->data[2] = RW;
|
|
s->datb[2] = RW;
|
|
s->data[3] = RW;
|
|
s->datb[3] = RW;
|
|
s->armed = RB & 1;
|
|
return src;
|
|
}
|
|
|
|
uae_u8 *save_custom_sprite(int num, size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
struct sprite *s = &spr[num];
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc(uae_u8, 30);
|
|
SL(s->pt); /* 120-13E SPRxPT */
|
|
SW(s->pos); /* 1x0 SPRxPOS */
|
|
SW(s->ctl); /* 1x2 SPRxPOS */
|
|
SW(s->data[0]); /* 1x4 SPRxDATA */
|
|
SW(s->datb[0]); /* 1x6 SPRxDATB */
|
|
SW(s->data[1]);
|
|
SW(s->datb[1]);
|
|
SW(s->data[2]);
|
|
SW(s->datb[2]);
|
|
SW(s->data[3]);
|
|
SW(s->datb[3]);
|
|
SB(s->armed ? 1 : 0);
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_custom_extra(uae_u8 *src)
|
|
{
|
|
uae_u32 v = restore_u32();
|
|
uae_u8 tmp = 0;
|
|
|
|
if (!(v & 1))
|
|
v = 0;
|
|
currprefs.cs_compatible = changed_prefs.cs_compatible = v >> 24;
|
|
cia_set_overlay((v & 2) != 0);
|
|
|
|
currprefs.genlock = changed_prefs.genlock = RBB;
|
|
currprefs.cs_rtc = changed_prefs.cs_rtc = RB;
|
|
RL; // currprefs.cs_rtc_adjust = changed_prefs.cs_rtc_adjust = RL;
|
|
|
|
currprefs.cs_a1000ram = changed_prefs.cs_a1000ram = RBB;
|
|
|
|
//currprefs.a2091rom.enabled = changed_prefs.a2091rom.enabled = RBB;
|
|
//currprefs.a4091rom.enabled = changed_prefs.a4091rom.enabled = RBB;
|
|
tmp = RBB;
|
|
tmp = RBB;
|
|
tmp = RBB;
|
|
|
|
currprefs.cs_pcmcia = changed_prefs.cs_pcmcia = RBB;
|
|
currprefs.cs_ciaatod = changed_prefs.cs_ciaatod = RB;
|
|
currprefs.cs_ciaoverlay = changed_prefs.cs_ciaoverlay = RBB;
|
|
|
|
tmp = RBB;
|
|
tmp = RBB;
|
|
|
|
currprefs.cs_agnusrev = changed_prefs.cs_agnusrev = SRB;
|
|
currprefs.cs_deniserev = changed_prefs.cs_deniserev = SRB;
|
|
currprefs.cs_fatgaryrev = changed_prefs.cs_fatgaryrev = SRB;
|
|
currprefs.cs_ramseyrev = changed_prefs.cs_ramseyrev = SRB;
|
|
|
|
currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p = RBB;
|
|
currprefs.cs_cd32cd = changed_prefs.cs_cd32cd = RBB;
|
|
currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram = RBB;
|
|
currprefs.cs_cdtvcd = changed_prefs.cs_cdtvcd = RBB;
|
|
currprefs.cs_cdtvram = changed_prefs.cs_cdtvram = RBB;
|
|
RB;
|
|
|
|
currprefs.cs_df0idhw = changed_prefs.cs_df0idhw = RBB;
|
|
tmp = RBB;
|
|
currprefs.cs_ide = changed_prefs.cs_ide = RB;
|
|
currprefs.cs_mbdmac = changed_prefs.cs_mbdmac = RB;
|
|
currprefs.cs_ksmirror_a8 = changed_prefs.cs_ksmirror_a8 = RBB;
|
|
currprefs.cs_ksmirror_e0 = changed_prefs.cs_ksmirror_e0 = RBB;
|
|
currprefs.cs_resetwarning = changed_prefs.cs_resetwarning = RBB;
|
|
currprefs.cs_z3autoconfig = changed_prefs.cs_z3autoconfig = RBB;
|
|
currprefs.cs_1mchipjumper = changed_prefs.cs_1mchipjumper = RBB;
|
|
currprefs.cs_bytecustomwritebug = changed_prefs.cs_bytecustomwritebug = RBB;
|
|
currprefs.cs_color_burst = changed_prefs.cs_color_burst = RBB;
|
|
currprefs.cs_toshibagary = changed_prefs.cs_toshibagary = RBB;
|
|
currprefs.cs_romisslow = changed_prefs.cs_romisslow = RBB;
|
|
|
|
currprefs.cs_ciatype[0] = changed_prefs.cs_ciatype[0] = RBB;
|
|
currprefs.cs_ciatype[1] = changed_prefs.cs_ciatype[1] = RBB;
|
|
|
|
currprefs.cs_memorypatternfill = changed_prefs.cs_memorypatternfill = RBB;
|
|
|
|
currprefs.cs_agnusmodel = changed_prefs.cs_agnusmodel = RBB;
|
|
currprefs.cs_agnussize = changed_prefs.cs_agnussize = RBB;
|
|
currprefs.cs_denisemodel = changed_prefs.cs_denisemodel = RBB;
|
|
|
|
return src;
|
|
}
|
|
|
|
uae_u8 *save_custom_extra(size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc(uae_u8, 1000);
|
|
|
|
SL((currprefs.cs_compatible << 24) | (get_mem_bank_real(0) != &chipmem_bank ? 2 : 0) | 1);
|
|
SB(currprefs.genlock ? 1 : 0);
|
|
SB(currprefs.cs_rtc);
|
|
SL(currprefs.cs_rtc_adjust);
|
|
|
|
SB(currprefs.cs_a1000ram ? 1 : 0);
|
|
|
|
SB(is_board_enabled(&currprefs, ROMTYPE_A2091, 0) ? 1 : 0);
|
|
SB(is_board_enabled(&currprefs, ROMTYPE_A4091, 0) ? 1 : 0);
|
|
SB(0);
|
|
|
|
SB(currprefs.cs_pcmcia ? 1 : 0);
|
|
SB(currprefs.cs_ciaatod);
|
|
SB(currprefs.cs_ciaoverlay ? 1 : 0);
|
|
|
|
SB(agnusa1000 ? 1 : 0);
|
|
SB(denisea1000_noehb ? 1 : 0);
|
|
|
|
SB(currprefs.cs_agnusrev);
|
|
SB(currprefs.cs_deniserev);
|
|
SB(currprefs.cs_fatgaryrev);
|
|
SB(currprefs.cs_ramseyrev);
|
|
|
|
SB(currprefs.cs_cd32c2p);
|
|
SB(currprefs.cs_cd32cd);
|
|
SB(currprefs.cs_cd32nvram);
|
|
SB(currprefs.cs_cdtvcd ? 1 : 0);
|
|
SB(currprefs.cs_cdtvram ? 1 : 0);
|
|
SB(0);
|
|
|
|
SB(currprefs.cs_df0idhw ? 1 : 0);
|
|
SB(agnusa1000 ? 1 : 0);
|
|
SB(currprefs.cs_ide);
|
|
SB(currprefs.cs_mbdmac);
|
|
SB(currprefs.cs_ksmirror_a8 ? 1 : 0);
|
|
SB(currprefs.cs_ksmirror_e0 ? 1 : 0);
|
|
SB(currprefs.cs_resetwarning ? 1 : 0);
|
|
SB(currprefs.cs_z3autoconfig ? 1 : 0);
|
|
SB(currprefs.cs_1mchipjumper ? 1 : 0);
|
|
|
|
SB(currprefs.cs_bytecustomwritebug ? 1 : 0);
|
|
SB(currprefs.cs_color_burst ? 1 : 0);
|
|
SB(currprefs.cs_toshibagary ? 1 : 0);
|
|
SB(currprefs.cs_romisslow ? 1 : 0);
|
|
|
|
SB(currprefs.cs_ciatype[0]);
|
|
SB(currprefs.cs_ciatype[1]);
|
|
|
|
SB(currprefs.cs_memorypatternfill);
|
|
|
|
SB(currprefs.cs_agnusmodel);
|
|
SB(currprefs.cs_agnussize);
|
|
SB(currprefs.cs_denisemodel);
|
|
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *save_custom_slots(size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc(uae_u8, 1000);
|
|
|
|
uae_u32 v = 1;
|
|
// copper final MOVE pending?
|
|
if (cop_state.state == COP_read1) {
|
|
v |= 2;
|
|
} else if (cop_state.state == COP_read2) {
|
|
v |= 4;
|
|
}
|
|
save_u32(v);
|
|
save_u32(cop_state.ip);
|
|
save_u16(cop_state.ir[0]);
|
|
save_u16(cop_state.ir[1]);
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
save_u16(cycle_line_pipe[i]);
|
|
save_u16(cycle_line_slot[i]);
|
|
}
|
|
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_custom_slots(uae_u8 *src)
|
|
{
|
|
uae_u32 v = restore_u32();
|
|
if (!(v & 1))
|
|
return src;
|
|
|
|
cop_state.ip = restore_u32();
|
|
cop_state.ir[0] = restore_u16();
|
|
cop_state.ir[1] = restore_u16();
|
|
cop_state.state = COP_stop;
|
|
if (v & 2) {
|
|
cop_state.state = COP_read1;
|
|
} else if (v & 4) {
|
|
cop_state.state = COP_read2;
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
cycle_line_pipe[i] = restore_u16();
|
|
cycle_line_slot[i] = (uae_u8)restore_u16();
|
|
}
|
|
|
|
return src;
|
|
}
|
|
|
|
uae_u8 *restore_custom_event_delay(uae_u8 *src)
|
|
{
|
|
if (restore_u32() != 1)
|
|
return src;
|
|
int cnt = restore_u8();
|
|
for (int i = 0; i < cnt; i++) {
|
|
uae_u8 type = restore_u8();
|
|
evt_t e = restore_u64();
|
|
uae_u32 data = restore_u32();
|
|
evfunc2 f = NULL;
|
|
switch(type)
|
|
{
|
|
case 1:
|
|
f = event_send_interrupt_do_ext;
|
|
break;
|
|
case 2:
|
|
f = event_doint_delay_do_ext;
|
|
break;
|
|
case 3:
|
|
f = event_audxdat_func;
|
|
break;
|
|
case 4:
|
|
f = event_setdsr;
|
|
break;
|
|
case 5:
|
|
f = event_CIA_synced_interrupt;
|
|
break;
|
|
case 6:
|
|
f = event_doint_delay_do_intreq;
|
|
break;
|
|
case 7:
|
|
f = event_doint_delay_do_intena;
|
|
break;
|
|
case 8:
|
|
f = event_CIA_tod_inc_event;
|
|
break;
|
|
case 9:
|
|
f = event_DISK_handler;
|
|
break;
|
|
case 10:
|
|
f = bitplane_dma_change;
|
|
break;
|
|
case 0:
|
|
write_log("ignored event type %d (%08x) restored\n", type, data);
|
|
break;
|
|
default:
|
|
write_log("unknown event type %d (%08x) restored\n", type, data);
|
|
break;
|
|
}
|
|
if (f) {
|
|
event2_newevent_xx(-1, e, data, f);
|
|
}
|
|
}
|
|
return src;
|
|
}
|
|
uae_u8 *save_custom_event_delay(size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
int cnt = 0, cnt2 = 0;
|
|
|
|
for (int i = ev2_misc; i < ev2_max; i++) {
|
|
struct ev2 *e = &eventtab2[i];
|
|
if (e->active) {
|
|
cnt++;
|
|
}
|
|
}
|
|
if (cnt == 0)
|
|
return NULL;
|
|
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc(uae_u8, 1000);
|
|
|
|
save_u32(1);
|
|
save_u8(cnt);
|
|
for (int i = ev2_misc; i < ev2_max; i++) {
|
|
struct ev2 *e = &eventtab2[i];
|
|
if (e->active) {
|
|
evfunc2 f = e->handler;
|
|
uae_u8 type = 0;
|
|
if (f == event_send_interrupt_do_ext) {
|
|
type = 1;
|
|
} else if (f == event_doint_delay_do_ext) {
|
|
type = 2;
|
|
} else if (f == event_audxdat_func) {
|
|
type = 3;
|
|
} else if (f == event_setdsr) {
|
|
type = 4;
|
|
} else if (f == event_CIA_synced_interrupt) {
|
|
type = 5;
|
|
} else if (f == event_doint_delay_do_intreq) {
|
|
type = 6;
|
|
} else if (f == event_doint_delay_do_intena) {
|
|
type = 7;
|
|
} else if (f == event_CIA_tod_inc_event) {
|
|
type = 8;
|
|
} else if (f == event_DISK_handler) {
|
|
type = 9;
|
|
} else if (f == bitplane_dma_change) {
|
|
type = 10;
|
|
} else {
|
|
write_log("unknown event2 handler %p\n", e->handler);
|
|
e->active = false;
|
|
f(e->data);
|
|
}
|
|
if (type) {
|
|
cnt2++;
|
|
}
|
|
save_u8(type);
|
|
save_u64(e->evtime - get_cycles());
|
|
save_u32(e->data);
|
|
}
|
|
}
|
|
write_log("%d pending events saved\n", cnt2);
|
|
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
|
|
uae_u8 *save_cycles(size_t *len, uae_u8 *dstptr)
|
|
{
|
|
uae_u8 *dstbak, *dst;
|
|
if (dstptr)
|
|
dstbak = dst = dstptr;
|
|
else
|
|
dstbak = dst = xmalloc(uae_u8, 1000);
|
|
save_u32(1);
|
|
save_u32(CYCLE_UNIT);
|
|
save_u64(get_cycles());
|
|
save_u32(extra_cycle);
|
|
*len = dst - dstbak;
|
|
return dstbak;
|
|
}
|
|
|
|
uae_u8 *restore_cycles(uae_u8 *src)
|
|
{
|
|
if (restore_u32() != 1)
|
|
return src;
|
|
restore_u32();
|
|
start_cycles = restore_u64();
|
|
extra_cycle = restore_u32();
|
|
if (extra_cycle >= 2 * CYCLE_UNIT)
|
|
extra_cycle = 0;
|
|
set_cycles(start_cycles);
|
|
clear_events();
|
|
return src;
|
|
}
|
|
|
|
#endif /* SAVESTATE */
|
|
|
|
void check_prefs_changed_custom(void)
|
|
{
|
|
bool syncchange = false;
|
|
|
|
if (!config_changed)
|
|
return;
|
|
currprefs.gfx_framerate = changed_prefs.gfx_framerate;
|
|
if (currprefs.turbo_emulation_limit != changed_prefs.turbo_emulation_limit) {
|
|
currprefs.turbo_emulation_limit = changed_prefs.turbo_emulation_limit;
|
|
if (changed_prefs.turbo_emulation) {
|
|
warpmode(changed_prefs.turbo_emulation);
|
|
}
|
|
}
|
|
if (currprefs.turbo_emulation != changed_prefs.turbo_emulation)
|
|
warpmode(changed_prefs.turbo_emulation);
|
|
if (inputdevice_config_change_test())
|
|
inputdevice_copyconfig (&changed_prefs, &currprefs);
|
|
currprefs.immediate_blits = changed_prefs.immediate_blits;
|
|
currprefs.waiting_blits = changed_prefs.waiting_blits;
|
|
currprefs.blitter_speed_throttle = changed_prefs.blitter_speed_throttle;
|
|
currprefs.collision_level = changed_prefs.collision_level;
|
|
if (!currprefs.keyboard_connected && changed_prefs.keyboard_connected) {
|
|
// send powerup sync
|
|
keyboard_connected(true);
|
|
} else if (currprefs.keyboard_connected && !changed_prefs.keyboard_connected) {
|
|
keyboard_connected(false);
|
|
}
|
|
currprefs.keyboard_connected = changed_prefs.keyboard_connected;
|
|
|
|
currprefs.cs_ciaatod = changed_prefs.cs_ciaatod;
|
|
currprefs.cs_rtc = changed_prefs.cs_rtc;
|
|
currprefs.cs_cd32cd = changed_prefs.cs_cd32cd;
|
|
currprefs.cs_cd32c2p = changed_prefs.cs_cd32c2p;
|
|
currprefs.cs_cd32nvram = changed_prefs.cs_cd32nvram;
|
|
currprefs.cs_cdtvcd = changed_prefs.cs_cdtvcd;
|
|
currprefs.cs_ide = changed_prefs.cs_ide;
|
|
currprefs.cs_pcmcia = changed_prefs.cs_pcmcia;
|
|
currprefs.cs_fatgaryrev = changed_prefs.cs_fatgaryrev;
|
|
currprefs.cs_ramseyrev = changed_prefs.cs_ramseyrev;
|
|
currprefs.cs_agnusrev = changed_prefs.cs_agnusrev;
|
|
currprefs.cs_deniserev = changed_prefs.cs_deniserev;
|
|
currprefs.cs_mbdmac = changed_prefs.cs_mbdmac;
|
|
currprefs.cs_df0idhw = changed_prefs.cs_df0idhw;
|
|
currprefs.cs_agnussize = changed_prefs.cs_agnussize;
|
|
currprefs.cs_z3autoconfig = changed_prefs.cs_z3autoconfig;
|
|
currprefs.cs_bytecustomwritebug = changed_prefs.cs_bytecustomwritebug;
|
|
currprefs.cs_color_burst = changed_prefs.cs_color_burst;
|
|
currprefs.cs_romisslow = changed_prefs.cs_romisslow;
|
|
currprefs.cs_toshibagary = changed_prefs.cs_toshibagary;
|
|
currprefs.cs_unmapped_space = changed_prefs.cs_unmapped_space;
|
|
currprefs.cs_eclockphase = changed_prefs.cs_eclockphase;
|
|
currprefs.cs_eclocksync = changed_prefs.cs_eclocksync;
|
|
currprefs.cs_ciatype[0] = changed_prefs.cs_ciatype[0];
|
|
currprefs.cs_ciatype[1] = changed_prefs.cs_ciatype[1];
|
|
currprefs.cs_memorypatternfill = changed_prefs.cs_memorypatternfill;
|
|
|
|
if (currprefs.chipset_mask != changed_prefs.chipset_mask ||
|
|
currprefs.picasso96_nocustom != changed_prefs.picasso96_nocustom ||
|
|
currprefs.ntscmode != changed_prefs.ntscmode ||
|
|
currprefs.cs_agnusmodel != changed_prefs.cs_agnusmodel ||
|
|
currprefs.cs_denisemodel != changed_prefs.cs_denisemodel ||
|
|
currprefs.cs_hvcsync != changed_prefs.cs_hvcsync
|
|
) {
|
|
currprefs.picasso96_nocustom = changed_prefs.picasso96_nocustom;
|
|
if (currprefs.ntscmode != changed_prefs.ntscmode) {
|
|
currprefs.ntscmode = changed_prefs.ntscmode;
|
|
new_beamcon0 = currprefs.ntscmode ? 0x00 : BEAMCON0_PAL;
|
|
}
|
|
if ((changed_prefs.chipset_mask & CSMASK_ECS_AGNUS) && !(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) {
|
|
new_beamcon0 = beamcon0_saved;
|
|
} else if (!(changed_prefs.chipset_mask & CSMASK_ECS_AGNUS) && (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) {
|
|
beamcon0_saved = beamcon0;
|
|
beamcon0 = new_beamcon0 = currprefs.ntscmode ? 0x00 : BEAMCON0_PAL;
|
|
diwhigh = 0;
|
|
diwhigh_written = 0;
|
|
bplcon0 &= ~(0x10 | 0x01);
|
|
}
|
|
if (currprefs.cs_hvcsync != changed_prefs.cs_hvcsync) {
|
|
syncchange = true;
|
|
nosignal_trigger = false;
|
|
nosignal_status = 0;
|
|
}
|
|
currprefs.chipset_mask = changed_prefs.chipset_mask;
|
|
currprefs.cs_agnusmodel = changed_prefs.cs_agnusmodel;
|
|
currprefs.cs_denisemodel = changed_prefs.cs_denisemodel;
|
|
currprefs.cs_hvcsync = changed_prefs.cs_hvcsync;
|
|
init_custom();
|
|
}
|
|
|
|
if (currprefs.chipset_hr != changed_prefs.chipset_hr) {
|
|
currprefs.chipset_hr = changed_prefs.chipset_hr;
|
|
init_custom();
|
|
}
|
|
if (syncchange) {
|
|
varsync_changed = 2;
|
|
}
|
|
|
|
#ifdef GFXFILTER
|
|
for (int i = 0; i < 2; i++) {
|
|
int idx = i == 0 ? 0 : 2;
|
|
struct gfx_filterdata *fd = &currprefs.gf[idx];
|
|
struct gfx_filterdata *fdcp = &changed_prefs.gf[idx];
|
|
|
|
fd->gfx_filter_horiz_zoom = fdcp->gfx_filter_horiz_zoom;
|
|
fd->gfx_filter_vert_zoom = fdcp->gfx_filter_vert_zoom;
|
|
fd->gfx_filter_horiz_offset = fdcp->gfx_filter_horiz_offset;
|
|
fd->gfx_filter_vert_offset = fdcp->gfx_filter_vert_offset;
|
|
fd->gfx_filter_scanlines = fdcp->gfx_filter_scanlines;
|
|
|
|
fd->gfx_filter_left_border = fdcp->gfx_filter_left_border;
|
|
fd->gfx_filter_right_border = fdcp->gfx_filter_right_border;
|
|
fd->gfx_filter_top_border = fdcp->gfx_filter_top_border;
|
|
fd->gfx_filter_bottom_border = fdcp->gfx_filter_bottom_border;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef CPUEMU_13
|
|
|
|
STATIC_INLINE void decide_fetch_ce(int hpos)
|
|
{
|
|
if ((line_cyclebased || blt_info.blitter_dangerous_bpl) && vpos < current_maxvpos()) {
|
|
decide_bpl_fetch(hpos);
|
|
}
|
|
}
|
|
|
|
// blitter not in nasty mode = CPU gets one cycle if it has been waiting
|
|
// at least 4 cycles (all DMA cycles count, not just blitter cycles, even
|
|
// blitter idle cycles do count!)
|
|
|
|
extern int cpu_tracer;
|
|
static int dma_cycle(int *mode, int *ipl)
|
|
{
|
|
int hpos_next, hpos_old;
|
|
|
|
blt_info.nasty_cnt = 1;
|
|
blt_info.wait_nasty = 0;
|
|
if (cpu_tracer < 0) {
|
|
return current_hpos_safe();
|
|
}
|
|
if (!currprefs.cpu_memory_cycle_exact) {
|
|
return current_hpos_safe();
|
|
}
|
|
while (currprefs.cpu_memory_cycle_exact) {
|
|
hpos_old = current_hpos_safe();
|
|
hpos_next = hpos_old + 1;
|
|
decide_line(hpos_next);
|
|
sync_copper(hpos_next);
|
|
decide_fetch_ce(hpos_next);
|
|
int bpldma = bitplane_dma_access(hpos_old, 0);
|
|
if (blt_info.blit_queued) {
|
|
decide_blitter(hpos_next);
|
|
// copper may have been waiting for the blitter
|
|
sync_copper(hpos_next);
|
|
}
|
|
if ((cycle_line_slot[hpos_old] & CYCLE_MASK) == 0 && !bpldma) {
|
|
alloc_cycle(hpos_old, CYCLE_CPU);
|
|
break;
|
|
}
|
|
if (blt_info.nasty_cnt > 0) {
|
|
blt_info.nasty_cnt++;
|
|
}
|
|
*ipl = regs.ipl_pin;
|
|
do_cycles(1 * CYCLE_UNIT);
|
|
/* bus was allocated to dma channel, wait for next cycle.. */
|
|
}
|
|
blt_info.nasty_cnt = 0;
|
|
return hpos_old;
|
|
}
|
|
|
|
static void sync_cycles(void)
|
|
{
|
|
if (extra_cycle) {
|
|
do_cycles(extra_cycle);
|
|
extra_cycle = 0;
|
|
}
|
|
evt_t c = get_cycles();
|
|
int extra = c & (CYCLE_UNIT - 1);
|
|
if (extra) {
|
|
extra = CYCLE_UNIT - extra;
|
|
do_cycles(extra);
|
|
}
|
|
}
|
|
|
|
void do_copper(void)
|
|
{
|
|
sync_cycles();
|
|
int hpos = current_hpos();
|
|
update_copper(hpos);
|
|
}
|
|
|
|
uae_u32 wait_cpu_cycle_read(uaecptr addr, int mode)
|
|
{
|
|
uae_u32 v = 0;
|
|
int hpos;
|
|
int ipl = regs.ipl[0];
|
|
evt_t now = get_cycles();
|
|
|
|
sync_cycles();
|
|
|
|
x_do_cycles_pre(CYCLE_UNIT);
|
|
|
|
hpos = dma_cycle(&mode, &ipl);
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
int reg = 0x1000;
|
|
if (mode == -3) {
|
|
reg |= 2;
|
|
v = regs.chipset_latch_rw;
|
|
} else if (mode < 0) {
|
|
reg |= 4;
|
|
} else if (mode > 0) {
|
|
reg |= 2;
|
|
} else {
|
|
reg |= 1;
|
|
}
|
|
record_dma_read(reg, addr, hpos, vpos, DMARECORD_CPU, mode == -2 || mode == 2 ? 0 : 1);
|
|
}
|
|
peekdma_data.mask = 0;
|
|
#endif
|
|
|
|
switch(mode)
|
|
{
|
|
case -1:
|
|
v = get_long(addr);
|
|
break;
|
|
case -2:
|
|
v = get_longi(addr);
|
|
break;
|
|
case 1:
|
|
v = get_word(addr);
|
|
break;
|
|
case 2:
|
|
v = get_wordi(addr);
|
|
break;
|
|
case 0:
|
|
v = get_byte(addr);
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value_pos(v, hpos, vpos);
|
|
}
|
|
#endif
|
|
|
|
regs.chipset_latch_rw = regs.chipset_latch_read = v;
|
|
|
|
x_do_cycles_post(CYCLE_UNIT, v);
|
|
|
|
// if IPL fetch was pending and CPU had wait states
|
|
// Use ipl_pin value from previous cycle
|
|
if (now == regs.ipl_evt) {
|
|
regs.ipl[0] = ipl;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
void wait_cpu_cycle_write(uaecptr addr, int mode, uae_u32 v)
|
|
{
|
|
int hpos;
|
|
int ipl = regs.ipl[0];
|
|
evt_t now = get_cycles();
|
|
|
|
sync_cycles();
|
|
|
|
x_do_cycles_pre(CYCLE_UNIT);
|
|
|
|
hpos = dma_cycle(&mode, &ipl);
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
int reg = 0x1100;
|
|
if (mode == -3) {
|
|
reg |= 2;
|
|
} else if (mode < 0) {
|
|
reg |= 4;
|
|
} else if (mode > 0) {
|
|
reg |= 2;
|
|
} else {
|
|
reg |= 1;
|
|
}
|
|
record_dma_write(reg, v, addr, hpos, vpos, DMARECORD_CPU, 1);
|
|
}
|
|
peekdma_data.mask = 0;
|
|
#endif
|
|
|
|
if (mode > -2) {
|
|
if (mode < 0) {
|
|
put_long(addr, v);
|
|
} else if (mode > 0) {
|
|
put_word(addr, v);
|
|
} else if (mode == 0) {
|
|
put_byte(addr, v);
|
|
}
|
|
}
|
|
|
|
regs.chipset_latch_rw = regs.chipset_latch_write = v;
|
|
|
|
x_do_cycles_post(CYCLE_UNIT, v);
|
|
|
|
// if IPL fetch was pending and CPU had wait states:
|
|
// Use ipl_pin value from previous cycle
|
|
if (now == regs.ipl_evt) {
|
|
regs.ipl[0] = ipl;
|
|
}
|
|
}
|
|
|
|
|
|
uae_u32 wait_cpu_cycle_read_ce020(uaecptr addr, int mode)
|
|
{
|
|
uae_u32 v = 0;
|
|
int hpos, ipl;
|
|
|
|
sync_cycles();
|
|
|
|
x_do_cycles_pre(CYCLE_UNIT);
|
|
|
|
hpos = dma_cycle(NULL, &ipl);
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
int reg = 0x1000;
|
|
if (mode < 0) {
|
|
reg |= 4;
|
|
} else if (mode > 0) {
|
|
reg |= 2;
|
|
} else {
|
|
reg |= 1;
|
|
}
|
|
record_dma_read(reg, addr, hpos, vpos, DMARECORD_CPU, mode == -2 || mode == 2 ? 0 : 1);
|
|
}
|
|
peekdma_data.mask = 0;
|
|
#endif
|
|
|
|
switch (mode) {
|
|
case -1:
|
|
v = get_long(addr);
|
|
break;
|
|
case -2:
|
|
v = get_longi(addr);
|
|
break;
|
|
case 1:
|
|
v = get_word(addr);
|
|
break;
|
|
case 2:
|
|
v = get_wordi(addr);
|
|
break;
|
|
case 0:
|
|
v = get_byte(addr);
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
record_dma_read_value_pos(v, hpos, vpos);
|
|
}
|
|
#endif
|
|
|
|
regs.chipset_latch_rw = regs.chipset_latch_read = v;
|
|
|
|
x_do_cycles_post(CYCLE_UNIT, v);
|
|
|
|
return v;
|
|
}
|
|
|
|
void wait_cpu_cycle_write_ce020(uaecptr addr, int mode, uae_u32 v)
|
|
{
|
|
int hpos, ipl;
|
|
|
|
sync_cycles();
|
|
|
|
x_do_cycles_pre(CYCLE_UNIT);
|
|
|
|
hpos = dma_cycle(NULL, &ipl);
|
|
|
|
#ifdef DEBUGGER
|
|
if (debug_dma) {
|
|
int reg = 0x1100;
|
|
if (mode < 0)
|
|
reg |= 4;
|
|
else if (mode > 0)
|
|
reg |= 2;
|
|
else
|
|
reg |= 1;
|
|
record_dma_write(reg, v, addr, hpos, vpos, DMARECORD_CPU, 1);
|
|
}
|
|
peekdma_data.mask = 0;
|
|
#endif
|
|
|
|
if (mode < 0) {
|
|
put_long(addr, v);
|
|
} else if (mode > 0) {
|
|
put_word(addr, v);
|
|
} else if (mode == 0) {
|
|
put_byte(addr, v);
|
|
}
|
|
|
|
regs.chipset_latch_rw = regs.chipset_latch_write = v;
|
|
|
|
x_do_cycles_post(CYCLE_UNIT, v);
|
|
}
|
|
|
|
void do_cycles_ce(int cycles)
|
|
{
|
|
cycles += extra_cycle;
|
|
while (cycles >= CYCLE_UNIT) {
|
|
int hpos = current_hpos() + 1;
|
|
decide_line(hpos);
|
|
sync_copper(hpos);
|
|
decide_fetch_ce(hpos);
|
|
if (blt_info.blit_queued) {
|
|
decide_blitter(hpos);
|
|
}
|
|
do_cycles(1 * CYCLE_UNIT);
|
|
cycles -= CYCLE_UNIT;
|
|
}
|
|
extra_cycle = cycles;
|
|
}
|
|
|
|
void do_cycles_ce020(int cycles)
|
|
{
|
|
int c;
|
|
evt_t cc;
|
|
int extra;
|
|
|
|
if (!cycles) {
|
|
return;
|
|
}
|
|
cc = get_cycles();
|
|
extra = cc & (CYCLE_UNIT - 1);
|
|
if (extra) {
|
|
extra = CYCLE_UNIT - extra;
|
|
if (extra >= cycles) {
|
|
do_cycles(cycles);
|
|
return;
|
|
}
|
|
do_cycles(extra);
|
|
cycles -= extra;
|
|
}
|
|
c = cycles;
|
|
while (c) {
|
|
int hpos = current_hpos() + 1;
|
|
decide_line(hpos);
|
|
sync_copper(hpos);
|
|
decide_fetch_ce(hpos);
|
|
if (blt_info.blit_queued) {
|
|
decide_blitter(hpos);
|
|
}
|
|
if (c < CYCLE_UNIT) {
|
|
break;
|
|
}
|
|
do_cycles(1 * CYCLE_UNIT);
|
|
c -= CYCLE_UNIT;
|
|
}
|
|
if (c) {
|
|
do_cycles(c);
|
|
}
|
|
}
|
|
|
|
bool is_cycle_ce(uaecptr addr)
|
|
{
|
|
addrbank *ab = get_mem_bank_real(addr);
|
|
if (!ab || (ab->flags & ABFLAG_CHIPRAM) || ab == &custom_bank) {
|
|
int hpos = current_hpos();
|
|
return (cycle_line_slot[hpos] & CYCLE_MASK) != 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
bool isvga(void)
|
|
{
|
|
if (programmedmode != 1) {
|
|
return false;
|
|
}
|
|
if (hblank_hz >= 20000) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ispal(int *lines)
|
|
{
|
|
if (lines) {
|
|
*lines = maxvpos_display;
|
|
}
|
|
if (programmedmode == 1) {
|
|
return currprefs.ntscmode == 0;
|
|
}
|
|
return maxvpos_display >= MAXVPOS_NTSC + (MAXVPOS_PAL - MAXVPOS_NTSC) / 2;
|
|
}
|
|
|
|
static void SET_LINE_CYCLEBASED(int hpos)
|
|
{
|
|
line_cyclebased = 1;
|
|
decide_line(hpos);
|
|
decide_fetch_safe(hpos);
|
|
}
|