$,...
&                        PERSONPHONE$
    RETURN SUBROUTINE
END

SUBROUTINE PAD(PAD$)
    FOR PADINDEX=LEN(PAD$)+1 TO MAXLEN(PAD$) DO PAD$[PADINDEX]=0
    LET LEN(PAD$)=MAXLEN(PAD$)
    RETURN SUBROUTINE
END

SUBROUTINE WRITEPERSONRECORD
    PAD(PERSONNAME$)
    PAD(PERSONTITLE$)
    PAD(PERSONCOMPANY$)
    PAD(PERSONSTREET$)
    PAD(PERSONCITY$)
    PAD(PERSONSTATECOUNTRY$)
    PAD(PERSONZIP$)
    PAD(PERSONPHONE$)
    WRITE #1@PERSONRECORD,PERSONNAMESAMELINK,PERSONCOMPANYSAMELINK,...
&                        PERSONNAME$,PERSONTITLE$,...
&                        PERSONCOMPANY$,PERSONSTREET$,...
&                        PERSONCITY$,PERSONSTATECOUNTRY$,PERSONZIP$,...
&                        PERSONPHONE$
    RETURN SUBROUTINE
END

.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 41
SUBROUTINE PRINTPERSONRECORD(WHERE)
    PRINT #WHERE,PERSONNAME$
    PRINT #WHERE,PERSONTITLE$
    PRINT #WHERE,PERSONCOMPANY$
    PRINT #WHERE,PERSONSTREET$
    PRINT #WHERE,PERSONCITY$
    PRINT #WHERE,PERSONSTATECOUNTRY$
    PRINT #WHERE,PERSONZIP$
    PRINT #WHERE,PERSONPHONE$
    PRINT #WHERE
    RETURN SUBROUTINE
END

DEF FINDANDDISPLAYPERSON
    REM THIS FUNCTION RETURNS FALSE IF "PERSONKEY$" CANNOT BE FOUND
    REM ELSE RETURNS TRUE AFTER DISPLAYING RECORD ABOUT PERSON
    IF ERROR WHEN
       PERSONRECORD=KEY(1,1,PERSONKEY$)
    THEN IF ERR=1075 THEN NOSUCHPERSON ELSE ERROR
    PRINT CLEARSCREEN$
    READPERSONRECORD
    PRINTPERSONRECORD(0)
    RETURN TRUE

NOSUCHPERSON: REM CAN'T FIND THE PERSON DESIRED, TRY KEYNEXT
    PERSONNAMESAMELINK=0
NEXTPERSON: REM TRY FOR NEXT PERSON
    IF PERSONNAMESAMELINK<>0
    THEN
        REM MORE THAN ONE GUY WITH THE SAME NAME
        PERSONRECORD=PERSONNAMESAMELINK
        GOTO DISPLAYNEXTPERSON
    FI
    IF ERROR WHEN
        PERSONRECORD=KEYNEXT(1,1,PERSONKEY$)
    THEN IF ERR=1001
         THEN
             PRINT "CAN'T FIND PERSON SELECTED."
             PERSONRECORD=0\COMMAND$=""\RETURN FALSE
         ELSE ERROR

.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 51
DISPLAYNEXTPERSON:
    PRINT CLEARSCREEN$;"PERHAPS YOU MEANT: "
    PRINT
    READPERSONRECORD
    PRINTPERSONRECORD(0)
    INPUT 'ENTER "YES" OR "NO", <CR> MEANS "NEXT" ' COMMAND$
    IF COMMAND$="" THEN NEXTPERSON
    ELSEIF UPPERCASE$(COMMAND$)="YES" THEN COMMAND$=""\RETURN TRUE
    ELSEIF UPPERCASE$(COMMAND$)="NO"
        THEN
            COMMAND$=""
            PERSONRECORD=0
            RETURN FALSE
    ELSE PERSONRECORD=0\RETURN FALSE
END

