home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-10-21 | 83.0 KB | 1,900 lines |
- (C)1990 Marquis Computing Inc., All rights reserved.
- BASIC Softips(tm)
- Entire contents copyrighted, 1990
- Issue number 1
- 10/20/1990
- 5
- TOC
- TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TO
-
- Welcome to BASIC Softips for November 1990. Thank you downloading it and good
- reading! Use the up/down arrows, pageup/pagedown, control pageup/pagedown
- and home and end keys to navigate through an article or section. Press the
- ALT key or click the mouse on the top line of the screen for a menu.
-
- √ Inside this issue get three FREE working programs!
- √ Tips to make your FOR...NEXT loops as much as 80% faster!
- √ Build a working, learning expert system
- √ Learn how to read Binary Coded Decimal
- √ Save 14K off an executable using PDS
- √ Explore the File Allocation Table & how DOS erases a file
- √ Use DOS interrupts to make your programs more powerful
-
- This months segments include:
-
- ■ FORUM - introduction to BASIC Softips...why the author is C-ing red!
- ■ Q&A - a forum to answer your questions...How to make a program
- run faster.
- ■ PROJECT OF THE MONTH - a multi-purpose input routine with commented
- source code that can save you over 14K off the size of an EXE!
- ■ LONG TERM PROJECT - a working expert system, part one...Just what is
- an expert system anyway? With source code for a working, learning
- expert system!
- ■ The BASICS - how dos manages disk drives...File Allocation Tables,
- FAT chains, DOS efficiency, erased files and more.
- ■ ADVANCED BASIC - part one...using interrupts to explore DOS. Includes
- source code for program to read and display DOS boot sector & FAT using
- BASIC.
- ■ BOOK OF THE MONTH - A review of ADVANCED MSDOS The Microsoft guide
- for Assembly Language and C programmers.
- ■ SOFTWARE OF THE MONTH - A hands on review of A.J.S. Publishings dBASE
- file support library, db/LIB.
- END TOC
- ADD 1
- Your add could be here! Very, very competitive rates. Reach you're marketplace
- using our unique, free and widely distributed computer based magazine.
- Rates are as low as $5.00 per add! Contact Hank at 1.201.707.1316 for more
- information. Call now!
- Can you afford not to?
- END ADD 1
- ADD 2
- This computer based magazine is free to over 400,000 thousand Bulletin Board
- subscribers! As we are able to document downloads, our readership will
- be determined, and then our add rates. But never fear! Our rates will always
- be the best in any computer magazine!
- BASIC Softips BASIC Softips BASIC Softips BASIC Softips BASIC Softips BASIC
- END ADD 2
- ADD 3
- Marquis Computing Inc., Call 1.201.707.1316 and ask for Hank
- ■ Custom software programming
- ■ DOS, dBASE and file utilities
- ■ Training and education
- ■ Technical writing & editing
- END ADD 3
- ADD 4
- To Subscribe to BASIC Softips for one year, send $12.00 with address to:
- Marquis Computing, 135 Chestnut Street, Bridgewater NJ 08807. You will
- receive your BASIC Softips on floppy diskette every month. Save downloading
- charges! Subscribe today. With each issue get the latest READER too!
- BASIC Softips BASIC Softips BASIC Softips BASIC Softips BASIC Softips BASIC
- END ADD 4
- ADD 5
- Do you want to write an article, author or sponsor a segment of BASIC Softips?
- We are actively soliciting articles on topics of programming and computers.
- Contact Marquis Computing, 135 Chestnut Street, Bridgewater NJ 08807. Or via
- CompuServe at 76120,2413. Heres your chance!
- END ADD 5
- Q&A Q&A
- Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A Q&A
-
- The Q&A section is for any questions you have regarding anything, except
- maybe the meaning of life.
-
- Q: What can I do to optimize a program for speed?
-
- A: We all want to know how to make our programs faster. There is no inherent
- reason a BASIC program should run slower than any other program. Often
- the reason our programs run slow is HOW we write them, not what language
- we use. Look at the example below. A simple FOR...NEXT loop to search
- through an array searching for a string.
-
- FOR X# = 1 TO UBOUND(Array$) STEP 1
- Search$ = UCASE$(LTRIM$(RTRIM$(SearchText$)))
- IF INSTR(UCASE$(Array$), Search$) > 0 THEN
- Element = X#
- EXIT FOR
- END IF
- NEXT X#
-
- Running this loop on my machine (a 80386 running with it's clock speed
- slowed to 10MHZ) this loop averaged .69 seconds over 10 runs. Then I
- began optimizing it. First, by moving the Search$ = UCASE$... line
- outside of the loop. This line is always the same, so why leave it
- inside the loop where we lose speed performing it each time? The code
- below averaged .46 seconds over 10 runs. A savings of an 33%! Move all
- non variant code to OUTSIDE of the loop.
-
- Search$ = UCASE$(LTRIM$(RTRIM$(SearchText$)))
- FOR X# = 1 TO UBOUND(Array$) STEP 1
- IF INSTR(UCASE$(Array$), Search$) > 0 THEN
- Element = X#
- EXIT FOR
- END IF
- NEXT X#
-
- Then I added a simple line, DEFINT A-Z, and changed the loop variable
- X# to X. The code below ran in an amazing .08 seconds a whopping 83%
- reduction in speed from the last change! What a difference changing the
- loop variable made! Integers are the 'natural' numbers of BASIC - use
- DEFINT A-Z at the top of each function, sub and module. And always use
- an integer loop variable, if you can.
-
- DEFINT A-Z
-
- Search$ = UCASE$(LTRIM$(RTRIM$(SearchText$)))
- FOR X = 1 TO UBOUND(Array$) STEP 1
- IF INSTR(UCASE$(Array$), Search$) > 0 THEN
- Element = X
- EXIT FOR
- END IF
- NEXT X
-
- Next, I moved the UBOUND(Array$) out, as shown below. Interestingly
- though, there was NO perceptible change in loop speed.
-
- DEFINT A-Z
-
- Count = UBOUND(Array$)
- Search$ = UCASE$(LTRIM$(RTRIM$(SearchText$)))
-
- FOR X = 1 TO Count STEP 1
- IF INSTR(UCASE$(Array$), Search$) > 0 THEN
- Element = X
- EXIT FOR
- END IF
- NEXT X
-
- I removed the STEP 1 line, as we are incrementing by one, and that is
- what BASIC FOR loops do by default. This brought about a .01 second
- reduction in run time. Finally, I removed the X from the NEXT X line.
- This brought about and additional .01 second reduction. I ended up
- with the code shown below. It runs in an average of.06 seconds. All
- the changed I made resulted in a loop running an incredible 82%
- decrease in execution time.
-
- DEFINT A-Z
-
- Count = UBOUND(Array$)
- Search$ = UCASE$(LTRIM$(RTRIM$(SearchText$)))
-
- FOR X = 1 TO Count
- IF INSTR(UCASE$(Array$), Search$) > 0 THEN
- Element = X
- EXIT FOR
- END IF
- NEXT
-
- So, often it's the WAY we program, not WHAT we program in that gives us
- slow programs. Imagine a whole application full of such loops or similar
- WHILE...WEND, DO...LOOP functions. Take a close look at all those loops
- to see if you can get some of these savings.
-
-
- If any of you have similar experiments or questions, please send them in!
-
- Please submit any questions, problems, corrections or
- comments to :
-
- Electronic (preferred):
- Editor
- BASIC Softips
- CompuServe 76120, 2413
-
- Paper (if you must):
- Editor
- BASIC Softips
- 135 Chestnut Street
- Bridgewater NJ 08807
-
-
- END Q&A Q&A
- FORUM FORUM
- FORUM FORUM FORUM FORUM FORUM FORUM FORUM FORUM FORUM FORUM FORUM FORUM FORUM
-
- C-ing red!
-
- I am writing this newsletter because I feel slighted. Everywhere I look,
- all I see is 'C'! People are always telling me (including on occasion MS
- tech support) that certain things just can't be done in BASIC. Well, to me,
- That's an open challenge! The whole concept of this magazine is to dispel
- the vicious rumors that BASIC is a toy. To that end, over the next series
- of these newsletters, we are going to blow the doors off of ANYONE who
- thinks BASIC is a toy!
-
- We will create a hyper text on-line help program, a multipurpose text entry
- routine, a natural language interpreter, a file unerase utility-our own
- version of NORTON utilities, a fully functional expert system, a database
- engine capable of reading and writing dBASE and much, much more.
-
- We will also cover, for newcomers, basic programming concepts such as
- modules, structured programming, functions, making full use of the world of
- DOS interrupts. You CAN do anything in BASIC that they can do in 'C'!
-
- We will also explore the world of BASIC libraries available to us - AJS
- Publishings dB/LIB, an excellent dBASE library. (By the way, how come no
- dBASE magazine ever mentions the fact that there is a real alternative to
- developing in dBASE or CLIPPER? I have written dBASE applications in
- guess what - BASIC!) We will also examine Crescent softwares fine general
- routine library QuickPac Professional and others.
-
- My goal is nothing less than to make BASIC programmers stand up, and say
- proudly "Yes, we used MS BASIC to develop that application." You see BASIC
- is not a toy anymore. Recent revisions to BASIC make it on par with virtually
- ant other language. The Professional Development Systems' inclusion on
- overlay support, plus stub-files and using BASICs built-in runtime module
- support allow you to create any application you can dream up. And in this
- newsletter you will see how to do it!
-
- Source Code you ask? Yes and lots of it. Example programs? Yes and lots of
- them. The whole goal of this magazine is faster, smaller, more full featured
- and more powerful programs - all using BASIC.
-
- Your comments?
- END FORUM FORUM
- PROJECT OF THE MONTH PROJECT OF THE MONTH
- PROJECT OF THE MONTH PROJECT OF THE MONTH PROJECT OF THE MONTH PROJECT OF THE
-
- This months project is a multiple purpose text entry routine, written
- entirely in BASIC. If will operate under QB or the PDS.
-
- In general, this program allows you more flexibility than using BASICs
- line input function. Although BASICs line input function offers full
- string editing support, you can only exit it with a carriage return.
- This program offers full editing support, but YOU define how to exit
- the routine. In addition, you can specify if the input will be character
- or numeric, if the string returned will be upper case or lower case and
- the color of the input. In the future we are going to expand this routine
- to give it a mask. This mask will let you return formatted strings from
- the user, for example Social Security numbers, phone numbers and more.
-
- As an added benefit, using this routine, and linking out full editing
- support using the PDS stub file NOEDIT.OBJ results in a savings of
- over 14,000 bytes of the size of a compiled executable! More powerful
- and smaller. That's what it's all about!
-
- Use the Cut segment command from the main utilities menu to save this
- file to disk. Give it a name like EDITOR1.BAS so you can keep them
- straight each month as we add features. When you load this into BASIC,
- delete all of the text lines above. The program starts immediately below.
-
- 'Start of program-------------------------------------------------------
- '
- '(C)Copyright 1990 Marquis Computing Inc. All rights reserved.
- 'You may use this program for anything or any purpose including inclusion
- 'into programs you write BUT you cannot sell this source code. Written by
- 'Hank Marquis. revised 9/8/90.
-
- DEFINT A-Z
- DECLARE SUB LineInput (Msg$, code$, Caps, Num, row, Col, Fore, back)
-
- 'you program code here
- ' .
- ' .
-
- Msg$ = "Enter your name:" 'prompt string
- Code$ = CHR$(27) + CHR$(13) 'exit on escape or enter
- Caps = 0 'do not force to capitals
- Num = 0 'do not force numeric input
- Row = 5 'print prompt on row 5
- Col = 5 'start printing ar column 5
- fore = 7 'use white for foreground
- back = 0 'use black as background
-
- LineInput Msg$, Code$, Caps, Num, Row, Col, Fore, Back 'make the call
-
- ' .
- ' .
- 'process Msg$ returned from user here
- '
-
-
- END
-
- DEFINT A-Z
- SUB LineInput (Msg$, code$, Caps, Num, row, Col, Fore, back)
-
- 'This sub routine is used to write a prompt string, and then accept
- ' input. By setting variables which are defined below you have
- ' control over where the editing happens and how.
- '
- 'All standard editing keys are supported :
- ' arrow left, arrow right = move one character left or right
- ' control arrow left, arrow right = move one word left or right
- ' home = jump to beginning of line
- ' end = jump to end of line
- ' backspace = delete the character to the left of the cursor
- ' delete = remove the character at the cursor
- '
- 'You may choose if the user will enter numbers or letters or
- ' if the input string will be capitalized. By setting Msg$ to "",
- ' no prompt string is presented, allowing use of LineInput to get
- ' single characters.
- '
- '
- 'Msg$ = on input the prompt message to be displayed
- ' = on exit the user entered string
- '
- 'Code$ = a concatenated string containing the key strokes to exit the
- ' sub routine. Example :
- ' Code$ = CHR$(13) + CHR$(27) 'sets exit code to the escape key or
- ' ' the enter key
- '
- 'Caps = force capitalization flag. When Caps=1 the string is forced to
- ' all capitals
- '
- 'Num = force numeric input flag. When Num=-1 the string is forced to
- ' be a numeric input. When Num is > 0, Num characters are input
- ' and then the routine is exited. Use this is determine the
- ' quantity of characters that a user can input
- '
- 'Row = Row to position prompt string on
- 'Col = Column to position prompt string at
- '
- '
- 'here are some examples of using line input
- '
- ' example #1 : print a prompt string and enter a string
- '
- ' Msg$ = "Enter your name:" 'prompt string
- ' Code$ = CHR$(27) + CHR$(13) 'exit on escape or enter
- ' Caps = 0 'do not force to capitals
- ' Num = 0 'do not force numeric input
- ' Row = 5 'print prompt on row 5
- ' Col = 5 'start printing ar column 5
- ' fore = 7 'use white for foreground
- ' back = 0 'use black as background
- ' LineInput Msg$, Code$, Caps, Num, Row, Col, Fore, Back
- '
- ' example #2 : print a prompt string and a return a number
- '
- ' Msg$ = "Enter birthday:" 'prompt string
- ' Code$ = CHR$(27) + CHR$(13) 'exit on escape or enter
- ' Caps = 0 'do not force to capitals
- ' Num = -1 'force numeric input
- ' Row = 5 'print prompt on row 5
- ' Col = 5 'start printing ar column 5
- ' fore = 7 'use white for foreground
- ' back = 0 'use black as background
- ' LineInput Msg$, Code$, Caps, Num, Row, Col, Fore, Back
- '
- ' example #3 : print a prompt and a return a string 4 characters long
- '
- ' Msg$ = "Enter address:" 'prompt string
- ' Code$ = CHR$(27) + CHR$(13) 'exit on escape or enter
- ' Caps = 0 'do not force to capitals
- ' Num = -1 'force numeric input
- ' Row = 5 'print prompt on row 5
- ' Col = 5 'start printing ar column 5
- ' fore = 7 'use white for foreground
- ' back = 0 'use black as background
- ' LineInput Msg$, Code$, Caps, Num, Row, Col, Fore, Back
- '
- ' example #4 : suppress prompt and a return a single capital character
- '
- ' Msg$ = "" 'prompt string
- ' Code$ = "" 'exit on any key
- ' Caps = 0 'do not force to capitals
- ' Num = 1 'exit on one character
- ' Row = 5 'print prompt on row 5
- ' Col = 5 'start printing ar column 5
- ' fore = 7 'use white for foreground
- ' back = 0 'use black as background
- ' LineInput Msg$, Code$, Caps, Num, Row, Col, Fore, Back
- '
- '
- 'Some things you could add are...
- '1) you could add a mask to the call like this:
- ' LineInput (Mask$, Msg$, Code$, Caps, Num, Row, Col, Fore, Back)
- ' where mask is a template like ...-...-... for phones or
- ' ...-..-.... for Social Security numbers etc.. To do this you would
- ' need to add some code in the section where the string is printed - but
- ' don't forget to add some code in the editing sections to jump over the
- ' mask!
- '
- '2) You could have BASIC format the numeric string input by using the
- ' PRINT USING command. You could also put this in the printing section
- ' like this maybe..
- '
- ' PrintString$ = ""
- ' FOR X = 1 TO LEN(text$)
- ' PrintString$ = PrintString$ + "#"
- ' IF X MOD 3 = 0 THEN PrintString$ = PrintString$ + ","
- ' NEXT
- ' PRINT USING PrintString; VAL(text$)
- '
- '-----------------------------------------------------------------------
-
- 'The main sub routine starts here.
- ' here we set up our variables and get ready to edit a string
-
- COLOR Fore, back 'set the colors
- Msg$ = LTRIM$(RTRIM$(Msg$)) 'use LRTRIM to remove any blanks from the
- ' left of the message string
-
- blank$ = SPACE$(1) 'make a blank line for use later on
-
- LOCATE row, Col, 1, 0, 7 'position the prompt string
- PRINT Msg$; 'print the prompt string message
-
- CurCol = LEN(Msg$) + 6 'set our position markers
- Col = CurCol ' " " " "
-
- StartScan = 7 'set for "insert cursor"
- StopScan = 7 'set for "insert cursor"
-
- ValidNums$ = "0123456789" 'build a string of valid numbers which
- ' can be entered
-
- '------------
- 'here we do the actual editing of the string
- 'this do...loop is used until a key in the Code$ string is entered,
- ' then the loop is exited.
- DO
- Ky$ = INKEY$ 'get a key from BASIC
- IF LEN(Ky$) > 0 THEN 'the user hit a key
-
- IF Caps THEN Ky$ = UCASE$(Ky$) 'set to caps if Caps flag set to 1
- 'Below, we make the value negative for extended key strokes
- ' but you could make them something else. Here we convert the
- ' key into ASC code. This is needed because BASIC returns
- ' a keystroke of 1 or 2 characters. This is an easy way to process
- ' extended keys like arrows, pageup, function keys and ALT & CTRL
- ' keys.
-
- IF LEN(Ky$) > 1 THEN 'this is an extended key code
- Ky = ASC(RIGHT$(Ky$, 1)) * -1
- ELSEIF LEN(Ky$) = 1 THEN 'this is a normal key
- Ky = ASC(RIGHT$(Ky$, 1))
- END IF 'end of...converting INKEY to ASC()
-
- IF Ky > 0 AND Num = -1 THEN
- IF INSTR(code$, CHR$(Ky)) = 0 AND INSTR(ValidNums$, Ky$) = 0 THEN
- 'if this key isn't an exit key AND
- 'we are checking for numbers AND
- 'if this is NOT a number THEN
- BEEP 'user did not enter a number
- Ky = 255 'set Ky to null so nothing happens
- END IF 'end of...not a number
- END IF 'end of...checking for numbers
-
-
- 'This select case block is where we determine the type of key
- ' entered, and then process it accordingly.
-
- SELECT CASE Ky
-
- CASE 8 'back space
- 'Back space removes the character to the LEFT of the
- ' cursor. Delete removes the current character.
- IF LEN(LTRIM$(RTRIM$(text$))) THEN
- CurCol = CurCol - 1
- F$ = LEFT$(text$, (CurCol - Col))
- L$ = RIGHT$(text$, LEN(text$) - (CurCol - Col) - 1)
- text$ = F$ + L$
- changed = 1
- END IF 'if procesable text$
-
- CASE 32 TO 125 ' the basic keyboard alphabet
- 'If we are inserting then break the sting into two sub
- ' strings called F$ - first and L$ - last. We break the string
- ' right at the current cursor position. Then, if we are inserting
- ' we add the new character Ky$ to the front off L$.
- ' for example :
- ' This is+sample string. (note the location of the cursor)
- ' F$ = This is
- ' L$ = sample string
- ' Ky$ = a
- ' L$ = "a" + "sample string" or "a sample string"
- ' Then we combine the parts,
- ' This is a sample string.
- IF Inserting THEN
- F$ = LEFT$(text$, (CurCol - Col))
- L$ = Ky$ + RIGHT$(text$, LEN(text$) - (CurCol - Col))
- text$ = F$ + L$
- CurCol = CurCol + 1
- changed = 1 'set the changed flag to print it
- ELSE 'we are overstriking
- text$ = text$ + SPACE$(1) 'make room for the next character
- CurCol = CurCol + 1 'bump up the position in the string
- MID$(text$, CurCol - Col) = Ky$ 'set the string to the character
- changed = 1 'set the changed flag to print it
- END IF 'insert/overstrike options
-
- CASE -71 'home
- CurCol = Col
- LOCATE row, CurCol, 1, StartScan, StopScan
-
- CASE -75 'left arrow
- CurCol = POS(0) - 1
- IF CurCol < Col THEN CurCol = LEN(text$) + Col
- LOCATE row, CurCol, 1, StartScan, StopScan
-
- CASE -77 'right arrow
- 'POS(0) returns from BASIC the current horizontal cursor position
- ' we add 1 to it to move the cursor position 1 to the right.
- ' Using LOCATE positions the cursor. If we had used the Changed
- ' variable to re-print the line, it would result in some
- ' flickering of the screen.
- CurCol = POS(0) + 1
- IF CurCol > LEN(text$) + Col THEN CurCol = Col
- LOCATE row, CurCol, 1, StartScan, StopScan
-
- CASE -79 'end
- 'The end of the editing box is the location of the start of
- ' the text string + the length of the text string$
- CurCol = LEN(text$) + Col
- LOCATE row, CurCol, 1, StartScan, StopScan
-
- CASE -83 'delete
- 'Delete is similar to backspace but also different. Delete
- ' pulls the rest of the string toward the left as it removes
- ' the character at the cursor.
-
- textwid = LEN(LTRIM$(RTRIM$(text$))) 'get length of text string
-
- IF textwid AND LEN(text$) - (CurCol - Col) - 2 > -1 THEN
- 'if there is a character to the right of the cursor is indicated
- ' by LEN(text$) - (CurCol - Col) - 2 > -1
- CurCol = CurCol - 1 'move cursor to the left 1 space
- F$ = LEFT$(text$, (CurCol - Col) + 1) 'break the string in half
- 'note that below, we take LEN(text$)...-2. The -2 is to
- ' get rid of the character at the cursor PLUS pull the
- ' end of the rest of L$ to the left.
- L$ = RIGHT$(text$, LEN(text$) - (CurCol - Col) - 2)
- CurCol = CurCol + 1 'move the cursor to the new character
- text$ = F$ + L$ 're-combine the string
- changed = 1 'set to re-print the string
-
- ELSE 'nothing to delete!
- BEEP
- END IF 'if processable text$
-
- CASE -82 'insert
- 'This is a toggle between inserting and overstriking.
- IF Inserting = 0 THEN
- Inserting = 1
- StartScan = 0 'this puts the cursor marker on the bottom line
- ELSE
- Inserting = 0
- StartScan = 7 'this puts the cursor marker on the top line
- END IF
- changed = 1
-
- CASE -115 'ctrl left
- 'start at the current cursor position and count BACK until the
- ' first space is found, then set the position of the space
- ' as the new current cursor position. Setting Changed = 1
- ' forces a re-print of the string, hence updating the display
- FOR X = CurCol - 1 TO Col + 1 STEP -1
- IF MID$(text$, X - Col, 1) = CHR$(32) THEN
- CurCol = X
- changed = 1
- EXIT FOR
- END IF
- NEXT
-
- CASE -116 'ctrl right
- 'start at the current cursor position and count UP until the
- ' first space is found, then set the position of the space
- ' as the new current cursor position. Setting Changed = 1
- ' forces a re-print of the string, hence updating the display
- FOR X = (CurCol - Col) + 1 TO (LEN(text$) + Col)
- IF MID$(text$, X, 1) = CHR$(32) THEN
- CurCol = X + Col
- changed = 1
- EXIT FOR
- END IF
- NEXT
-
- END SELECT
- IF changed THEN
- 'Changed is used to indicate that we made some change in the string
- ' or the cursor position. If Changed = 1 then we want to re-print
- ' the string.
- changed = 0 'reset changed flag to 0
-
- LOCATE row, Col, 0 'locate where the text Starts
- PRINT blank$; 'print a blank line to erase present line
-
- LOCATE row, Col, 0 'locate where the text Starts
- PRINT text$; 'print the text line
- blank$ = SPACE$(LEN(text$)) 'reset the blank line for next time
- LOCATE row, CurCol, 1, StartScan, StopScan 'position the cursor
- END IF 'end of ...if changed
-
- END IF 'end of ...user hit a key structure
-
- IF Ky > 0 THEN
- IF Num > 0 AND LEN(text$) >= Num OR INSTR(Code$, CHR$(Ky)) > 0 THEN
- 'if we have entered Num characters OR
- ' if an exit key was pressed
- ' then exit the loop
- EXIT DO
- END IF 'end of...if user hit exit key or length of string limit
- END IF 'end of...if this is a normal key
-
- LOOP
-
- '------------
- 'this is the end of the subroutine
-
- Msg$ = LTRIM$(RTRIM$(text$)) 'remove any leading or trailing blanks
-
- code$ = Ky$ 'set the Code$ variable to the last key
- ' entered so that you can tell what key
- ' the user selected to exit the routine.
-
- 'This is handy when you give the user
- ' a choice of two inputs and you want to
- ' know which one was selected.
-
- END SUB
-
-
- END PROJECT OF THE MONTH PROJECT OF THE MONTH
-
- LONG TERM LONG TERM
- LONG TERM PROJECT LONG TERM PROJECT LONG TERM PROJECT LONG TERM PROJECT LONG T
-
- Over the next few issues we will create a working, full featured expert
- system. Our expert will be able to do anything we want it to. Given
- a few examples it will be able to astound and amaze you and your friends.
- Then you can start adding artificial intelligence to your programs - and
- astound your users!
- The expert we will build will be of a type known as a forward chaining,
- parallel system. It will be a neural network. After we program it, we
- will give it some examples, and it will actually learn from its mistakes
- and get better and better as you use it. This wont be a "toy" either. Once
- we get done with it, I'll show you how to build in hooks for dBASE or Lotus
- so that you can give your expert applications real teeth! Sound interesting?
- Lets go! But first we must cover some basics of expert systems. The following
- article covers the basic operations and acronyms of what we are going to be
- doing.
-
- Just what is an Expert System?
-
- An expert system is a program which mimics a human expert in a given field.
- Expert systems usually focus in a narrow area of expertise. Their area of
- knowledge is known as a Domain Of Enquiry. Specialized knowledge is captured
- and stored in a Knowledge Base. In our expert, knowledge will be entered by
- you as you build it. Most expert systems consists of stated Rules. A rule is
- the chain of events or actions which will induce the expert to come to some
- conclusion. In our expert, the rules will be stored in an array. The
- Knowledge Base may shape these rules, allowing the expert system to make
- deductions. These rules are typically not variable, except by a programming
- effort referred to as Knowledge Engineering. Artificial Intelligence (AI)
- refers to the entire field of study - like physics. An expert system applies
- rules to its Knowledge Base and provides expert opinions. Well designed
- expert systems give performance equal to or better than their human
- counterparts. They do so in a manner which is easy, fast, inexpensive and
- consistent. Well designed expert systems are also easy to use. They require
- no specialized actions on the part of the user. They allow users the benefit
- of not having to be an expert in the field that the expert is. Better still,
- some even give an explanation as to why and how it came to a conclusion.
-
- How do expert systems get to be an expert?
-
- Most AI packages are "shells". A shell is a basic system which the user must
- program with both it's expertise and user interface. The user designs the
- layout of the system and then programs in the knowledge. Expert systems are
- provided with their knowledge. This knowledge is stored in rules. Most expert
- systems follow a rules based approach. In these systems the conclusions are
- predefined in a series of nested rules statements. For example :
-
- Rule 1:
- IF : symptom is intermittent bad data
- AND : facility is analog
- AND : protocol is ASCII
- AND : application is dial-up modem
- THEN : problem is line noise hits
-
- That looks a lot like BASIC and it is similar. Should the above rule prove
- faulty with usage, it is changed. The Knowledge Engineer is the programmer
- who would make the changes to the rules or modify the shell. Most expert
- system shell systems have no method of directly modifying their own rules. If
- there was a method for a program to modify itself, it could do so without
- requiring the expense and time of a knowledge engineer. While, in fact, self
- learning systems are somewhat rare, they do exist. A learning expert system
- has a built in mechanism to modify its own rules based on its performance
- feedback. That is the expert system can modify the manner it uses based on
- wether the experts operation was right or wrong. If the expert was wrong, it
- could modify the way it came to its deduction to reflect the real or desired
- outcome it could learn from experience. A neural network is an expert system
- which learns by example. You give it an outcome, along with the inputs which
- should give that outcome, and the expert builds its own rules. It mimics the
- actual process of the human mind - building its own pathways of connectivity.
- By repetitively showing the neural network examples of the desired result,
- the expert learns from experience. This is the process which our expert will
- follow, learning by experience.
-
- What is a good application for an expert system?
-
- Good applications for an expert system are any operations which are
- essentially a matching of multiple inputs to certain outputs or operations.
- Obviously, the human expert operates this way. The human expert observes the
- symptoms, the application or operation and then, based on experience,
- deduces the most likely cause. And that is the most important aspect.
- Human experts operate based on experience. Who would deny that a more
- experienced technician or professional makes better, more accurate decision
- than an inexperienced one? Likewise, for an expert system to be reliable,
- there has to be some history of past occurrences. Expert systems do not
- produce original thought. They simply relate one or more given conditions to
- a given result. Virtually any operation where there are certain associated
- inputs which typically result in the same output are good applications for
- an expert system. Some past expert systems have included:
-
- MYCIN - prescribing mediations,
- PROSPECTOR - determining chance of finding precious metals,
- DENDRAL - determining chemical structures and
- PUFF - breathing disorders.
-
- Notice how they all focus in some well defined area. Each also mimics an
- expert which is able to deduce the outcome based on asking some questions or
- observing some result. For example, PUFF, the expert system on breathing
- disorders mimics a medical specialist in respiratory ailments. Observations
- the Doctor might make are :
- This is a female.
- She is somewhat pale.
- She is complaining of shortness of breath.
- Based on these observations, questions the Doctor might ask are:
- Does your chest feel tight?
- Does you family have a history of asthma?
- These observations, in conjunction with the questions asked, gather data and
- build relationships which make an expert opinion, in this case a medical
- diagnosis. Furthermore, this process could be for any field. The more complex
- the item in question the larger the possible problems. The more complex the
- field, the longer it takes to learn the reasoning process. Doctors go to
- school for years to learn the Knowledge base of medical data then they learn
- by applying that knowledge base in real situations. So good applications for
- an expert system are those which:
-
- 1) Have an outcome which is deducible based on some inputs,
- 2) Have many variables and many possible results making for very complex
- deductions,
- 3) Have many relationships between and among the input variables and
- outputs,
- 4) Require years of study or experience to master,
- 5) Have fairly stable and consistent inputs or symptoms,
- 6) Posses an existent base of knowledge upon which to learn from and
- 7) Be an application valuable enough to warrant the benefits of an expert
- system.
-
- Ok. Enough about what an expert is, how do they work? Well, essentially, all
- the expert does is match patterns. Our expert will take a various amount of
- inputs and create a unique rule for each possible set of inputs. Then, when
- actually processing, it will compare the inputs against all the possible
- masks, choosing as the answer the closest match. This is referred to as fuzzy
- logic and is a powerful tool. The expert can even guess - pretty accurately!
-
- Out expert system, at this stage of evolution, will be menu driven. It
- firsts prompts for the total number of variable and results. The variables
- are the questions is will ask, the results are the answers it will give.
- To use the expert, think of some group of things or some problem that
- you will want the expert to differentiate between. This can by anything
- from why your car wont start, to medical diagnosis to psychology. Keep
- this in mind when entering the variables. Phrase each variable in the form
- of an attribute - something the expected result would posses, which describes
- it. For example, if one of your results is to be a CAR then a variable
- might be TIRES or WINDSHIELD. Keep all you dialog similar.
-
- After you select option 3, run expert, the expert system will prompt
- for each variable whether it is true or false. You must have a desired
- answer in mind when answering these prompts. This is because at this
- early stage the expert knows nothing! You need to train it first. We
- do this by you thinking of one of the results, then answering the
- variable prompts for that response. Initially the expert will be wrong.
- But as you give it examples of each response, the expert begins to learn.
- Finally, when fully trained, it will have learned to tell each result
- from the answers to the questions it asks. After each run, select option
- 1, display rules. What you will see is a matrix of the results to variables.
- As you use the expert, and it learns, this matrix will change. You will
- see positive numbers for true conditions and negative numbers for false
- conditions.
-
- While the expert in this stage of development is not directly usable in
- another program, think about what it can do. Given some set of inputs and
- outputs, and experience, the expert can accurately make deductions. And
- if is wrong it can modify itself to induce corrective actions. Now think
- about using such an expert in conjunction with a database or a spreadsheet.
- You begin to understand the power. For example you could build a credit
- risk assessment expert linked to a database, or a diagnostic system to
- recommend repair activities. The sky is the limit.
-
- Next month we are going to add a new feature known as NODES. This will
- allow our expert to use it's deductions as inputs to another deduction.
- This addition will allow our expert to analyze a situation, determine a
- course of action and then recommend another course of action based upon
- it's first deduction.
-
- The code below is for a fully functional, working expert system. Take a
- look at it. Each month we will add new parts, including a save to disk
- option, and more. For now though it is fully functional and is able to learn
- from its experience.
- Use the Cut segment command from the main utilities menu to save this
- file to disk. Give it a name like EXPERT1.BAS so you can keep them
- straight each month. When you load this into BASIC, delete all of the
- text lines above. The program starts immediately below.
-
- ' ------------------------------------------------------------------
- '
- '(C)Copyright 1990 Marquis Computing Inc. All rights reserved.
- 'You may use this program for anything or any purpose including inclusion
- 'into programs you write BUT you cannot sell this source code. Written by
- 'Hank Marquis.
-
- DEFINT A-Z
- CLS
-
- ' To make this a little easier for you, below I have stated the name &
- ' function of each array and variable .
- '
- ' VARIABLE NAME FUNCTION
- ' -------- ---- --------
- ' MV NUMBER OF VARIABLES THE MAX NUMBER OF VARIABLES
- ' MR NUMBER OF RESULTS THE MAX NUMBER OF RESULTS
- ' HI HIGHEST RESULT POINTS TO THE MOST LIKELY NR()
- ' D HIGHEST RESULT HOLDS LAST BEST GUESS
- '
- ' NR$(X) NUMBER OF RESULTS HOLDS THE CURRENT RESULT FOR
- ' R(X,Y) RULE ARRAY HOLDS THE RULE FOR EACH (X,Y)
- ' D(D) DECISION ARRAY HOLDS THE CURRENT BEST GUESS
- ' NV(X) NUMBER OF VARIABLES POINTS TO THE VARIABLE FOR CASE
- ' NV$(X) RESULT REGISTER FOR (X,Y) HOLDS THE ACTUAL
- ' RESULT.
-
-
- 'Setup expert definition-----
- '
- 'To start use 4 variables
- INPUT "How many variables "; MV
-
- 'To start use 3 results
- INPUT "How many results "; MR
- PRINT
-
- REDIM NR$(MR), R(MV, MR), D(MR), NV(MV), NV$(MV)
-
- 'lets get the expert to learn to tell the difference between a plane
- ' a boat and a car. So for the 4 variables give the expert the following
- ' responses :
- ' Variable 1 name : wings
- ' Variable 2 name : sails
- ' Variable 3 name : tires
- ' Variable 4 name : motor
- FOR I = 1 TO MV
- PRINT "Variable"; I; "name : ";
- LINE INPUT ; NV$(I)
- PRINT
- NEXT 'I
- PRINT
- 'For the three responses give it
- ' Result 1 name : car
- ' Result 2 name : boat
- ' Result 3 name : plane
-
- FOR I = 1 TO MR
- PRINT "Result"; I; "name : ";
- LINE INPUT ; NR$(I)
- PRINT
- NEXT 'I
-
- 'MENU SYSTEM -----
-
- DO
-
- 'Select option 1 to display the rules the expert develops
- ' during its use
- 'Option 2 quits
- 'Option 3 starts the expert and asks you for the input regarding the
- ' four variables, then it makes its guess.
-
- PRINT
- PRINT " 1 - Display rules"
- PRINT " 2 - Exit"
- PRINT " 3 - Run expert"
- X = VAL(UCASE$(INPUT$(1))) 'this is a quick way of getting one key
- ' stroke from BASIC
-
- SELECT CASE X
- CASE 1
- 'Prints a matrix of the relationship between inputs and outputs.
- PRINT
- FOR I = 1 TO MV
- FOR J = 1 TO MR
- PRINT R(I, J); " ";
- NEXT 'J
- PRINT
- NEXT 'I
-
- CASE 2
- 'as implies, ends program!
- END
- CASE 3
- CLS
- D = 0 'set last best guess to 0
- 'this FOR...NEXT loop resets the expert for each question
- FOR I = 1 TO MR
- D(I) = 0
- NEXT 'I
- GOSUB Engine 'run the expert system engine
- END SELECT
- LOOP
-
- END
-
-
- Engine: '------------------------------------------------
- '
- 'This is the main expert system code - called the inference engine
- ' 1) we get the user input
- ' 2) build a mask or rule
- ' 3) make a guess
- ' 4) and if wrong learn
-
- 'Get User Input------------------------------
-
- FOR I = 1 TO MV 'REPEAT SUB FOR "MV" MAXIMUM NUMBER OF VARIABLES
-
- 'ASK ABOUT THE CURRENT VARIABLE
-
- DO
- PRINT "Is variable"; I; "("; NV$(I); ") [T]rue or [F]alse?"
- YN$ = UCASE$(INPUT$(1))
-
- SELECT CASE YN$
- CASE "T"
- 'If the variable is true then set the array NV(I) to a 1 indicating
- ' that the result possess this attribute of NV(I)
- NV(I) = 1
- EXIT DO
- CASE "F"
- 'If the variable is false then set the rules array to a 0 indicating
- ' that the result does NOT possess this attribute of NV(I)
- NV(I) = 0
- EXIT DO
- END SELECT
-
- LOOP
-
- NEXT 'I
-
-
- 'Now NV() holds a mask which represents the characteristics of the
- ' given item, boat, plane or car. For each attribute which is true
- ' NV(x) = 1 and for each attribute which is false, NV(x) = 0.
- '
- 'for example if you had answered the prompts thinking of a car then NV()
- ' looks like this:
- ' NV(1) = 0 NO - wings
- ' NV(2) = 0 NO - sails
- ' NV(3) = 1 YES - tires
- ' NV(4) = 1 YES - motor
- '
- 'We know have an attribute mask of '0011' for car. Now lets see if the
- ' expert can learn to tell them apart.
-
- 'BUILD A RULE-------------------------------
-
- FOR I = 1 TO MV 'REPEAT FOR MAXIMUM NUMBER OF VARIABLES
- FOR J = 1 TO MR 'REPEAT FOR MAXIMUM NUMBER OF RESULTS
-
- 'Here is where we build a rule for this attempt. We multiply the
- ' experts memory which is array R() times the mask held in NV().
- ' If the mask attribute is 0 then the value held in R() is not
- ' added to the decision array D() for this response. If NV() holds a 1
- ' then D() has the value of R() added to it. In this way we are
- ' actually testing each possible result against the mask array NV()
- '
-
- D(J) = D(J) + NV(I) * R(I, J) 'PERFORM RULE
-
- NEXT 'J
- NEXT 'I
-
- ' Now D() holds a value for each result. The value is greater for each
- ' attribute of the item for which NV() was a 1. The highest value of D()
- ' is the best, most likely answer for the given responses held in the
- ' mask array NV()
-
- 'MAKE AN EDUCATED GUESS! ----------------------------------------------
-
- FOR I = 1 TO MR 'REPEAT FOR MAXIMUM NUMBER OF RESULTS
-
- IF D(I) > D OR D(I) = D THEN 'IS D(X) = TO 1 OR -1 ?
- D = D(I) 'IF 1 OR -1 THEN ASSIGN IT AS BEST GUESS
- HI = I
- END IF
-
- NEXT 'I
-
- 'Now HI and D hold the number of the result which scored highest against
- ' the mask array NV(). We now have the experts guess as to which result
- ' is the answer - it is result NR$(HI)
-
- 'ASK IF IT'S A CORRECT ASSUMPTION -------------------------------------
-
- PRINT
- PRINT "Is the answer "; NR$(HI); "? [Y/N]" 'lets see if we are right
- a$ = UCASE$(INPUT$(1)) 'get a Y/N from user
- IF a$ = "Y" THEN RETURN 'bug out - we are right!
- 'IF IT IS NOT CORRECT ADJUST THE RULES (LEARN) ------------------------
- 'The user did not indicate that this was the right answer, so it
- ' must be wrong. Get the right answer from the user.
- PRINT
- PRINT "Which result was it? [select from 1 to"; STR$(MR); "]"
- FOR I = 1 TO MR 'DISPLAY ALL THE POSSIBLE RESULTS
- PRINT I; "- "; NR$(I)
- NEXT 'I
-
- B = VAL(INPUT$(1)) 'GET THE USERS RESPONSE
-
- 'B now holds the CORRECT result number, which we just got from the
- ' user.
- 'For all the possible results, if the value of D() calculated above
- ' is greater or equal to the best guess D, then we need to reduce the
- ' value of this rule, as it was wrong.
-
- FOR I = 1 TO MR
- IF D(I) > D OR D(I) = D AND I <> B THEN
- FOR J = 1 TO MV
-
- 'Remove the value of NV() from the rule array R(I,J) for this
- ' result. This has the effect of further separating the
- ' masks - making the expert more accurate. This is the actual
- ' learning feedback process.
-
- R(J, I) = R(J, I) - NV(J)
-
- NEXT 'J
- END IF
-
- NEXT 'I
-
- FOR J = 1 TO MV
- 'As an extra measure, lets add the value of NV() to the correct
- ' result, giving us now a clear separation between the right
- ' result and the wrongly guessed result. The expert has now learned.
-
- R(J, B) = R(J, B) + NV(J)
-
- NEXT 'J
-
- 'FINISH----------------------
-
- RETURN
-
- 'END OF EXPERT SYSTEM CODE-----------------------
- END LONG TERM LONG TERM
- THE BASICS THE BASICS
- THE BASICS THE BASICS THE BASICS THE BASICS THE BASICS THE BASICS THE BASICS
-
- In this section we will cover topics of interest to new or intermediate
- users of BASIC. This month, the topic is DOS Disk Files, an introduction
- to how DOS manages files.
-
- Sectors & Clusters
- When a file is written to disk in DOS, it is divided into pieces,
- called sectors. Each sector is typically 512 bytes long. A byte
- refers to the grouping of eight bits - 10001101 is a byte.
- Several sectors are then combined into a unit called a cluster.
- The number of sectors per cluster depend upon the size of the
- disk. When a disk is formatted, this information is stamped on
- the disk in a special location, called the boot sector or BIOS
- Parameter Base. Each cluster represents a given amount of disk
- space. Depending on disk type (floppy or hard) and disk size
- the number of sectors per cluster in different.
-
- cluster
- +---------------------------------------+
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- | | | | |
- +---------------------------------------+
- +-------------------------------------> SECTORS
-
-
- file storage
- When a file is written to a disk, it is done so in blocks of
- bytes. In fact is written a sector at a time. Even if the file
- doesn't fill up a sector, it still uses an entire sectors worth of
- storage space. And, in turn, using one sector out of a cluster
- uses that entire cluster. For example, if a disk used 512 byte
- sectors (which is what most IBM disks use) and we have 4 sectors
- per cluster (again, common on most hard drives) then the minimum
- disk space needed for any file is 512 bytes X 4 sectors/cluster
- or 2048 bytes! If the file was a very small batch file of only 30
- bytes, it would really use 2048 bytes of disk space.
-
- cluster
- +---------------------------------------+
- |This is |systems. |part of |.........|
- |how data | |the |.........|
- |is stored|If the |cluster |.........|<-- wasted or slack
- |on most |file only|the rest |.........| area
- |disk |uses a |is waste |.........|
- +---------------------------------------+
- +-------------------------------------> SECTORS
-
-
- Now it isn't really using all that space, it's just allotted that much space
- due to the way that DOS organizes disks. When you use the DOS DIR
- command to display a file, you see the actual size of the file -
- not how much space it is really using. The method which DOS uses
- to add to a file on a hard of floppy disk is determined by the
- version of DOS which is being used. Early versions of DOS (before
- DOS version 3.0) used any next available cluster when a file was
- appended or added to. Later versions of DOS try to allocate new
- clusters which are contiguous - or together in a row. It is
- important to understand that as a file grows, the clusters which
- hold its data are often not one after the other. As shown above,
- as a file grows new clusters are added. If there are no more available
- clusters which are contiguous, DOS jumps to the next open cluster.
- This process is referred to a fragmentation.
-
- Keeping track of file allocations
- DOS employs a simple system of tracking the clusters in use by a
- file. This system is comprised of two parts - the File
- Allocation Table (FAT) and the directory entry. First, the
- directory entry. You see part of a DOS directory entry when you
- use the DOS DIR command to display files. The entry contains the
- file name, its size, its attribute and its starting cluster
- number. The directory entry containing the file name also holds
- the status of the file. If the file is erased, then the first
- character in the file name is the Greek letter omega. If the first
- character in a file name is omega then DOS doesn't display its data.
- DOS uses the starting cluster number of a files directory entry to
- identify the first cluster which holds this files data. The
- starting cluster number is converted into an offset which points
- to another special region of the disk, referred to as the File
- Allocation Table or FAT. The FAT comes right after the BIOS
- Parameter Base of boot sector of a disk. It is a matrix or table
- of numbers, called Cells. Each number points to a cluster on the
- disk. There may be more than one FAT and often there are two.
-
-
- cluster 0
- +---------------------+
- | BIOS Parameter Base |
- | or the BOOT sector | A DOS disk is laid out
- | | like this.
- |---------------------|
- | File Allocation |
- | Tables (FATS) |
- | |
- |---------------------|
- | ROOT Directory |
- | |
- |---------------------|
- | File space |
- | |
-
- cluster n
-
- Right after the FAT table(s) there is the root directory space. It is a fixed
- amount of disk space given the task of storing files and sub-directory
- entries. You can only have a limited amount of files and sub directories
- in the root because the root directory size if fixed. Other directories and
- their entries are stored as files in other clusters, randomly around the disk.
- Each FAT cell has a number, known by its position in the
- table, which represents its cluster. FAT cell 1 is for cluster number 1, FAT
- cell 2 for cluster 2 etc.. Every cluster has an entry in the FAT, and each
- FAT cell points to a cluster. In addition, each FAT cell can hold a value.
- These values are 0, the number of another FAT cell or the hexidecimal number
- FFF for a floppy or FFFF for a hard disk. If the FAT cell for a cluster
- is set to 0, then this FAT cells cluster is not in use. If the FAT cell
- contains a number of a legal cluster number, then the number is the next
- cluster containing data for this file. If the number is hex FFF or FFFF
- then this FAT cell cluster is the last cluster in the file.
-
- Example File Allocation chain for file SAMPLE.TXT
-
- FAT CELL1 FAT CELL2 FAT CELL3 FAT CELL4
- +---------------------------------------+
- | | | | |
- | 2 | 4 | FFFF | FFFF |
- | | | | |
- +---------------------------------------+
- | | | |
- | | | |
- | | | |
- | | | +>FAT cell 4 holds the value 65535
- | | | or HEX FFFF. For hard disks this
- | | | means the end of the file.
- | | |
- | | +> FAT cell 3 is used by another file
- | |
- | +>FAT cell 2 points to FAT cell 4
- |
- +>FAT cell 1 points to FAT cell 2 as the next cell in the chain
- for our example file.
-
-
- In the above diagram, the FAT chain for our file is 1 - 2 - 4.
- Each FAT cell points to a cluster, depending on an algorithm to
- convert FAT cell addresses to clusters. To read the file DOS goes
- along the FAT chain, reading each FAT cell, converting the FAT cell
- address into a cluster, and then reading that cluster.
-
-
- Erased files
- When DOS erases a file, it simply deletes the FAT chain by writing 0
- to all the cells of the file. Then DOS adds the character omega as the
- first character of the file name.
-
- Erased file SAMPLE.TXT old FAT chain
-
- FAT CELL1 FAT CELL2 FAT CELL3 FAT CELL4
- +---------------------------------------+
- | | | | |
- | 0 | 0 | 0 | 0 |
- | | | | |
- +---------------------------------------+
- Erased files directory entry
-
- file name attributes create date create time size starting cluster
- σAMPLE.TXT HIDDEN 01-01-1990 12:00:00 8723 1212
- |
- +> this is the omega character - when in the first character
- position of a files name, it means the file is deleted
-
- You can't see it or modify once this happens. A very important fact - the
- data in the disk sectors that were this file it still there. DOS does NOT
- remove the data when a file is deleted. It just erases it's FAT chain and
- changes the directory entry as shown above. It is then possible to UNERASE
- a deleted file! To unerase a file, you need to change the files directory
- entry, and rebuild the files FAT cell chain. Unfortunately, this isn't
- always so easy. Erased files may be recovered - we will build the routines
- and a file unerase program to do this in another issue. But think of this
- - how do we know which FAT cell is for which file if each erased files FAT
- cell is a zeroed out and not pointing to another cell!? Well, that's a
- story for another day...Hank Marquis
- END THE BASICS THE BASICS
- ADVANCED BASIC ADVANCED BASIC
- ADVANCED BASIC ADVANCED BASIC ADVANCED BASIC ADVANCED BASIC ADVANCED BASIC ADV
-
- This segment is dedicated to an in-depth study and application of an advanced
- programming topic. This month we are going to take a look at using interrupts
- in QuickBASIC to call low level DOS functions directly.
-
- Using interrupts offers all the power of DOS to your programs. With such
- power comes a responsibility - using the wrong interrupt or specifying
- invalid or wrong pointers can lose data or damage your hard disk! I always
- fully test ANY operation which writes to disk on a floppy BEFORE I try it
- on a hard disk and even then I always back up by hard disks first.
-
- What is a BASIC interrupt? Well, this function of BASIC lets us directly
- communicate with the CPU and all of its registers and flags. We are able
- to load registers with values, tell the CPU to execute to some DOS function
- and then read the results. This is a very powerful operation. You have
- access to every DOS system call. There is almost nothing that cant be done
- using these DOS system calls in part or whole.
-
- Read the segment in this months BASICS. It discusses the File Allocation
- Table and how DOS manages disks. Well, now lets use the BASIC CALL Interrupt
- function to actually read the BPB - BIOS Parameter Base - of a disk. As we
- are only going to read from the disk, this is a safe use of interrupts.
- For those souls seeking a higher level than I can provide here, I urge you
- to get a copy of this months book review - ADVANCED MSDOS: The Microsoft
- guide for Assembly language and C (there they go again...) programmers.
- Advanced MS-DOS covers all the DOS functions and how they are called. It
- also has passable explanations of the BPB and other interesting sections.
- First, just what is the BPB? The BPB resides just after the OEM name and
- version bytes, which in turn is just after the JUMP byte. The JUMP byte
- holds a command which tells the computer that the disk is formatted and
- points to a region of disk space located after the BPB. The BOOT Sector
- contains all the above.
-
- Item byte offset length
- into sector 0 in bytes
- +---------------------+
- | JUMP INSTRUCTION | 0 1
- |---------------------|
- | OEM NAME & VERSION | 4 8
- | |
-
- Start of BPB +---------------------+
- | bytes/sector | 12 2
- |---------------------|
- | sectors/cluster | 14 1
- |---------------------|
- | reserved sectors | 15 2
- |---------------------|
- | number of FATS | 17 1
- |---------------------|
- | max. root entries | 18 2
- |---------------------|
- | total sectors | 20 2
- |---------------------|
- | media descriptor | 22 1
- |---------------------|
- | sectors/FAT | 23 2
- |---------------------|
- | sectors/disk track | 25 2
- |---------------------|
- | number disk heads | 27 2
- |---------------------|
- End of BPB | hidden sectors | 29 2
- | |
-
- +---------------------+
- | bootstrap code |
-
-
- The boot Sector is logical sector 0. The boot sector holds much information
- but here we are only looking at the BPB. The BPB contains the information
- as the physical structure of the disk under use. It holds information
- to allow FAT calculations and hence read or write any file to the disk.
- We are now going to write a routine which will get the BPB from DOS, using
- the BASIC Call Interrupt routine and take a look into our own BPB.
- Following is a sub routine which reads the BPB, returning all the
- information it contains. By itself it isn't to much use - more interesting
- than useful. But as we go ahead in other issues, this sub routine becomes
- critical in building our unerase program!
-
- The call is :
-
- drive$ = "C"
- GetDOSBoot drive$, BPB, Regs, Status
- drive$ = the drive letter (A,B,C,D etc) we want to read
- BPB = the DOS Boot type array (DBType)
- Regs = the CPU register type array
- Status = a flag which, if not zero, indicates some sort of error
- occurred during the call.
-
- The code below will ONLY work under QuickBASIC. The PDS uses a different
- string memory management technique referred to as 'far strings'. Under a
- system using far strings all pointer contain to parts - a SEGMENT and an
- OFFSET. The segment points to a 64KB block of memory, the offset points to
- a distinct area within that segment. In the code below, the SADD function
- of QuickBASIC version 4.5 is used. It points to the offset only - QB4.5 does
- not use far strings - all strings are in one, known segment. To modify
- this program for use with the PDS you will need to also use the InteruptX
- routine, not Interrupt. Under InteruptX you will need to use the DS register
- as follows :
-
- DS = segment of string
- BX = offset of string
-
- After the program, we can display the results. A complete program is below.
- Here is the complete working program. Cut this out and load it into
- BASIC, then run it. You will need to load the quick library QB.QLB that
- came with QB. To load a quick library, start QuickBASIC as follows
-
- QB /L QB
-
- Use the Cut segment command from the main utilities menu to save this
- file to disk. Give it a name like DOSBOOT1.BAS so you can keep them
- straight each month. When you load this into BASIC, delete all of the
- text lines above. The program starts immediately below.
-
- 'start of program---------------------------------------------------------
- '
- '(C)Copyright 1990 Marquis Computing Inc. All rights reserved.
- 'You may use this program for anything or any purpose including inclusion
- 'into programs you write BUT you cannot sell this source code. Written by
- 'Hank Marquis. revised 9/8/90.
-
- DEFINT A-Z
-
- DECLARE SUB Interrupt (IntToCall, Regs AS ANY, Regs AS ANY)
- DECLARE SUB GetDOSBoot (drive$, BPB AS ANY, Regs AS ANY, Status%)
-
- 'To use the CALL Interrupt routine we first must build some TYPE arrays.
- 'QuickBASIC supports a Interrupt and InteruptX call, I have used the
- 'Interrupt here. The difference is that InteruptX gives access to all the
- 'registers and flags. We don't need them all, but we can build a type
- 'array with them anyway - always better to think of the future.
-
- 'Sometimes you will see reference to AX or AH or AL. These are the
- ' same register! What they mean are which byte of the 16 bit register
- ' we are talking about - AX is all sixteen bits, AH is the high order
- ' 8 bits and AL is the low order 8 bits. The same is true for BX, BH
- ' BL and the others.
- TYPE RegType
- 'This type defines all 80XXX type registers, pointers and
- ' flags. We are not going to use all of them in this
- ' program, but it doesn't hurt to be complete.
- AX AS INTEGER
- BX AS INTEGER
- CX AS INTEGER
- DX AS INTEGER
- BP AS INTEGER
- SI AS INTEGER
- DI AS INTEGER
- FLAGS AS INTEGER
- ES AS INTEGER
- DS AS INTEGER
- END TYPE
-
- 'Next we need to build a file info type array to hold all the DOS boot
- ' sector or BPB information that our sub routine will return.
-
- TYPE DBType
- 'Each item in the BPB will be returned using this type. The names of
- ' the type elements are accurate descriptions of the BPB data they
- ' return.
- OEM AS STRING * 8
- BytesPerSector AS INTEGER
- SectorsPerCluster AS INTEGER
- ReservedSectors AS INTEGER
- NumberOfFats AS INTEGER
- RootEntries AS INTEGER
- Sectors AS LONG
- Clusters AS LONG
- MediaType AS STRING * 45
- SectorsPerFAT AS INTEGER
- SectorsPerTrack AS INTEGER
- NumberOfHeads AS INTEGER
- NumberHiddenSectors AS INTEGER
- StartOFRoot AS INTEGER
- StartOFFAT AS LONG
- StartOfData AS INTEGER
- SizeOfDir AS INTEGER
- SizeOfFile AS DOUBLE
- END TYPE
-
- 'Now lets DIM the type arrays so that we can use them.
- DIM BPB AS DBType
- DIM Regs AS RegType
-
- 'this program reads the DOS boot sector or more accurately sector 0 of a
- 'drive and determines the disk file allocation system & capacities - this
- 'part of the drive is called the BIOS Parameter Block or BPB
-
- COLOR 7, 0
- CLS
- PRINT "Read & display the BIOS Parameter Base (boot sector)"
- PRINT "Select letter of drive to display [ABCDE etc]"
-
- drive$ = UCASE$(INPUT$(1)) 'pick a drive, any drive...
- GetDOSBoot drive$, BPB, Regs, Status 'Get the Boot Sector & parse
- ' it up
-
- IF Status THEN 'something bad happened
- END 'end program
- END IF
-
- 'This block of print commands is optional. It is put here to let you
- ' see the information returned by the GetDOSBoot sub routine.
-
- PRINT
- PRINT "Boot sector information for drive "; drive$; ":\"
- PRINT STRING$(60, "-")
- PRINT "DOS OEM label "; BPB.OEM
- PRINT "Bytes Per Sector "; BPB.BytesPerSector
- PRINT "Sectors/Cluster "; BPB.SectorsPerCluster
- PRINT "Clusters ";
- PRINT USING "##,###"; BPB.Clusters
- PRINT "Reserved Sectors "; BPB.ReservedSectors
- PRINT "Number FATS "; BPB.NumberOfFats
- PRINT "Root Dir Entries "; BPB.RootEntries; "(maximum)"
- PRINT "Sectors ";
- PRINT USING "##,###"; BPB.Sectors&
- PRINT "Media Descriptor "; BPB.MediaType
- PRINT "Sectors Per FAT "; BPB.SectorsPerFAT
- PRINT "Hidden Sectors "; BPB.NumberHiddenSectors
- PRINT "Start of FAT "; "sector #"; BPB.StartOFFAT
- PRINT "Start of data "; "sector #"; BPB.StartOfData
- PRINT "Drive capacity ";
- PRINT USING "###,###,###"; BPB.BytesPerSector * BPB.Sectors&;
- PRINT " (bytes)"
- PRINT "Sectors/track "; BPB.SectorsPerTrack
- PRINT "Drive heads "; BPB.NumberOfHeads
-
- END
-
- DEFINT A-Z
- SUB GetDOSBoot (drive$, BPB AS DBType, Regs AS RegType, Status)
-
- 'This sub reads the DOS BIOS Parameter Base (BPB) and determines
- ' the basic disk configuration of sectors, clusters, FATs, disk
- ' type, root entries, disk size etc..,
-
- '-Setup error trap-------------------------------------------------------
- Status = 1 'set error flag for error - guilty until proven innocent
-
- 'Convert drive into a number for DOS-------------------------------------
- drive$ = LEFT$(drive$, 1) 'drive$ should be A,B,C etc.,
- drive = ASC(UCASE$(drive$)) - 65 'change drive to a number where
- ' A = 1, B = 2 etc.,
-
- 'Set disk sector size----------------------------------------------------
- 'An IBM disk sector size is - 512 bytes. But what if we didn't know
- ' that? There is a DOS function call which can return the number of
- ' bytes per sector. It is Int &21H function &H36.
- '
- 'You use it as follows:
- '
- ' Regs.AX = &H3600 'function to call
- ' Regs.DX = Drive 'drive where 0=A, 1=B etc.,
- ' Interrupt &H21, Regs, Regs 'use DOS general interrupt &H21
- '
- 'This call returns the following information:
- '
- ' Regs.AX = sectors per cluster
- ' Regs.BX = number of available clusters
- ' Regs.CX = bytes per sector
- ' Regs.DX = cluster per drive
- '
- 'Now this information is also in it's raw form in the BPB, so we are
- ' going to use what we find in the BPB, even though this call could
- ' find out some of the same information. We are just using this call
- ' to get the number of bytes per sector.
- '-------------------------------------------------------------------
-
- Regs.AX = &H3600 'function &H36 - get free disk space
- Regs.DX = Drive + 1 'use drive entered + 1 because
- ' this function uses a slightly
- ' different syntax than INT &H25. &H36
- ' uses 0 for default, 1 for A etc.,
- ' &H25 uses 0 for A, 1 for B etc.,
-
- Interrupt &H21, Regs, Regs 'call &H21 - general DOS functions
-
- BytesInSector = Regs.CX 'Regs.CX now holds bytes per sector
-
- IF Regs.CX = 0 THEN EXIT SUB 'something bad happened so boogy...
-
- 'Now BytesInSector is the size of a sector. We will use this to make
- ' a string exactly one sector long to receive the BPB from the next
- ' DOS call we make.
-
- 'Read DOS Base Pointer (BPB)---------------------------------------------
-
- 'Most interrupts added in DOS 3.0 and up, though not all, use a null
- ' terminated string for these calls. We do that in BASIC by appending
- ' CHR$(0) to the end of the string, as shown below. This is referred to
- ' to as ASCIIZ
- '
- 'DOS interrupt &H25 is the DOS absolute read function. It will read
- ' as many sectors as you ask it for. We are going to read the whole
- ' boot sector - which is BytesInSector long - from above. We need to
- ' pre-fill a string to recieve the sector data first. That is why
- ' we needed to determine BytesInSector BEFORE calling this routine.
- '
- 'We call it with the following parameters under QB 4.5
- ' AX = drive to read (0 = A etc.,)
- ' BX = pointer to pre-filled ASCIIZ string to receive disk information
- ' CX = quantity of sectors to read
- ' DX = absolute disk sector number to start reading from to read
- '
- 'To use this routine under QBX you will need to make some changes. This
- ' is due to QBX use of far strings. You need to pass the segment as
- ' well the offset. You will need to use the SSEG() function.
- '
- ' DS = segment of string or Regs.DS = SSEG(string$)
- '
- '
- '----------------------------------------------------------------------
-
- DOSBOOT$ = SPACE$(BytesInSector) + CHR$(0) 'ASCIIZ to receive BPB data
- ' prefilled to correct
- ' length with spaces.
-
- Regs.AX = drive 'Set drive to get from
-
- Regs.BX = SADD(DOSBOOT$) 'set BX to point to location
- ' in memory of DOSBOOT$
-
- 'using SADD() above points to the memory location of the string. Here we
- ' are not actually working on the string itself - we are working on it
- ' indirectly using what is referred to as a "pointer"
-
- Regs.CX = 1 'read '1' sector
- Regs.DX = 0 'start reading at sector
- ' '0' boot sector
- Interrupt &H25, Regs, Regs 'call DOS-absolute read
-
- IF DOSBOOT$ = SPACE$(BytesInSector) + CHR$(0) THEN EXIT SUB
- 'the above line is true if there was an error - so if there was
- ' an error lets boogy... Note that Status was preset to indicate
- ' an error - so the calling program knows that this call was
- ' unsuccessful by default.
-
- 'If we got here then DOSBOOT$ now holds the DOS boot record!
-
- 'Parse BPB info------------------------------------------------------------
-
- 'If we got here, then we had a successful read. Lets begin to
- ' parse out the boot record information contained in the string DOSBOOT$
- ' which holds a copy of the boot sector. We will use the MID$ function of
- ' BASIC to retrieve sub strings out of the Boot record strings DOSBOOT$.
- ' As follows :
- '
- ' SubString = MID$(DOSBOOT$, Offset into boot sector, Bytes to fetch)
- '
- 'In most DOS applications numbers are converted into characters, then
- ' written to disk. This is called Binary coded decimal. For example
- ' dBASE does this also. To convert such a character string back into a
- ' number, first read the string. Then break it into sub strings of
- ' one character each. Then, depending on which character is which,
- ' convert into a number as follows. Leftmost character first, times
- ' decreasing powers of 16, as shown below:
- '
- ' stringtoconvert$ = "ABCD"
- '
- ' byte1$ = MID$(stringtoconvert$, 1, 1)
- ' byte2$ = MID$(stringtoconvert$, 2, 1)
- ' byte3$ = MID$(stringtoconvert$, 3, 1)
- ' byte4$ = MID$(stringtoconvert$, 4, 1)
- '
- ' byte4 = ASCII value of byte4$ * 4,294,967,296
- ' or
- ' byte4& = ASC(byte4$) * 4,294,967,296
- '
- ' byte3 = ASCII value of byte3$ * 65,536
- ' byte2 = ASCII value of byte2$ * 256
- ' byte1 = ASCII value of byte1$ * 1
- '
- ' then add them up:
- '
- ' value& = byte4& + byte3& + byte2& + byte1&
- '
- '---------------------------------------------------------------
-
- 'Here we start actually reading out the values of the BPB into
- ' the BPB type array
-
- 'get oem info...
- BPB.OEM = MID$(DOSBOOT$, 4, 8)
-
- 'figure bytes/sector.
- Work$ = MID$(DOSBOOT$, 12, 2) 'get sub string
- Byte1& = ASC(LEFT$(Work$, 1)) 'convert each character into ASCII
- Byte2& = ASC(RIGHT$(Work$, 1)) ' " " " " "
- BPB.BytesPerSector = (Byte2& * 256) + Byte1& 'convert into number
-
- 'figure sectors/fat cell (allocation unit)
- Work$ = MID$(DOSBOOT$, 14, 1)
- Byte1& = ASC(LEFT$(Work$, 1))
- BPB.SectorsPerCluster = Byte1&
-
- 'figure reserved sectors
- Work$ = MID$(DOSBOOT$, 15, 2)
- Byte1& = ASC(LEFT$(Work$, 1))
- Byte2& = ASC(RIGHT$(Work$, 1))
- BPB.ReservedSectors = (Byte2& * 256) + Byte1& - 1
- 'the above subtraction of 1 from the last line is an offset for a DOS
- ' oddity because BPB.ReservedSectors also shows the boot sector itself
-
- 'figure number of fats
- Work$ = MID$(DOSBOOT$, 17, 1)
- Byte1& = ASC(LEFT$(Work$, 1))
- BPB.NumberOfFats = Byte1&
-
- 'figure maximum root directory entries
- Work$ = MID$(DOSBOOT$, 18, 2)
- Byte1& = ASC(LEFT$(Work$, 1))
- Byte2& = ASC(RIGHT$(Work$, 1))
- BPB.RootEntries = (Byte2& * 256) + Byte1&
- 'figure number of sectors
- Work$ = MID$(DOSBOOT$, 20, 2)
- Byte1& = ASC(LEFT$(Work$, 1))
- Byte2& = ASC(RIGHT$(Work$, 1))
- BPB.Sectors& = (Byte2& * 256) + Byte1&
- 'figure media type where it is stored in HEX format, so first we
- ' read out its bytes, convert to a number, then into HEX
- Work$ = MID$(DOSBOOT$, 22, 1)
- MediaType$ = HEX$(ASC(LEFT$(Work$, 1)))
- Base$ = "[" + HEX$(ASC(LEFT$(Work$, 1))) + "H]"
-
- SELECT CASE MediaType$
- CASE "F8"
- MediaDesc$ = " hard disk drive"
- CASE ELSE
- MediaDesc$ = " floppy disk drive"
- END SELECT
-
- BPB.MediaType = Base$ + MediaDesc$
-
- 'figure number of sectors/fat
- Work$ = MID$(DOSBOOT$, 23, 2)
- Byte1& = ASC(LEFT$(Work$, 1))
- Byte2& = ASC(RIGHT$(Work$, 1))
- BPB.SectorsPerFAT = (Byte2& * 256) + Byte1&
-
- 'figure clusters
- BPB.Clusters = BPB.Sectors& \ BPB.SectorsPerCluster
-
- 'figure sectors/disk track
- Work$ = MID$(DOSBOOT$, 25, 2)
- Byte1& = ASC(LEFT$(Work$, 1))
- Byte2& = ASC(RIGHT$(Work$, 1))
- BPB.SectorsPerTrack = (Byte2& * 256) + Byte1&
-
- 'figure number of disk heads
- Work$ = MID$(DOSBOOT$, 27, 2)
- Byte1& = ASC(LEFT$(Work$, 1))
- Byte2& = ASC(RIGHT$(Work$, 1))
- BPB.NumberOfHeads = (Byte2& * 256) + Byte1&
-
- 'figure number (if any) of hidden sectors
- Work$ = MID$(DOSBOOT$, 29, 2)
- Byte1& = ASC(LEFT$(Work$, 1))
- Byte2& = ASC(RIGHT$(Work$, 1))
- BPB.NumberHiddenSectors = (Byte2& * 256) + Byte1&
-
- '------------------get FAT start address-------------------
- 'the FAT starts right after (boot_sector + reserved_sectors)
- 'so start of FAT is...0 + BPB.ReservedSectors + 1
- BPB.StartOFFAT = BPB.ReservedSectors + 1 '+1 to offset -1 from DOS
- ' reserved sectors count
-
- '------------------get root start address-------------------
- 'After the boot sector and the FAT comes the root directory
- 'Add 1 to account for the boot sector itself
- BPB.StartOFRoot = 1 + (BPB.NumberOfFats * BPB.SectorsPerFAT)
- '------------------get disk data start address-------------------
- 'After the boot sector, the FAT and the root directory, starts
- 'the actual disk space that we can use...
-
- BootSector = 1
- StartOfData = BootSector + (BPB.NumberOfFats * BPB.SectorsPerFAT)
- StartOfData = StartOfData + ((BPB.RootEntries * 32) \ BPB.BytesPerSector)
-
- BPB.StartOfData = StartOfData
-
- '------------------end of routine -------------------
- 'if we got here then we had no errors so set error flag Status
- ' to show no errors. The Type array BPB now holds all the DOS
- ' boot sector information for display
- '
- Status = 0 'we made with no errors, set error status to 0 or none
-
- END SUB
-
- END ADVANCED BASIC ADVANCED BASIC
- THE BOOK OF THE MONTH THE BOOK OF THE MONTH
- THE BOOK OF THE MONTH THE BOOK OF THE MONTH THE BOOK OF THE MONTH THE BOOK OF
-
- In this segment we review a book that has to do with programing. For November
- the book reviewed is ADVANCED MSDOS The Microsoft guide for Assembly Language
- and C programmers.
-
- Author : Ray Duncan
- Publisher : Microsoft Press
- Dated : 1986
- Cost : $22.95
- Available : this copy bought at Software Etc.,
-
- I needed to understand how DOS actually operates to write a program utility.
- I write in BASIC PDS because I like the flexibility, so I went forth into
- the book world seeking an in-depth DOS book featuring BASIC. And guess what?
- It seems that no such book exists. In desperation and despondence I bought
- Advanced MS DOS, hoping to glean enough about DOS to piece together my own
- code.
-
- Well, I was successful. The book is well endowed with examples, as the name
- would suggest though, they are almost all in assembly language. Luckily
- for me these examples are fully commented and explained in the text.
- Once upon a time when dinosaurs called the 6502 roamed the earth, and was
- king of it, I had played with assembler. But I am by no means expert in it.
- What I got was a good introduction to DOS and assembly language. Now this
- isn't bad, but it is not what I wanted.
- The author constantly refers to CP/M, as if we all already knew all about
- CP/M and wrote in it everyday. I guess in fairness the book was written
- for assembly language programers. Many of whom I guess used to write in
- CP/M. (Control Program Microcode, the predecessor of DOS written by Digital
- Research, the same folks who give us GEM Desktop and other programs.)
- Unfortunately, I never did write in CP/M so all of the examples relating to
- CP/M were useless to me! What I did learn from this study was that DOS is
- quite a bit a kludge of past systems. Operations and functions are often
- carried out in several ways to make them CP/M 'ish or "compatible" for
- re-writing code. I guess I now know what they say DOS is showing it's age.
- For the stout of heart and those interested in the evolution of DOS this is
- good reading.
-
- But to get back to my application for a minute. The books does explain
- well such concepts as directory entries, file management and memory
- management. Although I found myself reading and re-reading the "simplified"
- block structure of several functions. The real benefit to this book to me
- though is its complete indexing of all DOS functions, with examples and
- explanations. I found this to be of most value. Often, the examples in the
- book under each heading, while written for assembly language, are directly
- useable in BASIC using the Call Interrupt routine. At last, I had found my
- answer - I just couldn't figure it out without reading it 20 times!
- But, I did ultimately figure it out, and that's the point.
-
- In any event, the books is easier to read, more informative and holds more
- examples than the DOS Technical Reference from IBM. In summary, I found the
- book hard to read, it assumed the reader knew too much. The examples often
- said things like ..."use the directory entry to find..." without
- indicating how one might go about finding the directory entry and then
- processing it. This is where the reading of each section 20 times comes in.
-
- I did find the book interesting with is genealogy of DOS and very thorough
- explanation of exactly how DOS does anything it does. Also, as stated
- earlier, the complete indexing of functions and DOS calls in invaluable.
- As a side note, going through the amply commented examples given in assembly
- language you get a good understanding of how assembly language operates.
- And that is a valuable thing in itself.
-
- I recommend this book to anyone who writes in BASIC and is trying to get
- the most from DOS. A must for any programming using the CALL Interrupt or
- InterruptX routines or a BASIC programmer writing your own DOS functions.
-
- Hank Marquis
- END THE BOOK OF THE MONTH THE BOOK OF THE MONTH
- SOFTWARE OF THE MONTH SOFTWARE OF THE MONTH
- SOFTWARE OF THE MONTH SOFTWARE OF THE MONTH SOFTWARE OF THE MONTH SOFTWARE
-
- In this segment, we review a software program, utility or add-on for BASIC.
- This month, we take a look at the A.J.S. Publishing package db/LIB. An add
- on library for letting BASIC programmers read, write, modify, create and
- use dBASE III and dBASE III+ style databases and indexes.
-
- Publisher : A.J.S. Publishing
- Version tested : 2.0
- Dated : 1990
- Cost : $145.00 ($495.00 LAN version)
- Available : Contact A.J.S. publishing at:
- A.J.S. Publishing, Inc.
- P.O. Box 83220
- Los Angeles, CA 90083
- phone : 213.215.9145
-
- What is it? db/LIB is a set of routines which you include into programs
- you write. These sub routines let you do any virtually any database
- management function. You can read and write dBASE files, create dBASE files
- and indexes and an entire host of other database file and index functions.
-
- These routines are all written in assembly language and they are fast. I
- compared a program written using dBASE and one written in BASIC. On every
- account, the BASIC program was faster than dBASE itself! db/LIB indexed
- faster, searched faster and also resulted in a smaller program.
-
- Included with the package are 12 already written BASIC programs. Each
- amply demonstrating some needed database function. You are free to 'cut and
- paste' from these demos into your own applications. That is a real time
- saver. Also built into db/LIB is a full featured expression evaluator.
- Expressions make selecting data from a database easier. You build a string
- using BASIC syntax to select a record or record. For example, "EQUIPMENT
- .EQ. 'CODEX'" Then pass this to the expression evaluator. A routine then
- creates a macro, which you use on subsequent calls to check against each
- record. If the record matches the macro, the expression evaluator returns
- a "T", if is does not, it returns a "F". And it too is really fast. I had
- never used one before, and now I wonder how I ever got by without one.
-
- The book offers decent information, although at times the examples and
- lacking in depth and the writing is a bit 'high'. That is you read the
- calls two or three times until you understand it. My biggest complaint
- is not with the program - it is with the book. I used db/LIB for a
- recent database project (written, by the way, in QuickBASIC 4.5). As
- I was using the book everyday, it began to fall apart. I spoke with a
- printer friend who told me that the type of binding used on the db/LIB
- book was prone to falling apart. So far I haven't lost any pages, but
- the book is in tatters.
-
- The technical support from A.J.S. is fine. I never was put on hold, although
- once in a while I had to re-dial several times. But I always got though
- and got clear answers to my questions.
-
- From a technical note, I tried using db/LIB for another project. This one
- in the PDS. It turns out that db/LIB is not compatible with the PDS use of
- far strings. A.J.S. says they are working on an upgrade. That's not too bad
- as many add-on library vendors have to upgrade their product for far
- strings. Relatedly, I then tried using db/LIB with QB.45, to build
- a custom runtime module. The application had about 12 executable modules,
- each needing part or all of the A.J.S. library. A perfect application for
- a custom BASIC runtime module if I ever saw one. After the module was
- compiled, the application seized up every time. During an investigation I
- found out the assembly language calls (which is what db/LIB is written in)
- must be true FAR CALLS. One phone call to A.J.S. cleared it up. The db/LIB
- current package does not use FAR CALLS. The package for the PDS will. So I
- guess if you want to use a custom runtime module, you won't be using db/LIB.
- In summary, db/LIB lets BASIC programmers access the world of real database
- management. It is a no nonsense, fast, efficient and economical package.
- It's bevy of pre-written programs, each well written and commented are a
- real plus. The expression evaluator is a real gift - once you realize the
- power it offers, you find yourself using everywhere. If you are a BASIC
- programmer, writing in QuickBASIC and need to read/write dBASE files then
- db/LIB is for you. If you are writing programs and want to use a file
- structure that is an industry standard, then db/LIB is the choice. Even
- though I am sitting on the edge of my seat waiting for the far strings
- upgrade, I still highly recommend db/LIB from A.J.S. Publishing.
-
-
- END SOFTWARE OF THE MONTH SOFTWARE OF THE MONTH
-