|
|
1.1 root 1: /*
2: * UAE - The Un*x Amiga Emulator
1.1.1.3 root 3: *
1.1 root 4: * Custom chip emulation
1.1.1.3 root 5: *
1.1.1.4 root 6: * Copyright 1995-1998 Bernd Schmidt
1.1.1.2 root 7: * Copyright 1995 Alessandro Bissacco
1.1 root 8: */
9:
10: #include "sysconfig.h"
11: #include "sysdeps.h"
12:
13: #include <ctype.h>
14: #include <assert.h>
15:
16: #include "config.h"
17: #include "options.h"
1.1.1.3 root 18: #include "threaddep/penguin.h"
19: #include "uae.h"
20: #include "gensound.h"
21: #include "sounddep/sound.h"
1.1 root 22: #include "events.h"
23: #include "memory.h"
24: #include "custom.h"
1.1.1.2 root 25: #include "readcpu.h"
1.1 root 26: #include "newcpu.h"
27: #include "cia.h"
28: #include "disk.h"
29: #include "blitter.h"
30: #include "xwin.h"
1.1.1.3 root 31: #include "joystick.h"
32: #include "audio.h"
1.1 root 33: #include "keybuf.h"
34: #include "serial.h"
1.1.1.2 root 35: #include "osemu.h"
1.1.1.3 root 36: #include "autoconf.h"
37: #include "gui.h"
38: #include "picasso96.h"
1.1.1.4 root 39: #include "drawing.h"
1.1.1.2 root 40:
1.1.1.6 ! root 41: static unsigned int n_consecutive_skipped = 0;
! 42: static unsigned int total_skipped = 0;
! 43:
1.1.1.3 root 44: #define SPRITE_COLLISIONS
1.1.1.2 root 45:
1.1.1.3 root 46: /* Mouse and joystick emulation */
1.1.1.2 root 47:
1.1.1.3 root 48: int buttonstate[3];
49: static int mouse_x, mouse_y;
50: int joy0button, joy1button;
51: unsigned int joy0dir, joy1dir;
52:
53: /* Events */
1.1 root 54:
1.1.1.4 root 55: unsigned long int cycles, nextevent, is_lastline;
56: static int rpt_did_reset;
1.1.1.2 root 57: struct ev eventtab[ev_max];
58:
1.1.1.3 root 59: frame_time_t vsynctime, vsyncmintime;
60:
1.1.1.4 root 61: static int vpos;
62: static uae_u16 lof;
63: static int next_lineno;
64: static enum nln_how nextline_how;
65: static int lof_changed = 0;
1.1 root 66:
1.1.1.2 root 67: static const int dskdelay = 2; /* FIXME: ??? */
1.1 root 68:
1.1.1.3 root 69: static uae_u32 sprtaba[256],sprtabb[256];
70:
1.1.1.2 root 71: /*
72: * Hardware registers of all sorts.
73: */
1.1 root 74:
1.1.1.4 root 75: static void custom_wput_1 (int, uaecptr, uae_u32) REGPARAM;
76:
1.1 root 77: static int fmode;
78:
1.1.1.3 root 79: static uae_u16 cregs[256];
1.1 root 80:
1.1.1.3 root 81: uae_u16 intena,intreq;
82: uae_u16 dmacon;
83: uae_u16 adkcon; /* used by audio code */
1.1 root 84:
1.1.1.3 root 85: static uae_u32 cop1lc,cop2lc,copcon;
1.1 root 86:
1.1.1.2 root 87: /* This is but an educated guess. It seems to be correct, but this stuff
88: * isn't documented well. */
1.1.1.3 root 89: enum sprstate { SPR_stop, SPR_restart, SPR_waiting_start, SPR_waiting_stop };
90: static enum sprstate sprst[8];
91: static int spron[8];
92: static uaecptr sprpt[8];
1.1.1.2 root 93: static int sprxpos[8], sprvstart[8], sprvstop[8];
1.1 root 94:
1.1.1.6 ! root 95: static uae_u32 bpl1dat, bpl2dat, bpl3dat, bpl4dat, bpl5dat, bpl6dat, bpl7dat, bpl8dat;
! 96: static uae_s16 bpl1mod, bpl2mod;
1.1 root 97:
1.1.1.3 root 98: static uaecptr bplpt[8];
1.1.1.2 root 99: #ifndef SMART_UPDATE
100: static char *real_bplpt[8];
101: #endif
1.1 root 102:
103: /*static int blitcount[256]; blitter debug */
104:
1.1.1.2 root 105: static struct color_entry current_colors;
1.1.1.6 ! root 106: static unsigned int bplcon0, bplcon1, bplcon2, bplcon3, bplcon4;
1.1.1.3 root 107: static int nr_planes_from_bplcon0, corrected_nr_planes_from_bplcon0;
1.1.1.4 root 108: static unsigned int diwstrt, diwstop, diwhigh;
109: static int diwhigh_written;
110: static unsigned int ddfstrt, ddfstop;
1.1.1.2 root 111: static unsigned int sprdata[8], sprdatb[8], sprctl[8], sprpos[8];
112: static int sprarmed[8], sprite_last_drawn_at[8];
1.1.1.3 root 113: static int last_sprite_point, nr_armed;
114: static uae_u32 dskpt;
1.1.1.6 ! root 115: static uae_u16 dsklen, dsksync;
1.1.1.4 root 116: static int dsklength;
1.1 root 117:
1.1.1.4 root 118: /* The display and data fetch windows */
1.1 root 119:
1.1.1.4 root 120: enum diw_states
121: {
122: DIW_waiting_start, DIW_waiting_stop
123: };
1.1 root 124:
1.1.1.6 ! root 125: static int plffirstline, plflastline, plfstrt, plfstop, plflinelen;
! 126: int diwfirstword, diwlastword;
1.1.1.4 root 127: static enum diw_states diwstate, hdiwstate;
1.1 root 128:
1.1.1.4 root 129: /* Sprite collisions */
130: uae_u16 clxdat, clxcon;
131: int clx_sprmask;
132:
133: enum copper_states {
134: COP_stop,
135: COP_rdelay1, COP_read1, COP_read2,
136: COP_bltwait,
137: COP_wait1, COP_wait1b, COP_wait2,
138: };
139:
140: struct copper {
141: /* The current instruction words. */
142: unsigned int i1, i2;
143: enum copper_states state;
144: /* Instruction pointer. */
145: uaecptr ip;
146: int hpos, vpos, count;
147: unsigned int ignore_next;
148: unsigned int do_move;
149: enum diw_states vdiw;
150: };
151:
152: static struct copper cop_state;
1.1 root 153:
1.1.1.4 root 154: static void prepare_copper_1 (void);
155:
156: int dskdmaen; /* used in cia.c */
1.1 root 157:
158: /*
159: * Statistics
160: */
161:
1.1.1.2 root 162: /* Used also by bebox.cpp */
1.1.1.6 ! root 163: unsigned long int msecs = 0, frametime = 0, lastframetime = 0, timeframes = 0;
1.1 root 164: static unsigned long int seconds_base;
165: int bogusframe;
166:
1.1.1.4 root 167:
168: static int current_change_set;
169:
170: #ifdef OS_WITHOUT_MEMORY_MANAGEMENT
171: /* sam: Those arrays uses around 7Mb of BSS... That seems */
172: /* too much for AmigaDOS (uae crashes as soon as one loads */
173: /* it. So I use a different strategy here (realloc the */
174: /* arrays when needed. That strategy might be usefull for */
175: /* computer with low memory. */
176: struct sprite_draw *sprite_positions[2];
177: struct color_change *color_changes[2];
178: struct delay_change *delay_changes;
179: static int max_sprite_draw = 400;
180: static int delta_sprite_draw = 0;
181: static int max_color_change = 400;
182: static int delta_color_change = 0;
183: static int max_delay_change = 100;
184: static int delta_delay_change = 0;
185: #else
186: struct sprite_draw sprite_positions[2][MAX_REG_CHANGE];
187: struct color_change color_changes[2][MAX_REG_CHANGE];
188: /* We don't remember those across frames, that would be too much effort.
189: * We simply redraw the line whenever we see one of these. */
190: struct delay_change delay_changes[MAX_REG_CHANGE];
191: #endif
192:
193: struct decision line_decisions[2 * (maxvpos+1) + 1];
194: struct draw_info line_drawinfo[2][2 * (maxvpos+1) + 1];
195: struct color_entry color_tables[2][(maxvpos+1) * 2];
196:
197: struct sprite_draw *curr_sprite_positions, *prev_sprite_positions;
198: struct color_change *curr_color_changes, *prev_color_changes;
199: struct draw_info *curr_drawinfo, *prev_drawinfo;
200: struct color_entry *curr_color_tables, *prev_color_tables;
201:
202: static int next_color_change, next_sprite_draw, next_delay_change;
203: static int next_color_entry, remembered_color_entry;
204: static int color_src_match, color_dest_match, color_compare_result;
205:
206: /* These few are only needed during/at the end of the scanline, and don't
207: * have to be remembered. */
208: static int decided_bpl1mod, decided_bpl2mod, decided_nr_planes, decided_hires;
209:
1.1.1.5 root 210: static char thisline_changed;
211:
1.1.1.4 root 212:
213: #ifdef SMART_UPDATE
1.1.1.5 root 214: #define MARK_LINE_CHANGED do { thisline_changed = 1; } while (0)
1.1.1.4 root 215: #else
1.1.1.5 root 216: #define MARK_LINE_CHANGED do { ; } while (0)
1.1.1.4 root 217: #endif
218:
219: static struct decision thisline_decision;
220: static int modulos_added, plane_decided, color_decided, very_broken_program;
221:
1.1 root 222: /*
223: * helper functions
224: */
225:
1.1.1.4 root 226: int rpt_available = 0;
1.1.1.3 root 227:
1.1.1.4 root 228: void reset_frame_rate_hack (void)
229: {
230: if (currprefs.m68k_speed != -1)
231: return;
232:
233: if (! rpt_available) {
234: currprefs.m68k_speed = 0;
235: return;
236: }
1.1.1.3 root 237:
1.1.1.4 root 238: rpt_did_reset = 1;
239: is_lastline = 0;
240: vsyncmintime = read_processor_time() + vsynctime;
241: write_log ("Resetting frame rate hack\n");
242: }
1.1 root 243:
1.1.1.4 root 244: static __inline__ void prepare_copper (void)
1.1 root 245: {
1.1.1.4 root 246: if (cop_state.vpos > vpos
247: || cop_state.state == COP_stop)
248: {
249: eventtab[ev_copper].active = 0;
250: return;
251: }
252: prepare_copper_1 ();
1.1.1.3 root 253: }
254:
255: void check_prefs_changed_custom (void)
256: {
257: currprefs.framerate = changed_prefs.framerate;
258: /* Not really the right place... */
1.1.1.6 ! root 259: if (currprefs.jport0 != changed_prefs.jport0
! 260: || currprefs.jport1 != changed_prefs.jport1) {
! 261: currprefs.jport0 = changed_prefs.jport0;
! 262: currprefs.jport1 = changed_prefs.jport1;
1.1.1.3 root 263: joystick_setting_changed ();
1.1 root 264: }
1.1.1.3 root 265: currprefs.immediate_blits = changed_prefs.immediate_blits;
266: currprefs.blits_32bit_enabled = changed_prefs.blits_32bit_enabled;
267:
1.1 root 268: }
269:
1.1.1.3 root 270: static __inline__ void setclr (uae_u16 *p, uae_u16 val)
1.1 root 271: {
1.1.1.6 ! root 272: if (val & 0x8000)
1.1 root 273: *p |= val & 0x7FFF;
1.1.1.6 ! root 274: else
1.1 root 275: *p &= ~val;
276: }
277:
1.1.1.3 root 278: __inline__ int current_hpos (void)
1.1 root 279: {
280: return cycles - eventtab[ev_hsync].oldcycles;
281: }
282:
1.1.1.3 root 283: static __inline__ uae_u8 *pfield_xlateptr (uaecptr plpt, int bytecount)
1.1.1.2 root 284: {
1.1.1.4 root 285: if (!chipmem_bank.check (plpt, bytecount)) {
1.1.1.2 root 286: static int count = 0;
1.1.1.3 root 287: if (!count)
288: count++, write_log ("Warning: Bad playfield pointer\n");
1.1.1.2 root 289: return NULL;
290: }
1.1.1.4 root 291: return chipmem_bank.xlateaddr (plpt);
1.1.1.2 root 292: }
293:
1.1.1.6 ! root 294: static __inline__ void docols (struct color_entry *colentry)
1.1.1.3 root 295: {
296: #if AGA_CHIPSET == 0
297: int i;
298: for (i = 0; i < 32; i++) {
299: int v = colentry->color_regs[i];
300: if (v < 0 || v > 4095)
301: continue;
302: colentry->acolors[i] = xcolors[v];
303: }
304: #endif
305: }
306:
307: void notice_new_xcolors (void)
308: {
309: int i;
310:
311: docols(¤t_colors);
1.1.1.4 root 312: /* docols(&colors_for_drawing);*/
1.1.1.3 root 313: for (i = 0; i < (maxvpos+1)*2; i++) {
314: docols(color_tables[0]+i);
315: docols(color_tables[1]+i);
316: }
317: }
318:
1.1.1.4 root 319: static void do_sprites (int currvp, int currhp);
1.1.1.2 root 320:
321: static void remember_ctable (void)
322: {
323: if (remembered_color_entry == -1) {
324: /* The colors changed since we last recorded a color map. Record a
325: * new one. */
326: memcpy (curr_color_tables + next_color_entry, ¤t_colors, sizeof current_colors);
327: remembered_color_entry = next_color_entry++;
328: }
329: thisline_decision.ctable = remembered_color_entry;
330: if (color_src_match == -1 || color_dest_match != remembered_color_entry
331: || line_decisions[next_lineno].ctable != color_src_match)
332: {
333: /* The remembered comparison didn't help us - need to compare again. */
334: int oldctable = line_decisions[next_lineno].ctable;
335: int changed = 0;
336:
1.1.1.3 root 337: if (oldctable == -1) {
1.1.1.2 root 338: changed = 1;
339: color_src_match = color_dest_match = -1;
340: } else {
1.1.1.3 root 341: color_compare_result = memcmp (&prev_color_tables[oldctable].color_regs,
342: ¤t_colors.color_regs,
343: sizeof current_colors.color_regs) != 0;
344: if (color_compare_result)
345: changed = 1;
346: color_src_match = oldctable;
1.1.1.2 root 347: color_dest_match = remembered_color_entry;
1.1.1.3 root 348: }
1.1.1.5 root 349: thisline_changed |= changed;
1.1.1.3 root 350: } else {
351: /* We know the result of the comparison */
352: if (color_compare_result)
1.1.1.5 root 353: thisline_changed = 1;
1.1.1.3 root 354: }
355: }
356:
357: static void remember_ctable_for_border (void)
358: {
359: remember_ctable ();
1.1.1.2 root 360: }
361:
1.1.1.3 root 362: /* Called to determine the state of the horizontal display window state
363: * machine at the current position. It might have changed since we last
364: * checked. */
1.1.1.4 root 365: static void decide_diw (int hpos)
1.1.1.2 root 366: {
1.1.1.3 root 367: if (hdiwstate == DIW_waiting_start && thisline_decision.diwfirstword == -1
1.1.1.4 root 368: && PIXEL_XPOS (hpos) >= diwfirstword)
1.1.1.3 root 369: {
1.1.1.2 root 370: thisline_decision.diwfirstword = diwfirstword;
1.1.1.3 root 371: hdiwstate = DIW_waiting_stop;
1.1.1.2 root 372: /* Decide playfield delays only at DIW start, because they don't matter before and
373: * some programs change them after DDF start but before DIW start. */
374: thisline_decision.bplcon1 = bplcon1;
375: if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword)
1.1.1.5 root 376: MARK_LINE_CHANGED;
1.1.1.2 root 377: thisline_decision.diwlastword = -1;
378: }
1.1.1.3 root 379: if (hdiwstate == DIW_waiting_stop && thisline_decision.diwlastword == -1
1.1.1.4 root 380: && PIXEL_XPOS (hpos) >= diwlastword)
1.1.1.3 root 381: {
1.1.1.2 root 382: thisline_decision.diwlastword = diwlastword;
1.1.1.3 root 383: hdiwstate = DIW_waiting_start;
1.1.1.2 root 384: if (thisline_decision.diwlastword != line_decisions[next_lineno].diwlastword)
1.1.1.5 root 385: MARK_LINE_CHANGED;
1.1.1.2 root 386: }
387: }
388:
1.1.1.4 root 389: /* Called when we know that the line is not in the border and we want to draw
390: * it. The data fetch starting position and length are passed as parameters. */
1.1.1.2 root 391: static __inline__ void decide_as_playfield (int startpos, int len)
392: {
393: thisline_decision.which = 1;
394:
395: /* The latter condition might be able to happen in interlaced frames. */
396: if (vpos >= minfirstline && (thisframe_first_drawn_line == -1 || vpos < thisframe_first_drawn_line))
397: thisframe_first_drawn_line = vpos;
398: thisframe_last_drawn_line = vpos;
399:
400: thisline_decision.plfstrt = startpos;
401: thisline_decision.plflinelen = len;
402:
403: /* These are for comparison. */
404: thisline_decision.bplcon0 = bplcon0;
405: thisline_decision.bplcon2 = bplcon2;
406: #if AGA_CHIPSET == 1
407: thisline_decision.bplcon4 = bplcon4;
408: #endif
409:
410: #ifdef SMART_UPDATE
411: if (line_decisions[next_lineno].plfstrt != thisline_decision.plfstrt
412: || line_decisions[next_lineno].plflinelen != thisline_decision.plflinelen
413: || line_decisions[next_lineno].bplcon0 != thisline_decision.bplcon0
414: || line_decisions[next_lineno].bplcon2 != thisline_decision.bplcon2
415: #if AGA_CHIPSET == 1
416: || line_decisions[next_lineno].bplcon4 != thisline_decision.bplcon4
417: #endif
418: )
419: #endif /* SMART_UPDATE */
1.1.1.5 root 420: thisline_changed = 1;
1.1.1.2 root 421: }
422:
1.1.1.3 root 423: /* Called when we already decided whether the line is playfield or border,
424: * but are no longer sure that this was such a good idea. This can make a
1.1.1.4 root 425: * difference only for very badly written software.
426: *
427: * We try to handle two cases:
428: * a) bitplane DMA gets turned off during a line which was decided as plane
429: * b) bitplane DMA turns on for all or some planes, either after the line was
430: * decided as border, or after we made an incorrect nr_planes decision.
431: *
432: * Examples of programs which need this: Majic 12 "Ray of Hope 2" demo
433: * (final large scrolltext) and Masterblazer (menu screen).
434: *
435: * There's no hope of catching all cases; we'd need a cycle based emulation
436: * for that. I'm getting increasingly convinced that that would be a good
437: * idea.
438: */
439: static int broken_plane_sub[8];
440:
441: static void init_broken_program (void)
1.1.1.2 root 442: {
1.1.1.4 root 443: int i;
444: if (very_broken_program)
445: return;
446: very_broken_program = 1;
447: for (i = 0; i < 8; i++)
448: broken_plane_sub[i] = i >= decided_nr_planes || thisline_decision.which != 1 ? -1 : 0;
449: }
450:
451: static void adjust_broken_program (int hpos)
452: {
453: int tmp1, tmp2;
454: int i;
455: if (decided_hires) {
456: tmp1 = hpos & 3;
457: tmp2 = hpos & ~3;
458: } else {
459: tmp1 = hpos & 7;
460: tmp2 = hpos & ~7;
461: }
462: for (i = 0; i < decided_nr_planes; i++) {
463: if (broken_plane_sub[i] != -1)
464: continue;
465: if (decided_hires) {
466: broken_plane_sub[i] = (tmp2 - thisline_decision.plfstrt) / 4 * 2;
467: switch (i) {
468: case 3: broken_plane_sub[i] += 2; break; /* @@@ break was missing */
469: case 2: broken_plane_sub[i] += (tmp1 >= 3 ? 2 : 0); break;
470: case 1: broken_plane_sub[i] += (tmp1 >= 2 ? 2 : 0); break;
471: case 0: break;
472: }
473: } else {
474: broken_plane_sub[i] = (tmp2 - thisline_decision.plfstrt) / 8 * 2;
475: switch (i) {
476: case 5: broken_plane_sub[i] += (tmp1 >= 3 ? 2 : 0); break;
477: case 4: broken_plane_sub[i] += (tmp1 >= 7 ? 2 : 0); break;
478: case 3: broken_plane_sub[i] += (tmp1 >= 2 ? 2 : 0); break;
479: case 2: broken_plane_sub[i] += (tmp1 >= 6 ? 2 : 0); break;
480: case 1: broken_plane_sub[i] += (tmp1 >= 4 ? 2 : 0); break;
481: case 0: break;
482: }
483: }
484: }
485: }
1.1.1.2 root 486:
1.1.1.4 root 487: static __inline__ void post_decide_line (int hpos)
488: {
489: if (thisline_decision.which == 1
490: && hpos < thisline_decision.plfstrt + thisline_decision.plflinelen
1.1.1.6 ! root 491: && (GET_PLANES (bplcon0) == 0 || !dmaen (DMA_BITPLANE)))
1.1.1.2 root 492: {
493: /* This is getting gross... */
1.1.1.4 root 494: thisline_decision.plflinelen = hpos - thisline_decision.plfstrt;
1.1.1.2 root 495: thisline_decision.plflinelen &= 7;
496: if (thisline_decision.plflinelen == 0)
497: thisline_decision.which = -1;
498: /* ... especially THIS! */
499: modulos_added = 1;
500: return;
501: }
1.1.1.3 root 502:
1.1.1.4 root 503: if (diwstate == DIW_waiting_start
1.1.1.6 ! root 504: || GET_PLANES (bplcon0) == 0
1.1.1.4 root 505: || !dmaen (DMA_BITPLANE)
506: || hpos >= thisline_decision.plfstrt + thisline_decision.plflinelen)
1.1.1.2 root 507: return;
1.1.1.4 root 508:
509: if (thisline_decision.which == 1
510: && hpos > thisline_decision.plfstrt
511: && decided_nr_planes < nr_planes_from_bplcon0)
512: {
513: decided_hires = (bplcon0 & 0x8000) == 0x8000;
514: init_broken_program ();
515: decided_nr_planes = nr_planes_from_bplcon0;
1.1.1.6 ! root 516: thisline_decision.bplcon0 &= 0xFEF;
! 517: thisline_decision.bplcon0 |= bplcon0 & 0xF010;
1.1.1.4 root 518: adjust_broken_program (hpos);
1.1.1.5 root 519: MARK_LINE_CHANGED; /* Play safe. */
1.1.1.4 root 520: return;
521: }
522:
523: if (thisline_decision.which != -1)
524: return;
525:
1.1.1.2 root 526: #if 0 /* Can't warn because Kickstart 1.3 does it at reset time ;-) */
1.1.1.4 root 527: {
528: static int warned = 0;
529: if (!warned) {
530: write_log ("That program you are running is _really_ broken.\n");
531: warned = 1;
532: }
1.1.1.2 root 533: }
534: #endif
1.1.1.4 root 535: init_broken_program ();
1.1.1.2 root 536:
1.1.1.3 root 537: decided_nr_planes = nr_planes_from_bplcon0;
1.1.1.6 ! root 538: thisline_decision.bplcon0 &= 0xFEF;
! 539: thisline_decision.bplcon0 |= bplcon0 & 0xF010;
1.1.1.2 root 540:
1.1.1.5 root 541: MARK_LINE_CHANGED; /* Play safe. */
1.1.1.4 root 542: decide_as_playfield (plfstrt, plflinelen);
543: adjust_broken_program (hpos);
1.1.1.2 root 544: }
545:
1.1.1.4 root 546: static void decide_line_1 (int hpos)
1.1.1.2 root 547: {
1.1.1.4 root 548: do_sprites (vpos, hpos);
1.1.1.2 root 549:
550: /* Surprisingly enough, this seems to be correct here - putting this into
551: * decide_diw() results in garbage. */
552: if (diwstate == DIW_waiting_start && vpos == plffirstline) {
553: diwstate = DIW_waiting_stop;
554: }
555: if (diwstate == DIW_waiting_stop && vpos == plflastline) {
556: diwstate = DIW_waiting_start;
557: }
558:
559: if (framecnt != 0) {
560: /* thisline_decision.which = -2; This doesn't do anything but hurt, I think. */
561: return;
562: }
1.1.1.3 root 563:
564: if (!dmaen(DMA_BITPLANE) || diwstate == DIW_waiting_start || nr_planes_from_bplcon0 == 0) {
1.1.1.2 root 565: /* We don't want to draw this one. */
566: thisline_decision.which = -1;
1.1.1.3 root 567: thisline_decision.plfstrt = plfstrt;
568: thisline_decision.plflinelen = plflinelen;
1.1.1.2 root 569: return;
570: }
571:
572: decided_hires = (bplcon0 & 0x8000) == 0x8000;
1.1.1.3 root 573: decided_nr_planes = nr_planes_from_bplcon0;
1.1.1.2 root 574: #if 0
1.1.1.3 root 575: /* The blitter gets slower if there's high bitplane activity.
1.1.1.2 root 576: * @@@ but the values must be different. FIXME */
577: if (bltstate != BLT_done
578: && ((decided_hires && decided_nr_planes > 2)
579: || (!decided_hires && decided_nr_planes > 4)))
580: {
581: int pl = decided_nr_planes;
582: unsigned int n = (eventtab[ev_blitter].evtime-cycles);
583: if (n > plflinelen)
584: n = plflinelen;
585: n >>= 1;
586: if (decided_hires)
587: pl <<= 1;
588: if (pl == 8)
589: eventtab[ev_blitter].evtime += plflinelen;
590: else if (pl == 6)
591: eventtab[ev_blitter].evtime += n*2;
592: else if (pl == 5)
593: eventtab[ev_blitter].evtime += n*3/2;
594: events_schedule();
595: }
596: #endif
597: decide_as_playfield (plfstrt, plflinelen);
598: }
599:
1.1.1.3 root 600: /* Main entry point for deciding how to draw a line. May either do
601: * nothing, decide the line as border, or decide the line as playfield. */
1.1.1.4 root 602: static __inline__ void decide_line (int hpos)
1.1.1.2 root 603: {
1.1.1.4 root 604: if (thisline_decision.which == 0 && hpos >= plfstrt)
605: decide_line_1 (hpos);
1.1.1.2 root 606: }
607:
1.1.1.3 root 608: /* Called when a color is about to be changed (write to a color register),
609: * but the new color has not been entered into the table yet. */
1.1.1.4 root 610: static void record_color_change (int hpos, int regno, unsigned long value)
1.1.1.3 root 611: {
612: /* Early positions don't appear on-screen. */
1.1.1.4 root 613: if (framecnt != 0 || vpos < minfirstline || hpos < 0x18
1.1.1.3 root 614: /*|| currprefs.emul_accuracy == 0*/)
615: return;
616:
1.1.1.4 root 617: decide_diw (hpos);
618: decide_line (hpos);
1.1.1.3 root 619:
620: /* See if we can record the color table, but have not done so yet.
621: * @@@ There might be a minimal performance loss in case someone changes
622: * a color exactly at the start of the DIW. I don't think it can actually
623: * fail to work even in this case, but I'm not 100% sure.
624: * @@@ There might be a slightly larger performance loss if we're drawing
625: * this line as border... especially if there are changes in colors != 0
626: * we might end up setting line_changed for no reason. FIXME */
627: if (thisline_decision.diwfirstword >= 0
628: && thisline_decision.which != 0)
629: {
630: if (thisline_decision.ctable == -1)
631: remember_ctable ();
632: }
633:
634: /* Changes outside the DIW can be ignored if the modified color is not the
635: * background color, or if the accuracy is < 2. */
636: if ((regno != 0 || currprefs.emul_accuracy < 2)
637: && (diwstate == DIW_waiting_start || thisline_decision.diwfirstword < 0
638: || thisline_decision.diwlastword >= 0
639: || thisline_decision.which == 0
1.1.1.4 root 640: || (thisline_decision.which == 1 && hpos >= thisline_decision.plfstrt + thisline_decision.plflinelen)))
1.1.1.3 root 641: return;
642:
643: /* If the border is changed the first time before the DIW, record the
644: * original starting border value. */
645: if (regno == 0 && thisline_decision.color0 == 0xFFFFFFFFul && thisline_decision.diwfirstword < 0) {
646: thisline_decision.color0 = current_colors.color_regs[0];
647: if (line_decisions[next_lineno].color0 != value)
1.1.1.5 root 648: thisline_changed = 1;
1.1.1.3 root 649: }
650: /* Anything else gets recorded in the color_changes table. */
651: #ifdef OS_WITHOUT_MEMORY_MANAGEMENT
1.1.1.4 root 652: if (next_color_change >= max_color_change) {
1.1.1.3 root 653: ++delta_color_change;
654: return;
655: }
656: #endif
1.1.1.4 root 657: curr_color_changes[next_color_change].linepos = hpos;
1.1.1.3 root 658: curr_color_changes[next_color_change].regno = regno;
659: curr_color_changes[next_color_change++].value = value;
660: }
661:
662: /* Stupid hack to get some Sanity demos working. */
1.1.1.4 root 663: static void decide_delay (int hpos)
1.1.1.2 root 664: {
665: static int warned;
666:
667: /* Don't do anything if we're outside the DIW. */
668: if (thisline_decision.diwfirstword == -1 || thisline_decision.diwlastword > 0)
669: return;
1.1.1.4 root 670: decide_line (hpos);
1.1.1.2 root 671: /* Half-hearted attempt to get things right even when post_decide_line() changes
672: * the decision afterwards. */
673: if (thisline_decision.which != 1) {
674: thisline_decision.bplcon1 = bplcon1;
675: return;
676: }
677: /* @@@ Could check here for DDF stopping earlier than DIW */
1.1.1.3 root 678:
679: #ifdef OS_WITHOUT_MEMORY_MANAGEMENT
1.1.1.4 root 680: if (next_delay_change >= max_delay_change) {
1.1.1.3 root 681: ++delta_delay_change;
682: return;
683: }
684: #endif
1.1.1.4 root 685: delay_changes[next_delay_change].linepos = hpos;
1.1.1.2 root 686: delay_changes[next_delay_change++].value = bplcon1;
687: if (!warned) {
688: warned = 1;
1.1.1.3 root 689: write_log ("Program is torturing BPLCON1.\n");
1.1.1.2 root 690: }
691: }
692:
693: /*
1.1.1.4 root 694: * The decision which bitplane pointers to use is not made at plfstrt, since
1.1.1.2 root 695: * data fetch does not start for all planes at this point. Therefore, we wait
696: * for the end of the ddf area or the first write to any of the bitplane
697: * pointer registers, whichever comes last, before we decide which plane pointers
698: * to use.
699: * Call decide_line() before this function.
700: */
1.1.1.4 root 701: static void decide_plane (int hpos)
1.1.1.2 root 702: {
703: int i, bytecount;
704:
705: if (framecnt != 0 || plane_decided)
706: return;
707:
708: if (decided_nr_planes == -1 /* Still undecided */
1.1.1.4 root 709: || hpos < thisline_decision.plfstrt + thisline_decision.plflinelen)
1.1.1.2 root 710: return;
711:
712: plane_decided = 1;
713:
714: bytecount = plflinelen / (decided_hires ? 4 : 8) * 2;
715:
716: if (bytecount > MAX_WORDS_PER_LINE * 2) {
717: /* Can't happen. */
718: static int warned = 0;
719: if (!warned)
1.1.1.3 root 720: write_log ("Mysterious bug in decide_plane(). Please report.\n");
1.1.1.2 root 721: bytecount = 0;
722: }
723: if (!very_broken_program) {
1.1.1.3 root 724: uae_u8 *dataptr = line_data[next_lineno];
1.1.1.2 root 725: for (i = 0; i < decided_nr_planes; i++, dataptr += MAX_WORDS_PER_LINE*2) {
1.1.1.3 root 726: uaecptr pt = bplpt[i];
727: uae_u8 *real_ptr = pfield_xlateptr(pt, bytecount);
1.1.1.2 root 728: if (real_ptr == NULL)
729: real_ptr = pfield_xlateptr(0, 0);
730: #ifdef SMART_UPDATE
1.1.1.3 root 731: #if 1
1.1.1.5 root 732: if (thisline_changed)
1.1.1.2 root 733: memcpy (dataptr, real_ptr, bytecount);
1.1.1.3 root 734: else
735: #endif
1.1.1.5 root 736: thisline_changed |= memcmpy (dataptr, real_ptr, bytecount);
1.1.1.2 root 737: #else
738: real_bplpt[i] = real_ptr;
739: #endif
740: }
1.1 root 741: } else {
1.1.1.3 root 742: uae_u8 *dataptr = line_data[next_lineno];
1.1.1.2 root 743: for (i = 0; i < decided_nr_planes; i++, dataptr += MAX_WORDS_PER_LINE*2) {
1.1.1.3 root 744: uaecptr pt = bplpt[i];
745: uae_u8 *real_ptr;
746:
1.1.1.4 root 747: pt -= broken_plane_sub[i];
748: real_ptr = pfield_xlateptr (pt, bytecount);
1.1.1.2 root 749: if (real_ptr == NULL)
750: real_ptr = pfield_xlateptr(0, 0);
751: #ifdef SMART_UPDATE
1.1.1.5 root 752: if (!thisline_changed)
753: thisline_changed |= memcmpy (dataptr, real_ptr, bytecount);
1.1.1.2 root 754: else
755: memcpy (dataptr, real_ptr, bytecount);
756: #else
757: real_bplpt[i] = real_ptr;
758: #endif
759: }
760: }
761: }
762:
763: /*
764: * Called from the BPLxMOD routines, after a new value has been written.
765: * This routine decides whether the new value is already relevant for the
766: * current line.
767: */
1.1.1.4 root 768: static void decide_modulos (int hpos)
1.1.1.2 root 769: {
770: /* All this effort just for the Sanity WOC demo... */
1.1.1.4 root 771: decide_line (hpos);
772: if (decided_nr_planes != -1 && hpos >= thisline_decision.plfstrt + thisline_decision.plflinelen)
1.1.1.2 root 773: return;
774: decided_bpl1mod = bpl1mod;
775: decided_bpl2mod = bpl2mod;
776: }
777:
778: /*
1.1.1.3 root 779: * Add the modulos to the bitplane pointers if data fetch is already
780: * finished for the current line.
781: * Call decide_plane() before calling this, so that we won't use the
782: * new values of the plane pointers for the current line.
1.1.1.2 root 783: */
1.1.1.4 root 784: static void do_modulos (int hpos)
1.1.1.2 root 785: {
786: /* decided_nr_planes is != -1 if this line should be drawn by the
1.1.1.3 root 787: * display hardware, regardless of whether it fits on the emulated screen.
1.1.1.2 root 788: */
789: if (decided_nr_planes != -1 && plane_decided && !modulos_added
1.1.1.4 root 790: && hpos >= thisline_decision.plfstrt + thisline_decision.plflinelen)
1.1.1.2 root 791: {
792: int bytecount = thisline_decision.plflinelen / (decided_hires ? 4 : 8) * 2;
793: int add1 = bytecount + decided_bpl1mod;
794: int add2 = bytecount + decided_bpl2mod;
795:
796: if (!very_broken_program) {
797: switch (decided_nr_planes) {
798: case 8: bplpt[7] += add2;
799: case 7: bplpt[6] += add1;
800: case 6: bplpt[5] += add2;
801: case 5: bplpt[4] += add1;
802: case 4: bplpt[3] += add2;
803: case 3: bplpt[2] += add1;
804: case 2: bplpt[1] += add2;
805: case 1: bplpt[0] += add1;
806: }
1.1.1.4 root 807: } else switch (decided_nr_planes) {
808: case 8: bplpt[7] += add2 - broken_plane_sub[7];
809: case 7: bplpt[6] += add1 - broken_plane_sub[6];
810: case 6: bplpt[5] += add2 - broken_plane_sub[5];
811: case 5: bplpt[4] += add1 - broken_plane_sub[4];
812: case 4: bplpt[3] += add2 - broken_plane_sub[3];
813: case 3: bplpt[2] += add1 - broken_plane_sub[2];
814: case 2: bplpt[1] += add2 - broken_plane_sub[1];
815: case 1: bplpt[0] += add1 - broken_plane_sub[0];
1.1.1.2 root 816: }
817:
818: modulos_added = 1;
819: }
820: }
821:
1.1.1.3 root 822: static __inline__ void record_sprite (int spr, int sprxp)
1.1.1.2 root 823: {
1.1.1.3 root 824: int pos = next_sprite_draw;
825: unsigned int data, datb;
1.1.1.2 root 826:
1.1.1.3 root 827: #ifdef OS_WITHOUT_MEMORY_MANAGEMENT
828: if(pos >= max_sprite_draw) {
829: ++delta_sprite_draw;
1.1.1.2 root 830: return;
1.1.1.3 root 831: }
832: #endif
833: data = sprdata[spr];
834: datb = sprdatb[spr];
1.1.1.2 root 835:
1.1.1.3 root 836: /* XXX FIXME, this isn't very clever, but it might do */
837: for (;;) {
838: if (pos == curr_drawinfo[next_lineno].first_sprite_draw)
839: break;
840: if (curr_sprite_positions[pos-1].linepos < sprxp)
841: break;
842: if (curr_sprite_positions[pos-1].linepos == sprxp
843: && curr_sprite_positions[pos-1].num > spr)
844: break;
845: printf("Foo\n");
846: pos--;
847: }
848: if (pos != next_sprite_draw) {
849: int pos2 = next_sprite_draw;
850: while (pos2 != pos) {
851: curr_sprite_positions[pos2] = curr_sprite_positions[pos2-1];
852: pos2--;
853: }
854: }
855: curr_sprite_positions[pos].linepos = sprxp;
856: curr_sprite_positions[pos].num = spr;
857: curr_sprite_positions[pos].ctl = sprctl[spr];
858: curr_sprite_positions[pos].datab = ((sprtaba[data & 0xFF] << 16) | sprtaba[data >> 8]
859: | (sprtabb[datb & 0xFF] << 16) | sprtabb[datb >> 8]);
860: next_sprite_draw++;
861: }
1.1.1.2 root 862:
1.1.1.4 root 863: static void decide_sprites (int hpos)
1.1.1.3 root 864: {
865: int nrs[8], posns[8], count, i;
1.1.1.4 root 866: int point = PIXEL_XPOS (hpos);
1.1.1.2 root 867:
1.1.1.4 root 868: if (framecnt != 0 || hpos < 0x14 || nr_armed == 0 || point == last_sprite_point)
1.1.1.2 root 869: return;
870:
1.1.1.4 root 871: decide_diw (hpos);
872: decide_line (hpos);
1.1.1.3 root 873:
874: if (thisline_decision.which != 1)
1.1.1.2 root 875: return;
876:
1.1.1.3 root 877: count = 0;
878: for (i = 0; i < 8; i++) {
879: int sprxp = sprxpos[i];
880: int j, bestp;
1.1.1.2 root 881:
1.1.1.3 root 882: if (!sprarmed[i] || sprxp < 0 || sprxp > point || last_sprite_point >= sprxp)
883: continue;
884: if ((thisline_decision.diwfirstword >= 0 && sprxp + sprite_width < thisline_decision.diwfirstword)
885: || (thisline_decision.diwlastword >= 0 && sprxp > thisline_decision.diwlastword))
886: continue;
1.1.1.2 root 887:
1.1.1.3 root 888: for (bestp = 0; bestp < count; bestp++) {
889: if (posns[bestp] > sprxp)
890: break;
891: if (posns[bestp] == sprxp && nrs[bestp] < i)
892: break;
893: }
894: for (j = count; j > bestp; j--) {
895: posns[j] = posns[j-1];
896: nrs[j] = nrs[j-1];
897: }
898: posns[j] = sprxp;
899: nrs[j] = i;
900: count++;
901: }
902: for (i = 0; i < count; i++)
1.1.1.4 root 903: record_sprite (nrs[i], posns[i]);
1.1.1.3 root 904: last_sprite_point = point;
1.1.1.2 root 905: }
906:
1.1.1.3 root 907: /* End of a horizontal scan line. Finish off all decisions that were not
908: * made yet. */
1.1.1.2 root 909: static void finish_decisions (void)
910: {
911: struct draw_info *dip;
912: struct draw_info *dip_old;
913: struct decision *dp;
914: int changed;
1.1.1.4 root 915: int hpos = current_hpos ();
1.1.1.2 root 916:
917: if (framecnt != 0)
918: return;
919:
1.1.1.4 root 920: decide_diw (hpos);
1.1.1.2 root 921: if (thisline_decision.which == 0)
1.1.1.4 root 922: decide_line_1 (hpos);
1.1.1.3 root 923:
924: /* Large DIWSTOP values can cause the stop position never to be
925: * reached, so the state machine always stays in the same state and
926: * there's a more-or-less full-screen DIW. */
927: if (hdiwstate == DIW_waiting_stop) {
928: thisline_decision.diwlastword = max_diwlastword;
929: if (thisline_decision.diwlastword != line_decisions[next_lineno].diwlastword)
1.1.1.5 root 930: MARK_LINE_CHANGED;
1.1.1.3 root 931: }
932:
1.1.1.2 root 933: if (line_decisions[next_lineno].which != thisline_decision.which)
1.1.1.5 root 934: thisline_changed = 1;
1.1.1.4 root 935: decide_plane (hpos);
1.1.1.2 root 936:
937: dip = curr_drawinfo + next_lineno;
938: dip_old = prev_drawinfo + next_lineno;
939: dp = line_decisions + next_lineno;
1.1.1.5 root 940: changed = thisline_changed;
1.1.1.2 root 941:
942: if (thisline_decision.which == 1) {
1.1.1.4 root 943: record_diw_line (diwfirstword, diwlastword);
1.1.1.2 root 944:
1.1.1.4 root 945: decide_sprites (hpos);
1.1.1.3 root 946:
1.1.1.2 root 947: if (thisline_decision.bplcon1 != line_decisions[next_lineno].bplcon1)
948: changed = 1;
949: }
950:
951: dip->last_color_change = next_color_change;
952: dip->last_delay_change = next_delay_change;
953: dip->last_sprite_draw = next_sprite_draw;
1.1.1.3 root 954:
955: if (thisline_decision.ctable == -1) {
956: if (thisline_decision.which == 1)
957: remember_ctable ();
958: else
959: remember_ctable_for_border ();
960: }
1.1.1.2 root 961: if (thisline_decision.which == -1 && thisline_decision.color0 == 0xFFFFFFFFul)
962: thisline_decision.color0 = current_colors.color_regs[0];
963:
964: dip->nr_color_changes = next_color_change - dip->first_color_change;
965: dip->nr_sprites = next_sprite_draw - dip->first_sprite_draw;
966:
967: if (dip->first_delay_change != dip->last_delay_change)
968: changed = 1;
969: if (!changed
970: && (dip->nr_color_changes != dip_old->nr_color_changes
1.1.1.3 root 971: || (dip->nr_color_changes > 0
972: && memcmp (curr_color_changes + dip->first_color_change,
973: prev_color_changes + dip_old->first_color_change,
974: dip->nr_color_changes * sizeof *curr_color_changes) != 0)))
1.1.1.2 root 975: changed = 1;
976: if (!changed && thisline_decision.which == 1
977: && (dip->nr_sprites != dip_old->nr_sprites
978: || (dip->nr_sprites > 0
1.1.1.3 root 979: && memcmp (curr_sprite_positions + dip->first_sprite_draw,
980: prev_sprite_positions + dip_old->first_sprite_draw,
981: dip->nr_sprites * sizeof *curr_sprite_positions) != 0)))
1.1.1.2 root 982: changed = 1;
983:
984: if (changed) {
1.1.1.5 root 985: thisline_changed = 1;
1.1.1.2 root 986: *dp = thisline_decision;
987: } else
988: /* The only one that may differ: */
989: dp->ctable = thisline_decision.ctable;
990: }
991:
1.1.1.3 root 992: /* Set the state of all decisions to "undecided" for a new scanline. */
1.1.1.2 root 993: static void reset_decisions (void)
994: {
995: if (framecnt != 0)
996: return;
997: thisline_decision.which = 0;
998: decided_bpl1mod = bpl1mod;
999: decided_bpl2mod = bpl2mod;
1000: decided_nr_planes = -1;
1001:
1002: /* decided_hires shouldn't be touched before it's initialized by decide_line(). */
1003: thisline_decision.diwfirstword = -1;
1004: thisline_decision.diwlastword = -2;
1.1.1.3 root 1005: if (hdiwstate == DIW_waiting_stop) {
1006: thisline_decision.diwfirstword = PIXEL_XPOS (DISPLAY_LEFT_SHIFT/2);
1007: if (thisline_decision.diwfirstword != line_decisions[next_lineno].diwfirstword)
1.1.1.5 root 1008: MARK_LINE_CHANGED;
1.1.1.3 root 1009: }
1.1.1.2 root 1010: thisline_decision.ctable = -1;
1011: thisline_decision.color0 = 0xFFFFFFFFul;
1012:
1.1.1.5 root 1013: thisline_changed = 0;
1.1.1.2 root 1014: curr_drawinfo[next_lineno].first_color_change = next_color_change;
1015: curr_drawinfo[next_lineno].first_delay_change = next_delay_change;
1016: curr_drawinfo[next_lineno].first_sprite_draw = next_sprite_draw;
1017:
1.1.1.3 root 1018: /* memset(sprite_last_drawn_at, 0, sizeof sprite_last_drawn_at); */
1019: last_sprite_point = 0;
1.1.1.2 root 1020: modulos_added = 0;
1021: plane_decided = 0;
1022: color_decided = 0;
1023: very_broken_program = 0;
1024: }
1025:
1.1.1.3 root 1026: /* Initialize the decision array, once before the emulator really starts. */
1.1.1.2 root 1027: static void init_decisions (void)
1028: {
1029: size_t i;
1030: for (i = 0; i < sizeof line_decisions / sizeof *line_decisions; i++) {
1031: line_decisions[i].which = -2;
1.1 root 1032: }
1.1.1.2 root 1033: }
1034:
1.1.1.3 root 1035: /* Calculate display window and data fetch values from the corresponding
1036: * hardware registers. */
1.1.1.2 root 1037: static void calcdiw (void)
1038: {
1.1.1.4 root 1039: int hstrt = diwstrt & 0xFF;
1040: int hstop = diwstop & 0xFF;
1041: int vstrt = diwstrt >> 8;
1042: int vstop = diwstop >> 8;
1043:
1044: if (diwhigh_written) {
1045: hstrt |= ((diwhigh >> 5) & 1) << 8;
1046: hstop |= ((diwhigh >> 13) & 1) << 8;
1047: vstrt |= (diwhigh & 7) << 8;
1048: vstop |= ((diwhigh >> 8) & 7) << 8;
1049: } else {
1050: hstop += 0x100;
1051: if ((vstop & 0x80) == 0)
1052: vstop |= 0x100;
1053: }
1054:
1055: diwfirstword = coord_hw_to_native_x (hstrt - 1);
1056: diwlastword = coord_hw_to_native_x (hstop - 1);
1.1.1.2 root 1057:
1058: if (diwlastword > max_diwlastword)
1059: diwlastword = max_diwlastword;
1060: if (diwfirstword < 0)
1061: diwfirstword = 0;
1062: if (diwlastword < 0)
1063: diwlastword = 0;
1.1.1.3 root 1064:
1.1.1.4 root 1065: plffirstline = vstrt;
1066: plflastline = vstop;
1067:
1.1 root 1068: #if 0
1069: /* This happens far too often. */
1070: if (plffirstline < minfirstline) {
1071: fprintf(stderr, "Warning: Playfield begins before line %d!\n", minfirstline);
1072: plffirstline = minfirstline;
1073: }
1074: #endif
1.1.1.4 root 1075:
1.1 root 1076: #if 0 /* Turrican does this */
1077: if (plflastline > 313) {
1.1.1.4 root 1078: fprintf (stderr, "Warning: Playfield out of range!\n");
1.1 root 1079: plflastline = 313;
1080: }
1081: #endif
1.1.1.2 root 1082: plfstrt = ddfstrt;
1083: plfstop = ddfstop;
1.1 root 1084: if (plfstrt < 0x18) plfstrt = 0x18;
1.1.1.3 root 1085: if (plfstop < 0x18) plfstop = 0x18;
1.1 root 1086: if (plfstop > 0xD8) plfstop = 0xD8;
1087: if (plfstrt > plfstop) plfstrt = plfstop;
1088:
1089: /* ! If the masking operation is changed, the pfield_doline code could break
1090: * on some systems (alignment) */
1091: /* This actually seems to be correct now, at least the non-AGA stuff... */
1092: plfstrt &= ~3;
1093: plfstop &= ~3;
1.1.1.2 root 1094: /* @@@ Start looking for AGA bugs here... (or maybe even in the above masking ops) */
1.1 root 1095: if ((fmode & 3) == 0)
1096: plflinelen = (plfstop-plfstrt+15) & ~7;
1097: else if ((fmode & 3) == 3)
1098: plflinelen = (plfstop-plfstrt+63) & ~31;
1099: else
1100: plflinelen = (plfstop-plfstrt+31) & ~15;
1101: }
1102:
1.1.1.4 root 1103: /* Mousehack stuff */
1.1.1.2 root 1104:
1.1.1.4 root 1105: #define defstepx (1<<16)
1106: #define defstepy (1<<16)
1107: #define defxoffs 0
1108: #define defyoffs 0
1.1.1.3 root 1109:
1.1.1.4 root 1110: static const int docal = 60, xcaloff = 40, ycaloff = 20;
1111: static const int calweight = 3;
1112: static int lastsampledmx, lastsampledmy;
1113: static int lastspr0x,lastspr0y,lastdiffx,lastdiffy,spr0pos,spr0ctl;
1114: static int mstepx,mstepy,xoffs=defxoffs,yoffs=defyoffs;
1115: static int sprvbfl;
1.1.1.2 root 1116:
1.1.1.4 root 1117: int lastmx, lastmy;
1118: int newmousecounters;
1119: int ievent_alive = 0;
1.1 root 1120:
1.1.1.4 root 1121: static enum { unknown_mouse, normal_mouse, dont_care_mouse, follow_mouse } mousestate;
1.1 root 1122:
1.1.1.4 root 1123: static void mousehack_setdontcare (void)
1.1 root 1124: {
1.1.1.4 root 1125: if (mousestate == dont_care_mouse)
1.1.1.2 root 1126: return;
1127:
1.1.1.3 root 1128: write_log ("Don't care mouse mode set\n");
1129: mousestate = dont_care_mouse;
1130: lastspr0x = lastmx; lastspr0y = lastmy;
1131: mstepx = defstepx; mstepy = defstepy;
1132: }
1133:
1.1.1.4 root 1134: static void mousehack_setfollow (void)
1.1.1.3 root 1135: {
1.1.1.4 root 1136: if (mousestate == follow_mouse)
1137: return;
1138:
1.1.1.3 root 1139: write_log ("Follow sprite mode set\n");
1140: mousestate = follow_mouse;
1141: lastdiffx = lastdiffy = 0;
1142: sprvbfl = 0;
1.1.1.4 root 1143: spr0ctl = spr0pos = 0;
1.1.1.3 root 1144: mstepx = defstepx; mstepy = defstepy;
1145: }
1146:
1147: uae_u32 mousehack_helper (void)
1148: {
1149: int mousexpos, mouseypos;
1150:
1151: #ifdef PICASSO96
1152: if (picasso_on) {
1.1.1.4 root 1153: mousexpos = lastmx - picasso96_state.XOffset;
1154: mouseypos = lastmy - picasso96_state.YOffset;
1.1.1.3 root 1155: } else
1156: #endif
1157: {
1158: if (lastmy >= gfxvidinfo.height)
1.1.1.4 root 1159: lastmy = gfxvidinfo.height - 1;
1160: mouseypos = coord_native_to_amiga_y (lastmy) << 1;
1161: mousexpos = coord_native_to_amiga_x (lastmx);
1.1.1.3 root 1162: }
1163:
1164: switch (m68k_dreg (regs, 0)) {
1165: case 0:
1166: return ievent_alive ? -1 : needmousehack ();
1167: case 1:
1168: ievent_alive = 10;
1169: return mousexpos;
1170: case 2:
1171: return mouseypos;
1172: }
1173: return 0;
1174: }
1175:
1.1.1.4 root 1176: void togglemouse (void)
1.1.1.3 root 1177: {
1.1.1.4 root 1178: switch (mousestate) {
1179: case dont_care_mouse: mousehack_setfollow (); break;
1180: case follow_mouse: mousehack_setdontcare (); break;
1.1.1.3 root 1181: default: break; /* Nnnnnghh! */
1182: }
1183: }
1184:
1.1.1.4 root 1185: static __inline__ int adjust (int val)
1.1.1.3 root 1186: {
1.1.1.4 root 1187: if (val > 127)
1.1.1.3 root 1188: return 127;
1.1.1.4 root 1189: else if (val < -127)
1.1.1.3 root 1190: return -127;
1191: return val;
1192: }
1193:
1.1.1.4 root 1194: static void do_mouse_hack (void)
1.1.1.3 root 1195: {
1196: int spr0x = ((spr0pos & 0xff) << 2) | ((spr0ctl & 1) << 1);
1197: int spr0y = ((spr0pos >> 8) | ((spr0ctl & 4) << 6)) << 1;
1198: int diffx, diffy;
1199:
1200: if (ievent_alive > 0) {
1201: mouse_x = mouse_y = 0;
1202: return;
1203: }
1204: switch (mousestate) {
1205: case normal_mouse:
1206: diffx = lastmx - lastsampledmx;
1207: diffy = lastmy - lastsampledmy;
1208: if (!newmousecounters) {
1209: if (diffx > 127) diffx = 127;
1210: if (diffx < -127) diffx = -127;
1211: mouse_x += diffx;
1212: if (diffy > 127) diffy = 127;
1213: if (diffy < -127) diffy = -127;
1214: mouse_y += diffy;
1215: }
1216: lastsampledmx += diffx; lastsampledmy += diffy;
1217: break;
1218:
1219: case dont_care_mouse:
1.1.1.4 root 1220: diffx = adjust (((lastmx - lastspr0x) * mstepx) >> 16);
1221: diffy = adjust (((lastmy - lastspr0y) * mstepy) >> 16);
1222: lastspr0x = lastmx; lastspr0y = lastmy;
1223: mouse_x += diffx; mouse_y += diffy;
1.1.1.3 root 1224: break;
1225:
1226: case follow_mouse:
1.1.1.4 root 1227: if (sprvbfl && sprvbfl-- > 1) {
1.1.1.3 root 1228: int mousexpos, mouseypos;
1229:
1.1.1.4 root 1230: if ((lastdiffx > docal || lastdiffx < -docal)
1231: && lastspr0x != spr0x
1232: && spr0x > plfstrt*4 + 34 + xcaloff
1233: && spr0x < plfstop*4 - xcaloff)
1.1.1.3 root 1234: {
1235: int val = (lastdiffx << 16) / (spr0x - lastspr0x);
1.1.1.4 root 1236: if (val >= 0x8000)
1237: mstepx = (mstepx * (calweight - 1) + val) / calweight;
1.1.1.3 root 1238: }
1.1.1.4 root 1239: if ((lastdiffy > docal || lastdiffy < -docal)
1240: && lastspr0y != spr0y
1241: && spr0y > plffirstline + ycaloff
1242: && spr0y < plflastline - ycaloff)
1.1.1.3 root 1243: {
1.1.1.4 root 1244: int val = (lastdiffy << 16) / (spr0y - lastspr0y);
1245: if (val >= 0x8000)
1246: mstepy = (mstepy * (calweight - 1) + val) / calweight;
1.1.1.3 root 1247: }
1248: if (lastmy >= gfxvidinfo.height)
1249: lastmy = gfxvidinfo.height-1;
1.1.1.4 root 1250: mouseypos = coord_native_to_amiga_y (lastmy) << 1;
1251: mousexpos = coord_native_to_amiga_x (lastmx);
1.1.1.3 root 1252: diffx = adjust ((((mousexpos + xoffs - spr0x) & ~1) * mstepx) >> 16);
1253: diffy = adjust ((((mouseypos + yoffs - spr0y) & ~1) * mstepy) >> 16);
1.1.1.4 root 1254: lastspr0x = spr0x; lastspr0y = spr0y;
1255: lastdiffx = diffx; lastdiffy = diffy;
1256: mouse_x += diffx; mouse_y += diffy;
1.1.1.3 root 1257: }
1258: break;
1.1.1.4 root 1259:
1260: default:
1261: abort ();
1.1.1.3 root 1262: }
1263: }
1264:
1265: /*
1.1 root 1266: * register functions
1267: */
1268:
1.1.1.4 root 1269: static __inline__ uae_u16 DMACONR (void)
1.1 root 1270: {
1271: return (dmacon | (bltstate==BLT_done ? 0 : 0x4000)
1272: | (blt_info.blitzero ? 0x2000 : 0));
1273: }
1.1.1.4 root 1274: static __inline__ uae_u16 INTENAR (void)
1275: {
1276: return intena;
1277: }
1278: uae_u16 INTREQR (void)
1.1 root 1279: {
1.1.1.3 root 1280: return intreq | (currprefs.use_serial ? 0x0001 : 0);
1.1 root 1281: }
1.1.1.4 root 1282: static __inline__ uae_u16 ADKCONR (void)
1283: {
1284: return adkcon;
1285: }
1286: static __inline__ uae_u16 VPOSR (void)
1.1 root 1287: {
1288: #if AGA_CHIPSET == 1
1289: return (vpos >> 8) | lof | 0x2300;
1290: #elif defined (ECS_AGNUS)
1291: return (vpos >> 8) | lof | 0x2000;
1292: #else
1293: return (vpos >> 8) | lof;
1294: #endif
1295: }
1.1.1.4 root 1296: static void VPOSW (uae_u16 v)
1.1.1.2 root 1297: {
1298: if (lof != (v & 0x8000))
1299: lof_changed = 1;
1300: lof = v & 0x8000;
1.1.1.3 root 1301: /*
1302: * This register is much more fun on a real Amiga. You can program
1303: * refresh rates with it ;) But I won't emulate this...
1304: */
1.1.1.2 root 1305: }
1.1 root 1306:
1.1.1.4 root 1307: static __inline__ uae_u16 VHPOSR (void)
1308: {
1309: return (vpos << 8) | current_hpos();
1310: }
1311:
1312: static __inline__ void COP1LCH (uae_u16 v) { cop1lc = (cop1lc & 0xffff) | ((uae_u32)v << 16); }
1313: static __inline__ void COP1LCL (uae_u16 v) { cop1lc = (cop1lc & ~0xffff) | (v & 0xfffe); }
1314: static __inline__ void COP2LCH (uae_u16 v) { cop2lc = (cop2lc & 0xffff) | ((uae_u32)v << 16); }
1315: static __inline__ void COP2LCL (uae_u16 v) { cop2lc = (cop2lc & ~0xffff) | (v & 0xfffe); }
1316:
1317: static void COPJMP1 (uae_u16 a)
1318: {
1319: cop_state.ip = cop1lc;
1320: cop_state.do_move = 0;
1321: cop_state.ignore_next = 0;
1322: cop_state.state = COP_read1;
1323: cop_state.vpos = vpos;
1324: cop_state.hpos = current_hpos () & ~1;
1325: cop_state.count = current_hpos () & ~1;
1326: prepare_copper ();
1327: if (eventtab[ev_copper].evtime == cycles && eventtab[ev_copper].active)
1328: abort ();
1329: events_schedule ();
1330: }
1331:
1332: static void COPJMP2 (uae_u16 a)
1333: {
1334: cop_state.ip = cop2lc;
1335: cop_state.do_move = 0;
1336: cop_state.ignore_next = 0;
1337: cop_state.state = COP_read1;
1338: cop_state.vpos = vpos;
1339: cop_state.hpos = current_hpos () & ~1;
1340: cop_state.count = current_hpos () & ~1;
1341: prepare_copper ();
1342: if (eventtab[ev_copper].evtime == cycles && eventtab[ev_copper].active)
1343: abort ();
1344: events_schedule ();
1345: }
1346:
1347: static __inline__ void COPCON (uae_u16 a)
1348: {
1349: copcon = a;
1.1 root 1350: }
1.1.1.4 root 1351:
1352: static void DMACON (uae_u16 v)
1.1 root 1353: {
1354: int i, need_resched = 0;
1355:
1.1.1.3 root 1356: uae_u16 oldcon = dmacon;
1357:
1.1.1.4 root 1358: decide_line (current_hpos ());
1.1 root 1359: setclr(&dmacon,v); dmacon &= 0x1FFF;
1.1.1.3 root 1360: /* ??? post_decide_line (); */
1361:
1362: /* FIXME? Maybe we need to think a bit more about the master DMA enable
1.1 root 1363: * bit in these cases. */
1.1.1.3 root 1364: if ((dmacon & DMA_COPPER) > (oldcon & DMA_COPPER)) {
1.1.1.4 root 1365: cop_state.ip = cop1lc;
1366: cop_state.do_move = 0;
1367: cop_state.ignore_next = 0;
1368: cop_state.state = COP_read1;
1369: cop_state.vpos = vpos;
1370: cop_state.hpos = current_hpos () & ~1;
1371: cop_state.count = current_hpos () & ~1;
1372: prepare_copper ();
1373: if (eventtab[ev_copper].evtime == cycles && eventtab[ev_copper].active)
1374: abort ();
1375: need_resched = 1;
1.1 root 1376: }
1377: if ((dmacon & DMA_BLITPRI) > (oldcon & DMA_BLITPRI) && bltstate != BLT_done) {
1378: static int count = 0;
1379: if (!count) {
1380: count = 1;
1.1.1.3 root 1381: write_log ("warning: program is doing blitpri hacks.\n");
1.1 root 1382: }
1383: regs.spcflags |= SPCFLAG_BLTNASTY;
1384: }
1.1.1.4 root 1385:
1386: update_audio ();
1387:
1.1 root 1388: for (i = 0; i < 4; i++) {
1389: struct audio_channel_data *cdp = audio_channel + i;
1.1.1.3 root 1390:
1.1 root 1391: cdp->dmaen = (dmacon & 0x200) && (dmacon & (1<<i));
1392: if (cdp->dmaen) {
1393: if (cdp->state == 0) {
1394: cdp->state = 1;
1395: cdp->pt = cdp->lc;
1396: cdp->wper = cdp->per;
1397: cdp->wlen = cdp->len;
1398: cdp->data_written = 2;
1.1.1.4 root 1399: cdp->evtime = eventtab[ev_hsync].evtime - cycles;
1.1 root 1400: }
1401: } else {
1402: if (cdp->state == 1 || cdp->state == 5) {
1403: cdp->state = 0;
1404: cdp->current_sample = 0;
1405: }
1406: }
1407: }
1.1.1.4 root 1408:
1.1 root 1409: if (need_resched)
1410: events_schedule();
1411: }
1.1.1.2 root 1412:
1.1.1.3 root 1413: /*static int trace_intena = 0;*/
1.1.1.2 root 1414:
1.1.1.3 root 1415: static __inline__ void INTENA (uae_u16 v)
1.1.1.2 root 1416: {
1.1.1.3 root 1417: /* if (trace_intena)
1.1.1.4 root 1418: fprintf (stderr, "INTENA: %04x\n", v);*/
1.1.1.3 root 1419: setclr(&intena,v); regs.spcflags |= SPCFLAG_INT;
1.1.1.2 root 1420: }
1.1.1.3 root 1421: void INTREQ (uae_u16 v)
1.1 root 1422: {
1.1.1.3 root 1423: setclr(&intreq,v);
1424: regs.spcflags |= SPCFLAG_INT;
1.1.1.4 root 1425: if (( v & 0x8800) == 0x0800)
1426: serdat &= 0xbfff;
1.1.1.3 root 1427: }
1.1 root 1428:
1.1.1.3 root 1429: static void ADKCON (uae_u16 v)
1430: {
1431: unsigned long t;
1.1.1.4 root 1432:
1433: update_audio ();
1434:
1.1.1.3 root 1435: setclr (&adkcon,v);
1436: t = adkcon | (adkcon >> 4);
1437: audio_channel[0].adk_mask = (((t >> 0) & 1) - 1);
1438: audio_channel[1].adk_mask = (((t >> 1) & 1) - 1);
1439: audio_channel[2].adk_mask = (((t >> 2) & 1) - 1);
1440: audio_channel[3].adk_mask = (((t >> 3) & 1) - 1);
1441: }
1.1 root 1442:
1.1.1.4 root 1443: static void BPLPTH (int hpos, uae_u16 v, int num)
1.1.1.3 root 1444: {
1.1.1.4 root 1445: decide_line (hpos);
1446: decide_plane (hpos);
1447: do_modulos (hpos);
1448: bplpt[num] = (bplpt[num] & 0xffff) | ((uae_u32)v << 16);
1.1.1.3 root 1449: }
1.1.1.4 root 1450: static void BPLPTL (int hpos, uae_u16 v, int num)
1.1.1.3 root 1451: {
1.1.1.4 root 1452: decide_line (hpos);
1453: decide_plane (hpos);
1454: do_modulos (hpos);
1455: bplpt[num] = (bplpt[num] & ~0xffff) | (v & 0xfffe);
1.1.1.3 root 1456: }
1.1 root 1457:
1.1.1.4 root 1458: static void BPLCON0 (int hpos, uae_u16 v)
1.1 root 1459: {
1.1.1.2 root 1460: #if AGA_CHIPSET == 0
1461: v &= 0xFF0E;
1462: /* The Sanity WOC demo needs this at one place (at the end of the "Party Effect")
1463: * Disable bitplane DMA if someone tries to do more than 4 Hires bitplanes. */
1464: if ((v & 0xF000) > 0xC000)
1465: v &= 0xFFF;
1466: /* Don't want 7 lores planes either. */
1467: if ((v & 0x8000) == 0 && (v & 0x7000) == 0x7000)
1468: v &= 0xEFFF;
1469: #endif
1470: if (bplcon0 == v)
1.1 root 1471: return;
1.1.1.4 root 1472: decide_line (hpos);
1.1.1.2 root 1473: bplcon0 = v;
1.1.1.6 ! root 1474: nr_planes_from_bplcon0 = GET_PLANES (v);
! 1475: #if AGA_CHIPSET != 0
! 1476: /* It's not clear how the copper timings are affected by the number
! 1477: * of bitplanes on AGA machines */
! 1478: corrected_nr_planes_from_bplcon0 = 4;
! 1479: #else
! 1480: corrected_nr_planes_from_bplcon0 = nr_planes_from_bplcon0 << (bplcon0 & 0x8000 ? 1 : 0);
! 1481: #endif
1.1.1.4 root 1482: post_decide_line (hpos);
1.1 root 1483: }
1.1.1.4 root 1484: static __inline__ void BPLCON1 (int hpos, uae_u16 v)
1.1 root 1485: {
1.1.1.2 root 1486: if (bplcon1 == v)
1.1 root 1487: return;
1.1.1.4 root 1488: decide_diw (hpos);
1.1.1.2 root 1489: bplcon1 = v;
1.1.1.4 root 1490: decide_delay (hpos);
1.1 root 1491: }
1.1.1.4 root 1492: static __inline__ void BPLCON2 (int hpos, uae_u16 v)
1.1 root 1493: {
1.1.1.2 root 1494: if (bplcon2 != v)
1.1.1.4 root 1495: decide_line (hpos);
1.1.1.2 root 1496: bplcon2 = v;
1.1 root 1497: }
1.1.1.4 root 1498: static __inline__ void BPLCON3 (int hpos, uae_u16 v)
1.1 root 1499: {
1.1.1.2 root 1500: if (bplcon3 != v)
1.1.1.4 root 1501: decide_line (hpos);
1.1.1.3 root 1502: bplcon3 = v;
1.1 root 1503: }
1.1.1.4 root 1504: static __inline__ void BPLCON4 (int hpos, uae_u16 v)
1.1 root 1505: {
1.1.1.2 root 1506: if (bplcon4 != v)
1.1.1.4 root 1507: decide_line (hpos);
1.1.1.3 root 1508: bplcon4 = v;
1.1 root 1509: }
1510:
1.1.1.4 root 1511: static void BPL1MOD (int hpos, uae_u16 v)
1.1 root 1512: {
1513: v &= ~1;
1.1.1.3 root 1514: if ((uae_s16)bpl1mod == (uae_s16)v)
1.1 root 1515: return;
1516: bpl1mod = v;
1.1.1.4 root 1517: decide_modulos (hpos);
1.1 root 1518: }
1.1.1.2 root 1519:
1.1.1.4 root 1520: static void BPL2MOD (int hpos, uae_u16 v)
1.1.1.3 root 1521: {
1.1 root 1522: v &= ~1;
1.1.1.3 root 1523: if ((uae_s16)bpl2mod == (uae_s16)v)
1.1 root 1524: return;
1525: bpl2mod = v;
1.1.1.4 root 1526: decide_modulos (hpos);
1.1 root 1527: }
1528:
1.1.1.2 root 1529: /* We could do as well without those... */
1.1.1.3 root 1530: static __inline__ void BPL1DAT (uae_u16 v) { bpl1dat = v; }
1531: static __inline__ void BPL2DAT (uae_u16 v) { bpl2dat = v; }
1532: static __inline__ void BPL3DAT (uae_u16 v) { bpl3dat = v; }
1533: static __inline__ void BPL4DAT (uae_u16 v) { bpl4dat = v; }
1534: static __inline__ void BPL5DAT (uae_u16 v) { bpl5dat = v; }
1535: static __inline__ void BPL6DAT (uae_u16 v) { bpl6dat = v; }
1.1.1.6 ! root 1536: static __inline__ void BPL7DAT (uae_u16 v) { bpl7dat = v; }
! 1537: static __inline__ void BPL8DAT (uae_u16 v) { bpl8dat = v; }
1.1 root 1538:
1.1.1.4 root 1539: static void DIWSTRT (int hpos, uae_u16 v)
1.1 root 1540: {
1.1.1.4 root 1541: if (diwstrt == v && ! diwhigh_written)
1.1 root 1542: return;
1.1.1.4 root 1543: decide_line (hpos);
1544: diwhigh_written = 0;
1.1.1.3 root 1545: diwstrt = v;
1.1.1.4 root 1546: calcdiw ();
1.1 root 1547: }
1.1.1.4 root 1548:
1549: static void DIWSTOP (int hpos, uae_u16 v)
1.1 root 1550: {
1.1.1.4 root 1551: if (diwstop == v && ! diwhigh_written)
1.1 root 1552: return;
1.1.1.4 root 1553: decide_line (hpos);
1554: diwhigh_written = 0;
1.1.1.3 root 1555: diwstop = v;
1.1.1.4 root 1556: calcdiw ();
1557: }
1558:
1559: static void DIWHIGH (int hpos, uae_u16 v)
1560: {
1561: if (diwhigh_written && diwhigh == v)
1562: return;
1563: decide_line (hpos);
1564: diwhigh_written = 1;
1565: diwhigh = v;
1566: calcdiw ();
1.1 root 1567: }
1.1.1.4 root 1568:
1569: static void DDFSTRT (int hpos, uae_u16 v)
1.1.1.3 root 1570: {
1.1.1.6 ! root 1571: v &= AGA_CHIPSET ? 0x1FF : 0xFF;
1.1.1.2 root 1572: if (ddfstrt == v)
1.1 root 1573: return;
1.1.1.4 root 1574: decide_line (hpos);
1.1.1.3 root 1575: ddfstrt = v;
1.1.1.4 root 1576: calcdiw ();
1.1 root 1577: }
1.1.1.4 root 1578: static void DDFSTOP (int hpos, uae_u16 v)
1.1.1.3 root 1579: {
1.1.1.6 ! root 1580: v &= AGA_CHIPSET ? 0x1FF : 0xFF;
1.1.1.2 root 1581: if (ddfstop == v)
1.1 root 1582: return;
1.1.1.4 root 1583: decide_line (hpos);
1.1.1.2 root 1584: ddfstop = v;
1.1.1.4 root 1585: calcdiw ();
1.1 root 1586: }
1587:
1.1.1.4 root 1588: static void BLTADAT (uae_u16 v)
1.1 root 1589: {
1.1.1.2 root 1590: maybe_blit();
1591:
1592: blt_info.bltadat = v;
1.1 root 1593: }
1.1.1.2 root 1594: /*
1595: * "Loading data shifts it immediately" says the HRM. Well, that may
1596: * be true for BLTBDAT, but not for BLTADAT - it appears the A data must be
1597: * loaded for every word so that AFWM and ALWM can be applied.
1598: */
1.1.1.4 root 1599: static void BLTBDAT (uae_u16 v)
1.1 root 1600: {
1.1.1.3 root 1601: maybe_blit();
1.1.1.2 root 1602:
1603: if (bltcon1 & 2)
1604: blt_info.bltbhold = v << (bltcon1 >> 12);
1605: else
1606: blt_info.bltbhold = v >> (bltcon1 >> 12);
1607: blt_info.bltbdat = v;
1.1 root 1608: }
1.1.1.4 root 1609: static void BLTCDAT (uae_u16 v) { maybe_blit (); blt_info.bltcdat = v; }
1.1 root 1610:
1.1.1.4 root 1611: static void BLTAMOD (uae_u16 v) { maybe_blit (); blt_info.bltamod = (uae_s16)(v & 0xFFFE); }
1612: static void BLTBMOD (uae_u16 v) { maybe_blit (); blt_info.bltbmod = (uae_s16)(v & 0xFFFE); }
1613: static void BLTCMOD (uae_u16 v) { maybe_blit (); blt_info.bltcmod = (uae_s16)(v & 0xFFFE); }
1614: static void BLTDMOD (uae_u16 v) { maybe_blit (); blt_info.bltdmod = (uae_s16)(v & 0xFFFE); }
1.1 root 1615:
1.1.1.4 root 1616: static void BLTCON0 (uae_u16 v) { maybe_blit (); bltcon0 = v; blinea_shift = v >> 12; }
1.1.1.3 root 1617: /* The next category is "Most useless hardware register".
1.1 root 1618: * And the winner is... */
1.1.1.4 root 1619: static void BLTCON0L (uae_u16 v) { maybe_blit (); bltcon0 = (bltcon0 & 0xFF00) | (v & 0xFF); }
1620: static void BLTCON1 (uae_u16 v) { maybe_blit (); bltcon1 = v; }
1621:
1622: static void BLTAFWM (uae_u16 v) { maybe_blit (); blt_info.bltafwm = v; }
1623: static void BLTALWM (uae_u16 v) { maybe_blit (); blt_info.bltalwm = v; }
1.1 root 1624:
1.1.1.4 root 1625: static void BLTAPTH (uae_u16 v) { maybe_blit (); bltapt = (bltapt & 0xffff) | ((uae_u32)v << 16); }
1626: static void BLTAPTL (uae_u16 v) { maybe_blit (); bltapt = (bltapt & ~0xffff) | (v & 0xFFFE); }
1627: static void BLTBPTH (uae_u16 v) { maybe_blit (); bltbpt = (bltbpt & 0xffff) | ((uae_u32)v << 16); }
1628: static void BLTBPTL (uae_u16 v) { maybe_blit (); bltbpt = (bltbpt & ~0xffff) | (v & 0xFFFE); }
1629: static void BLTCPTH (uae_u16 v) { maybe_blit (); bltcpt = (bltcpt & 0xffff) | ((uae_u32)v << 16); }
1630: static void BLTCPTL (uae_u16 v) { maybe_blit (); bltcpt = (bltcpt & ~0xffff) | (v & 0xFFFE); }
1631: static void BLTDPTH (uae_u16 v) { maybe_blit (); bltdpt = (bltdpt & 0xffff) | ((uae_u32)v << 16); }
1632: static void BLTDPTL (uae_u16 v) { maybe_blit (); bltdpt = (bltdpt & ~0xffff) | (v & 0xFFFE); }
1.1 root 1633:
1.1.1.4 root 1634: static void BLTSIZE (uae_u16 v)
1.1 root 1635: {
1636: bltsize = v;
1.1.1.3 root 1637:
1.1.1.4 root 1638: maybe_blit ();
1.1 root 1639:
1640: blt_info.vblitsize = bltsize >> 6;
1641: blt_info.hblitsize = bltsize & 0x3F;
1642: if (!blt_info.vblitsize) blt_info.vblitsize = 1024;
1643: if (!blt_info.hblitsize) blt_info.hblitsize = 64;
1.1.1.3 root 1644:
1645: bltstate = BLT_init;
1.1.1.4 root 1646: do_blitter ();
1.1 root 1647: }
1.1.1.4 root 1648:
1649: static void BLTSIZV (uae_u16 v)
1.1 root 1650: {
1.1.1.4 root 1651: maybe_blit ();
1.1 root 1652: oldvblts = v & 0x7FFF;
1653: }
1.1.1.4 root 1654:
1655: static void BLTSIZH (uae_u16 v)
1.1 root 1656: {
1.1.1.4 root 1657: maybe_blit ();
1.1 root 1658: blt_info.hblitsize = v & 0x7FF;
1659: blt_info.vblitsize = oldvblts;
1660: if (!blt_info.vblitsize) blt_info.vblitsize = 32768;
1661: if (!blt_info.hblitsize) blt_info.hblitsize = 0x800;
1.1.1.2 root 1662: bltstate = BLT_init;
1.1.1.5 root 1663: do_blitter ();
1.1 root 1664: }
1.1.1.4 root 1665:
1666: static __inline__ void SPRxCTL_1 (uae_u16 v, int num)
1.1 root 1667: {
1.1.1.2 root 1668: int sprxp;
1669: sprctl[num] = v;
1.1.1.3 root 1670: nr_armed -= sprarmed[num];
1.1.1.2 root 1671: sprarmed[num] = 0;
1.1.1.3 root 1672: if (sprpos[num] == 0 && v == 0) {
1673: sprst[num] = SPR_stop;
1674: spron[num] = 0;
1675: } else
1676: sprst[num] = SPR_waiting_start;
1.1.1.2 root 1677:
1.1.1.4 root 1678: sprxp = coord_hw_to_native_x ((sprpos[num] & 0xFF) * 2 + (v & 1));
1.1.1.2 root 1679: sprxpos[num] = sprxp;
1680: sprvstart[num] = (sprpos[num] >> 8) | ((sprctl[num] << 6) & 0x100);
1681: sprvstop[num] = (sprctl[num] >> 8) | ((sprctl[num] << 7) & 0x100);
1682: }
1.1.1.4 root 1683: static __inline__ void SPRxPOS_1 (uae_u16 v, int num)
1.1.1.2 root 1684: {
1685: int sprxp;
1686: sprpos[num] = v;
1.1.1.4 root 1687: sprxp = coord_hw_to_native_x ((v & 0xFF) * 2 + (sprctl[num] & 1));
1.1.1.2 root 1688: sprxpos[num] = sprxp;
1689: sprvstart[num] = (sprpos[num] >> 8) | ((sprctl[num] << 6) & 0x100);
1690: }
1.1.1.4 root 1691: static __inline__ void SPRxDATA_1 (uae_u16 v, int num)
1.1.1.2 root 1692: {
1693: sprdata[num] = v;
1.1.1.3 root 1694: nr_armed += 1 - sprarmed[num];
1.1.1.2 root 1695: sprarmed[num] = 1;
1696: }
1.1.1.4 root 1697: static __inline__ void SPRxDATB_1 (uae_u16 v, int num)
1.1.1.2 root 1698: {
1699: sprdatb[num] = v;
1700: }
1.1.1.4 root 1701: static void SPRxDATA (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxDATA_1 (v, num); }
1702: static void SPRxDATB (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxDATB_1 (v, num); }
1703: static void SPRxCTL (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxCTL_1 (v, num); }
1704: static void SPRxPOS (int hpos, uae_u16 v, int num) { decide_sprites (hpos); SPRxPOS_1 (v, num); }
1705: static void SPRxPTH (int hpos, uae_u16 v, int num)
1.1 root 1706: {
1.1.1.4 root 1707: decide_sprites (hpos);
1.1.1.3 root 1708: sprpt[num] &= 0xffff;
1709: sprpt[num] |= (uae_u32)v << 16;
1710:
1711: if (sprst[num] == SPR_stop || vpos < vblank_endline)
1712: sprst[num] = SPR_restart;
1713: spron[num] = 1;
1.1 root 1714: }
1.1.1.4 root 1715: static void SPRxPTL (int hpos, uae_u16 v, int num)
1.1 root 1716: {
1.1.1.4 root 1717: decide_sprites (hpos);
1.1.1.3 root 1718: sprpt[num] &= ~0xffff;
1.1.1.2 root 1719: sprpt[num] |= v;
1.1 root 1720:
1.1.1.3 root 1721: if (sprst[num] == SPR_stop || vpos < vblank_endline)
1722: sprst[num] = SPR_restart;
1723: spron[num] = 1;
1724: }
1.1.1.4 root 1725: static void CLXCON (uae_u16 v)
1.1.1.3 root 1726: {
1727: clxcon = v;
1728: clx_sprmask = (((v >> 15) << 7) | ((v >> 14) << 5) | ((v >> 13) << 3) | ((v >> 12) << 1) | 0x55);
1729: }
1.1.1.4 root 1730: static uae_u16 CLXDAT (void)
1.1.1.3 root 1731: {
1732: uae_u16 v = clxdat;
1733: clxdat = 0;
1734: return v;
1735: }
1.1.1.4 root 1736: static void COLOR (int hpos, uae_u16 v, int num)
1.1 root 1737: {
1.1.1.3 root 1738:
1.1 root 1739: v &= 0xFFF;
1740: #if AGA_CHIPSET == 1
1741: {
1.1.1.2 root 1742: /* XXX Broken */
1.1.1.4 root 1743: int r,g,b;
1744: int cr,cg,cb;
1745: int colreg;
1.1.1.3 root 1746: uae_u32 cval;
1.1.1.4 root 1747:
1.1.1.2 root 1748: colreg = ((bplcon3 >> 13) & 7) * 32 + num;
1.1 root 1749: r = (v & 0xF00) >> 8;
1750: g = (v & 0xF0) >> 4;
1751: b = (v & 0xF) >> 0;
1.1.1.5 root 1752: cr = current_colors.color_regs[colreg] >> 16;
1753: cg = (current_colors.color_regs[colreg] >> 8) & 0xFF;
1754: cb = current_colors.color_regs[colreg] & 0xFF;
1.1 root 1755:
1.1.1.2 root 1756: if (bplcon3 & 0x200) {
1.1 root 1757: cr &= 0xF0; cr |= r;
1758: cg &= 0xF0; cg |= g;
1759: cb &= 0xF0; cb |= b;
1760: } else {
1761: cr = r + (r << 4);
1762: cg = g + (g << 4);
1763: cb = b + (b << 4);
1764: }
1765: cval = (cr << 16) | (cg << 8) | cb;
1.1.1.5 root 1766: if (cval == current_colors.color_regs[colreg])
1.1 root 1767: return;
1.1.1.5 root 1768:
1769: /* Call this with the old table still intact. */
1770: record_color_change (hpos, colreg, v);
1771: remembered_color_entry = -1;
1772: current_colors.color_regs[colreg] = cval;
1773: /* current_colors.acolors[colreg] = xcolors[v];*/
1.1 root 1774: }
1.1.1.3 root 1775: #else
1.1 root 1776: {
1.1.1.2 root 1777: if (current_colors.color_regs[num] == v)
1.1 root 1778: return;
1.1.1.2 root 1779: /* Call this with the old table still intact. */
1.1.1.4 root 1780: record_color_change (hpos, num, v);
1.1.1.2 root 1781: remembered_color_entry = -1;
1782: current_colors.color_regs[num] = v;
1783: current_colors.acolors[num] = xcolors[v];
1.1 root 1784: }
1785: #endif
1786: }
1787:
1.1.1.4 root 1788: static void DSKSYNC (uae_u16 v) { dsksync = v; }
1789: static void DSKDAT (uae_u16 v) { write_log ("DSKDAT written. Not good.\n"); }
1790: static void DSKPTH (uae_u16 v) { dskpt = (dskpt & 0xffff) | ((uae_u32)v << 16); }
1791: static void DSKPTL (uae_u16 v) { dskpt = (dskpt & ~0xffff) | (v); }
1.1 root 1792:
1.1.1.4 root 1793: static void DSKLEN (uae_u16 v)
1.1 root 1794: {
1795: if (v & 0x8000) {
1796: dskdmaen = dskdmaen == 1 ? 2 : 1;
1.1.1.3 root 1797: } else {
1.1 root 1798: dskdmaen = 0;
1799: if (eventtab[ev_diskblk].active)
1.1.1.3 root 1800: write_log ("warning: Disk DMA aborted!\n");
1.1 root 1801: eventtab[ev_diskblk].active = 0;
1802: events_schedule();
1.1.1.3 root 1803:
1.1 root 1804: }
1805: dsklen = dsklength = v; dsklength &= 0x3fff;
1806: if (dskdmaen == 2 && dsksync != 0x4489 && (adkcon & 0x400)) {
1.1.1.4 root 1807: write_log ("Non-standard sync: %04x len: %x\n", dsksync, dsklength);
1.1 root 1808: }
1809: if (dskdmaen > 1) {
1810: if (dsklen & 0x4000) {
1811: eventtab[ev_diskblk].active = 1;
1812: eventtab[ev_diskblk].oldcycles = cycles;
1813: eventtab[ev_diskblk].evtime = 40 + cycles; /* ??? */
1814: events_schedule();
1815: } else {
1816: int result = DISK_PrepareReadMFM(dsklength, dsksync, adkcon & 0x400);
1817: if (result) {
1818: eventtab[ev_diskblk].active = 1;
1819: eventtab[ev_diskblk].oldcycles = cycles;
1820: eventtab[ev_diskblk].evtime = result + cycles;
1821: events_schedule();
1822: }
1823: }
1824: }
1825: }
1826:
1.1.1.4 root 1827: static uae_u16 DSKBYTR (void)
1.1 root 1828: {
1.1.1.3 root 1829: uae_u16 v = (dsklen >> 1) & 0x6000;
1830: uae_u16 mfm, byte;
1.1.1.4 root 1831: if (DISK_GetData (&mfm, &byte))
1.1 root 1832: v |= 0x8000;
1833: v |= byte;
1.1.1.4 root 1834: if (dsksync == mfm)
1835: v |= 0x1000;
1.1 root 1836: return v;
1837: }
1838:
1.1.1.4 root 1839: static uae_u16 DSKDATR (void)
1.1 root 1840: {
1.1.1.3 root 1841: uae_u16 mfm, byte;
1.1.1.4 root 1842: DISK_GetData (&mfm, &byte);
1.1.1.3 root 1843: return mfm;
1.1 root 1844: }
1.1.1.3 root 1845:
1846: static uae_u16 potgo_value;
1847:
1.1.1.4 root 1848: static void POTGO (uae_u16 v)
1.1 root 1849: {
1.1.1.3 root 1850: potgo_value = v;
1851: }
1852:
1.1.1.4 root 1853: static uae_u16 POTGOR (void)
1.1.1.3 root 1854: {
1855: uae_u16 v = (potgo_value | (potgo_value << 1)) & 0xAA00;
1856: v |= v >> 1;
1857:
1.1.1.6 ! root 1858: if (JSEM_ISMOUSE (0, &currprefs)) {
1.1.1.3 root 1859: if (buttonstate[2])
1860: v &= 0xFBFF;
1861:
1862: if (buttonstate[1])
1863: v &= 0xFEFF;
1.1.1.6 ! root 1864: } else if (JSEM_ISJOY0 (0, &currprefs) || JSEM_ISJOY1 (0, &currprefs)) {
1.1.1.3 root 1865: if (joy0button & 2) v &= 0xfbff;
1866: if (joy0button & 4) v &= 0xfeff;
1867: }
1868:
1.1.1.6 ! root 1869: if (JSEM_ISJOY0 (1, &currprefs) || JSEM_ISJOY1 (1, &currprefs)) {
1.1.1.3 root 1870: if (joy1button & 2) v &= 0xbfff;
1871: if (joy1button & 4) v &= 0xefff;
1872: }
1.1 root 1873:
1874: return v;
1875: }
1.1.1.4 root 1876:
1877: static uae_u16 POT0DAT (void)
1.1 root 1878: {
1.1.1.3 root 1879: static uae_u16 cnt = 0;
1.1.1.6 ! root 1880: if (JSEM_ISMOUSE (0, &currprefs)) {
1.1.1.3 root 1881: if (buttonstate[2])
1882: cnt = ((cnt + 1) & 0xFF) | (cnt & 0xFF00);
1883: if (buttonstate[1])
1884: cnt += 0x100;
1885: }
1886:
1.1 root 1887: return cnt;
1888: }
1.1.1.4 root 1889: static uae_u16 JOY0DAT (void)
1.1 root 1890: {
1.1.1.6 ! root 1891: if (JSEM_ISMOUSE (0, &currprefs)) {
1.1.1.4 root 1892: do_mouse_hack ();
1.1.1.3 root 1893: return ((uae_u8)mouse_x) + ((uae_u16)mouse_y << 8);
1894: }
1.1 root 1895: return joy0dir;
1896: }
1.1.1.4 root 1897: static uae_u16 JOY1DAT (void)
1.1 root 1898: {
1.1.1.6 ! root 1899: if (JSEM_ISMOUSE (1, &currprefs)) {
1.1.1.4 root 1900: do_mouse_hack ();
1.1.1.3 root 1901: return ((uae_u8)mouse_x) + ((uae_u16)mouse_y << 8);
1902: }
1903: return joy1dir;
1.1 root 1904: }
1.1.1.4 root 1905: static void JOYTEST (uae_u16 v)
1.1 root 1906: {
1.1.1.6 ! root 1907: if (JSEM_ISMOUSE (0, &currprefs)) {
1.1.1.3 root 1908: mouse_x = v & 0xFC;
1909: mouse_y = (v >> 8) & 0xFC;
1.1 root 1910: }
1911: }
1912:
1.1.1.2 root 1913: /*
1.1.1.4 root 1914: * Here starts the copper code. Can you believe it used to be worse?
1.1.1.2 root 1915: */
1916:
1.1.1.4 root 1917: static __inline__ void copper_adjust_diw (struct copper *cst)
1.1.1.3 root 1918: {
1.1.1.4 root 1919: if (cst->vdiw == DIW_waiting_start && vpos == plffirstline)
1920: cst->vdiw = DIW_waiting_stop;
1921: if (cst->vdiw == DIW_waiting_stop && vpos == plflastline)
1922: cst->vdiw = DIW_waiting_start;
1923: }
1924:
1925: /* Determine which cycles are available for the copper in a display
1926: * with a agiven number of planes. */
1927: static int cycles_for_plane[9][8] = {
1928: { 0, -1, 0, -1, 0, -1, 0, -1 },
1929: { 0, -1, 0, -1, 0, -1, 0, -1 },
1930: { 0, -1, 0, -1, 0, -1, 0, -1 },
1931: { 0, -1, 0, -1, 0, -1, 0, -1 },
1932: { 0, -1, 0, -1, 0, -1, 0, -1 },
1933: { 0, -1, 1, -1, 0, -1, 0, -1 },
1934: { 0, -1, 1, -1, 0, -1, 1, -1 },
1935: { 1, -1, 1, -1, 1, -1, 1, -1 },
1936: { 1, -1, 1, -1, 1, -1, 1, -1 }
1937: };
1.1.1.2 root 1938:
1.1.1.4 root 1939: static unsigned int waitmasktab[256];
1.1.1.2 root 1940:
1.1.1.4 root 1941: #define COP_OFFSET 4
1942:
1943: static __inline__ int copper_in_playfield (enum diw_states diw, int hpos)
1.1.1.3 root 1944: {
1.1.1.4 root 1945: hpos -= COP_OFFSET;
1946: return diw == DIW_waiting_stop && hpos >= plfstrt && hpos < plfstrt + plflinelen;
1.1 root 1947: }
1948:
1.1.1.4 root 1949: static __inline__ int copper_cant_read (enum diw_states diw, int hpos, int planes)
1.1 root 1950: {
1.1.1.4 root 1951: int t;
1.1.1.2 root 1952:
1.1.1.4 root 1953: if (hpos >= ((maxhpos - 2) & ~1))
1.1.1.2 root 1954: return 1;
1.1.1.3 root 1955:
1.1.1.6 ! root 1956: #if AGA_CHIPSET != 0
! 1957: /* FIXME */
! 1958: return 0;
! 1959: #else
1.1.1.4 root 1960: if (! copper_in_playfield (diw, hpos))
1961: return 0;
1.1.1.3 root 1962:
1.1.1.4 root 1963: t = cycles_for_plane[planes][hpos & 7];
1964: #if 0
1965: if (t == -1)
1966: abort ();
1967: #endif
1968: return t;
1.1.1.6 ! root 1969: #endif
1.1.1.2 root 1970: }
1971:
1.1.1.4 root 1972: static void update_copper_1 (int until_hpos)
1.1.1.2 root 1973: {
1.1.1.4 root 1974: unsigned int vp, hp, vcmp, hcmp;
1975: int c_hpos = cop_state.hpos;
1976: int c_vpos = cop_state.vpos;
1.1.1.2 root 1977:
1.1.1.4 root 1978: vp = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80);
1979: hp = cop_state.count & (cop_state.i2 & 0xFE);
1980: vcmp = ((cop_state.i1 & (cop_state.i2 | 0x8000)) >> 8);
1981: hcmp = (cop_state.i1 & cop_state.i2 & 0xFE);
1.1 root 1982:
1.1.1.4 root 1983: if (cop_state.state == COP_wait2)
1984: cop_state.state = COP_wait1b;
1.1.1.3 root 1985:
1.1.1.4 root 1986: for (;;) {
1987: if (c_hpos == (maxhpos & ~1)) {
1988: c_hpos = 0;
1989: cop_state.count = 0;
1990: c_vpos++;
1991: }
1.1.1.3 root 1992:
1.1.1.4 root 1993: if (! dmaen (DMA_COPPER))
1994: cop_state.state = COP_stop;
1995:
1996: if (c_vpos > vpos
1997: || c_hpos > until_hpos
1998: || cop_state.state == COP_stop)
1.1.1.3 root 1999: break;
2000:
1.1.1.4 root 2001: if (c_hpos - COP_OFFSET == plfstrt)
2002: copper_adjust_diw (&cop_state);
1.1.1.3 root 2003:
1.1.1.4 root 2004: switch (cop_state.state) {
2005: case COP_rdelay1:
2006: cop_state.state = COP_read1;
1.1.1.3 root 2007: break;
2008:
1.1.1.4 root 2009: case COP_read1:
2010: if (copper_cant_read (cop_state.vdiw, c_hpos, corrected_nr_planes_from_bplcon0))
2011: break;
1.1.1.3 root 2012:
1.1.1.4 root 2013: if (cop_state.do_move) {
2014: cop_state.do_move = 0;
1.1.1.6 ! root 2015: if (cop_state.i1 < (copcon & 2 ? (AGA_CHIPSET ? 0 : 0x40u) : 0x80u)) {
1.1.1.4 root 2016: cop_state.state = COP_stop;
2017: break;
2018: }
2019: switch (cop_state.i1) {
2020: case 0x088:
2021: cop_state.ip = cop1lc;
2022: cop_state.state = COP_rdelay1;
2023: break;
2024: case 0x08A:
2025: cop_state.ip = cop2lc;
2026: cop_state.state = COP_rdelay1;
2027: break;
2028: default:
2029: custom_wput_1 (c_hpos, cop_state.i1, cop_state.i2);
2030: break;
2031: }
2032: if (cop_state.state != COP_read1)
1.1.1.3 root 2033: break;
2034: }
1.1.1.4 root 2035: cop_state.i1 = chipmem_bank.wget (cop_state.ip);
2036: cop_state.ip += 2;
2037: cop_state.state = COP_read2;
1.1.1.2 root 2038: break;
2039:
1.1.1.4 root 2040: case COP_read2:
2041: if (copper_cant_read (cop_state.vdiw, c_hpos, corrected_nr_planes_from_bplcon0))
2042: break;
2043: cop_state.i2 = chipmem_bank.wget (cop_state.ip);
2044: cop_state.ip += 2;
2045: if (cop_state.ignore_next) {
2046: cop_state.ignore_next = 0;
2047: cop_state.state = COP_read1;
1.1.1.2 root 2048: break;
1.1.1.4 root 2049: }
2050: if ((cop_state.i1 & 1) == 0) {
2051: cop_state.state = COP_read1;
2052: cop_state.do_move = 1;
1.1.1.2 root 2053: } else {
1.1.1.4 root 2054: vp = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80);
2055: hp = cop_state.count & (cop_state.i2 & 0xFE);
2056: vcmp = ((cop_state.i1 & (cop_state.i2 | 0x8000)) >> 8);
2057: hcmp = (cop_state.i1 & cop_state.i2 & 0xFE);
2058:
2059: if ((cop_state.i2 & 1) == 0) {
2060: cop_state.state = COP_wait1;
2061: if (cop_state.i1 == 0xFFFF && cop_state.i2 == 0xFFFE)
2062: cop_state.state = COP_stop;
2063: } else {
2064: /* Skip instruction. */
2065: if ((vp > vcmp || (vp == vcmp && hp >= hcmp))
2066: && ((cop_state.i2 & 0x8000) != 0 || ! (DMACONR() & 0x4000)))
2067: cop_state.ignore_next = 1;
2068: cop_state.state = COP_read1;
2069: }
1.1.1.2 root 2070: }
2071: break;
2072:
1.1.1.4 root 2073: case COP_wait1:
2074: if (copper_cant_read (cop_state.vdiw, c_hpos, corrected_nr_planes_from_bplcon0))
1.1.1.2 root 2075: break;
1.1.1.4 root 2076:
2077: /* fall through */
2078:
2079: case COP_wait1b:
2080: {
2081: int do_wait2 = cop_state.state == COP_wait1b;
2082: cop_state.state = COP_wait2;
2083: if (vp == vcmp && corrected_nr_planes_from_bplcon0 < 8) {
2084: int t = cop_state.count + 2;
2085: int next_count = (t & waitmasktab[cop_state.i2 & 0xfe]) | hcmp;
2086: int nexthpos;
2087: if (next_count < t)
2088: next_count = t;
2089: nexthpos = c_hpos + next_count - cop_state.count;
2090: if (nexthpos < (maxhpos & ~1)) {
2091: if (c_hpos - COP_OFFSET < plfstrt && nexthpos - COP_OFFSET >= plfstrt)
2092: copper_adjust_diw (&cop_state);
2093: c_hpos = nexthpos;
2094: cop_state.count = next_count;
2095: do_wait2 = 1;
2096: }
2097: }
2098: if (! do_wait2)
2099: break;
1.1.1.2 root 2100: }
2101:
1.1.1.4 root 2102: /* fall through */
2103:
2104: case COP_wait2:
2105: if (vp < vcmp) {
2106: if (c_hpos - COP_OFFSET < plfstrt)
2107: copper_adjust_diw (&cop_state);
2108:
2109: c_vpos++;
2110: c_hpos = 0;
2111: cop_state.count = 0;
2112: continue;
1.1.1.2 root 2113: }
1.1.1.4 root 2114: hp = cop_state.count & (cop_state.i2 & 0xFE);
2115: if (vp == vcmp && hp < hcmp)
2116: break;
2117: /* Now we know that the comparisons were successful. */
2118: if ((cop_state.i2 & 0x8000) == 0x8000 || ! (DMACONR() & 0x4000))
2119: cop_state.state = COP_read1;
2120: else {
2121: /* We need to wait for the blitter. It won't stop while
2122: * we're in update_copper, so we _could_ as well proceed to
2123: * until_hpos in one big step. There are some tricky
2124: * issues to be considered, though, so use the slow method
2125: * for now. */
2126: cop_state.state = COP_bltwait;
1.1.1.2 root 2127: }
1.1.1.4 root 2128: break;
1.1.1.2 root 2129:
1.1.1.4 root 2130: default:
2131: /* Delay cycles. */
2132: break;
1.1 root 2133: }
2134:
1.1.1.4 root 2135: c_hpos += 2;
2136: if (corrected_nr_planes_from_bplcon0 < 8 || ! copper_in_playfield (cop_state.vdiw, c_hpos))
2137: cop_state.count += 2;
2138: }
2139: cop_state.hpos = c_hpos;
2140: cop_state.vpos = c_vpos;
1.1 root 2141: }
2142:
1.1.1.4 root 2143: static __inline__ void update_copper (int until_hpos)
1.1 root 2144: {
1.1.1.4 root 2145: if (cop_state.vpos > vpos
2146: || cop_state.hpos > until_hpos
2147: || cop_state.state == COP_stop)
1.1 root 2148: return;
1.1.1.4 root 2149: update_copper_1 (until_hpos);
1.1 root 2150: }
2151:
1.1.1.4 root 2152: static int dangerous_reg (int reg)
1.1 root 2153: {
1.1.1.4 root 2154: /* Safe:
2155: * Bitplane pointers, control registers, modulos and data.
2156: * Sprite pointers, control registers, and data.
2157: * Color registers. */
2158: if (reg >= 0xE0 && reg < 0x1C0)
2159: return 0;
2160: return 1;
1.1 root 2161: }
2162:
1.1.1.4 root 2163: static void prepare_copper_1 (void)
1.1.1.2 root 2164: {
1.1.1.4 root 2165: struct copper cst = cop_state;
2166: unsigned int vp, hp, vcmp, hcmp;
1.1.1.2 root 2167:
1.1.1.4 root 2168: vp = vpos & (((cop_state.i2 >> 8) & 0x7F) | 0x80);
2169: hp = cop_state.count & (cop_state.i2 & 0xFE);
2170: vcmp = ((cop_state.i1 & (cop_state.i2 | 0x8000)) >> 8);
2171: hcmp = (cop_state.i1 & cop_state.i2 & 0xFE);
1.1.1.2 root 2172:
1.1.1.4 root 2173: if (cst.state == COP_wait2)
2174: cst.state = COP_wait1b;
1.1.1.3 root 2175:
1.1.1.4 root 2176: for (;;) {
2177: if (cst.hpos == (maxhpos & ~1)) {
2178: cst.hpos = 0;
2179: cst.count = 0;
2180: cst.vpos++;
1.1.1.2 root 2181: }
2182:
1.1.1.4 root 2183: if (! dmaen (DMA_COPPER))
2184: cst.state = COP_stop;
1.1.1.3 root 2185:
1.1.1.4 root 2186: if (cst.vpos > vpos
2187: || cst.state == COP_stop)
2188: {
2189: eventtab[ev_copper].active = 0;
2190: return;
1.1.1.2 root 2191: }
2192:
1.1.1.4 root 2193: if (cst.hpos - COP_OFFSET == plfstrt)
2194: copper_adjust_diw (&cst);
1.1.1.2 root 2195:
1.1.1.4 root 2196: switch (cst.state) {
2197: case COP_rdelay1:
2198: cst.state = COP_read1;
1.1.1.3 root 2199: break;
1.1.1.2 root 2200:
1.1.1.4 root 2201: case COP_read1:
2202: if (copper_cant_read (cst.vdiw, cst.hpos, corrected_nr_planes_from_bplcon0))
2203: break;
1.1.1.2 root 2204:
1.1.1.4 root 2205: if (cst.do_move) {
2206: cst.do_move = 0;
1.1.1.6 ! root 2207: if (cst.i1 < (copcon & 2 ? (AGA_CHIPSET ? 0 : 0x40u) : 0x80u)) {
1.1.1.4 root 2208: cst.state = COP_stop;
2209: eventtab[ev_copper].active = 0;
2210: return;
1.1.1.6 ! root 2211: } else if (dangerous_reg (cst.i1)) {
1.1.1.4 root 2212: eventtab[ev_copper].active = 1;
2213: eventtab[ev_copper].oldcycles = cycles;
2214: eventtab[ev_copper].evtime = cycles + cst.hpos - current_hpos () + (cst.vpos - cop_state.vpos) * maxhpos;
2215: return;
1.1.1.3 root 2216: }
2217: }
1.1.1.2 root 2218:
1.1.1.4 root 2219: cst.i1 = chipmem_bank.wget (cst.ip);
2220: cst.ip += 2;
2221: cst.state = COP_read2;
2222: break;
1.1.1.2 root 2223:
1.1.1.4 root 2224: case COP_read2:
2225: if (copper_cant_read (cst.vdiw, cst.hpos, corrected_nr_planes_from_bplcon0))
2226: break;
2227: cst.i2 = chipmem_bank.wget (cst.ip);
2228: cst.ip += 2;
2229: if (cst.ignore_next) {
2230: cst.ignore_next = 0;
2231: cst.state = COP_read1;
2232: break;
1.1.1.3 root 2233: }
1.1.1.4 root 2234: if ((cst.i1 & 1) == 0) {
2235: cst.state = COP_read1;
2236: cst.do_move = 1;
1.1.1.3 root 2237: } else {
1.1.1.4 root 2238: vp = vpos & (((cst.i2 >> 8) & 0x7F) | 0x80);
2239: hp = cst.count & (cst.i2 & 0xFE);
2240: vcmp = ((cst.i1 & (cst.i2 | 0x8000)) >> 8);
2241: hcmp = (cst.i1 & cst.i2 & 0xFE);
2242:
2243: if ((cst.i2 & 1) == 0) {
2244: cst.state = COP_wait1;
2245: if (cst.i1 == 0xFFFF && cst.i2 == 0xFFFE)
2246: cst.state = COP_stop;
1.1.1.3 root 2247: } else {
1.1.1.4 root 2248: /* Skip instruction. */
2249: if ((vp > vcmp || (vp == vcmp && hp >= hcmp))
2250: && ((cst.i2 & 0x8000) != 0 || ! (DMACONR() & 0x4000)))
2251: cst.ignore_next = 1;
2252: cst.state = COP_read1;
1.1.1.3 root 2253: }
2254: }
1.1.1.4 root 2255: break;
1.1 root 2256:
1.1.1.4 root 2257: case COP_wait1:
2258: if (copper_cant_read (cst.vdiw, cst.hpos, corrected_nr_planes_from_bplcon0))
2259: break;
1.1 root 2260:
1.1.1.4 root 2261: /* fall through */
1.1 root 2262:
1.1.1.4 root 2263: case COP_wait1b:
2264: {
2265: int do_wait2 = cst.state == COP_wait1b;
2266: cst.state = COP_wait2;
2267: if (vp == vcmp && corrected_nr_planes_from_bplcon0 < 8) {
2268: int t = cst.count + 2;
2269: int next_count = (t & waitmasktab[cst.i2 & 0xfe]) | hcmp;
2270: int nexthpos;
2271: if (next_count < t)
2272: next_count = t;
2273: nexthpos = cst.hpos + next_count - cst.count;
2274: if (nexthpos < (maxhpos & ~1)) {
2275: if (cst.hpos - COP_OFFSET < plfstrt && nexthpos - COP_OFFSET >= plfstrt)
2276: copper_adjust_diw (&cst);
2277: cst.hpos = nexthpos;
2278: cst.count = next_count;
2279: do_wait2 = 1;
2280: }
2281: }
2282: if (! do_wait2)
2283: break;
1.1 root 2284: }
1.1.1.3 root 2285:
1.1.1.4 root 2286: /* fall through */
2287:
2288: case COP_wait2:
2289: if (vp < vcmp) {
2290: if (cst.hpos - COP_OFFSET < plfstrt)
2291: copper_adjust_diw (&cst);
2292:
2293: cst.vpos++;
2294: cst.count = 0;
2295: cst.hpos = 0;
2296: continue;
1.1 root 2297: }
1.1.1.4 root 2298: hp = cst.count & (cst.i2 & 0xFE);
2299: if (vp == vcmp && hp < hcmp)
2300: break;
2301: #if 0
2302: if (nexthpos != -1)
2303: if (nexthpos != cst.count)
2304: printf ("ERROR\n");
2305: nexthpos = -1;
2306: #endif
2307: /* Now we know that the comparisons were successful. */
2308: if ((cst.i2 & 0x8000) == 0x8000 || ! (DMACONR() & 0x4000))
2309: cst.state = COP_read1;
2310: else {
2311: /* We need to wait for the blitter. It won't stop while
2312: * we're in update_copper, so we _could_ as well proceed to
2313: * until_hpos in one big step. There are some tricky
2314: * issues to be considered, though, so use the slow method
2315: * for now. */
2316: cst.state = COP_bltwait;
1.1 root 2317: }
1.1.1.4 root 2318: break;
1.1.1.2 root 2319:
1.1.1.4 root 2320: default:
2321: /* Delay cycles. */
2322: break;
1.1.1.2 root 2323: }
1.1.1.3 root 2324:
1.1.1.4 root 2325: cst.hpos += 2;
2326: if (corrected_nr_planes_from_bplcon0 < 8 || ! copper_in_playfield (cst.vdiw, cst.hpos))
2327: cst.count += 2;
1.1.1.3 root 2328: }
2329: }
1.1.1.2 root 2330:
1.1.1.4 root 2331: void blitter_done_notify (void)
1.1 root 2332: {
1.1.1.4 root 2333: if (cop_state.state != COP_bltwait)
2334: return;
2335: update_copper (current_hpos ());
2336: cop_state.state = COP_wait2;
2337: eventtab[ev_copper].active = 1;
2338: eventtab[ev_copper].oldcycles = cycles;
2339: eventtab[ev_copper].evtime = (cycles + 2) & ~1;
2340: events_schedule ();
1.1.1.2 root 2341: }
2342:
1.1.1.4 root 2343: static void do_copper (void)
1.1.1.2 root 2344: {
1.1.1.4 root 2345: int hpos = current_hpos ();
2346: update_copper (hpos);
2347: prepare_copper ();
2348: if (eventtab[ev_copper].evtime == cycles && eventtab[ev_copper].active)
2349: abort ();
1.1.1.2 root 2350: }
2351:
1.1.1.4 root 2352: static void diskblk_handler (void)
1.1.1.2 root 2353: {
1.1.1.4 root 2354: regs.spcflags |= SPCFLAG_DISK;
2355: eventtab[ev_diskblk].active = 0;
1.1.1.2 root 2356: }
2357:
1.1.1.4 root 2358: void do_disk (void)
1.1.1.2 root 2359: {
1.1.1.4 root 2360: if (dskdmaen != 2 && (regs.spcflags & SPCFLAG_DISK)) {
2361: static int warned = 0;
2362: if (!warned)
2363: warned++, write_log ("BUG!\n");
1.1.1.2 root 2364: return;
2365: }
1.1.1.4 root 2366: if (dmaen(0x10)){
2367: if (dsklen & 0x4000) {
2368: if (!chipmem_bank.check (dskpt, 2*dsklength)) {
2369: write_log ("warning: Bad disk write DMA pointer\n");
2370: } else {
2371: uae_u8 *mfmp = get_real_address (dskpt);
2372: int i;
2373: DISK_InitWrite();
1.1.1.3 root 2374:
1.1.1.4 root 2375: for (i = 0; i < dsklength; i++) {
2376: uae_u16 d = (*mfmp << 8) + *(mfmp+1);
2377: mfmwrite[i] = d;
2378: mfmp += 2;
2379: }
2380: DISK_WriteData(dsklength);
1.1.1.2 root 2381: }
1.1.1.4 root 2382: } else {
2383: DISK_ReadMFM (dskpt);
1.1.1.2 root 2384: }
1.1.1.4 root 2385: regs.spcflags &= ~SPCFLAG_DISK;
2386: INTREQ(0x9002);
2387: dskdmaen = -1;
1.1.1.2 root 2388: }
1.1.1.4 root 2389: }
1.1.1.2 root 2390:
1.1.1.4 root 2391: static void do_sprites (int currvp, int currhp)
2392: {
2393: int i;
2394: int maxspr;
1.1.1.2 root 2395:
1.1.1.4 root 2396: #if 0
2397: if (currvp == 0)
2398: return;
2399: #else
2400: /* I don't know whether this is right. Some programs write the sprite pointers
2401: * directly at the start of the copper list. With the currvp==0 check, the
2402: * first two words of data are read on the second line in the frame. The problem
2403: * occurs when the program jumps to another copperlist a few lines further down
2404: * which _also_ writes the sprite pointer registers. This means that a) writing
2405: * to the sprite pointers sets the state to SPR_restart; or b) that sprite DMA
2406: * is disabled until the end of the vertical blanking interval. The HRM
2407: * isn't clear - it says that the vertical sprite position can be set to any
2408: * value, but this wouldn't be the first mistake... */
2409: /* Update: I modified one of the programs to write the sprite pointers the
2410: * second time only _after_ the VBlank interval, and it showed the same behaviour
2411: * as it did unmodified under UAE with the above check. This indicates that the
2412: * solution below is correct. */
2413: if (currvp < vblank_endline)
2414: return;
1.1.1.2 root 2415: #endif
1.1.1.3 root 2416:
1.1.1.4 root 2417: /* The graph in the HRM, p. 195 seems to indicate that sprite 0 is
2418: * fetched at cycle 0x14 and thus can't be disabled by bitplane DMA. */
2419: maxspr = currhp/4 - 0x14/4;
2420: if (maxspr < 0)
2421: return;
2422: if (maxspr > 8)
2423: maxspr = 8;
1.1.1.2 root 2424:
1.1.1.4 root 2425: for (i = 0; i < maxspr; i++) {
2426: int fetch = 0;
1.1.1.2 root 2427:
1.1.1.4 root 2428: if (spron[i] == 0)
2429: continue;
1.1.1.2 root 2430:
1.1.1.4 root 2431: if (sprst[i] == SPR_restart) {
2432: fetch = 2;
2433: spron[i] = 1;
2434: } else if ((sprst[i] == SPR_waiting_start && sprvstart[i] == vpos) || sprst[i] == SPR_waiting_stop) {
2435: fetch = 1;
2436: sprst[i] = SPR_waiting_stop;
1.1.1.2 root 2437: }
1.1.1.4 root 2438: if (sprst[i] == SPR_waiting_stop && sprvstop[i] == vpos) {
2439: fetch = 2;
2440: sprst[i] = SPR_waiting_start;
1.1.1.2 root 2441: }
1.1.1.3 root 2442:
1.1.1.4 root 2443: if (fetch && dmaen(DMA_SPRITE)) {
2444: uae_u16 data1 = chipmem_bank.wget(sprpt[i]);
2445: uae_u16 data2 = chipmem_bank.wget(sprpt[i]+2);
2446: sprpt[i] += 4;
1.1.1.3 root 2447:
1.1.1.4 root 2448: if (fetch == 1) {
2449: /* Hack for X mouse auto-calibration */
2450: if (i == 0 && !sprvbfl && ((sprpos[0] & 0xff) << 2) > 2 * DISPLAY_LEFT_SHIFT) {
2451: spr0ctl=sprctl[0];
2452: spr0pos=sprpos[0];
2453: sprvbfl=2;
1.1.1.3 root 2454: }
1.1.1.4 root 2455: SPRxDATB_1(data2, i);
2456: SPRxDATA_1(data1, i);
2457: } else {
2458: SPRxPOS_1(data1, i);
2459: SPRxCTL_1(data2, i);
1.1.1.3 root 2460: }
1.1 root 2461: }
2462: }
2463: }
2464:
1.1.1.4 root 2465: static void init_sprites (void)
1.1 root 2466: {
1.1.1.4 root 2467: int i;
1.1.1.2 root 2468:
1.1.1.4 root 2469: for (i = 0; i < 8; i++) {
2470: /* ???? */
2471: sprst[i] = SPR_stop;
2472: spron[i] = 0;
2473: }
1.1.1.2 root 2474:
1.1.1.4 root 2475: memset (sprpos, 0, sizeof sprpos);
2476: memset (sprctl, 0, sizeof sprctl);
2477: }
1.1.1.2 root 2478:
1.1.1.3 root 2479: static void adjust_array_sizes (void)
1.1.1.2 root 2480: {
2481: #ifdef OS_WITHOUT_MEMORY_MANAGEMENT
1.1.1.3 root 2482: if (delta_sprite_draw) {
2483: void *p1,*p2;
2484: int mcc = max_sprite_draw + 200 + delta_sprite_draw;
2485: delta_sprite_draw = 0;
2486: p1 = realloc (sprite_positions[0], mcc * sizeof (struct sprite_draw));
2487: p2 = realloc (sprite_positions[1], mcc * sizeof (struct sprite_draw));
2488: if (p1) sprite_positions[0] = p1;
2489: if (p2) sprite_positions[1] = p2;
2490: if (p1 && p2) {
2491: fprintf (stderr, "new max_sprite_draw=%d\n",mcc);
1.1.1.2 root 2492: max_sprite_draw = mcc;
2493: }
2494: }
1.1.1.3 root 2495: if (delta_color_change) {
2496: void *p1,*p2;
2497: int mcc = max_color_change + 200 + delta_color_change;
2498: delta_color_change = 0;
2499: p1 = realloc (color_changes[0], mcc * sizeof (struct color_change));
2500: p2 = realloc (color_changes[1], mcc * sizeof (struct color_change));
2501: if (p1) color_changes[0] = p1;
2502: if (p2) color_changes[1] = p2;
2503: if (p1 && p2) {
2504: fprintf (stderr, "new max_color_change=%d\n",mcc);
1.1.1.2 root 2505: max_color_change = mcc;
1.1.1.3 root 2506: }
2507: }
2508: if (delta_delay_change) {
2509: void *p;
2510: int mcc = max_delay_change + 200 + delta_delay_change;
2511: delta_delay_change = 0;
2512: p = realloc (delay_changes, mcc * sizeof (struct delay_change));
2513: if (p) {
2514: fprintf (stderr, "new max_delay_change=%d\n",mcc);
2515: delay_changes = p;
2516: max_delay_change = mcc;
2517: }
1.1.1.2 root 2518: }
2519: #endif
1.1.1.3 root 2520: }
2521:
1.1.1.4 root 2522: static void init_hardware_frame (void)
1.1.1.3 root 2523: {
1.1.1.4 root 2524: next_lineno = 0;
2525: nextline_how = nln_normal;
2526: diwstate = DIW_waiting_start;
2527: hdiwstate = DIW_waiting_start;
2528: }
1.1.1.2 root 2529:
1.1.1.4 root 2530: void init_hardware_for_drawing_frame (void)
2531: {
1.1.1.3 root 2532: adjust_array_sizes ();
2533:
1.1.1.2 root 2534: next_color_change = 0;
2535: next_delay_change = 0;
2536: next_sprite_draw = 0;
1.1.1.3 root 2537:
1.1.1.2 root 2538: next_color_entry = 0;
2539: remembered_color_entry = -1;
2540: prev_sprite_positions = sprite_positions[current_change_set];
2541: curr_sprite_positions = sprite_positions[current_change_set ^ 1];
2542: prev_color_changes = color_changes[current_change_set];
2543: curr_color_changes = color_changes[current_change_set ^ 1];
2544: prev_color_tables = color_tables[current_change_set];
2545: curr_color_tables = color_tables[current_change_set ^ 1];
1.1.1.3 root 2546:
1.1.1.2 root 2547: prev_drawinfo = line_drawinfo[current_change_set];
1.1.1.4 root 2548: curr_drawinfo = line_drawinfo[current_change_set ^ 1];
2549: current_change_set ^= 1;
1.1.1.3 root 2550:
1.1.1.4 root 2551: color_src_match = color_dest_match = -1;
1.1.1.3 root 2552: }
2553:
1.1.1.2 root 2554: static void vsync_handler (void)
1.1 root 2555: {
1.1.1.4 root 2556: if (currprefs.m68k_speed == -1) {
2557: frame_time_t curr_time = read_processor_time ();
1.1.1.3 root 2558: vsyncmintime += vsynctime;
2559: /* @@@ Mathias? How do you think we should do this? */
2560: /* If we are too far behind, or we just did a reset, adjust the
2561: * needed time. */
1.1.1.4 root 2562: if ((long int)(curr_time - vsyncmintime) > 0 || rpt_did_reset)
1.1.1.3 root 2563: vsyncmintime = curr_time + vsynctime;
1.1.1.4 root 2564: rpt_did_reset = 0;
1.1.1.3 root 2565: }
1.1.1.4 root 2566:
1.1.1.3 root 2567: handle_events ();
1.1 root 2568:
1.1.1.3 root 2569: getjoystate (0, &joy0dir, &joy0button);
2570: getjoystate (1, &joy1dir, &joy1button);
1.1 root 2571:
1.1.1.4 root 2572: INTREQ (0x8020);
1.1.1.3 root 2573: if (bplcon0 & 4)
1.1.1.2 root 2574: lof ^= 0x8000;
1.1.1.3 root 2575:
1.1.1.4 root 2576: vsync_handle_redraw (lof, lof_changed);
2577: if (quit_program > 0)
1.1.1.3 root 2578: return;
2579:
1.1.1.4 root 2580: {
1.1.1.3 root 2581: static int cnt = 0;
2582: if (cnt == 0) {
2583: /* resolution_check_change (); */
2584: DISK_check_change ();
2585: cnt = 5;
2586: }
2587: cnt--;
1.1.1.2 root 2588: }
1.1.1.3 root 2589:
1.1.1.2 root 2590: lof_changed = 0;
1.1.1.3 root 2591:
1.1.1.4 root 2592: cop_state.ip = cop1lc;
2593: cop_state.state = COP_read1;
2594: cop_state.vpos = 0;
2595: cop_state.hpos = 0;
2596: cop_state.count = 0;
2597: cop_state.do_move = 0;
2598: cop_state.ignore_next = 0;
2599: cop_state.vdiw = DIW_waiting_start;
2600:
2601: init_hardware_frame ();
2602:
1.1 root 2603: #ifdef HAVE_GETTIMEOFDAY
2604: {
2605: struct timeval tv;
2606: unsigned long int newtime;
1.1.1.3 root 2607:
2608: gettimeofday(&tv,NULL);
1.1 root 2609: newtime = (tv.tv_sec-seconds_base) * 1000 + tv.tv_usec / 1000;
1.1.1.3 root 2610:
2611: if (!bogusframe) {
1.1.1.6 ! root 2612: lastframetime = newtime - msecs;
! 2613:
! 2614: #if 0 /* This doesn't appear to work too well yet... later. */
! 2615: if (n_consecutive_skipped > currprefs.sound_pri_cutoff
! 2616: || lastframetime < currprefs.sound_pri_time)
! 2617: {
! 2618: n_consecutive_skipped = 0;
! 2619: clear_inhibit_frame (IHF_SOUNDADJUST);
! 2620: } else {
! 2621: n_consecutive_skipped++;
! 2622: set_inhibit_frame (IHF_SOUNDADJUST);
! 2623: total_skipped++;
! 2624: }
! 2625: #endif
! 2626:
! 2627: frametime += lastframetime;
1.1 root 2628: timeframes++;
1.1.1.6 ! root 2629:
! 2630: if ((timeframes & 127) == 0)
! 2631: gui_fps (1000 * timeframes / frametime);
1.1 root 2632: }
2633: msecs = newtime;
2634: bogusframe = 0;
2635: }
2636: #endif
1.1.1.3 root 2637: if (ievent_alive > 0)
2638: ievent_alive--;
1.1 root 2639: CIA_vsync_handler();
2640: }
2641:
1.1.1.4 root 2642: static void hsync_handler (void)
1.1 root 2643: {
1.1.1.4 root 2644: update_copper (maxhpos);
1.1.1.3 root 2645:
1.1.1.2 root 2646: finish_decisions ();
1.1.1.4 root 2647: do_modulos (current_hpos ());
1.1.1.2 root 2648:
1.1.1.5 root 2649: hsync_record_line_state (next_lineno, nextline_how, thisline_changed);
1.1 root 2650:
2651: eventtab[ev_hsync].evtime += cycles - eventtab[ev_hsync].oldcycles;
2652: eventtab[ev_hsync].oldcycles = cycles;
2653: CIA_hsync_handler();
1.1.1.3 root 2654:
2655: if (currprefs.produce_sound > 0) {
1.1 root 2656: int nr;
1.1.1.4 root 2657:
2658: update_audio ();
2659:
1.1 root 2660: /* Sound data is fetched at the beginning of each line */
2661: for (nr = 0; nr < 4; nr++) {
2662: struct audio_channel_data *cdp = audio_channel + nr;
1.1.1.3 root 2663:
1.1 root 2664: if (cdp->data_written == 2) {
2665: cdp->data_written = 0;
2666: cdp->nextdat = chipmem_bank.wget(cdp->pt);
2667: cdp->pt += 2;
2668: if (cdp->state == 2 || cdp->state == 3) {
2669: if (cdp->wlen == 1) {
2670: cdp->pt = cdp->lc;
2671: cdp->wlen = cdp->len;
2672: cdp->intreq2 = 1;
2673: } else
2674: cdp->wlen--;
2675: }
2676: }
2677: }
2678: }
1.1.1.4 root 2679:
2680: hardware_line_completed (next_lineno);
2681:
1.1 root 2682: if (++vpos == (maxvpos + (lof != 0))) {
2683: vpos = 0;
2684: vsync_handler();
2685: }
1.1.1.2 root 2686:
1.1.1.4 root 2687: is_lastline = vpos + 1 == maxvpos + (lof != 0) && currprefs.m68k_speed == -1 && ! rpt_did_reset;
1.1.1.3 root 2688:
1.1.1.4 root 2689: if ((bplcon0 & 4) && currprefs.gfx_linedbl)
2690: notice_interlace_seen ();
1.1.1.2 root 2691:
2692: if (framecnt == 0) {
1.1.1.4 root 2693: int lineno = vpos;
2694: nextline_how = nln_normal;
1.1.1.3 root 2695: if (currprefs.gfx_linedbl) {
1.1 root 2696: lineno *= 2;
1.1.1.5 root 2697: nextline_how = currprefs.gfx_linedbl == 1 ? nln_doubled : nln_nblack;
1.1.1.2 root 2698: if (bplcon0 & 4) {
2699: if (!lof) {
2700: lineno++;
1.1.1.4 root 2701: nextline_how = nln_lower;
1.1.1.2 root 2702: } else {
1.1.1.4 root 2703: nextline_how = nln_upper;
1.1.1.2 root 2704: }
1.1 root 2705: }
2706: }
2707: next_lineno = lineno;
1.1.1.2 root 2708: reset_decisions ();
1.1 root 2709: }
1.1.1.3 root 2710: if (uae_int_requested) {
2711: set_uae_int_flag ();
2712: INTREQ (0xA000);
2713: }
1.1.1.4 root 2714: update_copper (0);
2715: prepare_copper ();
2716: if (eventtab[ev_copper].evtime == cycles && eventtab[ev_copper].active)
2717: abort ();
1.1.1.3 root 2718: }
2719:
2720: static void init_eventtab (void)
2721: {
2722: int i;
2723:
2724: for(i = 0; i < ev_max; i++) {
2725: eventtab[i].active = 0;
2726: eventtab[i].oldcycles = 0;
2727: }
1.1.1.4 root 2728:
1.1.1.3 root 2729: eventtab[ev_cia].handler = CIA_handler;
1.1.1.4 root 2730: eventtab[ev_copper].handler = do_copper;
1.1.1.3 root 2731: eventtab[ev_hsync].handler = hsync_handler;
2732: eventtab[ev_hsync].evtime = maxhpos + cycles;
2733: eventtab[ev_hsync].active = 1;
2734:
2735: eventtab[ev_blitter].handler = blitter_handler;
2736: eventtab[ev_blitter].active = 0;
2737: eventtab[ev_diskblk].handler = diskblk_handler;
2738: eventtab[ev_diskblk].active = 0;
2739: eventtab[ev_diskindex].handler = diskindex_handler;
2740: eventtab[ev_diskindex].active = 0;
1.1.1.4 root 2741:
1.1.1.3 root 2742: events_schedule ();
1.1 root 2743: }
2744:
1.1.1.3 root 2745: void customreset (void)
1.1 root 2746: {
1.1.1.4 root 2747: int i;
1.1 root 2748: #ifdef HAVE_GETTIMEOFDAY
2749: struct timeval tv;
2750: #endif
1.1.1.2 root 2751:
1.1.1.3 root 2752: for (i = 0; i < sizeof current_colors.color_regs / sizeof *current_colors.color_regs; i++)
2753: current_colors.color_regs[i] = -1;
2754:
2755: expamem_reset ();
1.1.1.4 root 2756:
1.1.1.3 root 2757: CIA_reset ();
2758: cycles = 0;
1.1 root 2759: regs.spcflags &= SPCFLAG_BRK;
1.1.1.3 root 2760:
1.1.1.2 root 2761: vpos = 0;
1.1 root 2762: lof = 0;
1.1.1.3 root 2763:
1.1.1.4 root 2764: if (needmousehack ()) {
1.1.1.3 root 2765: #if 0
1.1.1.4 root 2766: mousehack_setfollow();
1.1.1.3 root 2767: #else
1.1.1.4 root 2768: mousehack_setdontcare();
1.1.1.3 root 2769: #endif
1.1 root 2770: } else {
2771: mousestate = normal_mouse;
2772: }
1.1.1.3 root 2773: ievent_alive = 0;
2774:
2775: clx_sprmask = 0xFF;
2776: clxdat = 0;
2777:
1.1.1.4 root 2778: memset (sprarmed, 0, sizeof sprarmed);
1.1.1.3 root 2779: nr_armed = 0;
1.1 root 2780:
2781: dmacon = intena = 0;
2782: bltstate = BLT_done;
1.1.1.4 root 2783: cop_state.state = COP_stop;
1.1.1.2 root 2784: diwstate = DIW_waiting_start;
1.1.1.3 root 2785: hdiwstate = DIW_waiting_start;
1.1 root 2786: copcon = 0;
2787: dskdmaen = 0;
2788: cycles = 0;
1.1.1.3 root 2789:
2790: audio_reset ();
1.1 root 2791:
1.1.1.2 root 2792: bplcon4 = 0x11; /* Get AGA chipset into ECS compatibility mode */
2793: bplcon3 = 0xC00;
1.1 root 2794:
1.1.1.3 root 2795: init_eventtab ();
1.1.1.2 root 2796:
1.1.1.3 root 2797: init_sprites ();
2798:
1.1.1.2 root 2799: init_hardware_frame ();
1.1.1.4 root 2800: reset_drawing ();
2801:
1.1.1.2 root 2802: reset_decisions ();
1.1.1.3 root 2803:
1.1 root 2804: #ifdef HAVE_GETTIMEOFDAY
1.1.1.4 root 2805: gettimeofday (&tv, NULL);
1.1 root 2806: seconds_base = tv.tv_sec;
2807: bogusframe = 1;
2808: #endif
2809: }
2810:
1.1.1.3 root 2811: void dumpcustom (void)
1.1 root 2812: {
1.1.1.6 ! root 2813: write_log ("DMACON: %x INTENA: %x INTREQ: %x VPOS: %x HPOS: %x\n", DMACONR(),
! 2814: (unsigned int)intena, (unsigned int)intreq, (unsigned int)vpos, (unsigned int)current_hpos());
! 2815: write_log ("COP1LC: %08lx, COP2LC: %08lx\n", (unsigned long)cop1lc, (unsigned long)cop2lc);
1.1.1.3 root 2816: if (timeframes) {
1.1.1.6 ! root 2817: write_log ("Average frame time: %d ms [frames: %d time: %d]\n",
! 2818: frametime / timeframes, timeframes, frametime);
! 2819: if (total_skipped)
! 2820: write_log ("Skipped frames: %d\n", total_skipped);
1.1 root 2821: }
1.1.1.3 root 2822: dump_audio_bench ();
1.1.1.4 root 2823: /*for (i=0; i<256; i++) if (blitcount[i]) fprintf (stderr, "minterm %x = %d\n",i,blitcount[i]); blitter debug */
1.1 root 2824: }
2825:
1.1.1.3 root 2826: int intlev (void)
1.1 root 2827: {
1.1.1.3 root 2828: uae_u16 imask = intreq & intena;
1.1 root 2829: if (imask && (intena & 0x4000)){
2830: if (imask & 0x2000) return 6;
2831: if (imask & 0x1800) return 5;
2832: if (imask & 0x0780) return 4;
2833: if (imask & 0x0070) return 3;
2834: if (imask & 0x0008) return 2;
2835: if (imask & 0x0007) return 1;
2836: }
2837: return -1;
2838: }
2839:
1.1.1.4 root 2840: static void gen_custom_tables (void)
2841: {
2842: int i;
2843: for (i = 0; i < 256; i++) {
2844: unsigned int j;
2845: sprtaba[i] = ((((i >> 7) & 1) << 0)
2846: | (((i >> 6) & 1) << 2)
2847: | (((i >> 5) & 1) << 4)
2848: | (((i >> 4) & 1) << 6)
2849: | (((i >> 3) & 1) << 8)
2850: | (((i >> 2) & 1) << 10)
2851: | (((i >> 1) & 1) << 12)
2852: | (((i >> 0) & 1) << 14));
2853: sprtabb[i] = sprtaba[i] * 2;
2854: for (j = 0; j < 511; j = (j << 1) | 1)
2855: if ((i & ~j) == 0)
2856: waitmasktab[i] = ~j;
2857: }
2858: }
2859:
1.1.1.3 root 2860: void custom_init (void)
1.1 root 2861: {
1.1.1.4 root 2862: #ifdef OS_WITHOUT_MEMORY_MANAGEMENT
1.1 root 2863: int num;
1.1.1.2 root 2864:
1.1.1.4 root 2865: for (num = 0; num < 2; num++) {
2866: sprite_positions[num] = xmalloc (max_sprite_draw * sizeof (struct sprite_draw));
2867: color_changes[num] = xmalloc (max_color_change * sizeof (struct color_change));
1.1.1.2 root 2868: }
1.1.1.4 root 2869: delay_changes = xmalloc (max_delay_change * sizeof (struct delay_change));
1.1.1.2 root 2870: #endif
1.1.1.3 root 2871:
1.1.1.2 root 2872: init_decisions ();
2873:
1.1.1.4 root 2874: gen_custom_tables ();
2875: build_blitfilltable ();
2876:
2877: drawing_init ();
2878:
2879: mousestate = unknown_mouse;
2880:
2881: if (needmousehack ())
2882: mousehack_setfollow();
1.1 root 2883: }
2884:
2885: /* Custom chip memory bank */
2886:
1.1.1.4 root 2887: static uae_u32 custom_lget (uaecptr) REGPARAM;
2888: static uae_u32 custom_wget (uaecptr) REGPARAM;
2889: static uae_u32 custom_bget (uaecptr) REGPARAM;
2890: static void custom_lput (uaecptr, uae_u32) REGPARAM;
2891: static void custom_wput (uaecptr, uae_u32) REGPARAM;
2892: static void custom_bput (uaecptr, uae_u32) REGPARAM;
1.1 root 2893:
2894: addrbank custom_bank = {
2895: custom_lget, custom_wget, custom_bget,
2896: custom_lput, custom_wput, custom_bput,
2897: default_xlate, default_check
2898: };
2899:
1.1.1.3 root 2900: uae_u32 REGPARAM2 custom_wget (uaecptr addr)
1.1 root 2901: {
1.1.1.4 root 2902: switch (addr & 0x1FE) {
1.1 root 2903: case 0x002: return DMACONR();
2904: case 0x004: return VPOSR();
2905: case 0x006: return VHPOSR();
1.1.1.3 root 2906:
1.1 root 2907: case 0x008: return DSKDATR();
2908:
2909: case 0x00A: return JOY0DAT();
2910: case 0x00C: return JOY1DAT();
1.1.1.3 root 2911: case 0x00E: return CLXDAT();
1.1 root 2912: case 0x010: return ADKCONR();
2913:
2914: case 0x012: return POT0DAT();
2915: case 0x016: return POTGOR();
2916: case 0x018: return SERDATR();
2917: case 0x01A: return DSKBYTR();
2918: case 0x01C: return INTENAR();
2919: case 0x01E: return INTREQR();
2920: #if AGA_CHIPSET == 1
2921: case 0x07C: return 0xF8;
2922: #elif defined ECS_DENISE
2923: case 0x07C: return 0xFC;
2924: #endif
2925: default:
2926: custom_wput(addr,0);
2927: return 0xffff;
2928: }
2929: }
2930:
1.1.1.3 root 2931: uae_u32 REGPARAM2 custom_bget (uaecptr addr)
1.1 root 2932: {
2933: return custom_wget(addr & 0xfffe) >> (addr & 1 ? 0 : 8);
2934: }
2935:
1.1.1.3 root 2936: uae_u32 REGPARAM2 custom_lget (uaecptr addr)
1.1 root 2937: {
1.1.1.3 root 2938: return ((uae_u32)custom_wget(addr & 0xfffe) << 16) | custom_wget((addr+2) & 0xfffe);
1.1 root 2939: }
2940:
1.1.1.4 root 2941: void REGPARAM2 custom_wput_1 (int hpos, uaecptr addr, uae_u32 value)
1.1 root 2942: {
2943: addr &= 0x1FE;
1.1.1.3 root 2944: switch (addr) {
2945: case 0x020: DSKPTH (value); break;
2946: case 0x022: DSKPTL (value); break;
2947: case 0x024: DSKLEN (value); break;
2948: case 0x026: DSKDAT (value); break;
2949:
2950: case 0x02A: VPOSW (value); break;
2951: case 0x2E: COPCON (value); break;
2952: case 0x030: SERDAT (value); break;
2953: case 0x032: SERPER (value); break;
2954: case 0x34: POTGO (value); break;
2955: case 0x040: BLTCON0 (value); break;
2956: case 0x042: BLTCON1 (value); break;
2957:
2958: case 0x044: BLTAFWM (value); break;
2959: case 0x046: BLTALWM (value); break;
2960:
2961: case 0x050: BLTAPTH (value); break;
2962: case 0x052: BLTAPTL (value); break;
2963: case 0x04C: BLTBPTH (value); break;
2964: case 0x04E: BLTBPTL (value); break;
2965: case 0x048: BLTCPTH (value); break;
2966: case 0x04A: BLTCPTL (value); break;
2967: case 0x054: BLTDPTH (value); break;
2968: case 0x056: BLTDPTL (value); break;
2969:
2970: case 0x058: BLTSIZE (value); break;
2971:
2972: case 0x064: BLTAMOD (value); break;
2973: case 0x062: BLTBMOD (value); break;
2974: case 0x060: BLTCMOD (value); break;
2975: case 0x066: BLTDMOD (value); break;
2976:
2977: case 0x070: BLTCDAT (value); break;
2978: case 0x072: BLTBDAT (value); break;
2979: case 0x074: BLTADAT (value); break;
2980:
2981: case 0x07E: DSKSYNC (value); break;
2982:
2983: case 0x080: COP1LCH (value); break;
2984: case 0x082: COP1LCL (value); break;
2985: case 0x084: COP2LCH (value); break;
2986: case 0x086: COP2LCL (value); break;
2987:
2988: case 0x088: COPJMP1 (value); break;
2989: case 0x08A: COPJMP2 (value); break;
2990:
1.1.1.4 root 2991: case 0x08E: DIWSTRT (hpos, value); break;
2992: case 0x090: DIWSTOP (hpos, value); break;
2993: case 0x092: DDFSTRT (hpos, value); break;
2994: case 0x094: DDFSTOP (hpos, value); break;
1.1.1.3 root 2995:
2996: case 0x096: DMACON (value); break;
2997: case 0x098: CLXCON (value); break;
2998: case 0x09A: INTENA (value); break;
2999: case 0x09C: INTREQ (value); break;
3000: case 0x09E: ADKCON (value); break;
3001:
3002: case 0x0A0: AUDxLCH (0, value); break;
3003: case 0x0A2: AUDxLCL (0, value); break;
3004: case 0x0A4: AUDxLEN (0, value); break;
3005: case 0x0A6: AUDxPER (0, value); break;
3006: case 0x0A8: AUDxVOL (0, value); break;
3007: case 0x0AA: AUDxDAT (0, value); break;
3008:
3009: case 0x0B0: AUDxLCH (1, value); break;
3010: case 0x0B2: AUDxLCL (1, value); break;
3011: case 0x0B4: AUDxLEN (1, value); break;
3012: case 0x0B6: AUDxPER (1, value); break;
3013: case 0x0B8: AUDxVOL (1, value); break;
3014: case 0x0BA: AUDxDAT (1, value); break;
3015:
3016: case 0x0C0: AUDxLCH (2, value); break;
3017: case 0x0C2: AUDxLCL (2, value); break;
3018: case 0x0C4: AUDxLEN (2, value); break;
3019: case 0x0C6: AUDxPER (2, value); break;
3020: case 0x0C8: AUDxVOL (2, value); break;
3021: case 0x0CA: AUDxDAT (2, value); break;
3022:
3023: case 0x0D0: AUDxLCH (3, value); break;
3024: case 0x0D2: AUDxLCL (3, value); break;
3025: case 0x0D4: AUDxLEN (3, value); break;
3026: case 0x0D6: AUDxPER (3, value); break;
3027: case 0x0D8: AUDxVOL (3, value); break;
3028: case 0x0DA: AUDxDAT (3, value); break;
3029:
1.1.1.4 root 3030: case 0x0E0: BPLPTH (hpos, value, 0); break;
3031: case 0x0E2: BPLPTL (hpos, value, 0); break;
3032: case 0x0E4: BPLPTH (hpos, value, 1); break;
3033: case 0x0E6: BPLPTL (hpos, value, 1); break;
3034: case 0x0E8: BPLPTH (hpos, value, 2); break;
3035: case 0x0EA: BPLPTL (hpos, value, 2); break;
3036: case 0x0EC: BPLPTH (hpos, value, 3); break;
3037: case 0x0EE: BPLPTL (hpos, value, 3); break;
3038: case 0x0F0: BPLPTH (hpos, value, 4); break;
3039: case 0x0F2: BPLPTL (hpos, value, 4); break;
3040: case 0x0F4: BPLPTH (hpos, value, 5); break;
3041: case 0x0F6: BPLPTL (hpos, value, 5); break;
1.1.1.6 ! root 3042: case 0x0F8: BPLPTH (hpos, value, 6); break;
! 3043: case 0x0FA: BPLPTL (hpos, value, 6); break;
! 3044: case 0x0FC: BPLPTH (hpos, value, 7); break;
! 3045: case 0x0FE: BPLPTL (hpos, value, 7); break;
1.1.1.4 root 3046:
3047: case 0x100: BPLCON0 (hpos, value); break;
3048: case 0x102: BPLCON1 (hpos, value); break;
3049: case 0x104: BPLCON2 (hpos, value); break;
3050: case 0x106: BPLCON3 (hpos, value); break;
1.1.1.3 root 3051:
1.1.1.4 root 3052: case 0x108: BPL1MOD (hpos, value); break;
3053: case 0x10A: BPL2MOD (hpos, value); break;
1.1.1.3 root 3054:
3055: case 0x110: BPL1DAT (value); break;
3056: case 0x112: BPL2DAT (value); break;
3057: case 0x114: BPL3DAT (value); break;
3058: case 0x116: BPL4DAT (value); break;
3059: case 0x118: BPL5DAT (value); break;
3060: case 0x11A: BPL6DAT (value); break;
1.1.1.6 ! root 3061: case 0x11C: BPL7DAT (value); break;
! 3062: case 0x11E: BPL8DAT (value); break;
1.1 root 3063:
3064: case 0x180: case 0x182: case 0x184: case 0x186: case 0x188: case 0x18A:
3065: case 0x18C: case 0x18E: case 0x190: case 0x192: case 0x194: case 0x196:
3066: case 0x198: case 0x19A: case 0x19C: case 0x19E: case 0x1A0: case 0x1A2:
3067: case 0x1A4: case 0x1A6: case 0x1A8: case 0x1AA: case 0x1AC: case 0x1AE:
3068: case 0x1B0: case 0x1B2: case 0x1B4: case 0x1B6: case 0x1B8: case 0x1BA:
1.1.1.2 root 3069: case 0x1BC: case 0x1BE:
1.1.1.4 root 3070: COLOR (hpos, value & 0xFFF, (addr & 0x3E) / 2);
1.1.1.2 root 3071: break;
3072: case 0x120: case 0x124: case 0x128: case 0x12C:
1.1 root 3073: case 0x130: case 0x134: case 0x138: case 0x13C:
1.1.1.4 root 3074: SPRxPTH (hpos, value, (addr - 0x120) / 4);
1.1 root 3075: break;
1.1.1.2 root 3076: case 0x122: case 0x126: case 0x12A: case 0x12E:
1.1 root 3077: case 0x132: case 0x136: case 0x13A: case 0x13E:
1.1.1.4 root 3078: SPRxPTL (hpos, value, (addr - 0x122) / 4);
1.1 root 3079: break;
1.1.1.2 root 3080: case 0x140: case 0x148: case 0x150: case 0x158:
1.1 root 3081: case 0x160: case 0x168: case 0x170: case 0x178:
1.1.1.4 root 3082: SPRxPOS (hpos, value, (addr - 0x140) / 8);
1.1 root 3083: break;
1.1.1.2 root 3084: case 0x142: case 0x14A: case 0x152: case 0x15A:
1.1 root 3085: case 0x162: case 0x16A: case 0x172: case 0x17A:
1.1.1.4 root 3086: SPRxCTL (hpos, value, (addr - 0x142) / 8);
1.1 root 3087: break;
3088: case 0x144: case 0x14C: case 0x154: case 0x15C:
3089: case 0x164: case 0x16C: case 0x174: case 0x17C:
1.1.1.4 root 3090: SPRxDATA (hpos, value, (addr - 0x144) / 8);
1.1 root 3091: break;
1.1.1.2 root 3092: case 0x146: case 0x14E: case 0x156: case 0x15E:
1.1 root 3093: case 0x166: case 0x16E: case 0x176: case 0x17E:
1.1.1.4 root 3094: SPRxDATB (hpos, value, (addr - 0x146) / 8);
1.1 root 3095: break;
1.1.1.3 root 3096:
3097: case 0x36: JOYTEST (value); break;
3098: #if defined (ECS_AGNUS) || (AGA_CHIPSET == 1)
1.1 root 3099: case 0x5A: BLTCON0L(value); break;
1.1.1.3 root 3100: case 0x5C: BLTSIZV (value); break;
3101: case 0x5E: BLTSIZH (value); break;
1.1 root 3102: #endif
1.1.1.4 root 3103: #ifdef ECS_DENISE
3104: case 0x1E4: DIWHIGH (hpos, value); break;
3105: #endif
1.1 root 3106: #if AGA_CHIPSET == 1
1.1.1.4 root 3107: case 0x10C: BPLCON4 (hpos, value); break;
1.1.1.5 root 3108: case 0x1FC: fmode = value; calcdiw (); break;
1.1 root 3109: #endif
3110: }
3111: }
3112:
1.1.1.4 root 3113: void REGPARAM2 custom_wput (uaecptr addr, uae_u32 value)
3114: {
3115: int hpos = current_hpos ();
3116: update_copper (hpos);
3117: custom_wput_1 (hpos, addr, value);
3118: }
3119:
3120: void REGPARAM2 custom_bput (uaecptr addr, uae_u32 value)
1.1 root 3121: {
1.1.1.2 root 3122: static int warned = 0;
3123: /* Is this correct now? (There are people who bput things to the upper byte of AUDxVOL). */
1.1.1.3 root 3124: uae_u16 rval = (value << 8) | (value & 0xFF);
1.1.1.2 root 3125: custom_wput(addr, rval);
3126: if (!warned)
1.1.1.3 root 3127: write_log ("Byte put to custom register.\n"), warned++;
1.1 root 3128: }
3129:
1.1.1.3 root 3130: void REGPARAM2 custom_lput(uaecptr addr, uae_u32 value)
1.1 root 3131: {
3132: custom_wput(addr & 0xfffe, value >> 16);
1.1.1.3 root 3133: custom_wput((addr+2) & 0xfffe, (uae_u16)value);
1.1 root 3134: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.