SUBROUTINE ADDRECORD
    REM THIS SUBROUTINE ADDS A PERSON RECORD TO THE DATABASE
    REM BY INSERTING BOTH PERSONNAME$ AND PERSONCOMPANY$ AS KEYS
    REM IN KEY INDEXES 1 AND 2, RESPECTIVELY.
    REM IF A KEY ALREADY EXISTS, THE RECORD IS SIMPLY ADDED TO A CHAIN
    REM OF RECORDS THAT HAVE IDENTICAL KEYS. THIS WAY
    REM ALL PEOPLE IN THE SAME COMPANY ARE EASILY FOUND, AS
    REM ARE ALL PEOPLE WITH THE SAME NAME.
    PERSONNAMESAMELINK=0 \ REM ASSUME NO OTHER IDENTICAL NAMES
    PERSONCOMPANYSAMELINK=0 \ REM ASSUME NO OTHER IDENTICAL COMPANIES
    LET PERSONRECORD=GETSPACE(1,221)
    REM ADD PERSON TO NAME INDEX
    IF ERROR WHEN
        KEYINSERT(1,1,PERSONNAME$,PERSONRECORD)
    THEN
        REM THAT NAME ALREADY EXISTS, PLACE PERSON RECORD ON CHAIN
        IF ERR=1076
        THEN PERSONNAMESAMELINK=...
&            KEYREPLACE(1,1,PERSONNAME$,PERSONRECORD)
        ELSE ERROR
    FI
    REM ADD PERSON TO COMPANY INDEX
    IF ERROR WHEN
        KEYINSERT(1,2,PERSONCOMPANY$,PERSONRECORD)
    THEN
        REM THAT COMPANY ALREADY EXISTS, PLACE PERSON RECORD ON CHAIN
        IF ERR=1076
        THEN PERSONCOMPANYSAMELINK=...
&            KEYREPLACE(1,2,PERSONCOMPANY$,PERSONRECORD)
        ELSE ERROR
    FI
    WRITEPERSONRECORD
    RETURN SUBROUTINE
END

.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 52
SUBROUTINE DELETERECORD
    REM DELETE THE FOUND RECORD
    REM THIS UNDOES WHAT ADDRECORD DOES.
    REM THIS MAY REQUIRE SIMPLE REMOVAL FROM A CHAIN
    REM IF THE CHAIN GETS EMPTY, THE KEY MUST BE DELETED!
    REM DELETE FROM NAME KEY CHAIN FIRST
    PERSONPREVIOUS=KEY(1,1,PERSONNAME$)\! ERROR CANNOT OCCUR HERE
    IF PERSONPREVIOUS=PERSONRECORD
    THEN
        REM THIS RECORD IS THE FIRST RECORD ON A NAME CHAIN
        IF PERSONNAMESAMELINK=0
        THEN
            REM THIS RECORD IS ONLY RECORD WITH THIS PERSON NAME
            KEYDELETE(1,1,PERSONNAME$) \! POOF GOES THE NAME KEY
        ELSE
            REM THERE ARE OTHER RECORDS WITH THE SAME NAME
            REM REPLACE CHAIN HEAD WITH POINTER TO REST OF CHAIN
            PERSONRECORD=...
&               KEYREPLACE(1,1,PERSONNAME$,PERSONNAMESAMELINK)
        FI
    ELSE
        REM THIS RECORD IS SOMEWHERE ON A CHAIN...
        REM OF RECORDS WITH SAME NAME
FINDPREVIOUSPERSON: REPEAT
                        READ #1@PERSONPREVIOUS,PERSONNEXT
                        IF PERSONNEXT=PERSONRECORD
                        THEN EXIT FINDPREVIOUSPERSON
                        PERSONPREVIOUS=PERSONNEXT
                    END
         REM FOUND RECORD IN CHAIN WHOSE "NEXT" POINTER...
         REM SELECTS RECORD TO BE DELETED
         REM REMOVE THIS RECORD FROM THE CHAIN
         WRITE #1@PERSONPREVIOUS,PERSONNAMESAMELINK
    FI
    REM NOW DELETE FROM COMPANY KEY CHAIN
    COMPANYPREVIOUS=KEY(1,2,PERSONCOMPANY$)\! NO ERROR POSSIBLE
    IF COMPANYPREVIOUS=PERSONRECORD
    THEN
        REM THIS RECORD IS THE FIRST RECORD ON A COMPANY CHAIN
        IF PERSONCOMPANYSAMELINK=0
        THEN
            REM THIS RECORD IS THE ONLY RECORD WITH THIS COMPANY NAME
            KEYDELETE(1,2,PERSONCOMPANY$) \! POOF GOES THE NAME KEY
        ELSE
            REM THERE ARE OTHER RECORDS WITH THE SAME COMPANY NAME
            REM REPLACE CHAIN HEAD WITH POINTER TO REST OF CHAIN
            PERSONRECORD=...
