BCLS - A Programmable RPN Calculator with Strings for the portfolio by: Peter Baltus Estherdal 14 5551 BL Valkenswaard the Netherlands email: pbaltus@neabbs phone: 04902-47194 Release 1.0á January 4 1992 Contents: 1. Introduction 2. Using BCLS 3. BCLS Reference 4. Customizing BCLS 5. BCLS Limits 6. Future plans & improvements 7. Copyright 8. Acknowledgements 9. Update history Introduction BCLS is a programmable calculator program for the Atari Portfolio. It combines a programming language and an interactive interface to this language, and is optimized for use on the Portfolio by keeping it small, supporting several Portfolio specific functions, and efficient use of the small screen. A separate program for general IBM-PC compatible computers also exists as an aid in developing and porting software to/from the portfolio. You can use BCLS to: - carry out scientific, list & string calculations in RPN syntax - write just about any program - create pixel & line graphics - experiment with threaded languages Features of BCLS are similar to BCL, except that images cannot be saved in binary format. Instead, they are translated back to source before they are saved. Robustness has improved because no low-level access to program or data memory is available any more. Also, you will need to keep the C disk size to 50k or less in order to overlay the Portfolio built-in applications on top of BCLS. Otherwise, you still can use up to 64k of C disk. I developed this program as a successor to BCL, a numbers-only programmable calculator for the Portfolio. After using it for a while, I felt the need for strings, dynamic memory management, and decompilation for modifying exisiting code one procedure at a time. After playing around for a while with all kinds of extensions to BCL, using aproaches similar to FIG-Forth and polymath for fitting strings into the language, I decided I wasn't satisfied with either because they continued to feel like the strings had been added as an afterthought - which, of course, was the case. Therefore I started from scratch with a fully object-oriented implementation of a new language, which resembled BCL very much from the outside if used only for numerical calculations, because I liked that part of BCL quite a lot. Strings (and other data types) could now be implemented in a way similar to numbers using sub-typing (inheritance) in a very elegant way. I expected that this would result in a program much larger than the original BCL program, but it turned out that it increased by less than 50%. For these 50%, you get full string support, dynamic arrays, input & output files, all kinds of Portfolio specific primitives (dialing, sound, turning the calculator off from a program, etc), running external programs, and decompilation of separate words and/or the complete image. However, because of all the dynamic allocation and deallocation of memory and the somewhat more convoluted way in which the code gets executed in OO programs, BCLS is also considerably slower than BCL. Therefore, I am planning to develop it as a separate program, and continue to maintain and update BCL for numbers-only applications. 2. Using BCLS BCLS is distributed as an archive containing several files. When you unpack the archive, you should find the following files: bcls.exe the BCLS machine, or actual program b.bat batch file which starts BCLS with the standard source file std.bcl standard source file for BCL bcl.doc this file BCLS can run by itself, but most of the time, you will want to keep std.bcl available as well, because BCLS by itself isn't very useful except when building a new source file from scatch. To use the program, put BCLS.EXE in a directory that is in your PATH statement, or in the current directory, and put std.bcl also in the current directory. To start the BCLS program for normal operation, type BCLS STD.BCL at the DOS/DIP command prompt ("c>"). First you will see a message with the text "Loading...". After a while, the screen clears, and you will see a screen divided by a horizontal line and an information text at the bottom informing you about the memory left. BCLS has now read all the definitions in STD.BCL (go ahead and look into this file, if you like, it is an ASCII file that can be viewed and edited by the Portfolio editor or any other ascii editor). To see a list of all these definitions, type "list" and hit the enter key. You will see many familiar words and symbols like "+","-","=", "sin", "if", and some others which might seem less familiar, like "->" and "ssize". A list of these words can be found in the reference part (section 3) of this file. The screen is divided into 3 distinct parts: the part above the horizontal line is called the stackview, the line just below the horizontal line is called the input line, and the line at the bottom of the screen is called the message line. Anything you type in BCLS will appear on the input line, until it is complete. At that point, it will be interpreted: most input will be pushed onto the global stack of BCLS. This stack is made visible in the stackview part of the display. This is the case with e.g. numbers. Try typing the number 75 followed by the enter key. The input line clears, and the number 75 appears just above the horizontal line. This position is called the top of the stack, or stack position 1 (s1 for short). Typing another number, say 38, and hitting the enter key moves the number 75 up one line and puts 38 at s1. The position of 75 is called stack position 2 (s2 for short). You can continue this as often as you want (at least until the memory of your computer runs out): the previous numbers on the stack will continue to move upward each time you add a number to the stack. At some point, the number 75 will even move off the screen. This doesn't mean that it is not longer on the stack - in fact, it still is. There is just no room to display it. Now let's have a look at the different types of input we can provide for BCLS: - NUMBERS can be in just about any decimal format, and will be added to the stack. Numbers are terminated by a space or hitting the enter key. - STRINGS always start with a double quote, e.g. "This is a string". Strings are always added to the stack, and are terminated by a second double quote. They can include spaces and/or carriage returns. The maximum length of a string equals the width of the display, i.e. 40 characters on the Portfolio, and 80 characters on the PC version. - VECTORS Are lists of other items in BCLS. They start with an opening square bracket [ and a space, and are terminated by a closing square bracket ] and a space. Items in a vector are separated by spaces or carriage returns. As soon as you start entering a vector, the stackview clears and shows all the elements you are entering for the vector up to the closing bracket. At that point, the original stackview is restored and the new vector is added to the original stack. VECTORS are used for arrays, lists, and program blocks in BCLS. Vectors may be any length. If their total length exceeds the width of the display, they are displayed with several dots at the end of the vector, but they are still stored completely in computer memory. - PRIMITIVES are references to built-in routines in the BCLS program. They are used to create words that carry out simple actions in BCLS. Primitives start with an opening curly brace {, followed by a number. A primitive is terminated by a closing curly brace }. Primitives are always pushed onto the stack. - WORDREFS word references start with a single quote, and are terminated by a single quote as well. Word references can be up to 9 characters. Word references are always pushed onto the stack. You can only create word references to words which already exist. Use the "list" command to find out which words exist. Wordrefs are used to change the value of an existing word, or to refer to a word without evaluating it, e.g. to store a reference to a word as the value of another word. - WORDS Anything that is not enclosed in double quotes, single quotes, or square brackets, is checked against the list of existing words. If it matches, it is interpreted as a word. If it doesn't match, it is interpreted as a number. Words are the only items which are normally not pushed onto the stack. Instead, they are executed, and the action they carry out depend on the value that has been assigned to them. If their value is: a Number, then this number is added to the stack a String, then this string is added to the stack a Vector, then each of the elements of the vector is executed, again depending on the element's type a Wordref, then this wordref is added to the stack a Word, then this word is executed, again depending on the type of it's value. Words are terminated by a space or carriage return. Let's try a few examples to make sure that this is clear. Type in the text in the column under the header "type:", and verify that what you see corresponds with the comments in the column under the header "comment:". Remember to type a space or hit the enter key following numbers and words. For strings, wordrefs or primitives this is not necessary, but it doesn't cause any harm either. type: comment: 5 this is a number which is added to the stack 3 this is a number which is also added to the stack + this is a word which is executed immediately. It's actions include removing two items from the stack, adding them, and putting the result (8 in this case) back on pop this is a word which is executed immediately. It removes the value at s1. "abc" This is a string which is added to the stack [ this starts a new vector. Notice how "abc" is not visible any more. "de" This is a string which is added to the stack "xyz" This is a string which is added to the stack ] This terminates the new vector. Notice how "abc" reappeared at s2, while the vector [ "de" "xyz" ] appeared at s1. + This is a word which is executed immediately. It's actions include removing two items from the stack, and adding them together. In case of a vector, this addition is carried out on an element by element basis. Adding two strings is carried out by concatenating their characters, resulting in a new vector which is pushed onto the stack with the value: [ "abcde" "abcxyz" ] This example already shows some of the advantages of treating all elements in BCLS in a uniform way: the same word (in this case '+') can be used to carry out different functions depending on the values on the stack. Using the reference section of this manual, you should now be able to carry out many calculations and operations on numbers, lists and strings, basically using the BCLS program as a fancy calculator. At some point, you might want to save some of your results somewhere else besides keeping it on the stack. This is achieved by storing it in a word. Let's store your age in a word called "age": 25 "age" -> As you can see, you can use the word -> to create a new word, with a name corresponding to the item on the stack at s1, with a value corresponding to the item on the stack at s2. To retrieve the value of age, simply type: age Remember, words which have a number as their value always put this number onto the stack when executed. Next birthday, however, you will need to increment the value of age by 1. Executing 26 "age" -> results in an error message: "Error: age exists", which, of course, is true. The reason this error message was implemented is to prevent you from redefining existing words without realising it. Instead, you can use the following command to change the value of an existing word: 26 'age' -> As you see, we used a wordref to indicate which word we want to modify. Wordrefs can only be created for words which do exist, so they cannot be used to create new words. After several years, you might find this updating of your age becoming rather tedious, and you might want to automate it. This is where you would write a small program on a normal programmable calculator, and that's what you do in BCLS as well: [ age 1 + 'age' -> ] "birthday" -> What we did here was create a new word birthday, which has as it's value a vector with as elements the word age, the number 1, the word +, the wordref 'age', and the word ->. When birthday is executed, each of these elements will be executed in turn, resulting in the updating of the value of age to the old value + 1. To try this, type: age birthday age birthday age As you can see, each time you execute birthday, your age increases by one. As we have seen, programs in BCLS are just vectors with the appropriate command sequence as elements. Programs can then be stored into a word to make them available any time. Repetitive and conditional execution is also possible using the words 'if' and 'until', see section 3. Multidimensional vectors can be created by adding vectors as elements of other vectors, e.g.: [ [ 1 2 ] [ 3 4 ] ] To retrieve an element from a vector, you can use the word '@': [ "a" "b" "c" ] 2 @ results in "b". In the same way: [ [ 1 2 ] [ 3 4 ] ] 2 @ 1 @ results in 3. 3. BCLS Reference BCLS includes 53 primitives, which are assigned to words in the file std.bcl. This file can be modified to give different names to the words which invoke these primitives, or to leave out primitives which are not needed in an application. In the following table, primitive indicates the primitive number which implements an action, word indicates which word this primitive is assigned to in std.bcl, and comment explains what the action of the primitive is. In this comment, s1 refers to the item at the top of the stack, s2 to the item at stack position 2, s3 at the item at stack position 3, etc. primitive word comment {0} -> create a new variable: 738 "a" -> update an exisiting variable: 67 'a' -> {1} load load a file with BCLS input: "std.bcl" load {2} clr clears the stack {3} pop removes s1 from the stack {4} swap exchanges s1 and s2 {5} ? reads a word from the input (keyboard or file) {6} save writes everything in BCLS memory as BCLS source code to a file with the name of s1: "bclsext.bcl" save {7} if if the value of s3 corresponds to "true" or nonzero, then execute the item (word or vector or whatever) at s2, otherwise execute the item at s1: x 0 > [ x ] [ 0 x - ] if returns the absolute value of x {8} until executes s1 until it returns "true" or nonzero: 0 [ dup ! 1 + dup 100 > ] until displays the numbers 0 through 99 {9} list displays all existing words {10} ! display the item at s1 738 ! key pop displays the number 738 and waits for a keypress {11} dup add s1 to the stack {12} key reads a key and adds it to the stack {13} version adds a string with the BCLS version to the stack {14} graphics puts the computer display in graphics mode {15} text puts the computer display in text mode & clears it {16} line draws a line from s2 to s1, if s1 and s2 are vectors with 2 elements for x and y coordinates. [ 0 0 ] is topleft, and s3 indicates which color (0: white, 1: black, 129: invert) is to be used: graphics 1 [ 0 0 ] [ 239 63 ] line key pop text draws a line across the screen and waits for a key to be pressed {17} point draws a point on the screen at s1 with color s2. S1 should be a vector with 2 elements for the x and y coordinates. Coordinates and colors as in {16} {18} gotoxy move the text cursor to the position indicated by s1. S1 should be a 2 element vector. The 1st element indicates the column, the 2nd element the row of the display. The next ! command will write at this position. {19} error Display an error message with the text taken from s1. {20} sound Sound the speaker. S1 determines tone, s2 the duration of the sound {21} stackview if s1 is "true", then the stack will be displayed after each operation. Otherwise, the display will not be updated. {22} dial dial the phone number at s1. {23} off turn the portfolio off. When switched back on again by hitting any key, BCLS will continue. {24} @-> store s2 in s3 (a vector or string) at the position indicated by s1 {25} ssize number of elements on the stack {26} run run the external command whose path&filename is at s2 with arguments defined by s1: "c:\edlin.com" "std.bcl" run edits file std.bcl using edlin. Upon completion, control is returned to BCLS. {27} exec execute the item on top of the stack. Strings & numbers don't change, vectors execute all of their elements in sequence, and primtives, words and wordrefs execute. {28} push create a new vector with all elements on the stack in it, clear the stack, and add this new vector as the 1st element to this empty stack Useful for local variables, parameters, experiments. {29} endload close the input file, return input to keyboard {30} write redirect output to file with filename at s1 {31} endwrite close output file, return output to display The following primitives always take 2 items off the stack. If one is a number and the other a string, the number is turned into a string. If one is a vector and the other a number or string, the operation is carried out on each element of the vector with the number or string as argument, and the results are returned in a vector. If both items are vectors, the operation is carried out on corresponding items in the vectors up the the size of the smallest vector. The result is returned in a vector. {32} + add numbers concatenate strings - subtract numbers remove s1 string everywhere it occurs in s2 * multiplies numbers logical and of strings / divides numbers logical or of strings = returns true if both items are the same < returns true if s1 is a smaller number than s2 returns true if string s1 is first when s1 and s2 are sorted alphabetically <= returns true if s1 is smaller than or equal to s2 @ recall element s1 from vector or string s2 if s1 & s2 are strings, return the position of s2 within s1 , concatenate vectors & strings The following primitives take one element from the stack. If the item at the top of the stack is a vector, the primitive is applied to all elements of the vector and the result is returned as a vector. {41} <- recall the value of the word at s1 {42} number turn s1 into a number {43} string turn s1 into a string {44} char create a one character string with ascii value s1 {45} int create an integer with value s1 {46} size return the size of s1 (vector: #items, string: #chars, else=1) {47} class return the class of the item on top of stack: "String", "Word", "WordRef", "Primitive", "Vector", "Number", "Object" {48} sin return the sine of s1 {49} atn return the arctangent of s1 {50} exp return e (=2.718281...) to the power s1 {51} ln return the natural logarithm of s1 {52} # delete the 1st item from a string or vector Besides these primitives, some derived words are defined in std.bcl: pi the number 3.1415926535..... pi/2 half of pi cos cosine of s1 tan tangent of s1 chs -s1 inv 1/s1 ^ s2 to the power s1 sqrt square root of s1 sqr square of s1 (=s1^2) log 10-based logarithm of s1 asn arcsine of s1 acs arccosine of s1 not logical complement of s1 > "true" if s2 > s1 >= "true" if s2 >= s1 <> "true" if not s1=s2 !! write with carriage return & line feed tmpf file which is used when editing s1 with an external editor edit edit s1 with an external editor. most likely, you will have to modify this definition to match your computer. E.g. on a Toshiba T1000 which has edlin in it's C: romdisk root directory, the definition should read: [ tmpf write ! endwrite "c:\edlin.com" tmpf run tmpf load ] "edit" -> Of course, you can put in your own favorite editor. On the portfolio, a prompt will appear reading: "Edit bclstmp.tmp..." At this point, start the portfolio editor by /|\ E and edit the file bclstmp.tmp. Save the file, exit the editor, and hit any key to read in the modified value of s1 back onto the stack. 4. Customizing BCLS Customizing BCLS can be achieved quite easily by editing std.bcl and changing any names of primitives or other words defined in this file to suite your tastes. Take care to change any references to a word further down in this file if you change a name. E.g.: if you change '+' to '++' then you should also change '+' to '++' in the definition of cos and edit. You can also leave out definitions of words which you don't use to save memory. 5. Limits of BCLS BCLS has only a few limits: - words can be up to 9 characters long - strings can be up to 40 characters long on a portfolio, or 80 characters on a PC - recursion is limited by the amount of stack space allocated to BCLS.EXE, in this release: 5kbytes - memory usage is limited by the heap size, in this case: 7kbyte. It is displayed continuously at the bottom of the screen. - memory fragmentation can be monitored at the bottom of the screen in the "max: ..." field. If this number becomes too small while the "mem: ..." field indicates the total available amount of memory is still sufficient, it is best to defragment memory by saving the image, quiting BCLS, and starting BCLS again. This shouldn't happen frequently. If it does, please let me know. 6. Future plans & improvements I would like to add infix notation & symbolic expressions to BCLS. This shouldn't be very difficult to accomplish since the program was set up with this extension in mind. Menu's and borders in Portfolio style would be very nice, but I am not quite sure whether I want to pay the penalty (program size, reduced screen size) for this. I'll have to think about this a little more. I am also planning to optimize the program for a minimum of allocation and deallocation. At this moment, all stack operations do actually allocate copies of anything put onto the stack. Although this is the easiest way of getting the program to work correctly, it does impose a performance penalty. At some point, BCLS should be ported to TP6, because TP5.5 has a minor bug in it's allocation & deallocation of heap space, resulting in small losses of heap. Finally, BCLS should be ported to some other computers like the HP95. I don't think I will get around to this anytime soon. If anybody wants to try and implement some of these (or other) improvements, please contact me. I will make source code available (TP5.5) and help you out with any questions you might have (as far as time allows). 7. Copyright This program is copyright (c) Peter Baltus, January 1992. All rights reserved. This program may be used and distributed free of charge for non-commercial purposes only. No responsibility can be accepted for any loss or damage caused by this program. You don't have to pay to get it or use it, so don't expect me to foot the bill if this program causes problems or damage in any way whatsoever. If you want to obtain the source code or customized versions of BCLS, or want to use it for commercial purposes, please contact me and we will work something out. 8. Acknowledgements Bert Rozenberg for providing me with details on how to interface to the built-in routines of the portfolio, and for helping me to get TP programs beyond the EXEC ERROR stage on the portfolio Portfolio vereniging for publishing a magazine which got me interested into programming the portfolio in the first place STER BBS for providing a place to exchange ideas and software for the portfolio Robert Young for his ARJ program which is used to create the self extracting archive in which BCL is distributed Fabrice Bellard for his LZEXE program which is used to reduce the size of BCL.EXE Forth Interest Group for supporting the FORTH concept B.J. Gleason for providing a TP unit to interface to portfolio routines (although I didn't use it in this release of BCLS) AcornSoft Limited for creating the implementation of FORTH that got me interested in threaded languages (ATOM FORTH) Lobster Software for creating a very extensive forth-based language for mathematical applications (POLYMATH) 9. Update history January 4 1992 1.0á 1st public release of bcls