Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!rochester!cornell!uw-beaver!mit-eddie!genrad!decvax!decwrl!labrea!rocky!rokicki From: rokicki@rocky.STANFORD.EDU (Tomas Rokicki) Newsgroups: comp.sys.amiga Subject: BlitLab 1.2 (Part 1/4) Message-ID: <374@rocky.STANFORD.EDU> Date: Mon, 22-Jun-87 20:25:00 EDT Article-I.D.: rocky.374 Posted: Mon Jun 22 20:25:00 1987 Date-Received: Wed, 24-Jun-87 05:49:20 EDT Organization: Stanford University Computer Science Department Lines: 710 This and the next four messages contain BlitLab 1.2. If you are using or have used BlitLab 1.1, you need to look at this update. In this update, the line drawing algorithm is done correctly, from information gleaned from a disassembly of the ROMs. Included in this shar archive is a uuencoded binary and TeX-style documentation. ---start--- #! /bin/sh : This is a shar archive. Extract with sh, not csh. echo x - README cat > README << '20488!Funky!Stuff!' BlitLab 1.2 22 July 1987 This is release 1.2, which adds access to two additional blitter flags, and corrects the line drawing algorithm. It also includes the blit.tex documentation. If you are interested in drawing lines, this program and its documentation now contains information which cannot be found in any of the published documentation on the blitter. BlitLab is a program which lets you experiment with the blitter to your hearts content, in relative safety. It opens up a workbench window with gadgets for all of the registers of the blitter, and allows you to manipulate individual registers and perform blits on a magnified bitmap. This documentation is sketchy, but it probably won't get much better. This program was written for a blitter presentation at the BADGE meeting of 16 April 1987, and it met with such approval that I decided to make it available over the net. I wrote it for Manx C using 16 bit ints. It should port to Lattice with a minimum of effort, but it doesn't. The code itself may turn out to be interesting. I wrote the entire program in two days, but I tried to keep things clean and modular. Any comments on the code, either positive or constructive criticism, is welcome. On to the program. I'm going to describe operation of the program starting in the upper left hand corner and proceeding across and down. I will not describe the operation of the blitter. The large dark area in the upper left is the bit map we are going to play with. It has a width of 96 bits, or 12 bytes, or 6 words. It has a height of 32 rows. Remember these numbers well. You can draw in this area with the mouse and the two gadgets in the upper right. The topmost gadget is the Point/Box gadget. When set to point, you can draw individual pixels on the screen by clicking on the pixels with the select button. When set to box, you can draw a filled rectangle by clicking on the upper left hand pixel of the box you wish to draw with the select button, dragging the mouse and releasing the select button over the lower left hand pixel. The next gadget is the Clear/Set gadget, allowing you to clear or set points. Note that this is not a paint program, and the interaction speed may not be as fast as you are used to; no apologies. It was only intended to draw bits for the blitter. Below these two gadgets are two numbers. The adrs number holds the address of the word the cursor is currently pointing at, relative to the beginning of the bit array. (It is written as, for instance, M+382, which means the address of the array plus 382 bytes.) The shift value is the pixel, numbered from 0 at the left to 15 at the right. With these values, you can point at a pixel to get its address to enter into a string gadget below. The Calc gadget looks at the values you have entered for the blitter, and determines if any memory other than the bit array will be modified. If it will, it prints `Blit unsafe' on the title bar. This is a sanity check to keep you from crashing the system. Carefully check your values if this flag is on. The fact that this gadget writes `Blit unsafe' does not necessarily mean that the blit will destroy memory outside of the array; careful use of a source operand and mask bits might not pass this test but would be perfectly okay. I have allocated several extra rows above and below the bit array so minor errors shouldn't crash the system. Currently line mode is not checked, so use it with care. The GO gadget performs the blit you have indicated. After the blit, the results are updated in the magnified bit array rather slowly. It performs the same check performed by Calc before executing, and complains similarly. You can override the complaint by clicking on the GO gadget again. Underneath the magnified bit map, on the left, are four gadgets labeled SX, SY, EX, and EY. These bits get the starting and ending points for the line you wish to draw. The X values should range from 0 to 95; the Y values from 0 to 31. These are not checked. Be aware that these are not blitter registers; they are simply values that the program will use in calculating the actual blitter register contents necessary for the line draw. The (line)/LINE gadget turns on line mode. The Setup gadget takes the SX/SY/EX/EY values, and sets up most of the blitter registers to draw the line automatically. It does not set up the function register, because there are different ways to draw lines. For a solid line, use ~AC+A; for a textured line, use ~AC+AB; for an XOR line, use ~AC+A~C, for instance. It does not perform the blit, however. Note that if you want to draw a new line, you need to change the appropriate SX/SY/EX/EY values, then click Setup, then click GO. The next two gadgets, W and H, hold the size of the current blit. The W value is in words, and the H value is in bytes. Legal values range from 1-64 for W, and 1-1024 for H. (Of course, the useful range within this program is much smaller.) Next to the H gadget are gadgets for the DESC, FCI, IFE, and EFE bits of the gadget. These can get set or cleared by selecting them. Underneath these gadgets is the Function gadget. In this gadget you enter the function you want to perform. 0 is clear all, 255 is set all, and other combinations can be entered directly as minterms. For instance, the A~C+~CA presented earlier means exclusive or the A and C bits for the destination. The lower right hand corner contains the actual blitter registers and their values in hexadecimal. These are the values that are fed to the blitter when you select GO. As you enter new numbers into the string gadgets corresponding to blitter registers, these values will change to reflect the new values. (Actually, they will only change if you hit carriage return after entering the numbers into the string gadget; if you simply select another gadget after entering data into a string gadget, the old value will stay displayed until you select CALC or GO.) This can be used to calculate minterms, for instance. Entering ABC+A~B~C+~AB~C+~A~BC into the Function gadget hitting return will put 96 in the least significant eight bits of CON0; this is the hexadecimal value reflecting the above minterms. At the lower right is a table of string gadgets. Each row is for a single DMA channel. The first column is the USE bit; is that channel on or off? Y means on, N means off; selecting the gadget toggles it. The next column contains the start address for DMA. These numbers (as in the remaining string gadgets) can be entered in decimal, hexadecimal (preceded by a $), binary (preceded by a %), or as an offset from the start of the bit array (preceeded by M+). M alone represents the beginning of the blit array. The third column is the modulo value which is added to the DMA channel pointer at the end of each row; this value is in bytes and can be negative. The fourth column is the data register; if a channel is turned off, its data register can be preloaded with a value, and then it functions as a constant. For instance, to fill a block of memory with the value $E931, simply preload the A data register with this value, turn on only the D channel, and blit with the function A. Finally, the last column is the amount to shift the A and B operands, to the right, before using them. Finally, in the lower left corner of the DMA channel box are string gadgets for AFWM and ALWM. This program has been tested fairly carefully. Therefore, if the blitter appears to do something it shouldn't, you probably have set up the registers wrong. Remember, the Amiga blitter is a word blitter, not a bit blitter. It can be made to look like a bit blitter with some work. It is possible, however, that errant blits can muck up some system memory, or some internal variable memory, so if things really seem awry, reboot and go back into the program. Try the same values again, and see if they work this time. Blitter documentation is not included here currently. Sometime in the next week I will finish my blitter documentation; that documentation will tell you more than you ever wanted to know about the blitter. So, until then, I remain: Tomas Rokicki Box 2081 Stanford, CA 94305 723-1646 (office), 326-5312, -5681 (home) rokicki@sushi.stanford.edu ...lll-crg!decwrl!sushi.stanford.edu!rokicki 20488!Funky!Stuff! echo x - bits.c cat > bits.c << '20488!Funky!Stuff!' /* * This routine handles the display of the bit array in BlitLab. */ #include "structures.h" /* * This is the address of the real bits we operate on. */ extern short int *realbits ; static int safearr[192] ; allocbitmem() { extern void *allocmem() ; realbits = (short *)(allocmem(1000L, MEMF_CHIP | MEMF_CLEAR)) + 150 ; } pdot(x, y, on) int x, y, on ; { int off = (x >> 4) + y * 6 ; int bit = 1 << (15 - (x & 15)) ; if (on) { if ((realbits[off] & bit) == 0) { realbits[off] |= bit ; safearr[off] |= bit ; color(WHITE) ; fbox(x * 6 + HBITSTART, y * 3 + VBITSTART + 1, 4, 2) ; } } else { if (realbits[off] & bit) { realbits[off] &= ~bit ; safearr[off] &= ~bit ; color(BLACK) ; fbox(x * 6 + HBITSTART, y * 3 + VBITSTART + 1, 4, 2) ; } } } preg(x1, y1, x2, y2, on) int x1, y1, x2, y2, on ; { int i, j ; for (i=x1; i<=x2; i++) for (j=y1; j<=y2; j++) pdot(i, j, on) ; } /* * This routine writes out the new position to the screen. */ updatepos(x1, y1) int x1, y1 ; { char outbuf[4] ; sprintf(outbuf, "%3d", ((x1 >> 3) & ~1) + y1 * 12) ; drawtext(HLMGSTART+28, VLMG3+6, outbuf) ; sprintf(outbuf, "%2d", x1 & 15) ; drawtext(HLMGSTART+12, VLMG4+6, outbuf) ; } updatebits() { int x=HBITSTART, y=VBITSTART+1 ; register short *p1 = realbits, *p2 = safearr ; int i = 192 ; int rc = 6 ; int bit ; while (i-- > 0) { if (*p1 == *p2) { p1++ ; p2++ ; x += 6 * 16 ; } else { bit = 0x8000 ; while (bit != 0) { if ((*p2 & bit) != (*p1 & bit)) { color((*p1 & bit) ? WHITE : BLACK) ; fbox(x, y, 4, 2) ; } bit = (bit >> 1) & 0x7fff ; x += 6 ; } *p2++ = *p1++ ; } if (--rc == 0) { rc = 6 ; x = HBITSTART ; y += 3 ; } } } 20488!Funky!Stuff! echo x - blit.tex cat > blit.tex << '20488!Funky!Stuff!' % % Blitter manual % %\magnification=\magstep1 % % We need a macro to do double column output. These are lifted % from the TeXbook, with only minor modifications. % \newdimen\fullsize\fullsize=7.2truein\hoffset=-0.35truein \hsize=3.45truein % this is awfully narrow; will it work? \vsize=9truein \def\makefootline{\baselineskip24pt\hbox to\fullsize{\the\footline}} \let\lr=L \newbox\leftcolumn \output={\if L\lr \global\setbox\leftcolumn=\columnbox \global\let\lr=R \else\doubleformat\global\let\lr=L\fi \ifnum\outputpenalty>-20000\else\dosupereject\fi} \def\doubleformat{\shipout\vbox{\makeheadline \hbox to\fullsize{\box\leftcolumn\hfil\columnbox} \makefootline} \advancepageno} \def\columnbox{\leftline{\pagebody}} % % And now, some more typical macros. % %\special{landscape()} \raggedbottom \def\section#1\\{\goodbreak\vskip\baselineskip\leftline{\bf #1}\vb} \def\vb{\vskip\baselineskip} \def\b #1(){{\bf #1()}} \def\not{$\neg$} \def\xor{$\oplus$} % % Normally ~ is a tie. We need it often enough to imply logical % negation that we redefine it, making it an active character. % \catcode`\~=\active\let~=\not \def\reg#1/{{\tt #1\space}} % % Some verbatim macros: % {\obeyspaces\gdef {\ }} \def\begverb#1 {\goodbreak\vskip\baselineskip \begingroup\def\par{\leavevmode\endgraf}% \catcode`\\=12\catcode`\{=12 \catcode`\}=12\catcode`\$=12\catcode`\&=12 \catcode`\#=12\catcode`\%=12\catcode`\~=12 \catcode`\_=12\catcode`\^=12\obeyspaces\obeylines\tt \parindent=0pt\catcode#1=0} \def\endverb{\par\endgroup} % % Now the report itself. % \ \vb \centerline{\bf BlitLab and the Amiga Blitter} \centerline{Tomas Rokicki} \centerline{21 May 1987} \vb % % Prefatory apology % \section Preface\\ Due to time constraints, this is not yet a complete report. There are still a couple of things which are sketchy. But pretend it is complete; complain about missing items so I can add them; correct any inaccuracies in the report. This manual will not make much sense without a working copy of BlitLab, as most of the documentation is in the examples provided. So, if you don't have it yet, go out and get it. It is on disk 69 of Fred Fish's Freely Redistributable Software Library. Or, just ask me for a copy. % % Introduction % \section Introduction\\ So you have pored over the Hardware Manual and the ROM Kernel Manual, and you cannot find the information you need on the blitter. Well, never fear; all the information you should ever need about the blitter is contained in this one handy document. All information below was derived from the Hardware Manual, ROM Kernel Manual, and a lot of empirical testing. Using the blitter directly, as described in this report, however, bypasses the layers library. If you want to use these techniques for graphics, open your own custom screen; if you open any windows on this screen, be careful to not destroy the graphics rendered by Intuition. % % Introduction to the Hardware % \section The Hardware\\ The blitter comprises part of the Agnes chip in the Amiga, and can only access the lower 512K (chip) memory. To the 68000, it appears as a set of approximately twenty sixteen bit write only registers. It can use memory at twice the bandwidth of the 68000, or 3.6 megabytes per second (although, as we shall see, it doesn't always run this fast.) Any video memory accesses can slow the blitter down, whether for screen refresh or for the 68000. For instance, the standard two bit deep high resolution workbench screen can slow the blitter down by approximately 30\%. A low resolution single bit plane screen can slow it down by about 8\%. A high resolution four bit plane screen can slow down the blitter by about 60\%. The blitter is so fast, however, that even with this handicap it performs its tasks many times faster than the 68000. The first thing a programmer of this chip must realize is that the Amiga blitter is not a `bit' blitter; rather, it operates on words. With the appropriate programming, it can manipulate arbitrary bit rectangles. This fact must be kept in mind when programming the blitter. The blitter uses four DMA channels to perform its work; these are called A, B, and C (sources) and D (destination). Any or all of them may be disabled independently. The destination can be calculated from any of 256 possible logical equations on A, B, and C. The A and B sources can be shifted up to 15 bits to the right, and the first and last word in a line from the A source can be masked by a constant. Each of the four channels has its own modulo. The blitter also has an area fill and a line draw mode. % BlitLab \section BlitLab\\ To allow easy experimentation with the blitter, I have written a program called BlitLab. This program provides a laboratory in which you can play with the blitter registers in a safe manner. It is over 1300 lines of code that I wrote in under two days, so beware of any bugs. The blitter has a much greater potential for damage to system memory than the 68000 does, since once started it performs operations on large areas of memory without interruption or instruction checking. If the 68000 starts executing random data as instructions, it usually very quickly executes an odd-address or illegal op-code trap. The blitter, on the other hand, could easily wipe out kilobytes of data before the system noticed anything was amok. BlitLab therefore carefully checks the values you have entered to be sure that system memory will not be overwritten. Only if it will not be are you allowed to do the blit. That is, except for line mode; it is so complicated to check for line mode validity, that I let you trash memory with it. But more on that later. % Getting Access to the Blitter \section Getting Access\\ There are currently four ways you can use the blitter. Some work better than others. The first way is to use the standard ROM Kernel routines for graphics. This is the simplest and most reliable method; future blitters and operating systems will not disrupt your code. I am not going to discuss this approach here, because I don't want to, and all of that information is in the ROM Kernel Manuals. The second method is to arbitrarily write to the blitter registers, ignoring Intuition and friends. This is a good way to make enemies; you can trash disks as well as system memory, but it makes for good laughs on those slow winter nights. Just pop some random values into those blitter registers, and watch the pyrotechnics fly! The third method is a variation of the second, but you politely request permission from Intuition first, by calling \b OwnBlitter(). This routine notifies the Amiga that you want exclusive access to the blitter, and you don't want anyone else playing with it. After this call returns, you can almost use the blitter. Unfortunately, someone else may have already given the blitter something to do that hasn't completed; therefore, you should call \b WaitBlit() before actually mucking with the registers. This second routine blocks until the blitter is actually finished with its work. Once \b WaitBlit() returns, you are free to do what you like with the blitter. While you have the blitter, you must remember that Intuition cannot use it. Therefore, \b Text() calls will not work, and your debug printf's will block if they are written to the screen, for instance. The blitter is used for disk I/O and most user interaction like gadgets, so tying up the blitter for long periods of time (longer than, say, a few milliseconds) is considered highly unfriendly. Tying up the blitter for a second or more is grounds for lynching. When you are finished with the blitter, you should call \b DisownBlitter(), to allow Intuition to do what it likes. Remember, however, that \b DisownBlitter() might return before the blitter is finished with your last operation, so before you use any data created by the blitter, call \b WaitBlit(), to allow the blitter to finish. This last point is worth rereading, as it is often the source of some subtle bugs. Thus, your code might look like the following: \begverb{`\$} OwnBlit() ; WaitBlit() ; /* * here you can muck with the blitter * until it falls off . . . */ draw_my_polygons() ; /* for example */ DisownBlitter() ; /* * Now we want to examine the memory region * the blitter played with. */ WaitBlit() ; copy_to_disk() ; /* for example */ $endverb % % QBSBlit add more here at some later date % There is another way to gain access to the blitter; you can use the supplied blitter queue routines. This is described (perhaps inadequately) in the ROM Kernel Manual, Volume 1, pages 2-62 through 2-65. As of yet, I haven't had reason to use these routines; perhaps a later edition of this manuscript will have information on them. % % DMA channels % \section DMA Channels\\ So now we have control of the blitter; we can write to all of its registers and do whatever we like. Before we get into exactly what we can do, let me describe the blitter DMA channels. As mentioned, the blitter has four DMA channels, A, B, C, and D. These are shown in the lower right hand corner of the BlitLab display. What, you say you are not in BlitLab yet? Go to your Amiga, plug in a disk with BlitLab on it, and {\tt run blitlab}. The rest of this manual assumes that you have done this. If you are running an interlaced screen or odd colors, you might find the BlitLab display visually jarring; I recommend rebooting with the default WorkBench configuration and running BlitLab from there. Back to the DMA channels. The first three are always sources, the last is always a destination. You can use any combination of the four channels, from none of them to all four. Each of the four channels has an 18 bit address pointer which points to the memory it will use or modify. The least significant bit of the low order word, and the most significant fifteen bits of the high order word are ignored; this leaves 18 bits of a 32-bit pointer. Each channel also has an independent 15 bit signed modulo (in bytes, with the least significant bit again ignored.) For the three source DMA channels, there is also a data register which you can preload with constant data if the DMA channel is turned off. The DMA channels share a width and a height register. The width is in 16 bit words, and can take a value from 1 to 64. The height is in pixels, and can take a value from 1 to 1024. Thus, the largest rectangular field on which the blitter can directly operate is 1024 by 1024. However, larger fields can be handled by splitting a blit into smaller blits, and using the modulo fields appropriately. A key thing to remember here is that the width is in words, the modulos and pointers are in bytes, and the height is in pixels. You must remember this. \section Block Clear\\ The destination can receive any logical combination of its three source operands. Let's start experimenting. First, we'll try to clear memory. First we will try to clear memory. The large black rectangle in the upper left hand corner of the BlitLab window is the bit region we can experiment with. Let's set some random bits to make sure clear is working. Select the gadget currently marked `Clear'; it should toggle and be marked `Set'. Now, move the mouse into the black rectangle, hold down the left mouse button, and move the mouse around. Set pixels until you tire of the novelty. Note that as you move the mouse, the Adrs and Shift fields change; these will be useful in a second. Once you have a reasonable number of pixels on the screen, you are ready to begin. To enter data into a string gadget, select the string gadget, backspace over the old data, type new data, and then hit return. First, in the gadget marked W, enter the number 6. The field we are working on is 96 pixels wide by 32 pixels high; that is six words by 32 rows. Enter 32 into the H gadget. Now, turn on the D DMA channel by selecting the gadget marked `N' in the D row; it should toggle and show `Y'. Enter M into the PT column; this is the symbolic name for the address of the rectangle we are experimenting with. Enter 0 into the `Function' gadget near the center of the window. We are ready to go. At this point, select the `Calc' gadget. BlitLab will read the values you have entered and make sure that you are not going to clobber the system. If the blit is safe, it will do nothing; otherwise it will print an error message on the window title bar. If you get the error message, it is likely that your machine will crash if you ask it to perform the blit. Otherwise, go ahead and select the `GO' gadget. The pixels you so laboriously set should disappear. Congratulations, you have performed your first blit! You may have noticed that the pixels actually disappeared rather slowly. If this is the case, you have a defective Amiga. No, actually this is an artifact of the program; BlitLab is performing the blits in some other memory somewhere, and then copying and expanding the bits to the rectangle displayed on the screen. It is this updating and expanding that is slow, not the blit. So don't be alarmed. Before we get steeped in the explanations, let's experiment some more. Set the `Function' to 255, and hit `GO' again. This should set all of the pixels in our rectangle. Set it back to 0, and set the height to 16. Now, only half of the rectangle is cleared. Set the modulo (for the D channel) to 6 (bytes), and reduce the width to 3 (words, remember?) Set the `Function' back to 255, and `GO'. Now the upper left corner of the rectangle is cleared. Oops, we are probably getting ahead of ourselves here. For now, just take my word for the fact that setting the function to 0 clears the destination, and setting the function to 255 sets all of the bits in the destination. I am sure the width and height (that's the W and H gadgets) explain themselves well enough, as does the address pointer (that's PT.) But, how is the modulo interpreted? The algorithm the blitter uses to do a blit looks something like this: \begverb{`\$} doblit(daddress, height, width, dmodulo) char *daddress ; int height, width, dmodulo ; { int i, j ; for (j=0; j