The Root of the Problem (PC Tech Journal January 1987 by Murray L. Lesser) Building an intricate graphics program requires evaluation of thousands of square roots, but only to integer accuracy. BASIC compilers do it the hard way: they convert the integer function argument to a single-precision, floating-point number, take the square root of that, then convert the floating-point result back into an integer. ROOT.ASM produces the nearest integer to the actual square root of an integer argument. It returns a zero value for a negative argument rather than an error. Because the routine has no absolute jumps, it could be used to speed up interpreted BASIC if it were coded into an integer array. ROOT also can be BLOADed into an interpreted BASIC program. An example of ROOT in use under Microsoft's QuickBASIC is given in ROOTTEST.ASM. ROOTTEST runs four times faster using ROOT than it does using QuickBASIC's floating-point, square-root function. ; ROOT.ASM -- an assembled fast integer square root subroutine to be ; linked to Microsoft compiled BASIC programs. ; Call with ROOT(x,y) where x is an integer expression ; y is an integer variable ; The square root of x will be returned in y. (Negative input will ; return a zero.) ; ROOT is a binary adaptation of the synthetic division square root ; procedure shown in the paper by J. E. Meggit, "Pseudo Division and ; Pseudo Multiplication Processes." The IBM Journal of Research and ; Development, vol. 6, no. 2 (April 1962), pp. 210-226. ; Written by M. L. Lesser, April 13, 1986. ; Assembled with Microsoft MASM version 4.00 ; DATA SEGMENT BYTE PUBLIC 'CODE' DATA ENDS DGROUP GROUP DATA SQRT SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:SQRT,DS:DGROUP PUBLIC ROOT ROOT PROC FAR PUSH BP MOV BP,SP MOV BX,8[BP] MOV AX,[BX] XOR DX,DX MOV CX,8 MOV BX,4000H MOV DI,8000H MOV SI,2000H CMP AX,DX JLE DONE DOIT: SHL DX,1 CMP AX,BX JL NEXT INC DX SUB AX,BX ADD BX,DI NEXT: SUB BX,SI SHR DI,1 SHR DI,1 SHR SI,1 SHR SI,1 SHR BX,1 LOOP DOIT CMP AX,BX JLE DONE INC DX DONE: MOV BX,6[BP] MOV [BX],DX POP BP RET 4 ROOT ENDP SQRT ENDS END ROOTTTEST.ASM: ' TEST.BAS: An example of the use of ROOT.ASM. ' Compile with a Microsoft BASIC compiler and link to ROOT.OBJ defint a-c defsng d for a=0 to 32766 call root(a,b) let d=sqr(a) let c=d gosub 1000 if inkey$=chr$(3) then end next a let a=32767 call root(a,b) let=sqr(a) let c=d gosub 1000 end 1000 print a,b,c,d if b<>c then while inkey$<>"":wend return ----------------------------------------------------------------- Using PUT and GET on the IBM PC (COMPUTE! Magazine March 1987 by Rafael Gonzalez) IBM BASIC has two commands -- PUT and GET -- that make it easy for you to animate figures. These powerful commands appear frequently in games, but they have many other uses, as well. (PUT and GET are also used for random file operations, but with a different syntax.) You might think of PUT and GET as "bit pump" operations which move bits from memory onto the screen (PUT) and from the screen into memory (GET). GET reads the colors of the points within a rectangular screen area and stores that information in an array. The basic syntax for the command is: GET(x1,y1)-(x2,y2),array Each GET command includes two pairs of screen coordinates and an array name. The coordinates define the area to be captured and the array name tells BASIC where to store the image. The first pair of coordinates (x1 and y1 in this example) defines the upper left corner of the rectangle. The second pair (x2 and y2) defines the rectangle's lower right corner. (This is identical to the method used to define a rectangle in a LINE command with the B option.) The array used with GET must be of the numeric type. It can be any precision, although integer arrays are commonly used. Except for very small shapes, you must DIMension the array before using it. This task, in turn, requires that you calculate how big the array should be. The BASIC formula for calculating the array size is: 4 + INT((x * bits per pixel + 7) / 8) * y In this case, x and y are the lengths of the horizontal and vertical sides of the rectangle, respectively. The bits per pixel value is equal to 4 in low resolution, 2 or 4 in medium resolution, and 1 or 2 in high resolution, depending upon the current screen mode. For example, suppose you want to capture a 10 x 12-pixel image in medium resolution with GET. The number of bytes required is 4 + INT ((10 * 2 + 7) / 8) * 12, or 40 bytes. Next, you must consider how many bytes each element of the array contains. This factor depends on the array's precision. This table shows how many bytes are contained in each element of an integer, single-precision, or double-precision array: Bytes Array Type 2 Integer 4 Single-precision 8 Double-precision Since the example shape requires 40 bytes, it can be stored in an integer array containing 20 elements, a single-precision array containing 8 elements, or a double-precision array containing 5 elements. It's important to dimension an array of the proper size, since BASIC stops with the error message, "Illegal function call" if the array is too small. Using an overly large array doesn't do any harm. However, grossly overlarge arrays waste memory. The PUT command is the opposite of GET: Once you have stored a shape with GET, PUT can place the shape anywhere on the screen. The syntax is: PUT (x,y),array,action In this example, x and y set the coordinates where the upper left corner of the image will be placed and the variable array identifies the array which contains the shape. The optional parameter action lets you select different modes for a PUT operation. This part of the statement may consist of the word PSET, PRESET, XOR, OR, or AND. If you omit the action parameter, PUT defaults to XOR mode. The PUT mode determines how the placed shape interacts with graphics data that's already present in the same screen area. Type in and save the example program, then run it to see how the mode affects PUT. The program draws a multicolored background and PUTs the same sahep on the screen in five different places using all of the different modes. Here's what each mode does: PSET. In this mode, PUT simply stores the captured data on the screen, overwriting any graphics data that previously existed in the same area. In the example program the transferred image completely replaces the contents of that screen area. PRESET. This mode replaces all existing data, just as in PSET mode, but the image is reversed. That is, a value of 0 in the array causes the corresponding point on the screen to have attribute number 3, and vice versa. A value of 1 in the array causes the corresponding point on the screen to have attribute 2, and so forth. In the program, this mode causes the image to have a different color. AND. The AND mode sets pixels only at points that already contain data matching corresponding data in the transferred image. In the example program, only pixels that are originally cyan remain in the final image. OR. This mode superimposes an image onto existing data. XOR. The XOR mode is most often used for animation. When a pixel in the PUT image overlays a point on the screen that contains data, the point is inverted. This feature allows you to move a shape nondestructively over a complex background: When an image is PUT against a background twice, it restores the original data unchanged. The following tables show how AND, XOR, and OR modes affect screen attributes in medium-resolution mode (SCREEN 1 or SCREEN 4). AND --- Array Value screen 0 1 2 3 0 0 0 0 0 1 0 1 0 1 2 0 0 2 2 3 0 1 2 3 OR -- Array Value screen 0 1 2 3 0 0 1 2 3 1 1 1 3 3 2 2 3 2 3 3 3 3 3 3 XOR --- Array Value screen 0 1 2 3 0 0 1 2 3 1 1 0 3 2 2 2 3 0 1 3 3 2 1 0 The example program also demonstrates simple animation with PUT in XOR mode. After the five large shapes are drawn, it sends a small shape bouncing around the screen. The process of animation involves four basic steps: 1. Calculate a new position for the shape. 2. PUT the shape on the screen at its previous location (to erase the old image). 3. PUT the shape in its new position. 4. Return to step 1. Before you enter the loop, you must have PUT the shape on the screen once, so that the PUT in step 2 will erase it. This preliminary step is performed in line 440 of the program. Line 450 saves the old position of the shape in OLDX and OLDY before a new position is calculated in lines 460-490. BASIC animation with PUT always involves a certain amount of flickering, which results from the delay between the time the old shape is erased and the new one is drawn. To minimize flicker, you should perform the two PUTs as close together as possible. This reduces the amount of time that the shape is invisible. The example program accomplishes this by putting both PUT statements on the same line. The first statement in line 510 erases the old image, and the second statement draws the new one. The do-nothing loop in line 520 holds the new image on the screen for a short interval to alleviate flicker even further. Most programs won't need an explicit delay, since the program will be doing time-consuming tasks between each redraw. Once you understand the basics of GET and PUT, you may find many uses for these commands. A drawing program, for instance, may include a feature allowing you to copy one screen area to another. If you capture the indicated area with GET, it is effectively saved in an offscreen buffer, and can be replaced at any time with a simple PUT command. In fact, if sufficient memory is available, you can even GET an entire screen. To see the effect of a full-screen GET, press any key while the small box is moving on the screen. The program saves the current screen in the array SCRN2%, then PUTs on the screen an image previously stored in the array SCRN1%. Immediately thereafter, it restores the current image by PUTting the SCRN2% image back on the screen. As you can see, significant delays result from manipulating images of this size. 100 SCREEN 1:KEY OFF:RANDOMIZE TIMER 110 LINE (60,60)-(120,120),1,BF 120 X=61:Y=61 130 BITSPERPIXEL=2 140 NUM=4+INT((X*BITSPERPIXEL+7)/8)*Y 150 DIM LARGE%(NUM/2),SMALL%(NUM/4) 160 X=320:Y=200 170 NUM=4+INT((X*BITSPERPIXEL+7)/8)*Y 180 DIM SCRN1%(NUM/2),SCRN2%(NUM/2) 190 GET (60,60)-(120,120),LARGE% 200 GET (60,60)-(90,90),SMALL% 210 CLS 220 FOR COL=1 TO 3:FOR J=1 TO 50 230 X=INT(RND*319):Y=INT(RND*199) 240 PSET (X,Y),COL:NEXT:NEXT 250 GET (0,0)-(319,199),SCRN1% 260 FOR J=40 TO 140 STEP 10 270 LINE (0,J)-(320,J+1),1,BF 280 LINE (0,J+2)-(320,J+3),2,BF 290 LINE (0,J+4)-(320,J+5),3,BF 300 NEXT 310 PUT (30,20),LARGE%,PSET 320 PUT (120,20),LARGE%,PRESET 330 PUT (210,20),LARGE%,AND 340 PUT (70,110),LARGE%,OR 350 PUT (180,110),LARGE%,XOR 360 LOCATE 2,6:PRINT "PSET" 370 LOCATE 2,17:PRINT "PRESET" 380 LOCATE 2,29:PRINT "AND" 390 LOCATE 23,13:PRINT "OR" 400 LOCATE 23,26:PRINT "XOR" 410 X=10:Y=50:DX=2:DY=2 420 RLIM=320-32:LLIM=0 430 ULIM=0:DLIM=200-32 440 PUT (X,Y),SMALL% 450 OLDX=X:OLDY=Y 460 IF INKEY$<>"" THEN GET (0,0)-(319,199),SCRN2%:PUT (0,0),SCRN1%,AND:PUT (0,0),SCRN2%,PSET 470 X=X+DX 480 IF X=>RLIM OR X<=LLIM THEN DX=-DX 490 Y=Y+DY 500 IF Y<=ULIM OR Y>=DLIM THEN DY=-DY 510 PUT (OLDX,OLDY),SMALL%:PUT (X,Y),SMALL% 520 FOR J=0 TO 80:NEXT 530 GOTO 450 ----------------------------------------------------------------- Sowing a Random FIELD (PC World March 1987 The Help Screen) BASIC 2.0 supports random file record lengths up to 32,767 bytes. The problem is using a random file structure to store 2048 single- precision values (8192 bytes) per record. The trick is to use multiple FIELD statements. Consider: 1 OPEN "FOO" AS #1 2 FIELD 1,100 AS A$,200 AS B$ 3 FIELD 1,300 AS DUMMY$,40 AS C$ Note that line 3 uses a dummy variable to move the file buffer's pointer past the area assigned for A$ and B$ (100 + 200 = 300) so that the field definitions of line 2 are not lost.