&               KEYREPLACE(1,2,PERSONCOMPANY$,PERSONCOMPANYSAMELINK)
        FI
    ELSE
        REM THIS RECORD IS SOMEWHERE ON A CHAIN...
        REM OF RECORDS OF SAME COMPANY
.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 32
FINDPREVIOUSCOMPANY: REPEAT
                        READ #1@COMPANYPREVIOUS,PERSONNEXT,COMPANYNEXT
                        IF COMPANYNEXT=PERSONRECORD
                        THEN EXIT FINDPREVIOUSCOMPANY
                        COMPANYPREVIOUS=COMPANYNEXT
                    END
         REM FOUND RECORD IN CHAIN WHOSE "NEXT" POINTER...
         REM SELECTS RECORD TO BE DELETED
         REM REMOVE THIS RECORD FROM THE CHAIN
         WRITE #1@COMPANYPREVIOUS,PERSONNEXT,PERSONCOMPANYSAMELINK
    FI
    RETURN SUBROUTINE
END

SUBROUTINE MODIFY(MODIFYTITLE$,MODIFYTARGET$)
    PRINT MODIFYTITLE$;MODIFYTARGET$;
    FOR MODIFYCOUNT=1 TO LEN(MODIFYTARGET$)...
&       UNTIL MODIFYTARGET$[MODIFYCOUNT]=0 DO PRINT BACKSPACE$;
    INPUT '' COMMAND$
    IF COMMAND$="" THEN RETURN SUBROUTINE
    MODIFYTARGET$=UPPERCASE$(COMMAND$)
    RETURN SUBROUTINE
END

SUBROUTINE TRUNCATEBLANKS(STRINGTOBETRUNCATED$)
    FOR STRINGTOBETRUNCATEDINDEX=LEN(STRINGTOBETRUNCATED$) TO 1 STEP -1 ...
&       UNTIL STRINGTOBETRUNCATED$(STRINGTOBETRUNCATEDINDEX)<>:20
    NEXT STRINGTOBETRUNCATEDINDEX
    LET LEN(STRINGTOBETRUNCATED$)=STRINGTOBETRUNCATEDINDEX
    RETURN SUBROUTINE
END

.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 52
!****************************************
!         BEGIN MAIN PROGRAM               
!****************************************

BEGIN: PRINT "PHONEBOOK V1.0 (C) 1981 SOFTWARE DYNAMICS"
    LET COMMAND$="PHONEBOOK.DATA"
OPENFILE:
    IF ERROR WHEN
        OPEN #1,COMMAND$
    THEN
        IF ERR=1011
        THEN
            PRINT "CAN'T FIND ";COMMAND$
            PRINT "ENTER NAME OF PHONEBOOK FILE,"
            PRINT 'ENTER THE WORD "CREATE" TO CREATE ';COMMAND$
            INPUT "OR ENTER <CR> TO EXIT: " PERSONNAME$
            IF PERSONNAME$="" THEN EXIT
            ELSEIF UPPERCASE$(PERSONNAME$)="CREATE"
            THEN
                CREATE #1,COMMAND$
                KEYINIT(1,1,25,9) \ REM INITIALIZE "PERSON" INDEX
                KEYINIT(1,2,20,9) \ REM INITIALIZE "COMPANY" INDEX
            ELSE COMMAND$=PERSONNAME$\GOTO OPENFILE
        ELSE ERROR
    FI
PRINTMENU:
    PRINT CLEARSCREEN$;"COMMANDS: "
    PRINT "DUMP <FILE> -- DUMPS ENTIRE DATA BASE TO <FILE>"
    PRINT "LOAD <FILE> -- LOADS (OR ADDS) TO DATA BASE FROM <FILE>"
    PRINT "FIND <PERSON> -- FIND A PARTICULAR PERSON"
    PRINT "NEXT -- FIND NEXT PERSON"
    PRINT "COMPANY <COMPANYNAME> -- LOCATE A COMPANY"
    PRINT "NPIC -- FIND NEXT PERSON IN SAME COMPANY"
    PRINT "FIX <PERSON> -- CHANGE INFORMATION ABOUT A PERSON"
    PRINT "ADD <PERSON> -- ADD A PERSON TO THE PHONEBOOK"
    PRINT "DELETE <PERSON> -- DELETE A PERSON FROM THE PHONEBOOK"
    PRINT "EXIT -- LEAVE THIS PROGRAM"
    PRINT "HELP -- PRINTS THIS MENU"
    PRINT "<OTHER> -- IMPLIED FIND ON <OTHER>"
