A cook's tour of the RP/M O/S: Note: this document is not intended to replace the full RP operation manual. The fundamental concept of rp is it's management of "remote partitions" to run application programs. To understand this, we'll take a look at the standard CP/M run concept. All operating systems owe to the concept of a program that is continually resident in the system, providing both an interactive program to allow user control of the running of application programs, and a set of "system services" designed to simplify program construction. In CP/M, the system occupies the top portion of memory, and the application program the lower half. This scheme has the characteristics: 1. The O/S and the applications program are mutually exclusive; the space occupied by the system is unavailable to the applications program, and vice - versa. 2. The applications program is not prevented from direct access to O/S code, and therefore may "crash" the system by corrupting the O/S code. 3. The system relies on "good conduct" of the applications program in calling system services. In any system, the reality is that the O/S and application must make use of the same memory, and the same processor. However, two basic hardware aids can advance the level of the system considerably. These are a good system memory management scheme and the use of hardware timers with interrupts. Memory management allows the O/S and/or the application to see their own "virtual space", which allows the program to forget about the other programs running in the same processor, and makes "relocation" unnecessary. The programs can be protected from each other either implicitly, such as a trap generated by the memory management system if a program tries to access. Protection can also be inherent; if the memory management defines all available program memory without including the other program, the application simply does not have a means to damage the O/S. Finally, a hardware timer allows the system to set a maximum "time limit" for the execution of a program; if the program is crashed or simply uncooperative, it cannot permanently hold the CPU. A side benefit of the memory management for an address limited CPU such as the Z80 is that the program may have a full (64kb) partition or "virtual address space" to itself. Both the operating system and the application program can make use of the full address space available. The Z280 provides advanced abilities to set up partitions. Also present are tools for the system to transfer bytes in and out of the partition (but not allow program access to the O/S!), and for an organized exit back to the system from a partition. Besides the various traps and error conditions, the program obtains system services by the "SC" (system call) instruction. 65536 possible system calls are possible, but in reality a system call is one call, to one location, in the O/S. This simplifies the work of keeping track of the application considerably. The existence of a system call allows the program to make full use of it's memory space, without the problems associated with CP/M. It may start at address 0 (vs. $100), and use all the memory space it requires up until the end of memory at $ffff. This is indeed how the "native" service system for rp works. This will be discussed at the end of this tour. O/S emulation: With enough effort, there probably isn't any previous Z80 O/S environment that can't be emulated. The Z280 can simulate any memory arrangement, even trap and emulate I/O instructions. How much trouble it is, however, is another story. Fortunately, CP/M was constructed according to a few basic rules which make things simpler (and in my option had a lot to do with CP/M's portability): 1. The system calls all occur through a single "vector" (the BDOS jump). 2. No direct access to system data is allowed. 3. No specific location of the O/S was ever set. Of course, that's the way CP/M started out! Since then, two basic violations of these rules have become standard practice. Programs directly access the set of BIOS vectors, and directly setting the I/O connection byte (iobyte) before making a system call. This is done even by the original CP/M utility "pip". By and large, however, CP/M is a very reasonable target for emulation. The common example of running under coprocessors on an Apple or IBM - PC is proof of that. All right, so how do we emulate CP/M under rp/m? looking at the basic layout of a CP/M partition: ----------------- | System page | --> WBOOT vector | | --> BDOS vector ----------------- | TPA | | | | | | | | | ----------------- | System | | | ----------------- Several assumptions are standard. The BDOS vector both points to the entrance to the system services package, and also defines the last usable TPA byte plus one. The WBOOT vector points to the entrance to the warm boot routine, and incidentally indexes the BIOS vector table + 3. In the system page are defined several standard pieces of data, including current drive, user, I/O connection, and the command line and parameters passed to the program. The layout for RP looks like this: --------------- ------------------ | RP | | System page | | | ------------------ | | | TPA | | | | | | | | | | | | | | | | | | | ------------------ | | <<------------->> | System | | | | Communications | | | | Area (SCA) | --------------- ------------------ First of all, the applications program runs in an entirely different partition from the system. It's only method of getting back to the system partition is by a system call, or by tripping some error condition, such as execution of an illegal instruction, access or other violation. The system page is simply an emulation of the normal CP/M system page. The current drive, user, iobyte and command parameters are all updated by the O/S. The WBOOT and BDOS jumps index areas in the SCA for emulation. The SCA totally replaces the BDOS and BIOS sections in a CP/M system. Basically, all BDOS and BIOS function calls are turned into system calls here. There is, then, only the one entry back to the system: the SC instruction. When rp receives a system call, it emulates the CP/M action of the associated function. MULTIPLE TASK: Going from this to a multiple task situation only requires that instead of just one program partition, we implement N many partitions, each with their own CP/M compatible environment. Rp must "traffic" the system requests to and from each partition, simultaineously with other partition calls. Internally to rp, there are only "tasks", the number of which is only limited by the system memory available: -------------------- | rp | | | | -------------- | | | Task | | | | Control | | | | Block | | | -------------- | | | | -------------- | ----------------- | | TCB |----------->| Partition | | -------------- | ----------------- | | | -------------- | ----------------- | | TCB |----------->| Partition | | -------------- | ----------------- | | -------------------- Tasks executed in parallel by rp may execute entirely within the O/S partition, or they may be allocated an applications partition, and therefore run a CP/M application. THE RP MONITOR: The users view of the system usually is via the rp built - in monitor. All the commands found in CP/M, plus a few more, are implemented. The basic command line for rp appears as: {[;/&] } Much as a standard CP/M command line, except that any number of commands may be entered on a line, separated by either ";" or "&". Separation of commands by ";" causes each command to be executed, in turn, with rp waiting for the last one to complete before starting the next. Separation of commands by "&" means that rp will not wait for the last command to complete, but go on to start the next one (in parallel). commands are one of the following: 1. Aliases. If the command given matches an alias name, that is executed. 2. Built - in. If the command given matches an internal command, that is executed 3. Submit. If a file by the name command.sub exists, that is interpreted under the rules for an exec file. 4. Application program. If a file by the name of command.com or command.pgm exists, that is given it's own partition and executed. Note that these command types are executed in the order given. If an alias is defined with the same name as a application program, the alias is executed first. The search mechanism can be circumvented by directly specifying what type of command is meant. Therefore command.com can only be a CP/M command, command.sub can only be a submit, d0:command can only be an application or submit, etc. ALIASES AND SUBMITS: An alias is defined by the alias command: alias command All the text following the alias name is entered into the alias definition. When the alias is invoked, the contents of the alias are executed. Note that ALL the remaining line past the alias command is entered into the alias definition, including any ";" or "&" characters (therefore nothing further is executed after the alias definition). The current aliases defined may be listed out via the lalias command. In the case of a submit, the submit file is executed. Both the contents of an alias and a submit are the same command lines as can be executed directly. When a submit or alias is executed, each line will be printed out before it is executed, along with the name of the alias/submit, and the number of the line being executed. If printout is not desired, the commands noshowsubmit, or noshowalias will turn this off. The commands showsubmit, or showalias will turn the print back on again. Aliases and submits can be executed within other aliases or submits, with out any limit on the nesting level. STANDARD ALIASES: You have probably noticed that the built - commands are somewhat verbose. This is intentional. The long form command names are better for insertion in a alias or submit, to make them self - documenting, and they are less likely to conflict with the names of applications programs. Finally, new names for built - in commands can be created to your heart's content using the alias mechanism. For the rest of this tour, we will place the aliases we use for commands after the proper name as command (alias). Some of the commands already discussed are: showsubmit (shows), noshowsubmit (noshows), showalias (showa), noshowalias (noshowa). FILE SPECIFICATION: Files under rp consist of a file name, with optional extension. Where allowed, The characters "*" and "?" can be used to match multiple filenames. Otherwise, the characters "a" - "z", the digits, and the character "_". Note that rp does not allow ANY character to appear in a filename as was the case in CP/M. This is enforced in exchange for the advanced command processor. For the disk area prefix, all of the following are valid: d: - Drive. d0: - Drive/user. name: - Directory name. PATHS: Three paths may be set by command. commandpath (cpath) sets the search path for commands, submitpath (spath) sets the search path for submits, and helppath (hpath) sets the path for help files. The form is: cpath a0: b: command: Any number of valid area names may be specified. MACRO SUBSITUTION: Macros are character sequences that are expanded in each line before execution. They are introduced by the "!" character. The macros available are: !! - The character "!". ! - A word from the calling line, from the 1st to the ninth. ! - A string variable. !() - A string expression. !$ - The current time/date. !# - The current drive, in lower case. !% - The current drive, in upper case. !^ - The current drive/user, in lower case. !& - The current drive/user, in upper case. !* - The current directory label. !~ - The entire calling command line. The typical use of macros is to get parameters from the calling line for an alias or submit. The usual "0" - "9" is available, but the entire line can be processed to recover parameters past the ninth. EXPRESSIONS: A full expression processor is built in to rp. The key to understanding rp expressions is to realize that the only "type" operated on is the string. When a number is given, it is checked for proper numeric format, but actually entered as a string. When a math operator is given, the strings given as operands are converted to a value, the operation performed, then the result converted back to a string. The point of this is to dispense with the need for string conversion operators. This gives the expression processor the type freedom as found in the "snobol" language. The basic objects that can be operated on are: 1. 'constant' - Any quoted sequence of characters is a string. the " character is the same as any other character. Two quotes back - to - back are interpreted as a single quote. The character "\" is used to "force" the next character, including a quote or "\". Additionally, a standard ascii control mnemonic such "\eot", or "\cr" may appear, or if followed by a number, the ascii equivalent will be entered. 2. - Numbers, in the range 0 - 65535 may be entered. These may be preceded by "$" (hex), "@" (octal), "%" (binary). 3. - A character sequence beginning with a letter or "_", and followed by any sequence of letters, digits or "_" is a string variable. Note that macros can be used in expressions, as: '!1' would be a string containing the first caller parameter. The operators, in precedence order are: = - Numeric equality. == - String equality. # - Numeric inequality. ## - String inequality. < - Numeric less than. << - String less than. > - Numeric greater than. >> - String greater than. <= - Numeric less than or equal. <<= - String less than or equal. => - Numeric greater than or equal. =>> - String greater than or equal. + - Addition. ++ - Concatenation. - - Subtraction or - Logical "or". * - multiplication. ** - String a replicated b times. / - Division. mod - Modulo. xor - Logical "xor". shl - Value a shifted left b times. shr - Value a shifted right b times. [] - The bth character of string a. [,] - The characters in string a from position b to position c. [~] - The bth word of string a. [~,] - The words in string a from position b to position c. + - The positive value of a. - - The negative value of a. not - The logical "not" of a. $$ - The hexadecimal conversion of a. @@ - The octal conversion of a. %% - The binary conversion of a. ^ - The number of characters in string a. ~ - The number of words in string a. ? - A string containing the files matching the file specification a. Typical expressions: 2+2 - The single character string '3'. 2++2 - The string '22'. 2+'4' - The string '6'. 2++'4' - The string '24'. ~'the rain in spain' - The string '4'. 'hi'**5 - The string 'hihihihihi'. 'special'[5] - The string 'i'. 'the rain in spain'[~3] - The string 'in'. $$10 - The string '$a'. 5 < 10 - The string '65535'. 10 < 5 - The string '0'. ?'test.*' - A string containing all files 'test' of any extension. COMMON COMMANDS: The directory (dir) (ls) (files) (cat) command will list the directory of the current disk area, or any given area(s). The type command types out the contents of the given files(s). The directories (pwd) (map) command will print all disk areas (that are named) available for use. The erase (era) (scratch) command will erase the given files. copy will copy file(s) from anywhere to anywhere, and will also concatenate a group of files into one, or move multiple files, or even move files and change the names of the files at the same time. move is similar to copy, except the source will be deleted if the move is successful. The help command without parameters gives a list of helps available, and given a subject, will type out the help file for that. Echo types a line of text, and calculate (calc) types the result of a string expression. The comment (c) command causes the rest of the line after the command to be ignored. The prompt command sets the current command prompt. Assign assigns the value of a string variable, and input inputs a string from the console to a string variable. time prints or sets the current system time. Clock prints the system CPU clock speed, and tick prints the multiprocessor tick time. Mount and unmount cause a disk to be either placed on line, or taken off line. Rp is a strictly mount oriented system; all disks, even floppys, must be specifically mounted and unmounted to prevent malfunction. Although this may seem a rather painful restriction, it speeds O/S operations, because the disk does not have to be continually "checksummed" to see if it has been changed, and disk sectors may be buffered to and from the disk. The connect statement allows the connection of logical to physical devices, as: connect lst:=lpt: - Connect logical list to lpt: connect con:=crt: - Connect logical console to crt: For more information on these commands, use the help function under rp. RUNNING APPLICATIONS PROGRAMS: Besides just running an application program, several controls are available. Normally, CP/M translates the command line given the program to upper case. This causes many programs problems, such as find, which can't be used in a straight - forward way to look for lower case. nouppercase will allow lower case command lines to be passed to the program, and uppercase reverses this. Whether or not an application can perform direct I/O (execute an "in" or "out" instruction is controlled by the directio and nodirectio commands. If it is not desired to allow the program to access the disk directly via BIOS sector calls, the commands readdirect, noreaddirect, writedirect, and nowritedirect will allow/disallow this. Whether or not the program can set the attributes of a file (and thus, say, set a read only file back to read/ write) is controlled via the setattribute and nosetattribute commands. PROTECTIONS: In a multi - user environment, it is necessary to provide a means to disallow access to given disk areas/privileges/devices. All such permissions have only an exclude command to disallow them. This is an extra protection feature; once lost, there is nothing a program or user can do to get them back. The exclude command is used to exclude access to a given drive, drive/user, or device: exclude d: - Disallows access to drive d:. exclude command: - Disallows access to the command directory. exclude d1: - Disallows access to drive d:, user 1. exclude lpt: - Disallows access to the lpt: device. Excludedirect excludes the ability to perform direct disk reads and writes, and also excludes the permission to change that mode. Excludeio eliminates the ability to perform direct I/O. Excludemount takes away the ability to mount and unmount disks. Excludeattribute eliminates attribute set ability. excludetimeset disallows setting of the system time. FLOW OF CONTROL: A full set of flow - of - control commands are resident in rp. Together with the full expression ability, the exec "language" of rp is quite capable, and in fact, quite reasonable programs can be written in it. The prime limitation is simply speed. Running a program off disk will by definition not be fast. The statements if, else, elseif, and endif provide conditional execution. The repeat, until, while and endwhile commands provide looping control, and the command break will abort any loop. Finally, the label and goto commands allow completely arbitrary flow of control, to any point in the program, using standard alphanumeric labels. There is no limit whatever on where the flow - of - control commands may be executed. They may be used equally well immediately from the console, in an alias, or in a submit. No limit exists on how far a goto will jump, or how many repeat or while loops may be nested. A good example of general program construction is given by bbs.sub, the exec file that runs the bbs (temporarily, until we get a hardcoded bbs in). MULTITASKING/MULTIUSER: As stated, any number of tasks may be run under rp, and any number of users. The only limit is the available system memory used to keep track of these tasks. The current list of tasks running is given by the taskstatus (tstat) (ts) command. Several statistics will be given on each task, including the number of task (numbered from 0, the first task to be started in rp, to whatever task was started last), the name of the program running, whether the task is running or stopped, if it is attached to the console, what console it is attached to, the number of open files, the home disk directory, the time it was started and how long it has been running. Tasks may be killed with the kill command, or stopped with stop, or resumed with resume. A task may be detached from the console with detach, or reattached with attach. This is done if, say, you are going to use a full screen editor and do not want the printout from the task to disturb your work. The command excludeglobaltask (yes, that is the longest built - in command name) will eliminate access to tasks not connected to the present terminal. They will not appear in the task status, nor is any control over them allowed. As we have said, ANY built in command, alias, submit or application can be run either as a "foreground" or "background" task. Additionally, the execution processor that receives and executes user commands is itself a task under rp. The execute (exec) command causes a new executive to be branched off (an exec always runs in parallel). The exec command is the key to multiple user work: exec connect con:=crt: If the main system is running under, say, tty:, and another terminal (user) is available under crt:, the above statement would "start up" an executive for that terminal. The command: exec startup Would do the same, but cause the submit file startup.sub to be executed first, before allowing the user on that terminal to execute direct lines from the console. Typically, the startup file would have statements setting the console that is to be used, the home disk area, and the standard aliases used. Also, exclude statementsthe bbs terminal on this system. If multiple tasks are allowed to run on a single terminal, the result typically is chaos; tasks fight over the characters coming from the keyboard, and the outputs of the two tasks mix on output. To provide control for this situation, you can enable a multiple task "contention" handler for the console. The buffer and nobuffer statements enable and disable this mode. In buffered mode, each task has it's own input and output line buffer, and can output characters even when not currently being displayed on the console. If any task completes it's line (with cr/lf), the whole line is printed on the console, and whatever task was "interrupted" by this is reprinted. In other words, lines are allowed to mix on the console, but not characters. All the tasks currently running under the console act as if arranged in a circle. If console controls are enabled (discussed in a bit), the CTL-R character will cause the next task in the circle to be displayed on the console. The task being displayed is the one that gets the input from the console, and can be controlled by console characters. The controls and nocontrols commands enable or disable the console control characters. These are: CTL-R - Rotate to next console task. CTL-W - Stop presently displayed task (whether it wants to or not). CTL-E - Resume a task stopped by CTL-W. CTL-T - Terminate a task (whether it likes it or not). CTL-Y - Flush input queue (dispose of all waiting characters). These characters are deliberately picked to avoid the usual controls for a program, such as CTL-S (stop), CTL-Q (resume), CTL-C (cancel). The console control characters are processed without knowledge or permission from the application running; CTL-T will terminate even a crashed program. A 256 byte input queue may be enabled for each console on the system. The queue and noqueue commands enable or disable the queue. Characters will be saved in the input queue whether or not the program running is accepting them. This provides the "type ahead" function usually implemented in multitask systems. The CTL-Y character will cause any characters in the queue to be disposed of. If a task not being displayed requires input from the console, the command demand will allow the console display to be automatically shifted to that task. The nodemand command disables this mode. Experience with the multiple display system is best gained by actual use. THIS WAY OUT: With such a diverse environment as that of the Z80, rp was built with the intention of support for for multiple operating systems interfaces and file formats. Thus in the future, support for systems on the Z80 will be expanded. The systems on Z80 include CP/M 2.2, CP/M 3.0, ZCPR, MP/M, Concurrent CP/M, and OASIS being the most popular. With the notable exception of ZCPR, these systems all share one rather depressing characteristic; support by the original programmers has been suspended, and that support has not been carried on by others. It strikes this programmer that the attitude of both terminating support on a large piece of code AND refusing to properly pass on the source to others smacks of a "take your bat and ball and go home" attitude. In designing a new O/S interface, I think it helps to consider that you are creating a virtual standard. A list of calls should not be simply a random list of features, with more features dumped at the end of the list to add function, or often to correct deficiencies of the preceding calls. Creating a system interface that is regular enough to be easily ported to other systems is the greatest favor an O/S programmer can do for applications writers, and an acknowledgement that the applications are far more important than the O/S itself. Why design a custom interface for rp at all ? Starting with a clean slate enables us to cleanly and clearly support the new features of both rp and the Z280. I also believe that no microprocessor interface to date with the exception of UNIX (which did not start out on a microprocessor) displays good quality and careful design. Obviously, however, each new O/S designer has come to that conclusion, accounting for the interface to each new O/S being incompatible with others. Rp compensates (but does not ultimately correct !) for this situation by providing an organized solution for foreign interface support. These modules internal to rp are call Foreign Operating Systems, or FOS modules. A FOS under rp intercepts all system calls, traps and other exceptions from a target program. For the present version, two FOS modules are built in to rp; the CP/M fos and the RP fos. RP NATIVE INTERFACE: Calls to rp are performed directly by the Z280 "sc" instruction. Each system call number corresponds to a different system function. A standard set of system error codes is returned in a (with 0 meaning no error), and all parameters being passed in registers. If an invalid or unsupported system call is used, and error for that will be returned. The system calls used are quite simple and logical. File names are passed as strings, and the required parsing of file names is carried out by the O/S. The program is not required to keep or maintain "fcb's" in it's own memory. Files are kept track of using small logical file numbers. Files under rp are universal; the file string specified for opening can be the name of a file, an I/O device, or even a logical file/device name. Devices can be read or written the same way as ordinary files. Files under rp are blockless; the program can specify any number of bytes to be read or written from 1 to 65535, without any need to perform blocking or deblocking. The system calls currently available under rp fos are: terminate - Terminates the program run. Will also accept an error code, and print the message corresponding to the standard error. open - Accepts a file name string, and mode flags. The given file or device is opened, and a logical file number from 1 to 255 is returned. close - Closes a file by the logical file number. read - From 1 to 65535 bytes are read from the given file to program memory. write - From 1 to 65535 bytes are written to the file from program memory. location - The current byte location in the file is returned. position - The file position is moved to the given location. length - The number of bytes in a file are returned. size - The number of bytes in a file are set (the file is truncated). copy - A file by a given string is copied to the file given in a destination string. move - A file by a given string is moved to the file given in a destination string. time - Returns the current time and date. schedule - Accepts a time and date, and stops the program until PAST that time and date (if the date given has already past, return is immediate).