|
|
1.1.1.2 root 1: /*
1.1.1.3 root 2: * Hatari - blitter.c
1.1.1.2 root 3: *
1.1.1.12 root 4: * This file is distributed under the GNU General Public License, version 2
5: * or at your option any later version. Read the file gpl.txt for details.
1.1.1.2 root 6: *
1.1.1.7 root 7: * Blitter emulation. The 'Blitter' chip is found in the Mega-ST, STE/Mega-STE
8: * and Falcon. It provides a very fast BitBlit function in hardware.
1.1 root 9: *
1.1.1.7 root 10: * This file has originally been taken from STonX, but it has been completely
11: * modified for better maintainability and higher compatibility.
1.1.1.8 root 12: *
13: * NOTES:
14: * ----------------------------------------------------------------------------
15: * Strange end mask condition ((~(0xffff>>skew)) > end_mask_1)
16: *
17: * """Similarly the NFSR (aka post-flush) bit, when set, will prevent the last
18: * source read of the line. This read may not be necessary with certain
19: * combinations of end masks and skews."""
20: * - doesn't mean the blitter will skip source read by itself, just a hint
21: * for developers as far as i understand it.
22: * ----------------------------------------------------------------------------
23: * Does smudge mode change the line register ?
24: * ----------------------------------------------------------------------------
1.1 root 25: */
1.1.1.9 root 26:
1.1.1.8 root 27: const char Blitter_fileid[] = "Hatari blitter.c : " __DATE__ " " __TIME__;
1.1 root 28:
29: #include <SDL_types.h>
30: #include <stdio.h>
31: #include <stdlib.h>
32:
1.1.1.2 root 33: #include "main.h"
1.1 root 34: #include "blitter.h"
1.1.1.7 root 35: #include "configuration.h"
36: #include "dmaSnd.h"
1.1.1.3 root 37: #include "ioMem.h"
38: #include "m68000.h"
1.1.1.7 root 39: #include "mfp.h"
1.1.1.2 root 40: #include "memorySnapShot.h"
1.1 root 41: #include "stMemory.h"
1.1.1.11 root 42: #include "screen.h"
1.1.1.7 root 43: #include "video.h"
44:
1.1.1.8 root 45: /* Cycles to run for in non-hog mode */
46: #define NONHOG_CYCLES (64*4)
1.1 root 47:
1.1.1.8 root 48: /* BLiTTER registers, incs are signed, others unsigned */
49: #define REG_HT_RAM 0xff8a00 /* - 0xff8a1e */
1.1 root 50:
1.1.1.4 root 51: #define REG_SRC_X_INC 0xff8a20
52: #define REG_SRC_Y_INC 0xff8a22
53: #define REG_SRC_ADDR 0xff8a24
54:
55: #define REG_END_MASK1 0xff8a28
56: #define REG_END_MASK2 0xff8a2a
57: #define REG_END_MASK3 0xff8a2c
58:
59: #define REG_DST_X_INC 0xff8a2e
60: #define REG_DST_Y_INC 0xff8a30
61: #define REG_DST_ADDR 0xff8a32
62:
1.1.1.7 root 63: #define REG_X_COUNT 0xff8a36
64: #define REG_Y_COUNT 0xff8a38
1.1.1.4 root 65:
66: #define REG_BLIT_HOP 0xff8a3a /* halftone blit operation byte */
67: #define REG_BLIT_LOP 0xff8a3b /* logical blit operation byte */
1.1.1.7 root 68: #define REG_CONTROL 0xff8a3c
1.1.1.8 root 69: #define REG_SKEW 0xff8a3d
1.1.1.4 root 70:
1.1.1.14 root 71:
72: #define BLITTER_READ_WORD_BUS_ERR 0x0000 /* This value is returned when the blitter try to read a word */
73: /* in a region that would cause a bus error */
74: /* [NP] FIXME : for now we return a constant, but it should depend on the bus activity */
75:
1.1.1.8 root 76: /* Blitter registers */
77: typedef struct
78: {
79: Uint32 src_addr;
80: Uint32 dst_addr;
81: Uint32 words;
82: Uint32 lines;
83: short src_x_incr;
84: short src_y_incr;
85: short dst_x_incr;
86: short dst_y_incr;
87: Uint16 end_mask_1;
88: Uint16 end_mask_2;
89: Uint16 end_mask_3;
90: Uint8 hop;
91: Uint8 lop;
92: Uint8 ctrl;
93: Uint8 skew;
94: } BLITTERREGS;
95:
96: /* Blitter vars */
97: typedef struct
98: {
1.1.1.9 root 99: int pass_cycles;
100: int op_cycles;
1.1.1.8 root 101: Uint32 buffer;
102: Uint32 src_words_reset;
103: Uint32 dst_words_reset;
104: Uint32 src_words;
105: Uint8 hog;
106: Uint8 smudge;
107: Uint8 line;
108: Uint8 fxsr;
109: Uint8 nfsr;
110: Uint8 skew;
111: } BLITTERVARS;
112:
113: /* Blitter state */
114: typedef struct
115: {
116: Uint16 src_word;
117: Uint16 dst_word;
118: Uint16 end_mask;
119: Uint8 have_src;
120: Uint8 have_dst;
121: Uint8 fxsr;
122: Uint8 nfsr;
123: } BLITTERSTATE;
124:
1.1.1.9 root 125: /* Blitter logical op func */
126: typedef Uint16 (*BLITTER_OP_FUNC)(void);
127:
1.1.1.8 root 128: static BLITTERREGS BlitterRegs;
129: static BLITTERVARS BlitterVars;
130: static BLITTERSTATE BlitterState;
131: static Uint16 BlitterHalftone[16];
1.1.1.3 root 132:
1.1.1.9 root 133: static BLITTER_OP_FUNC Blitter_ComputeHOP;
134: static BLITTER_OP_FUNC Blitter_ComputeLOP;
135:
1.1.1.8 root 136: /*-----------------------------------------------------------------------*/
137: /**
1.1.1.15! root 138: * Count blitter cycles (this assumes blitter and CPU runs at the same freq)
1.1.1.8 root 139: */
1.1.1.3 root 140:
1.1.1.8 root 141: static void Blitter_AddCycles(int cycles)
1.1.1.4 root 142: {
1.1.1.15! root 143: int all_cycles = cycles + WaitStateCycles;
1.1.1.4 root 144:
1.1.1.9 root 145: BlitterVars.op_cycles += all_cycles;
1.1.1.4 root 146:
1.1.1.15! root 147: #ifdef OLD_CPU_SHIFT
1.1.1.9 root 148: nCyclesMainCounter += all_cycles >> nCpuFreqShift;
1.1.1.14 root 149: CyclesGlobalClockCounter += all_cycles >> nCpuFreqShift;
1.1.1.15! root 150: #else
! 151: nCyclesMainCounter += all_cycles;
! 152: CyclesGlobalClockCounter += all_cycles;
! 153: #endif
! 154: WaitStateCycles = 0;
1.1.1.9 root 155: }
156:
157: static void Blitter_FlushCycles(void)
158: {
1.1.1.15! root 159: #ifdef OLD_CPU_SHIFT
! 160: int op_cycles = INT_CONVERT_TO_INTERNAL(BlitterVars.op_cycles >> nCpuFreqShift, INT_CPU_CYCLE);
! 161: #else
1.1.1.9 root 162: int op_cycles = INT_CONVERT_TO_INTERNAL(BlitterVars.op_cycles, INT_CPU_CYCLE);
1.1.1.15! root 163: #endif
1.1.1.9 root 164:
165: BlitterVars.pass_cycles += BlitterVars.op_cycles;
166: BlitterVars.op_cycles = 0;
1.1.1.8 root 167:
1.1.1.9 root 168: PendingInterruptCount -= op_cycles;
1.1.1.8 root 169: while (PendingInterruptCount <= 0 && PendingInterruptFunction)
170: CALL_VAR(PendingInterruptFunction);
1.1 root 171: }
172:
1.1.1.14 root 173:
174: /*-----------------------------------------------------------------------*/
175: /**
176: * Handle bus arbitration when switching between CPU and Blitter
177: * When a write is made to FF8A3C to start the blitter, it will take a few cycles
178: * before doing the bus arbitration. During this time the CPU will be able to
179: * partially execute the next instruction in parallel to the blitter
180: * (until an access to the BUS is needed by the CPU).
181: *
182: * NOTE [NP] : this is mostly handled with hardcoded cases for now, as it
183: * requires cycle exact emulation to exactly know when bus is accessed
184: * by the CPU to prefetch the next word.
185: * More tests are needed on a real STE to have a proper model of this.
186: *
187: * Based on several examples, possible sequence when starting the blitter seems to be :
188: * - t+0 : write to FF8A3C
189: * - t+0 : CPU can still run during 4 cycles and access bus
190: * - t+4 : bus arbitration takes 4 cycles (no access for cpu and blitter during this time)
191: * - t+8 : blitter owns the bus and starts tranferring data
1.1.1.15! root 192: * (in case of MegaSTE bus arbitration takes 8 cycles instead of 4)
1.1.1.14 root 193: *
194: * When blitter stops owning the bus in favor of the cpu, this seems to always take 4 cycles
195: */
196: static void Blitter_BusArbitration ( int RequestBusMode )
197: {
198: int cycles;
199:
200: if ( RequestBusMode == BUS_MODE_BLITTER ) /* Bus is requested by the blitter */
201: {
202: //fprintf ( stderr , "blitter start pc %x %x\n" , M68000_GetPC() , M68000_InstrPC );
1.1.1.15! root 203: if ( ConfigureParams.System.nMachineType == MACHINE_MEGA_STE )
! 204: cycles = 8; /* MegaSTE blitter needs 4 extra cycles when requesting the bus */
! 205: else
! 206: cycles = 4; /* Default case : take 4 cycles when going from cpu to blitter */
1.1.1.14 root 207:
208: /* Different timing for some specific cases */
209:
210: /* 'Relapse - Graphix Sound 2' by Cybernetics (overscan plasma using blitter) */
211: /* $e764 : move.b d5,(a4) + dbra d1,$fff2 : 4 cycles of the dbra can be executed while blitter starts */
212: if ( STMemory_ReadLong ( M68000_InstrPC ) == 0x188551c9 ) /* PC = E764 */
1.1.1.15! root 213: cycles -= 4; /* 4 cycles less than default case */
1.1.1.14 root 214: }
215:
216: else /* Bus is requested by the cpu */
217: {
1.1.1.15! root 218: cycles = 4; /* Always 4 cycles (even for MegaSTE) */
1.1.1.14 root 219: }
220:
221: /* Add arbitration cycles and update BusMode */
222: if ( cycles > 0 )
223: {
224: Blitter_AddCycles(cycles);
225: Blitter_FlushCycles();
226: }
227: BusMode = RequestBusMode;
228: }
229:
230:
1.1.1.8 root 231: /*-----------------------------------------------------------------------*/
232: /**
233: * Read & Write operations
234: */
235: static Uint16 Blitter_ReadWord(Uint32 addr)
1.1.1.7 root 236: {
1.1.1.8 root 237: Uint16 value;
1.1 root 238:
1.1.1.14 root 239: /* When reading from a bus error region, just return a constant */
240: if ( STMemory_CheckRegionBusError ( addr ) )
241: value = BLITTER_READ_WORD_BUS_ERR;
1.1.1.8 root 242: else
1.1.1.14 root 243: value = (Uint16)get_word ( addr );
244: //fprintf ( stderr , "read %x %x %x\n" , addr , value , STMemory_CheckRegionBusError(addr) );
1.1.1.7 root 245:
1.1.1.8 root 246: Blitter_AddCycles(4);
1.1.1.7 root 247:
1.1.1.8 root 248: return value;
249: }
250:
251: static void Blitter_WriteWord(Uint32 addr, Uint16 value)
252: {
1.1.1.14 root 253: /* Call put_word only if the address doesn't point to a bus error region */
1.1.1.15! root 254: /* (also see SysMem_wput for addr < 0x8) */
1.1.1.14 root 255: if ( STMemory_CheckRegionBusError ( addr ) == false )
256: put_word ( addr , (Uint32)(value) );
257: //fprintf ( stderr , "write %x %x %x\n" , addr , value , STMemory_CheckRegionBusError(addr) );
1.1.1.7 root 258:
1.1.1.8 root 259: Blitter_AddCycles(4);
1.1.1.7 root 260: }
261:
1.1.1.8 root 262: /*-----------------------------------------------------------------------*/
1.1.1.7 root 263: /**
1.1.1.8 root 264: * Blitter emulation - level 1
1.1.1.7 root 265: */
1.1.1.8 root 266:
267: static void Blitter_BeginLine(void)
1.1.1.7 root 268: {
1.1.1.8 root 269: BlitterVars.src_words = BlitterVars.src_words_reset;
270: }
1.1.1.7 root 271:
1.1.1.8 root 272: static void Blitter_SetState(Uint8 fxsr, Uint8 nfsr, Uint16 end_mask)
273: {
274: BlitterState.end_mask = end_mask;
275: BlitterState.have_src = false;
276: BlitterState.have_dst = false;
277: BlitterState.fxsr = fxsr;
278: BlitterState.nfsr = nfsr;
279: }
280:
281: static void Blitter_SourceShift(void)
282: {
283: if (BlitterRegs.src_x_incr < 0)
284: BlitterVars.buffer >>= 16;
285: else
286: BlitterVars.buffer <<= 16;
287: }
288:
289: static void Blitter_SourceFetch(void)
290: {
291: Uint32 src_word = (Uint32)Blitter_ReadWord(BlitterRegs.src_addr);
292:
293: if (BlitterRegs.src_x_incr < 0)
294: BlitterVars.buffer |= src_word << 16;
295: else
296: BlitterVars.buffer |= src_word;
297:
298: if (BlitterVars.src_words == 1)
1.1.1.7 root 299: {
1.1.1.8 root 300: BlitterRegs.src_addr += BlitterRegs.src_y_incr;
301: }
302: else
303: {
304: --BlitterVars.src_words;
305: BlitterRegs.src_addr += BlitterRegs.src_x_incr;
1.1.1.7 root 306: }
307: }
308:
1.1.1.8 root 309: static Uint16 Blitter_SourceRead(void)
310: {
311: if (!BlitterState.have_src)
312: {
313: if (BlitterState.fxsr)
314: {
315: Blitter_SourceShift();
316: Blitter_SourceFetch();
317: }
1.1.1.7 root 318:
1.1.1.8 root 319: Blitter_SourceShift();
1.1.1.7 root 320:
1.1.1.8 root 321: if (!BlitterState.nfsr)
322: {
323: Blitter_SourceFetch();
324: }
1.1.1.7 root 325:
1.1.1.8 root 326: BlitterState.src_word = (Uint16)(BlitterVars.buffer >> BlitterVars.skew);
327: BlitterState.have_src = true;
1.1.1.7 root 328: }
1.1 root 329:
1.1.1.8 root 330: return BlitterState.src_word;
331: }
1.1 root 332:
1.1.1.8 root 333: static Uint16 Blitter_GetHalftoneWord(void)
334: {
335: if (BlitterVars.smudge)
336: return BlitterHalftone[Blitter_SourceRead() & 15];
337: else
338: return BlitterHalftone[BlitterVars.line];
339: }
1.1.1.4 root 340:
1.1.1.9 root 341: /* HOP */
342:
343: static Uint16 Blitter_HOP_0(void)
1.1 root 344: {
1.1.1.9 root 345: return 0xFFFF;
346: }
1.1.1.7 root 347:
1.1.1.9 root 348: static Uint16 Blitter_HOP_1(void)
349: {
350: return Blitter_GetHalftoneWord();
351: }
352:
353: static Uint16 Blitter_HOP_2(void)
354: {
355: return Blitter_SourceRead();
356: }
1.1.1.8 root 357:
1.1.1.9 root 358: static Uint16 Blitter_HOP_3(void)
359: {
360: return Blitter_SourceRead() & Blitter_GetHalftoneWord();
1.1.1.8 root 361: }
362:
1.1.1.9 root 363: static BLITTER_OP_FUNC Blitter_HOP_Table [4] =
364: {
365: Blitter_HOP_0,
366: Blitter_HOP_1,
367: Blitter_HOP_2,
368: Blitter_HOP_3
369: };
370:
371: static void Blitter_Select_HOP(void)
372: {
373: Blitter_ComputeHOP = Blitter_HOP_Table[BlitterRegs.hop];
374: }
375:
376: /* end HOP */
377:
1.1.1.8 root 378: static Uint16 Blitter_DestRead(void)
379: {
380: if (!BlitterState.have_dst)
381: {
382: BlitterState.dst_word = Blitter_ReadWord(BlitterRegs.dst_addr);
383: BlitterState.have_dst = true;
384: }
385:
386: return BlitterState.dst_word;
387: }
388:
1.1.1.9 root 389: /* LOP */
390:
391: static Uint16 Blitter_LOP_0(void)
392: {
393: return 0;
394: }
395:
396: static Uint16 Blitter_LOP_1(void)
1.1.1.8 root 397: {
1.1.1.9 root 398: return Blitter_ComputeHOP() & Blitter_DestRead();
399: }
1.1.1.8 root 400:
1.1.1.9 root 401: static Uint16 Blitter_LOP_2(void)
402: {
403: return Blitter_ComputeHOP() & ~Blitter_DestRead();
404: }
405:
406: static Uint16 Blitter_LOP_3(void)
407: {
408: return Blitter_ComputeHOP();
409: }
410:
411: static Uint16 Blitter_LOP_4(void)
412: {
413: return ~Blitter_ComputeHOP() & Blitter_DestRead();
414: }
415:
416: static Uint16 Blitter_LOP_5(void)
417: {
418: return Blitter_DestRead();
419: }
420:
421: static Uint16 Blitter_LOP_6(void)
422: {
423: return Blitter_ComputeHOP() ^ Blitter_DestRead();
424: }
425:
426: static Uint16 Blitter_LOP_7(void)
427: {
428: return Blitter_ComputeHOP() | Blitter_DestRead();
429: }
430:
431: static Uint16 Blitter_LOP_8(void)
432: {
433: return ~Blitter_ComputeHOP() & ~Blitter_DestRead();
434: }
435:
436: static Uint16 Blitter_LOP_9(void)
437: {
438: return ~Blitter_ComputeHOP() ^ Blitter_DestRead();
439: }
440:
441: static Uint16 Blitter_LOP_A(void)
442: {
443: return ~Blitter_DestRead();
444: }
1.1.1.8 root 445:
1.1.1.9 root 446: static Uint16 Blitter_LOP_B(void)
447: {
448: return Blitter_ComputeHOP() | ~Blitter_DestRead();
1.1.1.8 root 449: }
450:
1.1.1.9 root 451: static Uint16 Blitter_LOP_C(void)
452: {
453: return ~Blitter_ComputeHOP();
454: }
455:
456: static Uint16 Blitter_LOP_D(void)
457: {
458: return ~Blitter_ComputeHOP() | Blitter_DestRead();
459: }
460:
461: static Uint16 Blitter_LOP_E(void)
462: {
463: return ~Blitter_ComputeHOP() | ~Blitter_DestRead();
464: }
465:
466: static Uint16 Blitter_LOP_F(void)
467: {
468: return 0xFFFF;
469: }
470:
471: static BLITTER_OP_FUNC Blitter_LOP_Table [16] =
472: {
473: Blitter_LOP_0,
474: Blitter_LOP_1,
475: Blitter_LOP_2,
476: Blitter_LOP_3,
477: Blitter_LOP_4,
478: Blitter_LOP_5,
479: Blitter_LOP_6,
480: Blitter_LOP_7,
481: Blitter_LOP_8,
482: Blitter_LOP_9,
483: Blitter_LOP_A,
484: Blitter_LOP_B,
485: Blitter_LOP_C,
486: Blitter_LOP_D,
487: Blitter_LOP_E,
488: Blitter_LOP_F
489: };
490:
491: static void Blitter_Select_LOP(void)
492: {
493: Blitter_ComputeLOP = Blitter_LOP_Table[BlitterRegs.lop];
494: }
495:
496: /* end LOP */
497:
1.1.1.8 root 498: static Uint16 Blitter_ComputeMask(void)
499: {
500: return (Blitter_ComputeLOP() & BlitterState.end_mask) |
501: (Blitter_DestRead() & ~BlitterState.end_mask);
502: }
503:
504: static void Blitter_ProcessWord(void)
505: {
506: /* when NFSR, a read-modify-write is always performed */
507: Uint16 dst_data = ((BlitterState.nfsr || BlitterState.end_mask != 0xFFFF)
508: ? Blitter_ComputeMask()
509: : Blitter_ComputeLOP());
510:
511: Blitter_WriteWord(BlitterRegs.dst_addr, dst_data);
1.1 root 512:
1.1.1.8 root 513: if (BlitterRegs.words == 1)
1.1.1.7 root 514: {
1.1.1.8 root 515: BlitterRegs.dst_addr += BlitterRegs.dst_y_incr;
1.1.1.7 root 516: }
1.1.1.8 root 517: else
1.1.1.7 root 518: {
1.1.1.8 root 519: --BlitterRegs.words;
520: BlitterRegs.dst_addr += BlitterRegs.dst_x_incr;
1.1.1.7 root 521: }
1.1.1.8 root 522: }
523:
524: static void Blitter_EndLine(void)
525: {
526: --BlitterRegs.lines;
527: BlitterRegs.words = BlitterVars.dst_words_reset;
1.1.1.7 root 528:
1.1.1.8 root 529: if (BlitterRegs.dst_y_incr >= 0)
530: BlitterVars.line = (BlitterVars.line+1) & 15;
1.1.1.7 root 531: else
1.1.1.8 root 532: BlitterVars.line = (BlitterVars.line-1) & 15;
533: }
534:
535: /*-----------------------------------------------------------------------*/
536: /**
537: * Blitter emulation - level 2
538: */
539:
540: static void Blitter_SingleWord(void)
541: {
542: Blitter_BeginLine();
543: Blitter_SetState(BlitterVars.fxsr, BlitterVars.nfsr, BlitterRegs.end_mask_1);
544: Blitter_ProcessWord();
545: Blitter_EndLine();
546: }
547:
548: static void Blitter_FirstWord(void)
549: {
550: Blitter_BeginLine();
551: Blitter_SetState(BlitterVars.fxsr, 0, BlitterRegs.end_mask_1);
552: Blitter_ProcessWord();
553: }
554:
555: static void Blitter_MiddleWord(void)
556: {
557: Blitter_SetState(0, 0, BlitterRegs.end_mask_2);
558: Blitter_ProcessWord();
559: }
1.1.1.7 root 560:
1.1.1.8 root 561: static void Blitter_LastWord(void)
562: {
563: Blitter_SetState(0, BlitterVars.nfsr, BlitterRegs.end_mask_3);
564: Blitter_ProcessWord();
565: Blitter_EndLine();
1.1.1.7 root 566: }
1.1 root 567:
1.1.1.8 root 568: static void Blitter_Step(void)
569: {
570: if (BlitterVars.dst_words_reset == 1)
571: {
572: Blitter_SingleWord();
573: }
574: else if (BlitterRegs.words == BlitterVars.dst_words_reset)
575: {
576: Blitter_FirstWord();
577: }
578: else if (BlitterRegs.words == 1)
579: {
580: Blitter_LastWord();
581: }
582: else
583: {
584: Blitter_MiddleWord();
585: }
586: }
1.1 root 587:
1.1.1.3 root 588: /*-----------------------------------------------------------------------*/
1.1.1.5 root 589: /**
1.1.1.7 root 590: * Let's do the blit.
591: * Note that in non-HOG mode, the blitter only runs for 64 bus cycles (2 MHz!)
592: * before giving the bus back to the CPU. Due to this mode, this function must
593: * be able to abort and resume the blitting at any time.
1.1.1.5 root 594: */
1.1.1.8 root 595: static void Blitter_Start(void)
1.1.1.4 root 596: {
1.1.1.9 root 597: /* select HOP & LOP funcs */
598: Blitter_Select_HOP();
599: Blitter_Select_LOP();
600:
1.1.1.8 root 601: /* setup vars */
1.1.1.9 root 602: BlitterVars.pass_cycles = 0;
603: BlitterVars.op_cycles = 0;
1.1.1.12 root 604: BlitterVars.src_words_reset = BlitterVars.dst_words_reset + BlitterVars.fxsr - BlitterVars.nfsr;
1.1.1.8 root 605:
606: /* bus arbitration */
1.1.1.14 root 607: Blitter_BusArbitration ( BUS_MODE_BLITTER );
608:
609: /* Busy=1, set line to high/1 and clear interrupt */
610: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_GPU_DONE , MFP_GPIP_STATE_HIGH );
1.1.1.7 root 611:
612: /* Now we enter the main blitting loop */
613: do
614: {
1.1.1.8 root 615: Blitter_Step();
1.1.1.9 root 616: Blitter_FlushCycles();
1.1.1.8 root 617: }
618: while (BlitterRegs.lines > 0
1.1.1.9 root 619: && (BlitterVars.hog || BlitterVars.pass_cycles < NONHOG_CYCLES));
1.1.1.7 root 620:
1.1.1.8 root 621: /* bus arbitration */
1.1.1.14 root 622: Blitter_BusArbitration ( BUS_MODE_CPU );
1.1.1.7 root 623:
1.1.1.8 root 624: BlitterRegs.ctrl = (BlitterRegs.ctrl & 0xF0) | BlitterVars.line;
1.1.1.7 root 625:
1.1.1.8 root 626: if (BlitterRegs.lines == 0)
627: {
1.1.1.14 root 628: /* We're done, clear busy and hog bits */
629: BlitterRegs.ctrl &= ~(0x80|0x40);
1.1.1.7 root 630:
1.1.1.14 root 631: /* Busy=0, set line to low/0 and request interrupt */
632: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_GPU_DONE , MFP_GPIP_STATE_LOW );
1.1.1.7 root 633: }
1.1.1.8 root 634: else
1.1.1.7 root 635: {
1.1.1.8 root 636: /* Continue blitting later */
1.1.1.15! root 637: #ifdef OLD_CPU_SHIFT
! 638: CycInt_AddRelativeInterrupt(NONHOG_CYCLES, INT_CPU_CYCLE, INTERRUPT_BLITTER);
! 639: #else
1.1.1.10 root 640: CycInt_AddRelativeInterrupt(NONHOG_CYCLES, INT_CPU_CYCLE, INTERRUPT_BLITTER);
1.1.1.15! root 641: #endif
1.1.1.7 root 642: }
643: }
644:
1.1.1.8 root 645: /*-----------------------------------------------------------------------*/
646: /**
647: * Read blitter halftone ram.
648: */
649: static void Blitter_Halftone_ReadWord(int index)
650: {
651: IoMem_WriteWord(REG_HT_RAM + index + index, BlitterHalftone[index]);
652: }
653:
654: void Blitter_Halftone00_ReadWord(void) { Blitter_Halftone_ReadWord(0); }
655: void Blitter_Halftone01_ReadWord(void) { Blitter_Halftone_ReadWord(1); }
656: void Blitter_Halftone02_ReadWord(void) { Blitter_Halftone_ReadWord(2); }
657: void Blitter_Halftone03_ReadWord(void) { Blitter_Halftone_ReadWord(3); }
658: void Blitter_Halftone04_ReadWord(void) { Blitter_Halftone_ReadWord(4); }
659: void Blitter_Halftone05_ReadWord(void) { Blitter_Halftone_ReadWord(5); }
660: void Blitter_Halftone06_ReadWord(void) { Blitter_Halftone_ReadWord(6); }
661: void Blitter_Halftone07_ReadWord(void) { Blitter_Halftone_ReadWord(7); }
662: void Blitter_Halftone08_ReadWord(void) { Blitter_Halftone_ReadWord(8); }
663: void Blitter_Halftone09_ReadWord(void) { Blitter_Halftone_ReadWord(9); }
664: void Blitter_Halftone10_ReadWord(void) { Blitter_Halftone_ReadWord(10); }
665: void Blitter_Halftone11_ReadWord(void) { Blitter_Halftone_ReadWord(11); }
666: void Blitter_Halftone12_ReadWord(void) { Blitter_Halftone_ReadWord(12); }
667: void Blitter_Halftone13_ReadWord(void) { Blitter_Halftone_ReadWord(13); }
668: void Blitter_Halftone14_ReadWord(void) { Blitter_Halftone_ReadWord(14); }
669: void Blitter_Halftone15_ReadWord(void) { Blitter_Halftone_ReadWord(15); }
670:
671: /*-----------------------------------------------------------------------*/
672: /**
673: * Read blitter source x increment (0xff8a20).
674: */
675: void Blitter_SourceXInc_ReadWord(void)
676: {
677: IoMem_WriteWord(REG_SRC_X_INC, (Uint16)(BlitterRegs.src_x_incr));
678: }
679:
680: /*-----------------------------------------------------------------------*/
681: /**
682: * Read blitter source y increment (0xff8a22).
683: */
684: void Blitter_SourceYInc_ReadWord(void)
685: {
686: IoMem_WriteWord(REG_SRC_Y_INC, (Uint16)(BlitterRegs.src_y_incr));
687: }
1.1.1.7 root 688:
689: /*-----------------------------------------------------------------------*/
690: /**
691: * Read blitter source address (0xff8a24).
692: */
693: void Blitter_SourceAddr_ReadLong(void)
694: {
1.1.1.8 root 695: IoMem_WriteLong(REG_SRC_ADDR, BlitterRegs.src_addr);
1.1.1.4 root 696: }
697:
698: /*-----------------------------------------------------------------------*/
1.1.1.5 root 699: /**
700: * Read blitter endmask 1.
701: */
1.1.1.3 root 702: void Blitter_Endmask1_ReadWord(void)
1.1 root 703: {
1.1.1.8 root 704: IoMem_WriteWord(REG_END_MASK1, BlitterRegs.end_mask_1);
1.1 root 705: }
706:
1.1.1.3 root 707: /*-----------------------------------------------------------------------*/
1.1.1.5 root 708: /**
709: * Read blitter endmask 2.
710: */
1.1.1.3 root 711: void Blitter_Endmask2_ReadWord(void)
1.1 root 712: {
1.1.1.8 root 713: IoMem_WriteWord(REG_END_MASK2, BlitterRegs.end_mask_2);
1.1 root 714: }
715:
1.1.1.3 root 716: /*-----------------------------------------------------------------------*/
1.1.1.5 root 717: /**
718: * Read blitter endmask 3.
719: */
1.1.1.3 root 720: void Blitter_Endmask3_ReadWord(void)
1.1 root 721: {
1.1.1.8 root 722: IoMem_WriteWord(REG_END_MASK3, BlitterRegs.end_mask_3);
723: }
724:
725: /*-----------------------------------------------------------------------*/
726: /**
727: * Read blitter destination x increment (0xff8a2E).
728: */
729: void Blitter_DestXInc_ReadWord(void)
730: {
731: IoMem_WriteWord(REG_DST_X_INC, (Uint16)(BlitterRegs.dst_x_incr));
732: }
733:
734: /*-----------------------------------------------------------------------*/
735: /**
736: * Read blitter destination y increment (0xff8a30).
737: */
738: void Blitter_DestYInc_ReadWord(void)
739: {
740: IoMem_WriteWord(REG_DST_Y_INC, (Uint16)(BlitterRegs.dst_y_incr));
1.1 root 741: }
742:
1.1.1.3 root 743: /*-----------------------------------------------------------------------*/
1.1.1.5 root 744: /**
745: * Read blitter destination address.
746: */
1.1.1.3 root 747: void Blitter_DestAddr_ReadLong(void)
1.1 root 748: {
1.1.1.8 root 749: IoMem_WriteLong(REG_DST_ADDR, BlitterRegs.dst_addr);
1.1 root 750: }
751:
1.1.1.3 root 752: /*-----------------------------------------------------------------------*/
1.1.1.5 root 753: /**
754: * Read blitter words-per-line register.
755: */
1.1.1.3 root 756: void Blitter_WordsPerLine_ReadWord(void)
1.1 root 757: {
1.1.1.8 root 758: IoMem_WriteWord(REG_X_COUNT, (Uint16)(BlitterRegs.words & 0xFFFF));
1.1 root 759: }
760:
1.1.1.3 root 761: /*-----------------------------------------------------------------------*/
1.1.1.5 root 762: /**
763: * Read blitter lines-per-bitblock register.
764: */
1.1.1.3 root 765: void Blitter_LinesPerBitblock_ReadWord(void)
1.1 root 766: {
1.1.1.8 root 767: IoMem_WriteWord(REG_Y_COUNT, (Uint16)(BlitterRegs.lines & 0xFFFF));
1.1 root 768: }
769:
1.1.1.3 root 770: /*-----------------------------------------------------------------------*/
1.1.1.5 root 771: /**
772: * Read blitter halftone operation register.
773: */
1.1.1.3 root 774: void Blitter_HalftoneOp_ReadByte(void)
1.1 root 775: {
1.1.1.8 root 776: IoMem_WriteByte(REG_BLIT_HOP, BlitterRegs.hop);
1.1 root 777: }
778:
1.1.1.3 root 779: /*-----------------------------------------------------------------------*/
1.1.1.5 root 780: /**
781: * Read blitter logical operation register.
782: */
1.1.1.3 root 783: void Blitter_LogOp_ReadByte(void)
1.1 root 784: {
1.1.1.8 root 785: IoMem_WriteByte(REG_BLIT_LOP, BlitterRegs.lop);
1.1 root 786: }
787:
1.1.1.3 root 788: /*-----------------------------------------------------------------------*/
1.1.1.5 root 789: /**
790: * Read blitter control register.
791: */
1.1.1.4 root 792: void Blitter_Control_ReadByte(void)
1.1 root 793: {
1.1.1.7 root 794: /* busy, hog/blit, smudge, n/a, 4bits for line number */
1.1.1.8 root 795: IoMem_WriteByte(REG_CONTROL, BlitterRegs.ctrl);
1.1 root 796: }
797:
1.1.1.3 root 798: /*-----------------------------------------------------------------------*/
1.1.1.5 root 799: /**
800: * Read blitter skew register.
801: */
1.1.1.3 root 802: void Blitter_Skew_ReadByte(void)
1.1 root 803: {
1.1.1.8 root 804: IoMem_WriteByte(REG_SKEW, BlitterRegs.skew);
805: }
806:
807:
808: /*-----------------------------------------------------------------------*/
809: /**
810: * Write to blitter halftone ram.
811: */
812: static void Blitter_Halftone_WriteWord(int index)
813: {
814: BlitterHalftone[index] = IoMem_ReadWord(REG_HT_RAM + index + index);
815: }
816:
817: void Blitter_Halftone00_WriteWord(void) { Blitter_Halftone_WriteWord(0); }
818: void Blitter_Halftone01_WriteWord(void) { Blitter_Halftone_WriteWord(1); }
819: void Blitter_Halftone02_WriteWord(void) { Blitter_Halftone_WriteWord(2); }
820: void Blitter_Halftone03_WriteWord(void) { Blitter_Halftone_WriteWord(3); }
821: void Blitter_Halftone04_WriteWord(void) { Blitter_Halftone_WriteWord(4); }
822: void Blitter_Halftone05_WriteWord(void) { Blitter_Halftone_WriteWord(5); }
823: void Blitter_Halftone06_WriteWord(void) { Blitter_Halftone_WriteWord(6); }
824: void Blitter_Halftone07_WriteWord(void) { Blitter_Halftone_WriteWord(7); }
825: void Blitter_Halftone08_WriteWord(void) { Blitter_Halftone_WriteWord(8); }
826: void Blitter_Halftone09_WriteWord(void) { Blitter_Halftone_WriteWord(9); }
827: void Blitter_Halftone10_WriteWord(void) { Blitter_Halftone_WriteWord(10); }
828: void Blitter_Halftone11_WriteWord(void) { Blitter_Halftone_WriteWord(11); }
829: void Blitter_Halftone12_WriteWord(void) { Blitter_Halftone_WriteWord(12); }
830: void Blitter_Halftone13_WriteWord(void) { Blitter_Halftone_WriteWord(13); }
831: void Blitter_Halftone14_WriteWord(void) { Blitter_Halftone_WriteWord(14); }
832: void Blitter_Halftone15_WriteWord(void) { Blitter_Halftone_WriteWord(15); }
833:
834: /*-----------------------------------------------------------------------*/
835: /**
836: * Write to blitter source x increment.
837: */
838: void Blitter_SourceXInc_WriteWord(void)
839: {
840: BlitterRegs.src_x_incr = (short)(IoMem_ReadWord(REG_SRC_X_INC) & 0xFFFE);
1.1 root 841: }
842:
1.1.1.8 root 843: /*-----------------------------------------------------------------------*/
844: /**
845: * Write to blitter source y increment.
846: */
847: void Blitter_SourceYInc_WriteWord(void)
848: {
849: BlitterRegs.src_y_incr = (short)(IoMem_ReadWord(REG_SRC_Y_INC) & 0xFFFE);
850: }
1.1 root 851:
1.1.1.3 root 852: /*-----------------------------------------------------------------------*/
1.1.1.5 root 853: /**
1.1.1.7 root 854: * Write to blitter source address register (0xff8a24).
855: */
856: void Blitter_SourceAddr_WriteLong(void)
857: {
1.1.1.14 root 858: if ( ConfigureParams.System.bAddressSpace24 == true )
859: BlitterRegs.src_addr = IoMem_ReadLong(REG_SRC_ADDR) & 0x00FFFFFE; /* Normal STF/STE */
860: else
861: BlitterRegs.src_addr = IoMem_ReadLong(REG_SRC_ADDR) & 0xFFFFFFFE; /* Falcon with extra TT RAM */
1.1.1.7 root 862: }
863:
864: /*-----------------------------------------------------------------------*/
865: /**
1.1.1.5 root 866: * Write to blitter endmask 1.
867: */
1.1.1.3 root 868: void Blitter_Endmask1_WriteWord(void)
1.1 root 869: {
1.1.1.8 root 870: BlitterRegs.end_mask_1 = IoMem_ReadWord(REG_END_MASK1);
1.1 root 871: }
872:
1.1.1.3 root 873: /*-----------------------------------------------------------------------*/
1.1.1.5 root 874: /**
875: * Write to blitter endmask 2.
876: */
1.1.1.3 root 877: void Blitter_Endmask2_WriteWord(void)
1.1 root 878: {
1.1.1.8 root 879: BlitterRegs.end_mask_2 = IoMem_ReadWord(REG_END_MASK2);
1.1 root 880: }
881:
1.1.1.3 root 882: /*-----------------------------------------------------------------------*/
1.1.1.5 root 883: /**
884: * Write to blitter endmask 3.
885: */
1.1.1.3 root 886: void Blitter_Endmask3_WriteWord(void)
1.1 root 887: {
1.1.1.8 root 888: BlitterRegs.end_mask_3 = IoMem_ReadWord(REG_END_MASK3);
889: }
890:
891: /*-----------------------------------------------------------------------*/
892: /**
893: * Write to blitter destination x increment.
894: */
895: void Blitter_DestXInc_WriteWord(void)
896: {
897: BlitterRegs.dst_x_incr = (short)(IoMem_ReadWord(REG_DST_X_INC) & 0xFFFE);
898: }
899:
900: /*-----------------------------------------------------------------------*/
901: /**
902: * Write to blitter source y increment.
903: */
904: void Blitter_DestYInc_WriteWord(void)
905: {
906: BlitterRegs.dst_y_incr = (short)(IoMem_ReadWord(REG_DST_Y_INC) & 0xFFFE);
1.1 root 907: }
908:
1.1.1.3 root 909: /*-----------------------------------------------------------------------*/
1.1.1.5 root 910: /**
911: * Write to blitter destination address register.
912: */
1.1.1.3 root 913: void Blitter_DestAddr_WriteLong(void)
914: {
1.1.1.14 root 915: if ( ConfigureParams.System.bAddressSpace24 == true )
916: BlitterRegs.dst_addr = IoMem_ReadLong(REG_DST_ADDR) & 0x00FFFFFE; /* Normal STF/STE */
917: else
918: BlitterRegs.dst_addr = IoMem_ReadLong(REG_DST_ADDR) & 0xFFFFFFFE; /* Falcon with extra TT RAM */
1.1 root 919: }
920:
1.1.1.3 root 921: /*-----------------------------------------------------------------------*/
1.1.1.5 root 922: /**
923: * Write to blitter words-per-line register.
924: */
1.1.1.3 root 925: void Blitter_WordsPerLine_WriteWord(void)
1.1 root 926: {
1.1.1.8 root 927: Uint32 words = (Uint32)IoMem_ReadWord(REG_X_COUNT);
928:
929: if (words == 0)
930: words = 65536;
931:
932: BlitterRegs.words = words;
933: BlitterVars.dst_words_reset = words;
1.1 root 934: }
935:
1.1.1.3 root 936: /*-----------------------------------------------------------------------*/
1.1.1.5 root 937: /**
1.1.1.8 root 938: * Write to blitter lines-per-bitblock register.
1.1.1.5 root 939: */
1.1.1.3 root 940: void Blitter_LinesPerBitblock_WriteWord(void)
1.1 root 941: {
1.1.1.8 root 942: Uint32 lines = (Uint32)IoMem_ReadWord(REG_Y_COUNT);
943:
944: if (lines == 0)
945: lines = 65536;
946:
947: BlitterRegs.lines = lines;
1.1 root 948: }
949:
1.1.1.3 root 950: /*-----------------------------------------------------------------------*/
1.1.1.5 root 951: /**
952: * Write to blitter halftone operation register.
953: */
1.1.1.3 root 954: void Blitter_HalftoneOp_WriteByte(void)
1.1 root 955: {
1.1.1.4 root 956: /* h/ware reg masks out the top 6 bits! */
1.1.1.8 root 957: BlitterRegs.hop = IoMem_ReadByte(REG_BLIT_HOP) & 3;
1.1 root 958: }
959:
1.1.1.3 root 960: /*-----------------------------------------------------------------------*/
1.1.1.5 root 961: /**
962: * Write to blitter logical operation register.
963: */
1.1.1.3 root 964: void Blitter_LogOp_WriteByte(void)
1.1.1.7 root 965: {
1.1.1.4 root 966: /* h/ware reg masks out the top 4 bits! */
1.1.1.8 root 967: BlitterRegs.lop = IoMem_ReadByte(REG_BLIT_LOP) & 0xF;
1.1 root 968: }
969:
1.1.1.3 root 970: /*-----------------------------------------------------------------------*/
1.1.1.5 root 971: /**
972: * Write to blitter control register.
973: */
1.1.1.4 root 974: void Blitter_Control_WriteByte(void)
975: {
976: /* Control register bits:
977: * 0x80: busy bit
978: * - Turn on Blitter activity and stay "1" until copy finished
979: * 0x40: Blit-mode bit
980: * - 0: Blit mode, CPU and Blitter get 64 clockcycles in turns
981: * - 1: HOG Mode, Blitter reserves and hogs the bus for as long
982: * as the copy takes, CPU and DMA get no Bus access
983: * 0x20: Smudge mode
984: * - Which line of the halftone pattern to start with is
985: * read from the first source word when the copy starts
986: * 0x10: not used
987: * 0x0f
988: *
989: * The lowest 4 bits contain the Halftone pattern line number
990: */
1.1.1.7 root 991:
1.1.1.9 root 992: if (LOG_TRACE_LEVEL(TRACE_BLITTER))
1.1.1.3 root 993: {
1.1.1.9 root 994: int FrameCycles, HblCounterVideo, LineCycles;
995:
996: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
997:
998: LOG_TRACE_PRINT("blitter write ctrl=%x video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" ,
1.1.1.7 root 999: IoMem_ReadByte(REG_CONTROL) ,
1.1.1.9 root 1000: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.7 root 1001: }
1.1.1.3 root 1002:
1.1.1.8 root 1003: BlitterRegs.ctrl = IoMem_ReadByte(REG_CONTROL) & 0xEF;
1004:
1005: BlitterVars.hog = BlitterRegs.ctrl & 0x40;
1006: BlitterVars.smudge = BlitterRegs.ctrl & 0x20;
1007: BlitterVars.line = BlitterRegs.ctrl & 0xF;
1.1.1.7 root 1008:
1009: /* Remove old pending update interrupt */
1.1.1.10 root 1010: CycInt_RemovePendingInterrupt(INTERRUPT_BLITTER);
1.1.1.7 root 1011:
1012: /* Busy bit set? */
1.1.1.8 root 1013: if (BlitterRegs.ctrl & 0x80)
1.1.1.7 root 1014: {
1.1.1.8 root 1015: if (BlitterRegs.lines == 0)
1.1.1.7 root 1016: {
1.1.1.14 root 1017: /* We're done, clear busy and hog bits */
1018: BlitterRegs.ctrl &= ~(0x80|0x40);
1.1.1.7 root 1019: }
1020: else
1021: {
1.1.1.15! root 1022: /* Start blitting after the end of current instruction */
! 1023: #ifdef OLD_CPU_SHIFT
! 1024: CycInt_AddRelativeInterrupt((CurrentInstrCycles+WaitStateCycles)>>nCpuFreqShift,
1.1.1.10 root 1025: INT_CPU_CYCLE, INTERRUPT_BLITTER);
1.1.1.15! root 1026: #else
! 1027: CycInt_AddRelativeInterrupt( CurrentInstrCycles+WaitStateCycles, INT_CPU_CYCLE, INTERRUPT_BLITTER);
! 1028: /* [NP] TODO : use CycInt_AddRelativeInterrupt(0) instead to get an immediate interrupt */
! 1029: #endif
1.1.1.7 root 1030: }
1.1.1.3 root 1031: }
1.1 root 1032: }
1033:
1.1.1.3 root 1034: /*-----------------------------------------------------------------------*/
1.1.1.5 root 1035: /**
1036: * Write to blitter skew register.
1037: */
1.1.1.3 root 1038: void Blitter_Skew_WriteByte(void)
1039: {
1.1.1.8 root 1040: BlitterRegs.skew = IoMem_ReadByte(REG_SKEW);
1041: BlitterVars.fxsr = (BlitterRegs.skew & 0x80)?1:0;
1042: BlitterVars.nfsr = (BlitterRegs.skew & 0x40)?1:0;
1043: BlitterVars.skew = BlitterRegs.skew & 0xF;
1.1 root 1044: }
1.1.1.2 root 1045:
1046:
1047: /*-----------------------------------------------------------------------*/
1.1.1.5 root 1048: /**
1.1.1.7 root 1049: * Handler which continues blitting after 64 bus cycles.
1050: */
1051: void Blitter_InterruptHandler(void)
1052: {
1.1.1.10 root 1053: CycInt_AcknowledgeInterrupt();
1.1.1.7 root 1054:
1.1.1.8 root 1055: if (BlitterRegs.ctrl & 0x80)
1.1.1.7 root 1056: {
1.1.1.8 root 1057: Blitter_Start();
1.1.1.7 root 1058: }
1.1.1.8 root 1059: }
1.1.1.7 root 1060:
1.1.1.8 root 1061: /*-----------------------------------------------------------------------*/
1062: /**
1063: * Save/Restore snapshot of Blitter variables.
1064: */
1065: void Blitter_MemorySnapShot_Capture(bool bSave)
1066: {
1067: /* Save/Restore details */
1068: MemorySnapShot_Store(&BlitterRegs, sizeof(BlitterRegs));
1069: MemorySnapShot_Store(&BlitterVars, sizeof(BlitterVars));
1070: MemorySnapShot_Store(&BlitterHalftone, sizeof(BlitterHalftone));
1.1.1.2 root 1071: }
1.1.1.12 root 1072:
1073: /*-----------------------------------------------------------------------*/
1074: /**
1075: * Show Blitter register values.
1076: */
1.1.1.14 root 1077: void Blitter_Info(FILE *fp, Uint32 dummy)
1.1.1.12 root 1078: {
1079: BLITTERREGS *regs = &BlitterRegs;
1080:
1081: fprintf(fp, "src addr: 0x%06x\n", regs->src_addr);
1082: fprintf(fp, "dst addr: 0x%06x\n", regs->dst_addr);
1083: fprintf(fp, "words: %u\n", regs->words);
1084: fprintf(fp, "lines: %u\n", regs->lines);
1085: fprintf(fp, "src X-inc: %hd\n", regs->src_x_incr);
1086: fprintf(fp, "src Y-inc: %hd\n", regs->src_y_incr);
1087: fprintf(fp, "dst X-inc: %hd\n", regs->dst_x_incr);
1088: fprintf(fp, "dst Y-inc: %hd\n", regs->dst_y_incr);
1089: fprintf(fp, "end mask1: 0x%04x\n", regs->end_mask_1);
1090: fprintf(fp, "end mask2: 0x%04x\n", regs->end_mask_2);
1091: fprintf(fp, "end mask3: 0x%04x\n", regs->end_mask_3);
1092: fprintf(fp, "HOP: 0x%02x\n", regs->hop);
1093: fprintf(fp, "LOP: 0x%02x\n", regs->lop);
1094: fprintf(fp, "control: 0x%02x\n", regs->ctrl);
1095: fprintf(fp, "skew: 0x%02x\n", regs->skew);
1.1.1.13 root 1096: fprintf(fp, "Note: internally changed register values aren't visible to breakpoints\nor in memdump output until emulated code reads or writes them!\n");
1.1.1.12 root 1097: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.