ASKCOMMAND:
    INPUT "OK> " COMMAND$
INSPECTCOMMAND:
    IF LEN(COMMAND$)=0 THEN ASKCOMMAND
    LET COMMAND$=UPPERCASE$(COMMAND$)
    IF FIND(COMMAND$,"DUMP ")=1 THEN DUMP
    IF FIND(COMMAND$,"LOAD ")=1 THEN LOAD
    IF FIND(COMMAND$,"FIND ")=1
    THEN PERSONKEY$=RIGHT$(COMMAND$,6)\GOTO FIND1
    IF COMMAND$="NEXT" THEN FINDNEXTPERSON
    IF FIND(COMMAND$,"COMPANY ")=1 THEN COMPANY
    IF COMMAND$="NPIC" THEN NPIC
    IF FIND(COMMAND$,"FIX ")=1 THEN FIX
.ll 65
.pw 85
.im 2
    IF FIND(COMMAND$,"ADD ")=1 THEN ADD
    IF FIND(COMMAND$,"DELETE ")=1 THEN DELETE1
.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 52
    IF COMMAND$="EXIT" THEN EXIT
    IF COMMAND$="HELP" THEN PRINTMENU
OTHER: REM TRY TO FIND THE PERSON
    LET PERSONKEY$=COMMAND$
FIND1:
    IF FINDANDDISPLAYPERSON THEN ASKCOMMAND ELSE INSPECTCOMMAND

DELETE1:
    LET PERSONKEY$=RIGHT$(COMMAND$,8)
    IF NOT FINDANDDISPLAYPERSON THEN INSPECTCOMMAND
    DELETERECORD
    GOTO ASKCOMMAND

ADD: REM ADD A NEW PERSON
    LET PERSONNAME$=RIGHT$(COMMAND$,5)
    IF ERROR WHEN
       PERSONRECORD=KEY(1,1,PERSONNAME$)
    THEN
        REM THAT NAME ALREADY EXISTS!
        IF ERR=1076
        THEN
            READPERSONRECORD
            PRINT "THAT NAME IS A DUPLICATE OF: "
            PRINTPERSONRECORD(0)
            INPUT 'ENTER COMMAND (<CR> MEANS "ADD ANYWAY")' COMMAND$
            IF COMMAND$<>"" THEN INSPECTCOMMAND
       FI
    FI
    INPUT "TITLE:         " PERSONTITLE$
    INPUT "COMPANY:       " PERSONCOMPANY$
    INPUT "STREET/SUITE:  " PERSONSTREET$
    INPUT "CITY:          " PERSONCITY$
    INPUT "STATE/COUNTRY: " PERSONSTATECOUNTRY$
    INPUT "ZIP:           " PERSONZIP$
    INPUT "PHONE NUMBER:  " PERSONPHONE$
    LET PERSONCOMPANY$=UPPERCASE$(PERSONCOMPANY$)
    ADDRECORD
    GOTO ASKCOMMAND

FIX: LET PERSONKEY$=RIGHT$(COMMAND$,5)
    IF NOT FINDANDDISPLAYPERSON THEN INSPECTCOMMAND
    PRINT CLEARSCREEN$;
    DELETERECORD
    PRINT "TYPE <CR> TO LEAVE OLD VALUE ALONE"
    MODIFY("NAME:          ",PERSONNAME$)
    MODIFY("TITLE:         ",PERSONTITLE$)
    MODIFY("COMPANY:       ",PERSONCOMPANY$)
    MODIFY("STREET/SUITE:  ",PERSONSTREET$)
    MODIFY("CITY:          ",PERSONCITY$)
    MODIFY("STATE/COUNTRY: ",PERSONSTATECOUNTRY$)
    MODIFY("ZIP:           ",PERSONZIP$)
    MODIFY("PHONE NUMBER:  ",PERSONPHONE$)
.ll 65
.pw 85
.im 2
    ADDRECORD
    GOTO ASKCOMMAND
