--- uae/src/custom.c 2018/04/24 16:38:39 1.1 +++ uae/src/custom.c 2018/04/24 16:39:15 1.1.1.2 @@ -3,7 +3,8 @@ * * Custom chip emulation * - * (c) 1995 Bernd Schmidt, Alessandro Bissacco + * Copyright 1995, 1996, 1997 Bernd Schmidt + * Copyright 1995 Alessandro Bissacco */ #include "sysconfig.h" @@ -17,6 +18,7 @@ #include "events.h" #include "memory.h" #include "custom.h" +#include "readcpu.h" #include "newcpu.h" #include "cia.h" #include "disk.h" @@ -25,6 +27,22 @@ #include "os.h" #include "keybuf.h" #include "serial.h" +#include "osemu.h" +#include "machdep/penguin.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 */ @@ -36,16 +54,37 @@ #define SMART_UPDATE 1 +#ifdef SUPPORT_PENGUINS +#undef SMART_UPDATE +#define SMART_UDPATE 1 +#endif + +#if AGA_CHIPSET == 1 #define MAX_PLANES 8 +#else +#define MAX_PLANES 6 +#endif -#define PIXEL_XPOS(HPOS) (((HPOS)*2 - 0x30)*(use_lores ? 1 : 2)) +/* 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 orizontal and vertical offset for amiga and X + * The second line set the horizontal and vertical offset for amiga and X * pointer matching */ - + #define defstepx (1<<16) #define defstepy (1<<16) #define defxoffs 0 @@ -64,18 +103,23 @@ static int lastsampledmx, lastsampledmy; */ unsigned long int cycles, nextevent; +struct ev eventtab[ev_max]; + int vpos; UWORD lof; +static int lof_changed = 0, interlace_seen = 0; -struct ev eventtab[ev_max]; +static int copper_waiting_for_blitter, copper_active; +static const int dskdelay = 2; /* FIXME: ??? */ -int copper_active; +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]; -static const int dskdelay = 2; /* FIXME: ??? */ +static int dblpfofs[] = { 0, 2, 4, 8, 16, 32, 64, 128 }; - /* - * hardware register values that are visible/can be written to by someone - */ +/* + * Hardware registers of all sorts. + */ static int fmode; @@ -87,52 +131,39 @@ UWORD adkcon; /* used by audio code */ static ULONG cop1lc,cop2lc,copcon; -/* Kludge. FIXME: How does sprite restart after vsync work? */ -static int spron[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; -xcolnr acolors[64]; - -UBYTE *r_bplpt[MAX_PLANES]; -static CPTR bplpt[MAX_PLANES]; +static CPTR bplpt[8]; +#ifndef SMART_UPDATE +static char *real_bplpt[8]; +#endif /*static int blitcount[256]; blitter debug */ -struct bplinfo { +struct color_entry { #if AGA_CHIPSET == 0 - /* X86.S will break if this isn't at the beginning of the structure. */ + /* X86.S expects this at the start of the structure. */ + xcolnr acolors[32]; UWORD color_regs[32]; #else ULONG color_regs[256]; #endif - UWORD bplcon0,bplcon1,bplcon2,bplcon3,bplcon4; - UWORD diwstrt,diwstop,ddfstrt,ddfstop; - - UWORD sprdata[8], sprdatb[8], sprctl[8], sprpos[8]; - int sprarmed[8]; -} bpl_info; - -struct line_description -{ - int inborder; - xcolnr bordercol; - struct bplinfo bpl_info; - struct mem_notify_node *mnn; - CPTR bplpt[MAX_PLANES]; - int linedata_valid; }; -static int frame_redraw_necessary; - -/* 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[numscrlines * 2][MAX_PLANES][MAX_WORDS_PER_LINE*2]; -static struct line_description linedescr[numscrlines * 2]; - +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; @@ -145,61 +176,53 @@ static int sprvbfl; static enum { normal_mouse, dont_care_mouse, follow_mouse } mousestate; - /* - * "hidden" hardware registers - */ - -int dblpf_ind1[256], dblpf_ind2[256], dblpf_2nd1[256], dblpf_2nd2[256]; -int dblpf_aga1[256], dblpf_aga2[256], linear_map_256[256], lots_of_twos[256]; - -int dblpfofs[] = { 0, 2, 4, 8, 16, 32, 64, 128 }; - static ULONG coplc; -static UWORD copi1,copi2; +static unsigned int copi1,copi2; -static enum { - COP_stop, COP_read, COP_wait, COP_move, COP_skip +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; -int plffirstline,plflastline,plfstrt,plfstop,plflinelen; -int diwfirstword,diwlastword; -int plfpri[3]; - -int max_diwstop, prev_max_diwstop; +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 */ -int bpldelay1, bpldelay2; -int bplehb, bplham, bpldualpf, bpldualpfpri, bplplanecnt, bplhires; - -static int pfield_fullline,pfield_linedone; -static int pfield_linedmaon; -static int pfield_lastpart_hpos,last_sprite; -static int slowline_nextpos, slowline_linepos, slowline_lasttoscr; - +/* 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[1000]; + unsigned char apixels[880]; } pixdata; -char spixels[1000]; /* for sprites */ -char spixstate[1000]; /* more sprites */ +char spixels[880]; /* for sprites */ +char spixstate[880]; /* more sprites */ -ULONG ham_linebuf[1000]; -ULONG aga_linebuf[1000], *aga_lbufptr; +ULONG ham_linebuf[880]; +ULONG aga_linebuf[880], *aga_lbufptr; char *xlinebuffer; -int next_lineno, linetoscreen, line_in_border; +int next_lineno; +static int nln_how; + +static int *amiga2aspect_line_map, *native2amiga_line_map; +static int max_drawn_amiga_line; /* * Statistics */ -static unsigned long int msecs = 0, frametime = 0, timeframes = 0; +/* Used also by bebox.cpp */ +unsigned long int msecs = 0, frametime = 0, timeframes = 0; static unsigned long int seconds_base; int bogusframe; @@ -207,10 +230,6 @@ int bogusframe; * helper functions */ -static void pfield_doline_slow(int); -static void pfield_doline(void); -static void do_sprites(int, int); - int inhibit_frame; static int framecnt = 0; @@ -234,25 +253,808 @@ static __inline__ void setclr(UWORD *p, } } -static __inline__ int current_hpos(void) +__inline__ int current_hpos(void) { return cycles - eventtab[ev_hsync].oldcycles; } -static void calcdiw(void) +static __inline__ UBYTE *pfield_xlateptr(CPTR plpt, int bytecount) +{ + 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"); + } + return NULL; + } + return chipmem_bank.xlateaddr(plpt); +} + +static void calculate_copper_cycle_time (void) { - if (use_lores) { - diwfirstword = (bpl_info.diwstrt & 0xFF) - 0x30 - 1; - diwlastword = (bpl_info.diwstop & 0xFF) + 0x100 - 0x30 - 1; + int bplplanes; + + if (diwstate == DIW_waiting_start || !dmaen (DMA_BITPLANE)) { + copper_cycle_time = 2; + 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; + +static int last_redraw_point; + +static int first_drawn_line, last_drawn_line; +static int first_block_line, last_block_line; + +static void init_regchanges (void) +{ + size_t i; + + 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 +}; + +/* 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; +}; + +/* 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 int line_changed[2 * (maxvpos+1)]; + +#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 + +static struct decision thisline_decision; +static int modulos_added, plane_decided, color_decided, very_broken_program; + +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); + remembered_color_entry = next_color_entry++; + } + thisline_decision.ctable = remembered_color_entry; + if (color_src_match == -1 || color_dest_match != remembered_color_entry + || line_decisions[next_lineno].ctable != color_src_match) + { + /* The remembered comparison didn't help us - need to compare again. */ + int oldctable = line_decisions[next_lineno].ctable; + int changed = 0; + + if (oldctable == -1) + changed = 1; + 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_src_match = oldctable; + } + } +} + +static void decide_diw (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); + } +} + +static void record_color_change (int regno, unsigned long value) +{ + /* 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; + } + /* 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; + } +#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; +} + +static __inline__ void decide_as_playfield (int startpos, int len) +{ + thisline_decision.which = 1; + + /* 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; + + /* These are for comparison. */ + thisline_decision.bplcon0 = bplcon0; + thisline_decision.bplcon2 = bplcon2; +#if AGA_CHIPSET == 1 + 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 + || line_decisions[next_lineno].bplcon0 != thisline_decision.bplcon0 + || line_decisions[next_lineno].bplcon2 != thisline_decision.bplcon2 +#if AGA_CHIPSET == 1 + || line_decisions[next_lineno].bplcon4 != thisline_decision.bplcon4 +#endif + ) +#endif /* SMART_UPDATE */ + line_changed[next_lineno] = 1; +} + +static __inline__ void post_decide_line (void) +{ + static int warned = 0; + int tmp; + + 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; + } + + if (thisline_decision.which != -1 || diwstate == DIW_waiting_start || (bplcon0 & 0x7000) == 0 + || !dmaen (DMA_BITPLANE) || current_hpos () >= thisline_decision.plfstrt + thisline_decision.plflinelen) + 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; + } +#endif + + decided_hires = (bplcon0 & 0x8000) == 0x8000; + decided_nr_planes = ((bplcon0 & 0x7000) >> 12); + + /* 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 { - diwfirstword = (bpl_info.diwstrt & 0xFF) * 2 - 0x60 - 2; - diwlastword = (bpl_info.diwstop & 0xFF) * 2 + 0x200 - 0x60 - 2; + tmp = current_hpos () & ~7; + very_broken_program = current_hpos () & 7; } - if (diwfirstword < 0) diwfirstword = 0; - if (diwlastword > max_diwstop) max_diwstop = diwlastword; + MARK_LINE_CHANGED (next_lineno); /* Play safe. */ + decide_as_playfield (tmp, plflinelen + plfstrt - tmp); +} + +static void decide_line_1 (void) +{ + do_sprites(vpos, current_hpos ()); + + /* 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 (); + } + if (diwstate == DIW_waiting_stop && vpos == plflastline) { + diwstate = DIW_waiting_start; + calculate_copper_cycle_time (); + } + + if (framecnt != 0) { +/* thisline_decision.which = -2; This doesn't do anything but hurt, I think. */ + return; + } + + if (!dmaen(DMA_BITPLANE) || diwstate == DIW_waiting_start || (bplcon0 & 0x7000) == 0) { + /* We don't want to draw this one. */ + thisline_decision.which = -1; + return; + } + + 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(); + } +#endif + decide_as_playfield (plfstrt, plflinelen); +} + +static __inline__ void decide_line (void) +{ + if (thisline_decision.which == 0 && current_hpos() >= plfstrt) + decide_line_1 (); +} + +static void decide_delay (void) +{ + static int warned; + + /* 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; + } + /* @@@ 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"); + } +} + +/* + * 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) +{ + int i, bytecount; + + if (framecnt != 0 || plane_decided) + return; + + if (decided_nr_planes == -1 /* Still undecided */ + || current_hpos () < thisline_decision.plfstrt + thisline_decision.plflinelen) + return; + + plane_decided = 1; + + bytecount = plflinelen / (decided_hires ? 4 : 8) * 2; + + 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; + } + } 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; + } + } + 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 + } + } +} + +/* + * 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) +{ + /* 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; +} + +/* + * Call decide_plane() before calling this. + */ +static void do_modulos (void) +{ + /* 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; + } + } 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; + } + } + + modulos_added = 1; + } +} + +static void decide_sprite (int spr) +{ + int sprxp; + + if (framecnt != 0) + return; + + decide_diw (); + decide_line (); + + if (!sprarmed[spr] || thisline_decision.which != 1 || current_hpos () < 0x14) + return; + + 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) + return; - plffirstline = bpl_info.diwstrt >> 8; - plflastline = bpl_info.diwstop >> 8; + sprite_last_drawn_at[spr] = sprxp; + + /* 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)) + return; +#ifdef OS_WITHOUT_MEMORY_MANAGEMENT + if(next_sprite_draw >= _max_sprite_draw) { + _max_sprite_draw = next_sprite_draw; + 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]; +} + +static __inline__ void decide_sprite_1 (int spr) +{ + int sprxp = sprxpos[spr]; + + 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)) + return; + + 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]; +} + +static void finish_decisions (void) +{ + struct draw_info *dip; + struct draw_info *dip_old; + struct decision *dp; + int changed; + int i; + + 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 (); + + dip = curr_drawinfo + next_lineno; + dip_old = prev_drawinfo + next_lineno; + dp = line_decisions + next_lineno; + changed = line_changed[next_lineno]; + + 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; + } + + 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]; + + dip->nr_color_changes = next_color_change - dip->first_color_change; + dip->nr_sprites = next_sprite_draw - dip->first_sprite_draw; + + if (dip->first_delay_change != dip->last_delay_change) + 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))) + 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))) + changed = 1; + + if (changed) { + line_changed[next_lineno] = 1; + *dp = thisline_decision; + } else + /* The only one that may differ: */ + dp->ctable = thisline_decision.ctable; +} + +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.diwfirstword = -1; + thisline_decision.diwlastword = -2; + thisline_decision.ctable = -1; + thisline_decision.color0 = 0xFFFFFFFFul; + + line_changed[next_lineno] = 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; +} + +static void init_decisions (void) +{ + size_t i; + for (i = 0; i < sizeof line_decisions / sizeof *line_decisions; i++) { + line_decisions[i].which = -2; + } +} + +static void calcdiw (void) +{ + diwfirstword = ((diwstrt & 0xFF) - DISPLAY_LEFT_SHIFT - 1) << lores_shift; + diwlastword = ((diwstop & 0xFF) + 0x100 - DISPLAY_LEFT_SHIFT - 1) << lores_shift; + + if (diwlastword > max_diwlastword) + diwlastword = max_diwlastword; + if (diwfirstword < 0) + diwfirstword = 0; + if (diwlastword < 0) + diwlastword = 0; + + plffirstline = diwstrt >> 8; + plflastline = diwstop >> 8; #if 0 /* This happens far too often. */ if (plffirstline < minfirstline) { @@ -267,8 +1069,8 @@ static void calcdiw(void) plflastline = 313; } #endif - plfstrt = bpl_info.ddfstrt; - plfstop = bpl_info.ddfstop; + plfstrt = ddfstrt; + plfstop = ddfstop; if (plfstrt < 0x18) plfstrt = 0x18; if (plfstop > 0xD8) plfstop = 0xD8; if (plfstrt > plfstop) plfstrt = plfstop; @@ -278,13 +1080,13 @@ static void calcdiw(void) /* 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; - } /* @@ -299,15 +1101,20 @@ static void decode_ham6 (int pix, int st if (!bplham || bplplanecnt != 6) return; - if (pix <= diwfirstword) { - pix = diwfirstword; - lastcolor = bpl_info.color_regs[0]; - } - - while (pix < diwlastword && pix < stoppos) { + 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 = bpl_info.color_regs[pv]; break; + 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; @@ -316,7 +1123,7 @@ static void decode_ham6 (int pix, int st buf[pix++] = lastcolor; } } - +#if 0 static void decode_ham_aga (int pix, int stoppos) { static ULONG lastcolor; @@ -325,29 +1132,29 @@ static void decode_ham_aga (int pix, int if (!bplham || (bplplanecnt != 6 && bplplanecnt != 8)) return; - if (pix <= diwfirstword) { - pix = diwfirstword; - lastcolor = bpl_info.color_regs[0]; + if (pix <= dp_for_drawing->diwfirstword) { + pix = dp_for_drawing->diwfirstword; + lastcolor = colors_for_drawing.color_regs[0]; } - if (bplplanecnt == 6) { + if (dp_for_drawing->bplplanecnt == 6) { /* HAM 6 */ - while (pix < diwlastword && pix < stoppos) { + while (pix < dp_for_drawing->diwlastword && pix < stoppos) { int pv = pixdata.apixels[pix]; switch(pv & 0x30) { - case 0x00: lastcolor = bpl_info.color_regs[pv]; break; + 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 (bplplanecnt == 8) { + } else if (dp_for_drawing->bplplanecnt == 8) { /* HAM 8 */ - while (pix < diwlastword && pix < stoppos) { + while (pix < dp_for_drawing->diwlastword && pix < stoppos) { int pv = pixdata.apixels[pix]; switch(pv & 0x3) { - case 0x0: lastcolor = bpl_info.color_regs[pv >> 2]; break; + 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; @@ -356,62 +1163,16 @@ static void decode_ham_aga (int pix, int } } } +#endif -#define LINE_TO_SCR(NAME, TYPE, DO_DOUBLE) \ -static void NAME(int pix, int stoppos, int offset) \ -{ \ - TYPE *buf = (TYPE *)xlinebuffer; \ - int oldpix = pix; \ - buf -= pix; \ - if (DO_DOUBLE) offset /= sizeof(TYPE); \ - while (pix < diwfirstword && pix < stoppos) { \ - TYPE d = acolors[0]; \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - if (bplham && bplplanecnt == 6) { \ - /* HAM 6 */ \ - while (pix < diwlastword && pix < stoppos) { \ - 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 < diwlastword && pix < stoppos) { \ - int pixcol = pixdata.apixels[pix]; \ - TYPE d; \ - if (spixstate[pix]) { \ - d = acolors[pixcol]; \ - } else { \ - d = acolors[lookup[pixcol]]; \ - } \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - } else { \ - while (pix < diwlastword && pix < stoppos) { \ - TYPE d = acolors[pixdata.apixels[pix]]; \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - } \ - while (pix < stoppos) { \ - TYPE d = acolors[0]; \ - buf[pix] = d; if (DO_DOUBLE) buf[pix+offset] = d; \ - pix++; \ - } \ - xlinebuffer = (char *)(((TYPE *)xlinebuffer) + pix - oldpix); \ -} - +#if AGA_CHIPSET != 0 /* WARNING: Not too much of this will work correctly yet. */ static void pfield_linetoscr_aga(int pix, int stoppos) { ULONG *buf = aga_lbufptr; int i; - int xor = (UBYTE)(bpl_info.bplcon4 >> 8); + int xor = (UBYTE)(bplcon4 >> 8); int oldpix = pix; \ buf -= pix; \ @@ -420,7 +1181,7 @@ static void pfield_linetoscr_aga(int pix pixdata.apixels[i] ^= xor; while (pix < diwfirstword && pix < stoppos) { - buf[pix++] = bpl_info.color_regs[0]; + buf[pix++] = colors_for_drawing.color_regs[0]; } if (bplham) { while (pix < diwlastword && pix < stoppos) { @@ -437,19 +1198,19 @@ static void pfield_linetoscr_aga(int pix int pfno = lookup_no[pixcol]; if (spixstate[pix]) { - buf[pix] = bpl_info.color_regs[pixcol]; + buf[pix] = colors_for_drawing.color_regs[pixcol]; } else { int val = lookup[pixdata.apixels[pix]]; if (pfno == 2) - val += dblpfofs[(bpl_info.bplcon2 >> 10) & 7]; - buf[pix] = bpl_info.color_regs[val]; + 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 = bpl_info.color_regs[pixcol]; + ULONG d = colors_for_drawing.color_regs[pixcol]; /* What about sprites? */ if (pixcol & 0x20) d = (d & 0x777777) >> 1; @@ -459,12 +1220,12 @@ static void pfield_linetoscr_aga(int pix } else { while (pix < diwlastword && pix < stoppos) { int pixcol = pixdata.apixels[pix]; - buf[pix] = bpl_info.color_regs[pixcol]; + buf[pix] = colors_for_drawing.color_regs[pixcol]; pix++; } } while (pix < stoppos) { - buf[pix++] = bpl_info.color_regs[0]; + buf[pix++] = colors_for_drawing.color_regs[0]; } aga_lbufptr += pix - oldpix; } @@ -493,19 +1254,77 @@ static void aga_translate8(int start, in ((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) \ +static void NAME(char *buf, int start, int stop) \ { \ TYPE *b = (TYPE *)buf; \ int i;\ - int maxpos = gfxvidinfo.maxlinetoscr; \ - xcolnr col = acolors[0]; \ - if (!maxpos) maxpos = 796; \ - for (i = 0; i < maxpos; i++) \ - *b++ = col; \ + xcolnr col = colors_for_drawing.acolors[0]; \ + for (i = start; i < stop; i++) \ + b[i] = col; \ } LINE_TO_SCR(pfield_linetoscr_8, UBYTE, 0) @@ -529,203 +1348,287 @@ FILL_LINE(fill_line_32, ULONG) #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; + } +} + #undef pfield_linetoscr_full16 -extern void pfield_linetoscr_full8(int, int, int) __asm__("pfield_linetoscr_full8"); -extern void pfield_linetoscr_full16(int, int, int) __asm__("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); +#endif + } else { + pfield_linetoscr_asm16(pfield_linetoscr_normal_asm16, 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++) + ((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; + } +} + +#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 stop, int offset) +static void pfield_linetoscr_full8_double(int start, int lframe_end, int diw_end, int stop) { char *oldxlb = (char *)xlinebuffer; - pfield_linetoscr_full8(start,stop,offset); - xlinebuffer = oldxlb + offset; - pfield_linetoscr_full8(start,stop,offset); + 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 stop, int offset) +static void pfield_linetoscr_full16_double(int start, int lframe_end, int diw_end, int stop) { char *oldxlb = (char *)xlinebuffer; - pfield_linetoscr_full16(start,stop,offset); - xlinebuffer = oldxlb + offset; - pfield_linetoscr_full16(start,stop,offset); + 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(int y) +static __inline__ void fill_line(void) { switch (gfxvidinfo.pixbytes) { - case 1: fill_line_8(gfxvidinfo.bufmem + gfxvidinfo.rowbytes * y); break; - case 2: fill_line_16(gfxvidinfo.bufmem + gfxvidinfo.rowbytes * y); break; - case 4: fill_line_32(gfxvidinfo.bufmem + gfxvidinfo.rowbytes * y); break; + 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; } } +static int linetoscr_diw_end, linetoscr_diw_start; + +static void pfield_init_linetoscr (void) +{ + 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; +} + static void pfield_do_linetoscr(int start, int stop) { - int factor = use_lores ? 1 : 2; - int oldstop = stop; - int real_start, real_stop; - - start = PIXEL_XPOS(start); - if (start < 8*factor) - start = 8*factor; - stop = PIXEL_XPOS(stop); - if (stop > 406*factor) - stop = 406*factor; - + int lframe_end = linetoscr_diw_start, diw_end = linetoscr_diw_end; + + 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) return; - if (stop <= gfxvidinfo.x_adjust) - return; - - slowline_lasttoscr = oldstop; - - if (start < gfxvidinfo.x_adjust) - real_start = gfxvidinfo.x_adjust; - else - real_start = start; - - if (gfxvidinfo.maxlinetoscr) { - real_stop = gfxvidinfo.x_adjust + gfxvidinfo.maxlinetoscr; - if (real_stop > stop) - real_stop = stop; - } else - real_stop = stop; #if AGA_CHIPSET == 0 - if (start == 8*factor && stop == 406*factor) { + if (start == linetoscr_x_adjust && stop == linetoscr_right_x) { switch (gfxvidinfo.pixbytes) { - case 1: pfield_linetoscr_full8 (real_start, real_stop, 0); break; - case 2: pfield_linetoscr_full16 (real_start, real_stop, 0); break; - case 4: pfield_linetoscr_full32 (real_start, real_stop, 0); break; + 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 { - if (real_start >= real_stop) - return; switch (gfxvidinfo.pixbytes) { - case 1: pfield_linetoscr_8 (real_start, real_stop, 0); break; - case 2: pfield_linetoscr_16 (real_start, real_stop, 0); break; - case 4: pfield_linetoscr_32 (real_start, real_stop, 0); break; + 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(real_start, real_stop); + pfield_linetoscr_aga(start, lframe_end, diw_end, stop); #endif } -static void pfield_do_linetoscr_full(int double_line) +static void pfield_do_fill_line(int start, int stop) { - int factor = use_lores ? 1 : 2; - int stop, start; - - start = 8*factor; - stop = 406*factor; - - if (start < gfxvidinfo.x_adjust) - start = gfxvidinfo.x_adjust; - - if (gfxvidinfo.maxlinetoscr) { - int tmp = gfxvidinfo.x_adjust + gfxvidinfo.maxlinetoscr; - if (tmp < stop) - stop = tmp; + if (stop > linetoscr_right_x) + stop = linetoscr_right_x; + if (start < linetoscr_x_adjust) + start = linetoscr_x_adjust; + + if (start >= stop) + return; + + 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; } +} + +static void pfield_do_linetoscr_full(int double_line) +{ + 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; #if AGA_CHIPSET == 0 if (double_line) { switch (gfxvidinfo.pixbytes) { - case 1: pfield_linetoscr_full8_double (start, stop, gfxvidinfo.rowbytes); break; - case 2: pfield_linetoscr_full16_double (start, stop, gfxvidinfo.rowbytes); break; - case 4: pfield_linetoscr_full32_double (start, stop, gfxvidinfo.rowbytes); break; + 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, stop, 0); break; - case 2: pfield_linetoscr_full16 (start, stop, 0); break; - case 4: pfield_linetoscr_full32 (start, stop, 0); break; + 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 - pfield_linetoscr_aga(start, stop); + pfield_linetoscr_aga(start, lframe_end, diw_end, stop); #endif } -/* - * This function is called whenever a hardware register that controls the - * screen display is modified. Usually, this routine does nothing. But in - * some cases, e.g., when a color changes in mid-screen, as in copper-plasma - * effects, this function switches the update method from the fast full-line - * update to the much slower single-color-clock update. - */ -static void pfield_may_need_update(int colreg) -{ - int i; - - /* Ignore, if this happened before or after the DDF window */ - /* @@@ This breaks some copper plasmas. Not good... maybe a config - * option is needed. */ - if (framecnt != 0 || !pfield_linedmaon || current_hpos() <= plfstrt - || vpos < plffirstline || vpos < minfirstline || vpos >= plflastline - || next_lineno >= gfxvidinfo.maxline) - { - return; - } - /* - * If a color reg was modified, it is only important if we are within - * the DIW. - */ - if (PIXEL_XPOS(current_hpos()) <= diwfirstword && colreg) - return; - - /* - * If we are past the DDF window, me might as well draw the complete - * line now. - */ - if (current_hpos() > plfstrt + plflinelen && pfield_fullline) { - if (!pfield_linedone) - pfield_doline(); - pfield_linedone = 1; - return; - } - - do_sprites(vpos, current_hpos()); - if (pfield_fullline) { - pfield_lastpart_hpos = 0; - memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); - memset(spixstate,0,sizeof spixstate); - pfield_fullline = 0; - slowline_nextpos = -1; - slowline_linepos = 0; - slowline_lasttoscr = 0; - } else { - assert(pfield_lastpart_hpos <= current_hpos()); - } - for (i = pfield_lastpart_hpos; i < current_hpos(); i++) { - pfield_doline_slow(i); - } - if (colreg) { - pfield_do_linetoscr(slowline_lasttoscr, current_hpos()); - } - pfield_lastpart_hpos = current_hpos(); -} - -/* Apparently, the DMA bit is tested by the hardware at some point, - * presumably at the ddfstart position, to determine whether it - * ought to draw the line. - * This is probably not completely correct, but should not matter - * very much. - */ -static void pfield_calclinedma(void) -{ - if (current_hpos() >= plfstrt) - return; - - pfield_linedmaon = dmaen(DMA_BITPLANE); -} - /* * register functions */ -static UWORD DMACONR(void) +UWORD DMACONR(void) { return (dmacon | (bltstate==BLT_done ? 0 : 0x4000) | (blt_info.blitzero ? 0x2000 : 0)); @@ -746,7 +1649,12 @@ static UWORD VPOSR(void) return (vpos >> 8) | lof; #endif } -static void VPOSW(UWORD v) { lof = v & 0x8000; } +static void VPOSW(UWORD v) +{ + if (lof != (v & 0x8000)) + lof_changed = 1; + lof = v & 0x8000; +} static UWORD VHPOSR(void) { return (vpos << 8) | current_hpos(); } static void COP1LCH(UWORD v) { cop1lc= (cop1lc & 0xffff) | ((ULONG)v << 16); } @@ -760,6 +1668,7 @@ static void COPJMP1(UWORD a) 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; } static void COPJMP2(UWORD a) { @@ -767,26 +1676,31 @@ static void COPJMP2(UWORD a) 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; } - +static void COPCON(UWORD a) { copcon = a; } static void DMACON(UWORD v) { int i, need_resched = 0; UWORD oldcon = dmacon; + + decide_line(); setclr(&dmacon,v); dmacon &= 0x1FFF; - pfield_calclinedma(); + post_decide_line (); /* 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_SPRITE) > (oldcon & DMA_SPRITE)) { +#if 0 /* @@@ ??? Is this right now? */ + if ((dmacon & DMA_SPRITE) < (oldcon & DMA_SPRITE)) { int i; for (i = 0; i < 8; i++) - spron[i] = 1; + spron[i] = SPR_restart; } +#endif if ((dmacon & DMA_BLITPRI) > (oldcon & DMA_BLITPRI) && bltstate != BLT_done) { static int count = 0; if (!count) { @@ -822,6 +1736,7 @@ static void DMACON(UWORD v) } } #endif + calculate_copper_cycle_time (); if (copper_active && !eventtab[ev_copper].active) { eventtab[ev_copper].active = 1; eventtab[ev_copper].oldcycles = cycles; @@ -831,7 +1746,15 @@ static void DMACON(UWORD v) if (need_resched) events_schedule(); } -static void INTENA(UWORD v) { setclr(&intena,v); regs.spcflags |= SPCFLAG_INT; } + +static int trace_intena = 0; + +static void INTENA(UWORD v) +{ + if (trace_intena) + fprintf(stderr, "INTENA: %04x\n", v); + setclr(&intena,v); regs.spcflags |= SPCFLAG_INT; +} void INTREQ(UWORD v) { setclr(&intreq,v); @@ -841,79 +1764,77 @@ void INTREQ(UWORD v) static void ADKCON(UWORD v) { setclr(&adkcon,v); } -static void BPLPTH(UWORD v, int num) { bplpt[num] = (bplpt[num] & 0xffff) | ((ULONG)v << 16); } -static void BPLPTL(UWORD v, int num) { bplpt[num] = (bplpt[num] & ~0xffff) | (v & 0xFFFE); } +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); } -/* - * I've seen the listing of an example program that changes - * from lo- to hires while a line is being drawn. That's - * awful, but we want to emulate it. - */ -static void BPLCON0(UWORD v) +static void BPLCON0(UWORD v) { - if (bpl_info.bplcon0 == 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 (bplcon0 == v) return; - pfield_may_need_update(0); - bpl_info.bplcon0 = v; - bplhires = (v & 0x8000) == 0x8000; - bplplanecnt = (v & 0x7000) >> 12; - bplham = (v & 0x800) == 0x800; - bpldualpf = (v & 0x400) == 0x400; - bplehb = (v & 0xFDC0) == 0x6000 && !(bpl_info.bplcon2 & 0x200); /* see below */ + decide_line (); + bplcon0 = v; + post_decide_line (); + calculate_copper_cycle_time (); +#if 0 calcdiw(); /* This should go away. */ +#endif } static void BPLCON1(UWORD v) { - if (bpl_info.bplcon1 == v) + if (bplcon1 == v) return; - pfield_may_need_update(0); - bpl_info.bplcon1 = v; - bpldelay1 = v & 0xF; - bpldelay2 = (v >> 4) & 0xF; + decide_diw (); + bplcon1 = v; + decide_delay (); } static void BPLCON2(UWORD v) { - if (bpl_info.bplcon2 == v) - return; - pfield_may_need_update(0); - bpl_info.bplcon2 = v; - bpldualpfpri = (v & 0x40) == 0x40; - plfpri[1] = 1 << 2*(v & 7); - plfpri[2] = 1 << 2*((v>>3) & 7); - bplehb = (bpl_info.bplcon0 & 0xFDC0) == 0x6000 && !(v & 0x200); /* see above */ + if (bplcon2 != v) + decide_line (); + bplcon2 = v; } static void BPLCON3(UWORD v) { - if (bpl_info.bplcon3 == v) - return; - pfield_may_need_update(0); - bpl_info.bplcon3 = v; + if (bplcon3 != v) + decide_line (); + bplcon3 = v; } static void BPLCON4(UWORD v) { - if (bpl_info.bplcon4 == v) - return; - pfield_may_need_update(0); - bpl_info.bplcon4 = v; + if (bplcon4 != v) + decide_line (); + bplcon4 = v; } static void BPL1MOD(UWORD v) { v &= ~1; - if (bpl1mod == v) + if ((WORD)bpl1mod == (WORD)v) return; - pfield_may_need_update(0); bpl1mod = v; + decide_modulos (); } + static void BPL2MOD(UWORD v) { v &= ~1; - if (bpl2mod == v) + if ((WORD)bpl2mod == (WORD)v) return; - pfield_may_need_update(0); bpl2mod = v; + decide_modulos(); } +/* 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; } @@ -921,80 +1842,86 @@ static void BPL4DAT(UWORD v) { bpl4dat static void BPL5DAT(UWORD v) { bpl5dat = v; } static void BPL6DAT(UWORD v) { bpl6dat = v; } -/* We call pfield_may_need_update() from here. Actually, - * I have no idea what happens if someone changes ddf or - * diw mid-line, and I don't really want to know. I doubt - * that this sort of thing was ever used to create a - * useful effect. - */ static void DIWSTRT(UWORD v) { - if (bpl_info.diwstrt == v) + if (diwstrt == v) return; - pfield_may_need_update(0); - bpl_info.diwstrt = v; + decide_line (); + diwstrt = v; calcdiw(); } static void DIWSTOP(UWORD v) { - if (bpl_info.diwstop == v) + if (diwstop == v) return; - pfield_may_need_update(0); - bpl_info.diwstop = v; + diwstop = v; calcdiw(); } static void DDFSTRT(UWORD v) { - if (bpl_info.ddfstrt == v) + v &= 0xFF; + if (ddfstrt == v) return; - pfield_may_need_update(0); - bpl_info.ddfstrt = v; + decide_line (); + ddfstrt = v; calcdiw(); } static void DDFSTOP(UWORD v) { - if (bpl_info.ddfstop == v) + v &= 0xFF; + if (ddfstop == v) return; - pfield_may_need_update(0); - bpl_info.ddfstop = v; + decide_line (); + ddfstop = v; calcdiw(); } static void BLTADAT(UWORD v) { - maybe_blit(); - blt_info.bltadat = v; + maybe_blit(); + + blt_info.bltadat = v; } +/* + * "Loading data shifts it immediately" says the HRM. Well, that may + * be true for BLTBDAT, but not for BLTADAT - it appears the A data must be + * loaded for every word so that AFWM and ALWM can be applied. + */ static void BLTBDAT(UWORD v) { maybe_blit(); - blt_info.bltbdat = v; + + if (bltcon1 & 2) + blt_info.bltbhold = v << (bltcon1 >> 12); + else + blt_info.bltbhold = v >> (bltcon1 >> 12); + blt_info.bltbdat = v; } -static void BLTCDAT(UWORD v) { maybe_blit(); blt_info.bltcdat = v; } +static void BLTCDAT(UWORD v) { maybe_blit(); blt_info.bltcdat = v; } -static void BLTAMOD(UWORD v) { maybe_blit(); blt_info.bltamod = v & 0xFFFE; } -static void BLTBMOD(UWORD v) { maybe_blit(); blt_info.bltbmod = v & 0xFFFE; } -static void BLTCMOD(UWORD v) { maybe_blit(); blt_info.bltcmod = v & 0xFFFE; } -static void BLTDMOD(UWORD v) { maybe_blit(); blt_info.bltdmod = v & 0xFFFE; } +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 BLTCON0(UWORD v) { maybe_blit(); bltcon0 = v; blinea_shift = v >> 12; } +static void BLTCON0(UWORD 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(UWORD v) { maybe_blit(); bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF); } +static void BLTCON1(UWORD 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(UWORD v) { maybe_blit(); blt_info.bltafwm = v; } +static void BLTALWM(UWORD v) { maybe_blit(); blt_info.bltalwm = v; } -static void BLTAPTH(UWORD v) { maybe_blit(); bltapt= (bltapt & 0xffff) | ((ULONG)(v & 0x1F) << 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 & 0x1F) << 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 & 0x1F) << 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 & 0x1F) << 16); } -static void BLTDPTL(UWORD v) { maybe_blit(); bltdpt= (bltdpt & ~0xffff) | (v & 0xFFFE); } -static void BLTSIZE(UWORD 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) { bltsize = v; @@ -1006,63 +1933,83 @@ static void BLTSIZE(UWORD v) if (!blt_info.hblitsize) blt_info.hblitsize = 64; bltstate = BLT_init; - regs.spcflags |= SPCFLAG_BLIT; + do_blitter(); } static void BLTSIZV(UWORD v) { maybe_blit(); oldvblts = v & 0x7FFF; } -static void BLTSIZH(UWORD v) +static void BLTSIZH(UWORD v) { - maybe_blit(); + 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; - regs.spcflags |= SPCFLAG_BLIT; + bltstate = BLT_init; + do_blitter(); } -static void SPRxCTL_1(UWORD v, int num) +static void SPRxCTL_1(UWORD v, int num) { - bpl_info.sprctl[num] = v; - bpl_info.sprarmed[num] = 0; - if (bpl_info.sprpos[num] == 0 && v == 0) - spron[num] = 0; - else - spron[num] |= 2; -} -static void SPRxPOS_1(UWORD v, int num) -{ - bpl_info.sprpos[num] = v; -} -static void SPRxDATA_1(UWORD v, int num) -{ - bpl_info.sprdata[num] = v; - bpl_info.sprarmed[num] = 1; -} -static void SPRxDATB_1(UWORD v, int num) -{ - bpl_info.sprdatb[num] = v; -} -static void SPRxCTL(UWORD v, int num) { pfield_may_need_update(0); SPRxCTL_1(v, num); } -static void SPRxPOS(UWORD v, int num) { pfield_may_need_update(0); SPRxPOS_1(v, num); } -static void SPRxDATA(UWORD v, int num){ pfield_may_need_update(0); SPRxDATA_1(v, num); } -static void SPRxDATB(UWORD v, int num){ pfield_may_need_update(0); SPRxDATB_1(v, num); } -static void SPRxPTH(UWORD 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); +} +static void SPRxPOS_1(UWORD 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 (!spron[num]) spron[num] = 1; + 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; } -static void SPRxPTL(UWORD v, int num) +static void SPRxPTL(UWORD v, int num) { sprpt[num] &= ~0xffff; - sprpt[num] |= v; - if (!spron[num]) spron[num] = 1; + sprpt[num] |= v; +#if 1 + if (spron[num] == SPR_vtop) +#endif + spron[num] = SPR_restart; } -static void COLOR(UWORD v, int num) +static void COLOR(UWORD v, int num) { int r,g,b; int cr,cg,cb; @@ -1071,16 +2018,17 @@ static void COLOR(UWORD v, int num) v &= 0xFFF; #if AGA_CHIPSET == 1 { + /* XXX Broken */ ULONG cval; - colreg = ((bpl_info.bplcon3 >> 13) & 7) * 32 + num; + colreg = ((bplcon3 >> 13) & 7) * 32 + num; r = (v & 0xF00) >> 8; g = (v & 0xF0) >> 4; b = (v & 0xF) >> 0; - cr = bpl_info.color_regs[colreg] >> 16; - cg = (bpl_info.color_regs[colreg] >> 8) & 0xFF; - cb = bpl_info.color_regs[colreg] & 0xFF; + cr = color_regs[colreg] >> 16; + cg = (color_regs[colreg] >> 8) & 0xFF; + cb = color_regs[colreg] & 0xFF; - if (bpl_info.bplcon3 & 0x200) { + if (bplcon3 & 0x200) { cr &= 0xF0; cr |= r; cg &= 0xF0; cg |= g; cb &= 0xF0; cb |= b; @@ -1090,19 +2038,20 @@ static void COLOR(UWORD v, int num) cb = b + (b << 4); } cval = (cr << 16) | (cg << 8) | cb; - if (cval == bpl_info.color_regs[colreg]) + if (cval == color_regs[colreg]) return; - bpl_info.color_regs[colreg] = cval; + color_regs[colreg] = cval; pfield_may_need_update(1); } #else { - if (bpl_info.color_regs[num] == v) + if (current_colors.color_regs[num] == v) return; - pfield_may_need_update(1); - bpl_info.color_regs[num] = v; - acolors[num] = xcolors[v]; - acolors[num+32] = xcolors[(v >> 1) & 0x777]; + /* Call this with the old table still intact. */ + record_color_change (num, v); + remembered_color_entry = -1; + current_colors.color_regs[num] = v; + current_colors.acolors[num] = xcolors[v]; } #endif } @@ -1184,7 +2133,7 @@ static UWORD POT0DAT(void) return cnt; } -static UWORD JOY0DAT(void) { return joy0x + (joy0y << 8); } +static UWORD JOY0DAT(void) { return ((UBYTE)joy0x) + ((UWORD)joy0y << 8); } static UWORD JOY1DAT(void) { return joy0dir; @@ -1198,13 +2147,14 @@ static void AUDxLCH(int nr, UWORD v) { a 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) { - static int warned = 0; 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; @@ -1214,6 +2164,10 @@ static void AUDxPER(int nr, UWORD v) 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; } +/* + * Here starts the copper code. It should be rewritten. + */ + static int copcomp(void) { UWORD vp = vpos & (((copi2 >> 8) & 0x7F) | 0x80); @@ -1231,12 +2185,21 @@ static int calc_copcomp_true(int currvpo { UWORD vp = currvpos & (((copi2 >> 8) & 0x7F) | 0x80); UWORD hp = currhpos & (copi2 & 0xFE); - UWORD vcmp = copi1 >> 8; - UWORD hcmp = copi1 & 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; @@ -1251,19 +2214,20 @@ static int calc_copcomp_true(int currvpo cycleadd = maxhpos; vp = currvpos & (((copi2 >> 8) & 0x7F) | 0x80); } - if (coptime > 0 && bplhires && bplplanecnt == 4) + if (coptime > 0 && (bplcon0 & 0xF000) == 0xC000) return coptime; copper_time_hpos = currhpos; hp = copper_time_hpos & (copi2 & 0xFE); if (!(vp > vcmp)) { - while (hp < hcmp-2) { + while ((int)hp < ((int)hcmp)) { currhpos++; /* Copper DMA is turned off in Hires 4 bitplane mode */ - if (!bplhires || bplplanecnt < 4 || !dmaen(DMA_BITPLANE) - || currhpos < plfstrt-2 || currhpos > (plfstop+4)) + 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-4) { + if (currhpos > maxhpos) { /* Now, what? There might be a good position on the * next line. But it can also be the FFFF FFFE * case. @@ -1283,63 +2247,227 @@ static int calc_copcomp_true(int currvpo return coptime; } -static void copper_read(void) +/* + * 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--; + } + 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); +} + +static __inline__ int copper_init_read (int n_cycles) { if (dmaen(DMA_COPPER)){ - copi1 = chipmem_bank.wget(coplc); - copi2 = chipmem_bank.wget(coplc+2); - coplc += 4; - eventtab[ev_copper].oldcycles = cycles; - eventtab[ev_copper].evtime = ((copi1 & 1) ? (copi2 & 1) ? 10 : 8 : 4) + cycles; - copstate = (copi1 & 1) ? (copi2 & 1) ? COP_skip : COP_wait : COP_move; + 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 __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; +} + +static __inline__ void handle_bltfinish_wait (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; + } +} + +void blitter_done_notify (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 (); } } static void do_copper(void) -{ - switch(copstate){ - case COP_read: - copper_read(); - break; - case COP_move: - if (copi1 >= (copcon & 2 ? 0x40 : 0x80)) { - custom_bank.wput(copi1,copi2); - copper_read(); - } else { - copstate = COP_stop; +{ + 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; + + 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; + + 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; + + case COP_stop: eventtab[ev_copper].active = 0; copper_active = 0; + return; } - break; - case COP_skip: - if (calc_copcomp_true(vpos, current_hpos()) == 0) - coplc += 4; - copper_read(); - break; - case COP_wait: { - int coptime = calc_copcomp_true(vpos, current_hpos()); - if (coptime < 0) { - copstate = COP_stop; - eventtab[ev_copper].active = 0; - copper_active = 0; - } else { - if (!coptime) - copper_read(); - else { - eventtab[ev_copper].evtime = coptime + cycles; - eventtab[ev_copper].oldcycles = cycles; - } - } - break; - } - case COP_stop: - eventtab[ev_copper].active = 0; - copper_active = 0; - break; - } } static void diskblk_handler(void) @@ -1379,136 +2507,120 @@ void do_disk(void) } } -static __inline__ void pfield_fetchdata(void) -{ - if (dmaen(0x100) && pfield_linedmaon) { - switch(bplplanecnt){ - case 8: - bpl8dat = chipmem_bank.wget(bplpt[7]); bplpt[7] += 2; bpl8dat <<= 7; - case 7: - bpl7dat = chipmem_bank.wget(bplpt[6]); bplpt[6] += 2; bpl7dat <<= 6; - case 6: - bpl6dat = chipmem_bank.wget(bplpt[5]); bplpt[5] += 2; bpl6dat <<= 5; - case 5: - bpl5dat = chipmem_bank.wget(bplpt[4]); bplpt[4] += 2; bpl5dat <<= 4; - case 4: - bpl4dat = chipmem_bank.wget(bplpt[3]); bplpt[3] += 2; bpl4dat <<= 3; - case 3: - bpl3dat = chipmem_bank.wget(bplpt[2]); bplpt[2] += 2; bpl3dat <<= 2; - case 2: - bpl2dat = chipmem_bank.wget(bplpt[1]); bplpt[1] += 2; bpl2dat <<= 1; - case 1: - bpl1dat = chipmem_bank.wget(bplpt[0]); bplpt[0] += 2; - } - } -} - static void do_sprites(int currvp, int currhp) { int i; - int maxspr = currhp/4 - 0x18/4; - + /* 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; +#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; +#endif if (maxspr < 0) return; - if (maxspr > 7) - maxspr = 7; + if (maxspr > 8) + maxspr = 8; - for(i = last_sprite; i <= maxspr; i++) { - int vstart = (bpl_info.sprpos[i] >> 8) | ((bpl_info.sprctl[i] << 6) & 0x100); - int vstop = (bpl_info.sprctl[i] >> 8) | ((bpl_info.sprctl[i] << 7) & 0x100); - if ((vstart <= currvp && vstop >= currvp) || spron[i] == 1) { - if (dmaen(0x20)) { - UWORD data1 = chipmem_bank.wget(sprpt[i]); - UWORD data2 = chipmem_bank.wget(sprpt[i]+2); - sprpt[i] += 4; + 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 (vstop != currvp && spron[i] != 1) { - /* Hack for X mouse auto-calibration */ - if (i == 0 && !sprvbfl && ((bpl_info.sprpos[0]&0xff)<<2)>0x60) { - spr0ctl=bpl_info.sprctl[0]; - spr0pos=bpl_info.sprpos[0]; - sprvbfl=2; - } - SPRxDATB_1(data2, i); - SPRxDATA_1(data1, i); - } else { - SPRxPOS_1(data1, i); - SPRxCTL_1(data2, i); + 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); } } } - last_sprite = maxspr + 1; -} - -static __inline__ void pfield_modulos(int add) -{ - switch(bplplanecnt){ - case 8: - bplpt[7] += add + bpl2mod; - case 7: - bplpt[6] += add + bpl1mod; - case 6: - bplpt[5] += add + bpl2mod; - case 5: - bplpt[4] += add + bpl1mod; - case 4: - bplpt[3] += add + bpl2mod; - case 3: - bplpt[2] += add + bpl1mod; - case 2: - bplpt[1] += add + bpl2mod; - case 1: - bplpt[0] += add + bpl1mod; - } } #if AGA_CHIPSET == 0 -static void pfield_sprite (int num, int sprxp, UWORD data, UWORD datb, int lores) +static void pfield_sprite (int num, int sprxp, UWORD data, UWORD datb, int ctl) { 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; - for(i = 15; i >= 0; i--, data >>= 1, datb >>= 1) { - int sprxpos = sprxp + i*(lores ? 1 : 2); + 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; - /* When doing the slow line update, the following condition can - * happen. Lower-numbered sprites get the higher priority. */ - if (spixstate[sprxpos] & ((1 << num)-1)) - continue; - /* 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]) { + 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[sprxpos]]; + plno = lookup_no[pixdata.apixels[sprxp]]; if (plno != 0 && (1 << num) >= plfpri[plno]) continue; } - if ((bpl_info.sprctl[num] & 0x80) && (num & 1)) { + if ((ctl & 0x80) && (num & 1)) { /* Attached sprite */ col = ((data << 2) & 4) + ((datb << 3) & 8); - spixstate[sprxpos] |= 1 << (num-1); - spixels[sprxpos] = col; + spixstate[sprxp] |= 1 << (num-1); + spixels[sprxp] = col; } else { col = (data & 1) | ((datb << 1) & 2); - if (spixstate[sprxpos] & (1 << num)) { + if (spixstate[sprxp] & (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]; + if (spixstate[sprxp] & (1 << (num+1))) + col += spixels[sprxp]; /* Is there any sprite pixel at this position at all? */ if (!col) { - spixstate[sprxpos] &= ~(3 << num); + spixstate[sprxp] &= ~(3 << num); } else col += 16; } else { @@ -1518,22 +2630,23 @@ static void pfield_sprite (int num, int } } if (col) { - pixdata.apixels[sprxpos] = col; - spixstate[sprxpos] |= 1<= 0; i--, data >>= 1, datb >>= 1) { - int sprxpos = sprxp + i*(lores ? 1 : 2); + int sprxpos = sprxp + (i << lores_shift); int plno; int col; - /* When doing the slow line update, the following condition can - * happen. Lower-numbered sprites get the higher priority. */ - if (spixstate[sprxpos] & ((1 << num)-1)) - continue; - /* 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. */ @@ -1561,7 +2669,7 @@ static void pfield_sprite (int num, int continue; } - if ((bpl_info.sprctl[num] & 0x80) && (num & 1)) { + if ((sprctl[num] & 0x80) && (num & 1)) { /* Attached sprite */ col = ((data << 2) & 4) + ((datb << 3) & 8); spixstate[sprxpos] |= 1 << (num-1); @@ -1577,167 +2685,30 @@ static void pfield_sprite (int num, int if (!col) { spixstate[sprxpos] &= ~(3 << num); } else - col += ((bpl_info.bplcon4 << (num & 1 ? 4 : 0)) & 240); + col += ((bplcon4 << (num & 1 ? 4 : 0)) & 240); } else { if (col) { - col |= ((bpl_info.bplcon4 << (num & 1 ? 4 : 0)) & 240) | ((num & 6) << 1); + col |= ((bplcon4 << (num & 1 ? 4 : 0)) & 240) | ((num & 6) << 1); } } } if (col) { pixdata.apixels[sprxpos] = col; spixstate[sprxpos] |= 1<= 0; pix--) { - switch(bplplanecnt) { - case 8: - pixdata.apixels[pix + offs2] |= bpl8dat & 0x80; bpl8dat >>= 1; - case 7: - pixdata.apixels[pix + offs1] |= bpl7dat & 0x40; bpl7dat >>= 1; - case 6: - pixdata.apixels[pix + offs2] |= bpl6dat & 0x20; bpl6dat >>= 1; - case 5: - pixdata.apixels[pix + offs1] |= bpl5dat & 0x10; bpl5dat >>= 1; - case 4: - pixdata.apixels[pix + offs2] |= bpl4dat & 0x8; bpl4dat >>= 1; - case 3: - pixdata.apixels[pix + offs1] |= bpl3dat & 0x4; bpl3dat >>= 1; - case 2: - pixdata.apixels[pix + offs2] |= bpl2dat & 0x2; bpl2dat >>= 1; - case 1: - pixdata.apixels[pix + offs1] |= bpl1dat & 0x1; bpl1dat >>= 1; - } - } - } else { - int offs1 = xpos + 32 + bpldelay1*2; - int offs2 = xpos + 32 + bpldelay2*2; - - int pix; - for(pix = 30; pix >= 0; pix -= 2) { - switch(bplplanecnt) { - case 8: - pixdata.apixels[pix + offs2] |= bpl8dat & 0x80; - pixdata.apixels[pix + offs2 + 1] |= bpl8dat & 0x80; bpl8dat >>= 1; - case 7: - pixdata.apixels[pix + offs1] |= bpl7dat & 0x40; - pixdata.apixels[pix + offs1 + 1] |= bpl7dat & 0x40; bpl7dat >>= 1; - case 6: - pixdata.apixels[pix + offs2] |= bpl6dat & 0x20; - pixdata.apixels[pix + offs2 + 1] |= bpl6dat & 0x20; bpl6dat >>= 1; - case 5: - pixdata.apixels[pix + offs1] |= bpl5dat & 0x10; - pixdata.apixels[pix + offs1 + 1] |= bpl5dat & 0x10; bpl5dat >>= 1; - case 4: - pixdata.apixels[pix + offs2] |= bpl4dat & 0x8; - pixdata.apixels[pix + offs2 + 1] |= bpl4dat & 0x8; bpl4dat >>= 1; - case 3: - pixdata.apixels[pix + offs1] |= bpl3dat & 0x4; - pixdata.apixels[pix + offs1 + 1] |= bpl3dat & 0x4; bpl3dat >>= 1; - case 2: - pixdata.apixels[pix + offs2] |= bpl2dat & 0x2; - pixdata.apixels[pix + offs2 + 1] |= bpl2dat & 0x2; bpl2dat >>= 1; - case 1: - pixdata.apixels[pix + offs1] |= bpl1dat & 0x1; - pixdata.apixels[pix + offs1 + 1] |= bpl1dat & 0x1; bpl1dat >>= 1; - } - } - } -} - -static void pfield_doline_slow_l(int currhpos) -{ - int xpos = currhpos * 2 - 0x30; - - if (bplhires) { - int offs1 = xpos + 8 + bpldelay1; - int offs2 = xpos + 8 + bpldelay2; - - int pix; - for(pix = 7; pix >= 0; pix--) { - switch(bplplanecnt) { - case 8: - pixdata.apixels[pix + offs2] |= bpl8dat & 0x80; bpl8dat >>= 2; - case 7: - pixdata.apixels[pix + offs1] |= bpl7dat & 0x40; bpl7dat >>= 2; - case 6: - pixdata.apixels[pix + offs2] |= bpl6dat & 0x20; bpl6dat >>= 2; - case 5: - pixdata.apixels[pix + offs1] |= bpl5dat & 0x10; bpl5dat >>= 2; - case 4: - pixdata.apixels[pix + offs2] |= bpl4dat & 0x8; bpl4dat >>= 2; - case 3: - pixdata.apixels[pix + offs1] |= bpl3dat & 0x4; bpl3dat >>= 2; - case 2: - pixdata.apixels[pix + offs2] |= bpl2dat & 0x2; bpl2dat >>= 2; - case 1: - pixdata.apixels[pix + offs1] |= bpl1dat & 0x1; bpl1dat >>= 2; - } - } - } else { - int offs1 = xpos + 16 + bpldelay1; - int offs2 = xpos + 16 + bpldelay2; - - int pix; - for(pix = 15; pix >= 0; pix --) { - switch(bplplanecnt) { - case 8: - pixdata.apixels[pix + offs2] |= bpl8dat & 0x80; bpl8dat >>= 1; - case 7: - pixdata.apixels[pix + offs1] |= bpl7dat & 0x40; bpl7dat >>= 1; - case 6: - pixdata.apixels[pix + offs2] |= bpl6dat & 0x20; bpl6dat >>= 1; - case 5: - pixdata.apixels[pix + offs1] |= bpl5dat & 0x10; bpl5dat >>= 1; - case 4: - pixdata.apixels[pix + offs2] |= bpl4dat & 0x8; bpl4dat >>= 1; - case 3: - pixdata.apixels[pix + offs1] |= bpl3dat & 0x4; bpl3dat >>= 1; - case 2: - pixdata.apixels[pix + offs2] |= bpl2dat & 0x2; bpl2dat >>= 1; - case 1: - pixdata.apixels[pix + offs1] |= bpl1dat & 0x1; bpl1dat >>= 1; - } - } - } -} - ULONG hirestab_h[256][2]; ULONG lorestab_h[256][4]; @@ -1811,6 +2782,556 @@ static void gen_pfield_tables(void) } } +#ifndef SMART_UPDATE +#undef UNALIGNED_PROFITABLE +#endif + +#ifdef UNALIGNED_PROFITABLE +/* + * Be sure to pass a constant for the third and fourth parameters to get proper optimization. + */ +static __inline__ void doop_planes_hires_unaligned_h (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 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; + } + } +} + +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; + } 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; + } +} + +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; + } + } +} + +static __inline__ void set_planes_hires_unaligned_h (ULONG *app, UBYTE *ptr, int nplanes) +{ + 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; + } +} + +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; + } +} + +static __inline__ void set_planes_hires_unaligned_l (ULONG *app, UBYTE *ptr, int nplanes) +{ + 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 + 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; + } +} + +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; + + data1 = *(ptr + i); + v1 = lorestab_l[data1][0]; + v2 = lorestab_l[data1][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 (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; + } + + 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 (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; + } + + 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; + } + + 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; + } + + app[i*2] = v1; + app[i*2 + 1] = v2; + } +} + +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; + } + + 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; + } + } + } 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; + } + + 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; + } + } + } 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; + } + } + } +} + +#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; @@ -1900,10 +3421,11 @@ static __inline__ void pfield_setword_lo unsigned int bpldat1; \ UWORD data; \ unsigned int bpldat2 = 0; \ - for (i = plflinelen; i > 0; i -= LL_SUB) { \ + UBYTE *ptr = (POINTER); \ + for (i = dp_for_drawing->plflinelen; i > 0; i -= LL_SUB) { \ bpldat1 = bpldat2; \ - bpldat2 = (*POINTER << 8) | *(POINTER+1); \ - POINTER+=2; \ + bpldat2 = do_get_mem_word ((UWORD *)ptr); \ + ptr+=2; \ data = (bpldat1 << (16 - DELAY)) | (bpldat2 >> DELAY); \ FUNC(data, app, MULT); \ app += P_ADD; \ @@ -1912,12 +3434,16 @@ static __inline__ void pfield_setword_lo FUNC(data, app, MULT); \ } -#if 1 -static void pfield_doline_h(void) -{ - int xpos = plfstrt * 4 - 0x60; - int spr, sprites_seen; +#ifdef SMART_UPDATE +#define DATA_POINTER(n) (dataptr + (n)*MAX_WORDS_PER_LINE*2) +#else +#define DATA_POINTER(n) (real_bplpt[n]) +#endif +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); @@ -1925,38 +3451,39 @@ static void pfield_doline_h(void) int delay1 = 2*(bpldelay1 & 7); int delay2 = 2*(bpldelay2 & 7); unsigned char *app = pixdata.apixels + xpos1; + UBYTE *dataptr = line_data[lineno]; - DO_ONE_PLANE(r_bplpt[0], 0, pfield_setword_hires_h, delay1, 4, 16); + DO_ONE_PLANE(DATA_POINTER(0), 0, pfield_setword_hires_h, delay1, 4, 16); if (bplplanecnt > 2) { app = pixdata.apixels + xpos1; - DO_ONE_PLANE(r_bplpt[2], 2, pfield_orword_hires_h, delay1, 4, 16); + 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(r_bplpt[4], 4, pfield_orword_hires_h, delay1, 4, 16); + DO_ONE_PLANE(DATA_POINTER(4), 4, pfield_orword_hires_h, delay1, 4, 16); } if (bplplanecnt > 6) { app = pixdata.apixels + xpos1; - DO_ONE_PLANE(r_bplpt[6], 6, pfield_orword_hires_h, delay1, 4, 16); + 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(r_bplpt[1], 1, pfield_orword_hires_h, delay2, 4, 16); + DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_hires_h, delay2, 4, 16); } if (bplplanecnt > 3) { app = pixdata.apixels + xpos2; - DO_ONE_PLANE(r_bplpt[3], 3, pfield_orword_hires_h, delay2, 4, 16); + 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(r_bplpt[5], 5, pfield_orword_hires_h, delay2, 4, 16); + DO_ONE_PLANE(DATA_POINTER(5), 5, pfield_orword_hires_h, delay2, 4, 16); } if (bplplanecnt > 7) { app = pixdata.apixels + xpos2; - DO_ONE_PLANE(r_bplpt[7], 7, pfield_orword_hires_h, delay2, 4, 16); + DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_hires_h, delay2, 4, 16); } #endif } else { @@ -1966,72 +3493,50 @@ static void pfield_doline_h(void) if (bplplanecnt > 0) { int x = xpos + 32; unsigned char *app = pixdata.apixels + x; + UBYTE *dataptr = line_data[lineno]; - DO_ONE_PLANE(r_bplpt[0], 0, pfield_setword_lores_h, bpldelay1, 8, 32); + DO_ONE_PLANE(DATA_POINTER(0), 0, pfield_setword_lores_h, bpldelay1, 8, 32); if (bplplanecnt > 2) { app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[2], 2, pfield_orword_lores_h, bpldelay1, 8, 32); + DO_ONE_PLANE(DATA_POINTER(2), 2, pfield_orword_lores_h, bpldelay1, 8, 32); } if (bplplanecnt > 4) { app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[4], 4, pfield_orword_lores_h, bpldelay1, 8, 32); + 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(r_bplpt[6], 6, pfield_orword_lores_h, bpldelay1, 8, 32); + 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(r_bplpt[1], 1, pfield_orword_lores_h, bpldelay2, 8, 32); + DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_lores_h, bpldelay2, 8, 32); } if (bplplanecnt > 3) { app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[3], 3, pfield_orword_lores_h, bpldelay2, 8, 32); + DO_ONE_PLANE(DATA_POINTER(3), 3, pfield_orword_lores_h, bpldelay2, 8, 32); } if (bplplanecnt > 5) { app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[5], 5, pfield_orword_lores_h, bpldelay2, 8, 32); + 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(r_bplpt[7], 7, pfield_orword_lores_h, bpldelay2, 8, 32); + DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_lores_h, bpldelay2, 8, 32); } #endif } else { memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); } } - - decode_ham6 (16,812); - - linetoscreen = 1; - sprites_seen = 0; - - for(spr = 7; spr >= 0; spr--) { - if (bpl_info.sprarmed[spr]) { - int sprxp = ((bpl_info.sprpos[spr] & 0xFF) * 4) - 0x60 + (bpl_info.sprctl[spr] & 1)*2; - int i; - /* Ugh. Nasty bug. Let's rather lose some sprites than trash - * memory. */ - if (sprxp >= 0) { - if (!sprites_seen) { - sprites_seen = 1; - memset(spixstate,0,sizeof spixstate); - } - pfield_sprite (spr, sprxp, bpl_info.sprdata[spr], bpl_info.sprdatb[spr], 0); - } - } - } } -#endif -static void pfield_doline_l(void) + +static void pfield_doline_aligned_l (int lineno) { - int xpos = plfstrt * 2 - 0x30; - int spr; - int sprites_seen; + int xpos = dp_for_drawing->plfstrt * 2 - DISPLAY_LEFT_SHIFT; if (bplhires) { if (bplplanecnt > 0) { @@ -2040,38 +3545,39 @@ static void pfield_doline_l(void) int delay1 = (bpldelay1 & 7) * 2; int delay2 = (bpldelay2 & 7) * 2; unsigned char *app = pixdata.apixels + xpos1; + UBYTE *dataptr = line_data[lineno]; - DO_ONE_PLANE(r_bplpt[0], 0, pfield_setword_hires_l, delay1, 4, 8); + DO_ONE_PLANE(DATA_POINTER(0), 0, pfield_setword_hires_l, delay1, 4, 8); if (bplplanecnt > 2) { app = pixdata.apixels + xpos1; - DO_ONE_PLANE(r_bplpt[2], 2, pfield_orword_hires_l, delay1, 4, 8); + 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(r_bplpt[4], 4, pfield_orword_hires_l, delay1, 4, 8); + DO_ONE_PLANE(DATA_POINTER(4), 4, pfield_orword_hires_l, delay1, 4, 8); } if (bplplanecnt > 6) { app = pixdata.apixels + xpos1; - DO_ONE_PLANE(r_bplpt[6], 6, pfield_orword_hires_l, delay1, 4, 8); + 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(r_bplpt[1], 1, pfield_orword_hires_l, delay2, 4, 8); + DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_hires_l, delay2, 4, 8); } if (bplplanecnt > 3) { app = pixdata.apixels + xpos2; - DO_ONE_PLANE(r_bplpt[3], 3, pfield_orword_hires_l, delay2, 4, 8); + 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(r_bplpt[5], 5, pfield_orword_hires_l, delay2, 4, 8); + DO_ONE_PLANE(DATA_POINTER(5), 5, pfield_orword_hires_l, delay2, 4, 8); } if (bplplanecnt > 7) { app = pixdata.apixels + xpos2; - DO_ONE_PLANE(r_bplpt[7], 7, pfield_orword_hires_l, delay2, 4, 8); + DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_hires_l, delay2, 4, 8); } #endif } else { @@ -2083,290 +3589,91 @@ static void pfield_doline_l(void) int delay1 = bpldelay1; int delay2 = bpldelay2; unsigned char *app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[0], 0, pfield_setword_lores_l, delay1, 8, 16); + 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(r_bplpt[2], 2, pfield_orword_lores_l, delay1, 8, 16); + DO_ONE_PLANE(DATA_POINTER(2), 2, pfield_orword_lores_l, delay1, 8, 16); } if (bplplanecnt > 4) { app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[4], 4, pfield_orword_lores_l, delay1, 8, 16); + 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(r_bplpt[6], 6, pfield_orword_lores_l, delay1, 8, 16); + 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(r_bplpt[1], 1, pfield_orword_lores_l, delay2, 8, 16); + DO_ONE_PLANE(DATA_POINTER(1), 1, pfield_orword_lores_l, delay2, 8, 16); } if (bplplanecnt > 3) { app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[3], 3, pfield_orword_lores_l, delay2, 8, 16); + DO_ONE_PLANE(DATA_POINTER(3), 3, pfield_orword_lores_l, delay2, 8, 16); } if (bplplanecnt > 5) { app = pixdata.apixels + x; - DO_ONE_PLANE(r_bplpt[5], 5, pfield_orword_lores_l, delay2, 8, 16); + 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(r_bplpt[7], 7, pfield_orword_lores_l, delay2, 8, 16); + DO_ONE_PLANE(DATA_POINTER(7), 7, pfield_orword_lores_l, delay2, 8, 16); } #endif } else { memset(pixdata.apixels, 0, sizeof(pixdata.apixels)); } } - - decode_ham6 (8,406); - - linetoscreen = 1; - sprites_seen = 0; - - for(spr = 7; spr >= 0; spr--) { - if (bpl_info.sprarmed[spr]) { - int sprxp = ((bpl_info.sprpos[spr] & 0xFF) * 2) - 0x30 + (bpl_info.sprctl[spr] & 1); - int i; - /* Ugh. Nasty bug. Let's rather lose some sprites than trash - * memory. */ - if (sprxp >= 0) { - if (!sprites_seen) { - sprites_seen = 1; - memset(spixstate,0,sizeof spixstate); - } - pfield_sprite (spr, sprxp, bpl_info.sprdata[spr], bpl_info.sprdatb[spr], 1); - } - } - } } -static int bpl_data_differs(UBYTE *data, UBYTE *r_addr, int nbytes) -{ - return memcmpy(data, r_addr, nbytes); -} +#define pfield_doline_h pfield_doline_aligned_h +#define pfield_doline_l pfield_doline_aligned_l -static __inline__ int bplinfo_differs(struct bplinfo *a, struct bplinfo *b) -{ - int ncolors; - int i; - if (a->bplcon0 != b->bplcon0 - || a->bplcon1 != b->bplcon1 - || a->bplcon2 != b->bplcon2 - || a->bplcon3 != b->bplcon3 - || a->bplcon4 != b->bplcon4 - || a->diwstrt != b->diwstrt - || a->diwstop != b->diwstop - || a->ddfstrt != b->ddfstrt - || a->ddfstop != b->ddfstop) - return 1; - -#if 1 - /* This sometimes loses for sprite colors */ - ncolors = 1 << ((a->bplcon0 & 0x7000) >> 12); - if (ncolors == 64) - ncolors = (a->bplcon0 & 0x800) ? 16 : 32; - for (i = 0; i < ncolors; i++) - if (a->color_regs[i] != b->color_regs[i]) - return 1; -#else - /* ... and this will lose badly on few-color screens when we implement - * AGA. - */ - if (memcmp(a->color_regs, b->color_regs, sizeof a->color_regs) != 0) - return 1; -#endif - for (i = 0; i < 8; i++) { - if (a->sprarmed[i] != b->sprarmed[i]) - return 1; - if (a->sprarmed[i] - && (a->sprctl[i] != b->sprctl[i] - || a->sprpos[i] != b->sprpos[i] - || a->sprdata[i] != b->sprdata[i] - || a->sprdatb[i] != b->sprdatb[i])) - return 1; - } - return 0; -} +#endif /* UNALIGNED_PROFITABLE */ -static void pfield_doline(void) +static void pfield_adjust_delay (void) { - int bytecount = plflinelen / (bplhires ? 4 : 8) * 2; - int drawit = 0; + int ddf_left = dp_for_drawing->plfstrt; + int ddf_right = dp_for_drawing->plfstrt + dp_for_drawing->plflinelen; int i; - if (vpos < plffirstline || vpos >= plflastline) - return; - - if (!dmaen(0x100) || !pfield_linedmaon) { - line_in_border = 1; - return; - } - - for (i = 0; i < bplplanecnt; i++) { - r_bplpt[i] = pfield_xlateptr(bplpt[i], bytecount); - if (r_bplpt[i] == NULL) - return; - } - -#if SMART_UPDATE == 1 - drawit = bplinfo_differs(&bpl_info, &linedescr[next_lineno].bpl_info); - if (drawit) { - linedescr[next_lineno].bpl_info = bpl_info; - } - drawit |= !linedescr[next_lineno].linedata_valid | frame_redraw_necessary; - - if (bytecount <= MAX_WORDS_PER_LINE * 2) { - linedescr[next_lineno].linedata_valid = 1; - for (i = 0; i < bplplanecnt; i++) - drawit |= bpl_data_differs(line_data[next_lineno][i],r_bplpt[i], - bytecount); - - } else { - linedescr[next_lineno].linedata_valid = 0; - } -#endif - - pfield_modulos(bytecount); - -#if SMART_UPDATE != 0 - if (!drawit) - return; -#endif - - if (use_lores) - pfield_doline_l(); - else - pfield_doline_h(); -} - -static void pfield_doline_slow(int currhp) -{ - int xpos = PIXEL_XPOS(currhp); - - if (vpos < plffirstline || vpos >= plflastline) - return; - - if (currhp == plfstrt) - slowline_nextpos = currhp; - - if (currhp == slowline_nextpos) { - if (slowline_linepos >= plflinelen) { - /* The modulos must get added at exactly this point. */ - pfield_modulos(0); - slowline_nextpos = -1; - } else { - slowline_nextpos += bplhires ? 4 : 8; - slowline_linepos += bplhires ? 4 : 8; - - /* Hmmmm..... - * In theory, we could use the fast pfield_doline functions even - * in this case. We only need to do sprites, ham decoding and - * the line_to_scr stuff cycle-per-cycle. However, we would no - * longer be able to emulate tricks that switch lores/hires in - * the middle of a line, but I'm not sure whether this sort of - * thing actually works currently and whether it's worthwhile. - */ - - pfield_fetchdata(); - - if (use_lores) - pfield_doline_slow_l (currhp); - else - pfield_doline_slow_h (currhp); - /* @@@ need to test this */ - decode_ham6 (xpos, PIXEL_XPOS(slowline_nextpos)); + 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); } } - if (currhp > 48) { - int spr; - for(spr = 7; spr >= 0; spr--) { - if (bpl_info.sprarmed[spr] && currhp == (bpl_info.sprpos[spr] & 0xFF)) { - int sprxp = xpos + (bpl_info.sprctl[spr] & 1) * (use_lores ? 1 : 2); - pfield_sprite (spr, sprxp, bpl_info.sprdata[spr], bpl_info.sprdatb[spr], use_lores); - } - } - } } -static int first_drawn_line, last_drawn_line; -static int first_block_line, last_block_line; - -static void init_frame (void) +static void init_hardware_frame (void) { int i; - int maxpos = use_lores ? 400 : 800; - int old_pmds = prev_max_diwstop; - - if (max_diwstop == 0) - max_diwstop = diwlastword; - - if (max_diwstop < (use_lores ? 320 : 640) || max_diwstop > maxpos) - prev_max_diwstop = maxpos; - else - prev_max_diwstop = max_diwstop; - - max_diwstop = 0; - - memset(spron, 0, sizeof spron); - memset(bpl_info.sprpos, 0, sizeof bpl_info.sprpos); - memset(bpl_info.sprctl, 0, sizeof bpl_info.sprctl); - last_drawn_line = 0; - first_drawn_line = 32767; - - first_block_line = last_block_line = -2; - calc_adjustment(); - if (frame_redraw_necessary) - frame_redraw_necessary--; - - if (old_pmds != prev_max_diwstop) { - frame_redraw_necessary |= (bpl_info.bplcon0 & 4 ? 2 : 1); - } -} - -/* - * A raster line has been built in the graphics buffer. Tell the graphics code - * to do anything necessary to display it. - */ - -static void do_flush_line (int lineno) -{ - if (lineno < first_drawn_line) - first_drawn_line = lineno; - if (lineno > last_drawn_line) - last_drawn_line = lineno; - - 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; - } - } -} -/* - * One Amiga frame has been finished. Tell the graphics code about it. - * Note that the actual flush_scren() call is a no-op for all reasonable - * systems. - */ + for (i = 0; i < 8; i++) + spron[i] = SPR_vtop; +#if 0 + memset(sprpos, 0, sizeof sprpos); + memset(sprctl, 0, sizeof sprctl); +#endif -static void do_flush_screen (int start, int stop) -{ - if (gfxvidinfo.maxblocklines != 0 && first_block_line != -2) { - flush_block (first_block_line, last_block_line); - } - if (start <= stop) - flush_screen (start, stop); + next_lineno = 0; + nln_how = 0; + diwstate = DIW_waiting_start; } static void setdontcare(void) @@ -2449,20 +3756,15 @@ static void do_mouse_hack(void) int val = (lastdiffy<<16) / (spr0y-lastspr0y); if (val>=0x8000) mstepy=(mstepy*(calweight-1)+val)/calweight; } - mousexpos = lastmx; - if (gfxvidinfo.x_adjust) - mousexpos += gfxvidinfo.x_adjust; - mouseypos = lastmy; - - if(!correct_aspect) - mouseypos *= 2; - if(use_lores) - mousexpos *= 2; - if (gfxvidinfo.x_adjust) - mousexpos -= 16; - - diffx = adjust ((((mousexpos + 0x70 + xoffs - spr0x) & ~1) * mstepx) >> 16); - diffy = adjust ((((mouseypos + yoffs - spr0y+minfirstline*2) & ~1) * mstepy) >> 16); + 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; @@ -2471,7 +3773,583 @@ static void do_mouse_hack(void) } } -static void vsync_handler(void) +/* @@@ 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) +{ + if (lineno < first_drawn_line) + first_drawn_line = lineno; + if (lineno > last_drawn_line) + last_drawn_line = lineno; + + 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; + } + } +} + +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 +} + +/* + * 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) +{ + 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); +} + +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; + } +} + +static __inline__ void adjust_color0_for_color_change (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]; + } +} + +static __inline__ void do_color_changes (line_draw_func worker) +{ + 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; + } + } +} + +static void pfield_expand_dp_bplcon (void) +{ + 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; +} + +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; + 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; + + case LINE_DONE_AS_PREVIOUS: + line_decisions[lineno].which = -2; + /* fall through */ + case LINE_DONE: + 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 (!border) { + xlinebuffer = gfxvidinfo.bufmem + gfxvidinfo.rowbytes * gfx_ypos - gfxvidinfo.pixbytes * linetoscr_x_adjust; + aga_lbufptr = aga_linebuf; + + pfield_expand_dp_bplcon (); + +#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 (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 (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); + } + } + /* 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; + + 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; + } + 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); + } + } +} + +#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? */ + } + + 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; + + 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; + + pfield_draw_line (l, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]); + } + } +} + +static penguin_id our_penguin; + +static void kill_drawing_penguin (void) +{ + kill (our_penguin, SIGQUIT); +} + +#endif + +static int penguins_enabled_thisframe; + +static void init_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 + + 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; + + 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_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_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; + + 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; + + /* 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 +} + +static void finish_drawing_frame (void) +{ + int i; + +#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); +#endif + +#ifndef SMART_UPDATE + /* @@@ This isn't exactly right yet. FIXME */ + if (!interlace_seen) { + do_flush_screen (first_drawn_line, last_drawn_line); + return; + } +#endif + for (i = 0; i < max_ypos_thisframe; i++) { + int where; + int line = i + thisframe_y_adjust_real; + + if (linestate[line] == LINE_UNDECIDED) + break; + + where = amiga2aspect_line_map[i+min_ypos_for_screen]; + if (where >= gfxvidinfo.maxline) + break; + if (where == -1) + continue; + + pfield_draw_line (line, where, amiga2aspect_line_map[i+min_ypos_for_screen+1]); + } + do_flush_screen (first_drawn_line, last_drawn_line); +} + +static void vsync_handler (void) { UWORD dir; int button; @@ -2482,14 +4360,24 @@ static void vsync_handler(void) do_mouse_hack(); INTREQ(0x8020); - if (bpl_info.bplcon0 & 4) lof ^= 0x8000; + 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); - if (framecnt == 0) - do_flush_screen (first_drawn_line, last_drawn_line); - - count_frame(); - init_frame(); + init_hardware_frame(); #ifdef HAVE_GETTIMEOFDAY { struct timeval tv; @@ -2511,21 +4399,35 @@ static void vsync_handler(void) static void hsync_handler(void) { - int lineno = vpos - minfirstline; + int lineno = next_lineno; int lineisdouble = 0; int line_was_doubled = 0; - do_sprites(vpos, maxhpos); - last_sprite = 0; - - if (correct_aspect) { - lineno *= 2; - if (bpl_info.bplcon0 & 4) { - if(!lof) { - lineno++; - } - } else { - lineisdouble = 1; + finish_decisions (); + do_modulos (); + + 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; } } @@ -2554,137 +4456,76 @@ static void hsync_handler(void) } } } - - if (framecnt == 0 && vpos >= minfirstline && lineno < gfxvidinfo.maxline) { - if (vpos >= plffirstline && vpos < plflastline) - { - /* Finish the line, if we started doing it with the slow update. - * Otherwise, draw it entirely. */ - if (pfield_fullline) { - if (!pfield_linedone) { - /* This can turn on line_in_border if DMA is off */ - pfield_doline(); - } - if (linetoscreen) - pfield_do_linetoscr_full (line_was_doubled = lineisdouble); - } else { - int i; - for(i = pfield_lastpart_hpos; i < maxhpos; i++) - pfield_doline_slow(i); - pfield_do_linetoscr(slowline_lasttoscr, maxhpos); - /* The COLOR routine masks off the high nibble. This means - * that there will never be 0xFFFF in color_regs[0], and this - * means that the line will be drawn completely the next time - * we get into pfield_doline() - */ - linedescr[lineno].bpl_info.color_regs[0] = 0xFFFF; - linetoscreen = 1; - } - } else - line_in_border = 1; - - if (line_in_border && - (!linedescr[lineno].inborder - || linedescr[lineno].bordercol != acolors[0])) - { - linedescr[lineno].bordercol = acolors[0]; - linedescr[lineno].linedata_valid = 0; - - fill_line (lineno); - linetoscreen = 1; - } - - linedescr[lineno].inborder = line_in_border; - - if (linetoscreen) { -#if AGA_CHIPSET == 1 - switch (gfxvidinfo.pixbytes) { - case 1: aga_translate8 (0, aga_lbufptr-aga_linebuf); break; - case 2: aga_translate16 (0, aga_lbufptr-aga_linebuf); break; - case 4: aga_translate32 (0, aga_lbufptr-aga_linebuf); break; - } +#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 - do_flush_line (lineno); - } - if (lineisdouble) { - int drawit = 0; - - if (linedescr[lineno].inborder != linedescr[lineno+1].inborder - || (linedescr[lineno].inborder == 1 - && linedescr[lineno].bordercol != linedescr[lineno+1].bordercol)) { - drawit = 1; - - } else if (linedescr[lineno].inborder == 0 - && (linetoscreen - || (bplinfo_differs(&linedescr[lineno].bpl_info, - &linedescr[lineno+1].bpl_info)))) { - drawit = 1; - } - - if (drawit) { - linedescr[lineno+1].inborder = linedescr[lineno].inborder; - linedescr[lineno+1].bordercol = linedescr[lineno].bordercol; -#if SMART_UPDATE != 0 - linedescr[lineno+1].bpl_info = linedescr[lineno].bpl_info; -#endif - if (!line_was_doubled) { - if (line_in_border) - fill_line (lineno+1); - else - memcpy (gfxvidinfo.bufmem + (lineno+1)*gfxvidinfo.rowbytes, - gfxvidinfo.bufmem + lineno*gfxvidinfo.rowbytes, - gfxvidinfo.rowbytes); - } - do_flush_line (lineno+1); - } +#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]); } } - - pfield_calclinedma(); - +#endif if (++vpos == (maxvpos + (lof != 0))) { vpos = 0; vsync_handler(); } - - if (framecnt == 0) - { - lineno = vpos - minfirstline; - - if (correct_aspect) { + + if ((bplcon0 & 4) && gfx_requested_linedbl) { + interlace_seen = 1, penguins_enabled_thisframe = 0; + } + + if (framecnt == 0) { + lineno = vpos; + nln_how = 0; + if (gfx_requested_linedbl) { lineno *= 2; - if ((bpl_info.bplcon0 & 4) && !lof) { - lineno++; + nln_how = 1; + if (bplcon0 & 4) { + if (!lof) { + lineno++; + nln_how = 2; + } else { + nln_how = 3; + } } } - xlinebuffer = gfxvidinfo.bufmem + gfxvidinfo.rowbytes * lineno; - aga_lbufptr = aga_linebuf; next_lineno = lineno; - linetoscreen = 0; - line_in_border = 0; - pfield_fullline = 1; - pfield_linedone = 0; - pfield_lastpart_hpos = 0; + reset_decisions (); } - } void customreset(void) { - int i; + 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; - last_sprite = 0; - vpos = 0; + 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; - next_lineno = 0; max_diwstop = 0; if (needmousehack()) { @@ -2696,27 +4537,23 @@ void customreset(void) memset(spixstate, 0, sizeof(spixstate)); /*memset(blitcount, 0, sizeof(blitcount)); blitter debug */ - - for (i = 0; i < numscrlines*2; i++) { - linedescr[i].mnn = NULL; - linedescr[i].linedata_valid = 0; - linedescr[i].bpl_info.color_regs[0] = 0xFFFF; - linedescr[i].bplpt[0] = (CPTR)-1; + for (i = 0; i < (maxvpos+1)*2 + 1; i++) { + linestate[i] = LINE_UNDECIDED; } - 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); - bpl_info.bplcon4 = 0x11; /* Get AGA chipset into ECS compatibility mode */ - bpl_info.bplcon3 = 0xC00; + bplcon4 = 0x11; /* Get AGA chipset into ECS compatibility mode */ + bplcon3 = 0xC00; for(i = 0; i < ev_max; i++) { eventtab[i].active = 0; eventtab[i].oldcycles = 0; @@ -2751,9 +4588,73 @@ void customreset(void) eventtab[ev_sample].active = 0; } #endif - events_schedule(); + 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; + + 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; + } + } + } + + 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); + } + + line_drawn = (char *)malloc (gfxvidinfo.maxline); + + init_hardware_frame (); + init_drawing_frame (); + last_redraw_point = 0; + reset_decisions (); - init_frame(); #ifdef HAVE_GETTIMEOFDAY gettimeofday(&tv,NULL); seconds_base = tv.tv_sec; @@ -2783,6 +4684,8 @@ 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; } @@ -2790,9 +4693,23 @@ int intlev(void) void custom_init(void) { int num; + +#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(); - customreset(); + 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); @@ -2809,16 +4726,25 @@ void custom_init(void) } 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 } /* Custom chip memory bank */ static ULONG custom_lget(CPTR) REGPARAM; -static UWORD custom_wget(CPTR) REGPARAM; -static UBYTE custom_bget(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, UWORD) REGPARAM; -static void custom_bput(CPTR, UBYTE) REGPARAM; +static void custom_wput(CPTR, ULONG) REGPARAM; +static void custom_bput(CPTR, ULONG) REGPARAM; addrbank custom_bank = { default_alget, default_awget, @@ -2827,7 +4753,7 @@ addrbank custom_bank = { default_xlate, default_check }; -UWORD custom_wget(CPTR addr) +ULONG REGPARAM2 custom_wget(CPTR addr) { switch(addr & 0x1FE) { case 0x002: return DMACONR(); @@ -2838,6 +4764,7 @@ UWORD custom_wget(CPTR addr) case 0x00A: return JOY0DAT(); case 0x00C: return JOY1DAT(); + case 0x00E: return 0; /* CLXDAT */ case 0x010: return ADKCONR(); case 0x012: return POT0DAT(); @@ -2846,7 +4773,6 @@ UWORD custom_wget(CPTR addr) case 0x01A: return DSKBYTR(); case 0x01C: return INTENAR(); case 0x01E: return INTREQR(); - #if AGA_CHIPSET == 1 case 0x07C: return 0xF8; #elif defined ECS_DENISE @@ -2858,17 +4784,17 @@ UWORD custom_wget(CPTR addr) } } -UBYTE custom_bget(CPTR addr) +ULONG REGPARAM2 custom_bget(CPTR addr) { return custom_wget(addr & 0xfffe) >> (addr & 1 ? 0 : 8); } -ULONG custom_lget(CPTR addr) +ULONG REGPARAM2 custom_lget(CPTR addr) { return ((ULONG)custom_wget(addr & 0xfffe) << 16) | custom_wget((addr+2) & 0xfffe); } -void custom_wput(CPTR addr, UWORD value) +void REGPARAM2 custom_wput(CPTR addr, ULONG value) { addr &= 0x1FE; cregs[addr>>1] = value; @@ -2879,7 +4805,7 @@ void custom_wput(CPTR addr, UWORD value) 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; @@ -2990,22 +4916,22 @@ void custom_wput(CPTR addr, UWORD value) 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: + case 0x1BC: case 0x1BE: COLOR(value & 0xFFF, (addr & 0x3E) / 2); - break; - case 0x120: case 0x124: case 0x128: case 0x12C: + break; + case 0x120: case 0x124: case 0x128: case 0x12C: case 0x130: case 0x134: case 0x138: case 0x13C: SPRxPTH(value, (addr - 0x120) / 4); break; - case 0x122: case 0x126: case 0x12A: case 0x12E: + case 0x122: case 0x126: case 0x12A: case 0x12E: case 0x132: case 0x136: case 0x13A: case 0x13E: SPRxPTL(value, (addr - 0x122) / 4); break; - case 0x140: case 0x148: case 0x150: case 0x158: + case 0x140: case 0x148: case 0x150: case 0x158: case 0x160: case 0x168: case 0x170: case 0x178: SPRxPOS(value, (addr - 0x140) / 8); break; - case 0x142: case 0x14A: case 0x152: case 0x15A: + case 0x142: case 0x14A: case 0x152: case 0x15A: case 0x162: case 0x16A: case 0x172: case 0x17A: SPRxCTL(value, (addr - 0x142) / 8); break; @@ -3013,7 +4939,7 @@ void custom_wput(CPTR addr, UWORD value) case 0x164: case 0x16C: case 0x174: case 0x17C: SPRxDATA(value, (addr - 0x144) / 8); break; - case 0x146: case 0x14E: case 0x156: case 0x15E: + case 0x146: case 0x14E: case 0x156: case 0x15E: case 0x166: case 0x16E: case 0x176: case 0x17E: SPRxDATB(value, (addr - 0x146) / 8); break; @@ -3031,24 +4957,18 @@ void custom_wput(CPTR addr, UWORD value) } } -void custom_bput(CPTR addr, UBYTE value) +void REGPARAM2 custom_bput(CPTR addr, ULONG value) { - /* Yes, there are programs that do this. The programmers should be shot. - * This might actually work sometimes. */ - UWORD rval = value; - CPTR raddr = addr & 0x1FE; - if (addr & 1) { - rval |= cregs[raddr >> 1] & 0xFF00; - } else { - rval <<= 8; - rval |= cregs[raddr >> 1] & 0xFF; - } - custom_wput(raddr, rval); + 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); + if (!warned) + fprintf(stderr, "Byte put to custom register.\n"), warned++; } -void custom_lput(CPTR addr, ULONG value) +void REGPARAM2 custom_lput(CPTR addr, ULONG value) { custom_wput(addr & 0xfffe, value >> 16); custom_wput((addr+2) & 0xfffe, (UWORD)value); } -