--- uae/src/custom.c 2018/04/24 16:39:15 1.1.1.2 +++ uae/src/custom.c 2018/04/24 17:00:18 1.1.1.15 @@ -1,10 +1,11 @@ /* * UAE - The Un*x Amiga Emulator - * + * * Custom chip emulation - * - * Copyright 1995, 1996, 1997 Bernd Schmidt + * + * Copyright 1995-2001 Bernd Schmidt * Copyright 1995 Alessandro Bissacco + * Copyright 2000,2001 Toni Wilen */ #include "sysconfig.h" @@ -15,451 +16,423 @@ #include "config.h" #include "options.h" +#include "threaddep/penguin.h" +#include "uae.h" +#include "gensound.h" +#include "sounddep/sound.h" #include "events.h" #include "memory.h" #include "custom.h" -#include "readcpu.h" #include "newcpu.h" #include "cia.h" #include "disk.h" #include "blitter.h" #include "xwin.h" -#include "os.h" +#include "joystick.h" +#include "audio.h" #include "keybuf.h" #include "serial.h" #include "osemu.h" -#include "machdep/penguin.h" +#include "autoconf.h" +#include "gui.h" +#include "picasso96.h" +#include "drawing.h" +#include "savestate.h" -#ifdef X86_ASSEMBLY -#define LORES_HACK -#endif - -/* - * Several people have tried this define, with not much success. Turning on - * AGA garbles the screen. A place you could start looking is the calcdiw() - * function - the AGA timing parameters are different, and apparently I - * haven't figured out the correct formula yet. Pity, the current one looks - * logical. - * - * @@@ Probably won't compile in this version. - */ - -/* #define EMULATE_AGA */ - -#ifndef EMULATE_AGA -#define AGA_CHIPSET 0 -#else -#define AGA_CHIPSET 1 -#endif +static unsigned int n_consecutive_skipped = 0; +static unsigned int total_skipped = 0; -#define SMART_UPDATE 1 +#define SPRITE_COLLISIONS -#ifdef SUPPORT_PENGUINS -#undef SMART_UPDATE -#define SMART_UDPATE 1 -#endif +/* Mouse and joystick emulation */ -#if AGA_CHIPSET == 1 -#define MAX_PLANES 8 -#else -#define MAX_PLANES 6 -#endif +int buttonstate[3]; +static int mouse_x, mouse_y; +int joy0button, joy1button; +unsigned int joy0dir, joy1dir; -/* Fetched data spends 9 lores pixels somewhere in the chips before it appears - * on-screen. We don't emulate this. Instead, we cheat with the copper to - * compensate (much easier that way). */ -#define COPPER_MAGIC_FUDGE -9 -/* We ignore that many lores pixels at the start of the display. These are - * invisible anyway due to hardware DDF limits. */ -#define DISPLAY_LEFT_SHIFT 0x38 -static int lores_factor, lores_shift, sprite_width; - -#define PIXEL_XPOS(HPOS) (((HPOS)*2 - DISPLAY_LEFT_SHIFT) << lores_shift) - -/* @@@ Is maxhpos + 4 - 1 correct? (4 less isn't enough) */ -#define max_diwlastword (PIXEL_XPOS(maxhpos + 4 -1)) - -/* These are default values for mouse calibration. - * The first two are default values for mstepx and mstepy. - * The second line set the horizontal and vertical offset for amiga and X - * pointer matching - */ +/* Events */ -#define defstepx (1<<16) -#define defstepy (1<<16) -#define defxoffs 0 -#define defyoffs 0 - -/* Values below define mouse auto calibration process. - * They are not critical, change them if you want. - * The most important is calweight, which sets mouse adjustement rate */ - -static const int docal = 60, xcaloff = 40, ycaloff = 20; -static const int calweight = 3; -static int lastsampledmx, lastsampledmy; - - /* - * Events - */ - -unsigned long int cycles, nextevent; +unsigned long int currcycle, nextevent, is_lastline; +static int rpt_did_reset; struct ev eventtab[ev_max]; -int vpos; -UWORD lof; -static int lof_changed = 0, interlace_seen = 0; - -static int copper_waiting_for_blitter, copper_active; -static const int dskdelay = 2; /* FIXME: ??? */ - -static int dblpf_ind1[256], dblpf_ind2[256], dblpf_2nd1[256], dblpf_2nd2[256]; -static int dblpf_aga1[256], dblpf_aga2[256], linear_map_256[256], lots_of_twos[256]; +frame_time_t vsynctime, vsyncmintime; -static int dblpfofs[] = { 0, 2, 4, 8, 16, 32, 64, 128 }; +static int vpos; +static uae_u16 lof; +static int next_lineno; +static enum nln_how nextline_how; +static int lof_changed = 0; + +static uae_u32 sprtaba[256],sprtabb[256]; +static uae_u32 sprite_ab_merge[256]; +/* Tables for collision detection. */ +static uae_u32 sprclx[16], clxmask[16]; /* * Hardware registers of all sorts. */ -static int fmode; +static void custom_wput_1 (int, uaecptr, uae_u32) REGPARAM; + +static uae_u16 cregs[256]; -static UWORD cregs[256]; +uae_u16 intena,intreq; +uae_u16 dmacon; +uae_u16 adkcon; /* used by audio code */ -UWORD intena,intreq; -UWORD dmacon; -UWORD adkcon; /* used by audio code */ +static uae_u32 cop1lc,cop2lc,copcon; + +int maxhpos = MAXHPOS_PAL; +int maxvpos = MAXVPOS_PAL; +int minfirstline = MINFIRSTLINE_PAL; +int vblank_endline = VBLANK_ENDLINE_PAL; +int vblank_hz = VBLANK_HZ_PAL; +unsigned long syncbase; +static int fmode; +static unsigned int beamcon0, new_beamcon0; -static ULONG cop1lc,cop2lc,copcon; +#define MAX_SPRITES 8 /* This is but an educated guess. It seems to be correct, but this stuff * isn't documented well. */ -enum sprstate { SPR_vtop, SPR_restart, SPR_waiting_start, SPR_waiting_stop, SPR_stop }; -static enum sprstate spron[8]; -static CPTR sprpt[8]; -static int sprxpos[8], sprvstart[8], sprvstop[8]; - -static ULONG bpl1dat,bpl2dat,bpl3dat,bpl4dat,bpl5dat,bpl6dat,bpl7dat,bpl8dat; -static WORD bpl1mod,bpl2mod; - -static CPTR bplpt[8]; -#ifndef SMART_UPDATE -static char *real_bplpt[8]; -#endif - -/*static int blitcount[256]; blitter debug */ +enum sprstate { SPR_stop, SPR_restart, SPR_waiting_start, SPR_waiting_stop }; -struct color_entry { -#if AGA_CHIPSET == 0 - /* X86.S expects this at the start of the structure. */ - xcolnr acolors[32]; - UWORD color_regs[32]; -#else - ULONG color_regs[256]; -#endif +struct sprite { + uaecptr pt; + int on; + int xpos; + int vstart; + int vstop; + int armed; + enum sprstate state; }; +static struct sprite spr[8]; + +static int sprite_vblank_endline = VBLANK_ENDLINE_NTSC + 2; + +static unsigned int sprctl[MAX_SPRITES], sprpos[MAX_SPRITES]; +static uae_u16 sprdata[MAX_SPRITES][4], sprdatb[MAX_SPRITES][4]; +static int sprite_last_drawn_at[MAX_SPRITES]; +static int last_sprite_point, nr_armed; +static int sprite_width, sprres, sprite_buffer_res; + +static uae_u32 bpl1dat, bpl2dat, bpl3dat, bpl4dat, bpl5dat, bpl6dat, bpl7dat, bpl8dat; +static uae_s16 bpl1mod, bpl2mod; + +static uaecptr bplpt[8]; +uae_u8 *real_bplpt[8]; +/* Used as a debugging aid, to offset any bitplane temporarily. */ +int bploff[8]; + +/*static int blitcount[256]; blitter debug */ + static struct color_entry current_colors; -struct color_entry colors_for_drawing; -static unsigned int bplcon0,bplcon1,bplcon2,bplcon3,bplcon4; -static unsigned int diwstrt,diwstop,ddfstrt,ddfstop; -static unsigned int sprdata[8], sprdatb[8], sprctl[8], sprpos[8]; -static int sprarmed[8], sprite_last_drawn_at[8]; -static ULONG dskpt; -static UWORD dsklen,dsksync; - -static int joy0x, joy1x, joy0y, joy1y; -int joy0button; -UWORD joy0dir; -static int lastspr0x,lastspr0y,lastdiffx,lastdiffy,spr0pos,spr0ctl; -static int mstepx,mstepy,xoffs=defxoffs,yoffs=defyoffs; -static int sprvbfl; +static unsigned int bplcon0, bplcon1, bplcon2, bplcon3, bplcon4; +static int nr_planes_from_bplcon0, corrected_nr_planes_from_bplcon0; +static unsigned int diwstrt, diwstop, diwhigh; +static int diwhigh_written; +static unsigned int ddfstrt, ddfstop; -static enum { normal_mouse, dont_care_mouse, follow_mouse } mousestate; +/* The display and data fetch windows */ -static ULONG coplc; -static unsigned int copi1,copi2; +enum diw_states +{ + DIW_waiting_start, DIW_waiting_stop +}; -static enum copper_states { - COP_stop, COP_read, COP_do_read, COP_read_ignore, COP_do_read_ignore, COP_wait, COP_morewait, COP_move, COP_skip -} copstate; -/* The time the copper needs for one cycle depends on bitplane DMA. Whenever - * possible, we calculate a fixed value and store it here. */ -static int copper_cycle_time; - -static int dsklength; - -static int plffirstline,plflastline,plfstrt,plfstop,plflinelen; -static int diwfirstword,diwlastword; -static enum { DIW_waiting_start, DIW_waiting_stop } diwstate; - -int dskdmaen; /* used in cia.c */ - -/* 880 isn't a magic number, it's a safe number with some padding at the end. - * This used to be 1000, but that's excessive. (840 is too low). I'm too lazy - * to figure out the exact space needed. */ -union { - /* Let's try to align this thing. */ - double uupzuq; - long int cruxmedo; - unsigned char apixels[880]; -} pixdata; - -char spixels[880]; /* for sprites */ -char spixstate[880]; /* more sprites */ - -ULONG ham_linebuf[880]; -ULONG aga_linebuf[880], *aga_lbufptr; - -char *xlinebuffer; -int next_lineno; -static int nln_how; +static int plffirstline, plflastline; +static int plfstrt, plfstop; +static int last_diw_pix_hpos, last_ddf_pix_hpos, last_decide_line_hpos; +static int last_fetch_hpos, last_sprite_hpos; +int diwfirstword, diwlastword; +static enum diw_states diwstate, hdiwstate; + +/* Sprite collisions */ +static unsigned int clxdat, clxcon, clxcon2, clxcon_bpl_enable, clxcon_bpl_match; +static int clx_sprmask; + +enum copper_states { + COP_stop, + COP_read1_in2, + COP_read1_wr_in4, + COP_read1_wr_in2, + COP_read1, + COP_read2_wr_in2, + COP_read2, + COP_bltwait, + COP_wait_in4, + COP_wait_in2, + COP_skip_in4, + COP_skip_in2, + COP_wait1, + COP_wait +}; -static int *amiga2aspect_line_map, *native2amiga_line_map; -static int max_drawn_amiga_line; +struct copper { + /* The current instruction words. */ + unsigned int i1, i2; + unsigned int saved_i1, saved_i2; + enum copper_states state; + /* Instruction pointer. */ + uaecptr ip, saved_ip; + int hpos, vpos; + unsigned int ignore_next; + int vcmp, hcmp; + + /* When we schedule a copper event, knowing a few things about the future + of the copper list can reduce the number of sync_with_cpu calls + dramatically. */ + unsigned int first_sync; + unsigned int regtypes_modified; +}; + +#define REGTYPE_NONE 0 +#define REGTYPE_COLOR 1 +#define REGTYPE_SPRITE 2 +#define REGTYPE_PLANE 4 +#define REGTYPE_BLITTER 8 +#define REGTYPE_JOYPORT 16 +#define REGTYPE_DISK 32 +#define REGTYPE_POS 64 +#define REGTYPE_AUDIO 128 + +#define REGTYPE_ALL 255 +/* Always set in regtypes_modified, to enable a forced update when things like + DMACON, BPLCON0, COPJMPx get written. */ +#define REGTYPE_FORCE 256 + + +static unsigned int regtypes[512]; + +static struct copper cop_state; +static int copper_enabled_thisline; +static int cop_min_waittime; /* * Statistics */ /* Used also by bebox.cpp */ -unsigned long int msecs = 0, frametime = 0, timeframes = 0; +unsigned long int msecs = 0, frametime = 0, lastframetime = 0, timeframes = 0; static unsigned long int seconds_base; int bogusframe; +int n_frames; + +#define DEBUG_COPPER 0 +#if DEBUG_COPPER +/* 10000 isn't enough! */ +#define NR_COPPER_RECORDS 40000 +#else +#define NR_COPPER_RECORDS 1 +#endif + +/* Record copper activity for the debugger. */ +struct cop_record +{ + int hpos, vpos; + uaecptr addr; +}; +static struct cop_record cop_record[2][NR_COPPER_RECORDS]; +static int nr_cop_records[2]; +static int curr_cop_set; + +/* Recording of custom chip register changes. */ +static int current_change_set; + +#ifdef OS_WITHOUT_MEMORY_MANAGEMENT +/* sam: Those arrays uses around 7Mb of BSS... That seems */ +/* too much for AmigaDOS (uae crashes as soon as one loads */ +/* it. So I use a different strategy here (realloc the */ +/* arrays when needed. That strategy might be usefull for */ +/* computer with low memory. */ +struct sprite_entry *sprite_entries[2]; +struct color_change *color_changes[2]; +static int max_sprite_entry = 400; +static int delta_sprite_entry = 0; +static int max_color_change = 400; +static int delta_color_change = 0; +#else +struct sprite_entry sprite_entries[2][MAX_SPR_PIXELS / 16]; +struct color_change color_changes[2][MAX_REG_CHANGE]; +#endif + +struct decision line_decisions[2 * (MAXVPOS + 1) + 1]; +struct draw_info line_drawinfo[2][2 * (MAXVPOS + 1) + 1]; +struct color_entry color_tables[2][(MAXVPOS + 1) * 2]; + +static int next_sprite_entry = 0; +static int prev_next_sprite_entry; +static int next_sprite_forced = 1; + +struct sprite_entry *curr_sprite_entries, *prev_sprite_entries; +struct color_change *curr_color_changes, *prev_color_changes; +struct draw_info *curr_drawinfo, *prev_drawinfo; +struct color_entry *curr_color_tables, *prev_color_tables; + +static int next_color_change; +static int next_color_entry, remembered_color_entry; +static int color_src_match, color_dest_match, color_compare_result; + +static uae_u32 thisline_changed; + +#ifdef SMART_UPDATE +#define MARK_LINE_CHANGED do { thisline_changed = 1; } while (0) +#else +#define MARK_LINE_CHANGED do { ; } while (0) +#endif + +static struct decision thisline_decision; +static int passed_plfstop, fetch_cycle; + +enum fetchstate { + fetch_not_started, + fetch_started, + fetch_was_plane0 +} fetch_state; /* * helper functions */ -int inhibit_frame; -static int framecnt = 0; - -static __inline__ void count_frame(void) +uae_u32 get_copper_address (int copno) { - if (inhibit_frame) - framecnt = 1; - else { - framecnt++; - if (framecnt >= framerate) - framecnt = 0; + switch (copno) { + case 1: return cop1lc; + case 2: return cop2lc; + default: return 0; } } -static __inline__ void setclr(UWORD *p, UWORD val) +STATIC_INLINE void record_copper (uaecptr addr, int hpos, int vpos) { - if (val & 0x8000) { - *p |= val & 0x7FFF; - } else { - *p &= ~val; +#if DEBUG_COPPER + int t = nr_cop_records[curr_cop_set]; + if (t < NR_COPPER_RECORDS) { + cop_record[curr_cop_set][t].addr = addr; + cop_record[curr_cop_set][t].hpos = hpos; + cop_record[curr_cop_set][t].vpos = vpos; + nr_cop_records[curr_cop_set] = t + 1; } +#endif } -__inline__ int current_hpos(void) -{ - return cycles - eventtab[ev_hsync].oldcycles; -} - -static __inline__ UBYTE *pfield_xlateptr(CPTR plpt, int bytecount) +int find_copper_record (uaecptr addr, int *phpos, int *pvpos) { - if (!chipmem_bank.check(plpt,bytecount)) { - static int count = 0; - if (count < 5) { - count++; - fprintf(stderr, "Warning: Bad playfield pointer"); - if (count == 5) fprintf(stderr, " (no further warnings)"); - fprintf(stderr, "\n"); + int s = curr_cop_set ^ 1; + int t = nr_cop_records[s]; + int i; + for (i = 0; i < t; i++) { + if (cop_record[s][i].addr == addr) { + *phpos = cop_record[s][i].hpos; + *pvpos = cop_record[s][i].vpos; + return 1; } - return NULL; } - return chipmem_bank.xlateaddr(plpt); + return 0; } -static void calculate_copper_cycle_time (void) +int rpt_available = 0; + +void reset_frame_rate_hack (void) { - int bplplanes; - - if (diwstate == DIW_waiting_start || !dmaen (DMA_BITPLANE)) { - copper_cycle_time = 2; + if (currprefs.m68k_speed != -1) return; - } - bplplanes = (bplcon0 & 0x7000) >> 12; - if (bplcon0 & 0x8000) - bplplanes *= 2; - copper_cycle_time = bplplanes <= 4 ? 2 : -1; -} - -/* line_draw_funcs: pfield_do_linetoscr, pfield_do_fill_line, decode_ham6 */ -typedef void (*line_draw_func)(int, int); - -#define LINE_UNDECIDED 1 -#define LINE_DECIDED 2 -#define LINE_DECIDED_DOUBLE 3 -#define LINE_AS_PREVIOUS 4 -#define LINE_BORDER_NEXT 5 -#define LINE_BORDER_PREV 6 -#define LINE_DONE 7 -#define LINE_DONE_AS_PREVIOUS 8 -#define LINE_REMEMBERED_AS_PREVIOUS 9 - -static char *line_drawn; -static char linestate[(maxvpos + 1)*2 + 1]; - -static int frame_redraw_necessary; -static int min_diwstart, max_diwstop, prev_x_adjust, linetoscr_x_adjust, linetoscr_right_x; -static int thisframe_y_adjust, prev_y_adjust, thisframe_first_drawn_line, thisframe_last_drawn_line; -static int thisframe_y_adjust_real, max_ypos_thisframe, min_ypos_for_screen; - -/* 50 words give you 800 horizontal pixels. An A500 can't do that, so it ought - * to be enough. */ -#define MAX_WORDS_PER_LINE 50 -static UBYTE line_data[(maxvpos+1) * 2][MAX_PLANES * MAX_WORDS_PER_LINE * 2]; - -/* - * The idea behind this code is that at some point during each horizontal - * line, we decide how to draw this line. There are many more-or-less - * independent decisions, each of which can be taken at a different horizontal - * position. - * Sprites, color changes and bitplane delay changes are handled specially: - * There isn't a single decision, but a list of structures containing - * information on how to draw the line. - */ - -struct color_change { - int linepos; - int regno; - unsigned long value; -}; -struct sprite_draw { - int linepos; - int num; - int ctl; - UWORD data, datb; -}; - -struct delay_change { - int linepos; - unsigned int value; -}; - -/* Way too much... */ -#define MAX_REG_CHANGE ((maxvpos+1) * 2 * maxhpos) -static int current_change_set; - -/* Add others here (RISC OS, maybe Windows). Under Unix, these big arrays - * neither take up physical memory nor make the executable bigger so it's - * a very convenient way to get safety without overhead. */ -#ifdef AMIGA -#define OS_WITHOUT_MEMORY_MANAGEMENT -#endif - -#ifdef OS_WITHOUT_MEMORY_MANAGEMENT -/* sam: Those arrays uses around 7Mb of BBS... That seems */ -/* too much for AmigaDOS (uae crashes as soon as one loads */ -/* it. So I use a different strategy here (realloc the */ -/* arrays when needed. That strategy might be usefull for */ -/* computer with low memory. */ -static struct sprite_draw *sprite_positions[2]; -static int max_sprite_draw = 800; -static int _max_sprite_draw = 0; -static struct color_change *color_changes[2]; -static int max_color_change = 800; -static int _max_color_change = 0; -#else -static struct sprite_draw sprite_positions[2][MAX_REG_CHANGE]; -static struct color_change color_changes[2][MAX_REG_CHANGE]; -#endif -/* We don't remember those across frames, that would be too much effort. - * We simply redraw the line whenever we see one of these. */ -static struct delay_change delay_changes[MAX_REG_CHANGE]; -static struct sprite_draw *curr_sprite_positions, *prev_sprite_positions; -static struct color_change *curr_color_changes, *prev_color_changes; - -static int next_color_change, next_sprite_draw, next_delay_change; - -static struct color_entry color_tables[2][(maxvpos+1) * 2]; -static struct color_entry *curr_color_tables, *prev_color_tables; -static int next_color_entry, remembered_color_entry, drawing_color_matches; -static enum { color_match_acolors, color_match_full } color_match_type; -static int color_src_match, color_dest_match; + if (! rpt_available) { + currprefs.m68k_speed = 0; + return; + } -static int last_redraw_point; + rpt_did_reset = 1; + is_lastline = 0; + vsyncmintime = read_processor_time() + vsynctime; + write_log ("Resetting frame rate hack\n"); +} -static int first_drawn_line, last_drawn_line; -static int first_block_line, last_block_line; +void check_prefs_changed_custom (void) +{ + currprefs.gfx_framerate = changed_prefs.gfx_framerate; + /* Not really the right place... */ + if (currprefs.jport0 != changed_prefs.jport0 + || currprefs.jport1 != changed_prefs.jport1) { + currprefs.jport0 = changed_prefs.jport0; + currprefs.jport1 = changed_prefs.jport1; + joystick_setting_changed (); + } + currprefs.immediate_blits = changed_prefs.immediate_blits; + currprefs.blits_32bit_enabled = changed_prefs.blits_32bit_enabled; + currprefs.collision_level = changed_prefs.collision_level; + currprefs.fast_copper = changed_prefs.fast_copper; +} -static void init_regchanges (void) +STATIC_INLINE void setclr (uae_u16 *p, uae_u16 val) { - size_t i; + if (val & 0x8000) + *p |= val & 0x7FFF; + else + *p &= ~val; +} - next_color_change = 0; - next_sprite_draw = 0; - current_change_set = 0; - for (i = 0; i < sizeof linestate / sizeof *linestate; i++) - linestate[i] = LINE_UNDECIDED; -} - -/* struct decision contains things we save across drawing frames for - * comparison (smart update stuff). */ -struct decision { - unsigned long color0; - int which; - int plfstrt, plflinelen; - int diwfirstword, diwlastword; - int ctable; - - UWORD bplcon0, bplcon1, bplcon2; -#if 0 /* We don't need this. */ - UWORD bplcon3; -#endif -#if AGA_CHIPSET == 1 - UWORD bplcon4; -#endif -}; +__inline__ int current_hpos (void) +{ + return (get_cycles () - eventtab[ev_hsync].oldcycles) / CYCLE_UNIT; +} -/* Anything related to changes in hw registers during the DDF for one - * line. */ -struct draw_info { - int first_sprite_draw, last_sprite_draw; - int first_color_change, last_color_change; - int first_delay_change, last_delay_change; - int nr_color_changes, nr_sprites; -}; +STATIC_INLINE uae_u8 *pfield_xlateptr (uaecptr plpt, int bytecount) +{ + if (!chipmem_bank.check (plpt, bytecount)) { + static int count = 0; + if (!count) + count++, write_log ("Warning: Bad playfield pointer\n"); + return NULL; + } + return chipmem_bank.xlateaddr (plpt); +} -/* These few are only needed during/at the end of the scanline, and don't - * have to be remembered. */ -static int decided_bpl1mod, decided_bpl2mod, decided_nr_planes, decided_hires; - -/* These are generated by the drawing code from the line_decisions array for - * each line that needs to be drawn. */ -static int bplehb, bplham, bpldualpf, bpldualpfpri, bplplanecnt, bplhires; -static int bpldelay1, bpldelay2; -static int plfpri[3]; - -static struct decision line_decisions[2 * (maxvpos+1) + 1]; -static struct draw_info line_drawinfo[2][2 * (maxvpos+1) + 1]; -static struct draw_info *curr_drawinfo, *prev_drawinfo; -static struct decision *dp_for_drawing; -static struct draw_info *dip_for_drawing; +STATIC_INLINE void docols (struct color_entry *colentry) +{ + int i; -static int line_changed[2 * (maxvpos+1)]; + if (currprefs.chipset_mask & CSMASK_AGA) { + for (i = 0; i < 256; i++) { + int v = color_reg_get (colentry, i); + if (v < 0 || v > 16777215) + continue; + colentry->acolors[i] = CONVERT_RGB (v); + } + } else { + for (i = 0; i < 32; i++) { + int v = color_reg_get (colentry, i); + if (v < 0 || v > 4095) + continue; + colentry->acolors[i] = xcolors[v]; + } + } +} -#ifdef SMART_UPDATE -#define MARK_LINE_CHANGED(l) do { line_changed[l] = 1; } while (0) -#else -#define MARK_LINE_CHANGED(l) do { } while (0) -#endif +void notice_new_xcolors (void) +{ + int i; -static struct decision thisline_decision; -static int modulos_added, plane_decided, color_decided, very_broken_program; + docols(¤t_colors); +/* docols(&colors_for_drawing);*/ + for (i = 0; i < (MAXVPOS + 1)*2; i++) { + docols(color_tables[0]+i); + docols(color_tables[1]+i); + } +} -static void do_sprites(int currvp, int currhp); +static void do_sprites (int currvp, int currhp); static void remember_ctable (void) { if (remembered_color_entry == -1) { /* The colors changed since we last recorded a color map. Record a * new one. */ - memcpy (curr_color_tables + next_color_entry, ¤t_colors, sizeof current_colors); + color_reg_cpy (curr_color_tables + next_color_entry, ¤t_colors); remembered_color_entry = next_color_entry++; } thisline_decision.ctable = remembered_color_entry; @@ -470,591 +443,1482 @@ static void remember_ctable (void) int oldctable = line_decisions[next_lineno].ctable; int changed = 0; - if (oldctable == -1) + if (oldctable == -1) { changed = 1; - else if (fast_memcmp (&prev_color_tables[oldctable].color_regs, ¤t_colors.color_regs, - sizeof current_colors.color_regs) != 0) - changed = 1; - - if (changed) { color_src_match = color_dest_match = -1; - line_changed[next_lineno] = 1; } else { - color_dest_match = remembered_color_entry; + 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 decide_diw (void) +static void remember_ctable_for_border (void) { - if (thisline_decision.diwfirstword == -1 && PIXEL_XPOS (current_hpos ()) >= diwfirstword) { - thisline_decision.diwfirstword = diwfirstword; - /* Decide playfield delays only at DIW start, because they don't matter before and - * some programs change them after DDF start but before DIW start. */ - thisline_decision.bplcon1 = bplcon1; - if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword) - MARK_LINE_CHANGED (next_lineno); - thisline_decision.diwlastword = -1; - } - if (thisline_decision.diwlastword == -1 && (current_hpos () >= maxhpos || PIXEL_XPOS (current_hpos ()) >= diwlastword)) { - thisline_decision.diwlastword = diwlastword; - if (thisline_decision.diwlastword != line_decisions[next_lineno].diwlastword) - MARK_LINE_CHANGED (next_lineno); - } + remember_ctable (); } -static void record_color_change (int regno, unsigned long value) +/* Called to determine the state of the horizontal display window state + * machine at the current position. It might have changed since we last + * checked. */ +static void decide_diw (int hpos) { - /* Early positions don't appear on-screen. */ - if (framecnt != 0 || vpos < minfirstline || current_hpos () < 0x18) - return; - decide_diw (); - - /* See if we can record the color table, but have not done so yet. - * @@@ There might be a minimal performance loss in case someone changes - * a color exactly at the start of the DIW. I don't think it can actually - * fail to work even in this case, but I'm not 100% sure. - * @@@ There might be a slightly larger performance loss if we're drawing - * this line as border... especially if there are changes in colors != 0 - * we might end up setting line_changed for no reason. FIXME */ - if (thisline_decision.diwfirstword >= 0) { - if (thisline_decision.ctable == -1) - remember_ctable (); - } - - /* Changes outside the DIW can be ignored if the modified color is not the - * background color, or if the accuracy is 0. */ - if ((regno != 0 || emul_accuracy == 0) - && (thisline_decision.diwfirstword < 0 || thisline_decision.diwlastword >= 0)) - return; - - /* If the border is changed the first time before the DIW, record the - * original starting border value. */ - if (regno == 0 && thisline_decision.color0 == 0xFFFFFFFFul && thisline_decision.diwfirstword < 0) { - thisline_decision.color0 = current_colors.color_regs[0]; - if (line_decisions[next_lineno].color0 != value) - line_changed[next_lineno] = 1; + int pix_hpos = coord_diw_to_window_x (hpos * 2); + if (hdiwstate == DIW_waiting_start && thisline_decision.diwfirstword == -1 + && pix_hpos >= diwfirstword && last_diw_pix_hpos < diwfirstword) + { + thisline_decision.diwfirstword = diwfirstword < 0 ? 0 : diwfirstword; + hdiwstate = DIW_waiting_stop; + thisline_decision.diwlastword = -1; } - /* Anything else gets recorded in the color_changes table. */ -#ifdef OS_WITHOUT_MEMORY_MANAGEMENT - if(next_color_change >= _max_color_change) { - _max_color_change = next_color_change; - return; + if (hdiwstate == DIW_waiting_stop && thisline_decision.diwlastword == -1 + && pix_hpos >= diwlastword && last_diw_pix_hpos < diwlastword) + { + thisline_decision.diwlastword = diwlastword < 0 ? 0 : diwlastword; + hdiwstate = DIW_waiting_start; } -#endif - curr_color_changes[next_color_change].linepos = current_hpos (); - curr_color_changes[next_color_change].regno = regno; - curr_color_changes[next_color_change++].value = value; + last_diw_pix_hpos = pix_hpos; } -static __inline__ void decide_as_playfield (int startpos, int len) +/* The HRM says 0xD8, but that can't work... */ +#define HARD_DDF_STOP (0xD4) + +static void finish_playfield_line (void) { - thisline_decision.which = 1; + int m1, m2; /* The latter condition might be able to happen in interlaced frames. */ if (vpos >= minfirstline && (thisframe_first_drawn_line == -1 || vpos < thisframe_first_drawn_line)) thisframe_first_drawn_line = vpos; thisframe_last_drawn_line = vpos; - thisline_decision.plfstrt = startpos; - thisline_decision.plflinelen = len; + if ((currprefs.chipset_mask & CSMASK_AGA) && (fmode & 0x4000)) { + if (((diwstrt >> 8) ^ vpos) & 1) + m1 = m2 = bpl2mod; + else + m1 = m2 = bpl1mod; + } else { + m1 = bpl1mod; + m2 = bpl2mod; + } + + if (dmaen (DMA_BITPLANE)) + switch (nr_planes_from_bplcon0) { + case 8: bplpt[7] += m2; + case 7: bplpt[6] += m1; + case 6: bplpt[5] += m2; + case 5: bplpt[4] += m1; + case 4: bplpt[3] += m2; + case 3: bplpt[2] += m1; + case 2: bplpt[1] += m2; + case 1: bplpt[0] += m1; + } /* These are for comparison. */ thisline_decision.bplcon0 = bplcon0; thisline_decision.bplcon2 = bplcon2; -#if AGA_CHIPSET == 1 + thisline_decision.bplcon3 = bplcon3; thisline_decision.bplcon4 = bplcon4; -#endif #ifdef SMART_UPDATE - if (line_decisions[next_lineno].plfstrt != thisline_decision.plfstrt - || line_decisions[next_lineno].plflinelen != thisline_decision.plflinelen + 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 -#if AGA_CHIPSET == 1 + || line_decisions[next_lineno].bplcon3 != thisline_decision.bplcon3 || line_decisions[next_lineno].bplcon4 != thisline_decision.bplcon4 -#endif ) #endif /* SMART_UPDATE */ - line_changed[next_lineno] = 1; + thisline_changed = 1; } -static __inline__ void post_decide_line (void) +static int fetchmode; + +/* The fetch unit mainly controls ddf stop. It's the number of cycles that + are contained in an indivisible block during which ddf is active. E.g. + if DDF starts at 0x30, and fetchunit is 8, then possible DDF stops are + 0x30 + n * 8. */ +static int fetchunit, fetchunit_mask; +/* The delay before fetching the same bitplane again. Can be larger than + the number of bitplanes; in that case there are additional empty cycles + with no data fetch (this happens for high fetchmodes and low + resolutions). */ +static int fetchstart, fetchstart_shift, fetchstart_mask; +/* fm_maxplane holds the maximum number of planes possible with the current + fetch mode. This selects the cycle diagram: + 8 planes: 73516240 + 4 planes: 3120 + 2 planes: 10. */ +static int fm_maxplane, fm_maxplane_shift; + +/* The corresponding values, by fetchmode and display resolution. */ +static int fetchunits[] = { 8,8,8,0, 16,8,8,0, 32,16,8,0 }; +static int fetchstarts[] = { 3,2,1,0, 4,3,2,0, 5,4,3,0 }; +static int fm_maxplanes[] = { 3,2,1,0, 3,3,2,0, 3,3,3,0 }; + +/* Used by the copper. */ +static int estimated_last_fetch_cycle; +static int cycle_diagram_shift; + +static void estimate_last_fetch_cycle (int hpos) +{ + int fetchunit = fetchunits[fetchmode * 4 + GET_RES (bplcon0)]; + + if (! passed_plfstop) { + int stop = plfstop < hpos || plfstop > HARD_DDF_STOP ? HARD_DDF_STOP : plfstop; + /* We know that fetching is up-to-date up until hpos, so we can use fetch_cycle. */ + int fetch_cycle_at_stop = fetch_cycle + (stop - hpos); + int starting_last_block_at = (fetch_cycle_at_stop + fetchunit - 1) & ~(fetchunit - 1); + + estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit; + } else { + int starting_last_block_at = (fetch_cycle + fetchunit - 1) & ~(fetchunit - 1); + if (passed_plfstop == 2) + starting_last_block_at -= fetchunit; + + estimated_last_fetch_cycle = hpos + (starting_last_block_at - fetch_cycle) + fetchunit; + } +} + +static uae_u32 outword[MAX_PLANES]; +static int out_nbits, out_offs; +static uae_u32 todisplay[MAX_PLANES][4]; +static uae_u32 fetched[MAX_PLANES]; +static uae_u32 fetched_aga0[MAX_PLANES]; +static uae_u32 fetched_aga1[MAX_PLANES]; + +/* Expansions from bplcon0/bplcon1. */ +static int toscr_res, toscr_delay1, toscr_delay2, toscr_nr_planes, fetchwidth; + +/* 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 delayoffset; + +STATIC_INLINE void compute_delay_offset (int hpos) { - static int warned = 0; - int tmp; + /* this fixes most horizontal scrolling jerkyness but can't be correct */ + delayoffset = ((hpos - fm_maxplane - 0x18) & fetchstart_mask) << 1; + delayoffset &= ~7; + delayoffset &= 31; +} - if (thisline_decision.which == 1 && current_hpos () < thisline_decision.plfstrt + thisline_decision.plflinelen - && ((bplcon0 & 0x7000) == 0 || !dmaen (DMA_BITPLANE))) - { - /* This is getting gross... */ - thisline_decision.plflinelen = current_hpos() - thisline_decision.plfstrt; - thisline_decision.plflinelen &= 7; - if (thisline_decision.plflinelen == 0) - thisline_decision.which = -1; - /* ... especially THIS! */ - modulos_added = 1; - return; +static void expand_fmodes (void) +{ + int res = GET_RES(bplcon0); + int fm = fetchmode; + fetchunit = fetchunits[fm * 4 + res]; + fetchunit_mask = fetchunit - 1; + fetchstart_shift = fetchstarts[fm * 4 + res]; + fetchstart = 1 << fetchstart_shift; + fetchstart_mask = fetchstart - 1; + fm_maxplane_shift = fm_maxplanes[fm * 4 + res]; + fm_maxplane = 1 << fm_maxplane_shift; +} + +static int maxplanes_ocs[]={ 6,4,0,0 }; +static int maxplanes_ecs[]={ 6,4,2,0 }; +static int maxplanes_aga[]={ 8,4,2,0, 8,8,4,0, 8,8,8,0 }; + +/* Expand bplcon0/bplcon1 into the toscr_xxx variables. */ +static void compute_toscr_delay_1 (void) +{ + int delay1 = (bplcon1 & 0x0f) | ((bplcon1 & 0x0c00) >> 6); + int delay2 = ((bplcon1 >> 4) & 0x0f) | (((bplcon1 >> 4) & 0x0c00) >> 6); + int delaymask; + int fetchwidth = 16 << fetchmode; + + delay1 += delayoffset; + delay2 += delayoffset; + delaymask = (fetchwidth - 1) >> toscr_res; + toscr_delay1 = (delay1 & delaymask) << toscr_res; + toscr_delay2 = (delay2 & delaymask) << toscr_res; +} + +static void compute_toscr_delay (int hpos) +{ + int v = bplcon0; + int *planes; + + if (currprefs.chipset_mask & CSMASK_AGA) + planes = maxplanes_aga; + else if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) + planes = maxplanes_ocs; + else + planes = maxplanes_ecs; + /* Disable bitplane DMA if planes > maxplanes. This is needed e.g. by the + Sanity WOC demo (at the "Party Effect"). */ + if (GET_PLANES(v) > planes[fetchmode*4 + GET_RES (v)]) + v &= ~0x7010; + toscr_res = GET_RES (v); + + toscr_nr_planes = GET_PLANES (v); + + compute_toscr_delay_1 (); +} + +STATIC_INLINE void maybe_first_bpl1dat (int hpos) +{ + if (thisline_decision.plfleft == -1) { + thisline_decision.plfleft = hpos; + compute_delay_offset (hpos); + compute_toscr_delay_1 (); } - - if (thisline_decision.which != -1 || diwstate == DIW_waiting_start || (bplcon0 & 0x7000) == 0 - || !dmaen (DMA_BITPLANE) || current_hpos () >= thisline_decision.plfstrt + thisline_decision.plflinelen) +} + +STATIC_INLINE void fetch (int nr, int fm) +{ + uaecptr p; + if (nr >= toscr_nr_planes) return; -#if 0 /* Can't warn because Kickstart 1.3 does it at reset time ;-) */ - if (!warned) { - fprintf(stderr, "That program you are running is _really_ broken.\n"); - warned = 1; + p = bplpt[nr] + bploff[nr]; + switch (fm) { + case 0: + fetched[nr] = chipmem_wget (p); + bplpt[nr] += 2; + break; + case 1: + fetched_aga0[nr] = chipmem_lget (p); + bplpt[nr] += 4; + break; + case 2: + fetched_aga1[nr] = chipmem_lget (p); + fetched_aga0[nr] = chipmem_lget (p + 4); + bplpt[nr] += 8; + break; } + if (nr == 0) + fetch_state = fetch_was_plane0; +} + +static void clear_fetchbuffer (uae_u32 *ptr, int nwords) +{ + int i; + + if (! thisline_changed) + for (i = 0; i < nwords; i++) + if (ptr[i]) { + thisline_changed = 1; + break; + } + + memset (ptr, 0, nwords * 4); +} + +static void update_toscr_planes (void) +{ + if (toscr_nr_planes > thisline_decision.nr_planes) { + int j; + for (j = thisline_decision.nr_planes; j < toscr_nr_planes; j++) + clear_fetchbuffer ((uae_u32 *)(line_data[next_lineno] + 2 * MAX_WORDS_PER_LINE * j), out_offs); +#if 0 + if (thisline_decision.nr_planes > 0) + printf ("Planes from %d to %d\n", thisline_decision.nr_planes, toscr_nr_planes); #endif + thisline_decision.nr_planes = toscr_nr_planes; + } +} - decided_hires = (bplcon0 & 0x8000) == 0x8000; - decided_nr_planes = ((bplcon0 & 0x7000) >> 12); +STATIC_INLINE void toscr_3_ecs (int nbits) +{ + int delay1 = toscr_delay1; + int delay2 = toscr_delay2; + int i; + uae_u32 mask = 0xFFFF >> (16 - nbits); - /* Magic 12, Ray of Hope 2 demo does ugly things. Looks great from the - * outside, rotten inside. */ - if (decided_hires) { - tmp = current_hpos () & ~3; - very_broken_program = current_hpos () & 3; - } else { - tmp = current_hpos () & ~7; - very_broken_program = current_hpos () & 7; + for (i = 0; i < toscr_nr_planes; i += 2) { + outword[i] <<= nbits; + outword[i] |= (todisplay[i][0] >> (16 - nbits + delay1)) & mask; + todisplay[i][0] <<= nbits; + } + for (i = 1; i < toscr_nr_planes; i += 2) { + outword[i] <<= nbits; + outword[i] |= (todisplay[i][0] >> (16 - nbits + delay2)) & mask; + todisplay[i][0] <<= nbits; } - MARK_LINE_CHANGED (next_lineno); /* Play safe. */ - decide_as_playfield (tmp, plflinelen + plfstrt - tmp); } -static void decide_line_1 (void) +STATIC_INLINE void shift32plus (uae_u32 *p, int n) { - do_sprites(vpos, current_hpos ()); + uae_u32 t = p[1]; + t <<= n; + t |= p[0] >> (32 - n); + p[1] = t; +} + +STATIC_INLINE void aga_shift (uae_u32 *p, int n, int fm) +{ + if (fm == 2) { + shift32plus (p + 2, n); + shift32plus (p + 1, n); + } + shift32plus (p + 0, n); + p[0] <<= n; +} + +STATIC_INLINE void toscr_3_aga (int nbits, int fm) +{ + int delay1 = toscr_delay1; + int delay2 = toscr_delay2; + int i; + uae_u32 mask = 0xFFFF >> (16 - nbits); - /* Surprisingly enough, this seems to be correct here - putting this into - * decide_diw() results in garbage. */ - if (diwstate == DIW_waiting_start && vpos == plffirstline) { - diwstate = DIW_waiting_stop; - calculate_copper_cycle_time (); + { + int offs = (16 << fm) - nbits + delay1; + int off1 = offs >> 5; + if (off1 == 3) + off1 = 2; + offs -= off1 * 32; + for (i = 0; i < toscr_nr_planes; i += 2) { + uae_u32 t0 = todisplay[i][off1]; + uae_u32 t1 = todisplay[i][off1 + 1]; + uae_u64 t = (((uae_u64)t1) << 32) | t0; + outword[i] <<= nbits; + outword[i] |= (t >> offs) & mask; + aga_shift (todisplay[i], nbits, fm); + } } - if (diwstate == DIW_waiting_stop && vpos == plflastline) { - diwstate = DIW_waiting_start; - calculate_copper_cycle_time (); + { + int offs = (16 << fm) - nbits + delay2; + int off1 = offs >> 5; + if (off1 == 3) + off1 = 2; + offs -= off1 * 32; + for (i = 1; i < toscr_nr_planes; i += 2) { + uae_u32 t0 = todisplay[i][off1]; + uae_u32 t1 = todisplay[i][off1 + 1]; + uae_u64 t = (((uae_u64)t1) << 32) | t0; + outword[i] <<= nbits; + outword[i] |= (t >> offs) & mask; + aga_shift (todisplay[i], nbits, fm); + } } +} - if (framecnt != 0) { -/* thisline_decision.which = -2; This doesn't do anything but hurt, I think. */ - return; +static void toscr_2_0 (int nbits) { toscr_3_ecs (nbits); } +static void toscr_2_1 (int nbits) { toscr_3_aga (nbits, 1); } +static void toscr_2_2 (int nbits) { toscr_3_aga (nbits, 2); } + +STATIC_INLINE void toscr_1 (int nbits, int fm) +{ + switch (fm) { + case 0: + toscr_2_0 (nbits); + break; + case 1: + toscr_2_1 (nbits); + break; + case 2: + toscr_2_2 (nbits); + break; } - - if (!dmaen(DMA_BITPLANE) || diwstate == DIW_waiting_start || (bplcon0 & 0x7000) == 0) { - /* We don't want to draw this one. */ - thisline_decision.which = -1; - return; + + out_nbits += nbits; + if (out_nbits == 32) { + int i; + uae_u8 *dataptr = line_data[next_lineno] + out_offs * 4; + /* Don't use toscr_nr_planes here; if the plane count drops during the + line we still want the data to be correct for the full number of planes + over the full width of the line. */ + for (i = 0; i < thisline_decision.nr_planes; i++) { + uae_u32 *dataptr32 = (uae_u32 *)dataptr; + if (*dataptr32 != outword[i]) + thisline_changed = 1; + *dataptr32 = outword[i]; + dataptr += MAX_WORDS_PER_LINE * 2; + } + out_offs++; + out_nbits = 0; } +} - decided_hires = (bplcon0 & 0x8000) == 0x8000; - decided_nr_planes = ((bplcon0 & 0x7000) >> 12); -#if 0 - /* The blitter gets slower if there's high bitplane activity. - * @@@ but the values must be different. FIXME */ - if (bltstate != BLT_done - && ((decided_hires && decided_nr_planes > 2) - || (!decided_hires && decided_nr_planes > 4))) - { - int pl = decided_nr_planes; - unsigned int n = (eventtab[ev_blitter].evtime-cycles); - if (n > plflinelen) - n = plflinelen; - n >>= 1; - if (decided_hires) - pl <<= 1; - if (pl == 8) - eventtab[ev_blitter].evtime += plflinelen; - else if (pl == 6) - eventtab[ev_blitter].evtime += n*2; - else if (pl == 5) - eventtab[ev_blitter].evtime += n*3/2; - events_schedule(); +static void toscr_fm0 (int); +static void toscr_fm1 (int); +static void toscr_fm2 (int); + +STATIC_INLINE void toscr (int nbits, int fm) +{ + switch (fm) { + case 0: toscr_fm0 (nbits); break; + case 1: toscr_fm1 (nbits); break; + case 2: toscr_fm2 (nbits); break; } -#endif - decide_as_playfield (plfstrt, plflinelen); } -static __inline__ void decide_line (void) +STATIC_INLINE void toscr_0 (int nbits, int fm) { - if (thisline_decision.which == 0 && current_hpos() >= plfstrt) - decide_line_1 (); + int t; + + if (nbits > 16) { + toscr (16, fm); + nbits -= 16; + } + + t = 32 - out_nbits; + if (t < nbits) { + toscr_1 (t, fm); + nbits -= t; + } + toscr_1 (nbits, fm); } -static void decide_delay (void) +static void toscr_fm0 (int nbits) { toscr_0 (nbits, 0); } +static void toscr_fm1 (int nbits) { toscr_0 (nbits, 1); } +static void toscr_fm2 (int nbits) { toscr_0 (nbits, 2); } + +static int flush_plane_data (int fm) { - static int warned; + int i = 0; + int fetchwidth = 16 << fm; - /* Don't do anything if we're outside the DIW. */ - if (thisline_decision.diwfirstword == -1 || thisline_decision.diwlastword > 0) - return; - decide_line (); - /* Half-hearted attempt to get things right even when post_decide_line() changes - * the decision afterwards. */ - if (thisline_decision.which != 1) { - thisline_decision.bplcon1 = bplcon1; - return; + if (out_nbits <= 16) { + i += 16; + toscr_1 (16, fm); } - /* @@@ Could check here for DDF stopping earlier than DIW */ - - delay_changes[next_delay_change].linepos = current_hpos (); - delay_changes[next_delay_change++].value = bplcon1; - if (!warned) { - warned = 1; - fprintf (stderr, "Program is torturing BPLCON1.\n"); + if (out_nbits != 0) { + i += 32 - out_nbits; + toscr_1 (32 - out_nbits, fm); } + i += 32; + + toscr_1 (16, fm); + toscr_1 (16, fm); + return i >> (1 + toscr_res); } -/* - * The decision which bitplane pointers to use is not taken at plfstrt, since - * data fetch does not start for all planes at this point. Therefore, we wait - * for the end of the ddf area or the first write to any of the bitplane - * pointer registers, whichever comes last, before we decide which plane pointers - * to use. - * Call decide_line() before this function. - */ -static void decide_plane (void) +STATIC_INLINE void flush_display (int fm) { - int i, bytecount; + if (toscr_nbits > 0 && thisline_decision.plfleft != -1) + toscr (toscr_nbits, fm); + toscr_nbits = 0; +} - if (framecnt != 0 || plane_decided) +/* Called when all planes have been fetched, i.e. when a new block + of data is available to be displayed. The data in fetched[] is + moved into todisplay[]. */ +STATIC_INLINE void beginning_of_plane_block (int pos, int dma, int fm) +{ + int i; + + flush_display (fm); + + if (fm == 0) + for (i = 0; i < MAX_PLANES; i++) + todisplay[i][0] |= fetched[i]; + else + for (i = 0; i < MAX_PLANES; i++) { + if (fm == 2) + todisplay[i][1] = fetched_aga1[i]; + todisplay[i][0] = fetched_aga0[i]; + } + + maybe_first_bpl1dat (pos); +} + +#define SPEEDUP + +#ifdef SPEEDUP + +/* The usual inlining tricks - don't touch unless you know what you are doing. */ +STATIC_INLINE void long_fetch_ecs (int plane, int nwords, int weird_number_of_bits, int dma) +{ + uae_u16 *real_pt = (uae_u16 *)pfield_xlateptr (bplpt[plane] + bploff[plane], nwords * 2); + int delay = ((plane & 1) ? toscr_delay2 : toscr_delay1); + int tmp_nbits = out_nbits; + uae_u32 shiftbuffer = todisplay[plane][0]; + uae_u32 outval = outword[plane]; + uae_u32 fetchval = fetched[plane]; + uae_u32 *dataptr = (uae_u32 *)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE + 4 * out_offs); + + if (dma) + bplpt[plane] += nwords * 2; + + if (real_pt == 0) + /* @@@ Don't do this, fall back on chipmem_wget instead. */ return; - if (decided_nr_planes == -1 /* Still undecided */ - || current_hpos () < thisline_decision.plfstrt + thisline_decision.plflinelen) + while (nwords > 0) { + int bits_left = 32 - tmp_nbits; + uae_u32 t; + + shiftbuffer |= fetchval; + + t = (shiftbuffer >> delay) & 0xFFFF; + + if (weird_number_of_bits && bits_left < 16) { + outval <<= bits_left; + outval |= t >> (16 - bits_left); + thisline_changed |= *dataptr ^ outval; + *dataptr++ = outval; + + outval = t; + tmp_nbits = 16 - bits_left; + shiftbuffer <<= 16; + } else { + outval = (outval << 16) | t; + shiftbuffer <<= 16; + tmp_nbits += 16; + if (tmp_nbits == 32) { + thisline_changed |= *dataptr ^ outval; + *dataptr++ = outval; + tmp_nbits = 0; + } + } + nwords--; + if (dma) { + fetchval = do_get_mem_word (real_pt); + real_pt++; + } + } + fetched[plane] = fetchval; + todisplay[plane][0] = shiftbuffer; + outword[plane] = outval; +} + +STATIC_INLINE void long_fetch_aga (int plane, int nwords, int weird_number_of_bits, int fm, int dma) +{ + uae_u32 *real_pt = (uae_u32 *)pfield_xlateptr (bplpt[plane] + bploff[plane], nwords * 2); + int delay = ((plane & 1) ? toscr_delay2 : toscr_delay1); + int tmp_nbits = out_nbits; + uae_u32 *shiftbuffer = todisplay[plane]; + uae_u32 outval = outword[plane]; + uae_u32 fetchval0 = fetched_aga0[plane]; + uae_u32 fetchval1 = fetched_aga1[plane]; + uae_u32 *dataptr = (uae_u32 *)(line_data[next_lineno] + 2 * plane * MAX_WORDS_PER_LINE + 4 * out_offs); + int offs = (16 << fm) - 16 + delay; + int off1 = offs >> 5; + if (off1 == 3) + off1 = 2; + offs -= off1 * 32; + + if (dma) + bplpt[plane] += nwords * 2; + + if (real_pt == 0) + /* @@@ Don't do this, fall back on chipmem_wget instead. */ return; - plane_decided = 1; + while (nwords > 0) { + int i; - bytecount = plflinelen / (decided_hires ? 4 : 8) * 2; + shiftbuffer[0] = fetchval0; + if (fm == 2) + shiftbuffer[1] = fetchval1; - if (bytecount > MAX_WORDS_PER_LINE * 2) { - /* Can't happen. */ - static int warned = 0; - if (!warned) - fprintf (stderr, "Mysterious bug in decide_plane(). Please report.\n"); - bytecount = 0; - } - if (!very_broken_program) { - UBYTE *dataptr = line_data[next_lineno]; - for (i = 0; i < decided_nr_planes; i++, dataptr += MAX_WORDS_PER_LINE*2) { - CPTR pt = bplpt[i]; - UBYTE *real_ptr = pfield_xlateptr(pt, bytecount); - if (real_ptr == NULL) - real_ptr = pfield_xlateptr(0, 0); -#ifdef SMART_UPDATE - if (!line_changed[next_lineno]) - line_changed[next_lineno] |= memcmpy (dataptr, real_ptr, bytecount); - else - memcpy (dataptr, real_ptr, bytecount); -#else - real_bplpt[i] = real_ptr; -#endif - } - } else { - UBYTE *dataptr = line_data[next_lineno]; - for (i = 0; i < decided_nr_planes; i++, dataptr += MAX_WORDS_PER_LINE*2) { - CPTR pt = bplpt[i]; - UBYTE *real_ptr; - - if (decided_hires) { - switch (i) { - case 3: pt -= 2; - case 2: pt -= (very_broken_program >= 3 ? 2 : 0); break; - case 1: pt -= (very_broken_program >= 2 ? 2 : 0); break; - case 0: break; - } + for (i = 0; i < (1 << fm); i++) { + int bits_left = 32 - tmp_nbits; + + uae_u32 t0 = shiftbuffer[off1]; + uae_u32 t1 = shiftbuffer[off1 + 1]; + uae_u64 t = (((uae_u64)t1) << 32) | t0; + + t0 = (t >> offs) & 0xFFFF; + + if (weird_number_of_bits && bits_left < 16) { + outval <<= bits_left; + outval |= t0 >> (16 - bits_left); + + thisline_changed |= *dataptr ^ outval; + *dataptr++ = outval; + + outval = t0; + tmp_nbits = 16 - bits_left; + aga_shift (shiftbuffer, 16, fm); } else { - switch (i) { - case 5: pt -= (very_broken_program >= 3 ? 2 : 0); break; - case 4: pt -= (very_broken_program >= 7 ? 2 : 0); break; - case 3: pt -= (very_broken_program >= 2 ? 2 : 0); break; - case 2: pt -= (very_broken_program >= 6 ? 2 : 0); break; - case 1: pt -= (very_broken_program >= 4 ? 2 : 0); break; - case 0: break; + outval = (outval << 16) | t0; + aga_shift (shiftbuffer, 16, fm); + tmp_nbits += 16; + if (tmp_nbits == 32) { + thisline_changed |= *dataptr ^ outval; + *dataptr++ = outval; + tmp_nbits = 0; } } - real_ptr = pfield_xlateptr(pt, bytecount); - if (real_ptr == NULL) - real_ptr = pfield_xlateptr(0, 0); -#ifdef SMART_UPDATE - if (!line_changed[next_lineno]) - line_changed[next_lineno] |= memcmpy (dataptr, real_ptr, bytecount); - else - memcpy (dataptr, real_ptr, bytecount); -#else - real_bplpt[i] = real_ptr; -#endif + } + + nwords -= 1 << fm; + + if (dma) { + if (fm == 1) + fetchval0 = do_get_mem_long (real_pt); + else { + fetchval1 = do_get_mem_long (real_pt); + fetchval0 = do_get_mem_long (real_pt + 1); + } + real_pt += fm; } } + fetched_aga0[plane] = fetchval0; + fetched_aga1[plane] = fetchval1; + outword[plane] = outval; } -/* - * Called from the BPLxMOD routines, after a new value has been written. - * This routine decides whether the new value is already relevant for the - * current line. - */ -static void decide_modulos (void) +static void long_fetch_ecs_0 (int hpos, int nwords, int dma) { long_fetch_ecs (hpos, nwords, 0, dma); } +static void long_fetch_ecs_1 (int hpos, int nwords, int dma) { long_fetch_ecs (hpos, nwords, 1, dma); } +static void long_fetch_aga_1_0 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 0, 1, dma); } +static void long_fetch_aga_1_1 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 1, 1, dma); } +static void long_fetch_aga_2_0 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 0, 2, dma); } +static void long_fetch_aga_2_1 (int hpos, int nwords, int dma) { long_fetch_aga (hpos, nwords, 1, 2, dma); } + +static void do_long_fetch (int hpos, int nwords, int dma, int fm) { - /* All this effort just for the Sanity WOC demo... */ - decide_line (); - if (decided_nr_planes != -1 && current_hpos() >= thisline_decision.plfstrt + thisline_decision.plflinelen) - return; - decided_bpl1mod = bpl1mod; - decided_bpl2mod = bpl2mod; + int added; + int i; + + flush_display (fm); + switch (fm) { + case 0: + if (out_nbits & 15) { + for (i = 0; i < toscr_nr_planes; i++) + long_fetch_ecs_1 (i, nwords, dma); + } else { + for (i = 0; i < toscr_nr_planes; i++) + long_fetch_ecs_0 (i, nwords, dma); + } + break; + case 1: + if (out_nbits & 15) { + for (i = 0; i < toscr_nr_planes; i++) + long_fetch_aga_1_1 (i, nwords, dma); + } else { + for (i = 0; i < toscr_nr_planes; i++) + long_fetch_aga_1_0 (i, nwords, dma); + } + break; + case 2: + if (out_nbits & 15) { + for (i = 0; i < toscr_nr_planes; i++) + long_fetch_aga_2_1 (i, nwords, dma); + } else { + for (i = 0; i < toscr_nr_planes; i++) + long_fetch_aga_2_0 (i, nwords, dma); + } + break; + } + + out_nbits += nwords * 16; + out_offs += out_nbits >> 5; + out_nbits &= 31; + + if (dma && toscr_nr_planes > 0) + fetch_state = fetch_was_plane0; } -/* - * Call decide_plane() before calling this. - */ -static void do_modulos (void) +#endif + +/* make sure fetch that goes beyond maxhpos is finished */ +static void finish_final_fetch (int i, int fm) { - /* decided_nr_planes is != -1 if this line should be drawn by the - * display hardware, regardless of whether it fits on the emulated screen. - */ - if (decided_nr_planes != -1 && plane_decided && !modulos_added - && current_hpos() >= thisline_decision.plfstrt + thisline_decision.plflinelen) - { - int bytecount = thisline_decision.plflinelen / (decided_hires ? 4 : 8) * 2; - int add1 = bytecount + decided_bpl1mod; - int add2 = bytecount + decided_bpl2mod; - - if (!very_broken_program) { - switch (decided_nr_planes) { - case 8: bplpt[7] += add2; - case 7: bplpt[6] += add1; - case 6: bplpt[5] += add2; - case 5: bplpt[4] += add1; - case 4: bplpt[3] += add2; - case 3: bplpt[2] += add1; - case 2: bplpt[1] += add2; - case 1: bplpt[0] += add1; + passed_plfstop = 3; + + if (thisline_decision.plfleft != -1) { + i += flush_plane_data (fm); + thisline_decision.plfright = i; + thisline_decision.plflinelen = out_offs; + thisline_decision.bplres = toscr_res; + finish_playfield_line (); + } +} + +STATIC_INLINE int one_fetch_cycle_0 (int i, int ddfstop_to_test, int dma, int fm) +{ + if (! passed_plfstop && i == ddfstop_to_test) + passed_plfstop = 1; + + if ((fetch_cycle & fetchunit_mask) == 0) { + if (passed_plfstop == 2) { + finish_final_fetch (i, fm); + return 1; + } + if (passed_plfstop) + passed_plfstop++; + } + if (dma) { + /* fetchstart_mask can be larger than fm_maxplane if FMODE > 0. This means + that the remaining cycles are idle; we'll fall through the whole switch + without doing anything. */ + int cycle_start = fetch_cycle & fetchstart_mask; + switch (fm_maxplane) { + case 8: + switch (cycle_start) { + case 0: fetch (7, fm); break; + case 1: fetch (3, fm); break; + case 2: fetch (5, fm); break; + case 3: fetch (1, fm); break; + case 4: fetch (6, fm); break; + case 5: fetch (2, fm); break; + case 6: fetch (4, fm); break; + case 7: fetch (0, fm); break; } - } else if (bplhires) { - switch (decided_nr_planes) { - case 8: bplpt[7] += add2; - case 7: bplpt[6] += add1; - case 6: bplpt[5] += add2; - case 5: bplpt[4] += add1; - case 4: bplpt[3] += add2 - 2; - case 3: bplpt[2] += add1 - (very_broken_program >= 3 ? 2 : 0); - case 2: bplpt[1] += add2 - (very_broken_program >= 2 ? 2 : 0); - case 1: bplpt[0] += add1; - } - } else { - switch (decided_nr_planes) { - case 8: bplpt[7] += add2; - case 7: bplpt[6] += add1; - case 6: bplpt[5] += add2 - (very_broken_program >= 3 ? 2 : 0); - case 5: bplpt[4] += add1 - (very_broken_program >= 7 ? 2 : 0); - case 4: bplpt[3] += add2 - (very_broken_program >= 2 ? 2 : 0); - case 3: bplpt[2] += add1 - (very_broken_program >= 6 ? 2 : 0); - case 2: bplpt[1] += add2 - (very_broken_program >= 4 ? 2 : 0); - case 1: bplpt[0] += add1; - } + break; + case 4: + switch (cycle_start) { + case 0: fetch (3, fm); break; + case 1: fetch (1, fm); break; + case 2: fetch (2, fm); break; + case 3: fetch (0, fm); break; + } + break; + case 2: + switch (cycle_start) { + case 0: fetch (1, fm); break; + case 1: fetch (0, fm); break; + } + break; } + } + fetch_cycle++; + toscr_nbits += 2 << toscr_res; - modulos_added = 1; + if (toscr_nbits == 16) + flush_display (fm); + if (toscr_nbits > 16) + abort (); + + return 0; +} + +static int one_fetch_cycle_fm0 (int i, int ddfstop_to_test, int dma) { return one_fetch_cycle_0 (i, ddfstop_to_test, dma, 0); } +static int one_fetch_cycle_fm1 (int i, int ddfstop_to_test, int dma) { return one_fetch_cycle_0 (i, ddfstop_to_test, dma, 1); } +static int one_fetch_cycle_fm2 (int i, int ddfstop_to_test, int dma) { return one_fetch_cycle_0 (i, ddfstop_to_test, dma, 2); } + +STATIC_INLINE int one_fetch_cycle (int i, int ddfstop_to_test, int dma, int fm) +{ + switch (fm) { + case 0: return one_fetch_cycle_fm0 (i, ddfstop_to_test, dma); + case 1: return one_fetch_cycle_fm1 (i, ddfstop_to_test, dma); + case 2: return one_fetch_cycle_fm2 (i, ddfstop_to_test, dma); + default: abort (); } } -static void decide_sprite (int spr) +STATIC_INLINE void update_fetch (int until, int fm) { - int sprxp; + int pos; + int dma = dmaen (DMA_BITPLANE); - if (framecnt != 0) + int ddfstop_to_test; + + if (framecnt != 0 || passed_plfstop == 3) return; - decide_diw (); - decide_line (); + /* We need an explicit test against HARD_DDF_STOP here to guard against + programs that move the DDFSTOP before our current position before we + reach it. */ + ddfstop_to_test = HARD_DDF_STOP; + if (ddfstop >= last_fetch_hpos && ddfstop < HARD_DDF_STOP) + ddfstop_to_test = ddfstop; + + compute_toscr_delay (last_fetch_hpos); + update_toscr_planes (); + + pos = last_fetch_hpos; + cycle_diagram_shift = (last_fetch_hpos - fetch_cycle) & 7; + + /* First, a loop that prepares us for the speedup code. We want to enter + the SPEEDUP case with fetch_state == fetch_was_plane0, and then unroll + whole blocks, so that we end on the same fetch_state again. */ + for (; ; pos++) { + if (pos == until) { + if (until >= maxhpos && passed_plfstop == 2) { + finish_final_fetch (pos, fm); + return; + } + flush_display (fm); + return; + } + + if (fetch_state == fetch_was_plane0) + break; + + fetch_state = fetch_started; + if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm)) + return; + } + +#ifdef SPEEDUP + /* Unrolled version of the for loop below. */ + if (! passed_plfstop + && dma + && (fetch_cycle & fetchstart_mask) == (fm_maxplane & fetchstart_mask) +# if 0 + /* @@@ We handle this case, but the code would be simpler if we + * disallowed it - it may even be possible to guarantee that + * this condition never is false. Later. */ + && (out_nbits & 15) == 0 +# endif + && toscr_nr_planes == thisline_decision.nr_planes) + { + int offs = (pos - fetch_cycle) & fetchunit_mask; + int ddf2 = ((ddfstop_to_test - offs + fetchunit - 1) & ~fetchunit_mask) + offs; + int ddf3 = ddf2 + fetchunit; + int stop = until < ddf2 ? until : until < ddf3 ? ddf2 : ddf3; + int count; + + count = stop - pos; + + if (count >= fetchstart) { + count &= ~fetchstart_mask; + + if (thisline_decision.plfleft == -1) { + compute_delay_offset (pos); + compute_toscr_delay_1 (); + } + do_long_fetch (pos, count >> (3 - toscr_res), dma, fm); + + /* This must come _after_ do_long_fetch so as not to confuse flush_display + into thinking the first fetch has produced any output worth emitting to + the screen. But the calculation of delay_offset must happen _before_. */ + maybe_first_bpl1dat (pos); - if (!sprarmed[spr] || thisline_decision.which != 1 || current_hpos () < 0x14) + if (pos <= ddfstop_to_test && pos + count > ddfstop_to_test) + passed_plfstop = 1; + if (pos <= ddfstop_to_test && pos + count > ddf2) + passed_plfstop = 2; + pos += count; + fetch_cycle += count; + } + } +#endif + for (; pos < until; pos++) { + if (fetch_state == fetch_was_plane0) + beginning_of_plane_block (pos, dma, fm); + fetch_state = fetch_started; + + if (one_fetch_cycle (pos, ddfstop_to_test, dma, fm)) + return; + } + if (until >= maxhpos && passed_plfstop == 2) { + finish_final_fetch (pos, fm); return; + } + flush_display (fm); +} + +static void update_fetch_0 (int hpos) { update_fetch (hpos, 0); } +static void update_fetch_1 (int hpos) { update_fetch (hpos, 1); } +static void update_fetch_2 (int hpos) { update_fetch (hpos, 2); } + +STATIC_INLINE void decide_fetch (int hpos) +{ + if (fetch_state != fetch_not_started && hpos > last_fetch_hpos) { + switch (fetchmode) { + case 0: update_fetch_0 (hpos); break; + case 1: update_fetch_1 (hpos); break; + case 2: update_fetch_2 (hpos); break; + default: abort (); + } + } + last_fetch_hpos = hpos; +} - sprxp = sprxpos[spr]; - /* We _must_ check at the start of the sprite - we might avoid some - * unnecessary redraws by testing at the end, but that doesn't always - * work. */ - if (sprxp > PIXEL_XPOS (current_hpos ()) || sprite_last_drawn_at[spr] >= sprxp) +/* This function is responsible for turning on datafetch if necessary. */ +STATIC_INLINE void decide_line (int hpos) +{ + if (hpos <= last_decide_line_hpos) return; - - sprite_last_drawn_at[spr] = sprxp; + if (fetch_state != fetch_not_started) + return; + + /* Test if we passed the start of the DDF window. */ + if (last_decide_line_hpos < plfstrt && hpos >= plfstrt) { + /* First, take care of the vertical DIW. Surprisingly enough, this seems to be + correct here - putting this into decide_diw() results in garbage. */ + if (diwstate == DIW_waiting_start && vpos == plffirstline) { + diwstate = DIW_waiting_stop; + } + if (diwstate == DIW_waiting_stop && vpos == plflastline) { + diwstate = DIW_waiting_start; + } + + /* If DMA isn't on by the time we reach plfstrt, then there's no + bitplane DMA at all for the whole line. */ + if (dmaen (DMA_BITPLANE) + && diwstate == DIW_waiting_stop) + { + fetch_state = fetch_started; + fetch_cycle = 0; + last_fetch_hpos = plfstrt; + out_nbits = 0; + out_offs = 0; + toscr_nbits = 0; + + compute_toscr_delay (last_fetch_hpos); + + /* If someone already wrote BPL1DAT, clear the area between that point and + the real fetch start. */ + if (framecnt == 0) { + if (thisline_decision.plfleft != -1) { + out_nbits = (plfstrt - thisline_decision.plfleft) << (1 + toscr_res); + out_offs = out_nbits >> 5; + out_nbits &= 31; + } + update_toscr_planes (); + } + estimate_last_fetch_cycle (plfstrt); + last_decide_line_hpos = hpos; + do_sprites (vpos, plfstrt); + return; + } + } - /* Ignore sprites outside the DIW. There is a small problem here when - * we haven't decided diwstart yet - but that should be very rare and - * drawing one sprite too much doesn't hurt all that much. - * We don't draw sprites with zero data, unless they might be attached - * sprites. */ - if (sprxp < 0 - || (thisline_decision.diwfirstword >= 0 && sprxp + sprite_width < thisline_decision.diwfirstword) - || (thisline_decision.diwlastword >= 0 && sprxp > thisline_decision.diwlastword) - || (sprdata[spr] == 0 && sprdatb[spr] == 0 && (sprctl[(spr & ~1)+1] & 0x80) == 0)) + if (last_decide_line_hpos < 0x34) + do_sprites (vpos, hpos); + + last_decide_line_hpos = hpos; +} + +/* Called when a color is about to be changed (write to a color register), + * but the new color has not been entered into the table yet. */ +static void record_color_change (int hpos, int regno, unsigned long value) +{ + /* Early positions don't appear on-screen. */ + if (framecnt != 0 || vpos < minfirstline || hpos < 0x18 + /*|| currprefs.emul_accuracy == 0*/) return; + + decide_diw (hpos); + decide_line (hpos); + + if (thisline_decision.ctable == -1) + remember_ctable (); + #ifdef OS_WITHOUT_MEMORY_MANAGEMENT - if(next_sprite_draw >= _max_sprite_draw) { - _max_sprite_draw = next_sprite_draw; - return; + if (next_color_change >= max_color_change) { + ++delta_color_change; + return; } #endif - curr_sprite_positions[next_sprite_draw].linepos = sprxp; - curr_sprite_positions[next_sprite_draw].num = spr; - curr_sprite_positions[next_sprite_draw].ctl = sprctl[spr]; - curr_sprite_positions[next_sprite_draw].data = sprdata[spr]; - curr_sprite_positions[next_sprite_draw++].datb = sprdatb[spr]; + curr_color_changes[next_color_change].linepos = hpos; + curr_color_changes[next_color_change].regno = regno; + curr_color_changes[next_color_change++].value = value; +} + +typedef int sprbuf_res_t, cclockres_t, hwres_t, bplres_t; + +static void do_playfield_collisions (void) +{ + uae_u8 *ld = line_data[next_lineno]; + int i; + + if (clxcon_bpl_enable == 0) { + clxdat |= 1; + return; + } + + for (i = thisline_decision.plfleft; i < thisline_decision.plfright; i += 2) { + int j; + uae_u32 total = 0xFFFFFFFF; + for (j = 0; j < 8; j++) { + uae_u32 t = 0; + if ((clxcon_bpl_enable & (1 << j)) == 0) + t = 0xFFFFFFFF; + else if (j < thisline_decision.nr_planes) { + t = *(uae_u32 *)(line_data[next_lineno] + 2 * i + 2 * j * MAX_WORDS_PER_LINE); + t ^= ~(((clxcon_bpl_match >> j) & 1) - 1); + } + total &= t; + } + if (total) + clxdat |= 1; + } } -static __inline__ void decide_sprite_1 (int spr) +/* Sprite-to-sprite collisions are taken care of in record_sprite. This one does + playfield/sprite collisions. + That's the theory. In practice this doesn't work yet. I also suspect this code + is way too slow. */ +static void do_sprite_collisions (void) { - int sprxp = sprxpos[spr]; + int nr_sprites = curr_drawinfo[next_lineno].nr_sprites; + int first = curr_drawinfo[next_lineno].first_sprite_entry; + int i; + unsigned int collision_mask = clxmask[clxcon >> 12]; + int bplres = GET_RES (bplcon0); + hwres_t ddf_left = thisline_decision.plfleft * 2 << bplres; + hwres_t hw_diwlast = coord_window_to_diw_x (thisline_decision.diwlastword); + hwres_t hw_diwfirst = coord_window_to_diw_x (thisline_decision.diwfirstword); - if (sprxp > max_diwlastword || sprite_last_drawn_at[spr] >= sprxp - || sprxp < 0 - || (thisline_decision.diwfirstword >= 0 && sprxp + sprite_width < thisline_decision.diwfirstword) - || (thisline_decision.diwlastword >= 0 && sprxp > thisline_decision.diwlastword) - || (sprdata[spr] == 0 && sprdatb[spr] == 0 && (sprctl[(spr & ~1)+1] & 0x80) == 0)) + if (clxcon_bpl_enable == 0) { + clxdat |= 0x1FE; return; + } + + for (i = 0; i < nr_sprites; i++) { + struct sprite_entry *e = curr_sprite_entries + first + i; + sprbuf_res_t j; + sprbuf_res_t minpos = e->pos; + sprbuf_res_t maxpos = e->max; + hwres_t minp1 = minpos >> sprite_buffer_res; + hwres_t maxp1 = maxpos >> sprite_buffer_res; + + if (maxp1 > hw_diwlast) + maxpos = hw_diwlast << sprite_buffer_res; + if (maxp1 > thisline_decision.plfright * 2) + maxpos = thisline_decision.plfright * 2 << sprite_buffer_res; + if (minp1 < hw_diwfirst) + minpos = hw_diwfirst << sprite_buffer_res; + if (minp1 < thisline_decision.plfleft * 2) + minpos = thisline_decision.plfleft * 2 << sprite_buffer_res; + + for (j = minpos; j < maxpos; j++) { + int sprpix = spixels[e->first_pixel + j - e->pos] & collision_mask; + int k; + int offs; + + if (sprpix == 0) + continue; + + offs = ((j << bplres) >> sprite_buffer_res) - ddf_left; + sprpix = sprite_ab_merge[sprpix & 255] | (sprite_ab_merge[sprpix >> 8] << 2); + sprpix <<= 1; + + /* Loop over number of playfields. */ + for (k = 0; k < 2; k++) { + int l; + int match = 1; + int planes = ((currprefs.chipset_mask & CSMASK_AGA) ? 8 : 6); + + for (l = k; match && l < planes; l += 2) { + if (clxcon_bpl_enable & (1 << l)) { + 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 (t != ((clxcon_bpl_match >> l) & 1)) + match = 0; + } + } + if (match) + clxdat |= sprpix; + sprpix <<= 4; + } + } + } +} + +static void expand_sprres (void) +{ + switch ((bplcon3 >> 6) & 3) { + case 0: /* ECS defaults (LORES,HIRES=140ns,SHRES=70ns) */ + if ((currprefs.chipset_mask & CSMASK_ECS_DENISE) && GET_RES (bplcon0) == RES_SUPERHIRES) + sprres = RES_HIRES; + else + sprres = RES_LORES; + break; + case 1: + sprres = RES_LORES; + break; + case 2: + sprres = RES_HIRES; + break; + case 3: + sprres = RES_SUPERHIRES; + break; + } +} + +STATIC_INLINE void record_sprite_1 (uae_u16 *buf, uae_u32 datab, int num, int dbl, + unsigned int mask, int do_collisions, uae_u32 collision_mask) +{ + int j = 0; + while (datab) { + unsigned int tmp = *buf; + unsigned int col = (datab & 3) << (2 * num); + tmp |= col; + if ((j & mask) == 0) + *buf++ = tmp; + if (dbl) + *buf++ = tmp; + j++; + datab >>= 2; + if (do_collisions) { + tmp &= collision_mask; + if (tmp) { + unsigned int shrunk_tmp = sprite_ab_merge[tmp & 255] | (sprite_ab_merge[tmp >> 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 ECS), or in hires pixels + (if AGA). No support for SHRES sprites. */ + +static void record_sprite (int line, int num, int sprxp, uae_u16 *data, uae_u16 *datb, unsigned int ctl) +{ + struct sprite_entry *e = curr_sprite_entries + next_sprite_entry; + int i; + int word_offs; + uae_u16 *buf; + uae_u32 collision_mask; + int width = sprite_width; + int dbl = 0; + unsigned int mask = 0; + + if (sprres != RES_LORES) + thisline_decision.any_hires_sprites = 1; + + if (currprefs.chipset_mask & CSMASK_AGA) { + width = (width << 1) >> sprres; + dbl = sprite_buffer_res - sprres; + mask = sprres == RES_SUPERHIRES ? 1 : 0; + } + + /* Try to coalesce entries if they aren't too far apart. */ + if (! next_sprite_forced && e[-1].max + 16 >= sprxp) { + e--; + } else { + next_sprite_entry++; + e->pos = sprxp; + e->has_attached = 0; + } + + if (sprxp < e->pos) + abort (); + + e->max = sprxp + width; + e[1].first_pixel = e->first_pixel + ((e->max - e->pos + 3) & ~3); + next_sprite_forced = 0; + + collision_mask = clxmask[clxcon >> 12]; + word_offs = e->first_pixel + sprxp - e->pos; + + for (i = 0; i < sprite_width; i += 16) { + unsigned int da = *data; + unsigned int db = *datb; + uae_u32 datab = ((sprtaba[da & 0xFF] << 16) | sprtaba[da >> 8] + | (sprtabb[db & 0xFF] << 16) | sprtabb[db >> 8]); + + buf = spixels + word_offs + (i << dbl); + if (currprefs.collision_level > 0 && collision_mask) + record_sprite_1 (buf, datab, num, dbl, mask, 1, collision_mask); + else + record_sprite_1 (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 (ctl & (num << 7) & 0x80) { + uae_u32 state = 0x01010101 << (num - 1); + uae_u32 *stbuf = spixstate.words + (word_offs >> 2); + uae_u8 *stb1 = spixstate.bytes + word_offs; + for (i = 0; i < width; i += 8) { + stb1[0] |= state; + stb1[1] |= state; + stb1[2] |= state; + stb1[3] |= state; + stb1[4] |= state; + stb1[5] |= state; + stb1[6] |= state; + stb1[7] |= state; + stb1 += 8; + } + e->has_attached = 1; + } +} + +static void decide_sprites (int hpos) +{ + int nrs[MAX_SPRITES], posns[MAX_SPRITES]; + int count, i; + int point = hpos * 2; + int width = sprite_width; + int window_width = (width << lores_shift) >> sprres; + + if (framecnt != 0 || hpos < 0x14 || nr_armed == 0 || point == last_sprite_point) + return; + + decide_diw (hpos); + decide_line (hpos); + +#if 0 + /* This tries to detect whether the line is border, but that doesn't work, it's too early. */ + if (thisline_decision.plfleft == -1) + return; +#endif + count = 0; + for (i = 0; i < MAX_SPRITES; i++) { + int sprxp = spr[i].xpos; + int hw_xp = (sprxp >> sprite_buffer_res); + int window_xp = coord_hw_to_window_x (hw_xp) + (DIW_DDF_OFFSET << lores_shift); + int j, bestp; + + /* ??? It is uncertain where exactly sprite data is needed. It appears + to be used some time after the actual position given in SPRxPOS. + How soon after is an open question. For Battle Squadron, using a + value of 5 seems to be enough to meet all timing constraints. + This gives it 2 more color clock cycles until the data appears on + the screen. */ + hw_xp += 5; + if (! spr[i].armed || sprxp < 0 || hw_xp <= last_sprite_point || hw_xp > point) + continue; + if ((thisline_decision.diwfirstword >= 0 && window_xp + window_width < thisline_decision.diwfirstword) + || (thisline_decision.diwlastword >= 0 && window_xp > thisline_decision.diwlastword)) + continue; - curr_sprite_positions[next_sprite_draw].linepos = sprxp; - curr_sprite_positions[next_sprite_draw].num = spr; - curr_sprite_positions[next_sprite_draw].ctl = sprctl[spr]; - curr_sprite_positions[next_sprite_draw].data = sprdata[spr]; - curr_sprite_positions[next_sprite_draw++].datb = sprdatb[spr]; + /* 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] < i) + break; + } + for (j = count; j > bestp; j--) { + posns[j] = posns[j-1]; + nrs[j] = nrs[j-1]; + } + posns[j] = sprxp; + nrs[j] = i; + count++; + } + for (i = 0; i < count; i++) { + int nr = nrs[i]; + record_sprite (next_lineno, nr, spr[nr].xpos, sprdata[nr], sprdatb[nr], sprctl[nr]); + } + last_sprite_point = point; } +STATIC_INLINE int sprites_differ (struct draw_info *dip, struct draw_info *dip_old) +{ + struct sprite_entry *this_first = curr_sprite_entries + dip->first_sprite_entry; + struct sprite_entry *this_last = curr_sprite_entries + dip->last_sprite_entry; + struct sprite_entry *prev_first = prev_sprite_entries + dip_old->first_sprite_entry; + int npixels; + int i; + + if (dip->nr_sprites != dip_old->nr_sprites) + return 1; + + if (dip->nr_sprites == 0) + return 0; + + for (i = 0; i < dip->nr_sprites; i++) + if (this_first[i].pos != prev_first[i].pos + || this_first[i].max != prev_first[i].max + || this_first[i].has_attached != prev_first[i].has_attached) + return 1; + + npixels = this_last->first_pixel + (this_last->max - this_last->pos) - this_first->first_pixel; + if (memcmp (spixels + this_first->first_pixel, spixels + prev_first->first_pixel, + npixels * sizeof (uae_u16)) != 0) + return 1; + if (memcmp (spixstate.bytes + this_first->first_pixel, spixstate.bytes + prev_first->first_pixel, npixels) != 0) + return 1; + return 0; +} + +STATIC_INLINE int color_changes_differ (struct draw_info *dip, struct draw_info *dip_old) +{ + if (dip->nr_color_changes != dip_old->nr_color_changes) + return 1; + + if (dip->nr_color_changes == 0) + return 0; + if (memcmp (curr_color_changes + dip->first_color_change, + prev_color_changes + dip_old->first_color_change, + dip->nr_color_changes * sizeof *curr_color_changes) != 0) + return 1; + return 0; +} + +/* End of a horizontal scan line. Finish off all decisions that were not + * made yet. */ static void finish_decisions (void) { struct draw_info *dip; struct draw_info *dip_old; struct decision *dp; int changed; - int i; + int hpos = current_hpos (); if (framecnt != 0) return; - decide_diw (); - if (thisline_decision.which == 0) - decide_line_1 (); - if (line_decisions[next_lineno].which != thisline_decision.which) - line_changed[next_lineno] = 1; - decide_plane (); + decide_diw (hpos); + decide_line (hpos); + decide_fetch (hpos); + + if (thisline_decision.plfleft != -1 && thisline_decision.plflinelen == -1) { + if (fetch_state != fetch_not_started) + abort (); + thisline_decision.plfright = thisline_decision.plfleft; + thisline_decision.plflinelen = 0; + thisline_decision.bplres = RES_LORES; + } + + /* Large DIWSTOP values can cause the stop position never to be + * reached, so the state machine always stays in the same state and + * there's a more-or-less full-screen DIW. */ + if (hdiwstate == DIW_waiting_stop || thisline_decision.diwlastword > max_diwlastword) + 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 = line_changed[next_lineno]; + changed = thisline_changed; - if (thisline_decision.which == 1) { - if (diwlastword > max_diwstop) - max_diwstop = diwlastword; - if (diwfirstword < min_diwstart) - min_diwstart = diwfirstword; - - if (sprarmed[0]) decide_sprite_1 (0); - if (sprarmed[1]) decide_sprite_1 (1); - if (sprarmed[2]) decide_sprite_1 (2); - if (sprarmed[3]) decide_sprite_1 (3); - if (sprarmed[4]) decide_sprite_1 (4); - if (sprarmed[5]) decide_sprite_1 (5); - if (sprarmed[6]) decide_sprite_1 (6); - if (sprarmed[7]) decide_sprite_1 (7); - - if (thisline_decision.bplcon1 != line_decisions[next_lineno].bplcon1) - changed = 1; + if (thisline_decision.plfleft != -1) { + record_diw_line (thisline_decision.diwfirstword, thisline_decision.diwlastword); + + decide_sprites (hpos); } + dip->last_sprite_entry = next_sprite_entry; dip->last_color_change = next_color_change; - dip->last_delay_change = next_delay_change; - dip->last_sprite_draw = next_sprite_draw; - - /* Must do this for border also... this should be more clever. FIXME */ - if (/*thisline_decision.which == 1 && */thisline_decision.ctable == -1) - remember_ctable (); - if (thisline_decision.which == -1 && thisline_decision.color0 == 0xFFFFFFFFul) - thisline_decision.color0 = current_colors.color_regs[0]; + + if (thisline_decision.ctable == -1) { + if (thisline_decision.plfleft == -1) + remember_ctable_for_border (); + else + remember_ctable (); + } dip->nr_color_changes = next_color_change - dip->first_color_change; - dip->nr_sprites = next_sprite_draw - dip->first_sprite_draw; + dip->nr_sprites = next_sprite_entry - dip->first_sprite_entry; - if (dip->first_delay_change != dip->last_delay_change) + if (thisline_decision.plfleft != line_decisions[next_lineno].plfleft) changed = 1; - if (!changed - && (dip->nr_color_changes != dip_old->nr_color_changes - || (dip->nr_color_changes > 0 - && fast_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))) + if (! changed && color_changes_differ (dip, dip_old)) changed = 1; - if (!changed && thisline_decision.which == 1 - && (dip->nr_sprites != dip_old->nr_sprites - || (dip->nr_sprites > 0 - && fast_memcmp (curr_sprite_positions + dip->first_sprite_draw, - prev_sprite_positions + dip_old->first_sprite_draw, - dip->nr_sprites * sizeof *curr_sprite_positions) != 0))) + if (!changed && thisline_decision.plfleft != -1 && sprites_differ (dip, dip_old)) changed = 1; if (changed) { - line_changed[next_lineno] = 1; + thisline_changed = 1; *dp = thisline_decision; } else /* The only one that may differ: */ dp->ctable = thisline_decision.ctable; } +/* Set the state of all decisions to "undecided" for a new scanline. */ static void reset_decisions (void) { - int i; - if (framecnt != 0) return; - thisline_decision.which = 0; - decided_bpl1mod = bpl1mod; - decided_bpl2mod = bpl2mod; - decided_nr_planes = -1; - /* decided_hires shouldn't be touched before it's initialized by decide_line(). */ + thisline_decision.any_hires_sprites = 0; + thisline_decision.nr_planes = 0; + + thisline_decision.plfleft = -1; + thisline_decision.plflinelen = -1; + + /* decided_res shouldn't be touched before it's initialized by decide_line(). */ thisline_decision.diwfirstword = -1; thisline_decision.diwlastword = -2; + if (hdiwstate == DIW_waiting_stop) { + thisline_decision.diwfirstword = 0; + if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword) + MARK_LINE_CHANGED; + } thisline_decision.ctable = -1; - thisline_decision.color0 = 0xFFFFFFFFul; - line_changed[next_lineno] = 0; + thisline_changed = 0; curr_drawinfo[next_lineno].first_color_change = next_color_change; - curr_drawinfo[next_lineno].first_delay_change = next_delay_change; - curr_drawinfo[next_lineno].first_sprite_draw = next_sprite_draw; - - memset(sprite_last_drawn_at, 0, sizeof sprite_last_drawn_at); - modulos_added = 0; - plane_decided = 0; - color_decided = 0; - very_broken_program = 0; -} + curr_drawinfo[next_lineno].first_sprite_entry = next_sprite_entry; + next_sprite_forced = 1; -static void init_decisions (void) -{ - size_t i; - for (i = 0; i < sizeof line_decisions / sizeof *line_decisions; i++) { - line_decisions[i].which = -2; + /* memset(sprite_last_drawn_at, 0, sizeof sprite_last_drawn_at); */ + last_sprite_point = 0; + fetch_state = fetch_not_started; + passed_plfstop = 0; + + memset (todisplay, 0, sizeof todisplay); + memset (fetched, 0, sizeof fetched); + memset (fetched_aga0, 0, sizeof fetched_aga0); + memset (fetched_aga1, 0, sizeof fetched_aga1); + memset (outword, 0, sizeof outword); + + last_decide_line_hpos = -1; + last_diw_pix_hpos = -1; + last_ddf_pix_hpos = -1; + last_sprite_hpos = -1; + last_fetch_hpos = -1; +} + +/* set PAL or NTSC timing variables */ + +static void init_hz (void) +{ + int isntsc; + + beamcon0 = new_beamcon0; + + isntsc = beamcon0 & 0x20 ? 0 : 1; + if (!isntsc) { + maxvpos = MAXVPOS_PAL; + maxhpos = MAXHPOS_PAL; + minfirstline = MINFIRSTLINE_PAL; + vblank_endline = VBLANK_ENDLINE_PAL; + vblank_hz = VBLANK_HZ_PAL; + } else { + maxvpos = MAXVPOS_NTSC; + maxhpos = MAXHPOS_NTSC; + minfirstline = MINFIRSTLINE_NTSC; + vblank_endline = VBLANK_ENDLINE_NTSC; + vblank_hz = VBLANK_HZ_NTSC; } + vsynctime = syncbase / vblank_hz; + + write_log ("Using %s timing\n", isntsc ? "NTSC" : "PAL"); } static void calcdiw (void) { - diwfirstword = ((diwstrt & 0xFF) - DISPLAY_LEFT_SHIFT - 1) << lores_shift; - diwlastword = ((diwstop & 0xFF) + 0x100 - DISPLAY_LEFT_SHIFT - 1) << lores_shift; + int hstrt = diwstrt & 0xFF; + int hstop = diwstop & 0xFF; + int vstrt = diwstrt >> 8; + int vstop = diwstop >> 8; + + if (diwhigh_written) { + hstrt |= ((diwhigh >> 5) & 1) << 8; + hstop |= ((diwhigh >> 13) & 1) << 8; + vstrt |= (diwhigh & 7) << 8; + vstop |= ((diwhigh >> 8) & 7) << 8; + } else { + hstop += 0x100; + if ((vstop & 0x80) == 0) + vstop |= 0x100; + } - if (diwlastword > max_diwlastword) - diwlastword = max_diwlastword; + diwfirstword = coord_diw_to_window_x (hstrt); + diwlastword = coord_diw_to_window_x (hstop); if (diwfirstword < 0) diwfirstword = 0; - if (diwlastword < 0) - diwlastword = 0; - - plffirstline = diwstrt >> 8; - plflastline = diwstop >> 8; + + plffirstline = vstrt; + plflastline = vstop; + #if 0 /* This happens far too often. */ if (plffirstline < minfirstline) { @@ -1062,823 +1926,640 @@ static void calcdiw (void) plffirstline = minfirstline; } #endif - if ((plflastline & 0x80) == 0) plflastline |= 0x100; + #if 0 /* Turrican does this */ if (plflastline > 313) { - fprintf(stderr, "Warning: Playfield out of range!\n"); + fprintf (stderr, "Warning: Playfield out of range!\n"); plflastline = 313; } #endif plfstrt = ddfstrt; plfstop = ddfstop; - if (plfstrt < 0x18) plfstrt = 0x18; - if (plfstop > 0xD8) plfstop = 0xD8; - if (plfstrt > plfstop) plfstrt = plfstop; - - /* ! If the masking operation is changed, the pfield_doline code could break - * on some systems (alignment) */ - /* This actually seems to be correct now, at least the non-AGA stuff... */ - plfstrt &= ~3; - plfstop &= ~3; - /* @@@ Start looking for AGA bugs here... (or maybe even in the above masking ops) */ - if ((fmode & 3) == 0) - plflinelen = (plfstop-plfstrt+15) & ~7; - else if ((fmode & 3) == 3) - plflinelen = (plfstop-plfstrt+63) & ~31; - else - plflinelen = (plfstop-plfstrt+31) & ~15; + if (plfstrt < 0x18) + plfstrt = 0x18; } -/* - * Screen update macros/functions - */ +/* Mousehack stuff */ -static void decode_ham6 (int pix, int stoppos) -{ - static UWORD lastcolor; - ULONG *buf = ham_linebuf; +#define defstepx (1<<16) +#define defstepy (1<<16) +#define defxoffs 0 +#define defyoffs 0 - if (!bplham || bplplanecnt != 6) - return; - - if (stoppos > dp_for_drawing->diwlastword) - stoppos = dp_for_drawing->diwlastword; - if (pix < dp_for_drawing->diwfirstword) { - lastcolor = colors_for_drawing.color_regs[0]; - pix = dp_for_drawing->diwfirstword; - } -#ifdef LORES_HACK - if (gfx_requested_lores == 2) - pix <<= 1, stoppos <<= 1; -#endif - while (pix < stoppos) { - int pv = pixdata.apixels[pix]; - switch(pv & 0x30) { - case 0x00: lastcolor = colors_for_drawing.color_regs[pv]; break; - case 0x10: lastcolor &= 0xFF0; lastcolor |= (pv & 0xF); break; - case 0x20: lastcolor &= 0x0FF; lastcolor |= (pv & 0xF) << 8; break; - case 0x30: lastcolor &= 0xF0F; lastcolor |= (pv & 0xF) << 4; break; - } +static const int docal = 60, xcaloff = 40, ycaloff = 20; +static const int calweight = 3; +static int lastsampledmx, lastsampledmy; +static int lastspr0x,lastspr0y,lastdiffx,lastdiffy,spr0pos,spr0ctl; +static int mstepx,mstepy,xoffs=defxoffs,yoffs=defyoffs; +static int sprvbfl; - buf[pix++] = lastcolor; - } -} -#if 0 -static void decode_ham_aga (int pix, int stoppos) -{ - static ULONG lastcolor; - ULONG *buf = ham_linebuf; +int lastmx, lastmy; +int newmousecounters; +int ievent_alive = 0; - if (!bplham || (bplplanecnt != 6 && bplplanecnt != 8)) - return; - - if (pix <= dp_for_drawing->diwfirstword) { - pix = dp_for_drawing->diwfirstword; - lastcolor = colors_for_drawing.color_regs[0]; - } - - if (dp_for_drawing->bplplanecnt == 6) { - /* HAM 6 */ - while (pix < dp_for_drawing->diwlastword && pix < stoppos) { - int pv = pixdata.apixels[pix]; - switch(pv & 0x30) { - case 0x00: lastcolor = colors_for_drawing.color_regs[pv]; break; - case 0x10: lastcolor &= 0xFFFF00; lastcolor |= (pv & 0xF)*0x11; break; - case 0x20: lastcolor &= 0x00FFFF; lastcolor |= (pv & 0xF)*0x11 << 16; break; - case 0x30: lastcolor &= 0xFF00FF; lastcolor |= (pv & 0xF)*0x11 << 8; break; - } - buf[pix++] = lastcolor; - } - } else if (dp_for_drawing->bplplanecnt == 8) { - /* HAM 8 */ - while (pix < dp_for_drawing->diwlastword && pix < stoppos) { - int pv = pixdata.apixels[pix]; - switch(pv & 0x3) { - case 0x0: lastcolor = colors_for_drawing.color_regs[pv >> 2]; break; - case 0x1: lastcolor &= 0xFFFF03; lastcolor |= (pv & 0xFC); break; - case 0x2: lastcolor &= 0x03FFFF; lastcolor |= (pv & 0xFC) << 16; break; - case 0x3: lastcolor &= 0xFF03FF; lastcolor |= (pv & 0xFC) << 8; break; - } - buf[pix++] = lastcolor; - } - } -} -#endif - -#if AGA_CHIPSET != 0 -/* WARNING: Not too much of this will work correctly yet. */ +static enum { unknown_mouse, normal_mouse, dont_care_mouse, follow_mouse } mousestate; -static void pfield_linetoscr_aga(int pix, int stoppos) +static void mousehack_setdontcare (void) { - ULONG *buf = aga_lbufptr; - int i; - int xor = (UBYTE)(bplcon4 >> 8); - int oldpix = pix; \ - - buf -= pix; \ + if (mousestate == dont_care_mouse) + return; - for (i = 0; i < stoppos; i++) - pixdata.apixels[i] ^= xor; - - while (pix < diwfirstword && pix < stoppos) { - buf[pix++] = colors_for_drawing.color_regs[0]; - } - if (bplham) { - while (pix < diwlastword && pix < stoppos) { - ULONG d = ham_linebuf[pix]; - buf[pix] = d; - pix++; - } - } else if (bpldualpf) { - /* Dual playfield */ - int *lookup = bpldualpfpri ? dblpf_aga2 : dblpf_aga1; - int *lookup_no = bpldualpfpri ? dblpf_2nd2 : dblpf_2nd1; - while (pix < diwlastword && pix < stoppos) { - int pixcol = pixdata.apixels[pix]; - int pfno = lookup_no[pixcol]; - - if (spixstate[pix]) { - buf[pix] = colors_for_drawing.color_regs[pixcol]; - } else { - int val = lookup[pixdata.apixels[pix]]; - if (pfno == 2) - val += dblpfofs[(bplcon2 >> 10) & 7]; - buf[pix] = colors_for_drawing.color_regs[val]; - } - pix++; - } - } else if (bplehb) { - while (pix < diwlastword && pix < stoppos) { - int pixcol = pixdata.apixels[pix]; - ULONG d = colors_for_drawing.color_regs[pixcol]; - /* What about sprites? */ - if (pixcol & 0x20) - d = (d & 0x777777) >> 1; - buf[pix] = d; - pix++; - } - } else { - while (pix < diwlastword && pix < stoppos) { - int pixcol = pixdata.apixels[pix]; - buf[pix] = colors_for_drawing.color_regs[pixcol]; - pix++; - } - } - while (pix < stoppos) { - buf[pix++] = colors_for_drawing.color_regs[0]; - } - aga_lbufptr += pix - oldpix; + write_log ("Don't care mouse mode set\n"); + mousestate = dont_care_mouse; + lastspr0x = lastmx; lastspr0y = lastmy; + mstepx = defstepx; mstepy = defstepy; } -static void aga_translate32(int start, int stop) +static void mousehack_setfollow (void) { - memcpy (((ULONG *)xlinebuffer) + start, aga_linebuf + start, 4*(stop-start)); -} + if (mousestate == follow_mouse) + return; -static void aga_translate16(int start, int stop) -{ - int i; - for (i = start; i < stop; i++) { - ULONG d = aga_linebuf[i]; - UWORD v = ((d & 0xF0) >> 4) | ((d & 0xF000) >> 8) | ((d & 0xF00000) >> 12); - ((UWORD *)xlinebuffer)[i] = xcolors[v]; - } + write_log ("Follow sprite mode set\n"); + mousestate = follow_mouse; + lastdiffx = lastdiffy = 0; + sprvbfl = 0; + spr0ctl = spr0pos = 0; + mstepx = defstepx; mstepy = defstepy; } -static void aga_translate8(int start, int stop) +static uae_u32 mousehack_helper (void) { - int i; - for (i = start; i < stop; i++) { - ULONG d = aga_linebuf[i]; - UWORD v = ((d & 0xF0) >> 4) | ((d & 0xF000) >> 8) | ((d & 0xF00000) >> 12); - ((UBYTE *)xlinebuffer)[i] = xcolors[v]; - } -} -#endif - -static int linetoscr_double_offset; - -#define LINE_TO_SCR(NAME, TYPE, DO_DOUBLE) \ -static void NAME(int pix, int lframe_end, int diw_end, int stoppos) \ -{ \ - TYPE *buf = ((TYPE *)xlinebuffer); \ - int oldpix = pix; \ - /* These are different for register-allocation purposes. */ \ - TYPE d1, d2; \ - int offset; \ -\ - if (DO_DOUBLE) offset = linetoscr_double_offset / sizeof(TYPE); \ -\ - d1 = colors_for_drawing.acolors[0]; \ - while (pix < lframe_end) { \ - buf[pix] = d1; if (DO_DOUBLE) buf[pix+offset] = d1; \ - pix++; \ - } \ - if (bplham && bplplanecnt == 6) { \ - /* HAM 6 */ \ - while (pix < diw_end) { \ - TYPE d = xcolors[ham_linebuf[pix]]; \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - } else if (bpldualpf) { \ - /* Dual playfield */ \ - int *lookup = bpldualpfpri ? dblpf_ind2 : dblpf_ind1; \ - while (pix < diw_end) { \ - int pixcol = pixdata.apixels[pix]; \ - TYPE d; \ - if (spixstate[pix]) { \ - d = colors_for_drawing.acolors[pixcol]; \ - } else { \ - d = colors_for_drawing.acolors[lookup[pixcol]]; \ - } \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - } else if (bplehb) { \ - while (pix < diw_end) { \ - int p = pixdata.apixels[pix]; \ - TYPE d = colors_for_drawing.acolors[p]; \ - if (p > 32) d = xcolors[(colors_for_drawing.color_regs[p-32] >> 1) & 0x777]; \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - } else { \ - while (pix < diw_end) { \ - TYPE d = colors_for_drawing.acolors[pixdata.apixels[pix]]; \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - } \ - d2 = colors_for_drawing.acolors[0]; \ - while (pix < stoppos) { \ - buf[pix] = d2; if (DO_DOUBLE) buf[pix+offset] = d2; \ - pix++; \ - } \ -} - -#define FILL_LINE(NAME, TYPE) \ -static void NAME(char *buf, int start, int stop) \ -{ \ - TYPE *b = (TYPE *)buf; \ - int i;\ - xcolnr col = colors_for_drawing.acolors[0]; \ - for (i = start; i < stop; i++) \ - b[i] = col; \ -} - -LINE_TO_SCR(pfield_linetoscr_8, UBYTE, 0) -LINE_TO_SCR(pfield_linetoscr_16, UWORD, 0) -LINE_TO_SCR(pfield_linetoscr_32, ULONG, 0) -LINE_TO_SCR(pfield_linetoscr_8_double_slow, UBYTE, 1) -LINE_TO_SCR(pfield_linetoscr_16_double_slow, UWORD, 1) -LINE_TO_SCR(pfield_linetoscr_32_double_slow, ULONG, 1) - -FILL_LINE(fill_line_8, UBYTE) -FILL_LINE(fill_line_16, UWORD) -FILL_LINE(fill_line_32, ULONG) - -#define pfield_linetoscr_full8 pfield_linetoscr_8 -#define pfield_linetoscr_full16 pfield_linetoscr_16 -#define pfield_linetoscr_full32 pfield_linetoscr_32 - -#define pfield_linetoscr_full8_double pfield_linetoscr_8_double_slow -#define pfield_linetoscr_full16_double pfield_linetoscr_16_double_slow -#define pfield_linetoscr_full32_double pfield_linetoscr_32_double_slow - -#if 1 && defined(X86_ASSEMBLY) -#undef pfield_linetoscr_full8 -/* The types are lies, of course. */ -extern void pfield_linetoscr_normal_asm8(void) __asm__("pfield_linetoscr_normal_asm8"); -extern void pfield_linetoscr_ehb_asm8(void) __asm__("pfield_linetoscr_ehb_asm8"); -extern void pfield_linetoscr_ham6_asm8(void) __asm__("pfield_linetoscr_ham6_asm8"); -extern void pfield_linetoscr_dualpf_asm8(void) __asm__("pfield_linetoscr_dualpf_asm8"); -extern void pfield_linetoscr_hdouble_asm8(void) __asm__("pfield_linetoscr_hdouble_asm8"); -extern void pfield_linetoscr_asm8(void (*)(void), int, int, int, int, ...) __asm__("pfield_linetoscr_asm8"); - -static void pfield_linetoscr_full8(int pix, int lframe_end, int diw_end, int stoppos) -{ - int lframe_end_1, diw_end_1; - - lframe_end_1 = pix + ((lframe_end - pix) & ~3); - diw_end_1 = stoppos - ((stoppos - diw_end) & ~3); - - if (bplham && bplplanecnt == 6) { - pfield_linetoscr_asm8(pfield_linetoscr_ham6_asm8, pix, lframe_end_1, diw_end_1, stoppos); - } else if (bpldualpf) { - pfield_linetoscr_asm8(pfield_linetoscr_dualpf_asm8, pix, lframe_end_1, diw_end_1, stoppos, - bpldualpfpri ? dblpf_ind2 : dblpf_ind1); - } else if (bplehb) { - pfield_linetoscr_asm8(pfield_linetoscr_ehb_asm8, pix, lframe_end_1, diw_end_1, stoppos); -#ifdef LORES_HACK - } else if (gfx_requested_lores == 2) { - pfield_linetoscr_asm8(pfield_linetoscr_hdouble_asm8, pix, lframe_end_1, diw_end_1, stoppos); -#endif - } else { - pfield_linetoscr_asm8(pfield_linetoscr_normal_asm8, pix, lframe_end_1, diw_end_1, stoppos); - } - - /* The assembly functions work on aligned data, so we may have to do some - * additional work at the edges. */ - if (lframe_end != lframe_end_1) { - int i, c = colors_for_drawing.acolors[0]; - for (i = lframe_end_1; i < lframe_end; i++) - xlinebuffer[i] = c; - } - if (diw_end != diw_end_1) { - int i, c = colors_for_drawing.acolors[0]; - for (i = diw_end; i < diw_end_1; i++) - xlinebuffer[i] = c; - } -} + int mousexpos, mouseypos; -#undef pfield_linetoscr_full16 -extern void pfield_linetoscr_normal_asm16(void) __asm__("pfield_linetoscr_normal_asm16"); -extern void pfield_linetoscr_ehb_asm16(void) __asm__("pfield_linetoscr_ehb_asm16"); -extern void pfield_linetoscr_ham6_asm16(void) __asm__("pfield_linetoscr_ham6_asm16"); -extern void pfield_linetoscr_dualpf_asm16(void) __asm__("pfield_linetoscr_dualpf_asm16"); -extern void pfield_linetoscr_hdouble_asm16(void) __asm__("pfield_linetoscr_hdouble_asm16"); -extern void pfield_linetoscr_asm16(void (*)(void), int, int, int, int, ...) __asm__("pfield_linetoscr_asm16"); - -static void pfield_linetoscr_full16(int pix, int lframe_end, int diw_end, int stoppos) -{ - int lframe_end_1, diw_end_1; - - lframe_end_1 = pix + ((lframe_end - pix) & ~3); - diw_end_1 = stoppos - ((stoppos - diw_end) & ~3); - - if (bplham && bplplanecnt == 6) { - pfield_linetoscr_asm16(pfield_linetoscr_ham6_asm16, pix, lframe_end_1, diw_end_1, stoppos); - } else if (bpldualpf) { - pfield_linetoscr_asm16(pfield_linetoscr_dualpf_asm16, pix, lframe_end_1, diw_end_1, stoppos, - bpldualpfpri ? dblpf_ind2 : dblpf_ind1); - } else if (bplehb) { - pfield_linetoscr_asm16(pfield_linetoscr_ehb_asm16, pix, lframe_end_1, diw_end_1, stoppos); -#ifdef LORES_HACK - } else if (gfx_requested_lores == 2) { - pfield_linetoscr_asm16(pfield_linetoscr_hdouble_asm16, pix, lframe_end_1, diw_end_1, stoppos); +#ifdef PICASSO96 + if (picasso_on) { + mousexpos = lastmx - picasso96_state.XOffset; + mouseypos = lastmy - picasso96_state.YOffset; + } else #endif - } else { - pfield_linetoscr_asm16(pfield_linetoscr_normal_asm16, pix, lframe_end_1, diw_end_1, stoppos); + { + if (lastmy >= gfxvidinfo.height) + lastmy = gfxvidinfo.height - 1; + mouseypos = coord_native_to_amiga_y (lastmy) << 1; + mousexpos = coord_native_to_amiga_x (lastmx); } - /* The assembly functions work on aligned data, so we may have to do some - * additional work at the edges. */ - if (lframe_end != lframe_end_1) { - int i, c = colors_for_drawing.acolors[0]; - for (i = lframe_end_1; i < lframe_end; i++) - ((UWORD *)xlinebuffer)[i] = c; - } - if (diw_end != diw_end_1) { - int i, c = colors_for_drawing.acolors[0]; - for (i = diw_end; i < diw_end_1; i++) - ((UWORD *)xlinebuffer)[i] = c; + switch (m68k_dreg (regs, 0)) { + case 0: + return ievent_alive ? -1 : needmousehack (); + case 1: + ievent_alive = 10; + return mousexpos; + case 2: + return mouseypos; } + return 0; } -#ifndef NO_DOUBLING_LINETOSCR -#define NO_DOUBLING_LINETOSCR -#endif - -#endif - -#ifdef NO_DOUBLING_LINETOSCR -#undef pfield_linetoscr_full8_double -#undef pfield_linetoscr_full16_double -static void pfield_linetoscr_full8_double(int start, int lframe_end, int diw_end, int stop) -{ - char *oldxlb = (char *)xlinebuffer; - pfield_linetoscr_full8(start, lframe_end, diw_end, stop); - xlinebuffer = oldxlb + linetoscr_double_offset; - pfield_linetoscr_full8(start, lframe_end, diw_end, stop); -} -static void pfield_linetoscr_full16_double(int start, int lframe_end, int diw_end, int stop) -{ - char *oldxlb = (char *)xlinebuffer; - pfield_linetoscr_full16(start, lframe_end, diw_end, stop); - xlinebuffer = oldxlb + linetoscr_double_offset; - pfield_linetoscr_full16(start, lframe_end, diw_end, stop); -} -#endif - -static __inline__ void fill_line(void) +void togglemouse (void) { - switch (gfxvidinfo.pixbytes) { - case 1: fill_line_8(xlinebuffer, linetoscr_x_adjust, linetoscr_x_adjust + gfxvidinfo.maxlinetoscr); break; - case 2: fill_line_16(xlinebuffer, linetoscr_x_adjust, linetoscr_x_adjust + gfxvidinfo.maxlinetoscr); break; - case 4: fill_line_32(xlinebuffer, linetoscr_x_adjust, linetoscr_x_adjust + gfxvidinfo.maxlinetoscr); break; + switch (mousestate) { + case dont_care_mouse: mousehack_setfollow (); break; + case follow_mouse: mousehack_setdontcare (); break; + default: break; /* Nnnnnghh! */ } } -static int linetoscr_diw_end, linetoscr_diw_start; - -static void pfield_init_linetoscr (void) +STATIC_INLINE int adjust (int val) { - int ddf_left, ddf_right; - int mindelay = bpldelay1, maxdelay = bpldelay2; - if (bpldelay1 > bpldelay2) - maxdelay = bpldelay1, mindelay = bpldelay2; - - linetoscr_diw_start = dp_for_drawing->diwfirstword; - linetoscr_diw_end = dp_for_drawing->diwlastword; - - /* We should really look at DDF also when calculating max_diwstop/min_diwstrt, - * so that centering works better, but I'm afraid that might cost too many - * cycles. Plus it's dangerous, see the code below that handles the case - * with sprites. */ - if (dip_for_drawing->nr_sprites == 0) { - int hiresadjust = bplhires ? 4 : 8; - ddf_left = ((dp_for_drawing->plfstrt + hiresadjust)*2 + mindelay - DISPLAY_LEFT_SHIFT) << lores_shift; - ddf_right = ((dp_for_drawing->plfstrt + dp_for_drawing->plflinelen + hiresadjust)*2 + maxdelay - DISPLAY_LEFT_SHIFT) << lores_shift; - - if (linetoscr_diw_start < ddf_left) - linetoscr_diw_start = ddf_left; - if (linetoscr_diw_end > ddf_right) - linetoscr_diw_end = ddf_right; - - if (mindelay != maxdelay) { - /* Raahh... - * We just clear the maximum amount of space that may need to be - * cleared. We could do this exactly, but it would come out slower - * because of the overhead. */ - int strt = ddf_left; - if (gfx_requested_lores) { - fuzzy_memset_le32 (pixdata.apixels, 0, ddf_left, 15); - fuzzy_memset_le32 (pixdata.apixels, 0, ddf_right - 15, 15); - } else { - fuzzy_memset_le32 (pixdata.apixels, 0, ddf_left, 30); - fuzzy_memset_le32 (pixdata.apixels, 0, ddf_right - 30, 30); - } - } - } else { - int hiresadjust = bplhires ? 4 : 8; - /* We swap mindelay and maxdelay here to get rid of the mindelay != maxdelay check. - * Since we have to do a memset anyway (because there may be sprites), - * we might as well clear all at once. */ - ddf_left = ((dp_for_drawing->plfstrt + hiresadjust)*2 + maxdelay - DISPLAY_LEFT_SHIFT) << lores_shift; - ddf_right = ((dp_for_drawing->plfstrt + dp_for_drawing->plflinelen + hiresadjust)*2 + mindelay - DISPLAY_LEFT_SHIFT) << lores_shift; - if (linetoscr_diw_start < ddf_left) { - int strt = linetoscr_diw_start; - int stop = ddf_left; -#ifdef LORES_HACK - if (gfx_requested_lores == 2) - strt >>= 1, stop >>= 1; -#endif - if (strt < stop) - fuzzy_memset (pixdata.apixels, 0, strt, stop - strt); - } - - if (linetoscr_diw_end > ddf_right) { - int strt = ddf_right; - int stop = linetoscr_diw_end; -#ifdef LORES_HACK - if (gfx_requested_lores == 2) - strt >>= 1, stop >>= 1; -#endif - if (strt < stop) - fuzzy_memset (pixdata.apixels, 0, strt, stop - strt); - } - } - /* Perverse cases happen. */ - if (linetoscr_diw_end < linetoscr_diw_start) - linetoscr_diw_end = linetoscr_diw_start; + if (val > 127) + return 127; + else if (val < -127) + return -127; + return val; } -static void pfield_do_linetoscr(int start, int stop) +static void do_mouse_hack (void) { - int lframe_end = linetoscr_diw_start, diw_end = linetoscr_diw_end; + int spr0x = ((spr0pos & 0xff) << 2) | ((spr0ctl & 1) << 1); + int spr0y = ((spr0pos >> 8) | ((spr0ctl & 4) << 6)) << 1; + int diffx, diffy; - if (stop > linetoscr_right_x) - stop = linetoscr_right_x; - if (start < linetoscr_x_adjust) - start = linetoscr_x_adjust; - - if (lframe_end < start) - lframe_end = start; - if (diw_end > stop) - diw_end = stop; - - if (start >= stop) + if (ievent_alive > 0) { + mouse_x = mouse_y = 0; return; - -#if AGA_CHIPSET == 0 - if (start == linetoscr_x_adjust && stop == linetoscr_right_x) { - switch (gfxvidinfo.pixbytes) { - case 1: pfield_linetoscr_full8 (start, lframe_end, diw_end, stop); break; - case 2: pfield_linetoscr_full16 (start, lframe_end, diw_end, stop); break; - case 4: pfield_linetoscr_full32 (start, lframe_end, diw_end, stop); break; - } - } else { - switch (gfxvidinfo.pixbytes) { - case 1: pfield_linetoscr_8 (start, lframe_end, diw_end, stop); break; - case 2: pfield_linetoscr_16 (start, lframe_end, diw_end, stop); break; - case 4: pfield_linetoscr_32 (start, lframe_end, diw_end, stop); break; - } } -#else - pfield_linetoscr_aga(start, lframe_end, diw_end, stop); -#endif -} + switch (mousestate) { + case normal_mouse: + diffx = lastmx - lastsampledmx; + diffy = lastmy - lastsampledmy; + if (!newmousecounters) { + if (diffx > 127) diffx = 127; + if (diffx < -127) diffx = -127; + mouse_x += diffx; + if (diffy > 127) diffy = 127; + if (diffy < -127) diffy = -127; + mouse_y += diffy; + } + lastsampledmx += diffx; lastsampledmy += diffy; + break; -static void pfield_do_fill_line(int start, int stop) -{ - if (stop > linetoscr_right_x) - stop = linetoscr_right_x; - if (start < linetoscr_x_adjust) - start = linetoscr_x_adjust; + case dont_care_mouse: + diffx = adjust (((lastmx - lastspr0x) * mstepx) >> 16); + diffy = adjust (((lastmy - lastspr0y) * mstepy) >> 16); + lastspr0x = lastmx; lastspr0y = lastmy; + mouse_x += diffx; mouse_y += diffy; + break; - if (start >= stop) - return; + case follow_mouse: + if (sprvbfl && sprvbfl-- > 1) { + int mousexpos, mouseypos; - switch (gfxvidinfo.pixbytes) { - case 1: fill_line_8 (xlinebuffer, start, stop); break; - case 2: fill_line_16 (xlinebuffer, start, stop); break; - case 4: fill_line_32 (xlinebuffer, start, stop); break; + if ((lastdiffx > docal || lastdiffx < -docal) + && lastspr0x != spr0x + && spr0x > plfstrt*4 + 34 + xcaloff + && spr0x < plfstop*4 - xcaloff) + { + int val = (lastdiffx << 16) / (spr0x - lastspr0x); + if (val >= 0x8000) + mstepx = (mstepx * (calweight - 1) + val) / calweight; + } + if ((lastdiffy > docal || lastdiffy < -docal) + && lastspr0y != spr0y + && spr0y > plffirstline + ycaloff + && spr0y < plflastline - ycaloff) + { + int val = (lastdiffy << 16) / (spr0y - lastspr0y); + if (val >= 0x8000) + mstepy = (mstepy * (calweight - 1) + val) / calweight; + } + if (lastmy >= gfxvidinfo.height) + lastmy = gfxvidinfo.height-1; + mouseypos = coord_native_to_amiga_y (lastmy) << 1; + mousexpos = coord_native_to_amiga_x (lastmx); + diffx = adjust ((((mousexpos + xoffs - spr0x) & ~1) * mstepx) >> 16); + diffy = adjust ((((mouseypos + yoffs - spr0y) & ~1) * mstepy) >> 16); + lastspr0x = spr0x; lastspr0y = spr0y; + lastdiffx = diffx; lastdiffy = diffy; + mouse_x += diffx; mouse_y += diffy; + } + break; + + default: + abort (); } } -static void pfield_do_linetoscr_full(int double_line) +static int timehack_alive = 0; + +static uae_u32 timehack_helper (void) { - int start = linetoscr_x_adjust, stop = start + gfxvidinfo.maxlinetoscr; - int lframe_end = linetoscr_diw_start, diw_end = linetoscr_diw_end; - if (lframe_end < start) - lframe_end = start; - if (diw_end > stop) - diw_end = stop; +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + if (m68k_dreg (regs, 0) == 0) + return timehack_alive; -#if AGA_CHIPSET == 0 - if (double_line) { - switch (gfxvidinfo.pixbytes) { - case 1: pfield_linetoscr_full8_double (start, lframe_end, diw_end, stop); break; - case 2: pfield_linetoscr_full16_double (start, lframe_end, diw_end, stop); break; - case 4: pfield_linetoscr_full32_double (start, lframe_end, diw_end, stop); break; - } - } else - switch (gfxvidinfo.pixbytes) { - case 1: pfield_linetoscr_full8 (start, lframe_end, diw_end, stop); break; - case 2: pfield_linetoscr_full16 (start, lframe_end, diw_end, stop); break; - case 4: pfield_linetoscr_full32 (start, lframe_end, diw_end, stop); break; - } + timehack_alive = 10; + + gettimeofday (&tv, NULL); + put_long (m68k_areg (regs, 0), tv.tv_sec - (((365 * 8 + 2) * 24 - 2) * 60 * 60)); + put_long (m68k_areg (regs, 0) + 4, tv.tv_usec); + return 0; #else - pfield_linetoscr_aga(start, lframe_end, diw_end, stop); + return 2; #endif } - /* + /* * register functions */ - -UWORD DMACONR(void) +STATIC_INLINE uae_u16 DENISEID (void) +{ + if (currprefs.chipset_mask & CSMASK_AGA) + return 0xF8; + if (currprefs.chipset_mask & CSMASK_ECS_DENISE) + return 0xFC; + return 0xFFFF; +} +STATIC_INLINE uae_u16 DMACONR (void) { return (dmacon | (bltstate==BLT_done ? 0 : 0x4000) | (blt_info.blitzero ? 0x2000 : 0)); } -static UWORD INTENAR(void) { return intena; } -UWORD INTREQR(void) +STATIC_INLINE uae_u16 INTENAR (void) { - return intreq | (use_serial ? 0x0001 : 0); + return intena; } -static UWORD ADKCONR(void) { return adkcon; } -static UWORD VPOSR(void) +uae_u16 INTREQR (void) { -#if AGA_CHIPSET == 1 - return (vpos >> 8) | lof | 0x2300; -#elif defined (ECS_AGNUS) - return (vpos >> 8) | lof | 0x2000; -#else - return (vpos >> 8) | lof; -#endif + return intreq /* | (currprefs.use_serial ? 0x0001 : 0) */; } -static void VPOSW(UWORD v) +STATIC_INLINE uae_u16 ADKCONR (void) +{ + return adkcon; +} +STATIC_INLINE uae_u16 VPOSR (void) +{ + unsigned int csbit = currprefs.ntscmode ? 0x1000 : 0; + csbit |= (currprefs.chipset_mask & CSMASK_AGA) ? 0x2300 : 0; + csbit |= (currprefs.chipset_mask & CSMASK_ECS_AGNUS) ? 0x2000 : 0; + return (vpos >> 8) | lof | csbit; +} +static void VPOSW (uae_u16 v) { if (lof != (v & 0x8000)) lof_changed = 1; lof = v & 0x8000; + /* + * This register is much more fun on a real Amiga. You can program + * refresh rates with it ;) But I won't emulate this... + */ +} + +STATIC_INLINE uae_u16 VHPOSR (void) +{ + return (vpos << 8) | current_hpos (); } -static UWORD VHPOSR(void) { return (vpos << 8) | current_hpos(); } -static void COP1LCH(UWORD v) { cop1lc= (cop1lc & 0xffff) | ((ULONG)v << 16); } -static void COP1LCL(UWORD v) { cop1lc= (cop1lc & ~0xffff) | v; } -static void COP2LCH(UWORD v) { cop2lc= (cop2lc & 0xffff) | ((ULONG)v << 16); } -static void COP2LCL(UWORD v) { cop2lc= (cop2lc & ~0xffff) | v; } +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 COPJMP1(UWORD a) +static void start_copper (void) { - coplc = cop1lc; copstate = COP_read; - eventtab[ev_copper].active = 1; eventtab[ev_copper].oldcycles = cycles; - eventtab[ev_copper].evtime = 4 + cycles; events_schedule(); - copper_active = 1; - copper_waiting_for_blitter = 0; + int was_active = eventtab[ev_copper].active; + eventtab[ev_copper].active = 0; + if (was_active) + events_schedule (); + + cop_state.ignore_next = 0; + cop_state.state = COP_read1; + cop_state.vpos = vpos; + cop_state.hpos = current_hpos () & ~1; + + if (dmaen (DMA_COPPER)) { + copper_enabled_thisline = 1; + set_special (SPCFLAG_COPPER); + } } -static void COPJMP2(UWORD a) + +static void COPJMP1 (uae_u16 a) { - coplc = cop2lc; copstate = COP_read; - eventtab[ev_copper].active = 1; eventtab[ev_copper].oldcycles = cycles; - eventtab[ev_copper].evtime = 4 + cycles; events_schedule(); - copper_active = 1; - copper_waiting_for_blitter = 0; + cop_state.ip = cop1lc; + start_copper (); } -static void COPCON(UWORD a) { copcon = a; } -static void DMACON(UWORD v) + +static void COPJMP2 (uae_u16 a) { - int i, need_resched = 0; + cop_state.ip = cop2lc; + start_copper (); +} - UWORD oldcon = dmacon; - - decide_line(); - setclr(&dmacon,v); dmacon &= 0x1FFF; - post_decide_line (); - - /* FIXME? Maybe we need to think a bit more about the master DMA enable +STATIC_INLINE void COPCON (uae_u16 a) +{ + copcon = a; +} + +static void DMACON (int hpos, uae_u16 v) +{ + int i; + + uae_u16 oldcon = dmacon; + + decide_line (hpos); + decide_fetch (hpos); + + setclr (&dmacon, v); + dmacon &= 0x1FFF; + + /* FIXME? Maybe we need to think a bit more about the master DMA enable * bit in these cases. */ - if ((dmacon & DMA_COPPER) > (oldcon & DMA_COPPER)) { - COPJMP1(0); + if ((dmacon & DMA_COPPER) != (oldcon & DMA_COPPER)) { + eventtab[ev_copper].active = 0; } -#if 0 /* @@@ ??? Is this right now? */ - if ((dmacon & DMA_SPRITE) < (oldcon & DMA_SPRITE)) { - int i; - for (i = 0; i < 8; i++) - spron[i] = SPR_restart; + if ((dmacon & DMA_COPPER) > (oldcon & DMA_COPPER)) { + cop_state.ip = cop1lc; + cop_state.ignore_next = 0; + cop_state.state = COP_read1; + cop_state.vpos = vpos; + cop_state.hpos = hpos & ~1; + copper_enabled_thisline = 1; + set_special (SPCFLAG_COPPER); + } + if (! (dmacon & DMA_COPPER)) { + copper_enabled_thisline = 0; + unset_special (SPCFLAG_COPPER); + cop_state.state = COP_stop; } -#endif + if ((dmacon & DMA_BLITPRI) > (oldcon & DMA_BLITPRI) && bltstate != BLT_done) { static int count = 0; if (!count) { count = 1; - fprintf(stderr, "warning: program is doing blitpri hacks.\n"); + write_log ("warning: program is doing blitpri hacks.\n"); } - regs.spcflags |= SPCFLAG_BLTNASTY; + set_special (SPCFLAG_BLTNASTY); } -#ifndef DONT_WANT_SOUND - for (i = 0; i < 4; i++) { - struct audio_channel_data *cdp = audio_channel + i; - - cdp->dmaen = (dmacon & 0x200) && (dmacon & (1<dmaen) { - if (cdp->state == 0) { - cdp->state = 1; - cdp->pt = cdp->lc; - cdp->wper = cdp->per; - cdp->wlen = cdp->len; - cdp->data_written = 2; - eventtab[ev_aud0 + i].oldcycles = eventtab[ev_hsync].oldcycles; - eventtab[ev_aud0 + i].evtime = eventtab[ev_hsync].evtime; - eventtab[ev_aud0 + i].active = 1; - need_resched = 1; /* not _really_ necessary here, but... */ - } - } else { - if (cdp->state == 1 || cdp->state == 5) { - cdp->state = 0; - cdp->current_sample = 0; - eventtab[ev_aud0 + i].active = 0; - need_resched = 1; + if ((dmacon & (DMA_BLITPRI | DMA_BLITTER | DMA_MASTER)) != (DMA_BLITPRI | DMA_BLITTER | DMA_MASTER)) + unset_special (SPCFLAG_BLTNASTY); + + if (currprefs.produce_sound > 0) { + update_audio (); + + for (i = 0; i < 4; i++) { + struct audio_channel_data *cdp = audio_channel + i; + int chan_ena = (dmacon & 0x200) && (dmacon & (1<dmaen == chan_ena) + continue; + cdp->dmaen = chan_ena; + if (cdp->dmaen) { + if (cdp->state == 0) { + cdp->state = 1; + cdp->pt = cdp->lc; + cdp->wper = cdp->per; + cdp->wlen = cdp->len; + cdp->data_written = 2; + cdp->evtime = eventtab[ev_hsync].evtime - get_cycles (); + } + } else { + if (cdp->state == 1 || cdp->state == 5) { + cdp->state = 0; + cdp->last_sample = 0; + cdp->current_sample = 0; + } } } + schedule_audio (); } -#endif - calculate_copper_cycle_time (); - if (copper_active && !eventtab[ev_copper].active) { - eventtab[ev_copper].active = 1; - eventtab[ev_copper].oldcycles = cycles; - eventtab[ev_copper].evtime = 1 + cycles; - need_resched = 1; - } - if (need_resched) - events_schedule(); + events_schedule(); } -static int trace_intena = 0; +/*static int trace_intena = 0;*/ -static void INTENA(UWORD v) +STATIC_INLINE void INTENA (uae_u16 v) { - if (trace_intena) - fprintf(stderr, "INTENA: %04x\n", v); - setclr(&intena,v); regs.spcflags |= SPCFLAG_INT; +/* if (trace_intena) + fprintf (stderr, "INTENA: %04x\n", v);*/ + setclr (&intena,v); + /* There's stupid code out there that does + [some INTREQ bits at level 3 are set] + clear all INTREQ bits + Enable one INTREQ level 3 bit + Set level 3 handler + + If we set SPCFLAG_INT for the clear, then by the time the enable happens, + we'll have SPCFLAG_DOINT set, and the interrupt happens immediately, but + it needs to happen one insn later, when the new L3 handler has been + installed. */ + if (v & 0x8000) + set_special (SPCFLAG_INT); } -void INTREQ(UWORD v) + +void INTREQ_0 (uae_u16 v) { - setclr(&intreq,v); - regs.spcflags |= SPCFLAG_INT; - if ((v&0x8800)==0x0800) serdat&=0xbfff; - } + setclr (&intreq,v); + set_special (SPCFLAG_INT); +} -static void ADKCON(UWORD v) { setclr(&adkcon,v); } +void INTREQ (uae_u16 v) +{ + INTREQ_0 (v); + if ((v & 0x8800) == 0x0800) + serdat &= 0xbfff; + rethink_cias (); +} -static void BPLPTH(UWORD v, int num) { decide_line (); decide_plane(); do_modulos(); bplpt[num] = (bplpt[num] & 0xffff) | ((ULONG)v << 16); } -static void BPLPTL(UWORD v, int num) { decide_line (); decide_plane(); do_modulos(); bplpt[num] = (bplpt[num] & ~0xffff) | (v & 0xFFFE); } +static void update_adkmasks (void) +{ + unsigned long t; -static void BPLCON0(UWORD v) + t = adkcon | (adkcon >> 4); + audio_channel[0].adk_mask = (((t >> 0) & 1) - 1); + audio_channel[1].adk_mask = (((t >> 1) & 1) - 1); + audio_channel[2].adk_mask = (((t >> 2) & 1) - 1); + audio_channel[3].adk_mask = (((t >> 3) & 1) - 1); +} + +static void ADKCON (uae_u16 v) { -#if AGA_CHIPSET == 0 - v &= 0xFF0E; - /* The Sanity WOC demo needs this at one place (at the end of the "Party Effect") - * Disable bitplane DMA if someone tries to do more than 4 Hires bitplanes. */ - if ((v & 0xF000) > 0xC000) - v &= 0xFFF; - /* Don't want 7 lores planes either. */ - if ((v & 0x8000) == 0 && (v & 0x7000) == 0x7000) - v &= 0xEFFF; -#endif + if (currprefs.produce_sound > 0) + update_audio (); + + setclr (&adkcon,v); + update_adkmasks (); +} + +static void BEAMCON0 (uae_u16 v) +{ + new_beamcon0 = v & 0x20; +} + +static void BPLPTH (int hpos, uae_u16 v, int num) +{ + decide_line (hpos); + decide_fetch (hpos); + bplpt[num] = (bplpt[num] & 0xffff) | ((uae_u32)v << 16); +} +static void BPLPTL (int hpos, uae_u16 v, int num) +{ + decide_line (hpos); + decide_fetch (hpos); + bplpt[num] = (bplpt[num] & ~0xffff) | (v & 0xfffe); +} + +static void BPLCON0 (int hpos, uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) + v &= ~0x00F1; + else if (! (currprefs.chipset_mask & CSMASK_AGA)) + v &= ~0x00B1; + if (bplcon0 == v) return; - decide_line (); + decide_line (hpos); + decide_fetch (hpos); + bplcon0 = v; - post_decide_line (); - calculate_copper_cycle_time (); -#if 0 - calcdiw(); /* This should go away. */ -#endif + nr_planes_from_bplcon0 = GET_PLANES (v); + + if (currprefs.chipset_mask & CSMASK_AGA) + /* It's not clear how the copper timings are affected by the number + * of bitplanes on AGA machines */ + corrected_nr_planes_from_bplcon0 = 4; + else + corrected_nr_planes_from_bplcon0 = nr_planes_from_bplcon0 << GET_RES (bplcon0); + + if (currprefs.chipset_mask & CSMASK_AGA) { + decide_sprites (hpos); + expand_sprres (); + } + + expand_fmodes (); } -static void BPLCON1(UWORD v) + +STATIC_INLINE void BPLCON1 (int hpos, uae_u16 v) { if (bplcon1 == v) return; - decide_diw (); + decide_line (hpos); + decide_fetch (hpos); bplcon1 = v; - decide_delay (); } -static void BPLCON2(UWORD v) + +STATIC_INLINE void BPLCON2 (int hpos, uae_u16 v) { - if (bplcon2 != v) - decide_line (); + if (bplcon2 == v) + return; + decide_line (hpos); bplcon2 = v; } -static void BPLCON3(UWORD v) + +STATIC_INLINE void BPLCON3 (int hpos, uae_u16 v) { - if (bplcon3 != v) - decide_line (); - bplcon3 = v; + if (! (currprefs.chipset_mask & CSMASK_AGA)) + return; + if (bplcon3 == v) + return; + decide_line (hpos); + decide_sprites (hpos); + bplcon3 = v; + expand_sprres (); } -static void BPLCON4(UWORD v) + +STATIC_INLINE void BPLCON4 (int hpos, uae_u16 v) { - if (bplcon4 != v) - decide_line (); - bplcon4 = v; + if (! (currprefs.chipset_mask & CSMASK_AGA)) + return; + if (bplcon4 == v) + return; + decide_line (hpos); + bplcon4 = v; } -static void BPL1MOD(UWORD v) +static void BPL1MOD (int hpos, uae_u16 v) { v &= ~1; - if ((WORD)bpl1mod == (WORD)v) + if ((uae_s16)bpl1mod == (uae_s16)v) return; + decide_line (hpos); + decide_fetch (hpos); bpl1mod = v; - decide_modulos (); } -static void BPL2MOD(UWORD v) -{ +static void BPL2MOD (int hpos, uae_u16 v) +{ v &= ~1; - if ((WORD)bpl2mod == (WORD)v) + if ((uae_s16)bpl2mod == (uae_s16)v) return; + decide_line (hpos); + decide_fetch (hpos); bpl2mod = v; - decide_modulos(); } +STATIC_INLINE void BPL1DAT (int hpos, uae_u16 v) +{ + decide_line (hpos); + bpl1dat = v; + + maybe_first_bpl1dat (hpos); +} /* We could do as well without those... */ -static void BPL1DAT(UWORD v) { bpl1dat = v; } -static void BPL2DAT(UWORD v) { bpl2dat = v; } -static void BPL3DAT(UWORD v) { bpl3dat = v; } -static void BPL4DAT(UWORD v) { bpl4dat = v; } -static void BPL5DAT(UWORD v) { bpl5dat = v; } -static void BPL6DAT(UWORD v) { bpl6dat = v; } +STATIC_INLINE void BPL2DAT (uae_u16 v) { bpl2dat = v; } +STATIC_INLINE void BPL3DAT (uae_u16 v) { bpl3dat = v; } +STATIC_INLINE void BPL4DAT (uae_u16 v) { bpl4dat = v; } +STATIC_INLINE void BPL5DAT (uae_u16 v) { bpl5dat = v; } +STATIC_INLINE void BPL6DAT (uae_u16 v) { bpl6dat = v; } +STATIC_INLINE void BPL7DAT (uae_u16 v) { bpl7dat = v; } +STATIC_INLINE void BPL8DAT (uae_u16 v) { bpl8dat = v; } -static void DIWSTRT(UWORD v) +static void DIWSTRT (int hpos, uae_u16 v) { - if (diwstrt == v) + if (diwstrt == v && ! diwhigh_written) return; - decide_line (); - diwstrt = v; - calcdiw(); + decide_line (hpos); + diwhigh_written = 0; + diwstrt = v; + calcdiw (); } -static void DIWSTOP(UWORD v) + +static void DIWSTOP (int hpos, uae_u16 v) { - if (diwstop == v) + if (diwstop == v && ! diwhigh_written) + return; + decide_line (hpos); + diwhigh_written = 0; + diwstop = v; + calcdiw (); +} + +static void DIWHIGH (int hpos, uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_DENISE)) + return; + if (diwhigh_written && diwhigh == v) return; - diwstop = v; - calcdiw(); + decide_line (hpos); + diwhigh_written = 1; + diwhigh = v; + calcdiw (); } -static void DDFSTRT(UWORD v) -{ - v &= 0xFF; + +static void DDFSTRT (int hpos, uae_u16 v) +{ + v &= 0xFC; if (ddfstrt == v) return; - decide_line (); - ddfstrt = v; - calcdiw(); -} -static void DDFSTOP(UWORD v) -{ - v &= 0xFF; + decide_line (hpos); + ddfstrt = v; + calcdiw (); + if (ddfstop > 0xD4 && (ddfstrt & 4) == 4) { + static int last_warned; + last_warned = (last_warned + 1) & 4095; + if (last_warned == 0) + write_log ("WARNING! Very strange DDF values.\n"); + } +} + +static void DDFSTOP (int hpos, uae_u16 v) +{ + /* ??? "Virtual Meltdown" sets this to 0xD2 and expects it to behave + differently from 0xD0. RSI Megademo sets it to 0xd1 and expects it + to behave like 0xd0. So mask it with 0xFE. */ + v &= ~1; if (ddfstop == v) return; - decide_line (); + decide_line (hpos); + decide_fetch (hpos); ddfstop = v; - calcdiw(); + calcdiw (); + if (fetch_state != fetch_not_started) + estimate_last_fetch_cycle (hpos); + if (ddfstop > 0xD4 && (ddfstrt & 4) == 4) { + static int last_warned; + last_warned = (last_warned + 1) & 4095; + if (last_warned == 0) + write_log ("WARNING! Very strange DDF values.\n"); + write_log ("WARNING! Very strange DDF values.\n"); + } +} + +static void FMODE (uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_AGA)) + v = 0; + + fmode = v; + sprite_width = GET_SPRITEWIDTH (fmode); + switch (fmode & 3) { + case 0: + fetchmode = 0; + break; + case 1: + case 2: + fetchmode = 1; + break; + case 3: + fetchmode = 2; + break; + } + expand_fmodes (); } -static void BLTADAT(UWORD v) +static void BLTADAT (uae_u16 v) { - maybe_blit(); + maybe_blit (); blt_info.bltadat = v; } @@ -1887,9 +2568,9 @@ static void BLTADAT(UWORD v) * 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(UWORD v) +static void BLTBDAT (uae_u16 v) { - maybe_blit(); + maybe_blit (); if (bltcon1 & 2) blt_info.bltbhold = v << (bltcon1 >> 12); @@ -1897,136 +2578,207 @@ static void BLTBDAT(UWORD v) blt_info.bltbhold = v >> (bltcon1 >> 12); blt_info.bltbdat = v; } -static void BLTCDAT(UWORD v) { maybe_blit(); blt_info.bltcdat = v; } +static void BLTCDAT (uae_u16 v) { maybe_blit (); blt_info.bltcdat = v; } -static void BLTAMOD(UWORD v) { maybe_blit(); blt_info.bltamod = (WORD)(v & 0xFFFE); } -static void BLTBMOD(UWORD v) { maybe_blit(); blt_info.bltbmod = (WORD)(v & 0xFFFE); } -static void BLTCMOD(UWORD v) { maybe_blit(); blt_info.bltcmod = (WORD)(v & 0xFFFE); } -static void BLTDMOD(UWORD v) { maybe_blit(); blt_info.bltdmod = (WORD)(v & 0xFFFE); } +static void BLTAMOD (uae_u16 v) { maybe_blit (); blt_info.bltamod = (uae_s16)(v & 0xFFFE); } +static void BLTBMOD (uae_u16 v) { maybe_blit (); blt_info.bltbmod = (uae_s16)(v & 0xFFFE); } +static void BLTCMOD (uae_u16 v) { maybe_blit (); blt_info.bltcmod = (uae_s16)(v & 0xFFFE); } +static void BLTDMOD (uae_u16 v) { maybe_blit (); blt_info.bltdmod = (uae_s16)(v & 0xFFFE); } -static void BLTCON0(UWORD v) { maybe_blit(); bltcon0 = v; blinea_shift = v >> 12; } -/* The next category is "Most useless hardware register". +static void BLTCON0 (uae_u16 v) { maybe_blit (); bltcon0 = v; blinea_shift = v >> 12; } +/* The next category is "Most useless hardware register". * And the winner is... */ -static void BLTCON0L(UWORD v) { maybe_blit(); bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF); } -static void BLTCON1(UWORD v) { maybe_blit(); bltcon1 = v; } +static void BLTCON0L (uae_u16 v) +{ + if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + return; + maybe_blit (); bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF); +} +static void BLTCON1 (uae_u16 v) { maybe_blit (); bltcon1 = v; } -static void BLTAFWM(UWORD v) { maybe_blit(); blt_info.bltafwm = v; } -static void BLTALWM(UWORD v) { maybe_blit(); blt_info.bltalwm = v; } +static void BLTAFWM (uae_u16 v) { maybe_blit (); blt_info.bltafwm = v; } +static void BLTALWM (uae_u16 v) { maybe_blit (); blt_info.bltalwm = v; } -static void BLTAPTH(UWORD v) { maybe_blit(); bltapt= (bltapt & 0xffff) | ((ULONG)v << 16); } -static void BLTAPTL(UWORD v) { maybe_blit(); bltapt= (bltapt & ~0xffff) | (v & 0xFFFE); } -static void BLTBPTH(UWORD v) { maybe_blit(); bltbpt= (bltbpt & 0xffff) | ((ULONG)v << 16); } -static void BLTBPTL(UWORD v) { maybe_blit(); bltbpt= (bltbpt & ~0xffff) | (v & 0xFFFE); } -static void BLTCPTH(UWORD v) { maybe_blit(); bltcpt= (bltcpt & 0xffff) | ((ULONG)v << 16); } -static void BLTCPTL(UWORD v) { maybe_blit(); bltcpt= (bltcpt & ~0xffff) | (v & 0xFFFE); } -static void BLTDPTH(UWORD v) { maybe_blit(); bltdpt= (bltdpt & 0xffff) | ((ULONG)v << 16); } -static void BLTDPTL(UWORD v) { maybe_blit(); bltdpt= (bltdpt & ~0xffff) | (v & 0xFFFE); } -static void BLTSIZE(UWORD v) +static void BLTAPTH (uae_u16 v) { maybe_blit (); bltapt = (bltapt & 0xffff) | ((uae_u32)v << 16); } +static void BLTAPTL (uae_u16 v) { maybe_blit (); bltapt = (bltapt & ~0xffff) | (v & 0xFFFE); } +static void BLTBPTH (uae_u16 v) { maybe_blit (); bltbpt = (bltbpt & 0xffff) | ((uae_u32)v << 16); } +static void BLTBPTL (uae_u16 v) { maybe_blit (); bltbpt = (bltbpt & ~0xffff) | (v & 0xFFFE); } +static void BLTCPTH (uae_u16 v) { maybe_blit (); bltcpt = (bltcpt & 0xffff) | ((uae_u32)v << 16); } +static void BLTCPTL (uae_u16 v) { maybe_blit (); bltcpt = (bltcpt & ~0xffff) | (v & 0xFFFE); } +static void BLTDPTH (uae_u16 v) { maybe_blit (); bltdpt = (bltdpt & 0xffff) | ((uae_u32)v << 16); } +static void BLTDPTL (uae_u16 v) { maybe_blit (); bltdpt = (bltdpt & ~0xffff) | (v & 0xFFFE); } + +static void BLTSIZE (uae_u16 v) { - bltsize = v; - - maybe_blit(); + maybe_blit (); - blt_info.vblitsize = bltsize >> 6; - blt_info.hblitsize = bltsize & 0x3F; + 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; - - bltstate = BLT_init; - do_blitter(); + + bltstate = BLT_init; + do_blitter (); } -static void BLTSIZV(UWORD v) + +static void BLTSIZV (uae_u16 v) { - maybe_blit(); + if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + return; + maybe_blit (); oldvblts = v & 0x7FFF; } -static void BLTSIZH(UWORD v) + +static void BLTSIZH (uae_u16 v) { - maybe_blit(); + if (! (currprefs.chipset_mask & CSMASK_ECS_AGNUS)) + return; + maybe_blit (); blt_info.hblitsize = v & 0x7FF; blt_info.vblitsize = oldvblts; if (!blt_info.vblitsize) blt_info.vblitsize = 32768; if (!blt_info.hblitsize) blt_info.hblitsize = 0x800; bltstate = BLT_init; - do_blitter(); + do_blitter (); } -static void SPRxCTL_1(UWORD v, int num) + +STATIC_INLINE void SPRxCTL_1 (uae_u16 v, int num) { int sprxp; sprctl[num] = v; - sprarmed[num] = 0; - if (sprpos[num] == 0 && v == 0) - spron[num] = SPR_stop; - - sprxp = ((sprpos[num] & 0xFF) * 2) + (v & 1) - DISPLAY_LEFT_SHIFT; - if (!gfx_requested_lores) - sprxp *= 2; - sprxpos[num] = sprxp; - sprvstart[num] = (sprpos[num] >> 8) | ((sprctl[num] << 6) & 0x100); - sprvstop[num] = (sprctl[num] >> 8) | ((sprctl[num] << 7) & 0x100); + nr_armed -= spr[num].armed; + spr[num].armed = 0; + if (sprpos[num] == 0 && v == 0) { + spr[num].state = SPR_stop; + spr[num].on = 0; + } else if (spr[num].state != SPR_waiting_stop) { + spr[num].state = SPR_waiting_start; + spr[num].on = 1; + } + + sprxp = (sprpos[num] & 0xFF) * 2 + (v & 1); + + /* Quite a bit salad in this register... */ + if (currprefs.chipset_mask & CSMASK_AGA) { + /* We ignore the SHRES 35ns increment for now; SHRES support doesn't + work anyway, so we may as well restrict AGA sprites to a 70ns + resolution. */ + sprxp <<= 1; + sprxp |= (v >> 4) & 1; + } + spr[num].xpos = sprxp; + spr[num].vstart = (sprpos[num] >> 8) | ((sprctl[num] << 6) & 0x100); + spr[num].vstop = (sprctl[num] >> 8) | ((sprctl[num] << 7) & 0x100); + } -static void SPRxPOS_1(UWORD v, int num) +STATIC_INLINE void SPRxPOS_1 (uae_u16 v, int num) { int sprxp; sprpos[num] = v; - sprxp = ((v & 0xFF) * 2) + (sprctl[num] & 1) - DISPLAY_LEFT_SHIFT; - if (!gfx_requested_lores) - sprxp *= 2; - sprxpos[num] = sprxp; - sprvstart[num] = (sprpos[num] >> 8) | ((sprctl[num] << 6) & 0x100); -} -static void SPRxDATA_1(UWORD v, int num) -{ - sprdata[num] = v; - sprarmed[num] = 1; -} -static void SPRxDATB_1(UWORD v, int num) -{ - sprdatb[num] = v; -} -static void SPRxDATA(UWORD v, int num) { decide_sprite (num); SPRxDATA_1 (v, num); } -static void SPRxDATB(UWORD v, int num) { decide_sprite (num); SPRxDATB_1 (v, num); } -static void SPRxCTL(UWORD v, int num) { decide_sprite (num); SPRxCTL_1 (v, num); } -static void SPRxPOS(UWORD v, int num) { decide_sprite (num); SPRxPOS_1 (v, num); } -static void SPRxPTH(UWORD v, int num) -{ - sprpt[num] &= 0xffff; - sprpt[num] |= (ULONG)v << 16; -#if 1 - /* I don't like these, I'd prefer to set sprite state to SPR_restart on - * a vsync, but the Majic 12 Ray of Hope 2 demo doesn't like that. */ - if (spron[num] == SPR_vtop) -#endif - spron[num] = SPR_restart; + sprxp = (v & 0xFF) * 2 + (sprctl[num] & 1); + + if (currprefs.chipset_mask & CSMASK_AGA) { + sprxp <<= 1; + sprxp |= (sprctl[num] >> 4) & 1; + } + spr[num].xpos = sprxp; + spr[num].vstart = (sprpos[num] >> 8) | ((sprctl[num] << 6) & 0x100); } -static void SPRxPTL(UWORD v, int num) +STATIC_INLINE void SPRxDATA_1 (uae_u16 v, int num) { - sprpt[num] &= ~0xffff; - sprpt[num] |= v; -#if 1 - if (spron[num] == SPR_vtop) -#endif - spron[num] = SPR_restart; + sprdata[num][0] = v; + nr_armed += 1 - spr[num].armed; + spr[num].armed = 1; } +STATIC_INLINE void SPRxDATB_1 (uae_u16 v, int num) +{ + sprdatb[num][0] = v; +} +static void SPRxDATA (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxDATA_1 (v, num); } +static void SPRxDATB (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxDATB_1 (v, num); } +static void SPRxCTL (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxCTL_1 (v, num); } +static void SPRxPOS (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxPOS_1 (v, num); } +static void SPRxPTH (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + spr[num].pt &= 0xffff; + spr[num].pt |= (uae_u32)v << 16; -static void COLOR(UWORD v, int num) + if (spr[num].state == SPR_stop || vpos < sprite_vblank_endline) + spr[num].state = SPR_restart; + spr[num].on = 1; +} +static void SPRxPTL (int hpos, uae_u16 v, int num) +{ + decide_sprites (hpos); + spr[num].pt &= ~0xffff; + spr[num].pt |= v; + + if (spr[num].state == SPR_stop || vpos < sprite_vblank_endline) + spr[num].state = SPR_restart; + spr[num].on = 1; +} +static void CLXCON (uae_u16 v) +{ + clxcon = v; + clxcon_bpl_enable = (v >> 6) & 63; + clxcon_bpl_match = v & 63; + clx_sprmask = ((((v >> 15) & 1) << 7) | (((v >> 14) & 1) << 5) | (((v >> 13) & 1) << 3) | (((v >> 12) & 1) << 1) | 0x55); +} +static void CLXCON2 (uae_u16 v) +{ + if (!(currprefs.chipset_mask & CSMASK_AGA)) + return; + clxcon2 = v; + clxcon_bpl_enable |= v & (0x40|0x80); + clxcon_bpl_match |= (v & (0x01|0x02)) << 6; + } +static uae_u16 CLXDAT (void) +{ + uae_u16 v = clxdat; + clxdat = 0; + return v; +} + +static uae_u16 COLOR_READ (int num) +{ + int cr, cg, cb, colreg; + uae_u16 cval; + + if (!(currprefs.chipset_mask & CSMASK_AGA) || !(bplcon2 & 0x0100)) + return 0xffff; + + colreg = ((bplcon3 >> 13) & 7) * 32 + num; + cr = current_colors.color_regs_aga[colreg] >> 16; + cg = (current_colors.color_regs_aga[colreg] >> 8) & 0xFF; + cb = current_colors.color_regs_aga[colreg] & 0xFF; + if (bplcon3 & 0x200) + cval = ((cr & 15) << 12) | ((cg & 15) << 4) | ((cb & 15) << 0); + else + cval = ((cr >> 4) << 12) | ((cg >> 4) << 4) | ((cb >> 4) << 0); + return cval; +} + +static void COLOR_WRITE (int hpos, uae_u16 v, int num) { - int r,g,b; - int cr,cg,cb; - int colreg; - v &= 0xFFF; -#if AGA_CHIPSET == 1 - { - /* XXX Broken */ - ULONG cval; + if (currprefs.chipset_mask & CSMASK_AGA) { + int r,g,b; + int cr,cg,cb; + int colreg; + uae_u32 cval; + + /* writing is disabled when RDRAM=1 */ + if (bplcon2 & 0x0100) + return; + colreg = ((bplcon3 >> 13) & 7) * 32 + num; r = (v & 0xF00) >> 8; g = (v & 0xF0) >> 4; b = (v & 0xF) >> 0; - cr = color_regs[colreg] >> 16; - cg = (color_regs[colreg] >> 8) & 0xFF; - cb = color_regs[colreg] & 0xFF; + cr = current_colors.color_regs_aga[colreg] >> 16; + cg = (current_colors.color_regs_aga[colreg] >> 8) & 0xFF; + cb = current_colors.color_regs_aga[colreg] & 0xFF; if (bplcon3 & 0x200) { cr &= 0xF0; cr |= r; @@ -2038,2412 +2790,937 @@ static void COLOR(UWORD v, int num) cb = b + (b << 4); } cval = (cr << 16) | (cg << 8) | cb; - if (cval == color_regs[colreg]) + if (cval == current_colors.color_regs_aga[colreg]) return; - color_regs[colreg] = cval; - pfield_may_need_update(1); - } -#else - { - if (current_colors.color_regs[num] == v) + + /* Call this with the old table still intact. */ + record_color_change (hpos, colreg, cval); + remembered_color_entry = -1; + current_colors.color_regs_aga[colreg] = cval; + current_colors.acolors[colreg] = CONVERT_RGB (cval); + } else { + if (current_colors.color_regs_ecs[num] == v) return; /* Call this with the old table still intact. */ - record_color_change (num, v); + record_color_change (hpos, num, v); remembered_color_entry = -1; - current_colors.color_regs[num] = v; + current_colors.color_regs_ecs[num] = v; current_colors.acolors[num] = xcolors[v]; } -#endif } -static void DSKSYNC(UWORD v) { dsksync = v; } -static void DSKDAT(UWORD v) { fprintf(stderr, "DSKDAT written. Not good.\n"); } -static void DSKPTH(UWORD v) { dskpt = (dskpt & 0xffff) | ((ULONG)v << 16); } -static void DSKPTL(UWORD v) { dskpt = (dskpt & ~0xffff) | (v); } - -static void DSKLEN(UWORD v) -{ - if (v & 0x8000) { - dskdmaen = dskdmaen == 1 ? 2 : 1; - } else { - dskdmaen = 0; - if (eventtab[ev_diskblk].active) - fprintf(stderr, "warning: Disk DMA aborted!\n"); - eventtab[ev_diskblk].active = 0; - events_schedule(); - - } - dsklen = dsklength = v; dsklength &= 0x3fff; - if (dskdmaen == 2 && dsksync != 0x4489 && (adkcon & 0x400)) { - fprintf(stderr, "Non-standard sync: %04x len: %x\n", dsksync, dsklength); - } - if (dskdmaen > 1) { - if (dsklen & 0x4000) { - eventtab[ev_diskblk].active = 1; - eventtab[ev_diskblk].oldcycles = cycles; - eventtab[ev_diskblk].evtime = 40 + cycles; /* ??? */ - events_schedule(); - } else { - int result = DISK_PrepareReadMFM(dsklength, dsksync, adkcon & 0x400); - if (result) { - eventtab[ev_diskblk].active = 1; - eventtab[ev_diskblk].oldcycles = cycles; - eventtab[ev_diskblk].evtime = result + cycles; - events_schedule(); - } - } - } -} +static uae_u16 potgo_value; -static UWORD DSKBYTR(void) +static void POTGO (uae_u16 v) { - UWORD v = (dsklen >> 1) & 0x6000; - UWORD mfm, byte; - if (DISK_GetData(&mfm, &byte)) - v |= 0x8000; - v |= byte; - if (dsksync == mfm) v |= 0x1000; - return v; + potgo_value = v; } -static UWORD DSKDATR(void) -{ - UWORD mfm, byte; - DISK_GetData(&mfm, &byte); - return mfm; -} -static UWORD POTGOR(void) +static uae_u16 POTGOR (void) { - UWORD v = 0xFF00; - if (buttonstate[2]) - v &= 0xFBFF; + uae_u16 v = (potgo_value | (potgo_value >> 1)) & 0x5500; - if (buttonstate[1]) - v &= 0xFEFF; - - return v; -} -static UWORD POT0DAT(void) -{ - static UWORD cnt = 0; - if (buttonstate[2]) - cnt = ((cnt + 1) & 0xFF) | (cnt & 0xFF00); - if (buttonstate[1]) - cnt += 0x100; - - return cnt; -} -static UWORD JOY0DAT(void) { return ((UBYTE)joy0x) + ((UWORD)joy0y << 8); } -static UWORD JOY1DAT(void) -{ - return joy0dir; -} -static void JOYTEST(UWORD v) -{ - joy0x = joy1x = v & 0xFC; - joy0y = joy1y = (v >> 8) & 0xFC; -} -static void AUDxLCH(int nr, UWORD v) { audio_channel[nr].lc = (audio_channel[nr].lc & 0xffff) | ((ULONG)v << 16); } -static void AUDxLCL(int nr, UWORD v) { audio_channel[nr].lc = (audio_channel[nr].lc & ~0xffff) | (v & 0xFFFE); } -static void AUDxPER(int nr, UWORD v) -{ - if (v <= 0) { -#if 0 /* v == 0 is rather common, and harmless, and the value isn't signed anyway */ - static int warned = 0; - if (!warned) - fprintf(stderr, "Broken program accessing the sound hardware\n"), warned++; -#endif - v = 65535; - } - if (v < maxhpos/2 && produce_sound < 3) - v = maxhpos/2; - - audio_channel[nr].per = v; -} + v |= (~potgo_value & 0xAA00) >> 1; -static void AUDxVOL(int nr, UWORD v) { audio_channel[nr].vol = v & 64 ? 63 : v & 63; } -static void AUDxLEN(int nr, UWORD v) { audio_channel[nr].len = v; } + if (JSEM_ISMOUSE (0, &currprefs)) { + if (buttonstate[2]) + v &= 0xFBFF; -/* - * Here starts the copper code. It should be rewritten. - */ - -static int copcomp(void) -{ - UWORD vp = vpos & (((copi2 >> 8) & 0x7F) | 0x80); - UWORD hp = current_hpos() & (copi2 & 0xFE); - UWORD vcmp = copi1 >> 8; - UWORD hcmp = copi1 & 0xFE; - return (vp > vcmp || (vp == vcmp && hp >= hcmp)) && ((copi2 & 0x8000) || !(DMACONR() & 0x4000)); -} - -/* - * Calculate the minimum number of cycles after which the - * copper comparison becomes true. This is quite tricky. I hope it works. - */ -static int calc_copcomp_true(int currvpos, int currhpos) -{ - UWORD vp = currvpos & (((copi2 >> 8) & 0x7F) | 0x80); - UWORD hp = currhpos & (copi2 & 0xFE); - UWORD vcmp = ((copi1 & (copi2 | 0x8000)) >> 8); - UWORD hcmp = (copi1 & copi2 & 0xFE); - int copper_time_hpos; - int cycleadd = maxhpos - currhpos; - int coptime = 0; - - /* This is a kludge... the problem is that there are programs that wait for - * FFDDFFFE and then for a line in the second display half, and this doesn't - * work without this. I _think_ the reason why it works on the Amiga is that - * the last cycle in the line isn't available for the copper, but I'm not sure. - * OTOH, I'm pretty convinced that the copper timings are correct otherwise, so - * I added this rather than changing something else. */ - if (hcmp == 0xDC) - hcmp += 2; - - if ((vp > vcmp || (vp == vcmp && hp >= hcmp)) && ((copi2 & 0x8000) || !(DMACONR() & 0x4000))) - return 0; - - try_again: - - while (vp < vcmp) { - currvpos++; - if (currvpos > maxvpos + 1) - return -1; - currhpos = 0; - coptime += cycleadd; - cycleadd = maxhpos; - vp = currvpos & (((copi2 >> 8) & 0x7F) | 0x80); - } - if (coptime > 0 && (bplcon0 & 0xF000) == 0xC000) - return coptime; - copper_time_hpos = currhpos; - hp = copper_time_hpos & (copi2 & 0xFE); - if (!(vp > vcmp)) { - while ((int)hp < ((int)hcmp)) { - currhpos++; - /* Copper DMA is turned off in Hires 4 bitplane mode */ - if (decided_nr_planes != 4 /* -1 if before plfstrt */ - || !decided_hires - || current_hpos () >= (thisline_decision.plfstrt + thisline_decision.plflinelen)) - copper_time_hpos++; - - if (currhpos > maxhpos) { - /* Now, what? There might be a good position on the - * next line. But it can also be the FFFF FFFE - * case. - */ - currhpos = 0; - currvpos++; - vp = currvpos & (((copi2 >> 8) & 0x7F) | 0x80); - goto try_again; - } - coptime++; - hp = copper_time_hpos & (copi2 & 0xFE); - } + if (buttonstate[1]) + v &= 0xFEFF; + } else if (JSEM_ISJOY0 (0, &currprefs) || JSEM_ISJOY1 (0, &currprefs)) { + if (joy0button & 2) v &= 0xfbff; + if (joy0button & 4) v &= 0xfeff; } - if (coptime == 0) /* waiting for the blitter */ - return 1; - return coptime; -} - -/* - * Simple version of the above which only tries to get vpos correct. - */ -static int calc_copcomp_true_vpos(int currvpos, int currhpos) -{ - UWORD vp = currvpos & (((copi2 >> 8) & 0x7F) | 0x80); - UWORD hp = currhpos & (copi2 & 0xFE); - UWORD vcmp = ((copi1 & (copi2 | 0x8000)) >> 8); - UWORD hcmp = (copi1 & copi2 & 0xFE); - int copper_time_hpos; - int cycleadd = maxhpos - currhpos; - int coptime = 0; - - /* see above */ - if (hcmp == 0xDC) - hcmp += 2; - - if ((vp > vcmp || (vp == vcmp && hp >= hcmp)) && ((copi2 & 0x8000) || !(DMACONR() & 0x4000))) - return 0; - - while (vp < vcmp) { - currvpos++; - if (currvpos > maxvpos + 1) - return -1; - currhpos = 0; - coptime += cycleadd; - cycleadd = maxhpos; - vp = currvpos & (((copi2 >> 8) & 0x7F) | 0x80); - } - return coptime; -} - -static enum copper_states cop_cmds[4] = { COP_move, COP_wait, COP_move, COP_skip }; - -/* This function is not always correct, but it's the best we can do. */ -static int copper_memory_cycles (int n) -{ - int current_cycle = current_hpos (); - int n_needed = 0; - int planes = ((bplcon0 >> 12) & 7) * (bplcon0 & 0x8000 ? 2 : 1); - - while (n) { - /* This sucks. The DIW may end vertically at exactly this point. Therefore, - * we must call decide_line(). If we're vertically outside the DIW, - * copper_cycle_time will be != -1 after this call [Sanity Interference] */ - decide_line (); - - if (copper_cycle_time == -1 && current_cycle >= 0x14 - && current_cycle >= plfstrt && current_cycle < plfstrt + plflinelen - && (planes == 6 || (planes == 5 && (current_cycle % 8) < 4))) - { - n_needed += 4; - current_cycle += 4; - } else { - n_needed += 2; - current_cycle += 2; - } - n--; + if (JSEM_ISJOY0 (1, &currprefs) || JSEM_ISJOY1 (1, &currprefs)) { + if (joy1button & 2) v &= 0xbfff; + if (joy1button & 4) v &= 0xefff; } - return n_needed; -} -static __inline__ int calc_copper_cycles (int n_cycles) -{ - return copper_cycle_time != -1 ? copper_cycle_time * n_cycles : copper_memory_cycles (n_cycles); + return v; } -static __inline__ int copper_init_read (int n_cycles) +static uae_u16 POT0DAT (void) { - if (dmaen(DMA_COPPER)){ - int t = calc_copper_cycles (n_cycles); - - eventtab[ev_copper].evtime = t + cycles; - return 1; - } else { - copstate = COP_read; - eventtab[ev_copper].active = 0; - return 0; + static uae_u16 cnt = 0; + if (JSEM_ISMOUSE (0, &currprefs)) { + if (buttonstate[2]) + cnt = ((cnt + 1) & 0xFF) | (cnt & 0xFF00); + if (buttonstate[1]) + cnt += 0x100; } -} -static __inline__ void copper_read (void) -{ - int cmd; - - copi1 = chipmem_bank.wget(coplc); - copi2 = chipmem_bank.wget(coplc+2); - coplc += 4; - eventtab[ev_copper].oldcycles = cycles; - - cmd = (copi1 & 1) | ((copi2 & 1) << 1); - copstate = cop_cmds[cmd]; - eventtab[ev_copper].oldcycles = cycles; + return cnt; } - -static __inline__ void handle_bltfinish_wait (void) +static uae_u16 JOY0DAT (void) { - if (bltstate == BLT_done) { - copstate = COP_read; - /* Don't need to wait. Experimental: No copper wakeup time in this case. */ - } else { - eventtab[ev_copper].active = 0; - copstate = COP_morewait; - copper_waiting_for_blitter = 1; + if (JSEM_ISMOUSE (0, &currprefs)) { + do_mouse_hack (); + return ((uae_u8)mouse_x) + ((uae_u16)mouse_y << 8); } + return joy0dir; } - -void blitter_done_notify (void) +static uae_u16 JOY1DAT (void) { - if (copper_waiting_for_blitter) { - copper_waiting_for_blitter = 0; - eventtab[ev_copper].active = 1; - eventtab[ev_copper].oldcycles = cycles; - eventtab[ev_copper].evtime = 1 + cycles; - events_schedule (); + if (JSEM_ISMOUSE (1, &currprefs)) { + do_mouse_hack (); + return ((uae_u8)mouse_x) + ((uae_u16)mouse_y << 8); + } + return joy1dir; +} +static void JOYTEST (uae_u16 v) +{ + if (JSEM_ISMOUSE (0, &currprefs)) { + mouse_x = v & 0xFC; + mouse_y = (v >> 8) & 0xFC; } } -static void do_copper(void) -{ - int coptime, t; - for (;;) - switch(copstate){ - case COP_read: - eventtab[ev_copper].oldcycles = cycles; - if (!copper_init_read (2)) - return; - copstate = COP_do_read; - return; - - case COP_do_read: - copper_read (); - break; - - case COP_read_ignore: - eventtab[ev_copper].oldcycles = cycles; - if (!copper_init_read (2)) - return; - copstate = COP_do_read_ignore; - return; - - case COP_do_read_ignore: - copper_read (); - copstate = COP_read; - break; - - case COP_move: - if (copi1 >= (copcon & 2 ? 0x40 : 0x80)) { - custom_bank.wput(copi1,copi2); - copstate = COP_read; - break; - } else { - copstate = COP_stop; - eventtab[ev_copper].active = 0; - copper_active = 0; - } - return; - - case COP_skip: - copstate = COP_read; - if (calc_copcomp_true(vpos, current_hpos()) == 0) - copstate = COP_read_ignore; - break; +/* The copper code. The biggest nightmare in the whole emulator. + + Alright. The current theory: + 1. Copper moves happen 4 cycles after state READ2 is reached. + It can't happen immediately when we reach READ2, because the + data needs time to get back from the bus. 4 cycles appears + to be the time a chip memory access takes on the Amiga. + 2. As stated in the HRM, a WAIT really does need an extra cycle + to wake up. This is implemented by _not_ falling through from + a successful wait to READ1, but by starting the next cycle. + (Note: the extra cycle for the WAIT apparently really needs a + free cycle; i.e. contention with the bitplane fetch can slow + it down). + 3. Apparently, to compensate for the extra wake up cycle, a WAIT + will use the _incremented_ horizontal position, so the WAIT + cycle normally finishes two clocks earlier than the position + it was waiting for. The extra cycle then takes us to the + position that was waited for. + If the earlier cycle is busy with a bitplane, things change a bit. + E.g., waiting for position 0x50 in a 6 plane display: In cycle + 0x4e, we fetch BPL5, so the wait wakes up in 0x50, the extra cycle + takes us to 0x54 (since 0x52 is busy), then we have READ1/READ2, + and the next register write is at 0x5c. + 4. The last cycle in a line is not usable for the copper. + 5. A 4 cycle delay also applies to the WAIT instruction. This means + that the second of two back-to-back WAITs (or a WAIT whose + condition is immediately true) takes 8 cycles. + 6. This also applies to a SKIP instruction. The copper does not + fetch the next instruction while waiting for the second word of + a WAIT or a SKIP to arrive. + 7. A SKIP also seems to need an unexplained additional two cycles + after its second word arrives; this is _not_ a memory cycle (I + think, the documentation is pretty clear on this). + 8. Two additional cycles are inserted when writing to COPJMP1/2. */ + +/* Determine which cycles are available for the copper in a display + * with a agiven number of planes. */ +static int cycles_for_plane[9][8] = { + { 0, -1, 0, -1, 0, -1, 0, -1 }, + { 0, -1, 0, -1, 0, -1, 0, -1 }, + { 0, -1, 0, -1, 0, -1, 0, -1 }, + { 0, -1, 0, -1, 0, -1, 0, -1 }, + { 0, -1, 0, -1, 0, -1, 0, -1 }, + { 0, -1, 0, -1, 0, -1, 1, -1 }, + { 0, -1, 1, -1, 0, -1, 1, -1 }, + { 1, -1, 1, -1, 1, -1, 1, -1 }, + { 1, -1, 1, -1, 1, -1, 1, -1 } +}; - case COP_wait: - /* Recognize blitter wait statements. This is a speed optimization - * only.*/ - if (copi1 == 1 && copi2 == 0) { - handle_bltfinish_wait (); - if (!eventtab[ev_copper].active) - return; - break; - } - coptime = calc_copcomp_true_vpos(vpos, current_hpos()); - if (coptime > 0) { - copstate = COP_morewait; - eventtab[ev_copper].oldcycles = cycles; - eventtab[ev_copper].evtime = coptime + cycles; - return; - } - coptime = calc_copcomp_true(vpos, current_hpos()); - if (coptime < 0) { - copstate = COP_stop; - eventtab[ev_copper].active = 0; - copper_active = 0; - return; - } - if (coptime) { - copstate = COP_morewait; - eventtab[ev_copper].evtime = coptime + cycles; - return; - } - copstate = COP_read; - /* Experimental: no copper wakeup time in this case. The HRM says - * nothing about this. */ - break; +STATIC_INLINE int copper_cant_read (int hpos, int planes) +{ + int t; - case COP_morewait: - coptime = calc_copcomp_true(vpos, current_hpos()); - if (coptime < 0) { - copstate = COP_stop; - eventtab[ev_copper].active = 0; - copper_active = 0; - return; - } - if (coptime) { - eventtab[ev_copper].evtime = coptime + cycles; - return; - } - /* Copper wakeup time: 1 memory cycle, plus 2 for the next read */ - eventtab[ev_copper].oldcycles = cycles; - if (!copper_init_read (3)) - return; - copstate = COP_do_read; - return; + if (hpos + 1 >= maxhpos) + return 1; - case COP_stop: - eventtab[ev_copper].active = 0; - copper_active = 0; - return; - } -} + if (currprefs.chipset_mask & CSMASK_AGA) + /* FIXME */ + return 0; -static void diskblk_handler(void) -{ - regs.spcflags |= SPCFLAG_DISK; - eventtab[ev_diskblk].active = 0; -} + if (fetch_state == fetch_not_started || hpos < thisline_decision.plfleft) + return 0; -void do_disk(void) -{ - if (dskdmaen != 2 && (regs.spcflags & SPCFLAG_DISK)) { - fprintf(stderr, "BUG!\n"); - return; - } - if (dmaen(0x10)){ - if (dsklen & 0x4000) { - if (!chipmem_bank.check (dskpt, 2*dsklength)) { - fprintf(stderr, "warning: Bad disk write DMA pointer\n"); - } else { - UBYTE *mfmp = get_real_address (dskpt); - int i; - DISK_InitWrite(); - - for (i = 0; i < dsklength; i++) { - UWORD d = (*mfmp << 8) + *(mfmp+1); - mfmwrite[i] = d; - mfmp += 2; - } - DISK_WriteData(dsklength); - } - } else { - int result = DISK_ReadMFM (dskpt); - } - regs.spcflags &= ~SPCFLAG_DISK; - INTREQ(0x9002); - dskdmaen = -1; - } -} + if ((passed_plfstop == 3 && hpos >= thisline_decision.plfright) + || hpos >= estimated_last_fetch_cycle) + return 0; -static void do_sprites(int currvp, int currhp) -{ - int i; - /* The graph in the HRM, p. 195 seems to indicate that sprite 0 is - * fetched at cycle 0x14 and thus can't be disabled by bitplane DMA. */ - int maxspr = currhp/4 - 0x14/4; + t = cycles_for_plane[planes][(hpos + cycle_diagram_shift) & 7]; #if 0 - if (currvp == 0) - return; -#else - /* I don't know whether this is right. Some programs write the sprite pointers - * directly at the start of the copper list. With the currvp==0 check, the - * first two words of data are read on the second line in the frame. The problem - * occurs when the program jumps to another copperlist a few lines further down - * which _also_ writes the sprite pointer registers. This means that a) writing - * to the sprite pointers sets the state to SPR_restart; or b) that sprite DMA - * is disabled until the end of the vertical blanking interval. The HRM - * isn't clear - it says that the vertical sprite position can be set to any - * value, but this wouldn't be the first mistake... */ - /* Update: I modified one of the programs to write the sprite pointers the - * second time only _after_ the VBlank interval, and it showed the same behaviour - * as it did unmodified under UAE with the above check. This indicates that the - * solution below is correct. */ - if (currvp < vblank_endline) - return; + if (t == -1) + abort (); #endif - if (maxspr < 0) - return; - if (maxspr > 8) - maxspr = 8; - - for (i = 0; i < maxspr; i++) { - int fetch = 0; - - if (spron[i] == SPR_restart) { - fetch = 2; - spron[i] = SPR_waiting_start; - } else if ((spron[i] == SPR_waiting_start && sprvstart[i] == vpos) || spron[i] == SPR_waiting_stop) { - fetch = 1; - spron[i] = SPR_waiting_stop; - } - if (spron[i] == SPR_waiting_stop && sprvstop[i] == vpos) { - fetch = 2; - spron[i] = SPR_waiting_start; - } - - if (fetch && dmaen(DMA_SPRITE)) { - UWORD data1 = chipmem_bank.wget(sprpt[i]); - UWORD data2 = chipmem_bank.wget(sprpt[i]+2); - sprpt[i] += 4; - - if (fetch == 1) { - /* Hack for X mouse auto-calibration */ - if (i == 0 && !sprvbfl && ((sprpos[0] & 0xff) << 2) > 2 * DISPLAY_LEFT_SHIFT) { - spr0ctl=sprctl[0]; - spr0pos=sprpos[0]; - sprvbfl=2; - } - SPRxDATB_1(data2, i); - SPRxDATA_1(data1, i); - } else { - SPRxPOS_1(data1, i); - SPRxCTL_1(data2, i); - } - } - } + return t; } -#if AGA_CHIPSET == 0 -static void pfield_sprite (int num, int sprxp, UWORD data, UWORD datb, int ctl) +STATIC_INLINE int dangerous_reg (int reg) { - int i; - int sprx_inc; - int *lookup = bpldualpf ? (bpldualpfpri ? dblpf_ind2 : dblpf_ind1) : linear_map_256; - int *lookup_no = bpldualpf ? (bpldualpfpri ? dblpf_2nd2 : dblpf_2nd1) : lots_of_twos; - - sprx_inc = 1; - if (!gfx_requested_lores) - sprx_inc = 2; -#ifdef LORES_HACK - else if (gfx_requested_lores == 2) - sprxp >>= 1; -#endif - sprxp += sprx_inc * 16 - sprx_inc; - for(i = 15; i >= 0; i--, data >>= 1, datb >>= 1, sprxp -= sprx_inc) { - int plno; - int col; - - /* Check the priority, but only if we did not already put a sprite - * pixel at this position. If there's already a sprite pixel here, - * the priority was previously tested. */ - if (!spixstate[sprxp]) { - /* ??? What about hires mode when one hires pixel is 0, enabling the - * sprite, and the other is != 0, blocking it? */ - plno = lookup_no[pixdata.apixels[sprxp]]; - if (plno != 0 && (1 << num) >= plfpri[plno]) - continue; - } - - if ((ctl & 0x80) && (num & 1)) { - /* Attached sprite */ - col = ((data << 2) & 4) + ((datb << 3) & 8); - spixstate[sprxp] |= 1 << (num-1); - spixels[sprxp] = col; - } else { - col = (data & 1) | ((datb << 1) & 2); - if (spixstate[sprxp] & (1 << num)) { - /* Finish attached sprite */ - /* Did the upper half of the sprite have any bits set? */ - if (spixstate[sprxp] & (1 << (num+1))) - col += spixels[sprxp]; - /* Is there any sprite pixel at this position at all? */ - if (!col) { - spixstate[sprxp] &= ~(3 << num); - } else - col += 16; - } else { - if (col) { - col |= 16 | ((num & 6) << 1); - } - } - } - if (col) { - pixdata.apixels[sprxp] = col; - spixstate[sprxp] |= 1 << num; - - if (!gfx_requested_lores) { - pixdata.apixels[sprxp+1] = col; - spixstate[sprxp+1] |= 1 << num; - } - if (bplham && bplplanecnt == 6) { - ham_linebuf[sprxp] = colors_for_drawing.color_regs[col]; - if (!gfx_requested_lores) - ham_linebuf[sprxp+1] = colors_for_drawing.color_regs[col]; - } - } - } + /* Safe: + * Bitplane pointers, control registers, modulos and data. + * Sprite pointers, control registers, and data. + * Color registers. */ + if (reg >= 0xE0 && reg < 0x1C0) + return 0; + return 1; } -#else /* AGA version */ -static void pfield_sprite (int num, int sprxp, UWORD data, UWORD datb) -{ - int i; - int *lookup = bpldualpf ? (bpldualpfpri ? dblpf_ind2 : dblpf_ind1) : linear_map_256; - int *lookup_no = bpldualpf ? (bpldualpfpri ? dblpf_2nd2 : dblpf_2nd1) : lots_of_twos; +#define FAST_COPPER 1 - for(i = 15; i >= 0; i--, data >>= 1, datb >>= 1) { - int sprxpos = sprxp + (i << lores_shift); - int plno; - int col; - - /* Check the priority, but only if we did not already put a sprite - * pixel at this position. If there's already a sprite pixel here, - * the priority was previously tested. */ - if (!spixstate[sprxpos]) { - /* ??? What about hires mode when one hires pixel is 0, enabling the - * sprite, and the other is != 0, blocking it? */ - plno = lookup_no[pixdata.apixels[sprxpos]]; - if (plno != 0 && (1 << num) >= plfpri[plno]) - continue; - } - - if ((sprctl[num] & 0x80) && (num & 1)) { - /* Attached sprite */ - col = ((data << 2) & 4) + ((datb << 3) & 8); - spixstate[sprxpos] |= 1 << (num-1); - spixels[sprxpos] = col; - } else { - col = (data & 1) | ((datb << 1) & 2); - if (spixstate[sprxpos] & (1 << num)) { - /* Finish attached sprite */ - /* Did the upper half of the sprite have any bits set? */ - if (spixstate[sprxpos] & (1 << (num+1))) - col += spixels[sprxpos]; - /* Is there any sprite pixel at this position at all? */ - if (!col) { - spixstate[sprxpos] &= ~(3 << num); - } else - col += ((bplcon4 << (num & 1 ? 4 : 0)) & 240); - } else { - if (col) { - col |= ((bplcon4 << (num & 1 ? 4 : 0)) & 240) | ((num & 6) << 1); - } - } - } - if (col) { - pixdata.apixels[sprxpos] = col; - spixstate[sprxpos] |= 1<plflinelen >> 1; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v1, v2; - - data1 = *(ptr + i); - v1 = hirestab_h[data1][0]; - v2 = hirestab_h[data1][1]; - - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data2][0] << 2; - v2 |= hirestab_h[data2][1] << 2; - } - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data3][0] << 4; - v2 |= hirestab_h[data3][1] << 4; - } - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data4][0] << 6; - v2 |= hirestab_h[data4][1] << 6; - } - if (do_or) { - app[i*2] |= v1 << 1; - app[i*2 + 1] |= v2 << 1; - } else { - app[i*2] = v1; - app[i*2 + 1] = v2; - } + default: + break; } -} - -static __inline__ void doop_planes_lores_unaligned_h (ULONG *app, UBYTE *ptr, int nplanes, int do_or) -{ - int i; - int len = dp_for_drawing->plflinelen >> 2; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v1, v2, v3, v4; - - data1 = *(ptr + i); - v1 = lorestab_h[data1][0]; - v2 = lorestab_h[data1][1]; - v3 = lorestab_h[data1][2]; - v4 = lorestab_h[data1][3]; - - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data2][0] << 2; - v2 |= lorestab_h[data2][1] << 2; - v3 |= lorestab_h[data2][2] << 2; - v4 |= lorestab_h[data2][3] << 2; - } - - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data3][0] << 4; - v2 |= lorestab_h[data3][1] << 4; - v3 |= lorestab_h[data3][2] << 4; - v4 |= lorestab_h[data3][3] << 4; - } - - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data4][0] << 6; - v2 |= lorestab_h[data4][1] << 6; - v3 |= lorestab_h[data4][2] << 6; - v4 |= lorestab_h[data4][3] << 6; - } - if (do_or) { - app[i*4] |= v1 << 1; - app[i*4 + 1] |= v2 << 1; - app[i*4 + 2] |= v3 << 1; - app[i*4 + 3] |= v4 << 1; + /* Only needed for COP_wait, but let's shut up the compiler. */ + w1 = cop_state.saved_i1; + w2 = cop_state.saved_i2; + cop_state.first_sync = c_hpos; + cop_state.regtypes_modified = REGTYPE_FORCE; + + /* Get this case out of the way, so that the loop below only has to deal + with read1 and wait. */ + if (state == COP_read2) { + w1 = cop_state.i1; + if (w1 & 1) { + w2 = chipmem_wget (ip); + if (w2 & 1) + goto done; + state = COP_wait; + c_hpos += 4; + } else if (dangerous_reg (w1)) { + c_hpos += 4; + goto done; } else { - app[i*4] = v1; - app[i*4 + 1] = v2; - app[i*4 + 2] = v3; - app[i*4 + 3] = v4; - } - } -} - -static __inline__ void doop_planes_hires_unaligned_l (ULONG *app, UBYTE *ptr, int nplanes, int do_or) -{ - int i; - int len = dp_for_drawing->plflinelen >> 1; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v; - - data1 = *(ptr + i); - v = hirestab_l[data1][0]; - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data2][0] << 2; - } - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data3][0] << 4; - } - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data4][0] << 6; - } - if (do_or) - app[i] |= v << 1; - else - app[i] = v; + cop_state.regtypes_modified |= regtypes[w1 & 0x1FE]; + state = COP_read1; + c_hpos += 2; + } + ip += 2; + } + + while (c_hpos + 1 < maxhpos) { + if (state == COP_read1) { + w1 = chipmem_wget (ip); + if (w1 & 1) { + w2 = chipmem_wget (ip + 2); + if (w2 & 1) + break; + state = COP_wait; + c_hpos += 6; + } else if (dangerous_reg (w1)) { + c_hpos += 6; + goto done; + } else { + cop_state.regtypes_modified |= regtypes[w1 & 0x1FE]; + c_hpos += 4; + } + ip += 4; + } else if (state == COP_wait) { + if ((w2 & 0xFE) != 0xFE) + break; + else { + unsigned int vcmp = (w1 & (w2 | 0x8000)) >> 8; + unsigned int hcmp = (w1 & 0xFE); + + unsigned int vp = vpos & (((w2 >> 8) & 0x7F) | 0x80); + if (vp < vcmp) { + /* Whee. We can wait until the end of the line! */ + c_hpos = maxhpos; + } else if (vp > vcmp || hcmp <= c_hpos) { + state = COP_read1; + /* minimum wakeup time */ + c_hpos += 2; + } else { + state = COP_read1; + c_hpos = hcmp; + } + /* If this is the current instruction, remember that we don't + need to sync CPU and copper anytime soon. */ + if (cop_state.ip == ip) { + cop_state.first_sync = c_hpos; + } + } + } else + abort (); } -} -static __inline__ void doop_planes_lores_unaligned_l (ULONG *app, UBYTE *ptr, int nplanes, int do_or) -{ - int i; - int len = dp_for_drawing->plflinelen >> 2; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v1, v2; - - data1 = *(ptr + i); - v1 = lorestab_l[data1][0]; - v2 = lorestab_l[data1][1]; - - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data2][0] << 2; - v2 |= lorestab_l[data2][1] << 2; - } - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data3][0] << 4; - v2 |= lorestab_l[data3][1] << 4; - } - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data4][0] << 6; - v2 |= lorestab_l[data4][1] << 6; - } - if (do_or) { - app[i*2] |= v1 << 1; - app[i*2 + 1] |= v2 << 1; - } else { - app[i*2] = v1; - app[i*2 + 1] = v2; - } + done: + cycle_count = c_hpos - cop_state.hpos; + if (cycle_count >= 8) { + unset_special (SPCFLAG_COPPER); + eventtab[ev_copper].active = 1; + eventtab[ev_copper].oldcycles = get_cycles (); + eventtab[ev_copper].evtime = get_cycles () + cycle_count * CYCLE_UNIT; + events_schedule (); } } -static __inline__ void set_planes_hires_unaligned_h (ULONG *app, UBYTE *ptr, int nplanes) +static void perform_copper_write (int old_hpos) { - int i; - int len = dp_for_drawing->plflinelen >> 1; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v1, v2; - - data1 = *(ptr + i); - v1 = hirestab_h[data1][0]; - v2 = hirestab_h[data1][1]; - - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 1*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data2][0] << 1; - v2 |= hirestab_h[data2][1] << 1; - } - - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data3][0] << 2; - v2 |= hirestab_h[data3][1] << 2; - } - - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 3*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data4][0] << 3; - v2 |= hirestab_h[data4][1] << 3; - } - - if (nplanes > 4) { - unsigned int data5 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data5][0] << 4; - v2 |= hirestab_h[data5][1] << 4; - } - - if (nplanes > 5) { - unsigned int data6 = *(ptr + i + 5*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data6][0] << 5; - v2 |= hirestab_h[data6][1] << 5; - } - - if (nplanes > 6) { - unsigned int data7 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data7][0] << 6; - v2 |= hirestab_h[data7][1] << 6; - } - - if (nplanes > 7) { - unsigned int data8 = *(ptr + i + 7*MAX_WORDS_PER_LINE*2); - v1 |= hirestab_h[data8][0] << 7; - v2 |= hirestab_h[data8][1] << 7; - } - app[i*2] = v1; - app[i*2 + 1] = v2; + int vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); + int hp; + unsigned int address = cop_state.saved_i1 & 0x1FE; + + record_copper (cop_state.saved_ip - 4, old_hpos, vpos); + + if (address < (copcon & 2 ? ((currprefs.chipset_mask & CSMASK_AGA) ? 0 : 0x40u) : 0x80u)) { + cop_state.state = COP_stop; + copper_enabled_thisline = 0; + unset_special (SPCFLAG_COPPER); + return; } -} -static __inline__ void set_planes_lores_unaligned_h (ULONG *app, UBYTE *ptr, int nplanes) -{ - int i; - int len = dp_for_drawing->plflinelen >> 2; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v1, v2, v3, v4; - - data1 = *(ptr + i); - v1 = lorestab_h[data1][0]; - v2 = lorestab_h[data1][1]; - v3 = lorestab_h[data1][2]; - v4 = lorestab_h[data1][3]; - - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 1*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data2][0] << 1; - v2 |= lorestab_h[data2][1] << 1; - v3 |= lorestab_h[data2][2] << 1; - v4 |= lorestab_h[data2][3] << 1; - } - - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data3][0] << 2; - v2 |= lorestab_h[data3][1] << 2; - v3 |= lorestab_h[data3][2] << 2; - v4 |= lorestab_h[data3][3] << 2; - } - - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 3*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data4][0] << 3; - v2 |= lorestab_h[data4][1] << 3; - v3 |= lorestab_h[data4][2] << 3; - v4 |= lorestab_h[data4][3] << 3; - } - - if (nplanes > 4) { - unsigned int data5 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data5][0] << 4; - v2 |= lorestab_h[data5][1] << 4; - v3 |= lorestab_h[data5][2] << 4; - v4 |= lorestab_h[data5][3] << 4; - } - - if (nplanes > 5) { - unsigned int data6 = *(ptr + i + 5*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data6][0] << 5; - v2 |= lorestab_h[data6][1] << 5; - v3 |= lorestab_h[data6][2] << 5; - v4 |= lorestab_h[data6][3] << 5; - } - - if (nplanes > 6) { - unsigned int data7 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data7][0] << 6; - v2 |= lorestab_h[data7][1] << 6; - v3 |= lorestab_h[data7][2] << 6; - v4 |= lorestab_h[data7][3] << 6; - } - - if (nplanes > 7) { - unsigned int data8 = *(ptr + i + 7*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_h[data8][0] << 7; - v2 |= lorestab_h[data8][1] << 7; - v3 |= lorestab_h[data8][2] << 7; - v4 |= lorestab_h[data8][3] << 7; - } - app[i*4] = v1; - app[i*4 + 1] = v2; - app[i*4 + 2] = v3; - app[i*4 + 3] = v4; - } + if (address == 0x88) { + cop_state.ip = cop1lc; + cop_state.state = COP_read1_in2; + } else if (address == 0x8A) { + cop_state.ip = cop2lc; + cop_state.state = COP_read1_in2; + } else + custom_wput_1 (old_hpos, address, cop_state.saved_i2); } -static __inline__ void set_planes_hires_unaligned_l (ULONG *app, UBYTE *ptr, int nplanes) +static void update_copper (int until_hpos) { - int i; - int len = dp_for_drawing->plflinelen >> 1; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v; + int vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); + int c_hpos = cop_state.hpos; - data1 = *(ptr + i); - - v = hirestab_l[data1][0]; - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 1*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data2][0] << 1; - } - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data3][0] << 2; - } - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 3*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data4][0] << 3; - } - if (nplanes > 4) { - unsigned int data5 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data5][0] << 4; - } - if (nplanes > 5) { - unsigned int data6 = *(ptr + i + 5*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data6][0] << 5; - } - if (nplanes > 6) { - unsigned int data7 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data7][0] << 6; - } - if (nplanes > 7) { - unsigned int data8 = *(ptr + i + 7*MAX_WORDS_PER_LINE*2); - v |= hirestab_l[data8][0] << 7; - } - app[i] = v; - } -} + if (eventtab[ev_copper].active) + abort (); -static __inline__ void set_planes_lores_unaligned_l (ULONG *app, UBYTE *ptr, int nplanes) -{ - int i; - int len = dp_for_drawing->plflinelen >> 2; - for (i = 0; i < len; i++) { - unsigned int data1; - ULONG v1, v2; + if (cop_state.state == COP_wait && vp < cop_state.vcmp) + abort (); - data1 = *(ptr + i); - v1 = lorestab_l[data1][0]; - v2 = lorestab_l[data1][1]; + until_hpos &= ~1; - if (nplanes > 1) { - unsigned int data2 = *(ptr + i + 1*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data2][0] << 1; - v2 |= lorestab_l[data2][1] << 1; - } + if (until_hpos > (maxhpos & ~1)) + until_hpos = maxhpos & ~1; - if (nplanes > 2) { - unsigned int data3 = *(ptr + i + 2*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data3][0] << 2; - v2 |= lorestab_l[data3][1] << 2; - } + until_hpos += 2; + for (;;) { + int old_hpos = c_hpos; + int hp; - if (nplanes > 3) { - unsigned int data4 = *(ptr + i + 3*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data4][0] << 3; - v2 |= lorestab_l[data4][1] << 3; - } + if (c_hpos >= until_hpos) + break; - if (nplanes > 4) { - unsigned int data5 = *(ptr + i + 4*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data5][0] << 4; - v2 |= lorestab_l[data5][1] << 4; - } + /* So we know about the fetch state. */ + decide_line (c_hpos); - if (nplanes > 5) { - unsigned int data6 = *(ptr + i + 5*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data6][0] << 5; - v2 |= lorestab_l[data6][1] << 5; - } + switch (cop_state.state) { + case COP_read1_in2: + cop_state.state = COP_read1; + break; + case COP_read1_wr_in2: + cop_state.state = COP_read1; + perform_copper_write (old_hpos); + /* That could have turned off the copper. */ + if (! copper_enabled_thisline) + goto out; - if (nplanes > 6) { - unsigned int data7 = *(ptr + i + 6*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data7][0] << 6; - v2 |= lorestab_l[data7][1] << 6; - } - - if (nplanes > 7) { - unsigned int data8 = *(ptr + i + 7*MAX_WORDS_PER_LINE*2); - v1 |= lorestab_l[data8][0] << 7; - v2 |= lorestab_l[data8][1] << 7; - } + break; + case COP_read1_wr_in4: + cop_state.state = COP_read1_wr_in2; + break; + case COP_read2_wr_in2: + cop_state.state = COP_read2; + perform_copper_write (old_hpos); + /* That could have turned off the copper. */ + if (! copper_enabled_thisline) + goto out; - app[i*2] = v1; - app[i*2 + 1] = v2; - } -} + break; + case COP_wait_in2: + cop_state.state = COP_wait1; + break; + case COP_wait_in4: + cop_state.state = COP_wait_in2; + break; + case COP_skip_in2: + { + static int skipped_before; + unsigned int vcmp, hcmp, vp1, hp1; + cop_state.state = COP_read1_in2; -static void pfield_doline_unaligned_h (int lineno) -{ - int xpos = dp_for_drawing->plfstrt * 4 - DISPLAY_LEFT_SHIFT * 2; - - if (bplhires) { - int xpos1 = xpos + 16 + bpldelay1*2; - int xpos2; - UBYTE *dataptr = line_data[lineno]; - - if (bpldelay1 == bpldelay2) { - switch (bplplanecnt) { - case 1: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1); break; - case 2: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2); break; - case 3: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3); break; - case 4: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4); break; - case 5: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 5); break; - case 6: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 6); break; - case 7: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 7); break; - case 8: set_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 8); break; - } - } else { - switch (bplplanecnt) { - case 1: case 2: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1, 0); break; - case 3: case 4: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2, 0); break; - case 5: case 6: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3, 0); break; - case 7: case 8: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4, 0); break; - } + vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8; + hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xFE); - dataptr += MAX_WORDS_PER_LINE*2; - xpos2 = xpos + 16 + bpldelay2*2; - switch (bplplanecnt) { - case 2: case 3: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 1, 1); break; - case 4: case 5: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 2, 1); break; - case 6: case 7: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 3, 1); break; - case 8: doop_planes_hires_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 4, 1); break; + if (! skipped_before) { + skipped_before = 1; + write_log ("Program uses Copper SKIP instruction.\n"); } - } - } else { - int xpos1 = xpos + 32 + bpldelay1*2; - int xpos2; - UBYTE *dataptr = line_data[lineno]; - - if (bpldelay1 == bpldelay2) { - switch (bplplanecnt) { - case 1: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1); break; - case 2: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2); break; - case 3: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3); break; - case 4: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4); break; - case 5: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 5); break; - case 6: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 6); break; - case 7: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 7); break; - case 8: set_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 8); break; - } - } else { - switch (bplplanecnt) { - case 1: case 2: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1, 0); break; - case 3: case 4: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2, 0); break; - case 5: case 6: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3, 0); break; - case 7: case 8: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4, 0); break; - } - - dataptr += MAX_WORDS_PER_LINE*2; - xpos2 = xpos + 32 + bpldelay2*2; - switch (bplplanecnt) { - case 2: case 3: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 1, 1); break; - case 4: case 5: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 2, 1); break; - case 6: case 7: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 3, 1); break; - case 8: doop_planes_lores_unaligned_h ((ULONG *)(pixdata.apixels + xpos2), dataptr, 4, 1); break; - } - } - } -} -static void pfield_doline_unaligned_l (int lineno) -{ - int xpos = dp_for_drawing->plfstrt * 2 - DISPLAY_LEFT_SHIFT; - - if (bplhires) { - int xpos1 = xpos + 8 + bpldelay1; - int xpos2; - UBYTE *dataptr = line_data[lineno]; - - if (bpldelay1 == bpldelay2) { - switch (bplplanecnt) { - case 1: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1); break; - case 2: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2); break; - case 3: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3); break; - case 4: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4); break; - case 5: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 5); break; - case 6: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 6); break; - case 7: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 7); break; - case 8: set_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 8); break; - } - } else { - switch (bplplanecnt) { - case 1: case 2: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1, 0); break; - case 3: case 4: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2, 0); break; - case 5: case 6: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3, 0); break; - case 7: case 8: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4, 0); break; - } + vp1 = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); + hp1 = old_hpos & (cop_state.saved_i2 & 0xFE); - dataptr += MAX_WORDS_PER_LINE*2; - xpos2 = xpos + 8 + bpldelay2; - switch (bplplanecnt) { - case 2: case 3: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 1, 1); break; - case 4: case 5: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 2, 1); break; - case 6: case 7: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 3, 1); break; - case 8: doop_planes_hires_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 4, 1); break; - } + if ((vp1 > vcmp || (vp1 == vcmp && hp1 >= hcmp)) + && ((cop_state.saved_i2 & 0x8000) != 0 || ! (DMACONR() & 0x4000))) + cop_state.ignore_next = 1; + break; } - } else { - int xpos1 = xpos + 16 + bpldelay1; - int xpos2; - UBYTE *dataptr = line_data[lineno]; - - if (bpldelay1 == bpldelay2) { - switch (bplplanecnt) { - case 1: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1); break; - case 2: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2); break; - case 3: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3); break; - case 4: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4); break; - case 5: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 5); break; - case 6: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 6); break; - case 7: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 7); break; - case 8: set_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 8); break; - } - } else { - switch (bplplanecnt) { - case 1: case 2: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 1, 0); break; - case 3: case 4: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 2, 0); break; - case 5: case 6: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 3, 0); break; - case 7: case 8: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos1), dataptr, 4, 0); break; - } - - dataptr += MAX_WORDS_PER_LINE*2; - xpos2 = xpos + 16 + bpldelay2; - switch (bplplanecnt) { - case 2: case 3: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 1, 1); break; - case 4: case 5: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 2, 1); break; - case 6: case 7: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 3, 1); break; - case 8: doop_planes_lores_unaligned_l ((ULONG *)(pixdata.apixels + xpos2), dataptr, 4, 1); break; - } + case COP_skip_in4: + cop_state.state = COP_skip_in2; + break; + default: + break; } - } -} - -#define pfield_doline_h pfield_doline_unaligned_h -#define pfield_doline_l pfield_doline_unaligned_l - -#else /* not UNALIGNED_PROFITABLE */ - - -static __inline__ void pfield_orword_hires_h(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; - - *pixptr |= hirestab_h[data >> 8][0] << bit; - *(pixptr+1) |= hirestab_h[data >> 8][1] << bit; - *(pixptr+2) |= hirestab_h[data & 255][0] << bit; - *(pixptr+3) |= hirestab_h[data & 255][1] << bit; -} - -static __inline__ void pfield_orword_lores_h(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; - - *pixptr |= lorestab_h[data >> 8][0] << bit; - *(pixptr+1) |= lorestab_h[data >> 8][1] << bit; - *(pixptr+2) |= lorestab_h[data >> 8][2] << bit; - *(pixptr+3) |= lorestab_h[data >> 8][3] << bit; - *(pixptr+4) |= lorestab_h[data & 255][0] << bit; - *(pixptr+5) |= lorestab_h[data & 255][1] << bit; - *(pixptr+6) |= lorestab_h[data & 255][2] << bit; - *(pixptr+7) |= lorestab_h[data & 255][3] << bit; -} -static __inline__ void pfield_setword_hires_h(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; - - *pixptr = hirestab_h[data >> 8][0] << bit; - *(pixptr+1) = hirestab_h[data >> 8][1] << bit; - *(pixptr+2) = hirestab_h[data & 255][0] << bit; - *(pixptr+3) = hirestab_h[data & 255][1] << bit; -} - -static __inline__ void pfield_setword_lores_h(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; - - *pixptr = lorestab_h[data >> 8][0] << bit; - *(pixptr+1) = lorestab_h[data >> 8][1] << bit; - *(pixptr+2) = lorestab_h[data >> 8][2] << bit; - *(pixptr+3) = lorestab_h[data >> 8][3] << bit; - *(pixptr+4) = lorestab_h[data & 255][0] << bit; - *(pixptr+5) = lorestab_h[data & 255][1] << bit; - *(pixptr+6) = lorestab_h[data & 255][2] << bit; - *(pixptr+7) = lorestab_h[data & 255][3] << bit; -} + c_hpos += 2; + if (copper_cant_read (old_hpos, corrected_nr_planes_from_bplcon0)) + continue; -static __inline__ void pfield_orword_hires_l(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; + switch (cop_state.state) { + case COP_read1_wr_in4: + abort (); + + case COP_read1_wr_in2: + case COP_read1: + cop_state.i1 = chipmem_wget (cop_state.ip); + cop_state.ip += 2; + cop_state.state = cop_state.state == COP_read1 ? COP_read2 : COP_read2_wr_in2; + break; - *pixptr |= hirestab_l[data >> 8][0] << bit; - *(pixptr+1) |= hirestab_l[data & 255][0] << bit; -} + case COP_read2_wr_in2: + abort (); -static __inline__ void pfield_orword_lores_l(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; - - *pixptr |= lorestab_l[data >> 8][0] << bit; - *(pixptr+1) |= lorestab_l[data >> 8][1] << bit; - *(pixptr+2) |= lorestab_l[data & 255][0] << bit; - *(pixptr+3) |= lorestab_l[data & 255][1] << bit; -} + case COP_read2: + cop_state.i2 = chipmem_wget (cop_state.ip); + cop_state.ip += 2; + if (cop_state.ignore_next) { + cop_state.ignore_next = 0; + cop_state.state = COP_read1; + break; + } -static __inline__ void pfield_setword_hires_l(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; + cop_state.saved_i1 = cop_state.i1; + cop_state.saved_i2 = cop_state.i2; + cop_state.saved_ip = cop_state.ip; + + if (cop_state.i1 & 1) { + if (cop_state.i2 & 1) + cop_state.state = COP_skip_in4; + else + cop_state.state = COP_wait_in4; + } else + cop_state.state = COP_read1_wr_in4; + break; - *pixptr = hirestab_l[data >> 8][0] << bit; - *(pixptr+1) = hirestab_l[data & 255][0] << bit; -} + case COP_wait1: + cop_state.state = COP_wait; -static __inline__ void pfield_setword_lores_l(int data, unsigned char *dp, int bit) -{ - ULONG *pixptr = (ULONG *)dp; - - *pixptr = lorestab_l[data >> 8][0] << bit; - *(pixptr+1) = lorestab_l[data >> 8][1] << bit; - *(pixptr+2) = lorestab_l[data & 255][0] << bit; - *(pixptr+3) = lorestab_l[data & 255][1] << bit; -} - -#define DO_ONE_PLANE(POINTER, MULT, FUNC, DELAY, LL_SUB, P_ADD) { \ - int i; \ - unsigned int bpldat1; \ - UWORD data; \ - unsigned int bpldat2 = 0; \ - UBYTE *ptr = (POINTER); \ - for (i = dp_for_drawing->plflinelen; i > 0; i -= LL_SUB) { \ - bpldat1 = bpldat2; \ - bpldat2 = do_get_mem_word ((UWORD *)ptr); \ - ptr+=2; \ - data = (bpldat1 << (16 - DELAY)) | (bpldat2 >> DELAY); \ - FUNC(data, app, MULT); \ - app += P_ADD; \ - } \ - data = bpldat2 << (16 - DELAY); \ - FUNC(data, app, MULT); \ -} + cop_state.vcmp = (cop_state.saved_i1 & (cop_state.saved_i2 | 0x8000)) >> 8; + cop_state.hcmp = (cop_state.saved_i1 & cop_state.saved_i2 & 0xFE); -#ifdef SMART_UPDATE -#define DATA_POINTER(n) (dataptr + (n)*MAX_WORDS_PER_LINE*2) -#else -#define DATA_POINTER(n) (real_bplpt[n]) -#endif + vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); -static void pfield_doline_aligned_h (int lineno) -{ - int xpos = dp_for_drawing->plfstrt * 4 - DISPLAY_LEFT_SHIFT * 2; - - if (bplhires) { - if (bplplanecnt > 0) { - int xpos1 = xpos + 16 + (bpldelay1 >= 8 ? 16 : 0); - int xpos2 = xpos + 16 + (bpldelay2 >= 8 ? 16 : 0); - int delay1 = 2*(bpldelay1 & 7); - int delay2 = 2*(bpldelay2 & 7); - unsigned char *app = pixdata.apixels + xpos1; - UBYTE *dataptr = line_data[lineno]; - - DO_ONE_PLANE(DATA_POINTER(0), 0, pfield_setword_hires_h, delay1, 4, 16); - if (bplplanecnt > 2) { - app = pixdata.apixels + xpos1; - DO_ONE_PLANE(DATA_POINTER(2), 2, pfield_orword_hires_h, delay1, 4, 16); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 4) { - app = pixdata.apixels + xpos1; - DO_ONE_PLANE(DATA_POINTER(4), 4, pfield_orword_hires_h, delay1, 4, 16); - } - if (bplplanecnt > 6) { - app = pixdata.apixels + xpos1; - DO_ONE_PLANE(DATA_POINTER(6), 6, pfield_orword_hires_h, delay1, 4, 16); - } -#endif - if (bplplanecnt > 1) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_hires_h, delay2, 4, 16); - } - if (bplplanecnt > 3) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(3), 3, pfield_orword_hires_h, delay2, 4, 16); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 5) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(5), 5, pfield_orword_hires_h, delay2, 4, 16); - } - if (bplplanecnt > 7) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_hires_h, delay2, 4, 16); - } -#endif - } else { - memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); - } - } else { - if (bplplanecnt > 0) { - int x = xpos + 32; - unsigned char *app = pixdata.apixels + x; - UBYTE *dataptr = line_data[lineno]; - - DO_ONE_PLANE(DATA_POINTER(0), 0, pfield_setword_lores_h, bpldelay1, 8, 32); - if (bplplanecnt > 2) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(2), 2, pfield_orword_lores_h, bpldelay1, 8, 32); - } - if (bplplanecnt > 4) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(4), 4, pfield_orword_lores_h, bpldelay1, 8, 32); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 6) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(6), 6, pfield_orword_lores_h, bpldelay1, 8, 32); - } -#endif - if (bplplanecnt > 1) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_lores_h, bpldelay2, 8, 32); - } - if (bplplanecnt > 3) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(3), 3, pfield_orword_lores_h, bpldelay2, 8, 32); - } - if (bplplanecnt > 5) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(5), 5, pfield_orword_lores_h, bpldelay2, 8, 32); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 7) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_lores_h, bpldelay2, 8, 32); + if (cop_state.saved_i1 == 0xFFFF && cop_state.saved_i2 == 0xFFFE) { + cop_state.state = COP_stop; + copper_enabled_thisline = 0; + unset_special (SPCFLAG_COPPER); + goto out; + } + if (vp < cop_state.vcmp) { + copper_enabled_thisline = 0; + unset_special (SPCFLAG_COPPER); + goto out; + } + + /* fall through */ + do_wait: + case COP_wait: + if (vp < cop_state.vcmp) + abort (); + + hp = c_hpos & (cop_state.saved_i2 & 0xFE); + if (vp == cop_state.vcmp && hp < cop_state.hcmp) { + /* Position not reached yet. */ + if (currprefs.fast_copper && (cop_state.saved_i2 & 0xFE) == 0xFE) { + int wait_finish = cop_state.hcmp - 2; + /* This will leave c_hpos untouched if it's equal to wait_finish. */ + if (wait_finish < c_hpos) + abort (); + else if (wait_finish <= until_hpos) { + c_hpos = wait_finish; + } else + c_hpos = until_hpos; + } + break; } -#endif - } else { - memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); - } - } -} -static void pfield_doline_aligned_l (int lineno) -{ - int xpos = dp_for_drawing->plfstrt * 2 - DISPLAY_LEFT_SHIFT; - - if (bplhires) { - if (bplplanecnt > 0) { - int xpos1 = xpos + 8 + (bpldelay1 >= 8 ? 8 : 0); - int xpos2 = xpos + 8 + (bpldelay2 >= 8 ? 8 : 0); - int delay1 = (bpldelay1 & 7) * 2; - int delay2 = (bpldelay2 & 7) * 2; - unsigned char *app = pixdata.apixels + xpos1; - UBYTE *dataptr = line_data[lineno]; - - DO_ONE_PLANE(DATA_POINTER(0), 0, pfield_setword_hires_l, delay1, 4, 8); - if (bplplanecnt > 2) { - app = pixdata.apixels + xpos1; - DO_ONE_PLANE(DATA_POINTER(2), 2, pfield_orword_hires_l, delay1, 4, 8); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 4) { - app = pixdata.apixels + xpos1; - DO_ONE_PLANE(DATA_POINTER(4), 4, pfield_orword_hires_l, delay1, 4, 8); - } - if (bplplanecnt > 6) { - app = pixdata.apixels + xpos1; - DO_ONE_PLANE(DATA_POINTER(6), 6, pfield_orword_hires_l, delay1, 4, 8); - } -#endif - if (bplplanecnt > 1) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_hires_l, delay2, 4, 8); + /* Now we know that the comparisons were successful. We might still + have to wait for the blitter though. */ + if ((cop_state.saved_i2 & 0x8000) == 0 && (DMACONR() & 0x4000)) { + /* We need to wait for the blitter. */ + cop_state.state = COP_bltwait; + copper_enabled_thisline = 0; + unset_special (SPCFLAG_COPPER); + goto out; } - if (bplplanecnt > 3) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(3), 3, pfield_orword_hires_l, delay2, 4, 8); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 5) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(5), 5, pfield_orword_hires_l, delay2, 4, 8); - } - if (bplplanecnt > 7) { - app = pixdata.apixels + xpos2; - DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_hires_l, delay2, 4, 8); - } -#endif - } else { - memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); - } - } else { - if (bplplanecnt > 0) { - int x = xpos + 16; - int delay1 = bpldelay1; - int delay2 = bpldelay2; - unsigned char *app = pixdata.apixels + x; - UBYTE *dataptr = line_data[lineno]; - - DO_ONE_PLANE(DATA_POINTER(0), 0, pfield_setword_lores_l, delay1, 8, 16); - if (bplplanecnt > 2) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(2), 2, pfield_orword_lores_l, delay1, 8, 16); - } - if (bplplanecnt > 4) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(4), 4, pfield_orword_lores_l, delay1, 8, 16); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 6) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(6), 6, pfield_orword_lores_l, delay1, 8, 16); - } -#endif - if (bplplanecnt > 1) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_lores_l, delay2, 8, 16); - } - if (bplplanecnt > 3) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(3), 3, pfield_orword_lores_l, delay2, 8, 16); - } - if (bplplanecnt > 5) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(5), 5, pfield_orword_lores_l, delay2, 8, 16); - } -#if AGA_CHIPSET == 1 - if (bplplanecnt > 7) { - app = pixdata.apixels + x; - DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_lores_l, delay2, 8, 16); - } -#endif - } else { - memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); - } - } -} -#define pfield_doline_h pfield_doline_aligned_h -#define pfield_doline_l pfield_doline_aligned_l + record_copper (cop_state.ip - 4, old_hpos, vpos); -#endif /* UNALIGNED_PROFITABLE */ + cop_state.state = COP_read1; + break; -static void pfield_adjust_delay (void) -{ - int ddf_left = dp_for_drawing->plfstrt; - int ddf_right = dp_for_drawing->plfstrt + dp_for_drawing->plflinelen; - int i; - - for (i = dip_for_drawing->last_delay_change-1; i >= dip_for_drawing->first_delay_change-1; i--) { - int delayreg = i < dip_for_drawing->first_delay_change ? dp_for_drawing->bplcon1 : delay_changes[i].value; - int delay1 = delayreg & 0xF; - int delay2 = (delayreg >> 4) & 0xF; - int startpos = i == dip_for_drawing->last_delay_change - 1 ? ddf_right + 8 : delay_changes[i+1].linepos; - int stoppos = i < dip_for_drawing->first_delay_change ? ddf_left : delay_changes[i].linepos; - int j; - startpos = PIXEL_XPOS (startpos + (bplhires ? 4 : 8)); - stoppos = PIXEL_XPOS (stoppos + (bplhires ? 4 : 8)); - if (gfx_requested_lores == 0) - delay1 <<= 1, delay2 <<= 1; - else if (gfx_requested_lores == 2) - startpos >>= 1, stoppos >>= 1; - for (j = startpos-1; j >= stoppos; j--) { - pixdata.apixels [j] = (pixdata.apixels[j-delay1] & 0x55) | (pixdata.apixels[j-delay2] & 0xAA); + default: + break; } } -} -static void init_hardware_frame (void) -{ - int i; + out: + cop_state.hpos = c_hpos; - for (i = 0; i < 8; i++) - spron[i] = SPR_vtop; -#if 0 - memset(sprpos, 0, sizeof sprpos); - memset(sprctl, 0, sizeof sprctl); -#endif - - next_lineno = 0; - nln_how = 0; - diwstate = DIW_waiting_start; + /* The test against maxhpos also prevents us from calling predict_copper + when we are being called from hsync_handler, which would not only be + stupid, but actively harmful. */ + if (currprefs.fast_copper && (regs.spcflags & SPCFLAG_COPPER) && c_hpos + 8 < maxhpos) + predict_copper (); } -static void setdontcare(void) +static void compute_spcflag_copper (void) { - fprintf(stderr, "Don't care mouse mode set\n"); - mousestate = dont_care_mouse; - lastspr0x = lastmx; lastspr0y = lastmy; - mstepx = defstepx; mstepy = defstepy; -} + copper_enabled_thisline = 0; + unset_special (SPCFLAG_COPPER); + if (! dmaen (DMA_COPPER) || cop_state.state == COP_stop || cop_state.state == COP_bltwait) + return; -static void setfollow(void) -{ - fprintf(stderr, "Follow sprite mode set\n"); - mousestate = follow_mouse; - lastdiffx = lastdiffy = 0; - sprvbfl = 0; - spr0ctl=spr0pos = 0; - mstepx = defstepx; mstepy = defstepy; -} + if (cop_state.state == COP_wait) { + int vp = vpos & (((cop_state.saved_i2 >> 8) & 0x7F) | 0x80); -void togglemouse(void) -{ - switch(mousestate) { - case dont_care_mouse: setfollow(); break; - case follow_mouse: setdontcare(); break; - default: break; /* Nnnnnghh! */ + if (vp < cop_state.vcmp) + return; } -} + copper_enabled_thisline = 1; -static __inline__ int adjust(int val) -{ - if (val>127) - return 127; - else if (val<-127) - return -127; - return val; -} + if (currprefs.fast_copper) + predict_copper (); -static void do_mouse_hack(void) -{ - int spr0x = ((spr0pos & 0xff) << 2) | ((spr0ctl & 1) << 1); - int spr0y = ((spr0pos >> 8) | ((spr0ctl & 4) << 6)) << 1; - int diffx, diffy; - - switch (mousestate) { - case normal_mouse: - diffx = lastmx - lastsampledmx; - diffy = lastmy - lastsampledmy; - if (!newmousecounters) { - if (diffx > 127) diffx = 127; - if (diffx < -127) diffx = -127; - joy0x += diffx; - if (diffy > 127) diffy = 127; - if (diffy < -127) diffy = -127; - joy0y += diffy; - } - lastsampledmx += diffx; lastsampledmy += diffy; - break; - - case dont_care_mouse: - diffx = adjust (((lastmx-lastspr0x) * mstepx) >> 16); - diffy = adjust (((lastmy-lastspr0y) * mstepy) >> 16); - lastspr0x=lastmx; lastspr0y=lastmy; - joy0x+=diffx; joy0y+=diffy; - break; - - case follow_mouse: - if (sprvbfl && sprvbfl-- >1) { - int mousexpos, mouseypos; - - if ((lastdiffx > docal || lastdiffx < -docal) && lastspr0x != spr0x - && spr0x > plfstrt*4 + 34 + xcaloff && spr0x < plfstop*4 - xcaloff) - { - int val = (lastdiffx << 16) / (spr0x - lastspr0x); - if (val>=0x8000) mstepx=(mstepx*(calweight-1)+val)/calweight; - } - if ((lastdiffy > docal || lastdiffy < -docal) && lastspr0y != spr0y - && spr0y>plffirstline+ycaloff && spr0y=0x8000) mstepy=(mstepy*(calweight-1)+val)/calweight; - } - mousexpos = lastmx + linetoscr_x_adjust; - if (lastmy >= gfxvidinfo.maxline) - lastmy = gfxvidinfo.maxline-1; - mouseypos = native2amiga_line_map[lastmy] + thisframe_y_adjust - minfirstline; - mouseypos <<= 1; - mousexpos <<= (1-lores_shift); - mousexpos += 2*DISPLAY_LEFT_SHIFT; - diffx = adjust ((((mousexpos + xoffs - spr0x) & ~1) * mstepx) >> 16); - diffy = adjust ((((mouseypos + yoffs - spr0y) & ~1) * mstepy) >> 16); - lastspr0x=spr0x; lastspr0y=spr0y; - lastdiffx=diffx; lastdiffy=diffy; - joy0x+=diffx; joy0y+=diffy; - } - break; - } + if (! eventtab[ev_copper].active) + set_special (SPCFLAG_COPPER); } -/* @@@ Are those still useful? */ - -/* - * A raster line has been built in the graphics buffer. Tell the graphics code - * to do anything necessary to display it. - */ -static __inline__ void do_flush_line_1 (int lineno) +static void copper_handler (void) { - if (lineno < first_drawn_line) - first_drawn_line = lineno; - if (lineno > last_drawn_line) - last_drawn_line = lineno; + /* This will take effect immediately, within the same cycle. */ + set_special (SPCFLAG_COPPER); - if (gfxvidinfo.maxblocklines == 0) - flush_line(lineno); - else { - if ((last_block_line+1) != lineno) { - if (first_block_line != -2) - flush_block (first_block_line, last_block_line); - first_block_line = lineno; - } - last_block_line = lineno; - if (last_block_line - first_block_line >= gfxvidinfo.maxblocklines) { - flush_block (first_block_line, last_block_line); - first_block_line = last_block_line = -2; - } - } -} + if (! copper_enabled_thisline) + abort (); -static void do_flush_line (int lineno) -{ - /* We don't want to call X libraries from the second thread right now. */ -#ifndef SUPPORT_PENGUINS - do_flush_line_1 (lineno); -#else - line_drawn[lineno] = 1; -#endif + eventtab[ev_copper].active = 0; } -/* - * One Amiga frame has been finished. Tell the graphics code about it. - * Note that the actual flush_screen() call is a no-op for all reasonable - * systems. - */ - -static void do_flush_screen (int start, int stop) +void blitter_done_notify (void) { - int i; -#ifdef SUPPORT_PENGUINS - for (i = 0; i < gfxvidinfo.maxline; i++) { - if (line_drawn[i]) - do_flush_line_1 (i); - } -#endif - if (gfxvidinfo.maxblocklines != 0 && first_block_line != -2) { - flush_block (first_block_line, last_block_line); - } - if (start <= stop) - flush_screen (start, stop); -} + if (cop_state.state != COP_bltwait) + return; -static void adjust_drawing_colors (int ctable, int bplham) -{ - if (drawing_color_matches != ctable) { - if (bplham) { - memcpy (&colors_for_drawing, curr_color_tables + ctable, - sizeof colors_for_drawing); - color_match_type = color_match_full; - } else { - memcpy (colors_for_drawing.acolors, curr_color_tables[ctable].acolors, - sizeof colors_for_drawing.acolors); - color_match_type = color_match_acolors; - } - drawing_color_matches = ctable; - } else if (bplham && color_match_type != color_match_full) { - memcpy (colors_for_drawing.color_regs, curr_color_tables[ctable].color_regs, - sizeof colors_for_drawing.color_regs); - color_match_type = color_match_full; - } + copper_enabled_thisline = 1; + set_special (SPCFLAG_COPPER); + cop_state.state = COP_wait; } -static __inline__ void adjust_color0_for_color_change (void) +void do_copper (void) { - drawing_color_matches = -1; - if (dp_for_drawing->color0 != 0xFFFFFFFFul) { - colors_for_drawing.color_regs[0] = dp_for_drawing->color0; - colors_for_drawing.acolors[0] = xcolors[dp_for_drawing->color0]; - } + int hpos = current_hpos (); + update_copper (hpos); } -static __inline__ void do_color_changes (line_draw_func worker) +/* ADDR is the address that is going to be read/written; this access is + the reason why we want to update the copper. This function is also + used from hsync_handler to finish up the line; for this case, we check + hpos against maxhpos. */ +STATIC_INLINE void sync_copper_with_cpu (int hpos, int do_schedule, unsigned int addr) { - int lastpos = 0, nextpos, i; - struct color_change *cc = curr_color_changes + dip_for_drawing->first_color_change; - - for (i = dip_for_drawing->first_color_change; i <= dip_for_drawing->last_color_change; i++, cc++) { - if (i == dip_for_drawing->last_color_change) - nextpos = max_diwlastword; - else - nextpos = PIXEL_XPOS (cc->linepos) + (COPPER_MAGIC_FUDGE << lores_shift); - worker (lastpos, nextpos); - if (i != dip_for_drawing->last_color_change) { - colors_for_drawing.color_regs[cc->regno] = cc->value; - colors_for_drawing.acolors[cc->regno] = xcolors[cc->value]; - } - if (nextpos > lastpos) { - lastpos = nextpos; - if (lastpos >= linetoscr_right_x) - break; + /* Need to let the copper advance to the current position. */ + if (eventtab[ev_copper].active) { + if (hpos != maxhpos) { + /* There might be reasons why we don't actually need to bother + updating the copper. */ + if (hpos < cop_state.first_sync) + return; + + if ((cop_state.regtypes_modified & regtypes[addr & 0x1FE]) == 0) + return; } + + eventtab[ev_copper].active = 0; + if (do_schedule) + events_schedule (); + set_special (SPCFLAG_COPPER); } + if (copper_enabled_thisline) + update_copper (hpos); } -static void pfield_expand_dp_bplcon (void) +static void do_sprites (int currvp, int hpos) { - bplhires = (dp_for_drawing->bplcon0 & 0x8000) == 0x8000; - bplplanecnt = (dp_for_drawing->bplcon0 & 0x7000) >> 12; - bplham = (dp_for_drawing->bplcon0 & 0x800) == 0x800; -#if AGA_CHIPSET == 1 /* The KILLEHB bit exists in ECS, but is apparently meant for Genlock - * stuff, and it's set by some demos (e.g. Andromeda Seven Seas) */ - bplehb = ((dp_for_drawing->bplcon0 & 0xFCC0) == 0x6000 && !(dp_for_drawing->bplcon2 & 0x200)); -#else - bplehb = (dp_for_drawing->bplcon0 & 0xFC00) == 0x6000; -#endif - bpldelay1 = dp_for_drawing->bplcon1 & 0xF; - bpldelay2 = (dp_for_drawing->bplcon1 >> 4) & 0xF; - plfpri[1] = 1 << 2*(dp_for_drawing->bplcon2 & 7); - plfpri[2] = 1 << 2*((dp_for_drawing->bplcon2 >> 3) & 7); - bpldualpf = (dp_for_drawing->bplcon0 & 0x400) == 0x400; - bpldualpfpri = (dp_for_drawing->bplcon2 & 0x40) == 0x40; -} + int i; + int maxspr, minspr; + int sw = sprite_width >> 3; -static __inline__ void pfield_draw_line(int lineno, int gfx_ypos, int follow_ypos) -{ - int border = 0; - int to_screen = 0; - int do_double = 0; - - dp_for_drawing = 0; - dip_for_drawing = 0; - switch (linestate[lineno]) { - case LINE_AS_PREVIOUS: - case LINE_REMEMBERED_AS_PREVIOUS: - { - static int warned = 0; - if (!warned) - fprintf(stderr, "Shouldn't get here... this is a bug.\n"), warned++; - } - line_decisions[lineno].which = -2; + /* I don't know whether this is right. Some programs write the sprite pointers + * directly at the start of the copper list. With the test against currvp, the + * first two words of data are read on the second line in the frame. The problem + * occurs when the program jumps to another copperlist a few lines further down + * which _also_ writes the sprite pointer registers. This means that a) writing + * to the sprite pointers sets the state to SPR_restart; or b) that sprite DMA + * is disabled until the end of the vertical blanking interval. The HRM + * isn't clear - it says that the vertical sprite position can be set to any + * value, but this wouldn't be the first mistake... */ + /* Update: I modified one of the programs to write the sprite pointers the + * second time only _after_ the VBlank interval, and it showed the same behaviour + * as it did unmodified under UAE with the above check. This indicates that the + * solution below is correct. */ + /* Another update: seems like we have to use the NTSC value here (see Sanity Turmoil + * demo). */ + /* Maximum for Sanity Turmoil: 27. + Minimum for Sanity Arte: 22. */ + if (currvp < sprite_vblank_endline) return; - case LINE_BORDER_PREV: - border = 1; - dp_for_drawing = line_decisions + lineno - 1; - dip_for_drawing = curr_drawinfo + lineno - 1; - break; - - case LINE_BORDER_NEXT: - border = 1; - dp_for_drawing = line_decisions + lineno + 1; - dip_for_drawing = curr_drawinfo + lineno + 1; - break; + /* The graph in the HRM, p. 195 seems to indicate that sprite 0 is + * fetched at cycle 0x14 and thus can't be disabled by bitplane DMA. */ + maxspr = hpos / 4 - 0x14 / 4; + minspr = last_sprite_hpos / 4 - 0x14 / 4; - case LINE_DONE_AS_PREVIOUS: - line_decisions[lineno].which = -2; - /* fall through */ - case LINE_DONE: + if (minspr > MAX_SPRITES || maxspr < 0) return; - case LINE_DECIDED_DOUBLE: - line_decisions[lineno+1].which = -2; - if (follow_ypos != -1) { - do_double = 1; - linetoscr_double_offset = gfxvidinfo.rowbytes * (follow_ypos - gfx_ypos); - } - - /* fall through */ - default: - dip_for_drawing = curr_drawinfo + lineno; - dp_for_drawing = line_decisions + lineno; - if (dp_for_drawing->which != 1) - border = 1; - break; - } - - if (!line_changed[lineno] && !frame_redraw_necessary) { - /* The case where we can skip redrawing this line. If this line - * is supposed to be doubled, and the next line is remembered as - * having been doubled, then the next line is done as well. */ - if (do_double) { - if (linestate[lineno+1] != LINE_REMEMBERED_AS_PREVIOUS) { - memcpy (gfxvidinfo.bufmem + gfxvidinfo.rowbytes * follow_ypos, - gfxvidinfo.bufmem + gfxvidinfo.rowbytes * gfx_ypos, - gfxvidinfo.rowbytes); - line_decisions[lineno + 1].which = -2; - do_flush_line (follow_ypos); - } - linestate[lineno + 1] = LINE_DONE_AS_PREVIOUS; - } - linestate[lineno] = LINE_DONE; - return; - } + if (maxspr > MAX_SPRITES) + maxspr = MAX_SPRITES; + if (minspr < 0) + minspr = 0; - if (!border) { - xlinebuffer = gfxvidinfo.bufmem + gfxvidinfo.rowbytes * gfx_ypos - gfxvidinfo.pixbytes * linetoscr_x_adjust; - aga_lbufptr = aga_linebuf; - - pfield_expand_dp_bplcon (); + for (i = minspr; i < maxspr; i++) { + int fetch = 0; -#ifdef LORES_HACK - if (!bplhires && !gfx_requested_lores && dip_for_drawing->nr_color_changes == 0 - && !bplham && !bplehb && !bpldualpf) - gfx_requested_lores = 2; -#endif - pfield_init_linetoscr (); - if (dip_for_drawing->first_delay_change != dip_for_drawing->last_delay_change) { - bpldelay1 = bpldelay2 = 0; - if (gfx_requested_lores) - pfield_doline_l (lineno); - else - pfield_doline_h (lineno); - pfield_adjust_delay (); - } else { - if (gfx_requested_lores) - pfield_doline_l (lineno); - else - pfield_doline_h (lineno); - } - - /* Check color0 adjust only if we have color changes - shouldn't happen - * otherwise. */ - adjust_drawing_colors (dp_for_drawing->ctable, bplham || bplehb); - - /* The problem is that we must call decode_ham6() BEFORE we do the - * sprites. */ - if (bplham) { - if (dip_for_drawing->nr_color_changes == 0) { - /* The easy case: need to do HAM decoding only once for the - * full line. */ - decode_ham6 (linetoscr_x_adjust, linetoscr_right_x); - } else /* Argh. */ { - adjust_color0_for_color_change (); - do_color_changes (decode_ham6); - adjust_drawing_colors (dp_for_drawing->ctable, bplham || bplehb); - } - } + if (spr[i].on == 0) + continue; - if (dip_for_drawing->nr_sprites != 0) { - int spr; - for(spr = 7; spr >= 0; spr--) { - struct sprite_draw *spd = curr_sprite_positions + dip_for_drawing->first_sprite_draw; - int i; - - for (i = dip_for_drawing->first_sprite_draw; i < dip_for_drawing->last_sprite_draw; i++, spd++) { - if (spd->num != spr) - continue; - pfield_sprite (spr, spd->linepos, spd->data, spd->datb, spd->ctl); - } - } + if (spr[i].state == SPR_restart) { + fetch = 2; + spr[i].on = 1; + } else if ((spr[i].state == SPR_waiting_start && spr[i].vstart == vpos) || spr[i].state == SPR_waiting_stop) { + fetch = 1; + spr[i].state = SPR_waiting_stop; } - if (dip_for_drawing->nr_color_changes == 0) { - pfield_do_linetoscr_full (do_double); - do_flush_line (gfx_ypos); - linestate[lineno] = LINE_DONE; - - if (do_double) { - linestate[lineno + 1] = LINE_DONE_AS_PREVIOUS; - do_flush_line (follow_ypos); - } - } else { - int lastpos = 0, nextpos, i; - - adjust_color0_for_color_change (); - do_color_changes (pfield_do_linetoscr); - - linestate[lineno] = LINE_DONE; - do_flush_line (gfx_ypos); - if (do_double) { - memcpy (gfxvidinfo.bufmem + gfxvidinfo.rowbytes * follow_ypos, - gfxvidinfo.bufmem + gfxvidinfo.rowbytes * gfx_ypos, - gfxvidinfo.rowbytes); - linestate[lineno + 1] = LINE_DONE_AS_PREVIOUS; - line_decisions[lineno + 1].which = -2; - do_flush_line (follow_ypos); - } + if ((spr[i].state == SPR_waiting_stop || spr[i].state == SPR_waiting_start) && spr[i].vstop == vpos) { + fetch = 2; + spr[i].state = SPR_waiting_start; } - /* Clean up the spixstate array. */ - { - struct sprite_draw *spd = curr_sprite_positions + dip_for_drawing->first_sprite_draw; - int i; -#ifdef LORES_HACK - if (gfx_requested_lores == 2) { - for (i = dip_for_drawing->first_sprite_draw; i < dip_for_drawing->last_sprite_draw; i++, spd++) - fuzzy_memset_le32 (spixstate, 0, (spd->linepos >> 1), 16*sizeof *spixstate); - } else -#endif - if (gfx_requested_lores) { - for (i = dip_for_drawing->first_sprite_draw; i < dip_for_drawing->last_sprite_draw; i++, spd++) - fuzzy_memset_le32 (spixstate, 0, spd->linepos, 16*sizeof *spixstate); - } else { - for (i = dip_for_drawing->first_sprite_draw; i < dip_for_drawing->last_sprite_draw; i++, spd++) - fuzzy_memset_le32 (spixstate, 0, spd->linepos, 32*sizeof *spixstate); - } - } -#ifdef LORES_HACK - if (gfx_requested_lores == 2) - gfx_requested_lores = 0; -#endif - } else { - /* Border. */ - int i, lastpos = 0, nextpos; - struct color_change *cc; + if (fetch && dmaen (DMA_SPRITE)) { + uae_u16 data1 = chipmem_wget (spr[i].pt); + uae_u16 data2 = chipmem_wget (spr[i].pt + sw); - xlinebuffer = gfxvidinfo.bufmem + gfxvidinfo.rowbytes * gfx_ypos - gfxvidinfo.pixbytes * linetoscr_x_adjust; - - adjust_drawing_colors (dp_for_drawing->ctable, 0); - /* Check color0 adjust only if we have color changes - shouldn't happen - * otherwise. */ - - if (dip_for_drawing->nr_color_changes == 0) { - fill_line (); - do_flush_line (gfx_ypos); - linestate[lineno] = LINE_DONE; - if (do_double) { - xlinebuffer = gfxvidinfo.bufmem + gfxvidinfo.rowbytes * follow_ypos - gfxvidinfo.pixbytes * linetoscr_x_adjust; - fill_line (); - do_flush_line (follow_ypos); - linestate[lineno+1] = LINE_DONE_AS_PREVIOUS; + if (fetch == 1) { + /* Hack for X mouse auto-calibration */ + if (i == 0 && !sprvbfl && ((sprpos[0] & 0xff) << 2) > 2 * DISPLAY_LEFT_SHIFT) { + spr0ctl = sprctl[0]; + spr0pos = sprpos[0]; + sprvbfl = 2; + } + SPRxDATB_1 (data2, i); + SPRxDATA_1 (data1, i); + switch (sw) + { + case 64 >> 3: + sprdata[i][3] = chipmem_wget (spr[i].pt + 6); + sprdatb[i][3] = chipmem_wget (spr[i].pt + 6 + sw); + sprdata[i][2] = chipmem_wget (spr[i].pt + 4); + sprdatb[i][2] = chipmem_wget (spr[i].pt + 4 + sw); + /* fall through */ + case 32 >> 3: + sprdata[i][1] = chipmem_wget (spr[i].pt + 2); + sprdatb[i][1] = chipmem_wget (spr[i].pt + 2 + sw); + break; + } + } else { + SPRxPOS_1 (data1, i); + SPRxCTL_1 (data2, i); } - return; - } - - adjust_color0_for_color_change (); - do_color_changes (pfield_do_fill_line); - - do_flush_line (gfx_ypos); - linestate[lineno] = LINE_DONE; - if (do_double) { - memcpy (gfxvidinfo.bufmem + gfxvidinfo.rowbytes * follow_ypos, - gfxvidinfo.bufmem + gfxvidinfo.rowbytes * gfx_ypos, - gfxvidinfo.rowbytes); - linestate[lineno + 1] = LINE_DONE_AS_PREVIOUS; - line_decisions[lineno + 1].which = -2; - do_flush_line (follow_ypos); + spr[i].pt += sw * 2; } } + last_sprite_hpos = hpos; } -#ifdef SUPPORT_PENGUINS -static smp_comm_pipe drawing_pipe, drawing_lock_pipe; -#include -static void drawing_penguin (void) -{ - int l; - /* fprintf(stderr, "Hello, world!\n"); */ - signal (SIGINT, SIG_IGN); - - for (;;) { - /* Start of a frame. */ - int k = read_comm_pipe_int_blocking (&drawing_pipe); - if (k != -2) { - fprintf (stderr, "Penguin got out of sync.\n"); - return; /* what can we do? */ - } +static void init_sprites (void) +{ + int i; - for (;;) { - int i, where; - int l = read_comm_pipe_int_blocking (&drawing_pipe); - if (l == -1) { - /* End-of-frame synchronization. */ - write_comm_pipe_int (&drawing_lock_pipe, -1); - break; - } - /* l is the line that has been finished for drawing. */ - i = l - thisframe_y_adjust_real; - if (i < 0 || i >= max_ypos_thisframe) - continue; + for (i = 0; i < MAX_SPRITES; i++) { + /* ???? */ + spr[i].state = SPR_stop; + spr[i].on = 0; + } - if (linestate[l] == LINE_UNDECIDED) { - fprintf (stderr, "Line scheduled for drawing, but undecided %d!?\n", l); - continue; - } - where = amiga2aspect_line_map[i+min_ypos_for_screen]; - if (where >= gfxvidinfo.maxline || where == -1) - continue; + memset (sprpos, 0, sizeof sprpos); + memset (sprctl, 0, sizeof sprctl); +} - pfield_draw_line (l, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]); +static void adjust_array_sizes (void) +{ +#ifdef OS_WITHOUT_MEMORY_MANAGEMENT + if (delta_sprite_entry) { + void *p1,*p2; + int mcc = max_sprite_entry + 50 + delta_sprite_entry; + delta_sprite_entry = 0; + p1 = realloc (sprite_entries[0], mcc * sizeof (struct sprite_entry)); + p2 = realloc (sprite_entries[1], mcc * sizeof (struct sprite_entry)); + if (p1) sprite_entries[0] = p1; + if (p2) sprite_entries[1] = p2; + if (p1 && p2) { + fprintf (stderr, "new max_sprite_entry=%d\n",mcc); + max_sprite_entry = mcc; + } + } + if (delta_color_change) { + void *p1,*p2; + int mcc = max_color_change + 200 + delta_color_change; + delta_color_change = 0; + p1 = realloc (color_changes[0], mcc * sizeof (struct color_change)); + p2 = realloc (color_changes[1], mcc * sizeof (struct color_change)); + if (p1) color_changes[0] = p1; + if (p2) color_changes[1] = p2; + if (p1 && p2) { + fprintf (stderr, "new max_color_change=%d\n",mcc); + max_color_change = mcc; } } +#endif } -static penguin_id our_penguin; - -static void kill_drawing_penguin (void) +static void init_hardware_frame (void) { - kill (our_penguin, SIGQUIT); + next_lineno = 0; + nextline_how = nln_normal; + diwstate = DIW_waiting_start; + hdiwstate = DIW_waiting_start; } -#endif - -static int penguins_enabled_thisframe; - -static void init_drawing_frame (void) +void init_hardware_for_drawing_frame (void) { - int i, maxline; - -#ifdef OS_WITHOUT_MEMORY_MANAGEMENT - if(_max_sprite_draw > max_sprite_draw) { - void *p1,*p2; - int mcc = _max_sprite_draw + 128; - _max_sprite_draw = 0; - p1 = realloc(sprite_positions[0], mcc*sizeof(struct sprite_draw)); - p2 = realloc(sprite_positions[1], mcc*sizeof(struct sprite_draw)); - if(p1) sprite_positions[0] = p1; - if(p2) sprite_positions[1] = p2; - if(p1 && p2) { - max_sprite_draw = mcc; - printf("sam: sprite: %s\n", mcc); - } - } - if(_max_color_change > max_color_change) { - void *p1,*p2; - int mcc = _max_color_change + 128; - _max_color_change = 0; - p1 = realloc(color_changes[0], mcc*sizeof(struct color_change)); - p2 = realloc(color_changes[1], mcc*sizeof(struct color_change)); - if(p1) color_changes[0] = p1; - if(p2) color_changes[1] = p2; - if(p1 && p2) { - max_color_change = mcc; - printf("sam: color: %s\n", mcc); - } - } -#endif + adjust_array_sizes (); - if (max_diwstop == 0) - max_diwstop = diwlastword; - if (min_diwstart > max_diwstop) - min_diwstart = 0; - - if (thisframe_first_drawn_line == -1) - thisframe_first_drawn_line = minfirstline; - if (thisframe_first_drawn_line > thisframe_last_drawn_line) - thisframe_last_drawn_line = thisframe_first_drawn_line; + /* Avoid this code in the first frame after a customreset. */ + if (prev_sprite_entries) { + int first_pixel = prev_sprite_entries[0].first_pixel; + int npixels = prev_sprite_entries[prev_next_sprite_entry].first_pixel - first_pixel; + memset (spixels + first_pixel, 0, npixels * sizeof *spixels); + memset (spixstate.bytes + first_pixel, 0, npixels * sizeof *spixstate.bytes); + } + prev_next_sprite_entry = next_sprite_entry; next_color_change = 0; - next_delay_change = 0; - next_sprite_draw = 0; - maxline = gfx_requested_linedbl ? (maxvpos+1) * 2 + 1 : (maxvpos+1) + 1; -#ifdef SMART_UPDATE - for (i = 0; i < maxline; i++) - linestate[i] = linestate[i] == LINE_DONE_AS_PREVIOUS ? LINE_REMEMBERED_AS_PREVIOUS : LINE_UNDECIDED; -#else - memset (linestate, LINE_UNDECIDED, maxline); -#endif - last_drawn_line = 0; - first_drawn_line = 32767; - - first_block_line = last_block_line = -2; - if (test_drawing_speed) - frame_redraw_necessary = 1; - else if (frame_redraw_necessary) - frame_redraw_necessary--; - + next_sprite_entry = 0; next_color_entry = 0; remembered_color_entry = -1; - prev_sprite_positions = sprite_positions[current_change_set]; - curr_sprite_positions = sprite_positions[current_change_set ^ 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]; - drawing_color_matches = -1; - color_src_match = color_dest_match = -1; - prev_x_adjust = linetoscr_x_adjust; - prev_y_adjust = thisframe_y_adjust; + prev_drawinfo = line_drawinfo[current_change_set]; + curr_drawinfo = line_drawinfo[current_change_set ^ 1]; + current_change_set ^= 1; - if (gfx_requested_xcenter) { - if (max_diwstop - min_diwstart < gfxvidinfo.maxlinetoscr && gfx_requested_xcenter == 2) - /* Try to center. */ - linetoscr_x_adjust = ((max_diwstop - min_diwstart - gfxvidinfo.maxlinetoscr) / 2 + min_diwstart) & ~1; - else - linetoscr_x_adjust = max_diwstop - gfxvidinfo.maxlinetoscr; + color_src_match = color_dest_match = -1; - /* Should we try to be clever? */ - if (gfx_requested_xcenter == 2) { - /* Don't change x_adjust if wthe new display will fit in the old range. */ - if (linetoscr_x_adjust < prev_x_adjust && prev_x_adjust < min_diwstart) - linetoscr_x_adjust = prev_x_adjust; - } - } else - linetoscr_x_adjust = max_diwlastword - gfxvidinfo.maxlinetoscr; - if (linetoscr_x_adjust < 0) - linetoscr_x_adjust = 0; - - linetoscr_right_x = linetoscr_x_adjust + gfxvidinfo.maxlinetoscr; - if (linetoscr_right_x > max_diwlastword) - linetoscr_right_x = max_diwlastword; - - thisframe_y_adjust = minfirstline; - if (gfx_requested_ycenter && thisframe_first_drawn_line != -1) { - if (thisframe_last_drawn_line - thisframe_first_drawn_line < max_drawn_amiga_line && gfx_requested_ycenter == 2) - thisframe_y_adjust = (thisframe_last_drawn_line - thisframe_first_drawn_line - max_drawn_amiga_line) / 2 + thisframe_first_drawn_line; - else - thisframe_y_adjust = thisframe_first_drawn_line; - if (gfx_requested_ycenter == 2) { - if (thisframe_y_adjust != prev_y_adjust - && prev_y_adjust <= thisframe_first_drawn_line - && prev_y_adjust + max_drawn_amiga_line > thisframe_last_drawn_line) - thisframe_y_adjust = prev_y_adjust; - } - if (thisframe_y_adjust + max_drawn_amiga_line > maxvpos) - thisframe_y_adjust = maxvpos - max_drawn_amiga_line; - if (thisframe_y_adjust < minfirstline) - thisframe_y_adjust = minfirstline; - } - thisframe_y_adjust_real = thisframe_y_adjust << (gfx_requested_linedbl ? 1 : 0); - max_ypos_thisframe = (maxvpos - thisframe_y_adjust) << (gfx_requested_linedbl ? 1 : 0); - - if (prev_x_adjust != linetoscr_x_adjust || prev_y_adjust != thisframe_y_adjust) - frame_redraw_necessary |= (bplcon0 & 4) && gfx_requested_linedbl ? 2 : 1; - - max_diwstop = 0; - min_diwstart = 10000; - thisframe_first_drawn_line = -1; - thisframe_last_drawn_line = -1; -#ifdef SUPPORT_PENGUINS - penguins_enabled_thisframe = 1; - /* Tell the other thread that it can now expect data from us. */ - write_comm_pipe_int (&drawing_pipe, -2); - memset (line_drawn, 0, sizeof line_drawn); -#endif + /* Use both halves of the array in alternating fashion. */ + curr_sprite_entries[0].first_pixel = current_change_set * MAX_SPR_PIXELS; + next_sprite_forced = 1; } -static void finish_drawing_frame (void) -{ - int i; +static void do_savestate(void); -#ifdef SUPPORT_PENGUINS - /* Synchronize with other thread, then see whether there's something left for - * us to draw. @@@ This is probably a big waste of cycles if the two threads - * run at very different speeds. */ - write_comm_pipe_int (&drawing_pipe, -1); - read_comm_pipe_int_blocking (&drawing_lock_pipe); +static void vsync_handler (void) +{ +#if 0 + static int old_clxdat; + if (clxdat != old_clxdat) { + printf ("CLXDAT %04x\n", clxdat); + old_clxdat = clxdat; + } #endif + n_frames++; -#ifndef SMART_UPDATE - /* @@@ This isn't exactly right yet. FIXME */ - if (!interlace_seen) { - do_flush_screen (first_drawn_line, last_drawn_line); - return; + if (currprefs.m68k_speed == -1) { + frame_time_t curr_time = read_processor_time (); + vsyncmintime += vsynctime; + /* @@@ Mathias? How do you think we should do this? */ + /* If we are too far behind, or we just did a reset, adjust the + * needed time. */ + if ((long int)(curr_time - vsyncmintime) > 0 || rpt_did_reset) + vsyncmintime = curr_time + vsynctime; + rpt_did_reset = 0; } -#endif - for (i = 0; i < max_ypos_thisframe; i++) { - int where; - int line = i + thisframe_y_adjust_real; - if (linestate[line] == LINE_UNDECIDED) - break; + handle_events (); - where = amiga2aspect_line_map[i+min_ypos_for_screen]; - if (where >= gfxvidinfo.maxline) - break; - if (where == -1) - continue; + getjoystate (0, &joy0dir, &joy0button); + getjoystate (1, &joy1dir, &joy1button); - pfield_draw_line (line, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]); - } - do_flush_screen (first_drawn_line, last_drawn_line); -} + INTREQ (0x8020); + if (bplcon0 & 4) + lof ^= 0x8000; -static void vsync_handler (void) -{ - UWORD dir; - int button; + vsync_handle_redraw (lof, lof_changed); + if (quit_program > 0) + return; - handle_events(); - getjoystate(&joy0dir, &joy0button); + { + static int cnt = 0; + if (cnt == 0) { + /* resolution_check_change (); */ + DISK_check_change (); + cnt = 5; + } + cnt--; + } + + /* Start a new set of copper records. */ + curr_cop_set ^= 1; + nr_cop_records[curr_cop_set] = 0; + + /* For now, let's only allow this to change at vsync time. It gets too + * hairy otherwise. */ + if (beamcon0 != new_beamcon0) + init_hz (); - do_mouse_hack(); - - INTREQ(0x8020); - if (bplcon0 & 4) - lof ^= 0x8000; - - last_redraw_point++; - if (lof_changed || !interlace_seen || last_redraw_point >= 2 || lof) { - if (framecnt == 0) - finish_drawing_frame (); - count_frame (); - last_redraw_point = 0; - if (framecnt == 0) - init_drawing_frame (); - } - lof_changed = 0; - interlace_seen = 0; - COPJMP1(0); - - init_hardware_frame(); + + cop_state.ip = cop1lc; + cop_state.state = COP_read1; + cop_state.vpos = 0; + cop_state.hpos = 0; + cop_state.ignore_next = 0; + + init_hardware_frame (); + #ifdef HAVE_GETTIMEOFDAY { struct timeval tv; unsigned long int newtime; - - gettimeofday(&tv,NULL); + + gettimeofday (&tv,NULL); newtime = (tv.tv_sec-seconds_base) * 1000 + tv.tv_usec / 1000; - - if (!bogusframe) { - frametime += newtime - msecs; + + if (!bogusframe) { + lastframetime = newtime - msecs; + +#if 0 /* This doesn't appear to work too well yet... later. */ + if (n_consecutive_skipped > currprefs.sound_pri_cutoff + || lastframetime < currprefs.sound_pri_time) + { + n_consecutive_skipped = 0; + clear_inhibit_frame (IHF_SOUNDADJUST); + } else { + n_consecutive_skipped++; + set_inhibit_frame (IHF_SOUNDADJUST); + total_skipped++; + } +#endif + + frametime += lastframetime; timeframes++; + + if ((timeframes & 127) == 0) + gui_fps (1000 * timeframes / frametime); } msecs = newtime; bogusframe = 0; } #endif - CIA_vsync_handler(); + if (ievent_alive > 0) + ievent_alive--; + if (timehack_alive > 0) + timehack_alive--; + CIA_vsync_handler (); } -static void hsync_handler(void) -{ - int lineno = next_lineno; - int lineisdouble = 0; - int line_was_doubled = 0; - - finish_decisions (); - do_modulos (); +static void hsync_handler (void) +{ + /* Using 0x8A makes sure that we don't accidentally trip over the + modified_regtypes check. */ + sync_copper_with_cpu (maxhpos, 0, 0x8A); - if (framecnt == 0) { - switch (nln_how) { - case 0: - linestate[lineno] = LINE_DECIDED; - break; - case 1: - linestate[lineno] = LINE_DECIDED_DOUBLE; - if (linestate[lineno+1] != LINE_REMEMBERED_AS_PREVIOUS) - linestate[lineno+1] = LINE_AS_PREVIOUS; - break; - case 2: - if (linestate[lineno-1] == LINE_UNDECIDED) - linestate[lineno-1] = LINE_BORDER_NEXT; - linestate[lineno] = LINE_DECIDED; - break; - case 3: - linestate[lineno] = LINE_DECIDED; - if (linestate[lineno+1] == LINE_UNDECIDED - || linestate[lineno+1] == LINE_REMEMBERED_AS_PREVIOUS - || linestate[lineno+1] == LINE_AS_PREVIOUS) - linestate[lineno+1] = LINE_BORDER_PREV; - break; - } - } + finish_decisions (); + if (thisline_decision.plfleft != -1) { + if (currprefs.collision_level > 1) + do_sprite_collisions (); + if (currprefs.collision_level > 2) + do_playfield_collisions (); + } + hsync_record_line_state (next_lineno, nextline_how, thisline_changed); + + eventtab[ev_hsync].evtime += get_cycles () - eventtab[ev_hsync].oldcycles; + eventtab[ev_hsync].oldcycles = get_cycles (); + CIA_hsync_handler (); - eventtab[ev_hsync].evtime += cycles - eventtab[ev_hsync].oldcycles; - eventtab[ev_hsync].oldcycles = cycles; - CIA_hsync_handler(); - - if (produce_sound > 0) { + if (currprefs.produce_sound > 0) { int nr; + + update_audio (); + /* Sound data is fetched at the beginning of each line */ - for (nr = 0; nr < 4; nr++) { + for (nr = 0; nr < 6; nr++) { struct audio_channel_data *cdp = audio_channel + nr; - + if (cdp->data_written == 2) { cdp->data_written = 0; - cdp->nextdat = chipmem_bank.wget(cdp->pt); + cdp->nextdat = chipmem_wget(cdp->pt); cdp->pt += 2; if (cdp->state == 2 || cdp->state == 3) { if (cdp->wlen == 1) { @@ -4456,227 +3733,289 @@ static void hsync_handler(void) } } } -#ifdef SUPPORT_PENGUINS - if (framecnt == 0 && penguins_enabled_thisframe) { - /* This is awfully bad. Just think about how many syscalls this does - * in one second and you'll be ill. But before I change it I want to - * hear from someone who has an SMP machine just exactly how bad this - * is. */ - write_comm_pipe_int (&drawing_pipe, next_lineno); - } -#endif -#ifndef SMART_UPDATE - { - int i, where; - /* l is the line that has been finished for drawing. */ - i = next_lineno - thisframe_y_adjust_real; - if (i >= 0 && i < max_ypos_thisframe) { - where = amiga2aspect_line_map[i+min_ypos_for_screen]; - if (where < gfxvidinfo.maxline && where != -1) - pfield_draw_line (next_lineno, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]); - } - } -#endif + + hardware_line_completed (next_lineno); + if (++vpos == (maxvpos + (lof != 0))) { vpos = 0; - vsync_handler(); + vsync_handler (); } - if ((bplcon0 & 4) && gfx_requested_linedbl) { - interlace_seen = 1, penguins_enabled_thisframe = 0; - } + DISK_update (); + + is_lastline = vpos + 1 == maxvpos + (lof != 0) && currprefs.m68k_speed == -1 && ! rpt_did_reset; + + if ((bplcon0 & 4) && currprefs.gfx_linedbl) + notice_interlace_seen (); if (framecnt == 0) { - lineno = vpos; - nln_how = 0; - if (gfx_requested_linedbl) { + int lineno = vpos; + nextline_how = nln_normal; + if (currprefs.gfx_linedbl) { lineno *= 2; - nln_how = 1; + nextline_how = currprefs.gfx_linedbl == 1 ? nln_doubled : nln_nblack; if (bplcon0 & 4) { if (!lof) { lineno++; - nln_how = 2; + nextline_how = nln_lower; } else { - nln_how = 3; + nextline_how = nln_upper; } } } next_lineno = lineno; reset_decisions (); } + if (uae_int_requested) { + set_uae_int_flag (); + INTREQ (0xA000); + } + /* See if there's a chance of a copper wait ending this line. */ + cop_state.hpos = 0; + compute_spcflag_copper (); } -void customreset(void) +static void init_regtypes (void) { - int i, maxl; - double native_lines_per_amiga_line; -#ifdef HAVE_GETTIMEOFDAY - struct timeval tv; -#endif - - inhibit_frame = 0; - expamem_reset(); - CIA_reset(); - cycles = 0; - regs.spcflags &= SPCFLAG_BRK; - - lores_factor = gfx_requested_lores ? 1 : 2; - lores_shift = gfx_requested_lores ? 0 : 1; - sprite_width = gfx_requested_lores ? 16 : 32; - - vpos = 0; - lof = 0; - max_diwstop = 0; - - if (needmousehack()) { - if (mousestate != follow_mouse) setfollow(); - } else { - mousestate = normal_mouse; - } - - memset(spixstate, 0, sizeof(spixstate)); - - /*memset(blitcount, 0, sizeof(blitcount)); blitter debug */ - for (i = 0; i < (maxvpos+1)*2 + 1; i++) { - linestate[i] = LINE_UNDECIDED; + int i; + for (i = 0; i < 512; i += 2) { + regtypes[i] = REGTYPE_ALL; + if ((i >= 0x20 && i < 0x28) || i == 0x08 || i == 0x7E) + regtypes[i] = REGTYPE_DISK; + else if (i >= 0x68 && i < 0x70) + regtypes[i] = REGTYPE_NONE; + else if (i >= 0x40 && i < 0x78) + regtypes[i] = REGTYPE_BLITTER; + else if (i >= 0xA0 && i < 0xE0 && (i & 0xF) < 0xE) + regtypes[i] = REGTYPE_AUDIO; + else if (i >= 0xA0 && i < 0xE0) + regtypes[i] = REGTYPE_NONE; + else if (i >= 0xE0 && i < 0x100) + regtypes[i] = REGTYPE_PLANE; + else if (i >= 0x120 && i < 0x180) + regtypes[i] = REGTYPE_SPRITE; + else if (i >= 0x180 && i < 0x1C0) + regtypes[i] = REGTYPE_COLOR; + else switch (i) { + case 0x02: + /* DMACONR - setting this to REGTYPE_BLITTER will cause it to + conflict with DMACON (since that is REGTYPE_ALL), and the + blitter registers (for the BBUSY bit), but nothing else, + which is (I think) what we want. */ + regtypes[i] = REGTYPE_BLITTER; + break; + case 0x04: case 0x06: case 0x2A: case 0x2C: + regtypes[i] = REGTYPE_POS; + break; + case 0x0A: case 0x0C: + case 0x12: case 0x14: case 0x16: + case 0x36: + regtypes[i] = REGTYPE_JOYPORT; + break; + case 0x104: + case 0x102: + regtypes[i] = REGTYPE_PLANE; + break; + case 0x88: case 0x8A: + case 0x8E: case 0x90: case 0x92: case 0x94: + case 0x96: + case 0x100: + regtypes[i] |= REGTYPE_FORCE; + break; + } } - xlinebuffer = gfxvidinfo.bufmem; +} - dmacon = intena = 0; - bltstate = BLT_done; - copstate = COP_stop; - diwstate = DIW_waiting_start; - copcon = 0; - dskdmaen = 0; - cycles = 0; - - memset(audio_channel, 0, sizeof audio_channel); +void init_eventtab (void) +{ + int i; - bplcon4 = 0x11; /* Get AGA chipset into ECS compatibility mode */ - bplcon3 = 0xC00; - for(i = 0; i < ev_max; i++) { + currcycle = 0; + for (i = 0; i < ev_max; i++) { eventtab[i].active = 0; eventtab[i].oldcycles = 0; } - copper_active = 0; + eventtab[ev_cia].handler = CIA_handler; - eventtab[ev_copper].handler = do_copper; eventtab[ev_hsync].handler = hsync_handler; - eventtab[ev_hsync].evtime = maxhpos + cycles; + eventtab[ev_hsync].evtime = maxhpos * CYCLE_UNIT + get_cycles (); eventtab[ev_hsync].active = 1; + eventtab[ev_copper].handler = copper_handler; + eventtab[ev_copper].active = 0; eventtab[ev_blitter].handler = blitter_handler; eventtab[ev_blitter].active = 0; - eventtab[ev_diskblk].handler = diskblk_handler; - eventtab[ev_diskblk].active = 0; - eventtab[ev_diskindex].handler = diskindex_handler; - eventtab[ev_diskindex].active = 0; -#ifndef DONT_WANT_SOUND - eventtab[ev_aud0].handler = aud0_handler; - eventtab[ev_aud0].active = 0; - eventtab[ev_aud1].handler = aud1_handler; - eventtab[ev_aud1].active = 0; - eventtab[ev_aud2].handler = aud2_handler; - eventtab[ev_aud2].active = 0; - eventtab[ev_aud3].handler = aud3_handler; - eventtab[ev_aud3].active = 0; - if (sound_available) { - eventtab[ev_sample].active = 1; - eventtab[ev_sample].evtime += cycles; - eventtab[ev_sample].oldcycles = cycles; - } else { - eventtab[ev_sample].active = 0; - } -#endif + eventtab[ev_disk].handler = DISK_handler; + eventtab[ev_disk].active = 0; + eventtab[ev_audio].handler = audio_evhandler; + eventtab[ev_audio].active = 0; events_schedule (); +} - if (native2amiga_line_map) - free (native2amiga_line_map); - if (amiga2aspect_line_map) - free (amiga2aspect_line_map); - - /* At least for this array the +1 is necessary. */ - amiga2aspect_line_map = (int *)malloc (sizeof (int) * (maxvpos+1)*2 + 1); - native2amiga_line_map = (int *)malloc (sizeof (int) * gfxvidinfo.maxline); - - if (gfx_requested_correct_aspect) - native_lines_per_amiga_line = ((double)gfxvidinfo.maxline - * (gfx_requested_lores ? 320 : 640) - / (gfx_requested_linedbl ? 512 : 256) - / gfxvidinfo.maxlinetoscr); - else - native_lines_per_amiga_line = 1; +void customreset (void) +{ + int i; + int zero = 0; +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; +#endif - maxl = (maxvpos+1) * (gfx_requested_linedbl ? 2 : 1); - min_ypos_for_screen = minfirstline << (gfx_requested_linedbl ? 1 : 0); - max_drawn_amiga_line = -1; - for (i = 0; i < maxl; i++) { - int v = (i - min_ypos_for_screen) * native_lines_per_amiga_line; - if (v >= gfxvidinfo.maxline && max_drawn_amiga_line == -1) - max_drawn_amiga_line = i-min_ypos_for_screen; - if (i < min_ypos_for_screen || v >= gfxvidinfo.maxline) - v = -1; - amiga2aspect_line_map[i] = v; - } - if (gfx_requested_linedbl) - max_drawn_amiga_line >>= 1; - - for (i = 0; i < gfxvidinfo.maxline; i++) - native2amiga_line_map[i] = -1; - - if (native_lines_per_amiga_line < 1) { - /* Must omit drawing some lines. */ - for (i = maxl-1; i > min_ypos_for_screen; i--) { - if (amiga2aspect_line_map[i] == amiga2aspect_line_map[i-1]) { - if (gfx_requested_linedbl && (i & 1) == 0 && amiga2aspect_line_map[i+1] != -1) { - /* If only the first line of a line pair would be omitted, - * omit the second one instead to avoid problems with line - * doubling. */ - amiga2aspect_line_map[i] = amiga2aspect_line_map[i+1]; - amiga2aspect_line_map[i+1] = -1; - } else - amiga2aspect_line_map[i] = -1; + if (! savestate_state) { + currprefs.chipset_mask = changed_prefs.chipset_mask; + if ((currprefs.chipset_mask & CSMASK_AGA) == 0) { + for (i = 0; i < 32; i++) { + current_colors.color_regs_ecs[i] = 0; + current_colors.acolors[i] = xcolors[0]; + } + } else { + for (i = 0; i < 256; i++) { + current_colors.color_regs_aga[i] = 0; + current_colors.acolors[i] = CONVERT_RGB (zero); } } + + clx_sprmask = 0xFF; + clxdat = 0; + + /* Clear the armed flags of all sprites. */ + memset (spr, 0, sizeof spr); + nr_armed = 0; + + dmacon = intena = 0; + + copcon = 0; + DSKLEN (0, 0); + + bplcon4 = 0x11; /* Get AGA chipset into ECS compatibility mode */ + bplcon3 = 0xC00; + + FMODE (0); + CLXCON (0); } - for (i = maxl-1; i >= min_ypos_for_screen; i--) { - int j; - if (amiga2aspect_line_map[i] == -1) - continue; - for (j = amiga2aspect_line_map[i]; j < gfxvidinfo.maxline && native2amiga_line_map[j] == -1; j++) - native2amiga_line_map[j] = i >> (gfx_requested_linedbl ? 1 : 0); + n_frames = 0; + + expamem_reset (); + + DISK_reset (); + CIA_reset (); + unset_special (~(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE)); + + vpos = 0; + lof = 0; + + if (needmousehack ()) { +#if 0 + mousehack_setfollow(); +#else + mousehack_setdontcare(); +#endif + } else { + mousestate = normal_mouse; } - - line_drawn = (char *)malloc (gfxvidinfo.maxline); - + ievent_alive = 0; + 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, sizeof spixels); + memset (&spixstate, 0, sizeof spixstate); + + bltstate = BLT_done; + cop_state.state = COP_stop; + diwstate = DIW_waiting_start; + hdiwstate = DIW_waiting_start; + currcycle = 0; + + new_beamcon0 = currprefs.ntscmode ? 0x00 : 0x20; + init_hz (); + + audio_reset (); + + init_sprites (); + init_hardware_frame (); - init_drawing_frame (); - last_redraw_point = 0; + reset_drawing (); + reset_decisions (); - + #ifdef HAVE_GETTIMEOFDAY - gettimeofday(&tv,NULL); + gettimeofday (&tv, NULL); seconds_base = tv.tv_sec; bogusframe = 1; #endif + + init_regtypes (); + + sprite_buffer_res = currprefs.chipset_mask & CSMASK_AGA ? RES_HIRES : RES_LORES; + if (savestate_state == STATE_RESTORE) { + uae_u16 v; + uae_u32 vv; + + update_adkmasks (); + INTENA (0); + INTREQ (0); +#if 0 + DMACON (0, 0); +#endif + COPJMP1 (0); + if (diwhigh) + diwhigh_written = 1; + v = bplcon0; + BPLCON0 (0, 0); + BPLCON0 (0, v); + FMODE (v); + if (!(currprefs.chipset_mask & CSMASK_AGA)) { + for(i = 0 ; i < 32 ; i++) { + vv = current_colors.color_regs_ecs[i]; + current_colors.color_regs_ecs[i] = -1; + record_color_change (0, i, vv); + remembered_color_entry = -1; + current_colors.color_regs_ecs[i] = vv; + current_colors.acolors[i] = xcolors[vv]; + } + } else { + for(i = 0 ; i < 256 ; i++) { + vv = current_colors.color_regs_aga[i]; + current_colors.color_regs_aga[i] = -1; + record_color_change (0, i, vv); + remembered_color_entry = -1; + current_colors.color_regs_aga[i] = vv; + current_colors.acolors[i] = CONVERT_RGB(vv); + } + } + calcdiw (); + write_log ("State restored\n"); + dumpcustom (); + for (i = 0; i < 8; i++) + nr_armed += spr[i].armed != 0; + } + expand_sprres (); } -void dumpcustom(void) +void dumpcustom (void) { - int i; - fprintf(stderr, "DMACON: %x INTENA: %x INTREQ: %x VPOS: %x HPOS: %x\n", DMACONR(), - intena, intreq, vpos, current_hpos()); - if (timeframes) { - fprintf(stderr, "Average frame time: %d ms [frames: %d time: %d]\n", - frametime/timeframes, timeframes, frametime); + write_log ("DMACON: %x INTENA: %x INTREQ: %x VPOS: %x HPOS: %x\n", DMACONR(), + (unsigned int)intena, (unsigned int)intreq, (unsigned int)vpos, (unsigned int)current_hpos()); + write_log ("COP1LC: %08lx, COP2LC: %08lx\n", (unsigned long)cop1lc, (unsigned long)cop2lc); + write_log ("DIWSTRT: %04x DIWSTOP: %04x DDFSTRT: %04x DDFSTOP: %04x\n", + (unsigned int)diwstrt, (unsigned int)diwstop, (unsigned int)ddfstrt, (unsigned int)ddfstop); + if (timeframes) { + write_log ("Average frame time: %d ms [frames: %d time: %d]\n", + frametime / timeframes, timeframes, frametime); + if (total_skipped) + write_log ("Skipped frames: %d\n", total_skipped); } - /*for (i=0; i<256; i++) if (blitcount[i]) fprintf(stderr, "minterm %x = %d\n",i,blitcount[i]); blitter debug */ + /*for (i=0; i<256; i++) if (blitcount[i]) fprintf (stderr, "minterm %x = %d\n",i,blitcount[i]); blitter debug */ } -int intlev(void) +int intlev (void) { - UWORD imask = intreq & intena; + uae_u16 imask = intreq & intena; if (imask && (intena & 0x4000)){ if (imask & 0x2000) return 6; if (imask & 0x1800) return 5; @@ -4684,291 +4023,686 @@ int intlev(void) if (imask & 0x0070) return 3; if (imask & 0x0008) return 2; if (imask & 0x0007) return 1; - if (regs.spcflags & SPCFLAG_TIMER) - return 7; } return -1; } -void custom_init(void) +static void gen_custom_tables (void) { - int num; + int i; + for (i = 0; i < 256; i++) { + unsigned int j; + sprtaba[i] = ((((i >> 7) & 1) << 0) + | (((i >> 6) & 1) << 2) + | (((i >> 5) & 1) << 4) + | (((i >> 4) & 1) << 6) + | (((i >> 3) & 1) << 8) + | (((i >> 2) & 1) << 10) + | (((i >> 1) & 1) << 12) + | (((i >> 0) & 1) << 14)); + sprtabb[i] = sprtaba[i] * 2; + sprite_ab_merge[i] = (((i & 15) ? 1 : 0) + | ((i & 240) ? 2 : 0)); + } + for (i = 0; i < 16; i++) { + clxmask[i] = (((i & 1) ? 0xF : 0x3) + | ((i & 2) ? 0xF0 : 0x30) + | ((i & 4) ? 0xF00 : 0x300) + | ((i & 8) ? 0xF000 : 0x3000)); + sprclx[i] = (((i & 0x3) == 0x3 ? 1 : 0) + | ((i & 0x5) == 0x5 ? 2 : 0) + | ((i & 0x9) == 0x9 ? 4 : 0) + | ((i & 0x6) == 0x6 ? 8 : 0) + | ((i & 0xA) == 0xA ? 16 : 0) + | ((i & 0xC) == 0xC ? 32 : 0)) << 9; + } +} + +void custom_init (void) +{ + uaecptr pos; #ifdef OS_WITHOUT_MEMORY_MANAGEMENT - for(num=0;num<2;++num) { - sprite_positions[num] = malloc(max_sprite_draw * sizeof(struct sprite_draw)); - color_changes[num] = malloc(max_color_change * sizeof(struct color_change)); - if(!sprite_positions[num] || !color_changes[num]) { - printf("Out of mem int %s %d!\n",__FILE__,__LINE__); - abort(); - } - } -#endif - - if (needmousehack()) - setfollow(); - init_regchanges (); - init_decisions (); - - for (num = 0; num < 256; num++) { - int plane1 = (num & 1) | ((num >> 1) & 2) | ((num >> 2) & 4) | ((num >> 3) & 8); - int plane2 = ((num >> 1) & 1) | ((num >> 2) & 2) | ((num >> 3) & 4) | ((num >> 4) & 8); - dblpf_2nd1[num] = plane1 == 0 ? (plane2 == 0 ? 0 : 2) : 1; - dblpf_2nd2[num] = plane2 == 0 ? (plane1 == 0 ? 0 : 1) : 2; - dblpf_aga1[num] = plane1 == 0 ? plane2 : plane1; - dblpf_aga2[num] = plane2 == 0 ? plane1 : plane2; - if (plane2 > 0) plane2 += 8; - dblpf_ind1[num] = plane1 == 0 ? plane2 : plane1; - dblpf_ind2[num] = plane2 == 0 ? plane1 : plane2; - - lots_of_twos[num] = num == 0 ? 0 : 2; - linear_map_256[num] = num; + int num; + + for (num = 0; num < 2; num++) { + sprite_entries[num] = xmalloc (max_sprite_entry * sizeof (struct sprite_entry)); + color_changes[num] = xmalloc (max_color_change * sizeof (struct color_change)); } - build_blitfilltable(); - gen_pfield_tables(); - native2amiga_line_map = 0; - amiga2aspect_line_map = 0; - line_drawn = 0; -#ifdef SUPPORT_PENGUINS - init_comm_pipe (&drawing_pipe); - init_comm_pipe (&drawing_lock_pipe); - our_penguin = start_penguin (drawing_penguin); - atexit(kill_drawing_penguin); #endif + + pos = here (); + + org (0xF0FF70); + calltrap (deftrap (mousehack_helper)); + dw (RTS); + + org (0xF0FFA0); + calltrap (deftrap (timehack_helper)); + dw (RTS); + + org (pos); + + gen_custom_tables (); + build_blitfilltable (); + + drawing_init (); + + mousestate = unknown_mouse; + + if (needmousehack ()) + mousehack_setfollow(); } /* Custom chip memory bank */ -static ULONG custom_lget(CPTR) REGPARAM; -static ULONG custom_wget(CPTR) REGPARAM; -static ULONG custom_bget(CPTR) REGPARAM; -static void custom_lput(CPTR, ULONG) REGPARAM; -static void custom_wput(CPTR, ULONG) REGPARAM; -static void custom_bput(CPTR, ULONG) REGPARAM; +static uae_u32 custom_lget (uaecptr) REGPARAM; +static uae_u32 custom_wget (uaecptr) REGPARAM; +static uae_u32 custom_bget (uaecptr) REGPARAM; +static void custom_lput (uaecptr, uae_u32) REGPARAM; +static void custom_wput (uaecptr, uae_u32) REGPARAM; +static void custom_bput (uaecptr, uae_u32) REGPARAM; addrbank custom_bank = { - default_alget, default_awget, custom_lget, custom_wget, custom_bget, custom_lput, custom_wput, custom_bput, - default_xlate, default_check + default_xlate, default_check, NULL }; -ULONG REGPARAM2 custom_wget(CPTR addr) +STATIC_INLINE uae_u32 REGPARAM2 custom_wget_1 (uaecptr addr) { - switch(addr & 0x1FE) { - case 0x002: return DMACONR(); - case 0x004: return VPOSR(); - case 0x006: return VHPOSR(); - - case 0x008: return DSKDATR(); + special_mem |= S_READ; + switch (addr & 0x1FE) { + case 0x002: return DMACONR (); + case 0x004: return VPOSR (); + case 0x006: return VHPOSR (); + + case 0x008: return DSKDATR (current_hpos ()); + + case 0x00A: return JOY0DAT (); + case 0x00C: return JOY1DAT (); + case 0x00E: return CLXDAT (); + case 0x010: return ADKCONR (); + + case 0x012: return POT0DAT (); + case 0x016: return POTGOR (); + case 0x018: return SERDATR (); + case 0x01A: return DSKBYTR (current_hpos ()); + case 0x01C: return INTENAR (); + case 0x01E: return INTREQR (); + case 0x07C: return DENISEID (); + + 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: + return COLOR_READ ((addr & 0x3E) / 2); + break; - case 0x00A: return JOY0DAT(); - case 0x00C: return JOY1DAT(); - case 0x00E: return 0; /* CLXDAT */ - case 0x010: return ADKCONR(); - - case 0x012: return POT0DAT(); - case 0x016: return POTGOR(); - case 0x018: return SERDATR(); - case 0x01A: return DSKBYTR(); - case 0x01C: return INTENAR(); - case 0x01E: return INTREQR(); -#if AGA_CHIPSET == 1 - case 0x07C: return 0xF8; -#elif defined ECS_DENISE - case 0x07C: return 0xFC; -#endif default: - custom_wput(addr,0); + custom_wput (addr, 0); return 0xffff; } } -ULONG REGPARAM2 custom_bget(CPTR addr) +uae_u32 REGPARAM2 custom_wget (uaecptr addr) +{ + sync_copper_with_cpu (current_hpos (), 1, addr); + return custom_wget_1 (addr); +} + +uae_u32 REGPARAM2 custom_bget (uaecptr addr) { - return custom_wget(addr & 0xfffe) >> (addr & 1 ? 0 : 8); + special_mem |= S_READ; + return custom_wget (addr & 0xfffe) >> (addr & 1 ? 0 : 8); } -ULONG REGPARAM2 custom_lget(CPTR addr) +uae_u32 REGPARAM2 custom_lget (uaecptr addr) { - return ((ULONG)custom_wget(addr & 0xfffe) << 16) | custom_wget((addr+2) & 0xfffe); + special_mem |= S_READ; + return ((uae_u32)custom_wget (addr & 0xfffe) << 16) | custom_wget ((addr + 2) & 0xfffe); } -void REGPARAM2 custom_wput(CPTR addr, ULONG value) +void REGPARAM2 custom_wput_1 (int hpos, uaecptr addr, uae_u32 value) { addr &= 0x1FE; - cregs[addr>>1] = value; - switch(addr) { - case 0x020: DSKPTH(value); break; - case 0x022: DSKPTL(value); break; - case 0x024: DSKLEN(value); break; - case 0x026: DSKDAT(value); break; - - case 0x02A: VPOSW(value); break; - case 0x2E: COPCON(value); break; - case 0x030: SERDAT(value); break; - case 0x032: SERPER(value); break; - - case 0x040: BLTCON0(value); break; - case 0x042: BLTCON1(value); break; - - case 0x044: BLTAFWM(value); break; - case 0x046: BLTALWM(value); break; - - case 0x050: BLTAPTH(value); break; - case 0x052: BLTAPTL(value); break; - case 0x04C: BLTBPTH(value); break; - case 0x04E: BLTBPTL(value); break; - case 0x048: BLTCPTH(value); break; - case 0x04A: BLTCPTL(value); break; - case 0x054: BLTDPTH(value); break; - case 0x056: BLTDPTL(value); break; - - case 0x058: BLTSIZE(value); break; - - case 0x064: BLTAMOD(value); break; - case 0x062: BLTBMOD(value); break; - case 0x060: BLTCMOD(value); break; - case 0x066: BLTDMOD(value); break; - - case 0x070: BLTCDAT(value); break; - case 0x072: BLTBDAT(value); break; - case 0x074: BLTADAT(value); break; - - case 0x07E: DSKSYNC(value); break; - - case 0x080: COP1LCH(value); break; - case 0x082: COP1LCL(value); break; - case 0x084: COP2LCH(value); break; - case 0x086: COP2LCL(value); break; - - case 0x088: COPJMP1(value); break; - case 0x08A: COPJMP2(value); break; - - case 0x08E: DIWSTRT(value); break; - case 0x090: DIWSTOP(value); break; - case 0x092: DDFSTRT(value); break; - case 0x094: DDFSTOP(value); break; - - case 0x096: DMACON(value); break; - case 0x09A: INTENA(value); break; - case 0x09C: INTREQ(value); break; - case 0x09E: ADKCON(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: BPLPTH(value, 0); break; - case 0x0E2: BPLPTL(value, 0); break; - case 0x0E4: BPLPTH(value, 1); break; - case 0x0E6: BPLPTL(value, 1); break; - case 0x0E8: BPLPTH(value, 2); break; - case 0x0EA: BPLPTL(value, 2); break; - case 0x0EC: BPLPTH(value, 3); break; - case 0x0EE: BPLPTL(value, 3); break; - case 0x0F0: BPLPTH(value, 4); break; - case 0x0F2: BPLPTL(value, 4); break; - case 0x0F4: BPLPTH(value, 5); break; - case 0x0F6: BPLPTL(value, 5); break; - - case 0x100: BPLCON0(value); break; - case 0x102: BPLCON1(value); break; - case 0x104: BPLCON2(value); break; - case 0x106: BPLCON3(value); break; - - case 0x108: BPL1MOD(value); break; - case 0x10A: BPL2MOD(value); break; + switch (addr) { + case 0x020: DSKPTH (value); break; + case 0x022: DSKPTL (value); break; + case 0x024: DSKLEN (value, hpos); break; + case 0x026: DSKDAT (value); break; + + case 0x02A: VPOSW (value); break; + case 0x02E: COPCON (value); break; + case 0x030: SERDAT (value); break; + case 0x032: SERPER (value); break; + case 0x034: POTGO (value); break; + case 0x040: BLTCON0 (value); break; + case 0x042: BLTCON1 (value); break; + + case 0x044: BLTAFWM (value); break; + case 0x046: BLTALWM (value); break; + + case 0x050: BLTAPTH (value); break; + case 0x052: BLTAPTL (value); break; + case 0x04C: BLTBPTH (value); break; + case 0x04E: BLTBPTL (value); break; + case 0x048: BLTCPTH (value); break; + case 0x04A: BLTCPTL (value); break; + case 0x054: BLTDPTH (value); break; + case 0x056: BLTDPTL (value); break; + + case 0x058: BLTSIZE (value); break; + + case 0x064: BLTAMOD (value); break; + case 0x062: BLTBMOD (value); break; + case 0x060: BLTCMOD (value); break; + case 0x066: BLTDMOD (value); break; + + case 0x070: BLTCDAT (value); break; + case 0x072: BLTBDAT (value); break; + case 0x074: BLTADAT (value); break; + + case 0x07E: DSKSYNC (value); break; + + case 0x080: COP1LCH (value); break; + case 0x082: COP1LCL (value); break; + case 0x084: COP2LCH (value); break; + case 0x086: COP2LCL (value); break; + + case 0x088: COPJMP1 (value); break; + case 0x08A: COPJMP2 (value); break; + + case 0x08E: DIWSTRT (hpos, value); break; + case 0x090: DIWSTOP (hpos, value); break; + case 0x092: DDFSTRT (hpos, value); break; + case 0x094: DDFSTOP (hpos, value); break; + + case 0x096: DMACON (hpos, value); break; + case 0x098: CLXCON (value); break; + case 0x09A: INTENA (value); break; + case 0x09C: INTREQ (value); break; + case 0x09E: ADKCON (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: BPLPTH (hpos, value, 0); break; + case 0x0E2: BPLPTL (hpos, value, 0); break; + case 0x0E4: BPLPTH (hpos, value, 1); break; + case 0x0E6: BPLPTL (hpos, value, 1); break; + case 0x0E8: BPLPTH (hpos, value, 2); break; + case 0x0EA: BPLPTL (hpos, value, 2); break; + case 0x0EC: BPLPTH (hpos, value, 3); break; + case 0x0EE: BPLPTL (hpos, value, 3); break; + case 0x0F0: BPLPTH (hpos, value, 4); break; + case 0x0F2: BPLPTL (hpos, value, 4); break; + case 0x0F4: BPLPTH (hpos, value, 5); break; + case 0x0F6: BPLPTL (hpos, value, 5); break; + case 0x0F8: BPLPTH (hpos, value, 6); break; + case 0x0FA: BPLPTL (hpos, value, 6); break; + case 0x0FC: BPLPTH (hpos, value, 7); break; + case 0x0FE: BPLPTL (hpos, value, 7); break; + + case 0x100: BPLCON0 (hpos, value); break; + case 0x102: BPLCON1 (hpos, value); break; + case 0x104: BPLCON2 (hpos, value); break; + case 0x106: BPLCON3 (hpos, value); break; + + case 0x108: BPL1MOD (hpos, value); break; + case 0x10A: BPL2MOD (hpos, value); break; + + case 0x110: BPL1DAT (hpos, value); break; + case 0x112: BPL2DAT (value); break; + case 0x114: BPL3DAT (value); break; + case 0x116: BPL4DAT (value); break; + case 0x118: BPL5DAT (value); break; + case 0x11A: BPL6DAT (value); break; + case 0x11C: BPL7DAT (value); break; + case 0x11E: BPL8DAT (value); break; - case 0x110: BPL1DAT(value); break; - case 0x112: BPL2DAT(value); break; - case 0x114: BPL3DAT(value); break; - case 0x116: BPL4DAT(value); break; - case 0x118: BPL5DAT(value); break; - case 0x11A: BPL6DAT(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(value & 0xFFF, (addr & 0x3E) / 2); + 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(value, (addr - 0x120) / 4); + SPRxPTH (hpos, value, (addr - 0x120) / 4); break; case 0x122: case 0x126: case 0x12A: case 0x12E: case 0x132: case 0x136: case 0x13A: case 0x13E: - SPRxPTL(value, (addr - 0x122) / 4); + SPRxPTL (hpos, value, (addr - 0x122) / 4); break; case 0x140: case 0x148: case 0x150: case 0x158: case 0x160: case 0x168: case 0x170: case 0x178: - SPRxPOS(value, (addr - 0x140) / 8); + SPRxPOS (hpos, value, (addr - 0x140) / 8); break; case 0x142: case 0x14A: case 0x152: case 0x15A: case 0x162: case 0x16A: case 0x172: case 0x17A: - SPRxCTL(value, (addr - 0x142) / 8); + SPRxCTL (hpos, value, (addr - 0x142) / 8); break; case 0x144: case 0x14C: case 0x154: case 0x15C: case 0x164: case 0x16C: case 0x174: case 0x17C: - SPRxDATA(value, (addr - 0x144) / 8); + SPRxDATA (hpos, value, (addr - 0x144) / 8); break; case 0x146: case 0x14E: case 0x156: case 0x15E: case 0x166: case 0x16E: case 0x176: case 0x17E: - SPRxDATB(value, (addr - 0x146) / 8); + SPRxDATB (hpos, value, (addr - 0x146) / 8); break; - - case 0x36: JOYTEST(value); break; -#if defined(ECS_AGNUS) || (AGA_CHIPSET == 1) - case 0x5A: BLTCON0L(value); break; - case 0x5C: BLTSIZV(value); break; - case 0x5E: BLTSIZH(value); break; -#endif -#if AGA_CHIPSET == 1 - case 0x10C: BPLCON4(value); break; - case 0x1FC: fmode = value; break; -#endif + + case 0x36: JOYTEST (value); break; + case 0x5A: BLTCON0L (value); break; + case 0x5C: BLTSIZV (value); break; + case 0x5E: BLTSIZH (value); break; + case 0x1E4: DIWHIGH (hpos, value); break; + case 0x10C: BPLCON4 (hpos, value); break; + case 0x1FC: FMODE (value); break; } } -void REGPARAM2 custom_bput(CPTR addr, ULONG value) +void REGPARAM2 custom_wput (uaecptr addr, uae_u32 value) +{ + int hpos = current_hpos (); + special_mem |= S_WRITE; + + sync_copper_with_cpu (hpos, 1, addr); + custom_wput_1 (hpos, addr, value); +} + +void REGPARAM2 custom_bput (uaecptr addr, uae_u32 value) { static int warned = 0; /* Is this correct now? (There are people who bput things to the upper byte of AUDxVOL). */ - UWORD rval = (value << 8) | (value & 0xFF); - custom_wput(addr, rval); + uae_u16 rval = (value << 8) | (value & 0xFF); + special_mem |= S_WRITE; + custom_wput (addr, rval); if (!warned) - fprintf(stderr, "Byte put to custom register.\n"), warned++; + write_log ("Byte put to custom register.\n"), warned++; +} + +void REGPARAM2 custom_lput(uaecptr addr, uae_u32 value) +{ + special_mem |= S_WRITE; + custom_wput (addr & 0xfffe, value >> 16); + custom_wput ((addr + 2) & 0xfffe, (uae_u16)value); +} + +void custom_prepare_savestate (void) +{ + /* force blitter to finish, no support for saving full blitter state yet */ + if (eventtab[ev_blitter].active) { + unsigned int olddmacon = dmacon; + dmacon |= DMA_BLITTER; /* ugh.. */ + blitter_handler (); + dmacon = olddmacon; + } +} + +#define RB restore_u8 () +#define RW restore_u16 () +#define RL restore_u32 () + +uae_u8 *restore_custom (uae_u8 *src) +{ + uae_u16 dsklen, dskbytr, dskdatr; + int dskpt; + int i; + + audio_reset (); + + currprefs.chipset_mask = RL; + RW; /* 000 ? */ + RW; /* 002 DMACONR */ + RW; /* 004 VPOSR */ + RW; /* 006 VHPOSR */ + dskdatr = RW; /* 008 DSKDATR */ + RW; /* 00A JOY0DAT */ + RW; /* 00C JOY1DAT */ + clxdat = RW; /* 00E CLXDAT */ + RW; /* 010 ADKCONR */ + RW; /* 012 POT0DAT* */ + RW; /* 014 POT1DAT* */ + RW; /* 016 POTINP* */ + RW; /* 018 SERDATR* */ + dskbytr = RW; /* 01A DSKBYTR */ + RW; /* 01C INTENAR */ + RW; /* 01E INTREQR */ + dskpt = RL; /* 020-022 DSKPT */ + dsklen = RW; /* 024 DSKLEN */ + RW; /* 026 DSKDAT */ + RW; /* 028 REFPTR */ + lof = RW; /* 02A VPOSW */ + RW; /* 02C VHPOSW */ + COPCON(RW); /* 02E COPCON */ + RW; /* 030 SERDAT* */ + RW; /* 032 SERPER* */ + POTGO(RW); /* 034 POTGO */ + RW; /* 036 JOYTEST* */ + RW; /* 038 STREQU */ + RW; /* 03A STRVHBL */ + RW; /* 03C STRHOR */ + RW; /* 03E STRLONG */ + BLTCON0(RW); /* 040 BLTCON0 */ + BLTCON1(RW); /* 042 BLTCON1 */ + BLTAFWM(RW); /* 044 BLTAFWM */ + BLTALWM(RW); /* 046 BLTALWM */ + BLTCPTH(RL); /* 048-04B BLTCPT */ + BLTBPTH(RL); /* 04C-04F BLTBPT */ + BLTAPTH(RL); /* 050-053 BLTAPT */ + BLTDPTH(RL); /* 054-057 BLTDPT */ + RW; /* 058 BLTSIZE */ + RW; /* 05A BLTCON0L */ + oldvblts = RW; /* 05C BLTSIZV */ + RW; /* 05E BLTSIZH */ + BLTCMOD(RW); /* 060 BLTCMOD */ + BLTBMOD(RW); /* 062 BLTBMOD */ + BLTAMOD(RW); /* 064 BLTAMOD */ + BLTDMOD(RW); /* 066 BLTDMOD */ + RW; /* 068 ? */ + RW; /* 06A ? */ + RW; /* 06C ? */ + RW; /* 06E ? */ + BLTCDAT(RW); /* 070 BLTCDAT */ + BLTBDAT(RW); /* 072 BLTBDAT */ + BLTADAT(RW); /* 074 BLTADAT */ + RW; /* 076 ? */ + RW; /* 078 ? */ + RW; /* 07A ? */ + RW; /* 07C LISAID */ + DSKSYNC(RW); /* 07E DSKSYNC */ + cop1lc = RL; /* 080/082 COP1LC */ + cop2lc = RL; /* 084/086 COP2LC */ + RW; /* 088 ? */ + RW; /* 08A ? */ + RW; /* 08C ? */ + diwstrt = RW; /* 08E DIWSTRT */ + diwstop = RW; /* 090 DIWSTOP */ + ddfstrt = RW; /* 092 DDFSTRT */ + ddfstop = RW; /* 094 DDFSTOP */ + dmacon = RW & ~(0x2000|0x4000); /* 096 DMACON */ + CLXCON(RW); /* 098 CLXCON */ + intena = RW; /* 09A INTENA */ + intreq = RW; /* 09C INTREQ */ + adkcon = RW; /* 09E ADKCON */ + for (i = 0; i < 8; i++) + bplpt[i] = RL; + bplcon0 = RW; /* 100 BPLCON0 */ + bplcon1 = RW; /* 102 BPLCON1 */ + bplcon2 = RW; /* 104 BPLCON2 */ + bplcon3 = RW; /* 106 BPLCON3 */ + bpl1mod = RW; /* 108 BPL1MOD */ + bpl2mod = RW; /* 10A BPL2MOD */ + bplcon4 = RW; /* 10C BPLCON4 */ + RW; /* 10E CLXCON2* */ + for(i = 0; i < 8; i++) + RW; /* BPLXDAT */ + for(i = 0; i < 32; i++) + current_colors.color_regs_ecs[i] = RW; /* 180 COLORxx */ + RW; /* 1C0 ? */ + RW; /* 1C2 ? */ + RW; /* 1C4 ? */ + RW; /* 1C6 ? */ + RW; /* 1C8 ? */ + RW; /* 1CA ? */ + RW; /* 1CC ? */ + RW; /* 1CE ? */ + RW; /* 1D0 ? */ + RW; /* 1D2 ? */ + RW; /* 1D4 ? */ + RW; /* 1D6 ? */ + RW; /* 1D8 ? */ + RW; /* 1DA ? */ + new_beamcon0 = RW; /* 1DC BEAMCON0 */ + RW; /* 1DE ? */ + RW; /* 1E0 ? */ + RW; /* 1E2 ? */ + RW; /* 1E4 ? */ + RW; /* 1E6 ? */ + RW; /* 1E8 ? */ + RW; /* 1EA ? */ + RW; /* 1EC ? */ + RW; /* 1EE ? */ + RW; /* 1F0 ? */ + RW; /* 1F2 ? */ + RW; /* 1F4 ? */ + RW; /* 1F6 ? */ + RW; /* 1F8 ? */ + RW; /* 1FA ? */ + fmode = RW; /* 1FC FMODE */ + RW; /* 1FE ? */ + + DISK_restore_custom (dskpt, dsklen, dskdatr, dskbytr); + + return src; +} + + +#define SB save_u8 +#define SW save_u16 +#define SL save_u32 + +extern uae_u16 serper; + +uae_u8 *save_custom (int *len) +{ + uae_u8 *dstbak, *dst; + int i; + uae_u32 dskpt; + uae_u16 dsklen, dsksync, dskdatr, dskbytr; + + DISK_save_custom (&dskpt, &dsklen, &dsksync, &dskdatr, &dskbytr); + dstbak = dst = malloc (8+256*2); + SL (currprefs.chipset_mask); + SW (0); /* 000 ? */ + SW (dmacon); /* 002 DMACONR */ + SW (VPOSR()); /* 004 VPOSR */ + SW (VHPOSR()); /* 006 VHPOSR */ + SW (dskdatr); /* 008 DSKDATR */ + SW (JOY0DAT()); /* 00A JOY0DAT */ + SW (JOY1DAT()); /* 00C JOY1DAT */ + SW (clxdat); /* 00E CLXDAT */ + SW (ADKCONR()); /* 010 ADKCONR */ + SW (POT0DAT()); /* 012 POT0DAT */ + SW (POT0DAT()); /* 014 POT1DAT */ + SW (0) ; /* 016 POTINP * */ + SW (0); /* 018 SERDATR * */ + SW (dskbytr); /* 01A DSKBYTR */ + SW (INTENAR()); /* 01C INTENAR */ + SW (INTREQR()); /* 01E INTREQR */ + SL (dskpt); /* 020-023 DSKPT */ + SW (dsklen); /* 024 DSKLEN */ + SW (0); /* 026 DSKDAT */ + SW (0); /* 028 REFPTR */ + SW (lof); /* 02A VPOSW */ + SW (0); /* 02C VHPOSW */ + SW (copcon); /* 02E COPCON */ + SW (serper); /* 030 SERDAT * */ + SW (serdat); /* 032 SERPER * */ + SW (potgo_value); /* 034 POTGO */ + SW (0); /* 036 JOYTEST * */ + SW (0); /* 038 STREQU */ + SW (0); /* 03A STRVBL */ + SW (0); /* 03C STRHOR */ + SW (0); /* 03E STRLONG */ + SW (bltcon0); /* 040 BLTCON0 */ + SW (bltcon1); /* 042 BLTCON1 */ + SW (blt_info.bltafwm); /* 044 BLTAFWM */ + SW (blt_info.bltalwm); /* 046 BLTALWM */ + SL (bltcpt); /* 048-04B BLTCPT */ + SL (bltbpt); /* 04C-04F BLTCPT */ + SL (bltapt); /* 050-043 BLTCPT */ + SL (bltdpt); /* 054-057 BLTCPT */ + SW (0); /* 058 BLTSIZE */ + SW (0); /* 05A BLTCON0L (use BLTCON0 instead) */ + SW (oldvblts); /* 05C BLTSIZV */ + SW (blt_info.hblitsize); /* 05E BLTSIZH */ + SW (blt_info.bltcmod); /* 060 BLTCMOD */ + SW (blt_info.bltbmod); /* 062 BLTBMOD */ + SW (blt_info.bltamod); /* 064 BLTAMOD */ + SW (blt_info.bltdmod); /* 066 BLTDMOD */ + SW (0); /* 068 ? */ + SW (0); /* 06A ? */ + SW (0); /* 06C ? */ + SW (0); /* 06E ? */ + SW (blt_info.bltcdat); /* 070 BLTCDAT */ + SW (blt_info.bltbdat); /* 072 BLTBDAT */ + SW (blt_info.bltadat); /* 074 BLTADAT */ + SW (0); /* 076 ? */ + SW (0); /* 078 ? */ + SW (0); /* 07A ? */ + SW (DENISEID()); /* 07C DENISEID/LISAID */ + SW (dsksync); /* 07E DSKSYNC */ + SL (cop1lc); /* 080-083 COP1LC */ + SL (cop2lc); /* 084-087 COP2LC */ + SW (0); /* 088 ? */ + SW (0); /* 08A ? */ + SW (0); /* 08C ? */ + SW (diwstrt); /* 08E DIWSTRT */ + SW (diwstop); /* 090 DIWSTOP */ + SW (ddfstrt); /* 092 DDFSTRT */ + SW (ddfstop); /* 094 DDFSTOP */ + SW (dmacon); /* 096 DMACON */ + SW (clxcon); /* 098 CLXCON */ + SW (intena); /* 09A INTENA */ + SW (intreq); /* 09C INTREQ */ + SW (adkcon); /* 09E ADKCON */ + for (i = 0; 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 (0); /* 10E CLXCON2 */ + for (i = 0;i < 8; i++) + SW (0); /* 110 BPLxDAT */ + for ( i = 0; i < 32; i++) + SW (current_colors.color_regs_ecs[i]); /* 180-1BE COLORxx */ + SW (0); /* 1C0 */ + SW (0); /* 1C2 */ + SW (0); /* 1C4 */ + SW (0); /* 1C6 */ + SW (0); /* 1C8 */ + SW (0); /* 1CA */ + SW (0); /* 1CC */ + SW (0); /* 1CE */ + SW (0); /* 1D0 */ + SW (0); /* 1D2 */ + SW (0); /* 1D4 */ + SW (0); /* 1D6 */ + SW (0); /* 1D8 */ + SW (0); /* 1DA */ + SW (beamcon0); /* 1DC BEAMCON0 */ + SW (0); /* 1DE */ + SW (0); /* 1E0 */ + SW (0); /* 1E2 */ + SW (0); /* 1E4 */ + 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 (0); /* 1FA */ + SW (fmode); /* 1FC FMODE */ + SW (0xffff); /* 1FE */ + + *len = dst - dstbak; + return dstbak; } -void REGPARAM2 custom_lput(CPTR addr, ULONG value) +uae_u8 *restore_custom_agacolors (uae_u8 *src) { - custom_wput(addr & 0xfffe, value >> 16); - custom_wput((addr+2) & 0xfffe, (UWORD)value); + int i; + + for (i = 0; i < 256; i++) + current_colors.color_regs_aga[i] = RL; + return src; +} + +uae_u8 *save_custom_agacolors (int *len) +{ + uae_u8 *dstbak, *dst; + int i; + + dstbak = dst = malloc (256*4); + for (i = 0; i < 256; i++) + SL (current_colors.color_regs_aga[i]); + *len = dst - dstbak; + return dstbak; +} + +uae_u8 *restore_custom_sprite (uae_u8 *src, int num) +{ + spr[num].pt = RL; /* 120-13E SPRxPT */ + sprpos[num] = RW; /* 1x0 SPRxPOS */ + sprctl[num] = RW; /* 1x2 SPRxPOS */ + sprdata[num][0] = RW; /* 1x4 SPRxDATA */ + sprdatb[num][0] = RW; /* 1x6 SPRxDATB */ + sprdata[num][1] = RW; + sprdatb[num][1] = RW; + sprdata[num][2] = RW; + sprdatb[num][2] = RW; + sprdata[num][3] = RW; + sprdatb[num][3] = RW; + spr[num].armed = RB; + return src; +} + +uae_u8 *save_custom_sprite(int *len, int num) +{ + uae_u8 *dstbak, *dst; + + dstbak = dst = malloc (1 + 4 + 2 + 2 + 2 +2 + 3*2); + SL (spr[num].pt); /* 120-13E SPRxPT */ + SW (sprpos[num]); /* 1x0 SPRxPOS */ + SW (sprctl[num]); /* 1x2 SPRxPOS */ + SW (sprdata[num][0]); /* 1x4 SPRxDATA */ + SW (sprdatb[num][0]); /* 1x6 SPRxDATB */ + SW (sprdata[num][1]); + SW (sprdatb[num][1]); + SW (sprdata[num][2]); + SW (sprdatb[num][2]); + SW (sprdata[num][3]); + SW (sprdatb[num][3]); + SB (spr[num].armed ? 1 : 0); + *len = dst - dstbak; + return dstbak; }