.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 51
FINDNEXTPERSON:
    IF PERSONRECORD=0
    THEN PRINT "NOBODY SELECTED, CAN'T"\GOTO ASKCOMMAND
    IF PERSONNAMESAMELINK<>0
    THEN
        REM MORE THAN ONE GUY WITH SAME NAME
        PERSONRECORD=PERSONNAMESAMELINK
    ELSE
        IF ERROR WHEN
           PERSONRECORD=KEYNEXT(1,1,PERSONKEY$)
        THEN IF ERR=1001
             THEN PERSONRECORD=0\PRINT "CAN'T"\GOTO ASKCOMMAND
             ELSE ERROR
    FI
    PRINT CLEARSCREEN$;"PERHAPS YOU MEANT: "
    PRINT
    READPERSONRECORD
    PRINTPERSONRECORD(0)
    INPUT 'ENTER "YES", "NO", <CR> FOR "NEXT" OR COMMAND: ' COMMAND$
    IF LEN(COMMAND$)=0 THEN FINDNEXTPERSON
    ELSEIF UPPERCASE$(COMMAND$)="YES" THEN ASKCOMMAND
    ELSEIF UPPERCASE$(COMMAND$)="NO" THEN ASKCOMMAND
    ELSE INSPECTCOMMAND

NPIC:
    REM FIND NEXT PERSON WITHIN COMPANY
    IF PERSONRECORD=0
    THEN PRINT "NO COMPANY SELECTED"\GOTO ASKCOMMAND
    IF PERSONCOMPANYSAMELINK<>0
    THEN
        REM MORE THAN ONE GUY AT SAME COMPANY
        PERSONRECORD=PERSONCOMPANYSAMELINK
        GOTO COMPANYDISPLAY
    ELSE PRINT "NO MORE PEOPLE THERE..."\GOTO ASKCOMMAND

COMPANY:
    LET PERSONKEY$=RIGHT$(COMMAND$,9)
    IF ERROR WHEN
       PERSONRECORD=KEY(1,2,PERSONKEY$)
    THEN IF ERR=1075 THEN NOSUCHCOMPANY ELSE ERROR
COMPANYDISPLAY: PRINT CLEARSCREEN$; "PERHAPS YOU MEANT: "
    PRINT
    PERSONKEY$=PERSONNAME$ \ REM IN CASE "NEXT" IS INVOKED
    READPERSONRECORD
    PRINTPERSONRECORD(0)
    INPUT 'ENTER "YES","NO",<CR> FOR "NPIC" OR COMMAND: ' COMMAND$
    IF LEN(COMMAND$)=0 THEN NPIC
    ELSEIF UPPERCASE$(COMMAND$)="YES" THEN ASKCOMMAND
    ELSEIF UPPERCASE$(COMMAND$)="NO" THEN ASKCOMMAND
    ELSE INSPECTCOMMAND

.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 48
NOSUCHCOMPANY: REM CAN'T FIND THE COMPANY DESIRED, TRY KEYNEXT
    PRINT CLEARSCREEN$;"CAN'T FIND COMPANY: ";PERSONKEY$
NEXTCOMPANY: REM TRY FOR NEXT COMPANY
    IF ERROR WHEN
        PERSONRECORD=KEYNEXT(1,2,PERSONKEY$)
    THEN IF ERR=1001
         THEN
             PRINT "CAN'T FIND SELECTED COMPANY."
             PERSONRECORD=0\GOTO ASKCOMMAND
         ELSE ERROR
    READPERSONRECORD
    PRINT "PERHAPS YOU MEANT: ";PERSONCOMPANY$
    INPUT 'ENTER "YES" OR "NO";<CR> MEANS "NEXT" ' COMMAND$
    IF LEN(COMMAND$)=0 THEN NEXTCOMPANY
    ELSEIF UPPERCASE$(COMMAND$)="YES" THEN COMPANYDISPLAY
    ELSEIF UPPERCASE$(COMMAND$)="NO"
        THEN
            PERSONRECORD=0
            GOTO ASKCOMMAND
    ELSE PERSONRECORD=0\GOTO INSPECTCOMMAND

LOAD: REM LOAD CONTENTS OF SEQUENTIAL FILE INTO PHONEBOOK
    LET COMMAND$=RIGHT$(COMMAND$,6)
    OPEN #2,COMMAND$
    PRINT "LOADING ";COMMAND$
LOADLOOP:
    INPUT #2,PERSONNAME$
    IF EOF(2) THEN CLOSE #2\GOTO ASKCOMMAND
    IF PERSONNAME$="" THEN LOADLOOP
    TRUNCATEBLANKS(PERSONNAME$)
    INPUT #2,PERSONTITLE$
    TRUNCATEBLANKS(PERSONTITLE$)
    INPUT #2,PERSONCOMPANY$
    TRUNCATEBLANKS(PERSONCOMPANY$)
    INPUT #2,PERSONSTREET$
    TRUNCATEBLANKS(PERSONSTREET$)
    INPUT #2,PERSONCITY$
    TRUNCATEBLANKS(PERSONCITY$)
    INPUT #2,PERSONSTATECOUNTRY$
    TRUNCATEBLANKS(PERSONSTATECOUNTRY$)
    INPUT #2,PERSONZIP$
    TRUNCATEBLANKS(PERSONZIP$)
    INPUT #2,PERSONPHONE$
    TRUNCATEBLANKS(PERSONPHONE$)
    PRINT PERSONNAME$
    ADDRECORD
    GOTO LOADLOOP

