Date: 29 Jun 1993 09:09:00 -0400 (EDT) From: "John B. McVey (203) 727-7084" Subject: FB Tech Notes Posting To: Info-mac.Stanford From: McVey.John on Fri, Jun 25, 1993 6:51 PM Subject: FB Tech Note Posting To: Info-mac.Stanford The attched areTechnical notes of interest to Future Basic prog rammers. Suggeststed file name is FBasicTN.Vol1. Suggested directory is /tech/. The notes are Vol1 of a 3 volumes published to date and were furnished by the Future Basic publisher technical help department with the understanding that the notes would be made available on the Net. This file contains technical notes that provides solutions to some problems encountered when programming in FutureBASIC. FutureBASIC Technical Notes Help (Part 1) ____________________________________________ Introduction ____________________________________________ This help file contains technical notes that provides solutions to some problems encountered when programming in FutureBASIC. As new notes are added the updates will be made available via online services like America Online and AppleLink. These notes should be considered Freeware and can be diseminated by FutureBASIC owners as widely as possible to ensure that all FutureBASIC owners are informed of their contents. Non-FutureBASIC owners are restricted from such distribution. For more information you can contact: Zedcor Technical Support: 602.795.3996 Mon-Fri, 9AM-5PM MST ================================================== Technical Note #1 ____________________________________________ POS(0) vs. PRINT% WRITTEN: 4 NOV 92 (LFT) LAST UPDATE: 4 NOV 92 ____________________________________________ The POS(0) function fails to return the correct column position when used with PRINT%. Instead, it adds each subsequent column position to the previous one resulting in incorrect column values. The POS function returns a character position within the current output line. It is thus a holdover from character based displays of old (remember 24x80 displays?). Since the Macintosh uses a graphical display based on pixels, it is more natural to use a pixel based placement instead of columns. PRINT% was created to position text to within a resolution of 1 pixel and doesn't clear the buffer used by POS. Use the WINDOW(_penH) and WINDOW(_penV) functions to get the current pixel position of the text pen instead of POS. POS(0) does work correctly with the PRINT@ position which is character oriented. (Thanks to Rich Love for finding this.) ================================================== Technical Note #2 ____________________________________________ CALL values WRITTEN: 4 NOV 92 (LFT) LAST UPDATE: 4 NOV 92 ____________________________________________ When using the CALL statement to access external code resources, it is important to remember that CALL expects variables with type declarations to push them correctly on the stack. For example, the following pushes two integer variables, a long integer, and a string address onto the stack: CALL _"fooh",999, ( var1%, var2%, var3&, var4$) If, however, you pass an expression as a literal value, the CALL statement assumes that the expression represents a LONG INTEGER and places a long word on the stack, no matter what the actual expression value or size. For example: CALL _"fooh", 999, ( 5, var2%, var3&, var4$) pushes a long integer, an integer, a long integer, and a string address on the stack. You can override this by placing an integer type declaration (%) BEFORE the actual value. This tells CALL the actual size of the expression to place onto the stack. To correct the above problem, you would thus use: CALL _"fooh", 999, ( %5, var2%, var3&, var4$) ================================================== Technical Note #3 ____________________________________________ FILES$ & Boomerang WRITTEN: 10 NOV 92 (LFT) LAST UPDATE: 10 NOV 92 ____________________________________________ A program that doesn't use events may encounter problems using the FILES$ function on a Macintosh where Super Boomerang is running. The problem surfaces when an event is pending in the event queue and a call to FILES$ is made without any fileType specified. When the get dialog appears, Super Boomerang takes charge and selects the last file chosen in that directory, clicks the Open button, and returns the file name and volRefNum% for that file. The file may or may not be the one you wanted to open. The solution is to read the event with DIALOG ON and DIALOG OFF prior to using FILE$ to get a filename. This prevents Super Boomerang from taking over and opening any file it wants. For example: DIALOG ON DIALOG OFF fileName$ = FILES$ (_fOpen, "", , v olRefNum%) LONG IF filename$ <> "" ' and so onx END IF will solve the problem even if you are not using events in other portions of your program. Programs that handle events do not experience this problem. (Thanks to Tony Andreoli for finding and debugging this) ================================================== Technical Note #4 ____________________________________________ INCLUDE File Notes WRITTEN: 10 NOV 92 (LFT) LAST UPDATE: 10 NOV 92 ____________________________________________ When using INCLUDE files, it is important to remember the difference between _aplIncl (application only ), _resIncl (code resources ony), and _allIncl (both application & code resources) files. When you use: INCLUDE FILE _aplIncl in an INCLUDE file, this tells the compiler that all FutureBASIC and Toolbox calls and functions should be supported in the INCLUDE. However, when you use: INCLUDE FILE _resIncl the compiler uses the Mini-Runtime package which allows only a subset of FutureBASIC commands (those marked with an (M) in the Reference manual) and all Toolbox calls and functions. Also, when using: INCLUDE FILE _allIncl note that this version compiles both types of resources into the INCLUDE file. This means that the pass compiling the _aplIncl may be successful while the pass creating the _resIncl could fail with an error. Failure to take these differences into account can cause an error ("application only statement or function used in non-application program"). The solution is to replace the _resIncl or _allIncl with _aplIncl for application INLCUDES or strip out the offending application-only statements for a code resource INCLUDE. Note that FutureBASIC's BCD floating point routines may not be used when compiling applications that use the mini-runtime. ================================================== Technical Note #5 ____________________________________________ Using QB MBPC's in FB WRITTEN: 11 NOV 92 (LFT) LAST UPDATE: 11 NOV 92 ____________________________________________ It is possible to use MBPC code resources writ ten in other languages for QuickBASICx to be used in FutureBASIC. The key to using MBPCs or MBLC is to rebuild the code resource in the original environment without adding the BASICLib.asm file. This removes the header and callback routines to QB that are not supported in FutureBASIC. (Thanks to Rich Love for pointing this out) ================================================== Technical Note #6 ____________________________________________ MOUSE Speed Mischief WRITTEN: 19 NOV 92 (LFT) LAST UPDATE: 19 NOV 92 ____________________________________________ It seems that many people are looking for and never see the MOUSE(0) function return a 1 (_click1) value indicating a single mouse click. In most cases the value returned is -1 (_click1nDrag). The discrepency occurs between the time the mouse is initially clicked and the time its reported by the MOUSE(0) function. MOUSE(0) will always return the state of the button at the time of inquiry and if the button is still down the message will be _click1nDrag, not _click1. This is most noticeable on new, faster machines with higher clock rates. It is almost impossible in some cases to ever see a _click1 since the application vectors so fast that the MOUSE(0) statement see's a finger holding the mouse button down and reports _click1nDrag. The solution is to use the ABS function to return a value of _click1 no matter whether MOUSE(0) returns _click1 or _click1nDrag. ================================================== Technical Note #7 ____________________________________________ MacinTalk Rides Again WRITTEN: 1 DEC 92 (LFT) LAST UPDATE: 27 APR 93 __________________________ __________________ (String removed in FB v1.02) While MacinTalkx has never really been supported by Apple, using it in ZBASIC was made easy through the use of the OPEN TALK and TALK statements. While its not mentioned in the manuals (see the FutureBASIC Help file) these statements are still available and usable in FutureBASIC even under System 7. To make MacinTalk accessible its necessary to modify a STR# resource in the Futurex Extras file. Use the following steps to make the change: 1. Run ResEdit (FutureBASIC should not be running) 2. Open the Futurex Extras file 3. Open the STR# resource picker window 4. Open STR# 194 5. Locate: item # 14 "No MacinTalk Reader" 6. Replace: item #14 with "" (clear text in item only, don't delete) 7. Choose Save from the File menu 8. Close all windows and quit ResEdit. Why the change works: The MacinTalk driver has two speech options, one allows you to use English phrases for a speech string, the other enables you to use phonemes. The text in the STR# makes MacinTalk look for a phoneme file which isn't found. This results in a "File Not Found" error. Using MacinTalk: You can include the following function in your programs to make using MacinTalk even easier. Pass it a string and any changes to the speech settings and it does the work of opening the driver, speaking the string, and closing the driver. Modify to suit your own tastes. Ideally you can probably set the parameters to default values and just pass a string for it to speak. ' speech$...text to say ' speed%... rate of speech (85 - 425 words/minute, default = 150 wpm) ' pitch%....voice pitch (65 - 500 Hz, 110 default) ' talk%.....voice style: 0=natural, 1 = robotic ' sex%......male or female voice (never implemented by Apple) LOCAL FN DoTalk (speech$,speed%,pitch%,talk%,sex%) OPEN TALK, #-4, "", 512 TALK #-4, speed%, pitch%, talk%, sex% PRINT #-4, speech$ CLOSE #-4 END FN txt$ = "FutureBASIC can talk!) FN DoTalk (txt$, 150, 110, 0, 0) Warning: It is recommended that you use the above routine to open and then close MacinTalk after each use since the Sound Manager on newer machines is disconnected when MacinTalk is operating. This can prevent system beeps and other sound resources from being played. Also, there is no guarantee that MacinTalk will work on all machines. (Thanks to a host of people who wanted to see MacinTalk again.) ================================================== Technical Note #8 ____________________________________________ Array Disarray Bug WRITTEN: 2 DEC 92 (LFT) LAST UPDATE: 21 APR 93 ____________________________________________ (Bug corrected in FB v1.02) The first example demonstrates a bug in release 1.01 of FutureBASIC. Two ingredients are essential to manifest this bug: 1) The first array declared within a given scope (either local or global) must be exactly 8 bytes long. 2) References to any following array must involve a variable subscript. When both these conditions exist, references to the second array will INCORRECTLY refer to the elements of the first array. DIM myrect%(3) 'first array has size 8 bytes DIM duh%(100) Zero% = 0 duh% (Zero%) = 9 'arrays subscripted with a var PRINT duh% (Zero%) myrect% (Zero%) = 3 'refs to 1st array are same PRINT duh% (Zero%) 'as refs to 2nd...OUCH! INPUT Junk$ This problem can be bypassed by reversing the order of the DIM statements like this: DIM duh%(100) DIM myrect%(3) '2nd array has size 8 bytes Zero% = 0 duh% (Zero%) = 9 'arrays subscripted with a var PRINT duh% (Zero%) myrect% (Zero%) = 3 'refs to correct array PRINT duh% (Zero%) INPUT Junk$ Fixing this Bug: You can use ResEdit to correct this bug by modifying a word in a single CODE resource of FutureBASIC. The steps include: 1. Run ResEdit. 2. Open FutureBASIC. 3. Open the CODE resource picker (shows all CODE resources) 4. Open CODE #9. 5. Find offset &H3998 6. Replace &H660C3620 with &H660C603C 7. Save your changes. 8. Close FutureBASIC and quit ResEdit. (Thanks to Steve Ross for finding this nasty.) ================================================== Technical Note #9 ____________________________________________ READ/WRITE Records Bug WRITTEN: 7 DEC 92 (LFT) LAST UPDATE: 21 APR 93 ____________________________________________ (Bug corrected in FB v1.02) There is a problem in the reading and writing of record type variables using READ and WRITE. This bug causes the writing of record zero to actually write an incorrect length of data (many times larger) than the actual record size. The example below demonstrates the problem: DIM RECORD test 'define a record DIM rect.8 DIM 25 tmp$ DIM END RECORD .test DIM testRec.test 'define record variable CALL SETRECT (testRec.top%, 10, 10, 20, 20) testRec.tmp$ = "Record #1" OPEN "R", 1, "Test RW", _test, SYSTEM (_aplVol) RECORD #1, 0 'set record number WRITE #1, testRec 'write data to disk CALL SETRECT (testRec.top%, 50, 50, 70, 70) testRec.tmp$ = "Record #2" RECORD #1, 1 'set record number WRITE #1, testRec 'write data to disk CLOSE #1 OPEN "R", 1, "Test RW", _test, SYSTEM (_aplVol) RECORD #1, 0 'set record number READ #1, testRec 'read data from disk PRINT "--- RECORD #1 ---" 'show record data PRINT testRec.top%,testRec.left% PRINT testRec.bottom%,testRec.right% PRINT tmp$ RECORD #1, 1 'set record number READ #1, testRec 'read data from disk CLOSE #1 PRINT "--- RECORD #1 ---" 'show record data PRINT testRec.top%,testRec.left% PRINT testRec.bottom%,testRec.right% PRINT tmp$ STOP Fixing this Bug: You can use ResEdit to correct this bug by modifying a word in a single CODE resource of FutureBASIC. The steps include: 1. Run ResEdit. 2. Open FutureBASIC. 3. Open the CODE resource picker (shows all CODE resources) 4. Open CODE #9. 5. Find offset &H28B6 6. Replace &HEE8C3650 with &HEE8C3642 7. Save your changes. 8. Close FutureBASIC and quit ResEdit. (Thanks to Miro Valach for finding this nasty one.) to be cont... ================================================== Technical Note #10 ____________________________________________ Insensitive Include Files WRITTEN: 8 DEC 92 (LFT) LAST UPDATE: 8 DEC 92 ____________________________________________ When using the COMPILE statement in a program with the _caseInsensitive parameter, you must ensure that any INCLUDE files called by the main program have an identical statement. Ignoring this can cause all kinds of problems. When _caseInsensitive is used, FutureBASIC compares all variables AND function names in uppercase letters, if the INCLUDE file does not have this parameter set, the compiler can't match FN THIS (main program) with FN This (INCLUDE file). Most simply, if you use a COMPILE settings in the main program, use the same one in all INCLUDE files used by the program. (Thanks to Roger Smith for finding this.) ================================================== Technical Note #11 ____________________________________________ Help File Blues WRITTEN: 31 DEC 92 (LFT) LAST UPDATE: 31 DEC 92 ____________________________________________ When a user selects Help from the Apple menu its possible they will get an alert that displays a "Help file still under construction" message. This is somewhat misleading since the default help file is probably available but the Help tool isn't. The problem is that the Help tool has inadvertantly been "turned off" or not enabled to autostart when FutureBASIC is opened. To re-enable the Help tool, use the Tool Manager under the Tools menu to view the current set of tools. Select the Help tool, then enable the Autostart button. This places a black diamond in front of the Help tool showing you it autostart is set for that tool. When you close the Tool Manager, all tools set to autostart are re-initialized into memory. ================================================== Technical Note #12 ____________________________________________ MUNGER Toolbox Correction WRITTEN: 6 JAN 93 (LFT) LAST UPDATE: 21 APR 93 ____________________________________________ (Definition corrected in FB v1.02) A definition error was discovered in the Toolbox with FN MUNGER. As it stood, the function failed to replace text in the handle even when it found it. Fixing this Bug: You can use ResEdit to correct this problem by modifying a word in a resource in the Future Extras file. The steps include: 1. Run ResEdit. 2. Open Future Extras. 3. Open the TlBx resource picker (shows all TlBx resources) 4. Open TlBx #129. 5. Find offset &H496 6. Replace &H05010501 with &H01010101 7. Save your changes. 8. Close FutureBASIC and quit ResEdit. (Thanks to Gary Thompson for finding this one.) ================================================== Technical Note #13 ____________________________________________ OSEvt Equates Correction WRITTEN: 7 JAN 93 (LFT) LAST UPDATE: 21 APR 93 ____________________________________________ (Constants corrected in FB v1.02) Two value errors were discovered in the OSEvt equates, Currently the settings are: resumeFlag = 0 convertClipboardFlag = 1 while they should be (ref. IM v6, p.5-33): resumeFlag = 1 convertClipboardFlag = 2 Fixing this Bug: You can use ResEdit to correct this problem by modifying a word in a resource in the Future Extras file. The steps include: 1. Run ResEdit. 2. Open Future Extras. 3. Open the _Con resource picker (shows all _Con resources) 4. Open _Con #128. 5. Find offset &HEF8 6. Replace &H0000 with &H0001 7. Find offset &HEFC 8. Replace &H0001 with &H0002 9. Save your changes. 10. Close FutureBASIC and quit ResEdit. (Thanks to Rich Love for pointing to this one.) ================================================== Technical Note #14 ____________________________________________ The Lost Text Dance WRITTEN: 13 JAN 93 (LFT) LAST UPDATE: 13 JAN 93 ____________________________________________ The introduction of LOCAL FN was heralded as a boon to programmers being overrun with out-of-control global variables. This jubilation was justified, but, incorrect usage of LOCAL FNs can lead to a shortage of stack space in the program. This in turn can cause text in dialogs, menus, windows to not display, as well as system errors. Not something we are looking for in a well-behaved program. A LOCAL FN takes the parameters passed to it and places them on the stack. Any variables dimensioned inside the LOCAL FN are also created on the stack. The stack space allocated to a particular machine varies but in many instances will be around 8K-32K. If the size of the remaining stack is less than the LOCAL FN requires, a stack overflow can result, munging other code in memory The following example demonstrates the problem: LOCAL FN TestStack DIM 255 test$ (49) ' do something with these strings END FN If we imagine we had a normal 8K stack, this simple DIM statement overflows the stack immediately (256 bytes * 50 strings = 12800 bytes, or about 4K more than the stack can handle. The first symptoms of stack shortages will be evident when text that once appeared just fine now fails to appear in file dialogs, windows, and in extreme cases, menus. Let's state one truism now: YOU CAN NOT USE LOCAL FNs LIKE YOU WOULD GOTO! In other words, you cannot call FN after FN regardless of where they appear in the program just as you once did with GOTO. GOTO allows a one way jump to any point in a program. Unlike GOTO, each call to a LOCAL FN places a return address on the stack causing it to grow. With enough FN calls that DON'T RETURN to the originating point, its possible to execeed the original 8k of stack space very rapidly. Instead, a LOCAL FN must be considered like a GOSUB statement. A GOSUB jumps to any point in a program, BUT, it places a return address on the stack that allows program control to jump back to its previous position when a RETURN is encountered. The best example of performing calls to LOCAL FNs correctly are shown in the example programs included on the Examples disk. You can determine the amount of stack space available and reset it using the following routine: "IncreaseStackSpace" LONG IF [_applLimit] - [_heapEnd] < maxHeapSize& & [_applLimit], [_heapEnd] + maxHeapSize& END IF This routine should only be called ONCE in the program before executing any other initialization routines. (Thanks to host of people for finding this one.) ================================================== Technical Note #15 ____________________________________________ GETPIXELSSTATE Toolbox Correction WRITTEN: 19 JAN 93 (LFT) LAST UPDATE: 21 APR 93 ____________________________________________ (Toolbox corrected in FB v1.02) A definition error was discovered in the Toolbox with FN GETPIXELSSTATE. As defined, the function returned a word value when a longInt is expected. Fixing this Bug: You can use ResEdit to correct this problem by modifying a word in a resource in the Future Extras file. The steps include: 1. Run ResEdit. 2. Open Future Extras. 3. Open the TlBx resource picker (shows all TlBx resources) 4. Open TlBx #129. 5. Find offset &H7C6 6. Replace &H02F00004 with &H03F00004 7. Save your changes. 8. Close FutureBASIC and quit ResEdit. ================================================== Technical Note #16 ____________________________________________ Assembler Non-Features WRITTEN: 10 FEB 93 (JAR) LAST UPDATE: 5 APR 93 ____________________________________________ FutureBASIC's built-in assembler currently has a few limitations and, well, how shall we put it, "unexpected features"? If you use the built-in assembler, you will probably want to be aware of the following problems: 1) Spaces must NOT be used between instruction operands. For example, the following statement will assemble incorrectly: ` move.l ^MyVar&, d0 but ` move.l ^MyVar&,d0 will assemble correctly. 2) The assembler will not currently accept the condition code register as an operand. Thus, the instruction ` move CCR,-(sp) will cause the compiler to complain. Of course, you can always work around a problem like this by using the assembler's dc.w and dc.l directives. For instance, the above instruction could be assembled as follows: _PushCCR = &42E7 ` dc.w PushCCR or: _PushSR = &40E7 ` dc.w PushSR 3) The assembler reverses the operands of an EOR instruction (although it makes no similar mistake with the Winnie the Pooh instruction). For example, if you really want to exclusive-or the d2 data register with the d0 data register, be sure to write ` eor.l d0,d2 rather than ` eor.l d2,d0 as you would expect! ================================================== Technical Note #17 ____________________________________________ HandAndHand Flops WRITTEN: 23 FEB 93 (LFT) LAST UPDATE: 29 APR 93 ____________________________________________ (Added in FB v1.02) A definition error was discovered in the Toolbox with FN HANDANDHAND. As it stood, the function was missing a parameter and returned the incorrect result. Fixing this Bug: You can use ResEdit to correct this problem by modifying a word in a resource in the Future Extras file. The steps include: 1. Run ResEdit. 2. Open Future Extras. 3. Open the TlBx resource picker (shows all TlBx resources) 4. Open TlBx #129, "FN". 5. Select Find Hex from the Find menu. 6. Find Hex: 0506A9E40BFF and Change to: 0604A9E40B0CFF 7. Save your changes. 8. Close FutureBASIC and quit ResEdit. (Thanks to Chris Dwyer for finding this one.) ================================================== Technical Note #18 ____________________________________________ SETPTRSIZE Addition WRITTEN: 8 MAR 93 (LFT) LAST UPDATE: 21 APR 93 ____________________________________________ (Added in FB v1.02) The Toolbox procedure SETPTRSIZE mentioned in the Handbook (p.345) was discovered to not be available in FutureBASIC. Adding the Trap: You can use ResEdit to add this procedure using the following steps: 1. Run ResEdit. 2. Open a copy of FUTURE Extras. 3. Open the TlBx resource picker (shows all TlBx resources) 4. Open TlBx #129. 5. Select Find Offset from the Find menu. 6. Find Offset: 1DA 7. Enter at the selection point (the 06A8): 0604A0200B0AFF 8. Close the TlBx resources. 9. Open the STR# resource picker (shows all STR# resources) 10.Open STR# #129. 11.Locate item #65 (SETHANDLESIZE). 12.Click on the ***** above the 65. 12.Choose Create New Resource from the Resource menu. 13.Enter: SETPTRSIZE into the blank item (it should be between SETVOL and SETHANDLESIZE) 14. Close the STR# resource. 15.Save all of your changes. 16.Close FutureBASIC and quit ResEdit. 17.Switch the copy of FUTURE Extras with the original and test. ================================================== Technical Note #19 ____________________________________________ LOCAL FN Explained WRITTEN: 6 MAY 93 (LFT) LAST UPDATE: 6 MAY 93 ____________________________________________ Included here is additional information on the habits of LOCAL FNs hitherto unknown and undocumented. A LOCAL FN is a self-contained program within the confines of a regular program. This allows the programmer to isolate routines from each other and avoid variable corruption. When a LOCAL FN receives a parameter it makes a copy of the variable being passed thus ensuring that the original is never changed. When the routine exits via END FN or EXIT FN the local variable is removed from memory and no longer exists. Multi-variable updating There are three methods for updating multiple variables passed to a LOCAL FN. They include: globals, addresses, and records. The first is the easiest but not the cleanest method of updating several variables wihtin a LOCAL FN. Define all the variables that need updating as global. You can then access them directly and change their values easily. The second method requires that the address of the variable be passed to the LOCAL FN. The LOCAL FN makes changes by PEEKing and POKEing at the address. If you understand PEEK and POKE than this method works great. The final method requires that you define a temporary record structure to pass the variables into the LOCAL FN (a paramblock) and then pass the record address to the function. You than access and update the record variables directly using normal record input and output methods. (ie. myRecPtr&.varOne% = 10). This method works well for those who don't think in PEEK and POKE terms. Mysterious values In certain circumstances you may find that a variable you thought you had defined locally in a LOCAL FN has mysteriously appeared in your main program. This problem is easy to reproduce by defining a simple LOCAL FN that accepts a variable BUT DOESN'T return one. When used with the PRINT statement the value of the local variable 'q' is printed. Here is the example: LOCAL FN Fred (tmp%) q = 45 END FN PRINT FN Fred (21) Results in an output of: 45 Which is incorrect. Or is it? The reason this situation occurs is simple. FB always uses Register D0 to return variables from any FN. The last variable assigned in the FN will always be found in D0. The END FN statement doesn't clear D0 when it executes thus leaving D0 holding the last variable bag. The PRINT statement simply prints what it finds in Register D0, the mysterious local variable left there by FN Fred. If the END FN had contained a variable assignment, no anomoly would have occurred. LOCAL FN Fred (tmp%) q = 45 END FN = tmp% PRINT FN Fred (21) LOCAL FN Fred (tmp%) Results in an output of: 21 exactly as expected. ================================================== Technical Note #20 ____________________________________________ ON EVENT GOTO Error WRITTEN: 6 MAY 93 (LFT) LAST UPDATE: 6 MAY 93 ____________________________________________ The statement ON EVENT GOTO documented in the Reference manaul p. 261 is not a legal statement. You must use ON FN or ON GOSUB instead.