Certain aspects of VMS-BASIC remind me of FORTRAN (for example, the "PRINT USING", FORMAT$, and Matrix statements) while other portions offer a few COBOL-like features (multi-key ISAM file routines). That said, I am not going to document the language here since the Official BASIC Manuals are well written and complete. On top of that, I've have provided more than a few BASIC demo programs here but I will provide four examples here which may be of some interest to the casual reader.
Most BASIC implementations force programmers to trap run-time errors using a common handler like this:
1000 %title "vms-basic-demo_001.bas" !============================================================= ! title : vms-basic demo_001.bas ! author: NSR !============================================================= option type=explicit ! set no prompt ! declare string constant k_program = "vms-basic-demo" declare string constant k_version = "001" declare string junk$ ,& long i% on error goto trap ! Old-school BASIC !============================================================= ! main !============================================================= 2000 main: ! print k_program +"_"+ k_version ! print string$(len(k_program +"_"+ k_version), asc("=")) ! ! 2010 input "enter a number? "; i% ! print "you entered "; str$(i%) ! ! ! more code would appear here ! goto fini ! !============================================================= ! common error trap !============================================================= 32000 trap: ! select erl ! case 2010 ! print "-e-non-numeric character encountered" ! resume 2010 ! go back to line 2010 end select ! ! print "<<< common error trap >>>" print "please report this error to your system manager" print "error: "+ str$(err) print "line : "+ str$(erl) print "text : "+ ert$(err) input "hit <enter> to exit...";junk$ resume fini ! fix the stack ! ! That's All Folks... ! 32700 fini: ! print "bye..." ! end !
Legend: <sr> = system response <ur> = user response
<sr> $ ! this is my DCL prompt <ur> run vms-basic-demo_001 vms-basic-demo_001 ================== enter a number? abc -e-non-numeric character encountered enter a number? 123 you entered 123 bye... <sr> $
1000 %title "vms-basic-demo_002.bas" !============================================================= ! title : vms-basic demo_002.bas ! author: NSR !============================================================= option type=explicit ! set no prompt ! declare string constant k_program = "vms-basic-demo" declare string constant k_version = "002" declare string junk$ ,& long i% !============================================================= ! main !============================================================= 2000 main: ! print k_program +"_"+ k_version ! print string$(len(k_program +"_"+ k_version), asc("=")) ! 2010 when error in ! like Pascal, Modula, C++, etc. input "enter a number? "; i% ! use ! print ert$(err) ! retry ! end when print "you entered "; str$(i%) ! ! ! more code would appear here ! goto fini ! ! ! that's all folks ! 32700 fini: ! print "bye..." ! end !
<sr> $ <ur> run VMS-BASIC-DEMO_002 <sr> vms-basic-demo_002 ================== enter a number? ABC %Data format error enter a number? 123 you entered 123 bye... <sr> $
Many programmers will combine both techniques so that ON ERROR GOTO can be used to write untrapped errors to a diagnostic log file.
1000 %title "vms-basic-demo_003.bas" !============================================================= ! title : vms-basic demo_003.bas ! author: NSR !============================================================= option type=explicit ! set no prompt ! declare string constant k_program = "vms-basic-demo" declare string constant k_version = "003" declare string junk$ ,& long i% on error goto trap ! Old-school BASIC !============================================================= ! main !============================================================= 2000 main: ! print k_program +"_"+ k_version ! print string$(len(k_program +"_"+ k_version), asc("=")) ! 2010 when error in ! new-school BASIC input "enter a number? "; i% ! use ! print ert$(err) ! retry ! end when print "you entered "; str$(i%) ! ! ! more code would appear here ! goto fini ! !============================================================= ! common error trap !============================================================= 32000 trap: ! print "<<<common error trap >>>" print "error: "+ str$(err) print "line : "+ str$(erl) print "text : "+ ert$(err) ! insert code to write to a diagnostic log resume fini ! fix the stack ! ! that's all folks ! 32700 fini: ! print "bye..." ! end !
1000 %title "OpenVMS-BASIC-RMS-indexed-demo_xxx.bas" %sbttl "RMS (Record Management Services) Demo" %ident "version 101.1" !============================================================================================================== ! title : OpenVMS-BASIC-RMS-indexed-demo_xxx.bas ! author : Neil Rieck (https://neilrieck.net/links/cool_openvms.html) ! purpose: demos the use of RMS-based indexed file access for novice OpenVMS programmers ! scope : this educational program comes free of charge with no strings attached ! notes : 1. OpenVMS-BASIC has 'built in' support for RMS (Record Management Services) ! : 2. edit environment: VT-220, 132 column, 8 column tab stops at 1,9,17,25,.... ! : 3. all remarks begin in column 81 ! history: ! ver who when what ! --- --- ------ ---------------------------------------------------------------------------------------------- ! 100 NSR 020829 1. original program ! 101 NSR 050123 1. cleanup for public view !============================================================================================================== option type=explicit ! cuz tricks are for kids set no prompt ! no ? with INPUT ! ! <<< declare constants >>> ! declare string constant k_program = "OpenVMS-BASIC-RMS-Indexed-Demo" declare string constant k_idx_fs$ = "OpenVMS-BASIC-RMS-Indexed-Demo.dat" ! ! <<< mapped variables to 'lay out' a disk record >>> ! ! note: when the same map names is used, the second map overlays the first ! map (indexed_demo) string d21_first_name = 20 , ! 20 & d21_last_name = 20 , ! 40 & d21_telephone = 10 , ! 50 & d21_address = 20 , ! 70 & d21_city = 20 , ! 90 & d21_postal_code = 10 , !100 & fill$ = 50 , !150 room to grow & d21_align = 0 ! to enforce map alignment map (indexed_demo) string d21_whole_chunk = 150 , !150 & d21_align = 0 ! to enforce map alignment ! ! <<< declare variables >>> ! declare long handler_error% , & rec_count% , & string junk$ , & rfa rfa21 ! record file address (a 48 bit variable) ! !======================================================================= ! <<< main >>> !======================================================================= 2000 print k_program ! display program name print string$( len(k_program), ascii("=") ) ! now underline it on error goto trap ! margin #0, 132 ! this will not change the screen size ! !======================================================================= ! ! <<< delete all OpenVMS versions of our test file >>> ! input "OK to delete 'demo data files'? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto sortie if junk$ <> "Y" when error in while 1=1 ! make sure we get all versions kill k_idx_fs$ next use ! end when ! ! <<< open the file >>> ! ! "BASIC Open" notes: ! 1. open k_idx_fs$ for input as file #21 - the file must already exist ! 2. open k_idx_fs$ for output as file #21 - a new file version is always created ! 3. open k_idx_fs$ as file #21 - the file is created if it doesn't exit ! input "OK to create/open 'demo data file'? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto sortie if junk$ <> "Y" 3000 when error in print "-i- opening file: "; k_idx_fs$ open k_idx_fs$ as file #21 ! create the file if it doesn't exist & ,access modify ! we want to read + write & ,allow modify ! allow others to read + write while we do it & ,map indexed_demo ! & ,organization indexed ! & ,primary (d21_last_name, d21_first_name, d21_city) ! key #0 & ,alternate d21_last_name duplicates changes ! key #1 & ,alternate d21_telephone duplicates changes ! key #2 & ,alternate d21_telephone duplicates changes descending ! key #3 ! ! note: the connected channel is opened last but must be closed first ! print "-i- opening file: "; k_idx_fs$; " (connect)" open k_idx_fs$ as file #22 ! & ,access modify ! we want to read + write & ,allow modify ! allow others to read + write while we do it & ,map indexed_demo ! & ,organization indexed ! & ,connect 21 ! handler_error% = 0 ! show that all is well use handler_error% = err print "-e- error: "+str$( handler_error% )+" in phase #1" print "-i- text : "+ert$( handler_error% ) end when goto sortie if handler_error% <> 0 ! exit on ant errors ! ! <<< write some records >>> ! 4000 rec_count% = 0 input "OK to write 3 demo data records? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto sortie if junk$ <> "Y" when error in print "-i- writing file: "; k_idx_fs$ d21_whole_chunk = "" ! start with a clean buffer ! d21_first_name = "Ken" d21_last_name = "Olsen" d21_telephone = "4165553333" d21_address = "129 Parker Street" d21_city = "Toronto" ! this gets corrected below d21_postal_code = "01754" print "-i- writing record: "; str$(rec_count% + 1) ! put #21 ! write to file rec_count% = rec_count% + 1 ! ! d21_first_name = "Dave" d21_last_name = "Cutler" d21_telephone = "4165552222" d21_address = "220 Simcoe Street" d21_city = "Toronto" d21_postal_code = "M5T1T4" print "-i- writing record: "; str$(rec_count% + 1) put #21 ! write to file rec_count% = rec_count% + 1 ! d21_first_name = "Gordon" d21_last_name = "Bell" d21_telephone = "4165551111" d21_address = "483 Bay Street" d21_city = "Toronto" d21_postal_code = "M5G2C9" print "-i- writing record: "; str$(rec_count% + 1) put #21 ! write to file rec_count% = rec_count% + 1 ! print "-i- will rewrite previous record to force a duplicate key error (134)" print "-i- writing record: "; str$(rec_count% + 1) ! put #21 ! write to file rec_count% = rec_count% + 1 ! ! handler_error% = 0 use handler_error% = err print "-e- error: "+ str$( handler_error% )+" in phase #2" print "-i- text : "+ ert$( handler_error% ) print "-i- recs : "+ str$( rec_count% ) end when gosub read_sequentially ! display all records ! ! <<< read the file sequentially by index-key #1 >>> ! 5000 input "OK to display data records in reverse telephone order? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto sortie if junk$ <> "Y" when error in print "-i- reading file: "; k_idx_fs$; " by index-key-3" reset #21, key#3 ! find #21, key#3 gt " ", regardless x same as previous line handler_error% = 0 while 1=1 ! loop forever (until we trap out) get #21, regardless ! read without applying a record lock print "first name : "; d21_first_name print "last_name : "; d21_last_name print "telephone : "; d21_telephone print "address : "; d21_address print "city : "; d21_city print "postal code : "; d21_postal_code print "==============================" sleep 1 next use handler_error% = err print "-e- error: "+str$( handler_error% )+" in phase #4" print "-i- text : "+ert$( handler_error% ) end when ! ! <<< find/delete record "Cutler" >>> ! 6000 input "OK to delete record for 'Dave Cutler'? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto sortie if junk$ <> "Y" when error in find #21, key#1 nxeq "C " ! find (with lock) while 1=1 ! loop forever (until we trap out) get #21 ! read (with lock) if d21_last_name = "Cutler" and & d21_first_name = "Dave" then ! if Dave Cutler delete #21 ! print "-i- record deleted, looking for more people named 'Dave Cutler'" else ! cause error 11 if left$( d21_last_name,1) <> "C" ! exit if we've gone too far iterate ! end if next use handler_error% = err print "-e- error: "+str$( handler_error% )+" in phase #5" print "-i- text : "+ert$( handler_error% ) end when ! gosub read_sequentially ! display all records ! ! <<< delete record #2 >>> ! 7000 input "OK to delete 'Gordon Bell' using the RFA method? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto sortie if junk$ <> "Y" when error in find #21, key#1 ge "Bell", regardless ! Find (without lock) (ge = nxeq) while 1=1 ! get #21, regardless ! read (without lock) cause error 11 if pos(d21_last_name,"Bell",1)=0 ! exit if we've gone too far if d21_last_name = "Bell" and & d21_first_name = "Gordon" then ! if Gordon Bell rfa21 = getrfa(21) ! get the record file address get #22, rfa rfa21 ! position connected channel with LOCK delete #22 ! now delete print "-i- record deleted, looking for more people named 'Gordon Bell'" else iterate ! do another GET on orginal channel end if next use handler_error% = err print "-e- error: "+str$( handler_error% )+" in phase #6" print "-i- text : "+ert$( handler_error% ) end when gosub read_sequentially ! display all records ! ! <<< find/update record "Olsen" >>> ! 8000 input "OK to change Ken Olsen's City? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto sortie if junk$ <> "Y" find_update_reentry_point: when error in find #21, key#1 ge "Olsen", regardless ! set key while 1=1 get #21, regardless ! read without lock cause error 11 if pos(d21_last_name,"Olsen",1)=0 ! exit if we've gone too far if d21_first_name = "Ken" and & d21_last_name = "Olsen" and & d21_city = "Toronto" then rfa21 = getrfa(21) get #22, rfa rfa21 d21_city = "Maynard" d21_postal_code = "" update #22 print "-i- record update, looking for more people named 'Ken Olsen'" end if next use handler_error% = err print "-e- error: "+str$( handler_error% )+" in phase #7a" print "-i- text : "+ert$( handler_error% ) end when ! select handler_error% case 130 ! key not changeable (for primary keys only) when error in print "-i- attempting FIND-RFA" find #22, rfa rfa21 ! position with LOCK (but don't change data) print "-i- attempting DELETE" delete #22 ! delete print "-i- attempting PUT" put #22 ! write buffered data handler_error% = 0 use handler_error% = err print "-e- error: "+str$( handler_error% )+" in phase #7b" print "-i- text : "+ert$( handler_error% ) end when goto find_update_reentry_point if handler_error% = 0 ! look for more if successful case 11 ! end-of-file case 155 ! record-not-found case 131 ! no current key (no lock) end select ! gosub read_sequentially ! display all records ! print string$( 60, ascii("-") ) ! draw a line print "That's all for now" sleep 1 goto sortie !======================================================================= ! Subroutines !======================================================================= ! ! <<< read the file sequentially >>> ! 20000 read_sequentially: input "OK to display data records sequentially? (y/N) ";junk$ junk$ = left$( edit$(junk$,32+2), 1) ! upcase, no white space goto read_sequentially_exit if junk$ <> "Y" when error in print "-i- reading file: "; k_idx_fs$; " sequentially" handler_error% = 0 reset #21 ! rewind to BOF ! reset #21, key#0 x same as "reset #21" while 1=1 get #21, regardless ! read without applying a record lock print "first name : "; d21_first_name print "last_name : "; d21_last_name print "telephone : "; d21_telephone print "address : "; d21_address print "city : "; d21_city print "postal code : "; d21_postal_code print "==============================" sleep 1 next use handler_error% = err print "-e- error: "+str$( handler_error% )+" in phase #3" print "-i- text : "+ert$( handler_error% ) end when read_sequentially_exit: return !======================================================================== ! <<< Final Error Trap >>> ! ! If we've done a good job coding, we should never see this code !======================================================================== 31000 trap: print print "Common Error Trap" print "Line: "+ str$(erl) print "Err : "+ str$(err) print "Msg : "+ ert$(err) resume sortie ! fix the stack !======================================================================== ! ! <<< that's all folks >>> ! 32000 sortie: close 22 ! always close the connected channel first close 21 ! print "Adios..." ! end
The last line of a VMS-Basic program determines what the calling environment will see in shell variable: $STATUS
end ! will always return "1" (VMS-Success) by default end program x ! will always return the integer value of x end program x + reason * 32768 ! alternative exit to send more info back to DCL
0 = VMS-w- warning (Caveat: in the UNIX/C world, 0=good) 1 = VMS-s- success --------+-- odd is good 2 = VMS-e- error | 3 = VMS-i- informational --+ 4 = VMS-f- fatal 5 = VMS-?- undefined 6 = VMS-?- undefined 7 = VMS-?- undefined
$ define/proc YADA$DEMO_MODE whatever ! start basic application in desired mode $ define/proc YADA$DEMO_UNIV_FILE 'txt_fs' ! PROCESS THIS FILE $ run SOME_BASIC_PROGRAM.EXE ! $ my_status = f$integer($status) ! save a copy of $STATUS $ flag = my_status .and. 7 ! will be in the range of 0-7 $ reason = my_status / 32768 ! determine user-defined reason $ if (flag .ne. 1) ! not-equal-to $ then $ write sys$output "Oops, error ",flag," occurred due to reason: ",reason $ goto error_handler $ endif