.ll 65
.pw 85
.pa
.pw 120
.ll 100
.im 24
DUMP: REM DUMP PHONE NUMBER FILE ALPHABETICALLY BY PERSON
    LET COMMAND$=RIGHT$(COMMAND$,6)
    CREATE #2,COMMAND$
    PRINT "DUMPING DATABASE..."
    LET PERSONKEY$=""
    PERSONNAMESAMELINK=0
DUMPNEXTPERSONLOOP:
    IF PERSONNAMESAMELINK<>0
    THEN
        REM MORE THAN ONE GUY WITH THE SAME NAME
        PERSONRECORD=PERSONNAMESAMELINK
    ELSE
        IF ERROR WHEN
            PERSONRECORD=KEYNEXT(1,1,PERSONKEY$)
        THEN IF ERR=1001
             THEN CLOSE #2\GOTO ASKCOMMAND
             ELSE ERROR
    FI
    READPERSONRECORD
    PRINTPERSONRECORD(2)
    GOTO DUMPNEXTPERSONLOOP

END

.ll 65
.pw 85
.sh XVII: RUNTIME ERROR MESSAGES
.ju
.ll 65
.pw 85
.pa
.ix Error Runtime Messages
RUNTIME ERROR MESSAGES
.tc
.sp
.nj
.pw 120
.ll 100

 0  - Program completed normally
 1  - Operator requested Attention
 2  - Not used
 3  - Not used
 4  - Not used
 5  - Not used
 6  - RETURN without GOSUB
 7  - Conversion Error
 8  - Input Buffer Overflow
 9  - Array or Vector Subscript out of range
 10  - Runtime package self-checksum failed --> Suspect damaged RTP
 11  - String Subscript out of range
 12  - String subscript too large
 13  - Undefined Line Number encountered
 14  - Arithmetic Overflow
 15  - Non-Integer operand to Logical operator (& ! XOR COM **)
 16  - Concatenated String exceeds CATMAX
 17  - Tab count > 255
 18  - Invalid FORMAT string
 19  - I can't store that value into a byte
 20  - Illegal Argument to SIN/COS/TAN/ATN
 21  - Logarithm of 0 or negative number
 22  - Square root attempted on negative number
 23  - PEEK or POKE address < 0 or > 65535, or not an integer
 24  - POKE value < 0 or > 255, or not an integer
 25  - Attempt to POKE runtime package
 26  - Version number doesn't match BASIC Runtime Package
 27  - Wrong number of arguments to function/subroutine
 28  - Data space for BASIC program overlaps SDOS
 29  - Basic Program overlaps Runtime Package
 50  - Channel number > 255
 52  - File name is too long
 60  - File position < 0 or >= 2^31
.sp
.ix Errors Keyed File Package
KEYED FILE PACKAGE ERRORS
.sp
1001 - End of File encountered
.br
1075 - No such key
.br
1076 - Duplicate key
.br
1077 - Key branch Factor not large enough
.sp
.ix SDOS Errors Commonly Encountered
COMMONLY ENCOUNTERED SDOS ERRORS
.sp
1011 - No such file
.br
1023 - Filename doesn't start with A through Z or $
.br
1031 - Channel is already open
.br
1032 - Channel is closed
.br
.ju
.ll 65
.pw 85
.sp
Other error codes can be found in the SDOS Manual.
.sh SECTION XVIII: KEYWORDS
.pa
.ix Keywords
KEYWORDS
.tc
.sp
The following words are reserved keywords in BASIC 1.4, and
may not be used for variable, subroutine, function or
parameter names, or be used as labels.  Keywords are
recognized regardless of whether the constituent characters
are lower or upper case.
.sp
.nj
.im 21
          DIM COMMON REM PROGRAM DATA CONCATENATION
          INCLUDE
          ON ERROR GOTO ELN
          GOSUB POP RETURN
          FOR TO STEP CYCLE NEXT
          LET
          PRINT USING FORMAT TAB INPUT READ WRITE
          STOP EXIT
          WHILE UNTIL DO END
          REPEAT UNLESS WHEN
          IF THEN ELSE FI ELSEIF
          POSITION RESTORE OPEN CREATE CLOSE DELETE RENAME 
          CHAIN
          CALL POKE DEBUG SYSCALL
          SUBROUTINE DEF EXTERNAL
          LEN MAXLEN LEFT$ MID$ RIGHT$ ASC CHR$
          COM ATN SIN COS TAN LOG EXP SQR INT ABS SGN COL VAL PEEK FIND
          RND ERR PI AND OR NOT EOF ROWS COLUMNS
          TRUE FALSE XOR
          CAT DATE$ TIME$ COPYRIGHT$ NUM$ NUMF$ HEX$ UPPERCASE$ 
          LOWERCASE$
