DEC.BANKS@MARLBORO.DEC.COM (12/08/85)
From: Dawn Banks <DEC.BANKS@MARLBORO.DEC.COM> Herein lies the ABasiC source for the Mandelbrot zoom program. This distribution consists of two ABasiC programs: MandelMung.bas, which contains the source for the assembly language subroutine, and Mandelbrot.bas, which is the actual zoom program. To bootstrap these programs, run ABasiC, and give the following commands: RUN MandelMung ' which should take about 1 minute RUN Mandelbrot ' which should take about 15 minutes Running Mandelbrot the first time will cause it to compute the entire set (which is the part that takes 15 minutes), and write it to disk as MandelSet.320. This is a low resolution, 4 bit plane image in which each point is computed to 250 iterations. Any time Mandelbrot.bas is run after this, it will read either MandelSet.640 or MandelSet.320 and display it on the screen. Once done, it waits for the left mouse button to be pressed. When pressed, it prompts for a command. A complete list of available commands may be obtained by typing the "HELP" command. When typing commands, always type the command name, followed by a carraige return. Mandelbrot.bas will then prompt for all subsequent arguments needed to complete the command. Some of the more interesting commands are: SET which prompts for the X and Y coordinates, the DeltaX (difference between the X value at the right and left of the screen - not the difference between single points on the screen), screen resolution (0 or 1 as given to the basic SCREEN statement), bit plane count and iteration count. When prompting for each of these values, the default value will be given in parenthesis. MOUSE Use the mouse to set the coordinates. Move the pointer to the lower left of the desired coordinate "box", press the left mouse button, then move the pointer to the upper right of the box and press the mouse button again. Note that due to several bugs, this command will not work properly if issued after a set command, nor will the set command work properly if issued after a MOUSE command. SHOW Show the current coordinate settings GO Compute the set as given by the coordinates set by the SET or MOUSE commands. READ Read and display an existing set SAVE Save the current set (may not be done after a SET or MOUSE command, unless the set has been computed via the GO command). This will also save the coordinate data with the actual picture. EXIT Exit to ABasiC. Program notes: In order to speed computation of the set, an assembly language subroutine which does the actual per point iteration. Code readers will notice that it does not use the normal floating point representation. Since virtually all the points being computed have an absolute value in the range of .01 to 1.99, a floating binary representation isn't absolutely necessary. As a result, a 32 bit fixed point representation has been chosen in which the high order bit is the sign, the next higher bit is considered to be "before" the decimal point, and the remaining 30 bits are considered fraction. This yields an exclusive range of 0 to 2, and in many cases produces a couple more bits of precision than the normal 32 bit floating point, as well as reducing execution time. This results in low resolution screen computation time of between 15 - 45 minutes for most regions. As is well documented in the USENET Net.Micro.Amiga mailing list, high resolution screens of more than 2 bit planes will steal compute cycles from the 68000 processor. It is suggested that when computing high resolution sets, the screen should be "hidden" under a screen of fewer bit planes. Fortunately, the workbench screen fits this. Typing some combination of {Amiga key} N or M will bury the ABasiC screen without affecting the run, except to speed it up. Two last points: you're going to need at least 512K bytes of memory, and using 5 bit plane low resolution screens aren't going to do much for you, as the program doesn't set the last 16 color registers, which are by default mapped to the same colors as the first 16 registers. It wouldn't take much work to modify the program to do this, but we didn't. Happy hacking! MandelMung.bas: 1000 Dim code%(71),regs%(16) 1010 Restore 10000 1020 i% = 0: Read j$ 1030 While Asc(j$) <> 126 1040 If Asc(j$) = 38 Then Poke_W VarPtr(code%(0)) + i%, Val(j$): i% = i% + 2 1050 Read j$ 1060 WEnd 1070 BSave "MandelMung", VarPtr(code%(0)), 282 1999 End 1 ' 1 ' MANDEL - calculate Mandelbrot set membership for a point 1 ' 1 ' A point C (in the complex plane) belongs to the Mandelbrot set 1 ' if, after iteratively calculating the value Zn+1 = Zn2 + C (with Zo 1 ' being 0 + 0i) the value of Z converges or stabilizes. In this 1 ' algorithm, we actually just test if the magnitude of Z reaches or 1 ' exceeds 2. 1 ' 1 ' Arguments: 1 ' 1 ' D0 Cr (real component) in 2's complement fixed point binary 1 ' notation with 2 bits in front, 30 behind the binary point 1 ' 1 ' D1 Ci (imaginary component) 1 ' 1 ' D2 maximum number of iterations, 16 bit unsigned integer 1 ' 0 means 65536 1 ' 1 ' Returns: 1 ' 1 ' D2 number of iterations executed 1 ' 1 ' creams all data registers 1 ' 1 ' Stack and register usage: 1 ' 1 ' A(A7) saved maximum iteration count (integer word) 1 ' 1 ' 9(A7) -1 if Cr is negative, 0 otherwise (boolean byte) 1 ' 1 ' 4(A7) absolute value of Cr 1 ' 1 ' (A7) absolute value of Ci 1 ' 1 ' D0,D1,D3 miscellaneous temporaries 1 ' 1 ' D2 -1 if sign of Zr differs from sign of Cr, 0 otherwise 1 ' 1 ' D4 absolute value of Zr (usually) - an unsigned longword 1 ' fixed point number with 1 or 2 (depending on when) 1 ' bits in front of the binary point 1 ' 1 ' D5 absolute value of Zi (usually) 1 ' 1 ' D6 sign of Zi or 2ZrZi (depending on what calculation 1 ' is pending). 0 for positive, -1 for negative 1 ' 1 ' D7 iterations are counted down here using DBF. The value 1 ' starts out one less than the maximum count input in D2, 1 ' because of the behaviour of DBF 1 ' 1 ' Initialize stuff, set up stack frame 1 ' 1 ' MANDEL: 10000 Data &o051502 ," SUBQ.W #1,D2 Adjust count for DBF" 10002 Data &o037402 ," MOVE.W D2,-(A7) Save count on stack" 10004 Data &o037002 ," MOVE.W D2,D7 Move count to D7" 10006 Data &o045200 ," TST.L D0 Check sign of Cr" 10010 Data &o055702 ," SMI D2 Set D2 if negative" 10012 Data &h6A06 ," BPL 10$ Positive, no worry" 10014 Data &o042200 ," NEG.L D0 Cr < 0, |Cr| = -Cr" 10016 Data &h6900,&hAA ," BVS DONE1 Cr was -2, done" 1 ' 10022 Data &o045201 ," 10$: TST.L D1 Check sign of Ci" 10024 Data &h6A06 ," BPL 20$ Already positive" 10026 Data &o042201 ," NEG.L D1 Nope, make it +ive" 10030 Data &h6900,&hAC ," BVS DONE2 Ci was -2, done" 1 ' 10034 Data &o037402 ," 20$: MOVE.W D2,-(A7) Stack sign of Cr" 10036 Data &o027400 ," MOVE.L D0,-(A7) Put |Cr| on stack" 10040 Data &o027401 ," MOVE.L D1.-(A7) Put |Ci| on stack" 10042 Data &o074000 ," MOVEQ #0,D4 Clear Zr" 10044 Data &o075000 ," MOVEQ #0,D5 and Zi" 10046 Data &o076000 ," MOVEQ #0,D6 and sign of Zi" 1 ' 1 ' Add Ci to 2ZrZi and Cr to Zr2-Zi2 to get new Zi and Zr, respectively 1 ' 1 ' MNDLUP: 10050 Data &o045006 ," TST.B D6 Check sign of 2ZrZi" 10052 Data &h660C ," BNE 10$ Go do -ive things" 10054 Data &o155227 ," ADD.L (A7),D5 Add Ci to 2ZrZi" 10056 Data &h6500,&h92 ," BCS DONE Result >= 4, done" 10062 Data &h6B00,&h8E ," BMI DONE Result >= 2, done" 10066 Data &h600E ," BRA 20$ OK, go do real part" 1 ' 10070 Data &o115227 ," 10$: SUB.L (A7),D5 Add -Ci to 2ZrZi" 10072 Data &h6506 ," BCS 15$ Negative?" 10074 Data &h6B00,&h84 ," BMI DONE Result >= 2, done" 10100 Data &h6004 ," BRA 20$ OK, go do real part" 1 ' 10102 Data &o042205 ," 15$: NEG.L D5 Make Zi positive" 10104 Data &o043006 ," NOT.B D6 Note change in sign" 1 ' 10106 Data &o045002 ," 20$: TST.B D2 sgn(Zr2-Zi2)<>Sgn(Cr)" 10110 Data &h660A ," BNE 30$ Signs <>, subtract" 10112 Data &o154257,&h4 ," ADD.L 4(A7),D4 Add Cr to Zr2-Zi2" 10116 Data &h6572 ," BCS DONE Result >= 4, done" 10120 Data &h6B70 ," BMI DONE Result >= 2, done" 10122 Data &h600E ," BRA 40$ Go square Z" 1 ' 10124 Data &o114257,&h4,"30$: SUB.L 4(A7),D4 Add -Cr to Zr2-Zi2" 10130 Data &h6504 ," BCS 35$ Negative?" 10132 Data &h6B66 ," BMI DONE Result >= 2, done" 10134 Data &h6004 ," BRA 40$ |Cr|<|Zr2-Zi2|" 1 ' 10136 Data &o042204 ," 35$: NEG.L D4 Think positive" 10140 Data &o043002 ," NOT.B D2 Sign changed" 1 ' 10142 Data &o132057,&h9,"40$: CMP.B 9(A7),D2 Find sign of Zr" 10146 Data &h6702 ," BEQ 45$ Positive, go on" 10150 Data &o043006 ," NOT.B D6 Note sign of 2ZrZi" 1 ' 1 ' Square Z, i.e., calculate 2ZrZi and Zr2-Zi2 1 ' also, check magnitude of Z > 2 (Zr2+Zi2 > 4) 1 ' 10152 Data &o161604 ," 45$: ASL.L #1,D4 Align Zr" 10154 Data &o161605 ," ASL.L #1,D5 and Zi for BIGMUL" 10156 Data &o027404 ," MOVE.L D4,-(A7) Push Zr" 10160 Data &o027405 ," MOVE.L D5,-(A7) Push Zi" 10162 Data &h6158 ," BSR BIGMUL Get ZrZi" 10164 Data &o023037 ," MOVE.L (A7)+,D3 High end into D3" 10166 Data &o160727 ," ASL.W (A7) Get top of low end" 10170 Data &o161623 ," ROXL.L #1,D3 Make 2ZrZi" 10172 Data &o045127 ," TST.W (A7) Check next bit" 10174 Data &h6A02 ," BPL 50$ Is is set?" 10176 Data &o051203 ," ADDQ.L #1,D3 Yup, round up" 1 ' 10200 Data &o027204 ," 50$: MOVE.L D4,(A7) Put Zr on stack" 10202 Data &h6176 ," BSR BIGSQR Get Zr2" 10204 Data &o027405 ," MOVE.L D5,-(A7) Push Zi" 10206 Data &h6172 ," BSR BIGSQR Get Zi2" 10210 Data &o046327,&h33 ," MOVEM.L (A7),<D0,D1,D4,D5>" 10214 Data &o151205 ," ADD.L D5,D1 Add low parts" 10216 Data &o150604 ," ADDX.L D4,D0 Add high parts" 10220 Data &h6522 ," BCS DONE3 Zr2+Zi2 >= 4" 10222 Data &o46337,&h33 ," MOVEM.L (A7)+,<D0,D1,D4,D5>" 10226 Data &o012057,&h9 ," MOVE.B 9(A7),D2 Get sign of Cr" 10232 Data &o115201 ," SUB.L D1,D5 Subtract Zi2" 10234 Data &o114600 ," SUBX.L D0,D4 from Zr2" 10236 Data &h6406 ," BCC 60$ Positive?" 10240 Data &o042205 ," NEG.L D5 No, make it" 10242 Data &o040204 ," NEGX.L D4 positive" 10244 Data &o043002 ," NOT.B D2 sgn(Zr2-Zi2)<>sgn(Cr)" 1 ' 10246 Data &o045205 ," 60$: TST.L D5 Check top of bottom" 10250 Data &h6A02 ," BPL 70$ If 0, don't round" 10252 Data &o051204 ," ADDQ.L #1,D4 Round up Zr" 1 ' 10254 Data &o143505 ," 70$: EXG D3,D5 Move 2ZrZi to D5" 10256 Data &o050717,&hFF78 ," DBF D7,MNDLUP ReSlog" 10262 Data &h600E ," BRA DONE Out of count, done" 1 ' 1 ' Various ways out 1 ' 1 ' DONE3: 10264 Data &o047757,&h10 ," LEA 10(A7),A7 Pop off Zr2, Zi2" 10270 Data &H6008 ," BRA DONE Go finish up" 1 ' DONE1: 10272 Data &o045201 ," TST.L D1 Cr=-2, does Ci=0?" 10274 Data &h6608 ," BNE DONE2 No, no big deal" 10276 Data &o077377 ," MOVEQ #-1,D7 Pretend we DBFed" 10300 Data &h6004 ," BRA DONE2 Go end it all" 1 ' DONE: 10302 Data &o047757,&hA ," LEA A(A7),A7 Remove Ci, Cr, flags" 1 ' DONE2: 10306 Data &o117527 ," SUB.W D7,(A7) Figure how many" 10310 Data &o032037 ," MOVE.W (A7)+,D2 times, put in D2" 10312 Data &o047165 ," RTS POPJ" 1 ' 1 ' BIGMUL - unsigned 32 bit multiply 1 ' 1 ' Arguments: 1 ' 1 ' 2 unsigned longwords received on the stack 1 ' 1 ' Returns: 1 ' 1 ' 1 unsigned quadword on the stack 1 1 ' creams D0, D1, D2 and D3 1 ' 1 ' BIGMUL: 10314 Data &o046257,&hF,&h4," MOVEM.W 4(A7),<D0,D1,D2,D3>" 10322 Data &o140302 ," MULU D2,D0 Both high order parts" 10324 Data &o141303 ," MULU D3,D1 Both low order parts" 10326 Data &o142357,&h6 ," MULU 6(A7),D2 High by low" 10332 Data &o143357,&h4 ," MULU 4(A7),D3 Low by high" 10336 Data &o027500,&h4 ," MOVE.L D0,4(A7) Store high back" 10342 Data &o027501,&h8 ," MOVE.L D1,8(A7) Store low back" 10346 Data &o152203 ," ADD.L D3,D2 Combine middle parts" 1 ' 10350 Data &h6404 ," BIGCMN: BCC 10$ Any carry?" 10352 Data &o051157,&h4 ," ADDQ.W #1,4(A7) Yup, incr. top word" 1 ' 10356 Data &o152657,&h6,"10$: ADD.L D2,6(A7) Put middle in middle" 10362 Data &h6404 ," BCC 20$ Any carry?" 10364 Data &o051157,&h4 ," ADDQ.W #1,4(A7) Yup, incr. top word" 1 ' 10370 Data &o047165 ,"20$: RTS Done, return" 1 ' 1 ' BIGSQR - unsigned 32 bit square 1 ' 1 ' Arguments: 1 ' 1 ' 1 unsigned longword on the stack 1 ' 1 ' Returns: 1 ' 1 ' 1 unsigned quadword on the stack 1 ' 1 ' creams D0, D1 and D2 1 ' 1 ' BIGSQR: 10372 Data &o027427 ," MOVE.L (A7),-(A7) Move ret. addr. up" 10374 Data &o030057,&h8 ," MOVE.W 8(A7),D0 High order into D0" 10400 Data &o031057,&hA ," MOVE.W A(A7),D1 Low order into D1" 10404 Data &o032000 ," MOVE.W D0,D2 Copy high order to D0" 10406 Data &o140300 ," MULU D0,D0 Mpy. high by high" 10410 Data &o141301 ," MULU D1,D1 Mpy. low by low" 10412 Data &o142357,&hA ," MULU A(A7),D2 Mpy. high by low" 10416 Data &o027500,&h4 ," MOVE.L D0,4(A7) Store high back" 10422 Data &o027501,&h8 ," MOVE.L D1,8(A7) Store low back" 10426 Data &o152202 ," ADD.L D2,D2 Double the middle" 10430 Data &h60CE ," BRA BIGCMN Join up with BIGMUL" 29999 Data ~ Mandelbrot.bas: 1 ' 1 ' Tacky, hacky program to do Mandelbrot pretty color pictures 1 ' 1 ' Some storage declarations 1 ' XOrigin Real part Origin 1 ' YOrigin Imaginary part origin 1 ' Iterations% Maximum number of iterations for each point 1 ' DeltaX Maximum X delta 1 ' DeltaY Maximum Y delta 1 ' X% Current X coordinate 1 ' Y% Current Y coordinate 1 ' XInc% X increment 1 ' YInc% Y increment 1 ' ScreenX% Screen X coordinate 1 ' ScreenY% Screen Y coordinate 1 ' MouseX Screen X coordinate of mouse origin 1 ' MouseY Screen Y coordinate of mouse origin 1 ' MouseDX Screen delta X for mouse box 1 ' Scale%() Current scaling array 1 ' Screen% An array to save the screen into 1 ' FileName$ The filename to write the screen into 1 ' Resolution% Screen resolution (0=320, 1=640) 1 ' BitPlanes% Number of bit planes to use 1 ' Prompt$ Command prompt string 1 ' Command$ Command string 1 ' Upper$ String of upper case characters 1 ' Lower$ String of lower case characters 1 ' LowerCase% Non-zero if lower case characters are ok from the command 1 ' Valid% Non-zero if the screen data is valid 1 ' MouseValid% Non-zero if the mouse data is valid 1 ' Default Default numeric argument 1 ' Result Numeric argument from keyboard 1 ' Direction% Sign of direction for UP/DOWN/LEFT/RIGHT 1 ' Code% Array containing iteration loop code 1 ' Regs% Array containing registers for LibCall 1 ' ContourValid% Non-zero if contouring array has been built 1 ' Boot% True if we're bootstrapping 1 ' 1000 Dim Scale%(2000), Screen%(16010), Code%(150), Regs%(16) 1010 Upper$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 1020 Lower$ = "abcdefghijklmnopqrstuvwxyz" 1030 Def FnMax(a%, b%) = a% - (a% < b%) * (b% - a%) 1035 Boot% = 0 ' Assume not booting 1 ' 1 ' Read the initial mandelbrot set: 1 ' 1040 FileName$ = "MandelSet.640" 1050 GoSub 13000 : If Success% Then GoTo 1100 1060 FileName$ = "MandelSet.320" 1070 GoSub 13000 : If Success% Then GoTo 1100 1075 XOrigin = -2.0 : YOrigin = -1.125 : DeltaX = 3.99 : DeltaY = DeltaX * 180 / 320 1076 Iterations% = 250 : Resolution% = 0 ' Default iterations and resolution 1077 BitPlanes% = 4 ' Default number of bit planes 1080 FileName$ = "MandelSet.320" ' Default bootstrap filename 1090 Boot% = -1 ' We're bootstrapping ! 1 ' 1 ' Read the iteration loop from disk: 1 ' 1100 BLoad "MandelMung", VarPtr(Code%(0)) 1 ' 1 ' Zero all the regs: 1 ' 1110 For I% = 0 to 15 1120 Regs%(I%) = 0 1130 Next I% 1140 ContourValid% = 0 ' Contour array isn't valid yet 1150 MouseValid% = 0 ' No valid mouse data yet 1 ' 1 ' Setup the screen 1 ' 2000 RGB 0, 0, 0, 0 ' Set color register zero to black 2010 RGB 1, 6, 9, 15 ' Set color register 1 to dark blue 2020 Screen Resolution%, BitPlanes%, 0 ' Setup screen resolution 2030 ScnClr ' Make sure the screen is clear 2040 If Boot% Then GoTo 6000 ' Compute set if in boot mode 2050 GShape (0, 0), Screen%() ' Restore the old screen data 2060 Valid% = 1 ' Say our data isn't valid 1 ' 1 ' Look for some commands: 1 ' 3000 GoSub 10000 ' Wait for the user to be ready 3010 GShape (0, 0), Screen% ' Restore the screen 3015 Prompt$ = "Command: " ' Get the command prompt 3020 LowerCase% = 0 ' Make sure everything's upper case 3025 GoSub 14000 ' Get the command string 3030 If Command$ = "EXIT" Then End 3035 If Command$ = "GO" Then GoTo 6000 3040 If Command$ = "SAVE" Then GoTo 3100 3045 If Command$ = "RESET" Then GoTo 1010 3050 If Command$ = "READ" Then GoTo 3200 3055 If Command$ = "CLEAR" Then GoTo 3500 3060 If Command$ = "SET" Then GoTo 3300 3065 If Command$ = "SHOW" Then GoTo 3400 3070 If Command$ = "UP" Then GoTo 3600 3075 If Command$ = "DOWN" Then GoTo 3610 3077 If Command$ = "RIGHT" Then GoTo 3700 3079 If Command$ = "LEFT" Then GoTo 3710 3080 If Command$ = "ZOOM" Then GoTo 3800 3085 If Command$ = "MOUSE" Then GoTo 4000 3090 If Command$ = "SYSTEM" Then System 3097 If Command$ = "HELP" Then GoTo 3900 3098 If Command$ = "" Then GoTo 3000 3099 Print at (0,0) "? Command error";: GoTo 3000 1 ' 1 ' He wants to save something, prompt for the filename: 1 ' 3100 If Valid% Then GoTo 3120 ' Only do this if screen active 3110 Print at (0,0) "? Data not computed";: GoTo 3000 3120 Prompt$ = "File name: " ' Ask him for a filename 3130 LowerCase% = 1 ' Say lowercase here is ok 3140 GoSub 14000 ' Go get the filename 3150 FileName$ = Command$ ' Copy the filename 3160 GoSub 12000 ' Go write the file 3165 Boot% = 0 ' We aren't booting anymore I guess 3170 If Success% Then GoTo 3010 ' Ok, go ask for another command 3180 Print at (0,0) "? File write error"; 3190 GoTo 3000 ' Wait for the mouse again 1 ' 1 ' He wants to read a file. Prompt for it: 1 ' 3200 Prompt$ = "File name: " ' Ask him for a filename 3210 LowerCase% = 1 ' Say lowercase here is ok 3220 GoSub 14000 ' Ask for the filename 3230 FileName$ = Command$ ' Copy the filename 3240 GoSub 13000 ' Go read the file 3250 If Success% Then GoTo 2000 ' Re-init the screen if happy 3260 Print at (0,0) "? File read error - Reset"; 3270 GoSub 10000 ' Wait for a mouse button 3280 GoTo 1010 ' And restart 1 ' 1 ' Prompt the user for the new values: 1 ' 3300 Prompt$ = "X Origin " : Default = XOrigin : GoSub 16000 3305 XOrigin = Result 3310 Prompt$ = "Y Origin " : Default = YOrigin : GoSub 16000 3315 YOrigin = Result 3320 Prompt$ = "Delta X " : Default = DeltaX : GoSub 16000 3325 DeltaX = Result : DeltaY = DeltaX * 180/320 3330 Prompt$ = "Iterations " : Default = Iterations% : GoSub 16000 3335 Iterations% = Result 3340 Prompt$ = "Resolution " : Default = Resolution% : GoSub 16000 3345 Resolution% = Result 3350 Prompt$ = "Bit Planes " : Default = BitPlanes% : GoSub 16000 3355 BitPlanes% = Result 3360 If ((Resolution% + BitPlanes%) < 6) And (Resolution% > -1) And (Resolution% < 2) And (BitPlanes% > 0) Then 3390 3370 Print at (0,0) "? Illegal resolution/bitplane values" : GoTo 3000 3390 GoTo 3880 ' Say the screen and mouse isn't valid 1 ' 1 ' Show the current values: 1 ' 3400 If MouseValid% = 0 Then Print at (0,0) "X Origin = "; XOrigin; " Y Origin = "; YOrigin 3405 If MouseValid% = 1 Then Print at (0,0) "X Origin = "; MouseX ; " Y Origin = "; MouseY 3410 If MouseValid% = 0 Then Print "Delta X = "; DeltaX; " Delta Y = "; DeltaY 3415 If MouseValid% = 1 Then Print "Delta X = "; MouseDX;" Delta Y = "; MouseDY 3420 Print "Iterations = "; Iterations% 3430 Print "Resolution = "; Resolution% 3450 Print " Bit Planes = "; BitPlanes%; 3460 GoTo 3000 1 ' 1 ' Here if we want to clear the data just set. About the only thing that 1 ' can be done is default back to the current screen data 1 ' 3500 GoSub 13500 ' Yank the data back from Screen% 3510 Valid% = 1 ' Say the screen's valid again 3520 GoTo 2000 ' Back to the prompt loop 1 ' 1 ' Here if we want to increment Y some fraction of the screen: 1 ' 3600 Direction% = 1 : GoTo 3620 ' Set sign of increment 1 ' 1 ' Here if we want to decrement Y some fraction of the screen: 1 ' 3610 Direction% = -1 ' Set the sign of the increment 3620 Prompt$ = "Screen fraction " ' Set the prompt 3630 Default = .5 ' Assume half the screen 3640 GoSub 16000 ' Read the screen fraction 3650 YOrigin = YOrigin + (DeltaY * Result * Direction%) 3660 GoTo 3880 ' Screen data not valid 1 ' 1 ' Here if we want to increment X some fraction of the screen: 1 ' 3700 Direction% = 1 : GoTo 3720 ' Set sign of the increment 1 ' 1 ' Here if we want to decrement X some fraction of the screen: 1 ' 3710 Direction% = -1 ' Set sign of the increment 3720 Prompt$ = "Screen fraction " ' Set the prompt 3730 Default = .5 ' Assume half the screen 3740 GoSub 16000 ' Read the screen fraction 3750 XOrigin = XOrigin + (DeltaX * Result * Direction%) 3760 GoTo 3880 ' Screen is no longer valid 1 ' 1 ' If here we want to zoom in or out. > 1 is zoom in, < 1 is zoom out 1 ' 3800 Prompt$ = "Zoom Factor " ' Set the prompt 3810 Default = 2 ' Assume twice resolution 3820 GoSub 16000 ' Get the zoom factor 3830 If Result > 0 Then GoTo 3860 ' Check range 3840 Print "? Must be greater than 0" ' Complain 3850 GoTo 3000 ' Let him think about it 3860 DeltaX = DeltaX / Result ' Scale the DeltaX 3870 DeltaY = DeltaY / Result ' Scale the DeltaY 3880 Valid% = 0 ' Screen's no longer valid 3885 MouseValid% = 0 ' No more mouse data 3890 GoTo 3010 ' Prompt again 1 ' 1 ' Give some hint as to what we're about: 1 ' 3900 Print at (0,0) "One of the following commands:" 3905 Print "Clear Clear data just set" 3910 Print "Down Reset Y origin downwards" 3915 Print "Exit Exit Mandelbrot.Bas" 3920 Print "Go Compute the new set" 3925 Print "Help Show this text" 3930 Print "Left Reset X origin left" 3933 Print "Mouse Use mouse to set origin" 3935 Print "Read Read a saved screen" 3940 Print "Reset Reset program" 3945 Print "Right Reset X origin right" 3950 Print "Save Save the screen" 3955 Print "Set Set new data" 3960 Print "Show Show current settings" 3965 Print "System Exit to CLI or workbench" 3970 Print "Up Reset Y origin upwards" 3975 Print "Zoom Zoom current settings" 3980 Print "Press left mouse button to continue" 3999 GoTo 3000 1 ' 1 ' Here to set the new coordinates using the mouse: 1 ' 4000 GoSub 20000 ' Go get the new coordinates 4010 Print at (0,0) "X = "; MouseX 4020 Print "Y = "; MouseY 4030 Print "DeltaX = "; MouseDX 4040 Print "DeltaY = "; MouseDY 4050 GoTo 3000 1 ' 1 ' Main loop here: 1' 6000 If ContourValid%= 0 Then GoSub 11000 ' Compute the contour map 6001 If MouseValid% = 0 Then GoTo 6010 ' Skip this if mouse not valid 6002 XOrigin = MouseX : YOrigin = MouseY 6003 DeltaX = MouseDX : DeltaY = MouseDY 6010 Screen Resolution%, BitPlanes%, 0 ' Setup the screen resolution 6020 ScnClr ' And start with a clean screen 6023 XInc% = (2 ^ 30 * DeltaX / 320 / (Resolution% + 1)) 6024 YInc% = (2 ^ 30 * DeltaY / 180) ' Compute increments 6030 Y% = YOrigin * (2 ^ 30) ' Set initial Y 6040 For ScreenY% = 179 to 0 Step -1 ' Outside loop is Y% 6050 X% = XOrigin * (2 ^ 30) ' Set initial X again 6060 For ScreenX% = 0 to (320 * (Resolution% + 1) - 1) Step 1 1 ' 1 ' Initialize a single point's values: 1 ' 6070 Regs%(0) = X% : Regs%(1) = Y% 6080 Regs%(2) = Iterations% 1 ' 1 ' Per point loop: 1 ' 6100 LibCall VarPtr(Code%(0)), 0, Regs%() 6110 I% = Peek_W(VarPtr(Regs%(2)) + 2) ' Get the iteration count 6120 If I% > (Iterations% - 1) Then GoTo 6300 1 ' 1 ' Here if we bummed out. Plot the point 1 ' 6200 Draw (ScreenX%, ScreenY%), Scale%(I% + 1) 1 ' 1 ' Bump to the next point 1' 6300 X% = X% + XInc% 6310 Next ScreenX% 6320 Y% = Y% + YInc% 6330 Next ScreenY% 1 ' 1 ' All done! 1 ' 6340 Valid% = 1 ' Say we're valid again 6350 MouseValid% = 0 ' Mouse data isn't valid 6360 SShape (0, 0; (320 * (Resolution% + 1)) - 1, 199), Screen% 6370 If Boot% Then Goto 3160 Else GoTo 3000 1 ' 1 ' Write the data file out and quit 1 ' 7000 GoSub 10000 ' Wait for the mouse button 7010 GoSub 12000 ' Write the file out 7020 End ' End of proggie 1 ' 1 ' Subroutine to wait for the mouse button to be pressed 1 ' 10000 Ask Mouse X%, Y%, Button% 10010 If Button% = 0 Then GoTo 10000 10020 Return 1 ' 1 ' Subroutine to fill in the scaling array given the number of bit planes 1 ' desired. 1 ' 11000 Interval% = 1 11010 Print at (0,0) "Computing contouring array"; 11020 I% = 1 11030 Color% = 1 11040 For J% = 1 to Interval% 11050 If I% <= 2000 Then Scale%(I%) = Color% 11060 I% = I% + 1 11070 Next J% 11080 If I% > 1000 Then GoTo 11130 11090 Color% = Color% + 1 11100 If Color% < (2 ^ BitPlanes%) Then 11040 11110 Interval% = Interval% + 2 11120 GoTo 11030 11130 ContourValid% = 1 ' So we don't have to do this again 11140 Retu1 ' The disk file is created/read with BSAVE/BLOAD into array Screen%. 1 ' The first elements of this array are obtained from/fed to SShape/GShape. 1 ' We load values into this array following the screen data itself. On 1 ' write, the location in the array is determined by the resolution we're 1 ' writing. When reading the file, the offset can be obtained from the 1 ' resolution implied by the width paramter in the front of the array. 1 ' 1 ' The values appended to the screen array are: 1 ' 1 ' Byte # Contents 1 ' ==== = ======== 1 ' 0 File format version number (1) 1 ' 1 Screen resolution mode (0 or 1) 1 ' 2-3 Iteration count 1 ' 4-7 X Origin 1 ' 8-11 Y Origin 1 ' 12-15 Delta X 1 ' 16-19 Delta Y 1 ' 12000 SShape (0, 0; (320 * (Resolution% + 1) - 1), 199), Screen%() 12010 I% = (2000 * BitPlanes% * (Resolution% + 1)) + 2 12020 Poke VarPtr(Screen%(I%)), 2 12030 Poke VarPtr(Screen%(I%))+1, Resolution% 12040 Poke_W VarPtr(Screen%(I%))+2, Iterations% 12050 Screen%(I%+1) = Peek_L(VarPtr(XOrigin)) 12060 Screen%(I%+2) = Peek_L(VarPtr(YOrigin)) 12070 Screen%(I%+3) = Peek_L(VarPtr(DeltaX)) 12080 Screen%(I%+4) = Peek_L(VarPtr(DeltaY)) 12090 Success% = 0 : On Error GoTo 12130 ' In case of errors 12100 BSave FileName$, Varptr(Screen%(0)), (I% + 5) * 4 12110 Success% = 1 : On Error GoTo 0 12120 Return 12130 Resume 12120 1 ' 1 ' Converse of the previous routine, this routine restores a screen to 1 ' memory. See the previous subroutine for notes on the file format. 1 ' 13000 Success% = 0 : On Error GoTo 13660 ' In case the file lookup fails 13010 BLoad FileName$, VarPtr(Screen%(0)) 13020 On Error GoTo 0 13500 BitPlanes% = Peek_W(VarPtr(Screen%(0))) 13510 Width% = Peek_W(VarPtr(Screen%(0))+2) 13520 If Width% < 320 Then Resolution% = 0 Else Resolution% = 1 13530 I% = (2000 * BitPlanes% * (Resolution% + 1)) + 2 13540 FileVersion% = Peek(VarPtr(Screen%(I%))) 13550 If (FileVersion% > 0) And (FileVersion% < 3) Then GoTo 13580 13560 Print "? File version number error" 13570 Return 13580 If FileVersion% < 2 Then Iterations% = Peek(VarPtr(Screen%(I%))+2) 13590 If FileVersion% >= 2 Then Iterations% = Peek_W(VarPtr(Screen%(I%))+2) 13600 Poke_L VarPtr(XOrigin), Peek_L(VarPtr(Screen%(I%+1))) 13610 Poke_L VarPtr(YOrigin), Peek_L(VarPtr(Screen%(I%+2))) 13620 Poke_L VarPtr(DeltaX), Peek_L(VarPtr(Screen%(I%+3))) 13630 Poke_L VarPtr(DeltaY), Peek_L(VarPtr(Screen%(I%+4))) 13640 Success% = 1 13650 Return 13660 Resume 13650 1 ' 1 ' Subroutine to prompt for and read a command. Called with the command 1 ' prompt in Prompt$, and returns the command keyword (uppercased) in 1 ' Command$. We'll restore the screen after we're done. 1 ' 14000 Print at (0,0) Prompt$; 14010 Input "", Command$ 14020 GShape (0, 0), Screen% ' Restore the screen 14030 If LowerCase% Then Return ' Return now if lower case is ok 1 ' 1 ' Subroutine to uppercase the alpha characters in the command string. 1 ' 15000 For I% = 1 to Len(Command$) 15010 J% = InStr(1, Lower$, Mid$(Command$, I%, 1)) 15030 If J% Then Replace$(Command$, I%, 1) = Mid$(Upper$, J%, 1) 15040 Next I% 15050 Return 1 ' 1 ' Prompt for a numeric argument, preserving the old value if appropriate 1 ' 16000 Prompt$ = Prompt$ + "(" + Str$(Default) + "): " 16010 GoSub 14000 ' Go prompt and read the number 16020 If Command$ = "" Then Result = Default Else Result = Val(Command$) 16030 Return 1 ' 1 ' Routine to take new coordinates from mouse input. 1 ' User positions mouse to lower left corner of intended 1 ' area to zoom, clicks mouse button once. Position mouse 1 ' to upper left corner, click mouse button again. This 1 ' goes to great lengths to preserve the proper aspect 1 ' ratio. 1 ' 20000 PenA 1 ' Set the default writing color 20005 XoverY = 320 * (Resolution% + 1) / 180 : YoverX = 1 / XoverY 1 ' 1 ' Wait for the button to be pressed while on screen 1 ' 20010 Ask Mouse X%, Y%, Button% ' Go read the mouse 20020 If X% < 0 or Y% < 0 Then GoTo 20010 ' Ignore if not in window 20030 If X% > ((Resolution% + 1) * 320) - 1 Then GoTo 20010 20040 If Y% > 179 Then GoTo 20010 ' Ignore if too big also 20050 If Button% = 0 Then GoTo 20010 ' Wait for mouse buttom 20060 MouseX% = X% : MouseY% = Y% : NewDX% = 0 : OldDX% = 0 1 ' 1 ' Wait for the button to be released 1 ' 20070 While Button% <> 0: Ask Mouse X%, Y%, Button%: Wend 1 ' 1 ' Loop here waiting for the second pressing of the button 1 ' 20080 While Button% = 0 20090 If X% < MouseX% Then GoTo 20210 ' Don't make negative boxes 20100 If Y% > MouseY% Then GoTo 20210 20110 If X% > ((Resolution% + 1) * 320) -1 Then GoTo 20210 20120 If Y% > 179 Then GoTo 20210 ' Don't bother if too big 20130 OldDX% = NewDX% ' Copy the old delta X 20140 NewDX% = FnMax ((X% - MouseX%), int(XoverY * (MouseY% - Y%))) 20150 If NewDX% = OldDX% Then GoTo 20210 ' Don't bother if no change 20160 GShape (0, 0), Screen% ' Something moved, restore the old screen 20170 X% = MouseX% + NewDX% ' Find the corner 20180 Y% = MouseY% - int(YoverX * NewDX%) ' of the box 20190 Draw (MouseX%, MouseY% to X%, MouseY% to X%, Y%), 1 20200 Draw (X%, Y% to MouseX%, Y% to MouseX%, MouseY%), 1 20210 Ask Mouse X%, Y%, Button% ' Get the new mouse position 20220 Wend 20230 MouseValid% = 1 ' Say we have valid mouse data 20240 Gshape (0, 0), Screen% ' Restore the screen 20250 MouseX = XOrigin + DeltaX * MouseX% / (320 * (Resolution% + 1)) 20260 MouseY = YOrigin + (DeltaX * 180 / 320) * (179 - MouseY%) / 180 20270 MouseDX = DeltaX * NewDX% / (320 * (Resolution% + 1)) 20280 MouseDY = MouseDX * 180 / 320 20290 Return --------