/* * Some code to simulate the blitter! Original code by Dale Luck * Rewritten by Tomas Rokicki, 12 April 1988. */ #include "structures.h" /* * External values we use. This is where we get all the parameters * from. Upper case macros are used to reference this structure * by the names used by the hardware; BLTCON0, for instance, expands * to blitregs.con0. */ extern struct blitregs blitregs ; /* * This routine opens a log file if appropriate. */ extern FILE *openlogfile() ; /* * We use a few macros to make the code easier to understand. */ #define BLITTING_FORWARD (!(BLTCON1 & BLITREVERSE)) #define FILL_OK (BLTCON1 & FILL_OR) #define FILL_XOK (BLTCON1 & FILL_XOR) #define SRCA_ENABLED (BLTCON0 & SRCA) #define SRCB_ENABLED (BLTCON0 & SRCB) #define SRCC_ENABLED (BLTCON0 & SRCC) #define DEST_ENABLED (BLTCON0 & DEST) /* * This macro takes two 16-bit words and returns a 32-bit word * formed by concatenating the two words. */ #define CATWORDS(a,b) ((((long)(a))<<16)+(unsigned short)(b)) /* * This is the blitter simulator for running the blitter when not * in the line mode. */ int donotlineblit() { long aptr, bptr, cptr, dptr ; /* our pointer variables */ short a_old, b_old ; /* shifting old values */ short a_new, b_new ; /* the input values */ short a_masked ; /* a after masking */ short a_hold, b_hold, c_hold ; /* values we build up */ short h, v ; /* indices for iteration */ short hsize, vsize ; /* how much to blit */ short zeroflag ; /* see any non-zeros? */ short amod, bmod, cmod, dmod ; /* the actual values used */ short fill_bit ; /* for the fill mode */ short tiny_inc ; /* next address (2 or -2) */ short ashift, bshift ; /* shift values */ short minterm ; /* the actual function we evaluate */ short result ; /* the value to stuff in d */ short old_result ; /* the blitter is pipelined */ long old_address ; /* where to stuff it */ short i ; /* general purpose iterator */ FILE *f ; /* debug output */ /* * Now we initialize our pointer variables, by concatenating the * high and low order words, and clearing the least significant * bit. The blitter ignores that least significant bit. * Note that the words concatenated in these four statements are * actually adjacent in the actual blitter layout, so they can be * referenced by a single longword read or write. */ aptr = CATWORDS(BLTAPTH,BLTAPTL) & ~1 ; bptr = CATWORDS(BLTBPTH,BLTBPTL) & ~1 ; cptr = CATWORDS(BLTCPTH,BLTCPTL) & ~1 ; dptr = CATWORDS(BLTDPTH,BLTDPTL) & ~1 ; /* * Our modulos also lose their least significant bit. Note that * they are treated as signed 16 bit values. */ amod = BLTAMOD & ~1 ; bmod = BLTBMOD & ~1 ; cmod = BLTCMOD & ~1 ; dmod = BLTDMOD & ~1 ; /* * Next, we extract some information from our control words. * Note that if either the hsize or vsize are zero, their * maximum values are used instead. */ hsize = BLTSIZE & HSIZEMASK ; if (hsize == 0) hsize = 64 ; vsize = (BLTSIZE >> HSIZEBITS) & VSIZEMASK ; if (vsize == 0) vsize = 1024 ; /* * We don't want to ever print out more than 32K of debug; there's no * reason, so we turn tracing off if hsize*vsize > 218. */ if (hsize * (long)vsize > 218) f = NULL ; else f = openlogfile() ; ashift = (BLTCON0 >> ASHIFTSHIFT) & 0xf ; bshift = (BLTCON1 >> BSHIFTSHIFT) & 0xf ; minterm = BLTCON0 & 0xff ; /* * We initialize the zero flag, the old word registers for the * shifters, and get the initial data values for the A, B, and * C channels. */ zeroflag = 0 ; a_old = 0 ; b_old = 0 ; old_address = -1 ; a_new = BLTADAT ; b_new = BLTBDAT ; c_hold = BLTCDAT ; /* * If we are blitting forward, each time around each DMA channel's * pointer is increased by 2. Otherwise, it is decremented by 2. * In addition, when blitting backwards, our modulos are subtracted, * so we take care of that by negating them here. */ if (BLITTING_FORWARD) { tiny_inc = 2 ; } else { tiny_inc = -2 ; amod = - amod ; bmod = - bmod ; cmod = - cmod ; dmod = - dmod ; } /* * Debug stuff */ if (f) { fprintf(f, "\namod = %04x bmod = %04x cmod = %04x dmod = %04x\n", amod, bmod, cmod, dmod) ; fprintf(f, "adat = %04x bdat = %04x cdat = %04x size = %04x\n", a_new, b_new, c_hold, BLTSIZE) ; fprintf(f, "con0 = %04x con1 = %04x afwm = %04x alwm = %04x\n", BLTCON0, BLTCON1, BLTAFWM, BLTALWM) ; logflagdata(f) ; } /* * We iterate through the rows, and print debug stuff each time. */ for (v=0; v>>Row %d\n", v) ; /* * At the beginning of each row, the fill bit gets set to its initial * value from the control register. */ fill_bit = (BLTCON1 & FILL_CARRYIN ? 0xffff : 0) ; /* * Now we run through the columns, again printing debug information * each time around. */ for (h=0; h> ashift ; b_hold = CATWORDS(b_old,b_new) >> bshift ; } else { a_hold = CATWORDS(a_masked,a_old) >> (16 - ashift) ; b_hold = CATWORDS(b_new,b_old) >> (16 - bshift) ; } a_old = a_masked ; b_old = b_new ; /* * Our minterm calculation is next. */ result = (minterm & 1 ? ~a_hold & ~b_hold & ~c_hold : 0) | (minterm & 2 ? ~a_hold & ~b_hold & c_hold : 0) | (minterm & 4 ? ~a_hold & b_hold & ~c_hold : 0) | (minterm & 8 ? ~a_hold & b_hold & c_hold : 0) | (minterm & 16 ? a_hold & ~b_hold & ~c_hold : 0) | (minterm & 32 ? a_hold & ~b_hold & c_hold : 0) | (minterm & 64 ? a_hold & b_hold & ~c_hold : 0) | (minterm & 128 ? a_hold & b_hold & c_hold : 0) ; /* * If we are in the fill mode, we do the appropriate thing. Note that * fill only works properly when in the descending mode. FILL_XOK * turns off bits which toggle fill_bit to zero. */ if (FILL_OK || FILL_XOK) for (i=1; i; i <<= 1) if (result & i) { if (FILL_XOK && fill_bit) result &= ~i ; fill_bit = ~fill_bit ; } else result |= (i & fill_bit) ; /* * Debug stuff. */ if (f) fprintf(f, "aval = %04x bval = %04x cval = %04x dval = %04x\n", a_hold, b_hold, c_hold, result) ; /* * We or in our result to our zero flag; this way, if we ever hit a zero * result, zeroflag will be non-zero. */ zeroflag |= result ; /* * The actual writing is pipelined one stage; if we buffered a write * last time around, we write it here. */ if (old_address != -1) { if (f) fprintf(f, "*** [%08lx] <-- %04x\n", old_address, old_result) ; screenupdate(old_address, old_result) ; } /* * If the destination is enabled, we buffer up a value to be written the * next time around. Again, the pointer might not really be incremented * if the destination is disabled. */ if (DEST_ENABLED) { old_result = result ; old_address = dptr ; } dptr += tiny_inc ; } /* * Next, we add the modulos to all the pointers at the end of each row. */ aptr += amod ; bptr += bmod ; cptr += cmod ; dptr += dmod ; } /* * Finally, we are at the end of the blit; if we still have a write in * the pipeline, we write it here. */ if (old_address != -1) { if (f) fprintf(f, "*** [%08lx] <-- %04x\n", old_address, old_result) ; screenupdate(old_address, old_result) ; } if (f) fclose(f) ; /* * If we ever saw a non-zero value, then zeroflag is set, so we return * a zero; otherwise, we return a 1. */ return(!zeroflag) ; } /* * And this is the simulator for when we are in the line mode. * (Oh, shucks, you mean it's not written yet?) */ int dolineblit() { error("we can't simulate line blits yet.") ; Delay(50L) ; return(0) ; } /* * The actual routine. All we do is call the appropriate real * routine, depending on whether the line mode is set or not. */ int dosimblit() { error("Simulating . . .") ; if (BLTCON1 & LINEMODE) return(dolineblit()) ; else return(donotlineblit()) ; }