.sh SECTION XIX: STATEMENT SUMMARY
.pa
.ju
.ll 65
.pw 85
.tc
.ix Language Summary
.ix Statements
.ix Functions
.im 48
     LANGUAGE SUMMARY
     
     STATEMENTS                   FUNCTIONS
     
     PRINT                        PI
     PRINT USING                  SIN
     FORMAT                       COS
     LET                          TAN
     INPUT                        ATN
     GOTO                         LOG
     IF-THEN-ELSE-ELSEIF          EXP
     FOR-NEXT/CYCLE               SQR
     GOSUB/RETURN                 INT
     GOSUB POP                    ABS
     ON GOTO/GOSUB                SGN
     ON ERROR WHEN/DO/GOTO        ERR (error number)
     IF ERROR WHEN                ELN (error line number)
     ERROR                        LEN 
     REM (or "!")                 VAL (of string)
     DEF                          COM (logical complement)
     END                          PEEK
     OPEN                         EOF (end file test)
     CREATE                       NOT (IF cond invert)
     CLOSE                        FIND (string in string)
     DELETE                       MID$
     RENAME                       LEFT$
     PRINT #                      RIGHT$
     PRINT # USING                DATE$
     INPUT #                      TIME$
     READ # (binary)              NUM$ (unformatted conversion)
     WRITE # (binary)             NUMF$ (formatted conversion)
     POSITION #                   HEX$ (hex conversion)
     CHAIN                        ASC
     CALL                         COL (returns column position)
     SUBROUTINE                   CHR$
     CALL (assembly routine)      UPPERCASE$
     SYSCALL (SDOS interface)     LOWERCASE$
     DEBUG                        IF-THEN-ELSE-FI function
     DIM/COMMON                   MAXLEN
     POKE                         ROWS
     PROGRAM ORIGIN               COLUMNS
     DATA ORIGIN
     INCLUDE                      RND
     WHILE/UNTIL DO END           TRUE
     REPEAT UNLESS/WHEN END       FALSE
     GOTO ELN (used in error recovery)
     EXIT/STOP
     EXIT subroutine/labelname/indexvariable
.pa
.ix Data Types
.ix Statement Summary
.ix Operators
.ix Formatted Output
.ix I/O
.ix Features of SDOS Basic
.im 40
     DATA TYPES                                         OPERATORS
     
     9 digit decimal floating point                     + - * / ^
     16 bit positive integers                             & (and)
     Hex numbers                                           ! (or)
     Characters strings to 65534 characters                   XOR
     String arrays                                     ** (shift)
     Numeric vectors                                   - (negate)
     Numeric arrays                    CAT (string concatenation)
     Byte vectors                                 [] (substrings)
     
     FORMATTED OUTPUT
     
     Money format - floating dollar / trailing minus
     Exponential format
     Formatted numbers available as strings (NUMF$)
     
     I/O
     
     Channel oriented
     ASCII (print/input) variable length records
     Binary (read/write) reads any file accessible by byte
     Random positioning
     Multi-key file access procedures
     
     MISCELLANEOUS
     
     Multiple statements per line
     Multiple statements in THEN/ELSE clauses
     Line numbers needed only as targets of GOTO/GOSUB/ON ERROR
     High speed execution
     Error reporting line # at runtime
     Explicit pointer to compile time errors
     Compiled program is ROM-able
     Line number tracing, single step, and breakpoint facility
     Screen/File position control integrated into I/O statements
     Structured programming constructs
     Error trapping
     Parameterized subroutines and functions
     Excellent documentation
