RSX Systems Programming in C Anthony E. Scandora, Jr. Science Applications, Inc. 1701 E. Woodfield Road, Suite 819 Schaumburg, IL 60195 DRAFT February 9, 1984 1. Introduction C is a relatively low level language similar to BCPL and Bliss. It provides almost the full access to the computer's architecture that assembly language provides, in a language with structured control statements (if, else, for, while, etc.), structured data, flexible pointer arithmetic, and a simple macro processor. Like BCPL and Bliss, it does not have any I/O statements, and does not support string functions or exception handling. Such capabilities are implemented as subroutines, rather than language statements. Because of this, C can be used where it might appear that only assembly language can do the job, and existing subroutine libraries make it easy to use features that are found in higher level languages. That the Unix* operating system kernel, its compilers and utilities, and most applications that run under that operating system are all written in C is testimony to its versatility. Unix is written in and is very dependent on C. The converse is not true. C is available for all current PDP-11 operating systems from several vendors and from the Decus library, for the VAX from DEC and independent vendors, and from many sources for many other CPUs and operating systems. The language and its portable I/O library, which were developed along with the Unix operating system, provide full access to Unix system calls, which are implemented as C-callable functions. Special problems occur when trying to interface C programs to non-Unix operating systems. This paper addresses some of those problems for the RSX family of operating systems, including RSX-11M, RSX-11M-Plus, RSX-11S, IAS, P/OS, and the VAX/VMS AME. Some of these problems apply to RT-11, and to non-Unix operating systems in general. In an RSX environment, C can issue EMTs and service ASTs and SSTs. It can call FCS, RMS, CSI, .TPARS, $EDMSG, and the FMS-11 form driver, all without using the Unix-like portable I/O library. The SIG tape is going to press tomorrow. I am out of time. There will be an updated submission on the next tape. Any corrections or other comments will be appreciated. _______________ * Unix is a trademark that belongs to one of the remnants of what used to be Ma Bell. RSX Systems Programming in C Page 2 Files 2. Files This submission to the RSX/IAS SIGtape contains quite a few files that make it easy to use RSX's facilities from C. They have all been developed to serve real needs and are being used in production programs written entirely in C. That is not to say that they are all perfect. When I typed up all of the EMTs, for example, I did not try them all. I have used many of them, and so far, they have all worked. Files are in the following directories: [300,130] Documentation [300,131] EMT header files: *.h defining structures *.emt defining xxx$S forms of all the EMTs *.dpb defining the DPBs as structures *.ast and *.sst defining all of the ASTs and SSTs [300,132] SYSLIB interface fcs.h defining FCS, CSI, GCML control blocks fcscal.mac interface routine to FCS, CSI, GCML $EDMSG interface routine [300,133] RMS-11 interface (not on this tape) RAB and FAB defined as structures C-callable interface routines to all RMS functions [300,134] FMS-11 interface fdv.h defining arg block, req arg block as structures C-callable interface routines to all FDV functions RSX Systems Programming in C Page 3 Data Types 3. Data Types C supports one, two, and four byte signed and unsigned integers, single (four byte) and double (eight byte) precision floating point numbers, and bit fields. All other types are arrays or structures containing the primitive types or smaller arrays or structures. Since C was developed on a PDP-11, it handles the hardware's data types quite well. Unfortunately, the standard (Kernighan and Ritchie) is unclear about how to handle mixed sizes and if some of the types, like "unsigned char" are supported. The rest of this section is for the Whitesmiths compiler. I will add others later. 3.1 One Byte Integers Whitesmiths' compiler supports "char" and "unsigned char" types for one byte integers. Be careful with the "unsigned char" type. Sometimes it may fool you and get sign extended. Switch statements, subroutine arguments, and register variables, for example, promote "char" values to "int" values, sometimes with a sign extension. To be safe, when you move an "unsigned char" to a bigger type, and it with 0377 and be sure. 3.2 Two Byte Integers Whitesmiths compiler supports "int" and "short" signed two byte integers, and "unsigned" (unqualified), "unsigned short" and "unsigned int" unsigned two byte integers. The PDP-11 has two byte words, registers, and addresses, and you can hardly go wrong using one of these types. Do not use an "int" when you really want a pointer. If you are going to put an address in a variable, declare it to be a pointer. 3.3 Four Byte Integers Here is where you get in trouble. The PDP-11 floating point hardware, double register instructions, the RSX-11 operating system, and C all say that the high order word comes first. Whitesmiths C supports the "long" and "unsigned long" data types. Unfortunately, the VAX hardware, Fortran 77, Fortran IV, and RMS put the low order word first. The following routine will reverse the words of a long integer: RLONG:: MOV 2(SP),R1 ; low address word to high MOV 4(SP),R0 ; high address word to low RETURN 3.4 Floating Point Numbers C supports "float" and "double" four and eight byte floating point numbers. These are in the format of the PDP-11 floating point processor and are the same as VAX F and D floating point numbers. All floating point expressions in C are evaluated as doubles, and all arguments are passed to subroutines as doubles. RSX Systems Programming in C Page 4 Data Types 3.5 Character Strings C does not have any special support for character strings. A string is just an array of "char" one byte integers. By convention, strings are null terminated, and the compiler appends a null terminator to all C literal strings. 3.6 Radix 50 Names Radix 50 is a nice way for PDP-11s to encode three characters into a single word. Task names and partition names, for example, are 6 radix 50 characters padded right with blanks, and encoded into a 32 bit unsigned integer, but they are not too convenient for high level languages to deal with. Bliss and DEC's Fortran compilers understand radix 50 constants, but so far, C doesn't. Routines that convert between ASCII and radix 50 are available and easy to call. If you want a radix 50 constant for a task name or partition, create a short Macro file with a .RAD50 directive, look at its listing, and write a #define for the value as a long (6 character) or a short (3 character) constant. The following examples from a Macro listing: 1 000000 050712 131574 .RAD50 /MCR.../ 2 000004 026226 000000 .RAD50 /GEN / 3 000010 114730 062072 .RAD50 /XXXPAR/ Translate to: #define MCR___ 012162531574L #define GEN 005445400000L #define XXXPAR 023166062072L RSX Systems Programming in C Page 5 RSX Executive Directives 4. RSX Executive Directives The following section assumes familiarity with the RSX-11M/M-Plus Executive Reference Manual or its equivalent for IAS, P/OS, or the _________ _________ ______ VAX/VMS AME. The macros accompanying this paper are from the common RSXMAC.SML that is shipped with RSX-11M and M-Plus. Other operating systems may need minor changes. The AME's ELP$ directive and the P/OS-specific directives are not part of this distribution. All of the QIO macros ignore their priority arguments and build zeroes into their priority bytes. This is what the M/M-Plus RSXMAC.SML does. IAS really passes and uses that argument. 4.1 Macro EMTs Macro programs have two ways to execute directives: push a directive parameter block (DPB) on the stack and do an EMT 377, or push the address of a DPB on the stack and do an EMT 377. The first byte of all DPBs is always odd, and the exec uses that to decide if the DPB is on the stack or if the top of the stack points to it. DEC supplies macros with RSX/IAS to assemble static DPBs and a DIR$ macro to push a DPB's address on the stack and do an EMT 377. For example, the following assembles two DPBs: .MCALL MRKT$,STSE$ MRKT: MRKT$ 1,2,3 ; mark time on EFN 1 for 2 minutes STSE: STSE$ 1 ; stop for event flag 1 and the following uses their addresses to ask RSX to wait for two minutes: .MCALL DIR$ DIR$ #MRKT ; mark time on EFN 1 for 2 minutes BCS ERR ; don't continue if mark time failed DIR$ #STSE ; stop for event flag 1 DEC also supplies macros to build DPBs on the stack and do an EMT 377. The following does the same thing by building DPBs on the stack at runtime: .MCALL MRKT$S,STSE$S MRKT$S #1,#2,#3 ; mark time on EFN 1 for 2 minutes BCS ERR ; don't continue if mark time failed STSE$S #1 ; stop for event flag 1 RSX Systems Programming in C Page 6 RSX Executive Directives 4.2 The EMT function for C With the following emt function, .GLOBL $DSW EMT:: MOV (SP)+,R1 ; point top of stack at caller's args MOV SP,R0 ; save stack pointer EMT 377 ; execute directive and pop stack MOV R0,SP ; "unpop" stack MOV @#$DSW,R0 ; return directive status word JMP (R1) ; return to caller a C program can execute any RSX directive just like a Macro program. The emt routine positions its caller's argument(s) (either a DPB or a pointer to one) at the top of the stack, calls RSX with an EMT 377, cleans up, and returns the directive status word. Call "sts = emt (&dpb)" to use a DPB built elsewhere, and call "sts = emt (DIC, 2nd word, etc...)" to use the stack form. The carry bit is not returned to emt's C caller, but the DSW is. Macro programs may either test the carry bit (set means failure) or the DSW (negative means failure) after an EMT 377. C's emt routine returns the DSW, which will be negative after a failed directive. It is usually IS_SUC (1) for success or a negative error code. Directives that set or clear an event flag return IS_SET (2) or IS_CLR (0) for the previous state of the flag or IE_IEF (-97) for an illegal event flag. GMCR$ returns the number of bytes in the command line or IE_AST (-80) if there is no command line or if it has already been read. QIO$ and QIOW$ directives return IS_SUC to the DSW when the I/O packet is sent to the driver. You must check the first byte of the I/O status block to see if the I/O operation actually succeeded. 4.3 C EMT definitions Two sets of include files implement most of the directives for C exactly the way the system macro library implements them for Macro. C programs must #include the definition file for each macro or structure used, just as Macro programs must .MCALL every macro used. One set of include files (#include ) defines all of the DPBs as structures and has macros to initialize them like the xxx$ macros, and the other defines the stack forms (#include ) for all of the EMTs like the xxx$S macros. These directive macros are C translations of the common set of macros that DEC supplies for RSX-11M, 11S, and M-Plus in RSXMAC.SML. The .dpb files each contain a typedef named directive_DPB of a structure and a macro named directive_ to initialize it. The first argument of all of the initialization macros is a name for the variable to be initialized, and the rest of the arguments are the arguments defined in the Macro definitions in RSXMAC.SML. For example, clef.dpb has a typedef called CLEF_DPB and a macro called CLEF_ to initialize it. The statement "static CLEF_ (clef, 15)" expands to "static CLEF_DPB clef = {31, 2, 15}" which creates a static DPB called "clef" that asks RSX to clear event flag 15. This is used in a "sts = emt (&clef)" statement. You can change the "static" to an "extern" if you want to make the DPB's name known to other modules. RSX Systems Programming in C Page 7 RSX Executive Directives While the program is running, it can say "clef.efn = 16" to change the event flag number to 16. The names of the elements of all of the DPB structures are the names of the Macro arguments as documented in the Executive Reference Manual and the "Executive Directive Summary" in the _________ _________ ______ Mini-Reference manuals of RSX-11M and M-Plus and as defined in ______________ RSXMAC.SML. The names of the elements could have been their offset names (in the "clef.efn" example above, it would have been "clef.c_leef"), but that is ugly to look at and not documented in the Mini-Reference manuals. ______________ The .emt files each contain a single macro named directive_S that expands into an emt call. For example, clef.emt defines a macro called CLEF_S. The statement "sts = CLEF_S (15)" tells RSX to clear event flag 15. Since C's macro expander doesn't understand blank arguments, defaults must be explicitly specified as zeros. Every argument is cast to the proper type, so it is safe to use a zero (0) to default any argument. 4.4 Examples The Macro examples above translate into C as follows: #include /* this file described in next section */ #include #include static MRKT_ (mrkt, 1, 2, 3, 0); /* last arg is AST */ static STSE_ (stse, 1); if (emt (&mrkt) > 0) emt (&stse); and #include #include #include if (MRKT_S (1, 2, 3, 0) > 0) STSE_S (1); Note that the mark time directive has an AST address for its last argument. Macro assembles a zero for the AST if it is not specified, but C macro calls must define all their arguments, so the AST is specified as zero. 4.5 Memory Management (PLAS) Directives The Memory Management (PLAS) Directives require these structures: struct RDB { unsigned id, siz; long nam, par; int sts; unsigned pro; }; struct WDB { char id, apr; unsigned *bas, siz, rid, off, len; int sts, *srb; RSX Systems Programming in C Page 8 RSX Executive Directives }; C programs must #include or #include before #including any macros that reference RDBs or WDBs. 4.6 ASTs or SSTs Any macro that uses an AST or SST must #include before #including any macros that might have an AST or SST, even if the AST or SST is not used. 4.7 One Word DPBs Directives that have a one word DPB like STOP$ and SPND$ are always smaller and faster in their stack form. DEC supplies both forms of those macros (STOP$ and STOP$S), so to be consistent, there are .dpb and .emt files which define both forms of these directives for C. In most cases, you are better off with a "STOP_S ()" call than with a "static STOP_ (stop); emt (&stop)" call. 4.8 Exit Directives The C runtime library contains exit and exst subroutines which usually should be called to exit. However, the directives EXIF, EXIT, EXST, and RCVX are defined. Exit status codes EX_SUC, EX_ERR, EX_SEV, and EX_WAR are defined. 4.9 Cancel Mark Time The release of RSX-11M V3.2 and RSX-11M-Plus introduced selective CMKTs. CMKT is defined to be the global version with a one word DPB, and CMKTS has a three word DPB for selective CMKTs. PDP-11 Macro can count arguments and build either the one word global version or the three word selective version. C's macro processor is not smart enough to do that, so there must be two versions of this directive. 4.10 Unimplemented Directives ASTX is part of the AST/SST interface described later, and can not be called directly from C. CINT is not yet defined; it needs a support subroutine. GMCR_ and GMCR_DPB are defined in gmcr.dpb, but GMCR_S would be silly, so there is no gmcr.emt file. SCAL is an obsolete relic from M-Plus V1.0, not defined, and probably useless. RSX Systems Programming in C Page 9 ASTs and SSTs 5. ASTs and SSTs ASTs and SSTs are handled in C by Whitesmiths' entrap subroutine. The AST or SST traps to the following: jsr r1,@#entrap .word code,fn The C runtime routine entrap calls fn with the arguments: fn (code, r0, r1, ...) where code is the contents of the first word after the jsr r1, r0 and r1 are the saved registers, and the remaining arguments are what was on the stack at the beginning of the AST or SST. These should be modified with great caution, as they are the real words on the AST/SST stack, not copies, and they are used to restore the original environment. If code >= 0, it is the number of extra words on the AST stack after the DSW. Those words will be popped off the stack when fn returns, and then an ASTX$S will be done. If code < 0, it represents the number of words on the SST stack, including the PS and PC. -code - 2 words will be _________ popped off the stack when fn returns, then an RTI will be done. The following typedef defines that 4 word trap subroutine for C: typedef struct { int jsr_r1, *entrap, code, *fn; } AST; Macros are defined in to build the 4 word trap soubroutines for all of the ASTs and SSTs. The following example declares the entrap subroutine, uses SFPA_AST to build the jsr r1, entrap to call fp_fn on floating point exceptions, and then uses SFPA_S to do the emt: extern int entrap (); static SFPA_AST (sfpa_ast, _&fp_fn); SFPA_S (_&sfpa_ast); The above example requires &fp_fn to be known at compile time. The same thing can be done at run time as follows: extern int entrap(); int (*pfp_fn) (); static SFPA_AST (sfpa_ast, 0); sfpa_ast.fn = pfp_fn; SFPA_S (_&sfpa_ast); RSX Systems Programming in C Page 10 The Portable I/O Library and Record I/O 6. The Portable I/O Library and Record I/O There are several subroutine packages that are called "Portable I/O Libraries." These packages are all similar in function, and all use Unix-style character stream I/O. 6.1 Unix I/O System Calls The Unix open, create, close, read, and write system calls, or their equivalents on other systems, treat all devices and files as a stream of bytes with no record structure, express or implied. The Unix I/O system internally buffers disk blocks, hiding their structure from all programs, which can only see files as a randomly accessible array of bytes. By convention, text is stored with a single line feed character between lines, and the portable I/O library recognizes line feeds as line terminators. Beyond that, files have no implicit record structure. 6.2 Buffered I/O Functions The Unix read and write system calls present their callers with a byte stream interface to disk files, but using a byte stream interface to block oriented disks is inefficient. If you make a read or write system call for every character, you will burn up a lot of CPU cycles in system call overhead and force the operating system to block or unblock your data. It is much more efficient to block data yourself and only do reads and writes in 512 byte increments. To this end, Unix has a "portable I/O library," with functions like fopen, gets and printf, and Whitesmiths has a similar package with functions like fopen, getlin, and putfmt. These functions use a 512 byte buffer in the program's address space to block and unblock data, and only do read and write system calls in increments of full disk blocks. Routines to get and put characters, lines, and to use a format control string for conversion between text and binary, make it easy to do most I/O, even though there are no I/O statements in the language. 6.3 Files-11 Files RSX files are randomly addressable arrays of 512 byte blocks. RSX programs ultimately issue QIOs to transfer 512 byte blocks directly between disks and their own buffers. Unlike a Unix array of bytes, most RSX files are implicitly structured into logical records. Records can be fixed or variable length, and have certain attributes, such as whether or not records may span disk blocks, and that the first character of every record is a Fortran carriage control character or that there is an implicit carriage return and line feed between records. This informations is written into the index file at the time a file is created, and is read at the time it is opened. 6.4 RSX Logical Records DEC provides two sets of subroutines, the smaller and faster FCS-11*, _______________ * FCS is described in the "IAS/RSX I/O Operations Manual" in the the RSX-11M, 11S, M-Plus, and IAS manual sets. RSX Systems Programming in C Page 11 The Portable I/O Library and Record I/O and the 22KW, slower, and more powerful RMS-11**, to block the different record formats into 512 byte blocks and to issue QIOs to devices. Most programs use the FCS or RMS (one or the other) routines to transfer data a logical record at a time, and to issue the complex and only recently documented QIOs necessary to create, open, close, and delete files. High level language runtime systems and macro programs maintain simple control structures for FCS or RMS, and call their create, open, close, put, and get routines. These routines make the wide variety of on-disk structures transparent to their callers, who deal only with logical records. 6.5 Unix Streams vs. RSX Logical Records Unix byte streams map very simply onto RSX disk blocks, making it a trivial matter to produce C-callable read and write routines just like Unix's. Unfortunately, all RSX utilities (editors, compilers, print spoolers, ...) work with fixed or variable length logical records. If C programs did Unix-style stream I/O, they could not read text files created by EDT, and only C programs could read files created by C programs. The developers of VAX-11 C solved this problem by getting the RMS developers to put full support for stream files into VAX/VMS's RMS. A VAX-11 C program can write a byte stream file and the RMS get logical record subroutine will collect bytes from it until it hits a line feed, then it will return a logical record. In the other direction, the C read function can disassemble logical records into a byte stream. This is not easy to do. It is partially implemented in RMS-11, but most user programs and all RSX utilities use the smaller, faster FCS. Putting stream support into FCS without making it bigger would be a real challenge. The Whitesmiths read and write routines are called just like the Unix system calls, and they recognize line feeds as terminators, use private buffers to block lines of text into RSX variable length records and back, and actually read and write 512 byte disk blocks. Neither FCS nor RMS can do this, so the Whitesmiths I/O library must do its own record blocking and disk block I/O. It only uses FCS (RSX/IAS) or RMS (P/OS) to open and close files. The buffered I/O routines block and unblock 512 byte buffers in the form of a Unix byte stream, then they call read or write. This works, but it blocks and unblocks twice, once converting between the caller's stream and a 512 byte stream, and once between a 512 byte stream and Files-11 blocked logical records. The Decus C library solves the problem by only implementing buffered I/O. The Decus buffered I/O routines convert directly between the caller's stream and Files-11 blocked logical records. This is faster than Whitesmiths' method and uses less buffer space, but it does not emulate the Unix open, create, read, and write system calls. Programs that use these will work with the Whitesmiths library but must be rewritten for the Decus library. _______________ ** RMS is described in several manuals in the "PDP-11 Record Management System" volume(s) of the RSX-11M, M-Plus, IAS, and P/OS toolkit manual sets. RSX Systems Programming in C Page 12 The Portable I/O Library and Record I/O 6.6 What Should You Use? Many programs process text. They get a token or a line, parse it, do something with it, and format and put an output token or line. The C portable I/O libraries, though not easy to write for RSX, have been written and work for most text processing applications. If your application fits well with a C portable I/O library, use it. If you want to use Unix code under RSX or to write portable code, use a portable I/O library. Whitesmiths' portable I/O library allows you to write code that runs under any DEC PDP-11 operating system, VAX/VMS, Unix, its lookalikes, and many other operating systems. This library is not the same as the Unix library, but it is supported by Whitesmiths for many CPUs and operating systems. The Unix portable I/O library has become the "standard" C I/O library. There is a chapter on it in Kernighan and Ritchie's The C Programming ___ _ ___________ Language, which has become the defacto C standard. The Decus library ________ implements most of it for the RSX family, RT-11, and RSTS/E, and now, Whitesmiths has a version of it. You can use either the Whitesmiths library or the Unix library with the Whitesmiths complier. If you want to share code with any machine and operating system, use the Unix portable I/O library, which is now supported by Whitesmiths. If you are comfortable with the large selection of machines and operating systems supported by Whitesmiths, consider using their library. If you need to get at the guts of RSX I/O and don't care about portability, you can forget the portable I/O libraries and do your own QIOs or use one or more of DEC's I/O packages. RSX Systems Programming in C Page 13 Direct I/O Through QIOs 7. Direct I/O Through QIOs All RSX I/O is done through QIO and QIOW directives. DEC'S FCS-11, RMS-11, and the FMS-11 form driver all do QIOs for their callers, as do all versions of C portable I/O libraries. These subroutine packages exist because it is usually easier to use one of them than to do QIO directives. If you need to do your own QIOs, they are defined just like all of the other directives, and variations of the QIO macros and structures for disks, terminals, and setting status are defined (see the next section). You can #include to define an I/O status block, #include to define all QIO function codes, #include the right QIO definitions, and you will have all you need to do your own QIOs from C. 7.1 Special QIO Directives for Disk Files Qiod.emt, qiod.dpb, qiowd.emt, and qiowd.dpb define easier access to disk QIOs. All four of these have the same first six arguments (fnc, lun, efn, pri, iosb, ast) as all QIOs, and three final device specific arguments: a buffer, its length, and a (long) block number. Structure types QIOD_DPB and QIOWD_DPB's last three members are called stadd, size, and blk. Example: #include #include #include /* must be included even though not used */ #include #define INLUN 1 #define INEFN 1 #define OUTLUN 2 #define OUTEFN 2 static IOSB iosb; static char buf[512]; static QIOWD_ (rvb, IO_RVB, INLUN, INEFN, 0, &iosb, 0, \ buf, sizeof (buf), 0); static QIOWD_ (wvb, IO_WVB, OUTLUN, OUTEFN, 0, &iosb, 0, \ buf, sizeof (buf), 0); long inblock, outblock; rvb.blk = inblock; wvb.blk = outblock; for (; emt (&rvb), iosb.sts > 0; rvb.blk++, wvb.blk++) emt (&wvb); 7.2 Special QIOs for Terminals and Other Unit Record Devices Qiot.emt, qiot.dpb, qiowt.emt, and qiowt.dpb define easier access to terminal and other unit record QIOs. All four of these have the same first six arguments (fnc, lun, efn, pri, iosb, ast) as all QIOs, and three final device specific arguments: a buffer, its length, and a format control character or timeout. Structure types QIOT_DPB and QIOWT_DPB's last three members are called stadd, size, and vfctmo. QIOT_, QIOT_S, QIOWT_, and QIOWT_S all expand with -1 in the fourth and fifth parameter list words, guaranteeing an IE.EOF error if ever done on RSX Systems Programming in C Page 14 Direct I/O Through QIOs a LUN that is assigned to a disk. Example: #include #include /* must be included even though not used */ #include static char message[] = "Message text"; QIOWT_S (IO_WVB, 5, 1, 0, 0, 0, message, sizeof (message) - 1 , ' '); 7.3 Special QIOs for Setting Status QIOS_S and QIOWS_S are defined for setting status registers. Their first six arguments are the same as any QIO's, and they take one more, an int, called "sts" in a QIOS_DPB, which will be placed in the first word of the QIO parameter list. 7.4 (A)Synchronous I/O The RSX I/O system does not do any internal buffering or disk caching. Unix does some reading ahead and writing behind for performance, but programs have no control of this, and all I/O appears to be synchronous to all programs. RSX I/O is asynchronous and is done directly between a device controller and program memory. This forces programs to be at a fixed place in memory during a QIO, which may be transferring data to or from a program's buffer while the program is running. RSX Systems Programming in C Page 15 Calling FCS, CSI, and GCML from C 8. Calling FCS, CSI, and GCML from C Most RSX programs call FCS-11 subroutines to do I/O. They are all called with R0 pointing to a File Descriptor Block (FDB), a 96 byte data structure that contains their arguments, and some of the routines take a pointer to a Filename Block (FNB), the last 30 bytes of an FDB, in R1. They save all registers and return status codes in two one byte fields in the FDB and with the carry bit clear if successful and set if not. All C needs to call FCS is to define the FDB as a structure (#include ) and the following Macro interface routine: .GLOBL FCSCAL FCSCAL: MOV R5,-(SP) ; build frame pointer MOV SP,R5 MOV 6(R5),R0 ; second arg is FDB MOV 10(R5),R1 ; optional third arg is FNB CALL @4(R5) ; first arg is FCS routine to call MOV #1,R0 ; return 1 (C true) if carry is clear SBC R0 ; return 0 (C false) if carry is set MOV (SP)+,R5 ; restore frame pointer RETURN GCML and CSI, documented after FCS in the I/O Operations Manual, get and ___ __________ ______ parse command lines. Their control blocks are passed in R0 just like FDBs to FCS routines, and the fcscal routine can call them from C. GCML gets a command line. Its control block contains a buffer descriptor that gets copied into the CSI control block for parsing. The CSI control block contains the six word dataset descriptor with the device, directory, and filename components for the open call. After CSI parses a filespec into those components, the FDB can point to the dataset descriptors in the CSI control block for file operations. It is press time. More detail and examples are forthcoming. RSX Systems Programming in C Page 16 Calling RMS-11 from C 9. Calling RMS-11 from C The next SIG tape will have RAB and FAB structure definitions, files that define the symbolic constants in their fields and an include file that defines all of the RMS error codes. I don't have time right now to publish them. The following functions will also be defined: rclose (fab) rcon (rab) rcreat (fab, nam, fac) rdelet (rab) rdis (rab) rfind (rab) rfree (rab) rget (rab, ubf, usz) ropen (fab, nam, fac) rput (rab, rbf, rsz) rrewin (rab) rupdat (rab, rbf, rsz) RSX Systems Programming in C Page 17 Calling FMS-11 from C 10. Calling FMS-11 from C The FMS-11 form driver has its own special calling sequence and four special data structures. The "status block" is an array of two integers and the "impure area" is an array of integers that the form driver uses for scratch space. The "required argument block" points to the status block and impure area and contains the LUN that the form driver will use to get forms from form library files. Finally, the "argument block" contains a pointer to the required argument block, buffer descriptors, field descriptors, and a function code. All calls are made by a JSR PC,$FDV with R0 pointing to the argument block. When $FDV returns, the registers are saved, the carry is clear to indicate success or set to indicate trouble, the status block contains an explanation, and buffer and field descriptors in the argument block may be updated. All of these structures are defined by an #include . To set up these structures, do the following: #include static int fdvsts[2] = { 0 }; static int fdvimp[???] = { sizeof (fdvimp) ); static FDV_REQ fdvreq = { &fdvsts, FDVLUN, &fdvimp }; extern FDV_ARG _fdarg = { 0, &fdvreq }; The first declaration is the two word status block. Next comes the impure area, big enough to hold the biggest form. You can get impure area sizes from the form editor or the form utility. The manual says that you have to add 64 bytes to the displayed form size for the high level language interface. This is not that interface, and you do not need the extra 64 bytes. Note that this is an integer array, and that the form editor and utility display sizes in bytes. The first word of the impure area must contain its length in bytes. The third declaration is the required argument block. It points to the status and impure arrays and contains the LUN that the form driver will use to get forms from form libraries. If you are doing any other I/O, make sure that this LUN won't be used anywhere else, and if it is >6, that you have a UNITS= in the task builder command file. The fourth declaration is the argument block. It must have the global name _fdarg (.FDARG), and its second word must point to the required argument block. The rest of the argument block is filled in by the C form driver interface routines. The C form driver interface routines all find their argument block at the global symbol .FDARG (_fdarg). This saves passing the address of the argument block in every call at the expense of having to declare the argument block somewhere as an extern with a special name. C form driver routines that may need to specify a field all pass the field name as a pointer to a six upper case character blank filled string in the first argument and field index as an int in the second. The interface routines copy these to the nam and num fields of the argument block. Display routines take an output buffer whose address RSX Systems Programming in C Page 18 Calling FMS-11 from C and length are copied from the next two arguments to the val and len fields of the argument block. Routines that get data do not pass buffer descriptors as arguments, rather, when they return, the val and len fields of the argument block describe the data. This is particularly convenient for the Whitesmiths library, as they can be arguments to the cpybuf, btoi, and btod routines. The Unix library generally prefers null terminated strings to an address and length, so you may have to copy strings and append nulls there. You can write a simple routine to look at _fdarg.val and _fdarg.len and copy the string, minus its trailing blanks, and terminate it with a null. This is the Macro interface, not the "high level language" interface. The high level language interface is for Fortran, Basic, and Cobol and translates its calls to the Macro interface. It requires the extra 64 bytes in the impure area, has a few extra calls for its own use, copies strings with null terminators, and can be called from C like any other Fortran-callable routines. If you really prefer null terminated strings to addresses and lengths, the Fortran routines may help you out. On the other hand they are more awkward to use than the Macro calls, less flexible, and bigger. If you use the Fortran routines with Whitesmiths' C, you will discover a name conflict on finit. The form driver starts up with a routine of that name, and unfortunately, the Whitesmiths I/O library routine that initializes an I/O buffer has the same name. The following example gets an integer from a field and puts a float to another: int number; float real; char tmpbuf[80]; fdvget ("INTFLD", 0); btoi (_fdarg.val, _fdarg.len, &number, 10); fdvput ("REAL ", 0, tmpbuf, dtob (tmpbuf, real, 10, 5); The following form driver functions are defined: fdvall () fdvany () fdvcls () fdvcsh (nam, num) fdvdat (nam, num) fdvget (nam, num) fdvgsc (nam) fdvlst (val, len) fdvnam (nam) fdvopn (nam) fdvpal (val, len) fdvpsc (nam, val, len) fdvput (nam, num, val, len) fdvral () fdvrtn (nam, num) fdvsho (nam, num) fdvspf () RSX Systems Programming in C Page 19 Calling FMS-11 from C fdvspn () fdvtrm (trm, nam, val, len) They are all in the library [300,134]CFDV.OLB, which should be searched just before FDVLIB.