.ju
.ll 65
.pw 85
.sh SECTION XX: TABLE OF CONTENTS
.sh 
.pa
.np
.pc
.ce
TABLE OF CONTENTS
.sp
INTRODUCTION AND BASIC CONCEPTS
     SD BASIC, A COMPILER VERSION OF BASIC
     INTRODUCTION
     NOTATION
     PROGRAM ORGANIZATION
     VALUES
     CONSTANTS
     VARIABLES
     SUBSCRIPTING
     EXPRESSIONS
     CONDITIONAL EXPRESSIONS
.sp
STATEMENTS
     PRINT
     PRINT USING
     FORMAT
     LET
     INPUT
     GOTO
.sp
BLOCK BODIES
     IF (ELSEIF AND FI)
     WHILE
     UNTIL
     REPEAT
     FOR AND NEXT
     CYCLE
     EXIT
     GOSUB
     RETURN (SUBROUTINE AND <EXPRESSION>)
     GOSUB POP
     STOP
     ON
     REM
.sp
ERROR HANDLING
     ERROR
.sp
SUBROUTINES AND FUNCTIONS
     DEF
     USER-DEFINED SUBROUTINES
     CALL
     UNIFORM REFERENCE
.sp
FILE I/O
     OPEN
     CREATE
     CLOSE
     DELETE
     RENAME
     PRINT #
     INPUT #
     WRITE #
     READ #
     POSITION # OR RESTORE #
     IMPLIED POSITIONING
     CHAIN
     SYSCALL
     KEYED FILE PACKAGE
.sp
MISCELLANEOUS STATEMENTS
     DIM
     COMMON
     PROGRAM ORIGIN
     DATA ORIGIN
     CONCATENATION BUFFER SIZE
     INCLUDE
     END
.sp
ASSEMBLY LANGUAGE INTERFACE
     ASSEMBLY LANGUAGE SUBROUTINES
     ASSEMBLY LANGUAGE FUNCTIONS
     DEBUG
     POKE
.sp
BUILT-IN FUNCTIONS
     SQR(ARG)
     ATN(ARG)
     SIN(ARG)
     COS(ARG)
     TAN(ARG)
     LOG(ARG)
     EXP(ARG)
     RND
     ROWS(ARRAYNAME)
     COLUMNS(ARRAYNAME)
     LEN(STRINGNAME$)
     LEN(VECTORNAME)
     LEN(STRINGARRAYNAME$)
     MAXLEN(STRINGNAME$)
     MAXLEN(STRINGARRAYNAME$)
     MID$(STRINGNAME$,ARG2,ARG3)
     LEFT$(STRINGNAME$,ARG2)
     RIGHT$(STRINGNAME$,ARG2)
     UPPERCASE$(STRINGARGUMENT)
     LOWERCASE$(STRINGARGUMENT)
     EOF(ARG)
     COL(ARG)
     PEEK(ARG)
     COM(ARG)
     NOT(ARG)
     INT(ARG)
     ABS(ARG)
     SGN(ARG)
     ERR
     ELN
     PI
     ASC(STRINGEXPRESSION)
     CHR$(ARG)
     FIND(ARG1$,ARG2$)
     DATE$
     TIME$
     VAL(STRINGEXPRESSION)
     HEX$(ARG)
     NUM$(ARG)
     NUMF$(STRING1$,ARG2)
     TRUE
     FALSE
     IF
.sp
DATA STRUCTURES
.sp
FLOATING POINT PACKAGE
.sp
USING SOFTWARE DYNAMICS BASIC
     PASS I
     COMPILE TIME ERROR MESSAGES
     HINTS ON HOW TO HANDLE COMPILE-TIME ERRORS
     PASS II
     PROGRAM EXECUTION
     FIX, COMPILE AND FINDLABEL
     PROGRAM DEBUGGING AIDS
     DEBUGGING A COMPILED BASIC PRORAM
     SEPARATE COMPILATION
.sp
MOVING BASIC 1.4 TO SYSTEMS OTHER THAN SDOS
.sp
CONVERTING ANOTHER BASIC PROGRAM TO SD BASIC
.sp
.nn
DIFFERENCES BETWEEN MINIMAL ANSII
     STANDARD AND SD BASIC
.sp
PERFORMANCE CHARACTERISTICS
.sp
SAMPLE PROGRAMS
     THE GAME OF LIFE
     PHONEBOOK EXAMPLE PROGRAM
.sp
RUNTIME ERROR MESSAGES
.sp
KEYWORDS
.sp
LANGUAGE SUMMARY
